README: mention *crawl* branch
[s-mailx.git] / varmac.c
blob54c408515962030b3c1f4a3255b37c3f66c96634
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Variable and macro handling stuff.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #include "rcv.h"
41 #include "extern.h"
43 #define MACPRIME 23
44 #define MAC_HASH(S) (strhash(S) % MACPRIME)
46 enum mac_flags {
47 MAC_NONE = 0,
48 MAC_ACCOUNT = 1<<0,
49 MAC_TYPE_MASK = MAC_ACCOUNT,
50 MAC_UNDEF = 1<<1
53 struct macro {
54 struct macro *ma_next;
55 char *ma_name;
56 struct line *ma_contents;
57 enum mac_flags ma_flags;
60 struct line {
61 struct line *l_next;
62 size_t l_linesize;
63 char l_line[VFIELD_SIZE(sizeof(size_t))];
66 static struct macro *_macros[MACPRIME];
68 /* Special cased value string allocation */
69 static char * _vcopy(char const *str);
70 static void _vfree(char *cp);
72 /* Check for special housekeeping. */
73 static bool_t _check_special_vars(char const *name, bool_t enable,
74 char **value);
76 /* If a variable name begins with a lowercase-character and contains at
77 * least one '@', it is converted to all-lowercase. This is necessary
78 * for lookups of names based on email addresses.
80 * Following the standard, only the part following the last '@' should
81 * be lower-cased, but practice has established otherwise here */
82 static char const * _canonify(char const *vn);
84 /* Locate a variable and return its variable node */
85 static struct var * _lookup(const char *name, ui_it h, bool_t hisset);
87 /* Line *cp* consists solely of WS and a } */
88 static bool_t _is_closing_angle(char const *cp);
90 /* Lookup for macros/accounts */
91 static struct macro * _malook(const char *name, struct macro *data,
92 enum mac_flags macfl);
94 static int _maexec(struct macro *mp);
96 /* User display helpers */
97 static size_t _list_macros(FILE *fp, enum mac_flags macfl);
98 static void _list_line(FILE *fp, struct line *lp);
100 static void _undef1(const char *name, enum mac_flags macfl);
101 static void _freelines(struct line *lp);
103 static char *
104 _vcopy(char const *str)
106 char *news;
107 size_t len;
109 if (*str == '\0')
110 news = UNCONST("");
111 else {
112 len = strlen(str) + 1;
113 news = smalloc(len);
114 memcpy(news, str, len);
116 return news;
119 static void
120 _vfree(char *cp)
122 if (*cp)
123 free(cp);
126 static bool_t
127 _check_special_vars(char const *name, bool_t enable, char **value)
129 /* TODO _check_special_vars --> value cache */
130 char *cp = NULL;
131 bool_t rv = TRU1;
132 int flag = 0;
134 if (strcmp(name, "debug") == 0)
135 flag = OPT_DEBUG;
136 else if (strcmp(name, "header") == 0)
137 flag = OPT_N_FLAG, enable = ! enable;
138 else if (strcmp(name, "skipemptybody") == 0)
139 flag = OPT_E_FLAG;
140 else if (strcmp(name, "verbose") == 0)
141 flag = OPT_VERBOSE;
142 else if (strcmp(name, "folder") == 0) {
143 rv = var_folder_updated(*value, &cp);
144 if (rv && cp != NULL) {
145 _vfree(*value);
146 /* It's smalloc()ed, but ensure we don't leak */
147 if (*cp == '\0') {
148 *value = UNCONST("");
149 free(cp);
150 } else
151 *value = cp;
154 #if ! defined HAVE_READLINE && ! defined HAVE_EDITLINE &&\
155 defined HAVE_LINE_EDITOR
156 else if (strcmp(name, "line-editor-cursor-right") == 0) {
157 char const *x = cp = *value;
158 int c;
159 while (*x != '\0') {
160 c = expand_shell_escape(&x);
161 if (c < 0)
162 break;
163 *cp++ = (char)c;
165 *cp++ = '\0';
167 #endif
169 if (flag) {
170 if (enable)
171 options |= flag;
172 else
173 options &= ~flag;
175 return rv;
178 static char const *
179 _canonify(char const *vn)
181 if (! upperchar(*vn)) {
182 char const *vp;
184 for (vp = vn; *vp != '\0' && *vp != '@'; ++vp)
186 vn = (*vp == '@') ? i_strdup(vn) : vn;
188 return vn;
191 static struct var *
192 _lookup(const char *name, ui_it h, bool_t hisset)
194 struct var **vap, *lvp, *vp;
196 if (! hisset)
197 h = MAC_HASH(name);
198 vap = variables + h;
200 for (lvp = NULL, vp = *vap; vp != NULL; lvp = vp, vp = vp->v_link)
201 if (*vp->v_name == *name && strcmp(vp->v_name, name) == 0) {
202 /* Relink as head, hope it "sorts on usage" over time */
203 if (lvp != NULL) {
204 lvp->v_link = vp->v_link;
205 vp->v_link = *vap;
206 *vap = vp;
208 goto jleave;
210 vp = NULL;
211 jleave:
212 return vp;
215 static bool_t
216 _is_closing_angle(char const *cp)
218 bool_t rv = FAL0;
219 while (spacechar(*cp))
220 ++cp;
221 if (*cp++ != '}')
222 goto jleave;
223 while (spacechar(*cp))
224 ++cp;
225 rv = (*cp == '\0');
226 jleave:
227 return rv;
230 static struct macro *
231 _malook(const char *name, struct macro *data, enum mac_flags macfl)
233 enum mac_flags save_mfl;
234 ui_it h;
235 struct macro *lmp, *mp;
237 save_mfl = macfl;
238 macfl &= MAC_TYPE_MASK;
239 h = MAC_HASH(name);
241 for (lmp = NULL, mp = _macros[h]; mp != NULL;
242 lmp = mp, mp = mp->ma_next) {
243 if ((mp->ma_flags & MAC_TYPE_MASK) == macfl &&
244 strcmp(mp->ma_name, name) == 0) {
245 if (save_mfl & MAC_UNDEF) {
246 if (lmp == NULL)
247 _macros[h] = mp->ma_next;
248 else
249 lmp->ma_next = mp->ma_next;
251 goto jleave;
255 if (data != NULL) {
256 data->ma_next = _macros[h];
257 _macros[h] = data;
258 mp = NULL;
260 jleave:
261 return mp;
264 static int
265 _maexec(struct macro *mp)
267 int rv = 0;
268 struct line *lp;
269 char const *sp, *smax;
270 char *copy, *cp;
272 unset_allow_undefined = TRU1;
273 for (lp = mp->ma_contents; lp; lp = lp->l_next) {
274 sp = lp->l_line;
275 smax = sp + lp->l_linesize;
276 while (sp < smax &&
277 (blankchar(*sp) || *sp == '\n' || *sp == '\0'))
278 ++sp;
279 if (sp == smax)
280 continue;
281 cp = copy = ac_alloc(lp->l_linesize + (lp->l_line - sp));
283 *cp++ = (*sp != '\n') ? *sp : ' ';
284 while (++sp < smax);
285 rv = execute(copy, 0, (size_t)(cp - copy));
286 ac_free(copy);
288 unset_allow_undefined = FAL0;
289 return rv;
292 static size_t
293 _list_macros(FILE *fp, enum mac_flags macfl)
295 struct macro *mq;
296 char const *typestr;
297 ui_it ti, mc;
298 struct line *lp;
300 macfl &= MAC_TYPE_MASK;
301 typestr = (macfl & MAC_ACCOUNT) ? "account" : "define";
303 for (ti = mc = 0; ti < MACPRIME; ++ti)
304 for (mq = _macros[ti]; mq; mq = mq->ma_next)
305 if ((mq->ma_flags & MAC_TYPE_MASK) == macfl) {
306 if (++mc > 1)
307 fputc('\n', fp);
308 fprintf(fp, "%s %s {\n", typestr, mq->ma_name);
309 for (lp = mq->ma_contents; lp; lp = lp->l_next)
310 _list_line(fp, lp);
311 fputs("}\n", fp);
313 return mc;
316 static void
317 _list_line(FILE *fp, struct line *lp)
319 char const *sp = lp->l_line, *spmax = sp + lp->l_linesize;
320 int c;
322 for (; sp < spmax; ++sp) {
323 if ((c = *sp & 0xFF) != '\0') {
324 if (c == '\n')
325 putc('\\', fp);
326 putc(c, fp);
329 putc('\n', fp);
332 static void
333 _undef1(const char *name, enum mac_flags macfl)
335 struct macro *mp;
337 if ((mp = _malook(name, NULL, macfl | MAC_UNDEF)) != NULL) {
338 _freelines(mp->ma_contents);
339 free(mp->ma_name);
340 free(mp);
344 static void
345 _freelines(struct line *lp)
347 struct line *lq;
349 for (lq = NULL; lp != NULL; ) {
350 if (lq != NULL)
351 free(lq);
352 lq = lp;
353 lp = lp->l_next;
355 if (lq)
356 free(lq);
359 void
360 assign(char const *name, char const *value)
362 struct var *vp;
363 ui_it h;
364 char *oval;
366 if (value == NULL) {
367 bool_t save = unset_allow_undefined;
368 unset_allow_undefined = TRU1;
369 unset_internal(name);
370 unset_allow_undefined = save;
371 goto jleave;
374 name = _canonify(name);
375 h = MAC_HASH(name);
377 vp = _lookup(name, h, TRU1);
378 if (vp == NULL) {
379 vp = (struct var*)scalloc(1, sizeof *vp);
380 vp->v_name = _vcopy(name);
381 vp->v_link = variables[h];
382 variables[h] = vp;
383 oval = UNCONST("");
384 } else
385 oval = vp->v_value;
386 vp->v_value = _vcopy(value);
388 /* Check if update allowed XXX wasteful on error! */
389 if (! _check_special_vars(name, TRU1, &vp->v_value)) {
390 char *cp = vp->v_value;
391 vp->v_value = oval;
392 oval = cp;
394 if (*oval != '\0')
395 _vfree(oval);
396 jleave: ;
400 unset_internal(char const *name)
402 int ret = 1;
403 ui_it h;
404 struct var *vp;
406 name = _canonify(name);
407 h = MAC_HASH(name);
409 if ((vp = _lookup(name, h, TRU1)) == NULL) {
410 if (! sourcing && ! unset_allow_undefined) {
411 fprintf(stderr,
412 tr(203, "\"%s\": undefined variable\n"), name);
413 goto jleave;
415 } else {
416 /* Always listhead after _lookup() */
417 variables[h] = variables[h]->v_link;
418 _vfree(vp->v_name);
419 _vfree(vp->v_value);
420 free(vp);
422 _check_special_vars(name, FAL0, NULL);
424 ret = 0;
425 jleave:
426 return ret;
429 char *
430 value(const char *name)
432 struct var *vp;
433 char *vs;
435 name = _canonify(name);
436 if ((vp = _lookup(name, 0, FAL0)) == NULL) {
437 if ((vs = getenv(name)) != NULL && *vs)
438 vs = savestr(vs);
439 return (vs);
441 return (vp->v_value);
445 cdefine(void *v)
447 int rv = 1;
448 char **args = v;
449 char const *errs;
451 if (args[0] == NULL) {
452 errs = tr(504, "Missing macro name to `define'");
453 goto jerr;
455 if (args[1] == NULL || strcmp(args[1], "{") || args[2] != NULL) {
456 errs = tr(505, "Syntax is: define <name> {");
457 goto jerr;
459 rv = define1(args[0], 0);
460 jleave:
461 return rv;
462 jerr:
463 fprintf(stderr, "%s\n", errs);
464 goto jleave;
468 define1(char const *name, int account) /* TODO make static (`account'...)! */
470 int ret = 1, n;
471 struct macro *mp;
472 struct line *lp, *lst = NULL, *lnd = NULL;
473 char *linebuf = NULL;
474 size_t linesize = 0;
476 mp = scalloc(1, sizeof *mp);
477 mp->ma_name = sstrdup(name);
478 mp->ma_flags = account ? MAC_ACCOUNT : MAC_NONE;
480 for (;;) {
481 n = readline_input(LNED_LF_ESC, "", &linebuf, &linesize);
482 if (n < 0) {
483 fprintf(stderr,
484 tr(75, "Unterminated %s definition: \"%s\".\n"),
485 account ? "account" : "macro", mp->ma_name);
486 if (sourcing)
487 unstack();
488 goto jerr;
490 if (_is_closing_angle(linebuf))
491 break;
493 ++n;
494 lp = scalloc(1, sizeof(*lp) -
495 VFIELD_SIZEOF(struct line, l_line) + n);
496 lp->l_linesize = n;
497 memcpy(lp->l_line, linebuf, n);
498 assert(lp->l_line[n - 1] == '\0');
499 if (lst != NULL) {
500 lnd->l_next = lp;
501 lnd = lp;
502 } else
503 lst = lnd = lp;
505 mp->ma_contents = lst;
507 if (_malook(mp->ma_name, mp, mp->ma_flags) != NULL) {
508 if (! (mp->ma_flags & MAC_ACCOUNT)) {
509 fprintf(stderr, tr(76,
510 "A macro named \"%s\" already exists.\n"),
511 mp->ma_name);
512 lst = mp->ma_contents;
513 goto jerr;
515 _undef1(mp->ma_name, MAC_ACCOUNT);
516 _malook(mp->ma_name, mp, MAC_ACCOUNT);
519 ret = 0;
520 jleave:
521 if (linebuf != NULL)
522 free(linebuf);
523 return ret;
524 jerr:
525 if (lst != NULL)
526 _freelines(lst);
527 free(mp->ma_name);
528 free(mp);
529 goto jleave;
533 cundef(void *v)
535 int rv = 1;
536 char **args = v;
538 if (*args == NULL) {
539 fprintf(stderr, tr(506, "Missing macro name to `undef'\n"));
540 goto jleave;
543 _undef1(*args, MAC_NONE);
544 while (*++args);
545 rv = 0;
546 jleave:
547 return rv;
551 ccall(void *v)
553 int rv = 1;
554 char **args = v;
555 char const *errs, *name;
556 struct macro *mp;
558 if (args[0] == NULL || (args[1] != NULL && args[2] != NULL)) {
559 errs = tr(507, "Syntax is: call <%s>\n");
560 name = "name";
561 goto jerr;
563 if ((mp = _malook(*args, NULL, MAC_NONE)) == NULL) {
564 errs = tr(508, "Undefined macro called: \"%s\"\n");
565 name = *args;
566 goto jerr;
568 rv = _maexec(mp);
569 jleave:
570 return rv;
571 jerr:
572 fprintf(stderr, errs, name);
573 goto jleave;
577 callhook(char const *name, int newmail)
579 int len, r;
580 struct macro *mp;
581 char *var, *cp;
583 var = ac_alloc(len = strlen(name) + 13);
584 snprintf(var, len, "folder-hook-%s", name);
585 if ((cp = value(var)) == NULL && (cp = value("folder-hook")) == NULL) {
586 r = 0;
587 goto jleave;
589 if ((mp = _malook(cp, NULL, MAC_NONE)) == NULL) {
590 fprintf(stderr, tr(49, "Cannot call hook for folder \"%s\": "
591 "Macro \"%s\" does not exist.\n"),
592 name, cp);
593 r = 1;
594 goto jleave;
596 inhook = newmail ? 3 : 1;
597 r = _maexec(mp);
598 inhook = 0;
599 jleave:
600 ac_free(var);
601 return r;
605 cdefines(void *v)
607 FILE *fp;
608 char *cp;
609 (void)v;
611 if ((fp = Ftemp(&cp, "Ra", "w+", 0600, 1)) == NULL) {
612 perror("tmpfile");
613 return 1;
615 rm(cp);
616 Ftfree(&cp);
618 if (_list_macros(fp, MAC_NONE) > 0)
619 try_pager(fp);
620 Fclose(fp);
621 return 0;
625 callaccount(char const *name)
627 struct macro *mp;
629 mp = _malook(name, NULL, MAC_ACCOUNT);
630 return (mp == NULL) ? CBAD : _maexec(mp);
634 listaccounts(FILE *fp)
636 return (int)_list_macros(fp, MAC_ACCOUNT);
639 void
640 delaccount(const char *name)
642 _undef1(name, MAC_ACCOUNT);