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