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