/*************************************************************************** * Pinfo is a ncurses based lynx style info documentation browser * * Copyright (C) 1999 Przemek Borys * Copyright (C) 2005 Bas Zoetekouw * Copyright (C) 2005 Nathanael Nerode * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA ***************************************************************************/ #include "common_includes.h" typedef struct { char *suffix; char *command; } Suffixes; /****************************************************************************** * This piece of declarations says what to do with info files stored with * * different formats/compression methods, before putting them into a temporary * * file. I.e. you don't do anything to plain `.info' suffix; for a `.info.gz' * * you dump the file through `gunzip -d -c', etc. * ******************************************************************************/ #define SuffixesNumber 4 Suffixes suffixes[SuffixesNumber] = { {"", "cat"}, {".gz", "gzip -d -q -c"}, {".Z", "gzip -d -q -c"}, {".bz2", "bzip2 -d -c"} }; /*****************************************************************************/ char **infopaths = 0; int infopathcount = 0; int qsort_cmp(const void *base, const void *compared) { char *cbase =((TagTable *) base)->nodename; char *ccompared =((TagTable *) compared)->nodename; return compare_tag_table_string(cbase, ccompared); } int matchfile(char **buf, char *name) { #define Buf (*buf) DIR *dir; char *bname = basename(name); struct dirent *dp; int matched = 0; /* remove a possible ".info" from the end of the file name * we're looking for */ strip_info_suffix(bname); /* fix the name of the dir */ if (Buf[strlen(Buf)-1]!='/') { strcat(Buf,"/"); } strncat(Buf,name,bname-name); /* open the directory */ dir = opendir(Buf); if (dir == NULL) { return 0; } /* iterate over all files in the directory */ while ((dp = readdir(dir)) != NULL) { /* use strcat rather than strdup, because xmalloc handles all * malloc errors */ char *filename = xmalloc(strlen(dp->d_name)+1); char *pagename = xmalloc(strlen(dp->d_name)+1); strcat(filename, dp->d_name); strcat(pagename, dp->d_name); /* strip suffixes (so "gcc.info.gz" -> "gcc") */ strip_compression_suffix(pagename); strip_info_suffix(pagename); /* strip compression suffix from returned filename * decompresison and type matching will happen later * (sigh) */ strip_compression_suffix(filename); //fprintf(stdout,"Found filename `%s' (%s)\n", filename, pagename); /* compare this file with the file we're looking for */ if (strcmp(pagename,bname) == 0) { /* we found a match! */ matched++; /* put it in the buffer */ strncat(Buf, filename, 1023-strlen(Buf)); /* clean up, and exit the loop */ xfree(filename); xfree(pagename); break; } xfree(filename); xfree(pagename); } closedir(dir); if (matched) return 1; return 0; #undef Buf } FILE * dirpage_lookup(char **type, char ***message, unsigned long *lines, char *filename, char **first_node) { #define Type (*type) #define Message (*message) #define Lines (*lines) FILE *id = 0; int filenamelen = strlen(filename); int goodHit = 0; char name[256]; char file[256]; char *nameend, *filestart, *fileend, *dot; id = opendirfile(0); if (!id) return NULL; read_item(id, type, message, lines); /* search for node-links in every line */ for (unsigned long i = 1; i < Lines; i++) { if ( (Message[i][0] == '*') && (Message[i][1] == ' ') && ( nameend = strchr(Message[i], ':') ) && (*(nameend + 1) != ':') /* form: `* name:(file)node.' */ && (filestart = strchr(nameend, '(') ) && (fileend = strchr(filestart, ')') ) && (dot = strchr(fileend, '.') ) && (strncasecmp(filename, Message[i] + 2, filenamelen) == 0) ) { char *tmp; /* skip this hit if it is not a perfect match and * we have already found a previous partial match */ if ( ! ( (nameend - Message[i]) - 2 == filenamelen ) && goodHit ) { continue; } /* find the name of the node link */ tmp = name; strncpy(file, filestart + 1, fileend - filestart - 1); file[fileend - filestart - 1] = 0; strncpy(name, fileend + 1, dot - fileend - 1); name[dot - fileend - 1] = 0; while (isspace(*tmp)) tmp++; if (strlen(name)) { *first_node = xmalloc(strlen(tmp) + 1); strcpy((*first_node), tmp); } /* close the previously opened file */ if (id) { fclose(id); id = 0; } /* see if this info file exists */ id = openinfo(file, 0); if (id) { goodHit = 1; } } } /* if we haven't found anything, clean up and exit */ if (id && !goodHit) { fclose(id); id = 0; } /* return file we found */ return id; #undef Lines #undef Message #undef Type } void freeitem(char **type, char ***buf, unsigned long *lines) { #define Type (*type) #define Buf (*buf) #define Lines (*lines) if (Type != 0) { xfree(Type); Type = 0; } if (Buf != 0) { for (unsigned long i = 1; i <= Lines; i++) if (Buf[i] != 0) { xfree(Buf[i]); Buf[i] = 0; } xfree(Buf); Buf = 0; } #undef Type #undef Buf #undef Lines } void read_item(FILE * id, char **type, char ***buf, unsigned long *lines) { #define Type (*type) #define Buf (*buf) #define Lines (*lines) int i; freeitem(type, buf, lines); /* free previously allocated memory */ /* set number of lines to 0 */ Lines = 0; /* initial buffer allocation */ Buf = xmalloc(sizeof(char **)); /* seek precisely on the INFO_TAG (the seeknode function may be imprecise * in combination with some weird tag_tables). */ while (!feof(id) && fgetc(id) != INFO_TAG); /* then skip the trailing `\n' */ while (!feof(id) && fgetc(id) != '\n'); /* allocate and read the header line */ Type = xmalloc(1024); if (fgets(Type, 1024, id)==NULL) { /* nothing to do */ return; } Type = xrealloc(Type, strlen(Type) + 1); /* now iterate over the lines */ do { /* don't read after eof in info file */ if (feof(id)) break; /* realloc the previous line for it to fit exactly */ if (Lines) { Buf[Lines] = xrealloc(Buf[Lines], strlen(Buf[Lines]) + 1); } /* TODO: Weirdness going on here; looks like off-by-one error as Buf[0] is always "\0" */ /* increase the read lines number */ Lines++; /* allocate space for the new line */ Buf = xrealloc(Buf, sizeof(char **) *(Lines + 1)); Buf[Lines] = xmalloc(1024); Buf[Lines][0] = 0; /* if the line was not found in input file, fill the allocated space * with empty line. */ if (fgets(Buf[Lines], 1024, id) == NULL) strcpy(Buf[Lines], "\n"); else /* we can be sure that at least 1 char was read! */ { /* *sigh* indices contains \0's * which totally fucks up all strlen()s. * so replace it by a space */ i = 1023; /* find the end of the string */ while (Buf[Lines][i]=='\0' && i>=0) i--; /* and replace all \0's in the rest of the string by spaces */ while (i>=0) { if (Buf[Lines][i]=='\0' || Buf[Lines][i]=='\b') Buf[Lines][i]=' '; i--; } } } while (Buf[Lines][0] != INFO_TAG); /* repeat until new node mark is found */ /* added for simplifing two-line ismenu and isnote functs */ if (Lines) { strcpy(Buf[Lines], "\n"); Buf[Lines] = xrealloc(Buf[Lines], strlen(Buf[Lines]) + 1); } fseek(id, -2, SEEK_CUR); #undef Type #undef Buf #undef Lines } void load_indirect(char **message, unsigned long lines) { char *wsk; int cut = 0; /* number of invalid entries */ indirect = xmalloc((lines + 1) * sizeof(Indirect)); for (unsigned long i = 1; i < lines; i++) { char *check; wsk = message[i]; check = wsk + strlen(wsk); while (*(++wsk) != ':') /* check if this line keeps a real entry */ { if (wsk == check) /* * make sure wsk won't go out of range * in case the wsk would be corrupted. */ break; } if (*wsk) /* if the entry holds some data... */ { (*wsk) = 0; strncpy(indirect[i - cut].filename, message[i], 200); (*wsk) = ':'; indirect[i - cut].offset = atoi(wsk + 2); } else cut++; /* if the entry was invalid, make inirect count shorter */ } IndirectEntries = lines - 1 - cut; } void load_tag_table(char **message, unsigned long lines) { char *wsk, *wsk1; int is_indirect = 0; register unsigned int j; register char *res; int cut = 0; /* holds the number of corrupt lines */ /* * if in the first line there is a(indirect) string, skip that line * by adding the value of is_indirect=1 to all message[line] references. */ if (strcasecmp("(Indirect)", message[1]) == 0) is_indirect = 1; tag_table = xmalloc((lines + 1) * sizeof(TagTable)); for (unsigned long i = 1; i < lines - is_indirect; i++) { char *check; wsk = message[i + is_indirect]; check = wsk + strlen(wsk); while (!isspace(*(++wsk))) { if (wsk >= check) { wsk--; break; } } wsk++; wsk1 = wsk; check = wsk1 + strlen(wsk1); while (*(++wsk1) != INDIRECT_TAG) { if (wsk1 >= check) break; } if (wsk1 < check) { (*wsk1) = 0; /* * original: sprintf(tag_table[i-cut].nodename,"%s",wsk); * below is a faster version. */ res = memcpy(tag_table[i - cut].nodename, wsk, j =(size_t)(wsk1 - wsk)); (*(res += j + 1)) = 0; (*wsk1) = INDIRECT_TAG; wsk1++; tag_table[i - cut].offset = atoi(wsk1); } else cut++; /* increment the number of corrupt entries */ } TagTableEntries = lines - 1 - is_indirect - cut; /* FIXME: info should ALWAYS start at the 'Top' node, not at the first mentioned node(vide ocaml.info) */ for (unsigned int i = 1; i <= TagTableEntries; i++) { if (strcasecmp(tag_table[i].nodename, "Top") == 0) { FirstNodeOffset = tag_table[i].offset; strcpy(FirstNodeName, tag_table[i].nodename); } } qsort(&tag_table[1], TagTableEntries, sizeof(TagTable), qsort_cmp); } /* TODO: seek_indirect() and seek_tag_table() are almost identical: remove duplicate code */ int seek_indirect(FILE * id) { int finito = 0; long seek_pos; int input; char *type = xmalloc(1024); fseek(id, 0, SEEK_SET); while (!finito) /* * scan through the file, searching for "indirect:" * string in the type(header) line of node. */ { while ((input = fgetc(id)) != INFO_TAG) if (input == EOF) { if (type) { xfree(type); type = 0; } return 0; } seek_pos = ftell(id) - 2; fgetc(id); if (fgets(type, 1024, id)==0) { /* we're at the end of the file and haven't found any indirect refs. so bail out */ if (type) { xfree(type); type = 0; } return 0; } if (strncasecmp("Indirect:", type, strlen("Indirect:")) == 0) { finito = 1; } } xfree(type); type = 0; if (!curses_open) { printf(_("Searching for indirect done")); printf("\n"); } else { attrset(bottomline); mvhline(maxy - 1, 0, ' ', maxx); mvaddstr(maxy - 1, 0, _("Searching for indirect done")); attrset(normal); } fseek(id, seek_pos, SEEK_SET); return 1; } /* * second arg for dumping out verbose debug info or not :) */ int seek_tag_table(FILE * id,int quiet) { int finito = 0; long seek_pos; int input; char *type = xmalloc(1024); fseek(id, 0, SEEK_SET); /* * Scan through the file, searching for a string * "Tag Table:" in the type(header) line of node. */ while (!finito) { while ((input = fgetc(id)) != INFO_TAG) { if (input == EOF) { if (!quiet) { if (!curses_open) { printf(_("Warning: could not find tag table")); printf("\n"); } else { attrset(bottomline); mvhline(maxy - 1, 0, ' ', maxx); mvaddstr(maxy - 1, 0, _("Warning: could not find tag table")); attrset(normal); } } if (type) { xfree(type); type = 0; } return 2; } } seek_pos = ftell(id) - 2; while (fgetc(id) != '\n'); if (fgets(type, 1024, id)==NULL) { /* we're at the end of the file and haven't found a tag table. so bail out */ if (type) { xfree(type); type = 0; } return 2; } if (strncasecmp("Tag Table:", type, strlen("Tag Table:")) == 0) { finito = 1; } } xfree(type); type = 0; if (!curses_open) printf(_("Searching for tag table done\n")); else { attrset(bottomline); mvhline(maxy - 1, 0, ' ', maxx); mvaddstr(maxy - 1, 0, "Searching for tag table done"); attrset(normal); } fseek(id, seek_pos, SEEK_SET); return 1; } void buildcommand(char *dest, char *command, char *filename, const char *tmpfilename) { strcpy(dest, command); strcat(dest, " "); strcat(dest, filename); strcat(dest, "> "); strcat(dest, tmpfilename); } void builddircommand(char *dest, char *command, char *filename, const char *tmpfilename) { strcpy(dest, command); strcat(dest, " "); strcat(dest, filename); strcat(dest, ">> "); strcat(dest, tmpfilename); } FILE * opendirfile(int number) { FILE *id = NULL; char buf[1024]; /* holds local copy of filename */ char *bufend; /* points at the trailing 0 of initial name */ char command[1128]; /* holds command to evaluate for decompression of file */ int i, j; char *tmpfilename = NULL; size_t *fileendentries = xmalloc(infopathcount * sizeof(*fileendentries)); /* should really be off_t, but a signed type really doesn't make sense here */ int dir_found = 0; int dircount = 0; int lang_found; struct stat status; if (number == 0) /* initialize tmp filename for file 1 */ { /* close and delete old tmp file */ if (tmpfilename1) { unlink(tmpfilename1); /* erase old tmpfile */ free(tmpfilename1); } tmpfilename1 = make_tempfile(); tmpfilename = tmpfilename1; /* later we will refere only to tmp1 */ } for (i = 0; i < infopathcount; i++) /* go through all paths */ { lang_found = 0; strcpy(buf, infopaths[i]); /* build a filename */ strcat(buf, "/"); if (getenv("LANG") != NULL) strcat(buf, getenv("LANG")); strcat(buf, "/dir"); /* * remember the bufend to make it * possible later to glue compression suffixes. */ bufend = buf; bufend += strlen(buf); for (j = 0; j < SuffixesNumber; j++) /* go through all suffixes */ { strcat(buf, suffixes[j].suffix); if ((id = fopen(buf, "r")) != NULL) { fclose(id); builddircommand(command, suffixes[j].command, buf, tmpfilename); xsystem(command); lstat(tmpfilename, &status); fileendentries[dircount] = status.st_size; dircount++; dir_found = 1; lang_found = 1; } (*bufend) = 0; } /* same as above, but without $LANG support */ if (!lang_found) { strcpy(buf, infopaths[i]); /* build a filename */ strcat(buf, "/"); strcat(buf, "dir"); /* * remember the bufend to make it possible later to glue * compression suffixes. */ bufend = buf; bufend += strlen(buf); for (j = 0; j < SuffixesNumber; j++) /* go through all suffixes */ { strcat(buf, suffixes[j].suffix); if ((id = fopen(buf, "r")) != NULL) { fclose(id); builddircommand(command, suffixes[j].command, buf, tmpfilename); xsystem(command); lstat(tmpfilename, &status); fileendentries[dircount] = status.st_size; dircount++; dir_found = 1; } (*bufend) = 0; } } } if (dir_found) id = fopen(tmpfilename, "r"); /* * Filter the concatenated dir pages to exclude hidden parts of info * entries */ if (id) { char *tmp; size_t filelen, l; int aswitch = 0; int firstswitch = 0; dircount = 0; fseek(id, 0, SEEK_END); filelen = ftell(id); tmp = xmalloc(filelen); fseek(id, 0, SEEK_SET); if (fread(tmp, 1, filelen, id)!=filelen) { printf(_("Error while reading file '%s'"), tmp); closeprogram(); exit(1); } fclose(id); id = fopen(tmpfilename, "w"); for (l = 0; l < filelen; l++) { if (tmp[l] == INFO_TAG) { aswitch ^= 1; if (!firstswitch) fputc(tmp[l], id); firstswitch = 1; } else if ((aswitch) ||(!firstswitch)) fputc(tmp[l], id); if (l + 1 == fileendentries[dircount]) { if (aswitch != 0) aswitch = 0; dircount++; /* the last dircount should fit to the end of filelen */ } } fputc(INFO_TAG, id); fputc('\n', id); xfree(fileendentries); fclose(id); id = fopen(tmpfilename, "r"); xfree(tmp); return id; } return NULL; } /* * Note: openinfo is a function for reading info files, and putting * uncompressed content into a temporary filename. For a flexibility, there * are two temporary files supported, i.e. one for keeping opened info file, * and second for i.e. regexp search across info nodes, which are in other * info-subfiles. The temporary file 1 is refrenced by number=0, and file 2 by * number=1 Openinfo by default first tries the path stored in char * *filenameprefix and then in the rest of userdefined paths. */ FILE * openinfo(char *filename, int number) { FILE *id = NULL; #define BUF_LEN 1024 char *buf = xmalloc(BUF_LEN); /* holds local copy of filename */ char *bufend; /* points at the trailing 0 of initial name */ char command[1128]; /* holds command to evaluate for decompression of file */ int i, j; char *tmpfilename; if ((strncmp(filename, "dir", 3)==0) && !isalnum(filename[3])) { xfree(buf); return opendirfile(number); } if (number == 0) /* initialize tmp filename for file 1 */ { if (tmpfilename1) { unlink(tmpfilename1); /* erase old tmpfile */ free(tmpfilename1); } tmpfilename1 = make_tempfile(); tmpfilename = tmpfilename1; /* later we will refere only to tmp1 */ } else /* initialize tmp filename for file 2 */ { if (tmpfilename2) { unlink(tmpfilename2); /* erase old tmpfile */ free(tmpfilename2); } tmpfilename2 = make_tempfile(); tmpfilename = tmpfilename2; /* later we will refere only to tmp2 */ } for (i = -2; i < infopathcount; i++) /* go through all paths */ { if (i < 0) { /* * no filenameprefix, we don't navigate around any specific * infopage set, so simply scan all directories for a hit */ if (!filenameprefix) continue; /* build a filename: First (i == -2) try filenameprefix/filename, * then try with a .info appended */ if (i == -2) snprintf(buf, BUF_LEN, "%s/%s", filenameprefix, basename(filename)); else snprintf(buf, BUF_LEN, "%s/%s.info", filenameprefix, basename(filename)); } else { /* build a filename */ strcpy(buf, infopaths[i]); /* no match found in this directory */ if (! matchfile(&buf, filename)) continue; } bufend = buf; /* remember the bufend to make it possible later to glue compression * suffixes. */ bufend += strlen(buf); for (j = 0; j < SuffixesNumber; j++) /* go through all suffixes */ { strcat(buf, suffixes[j].suffix); if ((id = fopen(buf, "r")) != NULL) { fclose(id); clearfilenameprefix(); filenameprefix = strdup(buf); { /* small scope for removal of filename */ int prefixi, prefixlen = strlen(filenameprefix); for (prefixi = prefixlen; prefixi > 0; prefixi--) if (filenameprefix[prefixi] == '/') { filenameprefix[prefixi] = 0; break; } } buildcommand(command, suffixes[j].command, buf, tmpfilename); xsystem(command); id = fopen(tmpfilename, "r"); if (id) { xfree(buf); return id; } } (*bufend) = 0; } /* if we have a nonzero filename prefix, that is we view a set of * infopages, we don't want to search for a page in all * directories, but only in the prefix directory. Therefore break * here. */ if ((i == -1) &&(filenameprefix)) break; } xfree(buf); return 0; #undef BUF_LEN } void addrawpath(char *filename) { int len = strlen(filename); int i, pos; char tmp = '\0'; for (i = len; i >= 0; i--) { if (filename[i] == '/') { tmp = filename[i+1]; filename[i+1] = 0; pos = i+1; break; } } if (i < 0) pos = -1; infopaths = xrealloc(infopaths,(infopathcount + 3) *(sizeof(char *))); for (i = infopathcount; i > 0; i--) /* move entries to the right */ infopaths[i] = infopaths[i - 1]; if (pos > 0) infopaths[0]=strdup(filename); /* add new(raw) entry */ else infopaths[0]=strdup("./"); infopathcount++; if (pos > 0) /* recreate original filename */ filename[pos] = tmp; } int isininfopath(char *name) { int i; for (i = 0; i < infopathcount; i++) { if (strcmp(name, infopaths[i]) == 0) return 1; /* path already exists */ } return 0; /* path not found in previous links */ } /* returns the number of chars ch in string str */ unsigned int charcount(const char *str, const char ch) { int num = 0; const char *c; c = str; while (*c != '\0') { if (*c++ == ch) num++; } return num; } /* * find the paths where info files are to be found, * and put them in the global var infopaths[] */ void initpaths() { char emptystr[1] = ""; char **paths = NULL; char *infopath = NULL, *langpath = NULL; char *c, *dir, *env, *next; char *rawlang = NULL, *lang = NULL, *langshort = NULL; int ret; unsigned int i, j, maxpaths, numpaths = 0, infolen, langlen; size_t len; struct stat sbuf; ino_t *inodes; /* first concat the paths */ env = getenv("INFOPATH"); if (env == NULL) { env = emptystr; } infolen = strlen(env) + strlen(configuredinfopath) + 3; infopath = (char *) xmalloc( infolen ); strcat(infopath, env); strcat(infopath, ":"); strcat(infopath, configuredinfopath); /* end with a :, otherwise the strchr below will fail for the last entry */ strcat(infopath, ":"); /* alloc the paths[] array */ maxpaths = 3 * (charcount( infopath, ':' ) + 1); // *3 for $LANG paths = (char **) xmalloc( maxpaths * sizeof(char *) ); /* split at ':' and put the path components into paths[] */ dir = infopath; /* if this actually is a non-empty string, add it to paths[] */ while ( (next = strchr(dir, ':')) != NULL ) { *next = '\0'; /* terminate the string */ /* if the dir actually is a non-empty string, add it to paths[] */ if ( dir && strlen(dir)>0 ) { paths[numpaths++] = dir; } /* and advance the pointer to the next entry */ dir = next+1; } /* get the current $LANG, if any (to use for localized info pages) */ rawlang = getenv("LANG"); if (rawlang) { lang = strdup(rawlang); /* fix the lang string */ for (i=0; lang[i]!='\0'; i++) { /* cut off the charset */ if (lang[i]=='.') { lang[i]='\0'; } /* if lang is sublocalized (nl_BE or so), also use short version */ if (lang[i]=='_' && langshort==NULL) { langshort = strdup(lang); langshort[i] = '\0'; } } } /* if we have a LANG defined, add paths with this lang to the paths[] */ if (lang && strlen(lang)>0 ) { /* crude upper limit */ langlen = infolen + (strlen(lang)+2) * numpaths + 1; if (langshort!=NULL) langlen *= 2; langpath = (char *) xmalloc( langlen * sizeof(char) ); c = langpath; for (i=0; i %s\n", paths[i]); #endif /* ok, now we have all the (possibly) revelevant paths in paths[] */ /* now loop over them, see if they are valid and if they are duplicates*/ /* TODO: cleanup all malloc calls (get rid of cast, use sizeof(varname) instead of sizeof(type) */ inodes = (ino_t *) xmalloc( maxpaths * sizeof(ino_t) ); numpaths = 0; len = 0; for (i=0; i< maxpaths; i++) { /* TODO: check where these NULL paths come from */ if (paths[i]==NULL) continue; /* stat() the dir */ ret = stat( paths[i], &sbuf); /* and see if it could be opened */ if (ret < 0) { #ifdef ___DEBUG___ fprintf(stderr, "error while opening `%s': %s\n", paths[i], strerror(errno) ); #endif paths[i] = NULL; inodes[i] = 0; } else { inodes[i] = sbuf.st_ino; } /* now check if this path is a duplicate */ for (j=0; j %s\n", infopaths[i]); #endif } void create_indirect_tag_table() { FILE *id = 0; int initial; for (unsigned i = 1; i <= IndirectEntries; i++) { id = openinfo(indirect[i].filename, 1); initial = TagTableEntries + 1; if (!id) { /* display error message to make the user aware of * the broken info page */ char msg[1024]; snprintf(msg, 1024, "%s '%s' (%s)", _("Can't open file"), indirect[i].filename, _("press a key to continue") ); attrset(bottomline); mvhline(maxy - 1, 0, ' ', maxx); mvaddstr(maxy - 1, 0, msg); move(0, 0); attrset(normal); getch(); continue; } create_tag_table(id); FirstNodeOffset = tag_table[1].offset; strcpy(FirstNodeName, tag_table[1].nodename); fclose(id); for (unsigned j = initial; j <= TagTableEntries; j++) { tag_table[j].offset +=(indirect[i].offset - FirstNodeOffset); } } FirstNodeOffset = tag_table[1].offset; strcpy(FirstNodeName, tag_table[1].nodename); qsort(&tag_table[1], TagTableEntries, sizeof(TagTable), qsort_cmp); } void create_tag_table(FILE * id) { char *buf = xmalloc(1024); long oldpos; fseek(id, 0, SEEK_SET); if (!tag_table) tag_table = xmalloc((TagTableEntries + 2) * sizeof(TagTable)); else tag_table = xrealloc(tag_table,(TagTableEntries + 2) * sizeof(TagTable)); while (!feof(id)) { if (fgetc(id) == INFO_TAG) /* We've found a node entry! */ { while (fgetc(id) != '\n'); /* skip '\n' */ TagTableEntries++; /* increase the nuber of tag table entries */ oldpos = ftell(id); /* remember this file position! */ /* * it is a an eof-fake-node (in some info files it happens, that * the eof'ish end of node is additionaly signalised by an INFO_TAG * We give to such node an unlike to meet nodename. */ if (fgets(buf, 1024, id) == NULL) { tag_table = xrealloc(tag_table, sizeof(TagTable) *(TagTableEntries + 1)); strcpy(tag_table[TagTableEntries].nodename, "12#!@#4"); tag_table[TagTableEntries].offset = 0; } else { int colons = 0, i, j; int buflen = strlen(buf); for (i = 0; i < buflen; i++) { if (buf[i] == ':') colons++; if (colons == 2) /* * the string after the second colon * holds the name of current node. * The name may then end with `.', * or with a newline, which is scanned * bellow. */ { for (j = i + 2; j < buflen; j++) { if ((buf[j] == ',') ||(buf[j] == '\n')) { tag_table = xrealloc(tag_table, sizeof(TagTable) *(TagTableEntries + 1)); buf[j] = 0; buflen = j; strcpy(tag_table[TagTableEntries].nodename, buf + i + 2); tag_table[TagTableEntries].offset = oldpos - 2; break; } } break; } } /* end: for loop, looking for second colon */ } /* end: not a fake node */ } /* end: we've found a node entry(INFO_TAG) */ } /* end: global while loop, looping until eof */ xfree(buf); buf = 0; if (!indirect) { FirstNodeOffset = tag_table[1].offset; strcpy(FirstNodeName, tag_table[1].nodename); qsort(&tag_table[1], TagTableEntries, sizeof(TagTable), qsort_cmp); } } int seeknode(int tag_table_pos, FILE ** Id) { int i; FILE * newid; #define id (*Id) /* * Indirect nodes are seeked using a formula: * file-offset = tagtable_offset - indirect_offset + * + tagtable[1]_offset */ if (indirect) { for (i = IndirectEntries; i >= 1; i--) { if (indirect[i].offset <= tag_table[tag_table_pos].offset) { long off = tag_table[tag_table_pos].offset - indirect[i].offset + FirstNodeOffset - 4; newid = openinfo(indirect[i].filename, 0); if (newid == NULL) { return -1; closeprogram(); printf(_("Error: could not open info file part")); printf("\n"); exit(1); } fclose(id); id = newid; if (off > 0) fseek(id, off, SEEK_SET); else fseek(id, off, SEEK_SET); break; } } } else { long off = tag_table[tag_table_pos].offset - 4; if (off > 0) fseek(id, off, SEEK_SET); else fseek(id, off, SEEK_SET); } #undef id return 0; } /* removes trailing .gz, .bz2, etc. */ void strip_compression_suffix(char *file) { const size_t len = strlen(file); assert(len<1024); /* just some random limit */ char *found = 0; for (unsigned j = 0; j < SuffixesNumber; j++) { if ( (found = strstr(file, suffixes[j].suffix)) != NULL ) { if ( file + len == found + strlen(suffixes[j].suffix) ) { *found = '\0'; break; } } } } /* strip .info from and of string */ void strip_info_suffix(char *file) { const size_t len = strlen(file); assert(len<1024); /* just some random limit */ char *found = 0; const char suffix[6] = ".info"; if ( (found = strstr(file, suffix)) != NULL ) { if ( file + len == found + strlen(suffix) ) { *found = '\0'; } } }