Nico Golde:
[apps/madmutt.git] / complete.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #if HAVE_CONFIG_H
11 # include "config.h"
12 #endif
13
14 #include "mutt.h"
15 #ifdef USE_IMAP
16 #include "mx.h"
17 #include "imap.h"
18 #endif
19 #ifdef USE_NNTP
20 #include "nntp.h"
21 #endif
22
23 #include "lib/str.h"
24
25 #include <dirent.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30
31 /* given a partial pathname, this routine fills in as much of the rest of the
32  * path as is unique.
33  *
34  * return 0 if ok, -1 if no matches
35  */
36 int mutt_complete (char *s, size_t slen)
37 {
38   char *p;
39   DIR *dirp = NULL;
40   struct dirent *de;
41   int i, init = 0;
42   size_t len;
43   char dirpart[_POSIX_PATH_MAX], exp_dirpart[_POSIX_PATH_MAX];
44   char filepart[_POSIX_PATH_MAX];
45
46 #ifdef USE_IMAP
47   char imap_path[LONG_STRING];
48 #endif
49
50   dprint (2, (debugfile, "mutt_complete: completing %s\n", s));
51
52 #ifdef USE_NNTP
53   if (option (OPTNEWS)) {
54     LIST *l = CurrentNewsSrv->list;
55
56     strfcpy (filepart, s, sizeof (filepart));
57
58     /*
59      * special case to handle when there is no filepart yet.
60      * find the first subscribed newsgroup
61      */
62     if ((len = safe_strlen (filepart)) == 0) {
63       for (; l; l = l->next) {
64         NNTP_DATA *data = (NNTP_DATA *) l->data;
65
66         if (data && data->subscribed) {
67           strfcpy (filepart, data->group, sizeof (filepart));
68           init++;
69           l = l->next;
70           break;
71         }
72       }
73     }
74
75     for (; l; l = l->next) {
76       NNTP_DATA *data = (NNTP_DATA *) l->data;
77
78       if (data && data->subscribed &&
79           safe_strncmp (data->group, filepart, len) == 0) {
80         if (init) {
81           for (i = 0; filepart[i] && data->group[i]; i++) {
82             if (filepart[i] != data->group[i]) {
83               filepart[i] = 0;
84               break;
85             }
86           }
87           filepart[i] = 0;
88         }
89         else {
90           strfcpy (filepart, data->group, sizeof (filepart));
91           init = 1;
92         }
93       }
94     }
95
96     strcpy (s, filepart);
97
98     return (init ? 0 : -1);
99   }
100 #endif
101
102 #ifdef USE_IMAP
103   /* we can use '/' as a delimiter, imap_complete rewrites it */
104   if (*s == '=' || *s == '+' || *s == '!') {
105     if (*s == '!')
106       p = NONULL (Spoolfile);
107     else
108       p = NONULL (Maildir);
109
110     mutt_concat_path (imap_path, p, s + 1, sizeof (imap_path));
111   }
112   else
113     strfcpy (imap_path, s, sizeof (imap_path));
114
115   if (mx_get_magic (imap_path) == M_IMAP)
116     return imap_complete (s, slen, imap_path);
117 #endif
118
119   if (*s == '=' || *s == '+' || *s == '!') {
120     dirpart[0] = *s;
121     dirpart[1] = 0;
122     if (*s == '!')
123       strfcpy (exp_dirpart, NONULL (Spoolfile), sizeof (exp_dirpart));
124     else
125       strfcpy (exp_dirpart, NONULL (Maildir), sizeof (exp_dirpart));
126     if ((p = strrchr (s, '/'))) {
127       char buf[_POSIX_PATH_MAX];
128
129       *p++ = 0;
130       mutt_concat_path (buf, exp_dirpart, s + 1, sizeof (buf));
131       strfcpy (exp_dirpart, buf, sizeof (exp_dirpart));
132       snprintf (buf, sizeof (buf), "%s%s/", dirpart, s + 1);
133       strfcpy (dirpart, buf, sizeof (dirpart));
134       strfcpy (filepart, p, sizeof (filepart));
135     }
136     else
137       strfcpy (filepart, s + 1, sizeof (filepart));
138     dirp = opendir (exp_dirpart);
139   }
140   else {
141     if ((p = strrchr (s, '/'))) {
142       if (p == s) {             /* absolute path */
143         p = s + 1;
144         strfcpy (dirpart, "/", sizeof (dirpart));
145         exp_dirpart[0] = 0;
146         strfcpy (filepart, p, sizeof (filepart));
147         dirp = opendir (dirpart);
148       }
149       else {
150         *p = 0;
151         len = (size_t) (p - s);
152         strncpy (dirpart, s, len);
153         dirpart[len] = 0;
154         p++;
155         strfcpy (filepart, p, sizeof (filepart));
156         strfcpy (exp_dirpart, dirpart, sizeof (exp_dirpart));
157         mutt_expand_path (exp_dirpart, sizeof (exp_dirpart));
158         dirp = opendir (exp_dirpart);
159       }
160     }
161     else {
162       /* no directory name, so assume current directory. */
163       dirpart[0] = 0;
164       strfcpy (filepart, s, sizeof (filepart));
165       dirp = opendir (".");
166     }
167   }
168
169   if (dirp == NULL) {
170     dprint (1,
171             (debugfile, "mutt_complete(): %s: %s (errno %d).\n", exp_dirpart,
172              strerror (errno), errno));
173     return (-1);
174   }
175
176   /*
177    * special case to handle when there is no filepart yet.  find the first
178    * file/directory which is not ``.'' or ``..''
179    */
180   if ((len = safe_strlen (filepart)) == 0) {
181     while ((de = readdir (dirp)) != NULL) {
182       if (safe_strcmp (".", de->d_name) != 0
183           && safe_strcmp ("..", de->d_name) != 0) {
184         strfcpy (filepart, de->d_name, sizeof (filepart));
185         init++;
186         break;
187       }
188     }
189   }
190
191   while ((de = readdir (dirp)) != NULL) {
192     if (safe_strncmp (de->d_name, filepart, len) == 0) {
193       if (init) {
194         for (i = 0; filepart[i] && de->d_name[i]; i++) {
195           if (filepart[i] != de->d_name[i]) {
196             filepart[i] = 0;
197             break;
198           }
199         }
200         filepart[i] = 0;
201       }
202       else {
203         char buf[_POSIX_PATH_MAX];
204         struct stat st;
205
206         strfcpy (filepart, de->d_name, sizeof (filepart));
207
208         /* check to see if it is a directory */
209         if (dirpart[0]) {
210           strfcpy (buf, exp_dirpart, sizeof (buf));
211           strfcpy (buf + safe_strlen (buf), "/", sizeof (buf) - safe_strlen (buf));
212         }
213         else
214           buf[0] = 0;
215         strfcpy (buf + safe_strlen (buf), filepart, sizeof (buf) - safe_strlen (buf));
216         if (stat (buf, &st) != -1 && (st.st_mode & S_IFDIR))
217           strfcpy (filepart + safe_strlen (filepart), "/",
218                    sizeof (filepart) - safe_strlen (filepart));
219         init = 1;
220       }
221     }
222   }
223   closedir (dirp);
224
225   if (dirpart[0]) {
226     strfcpy (s, dirpart, slen);
227     if (safe_strcmp ("/", dirpart) != 0 && dirpart[0] != '='
228         && dirpart[0] != '+')
229       strfcpy (s + safe_strlen (s), "/", slen - safe_strlen (s));
230     strfcpy (s + safe_strlen (s), filepart, slen - safe_strlen (s));
231   }
232   else
233     strfcpy (s, filepart, slen);
234
235   return (init ? 0 : -1);
236 }