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