xref: /csrg-svn/usr.bin/pascal/src/pcproc.c (revision 1629)
1 /* Copyright (c) 1979 Regents of the University of California */
2 
3 static	char sccsid[] = "@(#)pcproc.c 1.3 10/28/80";
4 
5 #include "whoami.h"
6 #ifdef PC
7     /*
8      * and to the end of the file
9      */
10 #include "0.h"
11 #include "tree.h"
12 #include "opcode.h"
13 #include	"pc.h"
14 #include	"pcops.h"
15 
16 /*
17  * The following array is used to determine which classes may be read
18  * from textfiles. It is indexed by the return value from classify.
19  */
20 #define rdops(x) rdxxxx[(x)-(TFIRST)]
21 
22 int rdxxxx[] = {
23 	0,		/* -7 file types */
24 	0,		/* -6 record types */
25 	0,		/* -5 array types */
26 	O_READE,	/* -4 scalar types */
27 	0,		/* -3 pointer types */
28 	0,		/* -2 set types */
29 	0,		/* -1 string types */
30 	0,		/*  0 nil, no type */
31 	O_READE,	/*  1 boolean */
32 	O_READC,	/*  2 character */
33 	O_READ4,	/*  3 integer */
34 	O_READ8		/*  4 real */
35 };
36 
37 /*
38  * Proc handles procedure calls.
39  * Non-builtin procedures are "buck-passed" to func (with a flag
40  * indicating that they are actually procedures.
41  * builtin procedures are handled here.
42  */
43 pcproc(r)
44 	int *r;
45 {
46 	register struct nl *p;
47 	register int *alv, *al, op;
48 	struct nl *filetype, *ap;
49 	int argc, *argv, typ, fmtspec, strfmt, stkcnt, *file;
50 	char fmt, format[20], *strptr;
51 	int prec, field, strnglen, fmtlen, fmtstart, pu;
52 	int *pua, *pui, *puz;
53 	int i, j, k;
54 	int itemwidth;
55 	char	*readname;
56 	long	tempoff;
57 	long	readtype;
58 
59 #define	CONPREC 4
60 #define	VARPREC 8
61 #define	CONWIDTH 1
62 #define	VARWIDTH 2
63 #define SKIP 16
64 
65 	/*
66 	 * Verify that the name is
67 	 * defined and is that of a
68 	 * procedure.
69 	 */
70 	p = lookup(r[2]);
71 	if (p == NIL) {
72 		rvlist(r[3]);
73 		return;
74 	}
75 	if (p->class != PROC && p->class != FPROC) {
76 		error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]);
77 		rvlist(r[3]);
78 		return;
79 	}
80 	argv = r[3];
81 
82 	/*
83 	 * Call handles user defined
84 	 * procedures and functions.
85 	 */
86 	if (bn != 0) {
87 		call(p, argv, PROC, bn);
88 		return;
89 	}
90 
91 	/*
92 	 * Call to built-in procedure.
93 	 * Count the arguments.
94 	 */
95 	argc = 0;
96 	for (al = argv; al != NIL; al = al[2])
97 		argc++;
98 
99 	/*
100 	 * Switch on the operator
101 	 * associated with the built-in
102 	 * procedure in the namelist
103 	 */
104 	op = p->value[0] &~ NSTAND;
105 	if (opt('s') && (p->value[0] & NSTAND)) {
106 		standard();
107 		error("%s is a nonstandard procedure", p->symbol);
108 	}
109 	switch (op) {
110 
111 	case O_ABORT:
112 		if (argc != 0)
113 			error("null takes no arguments");
114 		return;
115 
116 	case O_FLUSH:
117 		if (argc == 0) {
118 			putleaf( P2ICON , 0 , 0 , P2INT , "_PFLUSH" );
119 			putop( P2UNARY P2CALL , P2INT );
120 			putdot( filename , line );
121 			return;
122 		}
123 		if (argc != 1) {
124 			error("flush takes at most one argument");
125 			return;
126 		}
127 		putleaf( P2ICON , 0 , 0
128 			, ADDTYPE( P2FTN | P2INT , P2PTR )
129 			, "_FLUSH" );
130 		ap = stklval(argv[1], NOFLAGS);
131 		if (ap == NIL)
132 			return;
133 		if (ap->class != FILET) {
134 			error("flush's argument must be a file, not %s", nameof(ap));
135 			return;
136 		}
137 		putop( P2CALL , P2INT );
138 		putdot( filename , line );
139 		return;
140 
141 	case O_MESSAGE:
142 	case O_WRITEF:
143 	case O_WRITLN:
144 		/*
145 		 * Set up default file "output"'s type
146 		 */
147 		file = NIL;
148 		filetype = nl+T1CHAR;
149 		/*
150 		 * Determine the file implied
151 		 * for the write and generate
152 		 * code to make it the active file.
153 		 */
154 		if (op == O_MESSAGE) {
155 			/*
156 			 * For message, all that matters
157 			 * is that the filetype is
158 			 * a character file.
159 			 * Thus "output" will suit us fine.
160 			 */
161 			putleaf( P2ICON , 0 , 0 , P2INT , "_PFLUSH" );
162 			putop( P2UNARY P2CALL , P2INT );
163 			putdot( filename , line );
164 			putRV( 0 , cbn , CURFILEOFFSET , P2PTR|P2STRTY );
165 			putLV( "__err" , 0 , 0 , P2PTR|P2STRTY );
166 			putop( P2ASSIGN , P2PTR|P2STRTY );
167 			putdot( filename , line );
168 		} else if (argv != NIL && (al = argv[1])[0] != T_WEXP) {
169 			/*
170 			 * If there is a first argument which has
171 			 * no write widths, then it is potentially
172 			 * a file name.
173 			 */
174 			codeoff();
175 			ap = stkrval(argv[1], NIL , RREQ );
176 			codeon();
177 			if (ap == NIL)
178 				argv = argv[2];
179 			if (ap != NIL && ap->class == FILET) {
180 				/*
181 				 * Got "write(f, ...", make
182 				 * f the active file, and save
183 				 * it and its type for use in
184 				 * processing the rest of the
185 				 * arguments to write.
186 				 */
187 				putRV( 0 , cbn , CURFILEOFFSET
188 					, P2PTR|P2STRTY );
189 				putleaf( P2ICON , 0 , 0
190 				    , ADDTYPE( P2FTN | P2INT , P2PTR )
191 				    , "_UNIT" );
192 				file = argv[1];
193 				filetype = ap->type;
194 				stklval(argv[1], NOFLAGS);
195 				putop( P2CALL , P2INT );
196 				putop( P2ASSIGN , P2PTR|P2STRTY );
197 				putdot( filename , line );
198 				/*
199 				 * Skip over the first argument
200 				 */
201 				argv = argv[2];
202 				argc--;
203 			} else {
204 				/*
205 				 * Set up for writing on
206 				 * standard output.
207 				 */
208 				putRV( 0, cbn , CURFILEOFFSET , P2PTR|P2STRTY );
209 				putLV( "_output" , 0 , 0 , P2PTR|P2STRTY );
210 				putop( P2ASSIGN , P2PTR|P2STRTY );
211 				putdot( filename , line );
212 			}
213 		} else {
214 			putRV( 0, cbn , CURFILEOFFSET , P2PTR|P2STRTY );
215 			putLV( "_output" , 0 , 0 , P2PTR|P2STRTY );
216 			putop( P2ASSIGN , P2PTR|P2STRTY );
217 			putdot( filename , line );
218 		}
219 		/*
220 		 * Loop and process each
221 		 * of the arguments.
222 		 */
223 		for (; argv != NIL; argv = argv[2]) {
224 			/*
225 			 * fmtspec indicates the type (CONstant or VARiable)
226 			 *	and number (none, WIDTH, and/or PRECision)
227 			 *	of the fields in the printf format for this
228 			 *	output variable.
229 			 * stkcnt is the number of longs pushed on the stack
230 			 * fmt is the format output indicator (D, E, F, O, X, S)
231 			 * fmtstart = 0 for leading blank; = 1 for no blank
232 			 */
233 			fmtspec = NIL;
234 			stkcnt = 0;
235 			fmt = 'D';
236 			fmtstart = 1;
237 			al = argv[1];
238 			if (al == NIL)
239 				continue;
240 			if (al[0] == T_WEXP)
241 				alv = al[1];
242 			else
243 				alv = al;
244 			if (alv == NIL)
245 				continue;
246 			codeoff();
247 			ap = stkrval(alv, NIL , RREQ );
248 			codeon();
249 			if (ap == NIL)
250 				continue;
251 			typ = classify(ap);
252 			if (al[0] == T_WEXP) {
253 				/*
254 				 * Handle width expressions.
255 				 * The basic game here is that width
256 				 * expressions get evaluated. If they
257 				 * are constant, the value is placed
258 				 * directly in the format string.
259 				 * Otherwise the value is pushed onto
260 				 * the stack and an indirection is
261 				 * put into the format string.
262 				 */
263 				if (al[3] == OCT)
264 					fmt = 'O';
265 				else if (al[3] == HEX)
266 					fmt = 'X';
267 				else if (al[3] != NIL) {
268 					/*
269 					 * Evaluate second format spec
270 					 */
271 					if ( constval(al[3])
272 					    && isa( con.ctype , "i" ) ) {
273 						fmtspec += CONPREC;
274 						prec = con.crval;
275 					} else {
276 						fmtspec += VARPREC;
277 					}
278 					fmt = 'f';
279 					switch ( typ ) {
280 					case TINT:
281 						if ( opt( 's' ) ) {
282 						    standard();
283 						    error("Writing %ss with two write widths is non-standard", clnames[typ]);
284 						}
285 						/* and fall through */
286 					case TDOUBLE:
287 						break;
288 					default:
289 						error("Cannot write %ss with two write widths", clnames[typ]);
290 						continue;
291 					}
292 				}
293 				/*
294 				 * Evaluate first format spec
295 				 */
296 				if (al[2] != NIL) {
297 					if ( constval(al[2])
298 					    && isa( con.ctype , "i" ) ) {
299 						fmtspec += CONWIDTH;
300 						field = con.crval;
301 					} else {
302 						fmtspec += VARWIDTH;
303 					}
304 				}
305 				if ((fmtspec & CONPREC) && prec < 0 ||
306 				    (fmtspec & CONWIDTH) && field < 0) {
307 					error("Negative widths are not allowed");
308 					continue;
309 				}
310 			}
311 			if (filetype != nl+T1CHAR) {
312 				if (fmt == 'O' || fmt == 'X') {
313 					error("Oct/hex allowed only on text files");
314 					continue;
315 				}
316 				if (fmtspec) {
317 					error("Write widths allowed only on text files");
318 					continue;
319 				}
320 				/*
321 				 * Generalized write, i.e.
322 				 * to a non-textfile.
323 				 */
324 				putleaf( P2ICON , 0 , 0
325 				    , ADDTYPE(
326 					ADDTYPE(
327 					    ADDTYPE( p2type( filetype )
328 						    , P2PTR )
329 					    , P2FTN )
330 					, P2PTR )
331 				    , "_FNIL" );
332 				stklval(file, NOFLAGS);
333 				putop( P2CALL
334 				    , ADDTYPE( p2type( filetype ) , P2PTR ) );
335 				putop( P2UNARY P2MUL , p2type( filetype ) );
336 				/*
337 				 * file^ := ...
338 				 */
339 				switch ( classify( filetype ) ) {
340 				    case TBOOL:
341 				    case TCHAR:
342 				    case TINT:
343 				    case TSCAL:
344 					precheck( filetype , "_RANG4"  , "_RSGN4" );
345 					    /* and fall through */
346 				    case TDOUBLE:
347 				    case TPTR:
348 					ap = rvalue( argv[1] , filetype , RREQ );
349 					break;
350 				    default:
351 					ap = rvalue( argv[1] , filetype , LREQ );
352 					break;
353 				}
354 				if (ap == NIL)
355 					continue;
356 				if (incompat(ap, filetype, argv[1])) {
357 					cerror("Type mismatch in write to non-text file");
358 					continue;
359 				}
360 				switch ( classify( filetype ) ) {
361 				    case TBOOL:
362 				    case TCHAR:
363 				    case TINT:
364 				    case TSCAL:
365 					    postcheck( filetype );
366 						/* and fall through */
367 				    case TDOUBLE:
368 				    case TPTR:
369 					    putop( P2ASSIGN , p2type( filetype ) );
370 					    putdot( filename , line );
371 					    break;
372 				    default:
373 					    putstrop( P2STASG
374 							, p2type( filetype )
375 							, lwidth( filetype )
376 							, align( filetype ) );
377 					    putdot( filename , line );
378 					    break;
379 				}
380 				/*
381 				 * put(file)
382 				 */
383 				putleaf( P2ICON , 0 , 0
384 				    , ADDTYPE( P2FTN | P2INT , P2PTR )
385 				    , "_PUT" );
386 				putRV( 0 , cbn , CURFILEOFFSET
387 					, P2PTR|P2STRTY );
388 				putop( P2CALL , P2INT );
389 				putdot( filename , line );
390 				continue;
391 			}
392 			/*
393 			 * Write to a textfile
394 			 *
395 			 * Evaluate the expression
396 			 * to be written.
397 			 */
398 			if (fmt == 'O' || fmt == 'X') {
399 				if (opt('s')) {
400 					standard();
401 					error("Oct and hex are non-standard");
402 				}
403 				if (typ == TSTR || typ == TDOUBLE) {
404 					error("Can't write %ss with oct/hex", clnames[typ]);
405 					continue;
406 				}
407 				if (typ == TCHAR || typ == TBOOL)
408 					typ = TINT;
409 			}
410 			/*
411 			 * If there is no format specified by the programmer,
412 			 * implement the default.
413 			 */
414 			switch (typ) {
415 			case TINT:
416 				if (fmt == 'f') {
417 					typ = TDOUBLE;
418 					goto tdouble;
419 				}
420 				if (fmtspec == NIL) {
421 					if (fmt == 'D')
422 						field = 10;
423 					else if (fmt == 'X')
424 						field = 8;
425 					else if (fmt == 'O')
426 						field = 11;
427 					else
428 						panic("fmt1");
429 					fmtspec = CONWIDTH;
430 				}
431 				break;
432 			case TCHAR:
433 			     tchar:
434 				fmt = 'c';
435 				break;
436 			case TSCAL:
437 				warning();
438 				if (opt('s')) {
439 					standard();
440 				}
441 				error("Writing scalars to text files is non-standard");
442 			case TBOOL:
443 				fmt = 's';
444 				break;
445 			case TDOUBLE:
446 			     tdouble:
447 				switch (fmtspec) {
448 				case NIL:
449 					field = 21;
450 					prec = 14;
451 					fmt = 'E';
452 					fmtspec = CONWIDTH + CONPREC;
453 					break;
454 				case CONWIDTH:
455 					if (--field < 1)
456 						field = 1;
457 					prec = field - 7;
458 					if (prec < 1)
459 						prec = 1;
460 					fmtspec += CONPREC;
461 					fmt = 'E';
462 					break;
463 				case VARWIDTH:
464 					fmtspec += VARPREC;
465 					fmt = 'E';
466 					break;
467 				case CONWIDTH + CONPREC:
468 				case CONWIDTH + VARPREC:
469 					if (--field < 1)
470 						field = 1;
471 				}
472 				format[0] = ' ';
473 				fmtstart = 0;
474 				break;
475 			case TSTR:
476 				constval( alv );
477 				switch ( classify( con.ctype ) ) {
478 				    case TCHAR:
479 					typ = TCHAR;
480 					goto tchar;
481 				    case TSTR:
482 					strptr = con.cpval;
483 					for (strnglen = 0;  *strptr++;  strnglen++) /* void */;
484 					strptr = con.cpval;
485 					break;
486 				    default:
487 					strnglen = width(ap);
488 					break;
489 				}
490 				fmt = 's';
491 				strfmt = fmtspec;
492 				if (fmtspec == NIL) {
493 					fmtspec = SKIP;
494 					break;
495 				}
496 				if (fmtspec & CONWIDTH) {
497 					if (field <= strnglen)
498 						fmtspec = SKIP;
499 					else
500 						field -= strnglen;
501 				}
502 				break;
503 			default:
504 				error("Can't write %ss to a text file", clnames[typ]);
505 				continue;
506 			}
507 			/*
508 			 * Generate the format string
509 			 */
510 			switch (fmtspec) {
511 			default:
512 				panic("fmt2");
513 			case NIL:
514 				if (fmt == 'c') {
515 					if ( opt( 't' ) ) {
516 					    putleaf( P2ICON , 0 , 0
517 						, ADDTYPE( P2FTN|P2INT , P2PTR )
518 						, "_WRITEC" );
519 					    putRV( 0 , cbn , CURFILEOFFSET
520 						    , P2PTR|P2STRTY );
521 					    stkrval( alv , NIL , RREQ );
522 					    putop( P2LISTOP , P2INT );
523 					} else {
524 					    putleaf( P2ICON , 0 , 0
525 						, ADDTYPE( P2FTN|P2INT , P2PTR )
526 						, "_fputc" );
527 					    stkrval( alv , NIL , RREQ );
528 					}
529 					putleaf( P2ICON , 0 , 0
530 					    , ADDTYPE( P2FTN | P2INT , P2PTR )
531 					    , "_ACTFILE" );
532 					putRV( 0, cbn , CURFILEOFFSET
533 						, P2PTR|P2STRTY );
534 					putop( P2CALL , P2INT );
535 					putop( P2LISTOP , P2INT );
536 					putop( P2CALL , P2INT );
537 					putdot( filename , line );
538 				} else  {
539 					sprintf(&format[1], "%%%c", fmt);
540 					goto fmtgen;
541 				}
542 			case SKIP:
543 				break;
544 			case CONWIDTH:
545 				sprintf(&format[1], "%%%1D%c", field, fmt);
546 				goto fmtgen;
547 			case VARWIDTH:
548 				sprintf(&format[1], "%%*%c", fmt);
549 				goto fmtgen;
550 			case CONWIDTH + CONPREC:
551 				sprintf(&format[1], "%%%1D.%1D%c", field, prec, fmt);
552 				goto fmtgen;
553 			case CONWIDTH + VARPREC:
554 				sprintf(&format[1], "%%%1D.*%c", field, fmt);
555 				goto fmtgen;
556 			case VARWIDTH + CONPREC:
557 				sprintf(&format[1], "%%*.%1D%c", prec, fmt);
558 				goto fmtgen;
559 			case VARWIDTH + VARPREC:
560 				sprintf(&format[1], "%%*.*%c", fmt);
561 			fmtgen:
562 				if ( opt( 't' ) ) {
563 				    putleaf( P2ICON , 0 , 0
564 					, ADDTYPE( P2FTN | P2INT , P2PTR )
565 					, "_WRITEF" );
566 				    putRV( 0 , cbn , CURFILEOFFSET
567 					    , P2PTR|P2STRTY );
568 				    putleaf( P2ICON , 0 , 0
569 					, ADDTYPE( P2FTN | P2INT , P2PTR )
570 					, "_ACTFILE" );
571 				    putRV( 0 , cbn , CURFILEOFFSET
572 					    , P2PTR|P2STRTY );
573 				    putop( P2CALL , P2INT );
574 				    putop( P2LISTOP , P2INT );
575 				} else {
576 				    putleaf( P2ICON , 0 , 0
577 					, ADDTYPE( P2FTN | P2INT , P2PTR )
578 					, "_fprintf" );
579 				    putleaf( P2ICON , 0 , 0
580 					, ADDTYPE( P2FTN | P2INT , P2PTR )
581 					, "_ACTFILE" );
582 				    putRV( 0 , cbn , CURFILEOFFSET
583 					    , P2PTR|P2STRTY );
584 				    putop( P2CALL , P2INT );
585 				}
586 				putCONG( &format[ fmtstart ]
587 					, strlen( &format[ fmtstart ] )
588 					, LREQ );
589 				putop( P2LISTOP , P2INT );
590 				if ( fmtspec & VARWIDTH ) {
591 					/*
592 					 * either
593 					 *	,(temp=width,MAX(temp,...)),
594 					 * or
595 					 *	, MAX( width , ... ) ,
596 					 */
597 				    if ( ( typ == TDOUBLE && al[3] == NIL )
598 					|| typ == TSTR ) {
599 					sizes[ cbn ].om_off -= sizeof( int );
600 					tempoff = sizes[ cbn ].om_off;
601 					putlbracket( ftnno , -tempoff );
602 					if ( tempoff < sizes[ cbn ].om_max ) {
603 					    sizes[ cbn ].om_max = tempoff;
604 					}
605 					putRV( 0 , cbn , tempoff , P2INT );
606 					ap = stkrval( al[2] , NIL , RREQ );
607 					putop( P2ASSIGN , P2INT );
608 					putleaf( P2ICON , 0 , 0
609 					    , ADDTYPE( P2FTN | P2INT , P2PTR )
610 					    , "_MAX" );
611 					putRV( 0 , cbn , tempoff , P2INT );
612 				    } else {
613 					if (opt('t')
614 					    || typ == TSTR || typ == TDOUBLE) {
615 					    putleaf( P2ICON , 0 , 0
616 						,ADDTYPE( P2FTN | P2INT, P2PTR )
617 						,"_MAX" );
618 					}
619 					ap = stkrval( al[2] , NIL , RREQ );
620 				    }
621 				    if (ap == NIL)
622 					    continue;
623 				    if (isnta(ap,"i")) {
624 					    error("First write width must be integer, not %s", nameof(ap));
625 					    continue;
626 				    }
627 				    switch ( typ ) {
628 				    case TDOUBLE:
629 					putleaf( P2ICON , 1 , 0 , P2INT , 0 );
630 					putop( P2LISTOP , P2INT );
631 					putleaf( P2ICON , 1 , 0 , P2INT , 0 );
632 					putop( P2LISTOP , P2INT );
633 					putop( P2CALL , P2INT );
634 					if ( al[3] == NIL ) {
635 						/*
636 						 * finish up the comma op
637 						 */
638 					    putop( P2COMOP , P2INT );
639 					    fmtspec &= ~VARPREC;
640 					    putop( P2LISTOP , P2INT );
641 					    putleaf( P2ICON , 0 , 0
642 						, ADDTYPE( P2FTN | P2INT , P2PTR )
643 						, "_MAX" );
644 					    putRV( 0 , cbn , tempoff , P2INT );
645 					    sizes[ cbn ].om_off += sizeof( int );
646 					    putleaf( P2ICON , 8 , 0 , P2INT , 0 );
647 					    putop( P2LISTOP , P2INT );
648 					    putleaf( P2ICON , 1 , 0 , P2INT , 0 );
649 					    putop( P2LISTOP , P2INT );
650 					    putop( P2CALL , P2INT );
651 					}
652 					putop( P2LISTOP , P2INT );
653 					break;
654 				    case TSTR:
655 					putleaf( P2ICON , strnglen , 0 , P2INT , 0 );
656 					putop( P2LISTOP , P2INT );
657 					putleaf( P2ICON , 0 , 0 , P2INT , 0 );
658 					putop( P2LISTOP , P2INT );
659 					putop( P2CALL , P2INT );
660 					putop( P2COMOP , P2INT );
661 					putop( P2LISTOP , P2INT );
662 					break;
663 				    default:
664 					if (opt('t')) {
665 					    putleaf( P2ICON , 0 , 0 , P2INT , 0 );
666 					    putop( P2LISTOP , P2INT );
667 					    putleaf( P2ICON , 0 , 0 , P2INT , 0 );
668 					    putop( P2LISTOP , P2INT );
669 					    putop( P2CALL , P2INT );
670 					}
671 					putop( P2LISTOP , P2INT );
672 					break;
673 				    }
674 				}
675 				/*
676 				 * If there is a variable precision,
677 				 * evaluate it
678 				 */
679 				if (fmtspec & VARPREC) {
680 					if (opt('t')) {
681 					putleaf( P2ICON , 0 , 0
682 					    , ADDTYPE( P2FTN | P2INT , P2PTR )
683 					    , "_MAX" );
684 					}
685 					ap = stkrval( al[3] , NIL , RREQ );
686 					if (ap == NIL)
687 						continue;
688 					if (isnta(ap,"i")) {
689 						error("Second write width must be integer, not %s", nameof(ap));
690 						continue;
691 					}
692 					if (opt('t')) {
693 					    putleaf( P2ICON , 0 , 0 , P2INT , 0 );
694 					    putop( P2LISTOP , P2INT );
695 					    putleaf( P2ICON , 0 , 0 , P2INT , 0 );
696 					    putop( P2LISTOP , P2INT );
697 					    putop( P2CALL , P2INT );
698 					}
699 				 	putop( P2LISTOP , P2INT );
700 				}
701 				/*
702 				 * evaluate the thing we want printed.
703 				 */
704 				switch ( typ ) {
705 				case TCHAR:
706 				case TINT:
707 				    stkrval( alv , NIL , RREQ );
708 				    putop( P2LISTOP , P2INT );
709 				    break;
710 				case TDOUBLE:
711 				    ap = stkrval( alv , NIL , RREQ );
712 				    if ( isnta( ap , "d" ) ) {
713 					putop( P2SCONV , P2DOUBLE );
714 				    }
715 				    putop( P2LISTOP , P2INT );
716 				    break;
717 				case TSCAL:
718 				case TBOOL:
719 				    putleaf( P2ICON , 0 , 0
720 					, ADDTYPE( P2FTN | P2INT , P2PTR )
721 					, "_NAM" );
722 				    ap = stkrval( alv , NIL , RREQ );
723 				    sprintf( format , PREFIXFORMAT , LABELPREFIX
724 					    , listnames( ap ) );
725 				    putleaf( P2ICON , 0 , 0 , P2PTR | P2CHAR
726 					    , format );
727 				    putop( P2LISTOP , P2INT );
728 				    putop( P2CALL , P2INT );
729 				    putop( P2LISTOP , P2INT );
730 				    break;
731 				case TSTR:
732 				    putCONG( "" , 0 , LREQ );
733 				    putop( P2LISTOP , P2INT );
734 				    break;
735 				}
736 				putop( P2CALL , P2INT );
737 				putdot( filename , line );
738 			}
739 			/*
740 			 * Write the string after its blank padding
741 			 */
742 			if (typ == TSTR ) {
743 				if ( opt( 't' ) ) {
744 				    putleaf( P2ICON , 0 , 0
745 					, ADDTYPE( P2FTN | P2INT , P2PTR )
746 					, "_WRITES" );
747 				    putRV( 0 , cbn , CURFILEOFFSET
748 					    , P2PTR|P2STRTY );
749 				    ap = stkrval(alv, NIL , RREQ );
750 				    putop( P2LISTOP , P2INT );
751 				} else {
752 				    putleaf( P2ICON , 0 , 0
753 					, ADDTYPE( P2FTN | P2INT , P2PTR )
754 					, "_fwrite" );
755 				    ap = stkrval(alv, NIL , RREQ );
756 				}
757 				if (strfmt & VARWIDTH) {
758 					    /*
759 					     *	min, inline expanded as
760 					     *	temp < len ? temp : len
761 					     */
762 					putRV( 0 , cbn , tempoff , P2INT );
763 					putleaf( P2ICON , strnglen , 0 , P2INT , 0 );
764 					putop( P2LT , P2INT );
765 					putRV( 0 , cbn , tempoff , P2INT );
766 					putleaf( P2ICON , strnglen , 0 , P2INT , 0 );
767 					putop( P2COLON , P2INT );
768 					putop( P2QUEST , P2INT );
769 				} else {
770 					if (   ( fmtspec & SKIP )
771 					    && ( strfmt & CONWIDTH ) ) {
772 						strnglen = field;
773 					}
774 					putleaf( P2ICON , strnglen , 0 , P2INT , 0 );
775 				}
776 				putop( P2LISTOP , P2INT );
777 				putleaf( P2ICON , 1 , 0 , P2INT , 0 );
778 				putop( P2LISTOP , P2INT );
779 				putleaf( P2ICON , 0 , 0
780 				    , ADDTYPE( P2FTN | P2INT , P2PTR )
781 				    , "_ACTFILE" );
782 				putRV( 0, cbn , CURFILEOFFSET , P2PTR|P2STRTY );
783 				putop( P2CALL , P2INT );
784 				putop( P2LISTOP , P2INT );
785 				putop( P2CALL , P2INT );
786 				putdot( filename , line );
787 			}
788 		}
789 		/*
790 		 * Done with arguments.
791 		 * Handle writeln and
792 		 * insufficent number of args.
793 		 */
794 		switch (p->value[0] &~ NSTAND) {
795 			case O_WRITEF:
796 				if (argc == 0)
797 					error("Write requires an argument");
798 				break;
799 			case O_MESSAGE:
800 				if (argc == 0)
801 					error("Message requires an argument");
802 			case O_WRITLN:
803 				if (filetype != nl+T1CHAR)
804 					error("Can't 'writeln' a non text file");
805 				if ( opt( 't' ) ) {
806 				    putleaf( P2ICON , 0 , 0
807 					, ADDTYPE( P2FTN | P2INT , P2PTR )
808 					, "_WRITLN" );
809 				    putRV( 0 , cbn , CURFILEOFFSET
810 					    , P2PTR|P2STRTY );
811 				} else {
812 				    putleaf( P2ICON , 0 , 0
813 					, ADDTYPE( P2FTN | P2INT , P2PTR )
814 					, "_fputc" );
815 				    putleaf( P2ICON , '\n' , 0 , P2CHAR , 0 );
816 				    putleaf( P2ICON , 0 , 0
817 					, ADDTYPE( P2FTN | P2INT , P2PTR )
818 					, "_ACTFILE" );
819 				    putRV( 0 , cbn , CURFILEOFFSET
820 					    , P2PTR|P2STRTY );
821 				    putop( P2CALL , P2INT );
822 				    putop( P2LISTOP , P2INT );
823 				}
824 				putop( P2CALL , P2INT );
825 				putdot( filename , line );
826 				break;
827 		}
828 		return;
829 
830 	case O_READ4:
831 	case O_READLN:
832 		/*
833 		 * Set up default
834 		 * file "input".
835 		 */
836 		file = NIL;
837 		filetype = nl+T1CHAR;
838 		/*
839 		 * Determine the file implied
840 		 * for the read and generate
841 		 * code to make it the active file.
842 		 */
843 		if (argv != NIL) {
844 			codeoff();
845 			ap = stkrval(argv[1], NIL , RREQ );
846 			codeon();
847 			if (ap == NIL)
848 				argv = argv[2];
849 			if (ap != NIL && ap->class == FILET) {
850 				/*
851 				 * Got "read(f, ...", make
852 				 * f the active file, and save
853 				 * it and its type for use in
854 				 * processing the rest of the
855 				 * arguments to read.
856 				 */
857 				file = argv[1];
858 				filetype = ap->type;
859 				putRV( 0, cbn , CURFILEOFFSET , P2PTR|P2STRTY );
860 				putleaf( P2ICON , 0 , 0
861 					, ADDTYPE( P2FTN | P2INT , P2PTR )
862 					, "_UNIT" );
863 				stklval(argv[1], NOFLAGS);
864 				putop( P2CALL , P2INT );
865 				putop( P2ASSIGN , P2PTR|P2STRTY );
866 				putdot( filename , line );
867 				argv = argv[2];
868 				argc--;
869 			} else {
870 				/*
871 				 * Default is read from
872 				 * standard input.
873 				 */
874 				putRV( 0, cbn , CURFILEOFFSET , P2PTR|P2STRTY );
875 				putLV( "_input" , 0 , 0 , P2PTR|P2STRTY );
876 				putop( P2ASSIGN , P2PTR|P2STRTY );
877 				putdot( filename , line );
878 				input->nl_flags |= NUSED;
879 			}
880 		} else {
881 			putRV( 0, cbn , CURFILEOFFSET , P2PTR|P2STRTY );
882 			putLV( "_input" , 0 , 0 , P2PTR|P2STRTY );
883 			putop( P2ASSIGN , P2PTR|P2STRTY );
884 			putdot( filename , line );
885 			input->nl_flags |= NUSED;
886 		}
887 		/*
888 		 * Loop and process each
889 		 * of the arguments.
890 		 */
891 		for (; argv != NIL; argv = argv[2]) {
892 			/*
893 			 * Get the address of the target
894 			 * on the stack.
895 			 */
896 			al = argv[1];
897 			if (al == NIL)
898 				continue;
899 			if (al[0] != T_VAR) {
900 				error("Arguments to %s must be variables, not expressions", p->symbol);
901 				continue;
902 			}
903 			codeoff();
904 			ap = stklval(al, MOD|ASGN|NOUSE);
905 			codeon();
906 			if (ap == NIL)
907 				continue;
908 			if (filetype != nl+T1CHAR) {
909 				/*
910 				 * Generalized read, i.e.
911 				 * from a non-textfile.
912 				 */
913 				if (incompat(filetype, ap, argv[1] )) {
914 					error("Type mismatch in read from non-text file");
915 					continue;
916 				}
917 				/*
918 				 * var := file ^;
919 				 */
920 				ap = lvalue( al , MOD | ASGN | NOUSE , RREQ );
921 				if ( isa( ap , "bsci" ) ) {
922 					precheck( ap , "_RANG4" , "_RSNG4" );
923 				}
924 				putleaf( P2ICON , 0 , 0
925 				    , ADDTYPE(
926 					ADDTYPE(
927 					    ADDTYPE(
928 						p2type( filetype ) , P2PTR )
929 					    , P2FTN )
930 					, P2PTR )
931 				    , "_FNIL" );
932 				if (file != NIL)
933 					stklval(file, NOFLAGS);
934 				else /* Magic */
935 					putRV( "_input" , 0 , 0
936 						, P2PTR | P2STRTY );
937 				putop( P2CALL , P2INT );
938 				switch ( classify( filetype ) ) {
939 				    case TBOOL:
940 				    case TCHAR:
941 				    case TINT:
942 				    case TSCAL:
943 				    case TDOUBLE:
944 				    case TPTR:
945 					putop( P2UNARY P2MUL
946 						, p2type( filetype ) );
947 				}
948 				switch ( classify( filetype ) ) {
949 				    case TBOOL:
950 				    case TCHAR:
951 				    case TINT:
952 				    case TSCAL:
953 					    postcheck( ap );
954 						/* and fall through */
955 				    case TDOUBLE:
956 				    case TPTR:
957 					    putop( P2ASSIGN , p2type( ap ) );
958 					    putdot( filename , line );
959 					    break;
960 				    default:
961 					    putstrop( P2STASG
962 							, p2type( ap )
963 							, lwidth( ap )
964 							, align( ap ) );
965 					    putdot( filename , line );
966 					    break;
967 				}
968 				/*
969 				 * get(file);
970 				 */
971 				putleaf( P2ICON , 0 , 0
972 					, ADDTYPE( P2FTN | P2INT , P2PTR )
973 					, "_GET" );
974 				putRV( 0 , cbn , CURFILEOFFSET
975 					, P2PTR|P2STRTY );
976 				putop( P2CALL , P2INT );
977 				putdot( filename , line );
978 				continue;
979 			}
980 			    /*
981 			     *	if you get to here, you are reading from
982 			     *	a text file.  only possiblities are:
983 			     *	character, integer, real, or scalar.
984 			     *	read( f , foo , ... ) is done as
985 			     *	foo := read( f ) with rangechecking
986 			     *	if appropriate.
987 			     */
988 			typ = classify(ap);
989 			op = rdops(typ);
990 			if (op == NIL) {
991 				error("Can't read %ss from a text file", clnames[typ]);
992 				continue;
993 			}
994 			    /*
995 			     *	left hand side of foo := read( f )
996 			     */
997 			ap = lvalue( al , MOD|ASGN|NOUSE , RREQ );
998 			if ( isa( ap , "bsci" ) ) {
999 			    precheck( ap , "_RANG4" , "_RSNG4" );
1000 			}
1001 			switch ( op ) {
1002 			    case O_READC:
1003 				readname = "_READC";
1004 				readtype = P2INT;
1005 				break;
1006 			    case O_READ4:
1007 				readname = "_READ4";
1008 				readtype = P2INT;
1009 				break;
1010 			    case O_READ8:
1011 				readname = "_READ8";
1012 				readtype = P2DOUBLE;
1013 				break;
1014 			    case O_READE:
1015 				readname = "_READE";
1016 				readtype = P2INT;
1017 				break;
1018 			}
1019 			putleaf( P2ICON , 0 , 0
1020 				, ADDTYPE( P2FTN | readtype , P2PTR )
1021 				, readname );
1022 			putRV( 0 , cbn , CURFILEOFFSET , P2PTR|P2STRTY );
1023 			if ( op == O_READE ) {
1024 				sprintf( format , PREFIXFORMAT , LABELPREFIX
1025 					, listnames( ap ) );
1026 				putleaf( P2ICON , 0 , 0 , P2PTR | P2CHAR
1027 					, format );
1028 				putop( P2LISTOP , P2INT );
1029 				warning();
1030 				if (opt('s')) {
1031 					standard();
1032 				}
1033 				error("Reading scalars from text files is non-standard");
1034 			}
1035 			putop( P2CALL , readtype );
1036 			if ( isa( ap , "bcsi" ) ) {
1037 			    postcheck( ap );
1038 			}
1039 			putop( P2ASSIGN , p2type( ap ) );
1040 			putdot( filename , line );
1041 		}
1042 		/*
1043 		 * Done with arguments.
1044 		 * Handle readln and
1045 		 * insufficient number of args.
1046 		 */
1047 		if (p->value[0] == O_READLN) {
1048 			if (filetype != nl+T1CHAR)
1049 				error("Can't 'readln' a non text file");
1050 			putleaf( P2ICON , 0 , 0
1051 				, ADDTYPE( P2FTN | P2INT , P2PTR )
1052 				, "_READLN" );
1053 			putRV( 0 , cbn , CURFILEOFFSET , P2PTR|P2STRTY );
1054 			putop( P2CALL , P2INT );
1055 			putdot( filename , line );
1056 		} else if (argc == 0)
1057 			error("read requires an argument");
1058 		return;
1059 
1060 	case O_GET:
1061 	case O_PUT:
1062 		if (argc != 1) {
1063 			error("%s expects one argument", p->symbol);
1064 			return;
1065 		}
1066 		putRV( 0 , cbn , CURFILEOFFSET , P2PTR|P2STRTY );
1067 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1068 			, "_UNIT" );
1069 		ap = stklval(argv[1], NOFLAGS);
1070 		if (ap == NIL)
1071 			return;
1072 		if (ap->class != FILET) {
1073 			error("Argument to %s must be a file, not %s", p->symbol, nameof(ap));
1074 			return;
1075 		}
1076 		putop( P2CALL , P2INT );
1077 		putop( P2ASSIGN , P2PTR|P2STRTY );
1078 		putdot( filename , line );
1079 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1080 			, op == O_GET ? "_GET" : "_PUT" );
1081 		putRV( 0 , cbn , CURFILEOFFSET , P2PTR|P2STRTY );
1082 		putop( P2CALL , P2INT );
1083 		putdot( filename , line );
1084 		return;
1085 
1086 	case O_RESET:
1087 	case O_REWRITE:
1088 		if (argc == 0 || argc > 2) {
1089 			error("%s expects one or two arguments", p->symbol);
1090 			return;
1091 		}
1092 		if (opt('s') && argc == 2) {
1093 			standard();
1094 			error("Two argument forms of reset and rewrite are non-standard");
1095 		}
1096 		putleaf( P2ICON , 0 , 0 , P2INT
1097 			, op == O_RESET ? "_RESET" : "_REWRITE" );
1098 		ap = stklval(argv[1], MOD|NOUSE);
1099 		if (ap == NIL)
1100 			return;
1101 		if (ap->class != FILET) {
1102 			error("First argument to %s must be a file, not %s", p->symbol, nameof(ap));
1103 			return;
1104 		}
1105 		if (argc == 2) {
1106 			/*
1107 			 * Optional second argument
1108 			 * is a string name of a
1109 			 * UNIX (R) file to be associated.
1110 			 */
1111 			al = argv[2];
1112 			al = stkrval(al[1], NOFLAGS , RREQ );
1113 			if (al == NIL)
1114 				return;
1115 			if (classify(al) != TSTR) {
1116 				error("Second argument to %s must be a string, not %s", p->symbol, nameof(al));
1117 				return;
1118 			}
1119 			strnglen = width(al);
1120 		} else {
1121 			putleaf( P2ICON , 0 , 0 , P2INT , 0 );
1122 			strnglen = 0;
1123 		}
1124 		putop( P2LISTOP , P2INT );
1125 		putleaf( P2ICON , strnglen , 0 , P2INT , 0 );
1126 		putop( P2LISTOP , P2INT );
1127 		putleaf( P2ICON , text(ap) ? 0: width(ap->type) , 0 , P2INT , 0 );
1128 		putop( P2LISTOP , P2INT );
1129 		putop( P2CALL , P2INT );
1130 		putdot( filename , line );
1131 		return;
1132 
1133 	case O_NEW:
1134 	case O_DISPOSE:
1135 		if (argc == 0) {
1136 			error("%s expects at least one argument", p->symbol);
1137 			return;
1138 		}
1139 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1140 			, op == O_DISPOSE ? "_DISPOSE" :
1141 				opt('t') ? "_NEWZ" : "_NEW" );
1142 		ap = stklval(argv[1], op == O_NEW ? ( MOD | NOUSE ) : MOD );
1143 		if (ap == NIL)
1144 			return;
1145 		if (ap->class != PTR) {
1146 			error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap));
1147 			return;
1148 		}
1149 		ap = ap->type;
1150 		if (ap == NIL)
1151 			return;
1152 		argv = argv[2];
1153 		if (argv != NIL) {
1154 			if (ap->class != RECORD) {
1155 				error("Record required when specifying variant tags");
1156 				return;
1157 			}
1158 			for (; argv != NIL; argv = argv[2]) {
1159 				if (ap->ptr[NL_VARNT] == NIL) {
1160 					error("Too many tag fields");
1161 					return;
1162 				}
1163 				if (!isconst(argv[1])) {
1164 					error("Second and successive arguments to %s must be constants", p->symbol);
1165 					return;
1166 				}
1167 				gconst(argv[1]);
1168 				if (con.ctype == NIL)
1169 					return;
1170 				if (incompat(con.ctype, (ap->ptr[NL_TAG])->type , NIL )) {
1171 					cerror("Specified tag constant type clashed with variant case selector type");
1172 					return;
1173 				}
1174 				for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain)
1175 					if (ap->range[0] == con.crval)
1176 						break;
1177 				if (ap == NIL) {
1178 					error("No variant case label value equals specified constant value");
1179 					return;
1180 				}
1181 				ap = ap->ptr[NL_VTOREC];
1182 			}
1183 		}
1184 		putleaf( P2ICON , width( ap ) , 0 , P2INT , 0 );
1185 		putop( P2LISTOP , P2INT );
1186 		putop( P2CALL , P2INT );
1187 		putdot( filename , line );
1188 		return;
1189 
1190 	case O_DATE:
1191 	case O_TIME:
1192 		if (argc != 1) {
1193 			error("%s expects one argument", p->symbol);
1194 			return;
1195 		}
1196 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1197 			, op == O_DATE ? "_DATE" : "_TIME" );
1198 		ap = stklval(argv[1], MOD|NOUSE);
1199 		if (ap == NIL)
1200 			return;
1201 		if (classify(ap) != TSTR || width(ap) != 10) {
1202 			error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap));
1203 			return;
1204 		}
1205 		putop( P2CALL , P2INT );
1206 		putdot( filename , line );
1207 		return;
1208 
1209 	case O_HALT:
1210 		if (argc != 0) {
1211 			error("halt takes no arguments");
1212 			return;
1213 		}
1214 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1215 			, "_HALT" );
1216 
1217 		putop( P2UNARY P2CALL , P2INT );
1218 		putdot( filename , line );
1219 		noreach = 1;
1220 		return;
1221 
1222 	case O_ARGV:
1223 		if (argc != 2) {
1224 			error("argv takes two arguments");
1225 			return;
1226 		}
1227 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1228 			, "_ARGV" );
1229 		ap = stkrval(argv[1], NIL , RREQ );
1230 		if (ap == NIL)
1231 			return;
1232 		if (isnta(ap, "i")) {
1233 			error("argv's first argument must be an integer, not %s", nameof(ap));
1234 			return;
1235 		}
1236 		al = argv[2];
1237 		ap = stklval(al[1], MOD|NOUSE);
1238 		if (ap == NIL)
1239 			return;
1240 		if (classify(ap) != TSTR) {
1241 			error("argv's second argument must be a string, not %s", nameof(ap));
1242 			return;
1243 		}
1244 		putop( P2LISTOP , P2INT );
1245 		putleaf( P2ICON , width( ap ) , 0 , P2INT , 0 );
1246 		putop( P2LISTOP , P2INT );
1247 		putop( P2CALL , P2INT );
1248 		putdot( filename , line );
1249 		return;
1250 
1251 	case O_STLIM:
1252 		if (argc != 1) {
1253 			error("stlimit requires one argument");
1254 			return;
1255 		}
1256 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1257 			, "_STLIM" );
1258 		ap = stkrval(argv[1], NIL , RREQ );
1259 		if (ap == NIL)
1260 			return;
1261 		if (isnta(ap, "i")) {
1262 			error("stlimit's argument must be an integer, not %s", nameof(ap));
1263 			return;
1264 		}
1265 		putop( P2CALL , P2INT );
1266 		putdot( filename , line );
1267 		return;
1268 
1269 	case O_REMOVE:
1270 		if (argc != 1) {
1271 			error("remove expects one argument");
1272 			return;
1273 		}
1274 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1275 			, "_REMOVE" );
1276 		ap = stkrval(argv[1], NOFLAGS , RREQ );
1277 		if (ap == NIL)
1278 			return;
1279 		if (classify(ap) != TSTR) {
1280 			error("remove's argument must be a string, not %s", nameof(ap));
1281 			return;
1282 		}
1283 		putleaf( P2ICON , width( ap ) , 0 , P2INT , 0 );
1284 		putop( P2LISTOP , P2INT );
1285 		putop( P2CALL , P2INT );
1286 		putdot( filename , line );
1287 		return;
1288 
1289 	case O_LLIMIT:
1290 		if (argc != 2) {
1291 			error("linelimit expects two arguments");
1292 			return;
1293 		}
1294 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1295 			, "_LLIMIT" );
1296 		ap = stklval(argv[1], NOFLAGS|NOUSE);
1297 		if (ap == NIL)
1298 			return;
1299 		if (!text(ap)) {
1300 			error("linelimit's first argument must be a text file, not %s", nameof(ap));
1301 			return;
1302 		}
1303 		al = argv[2];
1304 		ap = stkrval(al[1], NIL , RREQ );
1305 		if (ap == NIL)
1306 			return;
1307 		if (isnta(ap, "i")) {
1308 			error("linelimit's second argument must be an integer, not %s", nameof(ap));
1309 			return;
1310 		}
1311 		putop( P2LISTOP , P2INT );
1312 		putop( P2CALL , P2INT );
1313 		putdot( filename , line );
1314 		return;
1315 	case O_PAGE:
1316 		if (argc != 1) {
1317 			error("page expects one argument");
1318 			return;
1319 		}
1320 		putRV( 0 , cbn , CURFILEOFFSET , P2PTR|P2STRTY );
1321 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1322 			, "_UNIT" );
1323 		ap = stklval(argv[1], NOFLAGS);
1324 		if (ap == NIL)
1325 			return;
1326 		if (!text(ap)) {
1327 			error("Argument to page must be a text file, not %s", nameof(ap));
1328 			return;
1329 		}
1330 		putop( P2CALL , P2INT );
1331 		putop( P2ASSIGN , P2PTR|P2STRTY );
1332 		putdot( filename , line );
1333 		if ( opt( 't' ) ) {
1334 		    putleaf( P2ICON , 0 , 0
1335 			, ADDTYPE( P2FTN | P2INT , P2PTR )
1336 			, "_PAGE" );
1337 		    putRV( 0 , cbn , CURFILEOFFSET
1338 			    , P2PTR|P2STRTY );
1339 		} else {
1340 		    putleaf( P2ICON , 0 , 0
1341 			, ADDTYPE( P2FTN | P2INT , P2PTR )
1342 			, "_fputc" );
1343 		    putleaf( P2ICON , '\f' , 0 , P2CHAR , 0 );
1344 		    putleaf( P2ICON , 0 , 0
1345 			, ADDTYPE( P2FTN | P2INT , P2PTR )
1346 			, "_ACTFILE" );
1347 		    putRV( 0 , cbn , CURFILEOFFSET
1348 			    , P2PTR|P2STRTY );
1349 		    putop( P2CALL , P2INT );
1350 		    putop( P2LISTOP , P2INT );
1351 		}
1352 		putop( P2CALL , P2INT );
1353 		putdot( filename , line );
1354 		return;
1355 
1356 	case O_PACK:
1357 		if (argc != 3) {
1358 			error("pack expects three arguments");
1359 			return;
1360 		}
1361 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1362 			, "_PACK" );
1363 		pu = "pack(a,i,z)";
1364 		pua = (al = argv)[1];
1365 		pui = (al = al[2])[1];
1366 		puz = (al = al[2])[1];
1367 		goto packunp;
1368 	case O_UNPACK:
1369 		if (argc != 3) {
1370 			error("unpack expects three arguments");
1371 			return;
1372 		}
1373 		putleaf( P2ICON , 0 , 0 , ADDTYPE( P2FTN | P2INT , P2PTR )
1374 			, "_UNPACK" );
1375 		pu = "unpack(z,a,i)";
1376 		puz = (al = argv)[1];
1377 		pua = (al = al[2])[1];
1378 		pui = (al = al[2])[1];
1379 packunp:
1380 		ap = stkrval((int *) pui, NLNIL , RREQ );
1381 		if (ap == NIL)
1382 			return;
1383 		ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE);
1384 		if (ap == NIL)
1385 			return;
1386 		if (ap->class != ARRAY) {
1387 			error("%s requires a to be an unpacked array, not %s", pu, nameof(ap));
1388 			return;
1389 		}
1390 		putop( P2LISTOP , P2INT );
1391 		al = (struct nl *) stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE);
1392 		if (al->class != ARRAY) {
1393 			error("%s requires z to be a packed array, not %s", pu, nameof(ap));
1394 			return;
1395 		}
1396 		if (al->type == NIL || ap->type == NIL)
1397 			return;
1398 		if (al->type != ap->type) {
1399 			error("%s requires a and z to be arrays of the same type", pu, nameof(ap));
1400 			return;
1401 		}
1402 		putop( P2LISTOP , P2INT );
1403 		k = width(al);
1404 		itemwidth = width(ap->type);
1405 		ap = ap->chain;
1406 		al = al->chain;
1407 		if (ap->chain != NIL || al->chain != NIL) {
1408 			error("%s requires a and z to be single dimension arrays", pu);
1409 			return;
1410 		}
1411 		if (ap == NIL || al == NIL)
1412 			return;
1413 		/*
1414 		 * al is the range for z i.e. u..v
1415 		 * ap is the range for a i.e. m..n
1416 		 * i will be n-m+1
1417 		 * j will be v-u+1
1418 		 */
1419 		i = ap->range[1] - ap->range[0] + 1;
1420 		j = al->range[1] - al->range[0] + 1;
1421 		if (i < j) {
1422 			error("%s cannot have more elements in a (%d) than in z (%d)", pu, j, i);
1423 			return;
1424 		}
1425 		/*
1426 		 * get n-m-(v-u) and m for the interpreter
1427 		 */
1428 		i -= j;
1429 		j = ap->range[0];
1430 		putleaf( P2ICON , itemwidth , 0 , P2INT , 0 );
1431 		putop( P2LISTOP , P2INT );
1432 		putleaf( P2ICON , j , 0 , P2INT , 0 );
1433 		putop( P2LISTOP , P2INT );
1434 		putleaf( P2ICON , i , 0 , P2INT , 0 );
1435 		putop( P2LISTOP , P2INT );
1436 		putleaf( P2ICON , k , 0 , P2INT , 0 );
1437 		putop( P2LISTOP , P2INT );
1438 		putop( P2CALL , P2INT );
1439 		putdot( filename , line );
1440 		return;
1441 	case 0:
1442 		error("%s is an unimplemented 6400 extension", p->symbol);
1443 		return;
1444 
1445 	default:
1446 		panic("proc case");
1447 	}
1448 }
1449 #endif PC
1450