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