xref: /csrg-svn/usr.bin/pascal/src/proc.c (revision 9230)
1768Speter /* Copyright (c) 1979 Regents of the University of California */
2768Speter 
3*9230Smckusick static char sccsid[] = "@(#)proc.c 1.16 11/14/82";
4768Speter 
5768Speter #include "whoami.h"
6768Speter #ifdef OBJ
7768Speter     /*
8768Speter      *	and the rest of the file
9768Speter      */
10768Speter #include "0.h"
11768Speter #include "tree.h"
12768Speter #include "opcode.h"
13768Speter #include "objfmt.h"
14768Speter 
15768Speter /*
16*9230Smckusick  * The constant REALSPC defines the amount of forced padding preceeding
17*9230Smckusick  * real numbers when they are printed. If REALSPC == 0, then no padding
18*9230Smckusick  * is added, REALSPC == 1 adds one extra blank irregardless of the width
19*9230Smckusick  * specified by the user.
20*9230Smckusick  *
21*9230Smckusick  * N.B. - Values greater than one require program mods.
22*9230Smckusick  */
23*9230Smckusick #define	REALSPC	0
24*9230Smckusick 
25*9230Smckusick /*
26768Speter  * The following array is used to determine which classes may be read
27768Speter  * from textfiles. It is indexed by the return value from classify.
28768Speter  */
29768Speter #define rdops(x) rdxxxx[(x)-(TFIRST)]
30768Speter 
31768Speter int rdxxxx[] = {
32768Speter 	0,		/* -7 file types */
33768Speter 	0,		/* -6 record types */
34768Speter 	0,		/* -5 array types */
35768Speter 	O_READE,	/* -4 scalar types */
36768Speter 	0,		/* -3 pointer types */
37768Speter 	0,		/* -2 set types */
38768Speter 	0,		/* -1 string types */
39768Speter 	0,		/*  0 nil, no type */
40768Speter 	O_READE,	/*  1 boolean */
41768Speter 	O_READC,	/*  2 character */
42768Speter 	O_READ4,	/*  3 integer */
43768Speter 	O_READ8		/*  4 real */
44768Speter };
45768Speter 
46768Speter /*
47768Speter  * Proc handles procedure calls.
48768Speter  * Non-builtin procedures are "buck-passed" to func (with a flag
49768Speter  * indicating that they are actually procedures.
50768Speter  * builtin procedures are handled here.
51768Speter  */
52768Speter proc(r)
53768Speter 	int *r;
54768Speter {
55768Speter 	register struct nl *p;
56768Speter 	register int *alv, *al, op;
57768Speter 	struct nl *filetype, *ap;
58768Speter 	int argc, *argv, typ, fmtspec, strfmt, stkcnt, *file;
59768Speter 	char fmt, format[20], *strptr;
60768Speter 	int prec, field, strnglen, fmtlen, fmtstart, pu;
61768Speter 	int *pua, *pui, *puz;
62768Speter 	int i, j, k;
63768Speter 	int itemwidth;
643226Smckusic 	struct tmps soffset;
653851Speter 	struct nl	*tempnlp;
66768Speter 
67768Speter #define	CONPREC 4
68768Speter #define	VARPREC 8
69768Speter #define	CONWIDTH 1
70768Speter #define	VARWIDTH 2
71768Speter #define SKIP 16
72768Speter 
73768Speter 	/*
74768Speter 	 * Verify that the name is
75768Speter 	 * defined and is that of a
76768Speter 	 * procedure.
77768Speter 	 */
78768Speter 	p = lookup(r[2]);
79768Speter 	if (p == NIL) {
80768Speter 		rvlist(r[3]);
81768Speter 		return;
82768Speter 	}
831198Speter 	if (p->class != PROC && p->class != FPROC) {
84768Speter 		error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]);
85768Speter 		rvlist(r[3]);
86768Speter 		return;
87768Speter 	}
88768Speter 	argv = r[3];
89768Speter 
90768Speter 	/*
91768Speter 	 * Call handles user defined
92768Speter 	 * procedures and functions.
93768Speter 	 */
94768Speter 	if (bn != 0) {
95768Speter 		call(p, argv, PROC, bn);
96768Speter 		return;
97768Speter 	}
98768Speter 
99768Speter 	/*
100768Speter 	 * Call to built-in procedure.
101768Speter 	 * Count the arguments.
102768Speter 	 */
103768Speter 	argc = 0;
104768Speter 	for (al = argv; al != NIL; al = al[2])
105768Speter 		argc++;
106768Speter 
107768Speter 	/*
108768Speter 	 * Switch on the operator
109768Speter 	 * associated with the built-in
110768Speter 	 * procedure in the namelist
111768Speter 	 */
112768Speter 	op = p->value[0] &~ NSTAND;
113768Speter 	if (opt('s') && (p->value[0] & NSTAND)) {
114768Speter 		standard();
115768Speter 		error("%s is a nonstandard procedure", p->symbol);
116768Speter 	}
117768Speter 	switch (op) {
118768Speter 
119768Speter 	case O_ABORT:
120768Speter 		if (argc != 0)
121768Speter 			error("null takes no arguments");
122768Speter 		return;
123768Speter 
124768Speter 	case O_FLUSH:
125768Speter 		if (argc == 0) {
126768Speter 			put(1, O_MESSAGE);
127768Speter 			return;
128768Speter 		}
129768Speter 		if (argc != 1) {
130768Speter 			error("flush takes at most one argument");
131768Speter 			return;
132768Speter 		}
1332073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
134768Speter 		if (ap == NIL)
135768Speter 			return;
136768Speter 		if (ap->class != FILET) {
137768Speter 			error("flush's argument must be a file, not %s", nameof(ap));
138768Speter 			return;
139768Speter 		}
140768Speter 		put(1, op);
141768Speter 		return;
142768Speter 
143768Speter 	case O_MESSAGE:
144768Speter 	case O_WRITEF:
145768Speter 	case O_WRITLN:
146768Speter 		/*
147768Speter 		 * Set up default file "output"'s type
148768Speter 		 */
149768Speter 		file = NIL;
150768Speter 		filetype = nl+T1CHAR;
151768Speter 		/*
152768Speter 		 * Determine the file implied
153768Speter 		 * for the write and generate
154768Speter 		 * code to make it the active file.
155768Speter 		 */
156768Speter 		if (op == O_MESSAGE) {
157768Speter 			/*
158768Speter 			 * For message, all that matters
159768Speter 			 * is that the filetype is
160768Speter 			 * a character file.
161768Speter 			 * Thus "output" will suit us fine.
162768Speter 			 */
163768Speter 			put(1, O_MESSAGE);
164768Speter 		} else if (argv != NIL && (al = argv[1])[0] != T_WEXP) {
165768Speter 			/*
166768Speter 			 * If there is a first argument which has
167768Speter 			 * no write widths, then it is potentially
168768Speter 			 * a file name.
169768Speter 			 */
170768Speter 			codeoff();
171768Speter 			ap = stkrval(argv[1], NIL , RREQ );
172768Speter 			codeon();
173768Speter 			if (ap == NIL)
174768Speter 				argv = argv[2];
175768Speter 			if (ap != NIL && ap->class == FILET) {
176768Speter 				/*
177768Speter 				 * Got "write(f, ...", make
178768Speter 				 * f the active file, and save
179768Speter 				 * it and its type for use in
180768Speter 				 * processing the rest of the
181768Speter 				 * arguments to write.
182768Speter 				 */
183768Speter 				file = argv[1];
184768Speter 				filetype = ap->type;
1852073Smckusic 				stklval(argv[1], NIL , LREQ );
186768Speter 				put(1, O_UNIT);
187768Speter 				/*
188768Speter 				 * Skip over the first argument
189768Speter 				 */
190768Speter 				argv = argv[2];
191768Speter 				argc--;
1928538Speter 			} else {
193768Speter 				/*
194768Speter 				 * Set up for writing on
195768Speter 				 * standard output.
196768Speter 				 */
197768Speter 				put(1, O_UNITOUT);
1987953Speter 				output->nl_flags |= NUSED;
1998538Speter 			}
2008538Speter 		} else {
201768Speter 			put(1, O_UNITOUT);
2027953Speter 			output->nl_flags |= NUSED;
2038538Speter 		}
204768Speter 		/*
205768Speter 		 * Loop and process each
206768Speter 		 * of the arguments.
207768Speter 		 */
208768Speter 		for (; argv != NIL; argv = argv[2]) {
209768Speter 			/*
210768Speter 			 * fmtspec indicates the type (CONstant or VARiable)
211768Speter 			 *	and number (none, WIDTH, and/or PRECision)
212768Speter 			 *	of the fields in the printf format for this
213768Speter 			 *	output variable.
2143172Smckusic 			 * stkcnt is the number of bytes pushed on the stack
215768Speter 			 * fmt is the format output indicator (D, E, F, O, X, S)
216768Speter 			 * fmtstart = 0 for leading blank; = 1 for no blank
217768Speter 			 */
218768Speter 			fmtspec = NIL;
219768Speter 			stkcnt = 0;
220768Speter 			fmt = 'D';
221768Speter 			fmtstart = 1;
222768Speter 			al = argv[1];
223768Speter 			if (al == NIL)
224768Speter 				continue;
225768Speter 			if (al[0] == T_WEXP)
226768Speter 				alv = al[1];
227768Speter 			else
228768Speter 				alv = al;
229768Speter 			if (alv == NIL)
230768Speter 				continue;
231768Speter 			codeoff();
232768Speter 			ap = stkrval(alv, NIL , RREQ );
233768Speter 			codeon();
234768Speter 			if (ap == NIL)
235768Speter 				continue;
236768Speter 			typ = classify(ap);
237768Speter 			if (al[0] == T_WEXP) {
238768Speter 				/*
239768Speter 				 * Handle width expressions.
240768Speter 				 * The basic game here is that width
241768Speter 				 * expressions get evaluated. If they
242768Speter 				 * are constant, the value is placed
243768Speter 				 * directly in the format string.
244768Speter 				 * Otherwise the value is pushed onto
245768Speter 				 * the stack and an indirection is
246768Speter 				 * put into the format string.
247768Speter 				 */
248768Speter 				if (al[3] == OCT)
249768Speter 					fmt = 'O';
250768Speter 				else if (al[3] == HEX)
251768Speter 					fmt = 'X';
252768Speter 				else if (al[3] != NIL) {
253768Speter 					/*
254768Speter 					 * Evaluate second format spec
255768Speter 					 */
256768Speter 					if ( constval(al[3])
257768Speter 					    && isa( con.ctype , "i" ) ) {
258768Speter 						fmtspec += CONPREC;
259768Speter 						prec = con.crval;
260768Speter 					} else {
261768Speter 						fmtspec += VARPREC;
262768Speter 					}
263768Speter 					fmt = 'f';
264768Speter 					switch ( typ ) {
265768Speter 					case TINT:
266768Speter 						if ( opt( 's' ) ) {
267768Speter 						    standard();
268768Speter 						    error("Writing %ss with two write widths is non-standard", clnames[typ]);
269768Speter 						}
270768Speter 						/* and fall through */
271768Speter 					case TDOUBLE:
272768Speter 						break;
273768Speter 					default:
274768Speter 						error("Cannot write %ss with two write widths", clnames[typ]);
275768Speter 						continue;
276768Speter 					}
277768Speter 				}
278768Speter 				/*
279768Speter 				 * Evaluate first format spec
280768Speter 				 */
281768Speter 				if (al[2] != NIL) {
282768Speter 					if ( constval(al[2])
283768Speter 					    && isa( con.ctype , "i" ) ) {
284768Speter 						fmtspec += CONWIDTH;
285768Speter 						field = con.crval;
286768Speter 					} else {
287768Speter 						fmtspec += VARWIDTH;
288768Speter 					}
289768Speter 				}
290768Speter 				if ((fmtspec & CONPREC) && prec < 0 ||
291768Speter 				    (fmtspec & CONWIDTH) && field < 0) {
292768Speter 					error("Negative widths are not allowed");
293768Speter 					continue;
294768Speter 				}
2953179Smckusic 				if ( opt('s') &&
2963179Smckusic 				    ((fmtspec & CONPREC) && prec == 0 ||
2973179Smckusic 				    (fmtspec & CONWIDTH) && field == 0)) {
2983179Smckusic 					standard();
2993179Smckusic 					error("Zero widths are non-standard");
3003179Smckusic 				}
301768Speter 			}
302768Speter 			if (filetype != nl+T1CHAR) {
303768Speter 				if (fmt == 'O' || fmt == 'X') {
304768Speter 					error("Oct/hex allowed only on text files");
305768Speter 					continue;
306768Speter 				}
307768Speter 				if (fmtspec) {
308768Speter 					error("Write widths allowed only on text files");
309768Speter 					continue;
310768Speter 				}
311768Speter 				/*
312768Speter 				 * Generalized write, i.e.
313768Speter 				 * to a non-textfile.
314768Speter 				 */
3152073Smckusic 				stklval(file, NIL , LREQ );
316768Speter 				put(1, O_FNIL);
317768Speter 				/*
318768Speter 				 * file^ := ...
319768Speter 				 */
320768Speter 				ap = rvalue(argv[1], NIL);
321768Speter 				if (ap == NIL)
322768Speter 					continue;
323768Speter 				if (incompat(ap, filetype, argv[1])) {
324768Speter 					cerror("Type mismatch in write to non-text file");
325768Speter 					continue;
326768Speter 				}
327768Speter 				convert(ap, filetype);
328768Speter 				put(2, O_AS, width(filetype));
329768Speter 				/*
330768Speter 				 * put(file)
331768Speter 				 */
332768Speter 				put(1, O_PUT);
333768Speter 				continue;
334768Speter 			}
335768Speter 			/*
336768Speter 			 * Write to a textfile
337768Speter 			 *
338768Speter 			 * Evaluate the expression
339768Speter 			 * to be written.
340768Speter 			 */
341768Speter 			if (fmt == 'O' || fmt == 'X') {
342768Speter 				if (opt('s')) {
343768Speter 					standard();
344768Speter 					error("Oct and hex are non-standard");
345768Speter 				}
346768Speter 				if (typ == TSTR || typ == TDOUBLE) {
347768Speter 					error("Can't write %ss with oct/hex", clnames[typ]);
348768Speter 					continue;
349768Speter 				}
350768Speter 				if (typ == TCHAR || typ == TBOOL)
351768Speter 					typ = TINT;
352768Speter 			}
353768Speter 			/*
354768Speter 			 * Place the arguement on the stack. If there is
355768Speter 			 * no format specified by the programmer, implement
356768Speter 			 * the default.
357768Speter 			 */
358768Speter 			switch (typ) {
3596542Smckusick 			case TPTR:
3606542Smckusick 				warning();
3616542Smckusick 				if (opt('s')) {
3626542Smckusick 					standard();
3636542Smckusick 				}
3646542Smckusick 				error("Writing %ss to text files is non-standard",
3656542Smckusick 				    clnames[typ]);
3666542Smckusick 				/* and fall through */
367768Speter 			case TINT:
368768Speter 				if (fmt != 'f') {
369768Speter 					ap = stkrval(alv, NIL , RREQ );
3703172Smckusic 					stkcnt += sizeof(long);
371768Speter 				} else {
372768Speter 					ap = stkrval(alv, NIL , RREQ );
373768Speter 					put(1, O_ITOD);
3743172Smckusic 					stkcnt += sizeof(double);
375768Speter 					typ = TDOUBLE;
376768Speter 					goto tdouble;
377768Speter 				}
378768Speter 				if (fmtspec == NIL) {
379768Speter 					if (fmt == 'D')
380768Speter 						field = 10;
381768Speter 					else if (fmt == 'X')
382768Speter 						field = 8;
383768Speter 					else if (fmt == 'O')
384768Speter 						field = 11;
385768Speter 					else
386768Speter 						panic("fmt1");
387768Speter 					fmtspec = CONWIDTH;
388768Speter 				}
389768Speter 				break;
390768Speter 			case TCHAR:
391768Speter 			     tchar:
3922073Smckusic 				if (fmtspec == NIL) {
3932073Smckusic 					put(1, O_FILE);
3942073Smckusic 					ap = stkrval(alv, NIL , RREQ );
3953172Smckusic 					convert(nl + T4INT, INT_TYP);
3963172Smckusic 					put(2, O_WRITEC,
3973172Smckusic 						sizeof(char *) + sizeof(int));
3982073Smckusic 					fmtspec = SKIP;
3992073Smckusic 					break;
4002073Smckusic 				}
401768Speter 				ap = stkrval(alv, NIL , RREQ );
4023172Smckusic 				convert(nl + T4INT, INT_TYP);
4033172Smckusic 				stkcnt += sizeof(int);
404768Speter 				fmt = 'c';
405768Speter 				break;
406768Speter 			case TSCAL:
4071628Speter 				warning();
408768Speter 				if (opt('s')) {
409768Speter 					standard();
410768Speter 				}
4116542Smckusick 				error("Writing %ss to text files is non-standard",
4126542Smckusick 				    clnames[typ]);
4136542Smckusick 				/* and fall through */
414768Speter 			case TBOOL:
415768Speter 				stkrval(alv, NIL , RREQ );
4163076Smckusic 				put(2, O_NAM, (long)listnames(ap));
4173172Smckusic 				stkcnt += sizeof(char *);
418768Speter 				fmt = 's';
419768Speter 				break;
420768Speter 			case TDOUBLE:
421768Speter 				ap = stkrval(alv, TDOUBLE , RREQ );
4223172Smckusic 				stkcnt += sizeof(double);
423768Speter 			     tdouble:
424768Speter 				switch (fmtspec) {
425768Speter 				case NIL:
4263076Smckusic #					ifdef DEC11
4273076Smckusic 					    field = 21;
4283076Smckusic #					else
4293076Smckusic 					    field = 22;
4303076Smckusic #					endif DEC11
431768Speter 					prec = 14;
4323076Smckusic 					fmt = 'e';
433768Speter 					fmtspec = CONWIDTH + CONPREC;
434768Speter 					break;
435768Speter 				case CONWIDTH:
436*9230Smckusick 					field -= REALSPC;
437*9230Smckusick 					if (field < 1)
438768Speter 						field = 1;
4393076Smckusic #					ifdef DEC11
4403076Smckusic 					    prec = field - 7;
4413076Smckusic #					else
4423076Smckusic 					    prec = field - 8;
4433076Smckusic #					endif DEC11
444768Speter 					if (prec < 1)
445768Speter 						prec = 1;
446768Speter 					fmtspec += CONPREC;
4473076Smckusic 					fmt = 'e';
448768Speter 					break;
449768Speter 				case CONWIDTH + CONPREC:
450768Speter 				case CONWIDTH + VARPREC:
451*9230Smckusick 					field -= REALSPC;
452*9230Smckusick 					if (field < 1)
453768Speter 						field = 1;
454768Speter 				}
455768Speter 				format[0] = ' ';
456*9230Smckusick 				fmtstart = 1 - REALSPC;
457768Speter 				break;
458768Speter 			case TSTR:
459768Speter 				constval( alv );
460768Speter 				switch ( classify( con.ctype ) ) {
461768Speter 				    case TCHAR:
462768Speter 					typ = TCHAR;
463768Speter 					goto tchar;
464768Speter 				    case TSTR:
465768Speter 					strptr = con.cpval;
466768Speter 					for (strnglen = 0;  *strptr++;  strnglen++) /* void */;
467768Speter 					strptr = con.cpval;
468768Speter 					break;
469768Speter 				    default:
470768Speter 					strnglen = width(ap);
471768Speter 					break;
472768Speter 				}
473768Speter 				fmt = 's';
474768Speter 				strfmt = fmtspec;
475768Speter 				if (fmtspec == NIL) {
476768Speter 					fmtspec = SKIP;
477768Speter 					break;
478768Speter 				}
479768Speter 				if (fmtspec & CONWIDTH) {
480768Speter 					if (field <= strnglen) {
481768Speter 						fmtspec = SKIP;
482768Speter 						break;
483768Speter 					} else
484768Speter 						field -= strnglen;
485768Speter 				}
486768Speter 				/*
487768Speter 				 * push string to implement leading blank padding
488768Speter 				 */
489768Speter 				put(2, O_LVCON, 2);
490768Speter 				putstr("", 0);
4913172Smckusic 				stkcnt += sizeof(char *);
492768Speter 				break;
493768Speter 			default:
494768Speter 				error("Can't write %ss to a text file", clnames[typ]);
495768Speter 				continue;
496768Speter 			}
497768Speter 			/*
498768Speter 			 * If there is a variable precision, evaluate it onto
499768Speter 			 * the stack
500768Speter 			 */
501768Speter 			if (fmtspec & VARPREC) {
502768Speter 				ap = stkrval(al[3], NIL , RREQ );
503768Speter 				if (ap == NIL)
504768Speter 					continue;
505768Speter 				if (isnta(ap,"i")) {
506768Speter 					error("Second write width must be integer, not %s", nameof(ap));
507768Speter 					continue;
508768Speter 				}
509768Speter 				if ( opt( 't' ) ) {
510768Speter 				    put(3, O_MAX, 0, 0);
511768Speter 				}
5123172Smckusic 				convert(nl+T4INT, INT_TYP);
5133172Smckusic 				stkcnt += sizeof(int);
514768Speter 			}
515768Speter 			/*
516768Speter 			 * If there is a variable width, evaluate it onto
517768Speter 			 * the stack
518768Speter 			 */
519768Speter 			if (fmtspec & VARWIDTH) {
520768Speter 				if ( ( typ == TDOUBLE && fmtspec == VARWIDTH )
521768Speter 				    || typ == TSTR ) {
5223226Smckusic 					soffset = sizes[cbn].curtmps;
5233851Speter 					tempnlp = tmpalloc(sizeof(long),
5243226Smckusic 						nl+T4INT, REGOK);
5253851Speter 					put(2, O_LV | cbn << 8 + INDX,
5263851Speter 					    tempnlp -> value[ NL_OFFS ] );
527768Speter 				}
528768Speter 				ap = stkrval(al[2], NIL , RREQ );
529768Speter 				if (ap == NIL)
530768Speter 					continue;
531768Speter 				if (isnta(ap,"i")) {
532768Speter 					error("First write width must be integer, not %s", nameof(ap));
533768Speter 					continue;
534768Speter 				}
535768Speter 				/*
536768Speter 				 * Perform special processing on widths based
537768Speter 				 * on data type
538768Speter 				 */
539768Speter 				switch (typ) {
540768Speter 				case TDOUBLE:
541768Speter 					if (fmtspec == VARWIDTH) {
5423076Smckusic 						fmt = 'e';
543768Speter 						put(1, O_AS4);
5443851Speter 						put(2, O_RV4 | cbn << 8 + INDX,
5453851Speter 						    tempnlp -> value[NL_OFFS] );
5463076Smckusic #						ifdef DEC11
547*9230Smckusick 						    put(3, O_MAX, 7 + REALSPC, 1);
5483076Smckusic #						else
549*9230Smckusick 						    put(3, O_MAX, 8 + REALSPC, 1);
5503076Smckusic #						endif DEC11
5513172Smckusic 						convert(nl+T4INT, INT_TYP);
5523172Smckusic 						stkcnt += sizeof(int);
5533851Speter 						put(2, O_RV4 | cbn << 8 + INDX,
5543851Speter 						    tempnlp->value[NL_OFFS] );
555768Speter 						fmtspec += VARPREC;
5563226Smckusic 						tmpfree(&soffset);
557768Speter 					}
558*9230Smckusick 					put(3, O_MAX, REALSPC, 1);
559768Speter 					break;
560768Speter 				case TSTR:
561768Speter 					put(1, O_AS4);
5623851Speter 					put(2, O_RV4 | cbn << 8 + INDX,
5633851Speter 					    tempnlp -> value[ NL_OFFS ] );
564768Speter 					put(3, O_MAX, strnglen, 0);
565768Speter 					break;
566768Speter 				default:
567768Speter 					if ( opt( 't' ) ) {
568768Speter 					    put(3, O_MAX, 0, 0);
569768Speter 					}
570768Speter 					break;
571768Speter 				}
5723172Smckusic 				convert(nl+T4INT, INT_TYP);
5733172Smckusic 				stkcnt += sizeof(int);
574768Speter 			}
575768Speter 			/*
576768Speter 			 * Generate the format string
577768Speter 			 */
578768Speter 			switch (fmtspec) {
579768Speter 			default:
580768Speter 				panic("fmt2");
581768Speter 			case SKIP:
582768Speter 				break;
5832073Smckusic 			case NIL:
5842073Smckusic 				sprintf(&format[1], "%%%c", fmt);
5852073Smckusic 				goto fmtgen;
586768Speter 			case CONWIDTH:
5873076Smckusic 				sprintf(&format[1], "%%%d%c", field, fmt);
588768Speter 				goto fmtgen;
589768Speter 			case VARWIDTH:
590768Speter 				sprintf(&format[1], "%%*%c", fmt);
591768Speter 				goto fmtgen;
592768Speter 			case CONWIDTH + CONPREC:
5933076Smckusic 				sprintf(&format[1], "%%%d.%d%c", field, prec, fmt);
594768Speter 				goto fmtgen;
595768Speter 			case CONWIDTH + VARPREC:
5963076Smckusic 				sprintf(&format[1], "%%%d.*%c", field, fmt);
597768Speter 				goto fmtgen;
598768Speter 			case VARWIDTH + CONPREC:
5993076Smckusic 				sprintf(&format[1], "%%*.%d%c", prec, fmt);
600768Speter 				goto fmtgen;
601768Speter 			case VARWIDTH + VARPREC:
602768Speter 				sprintf(&format[1], "%%*.*%c", fmt);
603768Speter 			fmtgen:
604768Speter 				fmtlen = lenstr(&format[fmtstart], 0);
605768Speter 				put(2, O_LVCON, fmtlen);
606768Speter 				putstr(&format[fmtstart], 0);
607768Speter 				put(1, O_FILE);
6083172Smckusic 				stkcnt += 2 * sizeof(char *);
609768Speter 				put(2, O_WRITEF, stkcnt);
610768Speter 			}
611768Speter 			/*
612768Speter 			 * Write the string after its blank padding
613768Speter 			 */
614768Speter 			if (typ == TSTR) {
615768Speter 				put(1, O_FILE);
6163172Smckusic 				put(2, CON_INT, 1);
617768Speter 				if (strfmt & VARWIDTH) {
6183851Speter 					put(2, O_RV4 | cbn << 8 + INDX ,
6193851Speter 					    tempnlp -> value[ NL_OFFS ] );
620768Speter 					put(2, O_MIN, strnglen);
6213172Smckusic 					convert(nl+T4INT, INT_TYP);
6223226Smckusic 					tmpfree(&soffset);
623768Speter 				} else {
624768Speter 					if ((fmtspec & SKIP) &&
625768Speter 					   (strfmt & CONWIDTH)) {
626768Speter 						strnglen = field;
627768Speter 					}
6283172Smckusic 					put(2, CON_INT, strnglen);
629768Speter 				}
630768Speter 				ap = stkrval(alv, NIL , RREQ );
6313172Smckusic 				put(2, O_WRITES,
6323172Smckusic 					2 * sizeof(char *) + 2 * sizeof(int));
633768Speter 			}
634768Speter 		}
635768Speter 		/*
636768Speter 		 * Done with arguments.
637768Speter 		 * Handle writeln and
638768Speter 		 * insufficent number of args.
639768Speter 		 */
640768Speter 		switch (p->value[0] &~ NSTAND) {
641768Speter 			case O_WRITEF:
642768Speter 				if (argc == 0)
643768Speter 					error("Write requires an argument");
644768Speter 				break;
645768Speter 			case O_MESSAGE:
646768Speter 				if (argc == 0)
647768Speter 					error("Message requires an argument");
648768Speter 			case O_WRITLN:
649768Speter 				if (filetype != nl+T1CHAR)
650768Speter 					error("Can't 'writeln' a non text file");
651768Speter 				put(1, O_WRITLN);
652768Speter 				break;
653768Speter 		}
654768Speter 		return;
655768Speter 
656768Speter 	case O_READ4:
657768Speter 	case O_READLN:
658768Speter 		/*
659768Speter 		 * Set up default
660768Speter 		 * file "input".
661768Speter 		 */
662768Speter 		file = NIL;
663768Speter 		filetype = nl+T1CHAR;
664768Speter 		/*
665768Speter 		 * Determine the file implied
666768Speter 		 * for the read and generate
667768Speter 		 * code to make it the active file.
668768Speter 		 */
669768Speter 		if (argv != NIL) {
670768Speter 			codeoff();
671768Speter 			ap = stkrval(argv[1], NIL , RREQ );
672768Speter 			codeon();
673768Speter 			if (ap == NIL)
674768Speter 				argv = argv[2];
675768Speter 			if (ap != NIL && ap->class == FILET) {
676768Speter 				/*
677768Speter 				 * Got "read(f, ...", make
678768Speter 				 * f the active file, and save
679768Speter 				 * it and its type for use in
680768Speter 				 * processing the rest of the
681768Speter 				 * arguments to read.
682768Speter 				 */
683768Speter 				file = argv[1];
684768Speter 				filetype = ap->type;
6852073Smckusic 				stklval(argv[1], NIL , LREQ );
686768Speter 				put(1, O_UNIT);
687768Speter 				argv = argv[2];
688768Speter 				argc--;
689768Speter 			} else {
690768Speter 				/*
691768Speter 				 * Default is read from
692768Speter 				 * standard input.
693768Speter 				 */
694768Speter 				put(1, O_UNITINP);
695768Speter 				input->nl_flags |= NUSED;
696768Speter 			}
697768Speter 		} else {
698768Speter 			put(1, O_UNITINP);
699768Speter 			input->nl_flags |= NUSED;
700768Speter 		}
701768Speter 		/*
702768Speter 		 * Loop and process each
703768Speter 		 * of the arguments.
704768Speter 		 */
705768Speter 		for (; argv != NIL; argv = argv[2]) {
706768Speter 			/*
707768Speter 			 * Get the address of the target
708768Speter 			 * on the stack.
709768Speter 			 */
710768Speter 			al = argv[1];
711768Speter 			if (al == NIL)
712768Speter 				continue;
713768Speter 			if (al[0] != T_VAR) {
714768Speter 				error("Arguments to %s must be variables, not expressions", p->symbol);
715768Speter 				continue;
716768Speter 			}
717768Speter 			ap = stklval(al, MOD|ASGN|NOUSE);
718768Speter 			if (ap == NIL)
719768Speter 				continue;
720768Speter 			if (filetype != nl+T1CHAR) {
721768Speter 				/*
722768Speter 				 * Generalized read, i.e.
723768Speter 				 * from a non-textfile.
724768Speter 				 */
725768Speter 				if (incompat(filetype, ap, argv[1] )) {
726768Speter 					error("Type mismatch in read from non-text file");
727768Speter 					continue;
728768Speter 				}
729768Speter 				/*
730768Speter 				 * var := file ^;
731768Speter 				 */
732768Speter 				if (file != NIL)
7332073Smckusic 					stklval(file, NIL , LREQ );
734768Speter 				else /* Magic */
7353076Smckusic 					put(2, PTR_RV, (int)input->value[0]);
736768Speter 				put(1, O_FNIL);
737768Speter 				put(2, O_IND, width(filetype));
738768Speter 				convert(filetype, ap);
739768Speter 				if (isa(ap, "bsci"))
740768Speter 					rangechk(ap, ap);
741768Speter 				put(2, O_AS, width(ap));
742768Speter 				/*
743768Speter 				 * get(file);
744768Speter 				 */
745768Speter 				put(1, O_GET);
746768Speter 				continue;
747768Speter 			}
748768Speter 			typ = classify(ap);
749768Speter 			op = rdops(typ);
750768Speter 			if (op == NIL) {
751768Speter 				error("Can't read %ss from a text file", clnames[typ]);
752768Speter 				continue;
753768Speter 			}
754768Speter 			if (op != O_READE)
755768Speter 				put(1, op);
756768Speter 			else {
7573076Smckusic 				put(2, op, (long)listnames(ap));
7581628Speter 				warning();
759768Speter 				if (opt('s')) {
760768Speter 					standard();
761768Speter 				}
7621628Speter 				error("Reading scalars from text files is non-standard");
763768Speter 			}
764768Speter 			/*
765768Speter 			 * Data read is on the stack.
766768Speter 			 * Assign it.
767768Speter 			 */
768768Speter 			if (op != O_READ8 && op != O_READE)
769768Speter 				rangechk(ap, op == O_READC ? ap : nl+T4INT);
770768Speter 			gen(O_AS2, O_AS2, width(ap),
771768Speter 				op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2);
772768Speter 		}
773768Speter 		/*
774768Speter 		 * Done with arguments.
775768Speter 		 * Handle readln and
776768Speter 		 * insufficient number of args.
777768Speter 		 */
778768Speter 		if (p->value[0] == O_READLN) {
779768Speter 			if (filetype != nl+T1CHAR)
780768Speter 				error("Can't 'readln' a non text file");
781768Speter 			put(1, O_READLN);
782768Speter 		}
783768Speter 		else if (argc == 0)
784768Speter 			error("read requires an argument");
785768Speter 		return;
786768Speter 
787768Speter 	case O_GET:
788768Speter 	case O_PUT:
789768Speter 		if (argc != 1) {
790768Speter 			error("%s expects one argument", p->symbol);
791768Speter 			return;
792768Speter 		}
7932073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
794768Speter 		if (ap == NIL)
795768Speter 			return;
796768Speter 		if (ap->class != FILET) {
797768Speter 			error("Argument to %s must be a file, not %s", p->symbol, nameof(ap));
798768Speter 			return;
799768Speter 		}
800768Speter 		put(1, O_UNIT);
801768Speter 		put(1, op);
802768Speter 		return;
803768Speter 
804768Speter 	case O_RESET:
805768Speter 	case O_REWRITE:
806768Speter 		if (argc == 0 || argc > 2) {
807768Speter 			error("%s expects one or two arguments", p->symbol);
808768Speter 			return;
809768Speter 		}
810768Speter 		if (opt('s') && argc == 2) {
811768Speter 			standard();
812768Speter 			error("Two argument forms of reset and rewrite are non-standard");
813768Speter 		}
8142073Smckusic 		codeoff();
815768Speter 		ap = stklval(argv[1], MOD|NOUSE);
8162073Smckusic 		codeon();
817768Speter 		if (ap == NIL)
818768Speter 			return;
819768Speter 		if (ap->class != FILET) {
820768Speter 			error("First argument to %s must be a file, not %s", p->symbol, nameof(ap));
821768Speter 			return;
822768Speter 		}
8232073Smckusic 		put(2, O_CON24, text(ap) ? 0: width(ap->type));
824768Speter 		if (argc == 2) {
825768Speter 			/*
826768Speter 			 * Optional second argument
827768Speter 			 * is a string name of a
828768Speter 			 * UNIX (R) file to be associated.
829768Speter 			 */
830768Speter 			al = argv[2];
8312073Smckusic 			codeoff();
832768Speter 			al = stkrval(al[1], NOFLAGS , RREQ );
8332073Smckusic 			codeon();
834768Speter 			if (al == NIL)
835768Speter 				return;
836768Speter 			if (classify(al) != TSTR) {
837768Speter 				error("Second argument to %s must be a string, not %s", p->symbol, nameof(al));
838768Speter 				return;
839768Speter 			}
8402073Smckusic 			put(2, O_CON24, width(al));
8412073Smckusic 			al = argv[2];
8422073Smckusic 			al = stkrval(al[1], NOFLAGS , RREQ );
843768Speter 		} else {
8442073Smckusic 			put(2, O_CON24, 0);
8453076Smckusic 			put(2, PTR_CON, NIL);
846768Speter 		}
8472073Smckusic 		ap = stklval(argv[1], MOD|NOUSE);
848768Speter 		put(1, op);
849768Speter 		return;
850768Speter 
851768Speter 	case O_NEW:
852768Speter 	case O_DISPOSE:
853768Speter 		if (argc == 0) {
854768Speter 			error("%s expects at least one argument", p->symbol);
855768Speter 			return;
856768Speter 		}
857768Speter 		ap = stklval(argv[1], op == O_NEW ? ( MOD | NOUSE ) : MOD );
858768Speter 		if (ap == NIL)
859768Speter 			return;
860768Speter 		if (ap->class != PTR) {
861768Speter 			error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap));
862768Speter 			return;
863768Speter 		}
864768Speter 		ap = ap->type;
865768Speter 		if (ap == NIL)
866768Speter 			return;
8677966Smckusick 		if ((ap->nl_flags & NFILES) && op == O_DISPOSE)
8687966Smckusick 			op = O_DFDISP;
869768Speter 		argv = argv[2];
870768Speter 		if (argv != NIL) {
871768Speter 			if (ap->class != RECORD) {
872768Speter 				error("Record required when specifying variant tags");
873768Speter 				return;
874768Speter 			}
875768Speter 			for (; argv != NIL; argv = argv[2]) {
876768Speter 				if (ap->ptr[NL_VARNT] == NIL) {
877768Speter 					error("Too many tag fields");
878768Speter 					return;
879768Speter 				}
880768Speter 				if (!isconst(argv[1])) {
881768Speter 					error("Second and successive arguments to %s must be constants", p->symbol);
882768Speter 					return;
883768Speter 				}
884768Speter 				gconst(argv[1]);
885768Speter 				if (con.ctype == NIL)
886768Speter 					return;
887768Speter 				if (incompat(con.ctype, (ap->ptr[NL_TAG])->type , NIL )) {
888768Speter 					cerror("Specified tag constant type clashed with variant case selector type");
889768Speter 					return;
890768Speter 				}
891768Speter 				for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain)
892768Speter 					if (ap->range[0] == con.crval)
893768Speter 						break;
894768Speter 				if (ap == NIL) {
895768Speter 					error("No variant case label value equals specified constant value");
896768Speter 					return;
897768Speter 				}
898768Speter 				ap = ap->ptr[NL_VTOREC];
899768Speter 			}
900768Speter 		}
901768Speter 		put(2, op, width(ap));
902768Speter 		return;
903768Speter 
904768Speter 	case O_DATE:
905768Speter 	case O_TIME:
906768Speter 		if (argc != 1) {
907768Speter 			error("%s expects one argument", p->symbol);
908768Speter 			return;
909768Speter 		}
910768Speter 		ap = stklval(argv[1], MOD|NOUSE);
911768Speter 		if (ap == NIL)
912768Speter 			return;
913768Speter 		if (classify(ap) != TSTR || width(ap) != 10) {
914768Speter 			error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap));
915768Speter 			return;
916768Speter 		}
917768Speter 		put(1, op);
918768Speter 		return;
919768Speter 
920768Speter 	case O_HALT:
921768Speter 		if (argc != 0) {
922768Speter 			error("halt takes no arguments");
923768Speter 			return;
924768Speter 		}
925768Speter 		put(1, op);
926768Speter 		noreach = 1;
927768Speter 		return;
928768Speter 
929768Speter 	case O_ARGV:
930768Speter 		if (argc != 2) {
931768Speter 			error("argv takes two arguments");
932768Speter 			return;
933768Speter 		}
934768Speter 		ap = stkrval(argv[1], NIL , RREQ );
935768Speter 		if (ap == NIL)
936768Speter 			return;
937768Speter 		if (isnta(ap, "i")) {
938768Speter 			error("argv's first argument must be an integer, not %s", nameof(ap));
939768Speter 			return;
940768Speter 		}
941768Speter 		al = argv[2];
942768Speter 		ap = stklval(al[1], MOD|NOUSE);
943768Speter 		if (ap == NIL)
944768Speter 			return;
945768Speter 		if (classify(ap) != TSTR) {
946768Speter 			error("argv's second argument must be a string, not %s", nameof(ap));
947768Speter 			return;
948768Speter 		}
949768Speter 		put(2, op, width(ap));
950768Speter 		return;
951768Speter 
952768Speter 	case O_STLIM:
953768Speter 		if (argc != 1) {
954768Speter 			error("stlimit requires one argument");
955768Speter 			return;
956768Speter 		}
957768Speter 		ap = stkrval(argv[1], NIL , RREQ );
958768Speter 		if (ap == NIL)
959768Speter 			return;
960768Speter 		if (isnta(ap, "i")) {
961768Speter 			error("stlimit's argument must be an integer, not %s", nameof(ap));
962768Speter 			return;
963768Speter 		}
964768Speter 		if (width(ap) != 4)
965768Speter 			put(1, O_STOI);
966768Speter 		put(1, op);
967768Speter 		return;
968768Speter 
969768Speter 	case O_REMOVE:
970768Speter 		if (argc != 1) {
971768Speter 			error("remove expects one argument");
972768Speter 			return;
973768Speter 		}
9742073Smckusic 		codeoff();
975768Speter 		ap = stkrval(argv[1], NOFLAGS , RREQ );
9762073Smckusic 		codeon();
977768Speter 		if (ap == NIL)
978768Speter 			return;
979768Speter 		if (classify(ap) != TSTR) {
980768Speter 			error("remove's argument must be a string, not %s", nameof(ap));
981768Speter 			return;
982768Speter 		}
983768Speter 		put(2, O_CON24, width(ap));
9842073Smckusic 		ap = stkrval(argv[1], NOFLAGS , RREQ );
985768Speter 		put(1, op);
986768Speter 		return;
987768Speter 
988768Speter 	case O_LLIMIT:
989768Speter 		if (argc != 2) {
990768Speter 			error("linelimit expects two arguments");
991768Speter 			return;
992768Speter 		}
993768Speter 		al = argv[2];
994768Speter 		ap = stkrval(al[1], NIL , RREQ );
995768Speter 		if (ap == NIL)
996768Speter 			return;
997768Speter 		if (isnta(ap, "i")) {
998768Speter 			error("linelimit's second argument must be an integer, not %s", nameof(ap));
999768Speter 			return;
1000768Speter 		}
10012073Smckusic 		ap = stklval(argv[1], NOFLAGS|NOUSE);
10022073Smckusic 		if (ap == NIL)
10032073Smckusic 			return;
10042073Smckusic 		if (!text(ap)) {
10052073Smckusic 			error("linelimit's first argument must be a text file, not %s", nameof(ap));
10062073Smckusic 			return;
10072073Smckusic 		}
1008768Speter 		put(1, op);
1009768Speter 		return;
1010768Speter 	case O_PAGE:
1011768Speter 		if (argc != 1) {
1012768Speter 			error("page expects one argument");
1013768Speter 			return;
1014768Speter 		}
10152073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
1016768Speter 		if (ap == NIL)
1017768Speter 			return;
1018768Speter 		if (!text(ap)) {
1019768Speter 			error("Argument to page must be a text file, not %s", nameof(ap));
1020768Speter 			return;
1021768Speter 		}
1022768Speter 		put(1, O_UNIT);
1023768Speter 		put(1, op);
1024768Speter 		return;
1025768Speter 
10267928Smckusick 	case O_ASRT:
10277928Smckusick 		if (!opt('t'))
10287928Smckusick 			return;
10297928Smckusick 		if (argc == 0 || argc > 2) {
10307928Smckusick 			error("Assert expects one or two arguments");
10317928Smckusick 			return;
10327928Smckusick 		}
10337928Smckusick 		if (argc == 2) {
10347928Smckusick 			/*
10357928Smckusick 			 * Optional second argument is a string specifying
10367928Smckusick 			 * why the assertion failed.
10377928Smckusick 			 */
10387928Smckusick 			al = argv[2];
10397928Smckusick 			al = stkrval(al[1], NIL , RREQ );
10407928Smckusick 			if (al == NIL)
10417928Smckusick 				return;
10427928Smckusick 			if (classify(al) != TSTR) {
10437928Smckusick 				error("Second argument to assert must be a string, not %s", nameof(al));
10447928Smckusick 				return;
10457928Smckusick 			}
10467928Smckusick 		} else {
10477928Smckusick 			put(2, PTR_CON, NIL);
10487928Smckusick 		}
10497928Smckusick 		ap = stkrval(argv[1], NIL , RREQ );
10507928Smckusick 		if (ap == NIL)
10517928Smckusick 			return;
10527928Smckusick 		if (isnta(ap, "b"))
10537928Smckusick 			error("Assert expression must be Boolean, not %ss", nameof(ap));
10547928Smckusick 		put(1, O_ASRT);
10557928Smckusick 		return;
10567928Smckusick 
1057768Speter 	case O_PACK:
1058768Speter 		if (argc != 3) {
1059768Speter 			error("pack expects three arguments");
1060768Speter 			return;
1061768Speter 		}
1062768Speter 		pu = "pack(a,i,z)";
10633076Smckusic 		pua = argv[1];
10643076Smckusic 		al = argv[2];
10653076Smckusic 		pui = al[1];
10663076Smckusic 		alv = al[2];
10673076Smckusic 		puz = alv[1];
1068768Speter 		goto packunp;
1069768Speter 	case O_UNPACK:
1070768Speter 		if (argc != 3) {
1071768Speter 			error("unpack expects three arguments");
1072768Speter 			return;
1073768Speter 		}
1074768Speter 		pu = "unpack(z,a,i)";
10753076Smckusic 		puz = argv[1];
10763076Smckusic 		al = argv[2];
10773076Smckusic 		pua = al[1];
10783076Smckusic 		alv = al[2];
10793076Smckusic 		pui = alv[1];
1080768Speter packunp:
10812073Smckusic 		codeoff();
1082768Speter 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
10832073Smckusic 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
10842073Smckusic 		codeon();
1085768Speter 		if (ap == NIL)
1086768Speter 			return;
1087768Speter 		if (ap->class != ARRAY) {
1088768Speter 			error("%s requires a to be an unpacked array, not %s", pu, nameof(ap));
1089768Speter 			return;
1090768Speter 		}
1091768Speter 		if (al->class != ARRAY) {
1092768Speter 			error("%s requires z to be a packed array, not %s", pu, nameof(ap));
1093768Speter 			return;
1094768Speter 		}
1095768Speter 		if (al->type == NIL || ap->type == NIL)
1096768Speter 			return;
1097768Speter 		if (al->type != ap->type) {
1098768Speter 			error("%s requires a and z to be arrays of the same type", pu, nameof(ap));
1099768Speter 			return;
1100768Speter 		}
1101768Speter 		k = width(al);
1102768Speter 		itemwidth = width(ap->type);
1103768Speter 		ap = ap->chain;
1104768Speter 		al = al->chain;
1105768Speter 		if (ap->chain != NIL || al->chain != NIL) {
1106768Speter 			error("%s requires a and z to be single dimension arrays", pu);
1107768Speter 			return;
1108768Speter 		}
1109768Speter 		if (ap == NIL || al == NIL)
1110768Speter 			return;
1111768Speter 		/*
1112768Speter 		 * al is the range for z i.e. u..v
1113768Speter 		 * ap is the range for a i.e. m..n
1114768Speter 		 * i will be n-m+1
1115768Speter 		 * j will be v-u+1
1116768Speter 		 */
1117768Speter 		i = ap->range[1] - ap->range[0] + 1;
1118768Speter 		j = al->range[1] - al->range[0] + 1;
1119768Speter 		if (i < j) {
1120768Speter 			error("%s cannot have more elements in a (%d) than in z (%d)", pu, j, i);
1121768Speter 			return;
1122768Speter 		}
1123768Speter 		/*
1124768Speter 		 * get n-m-(v-u) and m for the interpreter
1125768Speter 		 */
1126768Speter 		i -= j;
1127768Speter 		j = ap->range[0];
11282073Smckusic 		put(2, O_CON24, k);
11292073Smckusic 		put(2, O_CON24, i);
11302073Smckusic 		put(2, O_CON24, j);
11312073Smckusic 		put(2, O_CON24, itemwidth);
11322073Smckusic 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
11332073Smckusic 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
11342073Smckusic 		ap = stkrval((int *) pui, NLNIL , RREQ );
11352073Smckusic 		if (ap == NIL)
11362073Smckusic 			return;
11372073Smckusic 		put(1, op);
1138768Speter 		return;
1139768Speter 	case 0:
11407928Smckusick 		error("%s is an unimplemented extension", p->symbol);
1141768Speter 		return;
1142768Speter 
1143768Speter 	default:
1144768Speter 		panic("proc case");
1145768Speter 	}
1146768Speter }
1147768Speter #endif OBJ
1148