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