1 /* The builtin functions of REXX/imc         (C) Ian Collier 1992 */
2 
3 #include<stdio.h>
4 #include<string.h>
5 #include<memory.h>
6 #include<unistd.h>
7 #include<stdlib.h>     /* includes bsearch, random */
8 #include<time.h>
9 #include<pwd.h>
10 #include<errno.h>
11 #include<fcntl.h>
12 #include<setjmp.h>
13 #include<sys/types.h>
14 #include<sys/time.h>
15 #ifndef Solaris
16 #include<sys/ioctl.h>
17 #endif
18 #include<sys/param.h>
19 #ifndef FIONREAD
20 #include<sys/filio.h>
21 #endif
22 #include<sys/stat.h>
23 #ifdef HAS_TTYCOM
24 #include<sys/ttycom.h>
25 #else
26 #include<termios.h>
27 #endif
28 #include"const.h"
29 #include"globals.h"
30 #include"functions.h"
31 #define STDIN 0
32 
33 
34 /* How to find the number of buffered bytes in a FILE *. */
35 #ifdef NO_CNT
36 # undef _CNT
37 # define _CNT(x) (0)
38 #endif
39 
40 #ifndef _CNT
41 # ifdef linux
42 #  define _CNT(fp) ((fp)->_IO_read_end - (fp)->_IO_read_ptr)
43 # else
44 #  if defined(__FreeBSD__)
45 #   define _CNT(fp) ((fp)->_r)
46 #  elif defined(__DragonFly__)
47 #   define _CNT(fp) (((struct __FILE_public *)fp)->_r)
48 #  else
49 #   define _CNT(fp) ((fp)->_cnt)
50 #  endif
51 # endif
52 #endif
53 
54 void rxsource();
55 void rxerror();
56 void rxlength();
57 void rxtime();
58 void rxdate();
59 void rxleft();
60 void rxright();
61 void rxstrip();
62 void rxvalue();
63 void rxdatatype();
64 void rxcopies();
65 void rxspace();
66 void rxrange();
67 void c2x(),c2d(),b2x(),b2d(),d2c(),d2b(),d2x(),x2c(),x2d(),x2b();
68 void xbyte();
69 void rxsystem();
70 void rxpos();
71 void rxlastpos();
72 void rxcentre();
73 void rxjustify();
74 void rxsubstr();
75 void rxarg();
76 void rxabbrev();
77 void rxabs();
78 void rxcompare();
79 void rxdelstr();
80 void rxdelword();
81 void rxinsert();
82 void rxmax();
83 void rxmin();
84 void rxoverlay();
85 void rxrandom();
86 void rxreverse();
87 void rxsign();
88 void rxsubword();
89 void rxsymbol();
90 void rxlate();
91 void rxtrunc();
92 void rxverify();
93 void rxword();
94 void rxwordindex();
95 void rxwordlength();
96 void rxwordpos();
97 void rxwords();
98 void rxdigits();
99 void rxfuzz();
100 void rxtrace();
101 void rxform();
102 void rxformat();
103 void rxqueued();
104 void rxlinesize();
105 void rxbitand();
106 void rxbitor();
107 void rxbitxor();
108 void rxuserid();
109 void rxgetcwd();
110 void rxchdir();
111 void rxgetenv();
112 void rxputenv();
113 void rxopen();
114 void rxlinein();
115 void rxlineout();
116 void rxcharin();
117 void rxcharout();
118 void rxchars();
119 void rxlines();
120 void rxchars2();
121 void rxclose();
122 void rxfileno();
123 void rxfdopen();
124 void rxpopen();
125 void rxpclose();
126 void rxftell();
127 void rxstream();
128 void rxaddress();
129 void rxcondition();
130 void rxfuncadd();
131 void rxfuncdrop();
132 void rxfuncquery();
133 
134 int compar();
135 
136 void binrel(); /* The calculator routine which implements binary relations */
137 
138 struct fnlist {char *name;void (*fn)();};
139 
rxfn(name,argc)140 int rxfn(name,argc)   /* does function if possible; returns 1 if successful */
141                       /* Returns -1 if the name was recognised as a math    */
142                       /* function, and 0 if the name was unrecognised.      */
143 char *name;           /* Name of the function to call */
144 int argc;             /* Number of arguments passed to it */
145 {
146    static struct fnlist names[]={   /* The name and address of ever builtin */
147       "ABBREV",     rxabbrev,       /* function, in alphabetical order      */
148       "ABS",        rxabs,
149       "ADDRESS",    rxaddress,
150       "ARG",        rxarg,
151       "B2D",        b2d,
152       "B2X",        b2x,
153       "BITAND",     rxbitand,
154       "BITOR",      rxbitor,
155       "BITXOR",     rxbitxor,
156       "C2D",        c2d,
157       "C2X",        c2x,
158       "CENTER",     rxcentre,
159       "CENTRE",     rxcentre,
160       "CHARIN",     rxcharin,
161       "CHAROUT",    rxcharout,
162       "CHARS",      rxchars,
163       "CHDIR",      rxchdir,
164       "CLOSE",      rxclose,
165       "COMPARE",    rxcompare,
166       "CONDITION",  rxcondition,
167       "COPIES",     rxcopies,
168       "D2B",        d2b,
169       "D2C",        d2c,
170       "D2X",        d2x,
171       "DATATYPE",   rxdatatype,
172       "DATE",       rxdate,
173       "DELSTR",     rxdelstr,
174       "DELWORD",    rxdelword,
175       "DIGITS",     rxdigits,
176       "ERRORTEXT",  rxerror,
177       "FDOPEN",     rxfdopen,
178       "FILENO",     rxfileno,
179       "FORM",       rxform,
180       "FORMAT",     rxformat,
181       "FTELL",      rxftell,
182       "FUZZ",       rxfuzz,
183       "GETCWD",     rxgetcwd,
184       "GETENV",     rxgetenv,
185       "INSERT",     rxinsert,
186       "JUSTIFY",    rxjustify,
187       "LASTPOS",    rxlastpos,
188       "LEFT",       rxleft,
189       "LENGTH",     rxlength,
190       "LINEIN",     rxlinein,
191       "LINEOUT",    rxlineout,
192       "LINES",      rxlines,
193       "LINESIZE",   rxlinesize,
194       "MAX",        rxmax,
195       "MIN",        rxmin,
196       "OPEN",       rxopen,
197       "OVERLAY",    rxoverlay,
198       "PCLOSE",     rxpclose,
199       "POPEN",      rxpopen,
200       "POS",        rxpos,
201       "PUTENV",     rxputenv,
202       "QUEUED",     rxqueued,
203       "RANDOM",     rxrandom,
204       "REVERSE",    rxreverse,
205       "RIGHT",      rxright,
206       "RXFUNCADD",  rxfuncadd,
207       "RXFUNCDROP", rxfuncdrop,
208       "RXFUNCQUERY",rxfuncquery,
209       "SIGN",       rxsign,
210       "SOURCELINE", rxsource,
211       "SPACE",      rxspace,
212       "STREAM",     rxstream,
213       "STRIP",      rxstrip,
214       "SUBSTR",     rxsubstr,
215       "SUBWORD",    rxsubword,
216       "SYMBOL",     rxsymbol,
217       "SYSTEM",     rxsystem,
218       "TIME",       rxtime,
219       "TRACE",      rxtrace,
220       "TRANSLATE",  rxlate,
221       "TRUNC",      rxtrunc,
222       "USERID",     rxuserid,
223       "VALUE",      rxvalue,
224       "VERIFY",     rxverify,
225       "WORD",       rxword,
226       "WORDINDEX",  rxwordindex,
227       "WORDLENGTH", rxwordlength,
228       "WORDPOS",    rxwordpos,
229       "WORDS",      rxwords,
230       "X2B",        x2b,
231       "X2C",        x2c,
232       "X2D",        x2d,
233       "XRANGE",     rxrange
234       };
235 #define nofun 0     /* "nofun" means "this function ain't here" */
236 #define numfun 87   /* The number of builtin functions */
237 
238    struct fnlist test;
239    struct fnlist *ptr;
240    test.name=name; /* Initialise a structure with the candidate name */
241    ptr=(struct fnlist *) /* Search for a builtin function */
242       bsearch((char*)&test,(char*)names,numfun,sizeof(struct fnlist),compar);
243    if(!ptr)return 0;    /* no function recognised */
244    (*(ptr->fn))(argc);  /* Call the builtin function */
245    return 1;            /* Done. */
246 }
247 
compar(s1,s2)248 int compar(s1,s2) /* Compares two items of a function list, */
249 char *s1,*s2;     /* as required by bsearch()               */
250 {
251    return strcmp(((struct fnlist*)s1)->name,((struct fnlist *)s2)->name);
252 }
253 
undelete(l)254 char *undelete(l) /* A utility function like delete(l) except that */
255 int *l;           /* the value isn't deleted from the stack */
256 {
257    char *ptr=cstackptr+ecstackptr-four;
258    (*l)= *(int *)ptr;
259    if(*l>=0)ptr-=align(*l);
260    else ptr=(char *)-1;/* I don't think this is ever used */
261    return ptr;
262 }
263 
264 /* The rest of this file contains the builtin functions listed in the
265    dictionary above.  In general, each function ABC() is implemented by
266    the C routine rxabc().  Each routine takes one parameter - namely
267    the number of arguments passed to the builtin function - and gives no
268    return value.  The arguments and result of the builtin function are
269    passed on the calculator stack.  A null argument (as in abc(x,,y))
270    is represented by a stacked value having length -1. */
271 
rxsource(argc)272 void rxsource(argc) /* souceline() function */
273 int argc;
274 {
275    int i;
276    char *s;
277    if(!argc){
278       stackint(lines); /* the number of source lines */
279       return;
280    }
281    if(argc!=1)die(Ecall);
282    if((i=getint(1))>lines||i<1)die(Erange);
283    s=source[i];
284    stack(s,strlen(s)); /* the ith source line */
285 }
286 
rxerror(argc)287 void rxerror(argc)  /* errortext() function */
288 int argc;
289 {
290    char *msg;
291    if(argc!=1)die(Ecall);
292    msg=message(getint(1));
293    stack(msg,strlen(msg));
294 }
rxlength(argc)295 void rxlength(argc)
296 int argc;
297 {
298    int l;
299    if(argc!=1)die(Ecall);
300    delete(&l);
301    stackint(l);
302 }
303 
304 /* This is used for TIME() with three parameters to collect an input time
305    and convert it into a tm structure for output.  Return 0 -> successful */
rxgettime(type,time,usec)306 static int rxgettime(type, time, usec)
307 char type;
308 struct tm *time;
309 long *usec;
310 {
311    int input;
312    char *string;
313    char ampm[2];
314    int len;
315    int i;
316    char c;
317    time->tm_hour = time->tm_min = time->tm_sec = 0;
318    *usec=0;
319    if (type=='H' || type=='M' || type=='S') {
320       input=getint(1);
321       if (input<0 || input>86400) return -1;
322    } else {
323       string=delete(&len);
324       for (i=0; i<len; i++) if (!string[i]) return -1;
325       string[len]=0;
326    }
327    switch (type) {
328       case 'C':
329          if (sscanf(string,"%2d:%2d%2c%c",&time->tm_hour,&time->tm_min,
330             ampm,&c) != 3) return -1;
331          if (time->tm_hour<1 || time->tm_hour>12) return -1;
332          if (ampm[1]!='m') return -1;
333          switch (ampm[0]) {
334             case 'a':
335                if (time->tm_hour==12) time->tm_hour=0;
336                break;
337             case 'p':
338                if (time->tm_hour!=12) time->tm_hour+=12;
339                break;
340             default: return -1;
341          }
342          break;
343       case 'H': time->tm_hour=input; break;
344       case 'L':
345          if (sscanf(string,"%2d:%2d:%2d.%c",&time->tm_hour,&time->tm_min,
346             &time->tm_sec,&c) !=4) return -1;
347          string=strchr(string,'.');
348          if (!string) return -1;
349          i=100000;
350          while((c=*++string)) {
351             if (c<'0' || c>'9') return -1;
352             *usec+=i*(c-'0');
353             i/=10;
354          }
355          break;
356       case 'M':
357          time->tm_hour=input/60;
358          time->tm_min=input%60;
359          break;
360       case 'N':
361          if (sscanf(string,"%2d:%2d:%2d%c",&time->tm_hour,&time->tm_min,
362             &time->tm_sec,&c) !=3) return -1;
363          break;
364       case 'S':
365          time->tm_hour=input/3600;
366          input=input%3600;
367          time->tm_min=input/60;
368          time->tm_sec=input%60;
369          break;
370       default: return -1;
371    }
372    if (time->tm_hour<0 || time->tm_hour>23 || time->tm_min<0 ||
373       time->tm_min>59 || time->tm_sec<0 || time->tm_sec>59) return -1;
374    return 0;
375 }
376 
rxtime(argc)377 void rxtime(argc)
378 int argc;
379 {
380    struct tm t,*t2;
381    struct timezone tz;
382    char ans[20];
383    char opt='N';
384    char type=0;
385    char *arg;
386    long e1;
387    long e2;
388    int l;
389    long usec;
390 #ifdef DECLARE_TIMEZONE  /* everything except Sun seems to declare this */
391    extern long int timezone;                               /* in time.h */
392 #endif
393    if(!(timeflag&2))
394       gettimeofday(&timestamp,&tz);/* Make a timestamp if necessary */
395    timeflag|=2;
396    if (argc>3) die(Ecall);
397    if (argc>1) {
398       if (argc==3) {
399          arg=delete(&l);
400          if(!l)die(Ecall);
401          type=arg[0]&0xdf;
402          if (isnull()) die(Ecall);
403       }
404       else type='N';
405       if (rxgettime(type,t2=&t,&usec)) die(Ecall);
406       argc=1;
407       if (isnull()) {
408          delete(&l);
409          argc--;
410       }
411    } else {
412       t2=localtime(&timestamp.tv_sec);/* t2 now contains all the necessary info */
413       usec=timestamp.tv_usec;
414    }
415    if(argc==1){
416       arg=delete(&l);
417       if(!l)die(Ecall);
418       opt=arg[0]&0xdf;
419       if (type) if (opt=='E' || opt=='R' || opt=='O') die(Ecall);
420    }
421    switch(opt){
422       case 'C':l=t2->tm_hour%12;
423          if(l==0)l=12;
424          sprintf(ans,"%d:%02d%s",l,t2->tm_min,(t2->tm_hour <12)?"am":"pm");
425          break;
426       case 'N':sprintf(ans,"%02d:%02d:%02d",t2->tm_hour,t2->tm_min,t2->tm_sec);
427          break;
428       case 'L':sprintf(ans,"%02d:%02d:%02d.%06d",t2->tm_hour,t2->tm_min,
429                        t2->tm_sec,usec);
430          break;
431       case 'H':sprintf(ans,"%d",t2->tm_hour);
432          break;
433       case 'M':sprintf(ans,"%d",(t2->tm_hour)*60+(t2->tm_min));
434          break;
435       case 'S':sprintf(ans,"%d",((t2->tm_hour)*60+(t2->tm_min))*60+(t2->tm_sec));
436          break;
437       case 'O':
438 #ifdef HAS_GMTOFF
439          sprintf(ans,"%ld",(long)(t2->tm_gmtoff));
440 #else
441          sprintf(ans,"%ld",-(long)timezone+3600*(t2->tm_isdst>0));
442 #endif
443          break;
444       case 'E':
445       case 'R':if(!(timeflag&1))secs=timestamp.tv_sec,
446                                 microsecs=timestamp.tv_usec;
447          timeflag|=1,
448          e2=timestamp.tv_usec-microsecs,
449          e1=timestamp.tv_sec-secs;
450          if(e2<0)e2+=1000000,e1--;
451          if(opt=='R')secs=timestamp.tv_sec,microsecs=timestamp.tv_usec;
452          if(e1||e2)sprintf(ans,"%ld.%06d",e1,e2);
453          else ans[0]='0',ans[1]=0; /* "0", not "0.000000" */
454          break;
455       default:die(Ecall);
456    }
457    stack(ans,strlen(ans));
458 }
459 
460 char *month[12]={"Jan","Feb","Mar","Apr","May","Jun","Jul",
461                  "Aug","Sep","Oct","Nov","Dec"};
462 /* month names originally for rxdate() but needed for the Rexx version string*/
463 
464 /* This is used in DATE() with three parameters to convert an input date
465    into a Unix date */
rxgetdate(type,thisyear)466 static time_t rxgetdate(type,thisyear)
467 char type;
468 int thisyear;
469 {
470    long t, t2;
471    char *date;
472    char mth[3];
473    struct tm time;
474    int len;
475    unsigned long maxtime = (~(unsigned long)0)>>1;
476    int i,y;
477    char c;
478    memset((void*)&time,0,sizeof time);
479    if (type=='B' || type=='C' || type=='D') {
480       t=getint(1);
481       if (t<0) return -1;
482    }
483    else {
484       date=delete(&len);
485       for (i=0; i<len; i++) if (!date[i]) return -1;
486       date[len]=0;
487    }
488    time.tm_isdst = 0;
489    time.tm_hour = 12; /* stop DST variations from changing the date */
490    time.tm_year = thisyear-1900;
491 
492    switch(type) {
493       case 'C':
494          if (t>36524) return -1;
495          y=t*100/36524; /* approximate year represented by input value */
496          if (y+2000-thisyear <= 50) t+=36524;
497          t+=693594L;
498          /* fall through */
499       case 'B':
500          t-=719162L;
501          if (t > (long)(maxtime/86400) || t < -(long)(maxtime/86400))
502             return -1;
503          return 86400*(time_t)t;
504       case 'J':
505          if (sscanf(date,"%2d%3ld%c",&y,&t,&c) != 2) return -1;
506          if (y<0) return -1;
507          if (y+2000-thisyear <= 50) y+=100;
508          time.tm_year = y;
509          /* fall through */
510       case 'D':
511          t2=mktime(&time);
512          if (t2==-1) return -1;
513          if (t>366) return -1;
514          return t2+t*86400;
515       case 'E':
516          if (sscanf(date,"%2d/%2d/%2d%c",&time.tm_mday,&time.tm_mon,
517             &y,&c) != 3) return -1;
518          if (y+2000-thisyear <= 50) y+=100;
519          time.tm_year=y;
520          break;
521       case 'N':
522          if (sscanf(date,"%2d %3c %4d%c",&time.tm_mday,mth,
523             &y,&c) != 3) return -1;
524          time.tm_year=y-1900;
525          for (i=0; i<12; i++) if (!memcmp(month[i],mth,3)) break;
526          if (i==12) return -1;
527          time.tm_mon=i+1;
528          break;
529       case 'O':
530          if (sscanf(date,"%2d/%2d/%2d%c",&y,&time.tm_mon,
531             &time.tm_mday,&c) != 3) return -1;
532          if (y+2000-thisyear <= 50) y+=100;
533          time.tm_year=y;
534          break;
535       case 'S':
536          if (sscanf(date,"%4d%2d%2d%c",&y,&time.tm_mon,
537             &time.tm_mday,&c) != 3) return -1;
538          time.tm_year=y-1900;
539          break;
540       case 'U':
541          if (sscanf(date,"%2d/%2d/%2d%c",&time.tm_mon,&time.tm_mday,
542             &y,&c) != 3) return -1;
543          if (y+2000-thisyear <= 50) y+=100;
544          time.tm_year=y;
545          break;
546       default: return -1;
547    }
548    time.tm_mon--;
549    if (time.tm_mday<1 || time.tm_mday>31 || time.tm_mon<0 || time.tm_mon>11
550       || time.tm_year<0) return -1;
551    return mktime(&time);
552 }
553 
rxdate(argc)554 void rxdate(argc)
555 int argc;
556 {
557    static char *wkday[7]={"Sunday","Monday","Tuesday","Wednesday",
558                           "Thursday","Friday","Saturday"};
559    static char *fullmonth[12]={"January","February","March","April","May",
560                           "June","July","August","September","October",
561                           "November","December"};
562    struct tm *t2;
563    struct timezone tz;
564    char ans[20];
565    char opt='N';
566    char type='N';
567    char *arg;
568    int l;
569    long t;
570    time_t time;
571    if(!(timeflag&2))
572       gettimeofday(&timestamp,&tz);/* Make a timestamp if necessary */
573    timeflag|=2;
574    time=timestamp.tv_sec;
575    t2=localtime(&timestamp.tv_sec);/* t2 now contains all the necessary info */
576    if(argc>3)die(Ecall);
577    if (argc>1) { /* get a type and an input date of that type */
578       if (argc==3) {
579          arg=delete(&l);
580          if(!l)die(Ecall);
581          type=arg[0]&0xdf;
582          if (isnull()) die(Ecall);
583       }
584       time=rxgetdate(type,t2->tm_year+1900);
585       if (time==-1) die(Ecall);
586       t2=localtime(&time);
587       argc=1;
588       if (isnull()) {
589          argc--;
590          delete(&l);
591       }
592    }
593    if(argc==1){
594       arg=delete(&l);
595       if(!l)die(Ecall);
596       opt=arg[0]&0xdf;
597    }
598    switch(opt){
599       case 'B':
600          if (time>=0) t=time/86400;
601          else t=-((-time-1)/86400)-1; /* make sure negative numbers round down */
602       sprintf(ans,"%ld",t+719162L);
603          break;
604       case 'C':
605          t=time/86400L+25568L;
606          if (t>36524) t-=36524;
607          sprintf(ans,"%ld",t);
608          break;
609       case 'D':sprintf(ans,"%d",t2 -> tm_yday+1);
610          break;
611       case 'E':sprintf(ans,"%02d/%02d/%02d",t2 ->tm_mday,t2->tm_mon+1,t2->tm_year%100);
612          break;
613       case 'J':sprintf(ans,"%02d%03d",t2->tm_year%100,t2->tm_yday+1);
614          break;
615       case 'M':strcpy(ans,fullmonth[t2->tm_mon]);
616          break;
617       case 'N':sprintf(ans,"%d %s %d",t2->tm_mday,month[t2->tm_mon],t2->tm_year+1900);
618          break;
619       case 'O':sprintf(ans,"%02d/%02d/%02d",t2->tm_year%100,t2->tm_mon+1,t2->tm_mday);
620          break;
621       case 'S':sprintf(ans,"%04d%02d%02d",t2->tm_year+1900,t2->tm_mon+1,t2->tm_mday);
622          break;
623       case 'U':sprintf(ans,"%02d/%02d/%02d",t2->tm_mon+1,t2->tm_mday,t2->tm_year%100);
624          break;
625       case 'W':strcpy(ans,wkday[t2->tm_wday]);
626          break;
627       default:die(Ecall);
628    }
629    stack(ans,strlen(ans));
630 }
rxstrip(argc)631 void rxstrip(argc)
632 int argc;
633 {
634    char *arg;
635    int len;
636    char strip=' ';
637    int flg=0;
638    if(argc>3||!argc)die(Ecall);
639    if(argc==3){
640       arg=delete(&len);
641       if(len>1||len==0)die(Ecall);
642       else if(len==1)strip=arg[0];
643    }
644    if(argc>1){
645       arg=delete(&len);
646       if(!len)die(Ecall);
647       else if(len>0)switch(arg[0]&0xdf){
648       case 'T':flg=1;
649          break;
650       case 'L':flg= -1;
651       case 'B':break;
652       default:die(Ecall);
653       }
654    }
655    arg=delete(&len);
656    if(len<0)die(Enoarg);
657    if(flg<=0)for(;arg[0]==strip&&len;arg++,len--); /* strip leading chars */
658    if(flg>=0){while(len--&&arg[len]==strip);len++;}/* strip trailing chars */
659    mtest(workptr,worklen,len+5,len+5); /* move to worksp before stacking, */
660    memcpy(workptr,arg,len);            /* as stack() will destroy this copy */
661    stack(workptr,len);
662 }
rxleft(argc)663 void rxleft(argc)
664 int argc;
665 {
666    char *arg;
667    int len;
668    int len1;
669    char pad=' ';
670    int num;
671    if(argc>3||argc<2)die(Ecall);
672    if(argc==3){
673       arg=delete(&len);
674       if(len>=0){
675          if(len!=1)die(Ecall);
676          pad=arg[0];
677       }
678    }
679    if((num=getint(1))<0)die(Ecall);
680    arg=delete(&len);
681    if(len<0)die(Enoarg);
682    len1=len>num?len:num;
683    mtest(workptr,worklen,len1+5,len1+5);
684    len1=len<num?len:num;
685    memcpy(workptr,arg,len1);
686    for(;len1<num;workptr[len1++]=pad);
687    stack(workptr,num);
688 }
rxright(argc)689 void rxright(argc)
690 int argc;
691 {
692    char *arg;
693    int len;
694    int len1;
695    int i;
696    char pad=' ';
697    int num;
698    if(argc>3||argc<2)die(Ecall);
699    if(argc==3){
700       arg=delete(&len);
701       if(len>0){
702          if(len!=1)die(Ecall);
703          pad=arg[0];
704       }
705    }
706    if((num=getint(1))<0)die(Ecall);
707    arg=delete(&len);
708    if(len<0)die(Enoarg);
709    len1=len>num?len:num;
710    mtest(workptr,worklen,len1+5,len1+5);
711    for(i=0;len+i<num;workptr[i++]=pad);
712    len1=len<num?len:num;
713    memcpy(workptr+i,arg+len-len1,len1);
714    stack(workptr,num);
715 }
716 
rxgetname(nl,t)717 char *rxgetname(nl,t) /* get a symbol (if compound symbol, substitute values
718                          in tail). Afterwards, t=0 if invalid, otherwise:
719                          1 normal symbol, 2 constant symbol, 3 number. */
720 int *nl,*t;           /* Return value is the name, nl is the length.  The   */
721 {                     /* result may contain garbage if the symbol was bad.  */
722    static char name[maxvarname];
723    int len,l,m,e,z;
724    char *arg;
725    char *val;
726    int p;
727    int i=0;
728    char c;
729    int dot=0;
730    int constsym;
731    if (num(&m,&e,&z,&l)>=0){
732       /* Symbol is a number - must not contain spaces or a leading sign.
733          Uppercase any 'e' in the exponent. */
734       (*t)=0;
735       arg=delete(&len);
736       if (len>=maxvarname-1) return name;
737       if (!rexxsymbol(arg[0])) return name;
738       if (!rexxsymbol(arg[len-1])) return name;
739       (*t)=3;
740       (*nl)=len;
741       memcpy(name,arg,len);
742       name[len]=0;
743       arg=strchr(name,'e');
744       if (arg) arg[0]='E';
745       return name;
746    }
747    arg=delete(&len);
748    if (len<=0) return *t=0,name;
749    constsym=rexxsymbol(uc(arg[0]))<=0; /* is it a constant symbol? */
750    (*t)=1+constsym;
751    if(len>=maxvarname-1)return *t=0,name;
752    while(len&&arg[0]!='.') {        /* Get the stem part */
753       name[i++]=c=uc((arg++)[0]),
754       len--;
755       if(!rexxsymbol(c))return *t=0,name;
756    }
757    if(len==1&&arg[0]=='.'&&!constsym)
758       dot=1,len--;                  /* Delete final dot of a stem */
759    while(len&&arg[0]=='.'){         /* Get each element of the tail */
760       dot=1;
761       name[p= i++]='.',
762       ++p,
763       ++arg,
764       len--;
765       while(len&&arg[0]!='.'){      /* copy the element */
766          c=name[i++]=uc((arg++)[0]),len--;
767          if(!rexxsymbol(c))return *t=0,name;
768       }
769       if(p!=i&&!constsym){          /* substitute it */
770          name[i]=0;
771          if(val=varget(name+p,i-p,&l)){
772             if(len+l>=maxvarname-1)return *t=0,name;
773             memcpy(name+p,val,l),i=p+l;
774          }
775       }
776    }
777    (*nl)=i;
778    name[i]=0;
779    if(dot&&!constsym)name[0]|=128; /* Compound symbols have the MSB set */
780    return name;
781 }
782 
rxvalue(argc)783 void rxvalue(argc)
784 int argc;
785 {
786    char *arg;
787    char *val;
788    char *pool=0;
789    char **entry;
790    int poollen;
791    char *new=0;
792    int newlen;
793    int l,len,t;
794    int oldlen;
795    int path;
796    if(argc==3){
797       pool=delete(&poollen);
798       argc--;
799       pool[poollen]=0;
800    }
801    if(argc==2){
802       new=delete(&newlen);
803       argc--;
804       if(newlen<0)new=0;
805       else{ /* stack will be corrupted, so copy to workspace */
806          mtest(workptr,worklen,newlen+1,newlen+1-worklen);
807          memcpy(workptr,new,newlen);
808          new=workptr;
809       }
810    }
811    if(argc!=1)die(Ecall);
812    if(pool)                /* The pool name determines what we do here */
813       if(!strcasecmp(pool,"ENVIRONMENT") || !strcmp(pool,"SYSTEM")){
814          arg=delete(&len);
815          if(len<1 || len>varnamelen-1)die(Ecall);
816          /* A valid environment variable contains REXX symbol characters
817             but no '$' or '.'.  It is not uppercased. */
818          if(whattype(arg[0])==2)die(Ecall);
819          for(l=0;l<len;l++)
820             if(whattype(arg[l])<1||arg[l]=='.'||arg[l]=='$')die(Ecall);
821             else varnamebuf[l]=arg[l];
822          arg=varnamebuf;
823          arg[len]=0;
824          if(val=getenv(arg))stack(val,strlen(val));
825          else stack(cnull,0);
826          if(!new)return;
827          if(memchr(new,0,newlen))die(Ecall);
828          path=strcmp(arg,"PATH");
829          entry=(char**)hashfind(0,arg,&l);
830          arg[len]='=';
831          arg[len+1]=0;
832          putenv(arg); /* release the previous copy from the environment */
833          if(!l)*entry=allocm(len+newlen+2);
834          else if(strlen(*entry)<len+newlen+2)
835             if(!(*entry=realloc(*entry,len+newlen+2)))die(Emem);
836          memcpy(*entry,arg,++len);
837          memcpy(*entry+len,new,newlen);
838          entry[0][len+newlen]=0;
839          putenv(*entry);
840          if(!path)hashclear(); /* clear shell's hash table on change of PATH */
841          return;
842       }
843       /* here add more "else if"s */
844       else if(strcasecmp(pool,"REXX"))die(Ecall);
845    arg=rxgetname(&len,&t); /* Get the symbol name, then try to get its value */
846    if (t>1) stack(arg,len); /* for constant symbol stack its name */
847    else if(t&&(val=varget(arg,len,&l)))stack(val,l);
848    else if(t<1)die(Ecall);/* die if it was bad */
849    else { /* stack the variable's name */
850       oldlen=len;
851       if((l=arg[0]&128)&&!memchr(arg,'.',len))arg[len++]='.';
852       arg[0]&=127,stack(arg,len);
853       arg[0]|=l;
854       len=oldlen;
855    }
856    if(new)
857       if (t>1) die(Ecall); /* can't set a constant symbol */
858       else varset(arg,len,new,newlen);
859 }
860 
rxdatatype(argc)861 void rxdatatype(argc)
862 int argc;
863 {
864    char *arg;
865    int len;
866    int i,numb=1,fst=1;
867    int m,e,z,l;
868    char c;
869    if(argc>2||!argc)die(Ecall);
870    if(argc==2&&isnull())delete(&len),argc--;
871    if(argc==1){
872       if(num(&m,&e,&z,&l)>=0)  /* numeric if true */
873          delete(&l),
874          stack("NUM",3);
875       else delete(&l),stack("CHAR",4);
876    }
877    else{
878       arg=delete(&len);
879       if(isnull())die(Enoarg);
880       if(len<1)die(Ecall);
881       switch(arg[0]&0xdf){ /* Depending on type, set i to the answer */
882       case 'A':arg=delete(&len);
883          if(!len){i=0;break;}
884          i=1;
885          while(len--)if((m=alphanum((arg++)[0]))<1||m==3)i=0;
886          break;
887       case 'B':arg=delete(&len);
888          if(!len){i=0;break;}
889          i=1;
890          while(len--)if((c=(arg++)[0])!='0'&&c!='1')i=0;
891          break;
892       case 'L':arg=delete(&len);
893          if(!len){i=0;break;}
894          i=1;
895          while(len--)if((c=(arg++)[0])<'a'||c>'z')i=0;
896          break;
897       case 'M':arg=delete(&len);
898          if(!len){i=0;break;}
899          i=1;
900          while(len--)if((c=(arg++)[0]|0x20)<'a'||c>'z')i=0;
901          break;
902       case 'N':i=(num(&m,&e,&z,&l)>=0),
903          delete(&len);
904          break;
905       case 'S':arg=rxgetname(&len,&l);
906          i = l>0;
907          break;
908       case 'U':arg=delete(&len);
909          if(!len){i=0;break;}
910          i=1;
911          while(len--)if((c=(arg++)[0])<'A'||c>'Z')i=0;
912          break;
913       case 'W':numb=num(&m,&e,&z,&l),
914          i=numb>=0&&(z||isint(numb,l,e)),
915          delete(&len);
916          break;
917       case 'X':arg=delete(&len);
918          i=1,l=0;
919     /*   while(len&&arg[0]==' ')arg++,len--; */
920          if(len && (arg[0]==' '||arg[len-1]==' ')){i=0; break;}
921          while(len){
922             if(arg[0]==' '){
923                if(fst)fst=0;
924                else if(l%2)i=0;
925                l=0;
926                while(len&&arg[0]==' ')arg++,len--;
927             }
928             if(len==0)break;
929             c=(arg++)[0],len--;
930             if((c-='0')<0)i=0;
931             else if(c>9){
932                if((c-=7)<10)i=0;
933                if(c>15)if((c-=32)<10)i=0;
934                if(c>15)i=0;
935             }
936             l++;
937          }
938          if(!fst&&(l%2))i=0;
939          break;
940       default:die(Ecall);
941       }
942       stack((c=i+'0',&c),1);
943    }
944 }
rxcopies(argc)945 void rxcopies(argc)
946 int argc;
947 {
948    int copies;
949    char *arg,*p;
950    char *mtest_old;
951    long mtest_diff;
952    int len;
953    int a;
954    if(argc!=2)die(Ecall);
955    if((copies=getint(1))<0)die(Ecall);
956    arg=delete(&len);
957    if(len<0)die(Enoarg);
958    if(!(len&&copies)){stack(cnull,0);return;}
959    if dtest(cstackptr,cstacklen,ecstackptr+len*copies+16,len*copies+16)
960       arg+=mtest_diff; /* Make room for the copies, then stack them directly */
961    for(a=len*(copies-1),p=arg+len;a--;p++[0]=arg++[0]);
962    ecstackptr+=align(len*=copies),
963    *(int *)(cstackptr+ecstackptr)=len,
964    ecstackptr+=four;
965 }
rxspace(argc)966 void rxspace(argc)
967 int argc;
968 {
969    char *arg;
970    int len;
971    int len1,len2;
972    char pad=' ';
973    int num=1;
974    int i;
975    if(argc<1||argc>3)die(Ecall);
976    if(argc==3){  /* First we find the character to pad with */
977       argc--;
978       arg=delete(&len);
979       if(len>=0){
980          if(len!=1)die(Ecall);
981          pad=arg[0];
982       }
983    }
984    if(argc==2){ /* Then the number of spaces between each word */
985       argc--;
986       if(isnull())delete(&len);
987       else if((num=getint(1))<0)die(Ecall);
988    }
989    arg=delete(&len); /* and finally the phrase to operate on */
990    if(len<0)die(Enoarg);
991    while(len--&&arg[0]==' ')arg++;
992    len++;
993    while(len--&&arg[len]==' ');
994    len++;
995    mtest(workptr,worklen,len*(num+1),len*(num+2));
996    for(len1=len2=0;len2<len;){ /* Make the result string in the workspace */
997       while((workptr[len1++]=arg[len2++])!=' '&&len2<=len);
998       while(len2<len&&arg[len2]==' ')len2++;
999       for(i=0,len1--;i<num;workptr[len1++]=pad)i++;
1000    }
1001    if(len)len1-=num;  /* Remove the padding from after the last word */
1002    stack(workptr,len1);
1003 }
rxrange(argc)1004 void rxrange(argc)
1005 int argc;
1006 {
1007    unsigned int c2=255;
1008    unsigned int c1=0;
1009    unsigned char *arg;
1010    int len;
1011    if(argc>2)die(Ecall);
1012    if(argc>1){
1013       arg=(unsigned char *)delete(&len);
1014       if(len>=0)
1015          if(len!=1)die(Ecall);
1016          else c2=arg[0];
1017    }
1018    if(argc){
1019       arg=(unsigned char *)delete(&len);
1020       if(len>=0)
1021          if(len!=1)die(Ecall);
1022          else c1=arg[0];
1023    }
1024    if(c1>c2)c2+=256;
1025    len=c2-c1+1;
1026    mtest(cstackptr,cstacklen,ecstackptr+len+16,len+16);
1027    for(arg=(unsigned char *)(cstackptr+ecstackptr);c1<=c2;(*(arg++))=(c1++)&255);
1028    *(int *)(cstackptr+(ecstackptr+=align(len)))=len,
1029    ecstackptr+=four;
1030 }
c2x(argc)1031 void c2x(argc)
1032 int argc;
1033 {
1034    char *arg;
1035    int len;
1036    int i;
1037    if(argc!=1)die(Ecall);
1038    arg=delete(&len);
1039    mtest(workptr,worklen,len+len,len+len-worklen);
1040    for(i=0;i<len;i++)xbyte(workptr+i+i,arg[i]);
1041    stack(workptr,len+len);
1042 }
xbyte(where,what)1043 void xbyte(where,what) /* Place two hex digits representing "what", "where" */
1044 char *where;
1045 unsigned char what;
1046 {
1047    unsigned char c1=what>>4;
1048    what&=15;
1049    if(what>9)what+=7;
1050    if(c1>9)c1+=7;
1051    where[0]=c1+'0',where[1]=what+'0';
1052 }
c2d(argc)1053 void c2d(argc)
1054 int argc;
1055 {
1056    unsigned char *arg;
1057    int len;
1058    int n=-1;
1059    unsigned int num=0;
1060    unsigned char sign;
1061    int s=0;
1062    if(argc==2){
1063       argc--;
1064       if((n=getint(1))<0)die(Ecall);
1065    }
1066    if(argc!=1)die(Ecall);
1067    arg=(unsigned char *)delete(&len);
1068    if(n<0)n=len+1;
1069    while(n-->0)
1070       if(len>0){
1071          num|=(sign=arg[--len])<<s;
1072          if(sign&&s>=8*four||(int)num<0)die(Ecall);
1073          s+=8;
1074       }
1075       else sign=0;
1076    sign= -(sign>127);
1077    while(s<8*four)num|=sign<<s,s+=8;
1078    stackint((int)num);
1079 }
b2x(argc)1080 void b2x(argc)
1081 int argc;
1082 {
1083    char *arg;
1084    char *ans;
1085    int len;
1086    int anslen=0;
1087    int n;
1088    int d;
1089    char c;
1090    if(argc!=1)die(Ecall);
1091    ans=arg=delete(&len);
1092    for(n=0;n<len && arg[n]!=' ' && arg[n]!='\t';n++);
1093                                     /* count up to first space */
1094    if(len && !n)die(Ebin);          /* leading spaces not allowed */
1095    if(!(n%=4))n=4;                  /* how many digits in first nybble */
1096    while(len){                      /* for each nybble */
1097       d=0;
1098       while(n--){                   /* for each digit */
1099          if(!len)die(Ebin);
1100          c=arg++[0];
1101          len--;
1102          if(c!='0' && c!='1')die(Ebin);
1103          d=(d<<1)+(c=='1');         /* add digit to d */
1104       }
1105       n=4;                          /* next nybble has 4 digits */
1106       if((d+='0')>'9')d+='A'-'9'-1; /* convert digit to hex */
1107       ans++[0]=d;
1108       anslen++;
1109       while(len && (*arg==' '||*arg=='\t')){
1110          arg++;                     /* spaces allowed between nybbles */
1111          if(!--len)die(Ebin);       /* trailing spaces not allowed */
1112       }
1113    }
1114    ecstackptr+=align(anslen);       /* finish the calculator stack */
1115    *(int*)(cstackptr+ecstackptr)=anslen;
1116    ecstackptr+=four;
1117 }
b2d(argc)1118 void b2d(argc)
1119 int argc;
1120 {
1121    char *arg;
1122    int len;
1123    if(argc!=1)die(Ecall);
1124    arg=delete(&len);
1125    /* hack: do b2c then c2d */
1126    mtest(workptr,worklen,len,len-worklen);
1127    memcpy(workptr,arg,len);
1128    stackb(workptr,len);
1129    c2d(1);
1130 }
d2c(argc)1131 void d2c(argc)
1132 int argc;
1133 {
1134    unsigned int num,minus;
1135    int n=-1;
1136    int l;
1137    unsigned char sign;
1138    char *ans;
1139    if(argc==2){
1140       argc--;
1141       if((n=getint(1))<0)die(Ecall);
1142    }
1143    if(argc!=1)die(Ecall);
1144    num=(unsigned)getint(1);
1145    minus=-num;
1146    sign=-((int)num<0);
1147    mtest(workptr,worklen,n<four?four:n,n+1+four);
1148    if(n<0){
1149       if(!num){
1150          stack("",1); /* stack d2c(0) - the null char from "" */
1151          return;
1152       }
1153       for(n=0,ans=workptr+four-1;num&&minus;n++,num>>=8,minus>>=8)
1154          *ans--=(char)num;
1155       stack(++ans,n);
1156       return;
1157    }
1158    for(l=n,ans=workptr+n-1;n--;num>>=8)*ans--=num?(char)num:sign;
1159    stack(workptr,l);
1160 }
d2b(argc)1161 void d2b(argc)
1162 int argc;
1163 {
1164    int num;
1165    char c[8*four];
1166    int i;
1167    if(argc!=1)die(Ecall);
1168    if((num=getint(1))<0)die(Ecall);
1169    if(!num)stack("00000000",8);
1170    else{
1171       for(i=8*four;num||(i&7);c[--i]=(num&1)+'0',num>>=1);
1172       stack(c+i,8*four-i);
1173    }
1174 }
d2x(argc)1175 void d2x(argc)
1176 int argc;
1177 {
1178    unsigned int num,minus;
1179    unsigned char sign;
1180    int l;
1181    int n=-1;
1182    char *ans;
1183    if(argc==2){
1184       argc--;
1185       if((n=getint(1))<0)die(Ecall);
1186    }
1187    if(argc!=1)die(Ecall);
1188    num=getint(1);
1189    minus=-num;
1190    sign=-((int)num<0);
1191    if(n<0){
1192       if(!num){stack("0",1);return;}
1193       mtest(workptr,worklen,2*four,2*four);
1194       for(n=0,ans=workptr+2*four-2;num&&minus;n+=2,num>>=8,minus>>=8)
1195          xbyte(ans,(char)num),ans-=2;
1196       if((ans+=2)[0]==(sign?'F':'0')&&(!sign||ans[1]>'7'))ans++,n--;
1197       stack(ans,n);
1198    }
1199    else{
1200       mtest(workptr,worklen,n+1,n+1-worklen);
1201       for(l=n,ans=workptr+n;n>0;n-=2,ans-=2,num>>=8)
1202          xbyte(ans,num?(char)num:sign);
1203       if(n<0)ans++;
1204       stack(ans+2,l);
1205    }
1206 }
x2c(argc)1207 void x2c(argc)
1208 int argc;
1209 {
1210    char *arg;
1211    int len;
1212    if(argc!=1)die(Ecall);
1213    arg=delete(&len);
1214    mtest(workptr,worklen,len+1,len+1-worklen);
1215    memcpy(workptr,arg,len),
1216    stackx(workptr,len);
1217 }
x2d(argc)1218 void x2d(argc)
1219 int argc;
1220 {
1221    char *arg;
1222    int len;
1223    int i;
1224    int num=0;
1225    int n=-1;
1226    char c;
1227    int k;
1228    int minus=0;
1229    if(argc==2){
1230       if((n=getint(1))<0)die(Ecall);
1231       argc--;
1232    }
1233    if(argc!=1)die(Ecall);
1234    arg=delete(&len);
1235    if(len<0)die(Enoarg);
1236    if(n<0)n=len+1;
1237    if(n==0){stack("0",1);return;}
1238    if(n<=len){
1239       k=n;
1240       arg+=len-k;
1241       if(arg[0]>='8')minus=(~(unsigned)0)<<(4*k);
1242    }
1243    else k=len;
1244    for(i=0;i<k;i++){
1245       if((c=arg[i]-'0')<0)die(Ehex);
1246       if(c>9){
1247          if((c-=7)<0)die(Ehex);
1248          if(c>15)if((c-=32)<0||c>15)die(Ehex);
1249       }
1250       if((num=num*16+c)<0)die(Erange);
1251    }
1252    stackint(num|minus);
1253 }
x2b(argc)1254 void x2b(argc)
1255 int argc;
1256 {
1257    char *arg,*ans;
1258    int len,anslen=0;
1259    int n;
1260    int i;
1261    int c;
1262    if(argc!=1)die(Ecall);
1263    arg=delete(&len);
1264    mtest(workptr,worklen,len,len-worklen);
1265    memcpy(workptr,arg,len);        /* copy the shorter string */
1266    arg=workptr;
1267    mtest(cstackptr,cstacklen,len*4+10,len*4+10-cstacklen);
1268                         /* prepare to stack the longer string */
1269    ans=cstackptr+ecstackptr;
1270    for(n=0;n<len && arg[n]!=' ' && arg[n]!='\t';n++);
1271                                     /* count up to first space */
1272    if(len && !n)die(Ebin);          /* leading spaces not allowed */
1273    n%=2;                            /* how many digits in first nybble */
1274    while(len){                      /* for each digit */
1275       c=arg++[0];
1276       len--;
1277       if((c<'0'||c>'9') && (c<'A'||c>'F') && (c<'a'||c>'f'))die(Ehex);
1278       if(c>='a')c-='a'-'A';         /* convert from hex */
1279       if((c-='0')>9)c-='A'-'9'-1;
1280       for(i=4;i--;anslen++,c=(c<<1)&15) /* convert to binary */
1281          ans++[0]=(c>=8)+'0';
1282       if(n)                         /* spaces allowed between nybbles */
1283          while(len && (*arg==' '||*arg=='\t')){
1284             arg++;
1285             if(!--len)die(Ebin);       /* trailing spaces not allowed */
1286          }
1287       n=!n;
1288    }
1289    if(n)die(Ehex);
1290    ecstackptr+=align(anslen);       /* finish the calculator stack */
1291    *(int*)(cstackptr+ecstackptr)=anslen;
1292    ecstackptr+=four;
1293 }
1294 
rxsystem(argc)1295 void rxsystem(argc)
1296 int argc;
1297 {
1298    char *arg;
1299    int len;
1300    FILE *p;
1301    char c;
1302    int rc;
1303    int type;
1304    if(argc!=1)die(Ecall);
1305    arg=delete(&len);
1306    arg[len]=0;
1307    len=0;
1308    if(p=popen(arg,"r")){ /* Open a pipe, read the output, close the pipe */
1309       while(1){
1310          c=getc(p);
1311          if(feof(p)||ferror(p))break;
1312          mtest(workptr,worklen,len+1,50);
1313          workptr[len++]=c;
1314       }
1315       rc=pclose(p)/256;
1316    }
1317    else rc= -1;
1318    stack(workptr,len);
1319    if(rc<0||rc==1)type=Efailure;
1320    else type=Eerror;
1321    rcset(rc,type,arg);
1322 }
1323 
rxseterr(info)1324 int rxseterr(info)        /* Set info->errnum to indicate the I/O error */
1325 struct fileinfo *info;    /* which just occurred on info->fp. */
1326 {
1327    info->errnum=Eerrno;
1328    if(feof(info->fp))info->errnum=Eerrno+Eeof;
1329    if(ferror(info->fp))info->errnum=errno+Eerrno;
1330    return 0;
1331 }
1332 
rxpos(argc)1333 void rxpos(argc)
1334 int argc;
1335 {
1336    char *s1,*s2,*p;
1337    int l1,l2,start;
1338    if(argc!=2&&argc!=3)die(Ecall);
1339    if(argc==3&&isnull())argc--,delete(&l1);
1340    if(argc==3)start=getint(1);
1341    else start=1;
1342    if(--start<0)die(Erange);
1343    p=(s1=delete(&l1))+start;
1344    if(l1<0)die(Enoarg);
1345    l1-=start,
1346    s2=delete(&l2);
1347    if(l2<0)die(Enoarg);
1348    if(l2==0){stack("0",1);return;}
1349    while(l1>=l2&&memcmp(p,s2,l2))p++,l1--;
1350    if(l1<l2)stack("0",1);
1351    else stackint(p-s1+1);
1352 }
rxlastpos(argc)1353 void rxlastpos(argc)
1354 int argc;
1355 {
1356    char *s1,*s2,*p;
1357    int l1,l2,start;
1358    if(argc!=2&&argc!=3)die(Ecall);
1359    if(argc==3&&isnull())argc--,delete(&l1);
1360    if(argc==3){
1361       start=getint(1);
1362       if(start<1)die(Erange);
1363    }
1364    else start=0;
1365    s1=delete(&l1),
1366    s2=delete(&l2);
1367    if(l1<0||l2<0)die(Enoarg);
1368    if(!l2){stack("0",1);return;}
1369    if(start&&start<l1)l1=start;
1370    p=s1+l1-l2;
1371    while(p>=s1&&memcmp(p,s2,l2))p--;
1372    if(p<s1)stack("0",1);
1373    else stackint(p-s1+1);
1374 }
rxsubstr(argc)1375 void rxsubstr(argc)
1376 int argc;
1377 {
1378    char *arg;
1379    int len;
1380    int len1,len2;
1381    int i;
1382    char pad=' ';
1383    int num;
1384    int strlen= -1;
1385    if(argc>4||argc<2)die(Ecall);
1386    if(argc==4){
1387       arg=delete(&len);
1388       if(len>=0)
1389          if(len!=1)die(Ecall);
1390          else pad=arg[0];
1391    }
1392    if(argc>2&&isnull())delete(&len1),argc=2;
1393    if(argc>2)if((strlen=getint(1))<0)die(Ecall);
1394    num=getint(1);
1395    arg=delete(&len);
1396    if(len<0)die(Enoarg);
1397    strlen=len1=strlen<0?len-num+1:strlen; /* fix up the default length */
1398    if(strlen<=0){          /* e.g. in substr("xyz",73) */
1399       stack("",0);
1400       return;
1401    }
1402    mtest(workptr,worklen,len1+5,len1+5);
1403    for(i=0;num<1&&len1;workptr[i++]=pad)num++,len1--; /* The initial padding */
1404    len2=len-num+1<len1?len-num+1:len1;
1405    if(len2<=0)len2=0;
1406    memcpy(workptr+i,arg+num-1,len2);  /* The substring */
1407    i+=len2;
1408    len1-=len2;
1409    for(;len1--;workptr[i++]=pad);    /* The final padding */
1410    stack(workptr,strlen);
1411 }
rxcentre(argc)1412 void rxcentre(argc)
1413 int argc;
1414 {
1415    char *arg;
1416    int len;
1417    int num;
1418    int i;
1419    int spleft;
1420    char pad=' ';
1421    if(argc==3){
1422       arg=delete(&len);
1423       if(len>=0)
1424          if(len!=1)die(Ecall);
1425          else pad=arg[0];
1426       argc--;
1427    }
1428    if(argc!=2)die(Ecall);
1429    if((num=getint(1))<=0)die(Ecall);
1430    arg=delete(&len);
1431    if(len<0)die(Enoarg);
1432    mtest(workptr,worklen,num+5,num+5);
1433    if(len>=num)memcpy(workptr,arg+(len-num)/2,num); /* centre window on text */
1434    else {                                           /* centre text in window */
1435       spleft=(num-len)/2;
1436       for(i=0;i<spleft;workptr[i++]=pad);
1437       memcpy(workptr+i,arg,len);
1438       for(i+=len;i<num;workptr[i++]=pad);
1439    }
1440    stack(workptr,num);
1441 }
rxjustify(argc)1442 void rxjustify(argc)
1443 int argc;
1444 {
1445    char *arg,*ptr;
1446    int len;
1447    int num;
1448    int i,j;
1449    int sp;
1450    int n=0;
1451    int a;
1452    char pad=' ';
1453    if(argc==3){
1454       arg=delete(&len);
1455       if(len>=0)
1456          if(len!=1)die(Ecall);
1457          else pad=arg[0];
1458       argc--;
1459    }
1460    if(argc!=2)die(Ecall);
1461    if((num=getint(1))<0)die(Ecall);
1462    rxspace(1);
1463    arg=delete(&len);
1464    if((sp=num-len)<=0){
1465       for(len=num,ptr=arg;len--;ptr++)if(ptr[0]==' ')ptr[0]=pad;
1466       stack(arg,num);
1467       return;
1468    }
1469    mtest(workptr,worklen,num+5,num+5);
1470    for(i=0;i<len;i++)if(arg[i]==' ')n++;
1471    if(!n){
1472       memcpy(workptr,arg,len);
1473       for(i=len;i<num;workptr[i++]=pad);
1474    }
1475    else{
1476       a=n/2;
1477       for(i=j=0;i<len;workptr[j++]=arg[i++])
1478          if(arg[i]==' '){
1479             arg[i]=pad;
1480             for(a+=sp;a>=n;a-=n)workptr[j++]=pad;
1481          }
1482    }
1483    stack(workptr,num);
1484 }
1485 
rxarg(argc)1486 void rxarg(argc)
1487 int argc;
1488 {
1489    int n;
1490    int i;
1491    int ex;
1492    char opt='A';
1493    char *arg;
1494    for(n=0;curargs[n];n++); /* count arguments to current procedure */
1495    if(argc>2)die(Ecall);
1496    if(argc>0&&isnull()){
1497       delete(&i);
1498       argc--;
1499       if(argc>0&&isnull()){
1500          delete(&i);
1501          argc--;
1502       }
1503    }
1504    if(argc==0){stackint(n);return;}
1505    if(argc==2){
1506       arg=delete(&i);
1507       if(i<1)die(Ecall);
1508       if((opt=arg[0]&0xdf)!='E'&&opt!='O')die(Ecall);
1509    }
1510    i=getint(1);
1511    if(i-- <=0)die(Ecall);
1512    ex=(i<n &&curarglen[i]>=0);
1513    switch(opt){
1514       case 'A':if(ex)stack(curargs[i],curarglen[i]);
1515          else stack(cnull,0);
1516          break;
1517       case 'O':ex=!ex;
1518       case 'E':stack((opt='0'+ex,&opt),1);
1519    }
1520 }
rxabbrev(argc)1521 void rxabbrev(argc)
1522 int argc;
1523 {
1524    int al= -1;
1525    char *longs,*shorts;
1526    int longl,shortl;
1527    char c;
1528    if(argc==3&&isnull())argc--,delete(&longl);
1529    if(argc==3)if((argc--,al=getint(1))<0)die(Ecall);
1530    if(argc!=2)die(Ecall);
1531    shorts=delete(&shortl);
1532    longs=delete(&longl);
1533    if(shortl<0||longl<0)die(Enoarg);
1534    if(al<0)al=shortl;
1535    c= '1'-(al>shortl||shortl>longl||memcmp(longs,shorts,shortl)),
1536    stack(&c,1);
1537 }
1538 
rxabs(argc)1539 void rxabs(argc)
1540 int argc;
1541 {
1542    int m,e,z,l,n;
1543    if(argc!=1)die(Ecall);
1544    if((n=num(&m,&e,&z,&l))<0)die(Enum);
1545    delete(&m);
1546    stacknum(workptr+n,l,e,0);
1547 }
1548 
rxcompare(argc)1549 void rxcompare(argc)
1550 int argc;
1551 {
1552    char pad=' ';
1553    char *s1,*s2;
1554    int l1,l2,l3;
1555    int i;
1556    if(argc==3){
1557       s1=delete(&l1);
1558       if(l1>=0)
1559          if(l1!=1)die(Ecall);
1560          else pad=s1[0];
1561       argc--;
1562    }
1563    if(argc!=2)die(Ecall);
1564    s2=delete(&l2),
1565    s1=delete(&l1);
1566    if(l1<0||l2<0)die(Enoarg);
1567    l3=((l1<l2)?l2:l1);  /* the length of the larger string */
1568    for(i=0;i<l3&&(i<l2?s2[i]:pad)==(i<l1?s1[i]:pad);i++);
1569    if(i++==l3)i=0;
1570    stackint(i);
1571 }
1572 
rxdelstr(argc)1573 void rxdelstr(argc)
1574 int argc;
1575 {
1576    int n,l,d= -1;
1577    int osp;
1578    char *s;
1579    if(argc==3){
1580       argc--;
1581       if(isnull())delete(&l);
1582       else if((d=getint(1))<0)die(Ecall);
1583    }
1584    if(argc!=2)die(Ecall);
1585    if((n=getint(1))<1)die(Ecall);
1586    osp=ecstackptr;
1587    s=delete(&l);
1588    if(l<0)die(Enoarg);
1589    if(n>l||!d){ecstackptr=osp;return;}/* delete nothing:return the old string*/
1590    mtest(workptr,worklen,l,l);
1591    n--;
1592    if(d<0||n+d>l)d=l-n;
1593    memcpy(workptr,s,n),
1594    memcpy(workptr+n,s+n+d,l-n-d);
1595    stack(workptr,l-d);
1596 }
1597 
rxdelword(argc)1598 void rxdelword(argc)
1599 int argc;
1600 {
1601    int n,l,d= -1,n1,d1,l1,i;
1602    int osp;
1603    char *s;
1604    if(argc==3){
1605       argc--;
1606       if(isnull())delete(&l);
1607       else if((d=getint(1))<0)die(Ecall);
1608    }
1609    if(argc!=2)die(Ecall);
1610    if((n=getint(1))<1)die(Ecall);
1611    osp=ecstackptr;
1612    s=delete(&l1);
1613    if(l1<0)die(Enoarg);
1614    for(i=0;i<l1&&s[i]==' ';i++);
1615    if(i==l1||!d){ecstackptr=osp;return;}
1616    n--;
1617    for(l=0;i<l1;l++){
1618       if(l==n)n1=i;
1619       if(l==n+d&&d>0)d1=i-n1;
1620       while(i<l1&&s[i]!=' ')i++;
1621       while(i<l1&&s[i]==' ')i++;
1622    }
1623    if(n>l-1){ecstackptr=osp;return;}
1624    mtest(workptr,worklen,l1,l1);
1625    if(d<0||n+d>l-1)d1=l1-n1;
1626    memcpy(workptr,s,n1),
1627    memcpy(workptr+n1,s+n1+d1,l1-n1-d1);
1628    stack(workptr,l1-d1);
1629 }
1630 
rxinsert(argc)1631 void rxinsert(argc)
1632 int argc;
1633 {
1634    char *new,*target;
1635    int nl,tl;
1636    int n=0,length= -1;
1637    int i;
1638    char pad=' ';
1639    if(argc==5){
1640       argc--;
1641       new=delete(&nl);
1642       if(nl>=0)
1643          if(nl==1)pad=new[0];
1644          else die(Ecall);
1645    }
1646    if(argc==4){
1647       argc--;
1648       if(isnull())delete(&nl);
1649       else if((length=getint(1))<0)die(Ecall);
1650    }
1651    if(argc==3){
1652       argc--;
1653       if(isnull())delete(&nl);
1654       else if((n=getint(1))<0)die(Ecall);
1655    }
1656    if(argc!=2)die(Ecall);
1657    target=delete(&tl);
1658    new=delete(&nl);
1659    if(tl<0||nl<0)die(Enoarg);
1660    if(length<0)length=nl;
1661    mtest(workptr,worklen,length+n+tl,length+n+tl);
1662    memcpy(workptr,target,n<tl?n:tl);
1663    if(n>tl)for(i=tl;i<n;workptr[i++]=pad);
1664    memcpy(workptr+n,new,length<nl?length:nl);
1665    if(length>nl)for(i=nl;i<length;workptr[i++ +n]=pad);
1666    if(n<tl)memcpy(workptr+n+length,target+n,tl-n);
1667    else tl=n;
1668    stack(workptr,tl+length);
1669 }
1670 
rxminmax(argc,op)1671 void rxminmax(argc,op) /* Calculate the minimum/maximum of a list of numbers */
1672 int argc;   /* How many numbers are supplied */
1673 int op;     /* What comparison operator to use */
1674 {
1675    int m1,z1,e1,l1,n1,m2,z2,e2,l2,n2,d,owp;
1676    if(!argc)die(Enoarg);
1677    if((n1=num(&m1,&e1,&z1,&l1))<0)die(Enum);
1678    delete(&d);
1679    owp=eworkptr;
1680    while(--argc){
1681       eworkptr=owp;
1682       if((n2=num(&m2,&e2,&z2,&l2))<0)die(Enum);
1683       stacknum(workptr+n1,l1,e1,m1);
1684       binrel(op);
1685       if((delete(&d))[0]=='1')n1=n2,m1=m2,e1=e2,l1=l2,owp=eworkptr;
1686    }
1687    stacknum(workptr+n1,l1,e1,m1);
1688 }
1689 
rxmax(argc)1690 void rxmax(argc)
1691 int argc;
1692 {
1693    rxminmax(argc,OPgeq);
1694 }
1695 
rxmin(argc)1696 void rxmin(argc)
1697 int argc;
1698 {
1699    rxminmax(argc,OPleq);
1700 }
1701 
rxoverlay(argc)1702 void rxoverlay(argc)
1703 int argc;
1704 {
1705    char *new,*target;
1706    int nl,tl;
1707    int n=1,length= -1;
1708    int i;
1709    char pad=' ';
1710    if(argc==5){
1711       argc--;
1712       new=delete(&nl);
1713       if(nl>=0)
1714          if(nl==1)pad=new[0];
1715          else die(Ecall);
1716    }
1717    if(argc==4){
1718       argc--;
1719       if(isnull())delete(&nl);
1720       else if((length=getint(1))<0)die(Ecall);
1721    }
1722    if(argc==3){
1723       argc--;
1724       if(isnull())delete(&nl);
1725       else if((n=getint(1))<=0)die(Ecall);
1726    }
1727    n--;
1728    if(argc!=2)die(Ecall);
1729    target=delete(&tl);
1730    new=delete(&nl);
1731    if(tl<0||nl<0)die(Enoarg);
1732    if(length<0)length=nl;
1733    mtest(workptr,worklen,length+n+tl,length+n+tl);
1734    memcpy(workptr,target,n<tl?n:tl);
1735    if(n>tl)for(i=tl;i<n;workptr[i++]=pad);
1736    memcpy(workptr+n,new,length<nl?length:nl);
1737    if(length>nl)for(i=nl;i<length;workptr[i++ +n]=pad);
1738    if(n+length<tl)memcpy(workptr+n+length,target+n+length,tl-n-length);
1739    else tl=n+length;
1740    stack(workptr,tl);
1741 }
1742 
rxrandom(argc)1743 void rxrandom(argc)
1744 int argc;
1745 {
1746    struct timeval t1;
1747    struct timezone tz;
1748    int min=0,max=999;
1749    int dummy;
1750 #ifdef DECLARE_RANDOM
1751    long random();   /* everything except Sun defines this in stdlib.h */
1752 #endif
1753    unsigned long r;
1754    if(argc==3){
1755       argc--;
1756       srandom(getint(1)),timeflag|=4;
1757    }
1758    if(!(timeflag&4)){
1759       timeflag|=4;
1760       gettimeofday(&t1,&tz);
1761       srandom(t1.tv_sec*50+(t1.tv_usec/19999));
1762    }
1763    if(argc>2)die(Ecall);
1764    if(argc&&isnull())argc--,delete(&dummy);
1765    if(argc&&isnull())argc--,delete(&dummy);
1766    if(argc)argc--,max=getint(1);
1767    if(argc)
1768       if(isnull())delete(&dummy);
1769       else min=getint(1);
1770    if(min>max||max-min>100000)die(Ecall);
1771    if(min==max)r=0;
1772    else max=max-min+1,
1773         r=(unsigned long)random()%max;
1774    stackint((int)r+min);
1775 }
1776 
rxreverse(argc)1777 void rxreverse(argc)
1778 int argc;
1779 {
1780    char *s;
1781    int i,l,l2;
1782    char c;
1783    if(argc!=1)die(Ecall);
1784    s=undelete(&l);
1785    l2=l--/2;
1786    for(i=0;i<l2;i++)c=s[i],s[i]=s[l-i],s[l-i]=c;
1787 }
1788 
rxsign(argc)1789 void rxsign(argc)
1790 int argc;
1791 {
1792    int m,z,e,l;
1793    char c;
1794    if(argc!=1)die(Ecall);
1795    if(num(&m,&e,&z,&l)<0)die(Enum);
1796    delete(&l);
1797    if(m)stack("-1",2);
1798    else c='1'-z,stack(&c,1);
1799 }
1800 
rxsubword(argc)1801 void rxsubword(argc)
1802 int argc;
1803 {
1804    char *s;
1805    int l,n,k= -1,i,n1,k1,l1;
1806    if(argc==3){
1807       if((k=getint(1))<0)die(Ecall);
1808       argc--;
1809    }
1810    if(argc!=2)die(Ecall);
1811    if((n=getint(1))<=0)die(Ecall);
1812    s=delete(&l1);
1813    if(l1<0)die(Enoarg);
1814    for(i=0;i<l1&&s[i]==' ';i++);
1815    n--;
1816    for(l=0;i<l1;l++){
1817       if(n==l)n1=i;
1818       if(k>=0&&k+n==l)k1=i-n1;
1819       while(i<l1&&s[i]!=' ')i++;
1820       while(i<l1&&s[i]==' ')i++;
1821    }
1822    if(n>=l||k==0){stack(cnull,0);return;}
1823    if(k<0||k+n>=l)k1=l1-n1;
1824    while(k1>0&&s[n1+k1-1]==' ')k1--;
1825    stack(s+n1,k1);
1826 }
1827 
rxsymbol(argc)1828 void rxsymbol(argc)
1829 int argc;
1830 {
1831    char *arg;
1832    int len,good;
1833    int l;
1834    if(argc!=1)die(Ecall);
1835    arg=rxgetname(&len,&good);
1836    if(good==1 && varget(arg,len,&l)) stack("VAR",3);
1837    else if(!good)stack("BAD",3);
1838    else stack("LIT",3);
1839 }
1840 
rxlate(argc)1841 void rxlate(argc)
1842 int argc;
1843 {
1844    char *s,*ti,*to;
1845    int sl,til= -1,tol=-1;
1846    int j;
1847    char pad=' ';
1848    if(argc==4){
1849       s=delete(&sl);
1850       if(sl==1)pad=s[0];
1851       else die(Ecall);
1852       argc--;
1853    }
1854    if(argc==3)argc--,ti=delete(&til);
1855    if(argc==2)argc--,to=delete(&tol);
1856    if(argc!=1)die(Ecall);
1857    s=undelete(&sl);
1858    if(sl<0)die(Enoarg);
1859    if(tol==-1&&til== -1)for(;sl--;s++)s[0]=uc(s[0]);
1860    else for(;sl--;s++){
1861       if(til== -1)j=s[0];
1862       else{
1863          for(j=0;j<til&&s[0]!=ti[j];j++);
1864          if(j==til)continue;
1865       }
1866       if(j>=tol)s[0]=pad;
1867       else s[0]=to[j];
1868    }
1869 }
1870 
rxtrunc(argc)1871 void rxtrunc(argc)
1872 int argc;
1873 {
1874    int d=0,n,m,e,z,l,i;
1875    char *p;
1876    if(argc==2){
1877       if(isnull())delete(&l);
1878       else if((d=getint(1))<0||d>5000)die(Ecall);
1879       argc--;
1880    }
1881    if(argc!=1)die(Ecall);
1882    eworkptr=2; /* Save room for a carry digits */
1883    if((n=num(&m,&e,&z,&l))<0)die(Enum); /* Get the number to truncate */
1884    delete(&i);
1885    if(e>0)i=l+d+e+5;
1886    else i=l+d+5;
1887    mtest(workptr,worklen,i,i);
1888    p=workptr+n;
1889    if(l>precision)  /* round it to precision before truncating */
1890    if(p[l=precision]>='5'){
1891       for(i=l-1;i>=0;i--){
1892          p[i]++;
1893          if(p[i]<='9')break;
1894          p[i]='0';
1895       }
1896       if(i<0)(--p)[0]='1',e++;
1897    }
1898    for(i=l;i<=e;p[i++]='0'); /* Extend the number to the decimal point */
1899    if(d==0&&e<0){p[0]='0';stack(p,1);return;}  /* 0 for trunc(x) where |x|<1 */
1900    if(d>0){
1901       if(e<0){
1902          if(e<-d)e= -d-1;
1903          for(i=l;i--;)p[i-e]=p[i];
1904          for(i=0;i<-e;p[i++]='0');
1905          l-=e;
1906          e=0;
1907       }
1908       if(l>e+1)for(i=l;i>e;i--)p[i+1]=p[i];
1909       p[e+1]='.';
1910       if(l<e+2)l=e+2;
1911       else l++;
1912       for(i=l;i<e+d+2;p[i++]='0');
1913       d++;
1914    }
1915    if(m)(--p)[0]='-',d++;
1916    stack(p,d+e+1);
1917 }
1918 
rxverify(argc)1919 void rxverify(argc)
1920 int argc;
1921 {
1922    char *s,*r;
1923    int sl,rl,st=1,opt=0;
1924    int i,j;
1925    if(argc==4){
1926       argc--;
1927       if(isnull())delete(&sl);
1928       else if((st=getint(1))<1)die(Ecall);
1929    }
1930    if(argc==3){
1931       argc--;
1932       s=delete(&sl);
1933       if(sl>=0){
1934          if(sl==0)die(Ecall);
1935          switch(s[0]&0xdf){
1936             case 'M':opt=1;
1937             case 'N':break;
1938             default:die(Ecall);
1939          }
1940       }
1941    }
1942    if(argc!=2)die(Ecall);
1943    r=delete(&rl),
1944    s=delete(&sl);
1945    if(rl<0||sl<0)die(Enoarg);
1946    if(st>sl)i=0;
1947    else{
1948       s+=(--st);
1949       for(i=st;i<sl;i++,s++){
1950          for(j=0;j<rl&&s[0]!=r[j];j++);
1951          if((j==rl)^opt)break;
1952       }
1953       if(i==sl)i=0;
1954       else i++;
1955    }
1956    stackint(i);
1957 }
1958 
rxword(argc)1959 void rxword(argc)
1960 int argc;
1961 {
1962    if(argc!=2)die(Ecall);
1963    stack("1",1);
1964    rxsubword(3);
1965 }
1966 
rxwordindex(argc)1967 void rxwordindex(argc)
1968 int argc;
1969 {
1970    char *s;
1971    int sl,n,i,l;
1972    if(argc!=2)die(Ecall);
1973    if((n=getint(1))<1)die(Ecall);
1974    s=delete(&sl);
1975    if(sl<0)die(Enoarg);
1976    for(i=0;i<sl&&s[0]==' ';s++,i++);
1977    n--;
1978    for(l=0;i<sl;l++){
1979       if(n==l)break;
1980       while(i<sl&&s[0]!=' ')i++,s++;
1981       while(i<sl&&s[0]==' ')i++,s++;
1982    }
1983    if(i==sl)i=0;
1984    else i++;
1985    stackint(i);
1986 }
1987 
rxwordlength(argc)1988 void rxwordlength(argc)
1989 int argc;
1990 {
1991    rxword(argc);
1992    rxlength(1);
1993 }
1994 
rxwordpos(argc)1995 void rxwordpos(argc)
1996 int argc;
1997 {
1998    char *p,*s;
1999    int pl,sl,st=1;
2000    int i,l,j,k;
2001    if(argc==3){
2002       if((st=getint(1))<1)die(Ecall);
2003       argc--;
2004    }
2005    if(argc!=2)die(Ecall);
2006    s=delete(&sl),
2007    p=delete(&pl);
2008    if(sl<0||pl<0)die(Enoarg);
2009    for(i=0;i<sl&&s[0]==' ';s++,i++);
2010    while(pl&&p[0]==' ')p++,pl--;
2011    while(pl--&&p[pl]==' ');
2012    if(!++pl){stack("0",1);return;}
2013    st--;
2014    for(l=0;i<sl;l++){
2015       if(l>=st){
2016          for(j=k=0;j<pl&&k<sl-i;j++,k++){
2017             if(s[k]!=p[j])break;
2018             if(s[k]!=' ')continue;
2019             while(++k<sl-i&&s[k]==' ');
2020             while(++j<pl&&p[j]==' ');
2021             j--,k--;
2022          }
2023          if(j==pl && (k==sl-i || s[k]==' '))break;
2024          if(k==sl-i){l= -1;break;}
2025       }
2026       while(i<sl&&s[0]!=' ')i++,s++;
2027       while(i<sl&&s[0]==' ')i++,s++;
2028    }
2029    if(i==sl)l=0;
2030    else l++;
2031    stackint(l);
2032 }
2033 
rxwords(argc)2034 void rxwords(argc)
2035 int argc;
2036 {
2037    char *s;
2038    int l1,l;
2039    if(argc!=1)die(Ecall);
2040    s=delete(&l1);
2041    while(l1&&s[0]==' ')s++,l1--;
2042    for(l=0;l1;l++){
2043       while(l1&&s[0]!=' ')s++,l1--;
2044       while(l1&&s[0]==' ')s++,l1--;
2045    }
2046    stackint(l);
2047 }
2048 
rxdigits(argc)2049 void rxdigits(argc)
2050 int argc;
2051 {
2052    if(argc)die(Ecall);
2053    stackint(precision);
2054 }
2055 
rxfuzz(argc)2056 void rxfuzz(argc)
2057 int argc;
2058 {
2059    if(argc)die(Ecall);
2060    stackint(precision-fuzz);
2061 }
2062 
rxaddress(argc)2063 void rxaddress(argc)
2064 int argc;
2065 {
2066    extern int address1;  /* from rexx.c */
2067    char *address=envtable[address1].name;
2068    if(argc)die(Ecall);
2069    stack(address,strlen(address));
2070 }
2071 
rxtrace(argc)2072 void rxtrace(argc)
2073 int argc;
2074 {
2075    char *arg;
2076    int len;
2077    char ans[2];
2078    int q=0;
2079    if(argc>1)die(Ecall);
2080    if(trcflag&Tinteract)ans[q++]='?';
2081    switch(trcflag&~Tinteract&0xff){
2082       case Tclauses:             ans[q]='A';break;
2083       case Tcommands|Terrors:    ans[q]='C';break;
2084       case Terrors:              ans[q]='E';break;
2085       case Tfailures:            ans[q]='F';break;
2086       case Tclauses|Tintermed:   ans[q]='I';break;
2087       case Tlabels:              ans[q]='L';break;
2088       case 0:                    ans[q]='O';break;
2089       case Tresults|Tclauses:    ans[q]='R';
2090    }
2091    if(argc){
2092       arg=delete(&len);
2093       if(!(trcflag&Tinteract)&&interact<0 ||
2094           (interact==interplev-1 && interact>=0)){
2095                /* if interactive trace, only interpret
2096                   trace in the actual command, also use old trace flag
2097                   as the starting value */
2098          if (interact>=0)trclp=2,trcflag=otrcflag;
2099          arg[len]=0;
2100          settrace(arg);
2101       }
2102    }
2103    stack(ans,++q);
2104 }
2105 
rxform(argc)2106 void rxform(argc)
2107 int argc;
2108 {
2109    if(argc)die(Ecall);
2110    if(numform)stack("ENGINEERING",11);
2111          else stack("SCIENTIFIC",10);
2112 }
2113 
rxformat(argc)2114 void rxformat(argc)
2115 int argc;
2116 {
2117    int n,l,e,m,z;
2118    int before=0,after= -1, expp= -1,expt= precision;
2119    char *ptr1;
2120    int len1=0;
2121    int i;
2122    int p;
2123    int c=argc;
2124    char *num1;
2125    int exp;
2126    if(argc==5){  /* Get the value of expt */
2127       argc--;
2128       if(!isnull()){if((expt=getint(1))<0)die(Ecall);}
2129       else delete(&i);
2130    }
2131    if(argc==4){  /* Get the value of expp */
2132       argc--;
2133       if(!isnull()){if((expp=getint(1))<0)die(Ecall);}
2134       else delete(&i);
2135    }
2136    if(argc==3){  /* Get the value of after */
2137       argc--;
2138       if(!isnull()){if((after=getint(1))<0)die(Ecall);}
2139       else delete(&i);
2140    }
2141    if(argc==2){  /* Get the value of before */
2142       argc--;
2143       if(!isnull()){if((before=getint(1))<=0)die(Ecall);}
2144       else delete(&i);
2145    }
2146    if(argc!=1)die(Ecall); /* The number to be formatted must be supplied */
2147    eworkptr=1;            /* allow for overflow one place to the left */
2148    if((n=num(&m,&e,&z,&l))<0)die(Enum);
2149    delete(&i);
2150    num1=n+workptr;
2151    if(c==1){ /* A simple format(number) command, in which case */
2152       stacknum(num1,l,e,m);                 /* format normally */
2153       return;
2154    }
2155    if(l>precision) /* Before processing, the number is rounded to digits() */
2156       if(num1[l=precision]>='5'){
2157          for(i=l-1;i>=0;i--){
2158             if(++num1[i]<='9')break;
2159             num1[i]='0';
2160          }
2161          if(i<0)*--num1='1';
2162       }
2163    i=l+before+after+expp+30;
2164    mtest(cstackptr,cstacklen,i+ecstackptr,i);
2165    ptr1=cstackptr+ecstackptr;
2166    if(z)num1[0]='0',m=e=0,l=1;              /* adjust zero to be just "0" */
2167    if(exp=((e<expt&&!(e<0&&l-e-1>2*expt))||!expp)) {/* no exponent */
2168       if(e<0)n=1+m;  /* calculate number of places before . */
2169       else n=e+1+m;
2170       p=1+e;
2171    }
2172    else{
2173       if(numform)n=1+m+e%3; /* number of places before . in expon. notation */
2174       else n=1+m;
2175       p=n-m;
2176    }
2177    if((p+=after)>precision||after<0)p=precision; /* what precision? */
2178    if (p<0 || (p==0&&num1[0]<'5')) { /* number is too small so make it "0" */
2179       num1[0]='0'; m=e=0; l=1;
2180    }
2181    if(l>p&&p>=0)  /* if l>p, round the number; if p<0 it needs rounding down */
2182       if(num1[l=p]>='5'){              /* anyway, so we don't need to bother */
2183          for(i=l-1;i>=0;i--){
2184             if(++num1[i]<='9')break;
2185             num1[i]='0';
2186          }
2187          if(i<0){
2188             (--num1)[0]='1';
2189             if(!l)l++; /* if that's the only '1' in the whole number, */
2190                        /* count it. */
2191             if(++e==expt&&expt&&expp)
2192                exp=0; /* just nudged into exponential form */
2193             if(exp){if(e>0)n++;}
2194             else
2195                if(numform)n=1+m+e%3;
2196                else n=1+m;
2197          }
2198       }
2199    /* should now have number rounded to fit into format, and n
2200       is the number of characters required for the integer part */
2201    if(before<n&&before)die(Eformat);
2202    for(n=before-n;n>0;n--)ptr1[len1++]=' ';
2203    if(m)ptr1[len1++]='-';
2204    if(exp){/* stack floating point number; no exponent */
2205       if(e<0){
2206          ptr1[len1++]='0';
2207          if(after){
2208             ptr1[len1++]='.';
2209             for(i= -1;i>e&&after;i--)ptr1[len1++]='0',after--;
2210          }
2211       }
2212       while(l&&(e>=0||after)){
2213          ptr1[len1++]=num1[0],
2214          num1++,
2215          l--,
2216          e--;
2217          if(l&&e==-1&&after)ptr1[len1++]='.';
2218          if(e<-1)after--;
2219       }
2220       while(e>-1)ptr1[len1++]='0',e--;
2221       if(after>0){
2222          if(e==-1)ptr1[len1++]='.';
2223          while(after--)ptr1[len1++]='0';
2224       }
2225    }
2226    else{/*stack floating point in appropriate form with exponent */
2227       ptr1[len1++]=num1[0];
2228       if(numform)while(e%3)
2229             e--,
2230             ptr1[len1++]=((--l)>0 ? (++num1)[0] : '0');
2231       else --l;
2232       if((l>0 && after<0)||after>0){
2233          ptr1[len1++]='.';
2234          while(l--&&after)ptr1[len1++]=(++num1)[0],after--;
2235          while(after-- >0)ptr1[len1++]='0';
2236       }
2237       if(!e){
2238          if(expp>0)for(i=expp+2;i--;ptr1[len1++]=' ');
2239       }
2240       else{
2241          ptr1[len1++]='E',
2242          ptr1[len1++]= e<0 ? '-' : '+',
2243          e=abs(e);
2244          for(p=0,i=1;i<=e;i*=10,p++);
2245          if(expp<0)expp=p;
2246          if(expp<p)die(Eformat);
2247          for(p=expp-p;p--;ptr1[len1++]='0');
2248          for(i/=10;i>=1;i/=10)
2249             ptr1[len1++]=e/i+'0',
2250             e%=i;
2251       }
2252    }
2253    *(int *)(ptr1+align(len1))=len1;
2254    ecstackptr+=align(len1)+four;
2255 }
2256 
rxqueued(argc)2257 void rxqueued(argc)
2258 int argc;
2259 {
2260    int l;
2261    static char buff[8];
2262    if(argc)die(Ecall);
2263    if(write(rxstacksock,"N",1)<1)die(Esys);
2264    if(read(rxstacksock,buff,7)<7)die(Esys);
2265    sscanf(buff,"%x",&l);
2266    stackint(l);
2267 }
2268 
rxlinesize(argc)2269 void rxlinesize(argc)
2270 int argc;
2271 {
2272    int ans;
2273    struct winsize sz;
2274    if(argc)die(Ecall);
2275    if(!ioctl(fileno(ttyout),TIOCGWINSZ,&sz))ans=sz.ws_col;
2276    else ans=0;
2277    stackint(ans);
2278 }
2279 
rxbitand(argc)2280 void rxbitand(argc)
2281 int argc;
2282 {
2283    char *arg1,*arg2,*argt;
2284    int len1,len2,lent;
2285    unsigned char pad=255;
2286    if(argc==3){
2287       argt=delete(&lent);
2288       if(lent!=1)die(Ecall);
2289       pad=argt[0];
2290       argc--;
2291    }
2292    if(argc==2){
2293       arg2=delete(&len2);
2294       if(len2==-1)len2=0;
2295    }
2296    else{
2297       if(argc!=1)die(Ecall);
2298       len2=0;
2299    }
2300    arg1=delete(&len1);
2301    if(len1<0)die(Ecall);
2302    if(len1<len2)argt=arg1,arg1=arg2,arg2=argt,lent=len1,len1=len2,len2=lent;
2303    argt=cstackptr+ecstackptr;
2304    for(lent=0;lent<len1;lent++)
2305       argt[lent]=arg1[lent]&(lent<len2?arg2[lent]:pad);
2306    argt+=lent=align(len1);
2307    *(int *)argt=len1;
2308    ecstackptr+=lent+four;
2309 }
rxbitor(argc)2310 void rxbitor(argc)
2311 int argc;
2312 {
2313    char *arg1,*arg2,*argt;
2314    int len1,len2,lent;
2315    char pad=0;
2316    if(argc==3){
2317       argt=delete(&lent);
2318       if(lent!=1)die(Ecall);
2319       pad=argt[0];
2320       argc--;
2321    }
2322    if(argc==2){
2323       arg2=delete(&len2);
2324       if(len2==-1)len2=0;
2325    }
2326    else{
2327       if(argc!=1)die(Ecall);
2328       len2=0;
2329    }
2330    arg1=delete(&len1);
2331    if(len1<0)die(Ecall);
2332    if(len1<len2)argt=arg1,arg1=arg2,arg2=argt,lent=len1,len1=len2,len2=lent;
2333    argt=cstackptr+ecstackptr;
2334    for(lent=0;lent<len1;lent++)
2335       argt[lent]=arg1[lent]|(lent<len2?arg2[lent]:pad);
2336    argt+=lent=align(len1);
2337    *(int *)argt=len1;
2338    ecstackptr+=lent+four;
2339 }
rxbitxor(argc)2340 void rxbitxor(argc)
2341 int argc;
2342 {
2343    char *arg1,*arg2,*argt;
2344    int len1,len2,lent;
2345    char pad=0;
2346    if(argc==3){
2347       argt=delete(&lent);
2348       if(lent!=1)die(Ecall);
2349       pad=argt[0];
2350       argc--;
2351    }
2352    if(argc==2){
2353       arg2=delete(&len2);
2354       if(len2==-1)len2=0;
2355    }
2356    else{
2357       if(argc!=1)die(Ecall);
2358       len2=0;
2359    }
2360    arg1=delete(&len1);
2361    if(len1<0)die(Ecall);
2362    if(len1<len2)argt=arg1,arg1=arg2,arg2=argt,lent=len1,len1=len2,len2=lent;
2363    argt=cstackptr+ecstackptr;
2364    for(lent=0;lent<len1;lent++)
2365       argt[lent]=arg1[lent]^(lent<len2?arg2[lent]:pad);
2366    argt+=lent=align(len1);
2367    *(int *)argt=len1;
2368    ecstackptr+=lent+four;
2369 }
2370 
rxuserid(argc)2371 void rxuserid(argc)
2372 int argc;
2373 {
2374    void endpwent();
2375    static int uid=-1;
2376    int cuid;
2377    static struct passwd *pw=0;
2378    if(argc)die(Ecall);
2379    if((cuid=getuid())!=uid)
2380       uid=cuid,
2381       pw=getpwuid(cuid),
2382       endpwent();
2383    if(!pw)stack(cnull,0);
2384    else stack(pw->pw_name,strlen(pw->pw_name));
2385 }
2386 
rxgetcwd(argc)2387 void rxgetcwd(argc)
2388 int argc;
2389 {
2390    static char name[MAXPATHLEN];
2391    if(argc)die(Ecall);
2392    if (!getcwd(name,MAXPATHLEN)) {
2393       char *err=strerror(errno);
2394       if (!err) err="Unknown error occurred";
2395       if (strlen(err) < MAXPATHLEN) strcpy(name,err);
2396       else {
2397          memcpy(name,err,MAXPATHLEN-1);
2398          name[MAXPATHLEN-1]=0;
2399       }
2400    }
2401    stack(name,strlen(name));
2402 }
2403 
rxchdir(argc)2404 void rxchdir(argc)
2405 int argc;
2406 {
2407    char *arg;
2408    int len;
2409    if(argc!=1)die(Ecall);
2410    arg=delete(&len);
2411    arg[len]=0; /* that location must exist since the length used to be
2412                   after the string */
2413    if(chdir(arg))stackint(errno);
2414    else stack("0",1);
2415 }
2416 
rxgetenv(argc)2417 void rxgetenv(argc)
2418 int argc;
2419 {
2420    char *arg;
2421    int len;
2422    if(argc!=1)die(Ecall);
2423    arg=delete(&len);
2424    arg[len]=0;
2425    if(arg=getenv(arg))stack(arg,strlen(arg));
2426    else stack(cnull,0);
2427 }
2428 
rxputenv(argc)2429 void rxputenv(argc)
2430 int argc;
2431 {
2432    char *arg;
2433    char *eptr;
2434    int len;
2435    int exist;
2436    char **value;
2437    int path;
2438    if(argc!=1)die(Ecall);
2439    arg=delete(&len);
2440    arg[len++]=0;
2441    if(!(eptr=strchr(arg,'=')))die(Ecall);
2442    eptr[0]=0;
2443    value=(char**)hashfind(0,arg,&exist);
2444    path=strcmp(arg,"PATH");
2445    eptr[0]='=';
2446    putenv(arg); /* release the previous copy from the environment */
2447    if(!exist)*value=allocm(len);
2448    else if(strlen(*value)<len)
2449       if(!(*value=realloc(*value,len)))die(Emem);
2450    strcpy(*value,arg);
2451    if(putenv(*value))stack("1",1);
2452    else stack("0",1);
2453    if(!path)hashclear(); /* clear shell's hash table on change of PATH */
2454 }
2455 
rxopen2(stream,mode,mlen,path,plen)2456 void rxopen2(stream,mode,mlen,path,plen)
2457 char *stream,*mode,*path;   /* implement open(stream,mode,path) */
2458 int mlen,plen;
2459 {
2460    char modeletter[3];
2461    struct fileinfo *info;
2462    FILE *fp;
2463    int rc;
2464    modeletter[0]='r';
2465    modeletter[1]=modeletter[2]=0;
2466    if(plen<=0)path=stream,plen=strlen(stream);
2467    if(memchr(path,0,plen))die(Ecall);
2468    path[plen]=0;
2469    if(mlen>0)switch(mode[0]&0xdf){
2470       case 'R': break;
2471       case 'W': modeletter[0]='w';
2472                 modeletter[1]='+';
2473                 break;
2474       case 'A': rc=access(path,F_OK);
2475                 modeletter[0]=rc?'w':'r';
2476                 modeletter[1]='+';
2477                 break;
2478       default:  die(Ecall);
2479    }
2480    if(info=(struct fileinfo *)hashget(1,stream,&rc)){
2481       fp=info->fp;          /* if "stream" already exists, perform freopen */
2482       free((char *)info);
2483       *(struct fileinfo **)hashfind(1,stream,&rc)=0;
2484       fp=freopen(path,modeletter,info->fp);
2485    }
2486    else fp=fopen(path,modeletter);
2487    if(!fp){
2488       stackint(errno);
2489       return;
2490    }
2491    if(modeletter[0]=='r'&&modeletter[1]=='+') /* for append, go to eof */
2492       fseek(fp,0L,2);
2493    info=fileinit(stream,path,fp);
2494    info->wr=modeletter[1]=='+';
2495    stack("0",1);
2496 }
2497 
rxopen(argc)2498 void rxopen(argc)
2499 int argc;
2500 {
2501    char *stream,*mode,*path;
2502    int len=0,mlen=0,plen;
2503    if(argc==3){
2504       argc--;
2505       stream=delete(&len);
2506       if(len<0)stream=0;
2507       else
2508          if(memchr(stream,0,len))die(Ecall);
2509          else stream[len]=0;
2510       if(!len)die(Ecall);
2511    }
2512    if(argc==2){
2513       argc--;
2514       mode=delete(&mlen);
2515    }
2516    if(argc!=1)die(Ecall);
2517    path=delete(&plen);
2518    if(plen<=0)die(Ecall);
2519    path[plen]=0;
2520    if(len<=0)stream=path,len=plen;
2521    rxopen2(stream,mode,mlen,path,plen);
2522 }
2523 
rxfdopen2(stream,mode,modelen,n,nlen)2524 void rxfdopen2(stream,mode,modelen,n,nlen) /* implement fdopen(stream,mode,n)*/
2525 char *stream;
2526 char *n;
2527 int nlen;
2528 char *mode;
2529 int modelen;
2530 {
2531    int fd;
2532    char fmode[3];
2533    FILE *fp;
2534    int streamlen=strlen(stream);
2535    fmode[0]='r';
2536    fmode[1]=fmode[2]=0;
2537    if(nlen<=0)n=stream,nlen=streamlen; /* default number is same as name */
2538    mtest(workptr,worklen,nlen+streamlen+2,nlen+streamlen+2-worklen);
2539    memcpy(workptr,n,nlen);
2540    workptr[nlen]=0;
2541    memcpy(workptr+nlen+1,stream,streamlen+1);
2542    eworkptr=nlen+streamlen+2;
2543    stack(workptr,nlen);
2544    fd=getint(1);       /* convert the fd to an integer */
2545    if(modelen>0)switch(mode[0]&0xdf){
2546       case 'R': break;
2547       case 'W': fmode[0]='w';
2548                 fmode[1]='+';
2549                 break;
2550       case 'A': fmode[0]='r';
2551                 fmode[1]='+';
2552                 break;
2553       default:  die(Ecall);
2554    }
2555    if(fp=fdopen(fd,fmode)){
2556       fileinit(workptr+nlen+1,cnull,fp)->wr=fmode[1]=='+';
2557       errno=0;
2558    }
2559    stackint(errno);
2560 }
2561 
rxfdopen(argc)2562 void rxfdopen(argc)
2563 int argc;
2564 {
2565    char *stream,*n,*mode;
2566    int len=0,nlen=0,modelen=0;
2567    if(argc==3){
2568       argc--;
2569       stream=delete(&len);
2570       if(len>0)
2571          if(memchr(stream,0,len))die(Ecall);
2572          else stream[len]=0;
2573       if(len==0)die(Ecall);
2574       stream[len]=0;
2575    }
2576    if(argc==2){
2577       argc--;
2578       mode=delete(&modelen);
2579       if(modelen==0)die(Ecall);
2580    }
2581    if(argc!=1)die(Ecall);
2582    n=delete(&nlen);
2583    n[nlen]=0;
2584    if(nlen<=0)die(Ecall);
2585    if(len<=0)stream=n,len=nlen;
2586    rxfdopen2(stream,mode,modelen,n,nlen);
2587 }
2588 
rxpopen2(stream,mode,mlen,command,comlen)2589 void rxpopen2(stream,mode,mlen,command,comlen)
2590 char *stream,*mode,*command;      /* implement popen(stream,mode,command) */
2591 int mlen,comlen;
2592 {
2593    char fmode[2];
2594    int rc;
2595    FILE *fp;
2596    struct fileinfo *info;
2597    fmode[0]='r';
2598    fmode[1]=0;
2599    if(mlen>0)fmode[0]=mode[0]|0x20;
2600    if(fmode[0]!='r'&&fmode[0]!='w')die(Ecall);
2601    if(comlen<=0)command=stream,comlen=strlen(stream);
2602    else command[comlen]=0;
2603    if(memchr(command,0,comlen))die(Ecall);
2604    if(fp=popen(command,fmode)){
2605       info=fileinit(stream,cnull,fp);
2606       info->wr=-(fmode[0]=='w'),
2607       info->lastwr=-(info->wr);
2608       rc=0;
2609    }
2610    else rc=errno;
2611    stackint(rc);
2612 }
2613 
rxpopen(argc)2614 void rxpopen(argc)
2615 int argc;
2616 {
2617    char *stream,*mode,*command;
2618    int len=0,mlen=0,comlen;
2619    if(argc==3){
2620       argc--;
2621       stream=delete(&len);
2622       if(len<0)stream=0;
2623       else
2624          if(memchr(stream,0,len))die(Ecall);
2625          else stream[len]=0;
2626       if(!len)die(Ecall);
2627    }
2628    if(argc==2){
2629       argc--;
2630       mode=delete(&mlen);
2631    }
2632    if(argc!=1)die(Ecall);
2633    command=delete(&comlen);
2634    if(comlen<=0)die(Ecall);
2635    command[comlen]=0;
2636    if(len<=0)stream=command,len=comlen;
2637    rxpopen2(stream,mode,mlen,command,comlen);
2638 }
2639 
rxlinein(argc)2640 void rxlinein(argc)
2641 int argc;
2642 {
2643    char *name=0;
2644    int lines=1;
2645    int pos= 0;
2646    int len;
2647    int call;
2648    int ch=0;
2649    long filepos;
2650    struct fileinfo *info;
2651    FILE *fp;
2652    if(argc==3){
2653       argc--;
2654       if(isnull())delete(&len);
2655       else if((lines=getint(1))!=0&&lines!=1)die(Ecall);
2656    }
2657    if(argc==2){
2658       argc--;
2659       if(isnull())delete(&len);
2660       else if((pos=getint(1))<1)die(Ecall);
2661    }
2662    if(argc==1){
2663       argc--;
2664       name=delete(&len);
2665       if(len<=0)name=0;
2666       else
2667          if(memchr(name,0,len))die(Ecall);
2668          else name[len]=0;
2669    }
2670    if(argc)die(Ecall);
2671    if(!name)name="stdin";
2672    if(!(info=(struct fileinfo *)hashget(1,name,&len))){/* If not found, then */
2673       fp=fopen(name,"r");                             /* open it for reading */
2674       info=fileinit(name,name,fp);
2675       if(!fp){
2676          info->errnum=errno+Eerrno;
2677          rcset(errno,Enotready,name);
2678          stack(cnull,0);
2679          return;
2680       }
2681       info->lastwr=0;
2682    }
2683    else fp=info->fp;
2684    if(!fp){
2685       rcset(info->errnum-Eerrno,Enotready,name);
2686       stack(cnull,0);
2687       return;
2688    }
2689    if(info->wr<0){
2690       info->errnum=Eread;
2691       rcset(Eread-Eerrno,Enotready,name);
2692       stack(cnull,0);
2693       return;
2694    }
2695    if(info->persist && info->lastwr==0 &&
2696          (filepos=ftell(info->fp))>=0 && filepos!=info->rdpos)
2697       info->rdpos=filepos,
2698       info->rdline=0; /* position has been disturbed by external prog */
2699    clearerr(fp);      /* Ignore errors and try from scratch */
2700    info->errnum=0;
2701    if(info->lastwr || pos>0)len=fseek(fp,info->rdpos,0);
2702    else len=0;
2703    info->lastwr=0;
2704    if(pos>0 && (len<0 || !info->persist)){
2705       info->errnum=Eseek;        /* Seek not allowed on transient stream */
2706       rcset(Eseek-Eerrno,Enotready,name);
2707       stack(cnull,0);
2708       return;
2709    }
2710    if(pos>0){                   /* Search for given line number (ugh!) */
2711       if(info->rdline==0 || info->rdline+info->rdchars>pos)
2712          fseek(fp,0L,0),
2713          info->rdline=1;
2714       info->rdchars=0;
2715       for(;ch!=EOF&&info->rdline<pos;info->rdline++)
2716          while((ch=getc(fp))!='\n'&&ch!=EOF);
2717       if(ch==EOF){
2718          info->rdline--;
2719          info->errnum=Ebounds;
2720          rcset(Ebounds-Eerrno,Enotready,name);
2721          stack(cnull,0);
2722          return;
2723       }
2724    }
2725    len=0;
2726    if(lines){
2727       call=sgstack[interplev].callon&(1<<Ihalt) |
2728            sgstack[interplev].delay &(1<<Ihalt);
2729       if(!call)siginterrupt(2,1); /* Allow ^C during read */
2730       while((ch=getc(fp))!='\n'&&ch!=EOF){
2731          mtest(pull,pulllen,len+1,256);
2732          pull[len++]=ch;
2733       }
2734       siginterrupt(2,0);
2735       if(delayed[Ihalt] && !call)
2736          delayed[Ihalt]=0,
2737          fseek(fp,info->rdpos,0), /* reset to start of line, if possible */
2738          die(Ehalt);
2739       if(info->rdline)info->rdline++;
2740       info->rdchars=0;
2741    }
2742    if(ch==EOF&&!len)rxseterr(info);
2743    if(info->persist && (info->rdpos=ftell(fp))<0)info->rdpos=0;
2744    if(info->errnum || setrcflag)rcset(info->errnum-Eerrno,Enotready,name);
2745    stack(pull,len);
2746 }
2747 
rxlineout(argc)2748 void rxlineout(argc)
2749 int argc;
2750 {
2751    char *name=0;
2752    char *file;
2753    int pos= 0;
2754    int charlen=0;
2755    int len;
2756    int acc;
2757    int ch=0;
2758    char *chars=0;
2759    long filepos;
2760    struct fileinfo *info;
2761    FILE *fp;
2762    if(argc==3){
2763       argc--;
2764       if(isnull())delete(&len);
2765       else if((pos=getint(1))<1)die(Ecall);
2766    }
2767    if(argc==2){
2768       argc--;
2769       chars=delete(&charlen);
2770       if(charlen<0)chars=0;
2771       else if(memchr(chars,'\n',charlen))die(Ecall);
2772    }
2773    if(argc==1){
2774       argc--;
2775       name=delete(&len);
2776       if(len<=0)name=0;
2777       else
2778          if(memchr(name,0,len))die(Ecall);
2779          else name[len]=0;
2780    }
2781    if(argc)die(Ecall);
2782    if(!name)name="stdout";
2783    if(!(info=(struct fileinfo *)hashget(1,name,&len))){
2784       acc=access(name,F_OK);  /* If not found in table, then open for append */
2785       fp=fopen(name,acc?"w+":"r+");
2786       if(fp)fseek(fp,0L,2);
2787       info=fileinit(name,name,fp);
2788       if(!fp){
2789          info->errnum=errno+Eerrno;
2790          rcset(errno,Enotready,name);
2791          stack(chars?"1":"0",1);
2792          return;
2793       }
2794       info->wr=1;
2795    }
2796    else fp=info->fp;
2797    if(!fp){
2798       rcset(info->errnum-Eerrno,Enotready,name);
2799       stack(chars?"1":"0",1);
2800       return;
2801    }
2802    if(!info->wr){  /* If it is open for reading, try to reopen for writing */
2803       file=(char*)(info+1);
2804       if(!file[0]){ /* reopen not allowed, since file name not given */
2805          info->errnum=Eaccess;
2806          rcset(Eaccess-Eerrno,Enotready,name);
2807          stack(chars?"1":"0",1);
2808          return;
2809       }
2810       if(!(fp=freopen(file,"r+",fp))){
2811          info->errnum=errno+Eerrno;
2812          fp=fopen(file,"r");/* try to regain read access */
2813          info->fp=fp;
2814          if(fp)fseek(fp,info->rdpos,0);
2815          rcset(info->errnum-Eerrno,Enotready,name);
2816          stack(chars?"1":"0",1);
2817          file[0]=0;         /* Prevent this whole thing from happening again */
2818          return;
2819       }
2820       info->wr=1;
2821       fseek(fp,0L,2);
2822       info->wrline=0;
2823       info->lastwr=1;
2824       if((info->wrpos=ftell(fp))<0)info->wrpos=0;
2825    }
2826    if(info->persist && info->lastwr &&
2827          (filepos=ftell(fp))>=0 && filepos!=info->wrpos)
2828       info->wrpos=filepos,
2829       info->wrline=0;  /* position has been disturbed by external prog */
2830    clearerr(fp);       /* Ignore errors and try from scratch */
2831    info->errnum=0;
2832    if(info->lastwr==0 || pos>0)len=fseek(fp,info->wrpos,0);
2833    else len=0;
2834    info->lastwr=1;
2835    if(pos>0 && (len<0 || !info->persist)){
2836       info->errnum=Eseek;       /* Seek not allowed on transient stream */
2837       rcset(Eseek-Eerrno,Enotready,name);
2838       stack(chars?"1":"0",1);
2839       return;
2840    }
2841    if(pos>0){                   /* Search for required line number (Ugh!) */
2842       if(info->wrline==0 || info->wrline+info->wrchars>pos)
2843          fseek(fp,0L,0),
2844          info->wrline=1;
2845       info->wrchars=0;
2846       for(;ch!=EOF&&info->wrline<pos;info->wrline++)
2847          while((ch=getc(fp))!='\n'&&ch!=EOF);
2848       fseek(fp,0L,1);          /* seek between read and write */
2849       if(ch==EOF){
2850          info->wrline--;
2851          info->errnum=Ebounds;
2852          rcset(Ebounds-Eerrno,Enotready,name);
2853          stack(chars?"1":"0",1);
2854          return;
2855       }
2856    }
2857    if(!chars){
2858       if(!pos){   /* No data and no position given so flush and go to EOF */
2859          if (fflush(fp)) rxseterr(info);
2860          fseek(fp,0L,2);
2861          info->wrline=0;
2862       }
2863       if((info->wrpos=ftell(fp))<0)info->wrpos=0; /* just pos given */
2864       if(info->errnum || setrcflag)rcset(info->errnum-Eerrno,Enotready,name);
2865       stack("0",1);
2866       return;
2867    }
2868    chars[charlen++]='\n';
2869    if(fwrite(chars,charlen,1,fp)){
2870       stack("0",1);
2871       if(info->wrline)info->wrline++;
2872       info->wrchars=0;
2873       if(info->persist && (info->wrpos=ftell(fp))<0) info->wrpos=0;
2874       if(setrcflag)rcset(0,Enotready,name);
2875    }else{
2876       stack("1",1);
2877       rxseterr(info);
2878       fseek(fp,info->wrpos,0);
2879       rcset(info->errnum-Eerrno,Enotready,name);
2880    }
2881 }
2882 
rxcharin(argc)2883 void rxcharin(argc)
2884 int argc;
2885 {
2886    char *name=0;
2887    int chars=1;
2888    int pos= 0;
2889    int len;
2890    int l;
2891    int call;
2892    long filepos;
2893    struct fileinfo *info;
2894    FILE *fp;
2895    if(argc==3){
2896       argc--;
2897       if(isnull())delete(&len);
2898       else if((chars=getint(1))<0)die(Ecall);
2899    }
2900    if(argc==2){
2901       argc--;
2902       if(isnull())delete(&len);
2903       else if((pos=getint(1))<1)die(Ecall);
2904    }
2905    if(argc==1){
2906       argc--;
2907       name=delete(&len);
2908       if(len<=0)name=0;
2909       else
2910          if(memchr(name,0,len))die(Ecall);
2911          else name[len]=0;
2912    }
2913    if(argc)die(Ecall);
2914    if(!name)name="stdin";
2915    if(!(info=(struct fileinfo *)hashget(1,name,&len))){
2916       fp=fopen(name,"r"); /* not found in table so try to open */
2917       info=fileinit(name,name,fp);
2918       if(!fp){
2919          info->errnum=errno+Eerrno;
2920          rcset(errno,Enotready,name);
2921          stack(cnull,0);
2922          return;
2923       }
2924       info->lastwr=0;
2925    }
2926    else fp=info->fp;
2927    if(!fp){
2928       rcset(info->errnum-Eerrno,Enotready,name);
2929       stack(cnull,0);
2930       return;
2931    }
2932    if(info->wr<0){
2933       info->errnum=Eread;
2934       rcset(Eread-Eerrno,Enotready,name);
2935       stack(cnull,0);
2936       return;
2937    }
2938    if(info->persist && info->lastwr==0 &&
2939          (filepos=ftell(info->fp))>=0 && filepos!=info->rdpos)
2940       info->rdpos=filepos,
2941       info->rdline=0; /* position has been disturbed by external prog */
2942    clearerr(fp);
2943    info->errnum=0;
2944    if(pos>0 && (!info->persist || fseek(fp,0L,2)<0)){
2945       info->errnum=Eseek;       /* Seek not allowed on transient stream */
2946       rcset(Eseek-Eerrno,Enotready,name);
2947       stack(cnull,0);
2948       return;
2949    }
2950    if(pos){
2951       filepos=ftell(fp);
2952       if(fseek(fp,(long)pos-1,0)>=0)info->rdpos=pos-1;
2953       info->rdline=0;
2954       if(filepos<pos){          /* Seek was out of bounds */
2955          info->errnum=Ebounds;
2956          rcset(Ebounds-Eerrno,Enotready,name);
2957          stack(cnull,0);
2958          return;
2959       }
2960    }
2961    else if(info->lastwr)fseek(fp,info->rdpos,0);
2962    info->lastwr=0;
2963    call=sgstack[interplev].callon&(1<<Ihalt) |
2964         sgstack[interplev].delay &(1<<Ihalt);
2965    if(!call)siginterrupt(2,1); /* allow ^C to interrupt */
2966    mtest(workptr,worklen,chars,chars-worklen);
2967    len=fread(workptr,1,chars,fp);
2968    siginterrupt(2,0);
2969    if(delayed[Ihalt] && !call)
2970       delayed[Ihalt]=0,
2971       fseek(fp,info->rdpos,0),
2972       die(Ehalt);
2973    if(len&&info->rdline){ /* Try to keep the line counter up to date */
2974       for(l=0;l<len;)if(workptr[l++]=='\n')info->rdline++;
2975       if(workptr[len-1]!='\n')info->rdchars=1;
2976    }
2977    if(len<chars)rxseterr(info);
2978    if(info->persist && (info->rdpos=ftell(fp))<0)info->rdpos=0;
2979    if(info->errnum || setrcflag)rcset(info->errnum-Eerrno,Enotready,name);
2980    stack(workptr,len);
2981 }
2982 
rxcharout(argc)2983 void rxcharout(argc)
2984 int argc;
2985 {
2986    char *name=0;
2987    char *file;
2988    int pos= 0;
2989    int charlen;
2990    int len;
2991    int acc;
2992    int l;
2993    char *chars=0;
2994    long filepos;
2995    struct fileinfo *info;
2996    FILE *fp;
2997    if(argc==3){
2998       argc--;
2999       if(isnull())delete(&len);
3000       else if((pos=getint(1))<1)die(Ecall);
3001    }
3002    if(argc==2){
3003       argc--;
3004       chars=delete(&charlen);
3005       if(charlen<0)chars=0,charlen=0;
3006    }
3007    else charlen=0;
3008    if(argc==1){
3009       argc--;
3010       name=delete(&len);
3011       if(len<=0)name=0;
3012       else
3013          if(memchr(name,0,len))die(Ecall);
3014          else name[len]=0;
3015    }
3016    if(argc)die(Ecall);
3017    if(!name)name="stdout";
3018    if(!(info=(struct fileinfo *)hashget(1,name,&len))){
3019       acc=access(name,F_OK); /* If not found in table, open for append */
3020       fp=fopen(name,acc?"w+":"r+");
3021       if(fp)fseek(fp,0L,2);
3022       info=fileinit(name,name,fp);
3023       if(!fp){
3024          info->errnum=errno+Eerrno;
3025          rcset(errno,Enotready,name);
3026          stackint(charlen);
3027          return;
3028       }
3029       info->wr=1;
3030    }
3031    else fp=info->fp;
3032    if(!fp){
3033       rcset(info->errnum-Eerrno,Enotready,name);
3034       stackint(charlen);
3035       return;
3036    }
3037    if(!info->wr){ /* If not open for write, try to gain write access */
3038       file=(char*)(info+1);
3039       if(!file[0]){
3040          info->errnum=Eaccess;
3041          rcset(Eaccess-Eerrno,Enotready,name);
3042          stackint(charlen);
3043          return;
3044       }
3045       if(!(fp=freopen(file,"r+",fp))){
3046          info->errnum=errno+Eerrno;
3047          fp=fopen(file,"r");/* try to regain read access */
3048          info->fp=fp;
3049          if(fp)fseek(fp,info->rdpos,0);
3050          rcset(info->errnum-Eerrno,Enotready,name);
3051          stackint(charlen);
3052          file[0]=0;         /* Prevent this whole thing from happening again */
3053          return;
3054       }
3055       info->wr=1;
3056       fseek(fp,0L,2);
3057       info->wrline=0;
3058       info->lastwr=1;
3059       if((info->wrpos=ftell(fp))<0)info->wrpos=0;
3060    }
3061    if(info->persist && info->lastwr &&
3062          (filepos=ftell(fp))>=0 && filepos!=info->wrpos)
3063       info->wrpos=filepos,
3064       info->wrline=0;  /* position has been disturbed */
3065    clearerr(fp);
3066    info->errnum=0;
3067    if(pos>0 && (!info->persist || fseek(fp,0L,2)<0)){
3068       info->errnum=Eseek;        /* Seek not allowed on transient stream */
3069       rcset(Eseek-Eerrno,Enotready,name);
3070       stackint(charlen);
3071       return;
3072    }
3073    if(pos){
3074       filepos=ftell(fp);
3075       if(fseek(fp,(long)pos-1,0)>=0)info->wrpos=pos-1;
3076       info->wrline=0;
3077       if(filepos+1<pos){        /* Seek was out of bounds */
3078          info->errnum=Ebounds;
3079          rcset(Ebounds-Eerrno,Enotready,name);
3080          stack(cnull,0);
3081          return;
3082       }
3083    }
3084    else if(info->lastwr==0)fseek(fp,info->wrpos,0);
3085    info->lastwr=1;
3086    if(!chars){
3087       if(!pos){   /* No data, no pos, so flush and seek to EOF */
3088          if (fflush(fp)) rxseterr(info);
3089          fseek(fp,0L,2);
3090          info->wrline=0;
3091       }
3092       if((info->wrpos=ftell(fp))<0)info->wrpos=0; /* no data, so OK */
3093       if(info->errnum || setrcflag)rcset(info->errnum-Eerrno,Enotready,name);
3094       stack("0",1);
3095       return;
3096    }
3097    len=fwrite(chars,1,charlen,fp);
3098    info->wrpos+=len;
3099    if(len&&info->wrline){
3100       for(l=0;l<len;)if(chars[l++]=='\n')info->wrline++;
3101       if(chars[len-1]!='\n')info->wrchars=1;
3102    }
3103    if(len<charlen)rxseterr(info);
3104    if(info->persist && (info->wrpos=ftell(fp))<0) info->wrpos=0;
3105    if(info->errnum || setrcflag)rcset(info->errnum-Eerrno,Enotready,name);
3106    stackint(charlen-len);
3107 }
3108 
rxchars(argc)3109 void rxchars(argc)
3110 int argc;
3111 {
3112    rxchars2(argc,0);
3113 }
rxlines(argc)3114 void rxlines(argc)
3115 int argc;
3116 {
3117    rxchars2(argc,1);
3118 }
3119 
rxchars2(argc,line)3120 void rxchars2(argc,line) /* = rxchars(argc) if line==0, or rxlines(argc) o/w */
3121 int argc,line;
3122 {
3123    long chars;
3124    long(filepos);
3125    int lines;
3126    char *name=0;
3127    int len;
3128    struct fileinfo *info;
3129    struct stat buf;
3130    int ch,c2;
3131    FILE *fp;
3132    if(argc==1){
3133       name=delete(&len);
3134       if(len<=0)name=0;
3135       else
3136          if(memchr(name,0,len))die(Ecall);
3137          else name[len]=0;
3138    }
3139    else if(argc)die(Ecall);
3140    if(!name)name="stdin";
3141    info=(struct fileinfo *)hashget(1,name,&len);
3142    if(info && !info->fp){
3143       rcset(info->errnum-Eerrno,Enotready,name);
3144       stack("0",1);
3145       return;
3146    }
3147    if(info && info->wr<0){
3148       info->errnum=Eread;
3149       rcset(Eread-Eerrno,Enotready,name);
3150       stack("0",1);
3151       return;
3152    }
3153    if(info){
3154 #ifdef FSTAT_FOR_CHARS  /* fstat appears to be quicker (and more
3155                            correct) than seeking to EOF and back. */
3156       if(   info->persist &&
3157             !fstat(fileno(info->fp),&buf) &&
3158             S_ISREG(buf.st_mode)){
3159          if(info->lastwr || (filepos=ftell(info->fp))<0)
3160             filepos=info->rdpos;
3161          chars=buf.st_size-filepos;
3162          if(chars<0)chars=0;
3163       } else
3164 #endif
3165       {
3166          if(info->lastwr)fseek(info->fp,info->rdpos,0);
3167          if(ioctl(fileno(info->fp),FIONREAD,&chars))chars=0;
3168          chars+=_CNT(info->fp); /* add the number of buffered chars */
3169       }
3170       if(line && info->persist && (filepos=ftell(info->fp))>=0){
3171          lines=0;
3172          c2='\n';
3173          while((ch=getc(info->fp))!=EOF){ /* count lines */
3174             if(ch=='\n')lines++;
3175             c2=ch;
3176          }
3177          if(c2!='\n')lines++;
3178          fseek(info->fp,filepos,0);
3179       }
3180       else lines=(chars>0);
3181    }
3182    else { /* Not open.  Try to open it (to see whether we have access) */
3183           /* Funny thing is, we only make a fileinfo structure for it if
3184              there is an error (to hold the error number). */
3185       chars=lines=0;
3186       if(!(fp=fopen(name,"r"))){
3187          info=fileinit(name,name,fp);
3188          info->errnum=errno+Eerrno;
3189          rcset(errno,Enotready,name);
3190       }
3191       else if(fstat(fileno(fp),&buf)){
3192          info=fileinit(name,name,fp);
3193          info->errnum=errno+Eerrno;
3194          rcset(errno,Enotready,name);
3195          /* file is still open, but that's OK since its info is stored */
3196       }
3197       else if(!S_ISREG(buf.st_mode)){
3198          /* Not a regular file.  Sometimes we are allowed to fopen a directory,
3199             in which case EISDIR should be reported.  Otherwise, since we
3200             were allowed to open the file, assume it is a readable file with
3201             no characters (e.g. a tty) and do not report an error. */
3202          if(S_ISDIR(buf.st_mode)){
3203             fclose(fp);
3204             info=fileinit(name,cnull,(FILE *)0);
3205             info->errnum=EISDIR+Eerrno;
3206             rcset(EISDIR,Enotready,name);
3207          }
3208          else fclose(fp);
3209       }
3210       else{
3211          chars=buf.st_size;
3212          if(line){    /* Count lines */
3213             c2='\n';
3214             while((ch=getc(fp))!=EOF){
3215                if(ch=='\n')lines++;
3216                c2=ch;
3217             }
3218             if(c2!='\n')lines++;
3219          }
3220          else lines=(chars>0);
3221          fclose(fp);
3222       }
3223    }
3224    if(line)stackint(lines);
3225    else stackint((int)chars); /* Ahem! */
3226 }
3227 
rxclose(argc)3228 void rxclose(argc)
3229 int argc;
3230 {
3231    char *name;
3232    int len;
3233    if(argc!=1)die(Ecall);
3234    name=delete(&len);
3235    if(memchr(name,0,len))die(Ecall);
3236    else name[len]=0;
3237    if(!len)die(Ecall);
3238    stackint(fileclose(name));
3239 }
3240 
rxpclose(argc)3241 void rxpclose(argc)
3242 int argc;
3243 {
3244    char *name;
3245    int len;
3246    int rc;
3247    char *ptr;
3248    struct fileinfo *info;
3249    if(argc!=1)die(Ecall);
3250    name=delete(&len);
3251    if(memchr(name,0,len))die(Ecall);
3252    else name[len]=0;
3253    if(!len)die(Ecall);
3254    ptr=hashsearch(1,name,&len);
3255    if(len&&(info=(struct fileinfo *)(((hashent *)ptr)->value))){
3256       if(info->fp)rc=pclose(info->fp);
3257       else rc=-1;
3258       if(info->fp && rc<0)fclose(info->fp); /* if error, close anyway */
3259       free((char*)info);
3260       ((hashent *)ptr)->value=0;
3261    }
3262    else rc=0;
3263    if(rc==-1)stack("-1",2);
3264    else stackint((char)(rc/256));
3265 }
3266 
rxfileno(argc)3267 void rxfileno(argc)
3268 int argc;
3269 {
3270    char *name;
3271    int len;
3272    struct fileinfo *info;
3273    if(argc!=1)die(Ecall);
3274    name=delete(&len);
3275    if(memchr(name,0,len))die(Ecall);
3276    else name[len]=0;
3277    if(!len)die(Ecall);
3278    if(!(info=(struct fileinfo *)hashget(1,name,&len)) || !(info->fp))
3279       stack("-1",2);
3280    else stackint(fileno(info->fp));
3281 }
3282 
rxftell(argc)3283 void rxftell(argc)
3284 int argc;
3285 {
3286    char *name;
3287    int len;
3288    struct fileinfo *info;
3289    if(argc!=1)die(Ecall);
3290    name=delete(&len);
3291    if(memchr(name,0,len))die(Ecall);
3292    else name[len]=0;
3293    if(!len)die(Ecall);
3294    if(!(info=(struct fileinfo *)hashget(1,name,&len)) || !(info->fp))len=-1;
3295    else len=ftell(info->fp); /* Ahem! */
3296    if(len>=0)len++;
3297    stackint(len);
3298 }
3299 
rxquery2(stream,info,param,len)3300 void rxquery2(stream,info,param,len) /* used for stream(file,"c","query ...") */
3301 char *stream;
3302 struct fileinfo *info;
3303 char *param;
3304 int len;
3305 {
3306    struct stat st;
3307    struct tm *tp;
3308    char *name;
3309    char *cp;
3310    char *dir;
3311    static char tmp[MAXPATHLEN];
3312    static char curdir[MAXPATHLEN];
3313    int statrc;
3314    int fd=-1;
3315    /* if the stream is open, fstat it, otherwise stat the named file */
3316    if (info && info->fp) {
3317       fd=fileno(info->fp);
3318       statrc=fstat(fd,&st);
3319    }
3320    else statrc=stat(stream,&st);
3321    if (statrc) { /* answer is "" if the file does not exist */
3322       stack(cnull,0);
3323       return;
3324    }
3325    tp=localtime(&st.st_mtime);
3326    param[len]=0;
3327    if (!strcasecmp(param,"datetime")) {
3328       sprintf(tmp,"%02d-%02d-%02d %02d:%02d:%02d",
3329          tp->tm_mon+1,tp->tm_mday,tp->tm_year%100,
3330          tp->tm_hour,tp->tm_min,tp->tm_sec);
3331       stack(tmp,strlen(tmp));
3332       return;
3333    }
3334    if (!strcasecmp(param,"exists")) {
3335       if (fd>=0) { /* stream is open; fetch the associated file name */
3336          name=(char*)(info+1);
3337          if (!name[0]) { /* no name known so return the stream name */
3338             stack(stream,strlen(stream));
3339             return;
3340          }
3341          if (stat(name,&st)) {
3342             /* name was known but the file does not seem to exist */
3343             stack(stream,strlen(stream));
3344             return;
3345          }
3346       }
3347       else name=stream; /* use the supplied name */
3348       /* since the stat worked the file exists so qualify and return it */
3349       /* (files of form "/foo" don't need qualification) */
3350       if (getcwd(curdir,sizeof curdir) && curdir[0]=='/' &&
3351           (cp=strrchr(name,'/')) != name) {
3352          dir=curdir;
3353          if (cp && cp-name<sizeof tmp) {
3354             memcpy(tmp,name,cp-name);
3355             tmp[cp-name]=0;
3356             if (!chdir(tmp) && getcwd(tmp,sizeof tmp) && tmp[0]=='/') {
3357                name=cp+1;
3358                dir=tmp;
3359             }
3360             chdir(curdir);
3361          }
3362          /* the answer is now dir concatenated to name */
3363          /* In case dir was not found or name is just '.', remove leading '.' */
3364          if (name[0]=='.') {
3365             if (name[1]=='/') name+=2;
3366             else if (!name[1]) name++;
3367          }
3368          if (strlen(name)+strlen(dir)+1 < sizeof tmp) {
3369             strcat(dir,"/");
3370             strcat(dir,name);
3371             name=dir;
3372          }
3373       }
3374       stack(name,strlen(name));
3375       return;
3376    }
3377    if (!strcasecmp(param,"handle")) {
3378       if (fd<0) stack(cnull,0);
3379       else stackint(fd);
3380       return;
3381    }
3382    if (!strcasecmp(param,"size")) {
3383       if (S_ISREG(st.st_mode)) stackint(st.st_size);
3384       else stack("0",1);
3385       return;
3386    }
3387    if (!strcasecmp(param,"streamtype")) {
3388       if (fd<0) stack("UNKNOWN",7);
3389       else if (info->persist) stack("PERSISTENT",10);
3390       else stack("TRANSIENT",9);
3391       return;
3392    }
3393    if (!strcasecmp(param,"timestamp")) {
3394       sprintf(tmp,"%04d-%02d-%02d %02d:%02d:%02d",
3395          tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,
3396          tp->tm_hour,tp->tm_min,tp->tm_sec);
3397       stack(tmp,strlen(tmp));
3398       return;
3399    }
3400    die(Ecall);
3401 }
3402 
rxstream(argc)3403 void rxstream(argc)
3404 int argc;
3405 {
3406    char *stream;
3407    char option='S';
3408    char *command=0;
3409    char *param;
3410    int comlen;
3411    int len;
3412    int isnull=0;
3413    int exist;
3414    char *answer;
3415    struct fileinfo *info;
3416    if(argc==3){
3417       command=delete(&comlen);
3418       argc--;
3419       if(comlen<=0)die(Ecall);
3420    }
3421    if(argc==2){
3422       stream=delete(&len);
3423       argc--;
3424       if(len==0)die(Ecall);
3425       if(len>0)option=stream[0]&0xdf;
3426    }
3427    if(argc!=1)die(Ecall);
3428    stream=delete(&len);
3429    if(len<0)die(Ecall);
3430    if(len==0){stream="stdin";isnull=1;}
3431    else {
3432       if(memchr(stream,0,len))die(Ecall);
3433       stream[len]=0;
3434    }
3435    info=(struct fileinfo *)hashget(1,stream,&exist);
3436    switch(option){
3437       case 'D': if(command)die(Ecall);
3438          if(!info)answer="Stream is not open";
3439          else if(!info->errnum)answer="Ready";
3440          else answer=message(info->errnum);
3441          stack(answer,strlen(answer));
3442          return;
3443       case 'S': if(command)die(Ecall);
3444          if(!info)stack("UNKNOWN",7);
3445          else if(!info->errnum)stack("READY",5);
3446          else if(info->errnum==Eeof+Eerrno || info->errnum<Eerrno)
3447             stack("NOTREADY",8);
3448          else stack("ERROR",5);
3449          return;
3450       case 'C': break; /* out of the switch to do the work */
3451       default: die(Ecall);
3452    }
3453    if(!command)die(Ecall);
3454    param=command;
3455    while(comlen--&& *param++!=' ');    /* Find the command end */
3456    if(comlen>=0){
3457       param[-1]=0;                     /* terminate the command */
3458       while(comlen--&& *param++==' '); /* Find the parameter */
3459       comlen++,param--;
3460    }
3461    else param[0]=comlen=0;
3462    /***/if(!strcasecmp(command,"close")){ /* syntax: "close" */
3463       if(comlen)die(Ecall);
3464       stackint(fileclose(stream));
3465    }
3466    else if(!strcasecmp(command,"fdopen")){/* syntax: "fdopen [mode][,n]" */
3467       char *n;
3468       if (isnull) die(Ecall);
3469       for(len=0;len<comlen&&param[len]!=','&&param[len]!=' ';len++);
3470       comlen-=len+1;
3471       for(n=param+len+1;comlen>0&&n[0]==' ';n++,comlen--);
3472       if(comlen<0)comlen=0;
3473       rxfdopen2(stream,param,len,n,comlen);
3474    }
3475    else if(!strcasecmp(command,"fileno")){/* syntax: "fileno" */
3476       if(info && info->fp)stackint(fileno(info->fp));
3477       else stack("-1",2);
3478    }
3479    else if(!strcasecmp(command,"flush")){ /* syntax: "flush" */
3480       if (isnull) die(Ecall);
3481       if(info && info->fp) {
3482          int answer=fflush(info->fp);
3483          if (answer<0) rxseterr(info);
3484          if(info->errnum || setrcflag)rcset(info->errnum-Eerrno,Enotready,stream);
3485          stackint(answer);
3486       }
3487       else stack("-1",2);
3488    }
3489    else if(!strcasecmp(command,"ftell")){ /* syntax: "ftell" */
3490       if(info && info->fp)stackint(ftell(info->fp));
3491       else stack("-1",2);
3492    }
3493    else if(!strcasecmp(command,"open")){  /* syntax: "open [mode][,path]" */
3494       char *path=0;
3495       if (isnull) die(Ecall);
3496       /* for compatibility, accept "open both *", "open write append" and */
3497       /* "open write replace" before parsing the usual parameters. */
3498       if (comlen==12 && !strncasecmp(param,"write append",comlen)) {
3499          param="a";
3500          len=1;
3501          comlen=0;
3502       }
3503       else if (comlen==13 && !strncasecmp(param,"write replace",comlen)) {
3504          param="w";
3505          len=1;
3506          comlen=0;
3507       }
3508       else if (comlen>4 && !strncasecmp(param,"both",5)){
3509          if (comlen==4) {
3510             param="a";
3511          }
3512          else if (comlen==11 && !strncasecmp(param+4," append",7)) {
3513             param="a";
3514          }
3515          else if (comlen==12 && !strncasecmp(param+4," replace",8)) {
3516             param="w";
3517          }
3518          else die(Ecall);
3519          len=1;
3520          comlen=0;
3521       }
3522       else {
3523          for(len=0;len<comlen&&param[len]!=','&&param[len]!=' ';len++);
3524          comlen-=len+1;
3525          for(path=param+len+1;comlen>0&&path[0]==' ';path++,comlen--);
3526          if(comlen<0)comlen=0;
3527       }
3528       rxopen2(stream,param,len,path,comlen);
3529    }
3530    else if(!strcasecmp(command,"pclose")){/* syntax: "pclose" */
3531       char *ptr=hashsearch(1,stream,&exist);
3532       int rc;
3533       if(exist&&(info=(struct fileinfo *)(((hashent *)ptr)->value))){
3534          if(info->fp)rc=pclose(info->fp);
3535          else rc=-1;
3536          if(info->fp && rc<0)fclose(info->fp); /* if error, close anyway */
3537          free((char*)info);
3538          ((hashent *)ptr)->value=0;
3539       }
3540       else rc=0;
3541       if(rc==-1)stack("-1",2);
3542       else stackint((char)(rc/256));
3543    }
3544    else if(!strcasecmp(command,"popen")){ /* syntax: "popen [mode][,command]"*/
3545       char *cmd;
3546       if (isnull) die(Ecall);
3547       for(len=0;len<comlen&&param[len]!=','&&param[len]!=' ';len++);
3548       comlen-=len+1;
3549       for(cmd=param+len+1;comlen>0&&cmd[0]==' ';cmd++,comlen--);
3550       if(comlen<0)comlen=0;
3551       rxpopen2(stream,param,len,cmd,comlen);
3552    }
3553    else if(!strcasecmp(command,"query")){ /* syntax: "query <info>" */
3554       rxquery2(stream,info,param,comlen);
3555    }
3556    else if (!strcasecmp(command,"persistent")){ /* syntax: persistent */
3557       if (info) {
3558          info->persist=1;
3559          stack("0",1);
3560       }
3561       else stack("-1",2);
3562    }
3563    else if (!strcasecmp(command,"transient")) {/* syntax: transient */
3564       if (info) {
3565          info->persist=0;
3566          stack("0",1);
3567       }
3568       else stack("-1",2);
3569    }
3570    else die(Ecall);
3571 }
3572 
rxcondition(argc)3573 void rxcondition(argc)
3574 int argc;
3575 {
3576    char option='I';
3577    char *arg;
3578    int len;
3579    int which=sgstack[interplev].which;
3580    if(argc>1)die(Ecall);
3581    if(argc){
3582       arg=delete(&len);
3583       if(len<=0)die(Ecall);
3584       option=arg[0]&0xdf;
3585    }
3586    switch(option){
3587       case 'I': arg=sgstack[interplev].type==1?"SIGNAL":"CALL";  break;
3588       case 'C': arg=conditions[which];                           break;
3589       case 'D': for(len=interplev;len>=0 && !(arg=sgstack[len].data);len--);
3590                                                                  break;
3591       case 'S': arg=sgstack[interplev].delay  &(1<<which)? "DELAY":
3592                     sgstack[interplev].callon &(1<<which)? "ON":
3593                     sgstack[interplev].bits   &(1<<which)? "ON":
3594                     "OFF";                                       break;
3595       default: die(Ecall);
3596    }
3597    if(!sgstack[interplev].type)arg=0;
3598    if(!arg)stack("",0);
3599    else stack(arg,strlen(arg));
3600 }
3601 
getstring()3602 static char *getstring() { /* unstack a string, check and nul-terminate it */
3603    char *ans;
3604    int len;
3605    ans=delete(&len);
3606    if (len<=1) die(Ecall);
3607    ans[len]=0;
3608    while (len--) if (!ans[len]) die(Ecall);
3609    return ans;
3610 }
3611 
3612 #define INCL_RXFUNC
3613 #include "rexxsaa.h"
rxfuncadd(argc)3614 void rxfuncadd(argc)
3615 int argc;
3616 {
3617    char *entry;
3618    char *dll;
3619    char *func;
3620    int ans;
3621    int i;
3622    char C,c;
3623    if (argc!=3) die(Ecall);
3624    entry=getstring();
3625    dll=getstring();
3626    func=getstring();
3627    ans=RexxRegisterFunctionDll(func,dll,entry);
3628    if (ans) {
3629       stackint(ans);
3630       return;
3631    }
3632    /* Also register the uppercase of the function */
3633    for(i=0;(c=func[i]);i++) {
3634       C=uc(c);
3635       if (c!=C) {ans=1; func[i]=C;}
3636    }
3637    if (ans) ans=RexxRegisterFunctionDll(func,dll,entry);
3638    stackint(ans);
3639 }
3640 
rxfuncdrop(argc)3641 void rxfuncdrop(argc)
3642 int argc;
3643 {
3644    char *func;
3645    int i;
3646    int ans=0;
3647    char c,C;
3648    int doupper=0;
3649    if (argc!=1) die(Ecall);
3650    func=getstring();
3651    ans=RexxDeregisterFunction(func);
3652    /* also drop the uppercase of the function */
3653    for(i=0;(c=func[i]);i++) {
3654       C=uc(c);
3655       if (c!=C) {doupper=1; func[i]=C;}
3656    }
3657    if (doupper) ans=ans && RexxDeregisterFunction(func);
3658    if (ans) stack("1",1);
3659    else stack("0",1);
3660 }
3661 
rxfuncquery(argc)3662 void rxfuncquery(argc)
3663 int argc;
3664 {
3665    char *func;
3666    int i;
3667    int ans=0;
3668    char c,C;
3669    if (argc!=1) die(Ecall);
3670    func=getstring();
3671    if (RexxQueryFunction(func)) {
3672       /* Also query the uppercase of the function */
3673       for(i=0;(c=func[i]);i++) {
3674          C=uc(c);
3675          if (c!=C) {ans=1; func[i]=C;}
3676       }
3677       if (ans) ans=RexxQueryFunction(func);
3678       else ans=1;
3679    }
3680    if (ans) stack("1",1);
3681    else stack("0",1);
3682 }
3683