/* * _read_symdir(), _write_symdir(), _free_symdir(): routines for * reading/writing the special directories used for symbolic links. * If symbolic links were not set active in UNIXMODE, then theses * routines are no-ops. * * Written by Eric R. Smith and placed in the public domain. Use * at your own risk. */ #include #include #include #include #include #include "symdir.h" #ifndef NAME_MAX # include #endif #ifndef _LIB_NAME_MAX # define _LIB_NAME_MAX NAME_MAX #endif /* * utility routine to provide buffered I/O for _read_symdir() */ #define ResetFgetc() Fgetc(-1) static int Fgetc(fd) int fd; { static unsigned char buf[BUFSIZ], *pos; static int siz = 0; if (fd < 0) { siz = 0; pos = buf; return 0; } if (siz == 0) { siz = Fread(fd, BUFSIZ, buf); pos = buf; if (siz <= 0) { siz = 0; pos = buf; return -1; } } siz--; return *pos++; } /* * Routines for keeping a cache of recently used symbolic directories. * The last 8 directories accessed are kept cached; this really helps * the directory searches in _unx2dos, but does eat some memory. * * entries are added to the cache by _free_symdir, and removed if they * fall off the end, or if they're retrieved by a subsequent _read_symdir. * * IMPLICIT ASSUMPTION: the same directory is never open more than once. */ #define CACHESIZE 8 static SYMDIR *in_cache; static SYMDIR *_cache_lookup(pth) const char *pth; { SYMDIR **prev = &in_cache, *cur = *prev; while (cur) { if (!strcmp(cur->s_pth, pth)) { /* remove from cache */ *prev = cur->s_nxt; cur->s_nxt = 0; return cur; } prev = &cur->s_nxt; cur = *prev; } return 0; } static void _cache_add(cur) SYMDIR *cur; { SYMDIR *nxt = 0; SYMENTRY *dir, *old; int count = 0; cur->s_nxt = in_cache; in_cache = cur; while (cur) { nxt = cur->s_nxt; ++count; if (count == CACHESIZE) cur->s_nxt = 0; else if (count > CACHESIZE) { dir = cur->s_dir; while (dir) { old = dir; dir = dir->next; free(old); } free(cur->s_pth); free(cur); } cur = nxt; } } /* * free a symbolic directory. Note that _unx2dos is expecting the entries * to be accessible, i.e. it knows that we're caching at least 1 directory. * If this ever changes, change _unx2dos. */ void _free_symdir(dir) SYMDIR *dir; { if (dir) _cache_add(dir); } /* * read in the symbolic directory corresponding to "path". * the symdir must be removed from the cache, since it may * be modified and written back via _write_symdir */ SYMDIR *_read_symdir(path) char *path; { char dirname[FILENAME_MAX], tmp[2*FILENAME_MAX], *p; int fd, c; SYMENTRY *old, *new; SYMDIR *dir; /* check that symbolic links are active */ if (!_lOK) { errno = EINVAL; return NULL; } strcpy(dirname, path); strcat(dirname, "\\"); strcat(dirname, _lDIR); if (dir = _cache_lookup(dirname)) return dir; ResetFgetc(); fd = Fopen(dirname, 0); if (fd < 0 && fd != -ENOENT) { errno = -fd; return NULL; } dir = (SYMDIR *)malloc(sizeof(SYMDIR)+strlen(tmp)+1); if (dir == NULL) { errno = ENOMEM; return dir; } dir->s_pth = strdup(dirname); if (!dir->s_pth) return NULL; dir->s_nxt = 0; old = NULL; if (fd == -ENOENT) goto done_directory; p = tmp; while ( (c = Fgetc(fd)) >= 0 ) { if (c == '\r') continue; if (c == '\n') { *p++ = 0; new= (SYMENTRY *)malloc(sizeof(SYMENTRY)+strlen(tmp)+1); if (new == 0) break; strcpy(new->linkname, tmp); new->linkto = new->linkname; for (p = new->linkname; *p; p++) { if (*p == '\t') { *p++ = 0; new->linkto = p; break; } } /* * Now we should check for any further fields, such as "flags" * it is very important that we save *all* the characters that were given * in the flags field, as well as setting the bits we understand. * That way, programs remain compatible with future versions of the * standard. */ for (;*p;p++) { if (*p == '\t') { *p++ = 0; break; } } new->cflags = p; /* save pointer to flag characters */ new->flags = 0; for (;*p;p++) { if (*p == 'A') new->flags |= SD_AUTO; } new->next = old; old = new; p = tmp; } else *p++ = c; } (void)Fclose(fd); done_directory: dir->s_dir = old; return dir; } /* * write a symbolic directory out onto a path */ int _write_symdir(path, dir) char *path; SYMDIR *dir; { SYMENTRY *new; int fd, r; char dirname[FILENAME_MAX]; /* check to see that symbolic links are OK */ if (!_lOK) return -EINVAL; strcpy(dirname, path); strcat(dirname, "\\"); strcat(dirname, _lDIR); if (dir->s_dir == NULL) { (void)Fdelete(dirname); return 0; } fd = Fcreate(dirname, 0); if (fd < 0) { return fd; } for (new = dir->s_dir; new; new = new->next) { (void)Fwrite(fd, strlen(new->linkname), new->linkname); (void)Fwrite(fd, 1L, "\t"); (void)Fwrite(fd, strlen(new->linkto), new->linkto); if (new->cflags[0]) { (void)Fwrite(fd, 1L, "\t"); (void)Fwrite(fd, strlen(new->cflags), new->cflags); } r = Fwrite(fd, 1L, "\n"); if (r <= 0) { (void)Fclose(fd); return r; } } (void)Fclose(fd); return 0; } /* * _symdir_lookup, _make_symlink: utility routines that are needed in * various places */ /* * return the symbolic directory entry for "name", or NULL if it is not * found */ SYMENTRY * _symdir_lookup(dir, name) SYMDIR *dir; const char *name; { SYMENTRY *ent; if (!dir) return 0; for (ent = dir->s_dir; ent; ent = ent->next) { if (!strcmp(ent->linkname, name)) return ent; } return 0; } /* * _make_autolink(dosname, source): make an "automatic" symbolic link, * with name "source", to the file whose full canonical dos pathname is in * "dosname". Returns 1 if link made, 0 if not. It checks the _lAUTO * flag, so parent functions don't have to. Parents are responsible * for dealing with any conflicts with existing files. */ int _make_autolink(dosname, source) char *dosname, *source; { char path[FILENAME_MAX], oldname[_LIB_NAME_MAX]; char *s, *p; SYMDIR *dir; SYMENTRY *d; if (!_lAUTO) return 0; strcpy(path, dosname); if ((p = strrchr(path, '\\'))) { *p++ = 0; _dos2unx(p, oldname); } else { _dos2unx(path, oldname); path[0] = 0; } if (!(dir = _read_symdir(path))) return 0; d = (SYMENTRY *) malloc(sizeof(SYMENTRY)+strlen(source)+strlen(oldname)+4); if (d == 0) { errno = ENOMEM; return -1; _free_symdir(dir); } /* * now set up the fields; remember to set the character flags (cflags) as well! */ strcpy(d->linkname, source); for (s = d->linkname; *s; s++) ; ++s; d->linkto = s; strcpy(s, oldname); for (; *s; s++) ; ++s; strcpy(s, "A"); /* AUTO flag */ d->cflags = s; d->flags = SD_AUTO; d->next = dir->s_dir; dir->s_dir = d; _write_symdir(path, dir); return 1; } static void _del_dir(cur) SYMDIR *cur; { SYMENTRY *dir, *old; dir = cur->s_dir; while (dir) { old = dir; dir = dir->next; free(old); } free(cur->s_pth); free(cur); } void _del_symdir_cache() { SYMDIR *cur = in_cache, *nxt = 0; while (cur) { nxt = cur->s_nxt; _del_dir(cur); cur = nxt; } in_cache = 0; }