1 /* ---------------------------------------------------------------------
2    $Id: redfront.c 5280 2020-02-20 09:55:58Z arthurcnorman $
3    ---------------------------------------------------------------------
4    (c) 1999-2009 A. Dolzmann and T. Sturm, 1999-2014 T. Sturm
5    ---------------------------------------------------------------------
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9 
10       * Redistributions of source code must retain the relevant
11    	copyright notice, this list of conditions and the following
12 	disclaimer.
13       * Redistributions in binary form must reproduce the above
14 	copyright notice, this list of conditions and the following
15 	disclaimer in the documentation and/or other materials provided
16 	with the distribution.
17 
18    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22    OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 
31 #include "redfront.h"
32 #include "findself.h"
33 
34 /* Exportable variables */
35 
36 int reduceProcessID;
37 
38 HANDLE_T MeToReduce[2];
39 HANDLE_T ReduceToMe[2];
40 
41 #ifdef NATIVE_WINDOWS
42 
43 #include <windows.h>
44 
redread(HANDLE_T h,void * buffer,int len)45 int redread(HANDLE_T h, void *buffer, int len)
46 {   DWORD res;
47     ReadFile(h, buffer, len, &res, NULL);
48     return res;
49 }
50 
redwrite(HANDLE_T h,void * buffer,int len)51 int redwrite(HANDLE_T h, void *buffer, int len)
52 {   DWORD res;
53     WriteFile(h, buffer, len, &res, NULL);
54     return res;
55 }
56 
redclose(HANDLE_T h)57 void redclose(HANDLE_T h)
58 {   CloseHandle(h);
59 }
60 
61 /*
62  * This does broadly what the Unis-style "pipe()" call does and sets up
63  * an array with entries that are the two ends of a new pipe.
64  */
65 
pipe(HANDLE_T * r)66 int pipe(HANDLE_T *r)
67 {  SECURITY_ATTRIBUTES sec;
68    sec.nLength = sizeof(sec);
69    sec.lpSecurityDescriptor = NULL;
70    sec.bInheritHandle = TRUE;
71    if (CreatePipe(&r[0], &r[1], &sec, 0) == 0) return -1;
72    else return 0;
73 }
74 
75 #else /* not NATIVE_WINDOWS */
76 
redread(HANDLE_T h,void * buffer,int len)77 int redread(HANDLE_T h, void *buffer, int len)
78 {   return read(h, buffer, len);
79 }
80 
redwrite(HANDLE_T h,void * buffer,int len)81 int redwrite(HANDLE_T h, void *buffer, int len)
82 {   return write(h, buffer, len);
83 }
84 
redclose(HANDLE_T h)85 void redclose(HANDLE_T h)
86 {   close(h);
87 }
88 
89 #endif
90 
91 int verbose = 0;
92 int unicode = 0;
93 int color = 1;
94 char *memory=NULL;
95 int xargstart;
96 
97 #define DEFAULT_REDFRONTCOLOR MAGENTA /* REDFRONT output */
98 #define DEFAULT_NORMALCOLOR USER      /* REDUCE terminal output */
99 #define DEFAULT_PROMPTCOLOR BLACK     /* REDUCE prompt */
100 #define DEFAULT_INPUTCOLOR RED	      /* REDUCE input line */
101 #define DEFAULT_OUTPUTCOLOR BLUE      /* REDUCE mathprint output */
102 #define DEFAULT_DEBUGCOLOR CYAN	      /* REDFRONT DEBUG output" */
103 
104 int redfrontcolor = DEFAULT_REDFRONTCOLOR; /* REDFRONT output */
105 int normalcolor = DEFAULT_NORMALCOLOR;	   /* REDUCE terminal output */
106 int promptcolor = DEFAULT_PROMPTCOLOR;	   /* REDUCE prompt */
107 int inputcolor = DEFAULT_INPUTCOLOR;	   /* REDUCE input line */
108 int outputcolor = DEFAULT_OUTPUTCOLOR;	   /* REDUCE mathprint output */
109 int debugcolor = DEFAULT_DEBUGCOLOR;	   /* REDFRONT DEBUG output */
110 
111 
112 
113 
114 
115 
main(int argc,char ** argv,char ** envp)116 int main(int argc,char **argv,char **envp) {
117   char **nargv;
118 
119   find_program_directory(argv[0]);
120 
121   parse_args(argc,argv);
122 
123   deb_init();
124 
125   line_init();
126 
127   line_init_history();
128 
129   init_channels();
130 
131   /* I am preparing the argument for the child's execv() here, because malloc()
132      after fork() might be problematic. */
133   nargv = create_call(argc,argv);
134 /*
135  * Native Windows does not provide a nice easy version of "fork", but here
136  * the child process will do little more than use execv to launch Reduce
137  * as a sub-process. It does so keeping stdin and stdout links so that the
138  * sub-process is accessible via a pipe. With windows this all has to be
139  * different code!
140  */
141 #ifdef NATIVE_WINDOWS
142 
143 // In (at least) early 2020 this was failing when I did not force
144 // virtual terminal processing for console output. The symptom was
145 // that escape sequences intended to change text colour were displayed
146 // as text rather than acted on. This arose under CMD. In a powershell
147 // the escape sequences are swallowed but it looks a bit as if colour
148 // changes do not then happen. ACN January 2020.
149   HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
150   DWORD oldMode;
151   GetConsoleMode(hOut, &oldMode);
152   SetConsoleMode(hOut, oldMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
153 
154   print_banner(verbose);
155   child(nargv); // Creates child process that runs Reduce, with pipes
156                 // to connect it to this process.
157   free(nargv);
158   parent();
159   SetConsoleMode(hOut, oldMode);
160 
161 #else
162 
163   print_banner(verbose);
164   if ((reduceProcessID = fork())) {  /* I am not the child */
165 
166     if (reduceProcessID < 0) {  /* Failure */
167       perror("cannot fork()");
168       rf_exit(-1);
169     }
170 
171     deb_fprintf(stderr,"parent: process alive - fork()=%d\n",reduceProcessID);
172 
173     free(nargv);
174 
175     parent();
176 
177   } else {  /* I am the child */
178 
179     deb_fprintf(stderr,"child: process alive - fork()=%d\n",reduceProcessID);
180 
181     child(nargv);
182   }
183 #endif
184 
185   return -1;
186 }
187 
188 
parse_args(int argc,char ** argv)189 void parse_args(int argc,char **argv) {
190   int c;
191 //  extern char *optarg;
192 //  extern int optind;
193   const char *os;
194   int errflg=0;
195 
196 #ifdef PSL
197   os = "bc:huvVm:";
198 #else
199   os = "bc:huvV";
200 #endif
201   while ((c = getopt(argc, argv, os)) != EOF)
202     switch (c) {
203     case 'h':
204       print_help(argv[0]);
205       rf_exit(1);
206       break;
207     case 'b':
208       color = 0;
209       break;
210     case 'c':
211       errflg += parse_colarg(optarg);
212       break;
213     case 'u':
214       unicode = 1;
215       break;
216     case 'v':
217     case 'V':
218       verbose = 1;
219       break;
220     case 'm':
221       memory = optarg;
222       break;
223     case '?':
224       errflg++;
225     }
226 
227   if (errflg) {
228     print_usage(argv[0]);
229     rf_exit(2);
230   }
231 
232   if (strcmp(argv[optind - 1],"--") == 0)
233   { // <options> + "--"
234     xargstart = optind;
235   }
236 #ifdef PSL
237   else if (memory == NULL && optind == argc - 1)
238   { // <options> + <memarg>
239     memory = argv[optind];
240     xargstart = argc;
241   }
242   else if (memory == NULL &&
243 	   optind < argc - 1 && strcmp(argv[optind + 1],"--") == 0)
244   { // <options> + <memarg> + "--"
245     memory = argv[optind];
246     xargstart = optind + 2;
247   }
248 #endif
249   else if (optind == argc)
250   { // <options>
251     xargstart = argc;
252   }
253   else
254   { print_usage(argv[0]);
255     rf_exit(2);
256   }
257 
258 #ifdef PSL
259   memory = parse_memarg(memory==NULL ? "0" : memory,argv[0]);
260 #endif
261 }
262 
parse_colarg(char * s)263 int parse_colarg(char *s) {
264   /* Parse the parameter of -c as a string similar to LSCOLORS
265      documented in the ls manpage. The order is redfrontcolor,
266      normalcolor, promptcolor, inputcolor, outputcolor, debugcolor. The
267      default choice corresponds to "fxxxxxbxexgx". At present,
268      background specifications are ignored and upper case (bold face) is
269      mapped to lower case. */
270   int c;
271 
272   c = map_colour(*s++);
273   redfrontcolor = (c<0) ? DEFAULT_REDFRONTCOLOR : c;
274   if (*s == 0) return 0;
275 
276   s++;
277   if (*s == 0) return 0;
278 
279   c = map_colour(*s++);
280   normalcolor = (c<0) ? DEFAULT_NORMALCOLOR : c;
281   if (*s == 0) return 0;
282 
283   s++;
284   if (*s == 0) return 0;
285 
286   c = map_colour(*s++);
287   promptcolor = (c<0) ? DEFAULT_PROMPTCOLOR : c;
288   if (*s == 0) return 0;
289 
290   s++;
291   if (*s == 0) return 0;
292 
293   c = map_colour(*s++);
294   inputcolor = (c<0) ? DEFAULT_INPUTCOLOR : c;
295   if (*s == 0) return 0;
296 
297   s++;
298   if (*s == 0) return 0;
299 
300   c = map_colour(*s++);
301   outputcolor = (c<0) ? DEFAULT_OUTPUTCOLOR : c;
302   if (*s == 0) return 0;
303 
304   s++;
305   if (*s == 0) return 0;
306 
307   c = map_colour(*s++);
308   debugcolor = (c<0) ? DEFAULT_DEBUGCOLOR : c;
309   if (*s == 0) return 0;
310 
311   s++;
312   if (*s == 0) return 0;
313 
314   return 1;
315 }
316 
map_colour(int ch)317 int map_colour(int ch) {
318   switch (ch) {
319   case 'a': case 'A': return BLACK;
320   case 'b': case 'B': return RED;
321   case 'c': case 'C': return GREEN;
322   case 'd': case 'D': return YELLOW;
323   case 'e': case 'E': return BLUE;
324   case 'f': case 'F': return MAGENTA;
325   case 'g': case 'G': return CYAN;
326   case 'h': case 'H': return WHITE;
327   case 'x':           return USER;
328   default:            return -1;
329   }
330 }
331 
parse_memarg(char * argstr,char * name)332 char *parse_memarg(char *argstr,char *name) {
333   /* Only used for PSL */
334   char *nargv2;
335   char lchar;
336   int i;
337 
338   i = strlen(argstr) - 1;
339   lchar = tolower(argstr[i]);
340   if (!isdigit(lchar) && lchar != 'm' && lchar != 'k') {
341     print_usage(name);
342     rf_exit(1);
343   }
344   i--;
345 
346   for (; i >= 0; i--)
347     if (!isdigit(argstr[i])) {
348       print_usage(name);
349       rf_exit(1);
350     }
351 
352   if (lchar == 'm' ) {
353     nargv2 = (char *)malloc(strlen(argstr)-1+6+1);
354     strncpy(nargv2,argstr,strlen(argstr)-1);
355     nargv2[strlen(argstr)-1] = '\0';
356     sprintf(nargv2,"%s000000",nargv2);
357     return nargv2;
358   }
359 
360   if (lchar == 'k' ) {
361     nargv2 = (char *)malloc(strlen(argstr)-1+3+1);
362     strncpy(nargv2,argstr,strlen(argstr)-1);
363     nargv2[strlen(argstr)-1] = '\0';
364     sprintf(nargv2,"%s000",nargv2);
365     return nargv2;
366   }
367 
368   return argstr;
369 }
370 
print_usage(char name[])371 void print_usage(char name[]) {
372 #ifdef PSL
373   fprintf(stderr,
374           "usage: %s [-bhuvV] [-c COLORSPEC] [[-m] NUMBER[kKmM]]\n",name);
375 #else
376   fprintf(stderr,"usage: %s [-bhuvV] [-c COLORSPEC]\n",name);
377 #endif
378 }
379 
print_help(char name[])380 void print_help(char name[]) {
381   int w=color;
382 
383   color = 0;
384   print_banner(1);
385   color=w;
386 
387   fprintf(stderr,"A REDUCE frontend\n\n");
388 
389   print_usage(name);
390 
391   fprintf(stderr,"       -b\t\t\
392 black and white mode, i.e. do not use ANSI colors\n");
393   fprintf(stderr,"       -c COLORSPEC\t\
394 specify colors for redfront output, normal output,\n");
395   fprintf(stderr,"         \t\t\
396 prompt, input, math output, debug output. The default\n");
397   fprintf(stderr,"         \t\t\
398 is fxxxxxbxexgx - see LSCOLORS in the ls manpage for\n");
399   fprintf(stderr,"         \t\tdetails\n");
400   fprintf(stderr,"       -h\t\tthis help message\n");
401 #ifdef PSL
402   fprintf(stderr,"       -m NUMBER [kKmM]\t\
403 memory allocation in Bytes [KB|MB]\n");
404 #endif
405   fprintf(stderr,"       -u\t\tuse unicode characters\n");
406   fprintf(stderr,"       -v, -V\t\tverbose\n\n");
407 
408   fprintf(stderr,"Examples: %s -uv\n",name);
409 #ifdef PSL
410   fprintf(stderr,"          %s -c xxxxxxbxexgx -m 96m.\n\n",name);
411 #else
412   fprintf(stderr,"          %s -c xxxxxxbxexgx -v\n\n",name);
413 #endif
414   fprintf(stderr,"Use TAB for completion of filenames and Reduce switches.\n");
415   fprintf(stderr,"There is a manpage available.\n");
416 }
417 
print_banner(int vb)418 void print_banner(int vb) {
419   textcolor(redfrontcolor);
420 
421   if (vb) {
422     printf("%s %s/%d, built %s ...\n",
423 	   PACKAGE_NAME,
424 	   PACKAGE_VERSION,
425 #ifdef USE_PIPES
426 	   2*USE_PIPES,
427 #else
428            0,
429 #endif
430 	   BUILDTIME);
431     //    if (unicode) printf("%c%c",0xC2,0xA9); else printf("(C)");
432     printf("(C)");
433     printf(" 1999-2008 A. Dolzmann, 1999-2014 T. Sturm\n");
434     printf("Based on earlier projects by C. Cannam and W. Neun\n");
435     printf("Reports bugs to <%s>\n\n",PACKAGE_BUGREPORT);
436   } else {
437     printf("%s %s, built %s ...\n",PACKAGE_NAME,PACKAGE_VERSION,BUILDTIME);
438   }
439   textcolor(normalcolor);
440 }
441 
init_channels(void)442 void init_channels(void) {
443 #ifdef USE_PIPES
444     if (pipe(MeToReduce) < 0) {
445       perror("failed to create pipe MeToReduce\n");
446       sig_killChild();
447       rf_exit(-1);
448     }
449     if (pipe(ReduceToMe) < 0) {
450       perror("failed to create pipe ReduceToMe\n");
451       sig_killChild();
452       rf_exit(-1);
453     }
454 #else // USE_PIPES
455     if (socketpair(AF_UNIX, SOCK_STREAM, 0, MeToReduce) < 0) {
456       perror("cannot open socket MeToReduce");
457       sig_killChild();
458       rf_exit(-1);
459     }
460     if (socketpair(AF_UNIX, SOCK_STREAM, 0, ReduceToMe) < 0) {
461       perror("cannot open socket ReduceToMe");
462       sig_killChild();
463       rf_exit(-1);
464     }
465 #endif // USE_PIPES
466 }
467 
textcolor(int fg)468 int textcolor(int fg) {
469   static int currentcolor=DEFAULT_REDFRONTCOLOR;
470   int oldcolor;
471 
472   oldcolor = currentcolor;
473   currentcolor = fg;
474   if (fg == 9)
475     resetcolor();
476   else
477     textcolor1(0,fg,9);
478   return oldcolor;
479 }
480 
textcolor1(int attr,int fg,int bg)481 void textcolor1(int attr, int fg, int bg) {
482   if (color) {
483     char command[13];
484 
485     stextcolor1(command,attr,fg,bg);
486     printf("%s", command);
487     fflush(stdout);
488   }
489 }
490 
stextcolor1(char command[],int attr,int fg,int bg)491 void stextcolor1(char command[],int attr,int fg,int bg) {
492   sprintf(command,"%c[%d;%d;%dm", 0x1B, attr, fg + 30, bg + 40);
493 }
494 
resetcolor(void)495 void resetcolor(void) {
496   if (color) {
497     printf("%c[0m",0x1B);
498     fflush(stdout);
499   }
500 }
501 
vbprintf(const char * msg,...)502 int vbprintf(const char *msg,...) {
503   int ecode=0;
504   int oldcolor;
505   va_list ap;
506 
507   if (!verbose)
508     return 0;
509 
510   va_start(ap,msg);
511   oldcolor = textcolor(redfrontcolor);
512   ecode = vprintf(msg,ap);
513   textcolor(oldcolor);
514   va_end(ap);
515 
516   return ecode;
517 }
518 
rf_exit(int ecode)519 void rf_exit(int ecode) {
520   deb_cleanup();
521   resetcolor();
522   exit(ecode);
523 }
524 
525 #ifndef REDCSL
526 #define REDCSL "redcsl"
527 #endif
528 #ifndef BOOTSTRAPREDUCE
529 #define BOOTSTRAPREDUCE "bootstrapreduce"
530 #endif
531 #ifndef REDPSL
532 #define REDPSL "redpsl"
533 #endif
534 #ifndef BAT
535 #define BAT ".bat"
536 #endif
537 
create_call(int argc,char * argv[])538 char **create_call(int argc,char *argv[]) {
539   char **nargv;
540   char *reducename;
541   int tempfd;
542   int i,j,xa0;
543 
544   deb_fprintf(stderr,"entering create_call\n");
545 
546   nargv = (char **)malloc((argc - xargstart + 6)*sizeof(char *));
547 
548 #ifdef PSL
549    reducename = (char *)malloc(strlen(programDir) + 16);
550    sprintf(reducename, "%s/%s", programDir, REDPSL);
551 #ifdef NATIVE_WINDOWS
552    strcat(reducename, BAT);
553 #endif
554 
555   if ((tempfd = open(reducename,O_RDONLY)) == -1) {  /* Does not check x */
556     char errstr[1024];
557     sprintf(errstr,"cannot open PSL executable (%s)", reducename);
558     perror(errstr);
559     rf_exit(-1);
560   } else
561     close(tempfd);
562 
563 /*
564  * If I launch psl using the "redpsl" script than that will cope with
565  * selecting a default memory - so if none is specified by the user
566  * of rfpsl I do not have anything to do here.
567  */
568   j = 0;
569   nargv[j++] = reducename;
570   if (memory != NULL &&
571       strcmp(memory, "0") != 0)  /* Actually I think this just becomes
572                                     a regular extra argument */
573   { nargv[j++] = "-td";
574     nargv[j++] = memory;
575   }
576   for (i = xargstart; i < argc; i++)
577     nargv[j++] = argv[i];
578   nargv[j] = (char *)0;
579 
580 
581 #else  /* Now the CSL version, including the bootstrapreduce case */
582 
583    reducename = (char *)malloc(strlen(programDir) + 16);
584 #ifdef BOOT
585    sprintf(reducename, "%s/%s", programDir, BOOTSTRAPREDUCE);
586 #else
587    sprintf(reducename, "%s/%s", programDir, REDCSL);
588 #endif
589 #ifdef NATIVE_WINDOWS
590    strcat(reducename, BAT);
591 #endif
592 
593   if ((tempfd = open(reducename,O_RDONLY)) == -1)
594   {  char errstr[1024];
595      sprintf(errstr,"cannot open CSL executable (%s)", reducename);
596      perror(errstr);
597     rf_exit(-1);
598   }
599   else close(tempfd);
600 
601   j = 0;
602   nargv[j++] = reducename;
603   nargv[j++] = "-w";
604   nargv[j++] = "-b";
605   if (verbose)           /* This could perhaps be a simple general case arg */
606     nargv[j++] = "-V";
607   for (i = xargstart; i < argc; i++)
608     nargv[j++] = argv[i];
609   nargv[j] = (char *)0;
610 
611 #endif
612 
613   for (i = 0; nargv[i] != (char *)0; i++)
614     deb_fprintf(stderr,"argv[%d]=%s\n",i,nargv[i]);
615 
616   deb_fprintf(stderr,"leaving create_call\n");
617 
618   return nargv;
619 }
620 
621 /* end of redfront.c */
622