1*22186Sdist /* 2*22186Sdist * Copyright (c) 1980 Regents of the University of California. 3*22186Sdist * All rights reserved. The Berkeley software License Agreement 4*22186Sdist * specifies the terms and conditions for redistribution. 5*22186Sdist */ 6768Speter 714740Sthien #ifndef lint 8*22186Sdist static char sccsid[] = "@(#)proc.c 5.1 (Berkeley) 06/05/85"; 9*22186Sdist #endif not lint 10768Speter 11768Speter #include "whoami.h" 12768Speter #ifdef OBJ 13768Speter /* 14768Speter * and the rest of the file 15768Speter */ 16768Speter #include "0.h" 17768Speter #include "tree.h" 18768Speter #include "opcode.h" 19768Speter #include "objfmt.h" 2011327Speter #include "tmps.h" 2114740Sthien #include "tree_ty.h" 22768Speter 23768Speter /* 2411882Smckusick * The constant EXPOSIZE specifies the number of digits in the exponent 2511882Smckusick * of real numbers. 2611882Smckusick * 279230Smckusick * The constant REALSPC defines the amount of forced padding preceeding 289230Smckusick * real numbers when they are printed. If REALSPC == 0, then no padding 299230Smckusick * is added, REALSPC == 1 adds one extra blank irregardless of the width 309230Smckusick * specified by the user. 319230Smckusick * 329230Smckusick * N.B. - Values greater than one require program mods. 339230Smckusick */ 3411882Smckusick #define EXPOSIZE 2 3511882Smckusick #define REALSPC 0 369230Smckusick 379230Smckusick /* 38768Speter * The following array is used to determine which classes may be read 39768Speter * from textfiles. It is indexed by the return value from classify. 40768Speter */ 41768Speter #define rdops(x) rdxxxx[(x)-(TFIRST)] 42768Speter 43768Speter int rdxxxx[] = { 44768Speter 0, /* -7 file types */ 45768Speter 0, /* -6 record types */ 46768Speter 0, /* -5 array types */ 47768Speter O_READE, /* -4 scalar types */ 48768Speter 0, /* -3 pointer types */ 49768Speter 0, /* -2 set types */ 50768Speter 0, /* -1 string types */ 51768Speter 0, /* 0 nil, no type */ 52768Speter O_READE, /* 1 boolean */ 53768Speter O_READC, /* 2 character */ 54768Speter O_READ4, /* 3 integer */ 55768Speter O_READ8 /* 4 real */ 56768Speter }; 57768Speter 58768Speter /* 59768Speter * Proc handles procedure calls. 60768Speter * Non-builtin procedures are "buck-passed" to func (with a flag 61768Speter * indicating that they are actually procedures. 62768Speter * builtin procedures are handled here. 63768Speter */ 64768Speter proc(r) 6514740Sthien struct tnode *r; 66768Speter { 67768Speter register struct nl *p; 6814740Sthien register struct tnode *alv, *al; 6914740Sthien register int op; 7014740Sthien struct nl *filetype, *ap, *al1; 7114740Sthien int argc, typ, fmtspec, strfmt, stkcnt; 7214740Sthien struct tnode *argv; 7314740Sthien char fmt, format[20], *strptr, *pu; 7414740Sthien int prec, field, strnglen, fmtlen, fmtstart; 7514740Sthien struct tnode *pua, *pui, *puz, *file; 76768Speter int i, j, k; 77768Speter int itemwidth; 783226Smckusic struct tmps soffset; 793851Speter struct nl *tempnlp; 80768Speter 81768Speter #define CONPREC 4 82768Speter #define VARPREC 8 83768Speter #define CONWIDTH 1 84768Speter #define VARWIDTH 2 85768Speter #define SKIP 16 86768Speter 87768Speter /* 88768Speter * Verify that the name is 89768Speter * defined and is that of a 90768Speter * procedure. 91768Speter */ 9214740Sthien p = lookup(r->pcall_node.proc_id); 93768Speter if (p == NIL) { 9414740Sthien rvlist(r->pcall_node.arg); 95768Speter return; 96768Speter } 971198Speter if (p->class != PROC && p->class != FPROC) { 98768Speter error("Can't call %s, its %s not a procedure", p->symbol, classes[p->class]); 9914740Sthien rvlist(r->pcall_node.arg); 100768Speter return; 101768Speter } 10214740Sthien argv = r->pcall_node.arg; 103768Speter 104768Speter /* 105768Speter * Call handles user defined 106768Speter * procedures and functions. 107768Speter */ 108768Speter if (bn != 0) { 10914740Sthien (void) call(p, argv, PROC, bn); 110768Speter return; 111768Speter } 112768Speter 113768Speter /* 114768Speter * Call to built-in procedure. 115768Speter * Count the arguments. 116768Speter */ 117768Speter argc = 0; 11814740Sthien for (al = argv; al != TR_NIL; al = al->list_node.next) 119768Speter argc++; 120768Speter 121768Speter /* 122768Speter * Switch on the operator 123768Speter * associated with the built-in 124768Speter * procedure in the namelist 125768Speter */ 126768Speter op = p->value[0] &~ NSTAND; 127768Speter if (opt('s') && (p->value[0] & NSTAND)) { 128768Speter standard(); 129768Speter error("%s is a nonstandard procedure", p->symbol); 130768Speter } 131768Speter switch (op) { 132768Speter 133768Speter case O_ABORT: 134768Speter if (argc != 0) 135768Speter error("null takes no arguments"); 136768Speter return; 137768Speter 138768Speter case O_FLUSH: 139768Speter if (argc == 0) { 14014740Sthien (void) put(1, O_MESSAGE); 141768Speter return; 142768Speter } 143768Speter if (argc != 1) { 144768Speter error("flush takes at most one argument"); 145768Speter return; 146768Speter } 14714740Sthien ap = stklval(argv->list_node.list, NIL ); 14814740Sthien if (ap == NLNIL) 149768Speter return; 150768Speter if (ap->class != FILET) { 151768Speter error("flush's argument must be a file, not %s", nameof(ap)); 152768Speter return; 153768Speter } 15414740Sthien (void) put(1, op); 155768Speter return; 156768Speter 157768Speter case O_MESSAGE: 158768Speter case O_WRITEF: 159768Speter case O_WRITLN: 160768Speter /* 161768Speter * Set up default file "output"'s type 162768Speter */ 163768Speter file = NIL; 164768Speter filetype = nl+T1CHAR; 165768Speter /* 166768Speter * Determine the file implied 167768Speter * for the write and generate 168768Speter * code to make it the active file. 169768Speter */ 170768Speter if (op == O_MESSAGE) { 171768Speter /* 172768Speter * For message, all that matters 173768Speter * is that the filetype is 174768Speter * a character file. 175768Speter * Thus "output" will suit us fine. 176768Speter */ 17714740Sthien (void) put(1, O_MESSAGE); 17814740Sthien } else if (argv != TR_NIL && (al = argv->list_node.list)->tag != 17914740Sthien T_WEXP) { 180768Speter /* 181768Speter * If there is a first argument which has 182768Speter * no write widths, then it is potentially 183768Speter * a file name. 184768Speter */ 185768Speter codeoff(); 18614740Sthien ap = stkrval(argv->list_node.list, NLNIL, (long) RREQ ); 187768Speter codeon(); 18814740Sthien if (ap == NLNIL) 18914740Sthien argv = argv->list_node.next; 19014740Sthien if (ap != NLNIL && ap->class == FILET) { 191768Speter /* 192768Speter * Got "write(f, ...", make 193768Speter * f the active file, and save 194768Speter * it and its type for use in 195768Speter * processing the rest of the 196768Speter * arguments to write. 197768Speter */ 19814740Sthien file = argv->list_node.list; 199768Speter filetype = ap->type; 20014740Sthien (void) stklval(argv->list_node.list, NIL ); 20114740Sthien (void) put(1, O_UNIT); 202768Speter /* 203768Speter * Skip over the first argument 204768Speter */ 20514740Sthien argv = argv->list_node.next; 206768Speter argc--; 2078538Speter } else { 208768Speter /* 209768Speter * Set up for writing on 210768Speter * standard output. 211768Speter */ 21214740Sthien (void) put(1, O_UNITOUT); 2137953Speter output->nl_flags |= NUSED; 2148538Speter } 2158538Speter } else { 21614740Sthien (void) put(1, O_UNITOUT); 2177953Speter output->nl_flags |= NUSED; 2188538Speter } 219768Speter /* 220768Speter * Loop and process each 221768Speter * of the arguments. 222768Speter */ 22314740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 224768Speter /* 225768Speter * fmtspec indicates the type (CONstant or VARiable) 226768Speter * and number (none, WIDTH, and/or PRECision) 227768Speter * of the fields in the printf format for this 228768Speter * output variable. 2293172Smckusic * stkcnt is the number of bytes pushed on the stack 230768Speter * fmt is the format output indicator (D, E, F, O, X, S) 231768Speter * fmtstart = 0 for leading blank; = 1 for no blank 232768Speter */ 233768Speter fmtspec = NIL; 234768Speter stkcnt = 0; 235768Speter fmt = 'D'; 236768Speter fmtstart = 1; 23714740Sthien al = argv->list_node.list; 23814740Sthien if (al == TR_NIL) 239768Speter continue; 24014740Sthien if (al->tag == T_WEXP) 24114740Sthien alv = al->wexpr_node.expr1; 242768Speter else 243768Speter alv = al; 24414740Sthien if (alv == TR_NIL) 245768Speter continue; 246768Speter codeoff(); 24714740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 248768Speter codeon(); 24914740Sthien if (ap == NLNIL) 250768Speter continue; 251768Speter typ = classify(ap); 25214740Sthien if (al->tag == T_WEXP) { 253768Speter /* 254768Speter * Handle width expressions. 255768Speter * The basic game here is that width 256768Speter * expressions get evaluated. If they 257768Speter * are constant, the value is placed 258768Speter * directly in the format string. 259768Speter * Otherwise the value is pushed onto 260768Speter * the stack and an indirection is 261768Speter * put into the format string. 262768Speter */ 26314740Sthien if (al->wexpr_node.expr3 == 26414740Sthien (struct tnode *) OCT) 265768Speter fmt = 'O'; 26614740Sthien else if (al->wexpr_node.expr3 == 26714740Sthien (struct tnode *) HEX) 268768Speter fmt = 'X'; 26914740Sthien else if (al->wexpr_node.expr3 != TR_NIL) { 270768Speter /* 271768Speter * Evaluate second format spec 272768Speter */ 27314740Sthien if ( constval(al->wexpr_node.expr3) 274768Speter && isa( con.ctype , "i" ) ) { 275768Speter fmtspec += CONPREC; 276768Speter prec = con.crval; 277768Speter } else { 278768Speter fmtspec += VARPREC; 279768Speter } 280768Speter fmt = 'f'; 281768Speter switch ( typ ) { 282768Speter case TINT: 283768Speter if ( opt( 's' ) ) { 284768Speter standard(); 285768Speter error("Writing %ss with two write widths is non-standard", clnames[typ]); 286768Speter } 287768Speter /* and fall through */ 288768Speter case TDOUBLE: 289768Speter break; 290768Speter default: 291768Speter error("Cannot write %ss with two write widths", clnames[typ]); 292768Speter continue; 293768Speter } 294768Speter } 295768Speter /* 296768Speter * Evaluate first format spec 297768Speter */ 29814740Sthien if (al->wexpr_node.expr2 != TR_NIL) { 29914740Sthien if ( constval(al->wexpr_node.expr2) 300768Speter && isa( con.ctype , "i" ) ) { 301768Speter fmtspec += CONWIDTH; 302768Speter field = con.crval; 303768Speter } else { 304768Speter fmtspec += VARWIDTH; 305768Speter } 306768Speter } 307768Speter if ((fmtspec & CONPREC) && prec < 0 || 308768Speter (fmtspec & CONWIDTH) && field < 0) { 309768Speter error("Negative widths are not allowed"); 310768Speter continue; 311768Speter } 3123179Smckusic if ( opt('s') && 3133179Smckusic ((fmtspec & CONPREC) && prec == 0 || 3143179Smckusic (fmtspec & CONWIDTH) && field == 0)) { 3153179Smckusic standard(); 3163179Smckusic error("Zero widths are non-standard"); 3173179Smckusic } 318768Speter } 319768Speter if (filetype != nl+T1CHAR) { 320768Speter if (fmt == 'O' || fmt == 'X') { 321768Speter error("Oct/hex allowed only on text files"); 322768Speter continue; 323768Speter } 324768Speter if (fmtspec) { 325768Speter error("Write widths allowed only on text files"); 326768Speter continue; 327768Speter } 328768Speter /* 329768Speter * Generalized write, i.e. 330768Speter * to a non-textfile. 331768Speter */ 33214740Sthien (void) stklval(file, NIL ); 33314740Sthien (void) put(1, O_FNIL); 334768Speter /* 335768Speter * file^ := ... 336768Speter */ 33714740Sthien ap = rvalue(argv->list_node.list, NLNIL, LREQ); 33814740Sthien if (ap == NLNIL) 339768Speter continue; 34014740Sthien if (incompat(ap, filetype, 34114740Sthien argv->list_node.list)) { 342768Speter cerror("Type mismatch in write to non-text file"); 343768Speter continue; 344768Speter } 345768Speter convert(ap, filetype); 34614740Sthien (void) put(2, O_AS, width(filetype)); 347768Speter /* 348768Speter * put(file) 349768Speter */ 35014740Sthien (void) put(1, O_PUT); 351768Speter continue; 352768Speter } 353768Speter /* 354768Speter * Write to a textfile 355768Speter * 356768Speter * Evaluate the expression 357768Speter * to be written. 358768Speter */ 359768Speter if (fmt == 'O' || fmt == 'X') { 360768Speter if (opt('s')) { 361768Speter standard(); 362768Speter error("Oct and hex are non-standard"); 363768Speter } 364768Speter if (typ == TSTR || typ == TDOUBLE) { 365768Speter error("Can't write %ss with oct/hex", clnames[typ]); 366768Speter continue; 367768Speter } 368768Speter if (typ == TCHAR || typ == TBOOL) 369768Speter typ = TINT; 370768Speter } 371768Speter /* 372768Speter * Place the arguement on the stack. If there is 373768Speter * no format specified by the programmer, implement 374768Speter * the default. 375768Speter */ 376768Speter switch (typ) { 3776542Smckusick case TPTR: 3786542Smckusick warning(); 3796542Smckusick if (opt('s')) { 3806542Smckusick standard(); 3816542Smckusick } 3826542Smckusick error("Writing %ss to text files is non-standard", 3836542Smckusick clnames[typ]); 3846542Smckusick /* and fall through */ 385768Speter case TINT: 386768Speter if (fmt != 'f') { 38714740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 3883172Smckusic stkcnt += sizeof(long); 389768Speter } else { 39014740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 39114740Sthien (void) put(1, O_ITOD); 3923172Smckusic stkcnt += sizeof(double); 393768Speter typ = TDOUBLE; 394768Speter goto tdouble; 395768Speter } 396768Speter if (fmtspec == NIL) { 397768Speter if (fmt == 'D') 398768Speter field = 10; 399768Speter else if (fmt == 'X') 400768Speter field = 8; 401768Speter else if (fmt == 'O') 402768Speter field = 11; 403768Speter else 404768Speter panic("fmt1"); 405768Speter fmtspec = CONWIDTH; 406768Speter } 407768Speter break; 408768Speter case TCHAR: 409768Speter tchar: 4102073Smckusic if (fmtspec == NIL) { 41114740Sthien (void) put(1, O_FILE); 41214740Sthien ap = stkrval(alv, NLNIL, (long) RREQ ); 4133172Smckusic convert(nl + T4INT, INT_TYP); 41414740Sthien (void) put(2, O_WRITEC, 4153172Smckusic sizeof(char *) + sizeof(int)); 4162073Smckusic fmtspec = SKIP; 4172073Smckusic break; 4182073Smckusic } 41914740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 4203172Smckusic convert(nl + T4INT, INT_TYP); 4213172Smckusic stkcnt += sizeof(int); 422768Speter fmt = 'c'; 423768Speter break; 424768Speter case TSCAL: 4251628Speter warning(); 426768Speter if (opt('s')) { 427768Speter standard(); 428768Speter } 4296542Smckusick error("Writing %ss to text files is non-standard", 4306542Smckusick clnames[typ]); 4316542Smckusick /* and fall through */ 432768Speter case TBOOL: 43314740Sthien (void) stkrval(alv, NLNIL , (long) RREQ ); 43414740Sthien (void) put(2, O_NAM, (long)listnames(ap)); 4353172Smckusic stkcnt += sizeof(char *); 436768Speter fmt = 's'; 437768Speter break; 438768Speter case TDOUBLE: 43914740Sthien ap = stkrval(alv, (struct nl *) TDOUBLE , (long) RREQ ); 4403172Smckusic stkcnt += sizeof(double); 441768Speter tdouble: 442768Speter switch (fmtspec) { 443768Speter case NIL: 44411882Smckusick field = 14 + (5 + EXPOSIZE); 44511882Smckusick prec = field - (5 + EXPOSIZE); 4463076Smckusic fmt = 'e'; 447768Speter fmtspec = CONWIDTH + CONPREC; 448768Speter break; 449768Speter case CONWIDTH: 4509230Smckusick field -= REALSPC; 4519230Smckusick if (field < 1) 452768Speter field = 1; 45311882Smckusick prec = field - (5 + EXPOSIZE); 454768Speter if (prec < 1) 455768Speter prec = 1; 456768Speter fmtspec += CONPREC; 4573076Smckusic fmt = 'e'; 458768Speter break; 459768Speter case CONWIDTH + CONPREC: 460768Speter case CONWIDTH + VARPREC: 4619230Smckusick field -= REALSPC; 4629230Smckusick if (field < 1) 463768Speter field = 1; 464768Speter } 465768Speter format[0] = ' '; 4669230Smckusick fmtstart = 1 - REALSPC; 467768Speter break; 468768Speter case TSTR: 46914740Sthien (void) constval( alv ); 470768Speter switch ( classify( con.ctype ) ) { 471768Speter case TCHAR: 472768Speter typ = TCHAR; 473768Speter goto tchar; 474768Speter case TSTR: 475768Speter strptr = con.cpval; 476768Speter for (strnglen = 0; *strptr++; strnglen++) /* void */; 477768Speter strptr = con.cpval; 478768Speter break; 479768Speter default: 480768Speter strnglen = width(ap); 481768Speter break; 482768Speter } 483768Speter fmt = 's'; 484768Speter strfmt = fmtspec; 485768Speter if (fmtspec == NIL) { 486768Speter fmtspec = SKIP; 487768Speter break; 488768Speter } 489768Speter if (fmtspec & CONWIDTH) { 490768Speter if (field <= strnglen) { 491768Speter fmtspec = SKIP; 492768Speter break; 493768Speter } else 494768Speter field -= strnglen; 495768Speter } 496768Speter /* 497768Speter * push string to implement leading blank padding 498768Speter */ 49914740Sthien (void) put(2, O_LVCON, 2); 500768Speter putstr("", 0); 5013172Smckusic stkcnt += sizeof(char *); 502768Speter break; 503768Speter default: 504768Speter error("Can't write %ss to a text file", clnames[typ]); 505768Speter continue; 506768Speter } 507768Speter /* 508768Speter * If there is a variable precision, evaluate it onto 509768Speter * the stack 510768Speter */ 511768Speter if (fmtspec & VARPREC) { 51214740Sthien ap = stkrval(al->wexpr_node.expr3, NLNIL , 51314740Sthien (long) RREQ ); 514768Speter if (ap == NIL) 515768Speter continue; 516768Speter if (isnta(ap,"i")) { 517768Speter error("Second write width must be integer, not %s", nameof(ap)); 518768Speter continue; 519768Speter } 520768Speter if ( opt( 't' ) ) { 52114740Sthien (void) put(3, O_MAX, 0, 0); 522768Speter } 5233172Smckusic convert(nl+T4INT, INT_TYP); 5243172Smckusic stkcnt += sizeof(int); 525768Speter } 526768Speter /* 527768Speter * If there is a variable width, evaluate it onto 528768Speter * the stack 529768Speter */ 530768Speter if (fmtspec & VARWIDTH) { 531768Speter if ( ( typ == TDOUBLE && fmtspec == VARWIDTH ) 532768Speter || typ == TSTR ) { 5333226Smckusic soffset = sizes[cbn].curtmps; 53414740Sthien tempnlp = tmpalloc((long) (sizeof(long)), 5353226Smckusic nl+T4INT, REGOK); 53614740Sthien (void) put(2, O_LV | cbn << 8 + INDX, 5373851Speter tempnlp -> value[ NL_OFFS ] ); 538768Speter } 53914740Sthien ap = stkrval(al->wexpr_node.expr2, NLNIL, (long) RREQ ); 540768Speter if (ap == NIL) 541768Speter continue; 542768Speter if (isnta(ap,"i")) { 543768Speter error("First write width must be integer, not %s", nameof(ap)); 544768Speter continue; 545768Speter } 546768Speter /* 547768Speter * Perform special processing on widths based 548768Speter * on data type 549768Speter */ 550768Speter switch (typ) { 551768Speter case TDOUBLE: 552768Speter if (fmtspec == VARWIDTH) { 5533076Smckusic fmt = 'e'; 55414740Sthien (void) put(1, O_AS4); 55514740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5563851Speter tempnlp -> value[NL_OFFS] ); 55714740Sthien (void) put(3, O_MAX, 55811882Smckusick 5 + EXPOSIZE + REALSPC, 1); 5593172Smckusic convert(nl+T4INT, INT_TYP); 5603172Smckusic stkcnt += sizeof(int); 56114740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5623851Speter tempnlp->value[NL_OFFS] ); 563768Speter fmtspec += VARPREC; 5643226Smckusic tmpfree(&soffset); 565768Speter } 56614740Sthien (void) put(3, O_MAX, REALSPC, 1); 567768Speter break; 568768Speter case TSTR: 56914740Sthien (void) put(1, O_AS4); 57014740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX, 5713851Speter tempnlp -> value[ NL_OFFS ] ); 57214740Sthien (void) put(3, O_MAX, strnglen, 0); 573768Speter break; 574768Speter default: 575768Speter if ( opt( 't' ) ) { 57614740Sthien (void) put(3, O_MAX, 0, 0); 577768Speter } 578768Speter break; 579768Speter } 5803172Smckusic convert(nl+T4INT, INT_TYP); 5813172Smckusic stkcnt += sizeof(int); 582768Speter } 583768Speter /* 584768Speter * Generate the format string 585768Speter */ 586768Speter switch (fmtspec) { 587768Speter default: 588768Speter panic("fmt2"); 589768Speter case SKIP: 590768Speter break; 5912073Smckusic case NIL: 5922073Smckusic sprintf(&format[1], "%%%c", fmt); 5932073Smckusic goto fmtgen; 594768Speter case CONWIDTH: 5953076Smckusic sprintf(&format[1], "%%%d%c", field, fmt); 596768Speter goto fmtgen; 597768Speter case VARWIDTH: 598768Speter sprintf(&format[1], "%%*%c", fmt); 599768Speter goto fmtgen; 600768Speter case CONWIDTH + CONPREC: 6013076Smckusic sprintf(&format[1], "%%%d.%d%c", field, prec, fmt); 602768Speter goto fmtgen; 603768Speter case CONWIDTH + VARPREC: 6043076Smckusic sprintf(&format[1], "%%%d.*%c", field, fmt); 605768Speter goto fmtgen; 606768Speter case VARWIDTH + CONPREC: 6073076Smckusic sprintf(&format[1], "%%*.%d%c", prec, fmt); 608768Speter goto fmtgen; 609768Speter case VARWIDTH + VARPREC: 610768Speter sprintf(&format[1], "%%*.*%c", fmt); 611768Speter fmtgen: 612768Speter fmtlen = lenstr(&format[fmtstart], 0); 61314740Sthien (void) put(2, O_LVCON, fmtlen); 614768Speter putstr(&format[fmtstart], 0); 61514740Sthien (void) put(1, O_FILE); 6163172Smckusic stkcnt += 2 * sizeof(char *); 61714740Sthien (void) put(2, O_WRITEF, stkcnt); 618768Speter } 619768Speter /* 620768Speter * Write the string after its blank padding 621768Speter */ 622768Speter if (typ == TSTR) { 62314740Sthien (void) put(1, O_FILE); 62414740Sthien (void) put(2, CON_INT, 1); 625768Speter if (strfmt & VARWIDTH) { 62614740Sthien (void) put(2, O_RV4 | cbn << 8 + INDX , 6273851Speter tempnlp -> value[ NL_OFFS ] ); 62814740Sthien (void) put(2, O_MIN, strnglen); 6293172Smckusic convert(nl+T4INT, INT_TYP); 6303226Smckusic tmpfree(&soffset); 631768Speter } else { 632768Speter if ((fmtspec & SKIP) && 633768Speter (strfmt & CONWIDTH)) { 634768Speter strnglen = field; 635768Speter } 63614740Sthien (void) put(2, CON_INT, strnglen); 637768Speter } 63814740Sthien ap = stkrval(alv, NLNIL , (long) RREQ ); 63914740Sthien (void) put(2, O_WRITES, 6403172Smckusic 2 * sizeof(char *) + 2 * sizeof(int)); 641768Speter } 642768Speter } 643768Speter /* 644768Speter * Done with arguments. 645768Speter * Handle writeln and 646768Speter * insufficent number of args. 647768Speter */ 648768Speter switch (p->value[0] &~ NSTAND) { 649768Speter case O_WRITEF: 650768Speter if (argc == 0) 651768Speter error("Write requires an argument"); 652768Speter break; 653768Speter case O_MESSAGE: 654768Speter if (argc == 0) 655768Speter error("Message requires an argument"); 656768Speter case O_WRITLN: 657768Speter if (filetype != nl+T1CHAR) 658768Speter error("Can't 'writeln' a non text file"); 65914740Sthien (void) put(1, O_WRITLN); 660768Speter break; 661768Speter } 662768Speter return; 663768Speter 664768Speter case O_READ4: 665768Speter case O_READLN: 666768Speter /* 667768Speter * Set up default 668768Speter * file "input". 669768Speter */ 670768Speter file = NIL; 671768Speter filetype = nl+T1CHAR; 672768Speter /* 673768Speter * Determine the file implied 674768Speter * for the read and generate 675768Speter * code to make it the active file. 676768Speter */ 67714740Sthien if (argv != TR_NIL) { 678768Speter codeoff(); 67914740Sthien ap = stkrval(argv->list_node.list, NLNIL, (long) RREQ ); 680768Speter codeon(); 68114740Sthien if (ap == NLNIL) 68214740Sthien argv = argv->list_node.next; 68314740Sthien if (ap != NLNIL && ap->class == FILET) { 684768Speter /* 685768Speter * Got "read(f, ...", make 686768Speter * f the active file, and save 687768Speter * it and its type for use in 688768Speter * processing the rest of the 689768Speter * arguments to read. 690768Speter */ 69114740Sthien file = argv->list_node.list; 692768Speter filetype = ap->type; 69314740Sthien (void) stklval(argv->list_node.list, NIL ); 69414740Sthien (void) put(1, O_UNIT); 69514740Sthien argv = argv->list_node.next; 696768Speter argc--; 697768Speter } else { 698768Speter /* 699768Speter * Default is read from 700768Speter * standard input. 701768Speter */ 70214740Sthien (void) put(1, O_UNITINP); 703768Speter input->nl_flags |= NUSED; 704768Speter } 705768Speter } else { 70614740Sthien (void) put(1, O_UNITINP); 707768Speter input->nl_flags |= NUSED; 708768Speter } 709768Speter /* 710768Speter * Loop and process each 711768Speter * of the arguments. 712768Speter */ 71314740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 714768Speter /* 715768Speter * Get the address of the target 716768Speter * on the stack. 717768Speter */ 71814740Sthien al = argv->list_node.list; 71914740Sthien if (al == TR_NIL) 720768Speter continue; 72114740Sthien if (al->tag != T_VAR) { 722768Speter error("Arguments to %s must be variables, not expressions", p->symbol); 723768Speter continue; 724768Speter } 725768Speter ap = stklval(al, MOD|ASGN|NOUSE); 72614740Sthien if (ap == NLNIL) 727768Speter continue; 728768Speter if (filetype != nl+T1CHAR) { 729768Speter /* 730768Speter * Generalized read, i.e. 731768Speter * from a non-textfile. 732768Speter */ 73314740Sthien if (incompat(filetype, ap, 73414740Sthien argv->list_node.list )) { 735768Speter error("Type mismatch in read from non-text file"); 736768Speter continue; 737768Speter } 738768Speter /* 739768Speter * var := file ^; 740768Speter */ 741768Speter if (file != NIL) 74216417Speter (void) stklval(file, NIL); 743768Speter else /* Magic */ 74416417Speter (void) put(2, PTR_RV, (int)input->value[0]); 74514740Sthien (void) put(1, O_FNIL); 74616417Speter if (isa(filetype, "bcsi")) { 74716417Speter int filewidth = width(filetype); 74816417Speter 74916417Speter switch (filewidth) { 75016417Speter case 4: 75116417Speter (void) put(1, O_IND4); 75216417Speter break; 75316417Speter case 2: 75416417Speter (void) put(1, O_IND2); 75516417Speter break; 75616417Speter case 1: 75716417Speter (void) put(1, O_IND1); 75816417Speter break; 75916417Speter default: 76016417Speter (void) put(2, O_IND, filewidth); 76116417Speter } 76216417Speter convert(filetype, ap); 76316417Speter rangechk(ap, ap); 76416417Speter (void) gen(O_AS2, O_AS2, 76516417Speter filewidth, width(ap)); 76616417Speter } else { 76716417Speter (void) put(2, O_IND, width(filetype)); 76816417Speter convert(filetype, ap); 76916417Speter (void) put(2, O_AS, width(ap)); 77016417Speter } 771768Speter /* 772768Speter * get(file); 773768Speter */ 77414740Sthien (void) put(1, O_GET); 775768Speter continue; 776768Speter } 777768Speter typ = classify(ap); 778768Speter op = rdops(typ); 779768Speter if (op == NIL) { 780768Speter error("Can't read %ss from a text file", clnames[typ]); 781768Speter continue; 782768Speter } 783768Speter if (op != O_READE) 78414740Sthien (void) put(1, op); 785768Speter else { 78614740Sthien (void) put(2, op, (long)listnames(ap)); 7871628Speter warning(); 788768Speter if (opt('s')) { 789768Speter standard(); 790768Speter } 7911628Speter error("Reading scalars from text files is non-standard"); 792768Speter } 793768Speter /* 794768Speter * Data read is on the stack. 795768Speter * Assign it. 796768Speter */ 797768Speter if (op != O_READ8 && op != O_READE) 798768Speter rangechk(ap, op == O_READC ? ap : nl+T4INT); 79914740Sthien (void) gen(O_AS2, O_AS2, width(ap), 800768Speter op == O_READ8 ? 8 : op == O_READ4 ? 4 : 2); 801768Speter } 802768Speter /* 803768Speter * Done with arguments. 804768Speter * Handle readln and 805768Speter * insufficient number of args. 806768Speter */ 807768Speter if (p->value[0] == O_READLN) { 808768Speter if (filetype != nl+T1CHAR) 809768Speter error("Can't 'readln' a non text file"); 81014740Sthien (void) put(1, O_READLN); 811768Speter } 812768Speter else if (argc == 0) 813768Speter error("read requires an argument"); 814768Speter return; 815768Speter 816768Speter case O_GET: 817768Speter case O_PUT: 818768Speter if (argc != 1) { 819768Speter error("%s expects one argument", p->symbol); 820768Speter return; 821768Speter } 82214740Sthien ap = stklval(argv->list_node.list, NIL ); 82314740Sthien if (ap == NLNIL) 824768Speter return; 825768Speter if (ap->class != FILET) { 826768Speter error("Argument to %s must be a file, not %s", p->symbol, nameof(ap)); 827768Speter return; 828768Speter } 82914740Sthien (void) put(1, O_UNIT); 83014740Sthien (void) put(1, op); 831768Speter return; 832768Speter 833768Speter case O_RESET: 834768Speter case O_REWRITE: 835768Speter if (argc == 0 || argc > 2) { 836768Speter error("%s expects one or two arguments", p->symbol); 837768Speter return; 838768Speter } 839768Speter if (opt('s') && argc == 2) { 840768Speter standard(); 841768Speter error("Two argument forms of reset and rewrite are non-standard"); 842768Speter } 8432073Smckusic codeoff(); 84414740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 8452073Smckusic codeon(); 84614740Sthien if (ap == NLNIL) 847768Speter return; 848768Speter if (ap->class != FILET) { 849768Speter error("First argument to %s must be a file, not %s", p->symbol, nameof(ap)); 850768Speter return; 851768Speter } 85214740Sthien (void) put(2, O_CON24, text(ap) ? 0: width(ap->type)); 853768Speter if (argc == 2) { 854768Speter /* 855768Speter * Optional second argument 856768Speter * is a string name of a 857768Speter * UNIX (R) file to be associated. 858768Speter */ 85914740Sthien al = argv->list_node.next; 8602073Smckusic codeoff(); 86114740Sthien al = (struct tnode *) stkrval(al->list_node.list, 86214740Sthien (struct nl *) NOFLAGS , (long) RREQ ); 8632073Smckusic codeon(); 86414740Sthien if (al == TR_NIL) 865768Speter return; 86614740Sthien if (classify((struct nl *) al) != TSTR) { 86714740Sthien error("Second argument to %s must be a string, not %s", p->symbol, nameof((struct nl *) al)); 868768Speter return; 869768Speter } 87014740Sthien (void) put(2, O_CON24, width((struct nl *) al)); 87114740Sthien al = argv->list_node.next; 87214740Sthien al = (struct tnode *) stkrval(al->list_node.list, 87314740Sthien (struct nl *) NOFLAGS , (long) RREQ ); 874768Speter } else { 87514740Sthien (void) put(2, O_CON24, 0); 87614740Sthien (void) put(2, PTR_CON, NIL); 877768Speter } 87814740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 87914740Sthien (void) put(1, op); 880768Speter return; 881768Speter 882768Speter case O_NEW: 883768Speter case O_DISPOSE: 884768Speter if (argc == 0) { 885768Speter error("%s expects at least one argument", p->symbol); 886768Speter return; 887768Speter } 88814740Sthien ap = stklval(argv->list_node.list, 88914740Sthien op == O_NEW ? ( MOD | NOUSE ) : MOD ); 89014740Sthien if (ap == NLNIL) 891768Speter return; 892768Speter if (ap->class != PTR) { 893768Speter error("(First) argument to %s must be a pointer, not %s", p->symbol, nameof(ap)); 894768Speter return; 895768Speter } 896768Speter ap = ap->type; 897768Speter if (ap == NIL) 898768Speter return; 8997966Smckusick if ((ap->nl_flags & NFILES) && op == O_DISPOSE) 9007966Smckusick op = O_DFDISP; 90114740Sthien argv = argv->list_node.next; 90214740Sthien if (argv != TR_NIL) { 903768Speter if (ap->class != RECORD) { 904768Speter error("Record required when specifying variant tags"); 905768Speter return; 906768Speter } 90714740Sthien for (; argv != TR_NIL; argv = argv->list_node.next) { 908768Speter if (ap->ptr[NL_VARNT] == NIL) { 909768Speter error("Too many tag fields"); 910768Speter return; 911768Speter } 91214740Sthien if (!isconst(argv->list_node.list)) { 913768Speter error("Second and successive arguments to %s must be constants", p->symbol); 914768Speter return; 915768Speter } 91614740Sthien gconst(argv->list_node.list); 917768Speter if (con.ctype == NIL) 918768Speter return; 91914740Sthien if (incompat(con.ctype, ( 92014740Sthien ap->ptr[NL_TAG])->type , TR_NIL )) { 921768Speter cerror("Specified tag constant type clashed with variant case selector type"); 922768Speter return; 923768Speter } 924768Speter for (ap = ap->ptr[NL_VARNT]; ap != NIL; ap = ap->chain) 925768Speter if (ap->range[0] == con.crval) 926768Speter break; 927768Speter if (ap == NIL) { 928768Speter error("No variant case label value equals specified constant value"); 929768Speter return; 930768Speter } 931768Speter ap = ap->ptr[NL_VTOREC]; 932768Speter } 933768Speter } 93414740Sthien (void) put(2, op, width(ap)); 935768Speter return; 936768Speter 937768Speter case O_DATE: 938768Speter case O_TIME: 939768Speter if (argc != 1) { 940768Speter error("%s expects one argument", p->symbol); 941768Speter return; 942768Speter } 94314740Sthien ap = stklval(argv->list_node.list, MOD|NOUSE); 94414740Sthien if (ap == NLNIL) 945768Speter return; 946768Speter if (classify(ap) != TSTR || width(ap) != 10) { 947768Speter error("Argument to %s must be a alfa, not %s", p->symbol, nameof(ap)); 948768Speter return; 949768Speter } 95014740Sthien (void) put(1, op); 951768Speter return; 952768Speter 953768Speter case O_HALT: 954768Speter if (argc != 0) { 955768Speter error("halt takes no arguments"); 956768Speter return; 957768Speter } 95814740Sthien (void) put(1, op); 95914740Sthien noreach = TRUE; /* used to be 1 */ 960768Speter return; 961768Speter 962768Speter case O_ARGV: 963768Speter if (argc != 2) { 964768Speter error("argv takes two arguments"); 965768Speter return; 966768Speter } 96714740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 96814740Sthien if (ap == NLNIL) 969768Speter return; 970768Speter if (isnta(ap, "i")) { 971768Speter error("argv's first argument must be an integer, not %s", nameof(ap)); 972768Speter return; 973768Speter } 97414740Sthien al = argv->list_node.next; 97514740Sthien ap = stklval(al->list_node.list, MOD|NOUSE); 97614740Sthien if (ap == NLNIL) 977768Speter return; 978768Speter if (classify(ap) != TSTR) { 979768Speter error("argv's second argument must be a string, not %s", nameof(ap)); 980768Speter return; 981768Speter } 98214740Sthien (void) put(2, op, width(ap)); 983768Speter return; 984768Speter 985768Speter case O_STLIM: 986768Speter if (argc != 1) { 987768Speter error("stlimit requires one argument"); 988768Speter return; 989768Speter } 99014740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 99114740Sthien if (ap == NLNIL) 992768Speter return; 993768Speter if (isnta(ap, "i")) { 994768Speter error("stlimit's argument must be an integer, not %s", nameof(ap)); 995768Speter return; 996768Speter } 997768Speter if (width(ap) != 4) 99814740Sthien (void) put(1, O_STOI); 99914740Sthien (void) put(1, op); 1000768Speter return; 1001768Speter 1002768Speter case O_REMOVE: 1003768Speter if (argc != 1) { 1004768Speter error("remove expects one argument"); 1005768Speter return; 1006768Speter } 10072073Smckusic codeoff(); 100814740Sthien ap = stkrval(argv->list_node.list, (struct nl *) NOFLAGS, 100914740Sthien (long) RREQ ); 10102073Smckusic codeon(); 101114740Sthien if (ap == NLNIL) 1012768Speter return; 1013768Speter if (classify(ap) != TSTR) { 1014768Speter error("remove's argument must be a string, not %s", nameof(ap)); 1015768Speter return; 1016768Speter } 101714740Sthien (void) put(2, O_CON24, width(ap)); 101814740Sthien ap = stkrval(argv->list_node.list, (struct nl *) NOFLAGS, 101914740Sthien (long) RREQ ); 102014740Sthien (void) put(1, op); 1021768Speter return; 1022768Speter 1023768Speter case O_LLIMIT: 1024768Speter if (argc != 2) { 1025768Speter error("linelimit expects two arguments"); 1026768Speter return; 1027768Speter } 102814740Sthien al = argv->list_node.next; 102914740Sthien ap = stkrval(al->list_node.list, NLNIL , (long) RREQ ); 1030768Speter if (ap == NIL) 1031768Speter return; 1032768Speter if (isnta(ap, "i")) { 1033768Speter error("linelimit's second argument must be an integer, not %s", nameof(ap)); 1034768Speter return; 1035768Speter } 103614740Sthien ap = stklval(argv->list_node.list, NOFLAGS|NOUSE); 103714740Sthien if (ap == NLNIL) 10382073Smckusic return; 10392073Smckusic if (!text(ap)) { 10402073Smckusic error("linelimit's first argument must be a text file, not %s", nameof(ap)); 10412073Smckusic return; 10422073Smckusic } 104314740Sthien (void) put(1, op); 1044768Speter return; 1045768Speter case O_PAGE: 1046768Speter if (argc != 1) { 1047768Speter error("page expects one argument"); 1048768Speter return; 1049768Speter } 105014740Sthien ap = stklval(argv->list_node.list, NIL ); 105114740Sthien if (ap == NLNIL) 1052768Speter return; 1053768Speter if (!text(ap)) { 1054768Speter error("Argument to page must be a text file, not %s", nameof(ap)); 1055768Speter return; 1056768Speter } 105714740Sthien (void) put(1, O_UNIT); 105814740Sthien (void) put(1, op); 1059768Speter return; 1060768Speter 10617928Smckusick case O_ASRT: 10627928Smckusick if (!opt('t')) 10637928Smckusick return; 10647928Smckusick if (argc == 0 || argc > 2) { 10657928Smckusick error("Assert expects one or two arguments"); 10667928Smckusick return; 10677928Smckusick } 10687928Smckusick if (argc == 2) { 10697928Smckusick /* 10707928Smckusick * Optional second argument is a string specifying 10717928Smckusick * why the assertion failed. 10727928Smckusick */ 107314740Sthien al = argv->list_node.next; 107414740Sthien al1 = stkrval(al->list_node.list, NLNIL , (long) RREQ ); 107514740Sthien if (al1 == NIL) 10767928Smckusick return; 107714740Sthien if (classify(al1) != TSTR) { 107814740Sthien error("Second argument to assert must be a string, not %s", nameof(al1)); 10797928Smckusick return; 10807928Smckusick } 10817928Smckusick } else { 108214740Sthien (void) put(2, PTR_CON, NIL); 10837928Smckusick } 108414740Sthien ap = stkrval(argv->list_node.list, NLNIL , (long) RREQ ); 10857928Smckusick if (ap == NIL) 10867928Smckusick return; 10877928Smckusick if (isnta(ap, "b")) 10887928Smckusick error("Assert expression must be Boolean, not %ss", nameof(ap)); 108914740Sthien (void) put(1, O_ASRT); 10907928Smckusick return; 10917928Smckusick 1092768Speter case O_PACK: 1093768Speter if (argc != 3) { 1094768Speter error("pack expects three arguments"); 1095768Speter return; 1096768Speter } 1097768Speter pu = "pack(a,i,z)"; 109814740Sthien pua = argv->list_node.list; 109914740Sthien al = argv->list_node.next; 110014740Sthien pui = al->list_node.list; 110114740Sthien alv = al->list_node.next; 110214740Sthien puz = alv->list_node.list; 1103768Speter goto packunp; 1104768Speter case O_UNPACK: 1105768Speter if (argc != 3) { 1106768Speter error("unpack expects three arguments"); 1107768Speter return; 1108768Speter } 1109768Speter pu = "unpack(z,a,i)"; 111014740Sthien puz = argv->list_node.list; 111114740Sthien al = argv->list_node.next; 111214740Sthien pua = al->list_node.list; 111314740Sthien alv = al->list_node.next; 111414740Sthien pui = alv->list_node.list; 1115768Speter packunp: 11162073Smckusic codeoff(); 1117768Speter ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 111814740Sthien al1 = stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 11192073Smckusic codeon(); 1120768Speter if (ap == NIL) 1121768Speter return; 1122768Speter if (ap->class != ARRAY) { 1123768Speter error("%s requires a to be an unpacked array, not %s", pu, nameof(ap)); 1124768Speter return; 1125768Speter } 112614740Sthien if (al1->class != ARRAY) { 1127768Speter error("%s requires z to be a packed array, not %s", pu, nameof(ap)); 1128768Speter return; 1129768Speter } 113014740Sthien if (al1->type == NIL || ap->type == NIL) 1131768Speter return; 113214740Sthien if (al1->type != ap->type) { 1133768Speter error("%s requires a and z to be arrays of the same type", pu, nameof(ap)); 1134768Speter return; 1135768Speter } 113614740Sthien k = width(al1); 1137768Speter itemwidth = width(ap->type); 1138768Speter ap = ap->chain; 113914740Sthien al1 = al1->chain; 114014740Sthien if (ap->chain != NIL || al1->chain != NIL) { 1141768Speter error("%s requires a and z to be single dimension arrays", pu); 1142768Speter return; 1143768Speter } 114414740Sthien if (ap == NIL || al1 == NIL) 1145768Speter return; 1146768Speter /* 114714740Sthien * al1 is the range for z i.e. u..v 1148768Speter * ap is the range for a i.e. m..n 1149768Speter * i will be n-m+1 1150768Speter * j will be v-u+1 1151768Speter */ 1152768Speter i = ap->range[1] - ap->range[0] + 1; 115314740Sthien j = al1->range[1] - al1->range[0] + 1; 1154768Speter if (i < j) { 115514740Sthien error("%s cannot have more elements in a (%d) than in z (%d)", pu, (char *) j, (char *) i); 1156768Speter return; 1157768Speter } 1158768Speter /* 1159768Speter * get n-m-(v-u) and m for the interpreter 1160768Speter */ 1161768Speter i -= j; 1162768Speter j = ap->range[0]; 116314740Sthien (void) put(2, O_CON24, k); 116414740Sthien (void) put(2, O_CON24, i); 116514740Sthien (void) put(2, O_CON24, j); 116614740Sthien (void) put(2, O_CON24, itemwidth); 116714740Sthien al1 = stklval(puz, op == O_UNPACK ? NOFLAGS : MOD|NOUSE); 11682073Smckusic ap = stklval(pua, op == O_PACK ? NOFLAGS : MOD|NOUSE); 116914740Sthien ap = stkrval(pui, NLNIL , (long) RREQ ); 11702073Smckusic if (ap == NIL) 11712073Smckusic return; 117214740Sthien (void) put(1, op); 1173768Speter return; 1174768Speter case 0: 11757928Smckusick error("%s is an unimplemented extension", p->symbol); 1176768Speter return; 1177768Speter 1178768Speter default: 1179768Speter panic("proc case"); 1180768Speter } 1181768Speter } 1182768Speter #endif OBJ 1183