xref: /illumos-gate/usr/src/cmd/awk/tran.c (revision 814a60b13c0ad90e5d2edfd29a7a84bbf416cc1a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #define	DEBUG
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include "awk.h"
39 #include "y.tab.h"
40 
41 #define	FULLTAB	2	/* rehash when table gets this x full */
42 #define	GROWTAB 4	/* grow table by this factor */
43 
44 Array	*symtab;	/* main symbol table */
45 
46 uchar	**FS;		/* initial field sep */
47 uchar	**RS;		/* initial record sep */
48 uchar	**OFS;		/* output field sep */
49 uchar	**ORS;		/* output record sep */
50 uchar	**OFMT;		/* output format for numbers */
51 Awkfloat *NF;		/* number of fields in current record */
52 Awkfloat *NR;		/* number of current record */
53 Awkfloat *FNR;		/* number of current record in current file */
54 uchar	**FILENAME;	/* current filename argument */
55 Awkfloat *ARGC;		/* number of arguments from command line */
56 uchar	**SUBSEP;	/* subscript separator for a[i,j,k]; default \034 */
57 Awkfloat *RSTART;	/* start of re matched with ~; origin 1 (!) */
58 Awkfloat *RLENGTH;	/* length of same */
59 
60 Cell	*recloc;	/* location of record */
61 Cell	*nrloc;		/* NR */
62 Cell	*nfloc;		/* NF */
63 Cell	*fnrloc;	/* FNR */
64 Array	*ARGVtab;	/* symbol table containing ARGV[...] */
65 Array	*ENVtab;	/* symbol table containing ENVIRON[...] */
66 Cell	*rstartloc;	/* RSTART */
67 Cell	*rlengthloc;	/* RLENGTH */
68 Cell	*symtabloc;	/* SYMTAB */
69 
70 Cell	*nullloc;
71 Node	*nullnode;	/* zero&null, converted into a node for comparisons */
72 
73 static	void	rehash(Array *);
74 
75 void
76 syminit(void)
77 {
78 	Cell	*p;
79 
80 	init_buf(&recdata, &record_size, LINE_INCR);
81 	record = recdata;
82 
83 	p = getfld(0);
84 	/* initialize $0 */
85 	p->nval = (uchar*) "$0";
86 	p->sval = recdata;
87 	p->tval = REC|STR|DONTFREE;
88 
89 	symtab = makesymtab(NSYMTAB);
90 	(void) setsymtab((uchar *)"0", (uchar *)"0", 0.0,
91 	    NUM|STR|CON|DONTFREE, symtab);
92 	/* this is used for if(x)... tests: */
93 	nullloc = setsymtab((uchar *)"$zero&null", (uchar *)"", 0.0,
94 	    NUM|STR|CON|DONTFREE, symtab);
95 	nullnode = valtonode(nullloc, CCON);
96 	recloc = getfld(0);
97 	FS = &setsymtab((uchar *)"FS", (uchar *)" ", 0.0,
98 	    STR|DONTFREE, symtab)->sval;
99 	RS = &setsymtab((uchar *)"RS", (uchar *)"\n", 0.0,
100 	    STR|DONTFREE, symtab)->sval;
101 	OFS = &setsymtab((uchar *)"OFS", (uchar *)" ", 0.0,
102 	    STR|DONTFREE, symtab)->sval;
103 	ORS = &setsymtab((uchar *)"ORS", (uchar *)"\n", 0.0,
104 	    STR|DONTFREE, symtab)->sval;
105 	OFMT = &setsymtab((uchar *)"OFMT", (uchar *)"%.6g", 0.0,
106 	    STR|DONTFREE, symtab)->sval;
107 	FILENAME = &setsymtab((uchar *)"FILENAME", (uchar *)"-", 0.0,
108 	    STR|DONTFREE, symtab)->sval;
109 	nfloc = setsymtab((uchar *)"NF", (uchar *)"", 0.0, NUM, symtab);
110 	NF = &nfloc->fval;
111 	nrloc = setsymtab((uchar *)"NR", (uchar *)"", 0.0, NUM, symtab);
112 	NR = &nrloc->fval;
113 	fnrloc = setsymtab((uchar *)"FNR", (uchar *)"", 0.0, NUM, symtab);
114 	FNR = &fnrloc->fval;
115 	SUBSEP = &setsymtab((uchar *)"SUBSEP", (uchar *)"\034", 0.0,
116 	    STR|DONTFREE, symtab)->sval;
117 	rstartloc = setsymtab((uchar *)"RSTART", (uchar *)"", 0.0,
118 	    NUM, symtab);
119 	RSTART = &rstartloc->fval;
120 	rlengthloc = setsymtab((uchar *)"RLENGTH", (uchar *)"", 0.0,
121 	    NUM, symtab);
122 	RLENGTH = &rlengthloc->fval;
123 	symtabloc = setsymtab((uchar *)"SYMTAB", (uchar *)"", 0.0, ARR, symtab);
124 	symtabloc->sval = (uchar *)symtab;
125 }
126 
127 void
128 arginit(int ac, uchar *av[])
129 {
130 	Cell *cp;
131 	int i;
132 	uchar temp[11];
133 
134 	/* first make FILENAME first real argument */
135 	for (i = 1; i < ac; i++) {
136 		if (!isclvar(av[i])) {
137 			(void) setsval(lookup((uchar *)"FILENAME", symtab),
138 			    av[i]);
139 			break;
140 		}
141 	}
142 	ARGC = &setsymtab((uchar *)"ARGC", (uchar *)"", (Awkfloat)ac,
143 	    NUM, symtab)->fval;
144 	cp = setsymtab((uchar *)"ARGV", (uchar *)"", 0.0, ARR, symtab);
145 	ARGVtab = makesymtab(NSYMTAB);	/* could be (int) ARGC as well */
146 	cp->sval = (uchar *) ARGVtab;
147 	for (i = 0; i < ac; i++) {
148 		(void) sprintf((char *)temp, "%d", i);
149 		if (is_number(*av)) {
150 			(void) setsymtab(temp, *av, atof((const char *)*av),
151 			    STR|NUM, ARGVtab);
152 		} else {
153 			(void) setsymtab(temp, *av, 0.0, STR, ARGVtab);
154 		}
155 		av++;
156 	}
157 }
158 
159 void
160 envinit(uchar *envp[])
161 {
162 	Cell *cp;
163 	uchar *p;
164 
165 	cp = setsymtab((uchar *)"ENVIRON", (uchar *)"", 0.0, ARR, symtab);
166 	ENVtab = makesymtab(NSYMTAB);
167 	cp->sval = (uchar *) ENVtab;
168 	for (; *envp; envp++) {
169 		if ((p = (uchar *)strchr((char *)*envp, '=')) == NULL)
170 			continue;
171 		*p++ = 0;	/* split into two strings at = */
172 		if (is_number(p)) {
173 			(void) setsymtab(*envp, p, atof((const char *)p),
174 			    STR|NUM, ENVtab);
175 		} else {
176 			(void) setsymtab(*envp, p, 0.0, STR, ENVtab);
177 		}
178 		/* restore in case env is passed down to a shell */
179 		p[-1] = '=';
180 	}
181 }
182 
183 Array *
184 makesymtab(int n)
185 {
186 	Array *ap;
187 	Cell **tp;
188 
189 	ap = (Array *)malloc(sizeof (Array));
190 	tp = (Cell **)calloc(n, sizeof (Cell *));
191 	if (ap == NULL || tp == NULL)
192 		ERROR "out of space in makesymtab" FATAL;
193 	ap->nelem = 0;
194 	ap->size = n;
195 	ap->tab = tp;
196 	return (ap);
197 }
198 
199 void
200 freesymtab(Cell *ap)	/* free symbol table */
201 {
202 	Cell *cp, *next;
203 	Array *tp;
204 	int i;
205 
206 	if (!isarr(ap))
207 		return;
208 	/*LINTED align*/
209 	tp = (Array *)ap->sval;
210 	if (tp == NULL)
211 		return;
212 	for (i = 0; i < tp->size; i++) {
213 		for (cp = tp->tab[i]; cp != NULL; cp = next) {
214 			next = cp->cnext;
215 			xfree(cp->nval);
216 			if (freeable(cp))
217 				xfree(cp->sval);
218 			free(cp);
219 		}
220 	}
221 	free(tp->tab);
222 	free(tp);
223 }
224 
225 void
226 freeelem(Cell *ap, uchar *s)		/* free elem s from ap (i.e., ap["s"] */
227 {
228 	Array *tp;
229 	Cell *p, *prev = NULL;
230 	int h;
231 
232 	/*LINTED align*/
233 	tp = (Array *)ap->sval;
234 	h = hash(s, tp->size);
235 	for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
236 		if (strcmp((char *)s, (char *)p->nval) == 0) {
237 			if (prev == NULL)	/* 1st one */
238 				tp->tab[h] = p->cnext;
239 			else			/* middle somewhere */
240 				prev->cnext = p->cnext;
241 			if (freeable(p))
242 				xfree(p->sval);
243 			free(p->nval);
244 			free(p);
245 			tp->nelem--;
246 			return;
247 		}
248 }
249 
250 Cell *
251 setsymtab(uchar *n, uchar *s, Awkfloat f, unsigned int t, Array *tp)
252 {
253 	register int h;
254 	register Cell *p;
255 
256 	if (n != NULL && (p = lookup(n, tp)) != NULL) {
257 		dprintf(("setsymtab found %p: n=%s", (void *)p, p->nval));
258 		dprintf((" s=\"%s\" f=%g t=%p\n",
259 		    p->sval, p->fval, (void *)p->tval));
260 		return (p);
261 	}
262 	p = (Cell *)malloc(sizeof (Cell));
263 	if (p == NULL)
264 		ERROR "symbol table overflow at %s", n FATAL;
265 	p->nval = tostring(n);
266 	p->sval = s ? tostring(s) : tostring((uchar *)"");
267 	p->fval = f;
268 	p->tval = t;
269 	p->csub = 0;
270 
271 	tp->nelem++;
272 	if (tp->nelem > FULLTAB * tp->size)
273 		rehash(tp);
274 	h = hash(n, tp->size);
275 	p->cnext = tp->tab[h];
276 	tp->tab[h] = p;
277 	dprintf(("setsymtab set %p: n=%s", (void *)p, p->nval));
278 	dprintf((" s=\"%s\" f=%g t=%p\n", p->sval, p->fval, (void *)p->tval));
279 	return (p);
280 }
281 
282 int
283 hash(uchar *s, int n)	/* form hash value for string s */
284 {
285 	register unsigned hashval;
286 
287 	for (hashval = 0; *s != '\0'; s++)
288 		hashval = (*s + 31 * hashval);
289 	return (hashval % n);
290 }
291 
292 static void
293 rehash(Array *tp)	/* rehash items in small table into big one */
294 {
295 	int i, nh, nsz;
296 	Cell *cp, *op, **np;
297 
298 	nsz = GROWTAB * tp->size;
299 	np = (Cell **)calloc(nsz, sizeof (Cell *));
300 	if (np == NULL)
301 		ERROR "out of space in rehash" FATAL;
302 	for (i = 0; i < tp->size; i++) {
303 		for (cp = tp->tab[i]; cp; cp = op) {
304 			op = cp->cnext;
305 			nh = hash(cp->nval, nsz);
306 			cp->cnext = np[nh];
307 			np[nh] = cp;
308 		}
309 	}
310 	free(tp->tab);
311 	tp->tab = np;
312 	tp->size = nsz;
313 }
314 
315 Cell *
316 lookup(uchar *s, Array *tp)	/* look for s in tp */
317 {
318 	register Cell *p;
319 	int h;
320 
321 	h = hash(s, tp->size);
322 	for (p = tp->tab[h]; p != NULL; p = p->cnext) {
323 		if (strcmp((char *)s, (char *)p->nval) == 0)
324 			return (p);	/* found it */
325 	}
326 	return (NULL);			/* not found */
327 }
328 
329 Awkfloat
330 setfval(Cell *vp, Awkfloat f)
331 {
332 	int	i;
333 
334 	if ((vp->tval & (NUM | STR)) == 0)
335 		funnyvar(vp, "assign to");
336 	if (vp->tval & FLD) {
337 		donerec = 0;	/* mark $0 invalid */
338 		i = fldidx(vp);
339 		if (i > *NF)
340 			newfld(i);
341 		dprintf(("setting field %d to %g\n", i, f));
342 	} else if (vp->tval & REC) {
343 		donefld = 0;	/* mark $1... invalid */
344 		donerec = 1;
345 	}
346 	vp->tval &= ~STR;	/* mark string invalid */
347 	vp->tval |= NUM;	/* mark number ok */
348 	dprintf(("setfval %p: %s = %g, t=%p\n", (void *)vp,
349 	    vp->nval ? vp->nval : (unsigned char *)"NULL",
350 	    f, (void *)vp->tval));
351 	return (vp->fval = f);
352 }
353 
354 void
355 funnyvar(Cell *vp, char *rw)
356 {
357 	if (vp->tval & ARR)
358 		ERROR "can't %s %s; it's an array name.", rw, vp->nval FATAL;
359 	if (vp->tval & FCN)
360 		ERROR "can't %s %s; it's a function.", rw, vp->nval FATAL;
361 	ERROR "funny variable %o: n=%s s=\"%s\" f=%g t=%o",
362 	    vp, vp->nval, vp->sval, vp->fval, vp->tval CONT;
363 }
364 
365 uchar *
366 setsval(Cell *vp, uchar *s)
367 {
368 	int	i;
369 
370 	if ((vp->tval & (NUM | STR)) == 0)
371 		funnyvar(vp, "assign to");
372 	if (vp->tval & FLD) {
373 		donerec = 0;	/* mark $0 invalid */
374 		i = fldidx(vp);
375 		if (i > *NF)
376 			newfld(i);
377 		dprintf(("setting field %d to %s\n", i, s));
378 	} else if (vp->tval & REC) {
379 		donefld = 0;	/* mark $1... invalid */
380 		donerec = 1;
381 	}
382 	vp->tval &= ~NUM;
383 	vp->tval |= STR;
384 	if (freeable(vp))
385 		xfree(vp->sval);
386 	vp->tval &= ~DONTFREE;
387 	dprintf(("setsval %p: %s = \"%s\", t=%p\n",
388 	    (void *)vp,
389 	    vp->nval ? (char *)vp->nval : "",
390 	    s,
391 	    (void *)(vp->tval ? (char *)vp->tval : "")));
392 	return (vp->sval = tostring(s));
393 }
394 
395 Awkfloat
396 r_getfval(Cell *vp)
397 {
398 	if ((vp->tval & (NUM | STR)) == 0)
399 		funnyvar(vp, "read value of");
400 	if ((vp->tval & FLD) && donefld == 0)
401 		fldbld();
402 	else if ((vp->tval & REC) && donerec == 0)
403 		recbld();
404 	if (!isnum(vp)) {	/* not a number */
405 		vp->fval = atof((const char *)vp->sval);	/* best guess */
406 		if (is_number(vp->sval) && !(vp->tval&CON))
407 			vp->tval |= NUM;	/* make NUM only sparingly */
408 	}
409 	dprintf(("getfval %p: %s = %g, t=%p\n",
410 	    (void *)vp, vp->nval, vp->fval, (void *)vp->tval));
411 	return (vp->fval);
412 }
413 
414 uchar *
415 r_getsval(Cell *vp)
416 {
417 	uchar s[256];
418 
419 	if ((vp->tval & (NUM | STR)) == 0)
420 		funnyvar(vp, "read value of");
421 	if ((vp->tval & FLD) && donefld == 0)
422 		fldbld();
423 	else if ((vp->tval & REC) && donerec == 0)
424 		recbld();
425 	if ((vp->tval & STR) == 0) {
426 		if (!(vp->tval&DONTFREE))
427 			xfree(vp->sval);
428 		if ((long long)vp->fval == vp->fval) {
429 			(void) snprintf((char *)s, sizeof (s),
430 			    "%.20g", vp->fval);
431 		} else {
432 			/*LINTED*/
433 			(void) snprintf((char *)s, sizeof (s),
434 			    (char *)*OFMT, vp->fval);
435 		}
436 		vp->sval = tostring(s);
437 		vp->tval &= ~DONTFREE;
438 		vp->tval |= STR;
439 	}
440 	dprintf(("getsval %p: %s = \"%s\", t=%p\n",
441 	    (void *)vp,
442 	    vp->nval ? (char *)vp->nval : "",
443 	    vp->sval ? (char *)vp->sval : "",
444 	    (void *)vp->tval));
445 	return (vp->sval);
446 }
447 
448 uchar *
449 tostring(uchar *s)
450 {
451 	register uchar *p;
452 
453 	p = (uchar *)malloc(strlen((char *)s)+1);
454 	if (p == NULL)
455 		ERROR "out of space in tostring on %s", s FATAL;
456 	(void) strcpy((char *)p, (char *)s);
457 	return (p);
458 }
459 
460 uchar *
461 qstring(uchar *s, int delim)	/* collect string up to delim */
462 {
463 	uchar *cbuf, *ret;
464 	int c, n;
465 	size_t	cbufsz, cnt;
466 
467 	init_buf(&cbuf, &cbufsz, LINE_INCR);
468 
469 	for (cnt = 0; (c = *s) != delim; s++) {
470 		if (c == '\n') {
471 			ERROR "newline in string %.10s...", cbuf SYNTAX;
472 		} else if (c != '\\') {
473 			expand_buf(&cbuf, &cbufsz, cnt);
474 			cbuf[cnt++] = c;
475 		} else {	/* \something */
476 			expand_buf(&cbuf, &cbufsz, cnt);
477 			switch (c = *++s) {
478 			case '\\':	cbuf[cnt++] = '\\'; break;
479 			case 'n':	cbuf[cnt++] = '\n'; break;
480 			case 't':	cbuf[cnt++] = '\t'; break;
481 			case 'b':	cbuf[cnt++] = '\b'; break;
482 			case 'f':	cbuf[cnt++] = '\f'; break;
483 			case 'r':	cbuf[cnt++] = '\r'; break;
484 			default:
485 				if (!isdigit(c)) {
486 					cbuf[cnt++] = c;
487 					break;
488 				}
489 				n = c - '0';
490 				if (isdigit(s[1])) {
491 					n = 8 * n + *++s - '0';
492 					if (isdigit(s[1]))
493 						n = 8 * n + *++s - '0';
494 				}
495 				cbuf[cnt++] = n;
496 				break;
497 			}
498 		}
499 	}
500 	cbuf[cnt] = '\0';
501 	ret = tostring(cbuf);
502 	free(cbuf);
503 	return (ret);
504 }
505