xref: /csrg-svn/usr.bin/pascal/src/proc.c (revision 11882)
1768Speter /* Copyright (c) 1979 Regents of the University of California */
2768Speter 
3*11882Smckusick static char sccsid[] = "@(#)proc.c 1.18 04/08/83";
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"
1411327Speter #include "tmps.h"
15768Speter 
16768Speter /*
17*11882Smckusick  * The constant EXPOSIZE specifies the number of digits in the exponent
18*11882Smckusick  * of real numbers.
19*11882Smckusick  *
209230Smckusick  * The constant REALSPC defines the amount of forced padding preceeding
219230Smckusick  * real numbers when they are printed. If REALSPC == 0, then no padding
229230Smckusick  * is added, REALSPC == 1 adds one extra blank irregardless of the width
239230Smckusick  * specified by the user.
249230Smckusick  *
259230Smckusick  * N.B. - Values greater than one require program mods.
269230Smckusick  */
27*11882Smckusick #define EXPOSIZE	2
28*11882Smckusick #define	REALSPC		0
299230Smckusick 
309230Smckusick /*
31768Speter  * The following array is used to determine which classes may be read
32768Speter  * from textfiles. It is indexed by the return value from classify.
33768Speter  */
34768Speter #define rdops(x) rdxxxx[(x)-(TFIRST)]
35768Speter 
36768Speter int rdxxxx[] = {
37768Speter 	0,		/* -7 file types */
38768Speter 	0,		/* -6 record types */
39768Speter 	0,		/* -5 array types */
40768Speter 	O_READE,	/* -4 scalar types */
41768Speter 	0,		/* -3 pointer types */
42768Speter 	0,		/* -2 set types */
43768Speter 	0,		/* -1 string types */
44768Speter 	0,		/*  0 nil, no type */
45768Speter 	O_READE,	/*  1 boolean */
46768Speter 	O_READC,	/*  2 character */
47768Speter 	O_READ4,	/*  3 integer */
48768Speter 	O_READ8		/*  4 real */
49768Speter };
50768Speter 
51768Speter /*
52768Speter  * Proc handles procedure calls.
53768Speter  * Non-builtin procedures are "buck-passed" to func (with a flag
54768Speter  * indicating that they are actually procedures.
55768Speter  * builtin procedures are handled here.
56768Speter  */
57768Speter proc(r)
58768Speter 	int *r;
59768Speter {
60768Speter 	register struct nl *p;
61768Speter 	register int *alv, *al, op;
62768Speter 	struct nl *filetype, *ap;
63768Speter 	int argc, *argv, typ, fmtspec, strfmt, stkcnt, *file;
64768Speter 	char fmt, format[20], *strptr;
65768Speter 	int prec, field, strnglen, fmtlen, fmtstart, pu;
66768Speter 	int *pua, *pui, *puz;
67768Speter 	int i, j, k;
68768Speter 	int itemwidth;
693226Smckusic 	struct tmps soffset;
703851Speter 	struct nl	*tempnlp;
71768Speter 
72768Speter #define	CONPREC 4
73768Speter #define	VARPREC 8
74768Speter #define	CONWIDTH 1
75768Speter #define	VARWIDTH 2
76768Speter #define SKIP 16
77768Speter 
78768Speter 	/*
79768Speter 	 * Verify that the name is
80768Speter 	 * defined and is that of a
81768Speter 	 * procedure.
82768Speter 	 */
83768Speter 	p = lookup(r[2]);
84768Speter 	if (p == NIL) {
85768Speter 		rvlist(r[3]);
86768Speter 		return;
87768Speter 	}
881198Speter 	if (p->class != PROC && p->class != FPROC) {
89768Speter 		error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]);
90768Speter 		rvlist(r[3]);
91768Speter 		return;
92768Speter 	}
93768Speter 	argv = r[3];
94768Speter 
95768Speter 	/*
96768Speter 	 * Call handles user defined
97768Speter 	 * procedures and functions.
98768Speter 	 */
99768Speter 	if (bn != 0) {
100768Speter 		call(p, argv, PROC, bn);
101768Speter 		return;
102768Speter 	}
103768Speter 
104768Speter 	/*
105768Speter 	 * Call to built-in procedure.
106768Speter 	 * Count the arguments.
107768Speter 	 */
108768Speter 	argc = 0;
109768Speter 	for (al = argv; al != NIL; al = al[2])
110768Speter 		argc++;
111768Speter 
112768Speter 	/*
113768Speter 	 * Switch on the operator
114768Speter 	 * associated with the built-in
115768Speter 	 * procedure in the namelist
116768Speter 	 */
117768Speter 	op = p->value[0] &~ NSTAND;
118768Speter 	if (opt('s') && (p->value[0] & NSTAND)) {
119768Speter 		standard();
120768Speter 		error("%s is a nonstandard procedure", p->symbol);
121768Speter 	}
122768Speter 	switch (op) {
123768Speter 
124768Speter 	case O_ABORT:
125768Speter 		if (argc != 0)
126768Speter 			error("null takes no arguments");
127768Speter 		return;
128768Speter 
129768Speter 	case O_FLUSH:
130768Speter 		if (argc == 0) {
131768Speter 			put(1, O_MESSAGE);
132768Speter 			return;
133768Speter 		}
134768Speter 		if (argc != 1) {
135768Speter 			error("flush takes at most one argument");
136768Speter 			return;
137768Speter 		}
1382073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
139768Speter 		if (ap == NIL)
140768Speter 			return;
141768Speter 		if (ap->class != FILET) {
142768Speter 			error("flush's argument must be a file, not %s", nameof(ap));
143768Speter 			return;
144768Speter 		}
145768Speter 		put(1, op);
146768Speter 		return;
147768Speter 
148768Speter 	case O_MESSAGE:
149768Speter 	case O_WRITEF:
150768Speter 	case O_WRITLN:
151768Speter 		/*
152768Speter 		 * Set up default file "output"'s type
153768Speter 		 */
154768Speter 		file = NIL;
155768Speter 		filetype = nl+T1CHAR;
156768Speter 		/*
157768Speter 		 * Determine the file implied
158768Speter 		 * for the write and generate
159768Speter 		 * code to make it the active file.
160768Speter 		 */
161768Speter 		if (op == O_MESSAGE) {
162768Speter 			/*
163768Speter 			 * For message, all that matters
164768Speter 			 * is that the filetype is
165768Speter 			 * a character file.
166768Speter 			 * Thus "output" will suit us fine.
167768Speter 			 */
168768Speter 			put(1, O_MESSAGE);
169768Speter 		} else if (argv != NIL && (al = argv[1])[0] != T_WEXP) {
170768Speter 			/*
171768Speter 			 * If there is a first argument which has
172768Speter 			 * no write widths, then it is potentially
173768Speter 			 * a file name.
174768Speter 			 */
175768Speter 			codeoff();
176768Speter 			ap = stkrval(argv[1], NIL , RREQ );
177768Speter 			codeon();
178768Speter 			if (ap == NIL)
179768Speter 				argv = argv[2];
180768Speter 			if (ap != NIL && ap->class == FILET) {
181768Speter 				/*
182768Speter 				 * Got "write(f, ...", make
183768Speter 				 * f the active file, and save
184768Speter 				 * it and its type for use in
185768Speter 				 * processing the rest of the
186768Speter 				 * arguments to write.
187768Speter 				 */
188768Speter 				file = argv[1];
189768Speter 				filetype = ap->type;
1902073Smckusic 				stklval(argv[1], NIL , LREQ );
191768Speter 				put(1, O_UNIT);
192768Speter 				/*
193768Speter 				 * Skip over the first argument
194768Speter 				 */
195768Speter 				argv = argv[2];
196768Speter 				argc--;
1978538Speter 			} else {
198768Speter 				/*
199768Speter 				 * Set up for writing on
200768Speter 				 * standard output.
201768Speter 				 */
202768Speter 				put(1, O_UNITOUT);
2037953Speter 				output->nl_flags |= NUSED;
2048538Speter 			}
2058538Speter 		} else {
206768Speter 			put(1, O_UNITOUT);
2077953Speter 			output->nl_flags |= NUSED;
2088538Speter 		}
209768Speter 		/*
210768Speter 		 * Loop and process each
211768Speter 		 * of the arguments.
212768Speter 		 */
213768Speter 		for (; argv != NIL; argv = argv[2]) {
214768Speter 			/*
215768Speter 			 * fmtspec indicates the type (CONstant or VARiable)
216768Speter 			 *	and number (none, WIDTH, and/or PRECision)
217768Speter 			 *	of the fields in the printf format for this
218768Speter 			 *	output variable.
2193172Smckusic 			 * stkcnt is the number of bytes pushed on the stack
220768Speter 			 * fmt is the format output indicator (D, E, F, O, X, S)
221768Speter 			 * fmtstart = 0 for leading blank; = 1 for no blank
222768Speter 			 */
223768Speter 			fmtspec = NIL;
224768Speter 			stkcnt = 0;
225768Speter 			fmt = 'D';
226768Speter 			fmtstart = 1;
227768Speter 			al = argv[1];
228768Speter 			if (al == NIL)
229768Speter 				continue;
230768Speter 			if (al[0] == T_WEXP)
231768Speter 				alv = al[1];
232768Speter 			else
233768Speter 				alv = al;
234768Speter 			if (alv == NIL)
235768Speter 				continue;
236768Speter 			codeoff();
237768Speter 			ap = stkrval(alv, NIL , RREQ );
238768Speter 			codeon();
239768Speter 			if (ap == NIL)
240768Speter 				continue;
241768Speter 			typ = classify(ap);
242768Speter 			if (al[0] == T_WEXP) {
243768Speter 				/*
244768Speter 				 * Handle width expressions.
245768Speter 				 * The basic game here is that width
246768Speter 				 * expressions get evaluated. If they
247768Speter 				 * are constant, the value is placed
248768Speter 				 * directly in the format string.
249768Speter 				 * Otherwise the value is pushed onto
250768Speter 				 * the stack and an indirection is
251768Speter 				 * put into the format string.
252768Speter 				 */
253768Speter 				if (al[3] == OCT)
254768Speter 					fmt = 'O';
255768Speter 				else if (al[3] == HEX)
256768Speter 					fmt = 'X';
257768Speter 				else if (al[3] != NIL) {
258768Speter 					/*
259768Speter 					 * Evaluate second format spec
260768Speter 					 */
261768Speter 					if ( constval(al[3])
262768Speter 					    && isa( con.ctype , "i" ) ) {
263768Speter 						fmtspec += CONPREC;
264768Speter 						prec = con.crval;
265768Speter 					} else {
266768Speter 						fmtspec += VARPREC;
267768Speter 					}
268768Speter 					fmt = 'f';
269768Speter 					switch ( typ ) {
270768Speter 					case TINT:
271768Speter 						if ( opt( 's' ) ) {
272768Speter 						    standard();
273768Speter 						    error("Writing %ss with two write widths is non-standard", clnames[typ]);
274768Speter 						}
275768Speter 						/* and fall through */
276768Speter 					case TDOUBLE:
277768Speter 						break;
278768Speter 					default:
279768Speter 						error("Cannot write %ss with two write widths", clnames[typ]);
280768Speter 						continue;
281768Speter 					}
282768Speter 				}
283768Speter 				/*
284768Speter 				 * Evaluate first format spec
285768Speter 				 */
286768Speter 				if (al[2] != NIL) {
287768Speter 					if ( constval(al[2])
288768Speter 					    && isa( con.ctype , "i" ) ) {
289768Speter 						fmtspec += CONWIDTH;
290768Speter 						field = con.crval;
291768Speter 					} else {
292768Speter 						fmtspec += VARWIDTH;
293768Speter 					}
294768Speter 				}
295768Speter 				if ((fmtspec & CONPREC) && prec < 0 ||
296768Speter 				    (fmtspec & CONWIDTH) && field < 0) {
297768Speter 					error("Negative widths are not allowed");
298768Speter 					continue;
299768Speter 				}
3003179Smckusic 				if ( opt('s') &&
3013179Smckusic 				    ((fmtspec & CONPREC) && prec == 0 ||
3023179Smckusic 				    (fmtspec & CONWIDTH) && field == 0)) {
3033179Smckusic 					standard();
3043179Smckusic 					error("Zero widths are non-standard");
3053179Smckusic 				}
306768Speter 			}
307768Speter 			if (filetype != nl+T1CHAR) {
308768Speter 				if (fmt == 'O' || fmt == 'X') {
309768Speter 					error("Oct/hex allowed only on text files");
310768Speter 					continue;
311768Speter 				}
312768Speter 				if (fmtspec) {
313768Speter 					error("Write widths allowed only on text files");
314768Speter 					continue;
315768Speter 				}
316768Speter 				/*
317768Speter 				 * Generalized write, i.e.
318768Speter 				 * to a non-textfile.
319768Speter 				 */
3202073Smckusic 				stklval(file, NIL , LREQ );
321768Speter 				put(1, O_FNIL);
322768Speter 				/*
323768Speter 				 * file^ := ...
324768Speter 				 */
325768Speter 				ap = rvalue(argv[1], NIL);
326768Speter 				if (ap == NIL)
327768Speter 					continue;
328768Speter 				if (incompat(ap, filetype, argv[1])) {
329768Speter 					cerror("Type mismatch in write to non-text file");
330768Speter 					continue;
331768Speter 				}
332768Speter 				convert(ap, filetype);
333768Speter 				put(2, O_AS, width(filetype));
334768Speter 				/*
335768Speter 				 * put(file)
336768Speter 				 */
337768Speter 				put(1, O_PUT);
338768Speter 				continue;
339768Speter 			}
340768Speter 			/*
341768Speter 			 * Write to a textfile
342768Speter 			 *
343768Speter 			 * Evaluate the expression
344768Speter 			 * to be written.
345768Speter 			 */
346768Speter 			if (fmt == 'O' || fmt == 'X') {
347768Speter 				if (opt('s')) {
348768Speter 					standard();
349768Speter 					error("Oct and hex are non-standard");
350768Speter 				}
351768Speter 				if (typ == TSTR || typ == TDOUBLE) {
352768Speter 					error("Can't write %ss with oct/hex", clnames[typ]);
353768Speter 					continue;
354768Speter 				}
355768Speter 				if (typ == TCHAR || typ == TBOOL)
356768Speter 					typ = TINT;
357768Speter 			}
358768Speter 			/*
359768Speter 			 * Place the arguement on the stack. If there is
360768Speter 			 * no format specified by the programmer, implement
361768Speter 			 * the default.
362768Speter 			 */
363768Speter 			switch (typ) {
3646542Smckusick 			case TPTR:
3656542Smckusick 				warning();
3666542Smckusick 				if (opt('s')) {
3676542Smckusick 					standard();
3686542Smckusick 				}
3696542Smckusick 				error("Writing %ss to text files is non-standard",
3706542Smckusick 				    clnames[typ]);
3716542Smckusick 				/* and fall through */
372768Speter 			case TINT:
373768Speter 				if (fmt != 'f') {
374768Speter 					ap = stkrval(alv, NIL , RREQ );
3753172Smckusic 					stkcnt += sizeof(long);
376768Speter 				} else {
377768Speter 					ap = stkrval(alv, NIL , RREQ );
378768Speter 					put(1, O_ITOD);
3793172Smckusic 					stkcnt += sizeof(double);
380768Speter 					typ = TDOUBLE;
381768Speter 					goto tdouble;
382768Speter 				}
383768Speter 				if (fmtspec == NIL) {
384768Speter 					if (fmt == 'D')
385768Speter 						field = 10;
386768Speter 					else if (fmt == 'X')
387768Speter 						field = 8;
388768Speter 					else if (fmt == 'O')
389768Speter 						field = 11;
390768Speter 					else
391768Speter 						panic("fmt1");
392768Speter 					fmtspec = CONWIDTH;
393768Speter 				}
394768Speter 				break;
395768Speter 			case TCHAR:
396768Speter 			     tchar:
3972073Smckusic 				if (fmtspec == NIL) {
3982073Smckusic 					put(1, O_FILE);
3992073Smckusic 					ap = stkrval(alv, NIL , RREQ );
4003172Smckusic 					convert(nl + T4INT, INT_TYP);
4013172Smckusic 					put(2, O_WRITEC,
4023172Smckusic 						sizeof(char *) + sizeof(int));
4032073Smckusic 					fmtspec = SKIP;
4042073Smckusic 					break;
4052073Smckusic 				}
406768Speter 				ap = stkrval(alv, NIL , RREQ );
4073172Smckusic 				convert(nl + T4INT, INT_TYP);
4083172Smckusic 				stkcnt += sizeof(int);
409768Speter 				fmt = 'c';
410768Speter 				break;
411768Speter 			case TSCAL:
4121628Speter 				warning();
413768Speter 				if (opt('s')) {
414768Speter 					standard();
415768Speter 				}
4166542Smckusick 				error("Writing %ss to text files is non-standard",
4176542Smckusick 				    clnames[typ]);
4186542Smckusick 				/* and fall through */
419768Speter 			case TBOOL:
420768Speter 				stkrval(alv, NIL , RREQ );
4213076Smckusic 				put(2, O_NAM, (long)listnames(ap));
4223172Smckusic 				stkcnt += sizeof(char *);
423768Speter 				fmt = 's';
424768Speter 				break;
425768Speter 			case TDOUBLE:
426768Speter 				ap = stkrval(alv, TDOUBLE , RREQ );
4273172Smckusic 				stkcnt += sizeof(double);
428768Speter 			     tdouble:
429768Speter 				switch (fmtspec) {
430768Speter 				case NIL:
431*11882Smckusick 					field = 14 + (5 + EXPOSIZE);
432*11882Smckusick 				        prec = field - (5 + EXPOSIZE);
4333076Smckusic 					fmt = 'e';
434768Speter 					fmtspec = CONWIDTH + CONPREC;
435768Speter 					break;
436768Speter 				case CONWIDTH:
4379230Smckusick 					field -= REALSPC;
4389230Smckusick 					if (field < 1)
439768Speter 						field = 1;
440*11882Smckusick 				        prec = field - (5 + EXPOSIZE);
441768Speter 					if (prec < 1)
442768Speter 						prec = 1;
443768Speter 					fmtspec += CONPREC;
4443076Smckusic 					fmt = 'e';
445768Speter 					break;
446768Speter 				case CONWIDTH + CONPREC:
447768Speter 				case CONWIDTH + VARPREC:
4489230Smckusick 					field -= REALSPC;
4499230Smckusick 					if (field < 1)
450768Speter 						field = 1;
451768Speter 				}
452768Speter 				format[0] = ' ';
4539230Smckusick 				fmtstart = 1 - REALSPC;
454768Speter 				break;
455768Speter 			case TSTR:
456768Speter 				constval( alv );
457768Speter 				switch ( classify( con.ctype ) ) {
458768Speter 				    case TCHAR:
459768Speter 					typ = TCHAR;
460768Speter 					goto tchar;
461768Speter 				    case TSTR:
462768Speter 					strptr = con.cpval;
463768Speter 					for (strnglen = 0;  *strptr++;  strnglen++) /* void */;
464768Speter 					strptr = con.cpval;
465768Speter 					break;
466768Speter 				    default:
467768Speter 					strnglen = width(ap);
468768Speter 					break;
469768Speter 				}
470768Speter 				fmt = 's';
471768Speter 				strfmt = fmtspec;
472768Speter 				if (fmtspec == NIL) {
473768Speter 					fmtspec = SKIP;
474768Speter 					break;
475768Speter 				}
476768Speter 				if (fmtspec & CONWIDTH) {
477768Speter 					if (field <= strnglen) {
478768Speter 						fmtspec = SKIP;
479768Speter 						break;
480768Speter 					} else
481768Speter 						field -= strnglen;
482768Speter 				}
483768Speter 				/*
484768Speter 				 * push string to implement leading blank padding
485768Speter 				 */
486768Speter 				put(2, O_LVCON, 2);
487768Speter 				putstr("", 0);
4883172Smckusic 				stkcnt += sizeof(char *);
489768Speter 				break;
490768Speter 			default:
491768Speter 				error("Can't write %ss to a text file", clnames[typ]);
492768Speter 				continue;
493768Speter 			}
494768Speter 			/*
495768Speter 			 * If there is a variable precision, evaluate it onto
496768Speter 			 * the stack
497768Speter 			 */
498768Speter 			if (fmtspec & VARPREC) {
499768Speter 				ap = stkrval(al[3], NIL , RREQ );
500768Speter 				if (ap == NIL)
501768Speter 					continue;
502768Speter 				if (isnta(ap,"i")) {
503768Speter 					error("Second write width must be integer, not %s", nameof(ap));
504768Speter 					continue;
505768Speter 				}
506768Speter 				if ( opt( 't' ) ) {
507768Speter 				    put(3, O_MAX, 0, 0);
508768Speter 				}
5093172Smckusic 				convert(nl+T4INT, INT_TYP);
5103172Smckusic 				stkcnt += sizeof(int);
511768Speter 			}
512768Speter 			/*
513768Speter 			 * If there is a variable width, evaluate it onto
514768Speter 			 * the stack
515768Speter 			 */
516768Speter 			if (fmtspec & VARWIDTH) {
517768Speter 				if ( ( typ == TDOUBLE && fmtspec == VARWIDTH )
518768Speter 				    || typ == TSTR ) {
5193226Smckusic 					soffset = sizes[cbn].curtmps;
5203851Speter 					tempnlp = tmpalloc(sizeof(long),
5213226Smckusic 						nl+T4INT, REGOK);
5223851Speter 					put(2, O_LV | cbn << 8 + INDX,
5233851Speter 					    tempnlp -> value[ NL_OFFS ] );
524768Speter 				}
525768Speter 				ap = stkrval(al[2], NIL , RREQ );
526768Speter 				if (ap == NIL)
527768Speter 					continue;
528768Speter 				if (isnta(ap,"i")) {
529768Speter 					error("First write width must be integer, not %s", nameof(ap));
530768Speter 					continue;
531768Speter 				}
532768Speter 				/*
533768Speter 				 * Perform special processing on widths based
534768Speter 				 * on data type
535768Speter 				 */
536768Speter 				switch (typ) {
537768Speter 				case TDOUBLE:
538768Speter 					if (fmtspec == VARWIDTH) {
5393076Smckusic 						fmt = 'e';
540768Speter 						put(1, O_AS4);
5413851Speter 						put(2, O_RV4 | cbn << 8 + INDX,
5423851Speter 						    tempnlp -> value[NL_OFFS] );
543*11882Smckusick 					        put(3, O_MAX,
544*11882Smckusick 						    5 + EXPOSIZE + REALSPC, 1);
5453172Smckusic 						convert(nl+T4INT, INT_TYP);
5463172Smckusic 						stkcnt += sizeof(int);
5473851Speter 						put(2, O_RV4 | cbn << 8 + INDX,
5483851Speter 						    tempnlp->value[NL_OFFS] );
549768Speter 						fmtspec += VARPREC;
5503226Smckusic 						tmpfree(&soffset);
551768Speter 					}
5529230Smckusick 					put(3, O_MAX, REALSPC, 1);
553768Speter 					break;
554768Speter 				case TSTR:
555768Speter 					put(1, O_AS4);
5563851Speter 					put(2, O_RV4 | cbn << 8 + INDX,
5573851Speter 					    tempnlp -> value[ NL_OFFS ] );
558768Speter 					put(3, O_MAX, strnglen, 0);
559768Speter 					break;
560768Speter 				default:
561768Speter 					if ( opt( 't' ) ) {
562768Speter 					    put(3, O_MAX, 0, 0);
563768Speter 					}
564768Speter 					break;
565768Speter 				}
5663172Smckusic 				convert(nl+T4INT, INT_TYP);
5673172Smckusic 				stkcnt += sizeof(int);
568768Speter 			}
569768Speter 			/*
570768Speter 			 * Generate the format string
571768Speter 			 */
572768Speter 			switch (fmtspec) {
573768Speter 			default:
574768Speter 				panic("fmt2");
575768Speter 			case SKIP:
576768Speter 				break;
5772073Smckusic 			case NIL:
5782073Smckusic 				sprintf(&format[1], "%%%c", fmt);
5792073Smckusic 				goto fmtgen;
580768Speter 			case CONWIDTH:
5813076Smckusic 				sprintf(&format[1], "%%%d%c", field, fmt);
582768Speter 				goto fmtgen;
583768Speter 			case VARWIDTH:
584768Speter 				sprintf(&format[1], "%%*%c", fmt);
585768Speter 				goto fmtgen;
586768Speter 			case CONWIDTH + CONPREC:
5873076Smckusic 				sprintf(&format[1], "%%%d.%d%c", field, prec, fmt);
588768Speter 				goto fmtgen;
589768Speter 			case CONWIDTH + VARPREC:
5903076Smckusic 				sprintf(&format[1], "%%%d.*%c", field, fmt);
591768Speter 				goto fmtgen;
592768Speter 			case VARWIDTH + CONPREC:
5933076Smckusic 				sprintf(&format[1], "%%*.%d%c", prec, fmt);
594768Speter 				goto fmtgen;
595768Speter 			case VARWIDTH + VARPREC:
596768Speter 				sprintf(&format[1], "%%*.*%c", fmt);
597768Speter 			fmtgen:
598768Speter 				fmtlen = lenstr(&format[fmtstart], 0);
599768Speter 				put(2, O_LVCON, fmtlen);
600768Speter 				putstr(&format[fmtstart], 0);
601768Speter 				put(1, O_FILE);
6023172Smckusic 				stkcnt += 2 * sizeof(char *);
603768Speter 				put(2, O_WRITEF, stkcnt);
604768Speter 			}
605768Speter 			/*
606768Speter 			 * Write the string after its blank padding
607768Speter 			 */
608768Speter 			if (typ == TSTR) {
609768Speter 				put(1, O_FILE);
6103172Smckusic 				put(2, CON_INT, 1);
611768Speter 				if (strfmt & VARWIDTH) {
6123851Speter 					put(2, O_RV4 | cbn << 8 + INDX ,
6133851Speter 					    tempnlp -> value[ NL_OFFS ] );
614768Speter 					put(2, O_MIN, strnglen);
6153172Smckusic 					convert(nl+T4INT, INT_TYP);
6163226Smckusic 					tmpfree(&soffset);
617768Speter 				} else {
618768Speter 					if ((fmtspec & SKIP) &&
619768Speter 					   (strfmt & CONWIDTH)) {
620768Speter 						strnglen = field;
621768Speter 					}
6223172Smckusic 					put(2, CON_INT, strnglen);
623768Speter 				}
624768Speter 				ap = stkrval(alv, NIL , RREQ );
6253172Smckusic 				put(2, O_WRITES,
6263172Smckusic 					2 * sizeof(char *) + 2 * sizeof(int));
627768Speter 			}
628768Speter 		}
629768Speter 		/*
630768Speter 		 * Done with arguments.
631768Speter 		 * Handle writeln and
632768Speter 		 * insufficent number of args.
633768Speter 		 */
634768Speter 		switch (p->value[0] &~ NSTAND) {
635768Speter 			case O_WRITEF:
636768Speter 				if (argc == 0)
637768Speter 					error("Write requires an argument");
638768Speter 				break;
639768Speter 			case O_MESSAGE:
640768Speter 				if (argc == 0)
641768Speter 					error("Message requires an argument");
642768Speter 			case O_WRITLN:
643768Speter 				if (filetype != nl+T1CHAR)
644768Speter 					error("Can't 'writeln' a non text file");
645768Speter 				put(1, O_WRITLN);
646768Speter 				break;
647768Speter 		}
648768Speter 		return;
649768Speter 
650768Speter 	case O_READ4:
651768Speter 	case O_READLN:
652768Speter 		/*
653768Speter 		 * Set up default
654768Speter 		 * file "input".
655768Speter 		 */
656768Speter 		file = NIL;
657768Speter 		filetype = nl+T1CHAR;
658768Speter 		/*
659768Speter 		 * Determine the file implied
660768Speter 		 * for the read and generate
661768Speter 		 * code to make it the active file.
662768Speter 		 */
663768Speter 		if (argv != NIL) {
664768Speter 			codeoff();
665768Speter 			ap = stkrval(argv[1], NIL , RREQ );
666768Speter 			codeon();
667768Speter 			if (ap == NIL)
668768Speter 				argv = argv[2];
669768Speter 			if (ap != NIL && ap->class == FILET) {
670768Speter 				/*
671768Speter 				 * Got "read(f, ...", make
672768Speter 				 * f the active file, and save
673768Speter 				 * it and its type for use in
674768Speter 				 * processing the rest of the
675768Speter 				 * arguments to read.
676768Speter 				 */
677768Speter 				file = argv[1];
678768Speter 				filetype = ap->type;
6792073Smckusic 				stklval(argv[1], NIL , LREQ );
680768Speter 				put(1, O_UNIT);
681768Speter 				argv = argv[2];
682768Speter 				argc--;
683768Speter 			} else {
684768Speter 				/*
685768Speter 				 * Default is read from
686768Speter 				 * standard input.
687768Speter 				 */
688768Speter 				put(1, O_UNITINP);
689768Speter 				input->nl_flags |= NUSED;
690768Speter 			}
691768Speter 		} else {
692768Speter 			put(1, O_UNITINP);
693768Speter 			input->nl_flags |= NUSED;
694768Speter 		}
695768Speter 		/*
696768Speter 		 * Loop and process each
697768Speter 		 * of the arguments.
698768Speter 		 */
699768Speter 		for (; argv != NIL; argv = argv[2]) {
700768Speter 			/*
701768Speter 			 * Get the address of the target
702768Speter 			 * on the stack.
703768Speter 			 */
704768Speter 			al = argv[1];
705768Speter 			if (al == NIL)
706768Speter 				continue;
707768Speter 			if (al[0] != T_VAR) {
708768Speter 				error("Arguments to %s must be variables, not expressions", p->symbol);
709768Speter 				continue;
710768Speter 			}
711768Speter 			ap = stklval(al, MOD|ASGN|NOUSE);
712768Speter 			if (ap == NIL)
713768Speter 				continue;
714768Speter 			if (filetype != nl+T1CHAR) {
715768Speter 				/*
716768Speter 				 * Generalized read, i.e.
717768Speter 				 * from a non-textfile.
718768Speter 				 */
719768Speter 				if (incompat(filetype, ap, argv[1] )) {
720768Speter 					error("Type mismatch in read from non-text file");
721768Speter 					continue;
722768Speter 				}
723768Speter 				/*
724768Speter 				 * var := file ^;
725768Speter 				 */
726768Speter 				if (file != NIL)
7272073Smckusic 					stklval(file, NIL , LREQ );
728768Speter 				else /* Magic */
7293076Smckusic 					put(2, PTR_RV, (int)input->value[0]);
730768Speter 				put(1, O_FNIL);
731768Speter 				put(2, O_IND, width(filetype));
732768Speter 				convert(filetype, ap);
733768Speter 				if (isa(ap, "bsci"))
734768Speter 					rangechk(ap, ap);
735768Speter 				put(2, O_AS, width(ap));
736768Speter 				/*
737768Speter 				 * get(file);
738768Speter 				 */
739768Speter 				put(1, O_GET);
740768Speter 				continue;
741768Speter 			}
742768Speter 			typ = classify(ap);
743768Speter 			op = rdops(typ);
744768Speter 			if (op == NIL) {
745768Speter 				error("Can't read %ss from a text file", clnames[typ]);
746768Speter 				continue;
747768Speter 			}
748768Speter 			if (op != O_READE)
749768Speter 				put(1, op);
750768Speter 			else {
7513076Smckusic 				put(2, op, (long)listnames(ap));
7521628Speter 				warning();
753768Speter 				if (opt('s')) {
754768Speter 					standard();
755768Speter 				}
7561628Speter 				error("Reading scalars from text files is non-standard");
757768Speter 			}
758768Speter 			/*
759768Speter 			 * Data read is on the stack.
760768Speter 			 * Assign it.
761768Speter 			 */
762768Speter 			if (op != O_READ8 && op != O_READE)
763768Speter 				rangechk(ap, op == O_READC ? ap : nl+T4INT);
764768Speter 			gen(O_AS2, O_AS2, width(ap),
765768Speter 				op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2);
766768Speter 		}
767768Speter 		/*
768768Speter 		 * Done with arguments.
769768Speter 		 * Handle readln and
770768Speter 		 * insufficient number of args.
771768Speter 		 */
772768Speter 		if (p->value[0] == O_READLN) {
773768Speter 			if (filetype != nl+T1CHAR)
774768Speter 				error("Can't 'readln' a non text file");
775768Speter 			put(1, O_READLN);
776768Speter 		}
777768Speter 		else if (argc == 0)
778768Speter 			error("read requires an argument");
779768Speter 		return;
780768Speter 
781768Speter 	case O_GET:
782768Speter 	case O_PUT:
783768Speter 		if (argc != 1) {
784768Speter 			error("%s expects one argument", p->symbol);
785768Speter 			return;
786768Speter 		}
7872073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
788768Speter 		if (ap == NIL)
789768Speter 			return;
790768Speter 		if (ap->class != FILET) {
791768Speter 			error("Argument to %s must be a file, not %s", p->symbol, nameof(ap));
792768Speter 			return;
793768Speter 		}
794768Speter 		put(1, O_UNIT);
795768Speter 		put(1, op);
796768Speter 		return;
797768Speter 
798768Speter 	case O_RESET:
799768Speter 	case O_REWRITE:
800768Speter 		if (argc == 0 || argc > 2) {
801768Speter 			error("%s expects one or two arguments", p->symbol);
802768Speter 			return;
803768Speter 		}
804768Speter 		if (opt('s') && argc == 2) {
805768Speter 			standard();
806768Speter 			error("Two argument forms of reset and rewrite are non-standard");
807768Speter 		}
8082073Smckusic 		codeoff();
809768Speter 		ap = stklval(argv[1], MOD|NOUSE);
8102073Smckusic 		codeon();
811768Speter 		if (ap == NIL)
812768Speter 			return;
813768Speter 		if (ap->class != FILET) {
814768Speter 			error("First argument to %s must be a file, not %s", p->symbol, nameof(ap));
815768Speter 			return;
816768Speter 		}
8172073Smckusic 		put(2, O_CON24, text(ap) ? 0: width(ap->type));
818768Speter 		if (argc == 2) {
819768Speter 			/*
820768Speter 			 * Optional second argument
821768Speter 			 * is a string name of a
822768Speter 			 * UNIX (R) file to be associated.
823768Speter 			 */
824768Speter 			al = argv[2];
8252073Smckusic 			codeoff();
826768Speter 			al = stkrval(al[1], NOFLAGS , RREQ );
8272073Smckusic 			codeon();
828768Speter 			if (al == NIL)
829768Speter 				return;
830768Speter 			if (classify(al) != TSTR) {
831768Speter 				error("Second argument to %s must be a string, not %s", p->symbol, nameof(al));
832768Speter 				return;
833768Speter 			}
8342073Smckusic 			put(2, O_CON24, width(al));
8352073Smckusic 			al = argv[2];
8362073Smckusic 			al = stkrval(al[1], NOFLAGS , RREQ );
837768Speter 		} else {
8382073Smckusic 			put(2, O_CON24, 0);
8393076Smckusic 			put(2, PTR_CON, NIL);
840768Speter 		}
8412073Smckusic 		ap = stklval(argv[1], MOD|NOUSE);
842768Speter 		put(1, op);
843768Speter 		return;
844768Speter 
845768Speter 	case O_NEW:
846768Speter 	case O_DISPOSE:
847768Speter 		if (argc == 0) {
848768Speter 			error("%s expects at least one argument", p->symbol);
849768Speter 			return;
850768Speter 		}
851768Speter 		ap = stklval(argv[1], op == O_NEW ? ( MOD | NOUSE ) : MOD );
852768Speter 		if (ap == NIL)
853768Speter 			return;
854768Speter 		if (ap->class != PTR) {
855768Speter 			error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap));
856768Speter 			return;
857768Speter 		}
858768Speter 		ap = ap->type;
859768Speter 		if (ap == NIL)
860768Speter 			return;
8617966Smckusick 		if ((ap->nl_flags & NFILES) && op == O_DISPOSE)
8627966Smckusick 			op = O_DFDISP;
863768Speter 		argv = argv[2];
864768Speter 		if (argv != NIL) {
865768Speter 			if (ap->class != RECORD) {
866768Speter 				error("Record required when specifying variant tags");
867768Speter 				return;
868768Speter 			}
869768Speter 			for (; argv != NIL; argv = argv[2]) {
870768Speter 				if (ap->ptr[NL_VARNT] == NIL) {
871768Speter 					error("Too many tag fields");
872768Speter 					return;
873768Speter 				}
874768Speter 				if (!isconst(argv[1])) {
875768Speter 					error("Second and successive arguments to %s must be constants", p->symbol);
876768Speter 					return;
877768Speter 				}
878768Speter 				gconst(argv[1]);
879768Speter 				if (con.ctype == NIL)
880768Speter 					return;
881768Speter 				if (incompat(con.ctype, (ap->ptr[NL_TAG])->type , NIL )) {
882768Speter 					cerror("Specified tag constant type clashed with variant case selector type");
883768Speter 					return;
884768Speter 				}
885768Speter 				for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain)
886768Speter 					if (ap->range[0] == con.crval)
887768Speter 						break;
888768Speter 				if (ap == NIL) {
889768Speter 					error("No variant case label value equals specified constant value");
890768Speter 					return;
891768Speter 				}
892768Speter 				ap = ap->ptr[NL_VTOREC];
893768Speter 			}
894768Speter 		}
895768Speter 		put(2, op, width(ap));
896768Speter 		return;
897768Speter 
898768Speter 	case O_DATE:
899768Speter 	case O_TIME:
900768Speter 		if (argc != 1) {
901768Speter 			error("%s expects one argument", p->symbol);
902768Speter 			return;
903768Speter 		}
904768Speter 		ap = stklval(argv[1], MOD|NOUSE);
905768Speter 		if (ap == NIL)
906768Speter 			return;
907768Speter 		if (classify(ap) != TSTR || width(ap) != 10) {
908768Speter 			error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap));
909768Speter 			return;
910768Speter 		}
911768Speter 		put(1, op);
912768Speter 		return;
913768Speter 
914768Speter 	case O_HALT:
915768Speter 		if (argc != 0) {
916768Speter 			error("halt takes no arguments");
917768Speter 			return;
918768Speter 		}
919768Speter 		put(1, op);
920768Speter 		noreach = 1;
921768Speter 		return;
922768Speter 
923768Speter 	case O_ARGV:
924768Speter 		if (argc != 2) {
925768Speter 			error("argv takes two arguments");
926768Speter 			return;
927768Speter 		}
928768Speter 		ap = stkrval(argv[1], NIL , RREQ );
929768Speter 		if (ap == NIL)
930768Speter 			return;
931768Speter 		if (isnta(ap, "i")) {
932768Speter 			error("argv's first argument must be an integer, not %s", nameof(ap));
933768Speter 			return;
934768Speter 		}
935768Speter 		al = argv[2];
936768Speter 		ap = stklval(al[1], MOD|NOUSE);
937768Speter 		if (ap == NIL)
938768Speter 			return;
939768Speter 		if (classify(ap) != TSTR) {
940768Speter 			error("argv's second argument must be a string, not %s", nameof(ap));
941768Speter 			return;
942768Speter 		}
943768Speter 		put(2, op, width(ap));
944768Speter 		return;
945768Speter 
946768Speter 	case O_STLIM:
947768Speter 		if (argc != 1) {
948768Speter 			error("stlimit requires one argument");
949768Speter 			return;
950768Speter 		}
951768Speter 		ap = stkrval(argv[1], NIL , RREQ );
952768Speter 		if (ap == NIL)
953768Speter 			return;
954768Speter 		if (isnta(ap, "i")) {
955768Speter 			error("stlimit's argument must be an integer, not %s", nameof(ap));
956768Speter 			return;
957768Speter 		}
958768Speter 		if (width(ap) != 4)
959768Speter 			put(1, O_STOI);
960768Speter 		put(1, op);
961768Speter 		return;
962768Speter 
963768Speter 	case O_REMOVE:
964768Speter 		if (argc != 1) {
965768Speter 			error("remove expects one argument");
966768Speter 			return;
967768Speter 		}
9682073Smckusic 		codeoff();
969768Speter 		ap = stkrval(argv[1], NOFLAGS , RREQ );
9702073Smckusic 		codeon();
971768Speter 		if (ap == NIL)
972768Speter 			return;
973768Speter 		if (classify(ap) != TSTR) {
974768Speter 			error("remove's argument must be a string, not %s", nameof(ap));
975768Speter 			return;
976768Speter 		}
977768Speter 		put(2, O_CON24, width(ap));
9782073Smckusic 		ap = stkrval(argv[1], NOFLAGS , RREQ );
979768Speter 		put(1, op);
980768Speter 		return;
981768Speter 
982768Speter 	case O_LLIMIT:
983768Speter 		if (argc != 2) {
984768Speter 			error("linelimit expects two arguments");
985768Speter 			return;
986768Speter 		}
987768Speter 		al = argv[2];
988768Speter 		ap = stkrval(al[1], NIL , RREQ );
989768Speter 		if (ap == NIL)
990768Speter 			return;
991768Speter 		if (isnta(ap, "i")) {
992768Speter 			error("linelimit's second argument must be an integer, not %s", nameof(ap));
993768Speter 			return;
994768Speter 		}
9952073Smckusic 		ap = stklval(argv[1], NOFLAGS|NOUSE);
9962073Smckusic 		if (ap == NIL)
9972073Smckusic 			return;
9982073Smckusic 		if (!text(ap)) {
9992073Smckusic 			error("linelimit's first argument must be a text file, not %s", nameof(ap));
10002073Smckusic 			return;
10012073Smckusic 		}
1002768Speter 		put(1, op);
1003768Speter 		return;
1004768Speter 	case O_PAGE:
1005768Speter 		if (argc != 1) {
1006768Speter 			error("page expects one argument");
1007768Speter 			return;
1008768Speter 		}
10092073Smckusic 		ap = stklval(argv[1], NIL , LREQ );
1010768Speter 		if (ap == NIL)
1011768Speter 			return;
1012768Speter 		if (!text(ap)) {
1013768Speter 			error("Argument to page must be a text file, not %s", nameof(ap));
1014768Speter 			return;
1015768Speter 		}
1016768Speter 		put(1, O_UNIT);
1017768Speter 		put(1, op);
1018768Speter 		return;
1019768Speter 
10207928Smckusick 	case O_ASRT:
10217928Smckusick 		if (!opt('t'))
10227928Smckusick 			return;
10237928Smckusick 		if (argc == 0 || argc > 2) {
10247928Smckusick 			error("Assert expects one or two arguments");
10257928Smckusick 			return;
10267928Smckusick 		}
10277928Smckusick 		if (argc == 2) {
10287928Smckusick 			/*
10297928Smckusick 			 * Optional second argument is a string specifying
10307928Smckusick 			 * why the assertion failed.
10317928Smckusick 			 */
10327928Smckusick 			al = argv[2];
10337928Smckusick 			al = stkrval(al[1], NIL , RREQ );
10347928Smckusick 			if (al == NIL)
10357928Smckusick 				return;
10367928Smckusick 			if (classify(al) != TSTR) {
10377928Smckusick 				error("Second argument to assert must be a string, not %s", nameof(al));
10387928Smckusick 				return;
10397928Smckusick 			}
10407928Smckusick 		} else {
10417928Smckusick 			put(2, PTR_CON, NIL);
10427928Smckusick 		}
10437928Smckusick 		ap = stkrval(argv[1], NIL , RREQ );
10447928Smckusick 		if (ap == NIL)
10457928Smckusick 			return;
10467928Smckusick 		if (isnta(ap, "b"))
10477928Smckusick 			error("Assert expression must be Boolean, not %ss", nameof(ap));
10487928Smckusick 		put(1, O_ASRT);
10497928Smckusick 		return;
10507928Smckusick 
1051768Speter 	case O_PACK:
1052768Speter 		if (argc != 3) {
1053768Speter 			error("pack expects three arguments");
1054768Speter 			return;
1055768Speter 		}
1056768Speter 		pu = "pack(a,i,z)";
10573076Smckusic 		pua = argv[1];
10583076Smckusic 		al = argv[2];
10593076Smckusic 		pui = al[1];
10603076Smckusic 		alv = al[2];
10613076Smckusic 		puz = alv[1];
1062768Speter 		goto packunp;
1063768Speter 	case O_UNPACK:
1064768Speter 		if (argc != 3) {
1065768Speter 			error("unpack expects three arguments");
1066768Speter 			return;
1067768Speter 		}
1068768Speter 		pu = "unpack(z,a,i)";
10693076Smckusic 		puz = argv[1];
10703076Smckusic 		al = argv[2];
10713076Smckusic 		pua = al[1];
10723076Smckusic 		alv = al[2];
10733076Smckusic 		pui = alv[1];
1074768Speter packunp:
10752073Smckusic 		codeoff();
1076768Speter 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
10772073Smckusic 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
10782073Smckusic 		codeon();
1079768Speter 		if (ap == NIL)
1080768Speter 			return;
1081768Speter 		if (ap->class != ARRAY) {
1082768Speter 			error("%s requires a to be an unpacked array, not %s", pu, nameof(ap));
1083768Speter 			return;
1084768Speter 		}
1085768Speter 		if (al->class != ARRAY) {
1086768Speter 			error("%s requires z to be a packed array, not %s", pu, nameof(ap));
1087768Speter 			return;
1088768Speter 		}
1089768Speter 		if (al->type == NIL || ap->type == NIL)
1090768Speter 			return;
1091768Speter 		if (al->type != ap->type) {
1092768Speter 			error("%s requires a and z to be arrays of the same type", pu, nameof(ap));
1093768Speter 			return;
1094768Speter 		}
1095768Speter 		k = width(al);
1096768Speter 		itemwidth = width(ap->type);
1097768Speter 		ap = ap->chain;
1098768Speter 		al = al->chain;
1099768Speter 		if (ap->chain != NIL || al->chain != NIL) {
1100768Speter 			error("%s requires a and z to be single dimension arrays", pu);
1101768Speter 			return;
1102768Speter 		}
1103768Speter 		if (ap == NIL || al == NIL)
1104768Speter 			return;
1105768Speter 		/*
1106768Speter 		 * al is the range for z i.e. u..v
1107768Speter 		 * ap is the range for a i.e. m..n
1108768Speter 		 * i will be n-m+1
1109768Speter 		 * j will be v-u+1
1110768Speter 		 */
1111768Speter 		i = ap->range[1] - ap->range[0] + 1;
1112768Speter 		j = al->range[1] - al->range[0] + 1;
1113768Speter 		if (i < j) {
1114768Speter 			error("%s cannot have more elements in a (%d) than in z (%d)", pu, j, i);
1115768Speter 			return;
1116768Speter 		}
1117768Speter 		/*
1118768Speter 		 * get n-m-(v-u) and m for the interpreter
1119768Speter 		 */
1120768Speter 		i -= j;
1121768Speter 		j = ap->range[0];
11222073Smckusic 		put(2, O_CON24, k);
11232073Smckusic 		put(2, O_CON24, i);
11242073Smckusic 		put(2, O_CON24, j);
11252073Smckusic 		put(2, O_CON24, itemwidth);
11262073Smckusic 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
11272073Smckusic 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
11282073Smckusic 		ap = stkrval((int *) pui, NLNIL , RREQ );
11292073Smckusic 		if (ap == NIL)
11302073Smckusic 			return;
11312073Smckusic 		put(1, op);
1132768Speter 		return;
1133768Speter 	case 0:
11347928Smckusick 		error("%s is an unimplemented extension", p->symbol);
1135768Speter 		return;
1136768Speter 
1137768Speter 	default:
1138768Speter 		panic("proc case");
1139768Speter 	}
1140768Speter }
1141768Speter #endif OBJ
1142