1 /*
2
3 eboard - chess client
4 http://www.bergo.eng.br/eboard
5 https://github.com/fbergo/eboard
6 Copyright (C) 2000-2016 Felipe Bergo
7 fbergo/at/gmail/dot/com
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 2 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, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 */
24
25
26 #include "util.h"
27 #include <iostream>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include "global.h"
34 #include "stl.h"
35
36 #include "config.h"
37 #include "eboard.h"
38
FileFinder()39 FileFinder::FileFinder() {
40
41 }
42
~FileFinder()43 FileFinder::~FileFinder() {
44 path.clear();
45 }
46
getPathCount()47 int FileFinder::getPathCount() {
48 return(path.size());
49 }
50
getPath(unsigned int i)51 string &FileFinder::getPath(unsigned int i) {
52 static string dot(".");
53 if ((i<0)||(i>=path.size()))
54 return(dot);
55 return(path[i]);
56 }
57
addDirectory(const char * dir)58 void FileFinder::addDirectory(const char *dir) { path.push_back(string(dir)); }
addDirectory(string & dir)59 void FileFinder::addDirectory(string &dir) { path.push_back(string(dir)); }
60
addMyDirectory(const char * dir)61 void FileFinder::addMyDirectory(const char *dir) {
62 string s;
63 s=global.env.Home; s+='/'; s+=dir;
64 path.push_back(s);
65 }
66
addMyDirectory(string & dir)67 void FileFinder::addMyDirectory(string &dir) {
68 string s;
69 s=global.env.Home; s+='/'; s+=dir;
70 path.push_back(s);
71 }
72
find(string & name,string & out)73 int FileFinder::find(string &name, string &out) {
74 int i,j;
75 string fullname;
76
77 ifstream f(name.c_str());
78 if (!f) {
79 j=path.size();
80 for(i=0;i<j;i++) {
81 fullname = path[i];
82 fullname += '/';
83 fullname += name;
84
85 ifstream g(fullname.c_str());
86 if (g) {
87 out=fullname;
88 g.close();
89 return 1;
90 }
91 }
92 return 0;
93 }
94 f.close();
95
96 // avoid problems with scripts when . is not in the PATH
97 out="./";
98 out+=name;
99
100 return 1;
101 }
102
find(const char * name,char * fullpath)103 int FileFinder::find(const char *name,char *fullpath) {
104 int i,j;
105 char fullname[512];
106
107 ifstream f(name);
108 if (!f) {
109 j=path.size();
110 for(i=0;i<j;i++) {
111 g_strlcpy(fullname,path[i].c_str(),512);
112 g_strlcat(fullname,"/",512);
113 g_strlcat(fullname,name,512);
114 ifstream g(fullname);
115 if (g) {
116 strcpy(fullpath,fullname);
117 g.close();
118 return 1;
119 }
120 }
121 return 0;
122 }
123 f.close();
124 g_strlcpy(fullpath,"./",512);
125 g_strlcat(fullpath,name,512);
126 return 1;
127 }
128
EboardFileFinder()129 EboardFileFinder::EboardFileFinder() {
130 addDirectory(".");
131 addMyDirectory(".eboard");
132 addMyDirectory("share/eboard");
133
134 addDirectory(DATADIR "/eboard");
135 addDirectory("/usr/share/eboard");
136 addDirectory("/usr/local/share/eboard");
137
138 }
139
140 // ---------------------------------------- pattern matching
141
142 PercASetPat percA;
143 PercUpperASetPat percUA;
144 PercBSetPat percB;
145 PercNSetPat percN;
146 PercUpperNSetPat percUN;
147 PercRSetPat percR;
148 PercSSetPat percS;
149 PercUpperSSetPat percUS;
150
Pattern()151 Pattern::Pattern() {
152 eternal=false;
153 }
154
~Pattern()155 Pattern::~Pattern() {
156
157 }
158
reset()159 void Pattern::reset() {
160
161 }
162
ExactStringPat(const char * pat)163 ExactStringPat::ExactStringPat(const char *pat) {
164 int i,j;
165 j=strlen(pat);
166 for(i=0;i<j;i++)
167 pattern.push_back(pat[i]);
168 }
169
tryMatch(list<char>::iterator & first,list<char>::iterator & last)170 int ExactStringPat::tryMatch(list<char>::iterator & first,
171 list<char>::iterator & last) {
172 list<char>::iterator mine,their;
173
174 for(mine=pattern.begin(), their=first;
175 (mine!=pattern.end()) && (their!=last);
176 mine++, their++) {
177 if (*mine != *their) return -1;
178 }
179 if (mine!=pattern.end()) return -1;
180 last=their;
181 return 0;
182 }
183
tryMatch(list<char>::iterator & first,list<char>::iterator & last)184 int CIExactStringPat::tryMatch(list<char>::iterator & first,
185 list<char>::iterator & last) {
186 list<char>::iterator mine,their;
187
188 for(mine=pattern.begin(), their=first;
189 (mine!=pattern.end()) && (their!=last);
190 mine++, their++) {
191 if (toupper(*mine) != toupper(*their)) return -1;
192 }
193 if (mine!=pattern.end()) return -1;
194 last=their;
195 return 0;
196 }
197
KleeneStarPat()198 KleeneStarPat::KleeneStarPat() {
199 reset();
200 }
201
tryMatch(list<char>::iterator & first,list<char>::iterator & last)202 int KleeneStarPat::tryMatch(list<char>::iterator & first,
203 list<char>::iterator & last) {
204 int i;
205 list<char>::iterator dupe1;
206
207 dupe1=first;
208 for(i=CallCount;i;i--) {
209 if (dupe1==last) return -1;
210 dupe1++;
211 }
212 ++CallCount;
213 last=dupe1;
214 return 1;
215 }
216
reset()217 void KleeneStarPat::reset() {
218 CallCount=0;
219 }
220
221 // ------------------------------------------ the set patterns
222
SetPat()223 SetPat::SetPat() {
224 myset=0;
225 }
226
addToSet(char c)227 inline void SetPat::addToSet(char c) {
228 if (myset)
229 myset[(unsigned char)c]=true;
230 }
231
addToSet(const char * s)232 void SetPat::addToSet(const char *s) {
233 while(*s)
234 addToSet(*(s++));
235 }
236
tryMatch(list<char>::iterator & first,list<char>::iterator & last)237 int SetPat::tryMatch(list<char>::iterator & first,
238 list<char>::iterator & last) {
239 list<char>::iterator mine;
240 if (! inSet(*first) )
241 return -1;
242
243 for(mine=first;mine!=last;mine++)
244 if (!inSet(*mine)) {
245 last=mine;
246 return 0;
247 }
248 last=mine;
249 return 0;
250 }
251
inSet(char c)252 inline bool SetPat::inSet(char c) {
253 return( myset[(unsigned char)c] );
254 }
255
clearSet()256 void SetPat::clearSet() {
257 if (myset)
258 for(int i=0;i<256;i++)
259 myset[i]=false;
260 }
261
includeUppercase()262 void SetPat::includeUppercase() {
263 char c;
264 for(c='A';c<='Z';c++)
265 addToSet(c);
266 }
267
includeLowercase()268 void SetPat::includeLowercase() {
269 char c;
270 for(c='a';c<='z';c++)
271 addToSet(c);
272 }
273
includeLetters()274 void SetPat::includeLetters() {
275 includeLowercase();
276 includeUppercase();
277 }
278
includeNumbers()279 void SetPat::includeNumbers() {
280 char c;
281 for(c='0';c<='9';c++)
282 addToSet(c);
283 }
284
285 // ----------------------------------- generic CharSet
286
CharSetPat()287 CharSetPat::CharSetPat() {
288 myset=theset;
289 clearSet();
290 }
291
292 // ----------------------------------- the percent-patterns
293
294 /*
295 %N = [0-9]
296 %n = [0-9] U {+,-}
297 %s = [A-Za-z]
298 %S = [A-Za-z0-9]
299 %a = [A-Za-z] U {(,),*}
300 %A = [A-Za-z0-9] U {(,),*}
301 %b = {space} U {tab}
302 %r = [0-9A-Za-z] U "()[]{}.,;:!@#$%^&*_-+=\\|<>?/~"
303 */
304
305 bool PercNSetPat::theset[256];
306 bool PercBSetPat::theset[256];
307 bool PercSSetPat::theset[256];
308 bool PercASetPat::theset[256];
309 bool PercRSetPat::theset[256];
310 bool PercUpperNSetPat::theset[256];
311 bool PercUpperASetPat::theset[256];
312 bool PercUpperSSetPat::theset[256];
313
PercNSetPat()314 PercNSetPat::PercNSetPat() {
315 myset=theset;
316 clearSet();
317 includeNumbers();
318 addToSet("+-");
319 }
320
PercSSetPat()321 PercSSetPat::PercSSetPat() {
322 myset=theset;
323 clearSet();
324 includeLetters();
325 }
326
PercUpperSSetPat()327 PercUpperSSetPat::PercUpperSSetPat() {
328 myset=theset;
329 clearSet();
330 includeLetters();
331 includeNumbers();
332 addToSet("_");
333 }
334
PercASetPat()335 PercASetPat::PercASetPat() {
336 myset=theset;
337 clearSet();
338 includeLetters();
339 addToSet("()*");
340 }
341
PercUpperASetPat()342 PercUpperASetPat::PercUpperASetPat() {
343 myset=theset;
344 clearSet();
345 includeLetters();
346 includeNumbers();
347 addToSet("()*");
348 }
349
PercBSetPat()350 PercBSetPat::PercBSetPat() {
351 myset=theset;
352 clearSet();
353 addToSet(" \t");
354 }
355
PercRSetPat()356 PercRSetPat::PercRSetPat() {
357 myset=theset;
358 clearSet();
359 includeLetters();
360 includeNumbers();
361 addToSet("()[]{}.,;:!@#$%^&*_-+=\\|<>?/~");
362 }
363
PercUpperNSetPat()364 PercUpperNSetPat::PercUpperNSetPat() {
365 myset=theset;
366 clearSet();
367 includeNumbers();
368 }
369
370 // -----------------------------------
371
PatternMatcher()372 PatternMatcher::PatternMatcher() {
373 token=(char *)malloc(bufsize=4096);
374 bound=&data;
375 }
376
~PatternMatcher()377 PatternMatcher::~PatternMatcher() {
378 list<Pattern *>::iterator pi;
379 for(pi=pattern.begin();pi!=pattern.end();pi++)
380 if (! (*pi)->eternal )
381 delete(*pi);
382 pattern.clear();
383 free(token);
384 }
385
getToken(int index)386 char * PatternMatcher::getToken(int index) {
387 int sz,i;
388 list<char>::iterator ci,p0,p1;
389 list<list<char>::iterator *>::iterator trav;
390
391 for(i=0,trav=matches.begin();i<index;i++) {
392 if (trav==matches.end()) return((char *) "<null>");
393 trav++;
394 }
395
396 p0=*(*trav);
397 trav++;
398 p1=*(*trav);
399
400 for(sz=0,ci=p0;ci!=p1;ci++)
401 sz++;
402 if (sz>bufsize)
403 token=(char *)realloc(token,bufsize=sz+16);
404 memset(token,0,bufsize);
405 for(i=0,ci=p0;ci!=p1;ci++,i++)
406 token[i]=*ci;
407 return token;
408 }
409
append(Pattern * p)410 void PatternMatcher::append(Pattern *p) {
411 cleanUp();
412 pattern.push_back(p);
413 }
414
reset()415 void PatternMatcher::reset() {
416 list<Pattern *>::iterator pi;
417 cleanUp();
418 for(pi=pattern.begin();pi!=pattern.end();pi++)
419 if (! (*pi)->eternal )
420 delete(*pi);
421 pattern.clear();
422 }
423
bindData(list<char> * newdata)424 void PatternMatcher::bindData(list<char> *newdata) {
425 bound=newdata;
426 }
427
match()428 int PatternMatcher::match() {
429 int i;
430 list<Pattern *>::iterator ip;
431 list<char>::iterator ic1,ic2;
432 list<char>::iterator *gnd;
433
434 lesserCleanUp();
435
436 if (!bound)
437 return 0;
438 i=recursiveMatch(ip=pattern.begin(),ic1=bound->begin(),ic2=bound->end());
439 if (i) {
440 gnd=new list<char>::iterator();
441 *gnd=bound->end();
442 matches.push_back(gnd);
443 }
444 return i;
445 }
446
match(const char * stryng)447 int PatternMatcher::match(const char *stryng) {
448 int i,j;
449 list<Pattern *>::iterator ip;
450 list<char>::iterator ic1,ic2;
451 list<char>::iterator *gnd;
452
453 cleanUp();
454 j=strlen(stryng);
455 for(i=0;i<j;i++)
456 data.push_back(stryng[i]);
457 bound=&data;
458
459 i=recursiveMatch(ip=pattern.begin(),ic1=data.begin(),ic2=data.end());
460 if (i) {
461 gnd=new list<char>::iterator();
462 *gnd=data.end();
463 matches.push_back(gnd);
464 }
465 return i;
466 }
467
lesserCleanUp()468 inline void PatternMatcher::lesserCleanUp() {
469 list<list<char>::iterator *>::iterator ti;
470 for(ti=matches.begin();ti!=matches.end();ti++)
471 delete(*ti);
472 matches.clear();
473 }
474
cleanUp()475 void PatternMatcher::cleanUp() {
476 lesserCleanUp();
477 data.clear();
478 }
479
recursiveMatch(list<Pattern * >::iterator & pat,list<char>::iterator & p0,list<char>::iterator & p1)480 int PatternMatcher::recursiveMatch(list<Pattern *>::iterator & pat,
481 list<char>::iterator & p0,
482 list<char>::iterator & p1) {
483 list<char>::iterator b,*z,ic1;
484 list<Pattern *>::iterator pat2;
485 int t,s;
486
487 // null pattern matches null string
488 if ((pat==pattern.end())&&(p0==bound->end()))
489 return 1;
490 // else null pattern matches nothing
491 if (pat==pattern.end())
492 return 0;
493
494 b=p1;
495 pat2=pat;
496
497 (*pat)->reset();
498 t=(*pat)->tryMatch(p0,b);
499
500 switch(t) {
501 case -1: // no match
502 return 0;
503 case 0: // one match
504 z=new list<char>::iterator();
505 (*z)=p0;
506 matches.push_back(z);
507 pat2++;
508 s=recursiveMatch(pat2,b,ic1=bound->end());
509 if (!s) { delete z; matches.pop_back(); }
510 return s;
511 case 1: // matched, and may match again
512 z=new list<char>::iterator();
513 (*z)=p0;
514 matches.push_back(z);
515 pat2++;
516
517 do {
518 s=recursiveMatch(pat2,b,ic1=bound->end());
519 if (!s) {
520 b=p1;
521 t=(*pat)->tryMatch(p0,b);
522 } else
523 return s;
524 } while(t>=0);
525
526 delete z; matches.pop_back();
527 return 0;
528 }
529 return 0;
530 }
531
532 // Ext
533
ExtPatternMatcher()534 ExtPatternMatcher::ExtPatternMatcher() : PatternMatcher() {
535
536 }
537
set(const char * hrp)538 void ExtPatternMatcher::set(const char *hrp) {
539 int i,L,tc;
540 char exactbuf[256],ebsz;
541
542 cleanUp();
543
544 if (!percA.eternal) {
545 percA.eternal=true;
546 percUA.eternal=true;
547 percB.eternal=true;
548 percN.eternal=true;
549 percUN.eternal=true;
550 percR.eternal=true;
551 percS.eternal=true;
552 percUS.eternal=true;
553 }
554
555 tc=0; // token count
556 L=strlen(hrp);
557 memset(exactbuf,0,256);
558 ebsz=0;
559 for(i=0;i<L;i++) {
560 switch(hrp[i]) {
561 case '*':
562 if (ebsz) {
563 append(new ExactStringPat(exactbuf));
564 ++tc;
565 memset(exactbuf,0,256);
566 ebsz=0;
567 }
568 append(new KleeneStarPat());
569 startokens.push_back(tc++);
570
571 break;
572 case '%':
573 if (i==(L-1)) {
574 cerr << _("<PatternMatcher::set> ** bad pattern string: ") << hrp << endl;
575 exit(67);
576 }
577 ++i;
578 if (hrp[i]=='*') {
579 exactbuf[ebsz++]='*';
580 break;
581 }
582 if (hrp[i]=='%') {
583 exactbuf[ebsz++]='%';
584 break;
585 } else {
586 if (ebsz) {
587 append(new ExactStringPat(exactbuf));
588 ++tc;
589 memset(exactbuf,0,256);
590 ebsz=0;
591 }
592 switch(hrp[i]) {
593 case 's':
594 append(&percS);
595 stokens.push_back(tc++);
596 break;
597 case 'S':
598 append(&percUS);
599 stokens.push_back(tc++);
600 break;
601 case 'n':
602 append(&percN);
603 ntokens.push_back(tc++);
604 break;
605 case 'N':
606 append(&percUN);
607 ntokens.push_back(tc++);
608 break;
609 case 'a':
610 append(&percA);
611 atokens.push_back(tc++);
612 break;
613 case 'A':
614 append(&percUA);
615 atokens.push_back(tc++);
616 break;
617 case 'b':
618 append(&percB);
619 btokens.push_back(tc++);
620 break;
621 case 'r':
622 append(&percR);
623 rtokens.push_back(tc++);
624 break;
625 default:
626 cerr << _("<PatternMatcher::set> ** bad pattern string: ") << hrp << endl;
627 exit(68);
628 }
629 }
630 break;
631 default:
632 exactbuf[ebsz++]=hrp[i];
633 break;
634 }
635 }
636 if (ebsz)
637 append(new ExactStringPat(exactbuf));
638 }
639
getXToken(vector<int> & v,int index)640 char *ExtPatternMatcher::getXToken(vector<int> &v, int index) {
641 if (index >= (signed int) (v.size()) )
642 return 0;
643 return(getToken(v[index]));
644 }
645
getSToken(int index)646 char * ExtPatternMatcher::getSToken(int index) {
647 return(getXToken(stokens,index));
648 }
649
getNToken(int index)650 char * ExtPatternMatcher::getNToken(int index) {
651 return(getXToken(ntokens,index));
652 }
653
getStarToken(int index)654 char * ExtPatternMatcher::getStarToken(int index) {
655 return(getXToken(startokens,index));
656 }
657
getAToken(int index)658 char * ExtPatternMatcher::getAToken(int index) {
659 return(getXToken(atokens,index));
660 }
661
getBToken(int index)662 char * ExtPatternMatcher::getBToken(int index) {
663 return(getXToken(btokens,index));
664 }
665
getRToken(int index)666 char * ExtPatternMatcher::getRToken(int index) {
667 return(getXToken(rtokens,index));
668 }
669
670 // ----------------------- PatternBinder
671
add(PatternMatcher * pm0,...)672 void PatternBinder::add(PatternMatcher *pm0,...) {
673 va_list ap;
674 PatternMatcher *pm;
675 group.push_back(pm0);
676 va_start(ap,pm0);
677 for(;;) {
678 pm=va_arg(ap,PatternMatcher *);
679 if (!pm) break;
680 group.push_back(pm);
681 }
682 va_end(ap);
683 }
684
prepare(const char * target)685 void PatternBinder::prepare(const char *target) {
686 list<PatternMatcher *>::iterator gi;
687 int i,j;
688
689 data.clear();
690
691 j=strlen(target);
692 for(i=0;i<j;i++)
693 data.push_back(target[i]);
694
695 for(gi=group.begin();gi!=group.end();gi++)
696 (*gi)->bindData(&data);
697 }
698
699 // ----------------------- zifstream
700
zifstream()701 zifstream::zifstream() {
702 compressed = false;
703 isopen = false;
704 failure = 0;
705 tmpfile[0] = 0;
706 origfile[0] = 0;
707 }
708
zifstream(char * name)709 zifstream::zifstream(char *name) {
710 compressed = false;
711 isopen = false;
712 failure = 0;
713 tmpfile[0] = 0;
714 origfile[0] = 0;
715 open(name);
716 }
717
open(char * name)718 void zifstream::open(char *name) {
719 int n;
720
721 n = strlen(name);
722 if (n>255) {
723 failure = 1;
724 return;
725 }
726
727 strcpy(origfile,name);
728
729 if (n>3)
730 if (!strcmp(&origfile[n-3],".gz")) {
731 compressed = true;
732 copen();
733 return;
734 }
735
736 x.open(origfile,ios::in);
737
738 if (!x) failure = 1;
739 if (x.is_open()) isopen = true;
740 }
741
getline(char * ptr,int len,char delim)742 bool zifstream::getline(char *ptr, int len, char delim) {
743 if (x.getline(ptr,len,delim))
744 return true;
745 else
746 return false;
747 }
748
seekg(unsigned long offset)749 bool zifstream::seekg(unsigned long offset) {
750 x.seekg(offset);
751 return((bool)(x.good()));
752 }
753
tellg()754 unsigned long zifstream::tellg() {
755 return( (unsigned long) x.tellg() );
756 }
757
operator !()758 bool zifstream::operator!() {
759 return(x.fail()!=0);
760 }
761
eof()762 bool zifstream::eof() {
763 return((bool)(x.eof()));
764 }
765
close()766 void zifstream::close() {
767 x.close();
768 if (isopen && compressed)
769 cclose();
770 isopen=false;
771 }
772
fail()773 bool zifstream::fail() {
774 return(failure!=0 || x.fail()!=0);
775 }
776
is_open()777 bool zifstream::is_open() {
778 return(isopen);
779 }
780
copen()781 void zifstream::copen() {
782 int i,n;
783 char cmd[1024];
784
785 n = snprintf(tmpfile,1024,"/tmp/eb%d-%s",(int) getpid(), origfile);
786
787 if (n >= 1024) {
788 failure = 1;
789 return;
790 }
791
792 for(i=5;i<n;i++)
793 if (tmpfile[i] == '/') tmpfile[i] = '_';
794
795 strcpy(ungzfile, tmpfile);
796 ungzfile[n-3] = 0;
797
798 n = snprintf(cmd,1024,"cp -f %s %s",origfile, tmpfile);
799
800 if (n>=1024 || system(cmd)!=0) {
801 failure = 1;
802 return;
803 }
804
805 n = snprintf(cmd,1024,"gzip -d %s",tmpfile);
806
807 if (n>=1024 || system(cmd)!=0) {
808 failure = 1;
809 unlink(tmpfile);
810 return;
811 }
812
813 x.open(ungzfile,ios::in);
814 if (!x) failure = 1;
815 if (x.is_open()) isopen = true;
816
817 if (!isopen) {
818 unlink(tmpfile);
819 unlink(ungzfile);
820 }
821 }
822
cclose()823 void zifstream::cclose() {
824 unlink(ungzfile);
825 }
826
Timestamp(int sec,int usec)827 Timestamp::Timestamp(int sec, int usec) {
828 S=sec;
829 U=usec;
830 }
831
Timestamp()832 Timestamp::Timestamp() {
833 (*this) = Timestamp::now();
834 }
835
operator =(Timestamp & t)836 Timestamp & Timestamp::operator=(Timestamp &t) {
837 S = t.S; U=t.U;
838 return(*this);
839 }
840
operator -(Timestamp & t)841 double Timestamp::operator-(Timestamp &t) {
842 int msec;
843 double sec;
844 msec = 1000*(S-t.S);
845 msec += (U/1000 - t.U/1000);
846 sec = (double) (msec / 1000.0);
847 return sec;
848 }
849
now()850 Timestamp & Timestamp::now() {
851 static Timestamp t(0,0);
852 struct timeval tv;
853 gettimeofday(&tv,0);
854 t.S = (int) tv.tv_sec;
855 t.U = (int) tv.tv_usec;
856 return(t);
857 }
858