home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / diskutil / docp / docp.c < prev    next >
C/C++ Source or Header  |  1993-08-03  |  52KB  |  2,235 lines

  1. /*
  2.     docp.c - Directory-Oriented CoPy
  3.  
  4.     Fancy file copy program for Atari ST
  5.  
  6.     Copywrite 1992, Roy Bixler
  7.     Originally by: David Oertel
  8.     Atari ST port, overall cheez-whiz: Roy Bixler
  9.  
  10.     This program is free software; you can redistribute it and/or modify
  11.     it under the terms of the GNU General Public License as published by
  12.     the Free Software Foundation; either version 1, or (at your option)
  13.     any later version.
  14.  
  15.     This program is distributed in the hope that it will be useful,
  16.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.     GNU General Public License for more details.
  19.  
  20.     You should have received a copy of the GNU General Public License
  21.     along with this program; if not, write to the Free Software
  22.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23.  
  24. */
  25.  
  26. #include <ctype.h>
  27. #include <fcntl.h>
  28. #include <osbind.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <types.h>
  33. #include <stat.h>
  34. #include <unistd.h>
  35.  
  36. #define MINT_LIB
  37. #ifdef MINT_LIB
  38.  
  39. /* long _read(int, void *, long); */
  40. /* long _write(int, const void *, long); */
  41.  
  42. #define read(x, y, z) _read(x, y, z)
  43. #define write(x, y, z) _write(x, y, z)
  44.  
  45. #else
  46.  
  47. #define read(x, y, z) lread(x, y, z)
  48. #define write(x, y, z) lwrite(x, y, z)
  49.  
  50. #endif
  51.  
  52. #define MAXDIR    128
  53. #define MAXPATH    128
  54.  
  55. #define OPT_LIST "abcdfghijlmnorstvwz?"
  56. #define GET_OPT_LIST "AaBbCcD:d:F:f:GgHhIiJjLlMmNnOoRrSsTtVvW:w:Zz?"
  57. #define DOT_NOTATION(dir) !strcmp(dir, ".")
  58. #define MAX_BUF 0xfffeU
  59.  
  60.  
  61. #include "docp.h"
  62. #include "doc.h"
  63. #include "elib.h"
  64. #include "protypes.h"
  65.  
  66. /* options */
  67.  
  68. extern int Optind;
  69. extern char *optarg;
  70. long Options = 0L;
  71. char Cur_source_dir[FILENAME_MAX], Org_dest_dir[FILENAME_MAX];
  72. ENTRY *Ptr;
  73. void *Buf_ptr;
  74. void *File_buf;
  75. int Reading_flag;    /* set if 'reading:' has been printed last
  76.                      * reset if 'writing:' has been printed last */
  77.  
  78. /* modes of date compare via 'd' option */
  79. #define D_BEFORE 1
  80. #define D_ON 2
  81. #define D_AFTER 4
  82.  
  83. DATE_NODE *Fdate;        /* used to store date entered via 'd' option */
  84. TIME_NODE *Ftime;        /* used to store time entered via 'w' option */
  85.  
  86. FILE *From_file_ptr;    /* file containing file list ('-f' option) */
  87. int Retry = 0;        /* set if target disk is full and user chooses to
  88.                      * continue */
  89. int Copied_a_file = 0;  /* set if at least one file was copied */
  90.  
  91. ENTRY *Src_tab[HASH_TAB_SIZE]; /* contains source-file names */
  92. ENTRY *Dst_tab[HASH_TAB_SIZE]; /* contains destination-file names */
  93.  
  94.  
  95. typedef struct file_list {
  96.     char *string;
  97.     struct file_list *next;
  98. } STDIN_TOKEN;
  99.  
  100. /* linked list globals containing file list from stdin */
  101. STDIN_TOKEN *Stdin_list_head = NULL;
  102. STDIN_TOKEN *Stdin_list_tail = NULL;
  103. STDIN_TOKEN *Stdin_list_current = NULL;
  104.  
  105. int main(int argc, char *argv[])
  106. {
  107.     char src_dir[FILENAME_MAX];
  108.     char dst_dir[FILENAME_MAX];
  109.     unsigned long buf_size;
  110.     int num_args;
  111.     char **argv_ptr;
  112.     char *all[2] = { "*.*", NULL };
  113.  
  114.       get_flags(argv, argc);
  115.     check_flags();
  116.     check_and_format_dirs(argc, argv, src_dir, dst_dir);
  117.     check_target_removeable(dst_dir);
  118.     File_buf = get_file_buf(&buf_size);
  119.     Buf_ptr = File_buf;
  120.     argv += Optind + 2;
  121.     num_args = argc - Optind - 2;
  122.     argv_ptr = argv;
  123.     if (Options & O_FROM_STDIN)
  124.         build_stdin_file_list(&num_args);
  125.     else if (Options & O_FROM_FILE){
  126.         char buf[80];
  127.  
  128.         num_args = 0;
  129.         while (fgets(buf, 79, From_file_ptr) != NULL)
  130.             num_args++;
  131.         rewind(From_file_ptr);
  132.     } else if (num_args == 0) {
  133.         num_args = 1;
  134.         argv_ptr = all;
  135.     }
  136.  
  137.     strcpy(Org_dest_dir, dst_dir);
  138.     if (Options & O_CHECK)
  139.         fprintf(stdout, "The following files would be copied or moved:\n");
  140.     else if (Options & O_ZAPTARGET)
  141.         zap_target(dst_dir, 1);
  142.     copy_files(src_dir, dst_dir, argv_ptr, 
  143.                num_args, File_buf, buf_size);
  144.     write_buffer(File_buf);
  145.     clear_archive_bits(src_dir);
  146.  
  147.     if (!Copied_a_file)
  148.         fprintf(stdout, "no files copied\n");
  149.  
  150.     free(File_buf);
  151.     exit(0);
  152. }
  153.  
  154. /*
  155.  *    check_and_format_dirs()
  156.  *
  157.  *    Input:
  158.  *        argc - command line argument count
  159.  *        argv - command line arguments
  160.  *        Optind - argument index from getopt()
  161.  *    Output:
  162.  *        src_dir - source directory name
  163.  *        dst_dir - destination directory name
  164.  *    Comments:
  165.  *        The source and destination directories are formatted
  166.  *        The source directory is checked for existence
  167.  *        The destination directory is created if it doesn't exist
  168.  *        The two directories are checked to insure that they are
  169.  *            different
  170.  *        The destination is checked to insure that it is not a
  171.  *            subdirectory of the source
  172.  */
  173.  
  174. void check_and_format_dirs(int argc, char *argv[],
  175.     char *src_dir, char *dst_dir)
  176. {
  177.     if ((argc-Optind) < 2)
  178.         usage();
  179.  
  180.     strcpy(src_dir, argv[Optind]);
  181.     check_if_dir_exists(src_dir, 0);
  182.     strcpy(dst_dir, argv[Optind + 1]);
  183.     check_if_dir_exists(dst_dir, 1);
  184.  
  185.     format_dir(argv[Optind], '\1', src_dir);
  186.     format_dir(argv[Optind + 1], '\1', dst_dir);
  187.  
  188.     check_if_dirs_compatible(src_dir, dst_dir);
  189. }
  190.  
  191. /*
  192.  *    check_target_removeable()
  193.  *
  194.  *    Input:
  195.  *        dst_dir - destination directory name
  196.  *    Output:
  197.  *        Options - will be modified if target directory is removeable
  198.  *                  (i.e. a floppy disk)
  199.  */
  200.  
  201. void check_target_removeable(char *dst_dir)
  202.  
  203. {
  204.     char dst_drive;
  205.  
  206.     if (islower(dst_drive = dst_dir[0]))
  207.         dst_drive = toupper(dst_drive);
  208.     if ((dst_drive == 'A') || (dst_drive == 'B'))
  209.         Options |= O_TARGET_REMOVEABLE;
  210. }
  211.  
  212. /*
  213.  *    check_if_dirs_compatible()
  214.  *
  215.  *    Input:
  216.  *        src_dir - The formatted source directory name
  217.  *        dst_dir - The formatted destination directory name
  218.  *    Comments:
  219.  *        terminates if:
  220.  *            1 - The two directories are the same
  221.  *            2 - The destination is a subdirectory of 
  222.  *                the source
  223.  */
  224.  
  225. void check_if_dirs_compatible(char *src_dir, char *dst_dir)
  226.  
  227. {
  228.     if (Options & O_CHECK)
  229.         return;
  230.     if (!strcmp(src_dir, dst_dir)){
  231.         fprintf(stderr,
  232.             "source and destination directories are the same\n");
  233.         exit(-1);
  234.     }
  235.     if ((Options & O_RECURSIVE) &&
  236.         !strncmp(src_dir, dst_dir, strlen(src_dir))){
  237.         fprintf(stderr,
  238.             "destination directory is a subdirectory of the source directory \nwhile in recursive mode\n");
  239.         exit(-1);
  240.     }
  241. }
  242.  
  243. /*
  244.  *    check_if_dir_exists()
  245.  *
  246.  *    Input:
  247.  *        dir - the directory name
  248.  *        is_dest - flag set if directory is a destination directory
  249.  *    Comments:
  250.  *        creates destination directory if it doesn't exist
  251.  *        terminates if the source directory doesn't exist
  252.  */
  253.  
  254. void check_if_dir_exists(char *dir, int is_dest)
  255.  
  256. {
  257.     int retval;
  258.     struct stat statbuf;
  259.  
  260.     if (!((DOT_NOTATION(dir)) ||
  261.           ((!stat(dir, &statbuf)) && (statbuf.st_mode & S_IFDIR))))
  262.         if ((is_dest) && ((Options & O_BATCH) ||
  263.                           (printf("directory %s does not exist - ", dir),
  264.                            ask_user("create it (Y/N/Q) ? "))))
  265.             create_dir(dir);
  266.         else {
  267.             printf("docp: directory '%s' does not exist\n", dir);
  268.             exit(-1);
  269.         }
  270. }
  271.  
  272. /*
  273.  *    create_dir()
  274.  *
  275.  *    Input:
  276.  *        dir - the directory to be created
  277.  *    Comments:
  278.  *        creates destination directory
  279.  */
  280.  
  281. void create_dir(char *dir)
  282.  
  283. {
  284.     int retval;
  285.     char next_dir[MAXDIR], *p;
  286.  
  287.     strcpy(next_dir, dir);
  288.     for (p=next_dir; *p; p++)
  289.         if (*p == '/')
  290.             *p = '\\';
  291.     
  292.     p = next_dir;
  293.     while ((p = strchr(p, '\\')) != NULL) {
  294.         *p = '\0';
  295.         mkdir(next_dir, 0);
  296.         *p++ = '\\';
  297.     }
  298.     ;
  299.     if (mkdir(dir, 0) == -1){
  300.         fprintf(stderr, "cannot create directory '%s'\n", dir);
  301.           exit(-1);
  302.       }
  303.     else if (Options & O_VERBOSE)
  304.         printf("created directory '%s'\n", dir);
  305. }
  306.   
  307. /*
  308.  *     return_to_cur_dir()
  309.  *
  310.  *    Input:
  311.  *        cur_dir - the current working directory
  312.  *        other_dir - the current working directory of the
  313.  *            destination drive
  314.  *    Comments:
  315.  *        returns to current working directory
  316.  */
  317.  
  318. void return_to_cur_dir(char *cur_dir)
  319. {
  320.     chdir(cur_dir);
  321. }
  322.  
  323. /*
  324.  *    root_dir()
  325.  *
  326.  *    Input:
  327.  *        dir - the directory to be checked
  328.  *    Output:
  329.  *        returns : 
  330.  *            1 - if 'dir' is a root directory
  331.  *            0 - otherwise
  332.  */
  333.  
  334. int root_dir(char *dir)
  335. {
  336.     if (((*(dir + 1) == ':') && (strlen(dir) == 2)) ||
  337.         ((strlen(dir) == 3) && (*(dir + 2) == '\\')))
  338.             return(1);
  339.     else
  340.         return(0);
  341. }
  342.  
  343. /*
  344.  *    copy_files()
  345.  *
  346.  *    Input:
  347.  *        src_dir - the source directory
  348.  *        dst_dir - the destination directory
  349.  *        file_spec - the command-line file list
  350.  *        num_args - the number of arguments in the file list
  351.  *        file_buf - the buffer for reading and writing files
  352.  *        buf_size - the size of 'file_buf'
  353.  *    Comments:
  354.  *        copy or move the files from the source directory to the
  355.  *        destination directory
  356.  */
  357.  
  358. void copy_files(char *src_dir, char *dst_dir, char *file_spec[], int num_args, 
  359.                 void *file_buf, unsigned long buf_size)
  360.  
  361. {
  362.     struct _dta fblk;
  363.     char ref_list[FILENAME_MAX];
  364.     char src_file[FILENAME_MAX], dst_file[FILENAME_MAX];
  365.     int done, index;
  366.     long file_size;
  367.     char *file;
  368.  
  369.     strcpy(Cur_source_dir, src_dir);
  370.     build_hash_tab(src_dir, Src_tab, num_args, file_spec);
  371.     build_hash_tab(dst_dir, Dst_tab, num_args, file_spec);
  372.  
  373.     file = get_first(&index);
  374.     while (file != NULL) {
  375.         strcpy(src_file, src_dir);
  376.         strcat(src_file, file);
  377.  
  378.         strcpy(dst_file, dst_dir);
  379.         strcat(dst_file, file);
  380.         if (should_file_copy(file, src_file))
  381.             if (Options & O_CHECK)
  382.                 fprintf(stdout, "\t%s -> %s\n", src_file, dst_file);
  383.             else
  384.                   copy_file(Ptr, dst_file, src_file, buf_size);
  385.         else
  386.             report_not_copied(src_file);
  387.  
  388.         file = get_next(&index);
  389.     }
  390.  
  391.     clear_archive_bits(src_dir);
  392.     copy_sub_dirs(src_dir, dst_dir, file_spec, num_args, file_buf, buf_size);
  393.     if (Options & O_MOVE)
  394.         rmdir(src_dir);    /* don't try too hard, but do it if we can ... */
  395.     if (Options & O_ZAPTARGET)
  396.         rmdir(dst_dir);
  397. }
  398.  
  399. /*
  400.  *    report_not_copied()
  401.  *
  402.  *    Input:
  403.  *        src_dir - source directory
  404.  *    Comments:
  405.  *        reports that a file was not copied
  406.  */
  407.  
  408. void report_not_copied(char *src_file)
  409. {
  410.     if ((Options & O_X_VERBOSE) && !(Options & O_CHECK)){
  411.         if (!Reading_flag){
  412.             fprintf(stdout, "reading:\n");
  413.             Reading_flag = 1;
  414.         }
  415.         printf("\t%s ** NOT COPIED **\n", src_file);
  416.     }
  417. }
  418.  
  419. /*
  420.  *    zap_target()
  421.  *
  422.  *    Input:
  423.  *        dst_dir - the destination directory
  424.  *        print_heading - true on entry, false for recursive calls
  425.  *    Comments:
  426.  *        zaps the files in the target directory
  427.  */
  428.  
  429. void zap_target(char *dst_dir, int print_banner)
  430.  
  431. {
  432.     int done, attrib = 0, i;
  433.     struct _dta *odta, fblk;
  434.     char dst_file[FILENAME_MAX], buf[MAXPATH+20];
  435.  
  436.     odta = (struct _dta *) Fgetdta();
  437.     Fsetdta(&fblk);
  438.     if (Options & O_COPY_HIDDEN)
  439.         attrib |= (FA_HIDDEN|FA_SYSTEM);
  440.     if ((Options & O_RECURSIVE) && (!(Options & O_GATHER)))
  441.         attrib |= FA_DIR;
  442.     strcpy(dst_file, dst_dir);
  443.     if (dst_file[(i = strlen(dst_file))-1] != '\\') {
  444.         strcat(dst_file, "\\");
  445.         i++;
  446.     }
  447.     strcat(dst_file, "*.*");
  448.     done = Fsfirst(dst_file, attrib);
  449.     if ((!done) && (Options & O_VERBOSE) && (print_banner))
  450.         printf("deleting:\n");
  451.     while (!done) {
  452.         strcpy(dst_file+i, fblk.dta_name);
  453.         if (fblk.dta_attribute & FA_DIR) {
  454.             if (!is_special(fblk.dta_name)) {
  455.                 zap_target(dst_file, 0);
  456.                 rmdir(dst_file);
  457.             }
  458.         }
  459.         else if ((!(Options & O_INTERACTIVE)) ||
  460.             (sprintf(buf, "Delete file %s (Y/N/Q) ?", dst_file),
  461.              ask_user(buf))) {
  462.             if (Options & O_VERBOSE)
  463.                 printf("\t%s\n", dst_file);
  464.             delete_file(dst_file, '\1');
  465.         }
  466.  
  467.         done = Fsnext();
  468.     }
  469.  
  470.     Fsetdta(odta);
  471. }
  472.  
  473. /*
  474.  *    build_hash_tab()
  475.  *
  476.  *    Input:
  477.  *        dir - the directory for which to table will be built
  478.  *        num_args - the number of arguments in the file list
  479.  *        file_spec - the command-line file list
  480.  *    Output:
  481.  *        hash_tab - the hash table containing all the file names
  482.  *            of the directory 'dir'
  483.  *    Comments:
  484.  *        builds a hash table containing all the file names of a 
  485.  *        directory specified by the command-line file list.
  486.  *        First it adds all the names specified by the file 
  487.  *        list, then it subtracts those specified
  488.  *        by the '-' notation in the file list.
  489.  */
  490.  
  491. void build_hash_tab(char *dir, ENTRY *hash_tab[],
  492.                     int num_args, char *file_spec[])
  493.  
  494. {
  495.     add_to_hash_tab(dir, hash_tab, 1, num_args, file_spec);
  496.     take_from_hash_tab(dir, hash_tab, num_args, file_spec);
  497. }
  498.  
  499. /*
  500.  *    add_to_hash_tab()
  501.  *
  502.  *    Input:
  503.  *        dir - the directory for which to table will be built
  504.  *        from_file_poss - is it possible to get file list from a file?
  505.  *        num_args - the number of arguments in the file list
  506.  *        file_spec - the command-line file list
  507.  *    Output:
  508.  *        hash_tab - the hash table containing all the file names
  509.  *            of the directory 'dir'
  510.  *    Comments:
  511.  *        adds all the files specified by the file list and within
  512.  *        the directory 'dir' to the hash table.
  513.  */
  514.  
  515. void add_to_hash_tab(char *dir, ENTRY *hash_tab[],
  516.                      int from_file_poss, int num_args, char *file_spec[])
  517.  
  518. {
  519.     int done;
  520.     struct _dta *odta, fblk;
  521.     char ref_list[FILENAME_MAX];
  522.     int i;
  523.     int files_added = 0;
  524.     char buf[80];
  525.  
  526.     if ((from_file_poss) && (Options & O_FROM_STDIN))
  527.         Stdin_list_current = Stdin_list_head;
  528.     if ((from_file_poss) && (Options & O_FROM_FILE))
  529.         rewind(From_file_ptr);
  530.     odta = (struct _dta *) Fgetdta();
  531.     Fsetdta(&fblk);
  532.     for (i = 0; i < num_args; i++){
  533.         strcpy(ref_list, dir);
  534.         get_file_spec(buf, file_spec, i);
  535.         if (*buf != '-'){
  536.             int attrib = 0;
  537.  
  538.             files_added = 1;
  539.               strcat(ref_list, (*buf == '\\') ? buf+1 : buf);
  540.             if (Options & O_COPY_HIDDEN)
  541.                 attrib |= (FA_HIDDEN|FA_SYSTEM);
  542.             done = Fsfirst(ref_list, attrib);
  543.             while (!done) {
  544.                 if (find_entry(fblk.dta_name, hash_tab) == NULL)
  545.                     add_entry(&fblk, hash_tab);
  546.                 done = Fsnext();
  547.             }
  548.         }
  549.     }
  550.  
  551.     if (!files_added){
  552.         char *all[2] = { "*.*", NULL };
  553.         
  554.         add_to_hash_tab(dir, hash_tab, 0, 1, all);
  555.     }
  556.     Fsetdta(odta);
  557. }
  558.  
  559. /*
  560.  *    take_from_hash_tab()
  561.  *
  562.  *    Input:
  563.  *        dir - the directory for which to table will be built
  564.  *        num_args - the number of arguments in the file list
  565.  *        file_spec - the command-line file list
  566.  *    Output:
  567.  *        hash_tab - the hash table containing all the file names
  568.  *            of the directory 'dir' and specified by the file
  569.  *            list, 'file_spec'.
  570.  *    Comments:
  571.  *        takes all the files specified by using the '-' notation 
  572.  *        and within the directory 'dir' from the hash table.
  573.  */
  574.  
  575. void take_from_hash_tab(char *dir, ENTRY *hash_tab[],
  576.                         int num_args, char *file_spec[])
  577.  
  578. {
  579.     int done;
  580.     struct _dta *odta, fblk;
  581.     char ref_list[FILENAME_MAX];
  582.     int i;
  583.     char buf[80];
  584.  
  585.     odta = (struct _dta *) Fgetdta();
  586.     Fsetdta(&fblk);
  587.     if (Options & O_FROM_STDIN)
  588.         Stdin_list_current = Stdin_list_head;
  589.     if (Options & O_FROM_FILE)
  590.         rewind(From_file_ptr);
  591.     for (i = 0; i < num_args; i++){
  592.         strcpy(ref_list, dir);
  593.         get_file_spec(buf, file_spec, i);
  594.         if (*buf == '-'){
  595.             int attrib = 0;
  596.               strcat(ref_list, buf + 1);
  597.             if (Options & O_COPY_HIDDEN)
  598.                 attrib |= (FA_HIDDEN|FA_SYSTEM);
  599.             done = Fsfirst(ref_list, attrib);
  600.               while (!done){
  601.                 remove_entry(fblk.dta_name, hash_tab);
  602.                 done = Fsnext();
  603.             }
  604.         }
  605.     }
  606.     Fsetdta(odta);
  607. }
  608.  
  609.  
  610. /*
  611.  *    get_file_spec()
  612.  *
  613.  *    Input:
  614.  *        file_spec - the command-line file list
  615.  *        i - index into the command-line file list
  616.  *    Output:
  617.  *        buf - the next token from the command line file list
  618.  *    Comments:
  619.  *        gets the next token from the command-line file list
  620.  */
  621.  
  622. void get_file_spec(char *buf, char *file_spec[], int i)
  623. {
  624.     if (Options & O_FROM_STDIN){
  625.         strcpy(buf, Stdin_list_current->string);
  626.         Stdin_list_current = Stdin_list_current->next;
  627.     } else if (Options & O_FROM_FILE){
  628.         fgets(buf, 79, From_file_ptr);
  629.         zap_trailing_nl(buf, 79, From_file_ptr); /* clobber newline */
  630.     } else {
  631.         strcpy(buf, file_spec[i]);
  632.     }
  633. }
  634.  
  635.  
  636. /*
  637.  *    add_entry()
  638.  *
  639.  *    Input:
  640.  *        fblk - structure containing the file name, date, and time
  641.  *    Output:
  642.  *        hash_tab - add entry to this array of pointers
  643.  *    Comments:
  644.  *        ands a file name along with its date and time to a hash table
  645.  */
  646.  
  647. void add_entry(struct _dta *fblk, ENTRY *hash_tab[])
  648. {
  649.     int bucket;
  650.     ENTRY *ptr;
  651.  
  652.     bucket = hashpjw(fblk->dta_name);
  653.     if (hash_tab[bucket] == NULL){
  654.         if ((hash_tab[bucket] = (ENTRY *)malloc(sizeof(ENTRY))) ==
  655.             NULL){
  656.             fprintf(stderr, "out of memory");
  657.             exit(-1);
  658.         }
  659.         hash_tab[bucket]->next = NULL;
  660.     } else {
  661.         if ((ptr = (ENTRY *)malloc(sizeof(ENTRY))) == NULL){
  662.             fprintf(stderr, "out of memory");
  663.             exit(-1);
  664.         }
  665.         ptr->next = hash_tab[bucket];
  666.         hash_tab[bucket] = ptr;
  667.     }
  668.     strcpy(hash_tab[bucket]->name, fblk->dta_name);
  669.     hash_tab[bucket]->attr = fblk->dta_attribute;
  670.     hash_tab[bucket]->copied = 0;
  671.     hash_tab[bucket]->fdate = fblk->dta_date;
  672.     hash_tab[bucket]->ftime = fblk->dta_time;
  673. }
  674.  
  675. /*
  676.  *    get_first()
  677.  *
  678.  *    Input:
  679.  *        none
  680.  *    Output:
  681.  *        index - index to first hash table bucket. Each bucket is a
  682.  *            linked-list of structures, one for each file.
  683.  *        Ptr - pointer to first hash table file entry
  684.  *        returns - the name of the first file in the hash table
  685.  *    Comments:
  686.  *        selects the proper hash table and finds its first entry
  687.  */
  688.  
  689.  
  690. char *get_first(int *index)
  691. {
  692.     *index = 0;
  693.  
  694.     Ptr = (Options & O_TARGET_DIR) ? Dst_tab[0] : Src_tab[0];
  695.     move_Ptr(index);
  696.  
  697.     return (Ptr == NULL) ? NULL : Ptr->name;
  698. }
  699.  
  700. /*
  701.  *    get_next()
  702.  *
  703.  *    Input:
  704.  *        index - index to current hash-table bucket. Each bucket is a
  705.  *            linked-list of structures, one for each file.
  706.  *        Ptr - pointer to current hash-table file entry
  707.  *    Output:
  708.  *        index - index to next hash table bucket (may not be different
  709.  *            from current bucket).
  710.  *        Ptr - pointer to next hash table file entry
  711.  *        returns - the name of the next file in the hash table
  712.  *        
  713.  *    Comments:
  714.  *        finds the next entry in the currently selected hash table
  715.  *        of file names
  716.  */
  717.  
  718. char *get_next(int *index)
  719. {
  720.     if (Ptr != NULL)
  721.         Ptr = Ptr->next;
  722.     move_Ptr(index);
  723.     return (Ptr == NULL) ? NULL : Ptr->name;
  724. }
  725.  
  726. /*
  727.  *    move_Ptr()
  728.  *
  729.  *    Input:
  730.  *        index - index to current hash-table bucket. Each bucket is a
  731.  *            linked-list of structures, one for each file.
  732.  *        Ptr - pointer to hash table file entry
  733.  *    Output:
  734.  *        index - index to hash-table bucket.
  735.  *        Ptr - pointer to hash table file entry
  736.  *    Comments:
  737.  *        finds the next non-NULL entry, only if the hash-table 
  738.  *            pointer is NULL
  739.  */
  740.  
  741. void move_Ptr(int *index)
  742. {
  743.     if (Ptr == NULL)
  744.         for ((*index)++; (*index < HASH_TAB_SIZE); (*index)++)
  745.             if ((Ptr = ((Options & O_TARGET_DIR) ? Dst_tab[*index]
  746.                                                  : Src_tab[*index])) != NULL)
  747.                 break;
  748. }
  749.  
  750. /*
  751.  * clear_archive_bits
  752.  *
  753.  * given a source directory, go through the hash table (up to the value
  754.  * of Ptr on entry to this function) and clear the archive bits of all
  755.  * file entries.  This has the desired effect of clearing archive bits
  756.  * of all source files which have been copied.
  757.  */
  758. void clear_archive_bits(char *src_dir)
  759.  
  760. {
  761.     ENTRY *Org_Ptr = Ptr;
  762.     char source_file[FILENAME_MAX], *cur_name;
  763.     int index, n;
  764.  
  765.     if ((Options & O_ARCHIVE) && (!(Options & O_CHECK))) {
  766.         n = strlen(src_dir);
  767.         strcpy(source_file, src_dir);
  768.         cur_name = get_first(&index);
  769.         while (Ptr != NULL) {
  770.             if (Ptr->copied) {
  771.                 strcat(source_file, cur_name);
  772.                 Fattrib(source_file, 1, ((Ptr->copied)&(~FA_CHANGED)));
  773.                 source_file[n] = '\0';
  774.             }
  775.             if (Org_Ptr == Ptr)
  776.                 break;
  777.             else
  778.                 cur_name = get_next(&index);
  779.         }
  780.     }
  781. }
  782.  
  783. /*
  784.  *    clear_hash_tab()
  785.  *
  786.  *    Input:
  787.  *        hash_tab - the hash table to be cleared
  788.  *    Output:
  789.  *        hash_tab - with all its entries cleared and all its
  790.  *            buckets set to NULL
  791.  *    Comments:
  792.  *        removes all entries from a hash table
  793.  */
  794.  
  795. void clear_hash_tab(ENTRY *hash_tab[])
  796. {
  797.     int i;
  798.     ENTRY *ptr, *temp;
  799.  
  800.     for (i = 0; i < HASH_TAB_SIZE; i++){
  801.  
  802.         ptr = hash_tab[i];
  803.         while (ptr != NULL){
  804.             temp = ptr;
  805.             ptr = ptr->next;
  806.             free(temp);
  807.         }
  808.         hash_tab[i] = NULL;
  809.     }
  810. }
  811.  
  812. /*
  813.  *    remove_entry()
  814.  *
  815.  *    Input:
  816.  *        file - the name of the file to be removed
  817.  *        hash_tab - the hash table from which the file is to be
  818.  *            removed
  819.  *    Output:
  820.  *        hash_tab - the hash table with the file removed
  821.  *    Comments:
  822.  *        removes one entry from a hash table
  823.  */
  824.  
  825. void remove_entry(char *file, ENTRY *hash_tab[])
  826. {
  827.     int bucket;
  828.     ENTRY *ptr, *temp;
  829.     ENTRY **lastptr;
  830.  
  831.     bucket = hashpjw(file);
  832.     if (hash_tab[bucket] != NULL) {
  833.         ptr = hash_tab[bucket];
  834.         lastptr = &(hash_tab[bucket]);
  835.         while(ptr != NULL){
  836.             if (!strcmp(file, ptr->name)){
  837.                 *lastptr = ptr->next;
  838.                 free(ptr);
  839.                 break;
  840.             }
  841.             lastptr = &(ptr->next);
  842.             ptr = ptr->next;
  843.         }
  844.     }
  845. }
  846.  
  847.  
  848. /*
  849.  *    find_entry()
  850.  *
  851.  *    Input:
  852.  *        file - the name of the file to be found
  853.  *        hash_tab - the hash table which is to be searched
  854.  *    Output:
  855.  *        returns - a pointer to the entry in the hash table,
  856.  *            or NULL if not found
  857.  *    Comments:
  858.  *        finds an entry in the hash table
  859.  */
  860.   
  861. ENTRY *find_entry(char *file, ENTRY *hash_tab[])
  862. {
  863.     int bucket;
  864.     ENTRY *ptr, *temp;
  865.  
  866.     bucket = hashpjw(file);
  867.     if (hash_tab[bucket] == NULL){
  868.         ptr = NULL;
  869.     } else {
  870.         ptr = hash_tab[bucket];
  871.         while(ptr != NULL){
  872.             if (!strcmp(file, ptr->name)){
  873.                 break;
  874.             }
  875.             ptr = ptr->next;
  876.         }
  877.     }
  878.     return(ptr);
  879. }
  880.  
  881. /*
  882.  *    copy_sub_dirs()
  883.  *
  884.  *    Input:
  885.  *        src_dir - the source directory
  886.  *        dst_dir - the destination directory
  887.  *        file_spec - the command-line file list
  888.  *        num_args - the number of arguments in the file list
  889.  *        file_buf - the buffer for reading and writing files
  890.  *        buf_size - the size of 'file_buf'
  891.  *    Comments:
  892.  *        copies the files in the sub-directories if the recursive
  893.  *        mode is specified
  894.  */
  895.  
  896. void copy_sub_dirs(char *src_dir, char *dst_dir, char *file_spec[],
  897.                    int num_args, void *file_buf, unsigned long buf_size)
  898.  
  899. {
  900.     int done;
  901.     char ref_list[FILENAME_MAX];
  902.     struct _dta *odta, fblk;
  903.  
  904.     if (Options & O_RECURSIVE){
  905.         strcpy(ref_list, src_dir);
  906.         strcat(ref_list, "*.*");
  907.         odta = (struct _dta *) Fgetdta();
  908.         Fsetdta(&fblk);
  909.  
  910.         done = Fsfirst(ref_list, FA_DIR);
  911.         while (!done){
  912.             if ((fblk.dta_attribute & FA_DIR) && (!is_special(fblk.dta_name))) {
  913.                 char new_src_dir[FILENAME_MAX], new_dst_dir[FILENAME_MAX];
  914.                 if (should_dir_copy(src_dir, dst_dir,  fblk.dta_name,
  915.                                     new_src_dir, new_dst_dir)){
  916.                     clear_hash_tab(Src_tab);
  917.                     clear_hash_tab(Dst_tab);
  918.                     copy_files(new_src_dir, new_dst_dir,
  919.                                file_spec, num_args,
  920.                                file_buf, buf_size);
  921.                 }
  922.             }
  923.             done = Fsnext();
  924.         }
  925.         Fsetdta(odta);
  926.     }
  927. }
  928.  
  929. /*
  930.  *    should_dir_copy()
  931.  *
  932.  *    Input:
  933.  *        src_dir - the full path of the current source directory
  934.  *        dst_dir - the full path of the current destination directory
  935.  *        name - the name of the sub-directory
  936.  *    Output:
  937.  *        new_src_dir - the full path of the source sub-directory
  938.  *        new_dst_dir - the full path of the destination sub-directory
  939.  *        returns - 1 if sub-directory should be copied
  940.  *              0 if sub-directory should NOT be copied
  941.  *    Comments:
  942.  *        determines whether a sub-directory should be copied
  943.  */
  944.  
  945. int should_dir_copy(char *src_dir, char *dst_dir, char *name,
  946.                     char *new_src_dir, char *new_dst_dir)
  947.  
  948. {
  949.     struct stat src_buf, dst_buf;
  950.     int ret_src, ret_dst;
  951.     int status;
  952.     int ret_val;
  953.  
  954.     strcpy(new_src_dir, src_dir);
  955.     strcat(new_src_dir, name);
  956.  
  957.     strcpy(new_dst_dir, dst_dir);
  958.     if (Options & O_GATHER)
  959.         new_dst_dir[strlen(new_dst_dir) - 1] = '\0'; /* chop slash */
  960.     else
  961.         strcat(new_dst_dir, name);
  962.  
  963.     ret_src = stat(new_src_dir, &src_buf);
  964.     ret_dst = stat(new_dst_dir, &dst_buf);
  965.  
  966.     if (ret_src == -1) /* sub dir does not exist in source dir */
  967.         ret_val = 0;
  968.     else if (!(src_buf.st_mode & S_IFDIR)) /* src dir is a file */
  969.         ret_val = 0;
  970.     else if (Options & (O_GATHER|O_CHECK))
  971.         ret_val = 1;
  972.     else if (ret_dst == -1) { /* destination dir does not exist */
  973.         status =  mkdir(new_dst_dir, 0);
  974.         if (status) {
  975.             fprintf(stderr, "unable to create directory\n");
  976.             exit(-1);
  977.         }
  978.         ret_val = 1;
  979.     } else
  980.         ret_val = 1;
  981.  
  982.     strcat(new_src_dir, "\\");
  983.     strcat(new_dst_dir, "\\");
  984.  
  985.     return(ret_val);
  986. }
  987.  
  988. /*
  989.  *    should_file_copy()
  990.  *
  991.  *    Input:
  992.  *        file - name of file to be copied
  993.  *        src_file - full path of file to be copied
  994.  *    Output:
  995.  *        returns: 1 if file should be copied/moved
  996.  *             0 if file should NOT be copied/moved
  997.  *    Comments:
  998.  *        looks up the file name in the source and destination
  999.  *        hash tables and determines whether a file should be
  1000.  *        copied/moved
  1001.  */
  1002.  
  1003. int should_file_copy(char *file, char *src_file)
  1004.  
  1005. {
  1006.     ENTRY *src, *dst;
  1007.  
  1008.     /* source does not exit */
  1009.     if ((src = find_entry(file, Src_tab)) != NULL) {
  1010.         if (Options & O_DATE_CHECK)
  1011.             if (!(within_date_range(src)))
  1012.                 return 0;
  1013.  
  1014.         if (Options & O_ARCHIVE)
  1015.             if (!(src->attr & FA_CHANGED))
  1016.                 return 0;
  1017.   
  1018.         if ((dst = find_entry(file, Dst_tab)) != NULL) {
  1019.  
  1020.             if ((Options & (O_CP_IF_SRC_NEWER|O_COPY_IF_SRC_OLDER)) ==
  1021.                 (O_CP_IF_SRC_NEWER|O_COPY_IF_SRC_OLDER))
  1022.                 return 0;
  1023.  
  1024.             if (Options & O_CP_IF_SRC_NEWER)
  1025.                 if (cmptime_entry(src, dst) <= 0L)
  1026.                     return 0;
  1027.  
  1028.             if (Options & O_COPY_IF_SRC_OLDER)
  1029.                 if (cmptime_entry(src, dst) >= 0L)
  1030.                     return 0;
  1031.  
  1032.         }
  1033.     }
  1034.     else /* source file missing?  of course don't copy (something's fishy!) */
  1035.         return 0;
  1036.  
  1037.     if (Options & O_INTERACTIVE) {
  1038.         char buf[MAXPATH + 20];
  1039.  
  1040.         sprintf(buf, "copy %s (Y/N/Q) ? ", src_file);
  1041.         return ask_user(buf);    /* user has the final say-so */
  1042.     }
  1043.  
  1044.     return 1;
  1045. }
  1046.  
  1047. /*
  1048.  * cmptime_entry
  1049.  *
  1050.  * given two files, return positive if the first has a more recent modification
  1051.  * date/time, zero if the files have the same modification date/time or
  1052.  * negative if the second is more recent.
  1053.  */
  1054. long cmptime_entry(ENTRY *a, ENTRY *b)
  1055. {
  1056.     return (((unsigned long)a->fdate) << 16 | (unsigned long)a->ftime) -
  1057.            (((unsigned long)b->fdate) << 16 | (unsigned long)b->ftime);
  1058. }
  1059.  
  1060. int within_date_range(ENTRY *src)
  1061. {
  1062.     int retval = 1;
  1063.     DATE_NODE *d = Fdate;
  1064.     TIME_NODE *t = Ftime;
  1065.     int saw_after_or_before = 0;
  1066.  
  1067.     /* AND the 'before' and 'after' modes */
  1068.     while (d != NULL){
  1069.         if ((d->mode & D_BEFORE) || (d->mode & D_AFTER))
  1070.             saw_after_or_before = 1;
  1071.         if (((d->mode & D_BEFORE) && (src->fdate >= d->fdate)) ||
  1072.             ((d->mode & D_AFTER) && (src->fdate <= d->fdate))){
  1073.                 retval = 0;
  1074.         }
  1075.         d = d->next;
  1076.     }
  1077.  
  1078.     if (!saw_after_or_before)
  1079.         retval = 0;
  1080.     /* OR the 'on' modes */
  1081.     d = Fdate;
  1082.     while (d != NULL){
  1083.         if ((d->mode & D_ON) && (src->fdate == d->fdate))
  1084.             retval = 1;
  1085.         d = d->next;
  1086.     }
  1087.  
  1088.     /* AND the 'before' and 'after' modes */
  1089.     if (retval)
  1090.         while (t != NULL){
  1091.             if (((t->mode & D_BEFORE) &&
  1092.                 (src->ftime >= t->ftime)) ||
  1093.                 ((t->mode & D_AFTER) &&
  1094.                 (src->ftime <= t->ftime)))
  1095.                     retval = 0;
  1096.             t = t->next;
  1097.         }
  1098.  
  1099.     return retval;
  1100. }
  1101.  
  1102. /*
  1103.  *    file_exists()
  1104.  *
  1105.  *    Input:
  1106.  *        name - full path of file
  1107.  *    Output:
  1108.  *        returns: 1 if file exists
  1109.  *             0 if file does NOT exist
  1110.  */
  1111.  
  1112. int file_exists(char *name)
  1113. {
  1114.     return (access(name, 0) == 0);
  1115. }
  1116.  
  1117. /*
  1118.  *    get_file_buf()
  1119.  *
  1120.  *    Input:
  1121.  *    Output:
  1122.  *        buf_size - size of buffer
  1123.  *        returns - pointer to buffer
  1124.  */
  1125.  
  1126. void *get_file_buf(unsigned long *buf_size)
  1127. {
  1128.     void *buf_mem;
  1129.  
  1130.     *buf_size = MAX_BUF;
  1131.     do {
  1132.         if ((buf_mem = malloc(*buf_size)) != NULL)
  1133.             break;
  1134.         *buf_size /= 2;
  1135.     } while (*buf_size >= 512);
  1136.  
  1137.     if (buf_mem == NULL){
  1138.         fprintf(stderr, "could not allocate file buffer\n");
  1139.         exit(-1);
  1140.     }
  1141.  
  1142.     return(buf_mem);
  1143. }
  1144.  
  1145. /*
  1146.  *    copy_file()
  1147.  *
  1148.  *    Input:
  1149.  *        ptr - pointer to hash table entry for file
  1150.  *        dst_file - full path of destination file
  1151.  *        src_file - full path of source file
  1152.  *        buf_size - size of buffer for file i/o
  1153.  *        File_buf - buffer for file i/o
  1154.  *        Buf_ptr - pointer to next available memory in i/o buffer
  1155.  *        Reading_flag - indicates whether 'reading:' has been printed
  1156.  *    Comments:
  1157.  *        copies source file to destination file
  1158.  */
  1159.  
  1160. void copy_file(ENTRY *ptr, char *dst_file, char *src_file,
  1161.                unsigned long buf_size)
  1162.  
  1163. {
  1164.     int src_handle, dst_handle;
  1165.     long bytes;
  1166.     int retval;
  1167.     long bytes_needed, bytes_left;
  1168.     struct stat stat_buf;
  1169.     _DOSTIME ftime_buf;
  1170.     char fattr;
  1171.  
  1172.     if ((Options & O_MOVE) && (*src_file == *dst_file)){
  1173.         if (!Reading_flag && (Options & O_VERBOSE)){
  1174.             fprintf(stdout, "renaming file:\n");
  1175.             fprintf(stdout, "\t%s -> %s\n", src_file, dst_file);
  1176.             Reading_flag = 0;
  1177.         }
  1178.         if (rename_file(ptr, src_file, dst_file))
  1179.             clean_up_and_exit();
  1180.     } else {
  1181.         if ((src_handle = open(src_file, O_RDONLY, 0)) < 0){
  1182.             fprintf(stderr, "unable to open file '%s'\n", src_file);
  1183.             clean_up_and_exit();
  1184.         }
  1185.         Fdatime(&ftime_buf, src_handle, 0);
  1186.         fstat(src_handle, &stat_buf);
  1187.         fattr = Fattrib(src_file, 0, 0);
  1188.         bytes_needed = sizeof(ENTRY *) + (strlen(src_file) + 1) +
  1189.             (strlen(dst_file) + 1) + sizeof(stat_buf.st_size) +
  1190.                 stat_buf.st_size + sizeof(_DOSTIME) +
  1191.                     sizeof(fattr);
  1192.         bytes_left = (char *)File_buf - (char *)Buf_ptr + buf_size;
  1193.  
  1194.         if ((bytes_needed > MAX_BUF) || (Options & O_INTERACTIVE)) {
  1195.             if (Buf_ptr != File_buf)
  1196.                 write_buffer(File_buf);
  1197.             copy_file_unbuffered(src_handle, ptr, src_file, dst_file,
  1198.                                  stat_buf.st_size, &ftime_buf, fattr,
  1199.                                  buf_size);
  1200.         } else {
  1201.             if (bytes_left < bytes_needed)
  1202.                 write_buffer(File_buf);
  1203.             if (!Reading_flag && (Options & O_VERBOSE)){
  1204.                 fprintf(stdout, "reading:\n");
  1205.                 Reading_flag = 1;
  1206.             }
  1207.             if (Options & O_VERBOSE)
  1208.                 fprintf(stdout, "\t%s\n", src_file);
  1209.             copy_file_to_buffer(src_handle, ptr, src_file, dst_file,
  1210.                                 &ftime_buf, stat_buf.st_size, fattr);
  1211.         }
  1212.         close(src_handle);
  1213.     }
  1214. }
  1215.  
  1216. /*
  1217.  *    rename_file()
  1218.  *
  1219.  *    Input:
  1220.  *        ptr - pointer to hash table entry (so we can mark this as 'copied')
  1221.  *        src_file - source file name (full path)
  1222.  *        dst_file - destination file name (full path)
  1223.  *    Comments:
  1224.  *        renames a file. if the destination file already exits,
  1225.  *        then it is deleted.
  1226.  */
  1227.  
  1228. int rename_file(ENTRY *ptr, char *src_file, char *dst_file)
  1229.  
  1230. {
  1231.     int old_attrib;
  1232.  
  1233.     if ((old_attrib = Fattrib(src_file, 0, 0)) < 0) {
  1234.         fprintf(stderr, "cannot move file %s\n", src_file);
  1235.         return -1;
  1236.     }
  1237.     if (rename(src_file, dst_file) == -1){
  1238.         if (file_exists(dst_file))
  1239.             if (delete_file(dst_file, '\0')){
  1240.                 if ((!(Options & O_BATCH)) &&
  1241.                     (printf("Target %s protected - ", dst_file),
  1242.                      !(ask_user("force move onto it (Y/N/Q)? ")))){
  1243.                     fprintf(stderr, "%s NOT moved to %s\n", src_file,
  1244.                             dst_file);
  1245.                     return -1;
  1246.                 }
  1247.                 if (delete_file(dst_file, '\1')){
  1248.                     fprintf(stderr, "could not remove %s\n", dst_file);
  1249.                     return -1;
  1250.                 }
  1251.             }
  1252.  
  1253.         if (old_attrib & FA_RDONLY)
  1254.             Fattrib(src_file, 1, old_attrib&(~FA_RDONLY));
  1255.         if (rename(src_file, dst_file) == -1){
  1256.             if (old_attrib & FA_RDONLY)
  1257.                 Fattrib(src_file, 1, old_attrib);
  1258.             fprintf(stderr, "cannot move file %s\n", src_file);
  1259.             return -1;
  1260.         }
  1261.     }
  1262.  
  1263.     Fattrib(dst_file, 1, old_attrib);
  1264.     Copied_a_file = 1;
  1265.     ptr->copied = (old_attrib) ? old_attrib : FA_CHANGED;
  1266.  
  1267.     return 0;
  1268. }
  1269.  
  1270. /*
  1271.  *    copy_file_to_buffer()
  1272.  *
  1273.  *    Input:
  1274.  *        src_handle - source file handle
  1275.  *        src_file - source file name (full path)
  1276.  *        dst_file - destination file name (full path)
  1277.  *        st_buf - stat buffer of source file
  1278.  *        attrib - file access mode (contains hidden)
  1279.  *    Output:
  1280.  *        Buf_ptr - pointer to unused position in buffer
  1281.  *    Comments:
  1282.  *        copies the source file along with a header to memory.
  1283.  *        the header contains source-file name, dest-file name,
  1284.  *        source-file date, source-file size, and the source-file
  1285.  *        modes.
  1286.  */
  1287. void copy_file_to_buffer(int src_handle, ENTRY *ptr,
  1288.                          char *src_file, char *dst_file,
  1289.                          _DOSTIME *ftime_buf, long fsize, char fattr)
  1290.  
  1291. {
  1292.     memcpy((char *)Buf_ptr, &ptr, sizeof(ENTRY *));
  1293.     Buf_ptr = (char *)Buf_ptr + sizeof(ENTRY *);
  1294.     strcpy((char *)Buf_ptr, src_file);
  1295.     Buf_ptr = (char *)Buf_ptr + strlen(src_file) + 1;
  1296.     strcpy((char *)Buf_ptr, dst_file);
  1297.     Buf_ptr = (char *)Buf_ptr + strlen(dst_file) + 1;
  1298.     memcpy(Buf_ptr, &fsize, sizeof(fsize));
  1299.     Buf_ptr = (char *) Buf_ptr + sizeof(long);
  1300.     memcpy(Buf_ptr, ftime_buf, sizeof(_DOSTIME));
  1301.     Buf_ptr = (char *) Buf_ptr + sizeof(_DOSTIME);
  1302.     memcpy(Buf_ptr, &fattr, sizeof(char));
  1303.     Buf_ptr = (char *) Buf_ptr + sizeof(char);
  1304.     read(src_handle, Buf_ptr, fsize);
  1305.     Buf_ptr = (char *) Buf_ptr + fsize;
  1306. }
  1307.  
  1308. /*
  1309.  *    copy_file_unbuffered()
  1310.  *
  1311.  *    Input:
  1312.  *         src_handle - handle of source file
  1313.  *        src_file - name of source file (full path name)
  1314.  *        dst_file - name of destination file (full path name)
  1315.  *        buf_size - size of file buffer
  1316.  *        Reading_flag - indicates whether 'reading:' has been printed
  1317.  *        File_buf - buffer for file i/o
  1318.  *    Output:
  1319.  *        Reading_flag - reset to indicate that the message
  1320.  *            'reading and writing file:' has been printed.
  1321.  *    Comments:
  1322.  *        copies the source file to the destination file without
  1323.  *        buffering (as is done with smaller files).  the file modes
  1324.  *        and file date are also copied.
  1325.  */
  1326.  
  1327. void copy_file_unbuffered(int src_handle, ENTRY *ptr,
  1328.                           char *src_file, char *dst_file,
  1329.                           long fsize, _DOSTIME *ftime_buf, char fattr,
  1330.                           unsigned long buf_size)
  1331. {
  1332.     unsigned bytes = 0;
  1333.     int dst_handle;
  1334.     int retval;
  1335.  
  1336.     do {
  1337.         Retry = 0;
  1338.         if (open_dest_file(&dst_handle, dst_file,
  1339.                            (fattr & FA_RDONLY) ? S_IREAD : S_IREAD | S_IWRITE))
  1340.             return;
  1341.         if (!Reading_flag && (Options & O_VERBOSE)){
  1342.             fprintf(stdout, "reading and writing file:\n");
  1343.             fprintf(stdout, "\t%s -> %s\n", src_file, dst_file);
  1344.             Reading_flag = 0;
  1345.         }
  1346.  
  1347.         if (!(Options & O_LARGEFILES))
  1348.             lseek(src_handle, 0L, SEEK_SET);
  1349.         copy_file_contents(src_handle, dst_handle, src_file, dst_file, 
  1350.                            buf_size);
  1351.     } while (Retry);
  1352.  
  1353.     if (Options & O_MOVE)
  1354.         delete_file(src_file, '\0');
  1355.     Copied_a_file = 1;
  1356.     Fdatime(ftime_buf, dst_handle, 1);
  1357.     close(dst_handle);
  1358.     Fattrib(dst_file, 1, fattr);
  1359.     ptr->copied = (fattr) ? fattr : FA_CHANGED;
  1360. }
  1361.  
  1362. /*
  1363.  * ensure_dest_dir_exist
  1364.  *
  1365.  * given a destination file name, first ensure the existence of the directory
  1366.  * which will contain it.  This function exists to make a target-disk swap work
  1367.  * together with the recursive option.  Return non-zero on success, zero on
  1368.  * failure.
  1369.  */
  1370. int ensure_dest_dir_exist(char *dst_file)
  1371.  
  1372. {
  1373.     int stat_err, ret_val = 1;
  1374.     struct stat statbuf;
  1375.     char *p;
  1376.  
  1377.     if ((p = strrchr(dst_file, '\\')) == NULL)
  1378.         return 0;
  1379.     *p = '\0';
  1380.     if ((!(stat_err = stat(dst_file, &statbuf))) &&
  1381.         (!(statbuf.st_mode & S_IFDIR)))
  1382.         ret_val = 0;
  1383.     else if (stat_err)
  1384.         create_dir(dst_file);
  1385.     *p = '\\';
  1386.  
  1387.     return ret_val;    /* if 'create_dir()' returns, it was successful */
  1388. }
  1389.  
  1390. /*
  1391.  * target_disk_full
  1392.  *
  1393.  * called when the target disk is full.  Depending on the options, it may
  1394.  * be possible to change target disks and go on.
  1395.  */
  1396. void target_disk_full(char *dst_file, int dst_handle)
  1397.  
  1398. {
  1399.     close(dst_handle);
  1400.     if (!(Options & O_LARGEFILES))
  1401.         delete_file(dst_file, '\1');
  1402.     if (ok_to_retry())
  1403.         if (ask_user("out of disk space, continue (Y/N/Q) ? ")) {
  1404.             if (Options & O_ZAPTARGET)
  1405.                 zap_target(Org_dest_dir, 1);
  1406.             Retry = 1;
  1407.         }
  1408.         else
  1409.             clean_up_and_exit();
  1410.     else {
  1411.         fprintf(stderr, "out of disk space\n");
  1412.         clean_up_and_exit();
  1413.     }
  1414. }
  1415.  
  1416. /*
  1417.  * clean_up_and_exit
  1418.  *
  1419.  * error occurred (like write error/target full).  Clean up (clear archive
  1420.  * bits of source files already copied) and exit.
  1421.  */
  1422. void clean_up_and_exit()
  1423.  
  1424. {
  1425.     clear_archive_bits(Cur_source_dir);
  1426.     exit(-1);
  1427. }
  1428.  
  1429. /*
  1430.  *    copy_file_contents()
  1431.  *
  1432.  *    Input:
  1433.  *         src_handle - handle of source file
  1434.  *         src_handle - handle of destination file
  1435.  *        src_file - name of source file (full path name)
  1436.  *        dst_file - name of destination file (full path name)
  1437.  *        buf_size - size of file buffer
  1438.  *        File_buf - buffer for file i/o
  1439.  *    Comments:
  1440.  *        copies the contents of the source file to the destination 
  1441.  *        file.
  1442.  */
  1443.  
  1444. void copy_file_contents(int src_handle, int dst_handle, char *src_file, 
  1445.                         char *dst_file, unsigned long buf_size)
  1446.  
  1447. {
  1448.     long last_read_pos, bytes_read, bytes_written;
  1449.  
  1450.     while(1){
  1451.         last_read_pos = tell(src_handle);
  1452.         bytes_read = read(src_handle, File_buf, buf_size);
  1453.         if (bytes_read == -1L){
  1454.             fprintf(stderr, "Can't read file '%s'\n", src_file);
  1455.             exit(-1);
  1456.         }
  1457.         if (bytes_read){
  1458.             bytes_written = write(dst_handle, File_buf,
  1459.                                   (unsigned long) bytes_read);
  1460.             if (bytes_written == -1L){
  1461.                 fprintf(stderr, "Can't write to file '%s'\n",
  1462.                     dst_file);
  1463.                 exit(-1);
  1464.             }
  1465.         } else
  1466.             break;
  1467.         if (bytes_read != bytes_written) {
  1468.             if (Options & O_LARGEFILES)
  1469.                 lseek(src_handle, last_read_pos+bytes_written, SEEK_SET);
  1470.             target_disk_full(dst_file, dst_handle);
  1471.             break;
  1472.         }
  1473.     }
  1474. }
  1475.  
  1476. /*
  1477.  *    ok_to_retry()
  1478.  *
  1479.  *    Input:
  1480.  *        none
  1481.  *    Comments:
  1482.  *        checks if the target directory was used to determine the file
  1483.  *        list.  If it was, then the copy is aborted when disk is full
  1484.  */
  1485.  
  1486. int ok_to_retry()
  1487.  
  1488. {
  1489.     return ((!(Options & O_BATCH)) && (Options & O_TARGET_REMOVEABLE) &&
  1490.             (!(Options &
  1491.                (O_TARGET_DIR | O_COPY_IF_SRC_OLDER | O_CP_IF_SRC_NEWER))));
  1492. }
  1493.  
  1494. void setftime(char *fname, struct stat *statbuf)
  1495.  
  1496. {
  1497.     struct utimbuf ftime;
  1498.  
  1499.     ftime.actime = statbuf->st_atime;
  1500.     ftime.modtime = statbuf->st_mtime;
  1501.     utime(fname, &ftime);
  1502. }
  1503.  
  1504. /*
  1505.  *    write_buffer()
  1506.  *
  1507.  *    Input:
  1508.  *        file_buf - memory buffer containing file contents along
  1509.  *            with their headers
  1510.  *        File_buf - starting location of memory buffer
  1511.  *    Output:
  1512.  *        Reading_flag - reset to indicate recent output of message,
  1513.  *            'writing:'
  1514.  *        Buf_ptr - location of available memory in memory buffer
  1515.  *    Comments:
  1516.  *        copies all the files contained in the memory buffer to their
  1517.  *        destination files
  1518.  */
  1519.  
  1520. void write_buffer(void *file_buf)
  1521.  
  1522. {
  1523.     ENTRY *ptr;
  1524.     int dst_handle;
  1525.     unsigned retval;
  1526.     char src_file[FILENAME_MAX];
  1527.     char dst_file[FILENAME_MAX];
  1528.     _DOSTIME ftime_buf;
  1529.     long fsize;
  1530.     char fattr;
  1531.  
  1532.     if ((Options & O_VERBOSE) && (file_buf < Buf_ptr))
  1533.         printf("writing:\n");
  1534.     Reading_flag = 0;
  1535.     while (file_buf < Buf_ptr){
  1536.         file_buf = get_header_info(file_buf, &ptr, src_file, dst_file,
  1537.                                    &ftime_buf, &fsize, &fattr);
  1538.         if (!(Options & O_CHECK))
  1539.             if (!write_dest_file(&dst_handle, dst_file, file_buf, fsize,
  1540.                                  fattr)) {
  1541.                 if (Options & O_MOVE)
  1542.                     delete_file(src_file, '\0');
  1543.                 Copied_a_file = 1;
  1544.                 Fdatime(&ftime_buf, dst_handle, 1);
  1545.                 close(dst_handle);
  1546.                 Fattrib(dst_file, 1, fattr);
  1547.                 ptr->copied = (fattr) ? fattr : FA_CHANGED;
  1548.             }
  1549.         file_buf = (char *)file_buf + fsize;
  1550.     }
  1551.     Buf_ptr = File_buf;
  1552. }
  1553.  
  1554. /*
  1555.  *    get_header_info()
  1556.  *
  1557.  *    Input:
  1558.  *        file_buf - pointer to memory buffer containing file 
  1559.  *            contents and header
  1560.  *    Output:
  1561.  *        src_file - source file name (full path)
  1562.  *        dst_file - destination file name (full path)
  1563.  *        stat_buf - file status buffer
  1564.  *        mode - mode settings of source file
  1565.  *        returns - location of next file in memory buffer
  1566.  *    Comments:
  1567.  *        gets a file's header information from the memory buffer
  1568.  */
  1569.  
  1570. void *get_header_info(void *file_buf, ENTRY **ptr,
  1571.                       char *src_file, char *dst_file, _DOSTIME * ftimebuf,
  1572.                       long *fsize, char *fattr)
  1573. {
  1574.     memcpy(ptr, (char *)file_buf, sizeof(ENTRY *));
  1575.     file_buf = (char *)file_buf + sizeof(ENTRY *);
  1576.     strcpy(src_file, (char *)file_buf);
  1577.     file_buf = (char *)file_buf + strlen((char *)file_buf) +  1;
  1578.     strcpy(dst_file, (char *)file_buf);
  1579.     file_buf = (char *)file_buf + strlen((char *)file_buf) +  1;
  1580.     memcpy(fsize, file_buf, sizeof(long));
  1581.     file_buf = (char *) file_buf + sizeof(long);
  1582.     memcpy(ftimebuf, file_buf, sizeof(_DOSTIME));
  1583.     file_buf = (char *) file_buf + sizeof(_DOSTIME);
  1584.     memcpy(fattr, file_buf, sizeof(char));
  1585.     file_buf = (char *) file_buf + sizeof(char);
  1586.     return(file_buf);
  1587. }
  1588.  
  1589. /*
  1590.  *    write_dest_file()
  1591.  *
  1592.  *    Input:
  1593.  *        dst_handle - handle of file to be written
  1594.  *        dst_file - full-path name of file to be written
  1595.  *        file_buf - memory buffer containing file contents
  1596.  *        file_size - size of file to be written
  1597.  *        fattr - file attributes
  1598.  *    Comments:
  1599.  *        copies a file's contents from a memory buffer to a disk file.
  1600.  *        Returns 0 normally, 1 if the destination couldn't be opened for write.
  1601.  */
  1602.  
  1603. int write_dest_file(int *dst_handle, char *dst_file, void *file_buf,
  1604.                     long file_size, int fattr)
  1605.  
  1606. {
  1607.     long bytes_written, written_so_far = 0L;
  1608.  
  1609.     do {
  1610.         Retry = 0;
  1611.         if (Options & O_VERBOSE)
  1612.             printf("\t%s\n", dst_file);
  1613.         if (open_dest_file(dst_handle, dst_file, ((fattr & FA_RDONLY)
  1614.                                                    ? S_IREAD
  1615.                                                    : S_IREAD | S_IWRITE)))
  1616.             break;
  1617.         bytes_written = write(*dst_handle, file_buf+written_so_far,
  1618.                               (unsigned long) file_size-written_so_far);
  1619.         if (bytes_written == -1L){
  1620.             fprintf(stderr, "Can't write to file '%s'\n",  dst_file);
  1621.             exit(-1);
  1622.         }
  1623.         if (bytes_written != (file_size-written_so_far)) {
  1624.             if (Options & O_LARGEFILES)
  1625.                 written_so_far += bytes_written;
  1626.             target_disk_full(dst_file, *dst_handle);
  1627.         }
  1628.         else
  1629.             return 0;
  1630.     } while (Retry);
  1631.  
  1632.     return 1;
  1633. }
  1634.  
  1635. /*
  1636.  *    open_dest_file()
  1637.  *
  1638.  *    Input:
  1639.  *        dst_handle - handle of file to be opened
  1640.  *        dst_file - full-path name of file to be opened
  1641.  *        mode - mode settings of file to be opened
  1642.  *    Comments:
  1643.  *        opens a file for write.  If not successful because file is read-only,
  1644.  *        tries to recover.
  1645.  */
  1646.  
  1647. int open_dest_file(int *handle, char *name, unsigned modes)
  1648. {
  1649.     if ((*handle = open((char *)name,
  1650.                         (Options & O_JOIN) ? O_WRONLY | O_APPEND | O_CREAT
  1651.                                            : O_WRONLY | O_TRUNC | O_CREAT,
  1652.                         (unsigned)modes)) < 0){
  1653.         struct stat statbuf;
  1654.  
  1655.         if (stat(name, &statbuf)) {
  1656.             ensure_dest_dir_exist(name);
  1657.             if ((*handle = open((char *)name,
  1658.                                 (Options & O_JOIN)
  1659.                                   ? O_WRONLY | O_APPEND | O_CREAT
  1660.                                   : O_WRONLY | O_TRUNC | O_CREAT,
  1661.                                 (unsigned)modes)) < 0){
  1662.                 fprintf(stderr, "unable to open file '%s' for write\n", name);
  1663.                 return -1;
  1664.             }
  1665.         }
  1666.         else if ((Options & O_BATCH) ||
  1667.                  (printf("file '%s' is not writeable.\n", name),
  1668.                   ask_user("Attempt to delete and overwrite it (Y/N/Q) ? "))) {
  1669.                 delete_file(name, '\1');
  1670.                 if ((*handle = open((char *)name,
  1671.                                     (Options & O_JOIN)
  1672.                                       ? O_WRONLY | O_APPEND | O_CREAT
  1673.                                       : O_WRONLY | O_TRUNC | O_CREAT,
  1674.                                     (unsigned)modes)) < 0) {
  1675.                     fprintf(stderr, "unable to open file '%s' for write\n",
  1676.                             name);
  1677.                     return -1;
  1678.                 }
  1679.             }
  1680.         else {
  1681.             printf("Skipped copy to '%s' (open for write failed).\n",
  1682.                    name);
  1683.             return -1;
  1684.         }
  1685.     }
  1686.  
  1687.     return 0;
  1688. }
  1689.  
  1690. /*
  1691.  *    usage()
  1692.  *
  1693.  *    Comments:
  1694.  *        outputs brief instructions on the proper use of docp
  1695.  */
  1696.  
  1697. void usage()
  1698. {
  1699.     fprintf(stderr, USAGE_MESS);
  1700.     exit(-1);
  1701. }
  1702.  
  1703. /*
  1704.  *    get_flags()
  1705.  *
  1706.  *    Input:
  1707.  *        argv - command line arguments
  1708.  *        argc - count of the command line arguments
  1709.  *    Output:
  1710.  *        From_file_ptr - pointer to file containing file list
  1711.  *    Comments:
  1712.  *        parses the flags specified on the command line, and sets
  1713.  *        the appropriate bit fields in a variable called 'options'
  1714.  */
  1715.  
  1716. void get_flags(char *argv[], int argc)
  1717.  
  1718. {
  1719.     int c;
  1720.     char from_file[FILENAME_MAX];
  1721.  
  1722.     while ((c = getopt(argc, argv, GET_OPT_LIST)) != EOF) {
  1723.         if (isupper(c))
  1724.             c = tolower(c);
  1725.         switch(c){
  1726.             case 'a': /* copy using archive bit */
  1727.                 Options |=  O_ARCHIVE;
  1728.                 break;
  1729.             case 'b': /* batch mode - don't ask questions */
  1730.                 Options |=  O_BATCH;
  1731.                 break;
  1732.             case 'c': /* check mode, don't copy files */
  1733.                 Options |=  O_VERBOSE | O_X_VERBOSE | O_CHECK;
  1734.                 break;
  1735.             case 'd': /* date check */
  1736.                 Options |=  O_DATE_CHECK;
  1737.                 set_Fdate(optarg);
  1738.                 break;
  1739.             case 'f': /* file list from file */
  1740.                 strcpy(from_file, optarg);
  1741.                 if (!strcmp(from_file, "-"))
  1742.                     Options |= O_FROM_STDIN;
  1743.                 else {
  1744.                     Options |= O_FROM_FILE;
  1745.                     if ((From_file_ptr = fopen(from_file, "r"))
  1746.                         == NULL){
  1747.                         fprintf(stderr, "unable to open '-f' file '%s'\n", from_file);
  1748.                         exit(-1);
  1749.                     }
  1750.                 }
  1751.                 break;
  1752.             case 'g': /* gather files into one directory */
  1753.                 Options |= O_GATHER | O_RECURSIVE;
  1754.                 break;
  1755.             case 'h': /* copy hidden files as well */
  1756.                 Options |= O_COPY_HIDDEN;
  1757.                 break;
  1758.             case 'i': /* interactive confirm */
  1759.                 Options |= O_INTERACTIVE;
  1760.                 break;
  1761.             case 'j': /* join files */
  1762.                 Options |= O_JOIN;
  1763.                 break;
  1764.             case 'l': /* large files (split) */
  1765.                 Options |= O_LARGEFILES;
  1766.                 break;
  1767.             case 'm': /* move mode */
  1768.                 Options |= O_MOVE;
  1769.                 break;
  1770.             case 'n': /* no action */
  1771.                 Options |= O_CP_IF_SRC_NEWER;
  1772.                 break;
  1773.             case 'o': /* copy if source is older */
  1774.                 Options |= O_COPY_IF_SRC_OLDER;
  1775.                 break;
  1776.             case 'r': /* update subdirectories */
  1777.                 Options |= O_RECURSIVE;
  1778.                 break;
  1779.             case 's': /* reference list from source */
  1780.                 Options |= O_SOURCE_DIR;
  1781.                 break;
  1782.             case 't':  /* get file list from target */
  1783.                 Options |= O_TARGET_DIR;
  1784.                 break;
  1785.             case 'v': /* verbose */
  1786.                 if (Options & O_VERBOSE)
  1787.                     Options |= O_X_VERBOSE;
  1788.                 else
  1789.                     Options |= O_VERBOSE;
  1790.  
  1791.                 break;
  1792.             case 'w': /* time check */
  1793.                 Options |=  O_TIME_CHECK;
  1794.                 set_Ftime(optarg);
  1795.                 break;
  1796.             case 'z': /* zap the target before copying */
  1797.                 Options |= O_ZAPTARGET;
  1798.                 break;
  1799.             case '?': /* documentation */
  1800.                 show_doc();
  1801.                 break;
  1802.             case '\0':
  1803.                 usage();
  1804.                 break;
  1805.             default:
  1806.                 break;
  1807.         }
  1808.     }
  1809.     set_defaults();
  1810. }
  1811.  
  1812. /*
  1813.  *    set_defaults()
  1814.  *
  1815.  *    Input:
  1816.  *        none
  1817.  *    Output:
  1818.  *        none
  1819.  *    Comments:
  1820.  *        sets the default command-line flags
  1821.  */
  1822.  
  1823. void set_defaults()
  1824.  
  1825. {
  1826.     if (!(Options & 
  1827.         (O_ARCHIVE | O_COPY_IF_SRC_OLDER | O_CP_IF_SRC_NEWER |
  1828.          O_DATE_CHECK | O_TIME_CHECK)))
  1829.         Options |= O_COPY_ALL;
  1830.  
  1831.     if (!(Options & (O_SOURCE_DIR | O_TARGET_DIR)))
  1832.         Options |= O_SOURCE_DIR;
  1833.     if ((Options & O_TIME_CHECK) && !(Options & O_DATE_CHECK)){
  1834.         Options |= O_DATE_CHECK;
  1835.         set_todays_date();
  1836.     }
  1837. }
  1838.  
  1839. /*
  1840.  *    check_flags()
  1841.  *
  1842.  *    Input:
  1843.  *        none
  1844.  *    Comments:
  1845.  *        terminates if the command-line flags are inconsistent
  1846.  */
  1847.  
  1848. void check_flags()
  1849. {
  1850.     if ((Options & O_SOURCE_DIR) && (Options & O_TARGET_DIR)){
  1851.         fprintf(stderr, "specify only one of '-s' and '-t' options\n");
  1852.         exit(-1);
  1853.     }
  1854.  
  1855.     if ((Options & O_BATCH) && (Options & O_INTERACTIVE)) {
  1856.         fprintf(stderr, "specify only one of '-b' and '-i' options\n");
  1857.         exit(-1);
  1858.     }
  1859. }
  1860.  
  1861. /*
  1862.  *    show_doc()
  1863.  *
  1864.  *    Comments:
  1865.  *        outputs expanded description of docp
  1866.  */
  1867.  
  1868. void show_doc()
  1869. {
  1870.     fprintf(stdout, FULL_DOC1);
  1871.     fprintf(stdout, FULL_DOC2);
  1872.     fprintf(stdout, FULL_DOC3);
  1873.     fprintf(stdout, FULL_DOC4);
  1874.     exit(0);
  1875. }
  1876.  
  1877. /*
  1878.  *    set_Ftime()
  1879.  *
  1880.  *    Input:
  1881.  *        time_str - time string following '-w' option. includes relation
  1882.  *            followed by time (e.g. "a3:15pm")
  1883.  *    Output:
  1884.  *        Ftime - global with reference time
  1885.  *        Time_check_mode - global with relationship to reference time
  1886.  *    Comments:
  1887.  *        takes time string specified in '-w' option and stores the
  1888.  *        time in Ftime and the relationship in Time_check_mode.
  1889.  */
  1890.  
  1891. void set_Ftime(char *time_str)
  1892. {
  1893.     TIME time;
  1894.     int i;
  1895.     char *time_arg = time_str;
  1896.  
  1897.     time.mode = 0;
  1898.     while ((*time_str == 'o') || (*time_str == 'b') || (*time_str == 'a'))
  1899.         switch(*time_str){
  1900.             case 'o':
  1901.                 time_str++;
  1902.                 time.mode |= D_ON;
  1903.                 break;
  1904.             case 'b':
  1905.                 time_str++;
  1906.                 time.mode |= D_BEFORE;
  1907.                 break;
  1908.             case 'a':
  1909.                 time_str++;
  1910.                 time.mode |= D_AFTER;
  1911.                 break;
  1912.             default:
  1913.                 break;
  1914.         }
  1915.     time.hour =  atoi(time_str);
  1916.     if ((time_str = get_field(time_str, (int *)&(time.min))) == NULL)
  1917.         bad_time(time_arg);
  1918.     while (*time_str != '\0')
  1919.         if (((*time_str == 'p') || (*time_str == 'P')) &&
  1920.             (time.hour < 13)){
  1921.             time.hour += 12;
  1922.             break;
  1923.         } else time_str++;
  1924.  
  1925.     check_time(&time, time_arg);
  1926.     store_time(&time);
  1927. }
  1928.  
  1929. /*
  1930.  *    store_time()
  1931.  *
  1932.  *    Input:
  1933.  *        time - structure containing time
  1934.  *    Output:
  1935.  *        Ftime - time in form returned by findnext()
  1936.  *    Comments:
  1937.  *        converts time to form returned by findnext()
  1938.  */
  1939.  
  1940. void store_time(TIME *time)
  1941. {
  1942.     TIME_NODE *d;
  1943.  
  1944.     if ((d = (TIME_NODE *)malloc(sizeof(TIME_NODE))) == NULL){
  1945.         fprintf(stderr, "out of memory");
  1946.         exit(-1);
  1947.     }
  1948.     d->ftime = (time->min << 5) | (time->hour << 11);
  1949.     d->mode = time->mode;
  1950.     d->next = Ftime;
  1951.     Ftime = d;
  1952. }
  1953.  
  1954. /*
  1955.  *    check_time()
  1956.  *
  1957.  *    Input:
  1958.  *        time - structure containing time
  1959.  *    Comments:
  1960.  *        does a crude check on the time entered
  1961.  */
  1962.  
  1963. void check_time(TIME *time, char *time_arg)
  1964. {
  1965.     if ((time->hour < 1) || (time->hour > 24) ||
  1966.          (time->min < 0) || (time->min > 60))
  1967.         bad_time(time_arg);
  1968. }
  1969.  
  1970. /*
  1971.  *    bad_time()
  1972.  *
  1973.  *    Comments:
  1974.  *        termination routine called when a bad time is found
  1975.  */
  1976.  
  1977. void bad_time(char *time_arg)
  1978. {
  1979.     fprintf(stderr, "bad time specified, '%s'\n", time_arg);
  1980.     exit(-1);
  1981. }
  1982.  
  1983. /*
  1984.  *    set_Fdate()
  1985.  *
  1986.  *    Input:
  1987.  *        date_str - date string following '-d' option. includes relation
  1988.  *            followed by date (e.g. ">=3/23/91")
  1989.  *    Output:
  1990.  *        Fdate - global with reference date
  1991.  *        Date_check_mode - global with relationship to reference date
  1992.  *    Comments:
  1993.  *        takes date string specified in '-d' option and stores the
  1994.  *        date in Fdate and the relationship in Date_check_mode.
  1995.  */
  1996.  
  1997. void set_Fdate(char *date_str)
  1998. {
  1999.     DATE date;
  2000.     int *p;
  2001.     int i;
  2002.  
  2003.     date.mode = 0;
  2004.     while ((*date_str == 'o') || (*date_str == 'b') || (*date_str == 'a')){
  2005.         switch(*date_str){
  2006.             case 'o':
  2007.                 date_str++;
  2008.                 date.mode |= D_ON;
  2009.                 break;
  2010.             case 'b':
  2011.                 date_str++;
  2012.                 date.mode |= D_BEFORE;
  2013.                 break;
  2014.             case 'a':
  2015.                 date_str++;
  2016.                 date.mode |= D_AFTER;
  2017.                 break;
  2018.             default:
  2019.                 break;
  2020.         }
  2021.     }
  2022.  
  2023.     convert_date_str(&date, date_str);
  2024.     check_date(&date, date_str);
  2025.     store_date(&date);
  2026. }
  2027.  
  2028. /*
  2029.  *    convert_date_str()
  2030.  *
  2031.  *    Input:
  2032.  *        date_str - date string from command line
  2033.  *    Output:
  2034.  *        date - numeric date structure
  2035.  *    Comments:
  2036.  *        converts command-line date string into numeric structure
  2037.  */
  2038.  
  2039. void convert_date_str(DATE *date, char *date_str)
  2040. {
  2041.     char *new_date_str;
  2042.  
  2043.     date->mo = atoi(date_str);
  2044.     if ((date_str = get_field(date_str, (int *)&(date->day))) == NULL)
  2045.         return;
  2046.     if ((date_str = get_field(date_str, (int *)&(date->year))) == NULL)
  2047.         return;
  2048. }
  2049.  
  2050.  
  2051. /*
  2052.  *    get_field()
  2053.  *
  2054.  *    Input:
  2055.  *        date_str - current position in date string from command line
  2056.  *    Output:
  2057.  *        date_str - new position in date string from command line
  2058.  *        date_field - numeric value of date field (day or year)
  2059.  *    Comments:
  2060.  *        gets numeric date from field of command-line date string
  2061.  */
  2062.  
  2063. char *get_field(char *string, int *field)
  2064. {
  2065.     char *new_string;
  2066.  
  2067.     new_string = strchr(string, '/');
  2068.     if (new_string == NULL)
  2069.         new_string = strchr(string, '-');
  2070.     if (new_string == NULL)
  2071.         new_string = strchr(string, ':');
  2072.     if (new_string == NULL){
  2073.         *field = 0;
  2074.     } else {
  2075.         new_string++;
  2076.         *field = atoi(new_string);
  2077.     }
  2078.  
  2079.     return(new_string);
  2080. }
  2081.  
  2082. /*
  2083.  *    set_todays_date()
  2084.  *
  2085.  *    Output:
  2086.  *        Fdate - adds today's date in date table with ON option set.
  2087.  *    Comments:
  2088.  *        stores today's date in date table.  Done when user only 
  2089.  *        specifies time option without date option.
  2090.  */
  2091.  
  2092. void set_todays_date()
  2093. {
  2094.     DATE date;
  2095.  
  2096.     get_todays_date(&date);
  2097.     date.mode = D_ON;
  2098.     store_date(&date);
  2099. }
  2100.  
  2101. /*
  2102.  *    get_todays_date()
  2103.  *
  2104.  *    Output:
  2105.  *        date - today's date from the operating system
  2106.  *    Comments:
  2107.  *        gets the current date from the operating system
  2108.  */
  2109.  
  2110. void get_todays_date(DATE *date)
  2111. {
  2112.     short cur_date = Tgetdate();
  2113.  
  2114.     date->year = (cur_date >> 9) + 80;
  2115.     date->mo = (cur_date >> 5) & 0xf;
  2116.     date->day = (cur_date) & 0x1f;
  2117. }
  2118.  
  2119. /*
  2120.  *    store_date()
  2121.  *
  2122.  *    Input:
  2123.  *        date - structure containing date
  2124.  *    Output:
  2125.  *        Fdate - date in form returned by findnext()
  2126.  *    Comments:
  2127.  *        converts date to form returned by findnext()
  2128.  */
  2129.  
  2130. void store_date(DATE *date)
  2131. {
  2132.     DATE_NODE *d;
  2133.  
  2134.     if ((d = (DATE_NODE *)malloc(sizeof(DATE_NODE))) == NULL){
  2135.         fprintf(stderr, "out of memory");
  2136.         exit(-1);
  2137.     }
  2138.     d->fdate = date->day | (date->mo << 5) | ((date->year - 80) << 9);
  2139.     d->mode = date->mode;
  2140.     d->next = Fdate;
  2141.     Fdate = d;
  2142. }
  2143.  
  2144. /*
  2145.  *    check_date()
  2146.  *
  2147.  *    Input:
  2148.  *        date - structure containing date
  2149.  *        date_str - date string from command line
  2150.  *    Comments:
  2151.  *        does a crude check on the date entered
  2152.  */
  2153.  
  2154. void check_date(DATE *date, char *date_str)
  2155. {
  2156.     if ((date->mo < 1) || (date->mo > 12) ||
  2157.          (date->day < 1) || (date->day > 31))
  2158.         bad_date(date_str);
  2159. }
  2160.  
  2161. /*
  2162.  *    bad_date()
  2163.  *
  2164.  *    Input:
  2165.  *        date_str - date string from command line
  2166.  *    Comments:
  2167.  *        termination routine called when a bad date is found
  2168.  */
  2169.  
  2170. void bad_date(char *date_str)
  2171. {
  2172.     fprintf(stderr, "bad date specified '%s'\n", date_str);
  2173.     exit(-1);
  2174. }
  2175.  
  2176. /*
  2177.  *    build_stdin_file_list()
  2178.  *
  2179.  *    Output:
  2180.  *        num_args - the number of args in the stdin file list
  2181.  *    Comments:
  2182.  *        builds linked list containing file list tokens from stdin.
  2183.  */
  2184.  
  2185. void build_stdin_file_list(int *num_args)
  2186. {
  2187.     char s[80];
  2188.  
  2189.     while (fgets(s, 79, stdin) != NULL){
  2190.         zap_trailing_nl(s, 79, stdin); /* clobber newline */
  2191.         add_to_stdin_list(s);
  2192.         (*num_args)++;
  2193.     }
  2194.     if (*num_args == 0){
  2195.         add_to_stdin_list("*.*");
  2196.         *num_args = 1;
  2197.     }
  2198. }
  2199.  
  2200.  
  2201. /*
  2202.  *    add_to_stdin_list()
  2203.  *
  2204.  *    input:
  2205.  *        s - the file list token
  2206.  *      Output:
  2207.  *              Stdin_list_head - first element of file list
  2208.  *              Stdin_list_tail - last element of file list
  2209.  *    Comments:
  2210.  *        adds a file list token to a linked list
  2211.  */
  2212.  
  2213. void add_to_stdin_list(char *s)
  2214. {
  2215.     STDIN_TOKEN *new_token;
  2216.  
  2217.     if ((new_token = (STDIN_TOKEN *)malloc(sizeof(STDIN_TOKEN))) == NULL){
  2218.             fprintf(stderr, "out of memory");
  2219.             exit(-1);
  2220.     }
  2221.     if ((new_token->string = (char *)malloc(strlen(s) + 1)) == NULL){
  2222.             fprintf(stderr, "out of memory");
  2223.             exit(-1);
  2224.     }
  2225.     strcpy(new_token->string, s);
  2226.     new_token->next = NULL;
  2227.     if (Stdin_list_head == NULL){
  2228.         Stdin_list_head = Stdin_list_tail = new_token;
  2229.         
  2230.     } else {
  2231.         Stdin_list_tail->next = new_token;
  2232.         Stdin_list_tail = new_token;
  2233.     }
  2234. }
  2235.