/* * Htags Hyper-text Reference Kit. * * This file is placed into the public domain by the author, * 2005, 2006 Tama Communications Corporation. * * Two library function, qsort(3) and bsearch(3), are required. */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/param.h> #include <sys/stat.h> #if defined(_WIN32) || defined(__DJGPP__) # define OPENMODE O_BINARY #else # define OPENMODE 0 #endif int htags_load_filemap(const char *); void htags_unload_filemap(void); int htags_path2url(const char *, int, char *, int); /* * Usage by example. * * Getting the URL of "i386/i386/identcpu.c" at 120, in variable url. * * char url[MAXPATHLEN+1]; * -- Load filemap generated by htags(1). * ret = htags_load_filemap("HTML/FILEMAP"); * if (ret != 0) * ERROR; * -- Get URL of the specified file name and line number. * ret = htags_path2url("i386/i386/identcpu.c", 120, url, sizeof(url)); * if (ret != 0) * ERROR; * url == 'S/1238.html#L120' * -- release resource * htags_unload_filemap(); * * Since the URL is a relative path from the HTML directory, you should * modify it so that your application program works well. */ /* * in memory FILEMAP * * Alphabetical sorted table. */ struct map { const char *name; const char *path; }; /* * Global variables. * * These variables are set by htags_load_filemap(), and referred by * htags_path2url() and htags_unload_filemap(). */ static char *global_contents; /* filemap contents */static struct map *global_map; /* file -> url mapping table */ static int global_maplines; /* the lines of global_map */ /*----------------------------------------------------------------------*/ /* Local functions */ /*----------------------------------------------------------------------*/ /* * load contents of FILEMAP into the memory. * * i) file path of FILEMAP * o) area file contents (malloced) * o) size size of area * r) 0 succesful * -1: out of memory. * -2: FILEMAP not found. * -3: cannot fstat FILEMAP. * -4: cannot read FILEMAP. */ static int load_filemap_contents(const char *file, char **area, int *size) { struct stat st; char *p = NULL; int status = 0; int fd = -1; /* Load FILEMAP contents */ status = -2; if ((fd = open(file, OPENMODE)) < 0) goto err; status = -3; if (fstat(fd, &st) < 0) goto err; status = -1; if ((p = (char *)malloc(st.st_size)) == NULL) goto err; status = -4; if (read(fd, p, st.st_size) != st.st_size) goto err; close(fd); *area = p; *size = st.st_size; return 0; err: if (fd != -1) close(fd); if (p != NULL) free(p); return status; } /* * comparison function for bsearch(). */ static int cmp(const void *s1, const void *s2) { return strcmp(((struct map *)s1)->name, ((struct map *)s2)->name); } /* * creates index for in core FILEMAP. * * i) area filemap contents * i) size size of area * o) map map structure * o) lines map lines * r) 0: succesful * -1: out of memory. * -5: illegal format. */ static int create_filemap_index(char *area, int size, struct map **map, int *lines) { char *p, *endp = area + size; struct map *m; int n = 0; int status; int i; /* Count line number */ for (p = area; p < endp; p++) if (*p == '\n') n++; status = -1; if ((m = (struct map *)malloc(sizeof(struct map) * n)) == NULL) return -1; /* * FILEMAP format: * <NAME>\t<PATH>\n */ p = area; for (i = 0; i < n; i++) { m[i].name = p; while (*p && *p != '\t') p++; if (*p == '\0') goto ferr; *p++ = '\0'; m[i].path = p; while (*p && *p != '\r' && *p != '\n') p++; if (*p == '\0') goto ferr; if (*p == '\r') *p++ = '\0'; if (*p == '\n') *p++ = '\0'; } qsort(m, n, sizeof(struct map), cmp); *map = m; *lines = n; return 0; ferr: free(m); return -5; } /* * unloads FILEMAP. */ static void unload_filemap(void) { (void)free(global_map); global_map = NULL; (void)free(global_contents); global_contents = NULL; } /*----------------------------------------------------------------------*/ /* Global functions */ /*----------------------------------------------------------------------*/ /* * loads FILEMAP. * * i) filemap FILEMAP file * go) global_contents filemap contents (for free) * go) global_map filemap index. * go) global_maplines lines of filemap index. * r) 0: succesful * -1: out of memory. * -2: FILEMAP not found. * -3: cannot fstat FILEMAP. * -4: cannot read FILEMAP. * -5: format error. */ int htags_load_filemap(const char *filemap) { int status = 0; char *area; int size, lines; struct map *map; status = load_filemap_contents(filemap, &area, &size); if (status < 0) return status; status = create_filemap_index(area, size, &map, &lines); if (status < 0) { (void)free(area); return status; } global_contents = area; global_map = map; global_maplines = lines; return 0; } /* * unloads FILEMAP. */ void htags_unload_filemap(void) { unload_filemap(); } /* * convert file path name into the URL in the hypertext. * * i) path path name in the filesystem. * i) line 0: ignored, !=0: line number in path. * o) url result url * i) size size of url * gi) global_contents filemap contents (for free) * gi) global_map filemap index. * gi) global_maplines lines of filemap index. * r) 0: succesful * 1: path not found * -1: filemap not loaded yet * * URL: S/<file id>.html#<line number> */ int htags_path2url(const char *path, int line, char *url, int size) { struct map tmp; struct map *result; if (global_contents == NULL || global_map == NULL) return -1; tmp.name = path; result = (struct map *)bsearch(&tmp, global_map, global_maplines, sizeof(struct map), cmp); if (result == NULL) return 1; if (line > 0) snprintf(url, size, "%s#L%d", result->path, line); else snprintf(url, size, "%s", result->path); return 0; } #ifdef TEST /* * Usage: htags_path2url filemap path [line] * * $ htags_path2url HTML/FILEMAP i386/i386/identcpu.c 120 * i386/i386/identcpu.c, line 120 => S/1238.html#L120 * $ _ */ int main(int argc, char **argv) { char url[MAXPATHLEN]; char *path, *html; int line = 0; int ret; if (argc < 3) { fprintf(stderr, "Usage: htags_path2url filemap path [line]\n"); exit(1); } html = argv[1]; path = argv[2]; if (argc > 3) line = atoi(argv[3]); ret = htags_load_filemap(html); if (ret != 0) { fprintf(stderr, "htags_load_filemap failed.("); switch (ret) { case -1: fprintf(stderr, "out of memory"); break; case -2: fprintf(stderr, "FILEMAP not found."); break; case -3: fprintf(stderr, "cannot fstat FILEMAP."); break; case -4: fprintf(stderr, "cannot read FILEMAP."); break; case -5: fprintf(stderr, "format error."); break; default: fprintf(stderr, "unknown error."); break; } fprintf(stderr, ")\n"); exit(1); } ret = htags_path2url(path, line, url, sizeof(url)); if (ret != 0) { fprintf(stderr, "htags_path2url failed.("); switch (ret) { case 1: fprintf(stderr, "path not found."); break; case -1: fprintf(stderr, "out of memory."); break; } fprintf(stderr, ")\n"); exit(1); } fprintf(stdout, "%s, line %d => %s\n", path, line, url); htags_unload_filemap(); return 0; } #endif