xref: /csrg-svn/usr.bin/pascal/src/proc.c (revision 8538)
1 /* Copyright (c) 1979 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)proc.c 1.15 10/14/82";
4 
5 #include "whoami.h"
6 #ifdef OBJ
7     /*
8      *	and the rest of the file
9      */
10 #include "0.h"
11 #include "tree.h"
12 #include "opcode.h"
13 #include "objfmt.h"
14 
15 /*
16  * The following array is used to determine which classes may be read
17  * from textfiles. It is indexed by the return value from classify.
18  */
19 #define rdops(x) rdxxxx[(x)-(TFIRST)]
20 
21 int rdxxxx[] = {
22 	0,		/* -7 file types */
23 	0,		/* -6 record types */
24 	0,		/* -5 array types */
25 	O_READE,	/* -4 scalar types */
26 	0,		/* -3 pointer types */
27 	0,		/* -2 set types */
28 	0,		/* -1 string types */
29 	0,		/*  0 nil, no type */
30 	O_READE,	/*  1 boolean */
31 	O_READC,	/*  2 character */
32 	O_READ4,	/*  3 integer */
33 	O_READ8		/*  4 real */
34 };
35 
36 /*
37  * Proc handles procedure calls.
38  * Non-builtin procedures are "buck-passed" to func (with a flag
39  * indicating that they are actually procedures.
40  * builtin procedures are handled here.
41  */
42 proc(r)
43 	int *r;
44 {
45 	register struct nl *p;
46 	register int *alv, *al, op;
47 	struct nl *filetype, *ap;
48 	int argc, *argv, typ, fmtspec, strfmt, stkcnt, *file;
49 	char fmt, format[20], *strptr;
50 	int prec, field, strnglen, fmtlen, fmtstart, pu;
51 	int *pua, *pui, *puz;
52 	int i, j, k;
53 	int itemwidth;
54 	struct tmps soffset;
55 	struct nl	*tempnlp;
56 
57 #define	CONPREC 4
58 #define	VARPREC 8
59 #define	CONWIDTH 1
60 #define	VARWIDTH 2
61 #define SKIP 16
62 
63 	/*
64 	 * Verify that the name is
65 	 * defined and is that of a
66 	 * procedure.
67 	 */
68 	p = lookup(r[2]);
69 	if (p == NIL) {
70 		rvlist(r[3]);
71 		return;
72 	}
73 	if (p->class != PROC && p->class != FPROC) {
74 		error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]);
75 		rvlist(r[3]);
76 		return;
77 	}
78 	argv = r[3];
79 
80 	/*
81 	 * Call handles user defined
82 	 * procedures and functions.
83 	 */
84 	if (bn != 0) {
85 		call(p, argv, PROC, bn);
86 		return;
87 	}
88 
89 	/*
90 	 * Call to built-in procedure.
91 	 * Count the arguments.
92 	 */
93 	argc = 0;
94 	for (al = argv; al != NIL; al = al[2])
95 		argc++;
96 
97 	/*
98 	 * Switch on the operator
99 	 * associated with the built-in
100 	 * procedure in the namelist
101 	 */
102 	op = p->value[0] &~ NSTAND;
103 	if (opt('s') && (p->value[0] & NSTAND)) {
104 		standard();
105 		error("%s is a nonstandard procedure", p->symbol);
106 	}
107 	switch (op) {
108 
109 	case O_ABORT:
110 		if (argc != 0)
111 			error("null takes no arguments");
112 		return;
113 
114 	case O_FLUSH:
115 		if (argc == 0) {
116 			put(1, O_MESSAGE);
117 			return;
118 		}
119 		if (argc != 1) {
120 			error("flush takes at most one argument");
121 			return;
122 		}
123 		ap = stklval(argv[1], NIL , LREQ );
124 		if (ap == NIL)
125 			return;
126 		if (ap->class != FILET) {
127 			error("flush's argument must be a file, not %s", nameof(ap));
128 			return;
129 		}
130 		put(1, op);
131 		return;
132 
133 	case O_MESSAGE:
134 	case O_WRITEF:
135 	case O_WRITLN:
136 		/*
137 		 * Set up default file "output"'s type
138 		 */
139 		file = NIL;
140 		filetype = nl+T1CHAR;
141 		/*
142 		 * Determine the file implied
143 		 * for the write and generate
144 		 * code to make it the active file.
145 		 */
146 		if (op == O_MESSAGE) {
147 			/*
148 			 * For message, all that matters
149 			 * is that the filetype is
150 			 * a character file.
151 			 * Thus "output" will suit us fine.
152 			 */
153 			put(1, O_MESSAGE);
154 		} else if (argv != NIL && (al = argv[1])[0] != T_WEXP) {
155 			/*
156 			 * If there is a first argument which has
157 			 * no write widths, then it is potentially
158 			 * a file name.
159 			 */
160 			codeoff();
161 			ap = stkrval(argv[1], NIL , RREQ );
162 			codeon();
163 			if (ap == NIL)
164 				argv = argv[2];
165 			if (ap != NIL && ap->class == FILET) {
166 				/*
167 				 * Got "write(f, ...", make
168 				 * f the active file, and save
169 				 * it and its type for use in
170 				 * processing the rest of the
171 				 * arguments to write.
172 				 */
173 				file = argv[1];
174 				filetype = ap->type;
175 				stklval(argv[1], NIL , LREQ );
176 				put(1, O_UNIT);
177 				/*
178 				 * Skip over the first argument
179 				 */
180 				argv = argv[2];
181 				argc--;
182 			} else {
183 				/*
184 				 * Set up for writing on
185 				 * standard output.
186 				 */
187 				put(1, O_UNITOUT);
188 				output->nl_flags |= NUSED;
189 			}
190 		} else {
191 			put(1, O_UNITOUT);
192 			output->nl_flags |= NUSED;
193 		}
194 		/*
195 		 * Loop and process each
196 		 * of the arguments.
197 		 */
198 		for (; argv != NIL; argv = argv[2]) {
199 			/*
200 			 * fmtspec indicates the type (CONstant or VARiable)
201 			 *	and number (none, WIDTH, and/or PRECision)
202 			 *	of the fields in the printf format for this
203 			 *	output variable.
204 			 * stkcnt is the number of bytes pushed on the stack
205 			 * fmt is the format output indicator (D, E, F, O, X, S)
206 			 * fmtstart = 0 for leading blank; = 1 for no blank
207 			 */
208 			fmtspec = NIL;
209 			stkcnt = 0;
210 			fmt = 'D';
211 			fmtstart = 1;
212 			al = argv[1];
213 			if (al == NIL)
214 				continue;
215 			if (al[0] == T_WEXP)
216 				alv = al[1];
217 			else
218 				alv = al;
219 			if (alv == NIL)
220 				continue;
221 			codeoff();
222 			ap = stkrval(alv, NIL , RREQ );
223 			codeon();
224 			if (ap == NIL)
225 				continue;
226 			typ = classify(ap);
227 			if (al[0] == T_WEXP) {
228 				/*
229 				 * Handle width expressions.
230 				 * The basic game here is that width
231 				 * expressions get evaluated. If they
232 				 * are constant, the value is placed
233 				 * directly in the format string.
234 				 * Otherwise the value is pushed onto
235 				 * the stack and an indirection is
236 				 * put into the format string.
237 				 */
238 				if (al[3] == OCT)
239 					fmt = 'O';
240 				else if (al[3] == HEX)
241 					fmt = 'X';
242 				else if (al[3] != NIL) {
243 					/*
244 					 * Evaluate second format spec
245 					 */
246 					if ( constval(al[3])
247 					    && isa( con.ctype , "i" ) ) {
248 						fmtspec += CONPREC;
249 						prec = con.crval;
250 					} else {
251 						fmtspec += VARPREC;
252 					}
253 					fmt = 'f';
254 					switch ( typ ) {
255 					case TINT:
256 						if ( opt( 's' ) ) {
257 						    standard();
258 						    error("Writing %ss with two write widths is non-standard", clnames[typ]);
259 						}
260 						/* and fall through */
261 					case TDOUBLE:
262 						break;
263 					default:
264 						error("Cannot write %ss with two write widths", clnames[typ]);
265 						continue;
266 					}
267 				}
268 				/*
269 				 * Evaluate first format spec
270 				 */
271 				if (al[2] != NIL) {
272 					if ( constval(al[2])
273 					    && isa( con.ctype , "i" ) ) {
274 						fmtspec += CONWIDTH;
275 						field = con.crval;
276 					} else {
277 						fmtspec += VARWIDTH;
278 					}
279 				}
280 				if ((fmtspec & CONPREC) && prec < 0 ||
281 				    (fmtspec & CONWIDTH) && field < 0) {
282 					error("Negative widths are not allowed");
283 					continue;
284 				}
285 				if ( opt('s') &&
286 				    ((fmtspec & CONPREC) && prec == 0 ||
287 				    (fmtspec & CONWIDTH) && field == 0)) {
288 					standard();
289 					error("Zero widths are non-standard");
290 				}
291 			}
292 			if (filetype != nl+T1CHAR) {
293 				if (fmt == 'O' || fmt == 'X') {
294 					error("Oct/hex allowed only on text files");
295 					continue;
296 				}
297 				if (fmtspec) {
298 					error("Write widths allowed only on text files");
299 					continue;
300 				}
301 				/*
302 				 * Generalized write, i.e.
303 				 * to a non-textfile.
304 				 */
305 				stklval(file, NIL , LREQ );
306 				put(1, O_FNIL);
307 				/*
308 				 * file^ := ...
309 				 */
310 				ap = rvalue(argv[1], NIL);
311 				if (ap == NIL)
312 					continue;
313 				if (incompat(ap, filetype, argv[1])) {
314 					cerror("Type mismatch in write to non-text file");
315 					continue;
316 				}
317 				convert(ap, filetype);
318 				put(2, O_AS, width(filetype));
319 				/*
320 				 * put(file)
321 				 */
322 				put(1, O_PUT);
323 				continue;
324 			}
325 			/*
326 			 * Write to a textfile
327 			 *
328 			 * Evaluate the expression
329 			 * to be written.
330 			 */
331 			if (fmt == 'O' || fmt == 'X') {
332 				if (opt('s')) {
333 					standard();
334 					error("Oct and hex are non-standard");
335 				}
336 				if (typ == TSTR || typ == TDOUBLE) {
337 					error("Can't write %ss with oct/hex", clnames[typ]);
338 					continue;
339 				}
340 				if (typ == TCHAR || typ == TBOOL)
341 					typ = TINT;
342 			}
343 			/*
344 			 * Place the arguement on the stack. If there is
345 			 * no format specified by the programmer, implement
346 			 * the default.
347 			 */
348 			switch (typ) {
349 			case TPTR:
350 				warning();
351 				if (opt('s')) {
352 					standard();
353 				}
354 				error("Writing %ss to text files is non-standard",
355 				    clnames[typ]);
356 				/* and fall through */
357 			case TINT:
358 				if (fmt != 'f') {
359 					ap = stkrval(alv, NIL , RREQ );
360 					stkcnt += sizeof(long);
361 				} else {
362 					ap = stkrval(alv, NIL , RREQ );
363 					put(1, O_ITOD);
364 					stkcnt += sizeof(double);
365 					typ = TDOUBLE;
366 					goto tdouble;
367 				}
368 				if (fmtspec == NIL) {
369 					if (fmt == 'D')
370 						field = 10;
371 					else if (fmt == 'X')
372 						field = 8;
373 					else if (fmt == 'O')
374 						field = 11;
375 					else
376 						panic("fmt1");
377 					fmtspec = CONWIDTH;
378 				}
379 				break;
380 			case TCHAR:
381 			     tchar:
382 				if (fmtspec == NIL) {
383 					put(1, O_FILE);
384 					ap = stkrval(alv, NIL , RREQ );
385 					convert(nl + T4INT, INT_TYP);
386 					put(2, O_WRITEC,
387 						sizeof(char *) + sizeof(int));
388 					fmtspec = SKIP;
389 					break;
390 				}
391 				ap = stkrval(alv, NIL , RREQ );
392 				convert(nl + T4INT, INT_TYP);
393 				stkcnt += sizeof(int);
394 				fmt = 'c';
395 				break;
396 			case TSCAL:
397 				warning();
398 				if (opt('s')) {
399 					standard();
400 				}
401 				error("Writing %ss to text files is non-standard",
402 				    clnames[typ]);
403 				/* and fall through */
404 			case TBOOL:
405 				stkrval(alv, NIL , RREQ );
406 				put(2, O_NAM, (long)listnames(ap));
407 				stkcnt += sizeof(char *);
408 				fmt = 's';
409 				break;
410 			case TDOUBLE:
411 				ap = stkrval(alv, TDOUBLE , RREQ );
412 				stkcnt += sizeof(double);
413 			     tdouble:
414 				switch (fmtspec) {
415 				case NIL:
416 #					ifdef DEC11
417 					    field = 21;
418 #					else
419 					    field = 22;
420 #					endif DEC11
421 					prec = 14;
422 					fmt = 'e';
423 					fmtspec = CONWIDTH + CONPREC;
424 					break;
425 				case CONWIDTH:
426 					if (--field < 1)
427 						field = 1;
428 #					ifdef DEC11
429 					    prec = field - 7;
430 #					else
431 					    prec = field - 8;
432 #					endif DEC11
433 					if (prec < 1)
434 						prec = 1;
435 					fmtspec += CONPREC;
436 					fmt = 'e';
437 					break;
438 				case CONWIDTH + CONPREC:
439 				case CONWIDTH + VARPREC:
440 					if (--field < 1)
441 						field = 1;
442 				}
443 				format[0] = ' ';
444 				fmtstart = 1;
445 				break;
446 			case TSTR:
447 				constval( alv );
448 				switch ( classify( con.ctype ) ) {
449 				    case TCHAR:
450 					typ = TCHAR;
451 					goto tchar;
452 				    case TSTR:
453 					strptr = con.cpval;
454 					for (strnglen = 0;  *strptr++;  strnglen++) /* void */;
455 					strptr = con.cpval;
456 					break;
457 				    default:
458 					strnglen = width(ap);
459 					break;
460 				}
461 				fmt = 's';
462 				strfmt = fmtspec;
463 				if (fmtspec == NIL) {
464 					fmtspec = SKIP;
465 					break;
466 				}
467 				if (fmtspec & CONWIDTH) {
468 					if (field <= strnglen) {
469 						fmtspec = SKIP;
470 						break;
471 					} else
472 						field -= strnglen;
473 				}
474 				/*
475 				 * push string to implement leading blank padding
476 				 */
477 				put(2, O_LVCON, 2);
478 				putstr("", 0);
479 				stkcnt += sizeof(char *);
480 				break;
481 			default:
482 				error("Can't write %ss to a text file", clnames[typ]);
483 				continue;
484 			}
485 			/*
486 			 * If there is a variable precision, evaluate it onto
487 			 * the stack
488 			 */
489 			if (fmtspec & VARPREC) {
490 				ap = stkrval(al[3], NIL , RREQ );
491 				if (ap == NIL)
492 					continue;
493 				if (isnta(ap,"i")) {
494 					error("Second write width must be integer, not %s", nameof(ap));
495 					continue;
496 				}
497 				if ( opt( 't' ) ) {
498 				    put(3, O_MAX, 0, 0);
499 				}
500 				convert(nl+T4INT, INT_TYP);
501 				stkcnt += sizeof(int);
502 			}
503 			/*
504 			 * If there is a variable width, evaluate it onto
505 			 * the stack
506 			 */
507 			if (fmtspec & VARWIDTH) {
508 				if ( ( typ == TDOUBLE && fmtspec == VARWIDTH )
509 				    || typ == TSTR ) {
510 					soffset = sizes[cbn].curtmps;
511 					tempnlp = tmpalloc(sizeof(long),
512 						nl+T4INT, REGOK);
513 					put(2, O_LV | cbn << 8 + INDX,
514 					    tempnlp -> value[ NL_OFFS ] );
515 				}
516 				ap = stkrval(al[2], NIL , RREQ );
517 				if (ap == NIL)
518 					continue;
519 				if (isnta(ap,"i")) {
520 					error("First write width must be integer, not %s", nameof(ap));
521 					continue;
522 				}
523 				/*
524 				 * Perform special processing on widths based
525 				 * on data type
526 				 */
527 				switch (typ) {
528 				case TDOUBLE:
529 					if (fmtspec == VARWIDTH) {
530 						fmt = 'e';
531 						put(1, O_AS4);
532 						put(2, O_RV4 | cbn << 8 + INDX,
533 						    tempnlp -> value[NL_OFFS] );
534 #						ifdef DEC11
535 						    put(3, O_MAX, 8, 1);
536 #						else
537 						    put(3, O_MAX, 9, 1);
538 #						endif DEC11
539 						convert(nl+T4INT, INT_TYP);
540 						stkcnt += sizeof(int);
541 						put(2, O_RV4 | cbn << 8 + INDX,
542 						    tempnlp->value[NL_OFFS] );
543 						fmtspec += VARPREC;
544 						tmpfree(&soffset);
545 					}
546 					put(3, O_MAX, 1, 1);
547 					break;
548 				case TSTR:
549 					put(1, O_AS4);
550 					put(2, O_RV4 | cbn << 8 + INDX,
551 					    tempnlp -> value[ NL_OFFS ] );
552 					put(3, O_MAX, strnglen, 0);
553 					break;
554 				default:
555 					if ( opt( 't' ) ) {
556 					    put(3, O_MAX, 0, 0);
557 					}
558 					break;
559 				}
560 				convert(nl+T4INT, INT_TYP);
561 				stkcnt += sizeof(int);
562 			}
563 			/*
564 			 * Generate the format string
565 			 */
566 			switch (fmtspec) {
567 			default:
568 				panic("fmt2");
569 			case SKIP:
570 				break;
571 			case NIL:
572 				sprintf(&format[1], "%%%c", fmt);
573 				goto fmtgen;
574 			case CONWIDTH:
575 				sprintf(&format[1], "%%%d%c", field, fmt);
576 				goto fmtgen;
577 			case VARWIDTH:
578 				sprintf(&format[1], "%%*%c", fmt);
579 				goto fmtgen;
580 			case CONWIDTH + CONPREC:
581 				sprintf(&format[1], "%%%d.%d%c", field, prec, fmt);
582 				goto fmtgen;
583 			case CONWIDTH + VARPREC:
584 				sprintf(&format[1], "%%%d.*%c", field, fmt);
585 				goto fmtgen;
586 			case VARWIDTH + CONPREC:
587 				sprintf(&format[1], "%%*.%d%c", prec, fmt);
588 				goto fmtgen;
589 			case VARWIDTH + VARPREC:
590 				sprintf(&format[1], "%%*.*%c", fmt);
591 			fmtgen:
592 				fmtlen = lenstr(&format[fmtstart], 0);
593 				put(2, O_LVCON, fmtlen);
594 				putstr(&format[fmtstart], 0);
595 				put(1, O_FILE);
596 				stkcnt += 2 * sizeof(char *);
597 				put(2, O_WRITEF, stkcnt);
598 			}
599 			/*
600 			 * Write the string after its blank padding
601 			 */
602 			if (typ == TSTR) {
603 				put(1, O_FILE);
604 				put(2, CON_INT, 1);
605 				if (strfmt & VARWIDTH) {
606 					put(2, O_RV4 | cbn << 8 + INDX ,
607 					    tempnlp -> value[ NL_OFFS ] );
608 					put(2, O_MIN, strnglen);
609 					convert(nl+T4INT, INT_TYP);
610 					tmpfree(&soffset);
611 				} else {
612 					if ((fmtspec & SKIP) &&
613 					   (strfmt & CONWIDTH)) {
614 						strnglen = field;
615 					}
616 					put(2, CON_INT, strnglen);
617 				}
618 				ap = stkrval(alv, NIL , RREQ );
619 				put(2, O_WRITES,
620 					2 * sizeof(char *) + 2 * sizeof(int));
621 			}
622 		}
623 		/*
624 		 * Done with arguments.
625 		 * Handle writeln and
626 		 * insufficent number of args.
627 		 */
628 		switch (p->value[0] &~ NSTAND) {
629 			case O_WRITEF:
630 				if (argc == 0)
631 					error("Write requires an argument");
632 				break;
633 			case O_MESSAGE:
634 				if (argc == 0)
635 					error("Message requires an argument");
636 			case O_WRITLN:
637 				if (filetype != nl+T1CHAR)
638 					error("Can't 'writeln' a non text file");
639 				put(1, O_WRITLN);
640 				break;
641 		}
642 		return;
643 
644 	case O_READ4:
645 	case O_READLN:
646 		/*
647 		 * Set up default
648 		 * file "input".
649 		 */
650 		file = NIL;
651 		filetype = nl+T1CHAR;
652 		/*
653 		 * Determine the file implied
654 		 * for the read and generate
655 		 * code to make it the active file.
656 		 */
657 		if (argv != NIL) {
658 			codeoff();
659 			ap = stkrval(argv[1], NIL , RREQ );
660 			codeon();
661 			if (ap == NIL)
662 				argv = argv[2];
663 			if (ap != NIL && ap->class == FILET) {
664 				/*
665 				 * Got "read(f, ...", make
666 				 * f the active file, and save
667 				 * it and its type for use in
668 				 * processing the rest of the
669 				 * arguments to read.
670 				 */
671 				file = argv[1];
672 				filetype = ap->type;
673 				stklval(argv[1], NIL , LREQ );
674 				put(1, O_UNIT);
675 				argv = argv[2];
676 				argc--;
677 			} else {
678 				/*
679 				 * Default is read from
680 				 * standard input.
681 				 */
682 				put(1, O_UNITINP);
683 				input->nl_flags |= NUSED;
684 			}
685 		} else {
686 			put(1, O_UNITINP);
687 			input->nl_flags |= NUSED;
688 		}
689 		/*
690 		 * Loop and process each
691 		 * of the arguments.
692 		 */
693 		for (; argv != NIL; argv = argv[2]) {
694 			/*
695 			 * Get the address of the target
696 			 * on the stack.
697 			 */
698 			al = argv[1];
699 			if (al == NIL)
700 				continue;
701 			if (al[0] != T_VAR) {
702 				error("Arguments to %s must be variables, not expressions", p->symbol);
703 				continue;
704 			}
705 			ap = stklval(al, MOD|ASGN|NOUSE);
706 			if (ap == NIL)
707 				continue;
708 			if (filetype != nl+T1CHAR) {
709 				/*
710 				 * Generalized read, i.e.
711 				 * from a non-textfile.
712 				 */
713 				if (incompat(filetype, ap, argv[1] )) {
714 					error("Type mismatch in read from non-text file");
715 					continue;
716 				}
717 				/*
718 				 * var := file ^;
719 				 */
720 				if (file != NIL)
721 					stklval(file, NIL , LREQ );
722 				else /* Magic */
723 					put(2, PTR_RV, (int)input->value[0]);
724 				put(1, O_FNIL);
725 				put(2, O_IND, width(filetype));
726 				convert(filetype, ap);
727 				if (isa(ap, "bsci"))
728 					rangechk(ap, ap);
729 				put(2, O_AS, width(ap));
730 				/*
731 				 * get(file);
732 				 */
733 				put(1, O_GET);
734 				continue;
735 			}
736 			typ = classify(ap);
737 			op = rdops(typ);
738 			if (op == NIL) {
739 				error("Can't read %ss from a text file", clnames[typ]);
740 				continue;
741 			}
742 			if (op != O_READE)
743 				put(1, op);
744 			else {
745 				put(2, op, (long)listnames(ap));
746 				warning();
747 				if (opt('s')) {
748 					standard();
749 				}
750 				error("Reading scalars from text files is non-standard");
751 			}
752 			/*
753 			 * Data read is on the stack.
754 			 * Assign it.
755 			 */
756 			if (op != O_READ8 && op != O_READE)
757 				rangechk(ap, op == O_READC ? ap : nl+T4INT);
758 			gen(O_AS2, O_AS2, width(ap),
759 				op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2);
760 		}
761 		/*
762 		 * Done with arguments.
763 		 * Handle readln and
764 		 * insufficient number of args.
765 		 */
766 		if (p->value[0] == O_READLN) {
767 			if (filetype != nl+T1CHAR)
768 				error("Can't 'readln' a non text file");
769 			put(1, O_READLN);
770 		}
771 		else if (argc == 0)
772 			error("read requires an argument");
773 		return;
774 
775 	case O_GET:
776 	case O_PUT:
777 		if (argc != 1) {
778 			error("%s expects one argument", p->symbol);
779 			return;
780 		}
781 		ap = stklval(argv[1], NIL , LREQ );
782 		if (ap == NIL)
783 			return;
784 		if (ap->class != FILET) {
785 			error("Argument to %s must be a file, not %s", p->symbol, nameof(ap));
786 			return;
787 		}
788 		put(1, O_UNIT);
789 		put(1, op);
790 		return;
791 
792 	case O_RESET:
793 	case O_REWRITE:
794 		if (argc == 0 || argc > 2) {
795 			error("%s expects one or two arguments", p->symbol);
796 			return;
797 		}
798 		if (opt('s') && argc == 2) {
799 			standard();
800 			error("Two argument forms of reset and rewrite are non-standard");
801 		}
802 		codeoff();
803 		ap = stklval(argv[1], MOD|NOUSE);
804 		codeon();
805 		if (ap == NIL)
806 			return;
807 		if (ap->class != FILET) {
808 			error("First argument to %s must be a file, not %s", p->symbol, nameof(ap));
809 			return;
810 		}
811 		put(2, O_CON24, text(ap) ? 0: width(ap->type));
812 		if (argc == 2) {
813 			/*
814 			 * Optional second argument
815 			 * is a string name of a
816 			 * UNIX (R) file to be associated.
817 			 */
818 			al = argv[2];
819 			codeoff();
820 			al = stkrval(al[1], NOFLAGS , RREQ );
821 			codeon();
822 			if (al == NIL)
823 				return;
824 			if (classify(al) != TSTR) {
825 				error("Second argument to %s must be a string, not %s", p->symbol, nameof(al));
826 				return;
827 			}
828 			put(2, O_CON24, width(al));
829 			al = argv[2];
830 			al = stkrval(al[1], NOFLAGS , RREQ );
831 		} else {
832 			put(2, O_CON24, 0);
833 			put(2, PTR_CON, NIL);
834 		}
835 		ap = stklval(argv[1], MOD|NOUSE);
836 		put(1, op);
837 		return;
838 
839 	case O_NEW:
840 	case O_DISPOSE:
841 		if (argc == 0) {
842 			error("%s expects at least one argument", p->symbol);
843 			return;
844 		}
845 		ap = stklval(argv[1], op == O_NEW ? ( MOD | NOUSE ) : MOD );
846 		if (ap == NIL)
847 			return;
848 		if (ap->class != PTR) {
849 			error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap));
850 			return;
851 		}
852 		ap = ap->type;
853 		if (ap == NIL)
854 			return;
855 		if ((ap->nl_flags & NFILES) && op == O_DISPOSE)
856 			op = O_DFDISP;
857 		argv = argv[2];
858 		if (argv != NIL) {
859 			if (ap->class != RECORD) {
860 				error("Record required when specifying variant tags");
861 				return;
862 			}
863 			for (; argv != NIL; argv = argv[2]) {
864 				if (ap->ptr[NL_VARNT] == NIL) {
865 					error("Too many tag fields");
866 					return;
867 				}
868 				if (!isconst(argv[1])) {
869 					error("Second and successive arguments to %s must be constants", p->symbol);
870 					return;
871 				}
872 				gconst(argv[1]);
873 				if (con.ctype == NIL)
874 					return;
875 				if (incompat(con.ctype, (ap->ptr[NL_TAG])->type , NIL )) {
876 					cerror("Specified tag constant type clashed with variant case selector type");
877 					return;
878 				}
879 				for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain)
880 					if (ap->range[0] == con.crval)
881 						break;
882 				if (ap == NIL) {
883 					error("No variant case label value equals specified constant value");
884 					return;
885 				}
886 				ap = ap->ptr[NL_VTOREC];
887 			}
888 		}
889 		put(2, op, width(ap));
890 		return;
891 
892 	case O_DATE:
893 	case O_TIME:
894 		if (argc != 1) {
895 			error("%s expects one argument", p->symbol);
896 			return;
897 		}
898 		ap = stklval(argv[1], MOD|NOUSE);
899 		if (ap == NIL)
900 			return;
901 		if (classify(ap) != TSTR || width(ap) != 10) {
902 			error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap));
903 			return;
904 		}
905 		put(1, op);
906 		return;
907 
908 	case O_HALT:
909 		if (argc != 0) {
910 			error("halt takes no arguments");
911 			return;
912 		}
913 		put(1, op);
914 		noreach = 1;
915 		return;
916 
917 	case O_ARGV:
918 		if (argc != 2) {
919 			error("argv takes two arguments");
920 			return;
921 		}
922 		ap = stkrval(argv[1], NIL , RREQ );
923 		if (ap == NIL)
924 			return;
925 		if (isnta(ap, "i")) {
926 			error("argv's first argument must be an integer, not %s", nameof(ap));
927 			return;
928 		}
929 		al = argv[2];
930 		ap = stklval(al[1], MOD|NOUSE);
931 		if (ap == NIL)
932 			return;
933 		if (classify(ap) != TSTR) {
934 			error("argv's second argument must be a string, not %s", nameof(ap));
935 			return;
936 		}
937 		put(2, op, width(ap));
938 		return;
939 
940 	case O_STLIM:
941 		if (argc != 1) {
942 			error("stlimit requires one argument");
943 			return;
944 		}
945 		ap = stkrval(argv[1], NIL , RREQ );
946 		if (ap == NIL)
947 			return;
948 		if (isnta(ap, "i")) {
949 			error("stlimit's argument must be an integer, not %s", nameof(ap));
950 			return;
951 		}
952 		if (width(ap) != 4)
953 			put(1, O_STOI);
954 		put(1, op);
955 		return;
956 
957 	case O_REMOVE:
958 		if (argc != 1) {
959 			error("remove expects one argument");
960 			return;
961 		}
962 		codeoff();
963 		ap = stkrval(argv[1], NOFLAGS , RREQ );
964 		codeon();
965 		if (ap == NIL)
966 			return;
967 		if (classify(ap) != TSTR) {
968 			error("remove's argument must be a string, not %s", nameof(ap));
969 			return;
970 		}
971 		put(2, O_CON24, width(ap));
972 		ap = stkrval(argv[1], NOFLAGS , RREQ );
973 		put(1, op);
974 		return;
975 
976 	case O_LLIMIT:
977 		if (argc != 2) {
978 			error("linelimit expects two arguments");
979 			return;
980 		}
981 		al = argv[2];
982 		ap = stkrval(al[1], NIL , RREQ );
983 		if (ap == NIL)
984 			return;
985 		if (isnta(ap, "i")) {
986 			error("linelimit's second argument must be an integer, not %s", nameof(ap));
987 			return;
988 		}
989 		ap = stklval(argv[1], NOFLAGS|NOUSE);
990 		if (ap == NIL)
991 			return;
992 		if (!text(ap)) {
993 			error("linelimit's first argument must be a text file, not %s", nameof(ap));
994 			return;
995 		}
996 		put(1, op);
997 		return;
998 	case O_PAGE:
999 		if (argc != 1) {
1000 			error("page expects one argument");
1001 			return;
1002 		}
1003 		ap = stklval(argv[1], NIL , LREQ );
1004 		if (ap == NIL)
1005 			return;
1006 		if (!text(ap)) {
1007 			error("Argument to page must be a text file, not %s", nameof(ap));
1008 			return;
1009 		}
1010 		put(1, O_UNIT);
1011 		put(1, op);
1012 		return;
1013 
1014 	case O_ASRT:
1015 		if (!opt('t'))
1016 			return;
1017 		if (argc == 0 || argc > 2) {
1018 			error("Assert expects one or two arguments");
1019 			return;
1020 		}
1021 		if (argc == 2) {
1022 			/*
1023 			 * Optional second argument is a string specifying
1024 			 * why the assertion failed.
1025 			 */
1026 			al = argv[2];
1027 			al = stkrval(al[1], NIL , RREQ );
1028 			if (al == NIL)
1029 				return;
1030 			if (classify(al) != TSTR) {
1031 				error("Second argument to assert must be a string, not %s", nameof(al));
1032 				return;
1033 			}
1034 		} else {
1035 			put(2, PTR_CON, NIL);
1036 		}
1037 		ap = stkrval(argv[1], NIL , RREQ );
1038 		if (ap == NIL)
1039 			return;
1040 		if (isnta(ap, "b"))
1041 			error("Assert expression must be Boolean, not %ss", nameof(ap));
1042 		put(1, O_ASRT);
1043 		return;
1044 
1045 	case O_PACK:
1046 		if (argc != 3) {
1047 			error("pack expects three arguments");
1048 			return;
1049 		}
1050 		pu = "pack(a,i,z)";
1051 		pua = argv[1];
1052 		al = argv[2];
1053 		pui = al[1];
1054 		alv = al[2];
1055 		puz = alv[1];
1056 		goto packunp;
1057 	case O_UNPACK:
1058 		if (argc != 3) {
1059 			error("unpack expects three arguments");
1060 			return;
1061 		}
1062 		pu = "unpack(z,a,i)";
1063 		puz = argv[1];
1064 		al = argv[2];
1065 		pua = al[1];
1066 		alv = al[2];
1067 		pui = alv[1];
1068 packunp:
1069 		codeoff();
1070 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
1071 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
1072 		codeon();
1073 		if (ap == NIL)
1074 			return;
1075 		if (ap->class != ARRAY) {
1076 			error("%s requires a to be an unpacked array, not %s", pu, nameof(ap));
1077 			return;
1078 		}
1079 		if (al->class != ARRAY) {
1080 			error("%s requires z to be a packed array, not %s", pu, nameof(ap));
1081 			return;
1082 		}
1083 		if (al->type == NIL || ap->type == NIL)
1084 			return;
1085 		if (al->type != ap->type) {
1086 			error("%s requires a and z to be arrays of the same type", pu, nameof(ap));
1087 			return;
1088 		}
1089 		k = width(al);
1090 		itemwidth = width(ap->type);
1091 		ap = ap->chain;
1092 		al = al->chain;
1093 		if (ap->chain != NIL || al->chain != NIL) {
1094 			error("%s requires a and z to be single dimension arrays", pu);
1095 			return;
1096 		}
1097 		if (ap == NIL || al == NIL)
1098 			return;
1099 		/*
1100 		 * al is the range for z i.e. u..v
1101 		 * ap is the range for a i.e. m..n
1102 		 * i will be n-m+1
1103 		 * j will be v-u+1
1104 		 */
1105 		i = ap->range[1] - ap->range[0] + 1;
1106 		j = al->range[1] - al->range[0] + 1;
1107 		if (i < j) {
1108 			error("%s cannot have more elements in a (%d) than in z (%d)", pu, j, i);
1109 			return;
1110 		}
1111 		/*
1112 		 * get n-m-(v-u) and m for the interpreter
1113 		 */
1114 		i -= j;
1115 		j = ap->range[0];
1116 		put(2, O_CON24, k);
1117 		put(2, O_CON24, i);
1118 		put(2, O_CON24, j);
1119 		put(2, O_CON24, itemwidth);
1120 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
1121 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
1122 		ap = stkrval((int *) pui, NLNIL , RREQ );
1123 		if (ap == NIL)
1124 			return;
1125 		put(1, op);
1126 		return;
1127 	case 0:
1128 		error("%s is an unimplemented extension", p->symbol);
1129 		return;
1130 
1131 	default:
1132 		panic("proc case");
1133 	}
1134 }
1135 #endif OBJ
1136