home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / dirutl / di11.arc / DISETUP.C < prev   
C/C++ Source or Header  |  1987-12-03  |  32KB  |  784 lines

  1. #include <stdio.h>
  2. #include <dir.h>
  3. #include <dos.h>
  4. #include <ctype.h>
  5. #include <string.h>
  6. #include <stdlib.h>
  7. #include "di.h"
  8. char *Tokenize(char S[],char SepChar);
  9. char *NextMatchFile(char FileSpec[],int Attributes);
  10. NameNode *LastElem(NameNode *List);
  11. NameNode *GetMatchFiles(char *Path,int Attribs);
  12. NameNode *ExpandPath(char Path[]);
  13. NameNode *CreateNameList(void);
  14. NameNode *CatNameLists(NameNode *Target,NameNode *Source);
  15. void AddFileWC(char File[]);
  16. void AddNode(NameNode *Node);
  17. void InsertList(NameNode *Target,NameNode *Source);
  18. void AddCDirs(NameNode *List);
  19. void AddAllWC(char Path[]);
  20. void FirstWild(char Path[],char *Head,char *WildSpec,char *Tail);
  21. void AddString(NameNode *List,char *S);
  22. void DeleteNext(NameNode *List);
  23. void FreeList(NameNode *List);
  24. void MakePathsFull(NameNode **List);
  25. void AddFlag(char *Flag,int *Attribute,ListType *ListSpec);
  26. FlagErr(int ErrWord);
  27. GetFullPath(char *Path);
  28. NullPath(char *Path);
  29. IsRoot(char *Dir);
  30. GetFullDir(char *Path);
  31. IsDir(char *Path);
  32. FindInt(int Arr[],int Targ);
  33.  
  34. void GetArgs(int argc,char *argv[],int Attribs[],ListType *ListSpec,
  35.  NameNode **Files)
  36. /* Converts an argument list to an array of search attribs (Attribs),
  37.    a list of filespecs (Files), and a set of listing type specifications
  38.    (ListSpec).    If any attrib flags are given, the previous values in
  39.    Attribs are erased.    If any filespecs are given, the previous Files
  40.    list is replaced & freed. */
  41. {
  42. int I,
  43. Attrib,     /* Search attributes given by one argument */
  44. NumAtts,    /* Number of attribute groups found */
  45. FileSpecGiven,    /* Whether any argument was a filespec */
  46. OldNum;     /* Number of attributes previously given in array */
  47. NameNode *PathList,    /* List of paths produced by expanding the wildcards
  48.                in a filespec arg */
  49. *List,        /* List of paths produced from filespec args */
  50. *CurFile;    /* Current end of the path list */
  51.  
  52.    List = CurFile = CreateNameList();    /* Initially set List and CurFile to
  53.                        point to an empty node */
  54.    OldNum = FindInt(Attribs,NO_AT);    /* Find number of attribs previously
  55.                        in Attribs */
  56.    NumAtts = 0;     /* Initialize current number of attribs to 0 */
  57.    FileSpecGiven = FALSE;
  58.    for (I = 1; I < argc; I++) {     /* For each argument */
  59.       if (strchr(FLAGSPECS,argv[I][0]) != NULL) { /* If arg is a flag group */
  60.      AddFlag(argv[I],&Attrib,ListSpec);    /* Interpret flag
  61.                         group and set variables */
  62.      if (Attrib != NO_AT) {     /* If group contained attributes */
  63.         ExTest(NumAtts == MAX_ATTS - 1,"Too many attribute groups.");
  64.         Attribs[NumAtts++] = Attrib; /* Add attrib and-term to Attribs */
  65.      }
  66.       }
  67.       else {
  68.      FileSpecGiven = TRUE;
  69.      PathList = ExpandPath(argv[I]);    /* Expand wildcards in arg */
  70.      if (PathList == NULL)
  71.         fprintf(stderr,"Invalid directory in path \"%s\".\n",argv[I]);
  72.      else
  73.         CurFile = CatNameLists(CurFile,PathList);    /* Add all paths
  74.             indicated by the arg to the total paths list */
  75.       }
  76.    }
  77.    CurFile = List;        /* Get rid of initial empty node */
  78.    List = List -> Next;
  79.    free(CurFile);
  80.    if (FileSpecGiven) {     /* If filespecs found, replace original path
  81.                    list with new list */
  82.       FreeList(*Files);
  83.       *Files = List;
  84.    }
  85.    if (ListSpec -> RecSearch)    /* If recursive search flag was given, find
  86.     all subdirs of the lowest-level dir given in each path and add them to
  87.     the list, with each added path having the file name of the path it was
  88.     derived from */
  89.       AddCDirs(*Files);
  90.    if (NumAtts == 0)    /* If no attrib flags were given, attrib array is
  91.                unchanged; set num of attribs to original num */
  92.       NumAtts = OldNum;
  93.    Attribs[NumAtts] = NO_AT;    /* Mark end of attrib array */
  94. }
  95.  
  96. void AddFlag(char *Flag,int *Attribute,ListType *ListSpec)
  97. /* Sets Attribute, ListSpec, and More() according to the flags in
  98.    Flag.  If Flag contains any attribute flags, they are combined to produce
  99.    attribute and-term Attribute.  Lower case attrib flags are interpreted
  100.    as 'true' (a file must have that attribute to match) and the corresponding
  101.    bit (as indicated by the FA_* constants) in the low byte of Attribute is
  102.    set.  Upper case flags are interpreted as 'false' (a file can not have
  103.    that attrib to match) and the corresponding bit in the high byte of
  104.    Attribute is set.  The 'e' flag (everything) sets Attribute to 0. An error
  105.    message is printed if both a true and a false flag are given in the same
  106.    and-term.
  107.    The value of RecSearch and the status of More() are toggled if their
  108.    corresponding flags are found.  The list specs ListType, Sort, and
  109.    LineEnts take on the value of the last flag that applies to them.
  110.    No error message is printed if more than one flag for one of them is
  111.    found while the env var and command line args are being read, so that
  112.    a command line flag can override an env var flag without a message.
  113.    If an illegal flag is given, an error message is printed. */
  114. {
  115. static char FlagDesc[] =    /* Printed if any illegal flags were given */
  116. "     DI: Directory Listing Utility   by  John H. DuBois III    v1.1\n"
  117. "Flags are:   Pause during listing;  list Long, Wide, or with Full path;\n"
  118. "print 1-6 entries / line if listing wide;  search directories reCursively;\n"
  119. "sort first by dIr attrib, eXtension, Time and date, siZe, or Name only;\n"
  120. "Kill headers;    print Unused (free) space on device in main header;\n"
  121. "print files with attribs: Everything (print all files), or\n"
  122. "Read only, Hidden, System, Directory, Volume label, Archive.\n";
  123.  
  124. int I,
  125. Attrib,     /* Attrib that one flag translated to */
  126. AttGiven,    /* Whether any attributes were given in the flag list */
  127. BadFlag;    /* Whether any illegal flags were found in the flag list */
  128. char F;     /* Upper case version of flag being interpreted */
  129.  
  130.    *Attribute = 0;    /* Initialize to no attributes */
  131.    AttGiven = BadFlag = FALSE;
  132.    if (Flag[1] == '\0') /* If arg started with a flag specifier but no flags
  133.                were given */
  134.       fputs("Empty flag.",stderr);
  135.    else
  136.       for (I = 1; Flag[I] != '\0'; I++) {    /* For each flag */
  137.      F = UpCase(Flag[I]);
  138.      Attrib = NO_AT;    /* Initially set Attrib to a value that no
  139.       attribute term can have, so that it can be determined if Attrib was
  140.       set by a flag */
  141.      switch(F) {
  142.         case 'F' :
  143.         case 'L' :
  144.         case 'W' :    ListSpec -> ListType = F; break;
  145.         case 'X' :
  146.         case 'Z' :
  147.         case 'N' :
  148.         case 'I' :
  149.         case 'T' :    ListSpec -> Sort = F; break;
  150.         case '1' :
  151.         case '2' :
  152.         case '3' :
  153.         case '4' :
  154.         case '5' :
  155.         case '6' :    ListSpec -> LineEnts = F - '0'; break;
  156.         case 'P' :    More(More(-2)); break;
  157.         case 'U' :    ListSpec -> PrintFree = !ListSpec -> PrintFree; break;
  158.         case 'K' :    ListSpec -> PrintHeaders = !ListSpec -> PrintHeaders;
  159.                break;
  160.         case 'C' :    ListSpec -> RecSearch = !ListSpec -> RecSearch; break;
  161.         case 'R' :    Attrib = FA_RDONLY;  break;
  162.         case 'H' :    Attrib = FA_HIDDEN;  break;
  163.         case 'S' :    Attrib = FA_SYSTEM;  break;
  164.         case 'V' :    Attrib = FA_LABEL;   break;
  165.         case 'D' :    Attrib = FA_DIREC;   break;
  166.         case 'A' :    Attrib = FA_ARCH;    break;
  167.         case 'E' :    Attrib = 0;         break;
  168.         default :    fprintf(stderr,"Bad flag: %c\n",Flag[I]);
  169.             BadFlag = TRUE;
  170.      }
  171.      if (Attrib != NO_AT) { /* Flag list contained attribute flags */
  172.         AttGiven = TRUE;
  173.         if (islower(Flag[I])) {    /* 'true' flag was given. Check
  174.          whether the corresponding 'false' flag has already been set */
  175.            if (!FlagErr((*Attribute >> 8) & Attrib))
  176.           *Attribute |= Attrib; /* Set the correct bit in lo byte */
  177.         }
  178.         else    /* 'false' flag was given. Check whether the
  179.          corresponding 'true' flag has already been set */
  180.            if (!FlagErr(*Attribute & Attrib))
  181.           *Attribute |= Attrib << 8;    /* Set the bit in hi byte */
  182.      }
  183.       }
  184.    if (!AttGiven)    /* If no attrib flags found, indicate this */
  185.       *Attribute = NO_AT;
  186.    if (BadFlag) /* If any bad flags were found, print list of valid flags */
  187.       fputs(FlagDesc,stderr);
  188. }
  189.  
  190. FlagErr(int ErrWord)
  191. /* ErrWord is produced by ANDing the one-byte version of an attrib with
  192.    a word containing previously given attribs, with the inverse byte
  193.    shifted to the low byte.  If an attribute flag with the opposite
  194.    value has already been read, the corresponding bit in ErrWord will
  195.    be set.  If any bits in ErrWord are set, an error message and the flag
  196.    are printed and TRUE is returned;  otherwise FALSE is returned */
  197. {
  198.  
  199.    if (ErrWord != 0) {
  200.       fprintf(stderr,"Conflicting attribute flag: %s\n",
  201.        AttribWtoS((char) ErrWord));
  202.       return(TRUE);
  203.    }
  204.    else
  205.       return(FALSE);
  206. }
  207.  
  208. GetEnvParams(int *Cnt,char *Vars[],char *Name)
  209. /* Convert the value of environment var Name to a list of parameters with
  210.    the same format as argv and argc.  Parameters in the var are separated
  211.    by spaces and tabs. A copy of the var value is made with
  212.    strdup(); the space after each param is converted to a null char; and a
  213.    pointer to the start of each param is put in Vars. The first element in
  214.    Vars is set to point to the start of the copied string so it can be freed.
  215.    Cnt is set to the number of args found + 1.    If the var is not found in
  216.    the environment, Cnt is set to 0 and FALSE is returned; otherwise TRUE is
  217.    returned.
  218. */
  219. {
  220. char *Envar,    /* Found environment var */
  221. *Value;     /* Copied env var */
  222. int Num;    /* Number of args found so far + 1 */
  223.  
  224.    *Cnt = Num = 0;
  225.    if ((Envar = getenv(Name)) == NULL)    /* Get env var */
  226.       return(FALSE);    /* If not found return FALSE */
  227.    else {
  228.       Value = strdup(Envar);    /* Copy env var value */
  229.       Vars[0] = Value;    /* Make first pointer point to string */
  230.       Num = 1;    /* Start with 1 to account for pointer set to Value */
  231.       Vars[1] = strtok(Value," \t");    /* Tokenize Value and put
  232.                        pointers in Vars */
  233.       while (Vars[Num] != NULL)
  234.      Vars[++Num] = strtok(NULL," ");
  235.       *Cnt = Num;
  236.       return(TRUE);
  237.    }
  238. }
  239.  
  240. NameNode *ExpandPath(char Path[])
  241. {
  242. NameNode *List, /* Start of the filespec list */
  243.  *Temp,     /* A node that is to be freed */
  244.  *Last,     /* End of the filespec list */
  245.  *NewDirs;    /* The directory list that a wildcard dir spec expanded to */
  246. char Head[MAXPATH],    /* The portion of the path before the first component
  247.                containing wildcards */
  248. Wild[MAXPATH],    /* The first component of the path containing wildcards */
  249. Tail[MAXPATH],    /* The portion of the path after the first component containing
  250.            wildcards */
  251. Dir[MAXPATH];    /* The current wildcard dir spec being expanded */
  252.  
  253.    Last = List = CreateNameList();    /* Initialize List with an empty node
  254.                        and make Last be the same node */
  255.    strupr(Path);    /* Convert Path to all upper case */
  256.    strcpy(List -> Name,Path);    /* Copy Path to the first node in the list */
  257.    AddAllWC(List -> Name);    /* Expand all implicit wildcard in the path */
  258.    FirstWild(List -> Name,Head,Wild,Tail);    /* Find the first wildcard
  259.     specification in the path that must be expanded to a list of directories */
  260.    while (Tail[0] != '\0') {    /* As long as Tail is not a null string, Tail
  261.     will contain the last component of the path (the file name).  Path
  262.     expansion should stop when the file name is reached since only the dir
  263.     wildcards are to be expanded.  Because all final paths will have the same
  264.     number of dirs expanded from wildcards in the path, as soon as one
  265.     completely expanded path is found all paths will have been completely
  266.     expanded */
  267.       strcpy(Dir,Head); /* Make Dir consist of the Head (which will contain no
  268.        wildcards) as dir path and Wild as the filename */
  269.       strcat(Dir,Wild);
  270.       NewDirs = GetMatchFiles(Dir,FA_REALDIR);    /* Get the full paths of all
  271.                            dir names that match Dir */
  272.       AddString(NewDirs,Tail);    /* Add the unexpanded portion of the path to
  273.                    all dirs found */
  274.       Last = CatNameLists(Last,NewDirs);    /* Add the new dirs found to
  275.                            the end of the dir list */
  276.       Temp = List;    /* Get rid of the first element of the list (the dir
  277.                that has been expanded */
  278.       List = List -> Next;
  279.       free(Temp);
  280.       FirstWild(List -> Name,Head,Wild,Tail);    /* Split up the next path in
  281.                            the list */
  282.    }
  283.    MakePathsFull(&List);    /* Convert relative paths to full path names
  284.                    and get rid of invalid paths */
  285.    return(List);
  286. }
  287.  
  288. void AddCDirs(NameNode *List)
  289. /* Adds child dirs to List with the same file name.  The name of each path in
  290.    List is split into directory and filename.  All subdirs of each dir are
  291.    found and inserted in the list immediately after the parent dir, with
  292.    the file name from the original path appended to them.  The same proceedure
  293.    is performed on each new dir found, so that at the end all decendant dirs
  294.    of the dir in the original path will have been added to the list with the
  295.    original file name appended. */
  296. {
  297. char Dir[MAXPATH],    /* The path being searched for subdirs, with the
  298.                original filename replaced by "*.*" */
  299. *NameStrt,        /* Start of the filename of the path */
  300. Name[13];        /* The original filename of the path */
  301. NameNode *SubList;    /* List of subdirs found */
  302.  
  303.    Name[13] = '\0';    /* If a file name was more than 12 chars, it will be
  304.             truncated.  Ensures that it will be null terninated */
  305.    while (List != NULL) {
  306.       strcpy(Dir,List -> Name);     /* Copy path so it can be altered */
  307.       NameStrt = strrchr(Dir,'\\');    /* Find start of file name */
  308.       strncpy(Name,NameStrt,12);    /* Save original file name */
  309.       strcpy(NameStrt + 1,"*.*");    /* Replace it in path with "*.*"
  310.                        so all subdirs will be found */
  311.       SubList = GetMatchFiles(Dir,FA_REALDIR);    /* Find subdirs, but not
  312.                            "." and ".." entries */
  313.       AddString(SubList,Name);    /* Add orig file name to all subdirs found */
  314.       InsertList(List,SubList); /* Insert list of child dirs immediately after
  315.     parent dir, so they will be the next to be searched for child dirs */
  316.       List = List -> Next;
  317.    }
  318. }
  319.  
  320. NameNode *GetMatchFiles(char *Path,int Attribs)
  321. /* Finds & makes a list of all files that match Path and Attribs. Returns a
  322.    pointer to the list */
  323. {
  324. NameNode *Temp, /* End of the list of matching files */
  325. *List;    /* List of matching files */
  326. char *File;    /* The name of a matching file */
  327.  
  328.    Temp = List = CreateNameList();    /* Initialize Temp and List to an
  329.                        empty node */
  330.    File = NextMatchFile(Path,Attribs);    /* Get the first matching file */
  331.    while (File != NULL) {    /* Do until no more matching files */
  332.       AddNode(Temp);        /* Add a new empty node to the list */
  333.       Temp = Temp -> Next;    /* Move Temp to point to it */
  334.       strcpy(Temp -> Name,File);    /* Copy the name of the matching file
  335.                        to the new node */
  336.       File = NextMatchFile(NULL,0);    /* Get next matching file */
  337.    }
  338.    Temp = List;     /* Get rid of the first empty node */
  339.    List = List -> Next;
  340.    free(Temp);
  341.    return(List);
  342. }
  343.  
  344. NameNode *CreateNameList()
  345. /* Creates a new NameNode, sets its Next pointer to NULL, and returns
  346.    a pointer to it */
  347. {
  348. NameNode *Temp;
  349.  
  350.    Temp = (NameNode *) MallocT(sizeof(NameNode));
  351.    Temp -> Next = NULL;
  352.    return(Temp);
  353. }
  354.  
  355. FindInt(int Arr[],int Targ)
  356. /* Searches array Arr for Targ & returns its index */
  357. {
  358. int I;
  359.  
  360.    for (I = 0; Arr[I] != Targ; I++) ;
  361.    return(I);
  362. }
  363.  
  364. void FreeList(NameNode *List)
  365. /* Frees all of the nodes in List */
  366. {
  367. NameNode *Temp;
  368.  
  369.    while (List != NULL) {
  370.       Temp = List;
  371.       List = List -> Next;
  372.       free(Temp);
  373.    }
  374. }
  375.  
  376. void AddAllWC(char Path[])
  377. /* Converts all implicit wildcards in Path, except for those in the last
  378.    file name given, to explicit wildcards.  A filename
  379.    is assumed to exist between every two backslashes and after the last
  380.    backslash.  If Path contains no backslashes, it is assumed to be one
  381.    filename.  */
  382. {
  383. char NewPath[MAXPATH],    /* The new path as it is being created from Path */
  384. *FSpec,     /* The file name being converted */
  385. Temp[12];
  386. int NameStart;
  387.  
  388.    strcpy(NewPath,Path);    /* Copy Path to NewPath so that possible
  389.             drive spec and leading backslash will be in NewPath */
  390.    if (Path[0] != '\0' && Path[1] == ':')    /* Find the first file name
  391.     in Path (at index 0 or 2, depending on whether a drive was specified */
  392.       NameStart = 2;
  393.    else
  394.       NameStart = 0;
  395.    FSpec = Tokenize(Path + NameStart,'\\');    /* Find the first filename in
  396.                            Path */
  397.    if (NewPath[NameStart] == '\\') {    /* If Path began with a backslash, the
  398.     first name returned by Tokenize will by the null string that preceded the
  399.     backslash, which should not be expanded.  Call Tokenize again, and make
  400.     NewPath end immediately after the first backslash */
  401.       FSpec = Tokenize(NULL,'\\');
  402.       NewPath[NameStart + 1] = '\0';
  403.    }
  404.    else
  405.       NewPath[NameStart] = '\0';    /* Remove everything except the drive
  406.                        spec, if given, from NewPath */
  407.    while (FSpec != NULL) { /* While there are filenames left to be converted */
  408.       strcpy(Temp,FSpec);    /* Copy the file name to Temp so it can be
  409.                    converted */
  410.       FSpec = Tokenize(NULL,'\\');    /* Find next file name */
  411.       if (FSpec != NULL)    /* Convert filename only if it is not the last
  412.                    file name */
  413.      AddFileWC(Temp);
  414.       strcat(NewPath,Temp);    /* Add name to new path */
  415.       strcat(NewPath,"\\");    /* Add a backslash */
  416.    }
  417.    NewPath[strlen(NewPath) - 1] = '\0'; /* Get rid of the last bckslsh added */
  418.    strcpy(Path,NewPath);    /* Copy converted path back to Path */
  419. }
  420.  
  421. void AddFileWC(char File[])
  422. /* Converts the implicit wildcards in File to explicit wildcards.  An implicit
  423.    file name of "*" exists if File has no file name, and an implicit file
  424.    extension of ".*" exists if File has no file ext. If File is "." or "..",
  425.    it is not altered. */
  426. {
  427. char *Ext;    /* Start of the file ext of File */
  428.  
  429.    Ext = strchr(File,'.');
  430.    if (Ext == File && !CPDir(File) || File[0] == '\0')    /* If File starts with
  431.     a dot and is not the current or parent dir ("." or ".."), then it has no
  432.     file name; make the file name "*". */
  433.       Insert(File,"*",0);
  434.    if (Ext == NULL)    /* If there was no dot in File, it has no file ext;
  435.                make the file ext ".*" */
  436.       strcat(File,".*");
  437. }
  438.  
  439. char *Tokenize(char S[],char SepChar)
  440. /* Converts S to tokens. On the first call, S should be the string to tokenize;
  441.    on subsequent calls it should be null.  S is a string of tokens separated
  442.    by single instances of SepChar.  If two SepChar's occur adjacent to each
  443.    other, a null token ("") is assumed to exist between them.  If S has a
  444.    leading or trailing SepChar, a null token is assumed to exist before or
  445.    after the string, respectively.  SepChar can be different between calls.
  446.    After each call to Tokenize, a null is written immediately after the token
  447.    found and a pointer to its start is returned.  Null tokens are returned as
  448.    empty strings.  When no more tokens are found, a NULL pointer is returned.
  449.    */
  450. {
  451. static char *List;    /* Start of list of tokens */
  452. char *Pos,    /* Position of end of current token */
  453. *Token; /* Start of token */
  454.  
  455.    if (S != NULL)  /* If first call to function, set List to string given */
  456.       List = S;
  457.    Token = List;   /* Current token starts at start of List */
  458.    if (List != NULL) {            /* If some tokens are left */
  459.       Pos = strchr(List,SepChar);    /* Find end of token */
  460.       if (Pos == NULL)    /* If no more tokens, the next call will return NULL */
  461.      List = NULL;
  462.       else {
  463.      *Pos = '\0';    /* Otherwise, add null termination to current token
  464.                and make List point to start of next token */
  465.      List = Pos + 1;
  466.       }
  467.    }
  468.    return(Token);
  469. }
  470.  
  471. void FirstWild(char Path[],char *Head,char *WildSpec,char *Tail)
  472. /* Finds the first wildcard filename in Path.  The portion of the path
  473.    preceeding that filename (if any), including trailing backslash, is
  474.    copied to Head.  The wildcard filename (without leading or trailing
  475.    backslashes) is copied to WildSpec.    The portion of the path after
  476.    the wildcard filename (including leading backslash) is copied to Tail */
  477. {
  478. char *PostSep,
  479. *PreSep,
  480. *FirstWC;
  481.  
  482.    strcpy(Head,Path);    /* Copy Path to Head so it can be altered */
  483.    FirstWC = Head + strcspn(Head,"*?"); /* Make FirstWC point to the first
  484.                        wildcard in Head */
  485.    if (*FirstWC == '\0') {    /* If no wildcards found, leave the entire
  486.         string in Head and make WildSpec and Tail null strings */
  487.       WildSpec[0] = '\0';
  488.       Tail[0] = '\0';
  489.    }
  490.    else {
  491.       PostSep = strchr(FirstWC,'\\');    /* Find the first backslash after the
  492.                        first wildcard */
  493.       if (PostSep != NULL) {    /* If there was one, then it is the start of
  494.        the tail and the end of the first wildcard spec */
  495.      strcpy(Tail,PostSep);
  496.      *PostSep = '\0';
  497.       }
  498.       else
  499.      Tail[0] = '\0';    /* If not, make Tail a null string */
  500.       PreSep = strrchr(Head,'\\');    /* Find the first backslash before the
  501.                        the first wildcard */
  502.       if (PreSep != NULL) {    /* If there was one, the char after it is the
  503.       start of the the first wildcard spec and the end of the Head */
  504.      strcpy(WildSpec,PreSep + 1);
  505.      *(PreSep + 1) = '\0';
  506.       }
  507.       else {
  508.      strcpy(WildSpec,Head); /* If not, everything in Head is the first
  509.                    wildcard spec */
  510.      Head[0] = '\0';
  511.       }
  512.    }
  513. }
  514.  
  515. void AddString(NameNode *List,char *S)
  516. /* Adds string S to every name in List */
  517. {
  518.    while (List != NULL) {
  519.       strcat(List -> Name,S);
  520.       List = List -> Next;
  521.    }
  522. }
  523.  
  524. void InsertList(NameNode *Target,NameNode *Source)
  525. /* Inserts list Source into the list in which Target is a node, immediately
  526.    after target. */
  527. {
  528. NameNode *Next;
  529.  
  530.    if (Source != NULL) {
  531.       Next = Target -> Next;
  532.       Target -> Next = Source;
  533.       LastElem(Source) -> Next = Next;
  534.    }
  535. }
  536.  
  537. NameNode *CatNameLists(NameNode *Target,NameNode *Source)
  538. /* Target points to the last node in a list.  Catenates Source to Target
  539.    and returns a pointer to the last node of the new list */
  540. {
  541.  
  542.    ExTest(Target == NULL,"Error: Null Target in CatNameLists().");
  543.    Target -> Next = Source;
  544.    return(LastElem(Target));
  545. }
  546.  
  547. NameNode *LastElem(NameNode *List)
  548. /* Returns a pointer to the last element of List */
  549. {
  550.    ExTest(List == NULL,"Error: Null pointer passed to LastElem()");
  551.    while(List -> Next != NULL)
  552.       List = List -> Next;
  553.    return(List);
  554. }
  555.  
  556. void AddNode(NameNode *Node)
  557. /* Creates a new node; links Node to it; and makes its Next pointer null */
  558. {
  559.    ExTest(Node == NULL,"NULL pointer passed to AddNode");
  560.    Node -> Next = (NameNode *) MallocT(sizeof(NameNode));
  561.    Node -> Next -> Next = NULL;
  562. }
  563.  
  564. char *NextMatchFile(char FileSpec[],int Attributes)
  565. /* Finds files that match FileSpec and have all attributes indicated by
  566.    Attributes, and returns the full path of the file,
  567.    including the drive and directory given in FileSpec. On the first call,
  568.    FileSpec and Attribs are given; on subsequent calls FileSpec should be
  569.    NULL.  The found file is put in a static buffer and a pointer to the
  570.    buffer is returned. If no matching file is found, a null pointer is
  571.    returned. */
  572. {
  573. static char File[MAXPATH],    /* Full file name buffer */
  574. Drive[MAXDRIVE],Dir[MAXDIR];    /* Drive and dir given in the filespec, to
  575.                    be added to each file found */
  576. static struct ffblk FFBlk;    /* Structure for directory searching routine */
  577. char Name[MAXFILE],Ext[MAXEXT]; /* Name and extension of found file */
  578. int Done;    /* Whether dir searching routine found a matching file */
  579. static int Attribs,    /* Saves Attributes between calls */
  580. RealDir;    /* Search for real dirs only */
  581.  
  582.    if (FileSpec != NULL) {    /* First call to function */
  583.       fnsplit(FileSpec,Drive,Dir,NULL,NULL); /* Get drive & dir of filespec */
  584.       if (Attributes & FA_REALDIR) {    /* If one of the attributes given is
  585.        the psuedo-attrib FA_REALDIR (match real dirs only, not "." or "..")
  586.        then set the variable that will save the search attribs between calls
  587.        to Attributes, with the psuedo-attrib replaced with the dir attrib,
  588.        and set RealDir */
  589.      Attribs = (Attributes & ~FA_REALDIR) | FA_DIREC;
  590.      RealDir = TRUE;
  591.       }
  592.       else {    /* Save attribs */
  593.      Attribs = Attributes;
  594.      RealDir = FALSE;
  595.       }
  596.       Done = findfirst(FileSpec,&FFBlk,Attribs);  /* Find first match file */
  597.    }
  598.    else     /* Use FFBlk set up by previous call */
  599.       Done = findnext(&FFBlk);
  600.    while (!Done && ((FFBlk.ff_attrib & Attribs) != Attribs ||
  601.     RealDir && CPDir(FFBlk.ff_name)))    /* Find next file that has all of the
  602.      attribs specified in Attribs and, if RealDir is set, is not the current
  603.      or parent dir entry ("." or "..") */
  604.       Done = findnext(&FFBlk);
  605.    if (Done)    /* If no more matching files return NULL */
  606.       return(NULL);
  607.    else {
  608.       fnsplit(FFBlk.ff_name,NULL,NULL,Name,Ext); /* Split matching file name
  609.                             into Name & Extension */
  610.       fnmerge(File,Drive,Dir,Name,Ext); /* Merge Name & Ext with previously
  611.                        given Drive & Dir */
  612.       return(File);
  613.    }
  614. }
  615.  
  616. void MakePathsFull(NameNode **List)
  617. /* Converts the relative paths given in List to full path names, and deletes
  618.    invalid paths from the list. If paths are removed from the front of the
  619.    list, List is changed to point to the new start of the list */
  620. {
  621. NameNode  *Last,    /* The last path converted */
  622.  *Temp;     /* Pointer to list as it is stepped through */
  623.  
  624.    while (*List != NULL && !GetFullPath((*List) -> Name)) {    /* Find the first valid
  625.     path in List (deleting invalid ones) and convert it to a full path */
  626.       Temp = *List;
  627.       *List = (*List) -> Next;
  628.       free(Temp);
  629.    }
  630.    if (*List != NULL) { /* If any valid dirs were given */
  631.       Temp = (*List) -> Next;    /* Start with the next node, since the current
  632.                    one has already been converted */
  633.       Last = *List;    /* The previous loop insured that there is a 'last'
  634.        node that can have its Next pointer moved beyond the current node, in
  635.        order to delete that node */
  636.       while (Temp != NULL) { /* For all of the rest of the paths in the list */
  637.      if (GetFullPath(Temp -> Name)) {    /* If path is valid */
  638.         Last = Temp;        /* Make Last point to the current
  639.                        path, then move to the next path */
  640.         Temp = Temp -> Next;
  641.      }
  642.      else { /* If the path is not valid */
  643.         DeleteNext(Last);    /* Delete the current path from the list
  644.          by making the Next pointer of the previous node point the next
  645.          node and freeing the current node */
  646.         Temp = Last -> Next;    /* Move to the next path */
  647.      }
  648.       }
  649.    }
  650. }
  651.  
  652. void DeleteNext(NameNode *List)
  653. /* Deletes the node that follows List by making List's Next pointer point to
  654.    the node beyond the next node, and freeing the deleted node */
  655. {
  656. NameNode *Temp; /* Node to be freed */
  657.  
  658.    ExTest(List == NULL || List -> Next == NULL,"Null pointer to DeleteNode");
  659.    Temp = List -> Next;
  660.    List -> Next = Temp -> Next;
  661.    free(Temp);
  662. }
  663.  
  664. GetFullPath(char *Path)
  665. /*
  666.    Given a relative path to a file or directory, finds the full path name
  667.    of the file or directory and replaces the original string with it.
  668.    If a drive is not specified in the path, the current drive is used.
  669.    Returns TRUE if the file is found, otherwise FALSE.
  670. */
  671. {
  672. char Drive[MAXDRIVE],Dir[MAXDIR],FileName[MAXFILE],Ext[MAXEXT], /* Used to
  673.                     split up and reassemble path */
  674. FindPath[MAXPATH];    /* New path */
  675. int Comps;    /* Components found in path */
  676.  
  677.    strcpy(FindPath,Path);    /* Make working copy of Path */
  678.    if (FindPath[0] == '\0' || FindPath[1] == ':' && strlen(FindPath) == 2)
  679.     /* If no path or only a drive is given, make path be the current
  680.         directory on the default or given drive. */
  681.       strcat(FindPath,".\\");
  682.    else if (IsDir(FindPath) && FindPath[strlen(FindPath) - 1] != '\\')
  683.      /* If path is a directory, append a backslash to it to indicate this */
  684.       strcat(FindPath,"\\");
  685.    Comps = fnsplit(FindPath,Drive,Dir,FileName,Ext);    /* Split path into its
  686.                                components */
  687.    strcpy(FindPath,Drive);    /* Generate path pointing to given directory
  688.                    or parent dirctory of given file */
  689.    strcat(FindPath,Dir);
  690.    strcat(FindPath,".");
  691.    if (strcmp(FileName,".") == 0 || !GetFullDir(FindPath))    /* Convert
  692.     directory path to full dir name; if dir specified in path was invalid, or
  693.     specified a parent directory beyond the root directory, return FALSE */
  694.       return(FALSE);
  695.    if (!(Comps & DRIVE)) {    /* If a drive was not specified, make path
  696.                    drive be the current drive */
  697.       Drive[0] = getdisk() + 'A';
  698.       Drive[1] = ':';
  699.    }
  700.    if (!(Comps & FILENAME))    /* If no file name given, make file name "*" */
  701.       strcpy(FileName,"*");
  702.    if (!(Comps & EXTENSION))    /* if no file ext given, make file ext ".*" */
  703.       strcpy(Ext,".*");
  704.    fnmerge(Path,Drive,FindPath,FileName,Ext); /* Create full path from comps */
  705.    return(TRUE);
  706. }
  707.  
  708. GetFullDir(char *Path)
  709. /* Given a path to a directory, converts it to a full directory name. If
  710.    given path did not point to a dir, returns FALSE; else returns TRUE */
  711. {
  712. struct ffblk FFBlk;    /* Structure for directory searching routine */
  713. char Dir[MAXPATH];    /* Temp storage of full path */
  714.  
  715.    if (!IsDir(Path))    /* If invalid directory, return FALSE */
  716.       return(FALSE);
  717.    Dir[0] = '\0';        /* Clear directory string */
  718.    while (!findfirst(Path,&FFBlk,0x3f)) {    /* Get name of directory
  719.      pointed to by Path until no more directories can be found */
  720.       if (!IsRoot(Path)) {    /* Check whether directory name found is the
  721.     name of the dir that is at the root of the drive given in the path,
  722.     which it could be if the drive is a substituded drive. If not,
  723.     insert the name followed by a backslash at the begining of Dir */
  724.      Insert(Dir,"\\",0);
  725.      Insert(Dir,FFBlk.ff_name,0);
  726.       }
  727.       strcat(Path,"\\..");    /* Append "\.." to Path so it will point
  728.        to the parent directory (if it exists) of the dir just found */
  729.    }
  730.    Insert(Dir,"\\",0);    /* Make Dir have a leading backslash */
  731.    strcpy(Path,Dir);    /* Move full dir name to Path */
  732.    return(TRUE);
  733. }
  734.  
  735. IsRoot(char *Dir)
  736. /* Returns TRUE if Dir is a path to the root of a real or substituted drive,
  737.    or is an invalid directory. Otherwise returns FALSE. */
  738. {
  739. struct ffblk FFBlk;    /* For dir searching routine */
  740. char Temp[MAXPATH];    /* Path to parent of Dir, if it exists */
  741.  
  742.    strcpy(Temp,Dir);
  743.    strcat(Temp,"\\..\\*.*");    /* Make Temp point to parent of Dir */
  744.    return(findfirst(Temp,&FFBlk,0x3f)); /* Return TRUE if Dir has no parent. */
  745. }
  746.  
  747. IsDir(char *Path)
  748. /* Returns true if Path is a directory, otherwise false. */
  749. {
  750. char Temp[MAXPATH];    /* Test string */
  751. struct ffblk FFBlk;    /* For directory searching routine */
  752.  
  753.    strcpy(Temp,Path);
  754.    if (Temp[0] != '\0' && Temp[strlen(Temp) - 1] != '\\')    /* If string is
  755.     not null and does not end in a backslash, append a backslash to it so it
  756.     will specify a directory */
  757.       strcat(Temp,"\\");
  758.    strcat(Temp,"*.*");    /* Make temp specify any file in the given directory */
  759.    return(!findfirst(Temp,&FFBlk,0x3f) || NullPath(Path));    /* If any files
  760.      are found, or it is a null path (contains only single .'s or backslashes)
  761.      it must be a directory. The only directory that does not have
  762.      any files is a root dir without ordinary files, directories, system files
  763.      or volume label. A path to such a directory must be a null path. */
  764. }
  765.  
  766. NullPath(char *Path)
  767. /* Checks whether Path is a null path (contains only single '.s and
  768.    backslashes). Returns TRUE if so, else returns FALSE. */
  769. {
  770. char Temp[MAXPATH];
  771.  
  772.  
  773.    if (DriveSpeced(Path))
  774.       strcpy(Temp,Path + 2);
  775.    else
  776.       strcpy(Temp,Path);
  777.    strcat(Temp,"\\");    /* Pad string with '\' so strstr will work despite
  778.                its bug */
  779.    return(strspn(Temp,"\\.") == strlen(Temp) && strstr(Temp,"..") == NULL);
  780.      /* Check that temp contains only \'s and .'s, and no two .'s are
  781.     adjacent */
  782. }
  783.  
  784.