1 /****************************************
2 *  Computer Algebra System SINGULAR     *
3 ****************************************/
4 /*
5 * ABSTRACT: i/o system
6 */
7 #include "kernel/mod2.h"
8 
9 /* I need myfread in standalone_parser */
10 #ifndef STANDALONE_PARSER
11 #include "omalloc/omalloc.h"
12 #include "misc/options.h"
13 #include "reporter/reporter.h"
14 #include "kernel/oswrapper/feread.h"
15 #include "Singular/fevoices.h"
16 #include "Singular/subexpr.h"
17 #include "Singular/ipshell.h"
18 #include "Singular/sdb.h"
19 
20 #include "misc/mylimits.h"
21 #include <unistd.h>
22 
23 #ifdef HAVE_PWD_H
24 #include <pwd.h>
25 #endif
26 
27 #define fePutChar(c) fputc((unsigned char)(c),stdout)
28 /*0 implementation */
29 
30 
31 VAR char fe_promptstr[] ="  ";
32 VAR FILE *File_Profiling=NULL;
33 
34 // line buffer for reading:
35 // minimal value for MAX_FILE_BUFFER: 4*4096 - see Tst/Long/gcd0_l.tst
36 // this is an upper limit for the size of monomials/numbers read via the interpreter
37 #define MAX_FILE_BUFFER 4*4096
38 
39 /**************************************************************************
40 * handling of 'voices'
41 **************************************************************************/
42 
43 EXTERN_VAR int blocknest; /* scaner.l internal */
44 
45 VAR int    yy_noeof=0;     // the scanner "state"
46 VAR int    yy_blocklineno; // to get the lineno of the block start from scanner
47 VAR Voice  *currentVoice = NULL;
48 // FILE   *feFilePending; /*temp. storage for grammar.y */
49 
50 //static const char * BT_name[]={"BT_none","BT_break","BT_proc","BT_example",
51 //                               "BT_file","BT_execute","BT_if","BT_else"};
52 /*2
53 * the name of the current 'Voice': the procname (or filename)
54 */
55 const char sNoName_fe[]="_";
VoiceName()56 const char * VoiceName()
57 {
58   if ((currentVoice!=NULL)
59   && (currentVoice->filename!=NULL))
60     return currentVoice->filename;
61   return sNoName_fe;
62 }
63 
VoiceLine()64 int VoiceLine()
65 {
66   if ((currentVoice!=NULL)
67   && (currentVoice->curr_lineno>=0))
68     return currentVoice->curr_lineno;
69   return -1;
70 }
71 
72 /*2
73 * the calling chain of Voices
74 */
VoiceBackTrack()75 void VoiceBackTrack()
76 {
77   Voice *p=currentVoice;
78   while (p->prev!=NULL)
79   {
80     p=p->prev;
81     char *s=p->filename;
82     if (s==NULL)
83       PrintS("-- called from ? --\n");
84     else
85       Print("-- called from %s --\n",s);
86   }
87 }
88 
89 /*2
90 * init a new voice similar to the current
91 */
Next()92 void Voice::Next()
93 {
94   Voice *p=new Voice;
95   // OB: ???
96   // Hmm... when Singular is used as batch file
97   // then this voice is never freed
98   omMarkAsStaticAddr(p);
99   if (currentVoice != NULL)
100   {
101     currentVoice->curr_lineno=yylineno;
102     currentVoice->next=p;
103   }
104   p->prev=currentVoice;
105   currentVoice=p;
106   //PrintS("Next:");
107 }
108 
Typ()109 feBufferTypes Voice::Typ()
110 {
111   switch(typ)
112   {
113     case BT_proc:
114     case BT_example:
115     case BT_file:
116       return typ;
117     default:
118       if (prev==NULL) return (feBufferTypes)0;
119       return prev->Typ();
120   }
121 }
122 
123 /*2
124 * start the file 'fname' (STDIN is stdin) as a new voice (cf.VFile)
125 * return FALSE on success, TRUE if an error occurs (file cannot be opened)
126 */
newFile(char * fname)127 BOOLEAN newFile(char *fname)
128 {
129   currentVoice->Next();
130   //Print(":File%d(%s):%s(%x)\n",
131   //  currentVoice->typ,BT_name[currentVoice->typ],fname,currentVoice);
132   currentVoice->filename   = omStrDup(fname);
133   omMarkAsStaticAddr(currentVoice->filename);
134   if (strcmp(fname,"STDIN") == 0)
135   {
136     currentVoice->files = stdin;
137     currentVoice->sw = BI_stdin;
138     currentVoice->start_lineno = 1;
139   }
140   else
141   {
142     currentVoice->sw = BI_file; /* needed by exitVoice below */
143     currentVoice->files = feFopen(fname,"r",NULL,TRUE);
144     if (currentVoice->files==NULL)
145     {
146       exitVoice();
147       return TRUE;
148     }
149     currentVoice->start_lineno = 0;
150   }
151   yylineno=currentVoice->start_lineno;
152   //Voice *p=currentVoice;
153   //PrintS("-----------------\ncurr:");
154   //do
155   //{
156   //Print("voice fn:%s\n",p->filename);
157   //p=p->prev;
158   //}
159   //while (p!=NULL);
160   //PrintS("----------------\n");
161   return FALSE;
162 }
163 
newBuffer(char * s,feBufferTypes t,procinfo * pi,int lineno)164 void newBuffer(char* s, feBufferTypes t, procinfo* pi, int lineno)
165 {
166   currentVoice->Next();
167   //Print(":Buffer%d(%s):%s(%x)\n",
168   //  t,BT_name[t],pname,currentVoice);
169   if (pi!=NULL)
170   {
171     long l=strlen(pi->procname);
172     if (pi->libname!=NULL) l+=strlen(pi->libname);
173     currentVoice->filename = (char *)omAlloc(l+3);
174     *currentVoice->filename='\0';
175     if (pi->libname!=NULL) strcat(currentVoice->filename,pi->libname);
176     strcat(currentVoice->filename,"::");
177     strcat(currentVoice->filename,pi->procname);
178     currentVoice->pi       = pi;
179   }
180   else
181   {
182     if(currentVoice->prev!=NULL)
183     {
184       currentVoice->filename = omStrDup(currentVoice->prev->filename);
185       currentVoice->pi       = currentVoice->prev->pi;
186     }
187     else
188     {
189       currentVoice->filename = omStrDup("");
190       currentVoice->pi       = pi;
191     }
192   }
193   currentVoice->buffer   = s;
194   currentVoice->sw       = BI_buffer;
195   currentVoice->typ      = t;
196   switch (t)
197   {
198     case BT_execute:
199                      yylineno-=2;
200                      break;
201     case BT_proc:
202     case BT_example:
203                      currentVoice->oldb=myynewbuffer();
204                      yylineno = lineno+1;
205                      break;
206     case BT_if:
207     case BT_else:
208     case BT_break:
209                      yylineno = yy_blocklineno-1;
210                      break;
211     //case BT_file:
212     default:
213                      yylineno = 1;
214                      break;
215   }
216   //Print("start body (%s) at line %d\n",BT_name[t],yylineno);
217   currentVoice->start_lineno = yylineno;
218   //printf("start buffer typ %d\n",t);
219   //Voice *p=currentVoice;
220   //PrintS("-----------------\ncurr:");
221   //do
222   //{
223   //Print("voice fn:%s\n",p->filename);
224   //p=p->prev;
225   //}
226   //while (p!=NULL);
227   //PrintS("----------------\n");
228 }
229 
230 /*2
231 * exit Buffer of type 'typ':
232 * returns 1 if buffer type could not be found
233 */
exitBuffer(feBufferTypes typ)234 BOOLEAN exitBuffer(feBufferTypes typ)
235 {
236   //printf("exitBuffer: %d(%s),(%x)\n",
237   //  typ,BT_name[typ], currentVoice);
238   //Voice *p=currentVoice;
239   //PrintS("-----------------\ncurr:");
240   //do
241   //{
242   //Print("voice fn:%s\n",p->filename);
243   //p=p->prev;
244   //}
245   //while (p!=NULL);
246   //PrintS("----------------\n");
247   if (typ == BT_break)  // valid inside for, while. may skip if, else
248   {
249     /*4 first check for valid buffer type, skip if/else*/
250     Voice *p=currentVoice;
251     loop
252     {
253       if ((p->typ != BT_if)
254       &&(p->typ != BT_else))
255       {
256         if (p->typ == BT_break /*typ*/)
257         {
258           while (p != currentVoice)
259           {
260             exitVoice();
261           }
262           exitVoice();
263           return FALSE;
264         }
265         else return TRUE;
266       }
267       if (p->prev==NULL) break;
268       p=p->prev;
269     }
270     /*4 break not inside a for/while: return an error*/
271     if (/*typ*/ BT_break != currentVoice->typ) return 1;
272     return exitVoice();
273   }
274 
275   if ((typ == BT_proc)
276   || (typ == BT_example))
277   {
278     Voice *p=currentVoice;
279     loop
280     {
281       if ((p->typ == BT_proc)
282       || (p->typ == BT_example))
283       {
284         while (p != currentVoice)
285         {
286           exitVoice();
287         }
288         exitVoice();
289         return FALSE;
290       }
291       if (p->prev==NULL) break;
292       p=p->prev;
293     }
294   }
295   /*4 return not inside a proc: return an error*/
296   return TRUE;
297 }
298 
299 /*2
300 * jump to the beginning of a buffer
301 */
contBuffer(feBufferTypes typ)302 BOOLEAN contBuffer(feBufferTypes typ)
303 {
304   //printf("contBuffer: %d(%s),(%x)\n",
305   //  typ,BT_name[typ], currentVoice);
306   if (typ == BT_break)  // valid inside for, while. may skip if, else
307   {
308     // first check for valid buffer type
309     Voice *p=currentVoice;
310     loop
311     {
312       if ((p->typ != BT_if)
313         &&(p->typ != BT_else))
314       {
315         if (p->typ == BT_break /*typ*/)
316         {
317           while (p != currentVoice)
318           {
319             exitVoice();
320           }
321           yylineno = currentVoice->start_lineno;
322           currentVoice->fptr=0;
323           return FALSE;
324         }
325         else return TRUE;
326       }
327       if (p->prev==NULL) break;
328       p=p->prev;
329     }
330   }
331   return TRUE;
332 }
333 
334 /*2
335 * leave a voice: kill local variables
336 * setup everything from the previous level
337 * return 1 if leaving the top level, 0 otherwise
338 */
exitVoice()339 BOOLEAN exitVoice()
340 {
341   //printf("exitVoice: %d(%s),(%x)\n",
342   //  currentVoice->typ,BT_name[currentVoice->typ], currentVoice);
343   //{
344   //Voice *p=currentVoice;
345   //PrintS("-----------------\ncurr:");
346   //do
347   //{
348   //Print("voice fn:%s\n",p->filename);
349   //p=p->prev;
350   //}
351   //while (p!=NULL);
352   //PrintS("----------------\n");
353   //}
354   if (currentVoice!=NULL)
355   {
356     if (currentVoice->oldb!=NULL)
357     {
358       myyoldbuffer(currentVoice->oldb);
359       currentVoice->oldb=NULL;
360     }
361     if (currentVoice->filename!=NULL)
362     {
363       omFree((ADDRESS)currentVoice->filename);
364       currentVoice->filename=NULL;
365     }
366     if (currentVoice->buffer!=NULL)
367     {
368       omFree((ADDRESS)currentVoice->buffer);
369       currentVoice->buffer=NULL;
370     }
371     if ((currentVoice->prev==NULL)
372     &&(currentVoice->sw==BI_file)
373     &&(currentVoice->files!=stdin))
374     {
375       currentVoice->prev=feInitStdin(currentVoice);
376     }
377     if (currentVoice->prev!=NULL)
378     {
379       //printf("exitVoice typ %d(%s)\n",
380       //  currentVoice->typ,BT_name[currentVoice->typ]);
381       if (currentVoice->typ==BT_if)
382       {
383         currentVoice->prev->ifsw=2;
384       }
385       else
386       {
387         currentVoice->prev->ifsw=0;
388       }
389       if ((currentVoice->sw == BI_file)
390       && (currentVoice->files!=NULL))
391       {
392         fclose(currentVoice->files);
393       }
394       yylineno=currentVoice->prev->curr_lineno;
395       currentVoice->prev->next=NULL;
396     }
397     Voice *p=currentVoice->prev;
398     delete currentVoice;
399     currentVoice=p;
400   }
401   return currentVoice==NULL;
402 }
403 
404 /*2
405 * set prompt_char
406 * only called with currentVoice->sw == BI_stdin
407 */
feShowPrompt(void)408 static void feShowPrompt(void)
409 {
410   fe_promptstr[0]=prompt_char;
411 }
412 
413 /*2
414 * print echo (si_echo or TRACE), set my_yylinebuf
415 */
fePrintEcho(char * anf,char *)416 static int fePrintEcho(char *anf, char */*b*/)
417 {
418   char *ss=strrchr(anf,'\n');
419   int len_s;
420   if (ss==NULL)
421   {
422     len_s=strlen(anf);
423   }
424   else
425   {
426     len_s=ss-anf+1;
427   }
428   // my_yylinebuf:
429   int mrc=si_min(len_s,79)-1;
430   strcpy(my_yylinebuf,anf+(len_s-1)-mrc);
431   if (my_yylinebuf[mrc] == '\n') my_yylinebuf[mrc] = '\0';
432   mrc--;
433   // handle echo:
434   if (((si_echo>myynest)
435     && ((currentVoice->typ==BT_proc)
436       || (currentVoice->typ==BT_example)
437       || (currentVoice->typ==BT_file)
438       || (currentVoice->typ==BT_none)
439     )
440     && (strncmp(anf,";return();",10)!=0)
441    )
442   || (traceit&TRACE_SHOW_LINE)
443   || (traceit&TRACE_SHOW_LINE1))
444   {
445     if (currentVoice->typ!=BT_example)
446     {
447       if (currentVoice->filename==NULL)
448         Print("(none) %3d%c ",yylineno,prompt_char);
449       else
450         Print("%s %3d%c ",currentVoice->filename,yylineno,prompt_char);
451      }
452     {
453       fwrite(anf,1,len_s,stdout);
454       mflush();
455     }
456     if (traceit&TRACE_SHOW_LINE)
457     {
458       char c;
459       do
460       {
461         c=fgetc(stdin);
462 	if (c=='n') traceit_stop=1;
463       }
464       while(c!='\n');
465     }
466   }
467   else if (traceit&TRACE_SHOW_LINENO)
468   {
469     Print("{%d}",yylineno);
470     mflush();
471   }
472   else if (traceit&TRACE_PROFILING)
473   {
474     if (File_Profiling==NULL)
475       File_Profiling=fopen("smon.out","a");
476     if (File_Profiling==NULL)
477       traceit &= (~TRACE_PROFILING);
478     else
479     {
480       if (currentVoice->filename==NULL)
481         fprintf(File_Profiling,"(none) %d\n",yylineno);
482       else
483         fprintf(File_Profiling,"%s %d\n",currentVoice->filename,yylineno);
484     }
485   }
486 #ifdef HAVE_SDB
487   if ((blocknest==0)
488   && (currentVoice->pi!=NULL)
489   && (currentVoice->pi->trace_flag!=0))
490   {
491     sdb(currentVoice, anf, len_s);
492   }
493 #endif
494   prompt_char = '.';
495   return len_s;
496 }
497 
feReadLine(char * b,int l)498 int feReadLine(char* b, int l)
499 {
500   char *s=NULL;
501   int offset = 0; /* will not be used if s==NULL*/
502   // try to read from the buffer into b, max l chars
503   if (currentVoice!=NULL)
504   {
505     if((currentVoice->buffer!=NULL)
506     && (currentVoice->buffer[currentVoice->fptr]!='\0'))
507     {
508   NewBuff:
509       REGISTER int i=0;
510       long startfptr=currentVoice->fptr;
511       long tmp_ptr=currentVoice->fptr;
512       l--;
513       loop
514       {
515         REGISTER char c=
516         b[i]=currentVoice->buffer[tmp_ptr/*currentVoice->fptr*/];
517         i++;
518         if (yy_noeof==noeof_block)
519         {
520           if (c<' ')  yylineno++;
521           else if (c=='}') break;
522         }
523         else
524         {
525           if ((c<' ') ||
526           (c==';') ||
527           (c==')')
528           )
529             break;
530         }
531         if (i>=l) break;
532         tmp_ptr++;/*currentVoice->fptr++;*/
533         if(currentVoice->buffer[tmp_ptr/*currentVoice->fptr*/]=='\0') break;
534       }
535       currentVoice->fptr=tmp_ptr;
536       b[i]='\0';
537       if (currentVoice->sw==BI_buffer)
538       {
539         BOOLEAN show_echo=FALSE;
540         char *anf;
541         long len;
542         if (startfptr==0)
543         {
544           anf=currentVoice->buffer;
545           const char *ss=strchr(anf,'\n');
546           if (ss==NULL) len=strlen(anf);
547           else          len=ss-anf;
548           show_echo=TRUE;
549         }
550         else if (/*(startfptr>0) &&*/
551         (currentVoice->buffer[startfptr-1]=='\n'))
552         {
553           anf=currentVoice->buffer+startfptr;
554           const char *ss=strchr(anf,'\n');
555           if (ss==NULL) len=strlen(anf);
556           else          len=ss-anf;
557           yylineno++;
558           show_echo=TRUE;
559         }
560         if (show_echo)
561         {
562           char *s=(char *)omAlloc(len+2);
563           strncpy(s,anf,len+2);
564           s[len+1]='\0';
565           fePrintEcho(s,b);
566           omFree((ADDRESS)s);
567         }
568       }
569       currentVoice->fptr++;
570       return i;
571     }
572     // no buffer there or e-o-buffer or eoln:
573     if (currentVoice->sw!=BI_buffer)
574     {
575       currentVoice->fptr=0;
576       if (currentVoice->buffer==NULL)
577       {
578         currentVoice->buffer=(char *)omAlloc(MAX_FILE_BUFFER-sizeof(ADDRESS));
579         omMarkAsStaticAddr(currentVoice->buffer);
580       }
581     }
582     offset=0;
583   NewRead:
584     yylineno++;
585     if (currentVoice->sw==BI_stdin)
586     {
587       feShowPrompt();
588       s=fe_fgets_stdin(fe_promptstr,
589                        &(currentVoice->buffer[offset]),
590                        omSizeOfAddr(currentVoice->buffer)-1-offset);
591       //int i=0;
592       //if (s!=NULL)
593       //  while((s[i]!='\0') /*&& (i<MAX_FILE_BUFFER)*/) {s[i] &= (char)127;i++;}
594     }
595     else if (currentVoice->sw==BI_file)
596     {
597       fseek(currentVoice->files,currentVoice->ftellptr,SEEK_SET);
598       s=fgets(currentVoice->buffer+offset,(MAX_FILE_BUFFER-1-sizeof(ADDRESS))-offset,
599               currentVoice->files);
600       if (s!=NULL)
601       {
602         currentVoice->ftellptr=ftell(currentVoice->files);
603         // ftell returns -1 for non-seekable streams, such as pipes
604         if (currentVoice->ftellptr<0)
605           currentVoice->ftellptr=0;
606       }
607     }
608     //else /* BI_buffer */ s==NULL  => return 0
609     // done by the default return
610   }
611   if (s!=NULL)
612   {
613     // handle prot:
614     if (feProt&SI_PROT_I)
615     {
616       fputs(s,feProtFile);
617     }
618     int rc=fePrintEcho(s,b)+1;
619     //s[strlen(s)+1]='\0'; add an second \0 at the end of the string
620     s[rc]='\0';
621     // handel \\ :
622     rc-=3; if (rc<0) rc=0;
623     if ((s[rc]=='\\')&&(currentVoice->sw!=BI_buffer))
624     {
625       s[rc]='\0';
626       offset+=rc;
627       if (offset<(int)omSizeOfAddr(currentVoice->buffer)) goto NewRead;
628     }
629     goto NewBuff;
630   }
631   /* else if (s==NULL) */
632   {
633     const char *err;
634     switch(yy_noeof)
635     {
636       case noeof_brace:
637       case noeof_block:
638         err="{...}";
639         break;
640       case noeof_asstring:
641         err="till `.`";
642         break;
643       case noeof_string:
644         err="string";
645         break;
646       case noeof_bracket:
647         err="(...)";
648         break;
649       case noeof_procname:
650         err="proc";
651         break;
652       case noeof_comment:
653         err="/*...*/";
654         break;
655       default:
656         return 0;
657     }
658     Werror("premature end of file while reading %s",err);
659     return 0;
660   }
661 }
662 
663 /*2
664 * init all data structures
665 */
666 #ifndef STDIN_FILENO
667 #define STDIN_FILENO 0
668 #endif
feInitStdin(Voice * pp)669 Voice * feInitStdin(Voice *pp)
670 {
671   Voice *p = new Voice;
672   p->files = stdin;
673   p->sw = (isatty(STDIN_FILENO)) ? BI_stdin : BI_file;
674   if ((pp!=NULL) && (pp->sw==BI_stdin) && (pp->files==stdin))
675   {
676     p->files=freopen("/dev/tty","r",stdin);
677     //stdin=p->files;
678     if (p->files==NULL)
679     {
680       p->files = stdin;
681       p->sw = BI_file;
682     }
683     else
684       p->sw = BI_stdin;
685   }
686   p->filename   = omStrDup("STDIN");
687   p->start_lineno   = 1;
688   omMarkAsStaticAddr(p);
689   omMarkAsStaticAddr(p->filename);
690   return p;
691 }
692 #endif
693 
694