]> granicus.if.org Git - neomutt/blob - complete.c
Convert mutt_attach_reply() to use buffer pool
[neomutt] / complete.c
1 /**
2  * @file
3  * String auto-completion routines
4  *
5  * @authors
6  * Copyright (C) 1996-2000,2007 Michael R. Elkins <me@mutt.org>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 /**
24  * @page complete String auto-completion routines
25  *
26  * String auto-completion routines
27  */
28
29 #include "config.h"
30 #include <dirent.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include "mutt/mutt.h"
36 #include "core/lib.h"
37 #include "globals.h"
38 #include "muttlib.h"
39 #include "options.h"
40 #ifdef USE_IMAP
41 #include "imap/imap.h"
42 #endif
43 #ifdef USE_NNTP
44 #include "nntp/nntp.h"
45 #endif
46
47 /**
48  * mutt_complete - Attempt to complete a partial pathname
49  * @param buf    Buffer containing pathname
50  * @param buflen Length of buffer
51  * @retval 0 if ok
52  * @retval -1 if no matches
53  *
54  * Given a partial pathname, fill in as much of the rest of the path as is
55  * unique.
56  */
57 int mutt_complete(char *buf, size_t buflen)
58 {
59   char *p = NULL;
60   DIR *dirp = NULL;
61   struct dirent *de = NULL;
62   int init = 0;
63   size_t len;
64   struct Buffer *dirpart = NULL;
65   struct Buffer *exp_dirpart = NULL;
66   struct Buffer *filepart = NULL;
67   struct Buffer *tmp = NULL;
68 #ifdef USE_IMAP
69   struct Buffer *imap_path = NULL;
70   int rc;
71 #endif
72
73   mutt_debug(LL_DEBUG2, "completing %s\n", buf);
74
75 #ifdef USE_NNTP
76   if (OptNews)
77     return nntp_complete(buf, buflen);
78 #endif
79
80 #ifdef USE_IMAP
81   imap_path = mutt_buffer_pool_get();
82   /* we can use '/' as a delimiter, imap_complete rewrites it */
83   if ((*buf == '=') || (*buf == '+') || (*buf == '!'))
84   {
85     if (*buf == '!')
86       p = NONULL(C_Spoolfile);
87     else
88       p = NONULL(C_Folder);
89
90     mutt_buffer_concat_path(imap_path, p, buf + 1);
91   }
92   else
93     mutt_buffer_strcpy(imap_path, buf);
94
95   if (imap_path_probe(mutt_b2s(imap_path), NULL) == MUTT_IMAP)
96   {
97     rc = imap_complete(buf, buflen, mutt_b2s(imap_path));
98     mutt_buffer_pool_release(&imap_path);
99     return rc;
100   }
101
102   mutt_buffer_pool_release(&imap_path);
103 #endif
104
105   dirpart = mutt_buffer_pool_get();
106   exp_dirpart = mutt_buffer_pool_get();
107   filepart = mutt_buffer_pool_get();
108   tmp = mutt_buffer_pool_get();
109
110   if ((*buf == '=') || (*buf == '+') || (*buf == '!'))
111   {
112     mutt_buffer_addch(dirpart, *buf);
113     if (*buf == '!')
114       mutt_buffer_strcpy(exp_dirpart, NONULL(C_Spoolfile));
115     else
116       mutt_buffer_strcpy(exp_dirpart, NONULL(C_Folder));
117     p = strrchr(buf, '/');
118     if (p)
119     {
120       mutt_buffer_concatn_path(tmp, mutt_b2s(exp_dirpart), mutt_buffer_len(exp_dirpart),
121                                buf + 1, (size_t)(p - buf - 1));
122       mutt_buffer_strcpy(exp_dirpart, mutt_b2s(tmp));
123       mutt_buffer_substrcpy(dirpart, buf, p + 1);
124       mutt_buffer_strcpy(filepart, p + 1);
125     }
126     else
127       mutt_buffer_strcpy(filepart, buf + 1);
128     dirp = opendir(mutt_b2s(exp_dirpart));
129   }
130   else
131   {
132     p = strrchr(buf, '/');
133     if (p)
134     {
135       if (p == buf) /* absolute path */
136       {
137         p = buf + 1;
138         mutt_buffer_strcpy(dirpart, "/");
139         mutt_buffer_strcpy(filepart, p);
140         dirp = opendir(mutt_b2s(dirpart));
141       }
142       else
143       {
144         mutt_buffer_substrcpy(dirpart, buf, p);
145         mutt_buffer_strcpy(filepart, p + 1);
146         mutt_buffer_strcpy(exp_dirpart, mutt_b2s(dirpart));
147         mutt_buffer_expand_path(exp_dirpart);
148         dirp = opendir(mutt_b2s(exp_dirpart));
149       }
150     }
151     else
152     {
153       /* no directory name, so assume current directory. */
154       mutt_buffer_strcpy(filepart, buf);
155       dirp = opendir(".");
156     }
157   }
158
159   if (!dirp)
160   {
161     mutt_debug(LL_DEBUG1, "%s: %s (errno %d)\n", mutt_b2s(exp_dirpart),
162                strerror(errno), errno);
163     goto cleanup;
164   }
165
166   /* special case to handle when there is no filepart yet.  find the first
167    * file/directory which is not "." or ".." */
168   len = mutt_buffer_len(filepart);
169   if (len == 0)
170   {
171     while ((de = readdir(dirp)))
172     {
173       if ((mutt_str_strcmp(".", de->d_name) != 0) &&
174           (mutt_str_strcmp("..", de->d_name) != 0))
175       {
176         mutt_buffer_strcpy(filepart, de->d_name);
177         init++;
178         break;
179       }
180     }
181   }
182
183   while ((de = readdir(dirp)))
184   {
185     if (mutt_str_strncmp(de->d_name, mutt_b2s(filepart), len) == 0)
186     {
187       if (init)
188       {
189         char *cp = filepart->data;
190
191         for (int i = 0; (*cp != '\0') && (de->d_name[i] != '\0'); i++, cp++)
192         {
193           if (*cp != de->d_name[i])
194             break;
195         }
196         *cp = '\0';
197         mutt_buffer_fix_dptr(filepart);
198       }
199       else
200       {
201         struct stat st;
202
203         mutt_buffer_strcpy(filepart, de->d_name);
204
205         /* check to see if it is a directory */
206         if (!mutt_buffer_is_empty(dirpart))
207         {
208           mutt_buffer_strcpy(tmp, mutt_b2s(exp_dirpart));
209           mutt_buffer_addch(tmp, '/');
210         }
211         else
212           mutt_buffer_reset(tmp);
213         mutt_buffer_addstr(tmp, mutt_b2s(filepart));
214         if ((stat(mutt_b2s(tmp), &st) != -1) && (st.st_mode & S_IFDIR))
215           mutt_buffer_addch(filepart, '/');
216         init = 1;
217       }
218     }
219   }
220   closedir(dirp);
221
222   if (!mutt_buffer_is_empty(dirpart))
223   {
224     mutt_str_strfcpy(buf, mutt_b2s(dirpart), buflen);
225     if ((mutt_str_strcmp("/", mutt_b2s(dirpart)) != 0) &&
226         (mutt_b2s(dirpart)[0] != '=') && (mutt_b2s(dirpart)[0] != '+'))
227     {
228       mutt_str_strfcpy(buf + strlen(buf), "/", buflen - strlen(buf));
229     }
230     mutt_str_strfcpy(buf + strlen(buf), mutt_b2s(filepart), buflen - strlen(buf));
231   }
232   else
233     mutt_str_strfcpy(buf, mutt_b2s(filepart), buflen);
234
235 cleanup:
236   mutt_buffer_pool_release(&dirpart);
237   mutt_buffer_pool_release(&exp_dirpart);
238   mutt_buffer_pool_release(&filepart);
239   mutt_buffer_pool_release(&tmp);
240
241   return init ? 0 : -1;
242 }