1768Speter /* Copyright (c) 1979 Regents of the University of California */ 2768Speter 3*14740Sthien #ifndef lint 4*14740Sthien static char sccsid[] = "@(#)proc.c 1.19 08/19/83"; 5*14740Sthien #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" 17*14740Sthien #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) 61*14740Sthien struct tnode *r; 62768Speter { 63768Speter register struct nl *p; 64*14740Sthien register struct tnode *alv, *al; 65*14740Sthien register int op; 66*14740Sthien struct nl *filetype, *ap, *al1; 67*14740Sthien int argc, typ, fmtspec, strfmt, stkcnt; 68*14740Sthien struct tnode *argv; 69*14740Sthien char fmt, format[20], *strptr, *pu; 70*14740Sthien int prec, field, strnglen, fmtlen, fmtstart; 71*14740Sthien 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 */ 88*14740Sthien p = lookup(r->pcall_node.proc_id); 89768Speter if (p == NIL) { 90*14740Sthien 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]); 95*14740Sthien rvlist(r->pcall_node.arg); 96768Speter return; 97768Speter } 98*14740Sthien argv = r->pcall_node.arg; 99768Speter 100768Speter /* 101768Speter * Call handles user defined 102768Speter * procedures and functions. 103768Speter */ 104768Speter if (bn != 0) { 105*14740Sthien (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; 114*14740Sthien 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) { 136*14740Sthien (void) put(1, O_MESSAGE); 137768Speter return; 138768Speter } 139768Speter if (argc != 1) { 140768Speter error("flush takes at most one argument"); 141768Speter return; 142768Speter } 143*14740Sthien ap = stklval(argv->list_node.list, NIL ); 144*14740Sthien 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 } 150*14740Sthien (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 */ 173*14740Sthien (void) put(1, O_MESSAGE); 174*14740Sthien } else if (argv != TR_NIL && (al = argv->list_node.list)->tag != 175*14740Sthien 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(); 182*14740Sthien ap = stkrval(argv->list_node.list, NLNIL, (long) RREQ ); 183768Speter codeon(); 184*14740Sthien if (ap == NLNIL) 185*14740Sthien argv = argv->list_node.next; 186*14740Sthien 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 */ 194*14740Sthien file = argv->list_node.list; 195768Speter filetype = ap->type; 196*14740Sthien (void) stklval(argv->list_node.list, NIL ); 197*14740Sthien (void) put(1, O_UNIT); 198768Speter /* 199768Speter * Skip over the first argument 200768Speter */ 201*14740Sthien argv = argv->list_node.next; 202768Speter argc--; 2038538Speter } else { 204768Speter /* 205768Speter * Set up for writing on 206768Speter * standard output. 207768Speter */ 208*14740Sthien (void) put(1, O_UNITOUT); 2097953Speter output->nl_flags |= NUSED; 2108538Speter } 2118538Speter } else { 212*14740Sthien (void) put(1, O_UNITOUT); 2137953Speter output->nl_flags |= NUSED; 2148538Speter } 215768Speter /* 216768Speter * Loop and process each 217768Speter * of the arguments. 218768Speter */ 219*14740Sthien 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; 233*14740Sthien al = argv->list_node.list; 234*14740Sthien if (al == TR_NIL) 235768Speter continue; 236*14740Sthien if (al->tag == T_WEXP) 237*14740Sthien alv = al->wexpr_node.expr1; 238768Speter else 239768Speter alv = al; 240*14740Sthien if (alv == TR_NIL) 241768Speter continue; 242768Speter codeoff(); 243*14740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 244768Speter codeon(); 245*14740Sthien if (ap == NLNIL) 246768Speter continue; 247768Speter typ = classify(ap); 248*14740Sthien 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 */ 259*14740Sthien if (al->wexpr_node.expr3 == 260*14740Sthien (struct tnode *) OCT) 261768Speter fmt = 'O'; 262*14740Sthien else if (al->wexpr_node.expr3 == 263*14740Sthien (struct tnode *) HEX) 264768Speter fmt = 'X'; 265*14740Sthien else if (al->wexpr_node.expr3 != TR_NIL) { 266768Speter /* 267768Speter * Evaluate second format spec 268768Speter */ 269*14740Sthien 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 */ 294*14740Sthien if (al->wexpr_node.expr2 != TR_NIL) { 295*14740Sthien 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 */ 328*14740Sthien (void) stklval(file, NIL ); 329*14740Sthien (void) put(1, O_FNIL); 330768Speter /* 331768Speter * file^ := ... 332768Speter */ 333*14740Sthien ap = rvalue(argv->list_node.list, NLNIL, LREQ); 334*14740Sthien if (ap == NLNIL) 335768Speter continue; 336*14740Sthien if (incompat(ap, filetype, 337*14740Sthien argv->list_node.list)) { 338768Speter cerror("Type mismatch in write to non-text file"); 339768Speter continue; 340768Speter } 341768Speter convert(ap, filetype); 342*14740Sthien (void) put(2, O_AS, width(filetype)); 343768Speter /* 344768Speter * put(file) 345768Speter */ 346*14740Sthien (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') { 383*14740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 3843172Smckusic stkcnt += sizeof(long); 385768Speter } else { 386*14740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 387*14740Sthien (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) { 407*14740Sthien (void) put(1, O_FILE); 408*14740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 4093172Smckusic convert(nl + T4INT, INT_TYP); 410*14740Sthien (void) put(2, O_WRITEC, 4113172Smckusic sizeof(char *) + sizeof(int)); 4122073Smckusic fmtspec = SKIP; 4132073Smckusic break; 4142073Smckusic } 415*14740Sthien 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: 429*14740Sthien (void) stkrval(alv, NLNIL , (long) RREQ ); 430*14740Sthien (void) put(2, O_NAM, (long)listnames(ap)); 4313172Smckusic stkcnt += sizeof(char *); 432768Speter fmt = 's'; 433768Speter break; 434768Speter case TDOUBLE: 435*14740Sthien 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: 465*14740Sthien (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 */ 495*14740Sthien (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) { 508*14740Sthien ap = stkrval(al->wexpr_node.expr3, NLNIL , 509*14740Sthien (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' ) ) { 517*14740Sthien (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; 530*14740Sthien tempnlp = tmpalloc((long) (sizeof(long)), 5313226Smckusic nl+T4INT, REGOK); 532*14740Sthien (void) put(2, O_LV | cbn << 8 + INDX, 5333851Speter tempnlp -> value[ NL_OFFS ] ); 534768Speter } 535*14740Sthien 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'; 550*14740Sthien (void) put(1, O_AS4); 551*14740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5523851Speter tempnlp -> value[NL_OFFS] ); 553*14740Sthien (void) put(3, O_MAX, 55411882Smckusick 5 + EXPOSIZE + REALSPC, 1); 5553172Smckusic convert(nl+T4INT, INT_TYP); 5563172Smckusic stkcnt += sizeof(int); 557*14740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5583851Speter tempnlp->value[NL_OFFS] ); 559768Speter fmtspec += VARPREC; 5603226Smckusic tmpfree(&soffset); 561768Speter } 562*14740Sthien (void) put(3, O_MAX, REALSPC, 1); 563768Speter break; 564768Speter case TSTR: 565*14740Sthien (void) put(1, O_AS4); 566*14740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5673851Speter tempnlp -> value[ NL_OFFS ] ); 568*14740Sthien (void) put(3, O_MAX, strnglen, 0); 569768Speter break; 570768Speter default: 571768Speter if ( opt( 't' ) ) { 572*14740Sthien (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); 609*14740Sthien (void) put(2, O_LVCON, fmtlen); 610768Speter putstr(&format[fmtstart], 0); 611*14740Sthien (void) put(1, O_FILE); 6123172Smckusic stkcnt += 2 * sizeof(char *); 613*14740Sthien (void) put(2, O_WRITEF, stkcnt); 614768Speter } 615768Speter /* 616768Speter * Write the string after its blank padding 617768Speter */ 618768Speter if (typ == TSTR) { 619*14740Sthien (void) put(1, O_FILE); 620*14740Sthien (void) put(2, CON_INT, 1); 621768Speter if (strfmt & VARWIDTH) { 622*14740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX , 6233851Speter tempnlp -> value[ NL_OFFS ] ); 624*14740Sthien (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 } 632*14740Sthien (void) put(2, CON_INT, strnglen); 633768Speter } 634*14740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 635*14740Sthien (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"); 655*14740Sthien (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 */ 673*14740Sthien if (argv != TR_NIL) { 674768Speter codeoff(); 675*14740Sthien ap = stkrval(argv->list_node.list, NLNIL, (long) RREQ ); 676768Speter codeon(); 677*14740Sthien if (ap == NLNIL) 678*14740Sthien argv = argv->list_node.next; 679*14740Sthien 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 */ 687*14740Sthien file = argv->list_node.list; 688768Speter filetype = ap->type; 689*14740Sthien (void) stklval(argv->list_node.list, NIL ); 690*14740Sthien (void) put(1, O_UNIT); 691*14740Sthien argv = argv->list_node.next; 692768Speter argc--; 693768Speter } else { 694768Speter /* 695768Speter * Default is read from 696768Speter * standard input. 697768Speter */ 698*14740Sthien (void) put(1, O_UNITINP); 699768Speter input->nl_flags |= NUSED; 700768Speter } 701768Speter } else { 702*14740Sthien (void) put(1, O_UNITINP); 703768Speter input->nl_flags |= NUSED; 704768Speter } 705768Speter /* 706768Speter * Loop and process each 707768Speter * of the arguments. 708768Speter */ 709*14740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 710768Speter /* 711768Speter * Get the address of the target 712768Speter * on the stack. 713768Speter */ 714*14740Sthien al = argv->list_node.list; 715*14740Sthien if (al == TR_NIL) 716768Speter continue; 717*14740Sthien 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); 722*14740Sthien if (ap == NLNIL) 723768Speter continue; 724768Speter if (filetype != nl+T1CHAR) { 725768Speter /* 726768Speter * Generalized read, i.e. 727768Speter * from a non-textfile. 728768Speter */ 729*14740Sthien if (incompat(filetype, ap, 730*14740Sthien 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*14740Sthien (void) stklval(file, NIL ); 739768Speter else /* Magic */ 740*14740Sthien (void) put(2, PTR_RV, (int)input->value[0]); 741*14740Sthien (void) put(1, O_FNIL); 742*14740Sthien (void) put(2, O_IND, width(filetype)); 743768Speter convert(filetype, ap); 744768Speter if (isa(ap, "bsci")) 745768Speter rangechk(ap, ap); 746*14740Sthien (void) put(2, O_AS, width(ap)); 747768Speter /* 748768Speter * get(file); 749768Speter */ 750*14740Sthien (void) put(1, O_GET); 751768Speter continue; 752768Speter } 753768Speter typ = classify(ap); 754768Speter op = rdops(typ); 755768Speter if (op == NIL) { 756768Speter error("Can't read %ss from a text file", clnames[typ]); 757768Speter continue; 758768Speter } 759768Speter if (op != O_READE) 760*14740Sthien (void) put(1, op); 761768Speter else { 762*14740Sthien (void) put(2, op, (long)listnames(ap)); 7631628Speter warning(); 764768Speter if (opt('s')) { 765768Speter standard(); 766768Speter } 7671628Speter error("Reading scalars from text files is non-standard"); 768768Speter } 769768Speter /* 770768Speter * Data read is on the stack. 771768Speter * Assign it. 772768Speter */ 773768Speter if (op != O_READ8 && op != O_READE) 774768Speter rangechk(ap, op == O_READC ? ap : nl+T4INT); 775*14740Sthien (void) gen(O_AS2, O_AS2, width(ap), 776768Speter op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2); 777768Speter } 778768Speter /* 779768Speter * Done with arguments. 780768Speter * Handle readln and 781768Speter * insufficient number of args. 782768Speter */ 783768Speter if (p->value[0] == O_READLN) { 784768Speter if (filetype != nl+T1CHAR) 785768Speter error("Can't 'readln' a non text file"); 786*14740Sthien (void) put(1, O_READLN); 787768Speter } 788768Speter else if (argc == 0) 789768Speter error("read requires an argument"); 790768Speter return; 791768Speter 792768Speter case O_GET: 793768Speter case O_PUT: 794768Speter if (argc != 1) { 795768Speter error("%s expects one argument", p->symbol); 796768Speter return; 797768Speter } 798*14740Sthien ap = stklval(argv->list_node.list, NIL ); 799*14740Sthien if (ap == NLNIL) 800768Speter return; 801768Speter if (ap->class != FILET) { 802768Speter error("Argument to %s must be a file, not %s", p->symbol, nameof(ap)); 803768Speter return; 804768Speter } 805*14740Sthien (void) put(1, O_UNIT); 806*14740Sthien (void) put(1, op); 807768Speter return; 808768Speter 809768Speter case O_RESET: 810768Speter case O_REWRITE: 811768Speter if (argc == 0 || argc > 2) { 812768Speter error("%s expects one or two arguments", p->symbol); 813768Speter return; 814768Speter } 815768Speter if (opt('s') && argc == 2) { 816768Speter standard(); 817768Speter error("Two argument forms of reset and rewrite are non-standard"); 818768Speter } 8192073Smckusic codeoff(); 820*14740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 8212073Smckusic codeon(); 822*14740Sthien if (ap == NLNIL) 823768Speter return; 824768Speter if (ap->class != FILET) { 825768Speter error("First argument to %s must be a file, not %s", p->symbol, nameof(ap)); 826768Speter return; 827768Speter } 828*14740Sthien (void) put(2, O_CON24, text(ap) ? 0: width(ap->type)); 829768Speter if (argc == 2) { 830768Speter /* 831768Speter * Optional second argument 832768Speter * is a string name of a 833768Speter * UNIX (R) file to be associated. 834768Speter */ 835*14740Sthien al = argv->list_node.next; 8362073Smckusic codeoff(); 837*14740Sthien al = (struct tnode *) stkrval(al->list_node.list, 838*14740Sthien (struct nl *) NOFLAGS , (long) RREQ ); 8392073Smckusic codeon(); 840*14740Sthien if (al == TR_NIL) 841768Speter return; 842*14740Sthien if (classify((struct nl *) al) != TSTR) { 843*14740Sthien error("Second argument to %s must be a string, not %s", p->symbol, nameof((struct nl *) al)); 844768Speter return; 845768Speter } 846*14740Sthien (void) put(2, O_CON24, width((struct nl *) al)); 847*14740Sthien al = argv->list_node.next; 848*14740Sthien al = (struct tnode *) stkrval(al->list_node.list, 849*14740Sthien (struct nl *) NOFLAGS , (long) RREQ ); 850768Speter } else { 851*14740Sthien (void) put(2, O_CON24, 0); 852*14740Sthien (void) put(2, PTR_CON, NIL); 853768Speter } 854*14740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 855*14740Sthien (void) put(1, op); 856768Speter return; 857768Speter 858768Speter case O_NEW: 859768Speter case O_DISPOSE: 860768Speter if (argc == 0) { 861768Speter error("%s expects at least one argument", p->symbol); 862768Speter return; 863768Speter } 864*14740Sthien ap = stklval(argv->list_node.list, 865*14740Sthien op == O_NEW ? ( MOD | NOUSE ) : MOD ); 866*14740Sthien if (ap == NLNIL) 867768Speter return; 868768Speter if (ap->class != PTR) { 869768Speter error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap)); 870768Speter return; 871768Speter } 872768Speter ap = ap->type; 873768Speter if (ap == NIL) 874768Speter return; 8757966Smckusick if ((ap->nl_flags & NFILES) && op == O_DISPOSE) 8767966Smckusick op = O_DFDISP; 877*14740Sthien argv = argv->list_node.next; 878*14740Sthien if (argv != TR_NIL) { 879768Speter if (ap->class != RECORD) { 880768Speter error("Record required when specifying variant tags"); 881768Speter return; 882768Speter } 883*14740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 884768Speter if (ap->ptr[NL_VARNT] == NIL) { 885768Speter error("Too many tag fields"); 886768Speter return; 887768Speter } 888*14740Sthien if (!isconst(argv->list_node.list)) { 889768Speter error("Second and successive arguments to %s must be constants", p->symbol); 890768Speter return; 891768Speter } 892*14740Sthien gconst(argv->list_node.list); 893768Speter if (con.ctype == NIL) 894768Speter return; 895*14740Sthien if (incompat(con.ctype, ( 896*14740Sthien ap->ptr[NL_TAG])->type , TR_NIL )) { 897768Speter cerror("Specified tag constant type clashed with variant case selector type"); 898768Speter return; 899768Speter } 900768Speter for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain) 901768Speter if (ap->range[0] == con.crval) 902768Speter break; 903768Speter if (ap == NIL) { 904768Speter error("No variant case label value equals specified constant value"); 905768Speter return; 906768Speter } 907768Speter ap = ap->ptr[NL_VTOREC]; 908768Speter } 909768Speter } 910*14740Sthien (void) put(2, op, width(ap)); 911768Speter return; 912768Speter 913768Speter case O_DATE: 914768Speter case O_TIME: 915768Speter if (argc != 1) { 916768Speter error("%s expects one argument", p->symbol); 917768Speter return; 918768Speter } 919*14740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 920*14740Sthien if (ap == NLNIL) 921768Speter return; 922768Speter if (classify(ap) != TSTR || width(ap) != 10) { 923768Speter error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap)); 924768Speter return; 925768Speter } 926*14740Sthien (void) put(1, op); 927768Speter return; 928768Speter 929768Speter case O_HALT: 930768Speter if (argc != 0) { 931768Speter error("halt takes no arguments"); 932768Speter return; 933768Speter } 934*14740Sthien (void) put(1, op); 935*14740Sthien noreach = TRUE; /* used to be 1 */ 936768Speter return; 937768Speter 938768Speter case O_ARGV: 939768Speter if (argc != 2) { 940768Speter error("argv takes two arguments"); 941768Speter return; 942768Speter } 943*14740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 944*14740Sthien if (ap == NLNIL) 945768Speter return; 946768Speter if (isnta(ap, "i")) { 947768Speter error("argv's first argument must be an integer, not %s", nameof(ap)); 948768Speter return; 949768Speter } 950*14740Sthien al = argv->list_node.next; 951*14740Sthien ap = stklval(al->list_node.list, MOD|NOUSE); 952*14740Sthien if (ap == NLNIL) 953768Speter return; 954768Speter if (classify(ap) != TSTR) { 955768Speter error("argv's second argument must be a string, not %s", nameof(ap)); 956768Speter return; 957768Speter } 958*14740Sthien (void) put(2, op, width(ap)); 959768Speter return; 960768Speter 961768Speter case O_STLIM: 962768Speter if (argc != 1) { 963768Speter error("stlimit requires one argument"); 964768Speter return; 965768Speter } 966*14740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 967*14740Sthien if (ap == NLNIL) 968768Speter return; 969768Speter if (isnta(ap, "i")) { 970768Speter error("stlimit's argument must be an integer, not %s", nameof(ap)); 971768Speter return; 972768Speter } 973768Speter if (width(ap) != 4) 974*14740Sthien (void) put(1, O_STOI); 975*14740Sthien (void) put(1, op); 976768Speter return; 977768Speter 978768Speter case O_REMOVE: 979768Speter if (argc != 1) { 980768Speter error("remove expects one argument"); 981768Speter return; 982768Speter } 9832073Smckusic codeoff(); 984*14740Sthien ap = stkrval(argv->list_node.list, (struct nl *) NOFLAGS, 985*14740Sthien (long) RREQ ); 9862073Smckusic codeon(); 987*14740Sthien if (ap == NLNIL) 988768Speter return; 989768Speter if (classify(ap) != TSTR) { 990768Speter error("remove's argument must be a string, not %s", nameof(ap)); 991768Speter return; 992768Speter } 993*14740Sthien (void) put(2, O_CON24, width(ap)); 994*14740Sthien ap = stkrval(argv->list_node.list, (struct nl *) NOFLAGS, 995*14740Sthien (long) RREQ ); 996*14740Sthien (void) put(1, op); 997768Speter return; 998768Speter 999768Speter case O_LLIMIT: 1000768Speter if (argc != 2) { 1001768Speter error("linelimit expects two arguments"); 1002768Speter return; 1003768Speter } 1004*14740Sthien al = argv->list_node.next; 1005*14740Sthien ap = stkrval(al->list_node.list, NLNIL , (long) RREQ ); 1006768Speter if (ap == NIL) 1007768Speter return; 1008768Speter if (isnta(ap, "i")) { 1009768Speter error("linelimit's second argument must be an integer, not %s", nameof(ap)); 1010768Speter return; 1011768Speter } 1012*14740Sthien ap = stklval(argv->list_node.list, NOFLAGS|NOUSE); 1013*14740Sthien if (ap == NLNIL) 10142073Smckusic return; 10152073Smckusic if (!text(ap)) { 10162073Smckusic error("linelimit's first argument must be a text file, not %s", nameof(ap)); 10172073Smckusic return; 10182073Smckusic } 1019*14740Sthien (void) put(1, op); 1020768Speter return; 1021768Speter case O_PAGE: 1022768Speter if (argc != 1) { 1023768Speter error("page expects one argument"); 1024768Speter return; 1025768Speter } 1026*14740Sthien ap = stklval(argv->list_node.list, NIL ); 1027*14740Sthien if (ap == NLNIL) 1028768Speter return; 1029768Speter if (!text(ap)) { 1030768Speter error("Argument to page must be a text file, not %s", nameof(ap)); 1031768Speter return; 1032768Speter } 1033*14740Sthien (void) put(1, O_UNIT); 1034*14740Sthien (void) put(1, op); 1035768Speter return; 1036768Speter 10377928Smckusick case O_ASRT: 10387928Smckusick if (!opt('t')) 10397928Smckusick return; 10407928Smckusick if (argc == 0 || argc > 2) { 10417928Smckusick error("Assert expects one or two arguments"); 10427928Smckusick return; 10437928Smckusick } 10447928Smckusick if (argc == 2) { 10457928Smckusick /* 10467928Smckusick * Optional second argument is a string specifying 10477928Smckusick * why the assertion failed. 10487928Smckusick */ 1049*14740Sthien al = argv->list_node.next; 1050*14740Sthien al1 = stkrval(al->list_node.list, NLNIL , (long) RREQ ); 1051*14740Sthien if (al1 == NIL) 10527928Smckusick return; 1053*14740Sthien if (classify(al1) != TSTR) { 1054*14740Sthien error("Second argument to assert must be a string, not %s", nameof(al1)); 10557928Smckusick return; 10567928Smckusick } 10577928Smckusick } else { 1058*14740Sthien (void) put(2, PTR_CON, NIL); 10597928Smckusick } 1060*14740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 10617928Smckusick if (ap == NIL) 10627928Smckusick return; 10637928Smckusick if (isnta(ap, "b")) 10647928Smckusick error("Assert expression must be Boolean, not %ss", nameof(ap)); 1065*14740Sthien (void) put(1, O_ASRT); 10667928Smckusick return; 10677928Smckusick 1068768Speter case O_PACK: 1069768Speter if (argc != 3) { 1070768Speter error("pack expects three arguments"); 1071768Speter return; 1072768Speter } 1073768Speter pu = "pack(a,i,z)"; 1074*14740Sthien pua = argv->list_node.list; 1075*14740Sthien al = argv->list_node.next; 1076*14740Sthien pui = al->list_node.list; 1077*14740Sthien alv = al->list_node.next; 1078*14740Sthien puz = alv->list_node.list; 1079768Speter goto packunp; 1080768Speter case O_UNPACK: 1081768Speter if (argc != 3) { 1082768Speter error("unpack expects three arguments"); 1083768Speter return; 1084768Speter } 1085768Speter pu = "unpack(z,a,i)"; 1086*14740Sthien puz = argv->list_node.list; 1087*14740Sthien al = argv->list_node.next; 1088*14740Sthien pua = al->list_node.list; 1089*14740Sthien alv = al->list_node.next; 1090*14740Sthien pui = alv->list_node.list; 1091768Speter packunp: 10922073Smckusic codeoff(); 1093768Speter ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 1094*14740Sthien al1 = stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 10952073Smckusic codeon(); 1096768Speter if (ap == NIL) 1097768Speter return; 1098768Speter if (ap->class != ARRAY) { 1099768Speter error("%s requires a to be an unpacked array, not %s", pu, nameof(ap)); 1100768Speter return; 1101768Speter } 1102*14740Sthien if (al1->class != ARRAY) { 1103768Speter error("%s requires z to be a packed array, not %s", pu, nameof(ap)); 1104768Speter return; 1105768Speter } 1106*14740Sthien if (al1->type == NIL || ap->type == NIL) 1107768Speter return; 1108*14740Sthien if (al1->type != ap->type) { 1109768Speter error("%s requires a and z to be arrays of the same type", pu, nameof(ap)); 1110768Speter return; 1111768Speter } 1112*14740Sthien k = width(al1); 1113768Speter itemwidth = width(ap->type); 1114768Speter ap = ap->chain; 1115*14740Sthien al1 = al1->chain; 1116*14740Sthien if (ap->chain != NIL || al1->chain != NIL) { 1117768Speter error("%s requires a and z to be single dimension arrays", pu); 1118768Speter return; 1119768Speter } 1120*14740Sthien if (ap == NIL || al1 == NIL) 1121768Speter return; 1122768Speter /* 1123*14740Sthien * al1 is the range for z i.e. u..v 1124768Speter * ap is the range for a i.e. m..n 1125768Speter * i will be n-m+1 1126768Speter * j will be v-u+1 1127768Speter */ 1128768Speter i = ap->range[1] - ap->range[0] + 1; 1129*14740Sthien j = al1->range[1] - al1->range[0] + 1; 1130768Speter if (i < j) { 1131*14740Sthien error("%s cannot have more elements in a (%d) than in z (%d)", pu, (char *) j, (char *) i); 1132768Speter return; 1133768Speter } 1134768Speter /* 1135768Speter * get n-m-(v-u) and m for the interpreter 1136768Speter */ 1137768Speter i -= j; 1138768Speter j = ap->range[0]; 1139*14740Sthien (void) put(2, O_CON24, k); 1140*14740Sthien (void) put(2, O_CON24, i); 1141*14740Sthien (void) put(2, O_CON24, j); 1142*14740Sthien (void) put(2, O_CON24, itemwidth); 1143*14740Sthien al1 = stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 11442073Smckusic ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 1145*14740Sthien ap = stkrval(pui, NLNIL , (long) RREQ ); 11462073Smckusic if (ap == NIL) 11472073Smckusic return; 1148*14740Sthien (void) put(1, op); 1149768Speter return; 1150768Speter case 0: 11517928Smckusick error("%s is an unimplemented extension", p->symbol); 1152768Speter return; 1153768Speter 1154768Speter default: 1155768Speter panic("proc case"); 1156768Speter } 1157768Speter } 1158768Speter #endif OBJ 1159