home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / FILE / DOCP_1_1.ZIP / DOCP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-04  |  56.0 KB  |  2,401 lines

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