using new string modifiers in Jambase
[k8jam.git] / src / compile.c
blobabb9a981dc703bb8098a5940a42672b29406da1d
1 /*
2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
6 /*
7 * compile.c - compile parsed jam statements
9 * External routines:
11 * compile_append() - append list results of two statements
12 * compile_break() - compile 'break/continue/return' rule
13 * compile_eval() - evaluate if to determine which leg to compile
14 * compile_foreach() - compile the "for x in y" statement
15 * compile_if() - compile 'if' rule
16 * compile_include() - support for 'include' - call include() on file
17 * compile_list() - expand and return a list
18 * compile_local() - declare (and set) local variables
19 * compile_null() - do nothing -- a stub for parsing
20 * compile_on() - run rule under influence of on-target variables
21 * compile_rule() - compile a single user defined rule
22 * compile_rules() - compile a chain of rules
23 * compile_set() - compile the "set variable" statement
24 * compile_setcomp() - support for `rule` - save parse tree
25 * compile_setexec() - support for `actions` - save execution string
26 * compile_settings() - compile the "on =" (set variable on exec) statement
27 * compile_switch() - compile 'switch' rule
29 * Internal routines:
31 * debug_compile() - printf with indent to show rule expansion.
32 * evaluate_rule() - execute a rule invocation
34 * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of
35 * the awkward sounding "settings".
36 * 04/12/94 (seiwald) - Combined build_depends() with build_includes().
37 * 04/12/94 (seiwald) - actionlist() now just appends a single action.
38 * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
39 * 05/13/94 (seiwald) - include files are now bound as targets, and thus
40 * can make use of $(SEARCH)
41 * 06/01/94 (seiwald) - new 'actions existing' does existing sources
42 * 08/23/94 (seiwald) - Support for '+=' (append to variable)
43 * 12/20/94 (seiwald) - NOTIME renamed NOTFILE.
44 * 01/22/95 (seiwald) - Exit rule.
45 * 02/02/95 (seiwald) - Always rule; LEAVES rule.
46 * 02/14/95 (seiwald) - NoUpdate rule.
47 * 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
48 * 09/07/00 (seiwald) - stop crashing when a rule redefines itself
49 * 09/11/00 (seiwald) - new evaluate_rule() for headers().
50 * 09/11/00 (seiwald) - rules now return values, accessed via [ rule arg ... ]
51 * 09/12/00 (seiwald) - don't complain about rules invoked without targets
52 * 01/13/01 (seiwald) - fix case where rule is defined within another
53 * 01/10/01 (seiwald) - built-ins split out to builtin.c.
54 * 01/11/01 (seiwald) - optimize compile_rules() for tail recursion
55 * 01/21/01 (seiwald) - replace evaluate_if() with compile_eval()
56 * 01/24/01 (seiwald) - 'while' statement
57 * 03/23/01 (seiwald) - "[ on target rule ]" support
58 * 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
59 * 03/02/02 (seiwald) - rules can be invoked via variable names
60 * 03/12/02 (seiwald) - &&,&,||,|,in now short-circuit again
61 * 03/25/02 (seiwald) - if ( "" a b ) one again returns true
62 * 06/21/02 (seiwald) - support for named parameters
63 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
64 * 10/22/02 (seiwald) - working return/break/continue statements
65 * 11/04/02 (seiwald) - const-ing for string literals
66 * 11/18/02 (seiwald) - remove bogus search() in 'on' statement.
67 * 12/17/02 (seiwald) - new copysettings() to protect target-specific vars
69 #include "jam.h"
71 #include "lists.h"
72 #include "parse.h"
73 #include "compile.h"
74 #include "variable.h"
75 #include "expand.h"
76 #include "rules.h"
77 #include "newstr.h"
78 #include "search.h"
79 #include "matchglob.h"
80 #include "filesys.h"
83 static const char *set_names[] = { "=", "+=", "-=", "?=" };
84 static void debug_compile (int which, const char *s);
88 * compile_append() - append list results of two statements
90 * parse->left more compile_append() by left-recursion
91 * parse->right single rule
93 LIST *compile_append (PARSE *parse, LOL *args, int *jmp) {
94 /* Append right to left. */
95 return list_append(
96 (*parse->left->func)(parse->left, args, jmp),
97 (*parse->right->func)(parse->right, args, jmp)
103 * compile_break() - compile 'break/continue/return' rule
105 * parse->left results
106 * parse->num JMP_BREAK/CONTINUE/RETURN
108 LIST *compile_break (PARSE *parse, LOL *args, int *jmp) {
109 LIST *lv = (*parse->left->func)(parse->left, args, jmp);
110 *jmp = parse->num;
111 return lv;
116 * compile_eval() - evaluate if to determine which leg to compile
118 * Returns:
119 * list if expression true - compile 'then' clause
120 * L0 if expression false - compile 'else' clause
122 static JAMFA_PURE int lcmp (LIST *t, LIST *s) {
123 int status = 0;
124 while (!status && (t || s)) {
125 const char *st = (t ? t->string : "");
126 const char *ss = (s ? s->string : "");
127 status = strcmp(st, ss);
128 t = (t ? list_next(t) : t);
129 s = (s ? list_next(s) : s);
131 return status;
135 static int recmp (LIST *s, LIST *restr) {
136 int res;
138 printf("restr: <%s>\n", (restr ? restr->string : "(null)"));
139 printf("s : <%s>\n", (s ? s->string : "(null)"));
141 regexp_t *re = regexp_compile((restr ? restr->string : ""), 0);
142 res = regexp_execute(re, (s ? s->string : ""), NULL, 0);
143 regexp_free(re);
144 return (res > 0);
148 LIST *compile_eval (PARSE *parse, LOL *args, int *jmp) {
149 LIST *ll, *lr, *s, *t;
150 int status = 0;
151 /* short circuit lr eval for &&, ||, 'in' and 'any-in' */
152 ll = (*parse->left->func)(parse->left, args, jmp);
153 lr = 0;
154 switch (parse->num) {
155 case EXPR_AND:
156 case EXPR_IN:
157 case EXPR_ANYIN:
158 if (ll) goto eval;
159 break;
160 case EXPR_OR:
161 if (!ll) goto eval;
162 break;
163 default:
164 eval: lr = (*parse->right->func)(parse->right, args, jmp);
165 break;
167 /* now eval */
168 switch (parse->num) {
169 case EXPR_NOT: if (!ll) status = 1; break;
170 case EXPR_AND: if (ll && lr) status = 1; break;
171 case EXPR_OR: if (ll || lr) status = 1; break;
172 case EXPR_IN:
173 /* "a in b": make sure each of ll is equal to something in lr */
174 for (t = ll; t; t = list_next(t)) {
175 for (s = lr; s; s = list_next(s)) if (strcmp(t->string, s->string) == 0) break;
176 if (!s) break;
178 /* No more ll? success */
179 if (!t) status = 1;
180 break;
181 case EXPR_ANYIN:
182 /* "a any-in b": does b contains at least one item from a? */
183 for (t = ll; t && !status; t = list_next(t)) {
184 for (s = lr; s; s = list_next(s)) if (strcmp(t->string, s->string) == 0) { status = 1; break; } /* success */
186 break;
187 case EXPR_EXISTS: if (lcmp(ll, L0) != 0) status = 1; break;
188 case EXPR_EQUALS: if (lcmp(ll, lr) == 0) status = 1; break;
189 case EXPR_NOTEQ: if (lcmp(ll, lr) != 0) status = 1; break;
190 case EXPR_LESS: if (lcmp(ll, lr) < 0) status = 1; break;
191 case EXPR_LESSEQ: if (lcmp(ll, lr) <= 0) status = 1; break;
192 case EXPR_MORE: if (lcmp(ll, lr) > 0) status = 1; break;
193 case EXPR_MOREEQ: if (lcmp(ll, lr) >= 0) status = 1; break;
194 case EXPR_REXPEQ: if (recmp(ll, lr)) status = 1; break;
196 if (DEBUG_IF) {
197 debug_compile(0, "if");
198 list_print(ll);
199 printf("(%d) ", status);
200 list_print_ex(stdout, lr, LPFLAG_NO_TRSPACE);
201 printf("\n");
203 /* find something to return */
204 /* in odd circumstances (like "" = "") we'll have to return a new string */
205 if (!status) t = NULL;
206 else if (ll) { t = ll; ll = NULL; }
207 else if (lr) { t = lr; lr = NULL; }
208 else t = list_new(L0, "1", 0);
209 if (ll) list_free(ll);
210 if (lr) list_free(lr);
211 return t;
216 * compile_foreach() - compile the "for x in y" statement
218 * Compile_foreach() resets the given variable name to each specified
219 * value, executing the commands enclosed in braces for each iteration.
221 * parse->string index variable
222 * parse->left variable values
223 * parse->right rule to compile
225 LIST *compile_foreach (PARSE *p, LOL *args, int *jmp) {
226 LIST *nv = (*p->left->func)(p->left, args, jmp);
227 LIST *result = NULL;
228 SETTINGS *s = NULL;
229 if (p->num) {
230 /* have 'local' */
231 s = addsettings(s, VAR_SET, p->string, L0);
232 pushsettings(s);
234 /* for each value for var */
235 for (LIST *l = nv; l && *jmp == JMP_NONE; l = list_next(l)) {
236 /* reset $(p->string) for each val */
237 var_set(p->string, list_new(L0, l->string, 1), VAR_SET);
238 /* keep only last result */
239 list_free(result);
240 result = (*p->right->func)(p->right, args, jmp);
241 /* continue loop? */
242 if (*jmp == JMP_CONTINUE) *jmp = JMP_NONE;
244 /* here by break/continue? */
245 if (*jmp == JMP_BREAK || *jmp == JMP_CONTINUE) *jmp = JMP_NONE;
246 if (p->num) {
247 /* restore locals */
248 popsettings(s);
249 freesettings(s);
251 list_free(nv);
252 /* returns result of last loop */
253 return result;
258 * compile_if() - compile 'if' rule
260 * parse->left condition tree
261 * parse->right then tree
262 * parse->third else tree
264 LIST *compile_if (PARSE *p, LOL *args, int *jmp) {
265 LIST *l = (*p->left->func)(p->left, args, jmp);
266 p = (l ? p->right : p->third);
267 list_free(l);
268 return (*p->func)(p, args, jmp);
273 * compile_include() - support for 'include' - call include() on file
275 * parse->left list of files to include (can only do 1)
276 * parse->num !0: softinclude
278 LIST *compile_include (PARSE *parse, LOL *args, int *jmp) {
279 int softinc = parse->num;
280 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
281 if (DEBUG_COMPILE) {
282 debug_compile(0, (softinc ? "softinclude" : "include"));
283 list_print_ex(stdout, nt, LPFLAG_NO_TRSPACE);
284 printf("\n");
286 if (nt) {
287 TARGET *t = bindtarget(nt->string);
288 /* bind the include file under the influence of "on-target" variables */
289 /* though they are targets, include files are not built with make() */
290 /* needn't copysettings(), as search sets no vars */
291 pushsettings(t->settings);
292 t->boundname = search(t->name, &t->time);
293 popsettings(t->settings);
294 /* don't parse missing file if NOCARE set */
295 if (t->time || !(t->flags&T_FLAG_NOCARE)) {
296 int doit = 1;
297 if (file_type(t->boundname) != 0 || access(t->boundname, R_OK) != 0) {
298 if (!softinc) {
299 printf("Failed to include file '%s'\n", t->boundname);
300 exit(EXITBAD);
302 doit = 0;
304 if (doit) parse_file(t->boundname);
307 list_free(nt);
308 return L0;
313 * compile_list() - expand and return a list
315 * parse->string - character string to expand
316 * parse->num - "don't expand" flag
318 LIST *compile_list (PARSE *parse, LOL *args, int *jmp) {
319 /* voodoo 1 means: parse->string is a copyable string */
320 return (parse->num ? list_new(L0, parse->string, 1) : var_expand(parse->string, NULL, args, 1));
325 * compile_local() - declare (and set) local variables
327 * parse->left list of variables
328 * parse->right list of values
329 * parse->third rules to execute
331 LIST *compile_local (PARSE *parse, LOL *args, int *jmp) {
332 LIST *l;
333 SETTINGS *s = NULL;
334 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
335 LIST *ns = (*parse->right->func)(parse->right, args, jmp);
336 LIST *result;
337 if (DEBUG_COMPILE) {
338 debug_compile(0, "local");
339 list_print(nt);
340 printf("= ");
341 list_print_ex(stdout, ns, LPFLAG_NO_TRSPACE);
342 printf("\n");
344 /* initial value is ns */
345 for (l = nt; l; l = list_next(l)) s = addsettings(s, 0, l->string, list_copy((LIST *)0, ns));
346 list_free(ns);
347 list_free(nt);
348 /* note that callees of the current context get this "local" */
349 /* variable, making it not so much local as layered */
350 pushsettings(s);
351 result = (*parse->third->func)(parse->third, args, jmp);
352 popsettings(s);
353 freesettings(s);
354 return result;
359 * compile_null() - do nothing -- a stub for parsing
361 JAMFA_CONST LIST *compile_null (PARSE *parse, LOL *args, int *jmp) {
362 return L0;
367 * compile_on() - run rule under influence of on-target variables
369 * parse->left target list; only first used
370 * parse->right rule to run
372 LIST *compile_on (PARSE *parse, LOL *args, int *jmp) {
373 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
374 LIST *result = NULL;
375 if (DEBUG_COMPILE) {
376 debug_compile(0, "on");
377 list_print_ex(stdout, nt, LPFLAG_NO_TRSPACE);
378 printf("\n");
380 /* copy settings, so that 'on target var on target = val' doesn't set var globally */
381 if (nt) {
382 TARGET *t = bindtarget(nt->string);
383 SETTINGS *s = copysettings(t->settings);
384 pushsettings(s);
385 result = (*parse->right->func)(parse->right, args, jmp);
386 popsettings(s);
387 freesettings(s);
389 list_free(nt);
390 return result;
395 * compile_rule() - compile a single user defined rule
397 * parse->left list of rules to run
398 * parse->right parameters (list of lists) to rule, recursing left
400 * Wrapped around evaluate_rule() so that headers() can share it.
402 LIST *compile_rule (PARSE *parse, LOL *args, int *jmp) {
403 LOL nargs[1];
404 LIST *result = NULL;
405 LIST *ll, *l;
406 PARSE *p;
407 /* list of rules to run -- normally 1! */
408 ll = (*parse->left->func)(parse->left, args, jmp);
409 /* build up the list of arg lists */
410 lol_init(nargs);
411 for (p = parse->right; p; p = p->left) lol_add(nargs, (*p->right->func)(p->right, args, jmp));
412 /* run rules, appending results from each */
413 for (l = ll; l; l = list_next(l)) result = evaluate_rule(l->string, nargs, result);
414 list_free(ll);
415 lol_free(nargs);
416 return result;
421 * evaluate_rule() - execute a rule invocation
423 LIST *evaluate_rule (const char *rulename, LOL *args, LIST *result) {
424 //#infdef OPT_EXPAND_RULE_NAMES_EXT
425 /*RULE *rule = bindrule(rulename);*/
426 //#else
427 RULE *rule;
428 char *expanded, *c;
429 dstring_t buf;
430 dstr_init(&buf);
431 if (var_string(rulename, &buf, args, ' ') < 0) {
432 dstr_done(&buf);
433 printf("Failed to expand rule %s -- expansion too long\n", rulename);
434 exit(EXITBAD);
436 expanded = dstr_cstr(&buf);
437 while (expanded[0] == ' ') ++expanded;
438 while ((c = strrchr(expanded, ' '))) *c = '\0';
439 if (DEBUG_COMPILE) {
440 debug_compile(1, rulename);
441 if (strcmp(rulename, expanded)) printf("-> %s ", expanded);
442 lol_print(args);
443 printf("\n");
445 rule = bindrule(expanded);
446 dstr_done(&buf);
447 //#endif
448 /* check traditional targets $(<) and sources $(>) */
449 if (!rule->actions && !rule->procedure) printf("warning: unknown rule %s\n", rule->name);
450 /* if this rule will be executed for updating the targets then construct the action for make() */
451 if (rule->actions) {
452 TARGETS *t;
453 ACTION *action;
454 /* the action is associated with this instance of this rule */
455 action = (ACTION *)malloc(sizeof(ACTION));
456 memset((char *)action, '\0', sizeof(*action));
457 action->rule = rule;
458 action->targets = targetlist((TARGETS *)0, lol_get(args, 0));
459 action->sources = targetlist((TARGETS *)0, lol_get(args, 1));
460 /* append this action to the actions of each target */
461 for (t = action->targets; t; t = t->next) t->target->actions = actionlist(t->target->actions, action);
463 /* now recursively compile any parse tree associated with this rule */
464 if (rule->procedure) {
465 PARSE *parse = rule->procedure;
466 SETTINGS *s = 0;
467 int jmp = JMP_NONE;
468 LIST *l;
469 int i;
470 /* build parameters as local vars */
471 for (l = rule->params, i = 0; l; l = l->next, i++) s = addsettings(s, 0, l->string, list_copy(L0, lol_get(args, i)));
472 /* run rule */
473 /* bring in local params */
474 /* refer/free to ensure rule not freed during use */
475 parse_refer(parse);
476 pushsettings(s);
477 result = list_append(result, (*parse->func)(parse, args, &jmp));
478 popsettings(s);
479 freesettings(s);
480 parse_free(parse);
482 if (DEBUG_COMPILE) debug_compile(-1, 0);
483 return result;
488 * compile_rules() - compile a chain of rules
490 * parse->left single rule
491 * parse->right more compile_rules() by right-recursion
493 LIST *compile_rules (PARSE *parse, LOL *args, int *jmp) {
494 /* ignore result from first statement; return the 2nd */
495 /* optimize recursion on the right by looping */
496 LIST *result = NULL;
497 while (*jmp == JMP_NONE && parse->func == compile_rules) {
498 list_free(result);
499 result = (*parse->left->func)(parse->left, args, jmp);
500 parse = parse->right;
502 if (*jmp == JMP_NONE) {
503 list_free(result);
504 result = (*parse->func)(parse, args, jmp);
506 return result;
511 * compile_set() - compile the "set variable" statement
513 * parse->left variable names
514 * parse->right variable values
515 * parse->num VAR_SET/APPEND/DEFAULT
517 LIST *compile_set (PARSE *parse, LOL *args, int *jmp) {
518 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
519 LIST *ns = (*parse->right->func)(parse->right, args, jmp);
520 LIST *l;
521 if (DEBUG_COMPILE) {
522 debug_compile(0, "set");
523 list_print(nt);
524 printf("%s ", set_names[parse->num]);
525 list_print_ex(stdout, ns, LPFLAG_NO_TRSPACE);
526 printf("\n");
528 /* call var_set to set variable */
529 /* var_set keeps ns, so need to copy it */
530 for (l = nt; l; l = list_next(l)) var_set(l->string, list_copy(L0, ns), parse->num);
531 list_free(nt);
532 return ns;
537 * compile_setcomp() - support for `rule` - save parse tree
539 * parse->string rule name
540 * parse->left list of argument names
541 * parse->right rules for rule
543 LIST *compile_setcomp (PARSE *parse, LOL *args, int *jmp) {
544 RULE *rule = bindrule(parse->string);
545 LIST *params = 0;
546 PARSE *p;
547 /* build param list */
548 for (p = parse->left; p; p = p->left) params = list_new(params, p->string, 1);
549 if (DEBUG_COMPILE) {
550 debug_compile(0, "rule");
551 printf("%s ", parse->string);
552 list_print_ex(stdout, params, LPFLAG_NO_TRSPACE);
553 printf("\n");
555 /* free old one, if present */
556 if (rule->procedure) parse_free(rule->procedure);
557 if (rule->params) list_free(rule->params);
558 rule->procedure = parse->right;
559 rule->params = params;
560 /* we now own this parse tree */
561 /* don't let parse_free() release it */
562 parse_refer(parse->right);
563 return L0;
568 * compile_setexec() - support for `actions` - save execution string
570 * parse->string rule name
571 * parse->string1 OS command string
572 * parse->num flags
573 * parse->left `bind` variables
575 * Note that the parse flags (as defined in compile.h) are transfered
576 * directly to the rule flags (as defined in rules.h).
578 LIST *compile_setexec (PARSE *parse, LOL *args, int *jmp) {
579 RULE *rule = bindrule(parse->string);
580 LIST *bindlist = (*parse->left->func)(parse->left, args, jmp);
581 /* free old one, if present */
582 if (rule->actions) {
583 freestr(rule->actions);
584 list_free(rule->bindlist);
586 rule->actions = copystr(parse->string1);
587 rule->bindlist = bindlist;
588 rule->flags = parse->num;
589 return L0;
594 * compile_settings() - compile the "on =" (set variable on exec) statement
596 * parse->left variable names
597 * parse->right target name
598 * parse->third variable value
599 * parse->num VAR_SET/APPEND/DEFAULT
601 LIST *compile_settings (PARSE *parse, LOL *args, int *jmp) {
602 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
603 LIST *ns = (*parse->third->func)(parse->third, args, jmp);
604 LIST *targets = (*parse->right->func)(parse->right, args, jmp);
605 LIST *ts;
606 if (DEBUG_COMPILE) {
607 debug_compile(0, "set");
608 list_print(nt);
609 printf("on ");
610 list_print(targets);
611 printf("%s ", set_names[parse->num]);
612 list_print_ex(stdout, ns, LPFLAG_NO_TRSPACE);
613 printf("\n");
615 /* call addsettings to save variable setting addsettings keeps ns, so need to copy it */
616 /* pass append flag to addsettings() */
617 for (ts = targets; ts; ts = list_next(ts)) {
618 TARGET *t = bindtarget(ts->string);
619 for (LIST *l = nt; l; l = list_next(l)) {
620 t->settings = addsettings(t->settings, parse->num, l->string, list_copy((LIST *)0, ns));
623 list_free(nt);
624 list_free(targets);
625 return ns;
630 * compile_switch() - compile 'switch' rule
632 * parse->left switch value (only 1st used)
633 * parse->right cases
635 * cases->left 1st case
636 * cases->right next cases
638 * case->string argument to match
639 * case->left parse tree to execute
641 LIST *compile_switch (PARSE *parse, LOL *args, int *jmp) {
642 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
643 LIST *result = NULL;
644 if (DEBUG_COMPILE) {
645 debug_compile(0, "switch");
646 list_print_ex(stdout, nt, LPFLAG_NO_TRSPACE);
647 printf("\n");
649 /* step through cases */
650 for (parse = parse->right; parse; parse = parse->right) {
651 if (!matchglob(parse->left->string, nt?nt->string:"")) {
652 /* get and exec parse tree for this case */
653 parse = parse->left->left;
654 result = (*parse->func)(parse, args, jmp);
655 break;
658 list_free(nt);
659 return result;
664 * compile_while() - compile 'while' rule
666 * parse->left condition tree
667 * parse->right execution tree
669 LIST *compile_while (PARSE *p, LOL *args, int *jmp) {
670 LIST *result = NULL;
671 LIST *l;
672 /* returns the value from the last execution of the block */
673 while ((*jmp == JMP_NONE ) && (l = (*p->left->func)(p->left, args, jmp))) {
674 /* always toss while's expression */
675 list_free(l);
676 /* keep only last result */
677 list_free(result);
678 result = (*p->right->func)(p->right, args, jmp);
679 /* continue loop? */
680 if (*jmp == JMP_CONTINUE) *jmp = JMP_NONE;
682 /* here by break/continue? */
683 if (*jmp == JMP_BREAK || *jmp == JMP_CONTINUE) *jmp = JMP_NONE;
684 /* returns result of last loop */
685 return result;
690 * debug_compile() - printf with indent to show rule expansion.
692 static void debug_compile (int which, const char *s) {
693 static int level = 0;
694 static const char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|";
695 int i = ((1+level)*2)%35;
696 if (which >= 0) printf("%*.*s ", i, i, indent);
697 if (s) printf("%s ", s);
698 level += which;