#include #include #include #include #include extern unsigned verbose; extern char *index(), *rindex(); extern char *malloc(); static char *GetPathString(); static char *NextDirName(); static char *ScanDir(); /* Define the maximum length of the search path variable or file. */ #define MAXPATHLENGTH 4096 /* Hacker's note: the following string constants are declared as static * strings with preset values. The reason for doing this is to allow * the end user to edit these variables (using FileZap, et al) to * customize the operation of MRMan. */ static char PATCHHEADER[] = "# Patch Area "; static char ENVDESC[] = "# Environment device name (32) ->"; /* ENVironment device name. */ static char ENVNAME[33] = "ENV:"; static char VARDESC[] = "# Search path variable name (32) ->"; /* ENVironment variable name which contains the Man search path. */ static char MANPATHVAR[33] = "ManPath"; /* If ENV:ManPath is undefined, the following name defines the default * Man directory. */ static char MANDIRDESC[] = "# Man directory logical name (32) ->"; static char MANDIR[33] = "MAN:"; static char MANPATHDESC[] = "# Man search path file name (32) ->"; /* If ENV:ManPath is undefined, this file contains the Man search path. */ static char MANPATHFILE[33] = "ManPath"; static char MANVIEWERDESC[] = "# Default viewer program name (32) ->"; /* The name of our viewing program: */ static char MANVIEWER[33] = "More"; #define SUFFIXES 3 static char SUFFIX1DESC[] = "# Suffix 1 (8) ->"; static char suffix1[9] = "man"; static char SUFFIX2DESC[] = "# Suffix 2 (8) ->"; static char suffix2[9] = "doc"; static char SUFFIX3DESC[] = "# Suffix 3 (8) ->"; static char suffix3[9] = "text"; static char *suffixes[SUFFIXES] = { suffix1, suffix2, suffix3 }; static char dirName[33]; static char fullPath[256]; static char manPathFile[256]; static unsigned checkEnvironment = 1; static char *manDir = MANDIR; static char searchPath; /* from ENV:ManPath or DOC:ManPath */ static char testName[33]; static char *theSubject; /* FUNCTION BuildPathName - concatenate name to directory or device spec. SYNOPSIS static char *BuildPathName(prefix, suffix, result) char *prefix, *suffix, *result; DESCRIPTION BuildPathName concatenates a simple file or directory name to a (possibly compound) pathname. Its primary function is to insert a slash character between and if does not end in a colon (:). BuildPathName returns . */ static char * BuildPathName(prefix, suffix, result) char *prefix, *suffix, *result; { register char prefixEndChar; register int prefixEndPos = strlen(prefix) - 1; /* The string may be empty. */ if (prefixEndPos >= 0) { strcpy(result, prefix); prefixEndChar = prefix[prefixEndPos]; if ((prefixEndChar != ':') && (prefixEndChar != '/')) strcat(result,"/"); } else *result = '\0'; strcat(result, suffix); return result; } /* FUNCTION GetManPath - get the Man search path. SYNOPSIS static char *GetManPath(dirName); char *dirName; DESCRIPTION GetManPath first looks for an ENVironment variable containing the Man search path. If not found, GetManPath next attempts to locate a file named by MANPATHFILE and stored in the directory whose name is pointed to by . If successful, GetManPath returns a pointer to a dynamically allocated buffer containing the search path. Otherwise, NULL is returned. */ static char * GetManPath(dirName) char *dirName; { char *sp = NULL; char tempName[256]; if (checkEnvironment) { BuildPathName(ENVNAME, MANPATHVAR, tempName); sp = GetPathString(tempName); } if (sp == NULL) { BuildPathName(dirName,MANPATHFILE,tempName); sp = GetPathString(tempName); } return sp; } /* FUNCTION GetPathString - get the MRMan search path string. SYNOPSIS static char *GetPathString(pathName) char *pathName; DESCRIPTION GetPathString attempts to read the contents of a file into a dynamically allocated buffer. The argement may be the name of a disk file or environment variable. CREDITS This code was pretty much lifted from Matt Dillon's GetDEnv routine. Thanks, Matt. */ static char * GetPathString(pathName) char *pathName; { char *res = NULL; /* result string */ long fh; /* file handle */ long len; if (fh = (long) Open(pathName, 1005L)) { len = (Seek(fh, 0L, 1L), Seek(fh, 0L, 0L)); if (len > MAXPATHLENGTH) { Printf("MRMan: search path file '%s' is too big (%ld)!\n", pathName, len); } else if (len >= 0 && (res = malloc((int) (len+1) ) ) ) { Seek(fh, 0L, -1L); if (Read(fh, res, len) != len) len = 0; res[len] = 0; } Close(fh); } return res; } /* FUNCTION GetViewerName - get the name of the preferred text viewer. SYNOPSIS char *GetViewerName(); DESCRIPTION GetViewerName first checks the environment variable MANVIEWER for the name of the program to be used for viewing man files. If it is undefined, the name in MANVIEWER is used. */ char * GetViewerName() { char *theViewer; char varName[33]; BuildPathName(ENVNAME, "MANVIEWER", varName); theViewer = GetPathString(varName); if (! theViewer) theViewer = MANVIEWER; return theViewer; } /* FUNCTION ScanDir - search directory for document file. SYNOPSIS static char *ScanDir(dirName, lock, scanSubDir) char *dirName; struct Lock *lock; unsigned scanSubDir DESCRIPTION ScanDir performs a search for a document, described by the package variable , beginning with directory . The directory is also described by , a filesystem lock on the directory. If is TRUE, ScanDir will also perform a recursive scan of subdirectories. Otherwise, the search is limited to the current directory. Each filename encountered is subjected to a set of pattern matches to determine if it meets the rules for eligibility. ScanDir returns a pointer to a full pathname if a file is found, NULL otherwise. The pointer returned points to the package variable and thus should not be freed. If the pathname is not used immediately, it should be copied, since subsequent calls to ScanDir or uses of fullPath will render it invalid. */ static char * ScanDir(dirName, lock, scanSubDir) char *dirName; struct Lock *lock; unsigned scanSubDir; { static char testName[33]; struct FileInfoBlock *fib; unsigned i, ok; char *newDirName; struct Lock *newLock = NULL; char *theFile = NULL; char *tp = NULL; if (verbose) Printf("Scanning directory '%s'.\n", dirName); fib = AllocMem((long) sizeof(*fib), 0l); if (! fib) { no_mem: Printf("MRMan: out of memory!\n"); goto done; } Examine(lock, fib); while (ExNext(lock, fib)) { /* Directory entries require special processing. */ if (fib->fib_DirEntryType > 0) { /* Subdirectory? */ /* Is it OK to scan subdirectories? */ if (! scanSubDir) continue; newDirName = malloc(strlen(dirName) + strlen(fib->fib_FileName) + 2); if (! newDirName) goto no_mem; BuildPathName(dirName, fib->fib_FileName, newDirName); newLock = (struct Lock *) Lock(newDirName, SHARED_LOCK); if (! newLock) { Printf("MRMan: failed to lock subdirectory '%s'.\n", newDirName); goto done; } /* Note: we ALWAYS scan sub-subdirectories. */ tp = ScanDir(newDirName, newLock, 1); UnLock(newLock); free(newDirName); if (tp) { theFile = tp; goto done; } else continue; } /* When attempting to match to the current filename, * we elminate any trailing suffix matter (following an embedded * period) from the comparison. Thus, the match will work for * files of the form "subject.man", "subject.doc", "subject.doc.Z", * etc. I realize that this is a pretty "hardwired" approach, but * it should work in most situations. */ strcpy(testName, fib->fib_FileName); /* If the file has a suffix, only allow one of the predefined * suffixes, above. Since the file may be compressed, we first * dispose of the trailing ".Z" suffix, if one exists. */ if (tp = rindex(testName, '.')) { *tp = '\0'; /* This might be a ".Z" suffix for a compressed file. If it * is, ignore it and look backward to the next suffix. */ if ( ! Strcmp(tp+1,"Z") ) tp = rindex(testName,'.'); if (tp) { *tp = '\0'; ++tp; for (i = 0, ok = 0; i < SUFFIXES; ++i) if (! Strcmp(tp, suffixes[i])) { ok = 1; break; } if (! ok ) continue; } } /* Now, drop ANY suffix material from the filename and compare it * to the subject string. */ if (tp = index(testName, '.')) *tp = '\0'; /* Truncate the string. */ if (!Strcmp(testName, theSubject)) { theFile = BuildPathName(dirName, fib->fib_FileName, fullPath); goto done; } } done: if (fib) FreeMem(fib, (long) sizeof(*fib)); return theFile; } /* FUNCTION ManSearch - top-level document search routine SYNOPSIS char *ManSearch(startDir, subject) char *startDir, subject; DESCRIPTION ManSearch performs a search for a document file which describes the . If is not NULL or empty, it is expected to contain the pathname of the directory where the search is to begin. In this case, the environment variable ManPath is not referenced. Otherwise, ManSearch performs its search according to the search rules described in the MRMan document. If a document file is found, a pointer to its name is returned. Otherwise, ManSearch returns NULL. */ char * ManSearch(startDir, subject) char *startDir, *subject; { char dirName[256]; struct FileInfoBlock *fib; struct Lock *lock; char *manPath, *mp; char *manPathSub, *mpSub; unsigned scanSubDir; char subDirName[256]; char *theFile = NULL; theSubject = subject; /* Copy subject to "global". */ fib = AllocMem((long) sizeof(*fib), 0L); if (fib == NULL) { no_mem: Printf("MRMan: out of memory!\n"); goto done; } /* The search for "subject" can use one of 3 different starting points. * If the parameter is non-null (!= NULL && contains text) * it is used. If the environment variable ManPath is defined, its * contents are used. Otherwise, an attempt is made to use the contents * of a file named "Man:ManPath". */ if (startDir && *startDir) { /* Valid starting directory? */ manDir = startDir; checkEnvironment = 0; /* Don't look at ENV:ManPath. */ } manPath = GetManPath(manDir); if (manPath == NULL) { /* As a last resort, create a path of one directory, the * default or user-specified MAN directory. */ manPath = malloc(strlen(manDir)+2); if (! manPath) goto no_mem; strcpy(manPath, manDir); } if (verbose) Printf("MRMan: search path is '%s'.\n", manPath); checkEnvironment = 0; /* We're done with ENV:ManPath. */ mp = manPath; while (mp) { mp = NextDirName(mp, dirName); if (! *dirName) break; /* Each top-level directory can, in turn, have its own ManPath * file which specifies the search order of its subdirectories. * Actually, the ManPath file can specify ANY directory. */ manPathSub = GetManPath(dirName); if (manPathSub == NULL) { /* No ManPath? */ scanSubDir = 1; /* Allow subdirectory scanning. */ manPathSub = malloc(strlen(dirName)+1); if (manPathSub == NULL) goto no_mem; strcpy(manPathSub,dirName); } else { scanSubDir = 0; /* Order is specified. */ if (verbose) Printf("Search path for '%s' is '%s'.\n",dirName, manPathSub); } mpSub = manPathSub; while (mpSub) { mpSub = NextDirName(mpSub, dirName); if (! *dirName) break; /* The directory name may be relative to where the ManPath * file is stored. If it is (no ':'), create an absolute * path. */ if (!index(dirName, ':')) { BuildPathName(manDir, dirName, subDirName); } else strcpy(subDirName, dirName); lock = (struct Lock *) Lock(subDirName, SHARED_LOCK); if (lock == NULL) { Printf("MRMan: could not lock '%s', error %ld\n", subDirName, IoErr()); continue; } if (! Examine(lock, fib)) { Printf("MRMan: could not examine '%s', error %ld\n", subDirName, IoErr()); UnLock(lock); continue; } if (fib->fib_DirEntryType <= 0) { Printf("MRMan: '%s' is not a directory!\n", subDirName); goto done; } theFile = ScanDir(subDirName, lock, scanSubDir); UnLock(lock); if (theFile) goto done; } /* while (mpSub) */ free(manPathSub); manPathSub = NULL; } done: if (fib) FreeMem(fib, (long) sizeof(*fib)); if (manPath) free(manPath); if (manPathSub) free(manPathSub); return theFile; } /* FUNCTION NextDirName - collect next directory name from search path SYNOPSIS static char *NextDirName(path, dirName) char *path, *dirName; DESCRIPTION NextDirName collects the next directory name token from the search path variable, , ignoring _leading_ white space and newlines. Semicolons separate name tokens. NextDirName stores its result in and returns an updated pointer to the . If is zero, no token was collected. */ static char * NextDirName(path, dirName) char *path, *dirName; { char c; short length = 0; *dirName = '\0'; if (path && *path) { while (c = *path++) { if (index(" \t\n;", c)) continue; break; } if (! c) path = NULL; else { while (c && (c != ';') && (c != '\n')) { if (length == 255) { Printf("MRMan: path element is too long!\n'%s'\n", dirName); dirName[0] = '\0'; path = NULL; } dirName[length++] = c; c = *path++; } dirName[length] = '\0'; if (! c) path = NULL; } } else { /* Search path variable is empty. */ path = NULL; } return path; }