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