148116Sbostic /*- 2*62213Sbostic * Copyright (c) 1980, 1993 3*62213Sbostic * The Regents of the University of California. All rights reserved. 448116Sbostic * 548116Sbostic * %sccs.include.redist.c% 622186Sdist */ 7768Speter 814740Sthien #ifndef lint 9*62213Sbostic static char sccsid[] = "@(#)proc.c 8.1 (Berkeley) 06/06/93"; 1048116Sbostic #endif /* not lint */ 11768Speter 12768Speter #include "whoami.h" 13768Speter #ifdef OBJ 14768Speter /* 15768Speter * and the rest of the file 16768Speter */ 17768Speter #include "0.h" 18768Speter #include "tree.h" 19768Speter #include "opcode.h" 20768Speter #include "objfmt.h" 2111327Speter #include "tmps.h" 2214740Sthien #include "tree_ty.h" 23768Speter 24768Speter /* 2511882Smckusick * The constant EXPOSIZE specifies the number of digits in the exponent 2611882Smckusick * of real numbers. 2711882Smckusick * 289230Smckusick * The constant REALSPC defines the amount of forced padding preceeding 299230Smckusick * real numbers when they are printed. If REALSPC == 0, then no padding 309230Smckusick * is added, REALSPC == 1 adds one extra blank irregardless of the width 319230Smckusick * specified by the user. 329230Smckusick * 339230Smckusick * N.B. - Values greater than one require program mods. 349230Smckusick */ 3511882Smckusick #define EXPOSIZE 2 3611882Smckusick #define REALSPC 0 379230Smckusick 389230Smckusick /* 39768Speter * The following array is used to determine which classes may be read 40768Speter * from textfiles. It is indexed by the return value from classify. 41768Speter */ 42768Speter #define rdops(x) rdxxxx[(x)-(TFIRST)] 43768Speter 44768Speter int rdxxxx[] = { 45768Speter 0, /* -7 file types */ 46768Speter 0, /* -6 record types */ 47768Speter 0, /* -5 array types */ 48768Speter O_READE, /* -4 scalar types */ 49768Speter 0, /* -3 pointer types */ 50768Speter 0, /* -2 set types */ 51768Speter 0, /* -1 string types */ 52768Speter 0, /* 0 nil, no type */ 53768Speter O_READE, /* 1 boolean */ 54768Speter O_READC, /* 2 character */ 55768Speter O_READ4, /* 3 integer */ 56768Speter O_READ8 /* 4 real */ 57768Speter }; 58768Speter 59768Speter /* 60768Speter * Proc handles procedure calls. 61768Speter * Non-builtin procedures are "buck-passed" to func (with a flag 62768Speter * indicating that they are actually procedures. 63768Speter * builtin procedures are handled here. 64768Speter */ 65768Speter proc(r) 6614740Sthien struct tnode *r; 67768Speter { 68768Speter register struct nl *p; 6914740Sthien register struct tnode *alv, *al; 7014740Sthien register int op; 7114740Sthien struct nl *filetype, *ap, *al1; 7214740Sthien int argc, typ, fmtspec, strfmt, stkcnt; 7314740Sthien struct tnode *argv; 7414740Sthien char fmt, format[20], *strptr, *pu; 7514740Sthien int prec, field, strnglen, fmtlen, fmtstart; 7614740Sthien struct tnode *pua, *pui, *puz, *file; 77768Speter int i, j, k; 78768Speter int itemwidth; 793226Smckusic struct tmps soffset; 803851Speter struct nl *tempnlp; 81768Speter 82768Speter #define CONPREC 4 83768Speter #define VARPREC 8 84768Speter #define CONWIDTH 1 85768Speter #define VARWIDTH 2 86768Speter #define SKIP 16 87768Speter 88768Speter /* 89768Speter * Verify that the name is 90768Speter * defined and is that of a 91768Speter * procedure. 92768Speter */ 9314740Sthien p = lookup(r->pcall_node.proc_id); 94768Speter if (p == NIL) { 9514740Sthien rvlist(r->pcall_node.arg); 96768Speter return; 97768Speter } 981198Speter if (p->class != PROC && p->class != FPROC) { 99768Speter error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]); 10014740Sthien rvlist(r->pcall_node.arg); 101768Speter return; 102768Speter } 10314740Sthien argv = r->pcall_node.arg; 104768Speter 105768Speter /* 106768Speter * Call handles user defined 107768Speter * procedures and functions. 108768Speter */ 109768Speter if (bn != 0) { 11014740Sthien (void) call(p, argv, PROC, bn); 111768Speter return; 112768Speter } 113768Speter 114768Speter /* 115768Speter * Call to built-in procedure. 116768Speter * Count the arguments. 117768Speter */ 118768Speter argc = 0; 11914740Sthien for (al = argv; al != TR_NIL; al = al->list_node.next) 120768Speter argc++; 121768Speter 122768Speter /* 123768Speter * Switch on the operator 124768Speter * associated with the built-in 125768Speter * procedure in the namelist 126768Speter */ 127768Speter op = p->value[0] &~ NSTAND; 128768Speter if (opt('s') && (p->value[0] & NSTAND)) { 129768Speter standard(); 130768Speter error("%s is a nonstandard procedure", p->symbol); 131768Speter } 132768Speter switch (op) { 133768Speter 134768Speter case O_ABORT: 135768Speter if (argc != 0) 136768Speter error("null takes no arguments"); 137768Speter return; 138768Speter 139768Speter case O_FLUSH: 140768Speter if (argc == 0) { 14114740Sthien (void) put(1, O_MESSAGE); 142768Speter return; 143768Speter } 144768Speter if (argc != 1) { 145768Speter error("flush takes at most one argument"); 146768Speter return; 147768Speter } 14814740Sthien ap = stklval(argv->list_node.list, NIL ); 14914740Sthien if (ap == NLNIL) 150768Speter return; 151768Speter if (ap->class != FILET) { 152768Speter error("flush's argument must be a file, not %s", nameof(ap)); 153768Speter return; 154768Speter } 15514740Sthien (void) put(1, op); 156768Speter return; 157768Speter 158768Speter case O_MESSAGE: 159768Speter case O_WRITEF: 160768Speter case O_WRITLN: 161768Speter /* 162768Speter * Set up default file "output"'s type 163768Speter */ 164768Speter file = NIL; 165768Speter filetype = nl+T1CHAR; 166768Speter /* 167768Speter * Determine the file implied 168768Speter * for the write and generate 169768Speter * code to make it the active file. 170768Speter */ 171768Speter if (op == O_MESSAGE) { 172768Speter /* 173768Speter * For message, all that matters 174768Speter * is that the filetype is 175768Speter * a character file. 176768Speter * Thus "output" will suit us fine. 177768Speter */ 17814740Sthien (void) put(1, O_MESSAGE); 17914740Sthien } else if (argv != TR_NIL && (al = argv->list_node.list)->tag != 18014740Sthien T_WEXP) { 181768Speter /* 182768Speter * If there is a first argument which has 183768Speter * no write widths, then it is potentially 184768Speter * a file name. 185768Speter */ 186768Speter codeoff(); 18714740Sthien ap = stkrval(argv->list_node.list, NLNIL, (long) RREQ ); 188768Speter codeon(); 18914740Sthien if (ap == NLNIL) 19014740Sthien argv = argv->list_node.next; 19114740Sthien if (ap != NLNIL && ap->class == FILET) { 192768Speter /* 193768Speter * Got "write(f, ...", make 194768Speter * f the active file, and save 195768Speter * it and its type for use in 196768Speter * processing the rest of the 197768Speter * arguments to write. 198768Speter */ 19914740Sthien file = argv->list_node.list; 200768Speter filetype = ap->type; 20114740Sthien (void) stklval(argv->list_node.list, NIL ); 20214740Sthien (void) put(1, O_UNIT); 203768Speter /* 204768Speter * Skip over the first argument 205768Speter */ 20614740Sthien argv = argv->list_node.next; 207768Speter argc--; 2088538Speter } else { 209768Speter /* 210768Speter * Set up for writing on 211768Speter * standard output. 212768Speter */ 21314740Sthien (void) put(1, O_UNITOUT); 2147953Speter output->nl_flags |= NUSED; 2158538Speter } 2168538Speter } else { 21714740Sthien (void) put(1, O_UNITOUT); 2187953Speter output->nl_flags |= NUSED; 2198538Speter } 220768Speter /* 221768Speter * Loop and process each 222768Speter * of the arguments. 223768Speter */ 22414740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 225768Speter /* 226768Speter * fmtspec indicates the type (CONstant or VARiable) 227768Speter * and number (none, WIDTH, and/or PRECision) 228768Speter * of the fields in the printf format for this 229768Speter * output variable. 2303172Smckusic * stkcnt is the number of bytes pushed on the stack 231768Speter * fmt is the format output indicator (D, E, F, O, X, S) 232768Speter * fmtstart = 0 for leading blank; = 1 for no blank 233768Speter */ 234768Speter fmtspec = NIL; 235768Speter stkcnt = 0; 236768Speter fmt = 'D'; 237768Speter fmtstart = 1; 23814740Sthien al = argv->list_node.list; 23914740Sthien if (al == TR_NIL) 240768Speter continue; 24114740Sthien if (al->tag == T_WEXP) 24214740Sthien alv = al->wexpr_node.expr1; 243768Speter else 244768Speter alv = al; 24514740Sthien if (alv == TR_NIL) 246768Speter continue; 247768Speter codeoff(); 24814740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 249768Speter codeon(); 25014740Sthien if (ap == NLNIL) 251768Speter continue; 252768Speter typ = classify(ap); 25314740Sthien if (al->tag == T_WEXP) { 254768Speter /* 255768Speter * Handle width expressions. 256768Speter * The basic game here is that width 257768Speter * expressions get evaluated. If they 258768Speter * are constant, the value is placed 259768Speter * directly in the format string. 260768Speter * Otherwise the value is pushed onto 261768Speter * the stack and an indirection is 262768Speter * put into the format string. 263768Speter */ 26414740Sthien if (al->wexpr_node.expr3 == 26514740Sthien (struct tnode *) OCT) 266768Speter fmt = 'O'; 26714740Sthien else if (al->wexpr_node.expr3 == 26814740Sthien (struct tnode *) HEX) 269768Speter fmt = 'X'; 27014740Sthien else if (al->wexpr_node.expr3 != TR_NIL) { 271768Speter /* 272768Speter * Evaluate second format spec 273768Speter */ 27414740Sthien if ( constval(al->wexpr_node.expr3) 275768Speter && isa( con.ctype , "i" ) ) { 276768Speter fmtspec += CONPREC; 277768Speter prec = con.crval; 278768Speter } else { 279768Speter fmtspec += VARPREC; 280768Speter } 281768Speter fmt = 'f'; 282768Speter switch ( typ ) { 283768Speter case TINT: 284768Speter if ( opt( 's' ) ) { 285768Speter standard(); 286768Speter error("Writing %ss with two write widths is non-standard", clnames[typ]); 287768Speter } 288768Speter /* and fall through */ 289768Speter case TDOUBLE: 290768Speter break; 291768Speter default: 292768Speter error("Cannot write %ss with two write widths", clnames[typ]); 293768Speter continue; 294768Speter } 295768Speter } 296768Speter /* 297768Speter * Evaluate first format spec 298768Speter */ 29914740Sthien if (al->wexpr_node.expr2 != TR_NIL) { 30014740Sthien if ( constval(al->wexpr_node.expr2) 301768Speter && isa( con.ctype , "i" ) ) { 302768Speter fmtspec += CONWIDTH; 303768Speter field = con.crval; 304768Speter } else { 305768Speter fmtspec += VARWIDTH; 306768Speter } 307768Speter } 308768Speter if ((fmtspec & CONPREC) && prec < 0 || 309768Speter (fmtspec & CONWIDTH) && field < 0) { 310768Speter error("Negative widths are not allowed"); 311768Speter continue; 312768Speter } 3133179Smckusic if ( opt('s') && 3143179Smckusic ((fmtspec & CONPREC) && prec == 0 || 3153179Smckusic (fmtspec & CONWIDTH) && field == 0)) { 3163179Smckusic standard(); 3173179Smckusic error("Zero widths are non-standard"); 3183179Smckusic } 319768Speter } 320768Speter if (filetype != nl+T1CHAR) { 321768Speter if (fmt == 'O' || fmt == 'X') { 322768Speter error("Oct/hex allowed only on text files"); 323768Speter continue; 324768Speter } 325768Speter if (fmtspec) { 326768Speter error("Write widths allowed only on text files"); 327768Speter continue; 328768Speter } 329768Speter /* 330768Speter * Generalized write, i.e. 331768Speter * to a non-textfile. 332768Speter */ 33314740Sthien (void) stklval(file, NIL ); 33414740Sthien (void) put(1, O_FNIL); 335768Speter /* 336768Speter * file^ := ... 337768Speter */ 33814740Sthien ap = rvalue(argv->list_node.list, NLNIL, LREQ); 33914740Sthien if (ap == NLNIL) 340768Speter continue; 34114740Sthien if (incompat(ap, filetype, 34214740Sthien argv->list_node.list)) { 343768Speter cerror("Type mismatch in write to non-text file"); 344768Speter continue; 345768Speter } 346768Speter convert(ap, filetype); 34714740Sthien (void) put(2, O_AS, width(filetype)); 348768Speter /* 349768Speter * put(file) 350768Speter */ 35114740Sthien (void) put(1, O_PUT); 352768Speter continue; 353768Speter } 354768Speter /* 355768Speter * Write to a textfile 356768Speter * 357768Speter * Evaluate the expression 358768Speter * to be written. 359768Speter */ 360768Speter if (fmt == 'O' || fmt == 'X') { 361768Speter if (opt('s')) { 362768Speter standard(); 363768Speter error("Oct and hex are non-standard"); 364768Speter } 365768Speter if (typ == TSTR || typ == TDOUBLE) { 366768Speter error("Can't write %ss with oct/hex", clnames[typ]); 367768Speter continue; 368768Speter } 369768Speter if (typ == TCHAR || typ == TBOOL) 370768Speter typ = TINT; 371768Speter } 372768Speter /* 373768Speter * Place the arguement on the stack. If there is 374768Speter * no format specified by the programmer, implement 375768Speter * the default. 376768Speter */ 377768Speter switch (typ) { 3786542Smckusick case TPTR: 3796542Smckusick warning(); 3806542Smckusick if (opt('s')) { 3816542Smckusick standard(); 3826542Smckusick } 3836542Smckusick error("Writing %ss to text files is non-standard", 3846542Smckusick clnames[typ]); 3856542Smckusick /* and fall through */ 386768Speter case TINT: 387768Speter if (fmt != 'f') { 38814740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 3893172Smckusic stkcnt += sizeof(long); 390768Speter } else { 39114740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 39214740Sthien (void) put(1, O_ITOD); 3933172Smckusic stkcnt += sizeof(double); 394768Speter typ = TDOUBLE; 395768Speter goto tdouble; 396768Speter } 397768Speter if (fmtspec == NIL) { 398768Speter if (fmt == 'D') 399768Speter field = 10; 400768Speter else if (fmt == 'X') 401768Speter field = 8; 402768Speter else if (fmt == 'O') 403768Speter field = 11; 404768Speter else 405768Speter panic("fmt1"); 406768Speter fmtspec = CONWIDTH; 407768Speter } 408768Speter break; 409768Speter case TCHAR: 410768Speter tchar: 4112073Smckusic if (fmtspec == NIL) { 41214740Sthien (void) put(1, O_FILE); 41314740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 4143172Smckusic convert(nl + T4INT, INT_TYP); 41514740Sthien (void) put(2, O_WRITEC, 4163172Smckusic sizeof(char *) + sizeof(int)); 4172073Smckusic fmtspec = SKIP; 4182073Smckusic break; 4192073Smckusic } 42014740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 4213172Smckusic convert(nl + T4INT, INT_TYP); 4223172Smckusic stkcnt += sizeof(int); 423768Speter fmt = 'c'; 424768Speter break; 425768Speter case TSCAL: 4261628Speter warning(); 427768Speter if (opt('s')) { 428768Speter standard(); 429768Speter } 4306542Smckusick error("Writing %ss to text files is non-standard", 4316542Smckusick clnames[typ]); 4326542Smckusick /* and fall through */ 433768Speter case TBOOL: 43414740Sthien (void) stkrval(alv, NLNIL , (long) RREQ ); 43514740Sthien (void) put(2, O_NAM, (long)listnames(ap)); 4363172Smckusic stkcnt += sizeof(char *); 437768Speter fmt = 's'; 438768Speter break; 439768Speter case TDOUBLE: 44014740Sthien ap = stkrval(alv, (struct nl *) TDOUBLE , (long) RREQ ); 4413172Smckusic stkcnt += sizeof(double); 442768Speter tdouble: 443768Speter switch (fmtspec) { 444768Speter case NIL: 44511882Smckusick field = 14 + (5 + EXPOSIZE); 44611882Smckusick prec = field - (5 + EXPOSIZE); 4473076Smckusic fmt = 'e'; 448768Speter fmtspec = CONWIDTH + CONPREC; 449768Speter break; 450768Speter case CONWIDTH: 4519230Smckusick field -= REALSPC; 4529230Smckusick if (field < 1) 453768Speter field = 1; 45411882Smckusick prec = field - (5 + EXPOSIZE); 455768Speter if (prec < 1) 456768Speter prec = 1; 457768Speter fmtspec += CONPREC; 4583076Smckusic fmt = 'e'; 459768Speter break; 460768Speter case CONWIDTH + CONPREC: 461768Speter case CONWIDTH + VARPREC: 4629230Smckusick field -= REALSPC; 4639230Smckusick if (field < 1) 464768Speter field = 1; 465768Speter } 466768Speter format[0] = ' '; 4679230Smckusick fmtstart = 1 - REALSPC; 468768Speter break; 469768Speter case TSTR: 47014740Sthien (void) constval( alv ); 471768Speter switch ( classify( con.ctype ) ) { 472768Speter case TCHAR: 473768Speter typ = TCHAR; 474768Speter goto tchar; 475768Speter case TSTR: 476768Speter strptr = con.cpval; 477768Speter for (strnglen = 0; *strptr++; strnglen++) /* void */; 478768Speter strptr = con.cpval; 479768Speter break; 480768Speter default: 481768Speter strnglen = width(ap); 482768Speter break; 483768Speter } 484768Speter fmt = 's'; 485768Speter strfmt = fmtspec; 486768Speter if (fmtspec == NIL) { 487768Speter fmtspec = SKIP; 488768Speter break; 489768Speter } 490768Speter if (fmtspec & CONWIDTH) { 491768Speter if (field <= strnglen) { 492768Speter fmtspec = SKIP; 493768Speter break; 494768Speter } else 495768Speter field -= strnglen; 496768Speter } 497768Speter /* 498768Speter * push string to implement leading blank padding 499768Speter */ 50014740Sthien (void) put(2, O_LVCON, 2); 501768Speter putstr("", 0); 5023172Smckusic stkcnt += sizeof(char *); 503768Speter break; 504768Speter default: 505768Speter error("Can't write %ss to a text file", clnames[typ]); 506768Speter continue; 507768Speter } 508768Speter /* 509768Speter * If there is a variable precision, evaluate it onto 510768Speter * the stack 511768Speter */ 512768Speter if (fmtspec & VARPREC) { 51314740Sthien ap = stkrval(al->wexpr_node.expr3, NLNIL , 51414740Sthien (long) RREQ ); 515768Speter if (ap == NIL) 516768Speter continue; 517768Speter if (isnta(ap,"i")) { 518768Speter error("Second write width must be integer, not %s", nameof(ap)); 519768Speter continue; 520768Speter } 521768Speter if ( opt( 't' ) ) { 52214740Sthien (void) put(3, O_MAX, 0, 0); 523768Speter } 5243172Smckusic convert(nl+T4INT, INT_TYP); 5253172Smckusic stkcnt += sizeof(int); 526768Speter } 527768Speter /* 528768Speter * If there is a variable width, evaluate it onto 529768Speter * the stack 530768Speter */ 531768Speter if (fmtspec & VARWIDTH) { 532768Speter if ( ( typ == TDOUBLE && fmtspec == VARWIDTH ) 533768Speter || typ == TSTR ) { 5343226Smckusic soffset = sizes[cbn].curtmps; 53514740Sthien tempnlp = tmpalloc((long) (sizeof(long)), 5363226Smckusic nl+T4INT, REGOK); 53714740Sthien (void) put(2, O_LV | cbn << 8 + INDX, 5383851Speter tempnlp -> value[ NL_OFFS ] ); 539768Speter } 54014740Sthien ap = stkrval(al->wexpr_node.expr2, NLNIL, (long) RREQ ); 541768Speter if (ap == NIL) 542768Speter continue; 543768Speter if (isnta(ap,"i")) { 544768Speter error("First write width must be integer, not %s", nameof(ap)); 545768Speter continue; 546768Speter } 547768Speter /* 548768Speter * Perform special processing on widths based 549768Speter * on data type 550768Speter */ 551768Speter switch (typ) { 552768Speter case TDOUBLE: 553768Speter if (fmtspec == VARWIDTH) { 5543076Smckusic fmt = 'e'; 55514740Sthien (void) put(1, O_AS4); 55614740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5573851Speter tempnlp -> value[NL_OFFS] ); 55814740Sthien (void) put(3, O_MAX, 55911882Smckusick 5 + EXPOSIZE + REALSPC, 1); 5603172Smckusic convert(nl+T4INT, INT_TYP); 5613172Smckusic stkcnt += sizeof(int); 56214740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5633851Speter tempnlp->value[NL_OFFS] ); 564768Speter fmtspec += VARPREC; 5653226Smckusic tmpfree(&soffset); 566768Speter } 56714740Sthien (void) put(3, O_MAX, REALSPC, 1); 568768Speter break; 569768Speter case TSTR: 57014740Sthien (void) put(1, O_AS4); 57114740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5723851Speter tempnlp -> value[ NL_OFFS ] ); 57314740Sthien (void) put(3, O_MAX, strnglen, 0); 574768Speter break; 575768Speter default: 576768Speter if ( opt( 't' ) ) { 57714740Sthien (void) put(3, O_MAX, 0, 0); 578768Speter } 579768Speter break; 580768Speter } 5813172Smckusic convert(nl+T4INT, INT_TYP); 5823172Smckusic stkcnt += sizeof(int); 583768Speter } 584768Speter /* 585768Speter * Generate the format string 586768Speter */ 587768Speter switch (fmtspec) { 588768Speter default: 589768Speter panic("fmt2"); 590768Speter case SKIP: 591768Speter break; 5922073Smckusic case NIL: 5932073Smckusic sprintf(&format[1], "%%%c", fmt); 5942073Smckusic goto fmtgen; 595768Speter case CONWIDTH: 5963076Smckusic sprintf(&format[1], "%%%d%c", field, fmt); 597768Speter goto fmtgen; 598768Speter case VARWIDTH: 599768Speter sprintf(&format[1], "%%*%c", fmt); 600768Speter goto fmtgen; 601768Speter case CONWIDTH + CONPREC: 6023076Smckusic sprintf(&format[1], "%%%d.%d%c", field, prec, fmt); 603768Speter goto fmtgen; 604768Speter case CONWIDTH + VARPREC: 6053076Smckusic sprintf(&format[1], "%%%d.*%c", field, fmt); 606768Speter goto fmtgen; 607768Speter case VARWIDTH + CONPREC: 6083076Smckusic sprintf(&format[1], "%%*.%d%c", prec, fmt); 609768Speter goto fmtgen; 610768Speter case VARWIDTH + VARPREC: 611768Speter sprintf(&format[1], "%%*.*%c", fmt); 612768Speter fmtgen: 613768Speter fmtlen = lenstr(&format[fmtstart], 0); 61414740Sthien (void) put(2, O_LVCON, fmtlen); 615768Speter putstr(&format[fmtstart], 0); 61614740Sthien (void) put(1, O_FILE); 6173172Smckusic stkcnt += 2 * sizeof(char *); 61814740Sthien (void) put(2, O_WRITEF, stkcnt); 619768Speter } 620768Speter /* 621768Speter * Write the string after its blank padding 622768Speter */ 623768Speter if (typ == TSTR) { 62414740Sthien (void) put(1, O_FILE); 62514740Sthien (void) put(2, CON_INT, 1); 626768Speter if (strfmt & VARWIDTH) { 62714740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX , 6283851Speter tempnlp -> value[ NL_OFFS ] ); 62914740Sthien (void) put(2, O_MIN, strnglen); 6303172Smckusic convert(nl+T4INT, INT_TYP); 6313226Smckusic tmpfree(&soffset); 632768Speter } else { 633768Speter if ((fmtspec & SKIP) && 634768Speter (strfmt & CONWIDTH)) { 635768Speter strnglen = field; 636768Speter } 63714740Sthien (void) put(2, CON_INT, strnglen); 638768Speter } 63914740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 64014740Sthien (void) put(2, O_WRITES, 6413172Smckusic 2 * sizeof(char *) + 2 * sizeof(int)); 642768Speter } 643768Speter } 644768Speter /* 645768Speter * Done with arguments. 646768Speter * Handle writeln and 647768Speter * insufficent number of args. 648768Speter */ 649768Speter switch (p->value[0] &~ NSTAND) { 650768Speter case O_WRITEF: 651768Speter if (argc == 0) 652768Speter error("Write requires an argument"); 653768Speter break; 654768Speter case O_MESSAGE: 655768Speter if (argc == 0) 656768Speter error("Message requires an argument"); 657768Speter case O_WRITLN: 658768Speter if (filetype != nl+T1CHAR) 659768Speter error("Can't 'writeln' a non text file"); 66014740Sthien (void) put(1, O_WRITLN); 661768Speter break; 662768Speter } 663768Speter return; 664768Speter 665768Speter case O_READ4: 666768Speter case O_READLN: 667768Speter /* 668768Speter * Set up default 669768Speter * file "input". 670768Speter */ 671768Speter file = NIL; 672768Speter filetype = nl+T1CHAR; 673768Speter /* 674768Speter * Determine the file implied 675768Speter * for the read and generate 676768Speter * code to make it the active file. 677768Speter */ 67814740Sthien if (argv != TR_NIL) { 679768Speter codeoff(); 68014740Sthien ap = stkrval(argv->list_node.list, NLNIL, (long) RREQ ); 681768Speter codeon(); 68214740Sthien if (ap == NLNIL) 68314740Sthien argv = argv->list_node.next; 68414740Sthien if (ap != NLNIL && ap->class == FILET) { 685768Speter /* 686768Speter * Got "read(f, ...", make 687768Speter * f the active file, and save 688768Speter * it and its type for use in 689768Speter * processing the rest of the 690768Speter * arguments to read. 691768Speter */ 69214740Sthien file = argv->list_node.list; 693768Speter filetype = ap->type; 69414740Sthien (void) stklval(argv->list_node.list, NIL ); 69514740Sthien (void) put(1, O_UNIT); 69614740Sthien argv = argv->list_node.next; 697768Speter argc--; 698768Speter } else { 699768Speter /* 700768Speter * Default is read from 701768Speter * standard input. 702768Speter */ 70314740Sthien (void) put(1, O_UNITINP); 704768Speter input->nl_flags |= NUSED; 705768Speter } 706768Speter } else { 70714740Sthien (void) put(1, O_UNITINP); 708768Speter input->nl_flags |= NUSED; 709768Speter } 710768Speter /* 711768Speter * Loop and process each 712768Speter * of the arguments. 713768Speter */ 71414740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 715768Speter /* 716768Speter * Get the address of the target 717768Speter * on the stack. 718768Speter */ 71914740Sthien al = argv->list_node.list; 72014740Sthien if (al == TR_NIL) 721768Speter continue; 72214740Sthien if (al->tag != T_VAR) { 723768Speter error("Arguments to %s must be variables, not expressions", p->symbol); 724768Speter continue; 725768Speter } 726768Speter ap = stklval(al, MOD|ASGN|NOUSE); 72714740Sthien if (ap == NLNIL) 728768Speter continue; 729768Speter if (filetype != nl+T1CHAR) { 730768Speter /* 731768Speter * Generalized read, i.e. 732768Speter * from a non-textfile. 733768Speter */ 73414740Sthien if (incompat(filetype, ap, 73514740Sthien argv->list_node.list )) { 736768Speter error("Type mismatch in read from non-text file"); 737768Speter continue; 738768Speter } 739768Speter /* 740768Speter * var := file ^; 741768Speter */ 742768Speter if (file != NIL) 74316417Speter (void) stklval(file, NIL); 744768Speter else /* Magic */ 74516417Speter (void) put(2, PTR_RV, (int)input->value[0]); 74614740Sthien (void) put(1, O_FNIL); 74716417Speter if (isa(filetype, "bcsi")) { 74816417Speter int filewidth = width(filetype); 74916417Speter 75016417Speter switch (filewidth) { 75116417Speter case 4: 75216417Speter (void) put(1, O_IND4); 75316417Speter break; 75416417Speter case 2: 75516417Speter (void) put(1, O_IND2); 75616417Speter break; 75716417Speter case 1: 75816417Speter (void) put(1, O_IND1); 75916417Speter break; 76016417Speter default: 76116417Speter (void) put(2, O_IND, filewidth); 76216417Speter } 76316417Speter convert(filetype, ap); 76416417Speter rangechk(ap, ap); 76516417Speter (void) gen(O_AS2, O_AS2, 76616417Speter filewidth, width(ap)); 76716417Speter } else { 76816417Speter (void) put(2, O_IND, width(filetype)); 76916417Speter convert(filetype, ap); 77016417Speter (void) put(2, O_AS, width(ap)); 77116417Speter } 772768Speter /* 773768Speter * get(file); 774768Speter */ 77514740Sthien (void) put(1, O_GET); 776768Speter continue; 777768Speter } 778768Speter typ = classify(ap); 779768Speter op = rdops(typ); 780768Speter if (op == NIL) { 781768Speter error("Can't read %ss from a text file", clnames[typ]); 782768Speter continue; 783768Speter } 784768Speter if (op != O_READE) 78514740Sthien (void) put(1, op); 786768Speter else { 78714740Sthien (void) put(2, op, (long)listnames(ap)); 7881628Speter warning(); 789768Speter if (opt('s')) { 790768Speter standard(); 791768Speter } 7921628Speter error("Reading scalars from text files is non-standard"); 793768Speter } 794768Speter /* 795768Speter * Data read is on the stack. 796768Speter * Assign it. 797768Speter */ 798768Speter if (op != O_READ8 && op != O_READE) 799768Speter rangechk(ap, op == O_READC ? ap : nl+T4INT); 80014740Sthien (void) gen(O_AS2, O_AS2, width(ap), 801768Speter op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2); 802768Speter } 803768Speter /* 804768Speter * Done with arguments. 805768Speter * Handle readln and 806768Speter * insufficient number of args. 807768Speter */ 808768Speter if (p->value[0] == O_READLN) { 809768Speter if (filetype != nl+T1CHAR) 810768Speter error("Can't 'readln' a non text file"); 81114740Sthien (void) put(1, O_READLN); 812768Speter } 813768Speter else if (argc == 0) 814768Speter error("read requires an argument"); 815768Speter return; 816768Speter 817768Speter case O_GET: 818768Speter case O_PUT: 819768Speter if (argc != 1) { 820768Speter error("%s expects one argument", p->symbol); 821768Speter return; 822768Speter } 82314740Sthien ap = stklval(argv->list_node.list, NIL ); 82414740Sthien if (ap == NLNIL) 825768Speter return; 826768Speter if (ap->class != FILET) { 827768Speter error("Argument to %s must be a file, not %s", p->symbol, nameof(ap)); 828768Speter return; 829768Speter } 83014740Sthien (void) put(1, O_UNIT); 83114740Sthien (void) put(1, op); 832768Speter return; 833768Speter 834768Speter case O_RESET: 835768Speter case O_REWRITE: 836768Speter if (argc == 0 || argc > 2) { 837768Speter error("%s expects one or two arguments", p->symbol); 838768Speter return; 839768Speter } 840768Speter if (opt('s') && argc == 2) { 841768Speter standard(); 842768Speter error("Two argument forms of reset and rewrite are non-standard"); 843768Speter } 8442073Smckusic codeoff(); 84514740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 8462073Smckusic codeon(); 84714740Sthien if (ap == NLNIL) 848768Speter return; 849768Speter if (ap->class != FILET) { 850768Speter error("First argument to %s must be a file, not %s", p->symbol, nameof(ap)); 851768Speter return; 852768Speter } 85314740Sthien (void) put(2, O_CON24, text(ap) ? 0: width(ap->type)); 854768Speter if (argc == 2) { 855768Speter /* 856768Speter * Optional second argument 857768Speter * is a string name of a 858768Speter * UNIX (R) file to be associated. 859768Speter */ 86014740Sthien al = argv->list_node.next; 8612073Smckusic codeoff(); 86214740Sthien al = (struct tnode *) stkrval(al->list_node.list, 86314740Sthien (struct nl *) NOFLAGS , (long) RREQ ); 8642073Smckusic codeon(); 86514740Sthien if (al == TR_NIL) 866768Speter return; 86714740Sthien if (classify((struct nl *) al) != TSTR) { 86814740Sthien error("Second argument to %s must be a string, not %s", p->symbol, nameof((struct nl *) al)); 869768Speter return; 870768Speter } 87114740Sthien (void) put(2, O_CON24, width((struct nl *) al)); 87214740Sthien al = argv->list_node.next; 87314740Sthien al = (struct tnode *) stkrval(al->list_node.list, 87414740Sthien (struct nl *) NOFLAGS , (long) RREQ ); 875768Speter } else { 87614740Sthien (void) put(2, O_CON24, 0); 87714740Sthien (void) put(2, PTR_CON, NIL); 878768Speter } 87914740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 88014740Sthien (void) put(1, op); 881768Speter return; 882768Speter 883768Speter case O_NEW: 884768Speter case O_DISPOSE: 885768Speter if (argc == 0) { 886768Speter error("%s expects at least one argument", p->symbol); 887768Speter return; 888768Speter } 88914740Sthien ap = stklval(argv->list_node.list, 89014740Sthien op == O_NEW ? ( MOD | NOUSE ) : MOD ); 89114740Sthien if (ap == NLNIL) 892768Speter return; 893768Speter if (ap->class != PTR) { 894768Speter error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap)); 895768Speter return; 896768Speter } 897768Speter ap = ap->type; 898768Speter if (ap == NIL) 899768Speter return; 9007966Smckusick if ((ap->nl_flags & NFILES) && op == O_DISPOSE) 9017966Smckusick op = O_DFDISP; 90214740Sthien argv = argv->list_node.next; 90314740Sthien if (argv != TR_NIL) { 904768Speter if (ap->class != RECORD) { 905768Speter error("Record required when specifying variant tags"); 906768Speter return; 907768Speter } 90814740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 909768Speter if (ap->ptr[NL_VARNT] == NIL) { 910768Speter error("Too many tag fields"); 911768Speter return; 912768Speter } 91314740Sthien if (!isconst(argv->list_node.list)) { 914768Speter error("Second and successive arguments to %s must be constants", p->symbol); 915768Speter return; 916768Speter } 91714740Sthien gconst(argv->list_node.list); 918768Speter if (con.ctype == NIL) 919768Speter return; 92014740Sthien if (incompat(con.ctype, ( 92114740Sthien ap->ptr[NL_TAG])->type , TR_NIL )) { 922768Speter cerror("Specified tag constant type clashed with variant case selector type"); 923768Speter return; 924768Speter } 925768Speter for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain) 926768Speter if (ap->range[0] == con.crval) 927768Speter break; 928768Speter if (ap == NIL) { 929768Speter error("No variant case label value equals specified constant value"); 930768Speter return; 931768Speter } 932768Speter ap = ap->ptr[NL_VTOREC]; 933768Speter } 934768Speter } 93514740Sthien (void) put(2, op, width(ap)); 936768Speter return; 937768Speter 938768Speter case O_DATE: 939768Speter case O_TIME: 940768Speter if (argc != 1) { 941768Speter error("%s expects one argument", p->symbol); 942768Speter return; 943768Speter } 94414740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 94514740Sthien if (ap == NLNIL) 946768Speter return; 947768Speter if (classify(ap) != TSTR || width(ap) != 10) { 948768Speter error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap)); 949768Speter return; 950768Speter } 95114740Sthien (void) put(1, op); 952768Speter return; 953768Speter 954768Speter case O_HALT: 955768Speter if (argc != 0) { 956768Speter error("halt takes no arguments"); 957768Speter return; 958768Speter } 95914740Sthien (void) put(1, op); 96014740Sthien noreach = TRUE; /* used to be 1 */ 961768Speter return; 962768Speter 963768Speter case O_ARGV: 964768Speter if (argc != 2) { 965768Speter error("argv takes two arguments"); 966768Speter return; 967768Speter } 96814740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 96914740Sthien if (ap == NLNIL) 970768Speter return; 971768Speter if (isnta(ap, "i")) { 972768Speter error("argv's first argument must be an integer, not %s", nameof(ap)); 973768Speter return; 974768Speter } 97514740Sthien al = argv->list_node.next; 97614740Sthien ap = stklval(al->list_node.list, MOD|NOUSE); 97714740Sthien if (ap == NLNIL) 978768Speter return; 979768Speter if (classify(ap) != TSTR) { 980768Speter error("argv's second argument must be a string, not %s", nameof(ap)); 981768Speter return; 982768Speter } 98314740Sthien (void) put(2, op, width(ap)); 984768Speter return; 985768Speter 986768Speter case O_STLIM: 987768Speter if (argc != 1) { 988768Speter error("stlimit requires one argument"); 989768Speter return; 990768Speter } 99114740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 99214740Sthien if (ap == NLNIL) 993768Speter return; 994768Speter if (isnta(ap, "i")) { 995768Speter error("stlimit's argument must be an integer, not %s", nameof(ap)); 996768Speter return; 997768Speter } 998768Speter if (width(ap) != 4) 99914740Sthien (void) put(1, O_STOI); 100014740Sthien (void) put(1, op); 1001768Speter return; 1002768Speter 1003768Speter case O_REMOVE: 1004768Speter if (argc != 1) { 1005768Speter error("remove expects one argument"); 1006768Speter return; 1007768Speter } 10082073Smckusic codeoff(); 100914740Sthien ap = stkrval(argv->list_node.list, (struct nl *) NOFLAGS, 101014740Sthien (long) RREQ ); 10112073Smckusic codeon(); 101214740Sthien if (ap == NLNIL) 1013768Speter return; 1014768Speter if (classify(ap) != TSTR) { 1015768Speter error("remove's argument must be a string, not %s", nameof(ap)); 1016768Speter return; 1017768Speter } 101814740Sthien (void) put(2, O_CON24, width(ap)); 101914740Sthien ap = stkrval(argv->list_node.list, (struct nl *) NOFLAGS, 102014740Sthien (long) RREQ ); 102114740Sthien (void) put(1, op); 1022768Speter return; 1023768Speter 1024768Speter case O_LLIMIT: 1025768Speter if (argc != 2) { 1026768Speter error("linelimit expects two arguments"); 1027768Speter return; 1028768Speter } 102914740Sthien al = argv->list_node.next; 103014740Sthien ap = stkrval(al->list_node.list, NLNIL , (long) RREQ ); 1031768Speter if (ap == NIL) 1032768Speter return; 1033768Speter if (isnta(ap, "i")) { 1034768Speter error("linelimit's second argument must be an integer, not %s", nameof(ap)); 1035768Speter return; 1036768Speter } 103714740Sthien ap = stklval(argv->list_node.list, NOFLAGS|NOUSE); 103814740Sthien if (ap == NLNIL) 10392073Smckusic return; 10402073Smckusic if (!text(ap)) { 10412073Smckusic error("linelimit's first argument must be a text file, not %s", nameof(ap)); 10422073Smckusic return; 10432073Smckusic } 104414740Sthien (void) put(1, op); 1045768Speter return; 1046768Speter case O_PAGE: 1047768Speter if (argc != 1) { 1048768Speter error("page expects one argument"); 1049768Speter return; 1050768Speter } 105114740Sthien ap = stklval(argv->list_node.list, NIL ); 105214740Sthien if (ap == NLNIL) 1053768Speter return; 1054768Speter if (!text(ap)) { 1055768Speter error("Argument to page must be a text file, not %s", nameof(ap)); 1056768Speter return; 1057768Speter } 105814740Sthien (void) put(1, O_UNIT); 105914740Sthien (void) put(1, op); 1060768Speter return; 1061768Speter 10627928Smckusick case O_ASRT: 10637928Smckusick if (!opt('t')) 10647928Smckusick return; 10657928Smckusick if (argc == 0 || argc > 2) { 10667928Smckusick error("Assert expects one or two arguments"); 10677928Smckusick return; 10687928Smckusick } 10697928Smckusick if (argc == 2) { 10707928Smckusick /* 10717928Smckusick * Optional second argument is a string specifying 10727928Smckusick * why the assertion failed. 10737928Smckusick */ 107414740Sthien al = argv->list_node.next; 107514740Sthien al1 = stkrval(al->list_node.list, NLNIL , (long) RREQ ); 107614740Sthien if (al1 == NIL) 10777928Smckusick return; 107814740Sthien if (classify(al1) != TSTR) { 107914740Sthien error("Second argument to assert must be a string, not %s", nameof(al1)); 10807928Smckusick return; 10817928Smckusick } 10827928Smckusick } else { 108314740Sthien (void) put(2, PTR_CON, NIL); 10847928Smckusick } 108514740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 10867928Smckusick if (ap == NIL) 10877928Smckusick return; 10887928Smckusick if (isnta(ap, "b")) 10897928Smckusick error("Assert expression must be Boolean, not %ss", nameof(ap)); 109014740Sthien (void) put(1, O_ASRT); 10917928Smckusick return; 10927928Smckusick 1093768Speter case O_PACK: 1094768Speter if (argc != 3) { 1095768Speter error("pack expects three arguments"); 1096768Speter return; 1097768Speter } 1098768Speter pu = "pack(a,i,z)"; 109914740Sthien pua = argv->list_node.list; 110014740Sthien al = argv->list_node.next; 110114740Sthien pui = al->list_node.list; 110214740Sthien alv = al->list_node.next; 110314740Sthien puz = alv->list_node.list; 1104768Speter goto packunp; 1105768Speter case O_UNPACK: 1106768Speter if (argc != 3) { 1107768Speter error("unpack expects three arguments"); 1108768Speter return; 1109768Speter } 1110768Speter pu = "unpack(z,a,i)"; 111114740Sthien puz = argv->list_node.list; 111214740Sthien al = argv->list_node.next; 111314740Sthien pua = al->list_node.list; 111414740Sthien alv = al->list_node.next; 111514740Sthien pui = alv->list_node.list; 1116768Speter packunp: 11172073Smckusic codeoff(); 1118768Speter ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 111914740Sthien al1 = stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 11202073Smckusic codeon(); 1121768Speter if (ap == NIL) 1122768Speter return; 1123768Speter if (ap->class != ARRAY) { 1124768Speter error("%s requires a to be an unpacked array, not %s", pu, nameof(ap)); 1125768Speter return; 1126768Speter } 112714740Sthien if (al1->class != ARRAY) { 1128768Speter error("%s requires z to be a packed array, not %s", pu, nameof(ap)); 1129768Speter return; 1130768Speter } 113114740Sthien if (al1->type == NIL || ap->type == NIL) 1132768Speter return; 113314740Sthien if (al1->type != ap->type) { 1134768Speter error("%s requires a and z to be arrays of the same type", pu, nameof(ap)); 1135768Speter return; 1136768Speter } 113714740Sthien k = width(al1); 1138768Speter itemwidth = width(ap->type); 1139768Speter ap = ap->chain; 114014740Sthien al1 = al1->chain; 114114740Sthien if (ap->chain != NIL || al1->chain != NIL) { 1142768Speter error("%s requires a and z to be single dimension arrays", pu); 1143768Speter return; 1144768Speter } 114514740Sthien if (ap == NIL || al1 == NIL) 1146768Speter return; 1147768Speter /* 114814740Sthien * al1 is the range for z i.e. u..v 1149768Speter * ap is the range for a i.e. m..n 1150768Speter * i will be n-m+1 1151768Speter * j will be v-u+1 1152768Speter */ 1153768Speter i = ap->range[1] - ap->range[0] + 1; 115414740Sthien j = al1->range[1] - al1->range[0] + 1; 1155768Speter if (i < j) { 115614740Sthien error("%s cannot have more elements in a (%d) than in z (%d)", pu, (char *) j, (char *) i); 1157768Speter return; 1158768Speter } 1159768Speter /* 1160768Speter * get n-m-(v-u) and m for the interpreter 1161768Speter */ 1162768Speter i -= j; 1163768Speter j = ap->range[0]; 116414740Sthien (void) put(2, O_CON24, k); 116514740Sthien (void) put(2, O_CON24, i); 116614740Sthien (void) put(2, O_CON24, j); 116714740Sthien (void) put(2, O_CON24, itemwidth); 116814740Sthien al1 = stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 11692073Smckusic ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 117014740Sthien ap = stkrval(pui, NLNIL , (long) RREQ ); 11712073Smckusic if (ap == NIL) 11722073Smckusic return; 117314740Sthien (void) put(1, op); 1174768Speter return; 1175768Speter case 0: 11767928Smckusick error("%s is an unimplemented extension", p->symbol); 1177768Speter return; 1178768Speter 1179768Speter default: 1180768Speter panic("proc case"); 1181768Speter } 1182768Speter } 1183768Speter #endif OBJ 1184