home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / flistfrontend / src / dirent.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-16  |  33.0 KB  |  1,234 lines

  1. #ifndef NO_IDENT
  2. static char *Id = "$Id: dirent.c,v 1.20 1995/11/16 22:18:29 tom Exp $";
  3. #endif
  4.  
  5. /*
  6.  * Title:    dirent.c
  7.  * Author:    T.E.Dickey
  8.  * Created:    30 Apr 1984
  9.  * Last update:
  10.  *        15 Nov 1995, latch width for filesize, allocated
  11.  *        25 Oct 1995, workaround for DEC-C bug in sprintf.
  12.  *        23 Oct 1995, added 'u(ser)' column
  13.  *        21 Oct 1995, /dlong repeated shows time in seconds.
  14.  *        28 May 1995, prototypes
  15.  *        18 Feb 1995, port to AXP (DATENT mods)
  16.  *        05 Dec 1989, corrected typeof(llast)
  17.  *        04 Nov 1988, took masking from group/member code displays
  18.  *                 (should work out maximum field length).
  19.  *                 Added code to support expiration-date display.
  20.  *        16 Aug 1988, corrected 'dirent_conv()', which permitted a
  21.  *                 buffer-overflow on terminals wider than 132!
  22.  *        09 Sep 1985, corrected collating-order: VMS apparently sorts
  23.  *                 up-to-and-including trailing '.' in filenames.
  24.  *        03 Sep 1985, added 'update' option to 'dirent_all' and to
  25.  *                 'dirent__read'.  Broke 'dirent__one' into chop-
  26.  *                 and look-parts for this purpose.
  27.  *        24 Aug 1985, use 'dirent_width' to reset entire-list too.
  28.  *        20 Aug 1985, broke out 'dirent_width'.
  29.  *        30 Jul 1985, 'numreads' (pruning only after first SYS$SEARCH)
  30.  *                 is obsolete with muti-level heap.
  31.  *        20 Jul 1985, added 'dirent_chk3', 'dirent_nul'.
  32.  *        14 Jul 1985, added calls on 'dirdata' module.
  33.  *        05 Jul 1985, moved 'dirpath_init' to main-loop.
  34.  *        04 Jul 1985, if file-not-found in 'dirent_old_any', do not
  35.  *                 assume NAM-block points to current parsed (expanded)
  36.  *                 string: it may contain the previous esa/rsa.
  37.  *        02 Jul 1985, cleanup of 'dirent.h' definitions
  38.  *        23 Jun 1985, cleanup of 'dirent_look', return status in 'dirent_chop'
  39.  *                 Use 'scanver' to (try to) bypass CC2.0 bug.  Got
  40.  *                 rid of unnecessary 'strlen' calls in 'dirent_chop'.
  41.  *        13 Jun 1985, added /AFTER and /BEFORE options (dateflag)
  42.  *        18 May 1985, added 'pcolumns'
  43.  *        26 Mar 1985, tuned column-widths for file-id, file-size
  44.  *        22 Mar 1985, added file-id, record-length conversions.
  45.  *        21 Mar 1985, added very-short date format.
  46.  *        08 Mar 1985, added 'dirent_isdir'.
  47.  *        03 Mar 1985, move flcols-init to main so we can treat this as
  48.  *                 a pure hierarchy.  Restrict 'dirent_col' so that
  49.  *                 we have a command field even if the name fills the
  50.  *                 line by itself (still no attribute columns).
  51.  *        01 Feb 1985, check for "*" device in 'dirent_glue' for 'dirfind'.
  52.  *                 and "*" version, as well as in 'dirent_chop' (to
  53.  *                 support read-list pruning 'dirread').
  54.  *        30 Dec 1984, deselect file if it is deleted.
  55.  *        24 Dec 1984, added 'D_mode' to drive day-of-week display.
  56.  *        22 Dec 1984, added ALLOC column-type.
  57.  *        21 Dec 1984, use 'flist_sysmsg'.
  58.  *        20 Dec 1984, decode file-org, format from single byte, driving
  59.  *                 'ccolumns' if non-sequential file.
  60.  *        14 Dec 1984, use single FAB for parsing, search, open where
  61.  *                 possible, to reduce paging.  Re-coded 'dirent_chop'
  62.  *                 to use existing NAM-block, if available.  Use global
  63.  *                 variable 'znam' in local routines 'dirent__one' and
  64.  *                 'dirent__read'.
  65.  *        12 Dec 1984, don't show error in 'dirent_chk'.
  66.  *        10 Dec 1984, use 'acplook', 'rmslook' for directory-read.
  67.  *                 Added 'nam' argument to 'dirent__read' for compat
  68.  *                 with new 'dirent__one()'.  Moved 'syshour' to
  69.  *                 'dirsrt'.
  70.  *        06 Dec 1984, use 'nameheap' to store filename, filetype.  Do
  71.  *                 better overflow check in 'dirent_conv'.  Added
  72.  *                 'p' (PATH) to conv_list.  Interlock 'dds_tell'
  73.  *                 call with 'flread'.
  74.  *        17 Nov 1984, added record-format stuff
  75.  *        15 Oct 1984, locked-files should have high-date, if unknown
  76.  *        10 Sep 1984, use "rmsinit_???", fixed "?locked", blocksize
  77.  *        03 Sep 1984, use "import"
  78.  *        27 Aug 1984, cleanup buffer sizes
  79.  *        15 Aug 1984, added 'dirent_dft()', multi-date support
  80.  *        14 Aug 1984, added 'ccolumns[]', 'dirent_col()'
  81.  *        06 Aug 1984, added '.fhour' component to FILENT
  82.  *        02 Aug 1984, broke out 'sysasctim' procedure
  83.  *        30 Jul 1984, added 'dirent_dlet' entry.
  84.  *        28 Jul 1984, correction to same-file login
  85.  *        27 Jul 1984, broke out pathlist/readlist logic to 'dirpath'.
  86.  *        26 Jul 1984, added 'dirent_chk2' code, 'readlist'
  87.  *        20 Jul 1984, added 'numreads' logic
  88.  *        17 Jul 1984, added V_opt/duplicate pruning to 'dirent__read'.
  89.  *        15 Jul 1984, make sort-keys for 'pathlist'
  90.  *        14 Jul 1984, use 'dclopt' to process options, added OWNER
  91.  *        10 Jul 1984, added warning-option to 'dirent_old'
  92.  *        04 Jul 1984, must clear temp in 'dirent_old' before compare
  93.  *        03 Jul 1984, broke out 'dirent_acc' as 'cmpprot.c', cleaned up.
  94.  *        02 Jul 1984, broke out 'dirent_path' as 'pathdown.c'
  95.  *        28 May 1984
  96.  *
  97.  * Function:    This module performs wildcard directory lookup using the
  98.  *        argc/argv argument list passed to the main program of "FLIST".
  99.  *        It processes options which permit "FLIST" to display less than
  100.  *        the full directory listing (for speed).
  101.  *
  102.  * Entry:    dirent:        Read directory, given argument list
  103.  *        dirent_acc:    Test a FILENT-block for needed access-mode
  104.  *        dirent_add:    increment 'numfiles', reallocate 'filelist[]'
  105.  *        dirent_all:    Load a series of files to 'filelist[]'
  106.  *        dirent_ccol:    Return crt command-column start
  107.  *        dirent_chk:    re-read a file-spec to update FILENT-block
  108.  *        dirent_chk2:    re-read a file-spec to update 'filelist[]'
  109.  *        dirent_chk3:    read a file-spec toupdate (?) 'filelist[]'
  110.  *        dirent_chop:    parse a file-spec to load FILENT-block
  111.  *        dirent_conv:    Convert a FILENT-block to CRT_COLS (max) string
  112.  *        dirent_dft:    Return wildcard default, based on V_opt
  113.  *        dirent_dlet:    Mark a 'filelist[]' entry deleted.
  114.  *        dirent_glue:    form a complete file-spec from FILENT block
  115.  *        dirent_glue2:    form a (short?) file-spec from FILENT block
  116.  *        dirent_init:    Initialize this module
  117.  *        dirent_isdir:    Test FILENT to see if it is a directory.
  118.  *        dirent_look:    lookup a single file-spec, returns full name
  119.  *        dirent_misc:    Set/clear bits in the '.fmisc' component
  120.  *        dirent_nul:    Create a null-entry in 'filelink'.
  121.  *        dirent_old:    find a filespec in 'filelist[]'
  122.  *        dirent_old1:    Lookup any file, returning FILENT-block
  123.  *        dirent_old_any:    Generalized lookup to 'filelist[]'
  124.  *        dirent_width:    latch maximum column-widths for new entry
  125.  *        dirent__cnv:    Convert numbers for display
  126.  *        dirent__date:    Select date-field
  127.  *        dirent__datechek:Test date-selection
  128.  *        dirent__look:    read a FILENT-block w/o SYS$SEARCH
  129.  *        dirent__one:    read a FILENT-block for all file-info
  130.  *        dirent__read:    read a FILENT-block, pruning if duplicates
  131.  */
  132.  
  133. #include    <starlet.h>
  134. #include    <lib$routines.h>
  135. #include    <stdlib.h>
  136. #include    <stdio.h>
  137. #include    <string.h>
  138. #include    <rms.h>
  139. #include    <descrip.h>
  140. #include    <stsdef.h>
  141.  
  142. #include    "flist.h"
  143. #define        DIRENT        /* local */
  144. #include    "dirent.h"
  145. #include    "dircmd.h"
  146. #include    "dirdata.h"
  147. #include    "dirpath.h"
  148. #include    "dirread.h"
  149. #include    "dclarg.h"
  150. #include    "dds.h"
  151.  
  152. #include    "getprot.h"
  153. #include    "rmsinit.h"
  154. #include    "nameheap.h"
  155. #include    "scanver.h"
  156.  
  157. #include    "strutils.h"
  158. #include    "sysutils.h"
  159.  
  160. #define    MAXFILES    (4096 / sizeof(filelist[0]))
  161.  
  162. #define    LATCH(i,f)    if ((f) > ccolumns[i])    ccolumns[i] = latch = len
  163.  
  164. static    void    dirent__cnv2 (int no_priv, char *c_, unsigned number);
  165. static    DATENT*    dirent__date (FILENT *z, int opt);
  166. static    int    dirent__look (FILENT *z, char *filespec);
  167. static    int    dirent__one (FILENT *z, char *filespec);
  168. static    int    dirent__read (char *filespec, FLINK **flink, int update);
  169. static    void    dirent_init (void);
  170.  
  171. /*
  172.  * Public data:
  173.  */
  174. import(filelist); import(numfiles); import(numdlets);
  175.  
  176. import(AnyXAB);
  177. import(A_opt);    import(D_opt);    import(M_opt);
  178. import(O_opt);    import(P_opt);    import(V_opt);
  179.  
  180. import(D_mode);
  181.  
  182. import(ccolumns);
  183. import(pcolumns);
  184. import(conv_list);
  185. import(maxfiles);
  186. import(namelist);
  187.  
  188. import(dateflag);
  189. import(datechek);
  190.  
  191.     int    prot_col;        /* (used by protection-edit)    */
  192.  
  193. /*
  194.  * Data structures used in directory lookup:
  195.  */
  196. static    struct    FAB    zfab;
  197. static    struct    NAM    znam;        /* used in wildcard parsing    */
  198.  
  199. static
  200. char    zrsa[NAM$C_MAXRSS],        /* resultant string area    */
  201.     zesa[NAM$C_MAXRSS];        /* expanded string area        */
  202.  
  203. /*
  204.  * Data structures used to obtain info on particular file:
  205.  */
  206. static    struct    XABALL    xaball;        /* Allocation data        */
  207. static    struct    XABDAT    xabdat;        /* Date and time        */
  208. static    struct    XABFHC    xabfhc;        /* File header characteristics    */
  209. static    struct    XABPRO    xabpro;        /* Protection            */
  210.  
  211. /*
  212.  * The RMS copy of the file-organization is simply masked from the ACP copy.
  213.  * Exploit this to make a mask for the format portion:
  214.  */
  215. #define    FAB$M_RFM    ((1 << FAB$V_ORG)-1)
  216.  
  217. static
  218. char    *known_org[] = {"SEQ", "REL", "IDX", "HSH"},
  219.     *known_rfm[] = {" ", "FIX", "VAR", "VFC", "STM", "STMLF", "STMCR"},
  220.     *known_rat[] = {"FTN", "CR", "PRN", "BLK"},
  221.     *day_of_week[] = {"Wed ", "Thu ", "Fri ", "Sat ", "Sun ", "Mon ", "Tue "};
  222. static
  223. int    absolute_day;    /* Current day-number, for short-date computation */
  224.  
  225. /* <dirent>:
  226.  * Main routine, read a list of filespecs into 'filelist[]'.
  227.  */
  228. int    dirent (DCLARG *arg_)
  229. {
  230.     int    j, didsome = 0;
  231.     char    c, *c_;
  232.  
  233.     dirread_init ();
  234.  
  235.     numfiles =        /* (no files in list yet)    */
  236.     numdlets = 0;        /* (of course, none deleted yet)*/
  237.  
  238.     dirent_width ((FILENT *)0);    /* Set nominal widths for entries*/
  239.  
  240.     filelist = calloc(maxfiles = MAXFILES, sizeof(filelist[0]));
  241.  
  242.     dirent_init();
  243.     for (; arg_; arg_ = arg_->dcl_next)
  244.     {
  245.         c_ = arg_->dcl_text;
  246.         if (!isopt(*c_))
  247.         {
  248.             dirent_all(c_, FALSE);
  249.             didsome++;
  250.         }
  251.     }
  252.     if (!(didsome || numfiles))
  253.         dirent_all ("*", FALSE);
  254.  
  255.     return (numfiles);
  256. }
  257.  
  258. /* <dirent_acc>:
  259.  * Test a file-entry to see if the user may access the file:
  260.  */
  261. int    dirent_acc (
  262.     FILENT    *z,        /* file to test            */
  263.     char    *mode)        /* List of access rights needed    */
  264. {
  265.     return (cmpprot (&z->f_getprot, mode));
  266. }
  267.  
  268. /* <dirent_add>:
  269.  * Whenever the amount of space reserved for 'filelist[]' is filled, reallocate
  270.  * to obtain reserve.
  271.  */
  272. int    dirent_add (void)
  273. {
  274.     if (++numfiles >= maxfiles)
  275.     {
  276.         maxfiles += MAXFILES;
  277.         filelist = realloc (filelist, maxfiles * sizeof(FILENT));
  278.     }
  279.     return (numfiles);
  280. }
  281.  
  282. /* <dirent_all>:
  283.  * Use a wildcard string to (possibly) examine all files in a directory.
  284.  *
  285.  * In the array 'readlist', maintain a list of the file-specs used
  286.  * as calls on this procedure, to support the READ command (re-read all
  287.  * specs).
  288.  */
  289. void    dirent_all(
  290.     char    *filespec,        /* specifies files to lookup    */
  291.     int    update)            /* true iff we *must* re-read    */
  292. {
  293.     unsigned status;
  294.     int    numfirst = numfiles,
  295.         newfiles = FALSE;    /* Latch for 'flread'    */
  296.     FLINK    *llast     = 0;
  297.  
  298.     /*
  299.      * Initialize file-access-block:
  300.      */
  301.     zfab.fab$l_fna = filespec;    /* given-names        */
  302.     zfab.fab$b_fns = strlen(filespec);
  303.  
  304.     if (sys$parse(&zfab) != RMS$_NORMAL)
  305.         return;
  306.  
  307.     dirread_put (&zfab);        /* Update 'readlist'    */
  308.  
  309.     /*
  310.      * MAXFILES is the number of files to access before reallocating
  311.      * the 'filelist[]' array to enlarge it.
  312.      */
  313.     for (;;)
  314.     {
  315.         dds_while(nullC);
  316.         if ((status = sys$search(&zfab)) == RMS$_NMF)
  317.             return;
  318.         else if (status == RMS$_NORMAL)
  319.         {
  320.             if (numfiles > numfirst)    newfiles = TRUE;
  321.             if (newfiles && (numfiles % 5 == 0) && numfiles)
  322.             {
  323.                 clrbeep ();
  324.                 dds_tell (nullC, -1);
  325.             }
  326.             zrsa[znam.nam$b_rsl] = EOS;
  327.             if (dirent__read (zrsa, &llast, update))
  328.             {
  329.                 if (dirent__datechek (&llast->fk))
  330.                 {
  331.                     filelist[numfiles] = llast;
  332.                     numfiles = dirent_add ();
  333.                 }
  334.             }
  335.         }
  336.         else
  337.             flist_sysmsg (status);
  338.     }
  339. }
  340.  
  341. /* <dirent_cat_n>:
  342.  */
  343. void    dirent_cat_n (char *s, FILENT *z)
  344. {
  345.     register len = strlen(z->fname);
  346.  
  347.     strncpy (s += strlen(s), z->fname, len);
  348.     s[len-NAME_DOT] = EOS;
  349. }
  350.  
  351. /* <dirent_ccol>:
  352.  * Compute the starting column number at which "visible" commands
  353.  * begin.  This is made a variable to support future (14-Aug-84) releases
  354.  * of VMS, which may use long filenames.
  355.  *
  356.  * Protect further against fools with long filenames on a narrow terminal
  357.  * by permitting the command-field to overlay the name-field if no room is
  358.  * found on the right of it.
  359.  */
  360. #define    PCOLUMNS(n) (pcolumns[n] ? min(pcolumns[n],ccolumns[n]) : ccolumns[n])
  361. int    dirent_ccol (void)
  362. {
  363.     register width = crt_width()-10,
  364.     ccol = PCOLUMNS(0) + PCOLUMNS(1) + ccolumns[2] + 6;
  365.     return (min(width, ccol));
  366. }
  367.  
  368. /* <dirent_chk>:
  369.  * Re-read a directory entry to obtain newer information about a file.
  370.  */
  371. int    dirent_chk (
  372.     FILENT    *z,        /* data block to load into    */
  373.     char    *filespec)    /* filename to check up on    */
  374. {
  375.     char    longspec[NAM$C_MAXRSS];
  376.  
  377.     if ((z->fstat = dirent_look (longspec, filespec)) == RMS$_NORMAL)
  378.     {
  379.         zrsa[znam.nam$b_rsl] = EOS;
  380.         return (dirent__one (z, zrsa));
  381.     }
  382.     else if (! zDELETED(z))
  383.     {
  384.         warn2 ("Unexpected RMS-err: '%s' -> %X", filespec, z->fstat);
  385.         flist_sysmsg (z->fstat);
  386.     }
  387.  
  388.     return (FALSE);        /* file was not found    */
  389. }
  390.  
  391. /* <dirent_chk2>:
  392.  * Re-read an entry in 'filelist[]'.  The primary intent of this is to check
  393.  * for deleted files, and to mark them as such in this code.  If the file
  394.  * still exists, TRUE is returned.  In either case, the display-line is
  395.  * refreshed.
  396.  */
  397. int    dirent_chk2 (int j)
  398. {
  399.     int    retval;
  400.     char    fullname[NAM$C_MAXRSS];
  401.  
  402.     dirent_glue (fullname, FK_(j));
  403.     if (! (retval = dirent_chk (FK_(j), fullname)))
  404.     {
  405.         if (DELETED(j))    dirent_dlet(j);
  406.     }
  407.     dds_line (j);
  408.     return (retval);
  409. }
  410.  
  411. /* <dirent_chk3>:
  412.  * Check a given filename.  If it exists, update the 'filelist[]' entry.  If
  413.  * not, mark the given 'filelist[]' entry deleted.  This is used in DELETE
  414.  * to uncover lower versions of a file which has been deleted.  Because the
  415.  * name may not necessarily be the same as that in the 'filelist[j]' entry,
  416.  * we must re-link the data-object to load altered data.
  417.  */
  418. int    dirent_chk3 (int j, char *fullname)
  419. {
  420.     FILENT    znew;        /* Don't destroy the old FILENT block    */
  421.     int    retval;
  422.  
  423.     if (retval = dirent_chk (&znew, fullname))
  424.         dds_add2 (&znew, j);
  425.     else
  426.     {
  427.         if (zDELETED((&znew)))
  428.         {
  429.             dirent_dlet(j);
  430.             dds_line (j);
  431.         }
  432.         else
  433.             dds_width (&znew, j);
  434.     }
  435.     return (retval);
  436. }
  437.  
  438. /* <dirent_chop>:
  439.  * Parse path, name, type and version for a full filename, loading it to
  440.  * the indicated FILENT entry.  The filespec is assumed to be syntactically
  441.  * correct, since it is obtained via RMS.
  442.  *
  443.  * We include the trailing '.' in the filename-string to force our sort to
  444.  * collate in the same manner as the SYS$SEARCH.
  445.  */
  446. int    dirent_chop (FILENT *z, char *filespec, struct NAM *nam_)
  447. {
  448.     struct    FAB    fab;
  449.     struct    NAM    nam;
  450.     int    len, num,
  451.         status    = RMS$_NORMAL;
  452.     char    esa    [NAM$C_MAXRSS];
  453.  
  454.     z->fvers = 0;
  455.  
  456.     if (!nam_)
  457.     {
  458.         /* Set up RMS buffers to do name-parse: */
  459.         rmsinit_fab (&fab, &nam, 0, filespec);
  460.         rmsinit_nam (&nam, 0, esa);
  461.  
  462.         if (! $VMS_STATUS_SUCCESS(status = sys$parse(&fab)))
  463.             return (status);
  464.         /* esa[] <= parsed name    */
  465.         nam_ = &nam;
  466.     }
  467.  
  468. #define    HEAP(n,l)    nameheap(nam_->n, nam_->l, (void *)&namelist)
  469.  
  470.     z->fvers = scanver (nam_->nam$l_ver, nam_->nam$b_ver);
  471.     z->ftype = HEAP(nam$l_type+1,nam$b_type-1);
  472.     z->fname = HEAP(nam$l_name,nam$b_name+NAME_DOT); /* name, with '.' */
  473.  
  474.     z->fpath_ = dirpath_sort (nam_->nam$l_node,
  475.                   len = nam_->nam$l_name - nam_->nam$l_node);
  476.     return (status);
  477. }
  478.  
  479. /* <dirent_conv>:
  480.  * Convert a FILENT entry to printing form, showing only those fields selected
  481.  * in 'conv_list[]'.
  482.  */
  483. void    dirent_conv (char *bfr, FILENT *z)
  484. {
  485.     DATENT    *date_;
  486.     int    j, num,
  487.         col0    = PCOLUMNS(0),
  488.         col1    = PCOLUMNS(1),
  489.         width    = crt_width(),
  490.         no_priv    = zNOPRIV(z);
  491.     static
  492.     char    sFMT1[]    = "%%-%d.%ds %%-%d.%ds;%%.%ds",
  493.         noDATE[] = "%-17s";
  494.  
  495.     char    tmp[CRT_COLS+256],
  496.         tempid[80],
  497.         *c_    = bfr,
  498.         *ccol_    = conv_list,
  499.         format    [sizeof(sFMT1)+20],
  500.         filesize[20],
  501.         version    [20],
  502.         misc    [30];
  503.  
  504. #define    NXT    strnull(strcat(c_, "  "))
  505. #define    AFTER_NXT (width - strlen(NXT) - 2)
  506.  
  507.     if (zDELETED(z))
  508.     {
  509.         *c_ = EOS;
  510.         return;
  511.     }
  512.  
  513.     if (z->fvers == WILD_VER)    strcpy (version, "*");
  514.     else if (z->fvers)        sprintf (version, "%d", z->fvers);
  515.     else                strcpy (version, "");
  516.  
  517.     sprintf  (format, sFMT1,
  518.         col0,col0,        /* 'fname'    */
  519.         col1,col1,        /* 'ftype'    */
  520.         ccolumns[2]+1);        /* 'fvers', pad    */
  521.     sprintf  (bfr, format, z->fname, z->ftype, version);
  522.     j = strlen(z->fname) - NAME_DOT;
  523.     if (j > col0)            bfr[col0]     = '|';
  524.     else if (j >= 0)        bfr[j]         = ' ';
  525.     if (strlen(z->ftype) > col1)    bfr[col0+col1+1] = '|';
  526.  
  527.     sprintf    (filesize, "%u", z->fsize);
  528.  
  529.     /*
  530.      * If I have any data-columns to show, pad the name out to the
  531.      * beginning of the "command-column" first.
  532.      */
  533.     if (AnyXAB)
  534.     {
  535.         num    = strlen(bfr);
  536.         c_    = bfr + num;        /* => ending-null */
  537.         while (num++ < dirent_ccol())
  538.             *c_++ = ' ';
  539.         *c_ = EOS;
  540.     }
  541.  
  542.     /*
  543.      * Check for early termination of this conversion:
  544.      */
  545.     if (zLOCKED(z))        /* file is locked, cannot open    */
  546.         strcpy (filesize, "*lock");
  547.     else if (z->fstat != RMS$_NORMAL && !no_priv)
  548.     {
  549.         if (z->fstat)        /* If zero, no data    */
  550.             sysgetmsg (z->fstat, NXT, AFTER_NXT);
  551.         return;
  552.     }
  553.     strcpy(tmp, bfr);        /* do the rest in a big-buffer    */
  554.     c_ = tmp + strlen(tmp);        /* ...to guard against overflow    */
  555.  
  556.     /*
  557.      * Format each display-column in succession, under control of the
  558.      * 'conv_list'.
  559.      */
  560.     prot_col = -1;            /* If visible, mask-col is reset */
  561.     while (*ccol_ && ((c_-tmp) < width))
  562.     {
  563.         c_ = NXT;
  564.         switch (*ccol_++)
  565.         {
  566.         case 'p':        /* PATH (node::directory) */
  567.             sprintf (format, "%%-%ds", ccolumns[3]);
  568.             sprintf (c_, format, zPATHOF(z));
  569.             break;
  570.  
  571.         case 'a':        /* ALLOC (allocation)    */
  572.             if (no_priv)
  573.                 sprintf (c_, "%-*s", ccolumns[8], " ");
  574.             else
  575.                 sprintf (c_, "%*.*u", ccolumns[8], ccolumns[8], z->fallc);
  576.             break;
  577.  
  578.         case 's':        /* SIZE (usage)        */
  579.             if (no_priv)
  580.                 sprintf (c_, "%-*s", ccolumns[7], " ");
  581.             else
  582.                 sprintf (c_, "%*.*s", ccolumns[7], ccolumns[7], filesize);
  583.             break;
  584.  
  585.         case 'l':        /* LENGTH (of record)    */
  586.             dirent__cnv2 (no_priv, c_, z->f_recl);
  587.             break;
  588.  
  589.         case 'f':        /* FORMAT (record)    */
  590.             *c_++ = (z->ftext ? '*' : ' ');
  591.             j = z->f_rfm & FAB$M_RFM;
  592.             if (j < SIZEOF(known_rfm))
  593.                 strcpy (misc, known_rfm[j]);
  594.             else
  595.                 strcpy (misc, "?");
  596.             if ((j = (z->f_rfm >> FAB$V_ORG)) > 0)
  597.             {
  598.                 strcat (misc, ",");
  599.                 if (j < SIZEOF(known_org))
  600.                     strcat (misc, known_org[j]);
  601.                 else
  602.                     strcat (misc, "?");
  603.             }
  604.             sprintf (format, "%%-%d.%ds", ccolumns[4],ccolumns[4]);
  605.             sprintf (c_, format, misc);
  606.             break;
  607.  
  608.         case 'x':        /* XAB (record attributes)    */
  609.             {
  610.                 int    bit;
  611.                 char    *d_ = misc;
  612.                 *d_ = EOS;
  613.                 for (bit = 0; bit < SIZEOF(known_rat); bit++)
  614.                     if (z->f_rat & (1 << bit))
  615.                     {
  616.                         if (d_ != misc)    *d_++ = ',';
  617.                         strcpy (d_, known_rat[bit]);
  618.                         d_ = strnull(d_);
  619.                     }
  620.                 *d_ = EOS;
  621.             }
  622.             sprintf (format, "%%-%d.%ds", ccolumns[5],ccolumns[5]);
  623.             sprintf (c_, format, misc);
  624.             break;
  625.  
  626.         case 'e':    date_    = &z->fexpr;    goto date;
  627.         case 'r':    date_    = &z->frevi;    goto date;
  628.         case 'b':    date_    = &z->fback;    goto date;
  629.         case 'c':    date_    = &z->fdate;    goto date;
  630.  
  631.         case 'd':        /* DATE "dd-mmm-yy hh:mm" */
  632.             date_ = dirent__date (z, D_opt);
  633.  
  634.         date:
  635.             if ((!no_priv)
  636.             &&  isOkDate(date_)
  637.             &&  !isBigDate(date_))
  638.             {
  639.                 if (D_mode != 0)
  640.                     lib$day (&j, date_);
  641.                 if (D_mode > 0)
  642.                 {
  643.                     strncpy (c_, day_of_week[j % 7], 4);
  644.                     c_ += 4;
  645.                 }
  646.                 sysasctim (c_, date_, D_mode <= 1 ? 18 : 21);
  647.                 if (D_mode < 0)    /* "dd-mmm hh:mm" */
  648.                 {        /* "dd-mmm-yyyy " */
  649.                     if (j+180 > absolute_day)
  650.                         strcpy (&c_[6], &c_[11]);
  651.                 }
  652.             }
  653.             else
  654.             {
  655.                 if (D_mode > 0)
  656.                 {
  657.                     strcpy (c_, "    ");
  658.                     if (D_mode > 1)
  659.                         strcat(c_, "   ");
  660.                     c_ = strnull(c_);
  661.                 }
  662.                 sprintf (c_, noDATE, " ");
  663.             }
  664.             if (D_mode < 0)    c_[12] = EOS;
  665.             break;
  666.  
  667.         case 'm':        /* MASK (protection, len=20) */
  668.             prot_col= 1 + (c_ - tmp);
  669.             num    = z->fprot;
  670.             for (j = 0; j < 4; j++)
  671.             {
  672.                 *c_++ = (num & XAB$M_NOREAD)  ? '-' : 'r';
  673.                 *c_++ = (num & XAB$M_NOWRITE) ? '-' : 'w';
  674.                 *c_++ = (num & XAB$M_NOEXE)   ? '-' : 'e';
  675.                 *c_++ = (num & XAB$M_NODEL)   ? '-' : 'd';
  676.                 *c_++ = ' ';
  677.                 num >>= 4;
  678.             }
  679.             *(--c_) = EOS;
  680.             break;
  681.  
  682.         case 'o':        /* OWNER (len=9)    */
  683.             if (no_priv)
  684.                 sprintf (c_, "%-9s", " ");
  685.             else
  686.                 sprintf (c_, "%4o,%4o",
  687.                     z->f_grp, z->f_mbm);
  688.             break;
  689.  
  690.         case 'u':        /* USER (len=18)    */
  691.             sprintf (c_, "%-*s", ccolumns[6], z->f_user ? z->f_user : "");
  692.             break;
  693.  
  694.         /* Patch: Limit this to 13 columns until either we extend the
  695.            'ccolumns' array, or find a file-id which has a nonzero
  696.            [2]-entry. */
  697.         case 'i':        /* IDENTIFIER (file-id)    */
  698.             if (no_priv)
  699.                 strcpy (misc, " ");
  700.             else
  701.                 sprintf (misc, "%d,%d,%d", z->fidnum[0],
  702.                     z->fidnum[1], z->fidnum[2]);
  703.             sprintf (c_, "%-13s", misc);
  704.         }
  705.     }
  706.     strncpy(bfr, tmp, CRT_COLS-1);
  707.     bfr[CRT_COLS-1] = '\0';
  708. }
  709.  
  710. /* <dirent_dft>:
  711.  * Return a pointer to wildcard, based on V_opt (VERSION).  If NOVERSION
  712.  * is selected, all wildcard-SYS$SEARCH activity will be coerced to seek
  713.  * only the current-version of files.  Otherwise, any version may be selected.
  714.  */
  715. char    *dirent_dft (void)
  716. {
  717.     return (V_opt ? "*.*;*" : "*.*;");
  718. }
  719.  
  720. /* <dirent_dlet>:
  721.  * Mark an entry in 'filelist[]' deleted.
  722.  */
  723. void    dirent_dlet (int j)
  724. {
  725.     FK(j).fstat = RMS$_FNF;
  726.     numdlets++;
  727.     if (j == dircmd_select(-2))    dircmd_select(-1);
  728.     dds_line (j);
  729. }
  730.  
  731. /* <dirent_glue>:
  732.  * Concatenate all substrings to make a complete file specification, for
  733.  * the indicated FILENT entry.
  734.  */
  735. char    *dirent_glue (char *filespec, FILENT *z)
  736. {
  737.     return (dirent_glue2 (filespec, z, -1));
  738. }
  739.  
  740. /* <dirent_glue2>:
  741.  * Concatenate all substrings to make a complete file specification, for
  742.  * the indicated FILENT entry.  The 'curfile' argument is provided so that
  743.  * warning messages and other similar functions can make the inclusion of
  744.  * the pathname dependent on whether it is the same as that of the current
  745.  * entry on the screen.  (The path of the current entry is also the default
  746.  * directory.)
  747.  */
  748. char    *dirent_glue2 (char *filespec, FILENT *z, int curfile)
  749. {
  750.     static    char    fmt[] =
  751. #if    NAME_DOT
  752.             "%s%s;";
  753. #else
  754.             "%s.%s;";
  755. #endif
  756.     if (curfile >= 0)
  757.     {
  758.         strcpy (filespec, (zPATHOF(z) != PATHOF(curfile))
  759.             ? zPATHOF(z) : "");
  760.     }
  761.     else
  762.         strcpy (filespec, zPATHOF(z));
  763.     if (strcmp(filespec, "*") == 0)    strcat (filespec, ":");
  764.  
  765.     sprintf (strnull(filespec), fmt, z->fname, z->ftype);
  766.     if (z->fvers == WILD_VER)
  767.         strcat (filespec, "*");
  768.     else if (z->fvers)
  769.         sprintf (strnull(filespec), "%d", z->fvers);
  770.     return (filespec);
  771. }
  772.  
  773. /* <dirent_init>:
  774.  * Initialize data structures used in this module (used only via main routine).
  775.  */
  776. static
  777. void    dirent_init(void)
  778. {
  779.     /*
  780.      * Chain pointers for XAB (the "nxt" is the same offset in all variations)
  781.      */
  782.     struct    XABALL    *first_ = 0, *last_ = 0;
  783.     int    today_date[2];
  784.  
  785.     /* dirent_all state: */
  786.  
  787.     rmsinit_fab (&zfab, &znam, dirent_dft(), 0);
  788.     rmsinit_nam (&znam, zrsa, zesa); /* Initialize name-block    */
  789.  
  790.     /* dirent__one state: */
  791.  
  792.     xaball    = cc$rms_xaball;
  793.     xabdat    = cc$rms_xabdat;
  794.     xabfhc    = cc$rms_xabfhc;
  795.     xabpro    = cc$rms_xabpro;
  796.  
  797.     /*
  798.      * Initialize extended-attribute-blocks.
  799.      *    For each file found, will retrieve the size "-a", the last
  800.      *    modification date "-d", and the protection codes "-m".
  801.      */
  802.     if (A_opt)
  803.     {
  804.         if (first_ == 0) first_           = (struct XABALL *)&xaball;
  805.         if (last_  != 0) last_->xab$l_nxt = (char *)&xaball;
  806.         xaball.xab$l_nxt = (char *)&xabfhc;
  807.         last_    = (struct XABALL *)&xabfhc;
  808.     }
  809.     if (D_opt)
  810.     {
  811.         if (first_ == 0) first_           = (struct XABALL *)&xabdat;
  812.         if (last_  != 0) last_->xab$l_nxt = (char *)&xabdat;
  813.         last_    = (struct XABALL *)&xabdat;
  814.     }
  815.     if (M_opt || O_opt)
  816.     {
  817.         if (first_ == 0) first_           = (struct XABALL *)&xabpro;
  818.         if (last_  != 0) last_->xab$l_nxt = (char *)&xabpro;
  819.         last_    = (struct XABALL *)&xabpro;
  820.     }
  821.     if (first_)
  822.     {
  823.         AnyXAB = TRUE;
  824.         zfab.fab$l_xab = (char *)first_; /* ...and => XAB chain    */
  825.     }
  826.  
  827.     pcolumns[0] = pcolumns[1] = 0;    /* Show name,type as-is    */
  828.     prot_col = -1;        /* assume no mask-column until displayed */
  829.     sys$gettim (today_date);
  830.     lib$day (&absolute_day, today_date);
  831. }
  832.  
  833. /* <dirent_isdir>:
  834.  * Test a FILENT block to see if it is a directory.  While ACP can do additional
  835.  * tests, we will assume that a ".DIR" filetype is enough indication.  Note
  836.  * that VMS insists that the version number of a directory must be "1".
  837.  */
  838. int    dirent_isdir (FILENT *z)
  839. {
  840.     return ((strcmp ("DIR", z->ftype) == 0) && (z->fvers <= 1));
  841. }
  842.  
  843. /* <dirent_look>:
  844.  * Lookup a single file, returning (via pointer) its fully-resolved name,
  845.  * and the lookup status.
  846.  */
  847. unsigned dirent_look (char *longspec, char *filespec)
  848. {
  849.     size_t    len;
  850.     unsigned status;
  851.  
  852.     zfab.fab$l_fna = filespec;
  853.     zfab.fab$b_fns = strlen(filespec);
  854.  
  855.     if ($VMS_STATUS_SUCCESS(status = sys$parse(&zfab)))
  856.     {
  857.         if ($VMS_STATUS_SUCCESS(status = sys$search(&zfab)))
  858.         {
  859.             strncpy (longspec, zrsa, len = znam.nam$b_rsl);
  860.             longspec[len] = EOS;
  861.         }
  862.     }
  863.     return (status);
  864. }
  865.  
  866. /* <dirent_misc>:
  867.  * Set/clear bits in the '.fmisc' component.  This is used to keep track of
  868.  * wildcard searches (limited to entries in 'filelist[]').  By flagging
  869.  * entries, we need examine each once, making it faster, and (in the case of
  870.  * DELETE) more precise.
  871.  *
  872.  * Patch: This doesn't clear-specific, or do testing.
  873.  */
  874. void    dirent_misc (
  875.     int    inx,        /* index into 'filelist[]', or negative    */
  876.     int    bitv)        /* value with 1's where setting is needed*/
  877. {
  878.     int    j;
  879.  
  880.     if (inx >= 0)    /* If legal index, set specific bit    */
  881.     {
  882.         FK(inx).fmisc |= bitv;
  883.     }
  884.     else            /* Do a global change        */
  885.     {
  886.         if (inx == -1)    /* Clear this bit everywhere ?    */
  887.         {
  888.             for (j = 0; j < numfiles; j++)
  889.                 FK(j).fmisc &= (~bitv);
  890.         }
  891.         else        /* Set this bit everywhere    */
  892.         {
  893.             for (j = 0; j < numfiles; j++)
  894.                 FK(j).fmisc |= bitv;
  895.         }
  896.     }
  897. }
  898.  
  899. /* <dirent_nul>:
  900.  * Create a dummy entry in 'filelink' so that we can mark an entry in 'filelist'
  901.  * deleted without clobbering our database entry for the file which used to be
  902.  * there.  For instance, we make this when we do a VERIFY and purge lots of
  903.  * renamed entries from 'filelist[]'.
  904.  */
  905. void    dirent_nul (int inx)
  906. {
  907.     FILENT    ztmp = FK(inx);        /* make sure path-name is legal    */
  908.     ztmp.fname = ztmp.ftype = "";    /* dummy-out the name, type    */
  909.     ztmp.fvers = 0;            /* ...and version        */
  910.     ztmp.fstat = RMS$_FNF;        /* Show a DELETED() code    */
  911.     dirdata_one (&ztmp, &filelist[inx]);
  912.     numdlets++;
  913.     dds_line(inx);
  914. }
  915.  
  916. /* <dirent_old>:
  917.  * Given a filespec, convert it to a FILENT structure, and attempt to find
  918.  * it in the current 'filelist[]' array.  If found, return its index.  Else,
  919.  * return a "-1" or "-2".
  920.  */
  921. int    dirent_old (
  922.     char    *filespec,
  923.     int    need)    /* 0: no warn, 1: any file, 2: in filelist[]    */
  924. {
  925.     FILENT    ztmp;
  926.  
  927.     return (dirent_old_any (&ztmp, filespec, need));
  928. }
  929.  
  930. /* <dirent_old1>:
  931.  * Lookup filename, returning TRUE if there is any file available by that name.
  932.  * If not, flag a warning message.  If the lookup is successful, will return
  933.  * the FILENT entry in the output argument.
  934.  */
  935. int    dirent_old1 (FILENT *z, char *spec)
  936. {
  937.     int    inx    = dirent_old_any (z, spec, 1);
  938.  
  939.     return (! didwarn());
  940. }
  941.  
  942. /* <dirent_old_any>:
  943.  * Given a filespec, convert it to a FILENT structure, and attempt to find
  944.  * it in the current 'filelist[]' array.  If found, return its index.  Else,
  945.  * return a "-1" or "-2".  If the file exists, the FILENT structure '*z' is
  946.  * loaded with the data.
  947.  */
  948. int    dirent_old_any (
  949.     FILENT    *z,        /* => buffer to load with file data    */
  950.     char    *filespec,    /* => file specification to lookup    */
  951.     int    need)        /* 0: no warn, 1: any file, 2: in filelist[] */
  952. {
  953.     char    longspec[NAM$C_MAXRSS];
  954.  
  955.     if (dirent_look (longspec, filespec) == RMS$_FNF)
  956.         dirent_chop (z, filespec, (struct NAM *)0);
  957.     else if (dirent__one (z, longspec))
  958.     {
  959.         int    j;
  960.         for (j = 0; j < numfiles; j++)
  961.         {
  962.             if (DELETED(j))                continue;
  963.             if (memcmp (z, FK_(j), FILENT_name_size) == 0)
  964.                 return (j);
  965.         }
  966.         if (need >= 2)
  967.             warn ("File '%s' is not a member of file-list", filespec);
  968.         return (-1);
  969.     }
  970.  
  971.     if (need >= 1)
  972.     {
  973.         if (zDELETED(z))
  974.             warn ("File not found: %s", filespec);
  975.         else
  976.             warn ("File isn't readable: %s", filespec);
  977.     }
  978.     return (-2);
  979. }
  980.  
  981. static
  982. int    digits_of (unsigned value)
  983. {
  984.     static    unsigned tens[20] = { 1 };
  985.     int    count;
  986.  
  987.     for (count = 1; count < sizeof(tens)/sizeof(tens[0]); count++)
  988.     {
  989.         if (tens[count] == 0)
  990.             tens[count] = 10 * tens[count-1];
  991.         if (value < tens[count]
  992.          || tens[count] <= tens[count-1])
  993.             break;
  994.     }
  995.     return count;
  996. }
  997.  
  998. /* <dirent_width>:
  999.  * Latch the widest field of each type which is actually used in the display
  1000.  * list.  We call this from points at which we are actually loading a FILENT
  1001.  * block to the display list; other points may read a FILENT block which will
  1002.  * be included in the 'dirdata' list, but not displayed.
  1003.  */
  1004. int    dirent_width (FILENT *z)
  1005. {
  1006.     register int    len, num, latch;
  1007.  
  1008.     if (z)
  1009.     {
  1010.         LATCH(0,len = strlen(z->fname)-1);
  1011.         LATCH(1,len = strlen(z->ftype));
  1012.         LATCH(2,len = digits_of(z->fvers));
  1013.         LATCH(3,len = strlen(zPATHOF(z)));
  1014.  
  1015.         /*
  1016.          * Latch the widest FORMAT description, so that we can do this
  1017.          * in minimum width:
  1018.          */
  1019.         num = z->f_rfm & FAB$M_RFM;
  1020.         if (num >= SIZEOF(known_rfm))    len = 1;
  1021.         else                len = strlen(known_rfm[num]);
  1022.         if (z->f_rfm > FAB$M_RFM)    len += 4;
  1023.         LATCH(4,len);
  1024.  
  1025.         len = 0;
  1026.         for (num = 1; num < (1 << SIZEOF(known_rat)); num <<= 1)
  1027.             if (num & z->f_rat)
  1028.             {
  1029.                 if (len)    len++;
  1030.                 len += 3;
  1031.             }
  1032.         LATCH(5,len);
  1033.  
  1034.         LATCH(6,len = strlen(z->f_user));
  1035.         LATCH(7,len = digits_of(z->fsize));
  1036.         LATCH(8,len = digits_of(z->fallc));
  1037.     }
  1038.     else        /* Reset entire list    */
  1039.     {
  1040.         ccolumns[0] = 9;    /* filename    */
  1041.         ccolumns[1] = 3;    /* filetype    */
  1042.         ccolumns[2] = 4;    /* version    */
  1043.         ccolumns[3] = 1;    /* pathname    */
  1044.         ccolumns[4] = 3;    /* format + (organization)    */
  1045.         ccolumns[5] = 1;    /* attributes    */
  1046.         ccolumns[6] = 8;    /* user-identifier */
  1047.         ccolumns[7] = 5;    /* filesize (blocks) */
  1048.         ccolumns[8] = 5;    /* allocation (blocks) */
  1049.         for (num = 0; num < numfiles; num++)
  1050.             if (!DELETED(num))
  1051.                 latch |= dirent_width (FK_(num));
  1052.     }
  1053.     return (latch);
  1054. }
  1055.  
  1056. /* <dirent__cnv2>:
  1057.  * Convert (for 'dirent_conv') 16-bit numbers.
  1058.  */
  1059. static
  1060. void    dirent__cnv2 (int no_priv, char *c_, unsigned number)
  1061. {
  1062.     if (no_priv)
  1063.         sprintf (c_, "%-5s", " ");
  1064.     else
  1065.         sprintf (c_, "%5u", number);
  1066. }
  1067.  
  1068. /* <dirent__date>:
  1069.  * Given a date-selection option (/DC, /DB, /DR) return the appropriate
  1070.  * pointer to a date-field.  Note that this is used to control the
  1071.  * interpretation of the /AFTER and /BEFORE options.
  1072.  */
  1073. static
  1074. DATENT    *dirent__date (FILENT *z, int opt)
  1075. {
  1076.     register DATENT    *date_;
  1077.  
  1078.     if (opt == 4)        date_    = &z->fexpr;
  1079.     else if (opt == 3)    date_    = &z->frevi;
  1080.     else if (opt == 2)    date_    = &z->fback;
  1081.     else            date_    = &z->fdate;
  1082.     return (date_);
  1083. }
  1084.  
  1085. /* <dirent__datechek>:
  1086.  * Test a FILENT structure to see if it is permitted by the current date-
  1087.  * selection option (/AFTER or /BEFORE).
  1088.  */
  1089. int    dirent__datechek (FILENT *z)
  1090. {
  1091.     register int ok = TRUE;
  1092.  
  1093.     if (dateflag[0])
  1094.     {
  1095.     register
  1096.     DATENT    *date_    = dirent__date (z, dateflag[1]);
  1097. #ifdef __alpha
  1098.     int    gt    = *date_ > datechek;
  1099. #else
  1100.     register
  1101.     int    gt    =   (date_->date64[1]  > datechek.date64[1])
  1102.             || ((date_->date64[0]  > datechek.date64[0]) &&
  1103.                 (date_->date64[1] == datechek.date64[1]));
  1104. #endif
  1105.         ok = (dateflag[0] > 0) ? gt : ! gt;
  1106.     }
  1107.     return (ok);
  1108. }
  1109.  
  1110. /* <dirent__look>:
  1111.  * Lookup information about a particular file, to fill all fields in the
  1112.  * indicated FILENT structure, except those set in 'dirent_chop'.  Returns
  1113.  * TRUE iff the file was found.
  1114.  *
  1115.  * To make the RMS-lookup go faster, we assume that the global 'znam' block
  1116.  * is set from the SYS$SEARCH for this file.
  1117.  */
  1118. static
  1119. int    dirent__look (
  1120.     FILENT    *z,            /* => Data block to load    */
  1121.     char    *filespec)        /* Full, unique filename    */
  1122. {
  1123.     unsigned status    = RMS$_NORMAL;        /* return-status    */
  1124.     int    ok    = TRUE;
  1125.     char    temp[80];
  1126.  
  1127.     /*
  1128.      * Fill the FILENT block with the name, and null-data:
  1129.      */
  1130.     memset (((char *)z) + FILENT_name_size, 0,
  1131.         sizeof(FILENT) - FILENT_name_size);
  1132.     z->f_grp = z->f_mbm = z->fprot = -1;
  1133.  
  1134.     /*
  1135.      * ACP is generally faster than RMS (less overhead), but cannot
  1136.      * (under VMS 3.7) go across DECNET.
  1137.      */
  1138.     if (znam.nam$l_fnb & NAM$M_NODE)
  1139.         status = rmslook (z, &zfab);
  1140.     else
  1141.         status = acplook (z, filespec, &znam);
  1142.  
  1143.     if (!P_opt && zNOPRIV(z))    ok = FALSE;
  1144.  
  1145.     vms_uid2s(temp, z->f_mbm, z->f_grp);
  1146.     if (!strcmp(temp, "[]"))
  1147.         temp[0] = '\0';
  1148.     /* FIXME: memory leak */
  1149.     z->f_user = strcpy(malloc(strlen(temp)+1), temp);
  1150.  
  1151.     return (ok);
  1152. }
  1153.  
  1154. /* <dirent__one>:
  1155.  * Lookup information about a particular file, to fill all fields in the
  1156.  * indicated FILENT structure.  Returns TRUE iff the file was found.
  1157.  * In all cases, the filename is parsed.
  1158.  */
  1159. static
  1160. int    dirent__one (
  1161.     FILENT    *z,            /* => Data block to load    */
  1162.     char    *filespec)        /* Full, unique filename    */
  1163. {
  1164.     dirent_chop (z, filespec, &znam);
  1165.     return (dirent__look(z, filespec));
  1166. }
  1167.  
  1168. /* <dirent__read>:
  1169.  * Lookup information about a particular file, to fill all fields in the
  1170.  * indicated FILENT structure.  Returns TRUE iff:
  1171.  *    (a) the file was found,
  1172.  *    (b) the file did not appear elsewhere in the list, and
  1173.  *    (c) the file does not conflict with /NOVERSION
  1174.  * If the name (counting version) is an exact duplicate, load the newer file
  1175.  * data, to support re-reads.
  1176.  */
  1177. static
  1178. int    dirent__read (
  1179.     char    *filespec,
  1180.     FLINK    **flink,
  1181.     int    update)        /* true if we *must* re-read directory    */
  1182. {
  1183.     FILENT    filent;
  1184.     int    j,
  1185.         ok    = TRUE;
  1186.  
  1187.     if (update)
  1188.     {
  1189.         if (! dirent__one (&filent, filespec))    return (FALSE);
  1190.     }
  1191.     else        /* Look in database before the physical directory */
  1192.     {
  1193.         dirent_chop (&filent, filespec, &znam);
  1194.         if ((j = dirdata_find (&filent, flink)) <= 0)
  1195.         {
  1196.             if (! dirent__look (&filent, filespec))
  1197.                 return (FALSE);
  1198.         }
  1199.         else
  1200.             filent    = (*flink)->fk;
  1201.     }
  1202.  
  1203.     if (! V_opt)    /* Suppress if a higher version is in 'filelist[]' */
  1204.     {
  1205.         if (update) j = dirdata_find (&filent, flink);
  1206.         /* Position the link-pointer if we have not already */
  1207.         if (dirdata_high (&filent, flink))
  1208.             ok = FALSE;
  1209.         else
  1210.         {
  1211.             dirdata_chg (&filent, flink, j);
  1212.             dirent_width (&filent);
  1213.         }
  1214.     }
  1215.     else        /* Create/replace (if duplicate) database entry */
  1216.     {
  1217.         dirdata_add (&filent, flink);    /* Get data's list-pointer */
  1218.         dirent_width (&filent);
  1219.     }
  1220.  
  1221.     if (ok)
  1222.     {
  1223.         for (j = 0; j < numfiles; j++)
  1224.         {
  1225.             if (filelist[j] == *flink)
  1226.             {
  1227.                 ok = FALSE;
  1228.                 break;
  1229.             }
  1230.         }
  1231.     }
  1232.     return (ok);
  1233. }
  1234.