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