Back to index

wims  3.65+svn20090927
lines.c
Go to the documentation of this file.
00001 /*    Copyright (C) 1998-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
00002  *
00003  *  This program is free software; you can redistribute it and/or modify
00004  *  it under the terms of the GNU General Public License as published by
00005  *  the Free Software Foundation; either version 2 of the License, or
00006  *  (at your option) any later version.
00007  *
00008  *  This program is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  *  GNU General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU General Public License
00014  *  along with this program; if not, write to the Free Software
00015  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00016  */
00017        /* line input / output routines */
00018 
00019 #include <stdarg.h>
00020 #define int_buf_size 40
00021 
00022        /* this is rapid. Output string will be erased at the next call. */
00023 char *int2str(int i)
00024 {
00025     int neg,t;
00026     static char int_buf[int_buf_size];
00027     int_buf[int_buf_size-1]=0;
00028     neg=0; if(i<0) {neg=1;i=-i;}
00029     for(t=int_buf_size-2;t>=0;) {
00030        int_buf[t--]=i%10+'0'; i/=10;
00031        if(i==0) break;
00032     }
00033     if(t<=0) {int_buf[0]=0; return int_buf;} /* should never occur. */
00034     if(neg) int_buf[t--]='-';
00035     t++; return int_buf+t;
00036 }
00037 
00038 void *xmalloc(size_t n)
00039 {
00040     void *p;
00041     p=malloc(n);
00042     if(p==NULL) {
00043        fprintf(stderr,"Malloc failure."); exit(1);
00044     }
00045     return p;
00046 }
00047 
00048 int msleep(int ms)
00049 {
00050     struct timespec req, rem;
00051     if(ms<=0) return 0;
00052     req.tv_sec=ms/1000; req.tv_nsec=(ms%1000)*1000*1000;
00053     return nanosleep(&req,&rem);
00054 }
00055 
00056   /* dos/mac to unix/linux translation */
00057 void _tolinux(char *p)
00058 {
00059     char *pp,*p1;
00060     pp=strchr(p,13); if(pp==NULL) return;
00061     for(p1=pp; *pp; pp++) {
00062        if(*pp==13) {
00063            if(*(pp+1)=='\n' || (pp>p && *(pp-1)=='\n') ||
00064               (*(pp+1)=='\\' && *(pp+2)=='\n')) continue;
00065            else *pp='\n';
00066        }
00067        *p1++=*pp;
00068     }
00069     *p1=0;
00070 }
00071 
00072        /* optimized and secure strcpy */
00073 void mystrncpy(char *dest, char *src, int lim)
00074 {
00075     int i;
00076     i=strlen(src);
00077     if(i<0 || (i>=lim && i>=MAX_LINELEN)) error1("cmd_output_too_long");
00078     if(i>=lim) i=lim-1;
00079     memmove(dest,src,i); dest[i]=0;
00080 }
00081 
00082        /* find matching parenthesis.
00083         * The entrance point should be after the opening 
00084         * parenthesis.
00085         * Returns NULL if unmatched. */
00086 char *find_matching(char *p, char c)
00087 {
00088     char *pp;
00089     int parenth, brak, brace;
00090     if(c=='|') {
00091        for(pp=p;*pp!=0;pp++) {
00092            switch(*pp) {
00093               case '|': return pp;
00094               case '(': {
00095                   pp=find_matching(pp+1,')');
00096                   if(pp==NULL) return NULL;
00097                   break;
00098               }
00099               case '[': {
00100                   pp=find_matching(pp+1,']');
00101                   if(pp==NULL) return NULL;
00102                   break;
00103               }
00104               case '{': {
00105                   pp=find_matching(pp+1,'}');
00106                   if(pp==NULL) return NULL;
00107                   break;
00108               }
00109               case ')':
00110               case ']':
00111               case '}': return NULL;
00112               
00113               default: break;
00114            }
00115        }
00116        return NULL;
00117     }
00118     parenth=brak=brace=0;
00119     for(pp=p; *pp!=0; pp++) {
00120        switch(*pp) {
00121            case '[': brak++; break;
00122            case ']': brak--; break;
00123            case '(': parenth++; break;
00124            case ')': parenth--; break;
00125            case '{': brace++; break;
00126            case '}': brace--; break;
00127            default: continue;
00128        }
00129        if(parenth<0 || brak<0 || brace<0) {
00130            if(*pp!=c || parenth>0 || brak>0 || brace>0) return NULL;
00131            else break;
00132        }
00133     }
00134     if(*pp!=c) return NULL;
00135     return pp;
00136 }
00137 
00138        /* Points to the end of the word */
00139 char *find_word_end(char *p)
00140 {
00141     while(!myisspace(*p) && *p!=0) p++;
00142     return p;
00143 }
00144 
00145        /* Strips leading spaces */
00146 char *find_word_start(char *p)
00147 {
00148     while(myisspace(*p)) p++;
00149     return p;
00150 }
00151 
00152        /* find a character in a string, but skipping parentheses. */
00153 char *strparchr(char *p, char c)
00154 {
00155     char *pp;
00156     
00157     for(pp=p;*pp && *pp!=c && pp-p<MAX_LINELEN; pp++) {
00158        switch(*pp) {
00159            case '(': pp=find_matching(pp+1,')'); break;
00160            case '[': pp=find_matching(pp+1,']'); break;
00161            case '{': pp=find_matching(pp+1,'}'); break;
00162        }
00163        if(pp==NULL) return NULL;
00164     }
00165     if(*pp==c) return pp; else return NULL;
00166 }
00167 
00168        /* search for string, skipping parentheses */
00169 char *strparstr(char *p, char *fnd)
00170 {
00171     char *pp, c;
00172     int n;
00173     
00174     if(*fnd==0) return p+strlen(p);
00175     c=*fnd; n=strlen(fnd);
00176     for(pp=p;pp-1!=NULL && *pp!=0; pp++) {
00177        if(*pp==c && (n==1 || strncmp(pp,fnd,n)==0)) return pp;
00178        switch(*pp) {
00179            case '(': pp=find_matching(pp+1,')'); break;
00180            case '[': pp=find_matching(pp+1,']'); break;
00181            case '{': pp=find_matching(pp+1,'}'); break;
00182        }
00183     }
00184     if(pp-1==NULL) pp=strstr(p,fnd);
00185     if(pp!=NULL) return pp; 
00186     else return p+strlen(p);
00187 }
00188     
00189        /* Points to the end of an item */
00190 char *find_item_end(char *p)
00191 {
00192     return strparstr(p,",");
00193 }
00194 
00195        /* Points to the end of an item */
00196 char *find_line_end(char *p)
00197 {
00198     char *pp=strstr(p,"\n");
00199     if(pp==NULL) pp=p+strlen(p);
00200     return pp;
00201 }
00202 
00203 char *charchr(char *p,char *w)
00204 {
00205     return strchr(p,w[0]);
00206 }
00207 
00208        /* Find first occurrence of word */
00209 char *wordchr(char *p, char *w)
00210 {
00211     char *r; int n;
00212 
00213     if(*w==0) return NULL;
00214     n=strlen(w);
00215     for(r=strstr(p,w);r!=NULL && 
00216        ( (r>p && !myisspace(*(r-1))) || (!myisspace(*(r+n)) && *(r+n)!=0) );
00217        r=strstr(r+1,w));
00218     return r;
00219 }
00220 
00221        /* Find first occurrence of item */
00222 char *itemchr(char *p, char *w)
00223 {
00224     char *r, *r1, *r2; int n;
00225 
00226     if(*w==0) return NULL;
00227     n=strlen(w);
00228     for(r=strstr(p,w);r!=NULL; r=strstr(r+1,w)) {
00229        r1=r-1;while(r1>=p && myisspace(*r1)) r1--;
00230        r2=find_word_start(r+n);
00231        if((r1<p || *r1==',') && (*r2==0 || *r2==',')) return r;
00232     }
00233     return r;
00234 }
00235 
00236        /* Find first occurrence of line */
00237 char *linechr(char *p, char *w)
00238 {
00239     char *r;
00240     int n;
00241 
00242     if(*w==0) return NULL;
00243     n=strlen(w);
00244     for(r=strstr(p,w);r!=NULL; r=strstr(r+1,w)) {
00245        if((r<=p || *(r-1)=='\n') && (*(r+n)==0 || *(r+n)=='\n'))
00246          break;
00247     }
00248     return r;
00249 }
00250 
00251        /* find a variable in a string (math expression).
00252         * Returns the pointer or NULL. */
00253 char *varchr(char *p, char *v)
00254 {
00255     char *pp; int n;
00256     if(*v==0) return NULL;
00257     n=strlen(v);
00258     for(pp=strstr(p,v); pp!=NULL; pp=strstr(pp+1,v)) {
00259        if((pp==p || (!myisalnum(*(pp-1)) && *(pp-1)!='_')) &&
00260           ((!myisalnum(*(pp+n)) && *(pp+n)!='_' && *(pp+n)!='\'')
00261            || *(pp+n)==0)) break;
00262     }
00263     return pp;
00264 }
00265 
00266 int _cutit_(char *p, char *list[], int max, char *end_finder(char *pt), int tag)
00267 {
00268     int i;
00269     char *pp, *pe, *p0;
00270     if(tag&1) pp=find_word_start(p); else pp=p; /* strip head space */
00271     for(i=0;i<max && *pp;i++) {
00272        pe=end_finder(pp);
00273        if((tag&2) && myisspace(pe[-1])) { /* strip tailing space */
00274            for(p0=pe; p0>pp && myisspace(p0[-1]); p0--);
00275            if(p0<pe) *p0=0;
00276        }
00277        if(*pe) *pe++=0;
00278        list[i]=pp;
00279        if(tag&1) pp=find_word_start(pe); else pp=pe;
00280     }
00281     return i;
00282 }
00283 
00284 int cutitems(char *p, char *list[], int max)
00285 {
00286     return _cutit_(p,list,max,find_item_end,3);
00287 }
00288 
00289 int cutwords(char *p, char *list[], int max)
00290 {
00291     return _cutit_(find_word_start(p),list,max,find_word_end,1);
00292 }
00293 
00294 int cutlines(char *p, char *list[], int max)
00295 {
00296     return _cutit_(p,list,max,find_line_end,0);
00297 }
00298 
00299 int cutchars(char *p, char *list[], int max)
00300 {
00301     int i; char *pp;
00302     for(i=0,pp=p;*pp && i<max;list[i++]=pp++);
00303     return i;
00304 }
00305 
00306        /* strip trailing spaces; return string end. */
00307 char *strip_trailing_spaces(char *p)
00308 {
00309     char *pp;
00310     if(*p==0) return p;
00311     for(pp=p+strlen(p)-1; pp>=p && myisspace(*pp); pp--);
00312     if(pp[1]) pp[1]=0; return pp;
00313 }
00314 
00315         /* Routines for quick search of item in a list. */
00316 
00317        /* Verify whether a list is well-ordered. For debugging uses.
00318         * Returns 0 if order is OK, -1 otherwise. */
00319 int verify_order(void *list, int items, size_t item_size)
00320 {
00321     int i; char *old, **p;
00322     p=list; old=*p;
00323     for(i=item_size;i<items*item_size;i+=item_size) {
00324        p=list+i;
00325        if(strcmp(*p,old)<0) {
00326            fprintf(stderr,"Table disorder: %s > %s",old,*p);
00327            exit(1);
00328        }
00329        old=*p;
00330     }
00331     return 0;
00332 }
00333 
00334        /* searches a list. Returns index if found, -1 if nomatch. 
00335         * Uses binary search, list must be sorted. */
00336 int search_list(void *list, int items, size_t item_size, const char *str)
00337 {
00338     int i1,i2,j,k;
00339     char **p;
00340     char c;
00341     
00342     if(items<=0) return -1;
00343     j=0; c=*str;
00344     p=list;
00345     k=**p-c; if(k==0) k=strcmp(*p,str);
00346     if(k==0) return k; if(k>0) return -1;
00347     p=list+(items-1)*item_size; 
00348     k=**p-c; if(k==0) k=strcmp(*p,str);
00349     if(k==0) return items-1; if(k<0) return ~items;
00350     for(i1=0,i2=items-1;i2>i1+1;) {
00351        j=(i2+i1)/2;
00352        p=list+(j*item_size);
00353        k=**p-c; if(k==0) k=strcmp(*p,str);
00354        if(k==0) return j;
00355        if(k>0) {i2=j; continue;}
00356        if(k<0) {i1=j; continue;}   
00357     }
00358     return ~i2;
00359 }
00360 
00361        /* Returns number of lines in string p */
00362 unsigned int linenum(char *p)
00363 {
00364     int i; char *pp;
00365 
00366     /* Ending blank line will be thus removed. */
00367     i=strlen(p); if(i>1 && *(p+i-1)=='\n') *(p+i-1)=0;
00368     if(*p=='\n') i=1; else i=0;
00369     for(pp=p; pp!=NULL && *pp!=0; pp=strchr(pp+1,'\n'), i++);
00370     return i;
00371 }
00372 
00373        /* Returns number of items in the list p, comma separated */
00374 unsigned int itemnum(char *p)
00375 {
00376     int i; char *pp;
00377 
00378     if(*p==0) return 0;
00379     for(i=0,pp=p; pp==p || *(pp-1)!=0; pp=find_item_end(pp)+1, i++);
00380     return i;
00381 }
00382 
00383        /* Returns number of words in string p */
00384 unsigned int wordnum(char *p)
00385 {
00386     int i; char *pp;
00387     
00388     for(i=0, pp=find_word_start(p); *pp!=0; i++) {
00389        while(!myisspace(*pp) && *pp!=0) pp++;
00390        while(myisspace(*pp)) pp++;
00391     }
00392     return i;
00393 }
00394 
00395        /* This is just to suppress an annoying compiler warning message. */
00396 unsigned int charnum(char *p)
00397 {
00398     return strlen(p);
00399 }
00400 
00401        /* find n-th line in string p */
00402 char *fnd_line(char *p, int n, char bf[])
00403 {
00404     char *pp;
00405     int i;
00406     
00407     for(i=1,pp=p; pp-1!=NULL && *pp!=0 && i<n; pp=strchr(pp,'\n')+1, i++);
00408     fnd_position=pp; if(pp-1==NULL) {
00409        fnd_position=NULL; fnd_nextpos=""; *bf=0; return bf;
00410     }
00411     for(i=0; *(pp+i)!=0 && *(pp+i)!='\n'; i++) *(bf+i)=*(pp+i);
00412     *(bf+i)=0;
00413     if(pp[i]=='\n') i++; fnd_nextpos=pp+i;
00414     return bf;
00415 }
00416 
00417        /* find n-th item in list p, comma separated */
00418 char *fnd_item(char *p, int n, char bf[])
00419 {
00420     char *pp, *pe;
00421     int i;
00422 
00423     for(i=1,pp=p; i<n && (pp==p || *(pp-1)!=0); pp=find_item_end(pp)+1, i++);
00424     fnd_position=pp; if(pp>p && *(pp-1)==0) {
00425        fnd_position=NULL; *bf=0; return bf;
00426     }
00427     pp=find_word_start(pp); pe=find_item_end(pp);
00428     if(*pe) fnd_nextpos=pe+1; else fnd_nextpos=pe;
00429     while(pe>pp && myisspace(*(pe-1))) pe--;
00430     memmove(bf,pp,pe-pp); bf[pe-pp]=0;
00431     return bf;
00432 }
00433 
00434        /* find n-th word in string p */
00435 char *fnd_word(char *p, int n, char bf[])
00436 {
00437     char *pp;
00438     int i;
00439     
00440     for(i=1, pp=find_word_start(p); *pp!=0 && i<n ; i++) {
00441        while(!myisspace(*pp) && *pp!=0) pp++;
00442        pp=find_word_start(pp);
00443     }
00444     if(*pp) fnd_position=pp; else fnd_position=NULL;
00445     for(i=0; *(pp+i)!=0 && !myisspace(*(pp+i)); i++) *(bf+i)=*(pp+i);
00446     fnd_nextpos=find_word_start(pp+i);
00447     *(bf+i)=0; 
00448     return bf;
00449 }
00450 
00451        /* find n-th char in string p */
00452 char *fnd_char(char *p, int n, char bf[])
00453 {
00454     int t;
00455     
00456     t=strlen(p);
00457     if(n>t || n<1) {*bf=0;fnd_position=NULL; fnd_nextpos="";}
00458     else {
00459        *bf=*(p+n-1); *(bf+1)=0;
00460        fnd_position=p+n-1;fnd_nextpos=p+n;
00461     }
00462     return bf;
00463 }
00464 
00465        /* Returns 1 if semicolons changed to new lines */
00466 int rows2lines(char *p)
00467 {
00468     char *pp, *p2;
00469     int t;
00470     if(strchr(p,'\n')!=NULL) return 0;
00471     for(t=0, pp=p; *pp; pp++) {
00472        if(*pp=='(') {p2=find_matching(pp+1,')'); if(p2!=NULL) pp=p2; continue;}
00473        if(*pp=='[') {p2=find_matching(pp+1,']'); if(p2!=NULL) pp=p2; continue;}
00474        if(*pp=='{') {p2=find_matching(pp+1,'}'); if(p2!=NULL) pp=p2; continue;}
00475        if(*pp==';') {*pp='\n'; t++; continue;}
00476        if(*pp=='&' && myisalpha(*(pp+1))) {
00477            for(p2=pp+1; myisalpha(*p2) && p2-pp<14; p2++);
00478            pp=p2; continue;
00479        }
00480        if(*pp=='&' && *(pp+1)=='#') {
00481            for(p2=pp+2; myisdigit(*p2) && p2-pp<6; p2++);
00482            pp=p2; continue;
00483        }
00484     }
00485     return t;
00486 }
00487 
00488 void lines2rows(char *p)
00489 {
00490     char *pp;
00491     strip_trailing_spaces(p);
00492     for(pp=strchr(find_word_start(p),'\n'); pp!=NULL; pp=strchr(pp+1,'\n'))
00493       *pp=';';
00494 }
00495 
00496 unsigned int rownum(char *p)
00497 {
00498     char buf[MAX_LINELEN+1];
00499     snprintf(buf,sizeof(buf),"%s",p);
00500     rows2lines(buf);
00501     return linenum(buf);
00502 }
00503 
00504        /* find n-th row in a matrix p */
00505 char *fnd_row(char *p, int n, char bf[])
00506 {
00507     rows2lines(p); return fnd_line(p,n,bf);
00508 }
00509 
00510        /* strstr but may have embedde zeros. 
00511         * Returns memory end if not found.
00512         * Supposes memory ends with 0. */
00513 char *memstr(char *s1, char *s2, int len)
00514 {
00515     char *p, *pp;
00516     pp=s1;
00517     for(p=s1; p<s1+len; p=pp) {
00518        pp=strstr(p,s2); if(pp!=NULL) break;
00519        pp=p+strlen(p);
00520        while(pp<s1+len && *pp==0) pp++;
00521     }
00522     return pp;
00523 }
00524 
00525        /* Check whether parentheses are balanced in a given string.
00526         * Returns 0 if OK. */
00527        /* style=0: simple check. style<>0: strong check. */
00528 int check_parentheses(char *p, int style)
00529 {
00530     int i,j,k;
00531     j=strlen(p);
00532     if(j>=MAX_LINELEN) return 65535;
00533     if(style!=0) {
00534        char buf[MAX_LINELEN+1];
00535        char *pp, *pe, c;
00536        for(pp=p;pp<p+j;pp++) {
00537            switch (*pp) {
00538               case ')':
00539               case ']':
00540               case '}': return -1;
00541               case '(': c=')'; goto find;
00542               case '[': c=']'; goto find;
00543               case '{': c='}';
00544               find: {
00545                   pe=find_matching(pp+1,c);
00546                   if(pe==NULL) return 1;
00547                   memcpy(buf,pp+1,pe-pp-1);
00548                   buf[pe-pp-1]=0;
00549                   if((k=check_parentheses(buf,1))!=0) return k;
00550                   else pp=pe;
00551               }
00552               default: break;
00553            }
00554        }
00555        return 0;
00556     }
00557     for(i=k=0;i<j && k>=0;i++) {
00558        if(*(p+i)=='(') k++;
00559        if(*(p+i)==')') k--;
00560     }
00561     return k;
00562 }
00563 
00564        /* Strip enclosing pairs of parentheses */
00565 void strip_enclosing_par(char *p)
00566 {
00567     char *p1;
00568     partest: strip_trailing_spaces(p);
00569     if(*p=='(') {
00570        p1=find_matching(p+1,')');
00571        if(p1 && *(p1+1)==0) {
00572            *p1=0; strcpy(p,find_word_start(p+1));
00573            goto partest;
00574        }
00575     }
00576     if(*p=='[') {
00577        p1=find_matching(p+1,']');
00578        if(p1 && *(p1+1)==0) {
00579            *p1=0; strcpy(p,find_word_start(p+1));
00580            goto partest;
00581        }
00582     }
00583     if(*p=='{') {
00584        p1=find_matching(p+1,'}');
00585        if(p1 && *(p1+1)==0) {
00586            *p1=0; strcpy(p,find_word_start(p+1));
00587            goto partest;
00588        }
00589     }
00590 }
00591 
00592        /* change all spaces into ' ', and collapse multiple occurences */
00593 void singlespace(char *p)
00594 {
00595     char *pp, *pt, *p2;
00596     for(pp=pt=p;*pp;pp++) {
00597        if(!myisspace(*pp)) {*pt++=*pp; continue; }
00598        *pt++=' ';
00599        for(p2=pp+1;myisspace(*p2);p2++); pp=--p2;
00600     }
00601     *pt=0;
00602 }
00603 
00604        /* collapses all space characters in string. */
00605 void nospace(char *p)
00606 {
00607     char *pp, *pt;
00608     for(pp=pt=p;*pp;pp++) if(!myisspace(*pp)) *pt++=*pp;
00609     *pt=0;
00610 }
00611 
00612 void _spaces2_(char *p, char c)
00613 {
00614     char *pp; int n;
00615     singlespace(p);
00616     n=strlen(p); if(*p==' ') {memmove(p,p+1,n);n--;}
00617     if(n==0) return; if(p[n-1]==' ') p[n-1]=0;
00618     for(pp=strchr(p,' '); pp; pp=strchr(pp,' ')) *pp++=c;
00619 }
00620        /* change words to items */
00621 void words2items(char *p)
00622 {   _spaces2_(p,',');       }
00623 
00624        /* change words to lines */
00625 void words2lines(char *p)
00626 {   _spaces2_(p,'\n');      }
00627 
00628        /* change lines to items */
00629 void lines2items(char *p)
00630 {
00631     char *pp;
00632     for(pp=strchr(p,'\n'); pp; pp=strchr(pp,'\n')) *pp++=',';
00633 }
00634 
00635        /* change lines to words */
00636 void lines2words(char *p)
00637 {
00638     char *pp;
00639     for(pp=strchr(p,'\n'); pp; pp=strchr(pp,'\n')) *pp++=' ';
00640 }
00641 
00642        /* change items to words */
00643 void items2words(char *p)
00644 {
00645     char *pp;
00646     for(pp=strparstr(p,",");*pp;pp=strparstr(pp+1,",")) *pp=' ';
00647 }
00648 
00649        /* change items to lines */
00650 void items2lines(char *p)
00651 {
00652     char *pp;
00653     for(pp=strparstr(p,",");*pp;pp=strparstr(pp+1,",")) *pp='\n';
00654 }
00655 
00656 char *acctab="",
00657      *deatab="ceeeeuuuuaaaaaoooooiiiinyyCEEEEUUUUAAAAAOOOOOIIIINY";
00658 
00659        /* fold accented letters to unaccented */
00660 void deaccent(char *p)
00661 {
00662     signed char *sp;
00663     char *v;
00664     for(sp=p;*sp;sp++) {
00665        if(*sp<0 && (v=strchr(acctab,*sp))!=NULL)
00666          *sp=*(deatab+(v-acctab));
00667     }
00668 }
00669 
00670 char *reaccents="'`\"^~";
00671 char *reaccentl="aeiouycnAEIOUYCN";
00672 char *reaccentab="\
00673 eiuyyyccccnnnn\
00674 EIUYYYYCCCCNNNN";
00675 
00676        /* compose accent using symbol keys */
00677 void reaccent(char *p)
00678 {
00679     char *sp, *ap, c;
00680     int i, k;
00681     if(*p==0) return;
00682     for(sp=p+1; *sp; sp++) {
00683        ap=strchr(reaccents,*sp); if(ap==NULL) continue;
00684        i=ap-reaccents;
00685        sp--; ap=strchr(reaccentl,*sp); if(ap==NULL) {sp++; continue;}
00686        k=ap-reaccentl;
00687        c=reaccentab[k*strlen(reaccents)+i];
00688        if(c!=*sp) {*sp=c; strcpy(sp+1,sp+2);}
00689        else sp++;
00690     }
00691 }
00692 
00693        /* modify a string. Bufferlen must be ast least MAX_LINELEN */
00694 void string_modify(char *start, char *bad_beg, char *bad_end, char *good,...)
00695 {
00696     char buf[MAX_LINELEN+1];
00697     int ln, le;
00698     va_list vp;
00699     
00700     va_start(vp,good);
00701     vsnprintf(buf,sizeof(buf),good,vp); va_end(vp);
00702     ln=strlen(buf); le=strlen(bad_end);
00703     if(ln+le+(bad_beg-start)>=MAX_LINELEN) {
00704        error1("string_too_long"); return;
00705     }
00706     if(ln!=bad_end-bad_beg) memmove(bad_beg+ln,bad_end,le+1);
00707     memmove(bad_beg,buf,ln);
00708 }
00709 
00710        /* returns number of bytes written */
00711 int catfile(FILE *outf, char *fn,...)
00712 {
00713     char buf[4096];
00714     va_list vp;
00715     int l,tot,fd;
00716     
00717     tot=0;
00718     va_start(vp,fn);
00719     vsnprintf(buf,sizeof(buf),fn,vp); va_end(vp);
00720     fd=open(buf,O_RDONLY); if(fd==-1) return 0;
00721     for(l=read(fd,buf,4096);l>0 && l<=4096; l=read(fd,buf,4096)) {
00722        fwrite(buf,1,l,outf); tot+=l;
00723     }
00724     close(fd);
00725     return tot;
00726 }
00727 
00728        /* returns -1 if error */
00729 long int filelength(char *fn,...)
00730 {
00731     char buf[MAX_FNAME+1];
00732     va_list vp;
00733     struct stat st;
00734     int l;
00735     
00736     va_start(vp,fn);
00737     vsnprintf(buf,sizeof(buf),fn,vp); va_end(vp);
00738     l=stat(buf,&st); if(l) return -1;
00739     return st.st_size;
00740 }
00741