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