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