Imported Upstream version 15.8a
[platform/upstream/cscope.git] / src / build.c
1 /*===========================================================================
2  Copyright (c) 1998-2000, The Santa Cruz Operation 
3  All rights reserved.
4  
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7
8  *Redistributions of source code must retain the above copyright notice,
9  this list of conditions and the following disclaimer.
10
11  *Redistributions in binary form must reproduce the above copyright notice,
12  this list of conditions and the following disclaimer in the documentation
13  and/or other materials provided with the distribution.
14
15  *Neither name of The Santa Cruz Operation nor the names of its contributors
16  may be used to endorse or promote products derived from this software
17  without specific prior written permission. 
18
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  INTERRUPTION)
27  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30  DAMAGE. 
31  =========================================================================*/
32
33
34 /*      cscope - interactive C symbol cross-reference
35  *
36  *      main functions
37  */
38
39 #include "build.h"
40
41 #include "global.h"             /* FIXME: get rid of this! */
42
43 #include "library.h"
44 #include "alloc.h"
45 #include "scanner.h"
46 #include "version.h"            /* for FILEVERSION */
47 #include "vp.h"
48
49 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
50 #include <ncurses.h>
51 #else
52 #include <curses.h>
53 #endif
54
55 /* Exported variables: */
56
57 BOOL    buildonly = NO;         /* only build the database */
58 BOOL    unconditional = NO;     /* unconditionally build database */
59 BOOL    fileschanged;           /* assume some files changed */
60
61 /* variable copies of the master strings... */
62 char    invname_buf[] = INVNAME;
63 char    invpost_buf[] = INVPOST;
64 char    reffile_buf[] = REFFILE;
65 char    *invname = invname_buf; /* inverted index to the database */
66 char    *invpost = invpost_buf; /* inverted index postings */
67 char    *reffile = reffile_buf; /* cross-reference file path name */
68
69 char    *newreffile;            /* new cross-reference file name */
70 FILE    *newrefs;               /* new cross-reference */
71 FILE    *postings;              /* new inverted index postings */
72 int     symrefs = -1;           /* cross-reference file */
73
74 INVCONTROL invcontrol;          /* inverted file control structure */
75
76
77 /* Local variables: */
78 static char *newinvname;        /* new inverted index file name */
79 static char *newinvpost;        /* new inverted index postings file name */
80 static long traileroffset;      /* file trailer offset */
81
82
83 /* Internal prototypes: */
84 static  void    cannotindex(void);
85 static  int     compare(const void *s1, const void *s2);
86 static  void    copydata(void);
87 static  void    copyinverted(void);
88 static  char    *getoldfile(void);
89 static  void    movefile(char *new, char *old);
90 static  void    putheader(char *dir);
91 static  void    fetch_include_from_dbase(char *, size_t);
92 static  void    putlist(char **names, int count);
93 static  BOOL    samelist(FILE *oldrefs, char **names, int count);
94
95
96 /* Error handling routine if inverted index creation fails */
97 static void
98 cannotindex(void)
99 {
100     fprintf(stderr, "\
101 cscope: cannot create inverted index; ignoring -q option\n");
102     invertedindex = NO;
103     errorsfound = YES;
104     fprintf(stderr, "\
105 cscope: removed files %s and %s\n", 
106             newinvname, newinvpost);
107     unlink(newinvname);
108     unlink(newinvpost);
109 }
110
111
112 /* see if the name list is the same in the cross-reference file */
113 static BOOL
114 samelist(FILE *oldrefs, char **names, int count)
115 {
116     char    oldname[PATHLEN + 1];   /* name in old cross-reference */
117     int     oldcount;
118     int     i;
119
120     /* see if the number of names is the same */
121     if (fscanf(oldrefs, "%d", &oldcount) != 1 ||
122         oldcount != count) {
123         return(NO);
124     }
125     /* see if the name list is the same */
126     for (i = 0; i < count; ++i) {
127         if ((1 != fscanf(oldrefs," %[^\n]",oldname)) ||
128             strnotequal(oldname, names[i])) {
129             return(NO);
130         }
131     }
132     return(YES);
133 }
134
135
136 /* create the file name(s) used for a new cross-referene */
137
138 void setup_build_filenames(char *reffile)
139 {
140     char *path;                 /* file pathname */
141     char *s;                    /* pointer to basename in path */
142
143     path = mymalloc(strlen(reffile) + 10);
144     strcpy(path, reffile);
145     s = mybasename(path);
146     *s = '\0';
147     strcat(path, "n");
148     ++s;
149     strcpy(s, mybasename(reffile));
150     newreffile = my_strdup(path);
151     strcpy(s, mybasename(invname));
152     newinvname = my_strdup(path);
153     strcpy(s, mybasename(invpost));
154     newinvpost = my_strdup(path);
155     free(path);
156 }
157
158 /* open the database */
159
160 void
161 opendatabase(void)
162 {
163     if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
164         cannotopen(reffile);
165         myexit(1);
166     }
167     blocknumber = -1;   /* force next seek to read the first block */
168         
169     /* open any inverted index */
170     if (invertedindex == YES &&
171         invopen(&invcontrol, invname, invpost, INVAVAIL) == -1) {
172         askforreturn();         /* so user sees message */
173         invertedindex = NO;
174     }
175 }
176
177
178 /* rebuild the database */
179 void
180 rebuild(void)
181 {
182     close(symrefs);
183     if (invertedindex == YES) {
184         invclose(&invcontrol);
185         nsrcoffset = 0;
186         npostings = 0;
187     }
188     build();
189     opendatabase();
190
191     /* revert to the initial display */
192     if (refsfound != NULL) {
193         fclose(refsfound);
194         refsfound = NULL;
195     }
196 }
197
198
199 /* build the cross-reference */
200 void
201 build(void)
202 {
203     unsigned long i;
204     FILE    *oldrefs;           /* old cross-reference file */
205     time_t  reftime;            /* old crossref modification time */
206     char    *file;              /* current file */
207     char    *oldfile;           /* file in old cross-reference */
208     char    newdir[PATHLEN + 1]; /* directory in new cross-reference */
209     char    olddir[PATHLEN + 1]; /* directory in old cross-reference */
210     char    oldname[PATHLEN + 1]; /* name in old cross-reference */
211     unsigned long oldnum;       /* number in old cross-ref */
212     struct  stat statstruct;    /* file status */
213     unsigned long firstfile;    /* first source file in pass */
214     unsigned long lastfile;     /* last source file in pass */
215     int     built = 0;          /* built crossref for these files */
216     int     copied = 0;         /* copied crossref for these files */
217     unsigned long fileindex;            /* source file name index */
218     BOOL    interactive = YES;  /* output progress messages */
219
220     /* normalize the current directory relative to the home directory so
221        the cross-reference is not rebuilt when the user's login is moved */
222     strcpy(newdir, currentdir);
223     if (strcmp(currentdir, home) == 0) {
224         strcpy(newdir, "$HOME");
225     } else if (strncmp(currentdir, home, strlen(home)) == 0) {
226         snprintf(newdir, sizeof(newdir), "$HOME%s", currentdir + strlen(home));
227     }
228     /* sort the source file names (needed for rebuilding) */
229     qsort(srcfiles, nsrcfiles, sizeof(char *), compare);
230
231     /* if there is an old cross-reference and its current directory matches */
232     /* or this is an unconditional build */
233     if ((oldrefs = vpfopen(reffile, "rb")) != NULL
234         && unconditional == NO
235         && fscanf(oldrefs, "cscope %d %" PATHLEN_STR "s", &fileversion, olddir) == 2 
236         && (strcmp(olddir, currentdir) == 0 /* remain compatible */
237             || strcmp(olddir, newdir) == 0)) {
238         /* get the cross-reference file's modification time */
239         fstat(fileno(oldrefs), &statstruct);
240         reftime = statstruct.st_mtime;
241         if (fileversion >= 8) {
242             BOOL        oldcompress = YES;
243             BOOL        oldinvertedindex = NO;
244             BOOL        oldtruncate = NO;
245             int c;
246
247             /* see if there are options in the database */
248             for (;;) {
249                 while((c = getc(oldrefs)) == ' ')
250                     ;           /* do nothing */
251                 if (c != '-') {
252                     ungetc(c, oldrefs);
253                     break;
254                 }
255                 switch (getc(oldrefs)) {
256                 case 'c':       /* ASCII characters only */
257                     oldcompress = NO;
258                     break;
259                 case 'q':       /* quick search */
260                     oldinvertedindex = YES;
261                     fscanf(oldrefs, "%ld", &totalterms);
262                     break;
263                 case 'T':       /* truncate symbols to 8 characters */
264                     oldtruncate = YES;
265                     break;
266                 }
267             }
268             /* check the old and new option settings */
269             if (oldcompress != compress || oldtruncate != trun_syms) {
270                 posterr("\
271 cscope: -c or -T option mismatch between command line and old symbol database\n");
272                 goto force;
273             }
274             if (oldinvertedindex != invertedindex) {
275                 posterr("\
276 cscope: -q option mismatch between command line and old symbol database\n");
277                 if (invertedindex == NO) {
278                     posterr("cscope: removed files %s and %s\n",
279                             invname, invpost);
280                     unlink(invname);
281                     unlink(invpost);
282                 }
283                 goto outofdate;
284             }
285             /* seek to the trailer */
286             if (fscanf(oldrefs, "%ld", &traileroffset) != 1 ||
287                 fseek(oldrefs, traileroffset, SEEK_SET) == -1) {
288                 posterr("cscope: incorrect symbol database file format\n");
289                 goto force;
290             }
291         }
292         /* if assuming that some files have changed */
293         if (fileschanged == YES) {
294             goto outofdate;
295         }
296         /* see if the directory lists are the same */
297         if (samelist(oldrefs, srcdirs, nsrcdirs) == NO 
298             || samelist(oldrefs, incdirs, nincdirs) == NO 
299             /* get the old number of files */
300             || fscanf(oldrefs, "%lu", &oldnum) != 1 
301             /* skip the string space size */
302             || (fileversion >= 9 && fscanf(oldrefs, "%*s") != 0)) {
303             goto outofdate;
304         }
305         /* see if the list of source files is the same and
306            none have been changed up to the included files */
307         for (i = 0; i < nsrcfiles; ++i) {
308             if ((1 != fscanf(oldrefs," %[^\n]",oldname))
309                 || strnotequal(oldname, srcfiles[i])
310                 || (lstat(srcfiles[i], &statstruct) != 0)
311                 || (statstruct.st_mtime > reftime)
312                 ) {
313                 goto outofdate;
314             }
315         }
316         /* the old cross-reference is up-to-date */
317         /* so get the list of included files */
318         while (i++ < oldnum && fgets(oldname, sizeof(oldname), oldrefs)) {
319             addsrcfile(oldname);
320         }
321         fclose(oldrefs);
322         return;
323                 
324     outofdate:
325         /* if the database format has changed, rebuild it all */
326         if (fileversion != FILEVERSION) {
327             fprintf(stderr, "\
328 cscope: converting to new symbol database file format\n");
329             goto force;
330         }
331         /* reopen the old cross-reference file for fast scanning */
332         if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
333             postfatal("cscope: cannot open file %s\n", reffile);
334             /* NOTREACHED */
335         }
336         /* get the first file name in the old cross-reference */
337         blocknumber = -1;
338         read_block();   /* read the first cross-ref block */
339         scanpast('\t'); /* skip the header */
340         oldfile = getoldfile();
341     } else {    /* force cross-referencing of all the source files */
342     force:      
343         reftime = 0;
344         oldfile = NULL;
345     }
346     /* open the new cross-reference file */
347     if ((newrefs = myfopen(newreffile, "wb")) == NULL) {
348         postfatal("cscope: cannot open file %s\n", reffile);
349         /* NOTREACHED */
350     }
351     if (invertedindex == YES && (postings = myfopen(temp1, "wb")) == NULL) {
352         cannotwrite(temp1);
353         cannotindex();
354     }
355     putheader(newdir);
356     fileversion = FILEVERSION;
357     if (buildonly == YES && verbosemode != YES && !isatty(0)) {
358         interactive = NO;
359     } else {
360         searchcount = 0;
361     }
362     /* output the leading tab expected by crossref() */
363     dbputc('\t');
364
365     /* make passes through the source file list until the last level of
366        included files is processed */
367     firstfile = 0;
368     lastfile = nsrcfiles;
369     if (invertedindex == YES) {
370         srcoffset = mymalloc((nsrcfiles + 1) * sizeof(long));
371     }
372     for (;;) {
373         progress("Building symbol database", (long)built,
374                  (long)lastfile);
375         if (linemode == NO)
376             refresh();
377
378         /* get the next source file name */
379         for (fileindex = firstfile; fileindex < lastfile; ++fileindex) {
380                         
381             /* display the progress about every three seconds */
382             if (interactive == YES && fileindex % 10 == 0) {
383                 progress("Building symbol database", fileindex, lastfile);
384             }
385             /* if the old file has been deleted get the next one */
386             file = srcfiles[fileindex];
387             while (oldfile != NULL && strcmp(file, oldfile) > 0) {
388                 oldfile = getoldfile();
389             }
390             /* if there isn't an old database or this is a new file */
391             if (oldfile == NULL || strcmp(file, oldfile) < 0) {
392                 crossref(file);
393                 ++built;
394             } else if (lstat(file, &statstruct) == 0
395                        && statstruct.st_mtime > reftime) {
396                 /* if this file was modified */
397                 crossref(file);
398                 ++built;
399                                 
400                 /* skip its old crossref so modifying the last source
401                  * file does not cause all included files to be built.
402                  * Unfortunately a new file that is alphabetically
403                  * last will cause all included files to be build, but
404                  * this is less likely */
405                 oldfile = getoldfile();
406             } else {    
407                 /* copy its cross-reference */
408                 putfilename(file);
409                 if (invertedindex == YES) {
410                     copyinverted();
411                 } else {
412                     copydata();
413                 }
414                 ++copied;
415                 oldfile = getoldfile();
416             }
417         }
418         /* see if any included files were found */
419         if (lastfile == nsrcfiles) {
420             break;
421         }
422         firstfile = lastfile;
423         lastfile = nsrcfiles;
424         if (invertedindex == YES) {
425             srcoffset = myrealloc(srcoffset,
426                                   (nsrcfiles + 1) * sizeof(long));
427         }
428         /* sort the included file names */
429         qsort(&srcfiles[firstfile], (lastfile - firstfile), 
430               sizeof(char *), compare);
431     }
432     /* add a null file name to the trailing tab */
433     putfilename("");
434     dbputc('\n');
435         
436     /* get the file trailer offset */
437     traileroffset = dboffset;
438         
439     /* output the source and include directory and file lists */
440     putlist(srcdirs, nsrcdirs);
441     putlist(incdirs, nincdirs);
442     putlist(srcfiles, nsrcfiles);
443     if (fflush(newrefs) == EOF) {
444         /* rewind doesn't check for write failure */
445         cannotwrite(newreffile);
446         /* NOTREACHED */
447     }
448
449     /* create the inverted index if requested */
450     if (invertedindex == YES) {
451         char    sortcommand[PATHLEN + 1];
452
453         if (fflush(postings) == EOF) {
454             cannotwrite(temp1);
455             /* NOTREACHED */
456         }
457         fstat(fileno(postings), &statstruct);
458         fclose(postings);
459         snprintf(sortcommand, sizeof(sortcommand), "env LC_ALL=C sort -T %s %s", tmpdir, temp1);
460         if ((postings = mypopen(sortcommand, "r")) == NULL) {
461             fprintf(stderr, "cscope: cannot open pipe to sort command\n");
462             cannotindex();
463         } else {
464             if ((totalterms = invmake(newinvname, newinvpost, postings)) > 0) {
465                 movefile(newinvname, invname);
466                 movefile(newinvpost, invpost);
467             } else {
468                 cannotindex();
469             }
470             mypclose(postings);
471         }
472         unlink(temp1);
473         free(srcoffset);
474     }
475     /* rewrite the header with the trailer offset and final option list */
476     rewind(newrefs);
477     putheader(newdir);
478     fclose(newrefs);
479         
480     /* close the old database file */
481     if (symrefs >= 0) {
482         close(symrefs);
483     }
484     if (oldrefs != NULL) {
485         fclose(oldrefs);
486     }
487     /* replace it with the new database file */
488     movefile(newreffile, reffile);
489 }
490         
491
492 /* string comparison function for qsort */
493 static int
494 compare(const void *arg_s1, const void *arg_s2)
495 {
496     const char **s1 = (const char **) arg_s1;
497     const char **s2 = (const char **) arg_s2;
498                         
499     return(strcmp(*s1, *s2));
500 }
501
502
503 /* seek to the trailer, in a given file */
504 void 
505 seek_to_trailer(FILE *f) 
506 {
507     if (fscanf(f, "%ld", &traileroffset) != 1) {
508         postfatal("cscope: cannot read trailer offset from file %s\n", reffile);
509         /* NOTREACHED */
510     }
511     if (fseek(f, traileroffset, SEEK_SET) == -1) {
512         postfatal("cscope: cannot seek to trailer in file %s\n", reffile);
513         /* NOTREACHED */
514     }
515 }
516
517
518 /* get the next file name in the old cross-reference */
519 static char *
520 getoldfile(void)
521 {
522     static char file[PATHLEN + 1];      /* file name in old crossref */
523
524     if (blockp != NULL) {
525         do {
526             if (*blockp == NEWFILE) {
527                 skiprefchar();
528                 fetch_string_from_dbase(file, sizeof(file));
529                 if (file[0] != '\0') {  /* if not end-of-crossref */
530                     return(file);
531                 }
532                 return(NULL);
533             }
534         } while (scanpast('\t') != NULL);
535     }
536     return(NULL);
537 }
538
539
540 /* Free all storage allocated for filenames: */
541 void free_newbuildfiles(void)
542 {
543     free(newinvname);
544     free(newinvpost);
545     free(newreffile);
546 }       
547
548
549 /* output the cscope version, current directory, database format options, and
550    the database trailer offset */
551 static void
552 putheader(char *dir)
553 {
554     dboffset = fprintf(newrefs, "cscope %d %s", FILEVERSION, dir);
555     if (compress == NO) {
556         dboffset += fprintf(newrefs, " -c");
557     }
558     if (invertedindex == YES) {
559         dboffset += fprintf(newrefs, " -q %.10ld", totalterms);
560     } else {    
561         /* leave space so if the header is overwritten without -q
562          * because writing the inverted index failed, the header
563          * is the same length */
564         dboffset += fprintf(newrefs, "              ");
565     }
566     if (trun_syms == YES) {
567         dboffset += fprintf(newrefs, " -T");
568     }
569
570     dboffset += fprintf(newrefs, " %.10ld\n", traileroffset);
571 #ifdef PRINTF_RETVAL_BROKEN
572     dboffset = ftell(newrefs); 
573 #endif
574 }
575
576
577 /* put the name list into the cross-reference file */
578 static void
579 putlist(char **names, int count)
580 {
581     int i, size = 0;
582         
583     fprintf(newrefs, "%d\n", count);
584     if (names == srcfiles) {
585
586         /* calculate the string space needed */
587         for (i = 0; i < count; ++i) {
588             size += strlen(names[i]) + 1;
589         }
590         fprintf(newrefs, "%d\n", size);
591     }
592     for (i = 0; i < count; ++i) {
593         if (fputs(names[i], newrefs) == EOF ||
594             putc('\n', newrefs) == EOF) {
595             cannotwrite(newreffile);
596             /* NOTREACHED */
597         }
598     }
599 }
600
601
602 /* copy this file's symbol data */
603 static void
604 copydata(void)
605 {
606     char symbol[PATLEN + 1];
607     char *cp;
608
609     setmark('\t');
610     cp = blockp;
611     for (;;) {
612         /* copy up to the next \t */
613         do {    /* innermost loop optimized to only one test */
614             while (*cp != '\t') {
615                 dbputc(*cp++);
616             }
617         } while (*++cp == '\0' && (cp = read_block()) != NULL);
618         dbputc('\t');   /* copy the tab */
619                 
620         /* get the next character */
621         /* HBB 2010-08-21: potential problem if above loop was left
622          * with cp==NULL */
623         if (cp && (*(cp + 1) == '\0')) {
624             cp = read_block();
625         }
626         /* exit if at the end of this file's data */
627         if (cp == NULL || *cp == NEWFILE) {
628             break;
629         }
630         /* look for an #included file */
631         if (*cp == INCLUDE) {
632             blockp = cp;
633             fetch_include_from_dbase(symbol, sizeof(symbol));
634             writestring(symbol);
635             setmark('\t');
636             cp = blockp;
637         }
638     }
639     blockp = cp;
640 }
641
642 /* copy this file's symbol data and output the inverted index postings */
643
644 static void
645 copyinverted(void)
646 {
647     char    *cp;
648     char    c;
649     int     type;   /* reference type (mark character) */
650     char    symbol[PATLEN + 1];
651
652     /* note: this code was expanded in-line for speed */
653     /* while (scanpast('\n') != NULL) { */
654     /* other macros were replaced by code using cp instead of blockp */
655     cp = blockp;
656     for (;;) {
657         setmark('\n');
658         do {    /* innermost loop optimized to only one test */
659             while (*cp != '\n') {
660                 dbputc(*cp++);
661             }
662         } while (*++cp == '\0' && (cp = read_block()) != NULL);
663         dbputc('\n');   /* copy the newline */
664                 
665         /* get the next character */
666         /* HBB 2010-08-21: potential problem if above loop was left
667          * with cp==NULL */
668         if (cp && (*(cp + 1) == '\0')) {
669             cp = read_block();
670         }
671         /* exit if at the end of this file's data */
672         if (cp == NULL) {
673             break;
674         }
675         switch (*cp) {
676         case '\n':
677             lineoffset = dboffset + 1;
678             continue;
679         case '\t':
680             dbputc('\t');
681             blockp = cp;
682             type = getrefchar();
683             switch (type) {
684             case NEWFILE:               /* file name */
685                 return;
686             case INCLUDE:               /* #included file */
687                 fetch_include_from_dbase(symbol, sizeof(symbol));
688                 goto output;
689             }
690             dbputc(type);
691             skiprefchar();
692             fetch_string_from_dbase(symbol, sizeof(symbol));
693             goto output;
694         }
695         c = *cp;
696         if (c & 0200) { /* digraph char? */
697             c = dichar1[(c & 0177) / 8];
698         }
699         /* if this is a symbol */
700         if (isalpha((unsigned char)c) || c == '_') {
701             blockp = cp;
702             fetch_string_from_dbase(symbol, sizeof(symbol));
703             type = ' ';
704         output:
705             putposting(symbol, type);
706             writestring(symbol);
707             if (blockp == NULL) {
708                 return;
709             }
710             cp = blockp;
711         }
712     }
713     blockp = cp;
714 }
715
716
717 /* replace the old file with the new file */
718 static void
719 movefile(char *new, char *old)
720 {
721     unlink(old);
722     if (rename(new, old) == -1) {
723         myperror("cscope");
724         postfatal("cscope: cannot rename file %s to file %s\n",
725                   new, old);
726         /* NOTREACHED */
727     }
728 }
729
730
731 /* process the #included file in the old database */
732 static void
733 fetch_include_from_dbase(char *s, size_t length)
734 {
735     dbputc(INCLUDE);
736     skiprefchar();
737     fetch_string_from_dbase(s, length);
738     incfile(s + 1, s);
739 }
740