1 // -*- mode:C++ ; compile-command: "g++ -I.. -g -c help.cc -Wall" -*-
2 //#define _SCL_SECURE_NO_WARNINGS
3 #include "giacPCH.h"
4 
5 #include "path.h"
6 /*
7  *  Copyright (C) 2000,14 B. Parisse, Institut Fourier, 38402 St Martin d'Heres
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 3 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 using namespace std;
23 #include <algorithm>
24 #include "gen.h"
25 #include "help.h"
26 #include <iostream>
27 #if !defined GIAC_HAS_STO_38 && !defined NSPIRE && !defined FXCG && !defined POCKETCAS
28 #include <fstream>
29 #endif
30 #include "global.h"
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #ifdef KHICAS
35 #include "kdisplay.h" // for select_item
36 #endif
37 
38 #if defined VISUALC || defined BESTA_OS
39 
40 
41 #define opendir FindFirstFile
42 #define readdir FindNextFile
43 #define closedir FindClose
44 #define DIR WIN32_FIND_DATA
45 #define GNUWINCE 1
46 
47 #else // VISUALC or BESTA_OS
48 
49 #ifdef HAVE_SYS_PARAM_H
50 #include <sys/param.h>
51 #endif
52 
53 #if !defined BESTA_OS && !defined NSPIRE && !defined FXCG && !defined KHICAS // test should always return true
54 #include <dirent.h>
55 #endif
56 
57 #endif // VISUALC or BESTA_OS
58 
59 #include "input_lexer.h"
60 
61 #ifndef NO_NAMESPACE_GIAC
62 namespace giac {
63 #endif // ndef NO_NAMESPACE_GIAC
64 
65   const int HELP_LANGUAGES=5;
66 
67   struct static_help_t {
68     const char * cmd_name;
69     const char * cmd_howto[HELP_LANGUAGES];
70     const char * cmd_syntax;
71     const char * cmd_related;
72     const char * cmd_examples;
73   };
74 
75   const static_help_t static_help[]={
76 #if defined NSPIRE_NEWLIB || defined NUMWORKS || (!defined RTOS_THREADX && !defined BESTA_OS && !defined GIAC_HAS_STO_38 && !defined(KHICAS) && !defined POCKETCAS)
77 #include "static_help.h"
78 #else
79     { "", { "", "", "", "",""}, "", "", "" },
80 #endif
81   };
82 
83   const int static_help_size=sizeof(static_help)/sizeof(static_help_t);
84 
85   struct static_help_sort {
static_help_sortgiac::static_help_sort86     static_help_sort() {}
operator ()giac::static_help_sort87     inline bool operator () (const static_help_t & a ,const static_help_t & b){
88       return strcmp(a.cmd_name, b.cmd_name) < 0;
89     }
90   };
91 
mon_max(int a,int b)92   inline int mon_max(int a,int b){
93     if (a>b)
94       return a;
95     else
96       return b;
97   }
98 
seconddec(const pair<int,int> & a,const pair<int,int> & b)99   bool seconddec (const pair<int,int> & a,const pair<int,int> & b){
100     return a.second>b.second;
101   }
102 
103   // NB: cmd_name may be localized but related is not localized
has_static_help(const char * & cmd_name,int lang,const char * & howto,const char * & syntax,const char * & related,const char * & examples)104   bool has_static_help(const char * & cmd_name,int lang,const char * & howto,const char * & syntax,const char * & related,const char * & examples){
105 #ifdef GIAC_HAS_STO_38
106     const char nullstring[]=" ";
107 #else
108     const char nullstring[]="";
109 #endif
110     if (lang<=0)
111       lang=2;
112     if (lang>HELP_LANGUAGES)
113       lang=2;
114     string s=unlocalize(cmd_name);
115     int l=int(s.size());
116     if (l==0) return false;
117     if ( (l>2) && (s[0]=='\'') && (s[l-1]=='\'') )
118       s=s.substr(1,l-2);
119 #ifdef KHICAS
120     int pos=0,kk,ks=s.size();
121     for (;pos<static_help_size;++pos){
122       if (strcmp(static_help[pos].cmd_name,s.c_str())>=0)
123 	break;
124     }
125     const char * items[1+static_help_size];
126     kk=0;
127     for (;pos<static_help_size;++kk,++pos){
128       const static_help_t & sh=static_help[pos];
129       const char * ptr=sh.cmd_name;
130       if (strcmp(ptr,s.c_str())==0){
131 	howto=sh.cmd_howto[lang-1];
132 	if (!howto)
133 	  howto=sh.cmd_howto[1];
134 	syntax=sh.cmd_syntax;
135 	if (!syntax)
136 	  syntax=nullstring;
137 	related=sh.cmd_related;
138 	if (!related)
139 	  related=nullstring;
140 	examples=sh.cmd_examples;
141 	if (!examples)
142 	  examples=nullstring;
143 	return true;
144       }
145       if (strlen(ptr)<ks || strncmp(ptr,s.c_str(),ks)!=0)
146 	break;
147       items[kk]=ptr;
148     }
149     items[kk]=0;
150     int r=select_item(items,"Select completion",false);
151     if (r<0)
152       return false;
153     cmd_name=items[r];
154     return has_static_help(items[r],lang,howto,syntax,related,examples);
155 #endif
156     static_help_t h={s.c_str(),{0,0,0,0,0},0,0,0};
157     std::pair<const static_help_t *,const static_help_t *> p=equal_range(static_help,static_help+static_help_size,h,static_help_sort());
158     if (p.first!=p.second && p.first!=static_help+static_help_size){
159       howto=p.first->cmd_howto[lang-1];
160       if (!howto)
161 	howto=p.first->cmd_howto[1];
162       syntax=p.first->cmd_syntax;
163       if (!syntax)
164 	syntax=nullstring;
165       related=p.first->cmd_related;
166       if (!related)
167 	related=nullstring;
168       examples=p.first->cmd_examples;
169       if (!examples)
170 	examples=nullstring;
171       return true;
172     }
173 #if defined EMCC
174     // Find closest string
175     syntax=nullstring;
176     related=nullstring;
177     static string res;
178     res="";
179     int best_score=0,cur_score;
180     vector< pair<int,int> > best_j;
181     for (int j=0;j<static_help_size;++j){
182       cur_score=score(s,static_help[j].cmd_name);
183       if (cur_score>best_score){
184 	best_score=cur_score;
185 	vector< pair<int,int> > tmp;
186 	for (unsigned k=0;k<best_j.size();++k){
187 	  if (best_j[k].second>=best_score-6)
188 	    tmp.push_back(best_j[k]);
189 	}
190 	best_j=tmp;
191 	best_j.push_back(pair<int,int>(j,cur_score));
192 	continue;
193       }
194       if (cur_score>=mon_max(best_score-6,0)){
195 	best_j.push_back(pair<int,int>(j,cur_score));
196       }
197     }
198     if (best_score>0){
199       sort(best_j.begin(),best_j.end(),seconddec);
200       vector< pair<int,int> >::iterator it=best_j.begin(),itend=best_j.end();
201       for (int k=1;(k<10) && (it!=itend);++k,++it){
202 	res = res+static_help[it->first].cmd_name;
203 	res = res+",";
204       }
205       if (!res.empty())
206 	res=res.substr(0,res.size()-1);
207     }
208     static string syn;
209     syn = gettext("Best match has score ") + printint(best_score) + "\n";
210     howto = syn.c_str();
211     examples = res.c_str();
212     return true;
213 #else
214     return false;
215 #endif
216   }
217 
output_quote(const string s)218   static std::string output_quote(const string s){
219     string res;
220     int ss=int(s.size());
221     for (int i=0;i<ss;++i){
222       switch (s[i]){
223       case '"':
224       case '\\':
225 	res += '\\';
226       default:
227 	res += s[i];
228       }
229     }
230     return res;
231   }
232 
233   // Run ./icas with export GIAC_DEBUG=-2 to print static_help.h and static_help_w.h, then sort in emacs
234   // /usr/share/giac/doc/fr or en -> longhelp.js or longhelp_en.js: html_mtt
235   // replace string \244 with :
236   // macro replace /usr/share/giac/doc/en/cascmd_en/ with ' and #... with '
237   // longhelp*.js should begin with var longhelp = {
238   // and end with };
output_static_help(vector<aide> & v,const vector<int> & langv)239   static bool output_static_help(vector<aide> & v,const vector<int> & langv){
240 #if !defined NSPIRE && !defined FXCG && !defined GIAC_HAS_STO_38
241     add_language(5,context0); // add german help de/aide_cas
242     cout << "Generating xcascmds, for UI.xcascmds in xcas.js, sort and esc-x replace-string ctrl-Q ctrl-j ret ret" << endl;
243     cout << "Copy in python.js. For xcasmod.js, replace \",\" by | " << endl;
244     cout << "Generating static_help.h (sort it in emacs)" << endl;
245     cout << "Generating static_help_w.h (same but UTF16)" << endl;
246     ofstream cmds("xcascmds");
247     cmds << "[" << endl;
248     ofstream of("static_help.h");
249     vector<aide>::iterator it=v.begin(),itend=v.end();
250     for (;it!=itend;){
251       cmds << '"' << output_quote(it->cmd_name) << '"' << "," << endl;
252       of << "{";
253       of << '"' << output_quote(it->cmd_name) << '"' << ",";
254       std::vector<localized_string> & blabla = it->blabla;
255       sort(blabla.begin(),blabla.end());
256       int blablapos=0;
257       of << "{";
258       for (int i=0;i<HELP_LANGUAGES;i++){
259 	if (i+1==blabla[blablapos].language && equalposcomp(langv,i+1)){
260 	  of << '"' << output_quote(blabla[blablapos].chaine) << '"' ;
261 	  blablapos++;
262 	}
263 	else
264 	  of << 0 ;
265 	if (i==HELP_LANGUAGES-1)
266 	  of << '}';
267 	else
268 	  of << ',';
269       }
270       of << "," << '"' << output_quote(it->syntax) << '"' << ',' ;
271       std::vector<std::string> & examples = it->examples;
272       int bs=int(examples.size());
273       if (bs){
274 	of << '"';
275 	for (int i=0;i<bs;i++){
276 	  of << output_quote(examples[i]) ;
277 	  if (i==bs-1)
278 	    of << '"';
279 	  else
280 	    of << ';';
281 	}
282       }
283       else
284 	of << 0 ;
285       of << "," ;
286       std::vector<indexed_string> & related = it->related;
287       bs=int(related.size());
288       if (bs){
289 	of << '"';
290 	for (int i=0;i<bs;i++){
291 	  of << output_quote(related[i].chaine) ;
292 	  if (i==bs-1)
293 	    of << '"';
294 	  else
295 	    of << ',';
296 	}
297       }
298       else
299 	of << 0;
300       of << "}";
301       ++it;
302       if (it==itend)
303 	break;
304       of << "," << endl;
305     }
306     cmds << "]" << endl;
307     of << endl;
308     ofstream ofw("static_help_w.h");
309     ofstream ofwindex("index_w.h");
310     ofwindex << "const TChooseItem index_w[]={" << endl;
311     for (it=v.begin();it!=itend;){
312       ofw << "{";
313       string cmd=it->cmd_name;
314       ofw << 'L' << '"' << output_quote(cmd) << '"' << ",";
315       if (cmd.size()>16)
316 	cmd=cmd.substr(0,16);
317       ofwindex << "{NULL,NULL, " << 'L' << '"' << output_quote(cmd) << '"' << ", HIDVoid }" ;
318       std::vector<localized_string> & blabla = it->blabla;
319       sort(blabla.begin(),blabla.end());
320       int bs=int(blabla.size());
321       ofw << "{";
322       for (int i=0;i<HELP_LANGUAGES;i++){
323 	if (i<bs && equalposcomp(langv,i+1))
324 	  ofw << 'L' << '"' << output_quote(blabla[i].chaine) << '"' ;
325 	else
326 	  ofw << 0 ;
327 	if (i==HELP_LANGUAGES-1)
328 	  ofw << '}';
329 	else
330 	  ofw << ',';
331       }
332       ofw << ",L" << '"' << output_quote(it->cmd_name) << '(' << output_quote(it->syntax) << ')' << '"' << ',' ;
333       std::vector<std::string> & examples = it->examples;
334       bs=int(examples.size());
335       if (bs>=1){
336 	ofw << 'L' << '"';
337 	ofw << output_quote(examples[0]) ;
338 	ofw << '"' << ',';
339 	if (bs>=2){
340 	  ofw << 'L' << '"';
341 	  ofw << output_quote(examples[1]) ;
342 	  ofw << '"' << ',';
343 	}
344 	else
345 	  ofw << 0 << ",";
346       }
347       else
348 	ofw << 0 << "," << 0 << ",";
349       std::vector<indexed_string> & related = it->related;
350       bs=int(related.size());
351       if (bs>=1){
352 	ofw << 'L' << '"';
353 	ofw << output_quote(related[0].chaine) ;
354 	ofw << '"' << ',';
355 	if (bs>=2){
356 	  ofw << 'L' << '"';
357 	  ofw << output_quote(related[1].chaine) ;
358 	  ofw << '"' << ',';
359 	}
360 	else
361 	  ofw << 0 << ",";
362       }
363       else
364 	ofw << 0 << "," << 0 << ",";
365       ofw << "}";
366       ++it;
367       if (it==itend)
368 	break;
369       ofw << "," << endl;
370       ofwindex << "," << endl;
371     }
372     ofw << endl;
373     ofwindex << "};" << endl;
374 #endif
375     return true;
376   }
377 
operator <(const indexed_string & is1,const indexed_string & is2)378   bool operator < (const indexed_string & is1,const indexed_string & is2){
379     if (is1.index!=is2.index) return is1.index<is2.index;
380     return (is1.chaine<is2.chaine);
381   }
382 
383   const char default_helpfile[]=giac_aide_location; // help filename
384   const int HELP_MAXLENSIZE = 1600; // less than 20 lines of 80 chars
385 
printint(int i)386   string printint(int i){
387     if (!i)
388       return string("0");
389     if (i<0)
390       return string("-")+printint(-i);
391     int length = (int) std::floor(std::log10((double) i));
392 #if defined VISUALC || defined BESTA_OS
393     char * s =new char[length+2];
394 #else
395     char s[length+2];
396 #endif
397     s[length+1]=0;
398     for (;length>-1;--length,i/=10)
399       s[length]=i%10+'0';
400 #if defined VISUALC || defined BESTA_OS
401      string res=s;
402      delete [] s;
403      return res;
404 #else
405     return s;
406 #endif
407   }
408 
max(int a,int b,int c)409   inline int max(int a,int b,int c){
410     if (a>=b){
411       if (a>=c)
412 	return a;
413       else
414 	return c;
415     }
416     if (b>=c)
417       return b;
418     else
419       return c;
420   }
421 
score(const string & s,const string & t)422   int score(const string & s,const string & t){
423     int ls=int(s.size()),lt=int(t.size());
424     if (!ls) return -1;
425     vector<int> cur_l, new_l(lt+1,0);
426     for (int j=0;j<=lt;++j)
427       cur_l.push_back(-j);
428     vector<int>::iterator newbeg=new_l.begin(),newend=new_l.end(),newit=newbeg;
429     vector<int>::iterator curbeg=cur_l.begin(),curit;//curend=cur_l.end(),
430     for (int i=0;i<ls;++i){
431       newit=newbeg;
432       curit=curbeg;
433       int oldres=-i,res;
434       for (int j=0;j<lt;++curit,++j){
435 	*newit=oldres;
436 	if (s[i]==t[j])
437 	  res=max(oldres-1,*(curit+1)-1,*curit+3);
438 	else {
439           if (abs(s[i]-t[j])==32)
440 	    res=max(oldres-1,*(curit+1)-1,*curit+2);
441           else
442 	    res=max(oldres-1,*(curit+1)-1,*curit-2);
443 	}
444 	++newit;
445 	oldres=res;
446       }
447       *newit=oldres;
448       copy(newbeg,newend,curbeg);
449     }
450     // alignement would be return *newit;
451     // we modify the returned value to increase the weight of the first char
452     if (!s.empty() && !t.empty()){
453       if (s[0]==t[0])
454 	return * newit+2;
455       else
456 	return *newit-2;
457     }
458     return *newit;
459   }
460 
alpha_order(const aide & a1,const aide & a2)461   bool alpha_order(const aide & a1,const aide & a2){
462     string s1 =a1.cmd_name;
463     string s2 =a2.cmd_name;
464     for (unsigned i=0;i<s1.size();++i)
465       s1[i]=tolower(s1[i]);
466     for (unsigned i=0;i<s2.size();++i)
467       s2[i]=tolower(s2[i]);
468     if (s1!=s2)
469       return s1<s2;
470     return a1.cmd_name< a2.cmd_name;
471   }
472 
find_synonymes(const std::string & cmd_name,vector<localized_string> & current_synonymes)473   static void find_synonymes(const std::string & cmd_name,vector<localized_string> & current_synonymes){
474     current_synonymes.clear();
475     // parse curren_aide.cmd_name for synonyms
476     string s=cmd_name,s1;
477     int i;
478     for (;;){
479       // cout << s << endl;
480       i=int(s.find(' '));
481       if (i<=0){
482 	if (!s.empty())
483 	  current_synonymes.push_back(localized_string(0,s));
484 	break;
485       }
486       s1=s.substr(0,i);
487       current_synonymes.push_back(localized_string(0,s1));
488       /* add also keyword translations of s1
489 	 multimap<string,localized_string>::iterator it=back_lexer_localization_map().find(s1),backend=back_lexer_localization_map().end(),itend=back_lexer_localization_map().upper_bound(s1);
490 	 if (it!=backend){
491 	 for (;it!=itend;++it){
492 	 current_synonymes.push_back(it->second);
493 	 }
494 	 }
495       */
496       s=s.substr(i+1,s.size()-i-1);
497     } // end for (;;)
498   }
499 
500 
readhelp(const char * f_name,int & count,bool warn)501   vector<aide> readhelp(const char * f_name,int & count,bool warn){
502       vector<aide> v(1);
503       readhelp(v,f_name,count,warn);
504     return v;
505   }
506   // FIXME: aide_cas may end with synonyms (# cmd synonym1 ...)
readhelp(vector<aide> & v,const char * f_name,int & count,bool warn)507   void readhelp(vector<aide> & v,const char * f_name,int & count,bool warn){
508     count=0;
509 #if !defined NSPIRE && !defined FXCG && !defined GIAC_HAS_STO_38
510     if (access(f_name,R_OK)){
511       if (warn)
512 	std::cerr << "Help file " << f_name << " not found" << endl;
513       return ;
514     }
515     // v.reserve(1600);
516     ifstream f(f_name);
517     char fs[HELP_MAXLENSIZE+1];
518     vector<localized_string> current_blabla;
519     vector<indexed_string> current_related;
520     vector<string> current_examples;
521     aide current_aide;
522     vector<int> vposition;
523     int vpositions;
524     string current_line;
525     vector<localized_string> current_synonymes;
526     while (f){
527       f.getline(fs,HELP_MAXLENSIZE,'\n');
528       if (!fs[0])
529 	continue;
530       current_line=fs;
531       if (fs[0]=='#'){
532 	current_aide.blabla=current_blabla;
533 	current_aide.examples=current_examples;
534 	current_aide.related=current_related;
535 	if (!current_aide.cmd_name.empty()){
536 	  find_synonymes(current_aide.cmd_name,current_synonymes);
537 	  current_aide.synonymes=current_synonymes;
538 	  vector<localized_string>::const_iterator it=current_synonymes.begin(),itend=current_synonymes.end();
539 	  vpositions=int(vposition.size());
540 	  for (int pos=0;it!=itend;++it,++pos){
541 	    current_aide.cmd_name=it->chaine;
542 	    if (pos<vpositions)
543 	      v[vposition[pos]]=current_aide;
544 	    else
545 	      v.push_back(current_aide);
546 	    ++count;
547 	  }
548 	} // end if (!current_aide.cmd_name.empty())
549 	current_blabla.clear();
550 	current_examples.clear();
551 	current_related.clear();
552 	vposition.clear();
553 	current_aide.cmd_name=current_line.size()>2?current_line.substr(2,current_line.size()-2):"";
554 	// search if cmd_name is already present in v
555 	// if so set vposition, current_blabla/examples/related accordingly
556 	find_synonymes(current_aide.cmd_name,current_synonymes);
557 	vector<localized_string>::const_iterator itbeg=current_synonymes.begin(),itend=current_synonymes.end(),it;
558 	vector<aide>::iterator itpos;
559 	for (it=itbeg;it!=itend;++it){
560 	  itpos=lower_bound(v.begin(),v.end(),current_aide,alpha_order);
561 	  if (itpos!=v.end()){
562 	    // --itpos;
563 	    if (itpos->cmd_name==it->chaine){ // already documented
564 	      current_synonymes=itpos->synonymes;
565 	      current_blabla=itpos->blabla;
566 	      current_examples=itpos->examples;
567 	      current_related=itpos->related;
568 	      vposition.push_back(int(itpos-v.begin()));
569 	    }
570 	  }
571 	}
572 	continue;
573       }
574       // look for space
575       int l=int(current_line.find_first_of(' '));
576       if ( (l==1) && (current_line[0]=='0') ){
577 	int cs=int(current_line.size());
578 	while (l<cs && current_line[l]==' '){ ++l; }
579         current_aide.syntax=current_line.substr(l,cs-l);
580         continue;
581       }
582       int n=0;
583       bool positif=true;
584       int i=0;
585       if (current_line[i]=='-'){
586 	positif=false;
587 	++i;
588       }
589       for (;i<l;++i){
590 	if ((current_line[i]<'0') || (current_line[i]>'9')){
591 	  n=0;
592 	  break;
593 	}
594 	else
595 	  n=10*n+(current_line[i]-int('0'));
596       }
597       if (!positif)
598 	n=-n;
599       if (n>0)
600 	current_blabla.push_back(localized_string(n,current_line.substr(l+1,current_line.size()-l)));
601       else {
602 	if (n<0)
603 	  current_related.push_back(indexed_string(-n,current_line.substr(l+1,current_line.size()-l)));
604 	else
605 	  current_examples.push_back(current_line);
606       }
607     } // end reading help from file
608     if (!current_aide.cmd_name.empty()){
609       current_aide.synonymes=vector<localized_string>(1,localized_string(0,current_aide.cmd_name));
610       current_aide.blabla=current_blabla;
611       current_aide.examples=current_examples;
612       current_aide.related=current_related;
613       v.push_back(current_aide);
614       count++;
615     }
616     sort(v.begin(),v.end(),alpha_order);
617     if (debug_infolevel==-2){
618       vector<int> langv;
619       langv.push_back(1);
620       langv.push_back(2);
621       langv.push_back(3);
622       langv.push_back(4);
623       langv.push_back(5);
624       output_static_help(v,langv);
625     }
626 #endif
627   }
628 
add_synonyme_name_to_examples(const aide & a)629   static aide add_synonyme_name_to_examples(const aide & a){
630     aide res(a);
631     std::vector<std::string>::iterator it=res.examples.begin(),itend=res.examples.end();
632     for (;it!=itend;++it){
633       if (!it->empty() && (*it)[0]==' ')
634 	continue;
635       // look for a (
636       unsigned i=unsigned(it->find('('));
637       if (i>0 && i<it->size()){ // check whether the beginning of the string is in synonyms
638 	string cmd=it->substr(0,i);
639 	std::vector<localized_string>::const_iterator jt=res.synonymes.begin(),jtend=res.synonymes.end();
640 	for (;jt!=jtend;++jt){
641 	  if (jt->chaine==cmd)
642 	    break;
643 	}
644 	if (jt!=jtend) // Yes, replace it
645 	  *it=res.cmd_name+it->substr(i,it->size()-i);
646 	else
647 	  *it=res.cmd_name+'('+*it+')';
648       }
649       else
650 	*it=res.cmd_name+'('+*it+')';
651     }
652     return res;
653   }
654 
helpon(const string & demande,const vector<aide> & v,int language,int count,bool with_op)655   aide helpon(const string & demande,const vector<aide> & v,int language,int count,bool with_op){
656     aide result;
657     string current(demande);
658     if (with_op)
659       result.syntax = gettext("No help available for ") +current +"\n";
660     else
661       result.syntax="NULL";
662     if (!count){
663       return result;
664     }
665     for (int i=1;;++i){
666       if (i==count){
667 	if (!with_op)
668 	  return result;
669 	// Find closest string
670 	int best_score=0,cur_score;
671 	vector< pair<int,int> > best_j;
672 	for (int j=1;j<count;++j){
673 	  cur_score=score(current,v[j].cmd_name);
674 	  if (cur_score>best_score){
675 	    best_score=cur_score;
676 	    vector< pair<int,int> > tmp;
677 	    for (unsigned k=0;k<best_j.size();++k){
678 	      if (best_j[k].second>=best_score-6)
679 		tmp.push_back(best_j[k]);
680 	    }
681 	    best_j=tmp;
682 	    best_j.push_back(pair<int,int>(j,cur_score));
683 	    continue;
684 	  }
685 	  if (cur_score>=mon_max(best_score-6,0)){
686 	    best_j.push_back(pair<int,int>(j,cur_score));
687 	  }
688 	}
689 	if (best_score>0){
690 	  sort(best_j.begin(),best_j.end(),seconddec);
691 	  vector< pair<int,int> >::iterator it=best_j.begin(),itend=best_j.end();
692 	  for (int k=1;(k<10) && (it!=itend);++k,++it)
693 	    result.related.push_back(indexed_string(k,v[it->first].cmd_name));
694 	}
695 	result.syntax += gettext("Best match has score ") + printint(best_score) + "\n";
696 	result.cmd_name = current;
697 	return result;
698       }
699       if (current==v[i].cmd_name){
700 	result=v[i];
701 	if (!with_op)
702 	  return add_synonyme_name_to_examples(result);
703 	result.syntax= current + "(" +result.syntax +")\n";
704 	return add_synonyme_name_to_examples(result);
705       }
706     } // end for i
707   }
708 
writehelp(const aide & cur_aide,int language)709   string writehelp(const aide & cur_aide,int language){
710     string result=cur_aide.syntax;
711     vector<localized_string>::const_iterator it=cur_aide.blabla.begin(),itend=cur_aide.blabla.end();
712     for (;it!=itend;++it){
713       if (it->language==language){
714 	result += it->chaine +'\n' ;
715 	break;
716       }
717     }
718     vector<indexed_string>::const_iterator iti=cur_aide.related.begin(),itiend=cur_aide.related.end();
719     if (itiend!=iti){
720       result +=  gettext("See also: ");
721       for (;iti!=itiend;++iti){
722 	result += printint(iti->index) + "/ " + iti->chaine + " ";
723       }
724       result += '\n' ;
725     }
726     vector<string>::const_iterator its=cur_aide.examples.begin(),itsend=cur_aide.examples.end();
727     for (int i=1;its!=itsend;++its,++i){
728       string current = "Ex" + printint(i)+':'+*its ;
729       result += current +'\n' ;
730       // system(current.c_str());
731     }
732     return result;
733   }
734 
735 #if !defined(NSPIRE_NEWLIB) && !defined(RTOS_THREADX) && !defined(EMCC) &&!defined(NSPIRE) && !defined FXCG && !defined(KHICAS) && !defined GIAC_HAS_STO_38
736   multimap<string,string> html_mtt,html_mall;
737   std::vector<std::string> html_vtt,html_vall;
738 
739   // WARNING rebuilding caches works with old version of hevea (1.10) but not with hevea 2.29
740   // find index nodes in file file
find_index(const std::string & current_dir,const std::string & file,multimap<std::string,std::string> & mtt,multimap<std::string,std::string> & mall,bool is_index=false,bool warn=false)741   static bool find_index(const std::string & current_dir,const std::string & file,multimap<std::string,std::string>&mtt,multimap<std::string,std::string>&mall,bool is_index=false,bool warn=false){
742     if (access(file.c_str(),R_OK))
743       return false;
744     ifstream i(file.c_str());
745     // Skip navigation panel
746 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
747     char * buf=new char[BUFFER_SIZE+1];
748 #else
749     char buf[BUFFER_SIZE+1];
750 #endif
751     for (;i && !i.eof();){
752       i.getline(buf,BUFFER_SIZE,'\n');
753       string s(buf),stmp;
754       if (s=="<!--End of Navigation Panel-->") // latex2html?
755 	break;
756       int t=int(s.size());
757       if (t>24 && ((stmp=s.substr(t-24,24))=="<LI CLASS=\"li-indexenv\">" || stmp=="<li class=\"li-indexenv\">")){
758 	// hevea file contains index
759 	for (;i && !i.eof(); ){
760 	  i.getline(buf,BUFFER_SIZE,'\n');
761 	  s=buf;
762 	  t=int(s.size());
763 	  if (t>29 && ((stmp=s.substr(0,29))=="</LI><LI CLASS=\"li-indexenv\">" || stmp=="</li><li class=\"li-indexenv\">")){
764 	    s=s.substr(29,s.size()-29);
765 	    t=int(s.size());
766 	    if (!t || s[0]=='<') // skip index words with special color/font
767 	      continue;
768 	    int endcmd=int(s.find("<")); // position of end of commandname
769 	    if (endcmd>2 && endcmd<t){
770 	      string cmdname=s.substr(0,endcmd-2);
771 	      s=s.substr(endcmd,t-endcmd); // s has all the links
772 	      vector<string> hrefs;
773 	      for (;;){
774 		t=int(s.size());
775 		endcmd=int(s.find("<a href=\""));
776 		if (endcmd<0 || endcmd+9>=t){
777 		  endcmd=int(s.find("<A HREF=\""));
778 		  if (endcmd<0 || endcmd+9>=t)
779 		    break;
780 		}
781 		s=s.substr(endcmd+9,s.size()-endcmd-9);
782 		t=int(s.size());
783 		endcmd=int(s.find("\""));
784 		if (endcmd<0 || endcmd+2>=t)
785 		  break;
786 		string link=s.substr(0,endcmd);
787 		if (link[0]=='#')
788 		  link = file + link;
789 		else
790 		  link = current_dir + link;
791 		s=s.substr(endcmd+2,s.size()-endcmd-2);
792 		t=int(s.size());
793 		if (t<3)
794 		  break;
795 		if (s.substr(0,3)=="<B>" || (t>30 && s.substr(0,29)=="<span style=\"font-weight:bold"))
796 		  hrefs.insert(hrefs.begin(),link);
797 		else
798 		  hrefs.push_back(link);
799 	      }
800 	      vector<string>::const_iterator it=hrefs.begin(),itend=hrefs.end();
801 	      for (;it!=itend;++it){
802 		if (it==hrefs.begin())
803 		  mtt.insert(pair<string,string>(cmdname,*it));
804 		mall.insert(pair<string,string>(cmdname,*it));
805 	      }
806 	    } // if (endcmd>2 && endcmd<t)
807 	  } // if (t>29 &&...
808 	} // for (;i && !i.eof();) end of file
809 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
810 	delete [] buf;
811 #endif
812 	return true;
813       } // end hevea file with index
814       // latex2html only?
815       if (t>14 && s.substr(t-14,14)=="Index</A></B> "){
816 	// look in the corresponding index file instead
817 	int t1=int(s.find("HREF"))+6;
818 	if (t1>=0 && t1<t-16){
819 	  s=s.substr(t1,t-16-t1);
820 	  if (warn)
821 	    cerr << "Using index " << s << endl;
822 	  find_index(current_dir,current_dir+s,mtt,mall,true,warn);
823 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
824 	  delete [] buf;
825 #endif
826 	  return true;
827 	}
828       }
829 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
830       delete [] buf;
831 #endif
832     }
833     string tmp;
834     char c;
835     for (;i && !i.eof();){
836       // read i search for a <A word
837       i >> tmp;
838       int l=int(tmp.size());
839       string tts;
840       if (is_index){
841 	if (l<13)
842 	  continue;
843 	int tmpl=0;
844 	if (tmp.substr(0,8)=="<STRONG>")
845 	  tmpl=8;
846 	if (tmp.substr(0,12)=="<DT><STRONG>")
847 	  tmpl=12;
848 	if (!tmpl)
849 	  continue;
850 	int l1=int(tmp.find("</STRONG>"));
851 	if (l1<=tmpl || l1>=l)
852 	  continue;
853 	tts=tmp.substr(tmpl,l1-tmpl);
854       }
855       else {
856 	if (l<2 || tmp.substr(l-2,2)!="<A")
857 	  continue;
858       }
859       // read the link
860       tmp="";
861       int s=0;
862       for (;i && !i.eof();){
863 	i.get(c);
864 	++s;
865 	tmp += c;
866 	if (s>4 && tmp.substr(s-4,4)=="<DT>"){
867 	  // no <B> found, truncate tmp to the first </A> found
868 	  int l=int(tmp.find("</A>"));
869 	  if (l<s && l>0)
870 	    tmp=tmp.substr(0,l);
871 	  s=int(tmp.size());
872 	  break;
873 	}
874 	if (s>8 && tmp.substr(s-8,8)=="</B></A>"){
875 	  // Find backward the first occurence of <A
876 	  int l=s-8;
877 	  for (;l>0;--l){
878 	    if (tmp[l]=='<' && tmp[l+1]=='A')
879 	      break;
880 	  }
881 	  if (l){
882 	    tmp=tmp.substr(l,s-l);
883 	    s -= l;
884 	  }
885 	  break;
886 	}
887       }
888       // cerr << tmp << endl;
889       // analysis, search for HREF
890       int href=int(tmp.find("HREF=\""));
891       if (href<0 || href+6>=s)
892 	continue;
893       string hrefs(current_dir);
894       int hrefend=0;
895       for (int j=href+6;j<s;++j){
896 	if (tmp[j]=='"'){ // remove HREF=
897 	  hrefend=j+1;
898 	  break;
899 	}
900 	hrefs += tmp[j];
901       }
902       if (!hrefend)
903 	continue;
904       if (is_index){
905 	mtt.insert(pair<string,string>(tts,hrefs));
906 	mall.insert(pair<string,string>(tts,hrefs));
907       }
908       else {
909 	// search for TT
910 	int tt=int(tmp.find("<TT>")),ttend=tt;
911 	if (tt>=0 && tt+6<s){
912 	  for (ttend+=4;ttend<s;++ttend){
913 	    if (tmp[ttend]=='<'){
914 	      ttend +=5;
915 	      break;
916 	    }
917 	    tts += tmp[ttend];
918 	  }
919 	  mtt.insert(pair<string,string>(tts,hrefs));
920 	  mall.insert(pair<string,string>(tts,hrefs));
921 	  tmp=tmp.substr(0,tt)+tmp.substr(ttend,tmp.size()-ttend);
922 	}
923 	// add href for all normal words
924 	s=int(tmp.size());
925 	int j=hrefend+1;
926 	for (;j<s;){
927 	  // read word
928 	  int pos=int(tmp.find(' ',j));
929 	  if (pos>j && pos<s){
930 	    // add it
931 	    string tmpins(tmp.substr(j,pos-j));
932 	    mall.insert(pair<string,string>(tmpins,hrefs));
933 	  }
934 	  if (pos==-1){
935 	    string tmpins(tmp.substr(j,s-j));
936 	    mall.insert(pair<string,string>(tmpins,hrefs));
937 	    break;
938 	  }
939 	  j=pos+1;
940 	}
941       } // end else is_index
942     }
943     return false;
944   }
945 
946   static const string subdir_strings[]={"cascmd","casgeo","casrouge","cassim","castor","tutoriel","casinter","casexo","cascas"};
947   static const int subdir_taille=sizeof(subdir_strings)/sizeof(string);
equalposcomp(const string * tab,const string & s)948   static int equalposcomp(const string * tab,const string & s){
949     int i=int(s.size())-1;
950     for (;i>=0;--i){
951       if (s[i]=='/')
952 	break;
953     }
954     ++i;
955     string t=s.substr(i,s.size()-i);
956     i=int(t.size())-1;
957     for (;i>=0;--i){
958       if (t[i]=='_')
959 	t=t.substr(0,i);
960     }
961     for (i=0;i<subdir_taille;++i){
962       // cerr << *(tab+i) << " " << t << endl;
963       if (*(tab+i)==t)
964 	return i+1;
965     }
966     return 0;
967   }
968 
969 #if ! (defined VISUALC || defined BESTA_OS || defined FREERTOS || defined NSPIRE || defined FXCG || defined NSPIRE_NEWLIB || defined(KHICAS))
970 #if defined WIN32 || !defined DT_DIR
dir_select(const struct dirent * d)971   static int dir_select (const struct dirent *d){
972     string s(d->d_name);
973     // cerr << s << endl;
974     int t=s.size();
975     if (s[t-1]=='\\'){
976       return s!="." && s!="..";
977     }
978     if (t<9)
979       return 0;
980     if (s[t-1]=='l'){
981       s=s.substr(0,t-1);
982       --t;
983     }
984     if (t>9)
985       s=s.substr(t-9,9);
986     return  s=="index.htm";
987   }
988 #else
989 // __APPLE_CC__ == 5666 on Mac OS X 10.6, 5658 on geogebra build system OS X 10.8
990 // should check __APPLE__ OS X version instead!
991 #if ( defined(__MAC_OS_X_VERSION_MAX_ALLOWED)&&  __MAC_OS_X_VERSION_MAX_ALLOWED<  1080 ) || ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED)&&  __IPHONE_OS_VERSION_MAX_ALLOWED<  60100 ) || ( defined(__OpenBSD__)&& OpenBSD<201905) || ( defined(__FreeBSD_version)&&  __FreeBSD_version<800501)
dir_select(struct dirent * d)992   static int dir_select (struct dirent *d){
993 #else
994   static int dir_select (const struct dirent *d){
995 #endif
996     string s(d->d_name);
997     if (d->d_type==DT_DIR || equalposcomp(subdir_strings,s)){
998       return s!="." && s!="..";
999     }
1000     int t=s.size();
1001     if (t<9)
1002       return 0;
1003     if (s[t-1]=='l'){
1004       s=s.substr(0,t-1);
1005       --t;
1006     }
1007     if (t>9)
1008       s=s.substr(t-9,9);
1009     return  s=="index.htm";
1010   }
1011 #endif
1012 #endif // visualc
1013 
1014   void find_all_index(const std::string & subdir,multimap<std::string,std::string> & mtt,multimap<std::string,std::string> & mall){
1015 #if defined GNUWINCE || defined __MINGW_H || defined __ANDROID__ || defined EMCC || defined NSPIRE_NEWLIB || defined FXCG || defined KHICAS
1016     return;
1017 #else
1018     // cerr << "HTML help Scanning " << subdir << endl;
1019     DIR *dp;
1020     struct dirent *ep;
1021 
1022     dp = opendir (subdir.c_str());
1023     if (dp != NULL){
1024       string s;
1025       int t;
1026       while ( (ep = readdir (dp)) ){
1027 	s=ep->d_name;
1028 	t=s.size();
1029 	if (t>5 && s.substr(t-4,4)=="html")
1030 	  html_vall.push_back(subdir+s);
1031       }
1032       closedir (dp);
1033     }
1034 
1035     struct dirent **eps;
1036     int n;
1037 #if defined APPLE_SMART || defined NO_SCANDIR
1038     n =-1;
1039 #else
1040     n = scandir (subdir.c_str(), &eps, dir_select, alphasort);
1041 #endif
1042     if (n >= 0){
1043       bool index_done=false;
1044       int cnt;
1045       for (cnt = -1; cnt < n; ++cnt){
1046 	string s;
1047 	if (cnt==-1)
1048 	  s="index.html";
1049 	else
1050 	  s=eps[cnt]->d_name;
1051 	s= subdir+s;
1052 #if defined WIN32 || !defined DT_DIR
1053         int t=s.size();
1054         if (s[t-1]=='\\')
1055 	  find_all_index(s+"/",mtt,mall);
1056         else {
1057 	  if (!index_done)
1058 	    index_done=find_index(subdir,s,mtt,mall);
1059 	}
1060 #else
1061 	unsigned char type=cnt>=0?eps[cnt]->d_type:0;
1062 	if (type==DT_DIR || equalposcomp(subdir_strings,s))
1063 	  find_all_index(s+"/",mtt,mall);
1064 	else {
1065 	  if (!index_done)
1066 	    index_done=find_index(subdir,s,mtt,mall);
1067 	}
1068 #endif
1069       }
1070     }
1071 #endif // GNUWINCE
1072   }
1073 
1074   // Return all HTML nodes refered to s in mtt
1075   std::vector<std::string> html_help(multimap<std::string,std::string> & mtt,const std::string & s){
1076     vector<string> v;
1077     multimap<string,string>::const_iterator it=mtt.lower_bound(s),itend=mtt.upper_bound(s);
1078     for (;it!=itend;++it){
1079       v.push_back(it->second);
1080     }
1081     return v;
1082   }
1083 
1084   string xcasroot_dir(const char * arg){
1085     string xcasroot;
1086     if (getenv("XCAS_ROOT")){
1087       xcasroot=string(getenv("XCAS_ROOT"));
1088       if (xcasroot.empty())
1089 	xcasroot="/";
1090       if (xcasroot[xcasroot.size()-1]!='/')
1091 	xcasroot+='/';
1092     }
1093     else {
1094       xcasroot=arg;
1095       int xcasroot_size=int(xcasroot.size())-1;
1096       for (;xcasroot_size>=0;--xcasroot_size){
1097 	if (xcasroot[xcasroot_size]=='/')
1098 	  break;
1099       }
1100       if (xcasroot_size>0)
1101 	xcasroot=xcasroot.substr(0,xcasroot_size)+"/";
1102       else {
1103 	if (access("/usr/bin/xcas",R_OK)==0)
1104 	  xcasroot="/usr/bin/";
1105 	else {
1106 #ifdef __APPLE__
1107 	if (access("/Applications/usr/bin/xcas",R_OK)==0)
1108 	  xcasroot="/Applications/usr/bin";
1109 #else
1110 	  if (access("/usr/local/bin/xcas",R_OK)==0)
1111 	    xcasroot="/usr/local/bin/";
1112 #endif
1113 	  else
1114 	    xcasroot="./";
1115 	}
1116       }
1117     }
1118     // ofstream of("/tmp/xcasroot");
1119     // of << xcasroot << endl;
1120     return xcasroot;
1121   }
1122 
1123   // extern int debug_infolevel;
1124   static bool get_index_from_cache(const char * filename, multimap<string,string> & multi,bool verbose){
1125 #if defined VISUALC || defined BESTA_OS  || defined FREERTOS
1126     char * buf = new char[BUFFER_SIZE];
1127 #else
1128     char buf[BUFFER_SIZE];
1129 #endif
1130     ifstream if_mtt(filename);
1131     int n=0;
1132     while (if_mtt && !if_mtt.eof()){
1133       if_mtt.getline(buf,BUFFER_SIZE,char(0xa4)); // was '�', utf8 not compatible, octal \244
1134       if (!if_mtt || if_mtt.eof()){
1135 	if (verbose)
1136 	  cerr << "// Read " << n << " entries from cache " << filename << endl;
1137 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
1138 	delete [] buf;
1139 #endif
1140 	return true;
1141       }
1142       string first(buf);
1143       if_mtt.getline(buf,BUFFER_SIZE,char(0xa4));
1144       if (!if_mtt || if_mtt.eof()){
1145 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
1146 	delete [] buf;
1147 #endif
1148 	return false;
1149       }
1150       multi.insert(pair<string,string>(first,buf));
1151       if (!(n%100)){ // check every 100 links if link exists
1152 	first=buf;
1153 	int l=int(first.size()),j;
1154 	char ch=0;
1155 	for (j=l-1;j>=0;--j){
1156 	  ch=first[j];
1157 	  if (ch=='#' || ch=='/')
1158 	    break;
1159 	}
1160 	if (j>0 && ch=='#')
1161 	  first=first.substr(0,j);
1162 	if (access(first.c_str(),R_OK)){
1163 	  multi.clear();
1164 	  cerr << "Wrong cache! " << filename << endl;
1165 	  if_mtt.close();
1166 #if !defined RTOS_THREADX && !defined BESTA_OS && !defined FREERTOS
1167 	  if (unlink(filename)==-1)
1168 	    cerr <<  "You don't have write permissions on " << filename <<".\nYou must ask someone who has write permissions to remove " << filename << endl;
1169 	  else
1170 	    cerr << "Cache file "<< filename << " has been deleted" << endl;
1171 #endif
1172 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
1173 	  delete [] buf;
1174 #endif
1175 	  return false;
1176 	}
1177       }
1178       ++n;
1179       if_mtt.getline(buf,BUFFER_SIZE,'\n');
1180     }
1181     if (verbose)
1182       cerr << "// Read " << n << " entries from cache " << filename ;
1183 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
1184     delete [] buf;
1185 #endif
1186     return true;
1187   }
1188 
1189   static bool get_index_from_cache(const char * filename, vector<string> & multi,bool verbose){
1190 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
1191     char * buf = new char[BUFFER_SIZE];
1192 #else
1193     char buf[BUFFER_SIZE];
1194 #endif
1195     ifstream if_mtt(filename);
1196     int n=0;
1197     while (if_mtt && !if_mtt.eof()){
1198       if_mtt.getline(buf,BUFFER_SIZE,char(0xa4));
1199       if (!if_mtt || if_mtt.eof()){
1200 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
1201 	delete [] buf;
1202 #endif
1203 	if (verbose)
1204 	  cerr << "// Read " << n << " entries from cache " << filename << endl;
1205 	return true;
1206       }
1207       multi.push_back(buf);
1208       ++n;
1209       if_mtt.getline(buf,BUFFER_SIZE,'\n');
1210     }
1211 #if defined VISUALC || defined BESTA_OS || defined FREERTOS
1212     delete [] buf;
1213 #endif
1214     if (verbose)
1215       cerr << "// Read " << n << " entries from cache " << filename ;
1216     return true;
1217   }
1218 
1219   string html_help_init(const char * arg,int language,bool verbose,bool force_rebuild){
1220     string xcasroot=xcasroot_dir(arg);
1221     // HTML online help
1222     string html_help_dir=xcasroot+"doc/";
1223     if (access(html_help_dir.c_str(),R_OK)){
1224 #ifdef __APPLE__
1225       if (!access("/Applications/usr/bin/icas",R_OK))
1226 	html_help_dir="/Applications/usr/share/giac/doc/";
1227 #else
1228       if (!access("/usr/bin/xcas",R_OK))
1229 	html_help_dir="/usr/share/giac/doc/";
1230 #endif
1231       else {
1232 	if (!access("/usr/local/bin/xcas",R_OK))
1233 	  html_help_dir="/usr/local/share/giac/doc/";
1234       }
1235     }
1236     if (access(html_help_dir.c_str(),R_OK) && xcasroot.size()>4 && xcasroot.substr(xcasroot.size()-4,4)=="bin/")
1237       html_help_dir=xcasroot.substr(0,xcasroot.size()-4)+"share/giac/doc/";
1238     if (access(html_help_dir.c_str(),R_OK))
1239       cerr << "Unable to open HTML doc directory " << html_help_dir << endl;
1240     html_help_dir += find_lang_prefix(language);
1241 #ifdef WIN32
1242     string html_help_dir_save=html_help_dir;
1243     html_help_dir +="cascmd_"+find_lang_prefix(giac::language(context0)); // temporary workaround, for win archive copy doc/fr/html_vall to doc/fr/cascmd_fr/html_vall and change path
1244 #endif
1245     html_mtt.clear();
1246     html_mall.clear();
1247     html_vall.clear();
1248     // Get indices from file cache if it exists
1249     if (!force_rebuild && !access((html_help_dir+"html_mtt").c_str(),R_OK) && !access((html_help_dir+"html_mall").c_str(),R_OK) && !access((html_help_dir+"html_vall").c_str(),R_OK)){
1250       if (get_index_from_cache((html_help_dir+"html_mtt").c_str(),html_mtt,verbose)&&
1251 	  get_index_from_cache((html_help_dir+"html_mall").c_str(),html_mall,verbose)&&
1252 	  get_index_from_cache((html_help_dir+"html_vall").c_str(),html_vall,verbose) )
1253 	return html_help_dir;
1254     }
1255     find_all_index(html_help_dir,html_mtt,html_mall);
1256 #ifdef WIN32
1257     for (unsigned i=0;i<sizeof(subdir_strings)/sizeof(const string);++i){
1258       find_all_index(html_help_dir_save+subdir_strings[i]+"/",html_mtt,html_mall);
1259     }
1260 #endif
1261     // Write all indices in a file cache
1262     ofstream of_mtt((html_help_dir+"html_mtt").c_str());
1263     multimap<string,string>::const_iterator it=html_mtt.begin(),itend=html_mtt.end();
1264     for (;it!=itend;++it)
1265       of_mtt << it->first << char(0xa4) << it->second << char(0xa4) << endl;
1266     of_mtt.close();
1267     ofstream of_mall((html_help_dir+"html_mall").c_str());
1268     it=html_mall.begin();itend=html_mall.end();
1269     for (;it!=itend;++it)
1270       of_mall << it->first << char(0xa4) << it->second << char(0xa4) << endl;
1271     of_mall.close();
1272     ofstream of_vall((html_help_dir+"html_vall").c_str());
1273     vector<string>::const_iterator st=html_vall.begin(),stend=html_vall.end();
1274     for (;st!=stend;++st)
1275       of_vall << *st << char(0xa4) << endl;
1276     of_vall.close();
1277     /*
1278     if (debug_infolevel){
1279       vector<string>::const_iterator it=html_vall.begin(),itend=html_vall.end();
1280       for (;it!=itend;++it)
1281 	cerr << *it << endl;
1282     }
1283     */
1284     return html_help_dir;
1285   }
1286 
1287   static bool multigrep(FILE * f,const string & s){
1288     int l=int(s.size());
1289     // find spaces
1290     string tmp;
1291     vector<string> vs;
1292     for (int i=0;i<l;++i){
1293       if (s[i]==' '){
1294 	if (!tmp.empty())
1295 	  vs.push_back(tmp);
1296 	tmp="";
1297       }
1298       else
1299 	tmp+=s[i];
1300     }
1301     if (!tmp.empty())
1302       vs.push_back(tmp);
1303     l=int(vs.size());
1304     if (!f || !l)
1305       return false;
1306     char c;
1307     for (tmp="";;){
1308       if (feof(f) || ferror(f)){
1309 	return false;
1310       }
1311       c=fgetc(f);
1312       if (c==char(0xc3)){
1313 	if (feof(f) || ferror(f))
1314 	  return false;
1315 	unsigned code=fgetc(f);
1316 	switch (code){
1317 	case 0xa8: case 0xa9: case 0xaa:
1318 	  c='e';
1319 	  break;
1320 	case 0xa0: case 0xa1: case 0xa2:
1321 	  c='a';
1322 	  break;
1323 	case 0xae: case 0xaf:
1324 	  c='i';
1325 	  break;
1326 	case 0xb4:
1327 	  c='o';
1328 	  break;
1329 	case 0xb9: case 0xbb:
1330 	  c='u';
1331 	  break;
1332 	case 0xa7:
1333 	  c='c';
1334 	  break;
1335 	}
1336       }
1337       c=tolower(c);
1338       if (c=='&'){
1339 	c=fgetc(f);
1340 	if (c=='#'){
1341 	  unsigned code=0,base=10;
1342 	  for (;;){
1343 	    if (feof(f) || ferror(f))
1344 	      return false;
1345 	    c=fgetc(f);
1346 	    if (c=='x' || c=='X')
1347 	      base=16;
1348 	    if (c=='o' || c=='O')
1349 	      base=8;
1350 	    if (c!=';'){
1351 	      if (base!=16)
1352 		code = code*base+c-'0';
1353 	      else {
1354 		if (c>='A' && c<='F')
1355 		  code = code*base + c-'A'+10;
1356 		if (c>='a' && c<='f')
1357 		  code = code*base + c-'a'+10;
1358 		if (c>='0' && c<='9')
1359 		  code = code*base + c-'0';
1360 	      }
1361 	    }
1362 	    else{
1363 	      switch (code){
1364 	      case 0xe8: case 0xe9: case 0xea:
1365 		c='e';
1366 		break;
1367 	      case 0xe0: case 0xe2:
1368 		c='a';
1369 		break;
1370 	      case 0xf4:
1371 		c='o';
1372 		break;
1373 	      case 0xf9: case 0xfb:
1374 		c='u';
1375 		break;
1376 	      case 0xe7:
1377 		c='c';
1378 		break;
1379 	      case 238:
1380 		c='i';
1381 		break;
1382 	      }
1383 	      break;
1384 	    }
1385 	  }
1386 	}
1387       }
1388       if (c==' '){
1389 	if (!tmp.empty()){ // search tmp in vs
1390 	  unsigned tmpl=unsigned(tmp.size()),tmpvs;
1391 	  for (int i=0;i<l;++i){
1392 	    if ( (tmpvs=unsigned(vs[i].size()))<=tmpl && tmp.substr(0,tmpvs)==vs[i]){
1393 	      vs.erase(vs.begin()+i);
1394 	      --l;
1395 	      if (l<=0)
1396 		return true;
1397 	    }
1398 	  }
1399 	}
1400 	tmp="";
1401       }
1402       else
1403 	tmp+=c;
1404     }
1405   }
1406 
1407   bool grep(FILE * f,const string & s){
1408     int l=int(s.size());
1409     int pos=0;
1410     if (!f || !l)
1411       return false;
1412     char c0=tolower(s[0]),c;
1413     for (;;){
1414       if (feof(f) || ferror(f)){
1415 	return false;
1416       }
1417       c=tolower(fgetc(f));
1418       if (c==tolower(s[pos])){
1419 	++pos;
1420 	if (pos==l){
1421 	  return true;
1422 	}
1423       }
1424       else {
1425 	if (c==c0)
1426 	  pos=1;
1427 	else
1428 	  pos=0;
1429       }
1430     }
1431   }
1432 
1433   bool grep(const string & filename,const string & s){
1434     FILE * f=fopen(filename.c_str(),"r");
1435     bool res=multigrep(f,s);
1436     if (f)
1437       fclose(f);
1438     return res;
1439   }
1440 #endif // RTOS_THREADX
1441 
1442   // static char otherchars[]="_.~������������������������������������������������������������������������������������������������";
1443 
1444   bool isalphan(char ch){
1445     if (ch>='0' && ch<='9')
1446       return true;
1447     if (ch>='a' && ch<='z')
1448       return true;
1449     if (ch>='A' && ch<='Z')
1450       return true;
1451     if (unsigned(ch)>128)
1452       return true;
1453     if (ch=='_' || ch=='.' || ch=='~')
1454       return true;
1455     /*
1456     char * ptr=otherchars;
1457     for (;*ptr;++ptr){
1458       if (ch==*ptr)
1459 	return true;
1460     }
1461     */
1462     return false;
1463   }
1464 
1465   std::string unlocalize(const std::string & s){
1466     std::string res,tmp;
1467     int ss=int(s.size());
1468     std::map<std::string,std::string>::const_iterator it,itend=lexer_localization_map().end();
1469     int mode=0; // 1 if inside a string
1470     for (int i=0;;++i){
1471       char ch=s[i];
1472       if (mode){
1473 	if (ch=='"'){
1474 	  if (res.empty() || res[res.size()-1]!='\\')
1475 	    mode=0;
1476 	}
1477 	res += ch;
1478 	if (i==ss)
1479 	  break;
1480 	continue;
1481       }
1482       if (i<ss && isalphan(ch))
1483 	tmp += ch;
1484       else { // search if tmp is in lexer_localization_map
1485 	it=lexer_localization_map().find(tmp);
1486 	if (it!=itend)
1487 	  tmp = it->second; // it is -> we must translate to giac
1488 	res += tmp;
1489 	tmp = "";
1490 	if (ch=='"'){
1491 	  if (res.empty() || res[res.size()-1]!='\\')
1492 	    mode=1;
1493 	}
1494 	if (i<ss)
1495 	  res += ch;
1496 	else
1497 	  break;
1498       }
1499     }
1500     if (mode==1)
1501       return unlocalize(s+'"');
1502     return res;
1503   }
1504 
1505   std::string localize(const std::string & s,int language){
1506     std::string res,tmp;
1507     int ss=int(s.size());
1508     int mode=0; // 1 if inside a string
1509     std::multimap<std::string,localized_string>::const_iterator it0,it,itend,backend=back_lexer_localization_map().end();
1510     for (int i=0;;++i){
1511       char ch=s[i];
1512       if (mode){
1513 	if (ch=='"'){
1514 	  if (res.empty() || res[res.size()-1]!='\\')
1515 	    mode=0;
1516 	}
1517 	res += ch;
1518 	if (i==ss)
1519 	  break;
1520 	continue;
1521       }
1522       if (i<ss && isalphan(s[i]))
1523 	tmp += s[i];
1524       else { // search if tmp is in back_lexer_localization_map()
1525 	it0=it=back_lexer_localization_map().find(tmp);
1526 	itend=back_lexer_localization_map().upper_bound(tmp);
1527 	if (it!=backend){
1528 	  for (;it!=itend;++it){
1529 	    if (it->second.language==language){
1530 	      tmp = it->second.chaine;
1531 	      break;
1532 	    }
1533 	  }
1534 	  if (it==itend)
1535 	    tmp = it0->second.chaine;
1536 	}
1537 	res += tmp;
1538 	tmp = "";
1539 	if (ch=='"'){
1540 	  if (res.empty() || res[res.size()-1]!='\\')
1541 	    mode=1;
1542 	}
1543 	if (i<ss)
1544 	  res += s[i];
1545 	else
1546 	  break;
1547       }
1548     }
1549     return res;
1550   }
1551 
1552 #ifndef NO_NAMESPACE_GIAC
1553 } // namespace giac
1554 #endif // ndef NO_NAMESPACE_GIAC
1555