# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. include include include "help.h" include "helpdir.h" .help helpdir .nf ___________________________________________________________________________ HELPDIR -- Routines for accessing helpdir files. A helpdir lists the modules in a package, as well as the files containing the help text and source for each module. There are two kinds of helpdir files: the 'packhelp' file lists the packages, and each package has its own helpdir file listing the modules within that package. The following functions are required to access help module lists: hp = hd_open (fname) hd_close (hp) modnum = hd_findmod (hp, modname) nchars = hd_getname (hp, modnum, field, outstr, maxch) hd_debug (hp, out) The HD_OPEN function opens the helpdir file and decodes the contents, producing a binary structure pointed to by hp. HD_FINDMOD searches for a module by name, returning the module number within the helpdir. HD_GETNAME fetches either the module name (field "mod") or a file name (fields "hlp", "sys", etc.). File names are returned as OS pathnames, with logical directory expansion taking place within the helpdir package. Helpdir files have the following structure: $defdir = pathname|ldir $ldir1 = pathname|ldir $ldir2 = pathname|ldir ... $ldirN = pathname|ldir module1 hlp=file, sys=file, src=file, pkg=file, men=file module2 hlp=file, etc. ... moduleN hlp=file, etc. The dollar signs are required to make it easy to distinguish logical directory declarations from module entries. Logical directories defined local to the help directory file are not expanded recursively. If the last nonwhite char on a line is a comma, the file list is assumed to be continued on the next line. If two or more files in a module list are the same, all but the first may be set to ".." and the last file name given will be used. Quotes are optional. All file assignments are optional, and they may occur in any order. .endhelp _______________________________________________________________________ # HD_OPEN -- Open the helpdir file, allocate descriptor and decode file # into descriptor. Sort module list when done, unless sorting is disabled # by inclusion of the directive ".nosort" in the text. Ignore comment lines and # blank lines. Ldir declarations and module entries may be given in any # order. pointer procedure hd_open (helpdir_file) char helpdir_file[ARB] bool sort_modules pointer sp, hp, ip, lbuf, defdir, word int fd, junk bool streq() int open(), getline(), fnroot(), fnldir(), ctowrd() pointer hd_putstr() errchk salloc, open, calloc, malloc, getline, hd_putstr errchk hd_putldiry, hd_putmodule, hd_sort_modules begin call smark (sp) call salloc (lbuf, SZ_LINE, TY_CHAR) call salloc (defdir, SZ_PATHNAME, TY_CHAR) call salloc (word, SZ_FNAME, TY_CHAR) # If helpdir file is not yet installed, print warning message # and return the NULL pointer, indicating that the help directory # could not be loaded. iferr (fd = open (helpdir_file, READ_ONLY, TEXT_FILE)) { call sfree (sp) call erract (EA_ERROR) return (NULL) } # Allocate and initialize descriptor and string buffer. Must init # nextch to 1 because 0 is the null index. call calloc (hp, LEN_HDSTRUCT, TY_STRUCT) call malloc (HD_SBUF(hp), SZ_SBUF, TY_CHAR) HD_DEFDIR(hp) = NULL HD_NEXTCH(hp) = 1 HD_SZSBUF(hp) = SZ_SBUF HD_MAXMODULES(hp) = MAX_MODULES HD_LENHD(hp) = LEN_HDSTRUCT sort_modules = true # Extract package name into string buffer. The package name is assumed # to be the root of the filename. junk = fnroot (helpdir_file, Memc[lbuf], SZ_LINE) HD_PAKNAME(hp) = hd_putstr (hp, Memc[lbuf]) # Extract directory prefix of the package help directory file. Make # it the default directory prefix for all filenames in the directory. if (fnldir (helpdir_file, Memc[defdir], SZ_PATHNAME) == 0) { call fpathname (helpdir_file, Memc[lbuf], SZ_LINE) junk = fnldir (Memc[lbuf], Memc[defdir], SZ_PATHNAME) } call sprintf (Memc[lbuf], SZ_LINE, "defdir = %s\n") call pargstr (Memc[defdir]) call hd_putldiry (hp, Memc[lbuf]) # Compile the file, processing all logical directory definitions, # set option statments, and module declarations. Ignore blank lines # and comment lines. while (getline (fd, Memc[lbuf]) != EOF) { for (ip=lbuf; IS_WHITE (Memc[ip]); ip=ip+1) ; if (Memc[ip] == '#' || Memc[ip] == '\n' || Memc[ip] == EOS) next # A leading dollar sign denotes a logical directory declaration. # A leading period denotes a compiler directive. # Anything else is a module entry. if (Memc[ip] == '$') { call hd_putldiry (hp, Memc[ip+1]) } else if (Memc[ip] == '.') { ip = ip + 1 if (ctowrd (Memc, ip, Memc[word], SZ_FNAME) <= 0) next # The directive ".nosort" disables sorting of the module list. # There are no other such directives at present, but they are # easy to add. if (streq (Memc[word], "nosort")) sort_modules = false else { call eprintf ("Warning: unknown directive %s in helpdir\n") call pargstr (Memc[word]) } next } else call hd_putmodule (hp, fd, Memc[lbuf]) } # We are all done with the helpdir file, so close it. Sort module # list alphabetically by name. call close (fd) if (sort_modules && HD_NMODULES(hp) > 1) call hd_sort_modules (hp) # Return any unused space in string buffer. call realloc (HD_SBUF(hp), HD_NEXTCH(hp), TY_CHAR) HD_SZSBUF(hp) = HD_NEXTCH(hp) # Return any unused module descriptors. HD_LENHD(hp) = HD_LENHD(hp) - LEN_MODSTRUCT * (HD_MAXMODULES(hp) - HD_NMODULES(hp)) HD_MAXMODULES(hp) = HD_NMODULES(hp) call realloc (hp, HD_LENHD(hp), TY_STRUCT) call sfree (sp) return (hp) end # HD_CLOSE -- Close the helpdir descriptor. The helpdir file has already # been closed; all we need do is return the string buffer and the helpdir # descriptor structure. procedure hd_close (hp) pointer hp begin if (hp != NULL) { call mfree (HD_SBUF(hp), TY_CHAR) call mfree (hp, TY_STRUCT) } end # HD_PUTLDIRY -- Decode a logical directory declaration and store it away # in the descriptor. We are passed the declaration minus the leading # dollar sign. Format: "variable = string". procedure hd_putldiry (hp, decl) pointer hp char decl[ARB] int ip, nldir, strp pointer sp, buf, op char hd_getc() int strncmp(), hd_putstr() errchk salloc, hd_getc, hd_putstr begin call smark (sp) call salloc (buf, SZ_LINE, TY_CHAR) # Do nothing if null declaration. for (ip=1; IS_WHITE(decl[ip]); ip=ip+1) ; if (decl[ip] == '\n' || decl[ip] == EOS || decl[ip] == '#') { call sfree (sp) return } # Extract "ldir=string", eliminating all whitespace, and deleting # the newline at the end of the statement. Quotes around the string # are optional and are deleted in hl_getc. for (op=buf; hd_getc (decl, ip, Memc[op]) != EOS; op=op+1) if (Memc[op] == '\n') op = op - 1 # Deposit the "ldir=string" in the string buffer, and set the # appropriate pointers for either defdir or a new ldir. strp = hd_putstr (hp, Memc[buf]) if (strncmp (Memc[buf], "defdir", 6) == 0) HD_DEFDIR(hp) = strp else { nldir = HD_NLDIRS(hp) + 1 if (nldir > MAX_LDIRS) call error (8, "Too many ldir declarations in helpdir") HD_NLDIRS(hp) = nldir HD_LDIR(hp,nldir) = strp } call sfree (sp) end # HD_PUTMODULE -- Put a module declaration into the helpdir descriptor. # Start new entry; save module name; process file names. If line ends # in a comma, get a new line. Print warning if an unknown file type # keyword is encountered. procedure hd_putmodule (hp, fd, lbuf) pointer hp int fd char lbuf[ARB] char ch int ip, junk, m, ftype, strp pointer sp, buf, op, sbuf, mp bool streq() char hd_getc(), hd_peekc() int getline(), ctowrd(), hd_putstr() errchk salloc, getline, hd_putstr, hd_getc begin call smark (sp) call salloc (buf, SZ_LINE, TY_CHAR) sbuf = HD_SBUF(hp) # Fetch module name. Cannot be null or line is blank and we would # not have been called. ip = 1 junk = ctowrd (lbuf, ip, Memc[buf], SZ_LINE) # Check if this is a redefinition of a module already defined. # If so, overwrite descriptor of earlier module, else allocate # a new descriptor. for (m=1; m <= HD_NMODULES(hp); m=m+1) { mp = HD_MODULE(hp,m) if (streq (Memc[buf], Memc[sbuf+M_NAME(mp)])) break } if (m > HD_NMODULES(hp)) { # If we are out of space for modules, increase the descriptor # structure size to allow more module descriptors. if (m > HD_MAXMODULES(hp)) { HD_LENHD(hp) = HD_LENHD(hp) + (INC_MODULES * LEN_MODSTRUCT) call realloc (hp, HD_LENHD(hp), TY_STRUCT) HD_MAXMODULES(hp) = HD_MAXMODULES(hp) + INC_MODULES } HD_NMODULES(hp) = m } mp = HD_MODULE(hp,m) call aclri (Memi[mp], LEN_MODSTRUCT) # Put module name in string buffer and save index of string in descr. M_NAME(mp) = hd_putstr (hp, Memc[buf]) # Process file name fields, if any. Unrecognized file type keywords # cause a warning to be issued. Redundant entires overwrite old # entries. Order makes no diff, absence is ok. op = buf ftype = TY_UNKNOWN strp = 0 repeat { ch = hd_getc (lbuf, ip, ch) switch (ch) { case '=': # Buffer contains the code word for the file being set. Memc[op] = EOS call strlwr (Memc[buf]) if ( streq (Memc[buf], "hlp")) ftype = TY_HLP else if (streq (Memc[buf], "sys")) ftype = TY_SYS else if (streq (Memc[buf], "src")) ftype = TY_SRC else if (streq (Memc[buf], "pkg")) ftype = TY_PKG else if (streq (Memc[buf], "men")) ftype = TY_MEN else { ftype = TY_UNKNOWN call eprintf ("Warning: bad file type `%s' in helpdir\n") call pargstr (Memc[buf]) } op = buf case ',', '\n', EOS: # Buffer contains the file name string. Put it in the string # buffer and save pointer in appropriate field of module # descriptor. Memc[op] = EOS # If filename is "..", i.e., "sys=..", the filename is identical # to that last specified. Use the file name pointer. if (streq (Memc[buf], "..")) { if (strp == 0) call error (9, "helpdir: `..' reference, no prev file") } else strp = hd_putstr (hp, Memc[buf]) switch (ftype) { case TY_HLP: M_HLP(mp) = strp case TY_SYS: M_SYS(mp) = strp case TY_SRC: M_SRC(mp) = strp case TY_PKG: M_PKG(mp) = strp case TY_MEN: M_MEN(mp) = strp } op = buf if (ch == '\n' || ch == EOS) break # end of statement else { # Check for continuation on next line. Read a new line into # the buffer if end of line follows comma. if (hd_peekc(lbuf,ip) == '\n' || hd_peekc(lbuf,ip) == EOS) { if (getline (fd, lbuf) == EOF) { call eprintf ("Unexpected EOF in helpdir file\n") call sfree (sp) return } ip = 1 } } default: # Regular character. Deposit in buffer. if (op >= buf+SZ_LINE) call error (10, "helpdir: buffer overflow reading modspec") Memc[op] = ch op = op + 1 } } call sfree (sp) end # HD_GETC -- Get next nonwhite character from the input line. Leave pointer # pointing to next character. Ignore quotes, so that file name strings can # be quoted without penalty. char procedure hd_getc (lbuf, ip, ch) char lbuf[ARB] int ip char ch char hd_peekc() begin ch = hd_peekc (lbuf, ip) if (ch != EOS) ip = ip + 1 return (ch) end # HD_PEEKC -- Peek at the next nonwhite character on a line, but do not # advance the input pointer past the character. Ignore quote characters # and comments. char procedure hd_peekc (lbuf, ip) char lbuf[ARB] int ip char ch begin for (ch=lbuf[ip]; ch != EOS; ch=lbuf[ip]) if (IS_WHITE(ch) || ch == '\'' || ch == '"') { ip = ip + 1 } else if (ch == '#') { while (lbuf[ip] != '\n' && lbuf[ip] != EOS) ip = ip + 1 } else break return (ch) end # HD_PUTSTR -- Put a string (incl EOS) in the string buffer at nextch. # If there is not enough space in the buffer, reallocate a larger buffer. # Return the index of the string in the string buffer. int procedure hd_putstr (hp, str) pointer hp char str[ARB] int nextch, nchars, strlen() errchk realloc begin # Null strings are not stored and cause a null index to be returned. nchars = strlen (str) if (nchars == 0) return (0) nextch = HD_NEXTCH(hp) if (nextch + nchars + 1 > HD_SZSBUF(hp)) { HD_SZSBUF(hp) = HD_SZSBUF(hp) + INC_SZSBUF call realloc (HD_SBUF(hp), HD_SZSBUF(hp), TY_CHAR) } call strcpy (str, Memc[HD_SBUF(hp) + nextch], ARB) HD_NEXTCH(hp) = nextch + nchars + 1 return (nextch) end # HD_SORT_MODULES -- Sort the module list alphabetically by name. # A simple exchange sort is ok because the sort time is negligible # compared to all the file accesses, Lroff etc. procedure hd_sort_modules (hp) pointer hp bool sorted int nmodules, m, mlen, i, temp pointer sbuf, mp1, mp2 bool strgt() begin nmodules = HD_NMODULES(hp) sbuf = HD_SBUF(hp) mlen = LEN_MODSTRUCT if (nmodules < 2) return repeat { sorted = true do m = 1, nmodules-1 { mp1 = HD_MODULE(hp,m) mp2 = mp1 + mlen if (strgt (Memc[sbuf+M_NAME(mp1)], Memc[sbuf+M_NAME(mp2)])) { do i = 0, mlen-1 { temp = Memi[mp1+i] Memi[mp1+i] = Memi[mp2+i] Memi[mp2+i] = temp } sorted = false } } } until (sorted) end # HD_FINDMOD -- Search for the named module and return the module number # if found. Abbreviations are permitted. An ambiguous abbreviation is # an error. int procedure hd_findmod (hp, modname) pointer hp char modname[ARB] int m, namelen, module pointer mp, sbuf int strlen(), strncmp() begin namelen = strlen (modname) if (namelen == 0) return (0) module = 0 sbuf = HD_SBUF(hp) for (m=1; m <= HD_NMODULES(hp); m=m+1) { mp = HD_MODULE(hp,m) if (strncmp (Memc[sbuf+M_NAME(mp)], modname, namelen) == 0) { if (strlen (Memc[sbuf+M_NAME(mp)]) == namelen) { return (m) # exact match } else if (module != 0) { call eprintf ("\n--> %s <--\n") call pargstr (modname) call error (11, "Ambiguous module name abbreviation") } else module = m } } return (module) end # HD_GETNAME -- Get the module name or a filename. If a filename is requested, # check if filename contains a logical directory reference. If yes, try to # satisfy the reference from the local list of ldirs (but no recursive refs # permitted here). If no, prepend the defdir string. define nullstr_ 91 define nodefdir_ 92 int procedure hd_getname (hp, m, field, outstr, maxch) pointer hp int m # module number int field # field code char outstr[ARB] int maxch int len_ldir, op pointer mp, sp, ldir, sbuf, fname_ptr, ip, subdir int strncmp(), gstrcpy(), hd_getldir(), fnldir() errchk salloc, hd_getldir begin call smark (sp) call salloc (ldir, SZ_PATHNAME, TY_CHAR) call salloc (subdir, SZ_FNAME, TY_CHAR) if (hp == NULL) call error (12, "hd_getname: bad helpdir descriptor") if (m < 1 || m > HD_NMODULES(hp)) { nullstr_ call sfree (sp) outstr[1] = EOS return (0) } mp = HD_MODULE(hp,m) sbuf = HD_SBUF(hp) switch (field) { case TY_MODNAME: call sfree (sp) return (gstrcpy (Memc[sbuf+M_NAME(mp)], outstr, maxch)) case TY_HLP: fname_ptr = M_HLP(mp) case TY_SYS: fname_ptr = M_SYS(mp) case TY_SRC: fname_ptr = M_SRC(mp) case TY_PKG: fname_ptr = M_PKG(mp) case TY_MEN: fname_ptr = M_MEN(mp) default: goto nullstr_ } # If index is zero, no filename was given. if (fname_ptr == 0) goto nullstr_ # Get ldir substring, if any, from filename. If no ldir, prepend # defdir and quit. Otherwise lookup ldir in local list. If found, # prepend value. Otherwise the ldir is a CL global one, and return # filename without modification. If the given ldir string begins # with "./", substitute the value of defdir for the ".". len_ldir = fnldir (Memc[sbuf+fname_ptr], Memc[ldir], SZ_PATHNAME) if (len_ldir == 0) { if (HD_DEFDIR(hp) == 0) goto nodefdir_ for (ip = sbuf + HD_DEFDIR(hp); Memc[ip] != '='; ip=ip+1) ; op = gstrcpy (Memc[ip+1], outstr, maxch) + 1 ip = sbuf + fname_ptr } else if (hd_getldir (hp, Memc[ldir], Memc[subdir], SZ_FNAME) == 0) { op = 1 ip = sbuf + fname_ptr } else { if (strncmp (Memc[subdir], "./", 2) == 0) { if (HD_DEFDIR(hp) == 0) nodefdir_ call error (13, "Default directory omitted in helpdir") for (ip = sbuf + HD_DEFDIR(hp); Memc[ip] != '='; ip=ip+1) ; op = gstrcpy (Memc[ip+1], outstr, maxch) + 1 ip = subdir + 2 } else { op = 1 ip = subdir } op = op + gstrcpy (Memc[ip], outstr[op], maxch - op + 1) ip = sbuf + fname_ptr + len_ldir } call sfree (sp) return (gstrcpy (Memc[ip], outstr[op], maxch - op + 1)) end # HD_GETLDIR -- Search the logical directory list for the package helpdir # file for the given ldir name. Return value in output string if found. int procedure hd_getldir (hp, ldir, outstr, maxch) pointer hp char ldir[ARB] char outstr[ARB] int maxch int i pointer sp, ip, op, sbuf, envvar, filvar bool streq() int gstrcpy() begin call smark (sp) call salloc (envvar, SZ_FNAME, TY_CHAR) call salloc (filvar, SZ_FNAME, TY_CHAR) sbuf = HD_SBUF(hp) # Ldir string has the form "ldir$". Extract the ldir name into the # filvar buffer, omitting the $ delimiter. i = 1 for (op=filvar; ldir[i] != '$' && ldir[i] != EOS; op=op+1) { Memc[op] = ldir[i] i = i + 1 } Memc[op] = EOS for (i=1; i <= HD_NLDIRS(hp); i=i+1) { # Extract the helpdir env variable name into the envvar buffer, # stopping when the '=' is reached. ip = sbuf + HD_LDIR(hp,i) for (op=envvar; Memc[ip] != '='; op=op+1) { if (Memc[ip] == EOS) call error (14, "helpdir: missing '=' in ldir declaration") Memc[op] = Memc[ip] ip = ip + 1 } Memc[op] = EOS # Return whatever follows the '=' if we have a match. The input # pointer is left pointing at the '='. if (streq (Memc[filvar], Memc[envvar])) { call sfree (sp) return (gstrcpy (Memc[ip+1], outstr, maxch)) } } outstr[1] = EOS call sfree (sp) return (0) end # HD_DEBUG -- Dump a compiled helpdir structure to the given output file. procedure hd_debug (hd, out) pointer hd #I compiled helpdir int out #O output file int i pointer sbuf, mp begin sbuf = HD_SBUF(hd) # General header stuff. call fprintf (out, "pakname=%d(%s), nmodules=%d, maxmodules=%d, lenhd=%d\n") call pargi (HD_PAKNAME(hd)) call pargstr (Memc[sbuf+HD_PAKNAME(hd)]) call pargi (HD_NMODULES(hd)) call pargi (HD_MAXMODULES(hd)) call pargi (HD_LENHD(hd)) call fprintf (out, "defdir=%d(%s)\n") call pargi (HD_DEFDIR(hd)) call pargstr (Memc[sbuf+HD_DEFDIR(hd)]) call fprintf (out, "sbuf=%x, szsbuf=%d, nextch=%d\n") call pargi (HD_SBUF(hd)) call pargi (HD_SZSBUF(hd)) call pargi (HD_NEXTCH(hd)) # List of defined logical directories. if (HD_NLDIRS(hd) > 0) { call fprintf (out, "nldirs=%d:\n") call pargi (HD_NLDIRS(hd)) do i = 1, HD_NLDIRS(hd) { call fprintf (out, "%7d %s\n") call pargi (i) call pargstr (Memc[sbuf+HD_LDIR(hd,i)]) } } # List the modules. if (HD_NMODULES(hd) > 0) { call fprintf (out, "modules:\n") do i = 1, HD_NMODULES(hd) { mp = HD_MODULE(hd,i) call fprintf (out, "%7d %20s\n") call pargi (i) call pargstr (Memc[sbuf+M_NAME(mp)]) if (M_HLP(mp) > 0) { call fprintf (out, "\t\t\t\tHLP=%d(%s)\n") call pargi (M_HLP(mp)) call pargstr (Memc[sbuf+M_HLP(mp)]) } if (M_SYS(mp) > 0) { call fprintf (out, "\t\t\t\tSYS=%d(%s)\n") call pargi (M_SYS(mp)) call pargstr (Memc[sbuf+M_SYS(mp)]) } if (M_SRC(mp) > 0) { call fprintf (out, "\t\t\t\tSRC=%d(%s)\n") call pargi (M_SRC(mp)) call pargstr (Memc[sbuf+M_SRC(mp)]) } if (M_PKG(mp) > 0) { call fprintf (out, "\t\t\t\tPKG=%d(%s)\n") call pargi (M_PKG(mp)) call pargstr (Memc[sbuf+M_PKG(mp)]) } if (M_MEN(mp) > 0) { call fprintf (out, "\t\t\t\tMEN=%d(%s)\n") call pargi (M_MEN(mp)) call pargstr (Memc[sbuf+M_MEN(mp)]) } } } end