1768Speter /* Copyright (c) 1979 Regents of the University of California */ 2768Speter 314740Sthien #ifndef lint 4*16417Speter static char sccsid[] = "@(#)proc.c 2.2 04/26/84"; 514740Sthien #endif 6768Speter 7768Speter #include "whoami.h" 8768Speter #ifdef OBJ 9768Speter /* 10768Speter * and the rest of the file 11768Speter */ 12768Speter #include "0.h" 13768Speter #include "tree.h" 14768Speter #include "opcode.h" 15768Speter #include "objfmt.h" 1611327Speter #include "tmps.h" 1714740Sthien #include "tree_ty.h" 18768Speter 19768Speter /* 2011882Smckusick * The constant EXPOSIZE specifies the number of digits in the exponent 2111882Smckusick * of real numbers. 2211882Smckusick * 239230Smckusick * The constant REALSPC defines the amount of forced padding preceeding 249230Smckusick * real numbers when they are printed. If REALSPC == 0, then no padding 259230Smckusick * is added, REALSPC == 1 adds one extra blank irregardless of the width 269230Smckusick * specified by the user. 279230Smckusick * 289230Smckusick * N.B. - Values greater than one require program mods. 299230Smckusick */ 3011882Smckusick #define EXPOSIZE 2 3111882Smckusick #define REALSPC 0 329230Smckusick 339230Smckusick /* 34768Speter * The following array is used to determine which classes may be read 35768Speter * from textfiles. It is indexed by the return value from classify. 36768Speter */ 37768Speter #define rdops(x) rdxxxx[(x)-(TFIRST)] 38768Speter 39768Speter int rdxxxx[] = { 40768Speter 0, /* -7 file types */ 41768Speter 0, /* -6 record types */ 42768Speter 0, /* -5 array types */ 43768Speter O_READE, /* -4 scalar types */ 44768Speter 0, /* -3 pointer types */ 45768Speter 0, /* -2 set types */ 46768Speter 0, /* -1 string types */ 47768Speter 0, /* 0 nil, no type */ 48768Speter O_READE, /* 1 boolean */ 49768Speter O_READC, /* 2 character */ 50768Speter O_READ4, /* 3 integer */ 51768Speter O_READ8 /* 4 real */ 52768Speter }; 53768Speter 54768Speter /* 55768Speter * Proc handles procedure calls. 56768Speter * Non-builtin procedures are "buck-passed" to func (with a flag 57768Speter * indicating that they are actually procedures. 58768Speter * builtin procedures are handled here. 59768Speter */ 60768Speter proc(r) 6114740Sthien struct tnode *r; 62768Speter { 63768Speter register struct nl *p; 6414740Sthien register struct tnode *alv, *al; 6514740Sthien register int op; 6614740Sthien struct nl *filetype, *ap, *al1; 6714740Sthien int argc, typ, fmtspec, strfmt, stkcnt; 6814740Sthien struct tnode *argv; 6914740Sthien char fmt, format[20], *strptr, *pu; 7014740Sthien int prec, field, strnglen, fmtlen, fmtstart; 7114740Sthien struct tnode *pua, *pui, *puz, *file; 72768Speter int i, j, k; 73768Speter int itemwidth; 743226Smckusic struct tmps soffset; 753851Speter struct nl *tempnlp; 76768Speter 77768Speter #define CONPREC 4 78768Speter #define VARPREC 8 79768Speter #define CONWIDTH 1 80768Speter #define VARWIDTH 2 81768Speter #define SKIP 16 82768Speter 83768Speter /* 84768Speter * Verify that the name is 85768Speter * defined and is that of a 86768Speter * procedure. 87768Speter */ 8814740Sthien p = lookup(r->pcall_node.proc_id); 89768Speter if (p == NIL) { 9014740Sthien rvlist(r->pcall_node.arg); 91768Speter return; 92768Speter } 931198Speter if (p->class != PROC && p->class != FPROC) { 94768Speter error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]); 9514740Sthien rvlist(r->pcall_node.arg); 96768Speter return; 97768Speter } 9814740Sthien argv = r->pcall_node.arg; 99768Speter 100768Speter /* 101768Speter * Call handles user defined 102768Speter * procedures and functions. 103768Speter */ 104768Speter if (bn != 0) { 10514740Sthien (void) call(p, argv, PROC, bn); 106768Speter return; 107768Speter } 108768Speter 109768Speter /* 110768Speter * Call to built-in procedure. 111768Speter * Count the arguments. 112768Speter */ 113768Speter argc = 0; 11414740Sthien for (al = argv; al != TR_NIL; al = al->list_node.next) 115768Speter argc++; 116768Speter 117768Speter /* 118768Speter * Switch on the operator 119768Speter * associated with the built-in 120768Speter * procedure in the namelist 121768Speter */ 122768Speter op = p->value[0] &~ NSTAND; 123768Speter if (opt('s') && (p->value[0] & NSTAND)) { 124768Speter standard(); 125768Speter error("%s is a nonstandard procedure", p->symbol); 126768Speter } 127768Speter switch (op) { 128768Speter 129768Speter case O_ABORT: 130768Speter if (argc != 0) 131768Speter error("null takes no arguments"); 132768Speter return; 133768Speter 134768Speter case O_FLUSH: 135768Speter if (argc == 0) { 13614740Sthien (void) put(1, O_MESSAGE); 137768Speter return; 138768Speter } 139768Speter if (argc != 1) { 140768Speter error("flush takes at most one argument"); 141768Speter return; 142768Speter } 14314740Sthien ap = stklval(argv->list_node.list, NIL ); 14414740Sthien if (ap == NLNIL) 145768Speter return; 146768Speter if (ap->class != FILET) { 147768Speter error("flush's argument must be a file, not %s", nameof(ap)); 148768Speter return; 149768Speter } 15014740Sthien (void) put(1, op); 151768Speter return; 152768Speter 153768Speter case O_MESSAGE: 154768Speter case O_WRITEF: 155768Speter case O_WRITLN: 156768Speter /* 157768Speter * Set up default file "output"'s type 158768Speter */ 159768Speter file = NIL; 160768Speter filetype = nl+T1CHAR; 161768Speter /* 162768Speter * Determine the file implied 163768Speter * for the write and generate 164768Speter * code to make it the active file. 165768Speter */ 166768Speter if (op == O_MESSAGE) { 167768Speter /* 168768Speter * For message, all that matters 169768Speter * is that the filetype is 170768Speter * a character file. 171768Speter * Thus "output" will suit us fine. 172768Speter */ 17314740Sthien (void) put(1, O_MESSAGE); 17414740Sthien } else if (argv != TR_NIL && (al = argv->list_node.list)->tag != 17514740Sthien T_WEXP) { 176768Speter /* 177768Speter * If there is a first argument which has 178768Speter * no write widths, then it is potentially 179768Speter * a file name. 180768Speter */ 181768Speter codeoff(); 18214740Sthien ap = stkrval(argv->list_node.list, NLNIL, (long) RREQ ); 183768Speter codeon(); 18414740Sthien if (ap == NLNIL) 18514740Sthien argv = argv->list_node.next; 18614740Sthien if (ap != NLNIL && ap->class == FILET) { 187768Speter /* 188768Speter * Got "write(f, ...", make 189768Speter * f the active file, and save 190768Speter * it and its type for use in 191768Speter * processing the rest of the 192768Speter * arguments to write. 193768Speter */ 19414740Sthien file = argv->list_node.list; 195768Speter filetype = ap->type; 19614740Sthien (void) stklval(argv->list_node.list, NIL ); 19714740Sthien (void) put(1, O_UNIT); 198768Speter /* 199768Speter * Skip over the first argument 200768Speter */ 20114740Sthien argv = argv->list_node.next; 202768Speter argc--; 2038538Speter } else { 204768Speter /* 205768Speter * Set up for writing on 206768Speter * standard output. 207768Speter */ 20814740Sthien (void) put(1, O_UNITOUT); 2097953Speter output->nl_flags |= NUSED; 2108538Speter } 2118538Speter } else { 21214740Sthien (void) put(1, O_UNITOUT); 2137953Speter output->nl_flags |= NUSED; 2148538Speter } 215768Speter /* 216768Speter * Loop and process each 217768Speter * of the arguments. 218768Speter */ 21914740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 220768Speter /* 221768Speter * fmtspec indicates the type (CONstant or VARiable) 222768Speter * and number (none, WIDTH, and/or PRECision) 223768Speter * of the fields in the printf format for this 224768Speter * output variable. 2253172Smckusic * stkcnt is the number of bytes pushed on the stack 226768Speter * fmt is the format output indicator (D, E, F, O, X, S) 227768Speter * fmtstart = 0 for leading blank; = 1 for no blank 228768Speter */ 229768Speter fmtspec = NIL; 230768Speter stkcnt = 0; 231768Speter fmt = 'D'; 232768Speter fmtstart = 1; 23314740Sthien al = argv->list_node.list; 23414740Sthien if (al == TR_NIL) 235768Speter continue; 23614740Sthien if (al->tag == T_WEXP) 23714740Sthien alv = al->wexpr_node.expr1; 238768Speter else 239768Speter alv = al; 24014740Sthien if (alv == TR_NIL) 241768Speter continue; 242768Speter codeoff(); 24314740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 244768Speter codeon(); 24514740Sthien if (ap == NLNIL) 246768Speter continue; 247768Speter typ = classify(ap); 24814740Sthien if (al->tag == T_WEXP) { 249768Speter /* 250768Speter * Handle width expressions. 251768Speter * The basic game here is that width 252768Speter * expressions get evaluated. If they 253768Speter * are constant, the value is placed 254768Speter * directly in the format string. 255768Speter * Otherwise the value is pushed onto 256768Speter * the stack and an indirection is 257768Speter * put into the format string. 258768Speter */ 25914740Sthien if (al->wexpr_node.expr3 == 26014740Sthien (struct tnode *) OCT) 261768Speter fmt = 'O'; 26214740Sthien else if (al->wexpr_node.expr3 == 26314740Sthien (struct tnode *) HEX) 264768Speter fmt = 'X'; 26514740Sthien else if (al->wexpr_node.expr3 != TR_NIL) { 266768Speter /* 267768Speter * Evaluate second format spec 268768Speter */ 26914740Sthien if ( constval(al->wexpr_node.expr3) 270768Speter && isa( con.ctype , "i" ) ) { 271768Speter fmtspec += CONPREC; 272768Speter prec = con.crval; 273768Speter } else { 274768Speter fmtspec += VARPREC; 275768Speter } 276768Speter fmt = 'f'; 277768Speter switch ( typ ) { 278768Speter case TINT: 279768Speter if ( opt( 's' ) ) { 280768Speter standard(); 281768Speter error("Writing %ss with two write widths is non-standard", clnames[typ]); 282768Speter } 283768Speter /* and fall through */ 284768Speter case TDOUBLE: 285768Speter break; 286768Speter default: 287768Speter error("Cannot write %ss with two write widths", clnames[typ]); 288768Speter continue; 289768Speter } 290768Speter } 291768Speter /* 292768Speter * Evaluate first format spec 293768Speter */ 29414740Sthien if (al->wexpr_node.expr2 != TR_NIL) { 29514740Sthien if ( constval(al->wexpr_node.expr2) 296768Speter && isa( con.ctype , "i" ) ) { 297768Speter fmtspec += CONWIDTH; 298768Speter field = con.crval; 299768Speter } else { 300768Speter fmtspec += VARWIDTH; 301768Speter } 302768Speter } 303768Speter if ((fmtspec & CONPREC) && prec < 0 || 304768Speter (fmtspec & CONWIDTH) && field < 0) { 305768Speter error("Negative widths are not allowed"); 306768Speter continue; 307768Speter } 3083179Smckusic if ( opt('s') && 3093179Smckusic ((fmtspec & CONPREC) && prec == 0 || 3103179Smckusic (fmtspec & CONWIDTH) && field == 0)) { 3113179Smckusic standard(); 3123179Smckusic error("Zero widths are non-standard"); 3133179Smckusic } 314768Speter } 315768Speter if (filetype != nl+T1CHAR) { 316768Speter if (fmt == 'O' || fmt == 'X') { 317768Speter error("Oct/hex allowed only on text files"); 318768Speter continue; 319768Speter } 320768Speter if (fmtspec) { 321768Speter error("Write widths allowed only on text files"); 322768Speter continue; 323768Speter } 324768Speter /* 325768Speter * Generalized write, i.e. 326768Speter * to a non-textfile. 327768Speter */ 32814740Sthien (void) stklval(file, NIL ); 32914740Sthien (void) put(1, O_FNIL); 330768Speter /* 331768Speter * file^ := ... 332768Speter */ 33314740Sthien ap = rvalue(argv->list_node.list, NLNIL, LREQ); 33414740Sthien if (ap == NLNIL) 335768Speter continue; 33614740Sthien if (incompat(ap, filetype, 33714740Sthien argv->list_node.list)) { 338768Speter cerror("Type mismatch in write to non-text file"); 339768Speter continue; 340768Speter } 341768Speter convert(ap, filetype); 34214740Sthien (void) put(2, O_AS, width(filetype)); 343768Speter /* 344768Speter * put(file) 345768Speter */ 34614740Sthien (void) put(1, O_PUT); 347768Speter continue; 348768Speter } 349768Speter /* 350768Speter * Write to a textfile 351768Speter * 352768Speter * Evaluate the expression 353768Speter * to be written. 354768Speter */ 355768Speter if (fmt == 'O' || fmt == 'X') { 356768Speter if (opt('s')) { 357768Speter standard(); 358768Speter error("Oct and hex are non-standard"); 359768Speter } 360768Speter if (typ == TSTR || typ == TDOUBLE) { 361768Speter error("Can't write %ss with oct/hex", clnames[typ]); 362768Speter continue; 363768Speter } 364768Speter if (typ == TCHAR || typ == TBOOL) 365768Speter typ = TINT; 366768Speter } 367768Speter /* 368768Speter * Place the arguement on the stack. If there is 369768Speter * no format specified by the programmer, implement 370768Speter * the default. 371768Speter */ 372768Speter switch (typ) { 3736542Smckusick case TPTR: 3746542Smckusick warning(); 3756542Smckusick if (opt('s')) { 3766542Smckusick standard(); 3776542Smckusick } 3786542Smckusick error("Writing %ss to text files is non-standard", 3796542Smckusick clnames[typ]); 3806542Smckusick /* and fall through */ 381768Speter case TINT: 382768Speter if (fmt != 'f') { 38314740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 3843172Smckusic stkcnt += sizeof(long); 385768Speter } else { 38614740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 38714740Sthien (void) put(1, O_ITOD); 3883172Smckusic stkcnt += sizeof(double); 389768Speter typ = TDOUBLE; 390768Speter goto tdouble; 391768Speter } 392768Speter if (fmtspec == NIL) { 393768Speter if (fmt == 'D') 394768Speter field = 10; 395768Speter else if (fmt == 'X') 396768Speter field = 8; 397768Speter else if (fmt == 'O') 398768Speter field = 11; 399768Speter else 400768Speter panic("fmt1"); 401768Speter fmtspec = CONWIDTH; 402768Speter } 403768Speter break; 404768Speter case TCHAR: 405768Speter tchar: 4062073Smckusic if (fmtspec == NIL) { 40714740Sthien (void) put(1, O_FILE); 40814740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 4093172Smckusic convert(nl + T4INT, INT_TYP); 41014740Sthien (void) put(2, O_WRITEC, 4113172Smckusic sizeof(char *) + sizeof(int)); 4122073Smckusic fmtspec = SKIP; 4132073Smckusic break; 4142073Smckusic } 41514740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 4163172Smckusic convert(nl + T4INT, INT_TYP); 4173172Smckusic stkcnt += sizeof(int); 418768Speter fmt = 'c'; 419768Speter break; 420768Speter case TSCAL: 4211628Speter warning(); 422768Speter if (opt('s')) { 423768Speter standard(); 424768Speter } 4256542Smckusick error("Writing %ss to text files is non-standard", 4266542Smckusick clnames[typ]); 4276542Smckusick /* and fall through */ 428768Speter case TBOOL: 42914740Sthien (void) stkrval(alv, NLNIL , (long) RREQ ); 43014740Sthien (void) put(2, O_NAM, (long)listnames(ap)); 4313172Smckusic stkcnt += sizeof(char *); 432768Speter fmt = 's'; 433768Speter break; 434768Speter case TDOUBLE: 43514740Sthien ap = stkrval(alv, (struct nl *) TDOUBLE , (long) RREQ ); 4363172Smckusic stkcnt += sizeof(double); 437768Speter tdouble: 438768Speter switch (fmtspec) { 439768Speter case NIL: 44011882Smckusick field = 14 + (5 + EXPOSIZE); 44111882Smckusick prec = field - (5 + EXPOSIZE); 4423076Smckusic fmt = 'e'; 443768Speter fmtspec = CONWIDTH + CONPREC; 444768Speter break; 445768Speter case CONWIDTH: 4469230Smckusick field -= REALSPC; 4479230Smckusick if (field < 1) 448768Speter field = 1; 44911882Smckusick prec = field - (5 + EXPOSIZE); 450768Speter if (prec < 1) 451768Speter prec = 1; 452768Speter fmtspec += CONPREC; 4533076Smckusic fmt = 'e'; 454768Speter break; 455768Speter case CONWIDTH + CONPREC: 456768Speter case CONWIDTH + VARPREC: 4579230Smckusick field -= REALSPC; 4589230Smckusick if (field < 1) 459768Speter field = 1; 460768Speter } 461768Speter format[0] = ' '; 4629230Smckusick fmtstart = 1 - REALSPC; 463768Speter break; 464768Speter case TSTR: 46514740Sthien (void) constval( alv ); 466768Speter switch ( classify( con.ctype ) ) { 467768Speter case TCHAR: 468768Speter typ = TCHAR; 469768Speter goto tchar; 470768Speter case TSTR: 471768Speter strptr = con.cpval; 472768Speter for (strnglen = 0; *strptr++; strnglen++) /* void */; 473768Speter strptr = con.cpval; 474768Speter break; 475768Speter default: 476768Speter strnglen = width(ap); 477768Speter break; 478768Speter } 479768Speter fmt = 's'; 480768Speter strfmt = fmtspec; 481768Speter if (fmtspec == NIL) { 482768Speter fmtspec = SKIP; 483768Speter break; 484768Speter } 485768Speter if (fmtspec & CONWIDTH) { 486768Speter if (field <= strnglen) { 487768Speter fmtspec = SKIP; 488768Speter break; 489768Speter } else 490768Speter field -= strnglen; 491768Speter } 492768Speter /* 493768Speter * push string to implement leading blank padding 494768Speter */ 49514740Sthien (void) put(2, O_LVCON, 2); 496768Speter putstr("", 0); 4973172Smckusic stkcnt += sizeof(char *); 498768Speter break; 499768Speter default: 500768Speter error("Can't write %ss to a text file", clnames[typ]); 501768Speter continue; 502768Speter } 503768Speter /* 504768Speter * If there is a variable precision, evaluate it onto 505768Speter * the stack 506768Speter */ 507768Speter if (fmtspec & VARPREC) { 50814740Sthien ap = stkrval(al->wexpr_node.expr3, NLNIL , 50914740Sthien (long) RREQ ); 510768Speter if (ap == NIL) 511768Speter continue; 512768Speter if (isnta(ap,"i")) { 513768Speter error("Second write width must be integer, not %s", nameof(ap)); 514768Speter continue; 515768Speter } 516768Speter if ( opt( 't' ) ) { 51714740Sthien (void) put(3, O_MAX, 0, 0); 518768Speter } 5193172Smckusic convert(nl+T4INT, INT_TYP); 5203172Smckusic stkcnt += sizeof(int); 521768Speter } 522768Speter /* 523768Speter * If there is a variable width, evaluate it onto 524768Speter * the stack 525768Speter */ 526768Speter if (fmtspec & VARWIDTH) { 527768Speter if ( ( typ == TDOUBLE && fmtspec == VARWIDTH ) 528768Speter || typ == TSTR ) { 5293226Smckusic soffset = sizes[cbn].curtmps; 53014740Sthien tempnlp = tmpalloc((long) (sizeof(long)), 5313226Smckusic nl+T4INT, REGOK); 53214740Sthien (void) put(2, O_LV | cbn << 8 + INDX, 5333851Speter tempnlp -> value[ NL_OFFS ] ); 534768Speter } 53514740Sthien ap = stkrval(al->wexpr_node.expr2, NLNIL, (long) RREQ ); 536768Speter if (ap == NIL) 537768Speter continue; 538768Speter if (isnta(ap,"i")) { 539768Speter error("First write width must be integer, not %s", nameof(ap)); 540768Speter continue; 541768Speter } 542768Speter /* 543768Speter * Perform special processing on widths based 544768Speter * on data type 545768Speter */ 546768Speter switch (typ) { 547768Speter case TDOUBLE: 548768Speter if (fmtspec == VARWIDTH) { 5493076Smckusic fmt = 'e'; 55014740Sthien (void) put(1, O_AS4); 55114740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5523851Speter tempnlp -> value[NL_OFFS] ); 55314740Sthien (void) put(3, O_MAX, 55411882Smckusick 5 + EXPOSIZE + REALSPC, 1); 5553172Smckusic convert(nl+T4INT, INT_TYP); 5563172Smckusic stkcnt += sizeof(int); 55714740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5583851Speter tempnlp->value[NL_OFFS] ); 559768Speter fmtspec += VARPREC; 5603226Smckusic tmpfree(&soffset); 561768Speter } 56214740Sthien (void) put(3, O_MAX, REALSPC, 1); 563768Speter break; 564768Speter case TSTR: 56514740Sthien (void) put(1, O_AS4); 56614740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5673851Speter tempnlp -> value[ NL_OFFS ] ); 56814740Sthien (void) put(3, O_MAX, strnglen, 0); 569768Speter break; 570768Speter default: 571768Speter if ( opt( 't' ) ) { 57214740Sthien (void) put(3, O_MAX, 0, 0); 573768Speter } 574768Speter break; 575768Speter } 5763172Smckusic convert(nl+T4INT, INT_TYP); 5773172Smckusic stkcnt += sizeof(int); 578768Speter } 579768Speter /* 580768Speter * Generate the format string 581768Speter */ 582768Speter switch (fmtspec) { 583768Speter default: 584768Speter panic("fmt2"); 585768Speter case SKIP: 586768Speter break; 5872073Smckusic case NIL: 5882073Smckusic sprintf(&format[1], "%%%c", fmt); 5892073Smckusic goto fmtgen; 590768Speter case CONWIDTH: 5913076Smckusic sprintf(&format[1], "%%%d%c", field, fmt); 592768Speter goto fmtgen; 593768Speter case VARWIDTH: 594768Speter sprintf(&format[1], "%%*%c", fmt); 595768Speter goto fmtgen; 596768Speter case CONWIDTH + CONPREC: 5973076Smckusic sprintf(&format[1], "%%%d.%d%c", field, prec, fmt); 598768Speter goto fmtgen; 599768Speter case CONWIDTH + VARPREC: 6003076Smckusic sprintf(&format[1], "%%%d.*%c", field, fmt); 601768Speter goto fmtgen; 602768Speter case VARWIDTH + CONPREC: 6033076Smckusic sprintf(&format[1], "%%*.%d%c", prec, fmt); 604768Speter goto fmtgen; 605768Speter case VARWIDTH + VARPREC: 606768Speter sprintf(&format[1], "%%*.*%c", fmt); 607768Speter fmtgen: 608768Speter fmtlen = lenstr(&format[fmtstart], 0); 60914740Sthien (void) put(2, O_LVCON, fmtlen); 610768Speter putstr(&format[fmtstart], 0); 61114740Sthien (void) put(1, O_FILE); 6123172Smckusic stkcnt += 2 * sizeof(char *); 61314740Sthien (void) put(2, O_WRITEF, stkcnt); 614768Speter } 615768Speter /* 616768Speter * Write the string after its blank padding 617768Speter */ 618768Speter if (typ == TSTR) { 61914740Sthien (void) put(1, O_FILE); 62014740Sthien (void) put(2, CON_INT, 1); 621768Speter if (strfmt & VARWIDTH) { 62214740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX , 6233851Speter tempnlp -> value[ NL_OFFS ] ); 62414740Sthien (void) put(2, O_MIN, strnglen); 6253172Smckusic convert(nl+T4INT, INT_TYP); 6263226Smckusic tmpfree(&soffset); 627768Speter } else { 628768Speter if ((fmtspec & SKIP) && 629768Speter (strfmt & CONWIDTH)) { 630768Speter strnglen = field; 631768Speter } 63214740Sthien (void) put(2, CON_INT, strnglen); 633768Speter } 63414740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 63514740Sthien (void) put(2, O_WRITES, 6363172Smckusic 2 * sizeof(char *) + 2 * sizeof(int)); 637768Speter } 638768Speter } 639768Speter /* 640768Speter * Done with arguments. 641768Speter * Handle writeln and 642768Speter * insufficent number of args. 643768Speter */ 644768Speter switch (p->value[0] &~ NSTAND) { 645768Speter case O_WRITEF: 646768Speter if (argc == 0) 647768Speter error("Write requires an argument"); 648768Speter break; 649768Speter case O_MESSAGE: 650768Speter if (argc == 0) 651768Speter error("Message requires an argument"); 652768Speter case O_WRITLN: 653768Speter if (filetype != nl+T1CHAR) 654768Speter error("Can't 'writeln' a non text file"); 65514740Sthien (void) put(1, O_WRITLN); 656768Speter break; 657768Speter } 658768Speter return; 659768Speter 660768Speter case O_READ4: 661768Speter case O_READLN: 662768Speter /* 663768Speter * Set up default 664768Speter * file "input". 665768Speter */ 666768Speter file = NIL; 667768Speter filetype = nl+T1CHAR; 668768Speter /* 669768Speter * Determine the file implied 670768Speter * for the read and generate 671768Speter * code to make it the active file. 672768Speter */ 67314740Sthien if (argv != TR_NIL) { 674768Speter codeoff(); 67514740Sthien ap = stkrval(argv->list_node.list, NLNIL, (long) RREQ ); 676768Speter codeon(); 67714740Sthien if (ap == NLNIL) 67814740Sthien argv = argv->list_node.next; 67914740Sthien if (ap != NLNIL && ap->class == FILET) { 680768Speter /* 681768Speter * Got "read(f, ...", make 682768Speter * f the active file, and save 683768Speter * it and its type for use in 684768Speter * processing the rest of the 685768Speter * arguments to read. 686768Speter */ 68714740Sthien file = argv->list_node.list; 688768Speter filetype = ap->type; 68914740Sthien (void) stklval(argv->list_node.list, NIL ); 69014740Sthien (void) put(1, O_UNIT); 69114740Sthien argv = argv->list_node.next; 692768Speter argc--; 693768Speter } else { 694768Speter /* 695768Speter * Default is read from 696768Speter * standard input. 697768Speter */ 69814740Sthien (void) put(1, O_UNITINP); 699768Speter input->nl_flags |= NUSED; 700768Speter } 701768Speter } else { 70214740Sthien (void) put(1, O_UNITINP); 703768Speter input->nl_flags |= NUSED; 704768Speter } 705768Speter /* 706768Speter * Loop and process each 707768Speter * of the arguments. 708768Speter */ 70914740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 710768Speter /* 711768Speter * Get the address of the target 712768Speter * on the stack. 713768Speter */ 71414740Sthien al = argv->list_node.list; 71514740Sthien if (al == TR_NIL) 716768Speter continue; 71714740Sthien if (al->tag != T_VAR) { 718768Speter error("Arguments to %s must be variables, not expressions", p->symbol); 719768Speter continue; 720768Speter } 721768Speter ap = stklval(al, MOD|ASGN|NOUSE); 72214740Sthien if (ap == NLNIL) 723768Speter continue; 724768Speter if (filetype != nl+T1CHAR) { 725768Speter /* 726768Speter * Generalized read, i.e. 727768Speter * from a non-textfile. 728768Speter */ 72914740Sthien if (incompat(filetype, ap, 73014740Sthien argv->list_node.list )) { 731768Speter error("Type mismatch in read from non-text file"); 732768Speter continue; 733768Speter } 734768Speter /* 735768Speter * var := file ^; 736768Speter */ 737768Speter if (file != NIL) 738*16417Speter (void) stklval(file, NIL); 739768Speter else /* Magic */ 740*16417Speter (void) put(2, PTR_RV, (int)input->value[0]); 74114740Sthien (void) put(1, O_FNIL); 742*16417Speter if (isa(filetype, "bcsi")) { 743*16417Speter int filewidth = width(filetype); 744*16417Speter 745*16417Speter switch (filewidth) { 746*16417Speter case 4: 747*16417Speter (void) put(1, O_IND4); 748*16417Speter break; 749*16417Speter case 2: 750*16417Speter (void) put(1, O_IND2); 751*16417Speter break; 752*16417Speter case 1: 753*16417Speter (void) put(1, O_IND1); 754*16417Speter break; 755*16417Speter default: 756*16417Speter (void) put(2, O_IND, filewidth); 757*16417Speter } 758*16417Speter convert(filetype, ap); 759*16417Speter rangechk(ap, ap); 760*16417Speter (void) gen(O_AS2, O_AS2, 761*16417Speter filewidth, width(ap)); 762*16417Speter } else { 763*16417Speter (void) put(2, O_IND, width(filetype)); 764*16417Speter convert(filetype, ap); 765*16417Speter (void) put(2, O_AS, width(ap)); 766*16417Speter } 767768Speter /* 768768Speter * get(file); 769768Speter */ 77014740Sthien (void) put(1, O_GET); 771768Speter continue; 772768Speter } 773768Speter typ = classify(ap); 774768Speter op = rdops(typ); 775768Speter if (op == NIL) { 776768Speter error("Can't read %ss from a text file", clnames[typ]); 777768Speter continue; 778768Speter } 779768Speter if (op != O_READE) 78014740Sthien (void) put(1, op); 781768Speter else { 78214740Sthien (void) put(2, op, (long)listnames(ap)); 7831628Speter warning(); 784768Speter if (opt('s')) { 785768Speter standard(); 786768Speter } 7871628Speter error("Reading scalars from text files is non-standard"); 788768Speter } 789768Speter /* 790768Speter * Data read is on the stack. 791768Speter * Assign it. 792768Speter */ 793768Speter if (op != O_READ8 && op != O_READE) 794768Speter rangechk(ap, op == O_READC ? ap : nl+T4INT); 79514740Sthien (void) gen(O_AS2, O_AS2, width(ap), 796768Speter op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2); 797768Speter } 798768Speter /* 799768Speter * Done with arguments. 800768Speter * Handle readln and 801768Speter * insufficient number of args. 802768Speter */ 803768Speter if (p->value[0] == O_READLN) { 804768Speter if (filetype != nl+T1CHAR) 805768Speter error("Can't 'readln' a non text file"); 80614740Sthien (void) put(1, O_READLN); 807768Speter } 808768Speter else if (argc == 0) 809768Speter error("read requires an argument"); 810768Speter return; 811768Speter 812768Speter case O_GET: 813768Speter case O_PUT: 814768Speter if (argc != 1) { 815768Speter error("%s expects one argument", p->symbol); 816768Speter return; 817768Speter } 81814740Sthien ap = stklval(argv->list_node.list, NIL ); 81914740Sthien if (ap == NLNIL) 820768Speter return; 821768Speter if (ap->class != FILET) { 822768Speter error("Argument to %s must be a file, not %s", p->symbol, nameof(ap)); 823768Speter return; 824768Speter } 82514740Sthien (void) put(1, O_UNIT); 82614740Sthien (void) put(1, op); 827768Speter return; 828768Speter 829768Speter case O_RESET: 830768Speter case O_REWRITE: 831768Speter if (argc == 0 || argc > 2) { 832768Speter error("%s expects one or two arguments", p->symbol); 833768Speter return; 834768Speter } 835768Speter if (opt('s') && argc == 2) { 836768Speter standard(); 837768Speter error("Two argument forms of reset and rewrite are non-standard"); 838768Speter } 8392073Smckusic codeoff(); 84014740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 8412073Smckusic codeon(); 84214740Sthien if (ap == NLNIL) 843768Speter return; 844768Speter if (ap->class != FILET) { 845768Speter error("First argument to %s must be a file, not %s", p->symbol, nameof(ap)); 846768Speter return; 847768Speter } 84814740Sthien (void) put(2, O_CON24, text(ap) ? 0: width(ap->type)); 849768Speter if (argc == 2) { 850768Speter /* 851768Speter * Optional second argument 852768Speter * is a string name of a 853768Speter * UNIX (R) file to be associated. 854768Speter */ 85514740Sthien al = argv->list_node.next; 8562073Smckusic codeoff(); 85714740Sthien al = (struct tnode *) stkrval(al->list_node.list, 85814740Sthien (struct nl *) NOFLAGS , (long) RREQ ); 8592073Smckusic codeon(); 86014740Sthien if (al == TR_NIL) 861768Speter return; 86214740Sthien if (classify((struct nl *) al) != TSTR) { 86314740Sthien error("Second argument to %s must be a string, not %s", p->symbol, nameof((struct nl *) al)); 864768Speter return; 865768Speter } 86614740Sthien (void) put(2, O_CON24, width((struct nl *) al)); 86714740Sthien al = argv->list_node.next; 86814740Sthien al = (struct tnode *) stkrval(al->list_node.list, 86914740Sthien (struct nl *) NOFLAGS , (long) RREQ ); 870768Speter } else { 87114740Sthien (void) put(2, O_CON24, 0); 87214740Sthien (void) put(2, PTR_CON, NIL); 873768Speter } 87414740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 87514740Sthien (void) put(1, op); 876768Speter return; 877768Speter 878768Speter case O_NEW: 879768Speter case O_DISPOSE: 880768Speter if (argc == 0) { 881768Speter error("%s expects at least one argument", p->symbol); 882768Speter return; 883768Speter } 88414740Sthien ap = stklval(argv->list_node.list, 88514740Sthien op == O_NEW ? ( MOD | NOUSE ) : MOD ); 88614740Sthien if (ap == NLNIL) 887768Speter return; 888768Speter if (ap->class != PTR) { 889768Speter error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap)); 890768Speter return; 891768Speter } 892768Speter ap = ap->type; 893768Speter if (ap == NIL) 894768Speter return; 8957966Smckusick if ((ap->nl_flags & NFILES) && op == O_DISPOSE) 8967966Smckusick op = O_DFDISP; 89714740Sthien argv = argv->list_node.next; 89814740Sthien if (argv != TR_NIL) { 899768Speter if (ap->class != RECORD) { 900768Speter error("Record required when specifying variant tags"); 901768Speter return; 902768Speter } 90314740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 904768Speter if (ap->ptr[NL_VARNT] == NIL) { 905768Speter error("Too many tag fields"); 906768Speter return; 907768Speter } 90814740Sthien if (!isconst(argv->list_node.list)) { 909768Speter error("Second and successive arguments to %s must be constants", p->symbol); 910768Speter return; 911768Speter } 91214740Sthien gconst(argv->list_node.list); 913768Speter if (con.ctype == NIL) 914768Speter return; 91514740Sthien if (incompat(con.ctype, ( 91614740Sthien ap->ptr[NL_TAG])->type , TR_NIL )) { 917768Speter cerror("Specified tag constant type clashed with variant case selector type"); 918768Speter return; 919768Speter } 920768Speter for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain) 921768Speter if (ap->range[0] == con.crval) 922768Speter break; 923768Speter if (ap == NIL) { 924768Speter error("No variant case label value equals specified constant value"); 925768Speter return; 926768Speter } 927768Speter ap = ap->ptr[NL_VTOREC]; 928768Speter } 929768Speter } 93014740Sthien (void) put(2, op, width(ap)); 931768Speter return; 932768Speter 933768Speter case O_DATE: 934768Speter case O_TIME: 935768Speter if (argc != 1) { 936768Speter error("%s expects one argument", p->symbol); 937768Speter return; 938768Speter } 93914740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 94014740Sthien if (ap == NLNIL) 941768Speter return; 942768Speter if (classify(ap) != TSTR || width(ap) != 10) { 943768Speter error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap)); 944768Speter return; 945768Speter } 94614740Sthien (void) put(1, op); 947768Speter return; 948768Speter 949768Speter case O_HALT: 950768Speter if (argc != 0) { 951768Speter error("halt takes no arguments"); 952768Speter return; 953768Speter } 95414740Sthien (void) put(1, op); 95514740Sthien noreach = TRUE; /* used to be 1 */ 956768Speter return; 957768Speter 958768Speter case O_ARGV: 959768Speter if (argc != 2) { 960768Speter error("argv takes two arguments"); 961768Speter return; 962768Speter } 96314740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 96414740Sthien if (ap == NLNIL) 965768Speter return; 966768Speter if (isnta(ap, "i")) { 967768Speter error("argv's first argument must be an integer, not %s", nameof(ap)); 968768Speter return; 969768Speter } 97014740Sthien al = argv->list_node.next; 97114740Sthien ap = stklval(al->list_node.list, MOD|NOUSE); 97214740Sthien if (ap == NLNIL) 973768Speter return; 974768Speter if (classify(ap) != TSTR) { 975768Speter error("argv's second argument must be a string, not %s", nameof(ap)); 976768Speter return; 977768Speter } 97814740Sthien (void) put(2, op, width(ap)); 979768Speter return; 980768Speter 981768Speter case O_STLIM: 982768Speter if (argc != 1) { 983768Speter error("stlimit requires one argument"); 984768Speter return; 985768Speter } 98614740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 98714740Sthien if (ap == NLNIL) 988768Speter return; 989768Speter if (isnta(ap, "i")) { 990768Speter error("stlimit's argument must be an integer, not %s", nameof(ap)); 991768Speter return; 992768Speter } 993768Speter if (width(ap) != 4) 99414740Sthien (void) put(1, O_STOI); 99514740Sthien (void) put(1, op); 996768Speter return; 997768Speter 998768Speter case O_REMOVE: 999768Speter if (argc != 1) { 1000768Speter error("remove expects one argument"); 1001768Speter return; 1002768Speter } 10032073Smckusic codeoff(); 100414740Sthien ap = stkrval(argv->list_node.list, (struct nl *) NOFLAGS, 100514740Sthien (long) RREQ ); 10062073Smckusic codeon(); 100714740Sthien if (ap == NLNIL) 1008768Speter return; 1009768Speter if (classify(ap) != TSTR) { 1010768Speter error("remove's argument must be a string, not %s", nameof(ap)); 1011768Speter return; 1012768Speter } 101314740Sthien (void) put(2, O_CON24, width(ap)); 101414740Sthien ap = stkrval(argv->list_node.list, (struct nl *) NOFLAGS, 101514740Sthien (long) RREQ ); 101614740Sthien (void) put(1, op); 1017768Speter return; 1018768Speter 1019768Speter case O_LLIMIT: 1020768Speter if (argc != 2) { 1021768Speter error("linelimit expects two arguments"); 1022768Speter return; 1023768Speter } 102414740Sthien al = argv->list_node.next; 102514740Sthien ap = stkrval(al->list_node.list, NLNIL , (long) RREQ ); 1026768Speter if (ap == NIL) 1027768Speter return; 1028768Speter if (isnta(ap, "i")) { 1029768Speter error("linelimit's second argument must be an integer, not %s", nameof(ap)); 1030768Speter return; 1031768Speter } 103214740Sthien ap = stklval(argv->list_node.list, NOFLAGS|NOUSE); 103314740Sthien if (ap == NLNIL) 10342073Smckusic return; 10352073Smckusic if (!text(ap)) { 10362073Smckusic error("linelimit's first argument must be a text file, not %s", nameof(ap)); 10372073Smckusic return; 10382073Smckusic } 103914740Sthien (void) put(1, op); 1040768Speter return; 1041768Speter case O_PAGE: 1042768Speter if (argc != 1) { 1043768Speter error("page expects one argument"); 1044768Speter return; 1045768Speter } 104614740Sthien ap = stklval(argv->list_node.list, NIL ); 104714740Sthien if (ap == NLNIL) 1048768Speter return; 1049768Speter if (!text(ap)) { 1050768Speter error("Argument to page must be a text file, not %s", nameof(ap)); 1051768Speter return; 1052768Speter } 105314740Sthien (void) put(1, O_UNIT); 105414740Sthien (void) put(1, op); 1055768Speter return; 1056768Speter 10577928Smckusick case O_ASRT: 10587928Smckusick if (!opt('t')) 10597928Smckusick return; 10607928Smckusick if (argc == 0 || argc > 2) { 10617928Smckusick error("Assert expects one or two arguments"); 10627928Smckusick return; 10637928Smckusick } 10647928Smckusick if (argc == 2) { 10657928Smckusick /* 10667928Smckusick * Optional second argument is a string specifying 10677928Smckusick * why the assertion failed. 10687928Smckusick */ 106914740Sthien al = argv->list_node.next; 107014740Sthien al1 = stkrval(al->list_node.list, NLNIL , (long) RREQ ); 107114740Sthien if (al1 == NIL) 10727928Smckusick return; 107314740Sthien if (classify(al1) != TSTR) { 107414740Sthien error("Second argument to assert must be a string, not %s", nameof(al1)); 10757928Smckusick return; 10767928Smckusick } 10777928Smckusick } else { 107814740Sthien (void) put(2, PTR_CON, NIL); 10797928Smckusick } 108014740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 10817928Smckusick if (ap == NIL) 10827928Smckusick return; 10837928Smckusick if (isnta(ap, "b")) 10847928Smckusick error("Assert expression must be Boolean, not %ss", nameof(ap)); 108514740Sthien (void) put(1, O_ASRT); 10867928Smckusick return; 10877928Smckusick 1088768Speter case O_PACK: 1089768Speter if (argc != 3) { 1090768Speter error("pack expects three arguments"); 1091768Speter return; 1092768Speter } 1093768Speter pu = "pack(a,i,z)"; 109414740Sthien pua = argv->list_node.list; 109514740Sthien al = argv->list_node.next; 109614740Sthien pui = al->list_node.list; 109714740Sthien alv = al->list_node.next; 109814740Sthien puz = alv->list_node.list; 1099768Speter goto packunp; 1100768Speter case O_UNPACK: 1101768Speter if (argc != 3) { 1102768Speter error("unpack expects three arguments"); 1103768Speter return; 1104768Speter } 1105768Speter pu = "unpack(z,a,i)"; 110614740Sthien puz = argv->list_node.list; 110714740Sthien al = argv->list_node.next; 110814740Sthien pua = al->list_node.list; 110914740Sthien alv = al->list_node.next; 111014740Sthien pui = alv->list_node.list; 1111768Speter packunp: 11122073Smckusic codeoff(); 1113768Speter ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 111414740Sthien al1 = stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 11152073Smckusic codeon(); 1116768Speter if (ap == NIL) 1117768Speter return; 1118768Speter if (ap->class != ARRAY) { 1119768Speter error("%s requires a to be an unpacked array, not %s", pu, nameof(ap)); 1120768Speter return; 1121768Speter } 112214740Sthien if (al1->class != ARRAY) { 1123768Speter error("%s requires z to be a packed array, not %s", pu, nameof(ap)); 1124768Speter return; 1125768Speter } 112614740Sthien if (al1->type == NIL || ap->type == NIL) 1127768Speter return; 112814740Sthien if (al1->type != ap->type) { 1129768Speter error("%s requires a and z to be arrays of the same type", pu, nameof(ap)); 1130768Speter return; 1131768Speter } 113214740Sthien k = width(al1); 1133768Speter itemwidth = width(ap->type); 1134768Speter ap = ap->chain; 113514740Sthien al1 = al1->chain; 113614740Sthien if (ap->chain != NIL || al1->chain != NIL) { 1137768Speter error("%s requires a and z to be single dimension arrays", pu); 1138768Speter return; 1139768Speter } 114014740Sthien if (ap == NIL || al1 == NIL) 1141768Speter return; 1142768Speter /* 114314740Sthien * al1 is the range for z i.e. u..v 1144768Speter * ap is the range for a i.e. m..n 1145768Speter * i will be n-m+1 1146768Speter * j will be v-u+1 1147768Speter */ 1148768Speter i = ap->range[1] - ap->range[0] + 1; 114914740Sthien j = al1->range[1] - al1->range[0] + 1; 1150768Speter if (i < j) { 115114740Sthien error("%s cannot have more elements in a (%d) than in z (%d)", pu, (char *) j, (char *) i); 1152768Speter return; 1153768Speter } 1154768Speter /* 1155768Speter * get n-m-(v-u) and m for the interpreter 1156768Speter */ 1157768Speter i -= j; 1158768Speter j = ap->range[0]; 115914740Sthien (void) put(2, O_CON24, k); 116014740Sthien (void) put(2, O_CON24, i); 116114740Sthien (void) put(2, O_CON24, j); 116214740Sthien (void) put(2, O_CON24, itemwidth); 116314740Sthien al1 = stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 11642073Smckusic ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 116514740Sthien ap = stkrval(pui, NLNIL , (long) RREQ ); 11662073Smckusic if (ap == NIL) 11672073Smckusic return; 116814740Sthien (void) put(1, op); 1169768Speter return; 1170768Speter case 0: 11717928Smckusick error("%s is an unimplemented extension", p->symbol); 1172768Speter return; 1173768Speter 1174768Speter default: 1175768Speter panic("proc case"); 1176768Speter } 1177768Speter } 1178768Speter #endif OBJ 1179