xref: /netbsd-src/bin/sh/show.c (revision fdd524d4ccd2bb0c6f67401e938dabf773eb0372)
1 /*	$NetBSD: show.c,v 1.33 2016/05/11 17:28:30 kre Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)show.c	8.3 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: show.c,v 1.33 2016/05/11 17:28:30 kre Exp $");
41 #endif
42 #endif /* not lint */
43 
44 #include <stdio.h>
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 
49 #include "shell.h"
50 #include "parser.h"
51 #include "nodes.h"
52 #include "mystring.h"
53 #include "show.h"
54 #include "options.h"
55 #ifndef SMALL
56 #define DEFINE_NODENAMES
57 #include "nodenames.h"
58 #endif
59 
60 
61 FILE *tracefile;
62 
63 #ifdef DEBUG
64 static int shtree(union node *, int, int, char *, FILE*);
65 static int shcmd(union node *, FILE *);
66 static int shsubsh(union node *, FILE *);
67 static int shredir(union node *, FILE *, int);
68 static int sharg(union node *, FILE *);
69 static int indent(int, char *, FILE *);
70 static void trstring(char *);
71 
72 void
73 showtree(union node *n)
74 {
75 	FILE *fp;
76 
77 	fp = tracefile ? tracefile : stdout;
78 
79 	trputs("showtree(");
80 		if (n == NULL)
81 			trputs("NULL");
82 		else if (n == NEOF)
83 			trputs("NEOF");
84 	trputs(") called\n");
85 	if (n != NULL && n != NEOF)
86 		shtree(n, 1, 1, NULL, fp);
87 }
88 
89 
90 static int
91 shtree(union node *n, int ind, int nl, char *pfx, FILE *fp)
92 {
93 	struct nodelist *lp;
94 	const char *s;
95 	int len;
96 
97 	if (n == NULL) {
98 		if (nl)
99 			fputc('\n', fp);
100 		return 0;
101 	}
102 
103 	len = indent(ind, pfx, fp);
104 	switch (n->type) {
105 	case NSEMI:
106 		s = "; ";
107 		len += 2;
108 		goto binop;
109 	case NAND:
110 		s = " && ";
111 		len += 4;
112 		goto binop;
113 	case NOR:
114 		s = " || ";
115 		len += 4;
116 binop:
117 		len += shtree(n->nbinary.ch1, 0, 0, NULL, fp);
118 		fputs(s, fp);
119 		if (len >= 60) {
120 			putc('\n', fp);
121 			len = indent(ind < 0 ? 2 : ind + 1, pfx, fp);
122 		}
123 		len += shtree(n->nbinary.ch2, 0, nl, NULL, fp);
124 		break;
125 	case NCMD:
126 		len += shcmd(n, fp);
127 		if (nl && len > 0)
128 			len = 0, putc('\n', fp);
129 		break;
130 	case NPIPE:
131 		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
132 			len += shcmd(lp->n, fp);
133 			if (lp->next) {
134 				len += 3, fputs(" | ", fp);
135 				if (len >= 60)  {
136 					fputc('\n', fp);
137 					len = indent(ind < 0 ? 2 : ind + 1,
138 					    pfx, fp);
139 				}
140 			}
141 		}
142 		if (n->npipe.backgnd)
143 			len += 2, fputs(" &", fp);
144 		if (nl || len >= 60)
145 			len = 0, fputc('\n', fp);
146 		break;
147 	case NSUBSHELL:
148 		len += shsubsh(n, fp);
149 		if (nl && len > 0)
150 			len = 0, putc('\n', fp);
151 		break;
152 	default:
153 #ifdef NODETYPENAME
154 		len += fprintf(fp, "<node type %d [%s]>", n->type,
155 		    NODETYPENAME(n->type));
156 #else
157 		len += fprintf(fp, "<node type %d>", n->type);
158 #endif
159 		if (nl)
160 			len = 0, putc('\n', fp);
161 		break;
162 	}
163 	return len;
164 }
165 
166 
167 
168 static int
169 shcmd(union node *cmd, FILE *fp)
170 {
171 	union node *np;
172 	int first;
173 	const char *s;
174 	int dftfd;
175 	int len = 0;
176 
177 	first = 1;
178 	for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
179 		if (! first)
180 			len++, fputc(' ', fp);
181 		len += sharg(np, fp);
182 		first = 0;
183 	}
184 	return len + shredir(cmd, fp, first);
185 }
186 
187 static int
188 shsubsh(union node *cmd, FILE *fp)
189 {
190 	int len = 6;
191 
192 	fputs(" ( ", fp);
193 	len += shtree(cmd->nredir.n, -1, 0, NULL, fp);
194 	fputs(" ) ", fp);
195 	len += shredir(cmd, fp, 1);
196 
197 	return len;
198 }
199 
200 static int
201 shredir(union node *cmd, FILE *fp, int first)
202 {
203 	union node *np;
204 	const char *s;
205 	int dftfd;
206 	int len = 0;
207 	char buf[106];
208 
209 	for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
210 		if (! first)
211 			len++, fputc(' ', fp);
212 		switch (np->nfile.type) {
213 			case NTO:	s = ">";  dftfd = 1; len += 1; break;
214 			case NCLOBBER:	s = ">|"; dftfd = 1; len += 2; break;
215 			case NAPPEND:	s = ">>"; dftfd = 1; len += 2; break;
216 			case NTOFD:	s = ">&"; dftfd = 1; len += 2; break;
217 			case NFROM:	s = "<";  dftfd = 0; len += 1; break;
218 			case NFROMFD:	s = "<&"; dftfd = 0; len += 2; break;
219 			case NFROMTO:	s = "<>"; dftfd = 0; len += 2; break;
220 			case NXHERE:	/* FALLTHROUGH */
221 			case NHERE:	s = "<<"; dftfd = 0; len += 2; break;
222 			default:   s = "*error*"; dftfd = 0; len += 7; break;
223 		}
224 		if (np->nfile.fd != dftfd)
225 			len += fprintf(fp, "%d", np->nfile.fd);
226 		fputs(s, fp);
227 		if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
228 			len += fprintf(fp, "%d", np->ndup.dupfd);
229 		} else
230 		    if (np->nfile.type == NHERE || np->nfile.type == NXHERE) {
231 			if (np->nfile.type == NHERE)
232 				fputc('\\', fp);
233 			fputs("!!!\n", fp);
234 			s = np->nhere.doc->narg.text;
235 			if (strlen(s) > 100) {
236 				memmove(buf, s, 100);
237 				buf[100] = '\0';
238 				strcat(buf, " ...");
239 				s = buf;
240 			}
241 			fputs(s, fp);
242 			fputs("!!!", fp);
243 			len = 3;
244 		} else {
245 			len += sharg(np->nfile.fname, fp);
246 		}
247 		first = 0;
248 	}
249 	return len;
250 }
251 
252 
253 
254 static int
255 sharg(union node *arg, FILE *fp)
256 {
257 	char *p;
258 	struct nodelist *bqlist;
259 	int subtype;
260 	int len = 0;
261 
262 	if (arg->type != NARG) {
263 		fprintf(fp, "<node type %d>\n", arg->type);
264 		abort();
265 	}
266 	bqlist = arg->narg.backquote;
267 	for (p = arg->narg.text ; *p ; p++) {
268 		switch (*p) {
269 		case CTLESC:
270 			putc(*++p, fp);
271 			len++;
272 			break;
273 		case CTLVAR:
274 			putc('$', fp);
275 			putc('{', fp);
276 			len += 2;
277 			subtype = *++p;
278 			if (subtype == VSLENGTH)
279 				len++, putc('#', fp);
280 
281 			while (*++p != '=')
282 				len++, putc(*p, fp);
283 
284 			if (subtype & VSNUL)
285 				len++, putc(':', fp);
286 
287 			switch (subtype & VSTYPE) {
288 			case VSNORMAL:
289 				putc('}', fp);
290 				len++;
291 				break;
292 			case VSMINUS:
293 				putc('-', fp);
294 				len++;
295 				break;
296 			case VSPLUS:
297 				putc('+', fp);
298 				len++;
299 				break;
300 			case VSQUESTION:
301 				putc('?', fp);
302 				len++;
303 				break;
304 			case VSASSIGN:
305 				putc('=', fp);
306 				len++;
307 				break;
308 			case VSTRIMLEFT:
309 				putc('#', fp);
310 				len++;
311 				break;
312 			case VSTRIMLEFTMAX:
313 				putc('#', fp);
314 				putc('#', fp);
315 				len += 2;
316 				break;
317 			case VSTRIMRIGHT:
318 				putc('%', fp);
319 				len++;
320 				break;
321 			case VSTRIMRIGHTMAX:
322 				putc('%', fp);
323 				putc('%', fp);
324 				len += 2;
325 				break;
326 			case VSLENGTH:
327 				break;
328 			default:
329 				len += fprintf(fp, "<subtype %d>", subtype);
330 			}
331 			break;
332 		case CTLENDVAR:
333 		     putc('}', fp);
334 		     len++;
335 		     break;
336 		case CTLBACKQ:
337 		case CTLBACKQ|CTLQUOTE:
338 			putc('$', fp);
339 			putc('(', fp);
340 			len += shtree(bqlist->n, -1, 0, NULL, fp) + 3;
341 			putc(')', fp);
342 			break;
343 		default:
344 			putc(*p, fp);
345 			len++;
346 			break;
347 		}
348 	}
349 	return len;
350 }
351 
352 
353 static int
354 indent(int amount, char *pfx, FILE *fp)
355 {
356 	int i;
357 	int len = 0;
358 
359 	/*
360 	 * in practice, pfx is **always** NULL
361 	 * but here, we assume if it were not, at least strlen(pfx) < 8
362 	 * if that is invalid, output will look messy
363 	 */
364 	for (i = 0 ; i < amount ; i++) {
365 		if (pfx && i == amount - 1)
366 			fputs(pfx, fp);
367 		putc('\t', fp);
368 		len |= 7;
369 		len++;
370 	}
371 	return len;
372 }
373 #endif
374 
375 
376 
377 /*
378  * Debugging stuff.
379  */
380 
381 
382 
383 
384 #ifdef DEBUG
385 void
386 trputc(int c)
387 {
388 	if (debug != 1 || !tracefile)
389 		return;
390 	putc(c, tracefile);
391 }
392 #endif
393 
394 void
395 trace(const char *fmt, ...)
396 {
397 #ifdef DEBUG
398 	va_list va;
399 
400 	if (debug != 1 || !tracefile)
401 		return;
402 	va_start(va, fmt);
403 	(void) vfprintf(tracefile, fmt, va);
404 	va_end(va);
405 #endif
406 }
407 
408 void
409 tracev(const char *fmt, va_list va)
410 {
411 #ifdef DEBUG
412 	va_list ap;
413 	if (debug != 1 || !tracefile)
414 		return;
415 	va_copy(ap, va);
416 	(void) vfprintf(tracefile, fmt, ap);
417 	va_end(ap);
418 #endif
419 }
420 
421 
422 #ifdef DEBUG
423 void
424 trputs(const char *s)
425 {
426 	if (debug != 1 || !tracefile)
427 		return;
428 	fputs(s, tracefile);
429 }
430 
431 
432 static void
433 trstring(char *s)
434 {
435 	char *p;
436 	char c;
437 
438 	if (debug != 1 || !tracefile)
439 		return;
440 	putc('"', tracefile);
441 	for (p = s ; *p ; p++) {
442 		switch (*p) {
443 		case '\n':  c = 'n';  goto backslash;
444 		case '\t':  c = 't';  goto backslash;
445 		case '\r':  c = 'r';  goto backslash;
446 		case '"':  c = '"';  goto backslash;
447 		case '\\':  c = '\\';  goto backslash;
448 		case CTLESC:  c = 'e';  goto backslash;
449 		case CTLVAR:  c = 'v';  goto backslash;
450 		case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash;
451 		case CTLBACKQ:  c = 'q';  goto backslash;
452 		case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash;
453 backslash:	  putc('\\', tracefile);
454 			putc(c, tracefile);
455 			break;
456 		default:
457 			if (*p >= ' ' && *p <= '~')
458 				putc(*p, tracefile);
459 			else {
460 				putc('\\', tracefile);
461 				putc(*p >> 6 & 03, tracefile);
462 				putc(*p >> 3 & 07, tracefile);
463 				putc(*p & 07, tracefile);
464 			}
465 			break;
466 		}
467 	}
468 	putc('"', tracefile);
469 }
470 #endif
471 
472 
473 void
474 trargs(char **ap)
475 {
476 #ifdef DEBUG
477 	if (debug != 1 || !tracefile)
478 		return;
479 	while (*ap) {
480 		trstring(*ap++);
481 		if (*ap)
482 			putc(' ', tracefile);
483 		else
484 			putc('\n', tracefile);
485 	}
486 #endif
487 }
488 
489 
490 #ifdef DEBUG
491 void
492 opentrace(void)
493 {
494 	char s[100];
495 #ifdef O_APPEND
496 	int flags;
497 #endif
498 
499 	if (debug != 1) {
500 		if (tracefile)
501 			fflush(tracefile);
502 		/* leave open because libedit might be using it */
503 		return;
504 	}
505 	snprintf(s, sizeof(s), "./trace.%d", (int)getpid());
506 	if (tracefile) {
507 		if (!freopen(s, "a", tracefile)) {
508 			fprintf(stderr, "Can't re-open %s\n", s);
509 			tracefile = NULL;
510 			debug = 0;
511 			return;
512 		}
513 	} else {
514 		if ((tracefile = fopen(s, "a")) == NULL) {
515 			fprintf(stderr, "Can't open %s\n", s);
516 			debug = 0;
517 			return;
518 		}
519 	}
520 #ifdef O_APPEND
521 	if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
522 		fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
523 #endif
524 	setlinebuf(tracefile);
525 	fputs("\nTracing started.\n", tracefile);
526 }
527 #endif /* DEBUG */
528