home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / windiff / scandir.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  38KB  |  1,268 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /****************************** Module Header *******************************
  13. * Module Name: SCANDIR.C
  14. *
  15. * Scan a directory tree and build a sorted list of filenames within that
  16. * tree.
  17. *
  18. * Functions:
  19. *
  20. * dir_buildlist()
  21. * dir_delete()
  22. * dir_isfile()
  23. * dir_firstitem()
  24. * dir_nextitem()
  25. * dir_findnextfile()
  26. * dir_getrelname()
  27. * dir_getfullname()
  28. * dir_getroot_list()
  29. * dir_getroot_item()
  30. * dir_freerelname()
  31. * dir_freefullname()
  32. * dir_freeroot_list()
  33. * dir_freerootitem()
  34. * dir_getopenname()
  35. * dir_freeopenname()
  36. * dir_openfile()
  37. * dir_closefile()
  38. * dir_filesize()
  39. * dir_startcopy()
  40. * dir_endcopy()
  41. * dir_copy()
  42. * dir_finalelem()
  43. * dir_cleardirect()
  44. * dir_adddirect()
  45. * dir_addfile()
  46. * dir_scan()
  47. * dir_isvaliddir()
  48. * dir_isvalidfile()
  49. * dir_fileinit()
  50. * dir_dirinit()
  51. * dir_getpathsize()
  52. * dir_findnextfile()
  53. *
  54. * Comments:
  55. *
  56. * The call dir_buildlist takes a pathname and returns a handle. Subsequent
  57. * calls to dir_firstitem and dir_nextitem return handles to
  58. * items within the list, from which you can get the name of the
  59. * file (relative to the original pathname, or complete), and filesize.
  60. *
  61. * The list can be either built entirely during the build call, or
  62. * built one directory at a time as required by dir_nextitem calls. This
  63. * option affects only relative performance, and is taken as a
  64. * recommendation only (ie some of the time we will ignore the flag).
  65. *
  66. * The list is ordered alphabetically (case-insensitive using lstrcmpi).
  67. * within any one directory, we list filenames before going on
  68. * to subdirectory contents.
  69. *
  70. * All memory is allocated from a gmem_* heap hHeap declared
  71. * and initialised elsewhere.
  72. *
  73. * The caller gets handles to two things: a DIRLIST, representing the
  74. * entire list of filenames, and a DIRITEM: one item within the list.
  75. *
  76. * From the DIRITEM he can get the filename (including or excluding the
  77. * tree root passed to dir_build*) - and also he can get to the next
  78. * DIRITEM.
  79. *
  80. * We permit lazy building of the tree (usually so the caller can keep
  81. * the user-interface up-to-date as we go along). In this case,
  82. * we need to store information about how far we have scanned and
  83. * what is next to do. We need to scan an entire directory at a time and then
  84. * sort it so we can return files in the correct order.
  85. *
  86. * We scan an entire directory and store it in a DIRECT struct. This contains
  87. * a list of DIRITEMs for the files in the current directory, and a list of
  88. * DIRECTs for the subdirectories (possible un-scanned).
  89. *
  90. * dir_nextitem will use the list functions to get the next DIRITEM on the list.
  91. * When the end of the list is reached, it will use the backpointer back to the
  92. * DIRECT struct to find the next directory to scan.
  93. *
  94. ****************************************************************************/
  95.  
  96. #include <windows.h>
  97. #include <stdlib.h>
  98. #include <string.h>
  99. #include <dos.h>
  100. #include <direct.h>
  101.  
  102. #include "gutils.h"
  103. #include "list.h"
  104. #include "scandir.h"
  105. #include "windiff.h"
  106. #include "wdiffrc.h"
  107.  
  108. /*
  109.  * Hold name and information about a given file (one ITEM in a DIRectory)
  110.  * caller's DIRITEM handle is a pointer to one of these structures
  111.  */
  112. struct diritem {
  113.         LPSTR name;             /* ptr to filename (final element only) */
  114.         long size;              /* filesize */
  115.         struct direct FAR * direct; /* containing directory */
  116.         LPSTR localname;        /* name of temp copy of file */
  117.         BOOL bLocalIsTemp;      /* true if localname is tempfile.
  118.                                  */
  119. };
  120.  
  121.  
  122. /* DIRECT: Hold state about directory and current position in list of filenames.
  123.  */
  124. typedef struct direct {
  125.         LPSTR relname;          /* name of dir relative to DIRLIST root */
  126.         DIRLIST head;           /* back ptr (to get fullname) */
  127.         struct direct FAR * parent; /* parent directory (NULL if above tree root)*/
  128.  
  129.         BOOL bScanned;          /* TRUE if scanned */
  130.         LIST diritems;          /* list of DIRITEMs for files in cur. dir */
  131.         LIST directs;           /* list of DIRECTs for child dirs */
  132.  
  133.         int pos;                /* where are we begin, files, dirs */
  134.         struct direct FAR * curdir; /* subdir being scanned (ptr to list element)*/
  135. } FAR * DIRECT;
  136.  
  137. /* Values for direct.pos */
  138. #define DL_FILES        1       /* reading files from the diritems */
  139. #define DL_DIRS         2       /* in the dirs: List_Next on curdir */
  140.  
  141.  
  142. /*
  143.  * The DIRLIST handle returned from a build function is in fact
  144.  * a pointer to one of these
  145.  */
  146. struct dirlist {
  147.  
  148.         char rootname[256];        /* name of root of tree */
  149.         BOOL bFile;             /* TRUE if root of tree is file, not dir */
  150.         DIRECT dot;             /* dir  for '.' - for tree root dir */
  151. };
  152.  
  153. extern BOOL bAbort;             /* from windiff.c (read only here). */
  154.  
  155.  
  156. /* ------ memory allocation ---------------------------------------------*/
  157.  
  158. /* All memory is allocated from a heap created by the application */
  159. extern HANDLE hHeap;
  160.  
  161. /*-- forward declaration of internal functions ---------------------------*/
  162.  
  163. LPSTR dir_finalelem(LPSTR path);
  164. void dir_cleardirect(DIRECT dir);
  165. void dir_adddirect(DIRECT dir, LPSTR path);
  166. void dir_addfile(DIRECT dir, LPSTR path, DWORD size);
  167. void dir_scan(DIRECT dir, BOOL bRecurse);
  168. BOOL dir_isvaliddir(LPSTR path);
  169. BOOL dir_isvalidfile(LPSTR path);
  170. void dir_fileinit(DIRITEM pfile, DIRECT dir, LPSTR path, long size);
  171. void dir_dirinit(DIRECT dir, DIRLIST head, DIRECT parent, LPSTR name);
  172. long dir_getpathsize(LPSTR path);
  173. DIRITEM dir_findnextfile(DIRLIST dl, DIRECT curdir);
  174.  
  175.  
  176.  
  177. /***************************************************************************
  178.  * Function: dir_buildlist
  179.  *
  180.  * Purpose:
  181.  *
  182.  * Build a list of filenames
  183.  *
  184.  * Optionally build the list on demand, in which case we scan the
  185.  * entire directory but don't recurse into subdirs until needed
  186.  *
  187.  */
  188.  
  189. DIRLIST
  190. dir_buildlist(LPSTR path, BOOL bOnDemand)
  191. {
  192.         DIRLIST dl;
  193.         BOOL bFile;
  194.         
  195.         /* first check if the path is valid */
  196.         if (dir_isvaliddir(path)) {
  197.                 bFile = FALSE;
  198.         } else if (dir_isvalidfile(path)) {
  199.                 bFile = TRUE;
  200.         } else {
  201.                 /* not valid */
  202.                 return(NULL);
  203.         }
  204.  
  205.  
  206.         /* alloc and init the DIRLIST head */
  207.  
  208.         dl = (DIRLIST) gmem_get(hHeap, sizeof(struct dirlist));
  209.         memset(dl, 0, sizeof(struct dirlist));
  210.  
  211.         /* convert the pathname to an absolute path */
  212.  
  213.         _fullpath(dl->rootname, path, sizeof(dl->rootname));
  214.  
  215.         dl->bFile = bFile;
  216.         /* make a '.' directory for the current directory -
  217.          * all files and subdirs will be listed from here
  218.          */
  219.         dl->dot = (DIRECT) gmem_get(hHeap, sizeof(struct direct));
  220.         dir_dirinit(dl->dot, dl, NULL, ".");
  221.  
  222.         /* were we given a file or a directory ? */
  223.         if (bFile) {
  224.                 /* its a file. create a single file entry
  225.                  * and set the state accordingly
  226.                  */
  227.                 dl->dot->bScanned = TRUE;
  228.  
  229.                 dir_addfile(dl->dot, dir_finalelem(path),
  230.                                 dir_getpathsize(path));
  231.  
  232.                 return(dl);
  233.         }
  234.  
  235.         /* scan the root directory and return. if we are asked
  236.          * to scan the whole thing, this will cause a recursive
  237.          * scan all the way down the tree
  238.          */
  239.         dir_scan(dl->dot, (!bOnDemand) );
  240.  
  241.         return(dl);
  242. } /* dir_buildlist */
  243.  
  244. /***************************************************************************
  245.  * Function: dir_delete
  246.  *
  247.  * Purpose:
  248.  *
  249.  * Free up the DIRLIST and all associated memory 
  250.  */
  251. void
  252. dir_delete(DIRLIST dl)
  253. {
  254.         if (dl == NULL) {
  255.                 return;
  256.         }
  257.         dir_cleardirect(dl->dot);
  258.         gmem_free(hHeap, (LPSTR) dl->dot, sizeof(struct direct));
  259.  
  260.  
  261.         gmem_free(hHeap, (LPSTR) dl, sizeof(struct dirlist));
  262. }
  263.  
  264.  
  265.  
  266. /***************************************************************************
  267.  * Function: dir_isfile
  268.  *
  269.  * Purpose:
  270.  *
  271.  * Was the original build request a file or a directory ? 
  272.  */
  273. BOOL
  274. dir_isfile(DIRLIST dl)
  275. {
  276.         if (dl == NULL) {
  277.                 return(FALSE);
  278.         }
  279.  
  280.         return(dl->bFile);
  281. }
  282.  
  283. /***************************************************************************
  284.  * Function: dir_firstitem
  285.  *
  286.  * Purpose:
  287.  *
  288.  * Return the first file in the list, or NULL if no files found.
  289.  * Returns a DIRITEM. This can be used to get filename, size and chcksum.
  290.  * If there are no files in the root, we recurse down until we find a file.
  291.  */
  292. DIRITEM
  293. dir_firstitem(DIRLIST dl)
  294. {
  295.         if (dl == NULL) {
  296.                 return(NULL);
  297.         }
  298.         /*
  299.          * reset the state to indicate that no files have been read yet
  300.          */
  301.         dl->dot->pos = DL_FILES;
  302.         dl->dot->curdir = NULL;
  303.  
  304.         /* now get the next filename */
  305.         return(dir_findnextfile(dl, dl->dot));
  306. } /* dir_firstitem */
  307.  
  308.  
  309. /***************************************************************************
  310.  * Function:dir_nextitem
  311.  *
  312.  * Purpose:
  313.  *
  314.  * Get the next filename after the one given.
  315.  *
  316.  * The List_Next function can give us the next element on the list of files.
  317.  * If this is null, we need to go back to the DIRECT and find the
  318.  * next list of files to traverse (in the next subdir).
  319.  *
  320.  * After scanning all the subdirs, return to the parent to scan further
  321.  * dirs that are peers of this, if there are any. If we have reached the end of
  322.  * the tree (no more dirs in dl->dot to scan), return NULL.
  323.  *
  324.  * Don't recurse to lower levels unless fDeep is TRUE
  325.  */
  326. DIRITEM
  327. dir_nextitem(DIRLIST dl, DIRITEM cur, BOOL fDeep)
  328. {
  329.         DIRITEM next;
  330.  
  331.         if ((dl == NULL) || (cur == NULL)) {
  332.                 return(NULL);
  333.         }
  334.         if (bAbort) return NULL;  /* user requested abort */
  335.  
  336.         if ( (next = List_Next(cur)) != NULL) {
  337.                 /* there was another file on this list */
  338.                 return(next);
  339.         }
  340.         if (!fDeep) return NULL;
  341.  
  342.         /* get the head of the next list of filenames from the directory */
  343.         cur->direct->pos = DL_DIRS;
  344.         cur->direct->curdir = NULL;
  345.         return(dir_findnextfile(dl, cur->direct));
  346. } /* dir_nextitem */
  347.  
  348. /***************************************************************************
  349.  * Function: dir_findnextfile
  350.  *
  351.  * Purpose:
  352.  *
  353.  * Gets the next file in the directory
  354.  */
  355. DIRITEM
  356. dir_findnextfile(DIRLIST dl, DIRECT curdir)
  357. {
  358.         DIRITEM curfile;
  359.  
  360.         if ((dl == NULL) || (curdir == NULL)) {
  361.                 return(NULL);
  362.         }
  363.  
  364.         /* scan the subdir if necessary */
  365.         if (!curdir->bScanned) {
  366.                 dir_scan(curdir, FALSE);
  367.         }
  368.  
  369.         /* have we already read the files in this directory ? */
  370.         if (curdir->pos == DL_FILES) {
  371.                 /* no - return head of file list */
  372.                 curfile = (DIRITEM) List_First(curdir->diritems);
  373.                 if (curfile != NULL) {
  374.                         return(curfile);
  375.                 }
  376.  
  377.                 /* no more files - try the subdirs */
  378.                 curdir->pos = DL_DIRS;
  379.         }
  380.  
  381.         /* try the next subdir on the list, if any */
  382.         /* is this the first or the next */
  383.         if (curdir->curdir == NULL) {
  384.                 curdir->curdir = (DIRECT) List_First(curdir->directs);
  385.         } else {
  386.                 curdir->curdir = (DIRECT) List_Next(curdir->curdir);
  387.         }
  388.         /* did we find a subdir ? */
  389.         if (curdir->curdir == NULL) {
  390.  
  391.                 /* no more dirs - go back to parent if there is one */
  392.                 if (curdir->parent == NULL) {
  393.                         /* no parent - we have exhausted the tree */
  394.                         return(NULL);
  395.                 }
  396.  
  397.                 /* reset parent state to indicate this is the current
  398.                  * directory - so that next gets the next after this.
  399.                  * this ensures that multiple callers of dir_nextitem()
  400.                  * to the same tree work.
  401.                  */
  402.                 curdir->parent->pos = DL_DIRS;
  403.                 curdir->parent->curdir = curdir;
  404.  
  405.                 return(dir_findnextfile(dl, curdir->parent));
  406.         }
  407.  
  408.         /* there is a next directory - set it to the
  409.          * beginning and get the first file from it
  410.          */
  411.         curdir->curdir->pos = DL_FILES;
  412.         curdir->curdir->curdir = NULL;
  413.         return(dir_findnextfile(dl, curdir->curdir));
  414.  
  415. } /* dir_findnextfile */
  416.  
  417.  
  418. /*-- pathnames ----
  419.  *
  420.  * This module supports two types of pathnames, called relative and full.
  421.  * Relative names are relative to the root passed in the initial call
  422.  * to dir_build*, and full names include the tree root.
  423.  *
  424.  * Note that this is a different distinction to relative vs absolute
  425.  * pathnames, since the tree root may still be either relative or absolute.
  426.  *
  427.  * Examples:
  428.  *
  429.  *  - if you called dir_buildlist("c:\")
  430.  *              getrelname gives:               ".\config.sys"
  431.  *              getfullname gives:              "c:\config.sys"
  432.  *
  433.  * - if you called dir_buildlist(".\geraintd")
  434.  *              getrelname gives:               ".\source\scandir.h"
  435.  *              getfullname gives either
  436.  *                      ".\geraintd\source\scandir.h"
  437.  *                    or "c:\geraintd\source\scandir.h"
  438.  *                   (depending on the implementation).
  439.  *
  440.  * To support this, we maintain the tree root name in the DIRLIST head, and
  441.  * in each directory, the name of that directory relative to tree root.
  442.  * Files just have the filename, so we need to prepend the directory name,
  443.  * and (for getfullname) the tree root name as well
  444.  *
  445.  * We store the directory name with a trailing
  446.  * slash to make concatenation easier
  447.  *
  448.  * -----
  449.  */
  450.  
  451. /***************************************************************************
  452.  * Function: dir_getrelname
  453.  *
  454.  * Purpose:
  455.  *
  456.  * Return the name of the current file relative to tree root
  457.  */
  458. LPSTR
  459. dir_getrelname(DIRITEM cur)
  460. {
  461.         LPSTR name;
  462.         int size;
  463.  
  464.         /* check this is a valid item */
  465.         if (cur == NULL) {
  466.                 return(NULL);
  467.         }
  468.         /* remember to include the NULL when sizing */
  469.         size = lstrlen(cur->direct->relname) + lstrlen(cur->name) + 1;
  470.         name = gmem_get(hHeap, size);
  471.         lstrcpy(name, cur->direct->relname);
  472.         lstrcat(name, cur->name);
  473.  
  474.         return(name);
  475. } /* dir_getrelname */
  476.  
  477. /***************************************************************************
  478.  * Function: dir_getfullname
  479.  *
  480.  * Purpose:
  481.  *
  482.  * Return the fullname of the file (including the tree root passed in) 
  483.  */
  484. LPSTR
  485. dir_getfullname(DIRITEM cur)
  486. {
  487.         LPSTR name;
  488.         int size;
  489.         LPSTR head;
  490.  
  491.         /* check this is a valid item */
  492.         if (cur == NULL)  {
  493.                 return(NULL);
  494.         }
  495.  
  496.         if (cur->direct->head->bFile) {
  497.                 return(cur->direct->head->rootname);
  498.         }
  499.  
  500.         /* remember to include the NULL when sizing */
  501.         size = lstrlen(cur->name) + 1;
  502.  
  503.         size += lstrlen(cur->direct->relname);
  504.  
  505.         /* add on root name */
  506.         head = cur->direct->head->rootname;
  507.         size += lstrlen(head);
  508.  
  509.         /* root names may not end in a slash. we need to
  510.          * insert one in this case. Also, relnames always begin .\, so
  511.          * we skip the . always, and the .\ if we don't need to
  512.          * append a slash
  513.          *
  514.          */
  515.         size--;         /* omit the '.' */
  516.         if (*CharPrev(head, head+lstrlen(head)) == '\\') {
  517.                 size--;                         /* omit the .\ */
  518.         }
  519.  
  520.         name = gmem_get(hHeap, size);
  521.  
  522.         lstrcpy(name, cur->direct->head->rootname);
  523.  
  524.         /* add relname and then name, omiting the .\ */
  525.  
  526.                 /* skip . or .\ before relname */
  527.                 if (*CharPrev(head, head+lstrlen(head)) == '\\') {
  528.                         lstrcat(name, &cur->direct->relname[2]);
  529.                 } else {
  530.                         lstrcat(name, &cur->direct->relname[1]);
  531.                 }
  532.                 lstrcat(name, cur->name);
  533.         return(name);
  534. } /* dir_getfullname */
  535.  
  536.  
  537. /***************************************************************************
  538.  * Function: dir_getroot_list
  539.  *
  540.  * Purpose:
  541.  *
  542.  * Return the name of the tree root given a handle to the DIRLIST.
  543.  */
  544. LPSTR
  545. dir_getroot_list(DIRLIST dl)
  546. {
  547.         if (dl == NULL) 
  548.                 return(NULL);
  549.         return(dl->rootname);
  550. } /* dir_getroot_list */
  551.  
  552. /***************************************************************************
  553.  * Function: dir_getroot_item
  554.  *
  555.  * Purpose:
  556.  *
  557.  * Return the root name of this tree given a handle to a DIRITEM in the
  558.  * list.
  559.  */
  560. LPSTR dir_getroot_item(DIRITEM item)
  561. {
  562.         if (item == NULL) 
  563.                 return(NULL);
  564.  
  565.         return(dir_getroot_list(item->direct->head));
  566. }
  567.  
  568.  
  569. /***************************************************************************
  570.  * Function: dir_freerelname
  571.  *
  572.  * Purpose:
  573.  *
  574.  * Free up a relname that we allocated. This interface allows us
  575.  * some flexibility in how we store relative and complete names
  576.  *
  577.  */
  578. void
  579. dir_freerelname(DIRITEM cur, LPSTR name)
  580. {
  581.         if((cur != NULL) && (name != NULL))
  582.                         gmem_free(hHeap, name, lstrlen(name) +1);
  583. } /* dir_freerelname */
  584.  
  585. /***************************************************************************
  586.  * Function: dir_freefullname
  587.  *
  588.  * Purpose:
  589.  *
  590.  */
  591. void
  592. dir_freefullname(DIRITEM cur, LPSTR name)
  593. {
  594.         if (cur->direct->head->bFile)
  595.                 return;
  596.  
  597.         if (name != NULL) 
  598.                 gmem_free(hHeap, name, lstrlen(name) + 1);
  599. } /* dir_freefullname            */
  600.  
  601. /***************************************************************************
  602.  * Function: dir_freeroot_list
  603.  *
  604.  * Purpose:
  605.  *
  606.  * Free up rootname allocated by dir_getroot_list.
  607.  * We just gave a pointer to the rootname, so do nothing.
  608.  */
  609. void
  610. dir_freeroot_list(DIRLIST dl, LPSTR name)
  611. {
  612.         if ((dl == NULL) || (name == NULL)) {
  613.                 return;
  614.         }
  615.         return;
  616. } /* dir_freeroot_list */
  617.  
  618. /***************************************************************************
  619.  * Function: dir_freeroot_item
  620.  *
  621.  * Purpose:
  622.  *
  623.  * Free up memory alloc-ed by a call to dir_getroot_item. 
  624.  */
  625. void
  626. dir_freeroot_item(DIRITEM item, LPSTR name)
  627. {
  628.         if ((item == NULL) || (name == NULL)) 
  629.                 return;
  630.         dir_freeroot_list(item->direct->head, name);
  631. }
  632.  
  633. /***************************************************************************
  634.  * Function: dir_getopenname
  635.  *
  636.  * Purpose:
  637.  *
  638.  * Get an open-able name for the file. This will be the same as the fullname.
  639.  */
  640. LPSTR
  641. dir_getopenname(DIRITEM item)
  642. {
  643.         LPSTR fname;
  644.  
  645.         if (item == NULL) 
  646.                 return(NULL);
  647.  
  648.         fname = dir_getfullname(item);
  649.  
  650.                 return(fname);
  651. } /* dir_getopenname */
  652.  
  653.  
  654. /***************************************************************************
  655.  * Function: dir_freeopenname
  656.  *
  657.  * Purpose:
  658.  *
  659.  * Free up memory created by a call to dir_getopenname(). This *may*
  660.  * cause the file to be deleted if it was a temporary copy.
  661.  */
  662. void
  663. dir_freeopenname(DIRITEM item, LPSTR openname)
  664. {
  665.         if ((item == NULL) || (openname == NULL)) 
  666.                 return;
  667.  
  668.         dir_freefullname(item, openname);
  669. } /* dir_freeopenname */
  670.  
  671. /***************************************************************************
  672.  * Function: dir_openfile
  673.  *
  674.  * Purpose:
  675.  *
  676.  * Return an open file handle to the file. 
  677.  */
  678. int
  679. dir_openfile(DIRITEM item)
  680. {
  681.         LPSTR fname;
  682.         int fh;
  683.         OFSTRUCT os;
  684.  
  685.         fname = dir_getfullname(item);
  686.         fh = OpenFile(fname, &os, OF_READ|OF_SHARE_DENY_NONE);
  687.         dir_freefullname(item, fname);
  688.         return(fh);
  689. } /* dir_openfile */
  690.  
  691. /***************************************************************************
  692.  * Function: dir_closefile
  693.  *
  694.  * Purpose:
  695.  *
  696.  * Close a file opened with dir_openfile.
  697.  */
  698. void
  699. dir_closefile(DIRITEM item, int fh)
  700. {
  701.         _lclose(fh);
  702.  
  703. } /* dir_closefile */
  704.  
  705.  
  706. /***************************************************************************
  707.  * Function: dir_getfilesize
  708.  *
  709.  * Purpose:
  710.  *
  711.  * Return the file size (set during scanning) 
  712.  */
  713. long
  714. dir_getfilesize(DIRITEM cur)
  715. {
  716.         /* check this is a valid item */
  717.         if (cur == NULL)
  718.                 return(0);
  719.  
  720.         return(cur->size);
  721. } /* dir_getfilesize */
  722.  
  723.  
  724.  
  725. /* ss_endcopy returns a number indicating the number of files copied,
  726.    but we may have some local copies too.  We need to count these
  727.    ourselves and add them in
  728. */
  729.  
  730. int nLocalCopies;        /* cleared in startcopy, ++d in copy
  731.                                 ** inspected in endcopy
  732.                                 */
  733.  
  734. /***************************************************************************
  735.  * Function: dir_startcopy
  736.  *
  737.  * Purpose:
  738.  *
  739.  * Start a bulk copy 
  740.  */
  741. BOOL dir_startcopy(DIRLIST dl)
  742. {
  743.         nLocalCopies = 0;
  744.         return(TRUE);
  745.  
  746. } /* dir_startcopy */
  747. /***************************************************************************
  748.  * Function: dir_endcopy
  749.  *
  750.  */
  751.  
  752. int dir_endcopy(DIRLIST dl)
  753. {
  754.         return(nLocalCopies);
  755.  
  756. } /* dir_endcopy */
  757.  
  758. /***************************************************************************
  759.  * Function: dir_copy
  760.  *
  761.  * Purpose:
  762.  *
  763.  * Create a copy of the file, in the new root directory. Creates sub-dirs as
  764.  * necessary. 
  765.  *
  766.  * Returns TRUE for success and FALSE for failure.
  767.  */
  768. BOOL dir_copy(DIRITEM item, LPSTR newroot)
  769. {
  770.         static char newpath[256];
  771.         LPSTR relname, fullname;
  772.         LPSTR pstart, pdest, pel;
  773.         BOOL bOK;
  774.  
  775.         BY_HANDLE_FILE_INFORMATION bhfi;
  776.         HANDLE hfile;
  777.  
  778.         /*
  779.          * check that the newroot directory itself exists
  780.          */
  781.         if ((item == NULL) || !dir_isvaliddir(newroot)) {
  782.                 return(FALSE);
  783.         }
  784.  
  785.         /*
  786.          * name of file relative to the tree root
  787.          */
  788.         relname = dir_getrelname(item);
  789.  
  790.         /*
  791.          * build the new pathname by concatenating the new root and
  792.          * the old relative name. add one path element at a time and
  793.          * ensure that the directory exists, creating it if necessary.
  794.          */
  795.         lstrcpy(newpath, newroot);
  796.  
  797.         /* add separating slash if not already there */
  798.         if (*CharPrev(newpath, newpath+lstrlen(newpath)) != '\\') {
  799.                 lstrcat(newpath, "\\");
  800.         }
  801.  
  802.         pstart = relname;
  803.         while ( (pel = strchr(pstart, '\\')) != NULL) {
  804.  
  805.                 /* found another element ending in slash. incr past the \\ */
  806.                 pel++;
  807.  
  808.                 /*
  809.                  * ignore .
  810.                  */
  811.                 if (strncmp(pstart, ".\\", 2) != 0) {
  812.  
  813.                         pdest = &newpath[lstrlen(newpath)];
  814.                         strncpy(pdest, pstart, pel - pstart);
  815.                         pdest[pel - pstart] = '\0';
  816.  
  817.                         /* create subdir if necessary */
  818.                         if (!dir_isvaliddir(newpath)) {
  819.                                 if (_mkdir(newpath) != 0) {
  820.                                         return(FALSE);
  821.                                 }
  822.                         }
  823.                 }
  824.  
  825.                 pstart = pel;
  826.         }
  827.  
  828.         /*
  829.          * there are no more slashes, so pstart points at the final
  830.          * element
  831.          */
  832.         lstrcat(newpath, pstart);
  833.  
  834.         fullname = dir_getfullname(item);
  835.  
  836.                 bOK = CopyFile(fullname, newpath, FALSE);
  837.  
  838.                 /* having copied the file, now copy the times, attributes */
  839.                 hfile = CreateFile(fullname, GENERIC_READ, 0, NULL,
  840.                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  841.                 bhfi.dwFileAttributes = GetFileAttributes(fullname);
  842.                 GetFileTime(hfile, &bhfi.ftCreationTime,
  843.                                 &bhfi.ftLastAccessTime, &bhfi.ftLastWriteTime);
  844.                 CloseHandle(hfile);
  845.  
  846.                 hfile = CreateFile(newpath, GENERIC_WRITE, 0, NULL,
  847.                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  848.                 SetFileTime(hfile, &bhfi.ftCreationTime,
  849.                                    &bhfi.ftLastAccessTime,
  850.                                    &bhfi.ftLastWriteTime);
  851.                 CloseHandle(hfile);
  852.                 SetFileAttributes(newpath, bhfi.dwFileAttributes);
  853.  
  854.  
  855.                 if (bOK) ++nLocalCopies;
  856.  
  857.         dir_freerelname(item, relname);
  858.         dir_freefullname(item, fullname);
  859.  
  860.         return(bOK);
  861. } /* dir_copy */
  862.  
  863.  
  864. /***************************************************************************
  865.  * Function: dir_dirinit
  866.  *
  867.  * Purpose:
  868.  *
  869.  * Fill out a new DIRECT for a subdirectory (pre-allocated).
  870.  * Init files and dirs lists to empty (List_Create). Set the relname
  871.  * of the directory by pre-pending the parent relname if there
  872.  * is a parent, and appending a trailing slash (if there isn't one).
  873.  */
  874. void
  875. dir_dirinit(DIRECT dir, DIRLIST head, DIRECT parent, LPSTR name)
  876. {
  877.         int size;
  878.  
  879.         dir->head = head;
  880.         dir->parent = parent;
  881.  
  882.         /* add on one for the null and one for the trailing slash */
  883.         size = lstrlen(name) + 2;
  884.         if (parent != NULL) {
  885.                 size += lstrlen(parent->relname);
  886.         }
  887.  
  888.         /* build the relname from the parent and the current name
  889.          * with a terminating slash
  890.          */
  891.         dir->relname = gmem_get(hHeap, size);
  892.         if (parent != NULL) {
  893.                 lstrcpy(dir->relname, parent->relname);
  894.         } else{
  895.                 dir->relname[0] = '\0';
  896.         }
  897.  
  898.         lstrcat(dir->relname, name);
  899.  
  900.         if (*CharPrev(dir->relname,
  901.                         dir->relname+lstrlen(dir->relname)) != '\\') {
  902.                 lstrcat(dir->relname, "\\");
  903.         }
  904.  
  905.         /* force name to lowercase */
  906.         AnsiLowerBuff(dir->relname, lstrlen(dir->relname));
  907.  
  908.         dir->diritems = List_Create();
  909.         dir->directs = List_Create();
  910.         dir->bScanned = FALSE;
  911.         dir->pos = DL_FILES;
  912.  
  913. } /* dir_dirinit */
  914.  
  915.  
  916. /***************************************************************************
  917.  * Function: dir_fileinit
  918.  *
  919.  * Purpose:
  920.  *
  921.  * Initialise the contents of an (allocated) DIRITEM struct. 
  922.  */
  923. void
  924. dir_fileinit(DIRITEM pfile, DIRECT dir, LPSTR path, long size)
  925. {
  926.  
  927.         pfile->name = gmem_get(hHeap, lstrlen(path) + 1);
  928.         lstrcpy(pfile->name, path);
  929.  
  930.         /* force name to lower case */
  931.         AnsiLowerBuff(pfile->name, lstrlen(path));
  932.  
  933.         pfile->direct = dir;
  934.         pfile->size = size;
  935.  
  936.         pfile->localname = NULL;
  937.  
  938. } /* dir_fileinit */
  939.  
  940. /***************************************************************************
  941.  * Function: dir_isfilevalid
  942.  *
  943.  * Purpose:
  944.  *
  945.  * Is this a valid file or not 
  946.  */
  947. BOOL
  948. dir_isvalidfile(LPSTR path)
  949. {
  950.         DWORD dwAttrib;
  951.  
  952.         dwAttrib = GetFileAttributes(path);
  953.         if (dwAttrib == -1) {
  954.                 return(FALSE);
  955.         }
  956.         if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
  957.                 return(FALSE);
  958.         }
  959.         return(TRUE);
  960. } /* dir_isvalidfile */
  961.  
  962. /***************************************************************************
  963.  * Function: dir_isvaliddir
  964.  *
  965.  * Purpose:
  966.  *
  967.  * Is this a valid directory ? 
  968.  */
  969. BOOL
  970. dir_isvaliddir(LPSTR path)
  971. {
  972.         DWORD dwAttrib;
  973.  
  974.         dwAttrib = GetFileAttributes(path);
  975.         if (dwAttrib == -1) {
  976.                 return(FALSE);
  977.         }
  978.         if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
  979.                 return(TRUE);
  980.         }
  981.         return(FALSE);
  982. } /* dir_isvaliddir */
  983.  
  984. /***************************************************************************
  985.  * Function: dir_scan
  986.  *
  987.  * Purpose:
  988.  *
  989.  * Scan the directory given. Add all files to the list
  990.  * in alphabetic order, and add all directories in alphabetic
  991.  * order to the list of child DIRITEMs. If bRecurse is true, go on to
  992.  * recursive call dir_scan for each of the child DIRITEMs
  993.  */
  994. void
  995. dir_scan(DIRECT dir, BOOL bRecurse)
  996. {
  997.         PSTR path;
  998.         int size;
  999.         DIRECT child;
  1000.         BOOL bMore;
  1001.         long filesize;
  1002.         BOOL bIsDir;
  1003.         LPSTR name;
  1004.  
  1005.         HANDLE hFind;
  1006.         WIN32_FIND_DATA finddata;
  1007.  
  1008.         char debugmsg[200];
  1009.         wsprintf(debugmsg, "scandir: %s %s\n",
  1010.                  dir->relname, bRecurse?"recursive":"non-recursive"
  1011.                 );
  1012.  
  1013.         /* make the complete search string including *.* */
  1014.         size = lstrlen(dir->head->rootname);
  1015.         size += lstrlen(dir->relname);
  1016.  
  1017.         /* add on one null and *.* */
  1018.         size += 4;
  1019.  
  1020.         path = LocalLock(LocalAlloc(LHND, size));
  1021.  
  1022.         lstrcpy(path, dir->head->rootname);
  1023.  
  1024.         /* omit the . at the beginning of the relname, and the
  1025.          * .\ if there is a trailing \ on the rootname
  1026.          */
  1027.         if (*CharPrev(path, path+lstrlen(path)) == '\\') {
  1028.                 lstrcat(path, &dir->relname[2]);
  1029.         } else {
  1030.                 lstrcat(path, &dir->relname[1]);
  1031.         }
  1032.         lstrcat(path, "*.*");
  1033.  
  1034.         /* read all entries in the directory */
  1035.         hFind = FindFirstFile(path, &finddata);
  1036.         bMore = (hFind != (HANDLE) -1);
  1037.         LocalUnlock(LocalHandle ( (PSTR) path));
  1038.         LocalFree(LocalHandle ( (PSTR) path));
  1039.  
  1040.         while (bMore) {
  1041.  
  1042.                 bIsDir = (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  1043.                 name = (LPSTR) &finddata.cFileName;
  1044.                 filesize = finddata.nFileSizeLow;
  1045.                 if (!bIsDir) {
  1046.  
  1047.                         dir_addfile(dir, name, filesize);
  1048.  
  1049.                 } else if ( (lstrcmp(name, ".") != 0) &&
  1050.                            ( lstrcmp(name, "..") != 0) ) {
  1051.  
  1052.                         dir_adddirect(dir, name);
  1053.                 }
  1054.  
  1055.                 bMore = FindNextFile(hFind, &finddata);
  1056.         }
  1057.  
  1058.         FindClose(hFind);
  1059.  
  1060.         dir->bScanned = TRUE;
  1061.         dir->pos = DL_FILES;
  1062.  
  1063.         if (bRecurse) {
  1064.                 List_TRAVERSE(dir->directs, child) {
  1065.                         dir_scan(child, TRUE);
  1066.                 }
  1067.         }
  1068.  
  1069. } /* dir_scan */
  1070.  
  1071.  
  1072. /***************************************************************************
  1073.  * Function: dir_addfile
  1074.  *
  1075.  * Purpose:
  1076.  *
  1077.  * Add the file 'path' to the list of files in dir, in order.
  1078.  */
  1079. void
  1080. dir_addfile(DIRECT dir, LPSTR path, DWORD size)
  1081. {
  1082.         DIRITEM pfile;
  1083.  
  1084.         AnsiLowerBuff(path, lstrlen(path));  // needless?
  1085.  
  1086.         List_TRAVERSE(dir->diritems, pfile) {
  1087.                 /////if (lstrcmpi(pfile->name, path) > 0) {
  1088.                 if (utils_CompPath(pfile->name, path) > 0) {
  1089.  
  1090.                         /* goes before this one */
  1091.                         pfile = List_NewBefore(dir->diritems, pfile, sizeof(struct diritem));
  1092.                         dir_fileinit(pfile, dir, path, size);
  1093.                         return;
  1094.                 }
  1095.         }
  1096.         /* goes at end */
  1097.         pfile = List_NewLast(dir->diritems, sizeof(struct diritem));
  1098.         dir_fileinit(pfile, dir, path, size);
  1099. } /* dir_addfile */
  1100.  
  1101.  
  1102. /***************************************************************************
  1103.  * Function: dir_addirect
  1104.  *
  1105.  * Purpose:
  1106.  *
  1107.  * Add a new directory in alphabetic order on
  1108.  * the list dir->directs
  1109.  *
  1110.  */
  1111. void
  1112. dir_adddirect(DIRECT dir, LPSTR path)
  1113. {
  1114.         DIRECT child;
  1115.         LPSTR finalel;
  1116.         char achTempName[256];
  1117.  
  1118.         AnsiLowerBuff(path, lstrlen(path));
  1119.         List_TRAVERSE(dir->directs, child) {
  1120.  
  1121.                 int cmpval;
  1122.  
  1123.                 /* we need to compare the child name with the new name.
  1124.                  * the child name is a relname with a trailing
  1125.                  * slash - so compare only the name up to but
  1126.                  * not including the final slash.
  1127.                  */
  1128.                 finalel = dir_finalelem(child->relname);
  1129.  
  1130.                 /*
  1131.                  * we cannot use strnicmp since this uses a different
  1132.                  * collating sequence to lstrcmpi. So copy the portion
  1133.                  * we are interested in to a null-term. buffer.
  1134.                  */
  1135.                 strncpy(achTempName, finalel, lstrlen(finalel)-1);
  1136.                 achTempName[lstrlen(finalel)-1] = '\0';
  1137.  
  1138.                 cmpval = utils_CompPath(achTempName, path);
  1139.  
  1140.                 if (cmpval > 0) {
  1141.  
  1142.                         /* goes before this one */
  1143.                         child = List_NewBefore(dir->directs, child, sizeof(struct direct));
  1144.                         dir_dirinit(child, dir->head, dir, path);
  1145.                         return;
  1146.                 }
  1147.         }
  1148.         /* goes at end */
  1149.         child = List_NewLast(dir->directs, sizeof(struct direct));
  1150.         dir_dirinit(child, dir->head, dir, path);
  1151. } /* dir_adddirect */
  1152.  
  1153.  
  1154. /***************************************************************************
  1155.  * Function: dir_cleardirect
  1156.  *
  1157.  * Purpose:
  1158.  *
  1159.  * Free all memory associated with a DIRECT (including freeing
  1160.  * child lists). Don't de-alloc the direct itself (allocated on a list)
  1161.  */
  1162. void
  1163. dir_cleardirect(DIRECT dir)
  1164. {
  1165.         DIRITEM pfile;
  1166.         DIRECT child;
  1167.  
  1168.         /* clear contents of files list */
  1169.         List_TRAVERSE(dir->diritems, pfile) {
  1170.                 gmem_free(hHeap, pfile->name, lstrlen(pfile->name));
  1171.                 if ((pfile->localname) && (pfile->bLocalIsTemp)) {
  1172.  
  1173.                         /*
  1174.                          * the copy will have copied the attributes,
  1175.                          * including read-only. We should unset this bit
  1176.                          * so we can delete the temp file.
  1177.                          */
  1178.                         SetFileAttributes(pfile->localname,
  1179.                                 GetFileAttributes(pfile->localname)
  1180.                                         & ~FILE_ATTRIBUTE_READONLY);
  1181.                         DeleteFile(pfile->localname);
  1182.                         gmem_free(hHeap, pfile->localname, 256);
  1183.                         pfile->localname = NULL;
  1184.                 }
  1185.  
  1186.         }
  1187.         List_Destroy(&dir->diritems);
  1188.  
  1189.         /* clear contents of dirs list (recursively) */
  1190.         List_TRAVERSE(dir->directs, child) {
  1191.                 dir_cleardirect(child);
  1192.         }
  1193.         List_Destroy(&dir->directs);
  1194.  
  1195.         gmem_free(hHeap, dir->relname, lstrlen(dir->relname) + 1);
  1196.  
  1197. } /* dir_cleardirect */
  1198.  
  1199. /***************************************************************************
  1200.  * Function: dir_finalelem
  1201.  *
  1202.  * Purpose:
  1203.  *
  1204.  * Return a pointer to the final element in a path. Note that
  1205.  * we may be passed relnames with a trailing final slash - ignore this
  1206.  * and return the element before that final slash.
  1207.  */
  1208. LPSTR
  1209. dir_finalelem(LPSTR path)
  1210. {
  1211.         LPSTR chp;
  1212.         int size;
  1213.  
  1214.         /* is the final character a slash ? */
  1215.         size = lstrlen(path) - 1;
  1216.         if (*(chp = CharPrev(path, path+lstrlen(path))) == '\\') {
  1217.                 /* find the slash before this */
  1218.                 while (chp > path) {
  1219.                         if (*(chp = CharPrev(path, chp)) == '\\') {
  1220.                                 /* skip the slash itself */
  1221.                                 chp++;
  1222.                                 break;
  1223.                         }
  1224.                 }
  1225.                 return(chp);
  1226.         }
  1227.         /* look for final slash */
  1228.         chp = strrchr(path, '\\');
  1229.         if (chp != NULL) {
  1230.                 return(chp+1);
  1231.         }
  1232.  
  1233.         /* no slash - is there a drive letter ? */
  1234.         chp = strrchr(path, ':');
  1235.         if (chp != NULL) {
  1236.                 return(chp+1);
  1237.         }
  1238.  
  1239.         /* this is a final-element anyway */
  1240.         return(path);
  1241.  
  1242. } /* dir_finalelem */
  1243.  
  1244. /***************************************************************************
  1245.  * Function: dir_getpathsize
  1246.  *
  1247.  * Purpose:
  1248.  *
  1249.  * Find the size of a file given a pathname to it 
  1250.  */
  1251. long
  1252. dir_getpathsize(LPSTR path)
  1253. {
  1254.         int fh;
  1255.         OFSTRUCT os;
  1256.         long size;
  1257.         fh = OpenFile(path, &os, OF_READ|OF_SHARE_DENY_NONE);
  1258.         if (fh == -1) {
  1259.                 return(0);
  1260.         }
  1261.  
  1262.         size = GetFileSize( (HANDLE) fh, NULL);
  1263.         _lclose(fh);
  1264.         return(size);
  1265. } /* dir_getpathsize */
  1266.  
  1267.  
  1268.