1 /****************************************
2 *  Computer Algebra System SINGULAR     *
3 ****************************************/
4 /*
5 * ABSTRACT: help system
6 */
7 
8 #include "kernel/mod2.h"
9 #include "omalloc/omalloc.h"
10 #include "misc/mylimits.h"
11 #include "resources/feResource.h"
12 #include "reporter/reporter.h"
13 #include "resources/omFindExec.h"
14 #include "reporter/si_signals.h"
15 
16 #include "ipid.h"
17 #include "ipshell.h"
18 #include "libparse.h"
19 #include "feOpt.h"
20 
21 #include "tok.h"
22 #include "fehelp.h"
23 
24 /*****************************************************************
25  *
26  * Declarations: Data  structures
27  *
28  *****************************************************************/
29 #define MAX_HE_ENTRY_LENGTH 160
30 typedef struct
31 {
32   char key[MAX_HE_ENTRY_LENGTH];
33   char node[MAX_HE_ENTRY_LENGTH];
34   char url[MAX_HE_ENTRY_LENGTH];
35   long  chksum;
36 } heEntry_s;
37 typedef  heEntry_s * heEntry;
38 
39 typedef void (*heBrowserHelpProc)(heEntry hentry, int br);
40 typedef BOOLEAN (*heBrowserInitProc)(int warn, int br);
41 
42 typedef struct
43 {
44   const char* browser;
45   heBrowserInitProc init_proc;
46   heBrowserHelpProc help_proc;
47   const char* required;
48   const char* action;
49 } heBrowser_s;
50 typedef heBrowser_s * heBrowser;
51 
52 /*****************************************************************
53  *
54  * Declarations: Local functions
55  *
56  *****************************************************************/
57 static char* strclean(char* str);
58 static BOOLEAN heKey2Entry(char* filename, char* key, heEntry hentry);
59 static int heReKey2Entry (char* filename, char* key, heEntry hentry);
60 static BOOLEAN strmatch(char* s, char* re);
61 static BOOLEAN heOnlineHelp(char* s);
62 static void heBrowserHelp(heEntry hentry);
63 static long heKeyChksum(char* key);
64 
65 // browser functions
66 static BOOLEAN heGenInit(int,int);    static void heGenHelp(heEntry hentry,int);
67                                       static void heBuiltinHelp(heEntry hentry,int);
68 static BOOLEAN heDummyInit(int,int);   static void heDummyHelp(heEntry hentry,int);
69 static BOOLEAN heEmacsInit(int,int);   static void heEmacsHelp(heEntry hentry,int);
70 
71 STATIC_VAR heBrowser heCurrentHelpBrowser = NULL;
72 STATIC_VAR int heCurrentHelpBrowserIndex= -1;
73 
74 
75 /*****************************************************************
76  *
77  * Definition: available help browsers
78  *
79  *****************************************************************/
80 // order is important -- first possible help is chosen
81 // moved to LIB/help.cnf
82 STATIC_VAR heBrowser_s *heHelpBrowsers=NULL;
83 
84 /*****************************************************************
85  *
86  * Implementation: public function
87  *
88  *****************************************************************/
feHelp(char * str)89 void feHelp(char *str)
90 {
91   str = strclean(str);
92   if (str == NULL) {heBrowserHelp(NULL); return;}
93 
94   if (strlen(str) > MAX_HE_ENTRY_LENGTH - 2)  // need room for extra **
95     str[MAX_HE_ENTRY_LENGTH - 3] = '\0';
96 
97   BOOLEAN key_is_regexp = (strchr(str, '*') != NULL);
98 
99 
100   heEntry_s hentry;
101   memset(&hentry,0,sizeof(hentry));
102   char* idxfile = feResource('x' /*"IdxFile"*/);
103 
104   // Try exact match of help string with key in index
105   if (!key_is_regexp && (idxfile != NULL) && heKey2Entry(idxfile, str, &hentry))
106   {
107     heBrowserHelp(&hentry);
108     return;
109   }
110 
111   // Try to match approximately with key in index file
112   if (idxfile != NULL)
113   {
114     if (heCurrentHelpBrowser == NULL) feHelpBrowser(NULL, 0);
115     assume(heCurrentHelpBrowser != NULL);
116 
117     StringSetS("");
118     int found = heReKey2Entry(idxfile, str, &hentry);
119 
120 
121     if (found == 0)
122     {
123       // try proc help and library help
124       if (! key_is_regexp && heOnlineHelp(str)) return;
125 
126       // Try to match with str*
127       char mkey[MAX_HE_ENTRY_LENGTH];
128       strcpy(mkey, str);
129       strcat(mkey, "*");
130       found = heReKey2Entry(idxfile, mkey, &hentry);
131       // Try to match with *str*
132       if (found == 0)
133       {
134         mkey[0] = '*';
135         strcpy(mkey + 1, str);
136         strcat(mkey, "*");
137         found = heReKey2Entry(idxfile, mkey, &hentry);
138       }
139 
140       // Print warning and return if nothing found
141       if (found == 0)
142       {
143         Warn("No help for topic '%s' (not even for '*%s*')", str, str);
144         WarnS("Try '?;'       for general help");
145         WarnS("or  '?Index;'  for all available help topics.");
146         return;
147       }
148     }
149 
150     // do help if unique match was found
151     if (found == 1)
152     {
153       heBrowserHelp(&hentry);
154       return;
155     }
156     // Print warning about multiple matches and return
157     if (key_is_regexp)
158       Warn("No unique help for '%s'", str);
159     else
160       Warn("No help for topic '%s'", str);
161     WarnS("Try one of");
162     char *matches=StringEndS();
163     PrintS(matches);
164     omFree(matches);
165     PrintLn();
166     return;
167   }
168 
169   // no idx file, let Browsers deal with it, if they can
170   strcpy(hentry.key, str);
171   *hentry.node = '\0';
172   *hentry.url = '\0';
173   hentry.chksum = 0;
174   heBrowserHelp(&hentry);
175 }
feBrowserFile()176 static void feBrowserFile()
177 {
178   FILE *f=feFopen("help.cnf","r",NULL,TRUE);
179   int br=0;
180   if (f!=NULL)
181   {
182     char buf[512];
183     while (fgets( buf, sizeof(buf), f))
184     {
185       if ((buf[0]!='#') && (buf[0]>' ')) br++;
186     }
187     fseek(f,0,SEEK_SET);
188     // for the 4(!) default browsers
189     heHelpBrowsers=(heBrowser_s*)omAlloc0((br+4)*sizeof(heBrowser_s));
190     br = 0;
191     while (fgets( buf, sizeof(buf), f))
192     {
193       if ((buf[0]!='#') && (buf[0]>' '))
194       {
195         char *name=strtok(buf,"!");
196         char *req=strtok(NULL,"!");
197         char *cmd=strtok(NULL,"!");
198         if ((name!=NULL) && (req!=NULL) && (cmd!=NULL))
199         {
200           while ((cmd[0]!='\0') && (cmd[strlen(cmd)-1]<=' '))
201             cmd[strlen(cmd)-1]='\0';
202           //Print("name %d >>%s<<\n\treq:>>%s<<\n\tcmd:>>%s<<\n",br,name,req,cmd);
203           heHelpBrowsers[br].browser=(char *)omStrDup(name);
204           heHelpBrowsers[br].init_proc=heGenInit;
205           heHelpBrowsers[br].help_proc=heGenHelp;
206           heHelpBrowsers[br].required=omStrDup(req);
207           heHelpBrowsers[br].action=omStrDup(cmd);
208           br++;
209         }
210         else
211         {
212           Print("syntax error in help.cnf, at line starting with %s\n",buf);
213         }
214       }
215     }
216     fclose(f);
217   }
218   else
219   {
220     // for the 4(!) default browsers
221     heHelpBrowsers=(heBrowser_s*)omAlloc0(4*sizeof(heBrowser_s));
222   }
223   heHelpBrowsers[br].browser="builtin";
224   heHelpBrowsers[br].init_proc=heGenInit;
225   heHelpBrowsers[br].help_proc=heBuiltinHelp;
226   heHelpBrowsers[br].required="i";
227   //heHelpBrowsers[br].action=NULL;
228   br++;
229   heHelpBrowsers[br].browser="dummy";
230   heHelpBrowsers[br].init_proc=heDummyInit;
231   heHelpBrowsers[br].help_proc=heDummyHelp;
232   //heHelpBrowsers[br].required=NULL;
233   //heHelpBrowsers[br].action=NULL;
234   br++;
235   heHelpBrowsers[br].browser="emacs";
236   heHelpBrowsers[br].init_proc=heEmacsInit;
237   heHelpBrowsers[br].help_proc=heEmacsHelp;
238   //heHelpBrowsers[br].required=NULL;
239   //heHelpBrowsers[br].action=NULL;
240   //br++;
241   //heHelpBrowsers[br].browser=NULL;
242   //heHelpBrowsers[br].init_proc=NULL;
243   //heHelpBrowsers[br].help_proc=NULL;
244   //heHelpBrowsers[br].required=NULL;
245   //heHelpBrowsers[br].action=NULL;
246 }
247 
feHelpBrowser(char * which,int warn)248 const char* feHelpBrowser(char* which, int warn)
249 {
250   int i = 0;
251 
252   // if no argument, choose first available help browser
253   if (heHelpBrowsers==NULL) feBrowserFile();
254   if (which == NULL || *which == '\0')
255   {
256     // return, if already set
257     if (heCurrentHelpBrowser != NULL)
258       return heCurrentHelpBrowser->browser;
259 
260     // First, try emacs, if emacs-option is set
261     if (feOptValue(FE_OPT_EMACS) != NULL)
262     {
263       while (heHelpBrowsers[i].browser != NULL)
264       {
265         if (strcmp(heHelpBrowsers[i].browser, "emacs") == 0 &&
266             (heHelpBrowsers[i].init_proc(0,i)))
267         {
268           heCurrentHelpBrowser = &(heHelpBrowsers[i]);
269           heCurrentHelpBrowserIndex=i;
270           goto Finish;
271         }
272         i++;
273       }
274       i=0;
275     }
276     while (heHelpBrowsers[i].browser != NULL)
277     {
278       if (heHelpBrowsers[i].init_proc(0,i))
279       {
280         heCurrentHelpBrowser = &(heHelpBrowsers[i]);
281         heCurrentHelpBrowserIndex=i;
282         goto Finish;
283       }
284       i++;
285     }
286     // should never get here
287     dReportBug("should never get here");
288   }
289 
290   // with argument, find matching help browser
291   while (heHelpBrowsers[i].browser != NULL &&
292          strcmp(heHelpBrowsers[i].browser, which) != 0)
293   {i++;}
294 
295   if (heHelpBrowsers[i].browser == NULL)
296   {
297     if (warn) Warn("No help browser '%s' available.", which);
298   }
299   else
300   {
301     // see whether we can init it
302     if (heHelpBrowsers[i].init_proc(warn,i))
303     {
304       heCurrentHelpBrowser = &(heHelpBrowsers[i]);
305       heCurrentHelpBrowserIndex=i;
306       goto Finish;
307     }
308   }
309 
310   // something went wrong
311   if (heCurrentHelpBrowser == NULL)
312   {
313     feHelpBrowser();
314     assume(heCurrentHelpBrowser != NULL);
315     if (warn)
316       Warn("Setting help browser to '%s'.", heCurrentHelpBrowser->browser);
317     return heCurrentHelpBrowser->browser;
318   }
319   else
320   {
321     // or, leave as is
322     if (warn)
323       Warn("Help browser stays at '%s'.",  heCurrentHelpBrowser->browser);
324     return heCurrentHelpBrowser->browser;
325   }
326 
327   Finish:
328   // update value of Browser Option
329   if (feOptSpec[FE_OPT_BROWSER].value == NULL ||
330       strcmp((char*) feOptSpec[FE_OPT_BROWSER].value,
331              heCurrentHelpBrowser->browser) != 0)
332   {
333     omfree(feOptSpec[FE_OPT_BROWSER].value);
334     feOptSpec[FE_OPT_BROWSER].value
335      = (void*) omStrDup(heCurrentHelpBrowser->browser);
336   }
337   return heCurrentHelpBrowser->browser;
338 }
339 
feStringAppendBrowsers(int warn)340 void  feStringAppendBrowsers(int warn)
341 {
342   int i;
343   StringAppendS("Available HelpBrowsers: ");
344 
345   i = 0;
346   if (heHelpBrowsers==NULL) feBrowserFile();
347   while (heHelpBrowsers[i].browser != NULL)
348   {
349     if (heHelpBrowsers[i].init_proc(warn,i))
350       StringAppend("%s, ", heHelpBrowsers[i].browser);
351     i++;
352   }
353   StringAppend("\nCurrent HelpBrowser: %s ", feHelpBrowser());
354 }
355 
356 
357 /*****************************************************************
358  *
359  * Implementation: local function
360  *
361  *****************************************************************/
362 // Remove whitspaces from beginning and end, return NULL if only whitespaces
strclean(char * str)363 static char* strclean(char* str)
364 {
365   if (str == NULL) return NULL;
366   char *s=str;
367   while ((*s <= ' ') && (*s != '\0')) s++;
368   if (*s == '\0') return NULL;
369   char *ss=s;
370   while (*ss!='\0') ss++;
371   ss--;
372   while ((*ss <= ' ') && (*ss != '\0'))
373   {
374     *ss='\0';
375     ss--;
376   }
377   if (*ss == '\0') return NULL;
378   return s;
379 }
380 
381 // Finds help entry for key:
382 // returns filled-in hentry and TRUE, on success
383 // FALSE, on failure
384 // Assumes that lines of idx file have the following form
385 // key\tnode\turl\tchksum\n (chksum ma be empty, then it is set to -1)
386 // and that lines are sorted alpahbetically w.r.t. index entries
heKey2Entry(char * filename,char * key,heEntry hentry)387 static BOOLEAN heKey2Entry(char* filename, char* key, heEntry hentry)
388 {
389   FILE* fd;
390   int c, k;
391   int kl, i;
392   *(hentry->key) = '\0';
393   *(hentry->url) = '\0';
394   *(hentry->node) = '\0';
395   hentry->chksum = 0;
396   if (filename == NULL || key == NULL)  return FALSE;
397   fd = fopen(filename, "r");
398   if (fd == NULL) return FALSE;
399   kl = strlen(key);
400 
401   k = key[0];
402   i = 0;
403   while ((c = getc(fd)) != EOF)
404   {
405     if (c < k)
406     {
407       /* Skip line */
408       while (getc(fd) != '\n') {};
409       if (i)
410       {
411         i=0;
412         k=key[0];
413       }
414     }
415     else if (c == k)
416     {
417       i++;
418       if (i == kl)
419       {
420         // \t must follow, otherwise only substring match
421         if (getc(fd) != '\t') goto Failure;
422 
423         // Now we found an exact match
424         if (hentry->key != key) strcpy(hentry->key, key);
425         // get node
426         i = 0;
427         while ((c = getc(fd)) != '\t' && c != EOF)
428         {
429           hentry->node[i] = c;
430           i++;
431         }
432         if (c == EOF) goto Failure;
433         if (hentry->node[0]=='\0')
434           strcpy(hentry->node,hentry->key);
435 
436         // get url
437         //hentry->node[i] = '\0';
438         i = 0;
439         while ((c = getc(fd)) != '\t' && c != EOF)
440         {
441           hentry->url[i] = c;
442           i++;
443         }
444         if (c == EOF) goto Failure;
445 
446         // get chksum
447         hentry->url[i] = '\0';
448 
449         if (si_fscanf(fd, "%ld\n", &(hentry->chksum)) != 1)
450         {
451           hentry->chksum = -1;
452         }
453         fclose(fd);
454         return TRUE;
455       }
456       else if (i > kl)
457       {
458         goto Failure;
459       }
460       else
461       {
462         k = key[i];
463       }
464     }
465     else
466     {
467       goto Failure;
468     }
469   }
470   Failure:
471   fclose(fd);
472   return FALSE;
473 }
474 
475 // return TRUE if s matches re
476 // FALSE, otherwise
477 // does not distinguish lower and upper cases
478 // inteprets * as wildcard
strmatch(char * s,char * re)479 static BOOLEAN strmatch(char* s, char* re)
480 {
481   if (s == NULL || *s == '\0')
482     return (re == NULL || *re == '\0' || strcmp(re, "*") == 0);
483   if (re == NULL || *re == '\0') return FALSE;
484 
485   int i;
486   char ls[MAX_HE_ENTRY_LENGTH + 1];
487   char rs[MAX_HE_ENTRY_LENGTH + 1];
488   char *l, *r, *ll, *rr;
489 
490   // make everything to lower case
491   i=1;
492   ls[0] = '\0';
493   do
494   {
495     if (*s >= 'A' && *s <= 'Z') ls[i] = *s + ('a' - 'A');
496     else ls[i] = *s;
497     i++;
498     s++;
499   } while (*s != '\0');
500   ls[i] = '\0';
501   l = &(ls[1]);
502 
503   i=1;
504   rs[0] = '\0';
505   do
506   {
507     if (*re >= 'A' && *re <= 'Z') rs[i]= *re + ('a' - 'A');
508     else rs[i] = *re;
509     i++;
510     re++;
511   } while (*re != '\0');
512   rs[i] = '\0';
513   r = &(rs[1]);
514 
515   // chopp of exact matches from beginning and end
516   while (*r != '*' && *r != '\0' && *l != '\0')
517   {
518     if (*r != *l) return FALSE;
519     *r = '\0';
520     *s = '\0';
521     r++;
522     l++;
523   }
524   if (*r == '\0') return (*l == '\0');
525   if (*r == '*' && r[1] == '\0') return TRUE;
526   if (*l == '\0') return FALSE;
527 
528   rr = &r[strlen(r) - 1];
529   ll = &l[strlen(l) - 1];
530   while (*rr != '*' && *rr != '\0' && *ll != '\0')
531   {
532     if (*rr != *ll) return FALSE;
533     *rr = '\0';
534     *ll = '\0';
535     rr--;
536     ll--;
537   }
538   if (*rr == '\0') return (*ll == '\0');
539   if (*rr == '*' && rr[-1] == '\0') return TRUE;
540   if (*ll == '\0') return FALSE;
541 
542   // now *r starts with a * and ends with a *
543   r++;
544   *rr = '\0'; rr--;
545   while (*r != '\0')
546   {
547     rr = r + 1;
548     while (*rr != '*' && *rr != '\0') rr++;
549     if (*rr == '*')
550     {
551       *rr = '\0';
552       rr++;
553     }
554     l = strstr(l, r);
555     if (l == NULL) return FALSE;
556     r = rr;
557   }
558   return TRUE;
559 }
560 
561 // similar to heKey2Entry, except that
562 // key is taken as regexp (see above)
563 // and number of matches is returned
564 // if number of matches > 0, then hentry contains entry for first match
565 // if number of matches > 1, matches are printed as komma-separated
566 // into global string
heReKey2Entry(char * filename,char * key,heEntry hentry)567 static int heReKey2Entry (char* filename, char* key, heEntry hentry)
568 {
569   int i = 0;
570   FILE* fd;
571   char index_key[MAX_HE_ENTRY_LENGTH];
572 
573   if (filename == NULL || key == NULL)  return 0;
574   fd = fopen(filename, "r");
575   if (fd == NULL) return 0;
576   memset(index_key,0,MAX_HE_ENTRY_LENGTH);
577   while (si_fscanf(fd, "%[^\t]\t%*[^\n]\n", index_key) == 1)
578   {
579     if ((index_key[MAX_HE_ENTRY_LENGTH-1]!='\0'))
580     {
581       index_key[MAX_HE_ENTRY_LENGTH-1]='\0';
582       Werror("index file corrupt at line >>%s<<",index_key);
583       break;
584     }
585     else if (strmatch(index_key, key))
586     {
587       i++;
588       if (i == 1)
589       {
590         heKey2Entry(filename, index_key, hentry);
591       }
592       else if (i == 2)
593       {
594         StringAppend("?%s; ?%s;", hentry->key, index_key);
595       }
596       else
597       {
598         StringAppend(" ?%s;", index_key);
599       }
600     }
601   }
602   fclose(fd);
603   return i;
604 }
605 
606 // test for h being a string and print it
hePrintHelpStr(const idhdl hh,const char * id,const char * pa)607 static void hePrintHelpStr(const idhdl hh,const char *id,const char *pa)
608 {
609   if ((hh!=NULL) && (IDTYP(hh)==STRING_CMD))
610   {
611     PrintS(IDSTRING(hh));
612     PrintLn();
613   }
614   else
615     Print("`%s` not found in package %s\n",id,pa);
616 }
617 // try to find the help string as a loaded procedure or library
618 // if found, display the help and return TRUE
619 // otherwise, return FALSE
heOnlineHelp(char * s)620 static BOOLEAN heOnlineHelp(char* s)
621 {
622   char *ss;
623   idhdl h;
624 
625   if ((ss=strstr(s,"::"))!=NULL)
626   {
627     *ss='\0';
628     ss+=2;
629     h=ggetid(s);
630     if (h!=NULL)
631     {
632       Print("help for %s from package %s\n",ss,s);
633       char s_help[200];
634       strcpy(s_help,ss);
635       strcat(s_help,"_help");
636       idhdl hh=IDPACKAGE(h)->idroot->get(s_help,0);
637       hePrintHelpStr(hh,s_help,s);
638       return TRUE;
639     }
640     else Print("package %s not found\n",s);
641     return TRUE; /* do not search the manual */
642   }
643   h=IDROOT->get(s,myynest);
644   // try help for a procedure
645   if (h!=NULL)
646   {
647     if  (IDTYP(h)==PROC_CMD)
648     {
649       char *lib=iiGetLibName(IDPROC(h));
650       if((lib!=NULL)&&(*lib!='\0'))
651       {
652         Print("// proc %s from lib %s\n",s,lib);
653         procinfov pi=IDPROC(h);
654         if (pi->language==LANG_SINGULAR)
655         {
656           s=iiGetLibProcBuffer(pi, 0);
657           if (s!=NULL)
658           {
659             PrintS(s);
660             omFree((ADDRESS)s);
661           }
662           return TRUE;
663         }
664       }
665     }
666     else if (IDTYP(h)==PACKAGE_CMD)
667     {
668       idhdl hh=IDPACKAGE(h)->idroot->get("info",0);
669       hePrintHelpStr(hh,"info",s);
670       return TRUE;
671     }
672     return FALSE;
673   }
674 
675   // try help for a library
676   int ls = strlen(s);
677   char* str = NULL;
678   // check that it ends with "[.,_]lib"
679   if (strlen(s) >=4 &&  strcmp(&s[ls-3], "lib") == 0)
680   {
681     if (s[ls - 4] == '.') str = s;
682     else
683     {
684       str = omStrDup(s);
685       str[ls - 4] = '.';
686     }
687   }
688   else
689   {
690     return FALSE;
691   }
692 
693   char libnamebuf[1024];
694   FILE *fp=NULL;
695   // first, search for library of that name
696   if ((str[1]!='\0') &&
697       ((iiLocateLib(str, libnamebuf) && (fp=feFopen(libnamebuf, "rb")) !=NULL)
698        ||
699        ((fp=feFopen(str,"rb", libnamebuf))!=NULL)))
700   {
701     EXTERN_VAR FILE *yylpin;
702     lib_style_types lib_style; // = OLD_LIBSTYLE;
703 
704     yylpin = fp;
705     yylplex(str, libnamebuf, &lib_style, IDROOT, FALSE, GET_INFO);
706     reinit_yylp();
707     if(lib_style == OLD_LIBSTYLE)
708     {
709       char buf[256];
710       fseek(fp, 0, SEEK_SET);
711       Warn( "library %s has an old format. Please fix it for the next time",
712             str);
713       if (str != s) omFree(str);
714       BOOLEAN found=FALSE;
715       while (fgets( buf, sizeof(buf), fp))
716       {
717         if (strncmp(buf,"//",2)==0)
718         {
719           if (found) return TRUE;
720         }
721         else if ((strncmp(buf,"proc ",5)==0)||(strncmp(buf,"LIB ",4)==0))
722         {
723           if (!found) WarnS("no help part in library found");
724           return TRUE;
725         }
726         else
727         {
728           found=TRUE;
729           PrintS(buf);
730         }
731       }
732     }
733     else
734     {
735       if (str != s) omFree(str);
736       fclose( yylpin );
737       PrintS(text_buffer);
738       omFree(text_buffer);
739       text_buffer=NULL;
740     }
741     return TRUE;
742   }
743 
744   if (str != s) omFree(str);
745   return FALSE;
746 }
747 
heKeyChksum(char * key)748 static long heKeyChksum(char* key)
749 {
750   if (key == NULL || *key == '\0') return 0;
751   idhdl h=IDROOT->get(key,myynest);
752   if ((h!=NULL) && (IDTYP(h)==PROC_CMD))
753   {
754     procinfo *pi = IDPROC(h);
755     if (pi != NULL) return pi->data.s.help_chksum;
756   }
757   return 0;
758 }
759 
760 /*****************************************************************
761  *
762  * Implementation : Help Browsers
763  *
764  *****************************************************************/
765 
766 STATIC_VAR BOOLEAN feHelpCalled = FALSE;
767 
heBrowserHelp(heEntry hentry)768 static void heBrowserHelp(heEntry hentry)
769 {
770   // check checksums of procs
771   int kchksum = (hentry != NULL && hentry->chksum > 0 ?
772                  heKeyChksum(hentry->key) : 0);
773   if (kchksum  && kchksum != hentry->chksum && heOnlineHelp(hentry->key))
774     return;
775 
776   if (heCurrentHelpBrowser == NULL) feHelpBrowser(NULL, 0);
777   assume(heCurrentHelpBrowser != NULL);
778   if (! feHelpCalled)
779   {
780     Warn("Displaying help in browser '%s'.", heCurrentHelpBrowser->browser);
781     //if (strcmp(heCurrentHelpBrowser->browser, "netscape") == 0 &&
782     //    feResource('h', 0) == NULL)
783     //{
784     //  Warn("Using URL '%s'.", feResource('u', 0));
785     //}
786     WarnS("Use 'system(\"--browser\", <browser>);' to change browser,");
787     StringSetS("where <browser> can be: ");
788     int i = 0;
789     i = 0;
790     while (heHelpBrowsers[i].browser != NULL)
791     {
792       if (heHelpBrowsers[i].init_proc(0,i))
793         StringAppend("\"%s\", ", heHelpBrowsers[i].browser);
794       i++;
795     }
796     char *browsers=StringEndS();
797     if (browsers[strlen(browsers)-2] == ',')
798     {
799       browsers[strlen(browsers)-2] = '.';
800       browsers[strlen(browsers)-1] = '\0';
801     }
802     WarnS(browsers);
803     omFree(browsers);
804   }
805 
806   heCurrentHelpBrowser->help_proc(hentry, heCurrentHelpBrowserIndex);
807   feHelpCalled = TRUE;
808 }
809 
810 #define MAX_SYSCMD_LEN MAXPATHLEN*2
heGenInit(int warn,int br)811 static BOOLEAN heGenInit(int warn, int br)
812 {
813   if (heHelpBrowsers[br].required==NULL) return TRUE;
814   const char *p=heHelpBrowsers[br].required;
815   while (*p>'\0')
816   {
817     switch (*p)
818     {
819       case '#': break;
820       case ' ': break;
821       case 'i': /* singular.hlp */
822       case 'x': /* singular.idx */
823       case 'h': /* html dir */
824                if (feResource(*p, warn) == NULL)
825                {
826                  if (warn) Warn("resource `%c` not found",*p);
827                  return FALSE;
828                }
829                break;
830       case 'D': /* DISPLAY */
831                if (getenv("DISPLAY") == NULL)
832                {
833                  if (warn) WarnS("resource `D` not found");
834                  return FALSE;
835                }
836                break;
837       case 'E': /* executable: E:xterm: */
838       case 'O': /* OS: O:ix86Mac-darwin/ppcMac-darwin: */
839                {
840                  char name[128];
841                  char exec[128];
842                  char op=*p;
843                  memset(name,0,128);
844                  int i=0;
845                  p++;
846                  while (((*p==':')||(*p<=' ')) && (*p!='\0')) p++;
847                  while((i<127) && (*p>' ') && (*p!=':'))
848                  {
849                    name[i]=*p; p++; i++;
850                  }
851                  if (i==0) return FALSE;
852 
853                  if ((op=='O') && (strcmp(name,S_UNAME)!=0))
854                    return FALSE;
855                  if ((op=='E') && (omFindExec(name,exec)==NULL))
856                  {
857                    if (warn) Warn("executable `%s` not found",name);
858                    return FALSE;
859                  }
860                }
861                break;
862       default: Warn("unknown char %c",*p);
863                break;
864     }
865     p++;
866   }
867   return TRUE;
868 }
869 
heGenHelp(heEntry hentry,int br)870 static void heGenHelp(heEntry hentry, int br)
871 {
872   char sys[MAX_SYSCMD_LEN];
873   const char *p=heHelpBrowsers[br].action;
874   if (p==NULL) {PrintS("no action ?\n"); return;}
875   memset(sys,0,MAX_SYSCMD_LEN);
876   int i=0;
877   while ((*p>'\0')&& (i<MAX_SYSCMD_LEN))
878   {
879     if ((*p)=='%')
880     {
881       p++;
882       switch (*p)
883       {
884         case 'f': /* local html:file */
885         case 'h': /* local html:URL */
886         case 'H': /* www html */
887                  {
888                    char temp[256];
889                    char *htmldir = feResource('h' /*"HtmlDir"*/);
890                    if ((*p=='h')&&(htmldir!=NULL))
891                      strcat(sys,"file://localhost");
892                    else if ((*p=='H')||(htmldir==NULL))
893                      htmldir = feResource('u' /* %H -> "ManualUrl"*/);
894                      /* always defined */
895                    if (hentry != NULL && *(hentry->url) != '\0')
896                    #ifdef HAVE_VSNPRINTF
897                    {
898                      if (*p=='H')
899                      #ifdef SINGULAR_4_2
900                        snprintf(temp,256,"%s/%d-%d/%s", htmldir,
901                                   SINGULAR_VERSION/1000,
902                                  (SINGULAR_VERSION % 1000)/100,
903                      #else
904                        snprintf(temp,256,"%s/%d-%d-%d/%s", htmldir,
905                                   SINGULAR_VERSION/1000,
906                                  (SINGULAR_VERSION % 1000)/100,
907                                  (SINGULAR_VERSION % 100)/10,
908                      #endif
909                        hentry->url);
910                      else
911                        snprintf(temp,256,"%s/%s", htmldir, hentry->url);
912                    }
913                    else
914                    {
915                      if (*p=='H')
916                        snprintf(temp,256,"%s/%d-%d-%d/index.htm", htmldir,
917                                   SINGULAR_VERSION/1000,
918                                  (SINGULAR_VERSION % 1000)/100,
919                                  (SINGULAR_VERSION % 100)/10
920                        );
921                      else
922                        snprintf(temp,256,"%s/index.htm", htmldir);
923                    }
924                    #else
925                    {
926                      if (*p=='H')
927                        sprintf(temp,"%s/%d-%d-%d/%s", htmldir,
928                                   SINGULAR_VERSION/1000,
929                                  (SINGULAR_VERSION % 1000)/100,
930                                  (SINGULAR_VERSION % 100)/10,
931                        hentry->url);
932                      else
933                        sprintf(temp,"%s/%d-%d-%d/%s", htmldir, hentry->url);
934                    }
935                    else
936                      if (*p=='H')
937                        sprintf(temp,"%s/%d-%d-%d/index.htm", htmldir,
938                                   SINGULAR_VERSION/1000,
939                                  (SINGULAR_VERSION % 1000)/100,
940                                  (SINGULAR_VERSION % 100)/10
941                        );
942                      else
943                        sprintf(temp,"%s/index.htm", htmldir);
944                    }
945                    #endif
946                    strcat(sys,temp);
947                    if ((*p)=='f')
948                    { // remove #SEC
949                      char *pp=(char *)strchr(sys,'#');
950                      if (pp!=NULL)
951                      {
952                        *pp='\0';
953                        i=strlen(sys);
954                        memset(pp,0,MAX_SYSCMD_LEN-i);
955                      }
956                    }
957                    i=strlen(sys);
958                    break;
959                  }
960         case 'i': /* singular.hlp */
961                  {
962                    char *i_res=feResource('i');
963                    if (i_res!=NULL) strcat(sys,i_res);
964                    else
965                    {
966                      WarnS("singular.hlp not found");
967                      return;
968                    }
969                    i=strlen(sys);
970                    break;
971                  }
972         case 'n': /* info node */
973                  {
974                    char temp[256];
975                    if ((hentry!=NULL) && (*(hentry->node) != '\0'))
976                      sprintf(temp,"%s",hentry->node);
977                    //else if ((hentry!=NULL) && (hentry->key!=NULL))
978                    //  sprintf(temp,"Index '%s'",hentry->key);
979                    else
980                      sprintf(temp,"Top");
981                    strcat(sys,temp);
982                    i=strlen(sys);
983                    break;
984                  }
985         case 'v': /* version number*/
986                  {
987                    char temp[256];
988                    sprintf(temp,"%d-%d-%d",SINGULAR_VERSION/1000,
989                                  (SINGULAR_VERSION % 1000)/100,
990                                  (SINGULAR_VERSION % 100)/10);
991                    strcat(sys,temp);
992                    i=strlen(sys);
993                    break;
994                  }
995         default: break;
996       }
997       p++;
998     }
999     else
1000     {
1001       sys[i]=*p;
1002       p++;i++;
1003     }
1004   }
1005   Print("running `%s`\n",sys);
1006   (void) system(sys);
1007 }
1008 
heDummyInit(int,int)1009 static BOOLEAN heDummyInit(int /*warn*/, int /*br*/)
1010 {
1011   return TRUE;
1012 }
heDummyHelp(heEntry,int)1013 static void heDummyHelp(heEntry /*hentry*/, int /*br*/)
1014 {
1015   WerrorS("No functioning help browser available.");
1016 }
1017 
heEmacsInit(int,int)1018 static BOOLEAN heEmacsInit(int /*warn*/, int /*br*/)
1019 {
1020   return TRUE;
1021 }
heEmacsHelp(heEntry hentry,int)1022 static void heEmacsHelp(heEntry hentry, int /*br*/)
1023 {
1024   WarnS("Your help command could not be executed. Use");
1025   Warn("C-h C-s %s",
1026        (hentry != NULL && *(hentry->node) != '\0' ? hentry->node : "Top"));
1027   WarnS("to enter the Singular online help. For general");
1028   WarnS("information on Singular running under Emacs, type C-h m.");
1029 }
1030 static int singular_manual(char *str, BOOLEAN isIndexEntry);
heBuiltinHelp(heEntry hentry,int)1031 static void heBuiltinHelp(heEntry hentry, int /*br*/)
1032 {
1033   char* node = omStrDup(hentry != NULL && *(hentry->key) != '\0' ?
1034                        hentry->key : "Top");
1035   singular_manual(node,(hentry != NULL) && *(hentry->url)!='\0');
1036   omFree(node);
1037 }
1038 
1039 
1040 /* ========================================================================== */
1041 // old, stupid builtin_help
1042 // This could be implemented much more clever, but I'm too lazy to do this now
1043 //
1044 #define HELP_OK        0
1045 #define FIN_INDEX    '\037'
1046 #define HELP_NOT_OPEN  1
1047 #define HELP_NOT_FOUND 2
1048 #define BUF_LEN        256
1049 #define IDX_LEN        256
1050 
tolow(char p)1051 static inline char tolow(char p)
1052 {
1053   if (('A'<=p)&&(p<='Z')) return p | 040;
1054   return p;
1055 }
1056 
1057 /*************************************************/
show(unsigned long offset,char * close)1058 static int show(unsigned long offset, char *close)
1059 { char buffer[BUF_LEN+1];
1060   int  lines = 0;
1061   FILE * help;
1062 
1063   if( (help = fopen(feResource('i'), "rb")) == NULL)
1064     return HELP_NOT_OPEN;
1065 
1066   fseek(help,  (long)(offset+1), (int)0);
1067   while( (!feof(help))
1068         && (*fgets(buffer, BUF_LEN, help) != EOF)
1069         && (buffer[0] != FIN_INDEX))
1070   {
1071     printf("%s", buffer);
1072     if(lines++==pagelength)
1073     {
1074       printf("\n Press <RETURN> to continue or x to exit help.\n");
1075       fflush(stdout);
1076       *close = (char)getchar();
1077       if(*close=='x')
1078       {
1079         getchar();
1080         break;
1081       }
1082       lines=0;
1083     }
1084   }
1085   if((*close!='x')&&(pagelength>0))
1086   {
1087     printf("\nEnd of part. Press <RETURN> to continue or x to exit help.\n");
1088     fflush(stdout);
1089     *close = (char)getchar();
1090     if(*close=='x')
1091       getchar();
1092   }
1093   fclose(help);
1094   return HELP_OK;
1095 }
1096 
1097 /*************************************************/
singular_manual(char * str,BOOLEAN isIndexEntry)1098 static int singular_manual(char *str, BOOLEAN isIndexEntry)
1099 { FILE *index=NULL;
1100   unsigned long offset;
1101   char *p,close=' ';
1102   int done = 0;
1103   char buffer[BUF_LEN+1],
1104        Index[IDX_LEN+1],
1105        String[IDX_LEN+1];
1106   Print("HELP >>%s>>\n",str);
1107 
1108   if( (index = fopen(feResource('i'), "rb")) == NULL)
1109   {
1110     return HELP_NOT_OPEN;
1111   }
1112 
1113   if (!isIndexEntry)
1114   {
1115     for(p=str; *p; p++) *p = tolow(*p);/* */
1116     do
1117     {
1118       p--;
1119     }
1120     while ((p != str) && (*p<=' '));
1121     p++;
1122     *p='\0';
1123     (void)sprintf(String, " %s ", str);
1124   }
1125   else
1126   {
1127     (void)sprintf(String, " %s", str);
1128   }
1129 
1130   while(!feof(index)
1131         && (fgets(buffer, BUF_LEN, index) != (char *)0)
1132         && (buffer[0] != FIN_INDEX));
1133 
1134   while(!feof(index))
1135   {
1136     if (fgets(buffer, BUF_LEN, index)==NULL) break; /*fill buffer */
1137     if (si_sscanf(buffer, "Node:%[^\177]\177%ld\n", Index, &offset)!=2)
1138       continue;
1139     if (!isIndexEntry)
1140     {
1141       for(p=Index; *p; p++) *p = tolow(*p);/* */
1142       (void)strcat(Index, " ");
1143       if( strstr(Index, String)!=NULL)
1144       {
1145         done++; (void)show(offset, &close);
1146       }
1147     }
1148     else if( strcmp(Index, String)==0)
1149     {
1150       done++; (void)show(offset, &close);
1151       break;
1152     }
1153     Index[0]='\0';
1154     if(close=='x')
1155     break;
1156   }
1157   if (index != NULL) (void)fclose(index);
1158   if(done==0)
1159   {
1160     Warn("`%s` not found",String);
1161     return HELP_NOT_FOUND;
1162   }
1163   return HELP_OK;
1164 }
1165 /*************************************************/
1166