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