f451e1a0fea0abd246fbebd6f234d2b426f305b8
[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       const char *q = NONULL(*s == '!' ? Spoolfile : Maildir);
107       mutt_concat_path (imap_path, q, s + 1, sizeof (imap_path));
108   }
109   else
110     strfcpy (imap_path, s, sizeof (imap_path));
111
112   if (mx_get_magic (imap_path) == M_IMAP)
113     return imap_complete (s, slen, imap_path);
114 #endif
115
116   if (*s == '=' || *s == '+' || *s == '!') {
117     dirpart[0] = *s;
118     dirpart[1] = 0;
119     if (*s == '!')
120       strfcpy (exp_dirpart, NONULL (Spoolfile), sizeof (exp_dirpart));
121     else
122       strfcpy (exp_dirpart, NONULL (Maildir), sizeof (exp_dirpart));
123     if ((p = strrchr (s, '/'))) {
124       char buf[_POSIX_PATH_MAX];
125
126       *p++ = 0;
127       mutt_concat_path (buf, exp_dirpart, s + 1, sizeof (buf));
128       strfcpy (exp_dirpart, buf, sizeof (exp_dirpart));
129       snprintf (buf, sizeof (buf), "%s%s/", dirpart, s + 1);
130       strfcpy (dirpart, buf, sizeof (dirpart));
131       strfcpy (filepart, p, sizeof (filepart));
132     }
133     else
134       strfcpy (filepart, s + 1, sizeof (filepart));
135     dirp = opendir (exp_dirpart);
136   }
137   else {
138     if ((p = strrchr (s, '/'))) {
139       if (p == s) {             /* absolute path */
140         p = s + 1;
141         strfcpy (dirpart, "/", sizeof (dirpart));
142         exp_dirpart[0] = 0;
143         strfcpy (filepart, p, sizeof (filepart));
144         dirp = opendir (dirpart);
145       }
146       else {
147         *p = 0;
148         len = (size_t) (p - s);
149         strncpy (dirpart, s, len);
150         dirpart[len] = 0;
151         p++;
152         strfcpy (filepart, p, sizeof (filepart));
153         strfcpy (exp_dirpart, dirpart, sizeof (exp_dirpart));
154         mutt_expand_path (exp_dirpart, sizeof (exp_dirpart));
155         dirp = opendir (exp_dirpart);
156       }
157     }
158     else {
159       /* no directory name, so assume current directory. */
160       dirpart[0] = 0;
161       strfcpy (filepart, s, sizeof (filepart));
162       dirp = opendir (".");
163     }
164   }
165
166   if (dirp == NULL) {
167     debug_print (1, ("%s: %s (errno %d).\n", exp_dirpart, strerror (errno), errno));
168     return (-1);
169   }
170
171   /*
172    * special case to handle when there is no filepart yet.  find the first
173    * file/directory which is not ``.'' or ``..''
174    */
175   if ((len = str_len (filepart)) == 0) {
176     while ((de = readdir (dirp)) != NULL) {
177       if (str_cmp (".", de->d_name) != 0
178           && str_cmp ("..", de->d_name) != 0) {
179         strfcpy (filepart, de->d_name, sizeof (filepart));
180         init++;
181         break;
182       }
183     }
184   }
185
186   while ((de = readdir (dirp)) != NULL) {
187     if (str_ncmp (de->d_name, filepart, len) == 0) {
188       if (init) {
189         for (i = 0; filepart[i] && de->d_name[i]; i++) {
190           if (filepart[i] != de->d_name[i]) {
191             filepart[i] = 0;
192             break;
193           }
194         }
195         filepart[i] = 0;
196       }
197       else {
198         char buf[_POSIX_PATH_MAX];
199         struct stat st;
200
201         strfcpy (filepart, de->d_name, sizeof (filepart));
202
203         /* check to see if it is a directory */
204         if (dirpart[0]) {
205           strfcpy (buf, exp_dirpart, sizeof (buf));
206           strfcpy (buf + str_len (buf), "/", sizeof (buf) - str_len (buf));
207         }
208         else
209           buf[0] = 0;
210         strfcpy (buf + str_len (buf), filepart, sizeof (buf) - str_len (buf));
211         if (stat (buf, &st) != -1 && (st.st_mode & S_IFDIR))
212           strfcpy (filepart + str_len (filepart), "/",
213                    sizeof (filepart) - str_len (filepart));
214         init = 1;
215       }
216     }
217   }
218   closedir (dirp);
219
220   if (dirpart[0]) {
221     strfcpy (s, dirpart, slen);
222     if (str_cmp ("/", dirpart) != 0 && dirpart[0] != '='
223         && dirpart[0] != '+')
224       strfcpy (s + str_len (s), "/", slen - str_len (s));
225     strfcpy (s + str_len (s), filepart, slen - str_len (s));
226   }
227   else
228     strfcpy (s, filepart, slen);
229
230   return (init ? 0 : -1);
231 }