* The TAB key allows autocomplete in the Fcc field in the composer headers,
[alpine.git] / pith / keyword.c
blob1bce0642e0b875cac3f136649de5824ad114b22f
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: keyword.c 807 2007-11-09 01:21:33Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/keyword.h"
21 #include "../pith/state.h"
22 #include "../pith/flag.h"
23 #include "../pith/string.h"
24 #include "../pith/status.h"
25 #include "../pith/util.h"
29 * Internal prototypes
34 * Read the keywords array into a KEYWORD_S structure.
35 * Make sure that all of the strings are UTF-8.
37 KEYWORD_S *
38 init_keyword_list(char **keywordarray)
40 char **t, *nickname, *keyword;
41 KEYWORD_S *head = NULL, **tail;
43 tail = &head;
44 for(t = keywordarray; t && *t && **t; t++){
45 nickname = keyword = NULL;
46 get_pair(*t, &nickname, &keyword, 0, 0);
47 *tail = new_keyword_s(keyword, nickname);
48 tail = &(*tail)->next;
50 if(keyword)
51 fs_give((void **) &keyword);
53 if(nickname)
54 fs_give((void **) &nickname);
57 return(head);
61 KEYWORD_S *
62 new_keyword_s(char *keyword, char *nickname)
64 KEYWORD_S *kw = NULL;
66 kw = (KEYWORD_S *) fs_get(sizeof(*kw));
67 memset(kw, 0, sizeof(*kw));
69 if(keyword && *keyword)
70 kw->kw = cpystr(keyword);
72 if(nickname && *nickname)
73 kw->nick = cpystr(nickname);
75 return(kw);
79 void
80 free_keyword_list(KEYWORD_S **kl)
82 if(kl && *kl){
83 if((*kl)->next)
84 free_keyword_list(&(*kl)->next);
86 if((*kl)->kw)
87 fs_give((void **) &(*kl)->kw);
89 if((*kl)->nick)
90 fs_give((void **) &(*kl)->nick);
92 fs_give((void **) kl);
98 * Return a pointer to the keyword associated with a nickname, or the
99 * input itself if no match.
101 char *
102 nick_to_keyword(char *nick)
104 KEYWORD_S *kw;
105 char *ret;
107 ret = nick;
108 for(kw = ps_global->keywords; kw; kw = kw->next)
109 if(!strcmp(nick, kw->nick ? kw->nick : kw->kw ? kw->kw : "")){
110 if(kw->nick)
111 ret = kw->kw;
113 break;
116 return(ret);
121 * Return a pointer to the nickname associated with a keyword, or the
122 * input itself if no match.
124 char *
125 keyword_to_nick(char *keyword)
127 KEYWORD_S *kw;
128 char *ret;
130 ret = keyword;
131 for(kw = ps_global->keywords; kw; kw = kw->next)
132 if(!strcmp(keyword, kw->kw ? kw->kw : "")){
133 if(kw->nick)
134 ret = kw->nick;
136 break;
139 return(ret);
144 * Return a pointer to the keyword associated with an initial, or the
145 * input itself if no match.
146 * Only works for ascii initials.
148 char *
149 initial_to_keyword(char *initial)
151 KEYWORD_S *kw;
152 char *ret;
153 int init;
154 int kwinit;
156 ret = initial;
157 if(initial[0] && initial[1] == '\0'){
158 init = initial[0];
159 if(isascii(init) && isupper(init))
160 init = tolower((unsigned char) init);
162 for(kw = ps_global->keywords; kw; kw = kw->next){
163 if(kw->nick)
164 kwinit = kw->nick[0];
165 else if(kw->kw)
166 kwinit = kw->kw[0];
168 if(isascii(kwinit) && isupper(kwinit))
169 kwinit = tolower((unsigned char) kwinit);
171 if(kwinit == init){
172 ret = kw->kw;
173 break;
178 return(ret);
183 user_flag_is_set(MAILSTREAM *stream, long unsigned int rawno, char *keyword)
185 int j, is_set = 0;
186 MESSAGECACHE *mc;
188 if(stream && keyword && keyword[0]){
189 if(rawno > 0L && stream
190 && rawno <= stream->nmsgs
191 && (mc = mail_elt(stream, rawno)) != NULL){
192 j = user_flag_index(stream, keyword);
193 if(j >= 0 && j < NUSERFLAGS && ((1 << j) & mc->user_flags))
194 is_set++;
198 return(is_set);
203 * Returns the bit position of the keyword in stream, else -1.
206 user_flag_index(MAILSTREAM *stream, char *keyword)
208 int i, retval = -1;
209 char *p;
211 if(stream && keyword && keyword[0]){
212 for(i = 0; i < NUSERFLAGS; i++)
213 if((p=stream_to_user_flag_name(stream, i)) && !strucmp(keyword, p)){
214 retval = i;
215 break;
219 return(retval);
223 char *
224 stream_to_user_flag_name(MAILSTREAM *stream, int k)
226 if(stream && stream->user_flags && k >= 0 && k < NUSERFLAGS)
227 return(stream->user_flags[k]);
229 return(NULL);
234 some_user_flags_defined(MAILSTREAM *stream)
236 int k;
238 if(stream)
239 for(k = 0; k < NUSERFLAGS; k++)
240 if(stream_to_user_flag_name(stream, k))
241 return 1;
243 return 0;
247 /*----------------------------------------------------------------------
248 Build flags string based on requested flags and what's set in messagecache
250 Args: flags -- flags to test
252 Result: allocated, space-delimited flags string is returned
253 ----*/
254 char *
255 flag_string(MAILSTREAM *stream, long rawno, long int flags)
257 MESSAGECACHE *mc;
258 char *p, *q, *returned_flags = NULL;
259 size_t len = 0;
260 int k;
262 mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
263 ? mail_elt(stream, rawno) : NULL;
264 if(!mc)
265 return returned_flags;
267 if((flags & F_DEL) && mc->deleted)
268 len += strlen("\\DELETED") + 1;
270 if((flags & F_ANS) && mc->answered)
271 len += strlen("\\ANSWERED") + 1;
273 if((flags & F_FWD) && user_flag_is_set(stream, rawno, FORWARDED_FLAG))
274 len += strlen(FORWARDED_FLAG) + 1;
276 if((flags & F_FLAG) && mc->flagged)
277 len += strlen("\\FLAGGED") + 1;
279 if((flags & F_SEEN) && mc->seen)
280 len += strlen("\\SEEN") + 1;
282 if((flags & F_KEYWORD) && stream->user_flags){
283 for(k = 0; k < NUSERFLAGS; k++){
284 if((q=stream_to_user_flag_name(stream, k))
285 && user_flag_is_set(stream, rawno, q)){
286 len += strlen(q) + 1;
291 returned_flags = (char *) fs_get((len+1) * sizeof(char));
292 p = returned_flags;
293 *p = '\0';
295 if((flags & F_DEL) && mc->deleted)
296 sstrncpy(&p, "\\DELETED ", len+1-(p-returned_flags));
298 if((flags & F_ANS) && mc->answered)
299 sstrncpy(&p, "\\ANSWERED ", len+1-(p-returned_flags));
301 if((flags & F_FWD) && user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
302 sstrncpy(&p, FORWARDED_FLAG, len+1-(p-returned_flags));
303 sstrncpy(&p, " ", len+1-(p-returned_flags));
306 if((flags & F_FLAG) && mc->flagged)
307 sstrncpy(&p, "\\FLAGGED ", len+1-(p-returned_flags));
309 if((flags & F_SEEN) && mc->seen)
310 sstrncpy(&p, "\\SEEN ", len+1-(p-returned_flags));
312 if((flags & F_KEYWORD) && stream->user_flags){
313 for(k = 0; k < NUSERFLAGS; k++){
314 if((q=stream_to_user_flag_name(stream, k))
315 && user_flag_is_set(stream, rawno, q)){
316 sstrncpy(&p, q, len+1-(p-returned_flags));
317 sstrncpy(&p, " ", len+1-(p-returned_flags));
318 len += strlen(p) + 1;
323 if(p != returned_flags && (len+1-(p-returned_flags)>0))
324 *--p = '\0';
326 return returned_flags;
330 long
331 get_msgno_by_msg_id(MAILSTREAM *stream, char *message_id, MSGNO_S *msgmap)
333 SEARCHPGM *pgm = NULL;
334 long hint = mn_m2raw(msgmap, mn_get_cur(msgmap));
335 long newmsgno = -1L;
336 int iter = 0;
337 MESSAGECACHE *mc;
338 extern MAILSTREAM *mm_search_stream;
339 extern long mm_search_count;
341 if(!(message_id && message_id[0]) || stream->nmsgs < 1L)
342 return(newmsgno);
344 mm_search_count = 0L;
345 mm_search_stream = stream;
346 while(mm_search_count == 0L && iter++ < 3
347 && (pgm = mail_newsearchpgm()) != NULL){
348 pgm->message_id = mail_newstringlist();
349 pgm->message_id->text.data = (unsigned char *) cpystr(message_id);
350 pgm->message_id->text.size = strlen(message_id);
352 if(iter > 1 || hint > stream->nmsgs)
353 iter++;
355 if(iter == 1){
356 /* restrict to hint message on first try */
357 pgm->msgno = mail_newsearchset();
358 pgm->msgno->first = pgm->msgno->last = hint;
360 else if(iter == 2){
361 /* restrict to last 50 messages on 2nd try */
362 pgm->msgno = mail_newsearchset();
363 if(stream->nmsgs > 100L)
364 pgm->msgno->first = stream->nmsgs-50L;
365 else{
366 pgm->msgno->first = 1L;
367 iter++;
370 pgm->msgno->last = stream->nmsgs;
373 pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
375 if(mm_search_count){
376 for(newmsgno=stream->nmsgs; newmsgno > 0L; newmsgno--)
377 if((mc = mail_elt(stream, newmsgno)) && mc->searched)
378 break;
382 return(mn_raw2m(msgmap, newmsgno));
387 * These chars are not allowed in keywords.
389 * Returns 0 if ok, 1 if not.
390 * Returns an allocated error message on error.
393 keyword_check(char *kw, char **error)
395 register char *t;
396 char buf[100], *p;
398 if(!kw || !kw[0])
399 return 1;
401 kw = nick_to_keyword(kw);
403 for(p = kw; p && *p; p++)
404 if(!isascii(*p)){
405 if(error)
406 *error = cpystr("Keywords must be all ASCII characters");
408 return 1;
411 if((t = strindex(kw, SPACE)) ||
412 (t = strindex(kw, '{')) ||
413 (t = strindex(kw, '(')) ||
414 (t = strindex(kw, ')')) ||
415 (t = strindex(kw, ']')) ||
416 (t = strindex(kw, '%')) ||
417 (t = strindex(kw, '"')) ||
418 (t = strindex(kw, '\\')) ||
419 (t = strindex(kw, '*'))){
420 char s[4];
421 s[0] = '"';
422 s[1] = *t;
423 s[2] = '"';
424 s[3] = '\0';
425 if(error){
426 snprintf(buf, sizeof(buf), "%s not allowed in keywords",
427 *t == SPACE ?
428 "Spaces" :
429 *t == '"' ?
430 "Quotes" :
431 *t == '%' ?
432 "Percents" :
434 *error = cpystr(buf);
437 return 1;
440 return 0;