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(×tamp,&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(×tamp.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(×tamp,&tz);/* Make a timestamp if necessary */
573 timeflag|=2;
574 time=timestamp.tv_sec;
575 t2=localtime(×tamp.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&−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&−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&¶m[len]!=','&¶m[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&¶m[len]!=','&¶m[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&¶m[len]!=','&¶m[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