xref: /csrg-svn/usr.bin/pascal/src/proc.c (revision 8538)
1768Speter /* Copyright (c) 1979 Regents of the University of California */
2768Speter 
3*8538Speter static char sccsid[] = "@(#)proc.c 1.15 10/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 /*
16768Speter  * The following array is used to determine which classes may be read
17768Speter  * from textfiles. It is indexed by the return value from classify.
18768Speter  */
19768Speter #define rdops(x) rdxxxx[(x)-(TFIRST)]
20768Speter 
21768Speter int rdxxxx[] = {
22768Speter 	0,		/* -7 file types */
23768Speter 	0,		/* -6 record types */
24768Speter 	0,		/* -5 array types */
25768Speter 	O_READE,	/* -4 scalar types */
26768Speter 	0,		/* -3 pointer types */
27768Speter 	0,		/* -2 set types */
28768Speter 	0,		/* -1 string types */
29768Speter 	0,		/*  0 nil, no type */
30768Speter 	O_READE,	/*  1 boolean */
31768Speter 	O_READC,	/*  2 character */
32768Speter 	O_READ4,	/*  3 integer */
33768Speter 	O_READ8		/*  4 real */
34768Speter };
35768Speter 
36768Speter /*
37768Speter  * Proc handles procedure calls.
38768Speter  * Non-builtin procedures are "buck-passed" to func (with a flag
39768Speter  * indicating that they are actually procedures.
40768Speter  * builtin procedures are handled here.
41768Speter  */
42768Speter proc(r)
43768Speter 	int *r;
44768Speter {
45768Speter 	register struct nl *p;
46768Speter 	register int *alv, *al, op;
47768Speter 	struct nl *filetype, *ap;
48768Speter 	int argc, *argv, typ, fmtspec, strfmt, stkcnt, *file;
49768Speter 	char fmt, format[20], *strptr;
50768Speter 	int prec, field, strnglen, fmtlen, fmtstart, pu;
51768Speter 	int *pua, *pui, *puz;
52768Speter 	int i, j, k;
53768Speter 	int itemwidth;
543226Smckusic 	struct tmps soffset;
553851Speter 	struct nl	*tempnlp;
56768Speter 
57768Speter #define	CONPREC 4
58768Speter #define	VARPREC 8
59768Speter #define	CONWIDTH 1
60768Speter #define	VARWIDTH 2
61768Speter #define SKIP 16
62768Speter 
63768Speter 	/*
64768Speter 	 * Verify that the name is
65768Speter 	 * defined and is that of a
66768Speter 	 * procedure.
67768Speter 	 */
68768Speter 	p = lookup(r[2]);
69768Speter 	if (p == NIL) {
70768Speter 		rvlist(r[3]);
71768Speter 		return;
72768Speter 	}
731198Speter 	if (p->class != PROC && p->class != FPROC) {
74768Speter 		error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]);
75768Speter 		rvlist(r[3]);
76768Speter 		return;
77768Speter 	}
78768Speter 	argv = r[3];
79768Speter 
80768Speter 	/*
81768Speter 	 * Call handles user defined
82768Speter 	 * procedures and functions.
83768Speter 	 */
84768Speter 	if (bn != 0) {
85768Speter 		call(p, argv, PROC, bn);
86768Speter 		return;
87768Speter 	}
88768Speter 
89768Speter 	/*
90768Speter 	 * Call to built-in procedure.
91768Speter 	 * Count the arguments.
92768Speter 	 */
93768Speter 	argc = 0;
94768Speter 	for (al = argv; al != NIL; al = al[2])
95768Speter 		argc++;
96768Speter 
97768Speter 	/*
98768Speter 	 * Switch on the operator
99768Speter 	 * associated with the built-in
100768Speter 	 * procedure in the namelist
101768Speter 	 */
102768Speter 	op = p->value[0] &~ NSTAND;
103768Speter 	if (opt('s') && (p->value[0] & NSTAND)) {
104768Speter 		standard();
105768Speter 		error("%s is a nonstandard procedure", p->symbol);
106768Speter 	}
107768Speter 	switch (op) {
108768Speter 
109768Speter 	case O_ABORT:
110768Speter 		if (argc != 0)
111768Speter 			error("null takes no arguments");
112768Speter 		return;
113768Speter 
114768Speter 	case O_FLUSH:
115768Speter 		if (argc == 0) {
116768Speter 			put(1, O_MESSAGE);
117768Speter 			return;
118768Speter 		}
119768Speter 		if (argc != 1) {
120768Speter 			error("flush takes at most one argument");
121768Speter 			return;
122768Speter 		}
1232073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
124768Speter 		if (ap == NIL)
125768Speter 			return;
126768Speter 		if (ap->class != FILET) {
127768Speter 			error("flush's argument must be a file, not %s", nameof(ap));
128768Speter 			return;
129768Speter 		}
130768Speter 		put(1, op);
131768Speter 		return;
132768Speter 
133768Speter 	case O_MESSAGE:
134768Speter 	case O_WRITEF:
135768Speter 	case O_WRITLN:
136768Speter 		/*
137768Speter 		 * Set up default file "output"'s type
138768Speter 		 */
139768Speter 		file = NIL;
140768Speter 		filetype = nl+T1CHAR;
141768Speter 		/*
142768Speter 		 * Determine the file implied
143768Speter 		 * for the write and generate
144768Speter 		 * code to make it the active file.
145768Speter 		 */
146768Speter 		if (op == O_MESSAGE) {
147768Speter 			/*
148768Speter 			 * For message, all that matters
149768Speter 			 * is that the filetype is
150768Speter 			 * a character file.
151768Speter 			 * Thus "output" will suit us fine.
152768Speter 			 */
153768Speter 			put(1, O_MESSAGE);
154768Speter 		} else if (argv != NIL && (al = argv[1])[0] != T_WEXP) {
155768Speter 			/*
156768Speter 			 * If there is a first argument which has
157768Speter 			 * no write widths, then it is potentially
158768Speter 			 * a file name.
159768Speter 			 */
160768Speter 			codeoff();
161768Speter 			ap = stkrval(argv[1], NIL , RREQ );
162768Speter 			codeon();
163768Speter 			if (ap == NIL)
164768Speter 				argv = argv[2];
165768Speter 			if (ap != NIL && ap->class == FILET) {
166768Speter 				/*
167768Speter 				 * Got "write(f, ...", make
168768Speter 				 * f the active file, and save
169768Speter 				 * it and its type for use in
170768Speter 				 * processing the rest of the
171768Speter 				 * arguments to write.
172768Speter 				 */
173768Speter 				file = argv[1];
174768Speter 				filetype = ap->type;
1752073Smckusic 				stklval(argv[1], NIL , LREQ );
176768Speter 				put(1, O_UNIT);
177768Speter 				/*
178768Speter 				 * Skip over the first argument
179768Speter 				 */
180768Speter 				argv = argv[2];
181768Speter 				argc--;
182*8538Speter 			} else {
183768Speter 				/*
184768Speter 				 * Set up for writing on
185768Speter 				 * standard output.
186768Speter 				 */
187768Speter 				put(1, O_UNITOUT);
1887953Speter 				output->nl_flags |= NUSED;
189*8538Speter 			}
190*8538Speter 		} else {
191768Speter 			put(1, O_UNITOUT);
1927953Speter 			output->nl_flags |= NUSED;
193*8538Speter 		}
194768Speter 		/*
195768Speter 		 * Loop and process each
196768Speter 		 * of the arguments.
197768Speter 		 */
198768Speter 		for (; argv != NIL; argv = argv[2]) {
199768Speter 			/*
200768Speter 			 * fmtspec indicates the type (CONstant or VARiable)
201768Speter 			 *	and number (none, WIDTH, and/or PRECision)
202768Speter 			 *	of the fields in the printf format for this
203768Speter 			 *	output variable.
2043172Smckusic 			 * stkcnt is the number of bytes pushed on the stack
205768Speter 			 * fmt is the format output indicator (D, E, F, O, X, S)
206768Speter 			 * fmtstart = 0 for leading blank; = 1 for no blank
207768Speter 			 */
208768Speter 			fmtspec = NIL;
209768Speter 			stkcnt = 0;
210768Speter 			fmt = 'D';
211768Speter 			fmtstart = 1;
212768Speter 			al = argv[1];
213768Speter 			if (al == NIL)
214768Speter 				continue;
215768Speter 			if (al[0] == T_WEXP)
216768Speter 				alv = al[1];
217768Speter 			else
218768Speter 				alv = al;
219768Speter 			if (alv == NIL)
220768Speter 				continue;
221768Speter 			codeoff();
222768Speter 			ap = stkrval(alv, NIL , RREQ );
223768Speter 			codeon();
224768Speter 			if (ap == NIL)
225768Speter 				continue;
226768Speter 			typ = classify(ap);
227768Speter 			if (al[0] == T_WEXP) {
228768Speter 				/*
229768Speter 				 * Handle width expressions.
230768Speter 				 * The basic game here is that width
231768Speter 				 * expressions get evaluated. If they
232768Speter 				 * are constant, the value is placed
233768Speter 				 * directly in the format string.
234768Speter 				 * Otherwise the value is pushed onto
235768Speter 				 * the stack and an indirection is
236768Speter 				 * put into the format string.
237768Speter 				 */
238768Speter 				if (al[3] == OCT)
239768Speter 					fmt = 'O';
240768Speter 				else if (al[3] == HEX)
241768Speter 					fmt = 'X';
242768Speter 				else if (al[3] != NIL) {
243768Speter 					/*
244768Speter 					 * Evaluate second format spec
245768Speter 					 */
246768Speter 					if ( constval(al[3])
247768Speter 					    && isa( con.ctype , "i" ) ) {
248768Speter 						fmtspec += CONPREC;
249768Speter 						prec = con.crval;
250768Speter 					} else {
251768Speter 						fmtspec += VARPREC;
252768Speter 					}
253768Speter 					fmt = 'f';
254768Speter 					switch ( typ ) {
255768Speter 					case TINT:
256768Speter 						if ( opt( 's' ) ) {
257768Speter 						    standard();
258768Speter 						    error("Writing %ss with two write widths is non-standard", clnames[typ]);
259768Speter 						}
260768Speter 						/* and fall through */
261768Speter 					case TDOUBLE:
262768Speter 						break;
263768Speter 					default:
264768Speter 						error("Cannot write %ss with two write widths", clnames[typ]);
265768Speter 						continue;
266768Speter 					}
267768Speter 				}
268768Speter 				/*
269768Speter 				 * Evaluate first format spec
270768Speter 				 */
271768Speter 				if (al[2] != NIL) {
272768Speter 					if ( constval(al[2])
273768Speter 					    && isa( con.ctype , "i" ) ) {
274768Speter 						fmtspec += CONWIDTH;
275768Speter 						field = con.crval;
276768Speter 					} else {
277768Speter 						fmtspec += VARWIDTH;
278768Speter 					}
279768Speter 				}
280768Speter 				if ((fmtspec & CONPREC) && prec < 0 ||
281768Speter 				    (fmtspec & CONWIDTH) && field < 0) {
282768Speter 					error("Negative widths are not allowed");
283768Speter 					continue;
284768Speter 				}
2853179Smckusic 				if ( opt('s') &&
2863179Smckusic 				    ((fmtspec & CONPREC) && prec == 0 ||
2873179Smckusic 				    (fmtspec & CONWIDTH) && field == 0)) {
2883179Smckusic 					standard();
2893179Smckusic 					error("Zero widths are non-standard");
2903179Smckusic 				}
291768Speter 			}
292768Speter 			if (filetype != nl+T1CHAR) {
293768Speter 				if (fmt == 'O' || fmt == 'X') {
294768Speter 					error("Oct/hex allowed only on text files");
295768Speter 					continue;
296768Speter 				}
297768Speter 				if (fmtspec) {
298768Speter 					error("Write widths allowed only on text files");
299768Speter 					continue;
300768Speter 				}
301768Speter 				/*
302768Speter 				 * Generalized write, i.e.
303768Speter 				 * to a non-textfile.
304768Speter 				 */
3052073Smckusic 				stklval(file, NIL , LREQ );
306768Speter 				put(1, O_FNIL);
307768Speter 				/*
308768Speter 				 * file^ := ...
309768Speter 				 */
310768Speter 				ap = rvalue(argv[1], NIL);
311768Speter 				if (ap == NIL)
312768Speter 					continue;
313768Speter 				if (incompat(ap, filetype, argv[1])) {
314768Speter 					cerror("Type mismatch in write to non-text file");
315768Speter 					continue;
316768Speter 				}
317768Speter 				convert(ap, filetype);
318768Speter 				put(2, O_AS, width(filetype));
319768Speter 				/*
320768Speter 				 * put(file)
321768Speter 				 */
322768Speter 				put(1, O_PUT);
323768Speter 				continue;
324768Speter 			}
325768Speter 			/*
326768Speter 			 * Write to a textfile
327768Speter 			 *
328768Speter 			 * Evaluate the expression
329768Speter 			 * to be written.
330768Speter 			 */
331768Speter 			if (fmt == 'O' || fmt == 'X') {
332768Speter 				if (opt('s')) {
333768Speter 					standard();
334768Speter 					error("Oct and hex are non-standard");
335768Speter 				}
336768Speter 				if (typ == TSTR || typ == TDOUBLE) {
337768Speter 					error("Can't write %ss with oct/hex", clnames[typ]);
338768Speter 					continue;
339768Speter 				}
340768Speter 				if (typ == TCHAR || typ == TBOOL)
341768Speter 					typ = TINT;
342768Speter 			}
343768Speter 			/*
344768Speter 			 * Place the arguement on the stack. If there is
345768Speter 			 * no format specified by the programmer, implement
346768Speter 			 * the default.
347768Speter 			 */
348768Speter 			switch (typ) {
3496542Smckusick 			case TPTR:
3506542Smckusick 				warning();
3516542Smckusick 				if (opt('s')) {
3526542Smckusick 					standard();
3536542Smckusick 				}
3546542Smckusick 				error("Writing %ss to text files is non-standard",
3556542Smckusick 				    clnames[typ]);
3566542Smckusick 				/* and fall through */
357768Speter 			case TINT:
358768Speter 				if (fmt != 'f') {
359768Speter 					ap = stkrval(alv, NIL , RREQ );
3603172Smckusic 					stkcnt += sizeof(long);
361768Speter 				} else {
362768Speter 					ap = stkrval(alv, NIL , RREQ );
363768Speter 					put(1, O_ITOD);
3643172Smckusic 					stkcnt += sizeof(double);
365768Speter 					typ = TDOUBLE;
366768Speter 					goto tdouble;
367768Speter 				}
368768Speter 				if (fmtspec == NIL) {
369768Speter 					if (fmt == 'D')
370768Speter 						field = 10;
371768Speter 					else if (fmt == 'X')
372768Speter 						field = 8;
373768Speter 					else if (fmt == 'O')
374768Speter 						field = 11;
375768Speter 					else
376768Speter 						panic("fmt1");
377768Speter 					fmtspec = CONWIDTH;
378768Speter 				}
379768Speter 				break;
380768Speter 			case TCHAR:
381768Speter 			     tchar:
3822073Smckusic 				if (fmtspec == NIL) {
3832073Smckusic 					put(1, O_FILE);
3842073Smckusic 					ap = stkrval(alv, NIL , RREQ );
3853172Smckusic 					convert(nl + T4INT, INT_TYP);
3863172Smckusic 					put(2, O_WRITEC,
3873172Smckusic 						sizeof(char *) + sizeof(int));
3882073Smckusic 					fmtspec = SKIP;
3892073Smckusic 					break;
3902073Smckusic 				}
391768Speter 				ap = stkrval(alv, NIL , RREQ );
3923172Smckusic 				convert(nl + T4INT, INT_TYP);
3933172Smckusic 				stkcnt += sizeof(int);
394768Speter 				fmt = 'c';
395768Speter 				break;
396768Speter 			case TSCAL:
3971628Speter 				warning();
398768Speter 				if (opt('s')) {
399768Speter 					standard();
400768Speter 				}
4016542Smckusick 				error("Writing %ss to text files is non-standard",
4026542Smckusick 				    clnames[typ]);
4036542Smckusick 				/* and fall through */
404768Speter 			case TBOOL:
405768Speter 				stkrval(alv, NIL , RREQ );
4063076Smckusic 				put(2, O_NAM, (long)listnames(ap));
4073172Smckusic 				stkcnt += sizeof(char *);
408768Speter 				fmt = 's';
409768Speter 				break;
410768Speter 			case TDOUBLE:
411768Speter 				ap = stkrval(alv, TDOUBLE , RREQ );
4123172Smckusic 				stkcnt += sizeof(double);
413768Speter 			     tdouble:
414768Speter 				switch (fmtspec) {
415768Speter 				case NIL:
4163076Smckusic #					ifdef DEC11
4173076Smckusic 					    field = 21;
4183076Smckusic #					else
4193076Smckusic 					    field = 22;
4203076Smckusic #					endif DEC11
421768Speter 					prec = 14;
4223076Smckusic 					fmt = 'e';
423768Speter 					fmtspec = CONWIDTH + CONPREC;
424768Speter 					break;
425768Speter 				case CONWIDTH:
426768Speter 					if (--field < 1)
427768Speter 						field = 1;
4283076Smckusic #					ifdef DEC11
4293076Smckusic 					    prec = field - 7;
4303076Smckusic #					else
4313076Smckusic 					    prec = field - 8;
4323076Smckusic #					endif DEC11
433768Speter 					if (prec < 1)
434768Speter 						prec = 1;
435768Speter 					fmtspec += CONPREC;
4363076Smckusic 					fmt = 'e';
437768Speter 					break;
438768Speter 				case CONWIDTH + CONPREC:
439768Speter 				case CONWIDTH + VARPREC:
440768Speter 					if (--field < 1)
441768Speter 						field = 1;
442768Speter 				}
443768Speter 				format[0] = ' ';
4448026Smckusick 				fmtstart = 1;
445768Speter 				break;
446768Speter 			case TSTR:
447768Speter 				constval( alv );
448768Speter 				switch ( classify( con.ctype ) ) {
449768Speter 				    case TCHAR:
450768Speter 					typ = TCHAR;
451768Speter 					goto tchar;
452768Speter 				    case TSTR:
453768Speter 					strptr = con.cpval;
454768Speter 					for (strnglen = 0;  *strptr++;  strnglen++) /* void */;
455768Speter 					strptr = con.cpval;
456768Speter 					break;
457768Speter 				    default:
458768Speter 					strnglen = width(ap);
459768Speter 					break;
460768Speter 				}
461768Speter 				fmt = 's';
462768Speter 				strfmt = fmtspec;
463768Speter 				if (fmtspec == NIL) {
464768Speter 					fmtspec = SKIP;
465768Speter 					break;
466768Speter 				}
467768Speter 				if (fmtspec & CONWIDTH) {
468768Speter 					if (field <= strnglen) {
469768Speter 						fmtspec = SKIP;
470768Speter 						break;
471768Speter 					} else
472768Speter 						field -= strnglen;
473768Speter 				}
474768Speter 				/*
475768Speter 				 * push string to implement leading blank padding
476768Speter 				 */
477768Speter 				put(2, O_LVCON, 2);
478768Speter 				putstr("", 0);
4793172Smckusic 				stkcnt += sizeof(char *);
480768Speter 				break;
481768Speter 			default:
482768Speter 				error("Can't write %ss to a text file", clnames[typ]);
483768Speter 				continue;
484768Speter 			}
485768Speter 			/*
486768Speter 			 * If there is a variable precision, evaluate it onto
487768Speter 			 * the stack
488768Speter 			 */
489768Speter 			if (fmtspec & VARPREC) {
490768Speter 				ap = stkrval(al[3], NIL , RREQ );
491768Speter 				if (ap == NIL)
492768Speter 					continue;
493768Speter 				if (isnta(ap,"i")) {
494768Speter 					error("Second write width must be integer, not %s", nameof(ap));
495768Speter 					continue;
496768Speter 				}
497768Speter 				if ( opt( 't' ) ) {
498768Speter 				    put(3, O_MAX, 0, 0);
499768Speter 				}
5003172Smckusic 				convert(nl+T4INT, INT_TYP);
5013172Smckusic 				stkcnt += sizeof(int);
502768Speter 			}
503768Speter 			/*
504768Speter 			 * If there is a variable width, evaluate it onto
505768Speter 			 * the stack
506768Speter 			 */
507768Speter 			if (fmtspec & VARWIDTH) {
508768Speter 				if ( ( typ == TDOUBLE && fmtspec == VARWIDTH )
509768Speter 				    || typ == TSTR ) {
5103226Smckusic 					soffset = sizes[cbn].curtmps;
5113851Speter 					tempnlp = tmpalloc(sizeof(long),
5123226Smckusic 						nl+T4INT, REGOK);
5133851Speter 					put(2, O_LV | cbn << 8 + INDX,
5143851Speter 					    tempnlp -> value[ NL_OFFS ] );
515768Speter 				}
516768Speter 				ap = stkrval(al[2], NIL , RREQ );
517768Speter 				if (ap == NIL)
518768Speter 					continue;
519768Speter 				if (isnta(ap,"i")) {
520768Speter 					error("First write width must be integer, not %s", nameof(ap));
521768Speter 					continue;
522768Speter 				}
523768Speter 				/*
524768Speter 				 * Perform special processing on widths based
525768Speter 				 * on data type
526768Speter 				 */
527768Speter 				switch (typ) {
528768Speter 				case TDOUBLE:
529768Speter 					if (fmtspec == VARWIDTH) {
5303076Smckusic 						fmt = 'e';
531768Speter 						put(1, O_AS4);
5323851Speter 						put(2, O_RV4 | cbn << 8 + INDX,
5333851Speter 						    tempnlp -> value[NL_OFFS] );
5343076Smckusic #						ifdef DEC11
5353076Smckusic 						    put(3, O_MAX, 8, 1);
5363076Smckusic #						else
5373076Smckusic 						    put(3, O_MAX, 9, 1);
5383076Smckusic #						endif DEC11
5393172Smckusic 						convert(nl+T4INT, INT_TYP);
5403172Smckusic 						stkcnt += sizeof(int);
5413851Speter 						put(2, O_RV4 | cbn << 8 + INDX,
5423851Speter 						    tempnlp->value[NL_OFFS] );
543768Speter 						fmtspec += VARPREC;
5443226Smckusic 						tmpfree(&soffset);
545768Speter 					}
546768Speter 					put(3, O_MAX, 1, 1);
547768Speter 					break;
548768Speter 				case TSTR:
549768Speter 					put(1, O_AS4);
5503851Speter 					put(2, O_RV4 | cbn << 8 + INDX,
5513851Speter 					    tempnlp -> value[ NL_OFFS ] );
552768Speter 					put(3, O_MAX, strnglen, 0);
553768Speter 					break;
554768Speter 				default:
555768Speter 					if ( opt( 't' ) ) {
556768Speter 					    put(3, O_MAX, 0, 0);
557768Speter 					}
558768Speter 					break;
559768Speter 				}
5603172Smckusic 				convert(nl+T4INT, INT_TYP);
5613172Smckusic 				stkcnt += sizeof(int);
562768Speter 			}
563768Speter 			/*
564768Speter 			 * Generate the format string
565768Speter 			 */
566768Speter 			switch (fmtspec) {
567768Speter 			default:
568768Speter 				panic("fmt2");
569768Speter 			case SKIP:
570768Speter 				break;
5712073Smckusic 			case NIL:
5722073Smckusic 				sprintf(&format[1], "%%%c", fmt);
5732073Smckusic 				goto fmtgen;
574768Speter 			case CONWIDTH:
5753076Smckusic 				sprintf(&format[1], "%%%d%c", field, fmt);
576768Speter 				goto fmtgen;
577768Speter 			case VARWIDTH:
578768Speter 				sprintf(&format[1], "%%*%c", fmt);
579768Speter 				goto fmtgen;
580768Speter 			case CONWIDTH + CONPREC:
5813076Smckusic 				sprintf(&format[1], "%%%d.%d%c", field, prec, fmt);
582768Speter 				goto fmtgen;
583768Speter 			case CONWIDTH + VARPREC:
5843076Smckusic 				sprintf(&format[1], "%%%d.*%c", field, fmt);
585768Speter 				goto fmtgen;
586768Speter 			case VARWIDTH + CONPREC:
5873076Smckusic 				sprintf(&format[1], "%%*.%d%c", prec, fmt);
588768Speter 				goto fmtgen;
589768Speter 			case VARWIDTH + VARPREC:
590768Speter 				sprintf(&format[1], "%%*.*%c", fmt);
591768Speter 			fmtgen:
592768Speter 				fmtlen = lenstr(&format[fmtstart], 0);
593768Speter 				put(2, O_LVCON, fmtlen);
594768Speter 				putstr(&format[fmtstart], 0);
595768Speter 				put(1, O_FILE);
5963172Smckusic 				stkcnt += 2 * sizeof(char *);
597768Speter 				put(2, O_WRITEF, stkcnt);
598768Speter 			}
599768Speter 			/*
600768Speter 			 * Write the string after its blank padding
601768Speter 			 */
602768Speter 			if (typ == TSTR) {
603768Speter 				put(1, O_FILE);
6043172Smckusic 				put(2, CON_INT, 1);
605768Speter 				if (strfmt & VARWIDTH) {
6063851Speter 					put(2, O_RV4 | cbn << 8 + INDX ,
6073851Speter 					    tempnlp -> value[ NL_OFFS ] );
608768Speter 					put(2, O_MIN, strnglen);
6093172Smckusic 					convert(nl+T4INT, INT_TYP);
6103226Smckusic 					tmpfree(&soffset);
611768Speter 				} else {
612768Speter 					if ((fmtspec & SKIP) &&
613768Speter 					   (strfmt & CONWIDTH)) {
614768Speter 						strnglen = field;
615768Speter 					}
6163172Smckusic 					put(2, CON_INT, strnglen);
617768Speter 				}
618768Speter 				ap = stkrval(alv, NIL , RREQ );
6193172Smckusic 				put(2, O_WRITES,
6203172Smckusic 					2 * sizeof(char *) + 2 * sizeof(int));
621768Speter 			}
622768Speter 		}
623768Speter 		/*
624768Speter 		 * Done with arguments.
625768Speter 		 * Handle writeln and
626768Speter 		 * insufficent number of args.
627768Speter 		 */
628768Speter 		switch (p->value[0] &~ NSTAND) {
629768Speter 			case O_WRITEF:
630768Speter 				if (argc == 0)
631768Speter 					error("Write requires an argument");
632768Speter 				break;
633768Speter 			case O_MESSAGE:
634768Speter 				if (argc == 0)
635768Speter 					error("Message requires an argument");
636768Speter 			case O_WRITLN:
637768Speter 				if (filetype != nl+T1CHAR)
638768Speter 					error("Can't 'writeln' a non text file");
639768Speter 				put(1, O_WRITLN);
640768Speter 				break;
641768Speter 		}
642768Speter 		return;
643768Speter 
644768Speter 	case O_READ4:
645768Speter 	case O_READLN:
646768Speter 		/*
647768Speter 		 * Set up default
648768Speter 		 * file "input".
649768Speter 		 */
650768Speter 		file = NIL;
651768Speter 		filetype = nl+T1CHAR;
652768Speter 		/*
653768Speter 		 * Determine the file implied
654768Speter 		 * for the read and generate
655768Speter 		 * code to make it the active file.
656768Speter 		 */
657768Speter 		if (argv != NIL) {
658768Speter 			codeoff();
659768Speter 			ap = stkrval(argv[1], NIL , RREQ );
660768Speter 			codeon();
661768Speter 			if (ap == NIL)
662768Speter 				argv = argv[2];
663768Speter 			if (ap != NIL && ap->class == FILET) {
664768Speter 				/*
665768Speter 				 * Got "read(f, ...", make
666768Speter 				 * f the active file, and save
667768Speter 				 * it and its type for use in
668768Speter 				 * processing the rest of the
669768Speter 				 * arguments to read.
670768Speter 				 */
671768Speter 				file = argv[1];
672768Speter 				filetype = ap->type;
6732073Smckusic 				stklval(argv[1], NIL , LREQ );
674768Speter 				put(1, O_UNIT);
675768Speter 				argv = argv[2];
676768Speter 				argc--;
677768Speter 			} else {
678768Speter 				/*
679768Speter 				 * Default is read from
680768Speter 				 * standard input.
681768Speter 				 */
682768Speter 				put(1, O_UNITINP);
683768Speter 				input->nl_flags |= NUSED;
684768Speter 			}
685768Speter 		} else {
686768Speter 			put(1, O_UNITINP);
687768Speter 			input->nl_flags |= NUSED;
688768Speter 		}
689768Speter 		/*
690768Speter 		 * Loop and process each
691768Speter 		 * of the arguments.
692768Speter 		 */
693768Speter 		for (; argv != NIL; argv = argv[2]) {
694768Speter 			/*
695768Speter 			 * Get the address of the target
696768Speter 			 * on the stack.
697768Speter 			 */
698768Speter 			al = argv[1];
699768Speter 			if (al == NIL)
700768Speter 				continue;
701768Speter 			if (al[0] != T_VAR) {
702768Speter 				error("Arguments to %s must be variables, not expressions", p->symbol);
703768Speter 				continue;
704768Speter 			}
705768Speter 			ap = stklval(al, MOD|ASGN|NOUSE);
706768Speter 			if (ap == NIL)
707768Speter 				continue;
708768Speter 			if (filetype != nl+T1CHAR) {
709768Speter 				/*
710768Speter 				 * Generalized read, i.e.
711768Speter 				 * from a non-textfile.
712768Speter 				 */
713768Speter 				if (incompat(filetype, ap, argv[1] )) {
714768Speter 					error("Type mismatch in read from non-text file");
715768Speter 					continue;
716768Speter 				}
717768Speter 				/*
718768Speter 				 * var := file ^;
719768Speter 				 */
720768Speter 				if (file != NIL)
7212073Smckusic 					stklval(file, NIL , LREQ );
722768Speter 				else /* Magic */
7233076Smckusic 					put(2, PTR_RV, (int)input->value[0]);
724768Speter 				put(1, O_FNIL);
725768Speter 				put(2, O_IND, width(filetype));
726768Speter 				convert(filetype, ap);
727768Speter 				if (isa(ap, "bsci"))
728768Speter 					rangechk(ap, ap);
729768Speter 				put(2, O_AS, width(ap));
730768Speter 				/*
731768Speter 				 * get(file);
732768Speter 				 */
733768Speter 				put(1, O_GET);
734768Speter 				continue;
735768Speter 			}
736768Speter 			typ = classify(ap);
737768Speter 			op = rdops(typ);
738768Speter 			if (op == NIL) {
739768Speter 				error("Can't read %ss from a text file", clnames[typ]);
740768Speter 				continue;
741768Speter 			}
742768Speter 			if (op != O_READE)
743768Speter 				put(1, op);
744768Speter 			else {
7453076Smckusic 				put(2, op, (long)listnames(ap));
7461628Speter 				warning();
747768Speter 				if (opt('s')) {
748768Speter 					standard();
749768Speter 				}
7501628Speter 				error("Reading scalars from text files is non-standard");
751768Speter 			}
752768Speter 			/*
753768Speter 			 * Data read is on the stack.
754768Speter 			 * Assign it.
755768Speter 			 */
756768Speter 			if (op != O_READ8 && op != O_READE)
757768Speter 				rangechk(ap, op == O_READC ? ap : nl+T4INT);
758768Speter 			gen(O_AS2, O_AS2, width(ap),
759768Speter 				op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2);
760768Speter 		}
761768Speter 		/*
762768Speter 		 * Done with arguments.
763768Speter 		 * Handle readln and
764768Speter 		 * insufficient number of args.
765768Speter 		 */
766768Speter 		if (p->value[0] == O_READLN) {
767768Speter 			if (filetype != nl+T1CHAR)
768768Speter 				error("Can't 'readln' a non text file");
769768Speter 			put(1, O_READLN);
770768Speter 		}
771768Speter 		else if (argc == 0)
772768Speter 			error("read requires an argument");
773768Speter 		return;
774768Speter 
775768Speter 	case O_GET:
776768Speter 	case O_PUT:
777768Speter 		if (argc != 1) {
778768Speter 			error("%s expects one argument", p->symbol);
779768Speter 			return;
780768Speter 		}
7812073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
782768Speter 		if (ap == NIL)
783768Speter 			return;
784768Speter 		if (ap->class != FILET) {
785768Speter 			error("Argument to %s must be a file, not %s", p->symbol, nameof(ap));
786768Speter 			return;
787768Speter 		}
788768Speter 		put(1, O_UNIT);
789768Speter 		put(1, op);
790768Speter 		return;
791768Speter 
792768Speter 	case O_RESET:
793768Speter 	case O_REWRITE:
794768Speter 		if (argc == 0 || argc > 2) {
795768Speter 			error("%s expects one or two arguments", p->symbol);
796768Speter 			return;
797768Speter 		}
798768Speter 		if (opt('s') && argc == 2) {
799768Speter 			standard();
800768Speter 			error("Two argument forms of reset and rewrite are non-standard");
801768Speter 		}
8022073Smckusic 		codeoff();
803768Speter 		ap = stklval(argv[1], MOD|NOUSE);
8042073Smckusic 		codeon();
805768Speter 		if (ap == NIL)
806768Speter 			return;
807768Speter 		if (ap->class != FILET) {
808768Speter 			error("First argument to %s must be a file, not %s", p->symbol, nameof(ap));
809768Speter 			return;
810768Speter 		}
8112073Smckusic 		put(2, O_CON24, text(ap) ? 0: width(ap->type));
812768Speter 		if (argc == 2) {
813768Speter 			/*
814768Speter 			 * Optional second argument
815768Speter 			 * is a string name of a
816768Speter 			 * UNIX (R) file to be associated.
817768Speter 			 */
818768Speter 			al = argv[2];
8192073Smckusic 			codeoff();
820768Speter 			al = stkrval(al[1], NOFLAGS , RREQ );
8212073Smckusic 			codeon();
822768Speter 			if (al == NIL)
823768Speter 				return;
824768Speter 			if (classify(al) != TSTR) {
825768Speter 				error("Second argument to %s must be a string, not %s", p->symbol, nameof(al));
826768Speter 				return;
827768Speter 			}
8282073Smckusic 			put(2, O_CON24, width(al));
8292073Smckusic 			al = argv[2];
8302073Smckusic 			al = stkrval(al[1], NOFLAGS , RREQ );
831768Speter 		} else {
8322073Smckusic 			put(2, O_CON24, 0);
8333076Smckusic 			put(2, PTR_CON, NIL);
834768Speter 		}
8352073Smckusic 		ap = stklval(argv[1], MOD|NOUSE);
836768Speter 		put(1, op);
837768Speter 		return;
838768Speter 
839768Speter 	case O_NEW:
840768Speter 	case O_DISPOSE:
841768Speter 		if (argc == 0) {
842768Speter 			error("%s expects at least one argument", p->symbol);
843768Speter 			return;
844768Speter 		}
845768Speter 		ap = stklval(argv[1], op == O_NEW ? ( MOD | NOUSE ) : MOD );
846768Speter 		if (ap == NIL)
847768Speter 			return;
848768Speter 		if (ap->class != PTR) {
849768Speter 			error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap));
850768Speter 			return;
851768Speter 		}
852768Speter 		ap = ap->type;
853768Speter 		if (ap == NIL)
854768Speter 			return;
8557966Smckusick 		if ((ap->nl_flags & NFILES) && op == O_DISPOSE)
8567966Smckusick 			op = O_DFDISP;
857768Speter 		argv = argv[2];
858768Speter 		if (argv != NIL) {
859768Speter 			if (ap->class != RECORD) {
860768Speter 				error("Record required when specifying variant tags");
861768Speter 				return;
862768Speter 			}
863768Speter 			for (; argv != NIL; argv = argv[2]) {
864768Speter 				if (ap->ptr[NL_VARNT] == NIL) {
865768Speter 					error("Too many tag fields");
866768Speter 					return;
867768Speter 				}
868768Speter 				if (!isconst(argv[1])) {
869768Speter 					error("Second and successive arguments to %s must be constants", p->symbol);
870768Speter 					return;
871768Speter 				}
872768Speter 				gconst(argv[1]);
873768Speter 				if (con.ctype == NIL)
874768Speter 					return;
875768Speter 				if (incompat(con.ctype, (ap->ptr[NL_TAG])->type , NIL )) {
876768Speter 					cerror("Specified tag constant type clashed with variant case selector type");
877768Speter 					return;
878768Speter 				}
879768Speter 				for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain)
880768Speter 					if (ap->range[0] == con.crval)
881768Speter 						break;
882768Speter 				if (ap == NIL) {
883768Speter 					error("No variant case label value equals specified constant value");
884768Speter 					return;
885768Speter 				}
886768Speter 				ap = ap->ptr[NL_VTOREC];
887768Speter 			}
888768Speter 		}
889768Speter 		put(2, op, width(ap));
890768Speter 		return;
891768Speter 
892768Speter 	case O_DATE:
893768Speter 	case O_TIME:
894768Speter 		if (argc != 1) {
895768Speter 			error("%s expects one argument", p->symbol);
896768Speter 			return;
897768Speter 		}
898768Speter 		ap = stklval(argv[1], MOD|NOUSE);
899768Speter 		if (ap == NIL)
900768Speter 			return;
901768Speter 		if (classify(ap) != TSTR || width(ap) != 10) {
902768Speter 			error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap));
903768Speter 			return;
904768Speter 		}
905768Speter 		put(1, op);
906768Speter 		return;
907768Speter 
908768Speter 	case O_HALT:
909768Speter 		if (argc != 0) {
910768Speter 			error("halt takes no arguments");
911768Speter 			return;
912768Speter 		}
913768Speter 		put(1, op);
914768Speter 		noreach = 1;
915768Speter 		return;
916768Speter 
917768Speter 	case O_ARGV:
918768Speter 		if (argc != 2) {
919768Speter 			error("argv takes two arguments");
920768Speter 			return;
921768Speter 		}
922768Speter 		ap = stkrval(argv[1], NIL , RREQ );
923768Speter 		if (ap == NIL)
924768Speter 			return;
925768Speter 		if (isnta(ap, "i")) {
926768Speter 			error("argv's first argument must be an integer, not %s", nameof(ap));
927768Speter 			return;
928768Speter 		}
929768Speter 		al = argv[2];
930768Speter 		ap = stklval(al[1], MOD|NOUSE);
931768Speter 		if (ap == NIL)
932768Speter 			return;
933768Speter 		if (classify(ap) != TSTR) {
934768Speter 			error("argv's second argument must be a string, not %s", nameof(ap));
935768Speter 			return;
936768Speter 		}
937768Speter 		put(2, op, width(ap));
938768Speter 		return;
939768Speter 
940768Speter 	case O_STLIM:
941768Speter 		if (argc != 1) {
942768Speter 			error("stlimit requires one argument");
943768Speter 			return;
944768Speter 		}
945768Speter 		ap = stkrval(argv[1], NIL , RREQ );
946768Speter 		if (ap == NIL)
947768Speter 			return;
948768Speter 		if (isnta(ap, "i")) {
949768Speter 			error("stlimit's argument must be an integer, not %s", nameof(ap));
950768Speter 			return;
951768Speter 		}
952768Speter 		if (width(ap) != 4)
953768Speter 			put(1, O_STOI);
954768Speter 		put(1, op);
955768Speter 		return;
956768Speter 
957768Speter 	case O_REMOVE:
958768Speter 		if (argc != 1) {
959768Speter 			error("remove expects one argument");
960768Speter 			return;
961768Speter 		}
9622073Smckusic 		codeoff();
963768Speter 		ap = stkrval(argv[1], NOFLAGS , RREQ );
9642073Smckusic 		codeon();
965768Speter 		if (ap == NIL)
966768Speter 			return;
967768Speter 		if (classify(ap) != TSTR) {
968768Speter 			error("remove's argument must be a string, not %s", nameof(ap));
969768Speter 			return;
970768Speter 		}
971768Speter 		put(2, O_CON24, width(ap));
9722073Smckusic 		ap = stkrval(argv[1], NOFLAGS , RREQ );
973768Speter 		put(1, op);
974768Speter 		return;
975768Speter 
976768Speter 	case O_LLIMIT:
977768Speter 		if (argc != 2) {
978768Speter 			error("linelimit expects two arguments");
979768Speter 			return;
980768Speter 		}
981768Speter 		al = argv[2];
982768Speter 		ap = stkrval(al[1], NIL , RREQ );
983768Speter 		if (ap == NIL)
984768Speter 			return;
985768Speter 		if (isnta(ap, "i")) {
986768Speter 			error("linelimit's second argument must be an integer, not %s", nameof(ap));
987768Speter 			return;
988768Speter 		}
9892073Smckusic 		ap = stklval(argv[1], NOFLAGS|NOUSE);
9902073Smckusic 		if (ap == NIL)
9912073Smckusic 			return;
9922073Smckusic 		if (!text(ap)) {
9932073Smckusic 			error("linelimit's first argument must be a text file, not %s", nameof(ap));
9942073Smckusic 			return;
9952073Smckusic 		}
996768Speter 		put(1, op);
997768Speter 		return;
998768Speter 	case O_PAGE:
999768Speter 		if (argc != 1) {
1000768Speter 			error("page expects one argument");
1001768Speter 			return;
1002768Speter 		}
10032073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
1004768Speter 		if (ap == NIL)
1005768Speter 			return;
1006768Speter 		if (!text(ap)) {
1007768Speter 			error("Argument to page must be a text file, not %s", nameof(ap));
1008768Speter 			return;
1009768Speter 		}
1010768Speter 		put(1, O_UNIT);
1011768Speter 		put(1, op);
1012768Speter 		return;
1013768Speter 
10147928Smckusick 	case O_ASRT:
10157928Smckusick 		if (!opt('t'))
10167928Smckusick 			return;
10177928Smckusick 		if (argc == 0 || argc > 2) {
10187928Smckusick 			error("Assert expects one or two arguments");
10197928Smckusick 			return;
10207928Smckusick 		}
10217928Smckusick 		if (argc == 2) {
10227928Smckusick 			/*
10237928Smckusick 			 * Optional second argument is a string specifying
10247928Smckusick 			 * why the assertion failed.
10257928Smckusick 			 */
10267928Smckusick 			al = argv[2];
10277928Smckusick 			al = stkrval(al[1], NIL , RREQ );
10287928Smckusick 			if (al == NIL)
10297928Smckusick 				return;
10307928Smckusick 			if (classify(al) != TSTR) {
10317928Smckusick 				error("Second argument to assert must be a string, not %s", nameof(al));
10327928Smckusick 				return;
10337928Smckusick 			}
10347928Smckusick 		} else {
10357928Smckusick 			put(2, PTR_CON, NIL);
10367928Smckusick 		}
10377928Smckusick 		ap = stkrval(argv[1], NIL , RREQ );
10387928Smckusick 		if (ap == NIL)
10397928Smckusick 			return;
10407928Smckusick 		if (isnta(ap, "b"))
10417928Smckusick 			error("Assert expression must be Boolean, not %ss", nameof(ap));
10427928Smckusick 		put(1, O_ASRT);
10437928Smckusick 		return;
10447928Smckusick 
1045768Speter 	case O_PACK:
1046768Speter 		if (argc != 3) {
1047768Speter 			error("pack expects three arguments");
1048768Speter 			return;
1049768Speter 		}
1050768Speter 		pu = "pack(a,i,z)";
10513076Smckusic 		pua = argv[1];
10523076Smckusic 		al = argv[2];
10533076Smckusic 		pui = al[1];
10543076Smckusic 		alv = al[2];
10553076Smckusic 		puz = alv[1];
1056768Speter 		goto packunp;
1057768Speter 	case O_UNPACK:
1058768Speter 		if (argc != 3) {
1059768Speter 			error("unpack expects three arguments");
1060768Speter 			return;
1061768Speter 		}
1062768Speter 		pu = "unpack(z,a,i)";
10633076Smckusic 		puz = argv[1];
10643076Smckusic 		al = argv[2];
10653076Smckusic 		pua = al[1];
10663076Smckusic 		alv = al[2];
10673076Smckusic 		pui = alv[1];
1068768Speter packunp:
10692073Smckusic 		codeoff();
1070768Speter 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
10712073Smckusic 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
10722073Smckusic 		codeon();
1073768Speter 		if (ap == NIL)
1074768Speter 			return;
1075768Speter 		if (ap->class != ARRAY) {
1076768Speter 			error("%s requires a to be an unpacked array, not %s", pu, nameof(ap));
1077768Speter 			return;
1078768Speter 		}
1079768Speter 		if (al->class != ARRAY) {
1080768Speter 			error("%s requires z to be a packed array, not %s", pu, nameof(ap));
1081768Speter 			return;
1082768Speter 		}
1083768Speter 		if (al->type == NIL || ap->type == NIL)
1084768Speter 			return;
1085768Speter 		if (al->type != ap->type) {
1086768Speter 			error("%s requires a and z to be arrays of the same type", pu, nameof(ap));
1087768Speter 			return;
1088768Speter 		}
1089768Speter 		k = width(al);
1090768Speter 		itemwidth = width(ap->type);
1091768Speter 		ap = ap->chain;
1092768Speter 		al = al->chain;
1093768Speter 		if (ap->chain != NIL || al->chain != NIL) {
1094768Speter 			error("%s requires a and z to be single dimension arrays", pu);
1095768Speter 			return;
1096768Speter 		}
1097768Speter 		if (ap == NIL || al == NIL)
1098768Speter 			return;
1099768Speter 		/*
1100768Speter 		 * al is the range for z i.e. u..v
1101768Speter 		 * ap is the range for a i.e. m..n
1102768Speter 		 * i will be n-m+1
1103768Speter 		 * j will be v-u+1
1104768Speter 		 */
1105768Speter 		i = ap->range[1] - ap->range[0] + 1;
1106768Speter 		j = al->range[1] - al->range[0] + 1;
1107768Speter 		if (i < j) {
1108768Speter 			error("%s cannot have more elements in a (%d) than in z (%d)", pu, j, i);
1109768Speter 			return;
1110768Speter 		}
1111768Speter 		/*
1112768Speter 		 * get n-m-(v-u) and m for the interpreter
1113768Speter 		 */
1114768Speter 		i -= j;
1115768Speter 		j = ap->range[0];
11162073Smckusic 		put(2, O_CON24, k);
11172073Smckusic 		put(2, O_CON24, i);
11182073Smckusic 		put(2, O_CON24, j);
11192073Smckusic 		put(2, O_CON24, itemwidth);
11202073Smckusic 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
11212073Smckusic 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
11222073Smckusic 		ap = stkrval((int *) pui, NLNIL , RREQ );
11232073Smckusic 		if (ap == NIL)
11242073Smckusic 			return;
11252073Smckusic 		put(1, op);
1126768Speter 		return;
1127768Speter 	case 0:
11287928Smckusick 		error("%s is an unimplemented extension", p->symbol);
1129768Speter 		return;
1130768Speter 
1131768Speter 	default:
1132768Speter 		panic("proc case");
1133768Speter 	}
1134768Speter }
1135768Speter #endif OBJ
1136