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