Show the Content-Description:, as applicable
[s-mailx.git] / accmacvar.c
blobc0f3bfdeb2ea9219626958729021cf8a857265f9
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Account, macro and variable handling.
3 *@ HOWTO add a new non-dynamic boolean or value option:
4 *@ - add an entry to nail.h:enum okeys
5 *@ - run mk-okey-map.pl
6 *@ - update the manual!
7 *@ TODO . should be recursive environment based.
8 *@ TODO Otherwise, the `localopts' should be an attribute of the lex_input.c
9 *@ TODO command context, so that it belongs to the execution context
10 *@ TODO we are running in, instead of being global data. See, e.g.,
11 *@ TODO the a_LEX_SLICE comment in lex_input.c.
12 *@ TODO . undefining and overwriting a macro should always be possible:
13 *@ TODO simply place the thing in a delete-later list and replace the
14 *@ TODO accessible entry! (instant delete if on top recursion level.)
15 *@ TODO . Likewise, overwriting an existing should be like delete+create
16 *@ TODO . once we can have non-fatal !0 returns for commands, we should
17 *@ TODO return error if "(environ)? unset" goes for non-existent.
19 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
20 * Copyright (c) 2012 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
23 * Copyright (c) 1980, 1993
24 * The Regents of the University of California. All rights reserved.
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 * 3. Neither the name of the University nor the names of its contributors
35 * may be used to endorse or promote products derived from this software
36 * without specific prior written permission.
38 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
39 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
42 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
44 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
45 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
50 #undef n_FILE
51 #define n_FILE accmacvar
53 #ifndef HAVE_AMALGAMATION
54 # include "nail.h"
55 #endif
57 #if !defined HAVE_SETENV && !defined HAVE_PUTENV
58 # error Exactly one of HAVE_SETENV and HAVE_PUTENV
59 #endif
61 /* Note: changing the hash function must be reflected in mk-okey-map.pl */
62 #define a_AMV_PRIME HSHSIZE
63 #define a_AMV_NAME2HASH(N) torek_hash(N)
64 #define a_AMV_HASH2PRIME(H) ((H) % a_AMV_PRIME)
66 enum a_amv_mac_flags{
67 a_AMV_MF_NONE = 0,
68 a_AMV_MF_ACC = 1<<0, /* This macro is an `account' */
69 a_AMV_MF_TYPE_MASK = a_AMV_MF_ACC,
70 a_AMV_MF_UNDEF = 1<<1, /* Unlink after lookup */
71 a_AMV_MF_DEL = 1<<7, /* Current `account': deleted while active */
72 a_AMV_MF__MAX = 0xFF
75 /* mk-okey-map.pl ensures that _VIRT implies _RDONLY and _NODEL, and that
76 * _IMPORT implies _ENV; it doesn't verify anything... */
77 enum a_amv_var_flags{
78 a_AMV_VF_NONE = 0,
79 a_AMV_VF_BOOL = 1<<0, /* ok_b_* */
80 a_AMV_VF_VIRT = 1<<1, /* "Stateless" automatic variable */
81 a_AMV_VF_NOLOPTS = 1<<2, /* May not be tracked by `localopts' */
82 a_AMV_VF_RDONLY = 1<<3, /* May not be set by user */
83 a_AMV_VF_NODEL = 1<<4, /* May not be deleted */
84 a_AMV_VF_NOTEMPTY = 1<<5, /* May not be assigned an empty value */
85 a_AMV_VF_NOCNTRLS = 1<<6, /* Value may not contain control characters */
86 a_AMV_VF_NUM = 1<<7, /* Value must be a 32-bit number */
87 a_AMV_VF_POSNUM = 1<<8, /* Value must be positive 32-bit number */
88 a_AMV_VF_VIP = 1<<9, /* Wants _var_check_vips() evaluation */
89 a_AMV_VF_IMPORT = 1<<10, /* Import ONLY from environ (before PS_STARTED) */
90 a_AMV_VF_ENV = 1<<11, /* Update environment on change */
91 a_AMV_VF_I3VAL = 1<<12, /* Has an initial value */
92 a_AMV_VF_DEFVAL = 1<<13, /* Has a default value */
93 a_AMV_VF_LINKED = 1<<14, /* `environ' linked */
94 a_AMV_VF__MASK = (1<<(14+1)) - 1
97 struct a_amv_mac{
98 struct a_amv_mac *am_next;
99 ui32_t am_maxlen; /* of any line in .am_line_dat */
100 ui32_t am_line_cnt; /* of *.am_line_dat (but NULL terminated) */
101 struct a_amv_mac_line **am_line_dat; /* TODO use deque? */
102 struct a_amv_var *am_lopts; /* `localopts' unroll list */
103 ui8_t am_flags; /* enum a_amv_mac_flags */
104 char am_name[n_VFIELD_SIZE(7)]; /* of this macro */
106 n_CTA(a_AMV_MF__MAX <= UI8_MAX, "Enumeration excesses storage datatype");
108 struct a_amv_mac_line{
109 ui32_t aml_len;
110 ui32_t aml_prespc; /* Number of leading SPC, for display purposes */
111 char aml_dat[n_VFIELD_SIZE(0)];
114 struct a_amv_mac_call_args{
115 char const *amca_name;
116 struct a_amv_mac const *amca_amp;
117 struct a_amv_var **amca_unroller;
118 void (*amca_hook_pre)(void *);
119 void *amca_hook_arg;
120 bool_t amca_lopts_on;
121 bool_t amca_ps_hook_mask;
122 ui8_t amca__pad[6];
125 struct a_amv_lostack{
126 struct a_amv_lostack *as_global_saved; /* Saved global XXX due to jump */
127 struct a_amv_mac_call_args *as_amcap;
128 struct a_amv_lostack *as_up; /* Outer context */
129 struct a_amv_var *as_lopts;
130 bool_t as_unroll; /* Unrolling enabled? */
131 ui8_t avs__pad[7];
134 struct a_amv_var{
135 struct a_amv_var *av_link;
136 char *av_value;
137 #ifdef HAVE_PUTENV
138 char *av_env; /* Actively managed putenv(3) memory */
139 #endif
140 ui16_t av_flags; /* enum a_amv_var_flags */
141 char av_name[n_VFIELD_SIZE(6)];
143 n_CTA(a_AMV_VF__MASK <= UI16_MAX, "Enumeration excesses storage datatype");
145 struct a_amv_var_map{
146 ui32_t avm_hash;
147 ui16_t avm_keyoff;
148 ui16_t avm_flags; /* enum a_amv_var_flags */
150 n_CTA(a_AMV_VF__MASK <= UI16_MAX, "Enumeration excesses storage datatype");
152 struct a_amv_var_virt{
153 ui32_t avv_okey;
154 ui8_t avv__dummy[4];
155 struct a_amv_var const *avv_var;
158 struct a_amv_var_defval{
159 ui32_t avdv_okey;
160 ui8_t avdv__pad[4];
161 char const *avdv_value; /* Only for !BOOL (otherwise plain existence) */
164 struct a_amv_var_carrier{
165 char const *avc_name;
166 ui32_t avc_hash;
167 ui32_t avc_prime;
168 struct a_amv_var *avc_var;
169 struct a_amv_var_map const *avc_map;
170 enum okeys avc_okey;
171 ui8_t avc__pad[4];
174 /* Include the constant mk-okey-map.pl output */
175 #include "version.h"
176 #include "okeys.h"
178 /* True boolean visualization: this string will not be copied to heap memory
179 * in a_amv_var_copy(), but we must avoid confusion with identical user data.
180 * While here, add a special "0" one and speed up *.exit-status* assignments! */
181 static char const a_amv_var_1[] = "1";
182 static char const a_amv_var_0[] = "0";
184 /* The currently active account */
185 static struct a_amv_mac *a_amv_acc_curr;
187 static struct a_amv_mac *a_amv_macs[a_AMV_PRIME]; /* TODO dynamically spaced */
189 /* Unroll list of currently running macro stack */
190 static struct a_amv_lostack *a_amv_lopts;
192 static struct a_amv_var *a_amv_vars[a_AMV_PRIME]; /* TODO dynamically spaced */
194 /* TODO We really deserve localopts support for *folder-hook*s, so hack it in
195 * TODO today via a static lostack, it should be a field in mailbox, once that
196 * TODO is a real multi-instance object */
197 static struct a_amv_var *a_amv_folder_hook_lopts;
199 /* TODO Rather ditto (except for storage -> cmd_ctx), compose hooks */
200 static struct a_amv_var *a_amv_compose_lopts;
202 /* Does cp consist solely of WS and a } */
203 static bool_t a_amv_mac_is_closing_angle(char const *cp);
205 /* Lookup for macros/accounts */
206 static struct a_amv_mac *a_amv_mac_lookup(char const *name,
207 struct a_amv_mac *newamp, enum a_amv_mac_flags amf);
209 /* Execute a macro; amcap must reside in LOFI memory */
210 static bool_t a_amv_mac_exec(struct a_amv_mac_call_args *amcap);
212 static void a_amv_mac__finalize(void *vp);
214 /* User display helpers */
215 static bool_t a_amv_mac_show(enum a_amv_mac_flags amf);
217 /* _def() returns error for faulty definitions and already existing * names,
218 * _undef() returns error if a named thing doesn't exist */
219 static bool_t a_amv_mac_def(char const *name, enum a_amv_mac_flags amf);
220 static bool_t a_amv_mac_undef(char const *name, enum a_amv_mac_flags amf);
222 /* */
223 static void a_amv_mac_free(struct a_amv_mac *amp);
225 /* Update replay-log */
226 static void a_amv_lopts_add(struct a_amv_lostack *alp, char const *name,
227 struct a_amv_var *oavp);
228 static void a_amv_lopts_unroll(struct a_amv_var **avpp);
230 /* Special cased value string allocation */
231 static char *a_amv_var_copy(char const *str);
232 static void a_amv_var_free(char *cp);
234 /* Check for special housekeeping */
235 static bool_t a_amv_var_check_vips(enum okeys okey, bool_t enable, char **val);
237 /* _VF_NOCNTRLS, _VF_NUM / _VF_POSNUM */
238 static bool_t a_amv_var_check_nocntrls(char const *val);
239 static bool_t a_amv_var_check_num(char const *val, bool_t pos);
241 /* If a variable name begins with a lowercase-character and contains at
242 * least one '@', it is converted to all-lowercase. This is necessary
243 * for lookups of names based on email addresses.
244 * Following the standard, only the part following the last '@' should
245 * be lower-cased, but practice has established otherwise here */
246 static char const *a_amv_var_canonify(char const *vn);
248 /* Try to reverse lookup an option name to an enum okeys mapping.
249 * Updates .avc_name and .avc_hash; .avc_map is NULL if none found */
250 static bool_t a_amv_var_revlookup(struct a_amv_var_carrier *avcp,
251 char const *name);
253 /* Lookup a variable from .avc_(map|name|hash), return whether it was found.
254 * Sets .avc_prime; .avc_var is NULL if not found.
255 * Here it is where we care for _I3VAL and _DEFVAL, too.
256 * An _I3VAL will be "consumed" as necessary anyway, but it won't be used to
257 * create a new variable if i3val_nonew is true; if i3val_nonew is TRUM1 then
258 * we set .avc_var to -1 and return true if that was the case, otherwise we'll
259 * return FAL0, then! */
260 static bool_t a_amv_var_lookup(struct a_amv_var_carrier *avcp,
261 bool_t i3val_nonew);
263 /* Set var from .avc_(map|name|hash), return success */
264 static bool_t a_amv_var_set(struct a_amv_var_carrier *avcp, char const *value,
265 bool_t force_env);
267 static bool_t a_amv_var__putenv(struct a_amv_var_carrier *avcp,
268 struct a_amv_var *avp);
270 /* Clear var from .avc_(map|name|hash); sets .avc_var=NULL, return success */
271 static bool_t a_amv_var_clear(struct a_amv_var_carrier *avcp, bool_t force_env);
273 static bool_t a_amv_var__clearenv(char const *name, char *value);
275 /* List all variables */
276 static void a_amv_var_show_all(void);
278 static int a_amv_var__show_cmp(void const *s1, void const *s2);
280 /* Actually do print one, return number of lines written */
281 static size_t a_amv_var_show(char const *name, FILE *fp, struct n_string *msgp);
283 /* Shared c_set() and c_environ():set impl, return success */
284 static bool_t a_amv_var_c_set(char **ap, bool_t issetenv);
286 static bool_t
287 a_amv_mac_is_closing_angle(char const *cp){
288 bool_t rv;
289 NYD2_ENTER;
291 while(spacechar(*cp))
292 ++cp;
294 if((rv = (*cp++ == '}'))){
295 while(spacechar(*cp))
296 ++cp;
297 rv = (*cp == '\0');
299 NYD2_LEAVE;
300 return rv;
303 static struct a_amv_mac *
304 a_amv_mac_lookup(char const *name, struct a_amv_mac *newamp,
305 enum a_amv_mac_flags amf){
306 struct a_amv_mac *amp, **ampp;
307 ui32_t h;
308 enum a_amv_mac_flags save_amf;
309 NYD2_ENTER;
311 save_amf = amf;
312 amf &= a_AMV_MF_TYPE_MASK;
313 h = a_AMV_NAME2HASH(name);
314 h = a_AMV_HASH2PRIME(h);
315 ampp = &a_amv_macs[h];
317 for(amp = *ampp; amp != NULL; ampp = &(*ampp)->am_next, amp = amp->am_next){
318 if((amp->am_flags & a_AMV_MF_TYPE_MASK) == amf &&
319 !strcmp(amp->am_name, name)){
320 if(n_LIKELY((save_amf & a_AMV_MF_UNDEF) == 0))
321 goto jleave;
323 *ampp = amp->am_next;
325 if((amf & a_AMV_MF_ACC) &&
326 account_name != NULL && !strcmp(account_name, name)){
327 amp->am_flags |= a_AMV_MF_DEL;
328 n_err(_("Delayed deletion of active account: %s\n"), name);
329 }else{
330 a_amv_mac_free(amp);
331 amp = (struct a_amv_mac*)-1;
333 goto jleave;
337 if(newamp != NULL){
338 ampp = &a_amv_macs[h];
339 newamp->am_next = *ampp;
340 *ampp = newamp;
341 amp = NULL;
343 jleave:
344 NYD2_LEAVE;
345 return amp;
348 static bool_t
349 a_amv_mac_exec(struct a_amv_mac_call_args *amcap){
350 struct a_amv_lostack *losp;
351 struct a_amv_mac_line **amlp;
352 char **args_base, **args;
353 struct a_amv_mac const *amp;
354 bool_t rv;
355 NYD2_ENTER;
357 amp = amcap->amca_amp;
358 /* XXX Unfortunately we yet need to dup the macro lines! :( */
359 args_base = args = smalloc(sizeof(*args) * (amp->am_line_cnt +1));
360 for(amlp = amp->am_line_dat; *amlp != NULL; ++amlp)
361 *(args++) = sbufdup((*amlp)->aml_dat, (*amlp)->aml_len);
362 *args = NULL;
364 losp = n_lofi_alloc(sizeof *losp);
365 losp->as_global_saved = a_amv_lopts;
366 if((losp->as_amcap = amcap)->amca_unroller == NULL){
367 losp->as_up = losp->as_global_saved;
368 losp->as_lopts = NULL;
369 }else{
370 losp->as_up = NULL;
371 losp->as_lopts = *amcap->amca_unroller;
373 losp->as_unroll = amcap->amca_lopts_on;
375 a_amv_lopts = losp;
376 if(amcap->amca_hook_pre != NULL)
377 (*amcap->amca_hook_pre)(amcap->amca_hook_arg);
378 rv = n_source_macro(n_LEXINPUT_NONE, amp->am_name, args_base,
379 &a_amv_mac__finalize, losp);
380 NYD2_LEAVE;
381 return rv;
384 static void
385 a_amv_mac__finalize(void *vp){
386 struct a_amv_mac_call_args *amcap;
387 struct a_amv_lostack *losp;
388 NYD2_ENTER;
390 losp = vp;
391 a_amv_lopts = losp->as_global_saved;
393 if((amcap = losp->as_amcap)->amca_unroller == NULL){
394 if(losp->as_lopts != NULL)
395 a_amv_lopts_unroll(&losp->as_lopts);
396 }else
397 *amcap->amca_unroller = losp->as_lopts;
399 if(amcap->amca_ps_hook_mask)
400 pstate &= ~PS_HOOK_MASK;
402 n_lofi_free(losp);
403 n_lofi_free(amcap);
404 NYD2_LEAVE;
407 static bool_t
408 a_amv_mac_show(enum a_amv_mac_flags amf){
409 size_t lc, mc, ti, i;
410 char const *typestr;
411 FILE *fp;
412 bool_t rv;
413 NYD2_ENTER;
415 rv = FAL0;
417 if((fp = Ftmp(NULL, "deflist", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
418 NULL){
419 n_perr(_("Can't create temporary file for `define' or `account' listing"),
421 goto jleave;
424 amf &= a_AMV_MF_TYPE_MASK;
425 typestr = (amf & a_AMV_MF_ACC) ? "account" : "define";
427 for(lc = mc = ti = 0; ti < a_AMV_PRIME; ++ti){
428 struct a_amv_mac *amp;
430 for(amp = a_amv_macs[ti]; amp != NULL; amp = amp->am_next){
431 if((amp->am_flags & a_AMV_MF_TYPE_MASK) == amf){
432 struct a_amv_mac_line **amlpp;
434 if(++mc > 1){
435 putc('\n', fp);
436 ++lc;
438 ++lc;
439 fprintf(fp, "%s %s {\n", typestr, amp->am_name);
440 for(amlpp = amp->am_line_dat; *amlpp != NULL; ++lc, ++amlpp){
441 for(i = (*amlpp)->aml_prespc; i > 0; --i)
442 putc(' ', fp);
443 fputs((*amlpp)->aml_dat, fp);
444 putc('\n', fp);
446 fputs("}\n", fp);
447 ++lc;
451 if(mc > 0)
452 page_or_print(fp, lc);
454 rv = (ferror(fp) == 0);
455 Fclose(fp);
456 jleave:
457 NYD2_LEAVE;
458 return rv;
461 static bool_t
462 a_amv_mac_def(char const *name, enum a_amv_mac_flags amf){
463 struct str line;
464 ui32_t line_cnt, maxlen;
465 struct linelist{
466 struct linelist *ll_next;
467 struct a_amv_mac_line *ll_amlp;
468 } *llp, *ll_head, *ll_tail;
469 union {size_t s; int i; ui32_t ui; size_t l;} n;
470 struct a_amv_mac *amp;
471 bool_t rv;
472 NYD2_ENTER;
474 memset(&line, 0, sizeof line);
475 rv = FAL0;
476 amp = NULL;
478 /* Read in the lines which form the macro content */
479 for(ll_tail = ll_head = NULL, line_cnt = maxlen = 0;;){
480 ui32_t leaspc;
481 char *cp;
483 n.i = n_lex_input(n_LEXINPUT_CTX_DEFAULT | n_LEXINPUT_NL_ESC, n_empty,
484 &line.s, &line.l, NULL);
485 if(n.ui == 0)
486 continue;
487 if(n.i < 0){
488 n_err(_("Unterminated %s definition: %s\n"),
489 (amf & a_AMV_MF_ACC ? "account" : "macro"), name);
490 goto jerr;
492 if(a_amv_mac_is_closing_angle(line.s))
493 break;
495 /* Trim WS, remember amount of leading spaces for display purposes */
496 for(cp = line.s, leaspc = 0; n.ui > 0; ++cp, --n.ui)
497 if(*cp == '\t')
498 leaspc = (leaspc + 8u) & ~7u;
499 else if(*cp == ' ')
500 ++leaspc;
501 else
502 break;
503 for(; n.ui > 0 && whitechar(cp[n.ui - 1]); --n.ui)
505 if(n.ui == 0)
506 continue;
508 maxlen = n_MAX(maxlen, n.ui);
509 cp[n.ui++] = '\0';
511 if(n_LIKELY(++line_cnt < UI32_MAX)){
512 struct a_amv_mac_line *amlp;
514 llp = salloc(sizeof *llp);
515 if(ll_head == NULL)
516 ll_head = llp;
517 else
518 ll_tail->ll_next = llp;
519 ll_tail = llp;
520 llp->ll_next = NULL;
521 llp->ll_amlp = amlp = smalloc(n_VSTRUCT_SIZEOF(struct a_amv_mac_line,
522 aml_dat) + n.ui);
523 amlp->aml_len = n.ui -1;
524 amlp->aml_prespc = leaspc;
525 memcpy(amlp->aml_dat, cp, n.ui);
526 }else{
527 n_err(_("Too much content in %s definition: %s\n"),
528 (amf & a_AMV_MF_ACC ? "account" : "macro"), name);
529 goto jerr;
533 /* Create the new macro */
534 n.s = strlen(name) +1;
535 amp = smalloc(n_VSTRUCT_SIZEOF(struct a_amv_mac, am_name) + n.s);
536 amp->am_next = NULL;
537 amp->am_maxlen = maxlen;
538 amp->am_line_cnt = line_cnt;
539 amp->am_flags = amf;
540 amp->am_lopts = NULL;
541 memcpy(amp->am_name, name, n.s);
542 /* C99 */{
543 struct a_amv_mac_line **amlpp;
545 amp->am_line_dat = amlpp = smalloc(sizeof(*amlpp) * ++line_cnt);
546 for(llp = ll_head; llp != NULL; llp = llp->ll_next)
547 *amlpp++ = llp->ll_amlp;
548 *amlpp = NULL;
551 /* Finally check whether such a macro already exists, in which case we throw
552 * it all away again. At least we know it would have worked */
553 if(a_amv_mac_lookup(name, amp, amf) != NULL){
554 n_err(_("There is already a %s of name: %s\n"),
555 (amf & a_AMV_MF_ACC ? "account" : "macro"), name);
556 goto jerr;
559 rv = TRU1;
560 jleave:
561 if(line.s != NULL)
562 free(line.s);
563 NYD2_LEAVE;
564 return rv;
566 jerr:
567 for(llp = ll_head; llp != NULL; llp = llp->ll_next)
568 free(llp->ll_amlp);
569 if(amp != NULL){
570 free(amp->am_line_dat);
571 free(amp);
573 goto jleave;
576 static bool_t
577 a_amv_mac_undef(char const *name, enum a_amv_mac_flags amf){
578 struct a_amv_mac *amp;
579 bool_t rv;
580 NYD2_ENTER;
582 rv = TRU1;
584 if(n_LIKELY(name[0] != '*' || name[1] != '\0')){
585 if((amp = a_amv_mac_lookup(name, NULL, amf | a_AMV_MF_UNDEF)) == NULL){
586 n_err(_("%s not defined: %s\n"),
587 (amf & a_AMV_MF_ACC ? "Account" : "Macro"), name);
588 rv = FAL0;
590 }else{
591 struct a_amv_mac **ampp, *lamp;
593 for(ampp = a_amv_macs; PTRCMP(ampp, <, &a_amv_macs[n_NELEM(a_amv_macs)]);
594 ++ampp)
595 for(lamp = NULL, amp = *ampp; amp != NULL;){
596 if((amp->am_flags & a_AMV_MF_TYPE_MASK) == amf){
597 /* xxx Expensive but rare: be simple */
598 a_amv_mac_lookup(amp->am_name, NULL, amf | a_AMV_MF_UNDEF);
599 amp = (lamp == NULL) ? *ampp : lamp->am_next;
600 }else{
601 lamp = amp;
602 amp = amp->am_next;
606 NYD2_LEAVE;
607 return rv;
610 static void
611 a_amv_mac_free(struct a_amv_mac *amp){
612 struct a_amv_mac_line **amlpp;
613 NYD2_ENTER;
615 for(amlpp = amp->am_line_dat; *amlpp != NULL; ++amlpp)
616 free(*amlpp);
617 free(amp->am_line_dat);
618 free(amp);
619 NYD2_LEAVE;
622 static void
623 a_amv_lopts_add(struct a_amv_lostack *alp, char const *name,
624 struct a_amv_var *oavp){
625 struct a_amv_var *avp;
626 size_t nl, vl;
627 NYD2_ENTER;
629 /* Propagate unrolling up the stack, as necessary */
630 assert(alp != NULL);
631 for(;;){
632 if(alp->as_unroll)
633 break;
634 if((alp = alp->as_up) == NULL)
635 goto jleave;
638 /* Check whether this variable is handled yet */
639 for(avp = alp->as_lopts; avp != NULL; avp = avp->av_link)
640 if(!strcmp(avp->av_name, name))
641 goto jleave;
643 nl = strlen(name) +1;
644 vl = (oavp != NULL) ? strlen(oavp->av_value) +1 : 0;
645 avp = smalloc(n_VSTRUCT_SIZEOF(struct a_amv_var, av_name) + nl + vl);
646 avp->av_link = alp->as_lopts;
647 alp->as_lopts = avp;
648 memcpy(avp->av_name, name, nl);
649 if(vl == 0){
650 avp->av_value = NULL;
651 avp->av_flags = 0;
652 #ifdef HAVE_PUTENV
653 avp->av_env = NULL;
654 #endif
655 }else{
656 avp->av_value = &avp->av_name[nl];
657 avp->av_flags = oavp->av_flags;
658 memcpy(avp->av_value, oavp->av_value, vl);
659 #ifdef HAVE_PUTENV
660 avp->av_env = (oavp->av_env == NULL) ? NULL : sstrdup(oavp->av_env);
661 #endif
663 jleave:
664 NYD2_LEAVE;
667 static void
668 a_amv_lopts_unroll(struct a_amv_var **avpp){
669 struct a_amv_lostack *save_alp;
670 bool_t reset;
671 struct a_amv_var *x, *avp;
672 NYD2_ENTER;
674 avp = *avpp;
675 *avpp = NULL;
676 reset = !(pstate & PS_ROOT);
678 save_alp = a_amv_lopts;
679 a_amv_lopts = NULL;
680 while(avp != NULL){
681 x = avp;
682 avp = avp->av_link;
683 pstate |= PS_ROOT;
684 vok_vset(x->av_name, x->av_value);
685 if(reset)
686 pstate &= ~PS_ROOT;
687 free(x);
689 a_amv_lopts = save_alp;
690 NYD2_LEAVE;
693 static char *
694 a_amv_var_copy(char const *str){
695 char *news;
696 size_t len;
697 NYD2_ENTER;
699 if(*str == '\0')
700 news = n_UNCONST(n_empty);
701 else if(str[1] == '\0'){
702 if(str[0] == '1')
703 news = n_UNCONST(a_amv_var_1);
704 else if(str[0] == '0')
705 news = n_UNCONST(a_amv_var_0);
706 else
707 goto jheap;
708 }else{
709 jheap:
710 len = strlen(str) +1;
711 news = smalloc(len);
712 memcpy(news, str, len);
714 NYD2_LEAVE;
715 return news;
718 static void
719 a_amv_var_free(char *cp){
720 NYD2_ENTER;
721 if(cp[0] != '\0' && cp != a_amv_var_1 && cp != a_amv_var_0)
722 free(cp);
723 NYD2_LEAVE;
726 static bool_t
727 a_amv_var_check_vips(enum okeys okey, bool_t enable, char **val){
728 int flag;
729 bool_t ok;
730 NYD2_ENTER;
732 ok = TRU1;
733 flag = 0;
735 switch(okey){
736 case ok_b_debug:
737 flag = OPT_DEBUG;
738 break;
739 case ok_v_HOME:
740 /* Invalidate any resolved folder then, too
741 * FALLTHRU */
742 case ok_v_folder:
743 ok = !(pstate & PS_ROOT);
744 pstate |= PS_ROOT;
745 ok_vclear(_folder_resolved);
746 if(ok)
747 pstate &= ~PS_ROOT;
748 ok = TRU1;
749 break;
750 case ok_b_header:
751 flag = OPT_N_FLAG;
752 enable = !enable;
753 break;
754 case ok_b_memdebug:
755 flag = OPT_MEMDEBUG;
756 break;
757 case ok_b_POSIXLY_CORRECT:
758 if(!(pstate & PS_ROOT)){
759 bool_t reset = !(pstate & PS_ROOT);
761 pstate |= PS_ROOT;
762 if(enable)
763 ok_bset(posix);
764 else
765 ok_bclear(posix);
766 if(reset)
767 pstate &= ~PS_ROOT;
769 break;
770 case ok_b_posix:
771 if(!(pstate & PS_ROOT)){
772 bool_t reset = !(pstate & PS_ROOT);
774 pstate |= PS_ROOT;
775 if(enable)
776 ok_bset(POSIXLY_CORRECT);
777 else
778 ok_bclear(POSIXLY_CORRECT);
779 if(reset)
780 pstate &= ~PS_ROOT;
782 break;
783 case ok_b_skipemptybody:
784 flag = OPT_E_FLAG;
785 break;
786 case ok_b_typescript_mode:
787 if(enable){
788 ok_bset(colour_disable);
789 ok_bset(line_editor_disable);
790 if(!(pstate & PS_STARTED))
791 ok_bset(termcap_disable);
793 case ok_v_umask:
794 assert(enable);
795 if(**val != '\0'){
796 ul_i ul;
798 if((ul = strtoul(*val, NULL, 0)) & ~0777u){ /* (is valid _VF_POSNUM) */
799 n_err(_("Invalid *umask* setting: %s\n"), *val);
800 ok = FAL0;
801 }else
802 umask((mode_t)ul);
804 break;
805 case ok_b_verbose:
806 flag = (enable && !(options & OPT_VERB))
807 ? OPT_VERB : OPT_VERB | OPT_VERBVERB;
808 break;
809 default:
810 DBG( n_err("Implementation error: never heard of %u\n", ok); )
811 break;
814 if(flag){
815 if(enable)
816 options |= flag;
817 else
818 options &= ~flag;
820 NYD2_LEAVE;
821 return ok;
824 static bool_t
825 a_amv_var_check_nocntrls(char const *val){
826 char c;
827 NYD2_ENTER;
829 while((c = *val++) != '\0')
830 if(cntrlchar(c))
831 break;
832 NYD2_LEAVE;
833 return (c == '\0');
836 static bool_t
837 a_amv_var_check_num(char const *val, bool_t pos){ /* TODO intmax_t anywhere! */
838 /* TODO The internal/environment variables which are num= or posnum= should
839 * TODO gain special lookup functions, or the return should be void* and
840 * TODO castable to integer; i.e. no more strtoX() should be needed.
841 * TODO I.e., the result of this function should instead be stored.
842 * TODO Use intmax_t IF that is sizeof(void*) only? */
843 bool_t rv;
844 NYD2_ENTER;
846 rv = TRU1;
848 if(*val != '\0'){ /* Would be _VF_NOTEMPTY if not allowed */
849 char *eptr;
850 union {long s; unsigned long u;} i;
852 if(!pos){
853 i.s = strtol(val, &eptr, 0); /* TODO strtoimax() */
855 if(*eptr != '\0' ||
856 ((i.s == LONG_MIN || i.s == LONG_MAX) && errno == ERANGE))
857 rv = FAL0;
858 #if INT_MIN != LONG_MIN
859 else if(i.s < INT_MIN)
860 rv = FAL0;
861 #endif
862 #if INT_MAX != LONG_MAX
863 else if(i.s > INT_MAX)
864 rv = FAL0;
865 #endif
866 }else{
867 i.u = strtoul(val, &eptr, 0); /* TODO strtoumax() */
869 if(*eptr != '\0' || (i.u == ULONG_MAX && errno == ERANGE))
870 rv = FAL0;
871 #if UINT_MAX != ULONG_MAX
872 else if(i.u > UINT_MAX)
873 rv = FAL0;
874 #endif
877 NYD2_LEAVE;
878 return rv;
881 static char const *
882 a_amv_var_canonify(char const *vn){
883 NYD2_ENTER;
884 if(!upperchar(*vn)){
885 char const *vp;
887 for(vp = vn; *vp != '\0' && *vp != '@'; ++vp)
889 vn = (*vp == '@') ? i_strdup(vn) : vn;
891 NYD2_LEAVE;
892 return vn;
895 static bool_t
896 a_amv_var_revlookup(struct a_amv_var_carrier *avcp, char const *name){
897 ui32_t hash, i, j;
898 struct a_amv_var_map const *avmp;
899 NYD2_ENTER;
901 avcp->avc_name = name = a_amv_var_canonify(name);
902 avcp->avc_hash = hash = a_AMV_NAME2HASH(name);
904 for(i = hash % a_AMV_VAR_REV_PRIME, j = 0; j <= a_AMV_VAR_REV_LONGEST; ++j){
905 ui32_t x;
907 if((x = a_amv_var_revmap[i]) == a_AMV_VAR_REV_ILL)
908 break;
910 avmp = &a_amv_var_map[x];
911 if(avmp->avm_hash == hash &&
912 !strcmp(&a_amv_var_names[avmp->avm_keyoff], name)){
913 avcp->avc_map = avmp;
914 avcp->avc_okey = (enum okeys)x;
915 goto jleave;
918 if(++i == a_AMV_VAR_REV_PRIME){
919 #ifdef a_AMV_VAR_REV_WRAPAROUND
920 i = 0;
921 #else
922 break;
923 #endif
926 avcp->avc_map = NULL;
927 avcp = NULL;
928 jleave:
929 NYD2_LEAVE;
930 return (avcp != NULL);
933 static bool_t
934 a_amv_var_lookup(struct a_amv_var_carrier *avcp, bool_t i3val_nonew){
935 size_t i;
936 char const *cp;
937 struct a_amv_var_map const *avmp;
938 struct a_amv_var *avp;
939 NYD2_ENTER;
941 /* C99 */{
942 struct a_amv_var **avpp, *lavp;
944 avpp = &a_amv_vars[avcp->avc_prime = a_AMV_HASH2PRIME(avcp->avc_hash)];
946 for(lavp = NULL, avp = *avpp; avp != NULL; lavp = avp, avp = avp->av_link)
947 if(!strcmp(avp->av_name, avcp->avc_name)){
948 /* Relink as head, hope it "sorts on usage" over time.
949 * _clear() relies on this behaviour */
950 if(lavp != NULL){
951 lavp->av_link = avp->av_link;
952 avp->av_link = *avpp;
953 *avpp = avp;
955 goto jleave;
959 /* If this is not an assembled variable we need to consider some special
960 * initialization cases and eventually create the variable anew */
961 if(n_LIKELY((avmp = avcp->avc_map) != NULL)){
962 /* Does it have an import-from-environment flag? */
963 if(n_UNLIKELY((avmp->avm_flags & (a_AMV_VF_IMPORT | a_AMV_VF_ENV)) != 0)){
964 if(n_LIKELY((cp = getenv(avcp->avc_name)) != NULL)){
965 /* May be better not to use that one, though? */
966 bool_t isempty, isbltin;
968 isempty = (*cp == '\0' &&
969 (avmp->avm_flags & a_AMV_VF_NOTEMPTY) != 0);
970 isbltin = ((avmp->avm_flags & (a_AMV_VF_I3VAL | a_AMV_VF_DEFVAL)
971 ) != 0);
973 if(n_UNLIKELY(isempty)){
974 if(!isbltin)
975 goto jerr;
976 }else if(n_LIKELY(*cp != '\0')){
977 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NOCNTRLS) &&
978 !a_amv_var_check_nocntrls(cp))){
979 n_err(_("Ignoring environment, control characters "
980 "invalid in variable: %s\n"), avcp->avc_name);
981 goto jerr;
983 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NUM) &&
984 !a_amv_var_check_num(cp, FAL0))){
985 n_err(_("Environment variable value not a number "
986 "or out of range: %s\n"), avcp->avc_name);
987 goto jerr;
989 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_POSNUM) &&
990 !a_amv_var_check_num(cp, TRU1))){
991 n_err(_("Environment variable value not a number, "
992 "negative or out of range: %s\n"), avcp->avc_name);
993 goto jerr;
996 goto jnewval;
1000 /* A first-time init switch is to be handled now and here */
1001 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_I3VAL) != 0)){
1002 static struct a_amv_var_defval const **arr,
1003 *arr_base[a_AMV_VAR_I3VALS_CNT +1];
1005 if(arr == NULL){
1006 arr = &arr_base[0];
1007 arr[i = a_AMV_VAR_I3VALS_CNT] = NULL;
1008 while(i-- > 0)
1009 arr[i] = &a_amv_var_i3vals[i];
1012 for(i = 0; arr[i] != NULL; ++i)
1013 if(arr[i]->avdv_okey == avcp->avc_okey){
1014 cp = (avmp->avm_flags & a_AMV_VF_BOOL) ? n_empty
1015 : arr[i]->avdv_value;
1016 /* Remove this entry, hope entire block becomes no-op asap */
1018 arr[i] = arr[i + 1];
1019 while(arr[i++] != NULL);
1021 if(!i3val_nonew)
1022 goto jnewval;
1023 if(i3val_nonew == TRUM1)
1024 avp = (struct a_amv_var*)-1;
1025 goto jleave;
1029 /* The virtual variables */
1030 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_VIRT) != 0)){
1031 for(i = 0; i < a_AMV_VAR_VIRTS_CNT; ++i)
1032 if(a_amv_var_virts[i].avv_okey == avcp->avc_okey){
1033 avp = n_UNCONST(a_amv_var_virts[i].avv_var);
1034 goto jleave;
1036 /* Not reached */
1039 /* Place this last because once it is set first the variable will never
1040 * be removed again and thus match in the first block above */
1041 if(n_UNLIKELY(avmp->avm_flags & a_AMV_VF_DEFVAL) != 0){
1042 for(i = 0; i < a_AMV_VAR_DEFVALS_CNT; ++i)
1043 if(a_amv_var_defvals[i].avdv_okey == avcp->avc_okey){
1044 cp = (avmp->avm_flags & a_AMV_VF_BOOL) ? n_empty
1045 : a_amv_var_defvals[i].avdv_value;
1046 goto jnewval;
1051 jerr:
1052 avp = NULL;
1053 jleave:
1054 avcp->avc_var = avp;
1055 NYD2_LEAVE;
1056 return (avp != NULL);
1058 jnewval: /* C99 */{
1059 struct a_amv_var **avpp;
1060 size_t l;
1062 l = strlen(avcp->avc_name) +1;
1063 avcp->avc_var = avp = smalloc(n_VSTRUCT_SIZEOF(struct a_amv_var, av_name
1064 ) + l);
1065 avp->av_link = *(avpp = &a_amv_vars[avcp->avc_prime]);
1066 *avpp = avp;
1067 memcpy(avp->av_name, avcp->avc_name, l);
1068 avp->av_value = a_amv_var_copy(cp);
1069 #ifdef HAVE_PUTENV
1070 avp->av_env = NULL;
1071 #endif
1072 avp->av_flags = avmp->avm_flags;
1074 if(avp->av_flags & a_AMV_VF_VIP)
1075 a_amv_var_check_vips(avcp->avc_okey, TRU1, &avp->av_value);
1076 if(avp->av_flags & a_AMV_VF_ENV)
1077 a_amv_var__putenv(avcp, avp);
1078 goto jleave;
1082 static bool_t
1083 a_amv_var_set(struct a_amv_var_carrier *avcp, char const *value,
1084 bool_t force_env){
1085 struct a_amv_var *avp;
1086 char *oval;
1087 struct a_amv_var_map const *avmp;
1088 bool_t rv;
1089 NYD2_ENTER;
1091 if(value == NULL){
1092 rv = a_amv_var_clear(avcp, force_env);
1093 goto jleave;
1096 if((avmp = avcp->avc_map) != NULL){
1097 rv = FAL0;
1099 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_RDONLY) != 0 &&
1100 !(pstate & PS_ROOT))){
1101 value = N_("Variable is readonly: %s\n");
1102 goto jeavmp;
1104 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NOTEMPTY) && *value == '\0')){
1105 value = N_("Variable must not be empty: %s\n");
1106 goto jeavmp;
1108 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NOCNTRLS) != 0 &&
1109 !a_amv_var_check_nocntrls(value))){
1110 value = N_("Variable forbids control characters: %s\n");
1111 goto jeavmp;
1113 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NUM) &&
1114 !a_amv_var_check_num(value, FAL0))){
1115 value = N_("Variable value not a number or out of range: %s\n");
1116 goto jeavmp;
1118 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_POSNUM) &&
1119 !a_amv_var_check_num(value, TRU1))){
1120 value = _("Variable value not a number, negative, "
1121 "or out of range: %s\n");
1122 goto jeavmp;
1124 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_IMPORT) != 0 &&
1125 !(pstate & (PS_ROOT | PS_STARTED)))){
1126 value = N_("Variable cannot be set in a resource file: %s\n");
1127 jeavmp:
1128 n_err(V_(value), avcp->avc_name);
1129 goto jleave;
1133 rv = TRU1;
1134 a_amv_var_lookup(avcp, TRU1);
1136 /* Don't care what happens later on, store this in the unroll list */
1137 if(a_amv_lopts != NULL &&
1138 (avmp == NULL || !(avmp->avm_flags & a_AMV_VF_NOLOPTS)))
1139 a_amv_lopts_add(a_amv_lopts, avcp->avc_name, avcp->avc_var);
1141 if((avp = avcp->avc_var) == NULL){
1142 struct a_amv_var **avpp;
1143 size_t l;
1145 l = strlen(avcp->avc_name) +1;
1146 avcp->avc_var = avp = smalloc(n_VSTRUCT_SIZEOF(struct a_amv_var, av_name
1147 ) + l);
1148 avp->av_link = *(avpp = &a_amv_vars[avcp->avc_prime]);
1149 *avpp = avp;
1150 #ifdef HAVE_PUTENV
1151 avp->av_env = NULL;
1152 #endif
1153 memcpy(avp->av_name, avcp->avc_name, l);
1154 avp->av_flags = (avmp != NULL) ? avmp->avm_flags : 0;
1155 oval = n_UNCONST(n_empty);
1156 }else
1157 oval = avp->av_value;
1159 if(avmp == NULL)
1160 avp->av_value = a_amv_var_copy(value);
1161 else{
1162 /* Via `set' etc. the user may give even boolean options non-boolean
1163 * values, ignore that and force boolean */
1164 if(avp->av_flags & a_AMV_VF_BOOL){
1165 if((options & OPT_D_VV) && *value != '\0')
1166 n_err(_("Ignoring value of boolean variable: %s: %s\n"),
1167 avcp->avc_name, value);
1168 avp->av_value = n_UNCONST(a_amv_var_1);
1169 }else
1170 avp->av_value = a_amv_var_copy(value);
1172 /* Check if update allowed XXX wasteful on error! */
1173 if((avp->av_flags & a_AMV_VF_VIP) &&
1174 !(rv = a_amv_var_check_vips(avcp->avc_okey, TRU1, &avp->av_value))){
1175 char *cp = avp->av_value;
1177 avp->av_value = oval;
1178 oval = cp;
1182 if(force_env && !(avp->av_flags & a_AMV_VF_ENV))
1183 avp->av_flags |= a_AMV_VF_LINKED;
1184 if(avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_LINKED))
1185 rv = a_amv_var__putenv(avcp, avp);
1187 a_amv_var_free(oval);
1188 jleave:
1189 NYD2_LEAVE;
1190 return rv;
1193 static bool_t
1194 a_amv_var__putenv(struct a_amv_var_carrier *avcp, struct a_amv_var *avp){
1195 #ifndef HAVE_SETENV
1196 char *cp;
1197 #endif
1198 bool_t rv;
1199 NYD2_ENTER;
1201 #ifdef HAVE_SETENV
1202 rv = (setenv(avcp->avc_name, avp->av_value, 1) == 0);
1203 #else
1204 cp = sstrdup(savecatsep(avcp->avc_name, '=', avp->av_value));
1206 if((rv = (putenv(cp) == 0))){
1207 char *ocp;
1209 if((ocp = avp->av_env) != NULL)
1210 free(ocp);
1211 avp->av_env = cp;
1212 }else
1213 free(cp);
1214 #endif
1215 NYD2_LEAVE;
1216 return rv;
1219 static bool_t
1220 a_amv_var_clear(struct a_amv_var_carrier *avcp, bool_t force_env){
1221 struct a_amv_var **avpp, *avp;
1222 struct a_amv_var_map const *avmp;
1223 bool_t rv;
1224 NYD2_ENTER;
1226 rv = FAL0;
1228 if(n_LIKELY((avmp = avcp->avc_map) != NULL)){
1229 if(n_UNLIKELY((avmp->avm_flags & a_AMV_VF_NODEL) != 0 &&
1230 !(pstate & PS_ROOT))){
1231 n_err(_("Variable may not be unset: %s\n"), avcp->avc_name);
1232 goto jleave;
1234 if((avmp->avm_flags & a_AMV_VF_VIP) &&
1235 !a_amv_var_check_vips(avcp->avc_okey, FAL0, NULL))
1236 goto jleave;
1239 rv = TRU1;
1241 if(n_UNLIKELY(!a_amv_var_lookup(avcp, TRUM1))){
1242 if(force_env){
1243 jforce_env:
1244 rv = a_amv_var__clearenv(avcp->avc_name, NULL);
1245 }else if(!(pstate & (PS_ROOT | PS_ROBOT)) && (options & OPT_D_V))
1246 n_err(_("Can't unset undefined variable: %s\n"), avcp->avc_name);
1247 goto jleave;
1248 }else if(avcp->avc_var == (struct a_amv_var*)-1){
1249 avcp->avc_var = NULL;
1250 if(force_env)
1251 goto jforce_env;
1252 goto jleave;
1255 if(a_amv_lopts != NULL &&
1256 (avmp == NULL || !(avmp->avm_flags & a_AMV_VF_NOLOPTS)))
1257 a_amv_lopts_add(a_amv_lopts, avcp->avc_name, avcp->avc_var);
1259 avp = avcp->avc_var;
1260 avcp->avc_var = NULL;
1261 avpp = &a_amv_vars[avcp->avc_prime];
1262 assert(*avpp == avp); /* (always listhead after lookup()) */
1263 *avpp = (*avpp)->av_link;
1265 /* C99 */{
1266 #ifdef HAVE_SETENV
1267 char *envval = NULL;
1268 #else
1269 char *envval = avp->av_env;
1270 #endif
1271 if((avp->av_flags & (a_AMV_VF_ENV | a_AMV_VF_LINKED)) || envval != NULL)
1272 rv = a_amv_var__clearenv(avp->av_name, envval);
1274 a_amv_var_free(avp->av_value);
1275 free(avp);
1277 /* XXX Fun part, extremely simple-minded for now: if this variable has
1278 * XXX a default value, immediately reinstantiate it! */
1279 if(n_UNLIKELY(avmp != NULL && (avmp->avm_flags & a_AMV_VF_DEFVAL) != 0))
1280 a_amv_var_lookup(avcp, TRU1);
1281 jleave:
1282 NYD2_LEAVE;
1283 return rv;
1286 static bool_t
1287 a_amv_var__clearenv(char const *name, char *value){
1288 #ifndef HAVE_SETENV
1289 extern char **environ;
1290 char **ecpp;
1291 #endif
1292 bool_t rv;
1293 NYD2_ENTER;
1294 n_UNUSED(value);
1296 #ifdef HAVE_SETENV
1297 unsetenv(name);
1298 rv = TRU1;
1299 #else
1300 if(value != NULL)
1301 for(ecpp = environ; *ecpp != NULL; ++ecpp)
1302 if(*ecpp == value){
1303 free(value);
1305 ecpp[0] = ecpp[1];
1306 while(*ecpp++ != NULL);
1307 break;
1309 rv = TRU1;
1310 #endif
1311 NYD2_LEAVE;
1312 return rv;
1315 static void
1316 a_amv_var_show_all(void){
1317 struct n_string msg, *msgp;
1318 FILE *fp;
1319 size_t no, i;
1320 struct a_amv_var *avp;
1321 char const **vacp, **cap;
1322 NYD2_ENTER;
1324 if((fp = Ftmp(NULL, "setlist", OF_RDWR | OF_UNLINK | OF_REGISTER)) == NULL){
1325 n_perr(_("Can't create temporary file for `set' listing"), 0);
1326 goto jleave;
1329 /* We need to instantiate first-time-inits and default values here, so that
1330 * they will be regular members of our _vars[] table */
1331 for(i = a_AMV_VAR_I3VALS_CNT; i-- > 0;)
1332 n_var_oklook(a_amv_var_i3vals[i].avdv_okey);
1333 for(i = a_AMV_VAR_DEFVALS_CNT; i-- > 0;)
1334 n_var_oklook(a_amv_var_defvals[i].avdv_okey);
1336 for(no = i = 0; i < a_AMV_PRIME; ++i)
1337 for(avp = a_amv_vars[i]; avp != NULL; avp = avp->av_link)
1338 ++no;
1339 no += a_AMV_VAR_VIRTS_CNT;
1341 vacp = salloc(no * sizeof(*vacp));
1343 for(cap = vacp, i = 0; i < a_AMV_PRIME; ++i)
1344 for(avp = a_amv_vars[i]; avp != NULL; avp = avp->av_link)
1345 *cap++ = avp->av_name;
1346 for(i = a_AMV_VAR_VIRTS_CNT; i-- > 0;)
1347 *cap++ = a_amv_var_virts[i].avv_var->av_name;
1349 if(no > 1)
1350 qsort(vacp, no, sizeof *vacp, &a_amv_var__show_cmp);
1352 msgp = &msg;
1353 msgp = n_string_reserve(n_string_creat(msgp), 80);
1354 for(i = 0, cap = vacp; no != 0; ++cap, --no)
1355 i += a_amv_var_show(*cap, fp, msgp);
1356 n_string_gut(&msg);
1358 page_or_print(fp, i);
1359 Fclose(fp);
1360 jleave:
1361 NYD2_LEAVE;
1364 static int
1365 a_amv_var__show_cmp(void const *s1, void const *s2){
1366 int rv;
1367 NYD2_ENTER;
1369 rv = strcmp(*(char**)n_UNCONST(s1), *(char**)n_UNCONST(s2));
1370 NYD2_LEAVE;
1371 return rv;
1374 static size_t
1375 a_amv_var_show(char const *name, FILE *fp, struct n_string *msgp){
1376 struct a_amv_var_carrier avc;
1377 char const *quote;
1378 size_t i;
1379 NYD2_ENTER;
1381 msgp = n_string_trunc(msgp, 0);
1382 i = 0;
1384 a_amv_var_revlookup(&avc, name);
1385 if(!a_amv_var_lookup(&avc, FAL0)){
1386 struct str s;
1388 msgp = n_string_assign_cp(msgp, _("No such variable: "));
1389 s.s = n_UNCONST(name);
1390 s.l = UIZ_MAX;
1391 msgp = n_shexp_quote(msgp, &s, FAL0);
1392 goto jleave;
1395 if(options & OPT_D_V){
1396 if(avc.avc_map == NULL){
1397 msgp = n_string_push_c(msgp, '#');
1398 msgp = n_string_push_cp(msgp, "assembled");
1399 i = 1;
1401 /* C99 */{
1402 struct{
1403 ui16_t flags;
1404 char msg[22];
1405 } const tbase[] = {
1406 {a_AMV_VF_VIRT, "virtual"},
1407 {a_AMV_VF_RDONLY, "readonly"},
1408 {a_AMV_VF_NODEL, "nodelete"},
1409 {a_AMV_VF_NOTEMPTY, "notempty"},
1410 {a_AMV_VF_NOCNTRLS, "no-control-chars"},
1411 {a_AMV_VF_NUM, "number"},
1412 {a_AMV_VF_POSNUM, "positive-number"},
1413 {a_AMV_VF_IMPORT, "import-environ-first\0"},
1414 {a_AMV_VF_ENV, "sync-environ"},
1415 {a_AMV_VF_I3VAL, "initial-value"},
1416 {a_AMV_VF_DEFVAL, "default-value"},
1417 {a_AMV_VF_LINKED, "`environ' link"}
1418 }, *tp;
1420 for(tp = tbase; PTRCMP(tp, <, &tbase[n_NELEM(tbase)]); ++tp)
1421 if(avc.avc_var->av_flags & tp->flags){
1422 msgp = n_string_push_c(msgp, (i++ == 0 ? '#' : ','));
1423 msgp = n_string_push_cp(msgp, tp->msg);
1427 if(i > 0)
1428 msgp = n_string_push_cp(msgp, "\n ");
1431 if(avc.avc_var->av_flags & a_AMV_VF_RDONLY)
1432 msgp = n_string_push_cp(msgp, "# ");
1433 n_UNINIT(quote, NULL);
1434 if(!(avc.avc_var->av_flags & a_AMV_VF_BOOL)){
1435 quote = n_shexp_quote_cp(avc.avc_var->av_value, TRU1);
1436 if(strcmp(quote, avc.avc_var->av_value))
1437 msgp = n_string_push_cp(msgp, "wysh ");
1439 if(avc.avc_var->av_flags & a_AMV_VF_LINKED)
1440 msgp = n_string_push_cp(msgp, "environ ");
1441 msgp = n_string_push_cp(msgp, "set ");
1442 msgp = n_string_push_cp(msgp, name);
1443 if(!(avc.avc_var->av_flags & a_AMV_VF_BOOL)){
1444 msgp = n_string_push_c(msgp, '=');
1445 msgp = n_string_push_cp(msgp, quote);
1448 jleave:
1449 msgp = n_string_push_c(msgp, '\n');
1450 fputs(n_string_cp(msgp), fp);
1451 NYD2_ENTER;
1452 return (i > 0 ? 2 : 1);
1455 static bool_t
1456 a_amv_var_c_set(char **ap, bool_t issetenv){
1457 char *cp, *cp2, *varbuf, c;
1458 size_t errs;
1459 NYD2_ENTER;
1461 errs = 0;
1462 jouter:
1463 while((cp = *ap++) != NULL){
1464 /* Isolate key */
1465 cp2 = varbuf = salloc(strlen(cp) +1);
1467 for(; (c = *cp) != '=' && c != '\0'; ++cp){
1468 if(cntrlchar(c) || whitechar(c)){
1469 n_err(_("Variable name with control character ignored: %s\n"),
1470 ap[-1]);
1471 ++errs;
1472 goto jouter;
1474 *cp2++ = c;
1476 *cp2 = '\0';
1477 if(c == '\0')
1478 cp = n_UNCONST(n_empty);
1479 else
1480 ++cp;
1482 if(varbuf == cp2){
1483 n_err(_("Empty variable name ignored\n"));
1484 ++errs;
1485 }else{
1486 struct a_amv_var_carrier avc;
1487 bool_t isunset;
1489 if((isunset = (varbuf[0] == 'n' && varbuf[1] == 'o')))
1490 varbuf = &varbuf[2];
1492 a_amv_var_revlookup(&avc, varbuf);
1494 if(isunset)
1495 errs += !a_amv_var_clear(&avc, issetenv);
1496 else
1497 errs += !a_amv_var_set(&avc, cp, issetenv);
1500 NYD2_LEAVE;
1501 return (errs == 0);
1504 FL int
1505 c_define(void *v){
1506 int rv;
1507 char **args;
1508 NYD_ENTER;
1510 rv = 1;
1512 if((args = v)[0] == NULL){
1513 rv = (a_amv_mac_show(a_AMV_MF_NONE) == FAL0);
1514 goto jleave;
1517 if(args[1] == NULL || args[1][0] != '{' || args[1][1] != '\0' ||
1518 args[2] != NULL){
1519 n_err(_("Synopsis: define: <name> {\n"));
1520 goto jleave;
1523 rv = (a_amv_mac_def(args[0], a_AMV_MF_NONE) == FAL0);
1524 jleave:
1525 NYD_LEAVE;
1526 return rv;
1529 FL int
1530 c_undefine(void *v){
1531 int rv;
1532 char **args;
1533 NYD_ENTER;
1535 rv = 0;
1536 args = v;
1538 rv |= !a_amv_mac_undef(*args, a_AMV_MF_NONE);
1539 while(*++args != NULL);
1540 NYD_LEAVE;
1541 return rv;
1544 FL int
1545 c_call(void *v){
1546 struct a_amv_mac_call_args *amcap;
1547 char const *errs, *name;
1548 struct a_amv_mac *amp;
1549 char **args;
1550 int rv;
1551 NYD_ENTER;
1553 rv = 1;
1555 if((args = v)[0] == NULL || (args[1] != NULL && args[2] != NULL)){
1556 errs = _("Synopsis: call: <%s>\n");
1557 name = "name";
1558 goto jerr;
1561 if((amp = a_amv_mac_lookup(name = *args, NULL, a_AMV_MF_NONE)) == NULL){
1562 errs = _("Undefined macro `call'ed: %s\n");
1563 goto jerr;
1566 amcap = n_lofi_alloc(sizeof *amcap);
1567 memset(amcap, 0, sizeof *amcap);
1568 amcap->amca_name = name;
1569 amcap->amca_amp = amp;
1570 rv = (a_amv_mac_exec(amcap) == FAL0);
1571 jleave:
1572 NYD_LEAVE;
1573 return rv;
1574 jerr:
1575 n_err(errs, name);
1576 goto jleave;
1579 FL bool_t
1580 check_folder_hook(bool_t nmail){ /* TODO temporary, v15: drop */
1581 struct a_amv_mac_call_args *amcap;
1582 struct a_amv_mac *amp;
1583 size_t len;
1584 char *var, *cp;
1585 bool_t rv;
1586 NYD_ENTER;
1588 rv = TRU1;
1589 var = salloc(len = strlen(mailname) + sizeof("folder-hook-") -1 +1);
1591 /* First try the fully resolved path */
1592 snprintf(var, len, "folder-hook-%s", mailname);
1593 if((cp = vok_vlook(var)) != NULL)
1594 goto jmac;
1596 /* If we are under *folder*, try the usual +NAME syntax, too */
1597 if(displayname[0] == '+'){
1598 char *x;
1600 for(x = &mailname[len]; x != mailname; --x)
1601 if(x[-1] == '/'){
1602 snprintf(var, len, "folder-hook-+%s", x);
1603 if((cp = vok_vlook(var)) != NULL)
1604 goto jmac;
1605 break;
1609 /* Plain *folder-hook* is our last try */
1610 if((cp = ok_vlook(folder_hook)) == NULL)
1611 goto jleave;
1613 jmac:
1614 if((amp = a_amv_mac_lookup(cp, NULL, a_AMV_MF_NONE)) == NULL){
1615 n_err(_("Cannot call *folder-hook* for %s: macro does not exist: %s\n"),
1616 n_shexp_quote_cp(displayname, FAL0), cp);
1617 rv = FAL0;
1618 goto jleave;
1621 amcap = n_lofi_alloc(sizeof *amcap);
1622 memset(amcap, 0, sizeof *amcap);
1623 amcap->amca_name = cp;
1624 amcap->amca_amp = amp;
1625 pstate &= ~PS_HOOK_MASK;
1626 if(nmail){
1627 amcap->amca_unroller = NULL;
1628 pstate |= PS_HOOK_NEWMAIL;
1629 }else{
1630 amcap->amca_unroller = &a_amv_folder_hook_lopts;
1631 pstate |= PS_HOOK;
1633 amcap->amca_lopts_on = TRU1;
1634 amcap->amca_ps_hook_mask = TRU1;
1635 rv = a_amv_mac_exec(amcap);
1636 pstate &= ~PS_HOOK_MASK;
1638 jleave:
1639 NYD_LEAVE;
1640 return rv;
1643 FL int
1644 c_account(void *v){
1645 struct a_amv_mac_call_args *amcap;
1646 struct a_amv_mac *amp;
1647 char **args;
1648 int rv, i, oqf, nqf;
1649 NYD_ENTER;
1651 rv = 1;
1653 if((args = v)[0] == NULL){
1654 rv = (a_amv_mac_show(a_AMV_MF_ACC) == FAL0);
1655 goto jleave;
1658 if(args[1] && args[1][0] == '{' && args[1][1] == '\0'){
1659 if(args[2] != NULL){
1660 n_err(_("Synopsis: account: <name> {\n"));
1661 goto jleave;
1663 if(!asccasecmp(args[0], ACCOUNT_NULL)){
1664 n_err(_("`account': cannot use reserved name: %s\n"),
1665 ACCOUNT_NULL);
1666 goto jleave;
1668 rv = (a_amv_mac_def(args[0], a_AMV_MF_ACC) == FAL0);
1669 goto jleave;
1672 if(pstate & PS_HOOK_MASK){
1673 n_err(_("`account': can't change account from within a hook\n"));
1674 goto jleave;
1677 save_mbox_for_possible_quitstuff();
1679 amp = NULL;
1680 if(asccasecmp(args[0], ACCOUNT_NULL) != 0 &&
1681 (amp = a_amv_mac_lookup(args[0], NULL, a_AMV_MF_ACC)) == NULL) {
1682 n_err(_("`account': account does not exist: %s\n"), args[0]);
1683 goto jleave;
1686 oqf = savequitflags();
1688 if(a_amv_acc_curr != NULL){
1689 if(a_amv_acc_curr->am_lopts != NULL)
1690 a_amv_lopts_unroll(&a_amv_acc_curr->am_lopts);
1691 if(a_amv_acc_curr->am_flags & a_AMV_MF_DEL)
1692 a_amv_mac_free(a_amv_acc_curr);
1695 account_name = (amp != NULL) ? amp->am_name : NULL;
1696 a_amv_acc_curr = amp;
1698 if(amp != NULL){
1699 bool_t ok;
1700 assert(amp->am_lopts == NULL);
1701 amcap = n_lofi_alloc(sizeof *amcap);
1702 memset(amcap, 0, sizeof *amcap);
1703 amcap->amca_name = amp->am_name;
1704 amcap->amca_amp = amp;
1705 amcap->amca_unroller = &amp->am_lopts;
1706 amcap->amca_lopts_on = TRU1;
1707 ok = a_amv_mac_exec(amcap);
1708 if(!ok){
1709 /* XXX account switch incomplete, unroll? */
1710 n_err(_("`account': failed to switch to account: %s\n"), amp->am_name);
1711 goto jleave;
1715 /* C99 */{
1716 bool_t reset = !(pstate & PS_ROOT);
1718 pstate |= PS_ROOT;
1719 if(amp != NULL)
1720 ok_vset(_account_name, amp->am_name);
1721 else
1722 ok_vclear(_account_name);
1723 if(reset)
1724 pstate &= ~PS_ROOT;
1727 if((pstate & (PS_STARTED | PS_HOOK_MASK)) == PS_STARTED){
1728 nqf = savequitflags(); /* TODO obsolete (leave -> void -> new box!) */
1729 restorequitflags(oqf);
1730 if((i = setfile("%", 0)) < 0)
1731 goto jleave;
1732 check_folder_hook(FAL0);
1733 if(i > 0 && !ok_blook(emptystart))
1734 goto jleave;
1735 announce(ok_blook(bsdcompat) || ok_blook(bsdannounce));
1736 restorequitflags(nqf);
1738 rv = 0;
1739 jleave:
1740 NYD_LEAVE;
1741 return rv;
1744 FL int
1745 c_unaccount(void *v){
1746 int rv;
1747 char **args;
1748 NYD_ENTER;
1750 rv = 0;
1751 args = v;
1753 rv |= !a_amv_mac_undef(*args, a_AMV_MF_ACC);
1754 while(*++args != NULL);
1755 NYD_LEAVE;
1756 return rv;
1759 FL int
1760 c_localopts(void *v){
1761 char **argv;
1762 int rv;
1763 NYD_ENTER;
1765 rv = 1;
1767 if(a_amv_lopts == NULL){
1768 n_err(_("Cannot use `localopts' in this context "
1769 "(not in `define' or `account', nor special hook)\n"));
1770 goto jleave;
1773 a_amv_lopts->as_unroll = (boolify(*(argv = v), UIZ_MAX, FAL0) > 0);
1774 rv = 0;
1775 jleave:
1776 NYD_LEAVE;
1777 return rv;
1780 FL void
1781 temporary_call_compose_mode_hook(char const *macname,
1782 void (*hook_pre)(void *), void *hook_arg){
1783 /* TODO call_compose_mode_hook() temporary, v15: generalize; see a_LEX_SLICE
1784 * TODO comment in lex_input.c for the right way of doing things! */
1785 static struct a_amv_lostack *cmh_losp;
1786 struct a_amv_mac_call_args *amcap;
1787 struct a_amv_mac *amp;
1788 NYD_ENTER;
1790 amp = NULL;
1792 if(macname == (char*)-1){
1793 a_amv_mac__finalize(cmh_losp);
1794 cmh_losp = NULL;
1795 }else if(macname != NULL &&
1796 (amp = a_amv_mac_lookup(macname, NULL, a_AMV_MF_NONE)) == NULL)
1797 n_err(_("Cannot call *on-compose-**: macro does not exist: %s\n"),
1798 macname);
1799 else{
1800 amcap = n_lofi_alloc(sizeof *amcap);
1801 memset(amcap, 0, sizeof *amcap);
1802 amcap->amca_name = (macname != NULL) ? macname : "on-compose-done-shell";
1803 amcap->amca_amp = amp;
1804 amcap->amca_unroller = &a_amv_compose_lopts;
1805 amcap->amca_hook_pre = hook_pre;
1806 amcap->amca_hook_arg = hook_arg;
1807 amcap->amca_lopts_on = TRU1;
1808 amcap->amca_ps_hook_mask = TRU1;
1809 pstate &= ~PS_HOOK_MASK;
1810 pstate |= PS_HOOK;
1811 if(macname != NULL)
1812 a_amv_mac_exec(amcap);
1813 else{
1814 cmh_losp = n_lofi_alloc(sizeof *cmh_losp);
1815 cmh_losp->as_global_saved = a_amv_lopts;
1816 cmh_losp->as_up = NULL;
1817 cmh_losp->as_lopts = *(cmh_losp->as_amcap = amcap)->amca_unroller;
1818 cmh_losp->as_unroll = TRU1;
1819 a_amv_lopts = cmh_losp;
1822 NYD_LEAVE;
1825 FL void
1826 temporary_unroll_compose_mode(void){ /* XXX intermediate hack */
1827 NYD_ENTER;
1828 if(a_amv_compose_lopts != NULL){
1829 void *save = a_amv_lopts;
1831 a_amv_lopts = NULL;
1832 a_amv_lopts_unroll(&a_amv_compose_lopts);
1833 a_amv_compose_lopts = NULL;
1834 a_amv_lopts = save;
1836 NYD_LEAVE;
1839 FL void
1840 temporary_localopts_folder_hook_unroll(void){ /* XXX intermediate hack */
1841 NYD_ENTER;
1842 if(a_amv_folder_hook_lopts != NULL){
1843 void *save = a_amv_lopts;
1845 a_amv_lopts = NULL;
1846 a_amv_lopts_unroll(&a_amv_folder_hook_lopts);
1847 a_amv_folder_hook_lopts = NULL;
1848 a_amv_lopts = save;
1850 NYD_LEAVE;
1853 FL char *
1854 n_var_oklook(enum okeys okey){
1855 struct a_amv_var_carrier avc;
1856 char *rv;
1857 struct a_amv_var_map const *avmp;
1858 NYD_ENTER;
1860 avc.avc_map = avmp = &a_amv_var_map[okey];
1861 avc.avc_name = a_amv_var_names + avmp->avm_keyoff;
1862 avc.avc_hash = avmp->avm_hash;
1863 avc.avc_okey = okey;
1865 if(a_amv_var_lookup(&avc, FAL0))
1866 rv = avc.avc_var->av_value;
1867 else
1868 rv = NULL;
1869 NYD_LEAVE;
1870 return rv;
1873 FL bool_t
1874 n_var_okset(enum okeys okey, uintptr_t val){
1875 struct a_amv_var_carrier avc;
1876 bool_t ok;
1877 struct a_amv_var_map const *avmp;
1878 NYD_ENTER;
1880 avc.avc_map = avmp = &a_amv_var_map[okey];
1881 avc.avc_name = a_amv_var_names + avmp->avm_keyoff;
1882 avc.avc_hash = avmp->avm_hash;
1883 avc.avc_okey = okey;
1885 ok = a_amv_var_set(&avc, (val == 0x1 ? n_empty : (char const*)val), FAL0);
1886 NYD_LEAVE;
1887 return ok;
1890 FL bool_t
1891 n_var_okclear(enum okeys okey){
1892 struct a_amv_var_carrier avc;
1893 bool_t rv;
1894 struct a_amv_var_map const *avmp;
1895 NYD_ENTER;
1897 avc.avc_map = avmp = &a_amv_var_map[okey];
1898 avc.avc_name = a_amv_var_names + avmp->avm_keyoff;
1899 avc.avc_hash = avmp->avm_hash;
1900 avc.avc_okey = okey;
1902 rv = a_amv_var_clear(&avc, FAL0);
1903 NYD_LEAVE;
1904 return rv;
1907 FL char *
1908 n_var_voklook(char const *vokey){
1909 struct a_amv_var_carrier avc;
1910 char *rv;
1911 NYD_ENTER;
1913 a_amv_var_revlookup(&avc, vokey);
1915 rv = a_amv_var_lookup(&avc, FAL0) ? avc.avc_var->av_value : NULL;
1916 NYD_LEAVE;
1917 return rv;
1920 FL bool_t
1921 n_var_vokset(char const *vokey, uintptr_t val){
1922 struct a_amv_var_carrier avc;
1923 bool_t ok;
1924 NYD_ENTER;
1926 a_amv_var_revlookup(&avc, vokey);
1928 ok = a_amv_var_set(&avc, (val == 0x1 ? n_empty : (char const*)val), FAL0);
1929 NYD_LEAVE;
1930 return ok;
1933 FL bool_t
1934 n_var_vokclear(char const *vokey){
1935 struct a_amv_var_carrier avc;
1936 bool_t ok;
1937 NYD_ENTER;
1939 a_amv_var_revlookup(&avc, vokey);
1941 ok = a_amv_var_clear(&avc, FAL0);
1942 NYD_LEAVE;
1943 return ok;
1946 #ifdef HAVE_SOCKETS
1947 FL char *
1948 n_var_xoklook(enum okeys okey, struct url const *urlp,
1949 enum okey_xlook_mode oxm){
1950 struct a_amv_var_carrier avc;
1951 struct str const *us;
1952 size_t nlen;
1953 char *nbuf, *rv;
1954 struct a_amv_var_map const *avmp;
1955 NYD_ENTER;
1957 assert(oxm & (OXM_PLAIN | OXM_H_P | OXM_U_H_P));
1959 /* For simplicity: allow this case too */
1960 if(!(oxm & (OXM_H_P | OXM_U_H_P))){
1961 nbuf = NULL;
1962 goto jplain;
1965 avc.avc_map = avmp = &a_amv_var_map[okey];
1966 avc.avc_name = a_amv_var_names + avmp->avm_keyoff;
1967 avc.avc_okey = okey;
1969 us = (oxm & OXM_U_H_P) ? &urlp->url_u_h_p : &urlp->url_h_p;
1970 nlen = strlen(avc.avc_name);
1971 nbuf = n_lofi_alloc(nlen + 1 + us->l +1);
1972 memcpy(nbuf, avc.avc_name, nlen);
1973 nbuf[nlen++] = '-';
1975 /* One of .url_u_h_p and .url_h_p we test in here */
1976 memcpy(nbuf + nlen, us->s, us->l +1);
1977 avc.avc_name = a_amv_var_canonify(nbuf);
1978 avc.avc_hash = a_AMV_NAME2HASH(avc.avc_name);
1979 if(a_amv_var_lookup(&avc, FAL0))
1980 goto jvar;
1982 /* The second */
1983 if(oxm & OXM_H_P){
1984 us = &urlp->url_h_p;
1985 memcpy(nbuf + nlen, us->s, us->l +1);
1986 avc.avc_name = a_amv_var_canonify(nbuf);
1987 avc.avc_hash = a_AMV_NAME2HASH(avc.avc_name);
1988 if(a_amv_var_lookup(&avc, FAL0)){
1989 jvar:
1990 rv = avc.avc_var->av_value;
1991 goto jleave;
1995 jplain:
1996 rv = (oxm & OXM_PLAIN) ? n_var_oklook(okey) : NULL;
1997 jleave:
1998 if(nbuf != NULL)
1999 n_lofi_free(nbuf);
2000 NYD_LEAVE;
2001 return rv;
2003 #endif /* HAVE_SOCKETS */
2005 FL int
2006 c_set(void *v){
2007 char **ap;
2008 int err;
2009 NYD_ENTER;
2011 if(*(ap = v) == NULL){
2012 a_amv_var_show_all();
2013 err = 0;
2014 }else
2015 err = !a_amv_var_c_set(ap, FAL0);
2016 NYD_LEAVE;
2017 return err;
2020 FL int
2021 c_unset(void *v){
2022 char **ap;
2023 int err;
2024 NYD_ENTER;
2026 for(err = 0, ap = v; *ap != NULL; ++ap)
2027 err |= !n_var_vokclear(*ap);
2028 NYD_LEAVE;
2029 return err;
2032 FL int
2033 c_varshow(void *v){
2034 char **ap;
2035 NYD_ENTER;
2037 if(*(ap = v) == NULL)
2038 v = NULL;
2039 else{
2040 struct n_string msg, *msgp = &msg;
2042 msgp = n_string_creat(msgp);
2043 for(; *ap != NULL; ++ap)
2044 a_amv_var_show(*ap, stdout, msgp);
2045 n_string_gut(msgp);
2047 NYD_LEAVE;
2048 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
2051 FL int
2052 c_varedit(void *v){
2053 struct a_amv_var_carrier avc;
2054 FILE *of, *nf;
2055 char *val, **argv;
2056 int err;
2057 sighandler_type sigint;
2058 NYD_ENTER;
2060 sigint = safe_signal(SIGINT, SIG_IGN);
2062 for(err = 0, argv = v; *argv != NULL; ++argv){
2063 memset(&avc, 0, sizeof avc);
2065 a_amv_var_revlookup(&avc, *argv);
2067 if(avc.avc_map != NULL){
2068 if(avc.avc_map->avm_flags & a_AMV_VF_BOOL){
2069 n_err(_("`varedit': cannot edit boolean variable: %s\n"),
2070 avc.avc_name);
2071 continue;
2073 if(avc.avc_map->avm_flags & a_AMV_VF_RDONLY){
2074 n_err(_("`varedit': cannot edit readonly variable: %s\n"),
2075 avc.avc_name);
2076 continue;
2080 a_amv_var_lookup(&avc, FAL0);
2082 if((of = Ftmp(NULL, "varedit", OF_RDWR | OF_UNLINK | OF_REGISTER)) ==
2083 NULL){
2084 n_perr(_("`varedit': can't create temporary file, bailing out"), 0);
2085 err = 1;
2086 break;
2087 }else if(avc.avc_var != NULL && *(val = avc.avc_var->av_value) != '\0' &&
2088 sizeof *val != fwrite(val, strlen(val), sizeof *val, of)){
2089 n_perr(_("`varedit' failed to write old value to temporary file"), 0);
2090 Fclose(of);
2091 err = 1;
2092 continue;
2095 fflush_rewind(of);
2096 nf = run_editor(of, (off_t)-1, 'e', FAL0, NULL, NULL, SEND_MBOX, sigint);
2097 Fclose(of);
2099 if(nf != NULL){
2100 int c;
2101 char *base;
2102 off_t l;
2104 l = fsize(nf);
2105 assert(l >= 0);
2106 base = salloc((size_t)l +1);
2108 for(l = 0, val = base; (c = getc(nf)) != EOF; ++val)
2109 if(c == '\n' || c == '\r'){
2110 *val = ' ';
2111 ++l;
2112 }else{
2113 *val = (char)(uc_i)c;
2114 l = 0;
2116 val -= l;
2117 *val = '\0';
2119 if(!a_amv_var_set(&avc, base, FAL0))
2120 err = 1;
2122 Fclose(nf);
2123 }else{
2124 n_err(_("`varedit': can't start $EDITOR, bailing out\n"));
2125 err = 1;
2126 break;
2130 safe_signal(SIGINT, sigint);
2131 NYD_LEAVE;
2132 return err;
2135 FL int
2136 c_environ(void *v){
2137 struct a_amv_var_carrier avc;
2138 int err;
2139 char **ap;
2140 bool_t islnk;
2141 NYD_ENTER;
2143 if((islnk = !strcmp(*(ap = v), "link")) || !strcmp(*ap, "unlink")){
2144 for(err = 0; *++ap != NULL;){
2145 a_amv_var_revlookup(&avc, *ap);
2147 if(a_amv_var_lookup(&avc, FAL0) && (islnk ||
2148 (avc.avc_var->av_flags & a_AMV_VF_LINKED))){
2149 if(!islnk){
2150 avc.avc_var->av_flags &= ~a_AMV_VF_LINKED;
2151 continue;
2152 }else if(avc.avc_var->av_flags & (a_AMV_VF_ENV | a_AMV_VF_LINKED)){
2153 if(options & OPT_D_V)
2154 n_err(_("`environ': link: already established: %s\n"), *ap);
2155 continue;
2157 avc.avc_var->av_flags |= a_AMV_VF_LINKED;
2158 if(!(avc.avc_var->av_flags & a_AMV_VF_ENV))
2159 a_amv_var__putenv(&avc, avc.avc_var);
2160 }else if(!islnk){
2161 n_err(_("`environ': unlink: no link established: %s\n"), *ap);
2162 err = 1;
2163 }else{
2164 char const *evp = getenv(*ap);
2166 if(evp != NULL)
2167 err |= !a_amv_var_set(&avc, evp, TRU1);
2168 else{
2169 n_err(_("`environ': link: cannot link to non-existent: %s\n"),
2170 *ap);
2171 err = 1;
2175 }else if(!strcmp(*ap, "set"))
2176 err = !a_amv_var_c_set(++ap, TRU1);
2177 else if(!strcmp(*ap, "unset")){
2178 for(err = 0; *++ap != NULL;){
2179 a_amv_var_revlookup(&avc, *ap);
2181 if(!a_amv_var_clear(&avc, TRU1))
2182 err = 1;
2184 }else{
2185 n_err(_("Synopsis: environ: <link|set|unset> <variable>...\n"));
2186 err = 1;
2188 NYD_LEAVE;
2189 return err;
2192 /* s-it-mode */