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>.
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
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
44 #define MAC_HASH(S) (strhash(S) % MACPRIME)
49 MAC_TYPE_MASK
= MAC_ACCOUNT
,
54 struct macro
*ma_next
;
56 struct line
*ma_contents
;
57 enum mac_flags ma_flags
;
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
,
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
);
104 _vcopy(char const *str
)
112 len
= strlen(str
) + 1;
114 memcpy(news
, str
, len
);
127 _check_special_vars(char const *name
, bool_t enable
, char **value
)
129 /* TODO _check_special_vars --> value cache */
134 if (strcmp(name
, "debug") == 0)
136 else if (strcmp(name
, "header") == 0)
137 flag
= OPT_N_FLAG
, enable
= ! enable
;
138 else if (strcmp(name
, "skipemptybody") == 0)
140 else if (strcmp(name
, "verbose") == 0)
142 else if (strcmp(name
, "folder") == 0) {
143 rv
= var_folder_updated(*value
, &cp
);
144 if (rv
&& cp
!= NULL
) {
146 /* It's smalloc()ed, but ensure we don't leak */
148 *value
= UNCONST("");
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
;
160 c
= expand_shell_escape(&x
);
179 _canonify(char const *vn
)
181 if (! upperchar(*vn
)) {
184 for (vp
= vn
; *vp
!= '\0' && *vp
!= '@'; ++vp
)
186 vn
= (*vp
== '@') ? i_strdup(vn
) : vn
;
192 _lookup(const char *name
, ui_it h
, bool_t hisset
)
194 struct var
**vap
, *lvp
, *vp
;
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 */
204 lvp
->v_link
= vp
->v_link
;
216 _is_closing_angle(char const *cp
)
219 while (spacechar(*cp
))
223 while (spacechar(*cp
))
230 static struct macro
*
231 _malook(const char *name
, struct macro
*data
, enum mac_flags macfl
)
233 enum mac_flags save_mfl
;
235 struct macro
*lmp
, *mp
;
238 macfl
&= MAC_TYPE_MASK
;
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
) {
247 _macros
[h
] = mp
->ma_next
;
249 lmp
->ma_next
= mp
->ma_next
;
256 data
->ma_next
= _macros
[h
];
265 _maexec(struct macro
*mp
)
269 char const *sp
, *smax
;
272 unset_allow_undefined
= TRU1
;
273 for (lp
= mp
->ma_contents
; lp
; lp
= lp
->l_next
) {
275 smax
= sp
+ lp
->l_linesize
;
277 (blankchar(*sp
) || *sp
== '\n' || *sp
== '\0'))
281 cp
= copy
= ac_alloc(lp
->l_linesize
+ (lp
->l_line
- sp
));
283 *cp
++ = (*sp
!= '\n') ? *sp
: ' ';
285 rv
= execute(copy
, 0, (size_t)(cp
- copy
));
288 unset_allow_undefined
= FAL0
;
293 _list_macros(FILE *fp
, enum mac_flags macfl
)
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
) {
308 fprintf(fp
, "%s %s {\n", typestr
, mq
->ma_name
);
309 for (lp
= mq
->ma_contents
; lp
; lp
= lp
->l_next
)
317 _list_line(FILE *fp
, struct line
*lp
)
319 char const *sp
= lp
->l_line
, *spmax
= sp
+ lp
->l_linesize
;
322 for (; sp
< spmax
; ++sp
) {
323 if ((c
= *sp
& 0xFF) != '\0') {
333 _undef1(const char *name
, enum mac_flags macfl
)
337 if ((mp
= _malook(name
, NULL
, macfl
| MAC_UNDEF
)) != NULL
) {
338 _freelines(mp
->ma_contents
);
345 _freelines(struct line
*lp
)
349 for (lq
= NULL
; lp
!= NULL
; ) {
360 assign(char const *name
, char const *value
)
367 bool_t save
= unset_allow_undefined
;
368 unset_allow_undefined
= TRU1
;
369 unset_internal(name
);
370 unset_allow_undefined
= save
;
374 name
= _canonify(name
);
377 vp
= _lookup(name
, h
, TRU1
);
379 vp
= (struct var
*)scalloc(1, sizeof *vp
);
380 vp
->v_name
= _vcopy(name
);
381 vp
->v_link
= variables
[h
];
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
;
400 unset_internal(char const *name
)
406 name
= _canonify(name
);
409 if ((vp
= _lookup(name
, h
, TRU1
)) == NULL
) {
410 if (! sourcing
&& ! unset_allow_undefined
) {
412 tr(203, "\"%s\": undefined variable\n"), name
);
416 /* Always listhead after _lookup() */
417 variables
[h
] = variables
[h
]->v_link
;
422 _check_special_vars(name
, FAL0
, NULL
);
430 value(const char *name
)
435 name
= _canonify(name
);
436 if ((vp
= _lookup(name
, 0, FAL0
)) == NULL
) {
437 if ((vs
= getenv(name
)) != NULL
&& *vs
)
441 return (vp
->v_value
);
451 if (args
[0] == NULL
) {
452 errs
= tr(504, "Missing macro name to `define'");
455 if (args
[1] == NULL
|| strcmp(args
[1], "{") || args
[2] != NULL
) {
456 errs
= tr(505, "Syntax is: define <name> {");
459 rv
= define1(args
[0], 0);
463 fprintf(stderr
, "%s\n", errs
);
468 define1(char const *name
, int account
) /* TODO make static (`account'...)! */
472 struct line
*lp
, *lst
= NULL
, *lnd
= NULL
;
473 char *linebuf
= NULL
;
476 mp
= scalloc(1, sizeof *mp
);
477 mp
->ma_name
= sstrdup(name
);
478 mp
->ma_flags
= account
? MAC_ACCOUNT
: MAC_NONE
;
481 n
= readline_input(LNED_LF_ESC
, "", &linebuf
, &linesize
);
484 tr(75, "Unterminated %s definition: \"%s\".\n"),
485 account
? "account" : "macro", mp
->ma_name
);
490 if (_is_closing_angle(linebuf
))
494 lp
= scalloc(1, sizeof(*lp
) -
495 VFIELD_SIZEOF(struct line
, l_line
) + n
);
497 memcpy(lp
->l_line
, linebuf
, n
);
498 assert(lp
->l_line
[n
- 1] == '\0');
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"),
512 lst
= mp
->ma_contents
;
515 _undef1(mp
->ma_name
, MAC_ACCOUNT
);
516 _malook(mp
->ma_name
, mp
, MAC_ACCOUNT
);
539 fprintf(stderr
, tr(506, "Missing macro name to `undef'\n"));
543 _undef1(*args
, MAC_NONE
);
555 char const *errs
, *name
;
558 if (args
[0] == NULL
|| (args
[1] != NULL
&& args
[2] != NULL
)) {
559 errs
= tr(507, "Syntax is: call <%s>\n");
563 if ((mp
= _malook(*args
, NULL
, MAC_NONE
)) == NULL
) {
564 errs
= tr(508, "Undefined macro called: \"%s\"\n");
572 fprintf(stderr
, errs
, name
);
577 callhook(char const *name
, int newmail
)
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
) {
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"),
596 inhook
= newmail
? 3 : 1;
611 if ((fp
= Ftemp(&cp
, "Ra", "w+", 0600, 1)) == NULL
) {
618 if (_list_macros(fp
, MAC_NONE
) > 0)
625 callaccount(char const *name
)
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
);
640 delaccount(const char *name
)
642 _undef1(name
, MAC_ACCOUNT
);