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