show.c revision 1.39 1 /* $NetBSD: show.c,v 1.39 2017/05/13 03:26:03 kre Exp $ */
2
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * Kenneth Almquist.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #include <sys/cdefs.h>
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95";
41 #else
42 __RCSID("$NetBSD: show.c,v 1.39 2017/05/13 03:26:03 kre Exp $");
43 #endif
44 #endif /* not lint */
45
46 #include <stdio.h>
47 #include <stdarg.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <fcntl.h>
51 #include <errno.h>
52
53 #include <sys/types.h>
54 #include <sys/uio.h>
55
56 #include "shell.h"
57 #include "parser.h"
58 #include "nodes.h"
59 #include "mystring.h"
60 #include "show.h"
61 #include "options.h"
62 #include "redir.h"
63 #include "error.h"
64
65 #if defined(DEBUG) && !defined(DBG_PID)
66 /*
67 * If this is compiled, it means this is being compiled in a shell that still
68 * has an older shell.h (a simpler TRACE() mechanism than is coming soon.)
69 *
70 * Compensate for as much of that as is missing and is needed here
71 * to compile and operate at all. After the other changes have appeared,
72 * this little block can (and should be) deleted (sometime).
73 *
74 * Try to avoid waiting 22 years...
75 */
76 #define DBG_PID 1
77 #define DBG_NEST 2
78 #endif
79
80 #define DEFINE_NODENAMES
81 #include "nodenames.h" /* does almost nothing if !defined(DEBUG) */
82
83 #define TR_STD_WIDTH 60 /* tend to fold lines wider than this */
84 #define TR_IOVECS 10 /* number of lines or trace (max) / write */
85
86 typedef struct traceinfo {
87 int tfd; /* file descriptor for open trace file */
88 int nxtiov; /* the buffer we should be writing to */
89 char lastc; /* the last non-white character output */
90 uint8_t supr; /* char classes to supress after \n */
91 pid_t pid; /* process id of process that opened that file */
92 size_t llen; /* number of chars in current output line */
93 size_t blen; /* chars used in current buffer being filled */
94 char * tracefile; /* name of the tracefile */
95 struct iovec lines[TR_IOVECS]; /* filled, flling, pending buffers */
96 } TFILE;
97
98 /* These are auto turned off when non white space is printed */
99 #define SUP_NL 0x01 /* don't print \n */
100 #define SUP_SP 0x03 /* supress spaces */
101 #define SUP_WSP 0x04 /* supress all white space */
102
103 #ifdef DEBUG /* from here to end of file ... */
104
105 TFILE tracedata, *tracetfile;
106 FILE *tracefile; /* just for histedit */
107
108 uint64_t DFlags; /* currently enabled debug flags */
109 int ShNest; /* depth of shell (internal) nesting */
110
111 static void shtree(union node *, int, int, int, TFILE *);
112 static void shcmd(union node *, TFILE *);
113 static void shsubsh(union node *, TFILE *);
114 static void shredir(union node *, TFILE *, int);
115 static void sharg(union node *, TFILE *);
116 static void indent(int, TFILE *);
117 static void trstring(const char *);
118 static void trace_putc(char, TFILE *);
119 static void trace_puts(const char *, TFILE *);
120 static void trace_flush(TFILE *, int);
121 static char *trace_id(TFILE *);
122 static void trace_fd_swap(int, int);
123
124 inline static int trlinelen(TFILE *);
125
126
127 /*
128 * These functions are the externally visible interface
129 * (but only for a DEBUG shell.)
130 */
131
132 void
133 opentrace(void)
134 {
135 char *s;
136 int fd;
137 int i;
138 pid_t pid;
139
140 if (debug != 1) {
141 /* leave fd open because libedit might be using it */
142 if (tracefile)
143 fflush(tracefile);
144 if (tracetfile)
145 trace_flush(tracetfile, 1);
146 return;
147 }
148 #if DBG_PID == 1 /* using old shell.h, old tracing method */
149 DFlags = DBG_PID; /* just force DBG_PID on, and leave it ... */
150 #endif
151 pid = getpid();
152 if (asprintf(&s, "trace.%jd", (intmax_t)pid) <= 0) {
153 debug = 0;
154 error("Cannot asprintf tracefilename");
155 };
156
157 fd = open(s, O_WRONLY|O_APPEND|O_CREAT, 0666);
158 if (fd == -1) {
159 debug = 0;
160 error("Can't open tracefile: %s (%s)\n", s, strerror(errno));
161 }
162 fd = to_upper_fd(fd);
163 if (fd <= 2) {
164 (void) close(fd);
165 debug = 0;
166 error("Attempt to use fd %d as tracefile thwarted\n", fd);
167 }
168 register_sh_fd(fd, trace_fd_swap);
169
170 /*
171 * This stuff is just so histedit has a FILE * to use
172 */
173 if (tracefile)
174 (void) fclose(tracefile); /* also closes tfd */
175 tracefile = fdopen(fd, "a"); /* don't care if it is NULL */
176 if (tracefile) /* except here... */
177 setlinebuf(tracefile);
178
179 /*
180 * Now the real tracing setup
181 */
182 if (tracedata.tfd > 0 && tracedata.tfd != fd)
183 (void) close(tracedata.tfd); /* usually done by fclose() */
184
185 tracedata.tfd = fd;
186 tracedata.pid = pid;
187 tracedata.nxtiov = 0;
188 tracedata.blen = 0;
189 tracedata.llen = 0;
190 tracedata.lastc = '\0';
191 tracedata.supr = SUP_NL | SUP_WSP;
192
193 #define replace(f, v) do { \
194 if (tracedata.f != NULL) \
195 free(tracedata.f); \
196 tracedata.f = v; \
197 } while (/*CONSTCOND*/ 0)
198
199 replace(tracefile, s);
200
201 for (i = 0; i < TR_IOVECS; i++) {
202 replace(lines[i].iov_base, NULL);
203 tracedata.lines[i].iov_len = 0;
204 }
205
206 #undef replace
207
208 tracetfile = &tracedata;
209
210 trace_puts("\nTracing started.\n", tracetfile);
211 }
212
213 void
214 trace(const char *fmt, ...)
215 {
216 va_list va;
217 char *s;
218
219 if (debug != 1 || !tracetfile)
220 return;
221 va_start(va, fmt);
222 (void) vasprintf(&s, fmt, va);
223 va_end(va);
224
225 trace_puts(s, tracetfile);
226 free(s);
227 if (tracetfile->llen == 0)
228 trace_flush(tracetfile, 0);
229 }
230
231 void
232 tracev(const char *fmt, va_list va)
233 {
234 va_list ap;
235 char *s;
236
237 if (debug != 1 || !tracetfile)
238 return;
239 va_copy(ap, va);
240 (void) vasprintf(&s, fmt, ap);
241 va_end(ap);
242
243 trace_puts(s, tracetfile);
244 free(s);
245 if (tracetfile->llen == 0)
246 trace_flush(tracetfile, 0);
247 }
248
249
250 void
251 trputs(const char *s)
252 {
253 if (debug != 1 || !tracetfile)
254 return;
255 trace_puts(s, tracetfile);
256 }
257
258 void
259 trputc(int c)
260 {
261 if (debug != 1 || !tracetfile)
262 return;
263 trace_putc(c, tracetfile);
264 }
265
266 void
267 showtree(union node *n)
268 {
269 TFILE *fp;
270
271 if ((fp = tracetfile) == NULL)
272 return;
273
274 trace_puts("showtree(", fp);
275 if (n == NULL)
276 trace_puts("NULL", fp);
277 else if (n == NEOF)
278 trace_puts("NEOF", fp);
279 else
280 trace("%p", n);
281 trace_puts(") called\n", fp);
282 if (n != NULL && n != NEOF)
283 shtree(n, 1, 1, 1, fp);
284 }
285
286 void
287 trargs(char **ap)
288 {
289 if (debug != 1 || !tracetfile)
290 return;
291 while (*ap) {
292 trstring(*ap++);
293 if (*ap)
294 trace_putc(' ', tracetfile);
295 else
296 trace_putc('\n', tracetfile);
297 }
298 }
299
300
301 /*
302 * Beyond here we just have the implementation of all of that
303 */
304
305
306 inline static int
307 trlinelen(TFILE * fp)
308 {
309 return fp->llen;
310 }
311
312 static void
313 shtree(union node *n, int ind, int ilvl, int nl, TFILE *fp)
314 {
315 struct nodelist *lp;
316 const char *s;
317
318 if (n == NULL) {
319 if (nl)
320 trace_putc('\n', fp);
321 return;
322 }
323
324 indent(ind, fp);
325 switch (n->type) {
326 case NSEMI:
327 s = NULL;
328 goto binop;
329 case NAND:
330 s = " && ";
331 goto binop;
332 case NOR:
333 s = " || ";
334 binop:
335 shtree(n->nbinary.ch1, 0, ilvl, 0, fp);
336 if (s != NULL)
337 trace_puts(s, fp);
338 if (trlinelen(fp) >= TR_STD_WIDTH) {
339 trace_putc('\n', fp);
340 indent(ind < 0 ? 2 : ind + 1, fp);
341 } else if (s == NULL) {
342 if (fp->lastc != '&')
343 trace_puts("; ", fp);
344 else
345 trace_putc(' ', fp);
346 }
347 shtree(n->nbinary.ch2, 0, ilvl, nl, fp);
348 break;
349 case NCMD:
350 shcmd(n, fp);
351 if (n->ncmd.backgnd)
352 trace_puts(" &", fp);
353 if (nl && trlinelen(fp) > 0)
354 trace_putc('\n', fp);
355 break;
356 case NPIPE:
357 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
358 shtree(lp->n, 0, ilvl, 0, fp);
359 if (lp->next) {
360 trace_puts(" |", fp);
361 if (trlinelen(fp) >= TR_STD_WIDTH) {
362 trace_putc('\n', fp);
363 indent((ind < 0 ? ilvl : ind) + 1, fp);
364 } else
365 trace_putc(' ', fp);
366 }
367 }
368 if (n->npipe.backgnd)
369 trace_puts(" &", fp);
370 if (nl || trlinelen(fp) >= TR_STD_WIDTH)
371 trace_putc('\n', fp);
372 break;
373 case NBACKGND:
374 case NSUBSHELL:
375 shsubsh(n, fp);
376 if (n->type == NBACKGND)
377 trace_puts(" &", fp);
378 if (nl && trlinelen(fp) > 0)
379 trace_putc('\n', fp);
380 break;
381 case NDEFUN:
382 trace_puts(n->narg.text, fp);
383 trace_puts("() {\n", fp);
384 indent(ind, fp);
385 shtree(n->narg.next, (ind < 0 ? ilvl : ind) + 1, ilvl+1, 1, fp);
386 indent(ind, fp);
387 trace_puts("}\n", fp);
388 break;
389 case NDNOT:
390 trace_puts("! ", fp);
391 /* FALLTHROUGH */
392 case NNOT:
393 trace_puts("! ", fp);
394 shtree(n->nnot.com, -1, ilvl, nl, fp);
395 break;
396 case NREDIR:
397 shtree(n->nredir.n, -1, ilvl, 0, fp);
398 shredir(n->nredir.redirect, fp, n->nredir.n == NULL);
399 if (nl)
400 trace_putc('\n', fp);
401 break;
402
403 case NIF:
404 itsif:
405 trace_puts("if ", fp);
406 shtree(n->nif.test, -1, ilvl, 0, fp);
407 if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
408 if (fp->lastc != '&')
409 trace_puts(" ;", fp);
410 } else
411 indent(ilvl, fp);
412 trace_puts(" then ", fp);
413 if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
414 indent(ilvl+1, fp);
415 shtree(n->nif.ifpart, -1, ilvl + 1, 0, fp);
416 if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
417 if (fp->lastc != '&')
418 trace_puts(" ;", fp);
419 } else
420 indent(ilvl, fp);
421 if (n->nif.elsepart && n->nif.elsepart->type == NIF) {
422 if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
423 indent(ilvl, fp);
424 n = n->nif.elsepart;
425 trace_puts(" el", fp);
426 goto itsif;
427 }
428 if (n->nif.elsepart) {
429 if (nl || trlinelen(fp) > TR_STD_WIDTH - 24)
430 indent(ilvl+1, fp);
431 trace_puts(" else ", fp);
432 shtree(n->nif.elsepart, -1, ilvl + 1, 0, fp);
433 if (fp->lastc != '&')
434 trace_puts(" ;", fp);
435 }
436 trace_puts(" fi", fp);
437 if (nl)
438 trace_putc('\n', fp);
439 break;
440
441 case NWHILE:
442 trace_puts("while ", fp);
443 goto aloop;
444 case NUNTIL:
445 trace_puts("until ", fp);
446 aloop:
447 shtree(n->nbinary.ch1, -1, ilvl, 0, fp);
448 if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
449 if (fp->lastc != '&')
450 trace_puts(" ;", fp);
451 } else
452 trace_putc('\n', fp);
453 trace_puts(" do ", fp);
454 shtree(n->nbinary.ch1, -1, ilvl + 1, 1, fp);
455 trace_puts(" done ", fp);
456 if (nl)
457 trace_putc('\n', fp);
458 break;
459
460 case NFOR:
461 trace_puts("for ", fp);
462 trace_puts(n->nfor.var, fp);
463 if (n->nfor.args) {
464 union node *argp;
465
466 trace_puts(" in ", fp);
467 for (argp = n->nfor.args; argp; argp=argp->narg.next) {
468 sharg(argp, fp);
469 trace_putc(' ', fp);
470 }
471 if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) {
472 if (fp->lastc != '&')
473 trace_putc(';', fp);
474 } else
475 trace_putc('\n', fp);
476 }
477 trace_puts(" do ", fp);
478 shtree(n->nfor.body, -1, ilvl + 1, 0, fp);
479 if (fp->lastc != '&')
480 trace_putc(';', fp);
481 trace_puts(" done", fp);
482 if (nl)
483 trace_putc('\n', fp);
484 break;
485
486 case NCASE:
487 trace_puts("case ", fp);
488 sharg(n->ncase.expr, fp);
489 trace_puts(" in", fp);
490 if (nl)
491 trace_putc('\n', fp);
492 {
493 union node *cp;
494
495 for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) {
496 union node *patp;
497
498 if (nl || trlinelen(fp) > TR_STD_WIDTH - 16)
499 indent(ilvl, fp);
500 else
501 trace_putc(' ', fp);
502 trace_putc('(', fp);
503 patp = cp->nclist.pattern;
504 while (patp != NULL) {
505 trace_putc(' ', fp);
506 sharg(patp, fp);
507 trace_putc(' ', fp);
508 if ((patp = patp->narg.next) != NULL)
509 trace_putc('|', fp);
510 }
511 trace_putc(')', fp);
512 if (nl)
513 indent(ilvl + 1, fp);
514 else
515 trace_putc(' ', fp);
516 shtree(cp->nclist.body, -1, ilvl+2, 0, fp);
517 if (cp->type == NCLISTCONT)
518 trace_puts(" ;&", fp);
519 else
520 trace_puts(" ;;", fp);
521 if (nl)
522 trace_putc('\n', fp);
523 }
524 }
525 if (nl) {
526 trace_putc('\n', fp);
527 indent(ind, fp);
528 } else
529 trace_putc(' ', fp);
530 trace_puts("esac", fp);
531 if (nl)
532 trace_putc('\n', fp);
533 break;
534
535 default: {
536 char *str;
537
538 asprintf(&str, "<node type %d [%s]>", n->type,
539 NODETYPENAME(n->type));
540 trace_puts(str, fp);
541 free(str);
542 if (nl)
543 trace_putc('\n', fp);
544 }
545 break;
546 }
547 }
548
549
550 static void
551 shcmd(union node *cmd, TFILE *fp)
552 {
553 union node *np;
554 int first;
555
556 first = 1;
557 for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
558 if (! first)
559 trace_putc(' ', fp);
560 sharg(np, fp);
561 first = 0;
562 }
563 shredir(cmd->ncmd.redirect, fp, first);
564 }
565
566 static void
567 shsubsh(union node *cmd, TFILE *fp)
568 {
569 trace_puts(" ( ", fp);
570 shtree(cmd->nredir.n, -1, 3, 0, fp);
571 trace_puts(" ) ", fp);
572 shredir(cmd->ncmd.redirect, fp, 1);
573 }
574
575 static void
576 shredir(union node *np, TFILE *fp, int first)
577 {
578 const char *s;
579 int dftfd;
580 char buf[106];
581
582 for ( ; np ; np = np->nfile.next) {
583 if (! first)
584 trace_putc(' ', fp);
585 switch (np->nfile.type) {
586 case NTO: s = ">"; dftfd = 1; break;
587 case NCLOBBER: s = ">|"; dftfd = 1; break;
588 case NAPPEND: s = ">>"; dftfd = 1; break;
589 case NTOFD: s = ">&"; dftfd = 1; break;
590 case NFROM: s = "<"; dftfd = 0; break;
591 case NFROMFD: s = "<&"; dftfd = 0; break;
592 case NFROMTO: s = "<>"; dftfd = 0; break;
593 case NXHERE: /* FALLTHROUGH */
594 case NHERE: s = "<<"; dftfd = 0; break;
595 default: s = "*error*"; dftfd = 0; break;
596 }
597 if (np->nfile.fd != dftfd) {
598 sprintf(buf, "%d", np->nfile.fd);
599 trace_puts(buf, fp);
600 }
601 trace_puts(s, fp);
602 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
603 if (np->ndup.vname)
604 sharg(np->ndup.vname, fp);
605 else {
606 sprintf(buf, "%d", np->ndup.dupfd);
607 trace_puts(buf, fp);
608 }
609 } else
610 if (np->nfile.type == NHERE || np->nfile.type == NXHERE) {
611 if (np->nfile.type == NHERE)
612 trace_putc('\\', fp);
613 trace_puts("!!!\n", fp);
614 s = np->nhere.doc->narg.text;
615 if (strlen(s) > 100) {
616 memmove(buf, s, 100);
617 buf[100] = '\0';
618 strcat(buf, " ...\n");
619 s = buf;
620 }
621 trace_puts(s, fp);
622 trace_puts("!!! ", fp);
623 } else {
624 sharg(np->nfile.fname, fp);
625 }
626 first = 0;
627 }
628 }
629
630 static void
631 sharg(union node *arg, TFILE *fp)
632 {
633 char *p, *s;
634 struct nodelist *bqlist;
635 int subtype = 0;
636 int quoted = 0;
637
638 if (arg->type != NARG) {
639 asprintf(&s, "<node type %d> ! NARG\n", arg->type);
640 trace_puts(s, fp);
641 abort(); /* no need to free s, better not to */
642 }
643
644 bqlist = arg->narg.backquote;
645 for (p = arg->narg.text ; *p ; p++) {
646 switch (*p) {
647 case CTLESC:
648 trace_putc('\\', fp);
649 trace_putc(*++p, fp);
650 break;
651
652 case CTLVAR:
653 subtype = *++p;
654 if (!quoted != !(subtype & VSQUOTE))
655 trace_putc('"', fp);
656 trace_putc('$', fp);
657 trace_putc('{', fp);
658 if ((subtype & VSTYPE) == VSLENGTH)
659 trace_putc('#', fp);
660 if (subtype & VSLINENO)
661 trace_puts("LINENO=", fp);
662
663 while (*++p != '=')
664 trace_putc(*p, fp);
665
666 if (subtype & VSNUL)
667 trace_putc(':', fp);
668
669 switch (subtype & VSTYPE) {
670 case VSNORMAL:
671 /* { */
672 trace_putc('}', fp);
673 if (!quoted != !(subtype & VSQUOTE))
674 trace_putc('"', fp);
675 break;
676 case VSMINUS:
677 trace_putc('-', fp);
678 break;
679 case VSPLUS:
680 trace_putc('+', fp);
681 break;
682 case VSQUESTION:
683 trace_putc('?', fp);
684 break;
685 case VSASSIGN:
686 trace_putc('=', fp);
687 break;
688 case VSTRIMLEFTMAX:
689 trace_putc('#', fp);
690 /* FALLTHROUGH */
691 case VSTRIMLEFT:
692 trace_putc('#', fp);
693 break;
694 case VSTRIMRIGHTMAX:
695 trace_putc('%', fp);
696 /* FALLTHROUGH */
697 case VSTRIMRIGHT:
698 trace_putc('%', fp);
699 break;
700 case VSLENGTH:
701 break;
702 default: {
703 char str[32];
704
705 snprintf(str, sizeof str,
706 "<subtype %d>", subtype);
707 trace_puts(str, fp);
708 }
709 break;
710 }
711 break;
712 case CTLENDVAR:
713 /* { */
714 trace_putc('}', fp);
715 if (!quoted != !(subtype & VSQUOTE))
716 trace_putc('"', fp);
717 subtype = 0;
718 break;
719
720 case CTLBACKQ|CTLQUOTE:
721 if (!quoted)
722 trace_putc('"', fp);
723 /* FALLTHRU */
724 case CTLBACKQ:
725 trace_putc('$', fp);
726 trace_putc('(', fp);
727 if (bqlist) {
728 shtree(bqlist->n, -1, 3, 0, fp);
729 bqlist = bqlist->next;
730 } else
731 trace_puts("???", fp);
732 trace_putc(')', fp);
733 if (!quoted && *p == (CTLBACKQ|CTLQUOTE))
734 trace_putc('"', fp);
735 break;
736
737 case CTLQUOTEMARK:
738 if (subtype != 0 || !quoted) {
739 trace_putc('"', fp);
740 quoted++;
741 }
742 break;
743 case CTLQUOTEEND:
744 trace_putc('"', fp);
745 quoted--;
746 break;
747 case CTLARI:
748 trace_puts("$(( ", fp);
749 break;
750 case CTLENDARI:
751 trace_puts(" ))", fp);
752 break;
753
754 default:
755 if (*p == '$')
756 trace_putc('\\', fp);
757 trace_putc(*p, fp);
758 break;
759 }
760 }
761 if (quoted)
762 trace_putc('"', fp);
763 }
764
765
766 static void
767 indent(int amount, TFILE *fp)
768 {
769 int i;
770
771 if (amount <= 0)
772 return;
773
774 amount <<= 2; /* indent slots -> chars */
775
776 i = trlinelen(fp);
777 fp->supr = SUP_NL;
778 if (i > amount) {
779 trace_putc('\n', fp);
780 i = 0;
781 }
782 fp->supr = 0;
783 for (; i < amount - 7 ; i++) {
784 trace_putc('\t', fp);
785 i |= 7;
786 }
787 while (i < amount) {
788 trace_putc(' ', fp);
789 i++;
790 }
791 fp->supr = SUP_WSP;
792 }
793
794 static void
795 trace_putc(char c, TFILE *fp)
796 {
797 char *p;
798
799 if (c == '\0')
800 return;
801 if (debug == 0 || fp == NULL)
802 return;
803
804 if (fp->llen == 0) {
805 if (fp->blen != 0)
806 abort();
807
808 if ((fp->supr & SUP_NL) && c == '\n')
809 return;
810 if ((fp->supr & (SUP_WSP|SUP_SP)) && c == ' ')
811 return;
812 if ((fp->supr & SUP_WSP) && c == '\t')
813 return;
814
815 if (fp->nxtiov >= TR_IOVECS - 1) /* should be rare */
816 trace_flush(fp, 0);
817
818 p = trace_id(fp);
819 if (p != NULL) {
820 fp->lines[fp->nxtiov].iov_base = p;
821 fp->lines[fp->nxtiov].iov_len = strlen(p);
822 fp->nxtiov++;
823 }
824 } else if (fp->blen && fp->blen >= fp->lines[fp->nxtiov].iov_len) {
825 fp->blen = 0;
826 if (++fp->nxtiov >= TR_IOVECS)
827 trace_flush(fp, 0);
828 }
829
830 if (fp->lines[fp->nxtiov].iov_len == 0) {
831 p = (char *)malloc(2 * TR_STD_WIDTH);
832 if (p == NULL) {
833 trace_flush(fp, 1);
834 debug = 0;
835 return;
836 }
837 *p = '\0';
838 fp->lines[fp->nxtiov].iov_base = p;
839 fp->lines[fp->nxtiov].iov_len = 2 * TR_STD_WIDTH;
840 fp->blen = 0;
841 }
842
843 p = (char *)fp->lines[fp->nxtiov].iov_base + fp->blen++;
844 *p++ = c;
845 *p = 0;
846
847 if (c != ' ' && c != '\t' && c != '\n') {
848 fp->lastc = c;
849 fp->supr = 0;
850 }
851
852 if (c == '\n') {
853 fp->lines[fp->nxtiov++].iov_len = fp->blen;
854 fp->blen = 0;
855 fp->llen = 0;
856 fp->supr |= SUP_NL;
857 return;
858 }
859
860 if (c == '\t')
861 fp->llen |= 7;
862 fp->llen++;
863 }
864
865 void
866 trace_flush(TFILE *fp, int all)
867 {
868 int niov, i;
869 ssize_t written;
870
871 niov = fp->nxtiov;
872 if (all && fp->blen > 0) {
873 fp->lines[niov].iov_len = fp->blen;
874 fp->blen = 0;
875 fp->llen = 0;
876 niov++;
877 }
878 if (niov == 0)
879 return;
880 if (fp->blen > 0 && --niov == 0)
881 return;
882 written = writev(fp->tfd, fp->lines, niov);
883 for (i = 0; i < niov; i++) {
884 free(fp->lines[i].iov_base);
885 fp->lines[i].iov_base = NULL;
886 fp->lines[i].iov_len = 0;
887 }
888 if (written == -1) {
889 if (fp->blen > 0) {
890 free(fp->lines[niov].iov_base);
891 fp->lines[niov].iov_base = NULL;
892 fp->lines[niov].iov_len = 0;
893 }
894 debug = 0;
895 fp->blen = 0;
896 fp->llen = 0;
897 return;
898 }
899 if (fp->blen > 0) {
900 fp->lines[0].iov_base = fp->lines[niov].iov_base;
901 fp->lines[0].iov_len = fp->lines[niov].iov_len;
902 fp->lines[niov].iov_base = NULL;
903 fp->lines[niov].iov_len = 0;
904 }
905 fp->nxtiov = 0;
906 }
907
908 void
909 trace_puts(const char *s, TFILE *fp)
910 {
911 char c;
912
913 while ((c = *s++) != '\0')
914 trace_putc(c, fp);
915 }
916
917 inline static char *
918 trace_id(TFILE *tf)
919 {
920 int i;
921 char indent[16];
922 char *p;
923
924 if (DFlags & DBG_NEST) {
925 p = indent;
926 for (i = 0; i < 6; i++)
927 *p++ = (i < ShNest) ? '#' : ' ';
928 while (i++ < ShNest && p < &indent[sizeof indent - 1])
929 *p++ = '#';
930 *p = '\0';
931 } else
932 indent[0] = '\0';
933
934 if (DFlags & DBG_PID) {
935 i = getpid();
936 (void) asprintf(&p, "%5d%c%s\t", i,
937 i == tf->pid ? ':' : '=', indent);
938 return p;
939 } else if (DFlags & DBG_NEST) {
940 *p++ = '\t';
941 *p = '\0';
942 (void) asprintf(&p, "%s\t", indent);
943 return p;
944 }
945 return NULL;
946 }
947
948 /*
949 * Used only from trargs(), which itself is used only to print
950 * arg lists (argv[]) either that passed into this shell, or
951 * the arg list about to be given to some other command (incl
952 * builtin, and function) as their argv[]. If any of the CTL*
953 * chars seem to appear, they really should be just treated as data,
954 * not special... But this is just debug, so, who cares!
955 */
956 static void
957 trstring(const char *s)
958 {
959 const char *p;
960 char c;
961 TFILE *fp;
962
963 if (debug != 1 || !tracetfile)
964 return;
965 fp = tracetfile;
966 trace_putc('"', fp);
967 for (p = s ; *p ; p++) {
968 switch (*p) {
969 case '\n': c = 'n'; goto backslash;
970 case '\t': c = 't'; goto backslash;
971 case '\r': c = 'r'; goto backslash;
972 case '"': c = '"'; goto backslash;
973 case '\\': c = '\\'; goto backslash;
974 case CTLESC: c = 'e'; goto backslash;
975 case CTLVAR: c = 'v'; goto backslash;
976 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
977 case CTLBACKQ: c = 'q'; goto backslash;
978 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
979 backslash: trace_putc('\\', fp);
980 trace_putc(c, fp);
981 break;
982 default:
983 if (*p >= ' ' && *p <= '~')
984 trace_putc(*p, fp);
985 else {
986 trace_putc('\\', fp);
987 trace_putc(*p >> 6 & 03, fp);
988 trace_putc(*p >> 3 & 07, fp);
989 trace_putc(*p & 07, fp);
990 }
991 break;
992 }
993 }
994 trace_putc('"', fp);
995 }
996
997 /*
998 * deal with the user "accidentally" picking our fd to use.
999 */
1000 static void
1001 trace_fd_swap(int from, int to)
1002 {
1003 if (tracetfile == NULL || from == to)
1004 return;
1005
1006 tracetfile->tfd = to;
1007
1008 /*
1009 * This is just so histedit has a stdio FILE* to use.
1010 */
1011 if (tracefile)
1012 fclose(tracefile);
1013 tracefile = fdopen(to, "a");
1014 if (tracefile)
1015 setlinebuf(tracefile);
1016 }
1017
1018 #endif /* DEBUG */
1019