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