/* docp.c - Directory-Oriented CoPy Fancy file copy program for Atari ST Copywrite 1992, Roy Bixler Originally by: David Oertel Atari ST port, overall cheez-whiz: Roy Bixler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #define MINT_LIB #ifdef MINT_LIB /* long _read(int, void *, long); */ /* long _write(int, const void *, long); */ #define read(x, y, z) _read(x, y, z) #define write(x, y, z) _write(x, y, z) #else #define read(x, y, z) lread(x, y, z) #define write(x, y, z) lwrite(x, y, z) #endif #define MAXDIR 128 #define MAXPATH 128 #define OPT_LIST "abcdfghijlmnorstvwz?" #define GET_OPT_LIST "AaBbCcD:d:F:f:GgHhIiJjLlMmNnOoRrSsTtVvW:w:Zz?" #define DOT_NOTATION(dir) !strcmp(dir, ".") #define MAX_BUF 0xfffeU #include "docp.h" #include "doc.h" #include "elib.h" #include "protypes.h" /* options */ extern int Optind; extern char *optarg; long Options = 0L; char Cur_source_dir[FILENAME_MAX], Org_dest_dir[FILENAME_MAX]; ENTRY *Ptr; void *Buf_ptr; void *File_buf; int Reading_flag; /* set if 'reading:' has been printed last * reset if 'writing:' has been printed last */ /* modes of date compare via 'd' option */ #define D_BEFORE 1 #define D_ON 2 #define D_AFTER 4 DATE_NODE *Fdate; /* used to store date entered via 'd' option */ TIME_NODE *Ftime; /* used to store time entered via 'w' option */ FILE *From_file_ptr; /* file containing file list ('-f' option) */ int Retry = 0; /* set if target disk is full and user chooses to * continue */ int Copied_a_file = 0; /* set if at least one file was copied */ ENTRY *Src_tab[HASH_TAB_SIZE]; /* contains source-file names */ ENTRY *Dst_tab[HASH_TAB_SIZE]; /* contains destination-file names */ typedef struct file_list { char *string; struct file_list *next; } STDIN_TOKEN; /* linked list globals containing file list from stdin */ STDIN_TOKEN *Stdin_list_head = NULL; STDIN_TOKEN *Stdin_list_tail = NULL; STDIN_TOKEN *Stdin_list_current = NULL; int main(int argc, char *argv[]) { char src_dir[FILENAME_MAX]; char dst_dir[FILENAME_MAX]; unsigned long buf_size; int num_args; char **argv_ptr; char *all[2] = { "*.*", NULL }; get_flags(argv, argc); check_flags(); check_and_format_dirs(argc, argv, src_dir, dst_dir); check_target_removeable(dst_dir); File_buf = get_file_buf(&buf_size); Buf_ptr = File_buf; argv += Optind + 2; num_args = argc - Optind - 2; argv_ptr = argv; if (Options & O_FROM_STDIN) build_stdin_file_list(&num_args); else if (Options & O_FROM_FILE){ char buf[80]; num_args = 0; while (fgets(buf, 79, From_file_ptr) != NULL) num_args++; rewind(From_file_ptr); } else if (num_args == 0) { num_args = 1; argv_ptr = all; } strcpy(Org_dest_dir, dst_dir); if (Options & O_CHECK) fprintf(stdout, "The following files would be copied or moved:\n"); else if (Options & O_ZAPTARGET) zap_target(dst_dir, 1); copy_files(src_dir, dst_dir, argv_ptr, num_args, File_buf, buf_size); write_buffer(File_buf); clear_archive_bits(src_dir); if (!Copied_a_file) fprintf(stdout, "no files copied\n"); free(File_buf); exit(0); } /* * check_and_format_dirs() * * Input: * argc - command line argument count * argv - command line arguments * Optind - argument index from getopt() * Output: * src_dir - source directory name * dst_dir - destination directory name * Comments: * The source and destination directories are formatted * The source directory is checked for existence * The destination directory is created if it doesn't exist * The two directories are checked to insure that they are * different * The destination is checked to insure that it is not a * subdirectory of the source */ void check_and_format_dirs(int argc, char *argv[], char *src_dir, char *dst_dir) { if ((argc-Optind) < 2) usage(); strcpy(src_dir, argv[Optind]); check_if_dir_exists(src_dir, 0); strcpy(dst_dir, argv[Optind + 1]); check_if_dir_exists(dst_dir, 1); format_dir(argv[Optind], '\1', src_dir); format_dir(argv[Optind + 1], '\1', dst_dir); check_if_dirs_compatible(src_dir, dst_dir); } /* * check_target_removeable() * * Input: * dst_dir - destination directory name * Output: * Options - will be modified if target directory is removeable * (i.e. a floppy disk) */ void check_target_removeable(char *dst_dir) { char dst_drive; if (islower(dst_drive = dst_dir[0])) dst_drive = toupper(dst_drive); if ((dst_drive == 'A') || (dst_drive == 'B')) Options |= O_TARGET_REMOVEABLE; } /* * check_if_dirs_compatible() * * Input: * src_dir - The formatted source directory name * dst_dir - The formatted destination directory name * Comments: * terminates if: * 1 - The two directories are the same * 2 - The destination is a subdirectory of * the source */ void check_if_dirs_compatible(char *src_dir, char *dst_dir) { if (Options & O_CHECK) return; if (!strcmp(src_dir, dst_dir)){ fprintf(stderr, "source and destination directories are the same\n"); exit(-1); } if ((Options & O_RECURSIVE) && !strncmp(src_dir, dst_dir, strlen(src_dir))){ fprintf(stderr, "destination directory is a subdirectory of the source directory \nwhile in recursive mode\n"); exit(-1); } } /* * check_if_dir_exists() * * Input: * dir - the directory name * is_dest - flag set if directory is a destination directory * Comments: * creates destination directory if it doesn't exist * terminates if the source directory doesn't exist */ void check_if_dir_exists(char *dir, int is_dest) { int retval; struct stat statbuf; if (!((DOT_NOTATION(dir)) || ((!stat(dir, &statbuf)) && (statbuf.st_mode & S_IFDIR)))) if ((is_dest) && ((Options & O_BATCH) || (printf("directory %s does not exist - ", dir), ask_user("create it (Y/N/Q) ? ")))) create_dir(dir); else { printf("docp: directory '%s' does not exist\n", dir); exit(-1); } } /* * create_dir() * * Input: * dir - the directory to be created * Comments: * creates destination directory */ void create_dir(char *dir) { int retval; char next_dir[MAXDIR], *p; strcpy(next_dir, dir); for (p=next_dir; *p; p++) if (*p == '/') *p = '\\'; p = next_dir; while ((p = strchr(p, '\\')) != NULL) { *p = '\0'; mkdir(next_dir, 0); *p++ = '\\'; } ; if (mkdir(dir, 0) == -1){ fprintf(stderr, "cannot create directory '%s'\n", dir); exit(-1); } else if (Options & O_VERBOSE) printf("created directory '%s'\n", dir); } /* * return_to_cur_dir() * * Input: * cur_dir - the current working directory * other_dir - the current working directory of the * destination drive * Comments: * returns to current working directory */ void return_to_cur_dir(char *cur_dir) { chdir(cur_dir); } /* * root_dir() * * Input: * dir - the directory to be checked * Output: * returns : * 1 - if 'dir' is a root directory * 0 - otherwise */ int root_dir(char *dir) { if (((*(dir + 1) == ':') && (strlen(dir) == 2)) || ((strlen(dir) == 3) && (*(dir + 2) == '\\'))) return(1); else return(0); } /* * copy_files() * * Input: * src_dir - the source directory * dst_dir - the destination directory * file_spec - the command-line file list * num_args - the number of arguments in the file list * file_buf - the buffer for reading and writing files * buf_size - the size of 'file_buf' * Comments: * copy or move the files from the source directory to the * destination directory */ void copy_files(char *src_dir, char *dst_dir, char *file_spec[], int num_args, void *file_buf, unsigned long buf_size) { struct _dta fblk; char ref_list[FILENAME_MAX]; char src_file[FILENAME_MAX], dst_file[FILENAME_MAX]; int done, index; long file_size; char *file; strcpy(Cur_source_dir, src_dir); build_hash_tab(src_dir, Src_tab, num_args, file_spec); build_hash_tab(dst_dir, Dst_tab, num_args, file_spec); file = get_first(&index); while (file != NULL) { strcpy(src_file, src_dir); strcat(src_file, file); strcpy(dst_file, dst_dir); strcat(dst_file, file); if (should_file_copy(file, src_file)) if (Options & O_CHECK) fprintf(stdout, "\t%s -> %s\n", src_file, dst_file); else copy_file(Ptr, dst_file, src_file, buf_size); else report_not_copied(src_file); file = get_next(&index); } clear_archive_bits(src_dir); copy_sub_dirs(src_dir, dst_dir, file_spec, num_args, file_buf, buf_size); if (Options & O_MOVE) rmdir(src_dir); /* don't try too hard, but do it if we can ... */ if (Options & O_ZAPTARGET) rmdir(dst_dir); } /* * report_not_copied() * * Input: * src_dir - source directory * Comments: * reports that a file was not copied */ void report_not_copied(char *src_file) { if ((Options & O_X_VERBOSE) && !(Options & O_CHECK)){ if (!Reading_flag){ fprintf(stdout, "reading:\n"); Reading_flag = 1; } printf("\t%s ** NOT COPIED **\n", src_file); } } /* * zap_target() * * Input: * dst_dir - the destination directory * print_heading - true on entry, false for recursive calls * Comments: * zaps the files in the target directory */ void zap_target(char *dst_dir, int print_banner) { int done, attrib = 0, i; struct _dta *odta, fblk; char dst_file[FILENAME_MAX], buf[MAXPATH+20]; odta = (struct _dta *) Fgetdta(); Fsetdta(&fblk); if (Options & O_COPY_HIDDEN) attrib |= (FA_HIDDEN|FA_SYSTEM); if ((Options & O_RECURSIVE) && (!(Options & O_GATHER))) attrib |= FA_DIR; strcpy(dst_file, dst_dir); if (dst_file[(i = strlen(dst_file))-1] != '\\') { strcat(dst_file, "\\"); i++; } strcat(dst_file, "*.*"); done = Fsfirst(dst_file, attrib); if ((!done) && (Options & O_VERBOSE) && (print_banner)) printf("deleting:\n"); while (!done) { strcpy(dst_file+i, fblk.dta_name); if (fblk.dta_attribute & FA_DIR) { if (!is_special(fblk.dta_name)) { zap_target(dst_file, 0); rmdir(dst_file); } } else if ((!(Options & O_INTERACTIVE)) || (sprintf(buf, "Delete file %s (Y/N/Q) ?", dst_file), ask_user(buf))) { if (Options & O_VERBOSE) printf("\t%s\n", dst_file); delete_file(dst_file, '\1'); } done = Fsnext(); } Fsetdta(odta); } /* * build_hash_tab() * * Input: * dir - the directory for which to table will be built * num_args - the number of arguments in the file list * file_spec - the command-line file list * Output: * hash_tab - the hash table containing all the file names * of the directory 'dir' * Comments: * builds a hash table containing all the file names of a * directory specified by the command-line file list. * First it adds all the names specified by the file * list, then it subtracts those specified * by the '-' notation in the file list. */ void build_hash_tab(char *dir, ENTRY *hash_tab[], int num_args, char *file_spec[]) { add_to_hash_tab(dir, hash_tab, 1, num_args, file_spec); take_from_hash_tab(dir, hash_tab, num_args, file_spec); } /* * add_to_hash_tab() * * Input: * dir - the directory for which to table will be built * from_file_poss - is it possible to get file list from a file? * num_args - the number of arguments in the file list * file_spec - the command-line file list * Output: * hash_tab - the hash table containing all the file names * of the directory 'dir' * Comments: * adds all the files specified by the file list and within * the directory 'dir' to the hash table. */ void add_to_hash_tab(char *dir, ENTRY *hash_tab[], int from_file_poss, int num_args, char *file_spec[]) { int done; struct _dta *odta, fblk; char ref_list[FILENAME_MAX]; int i; int files_added = 0; char buf[80]; if ((from_file_poss) && (Options & O_FROM_STDIN)) Stdin_list_current = Stdin_list_head; if ((from_file_poss) && (Options & O_FROM_FILE)) rewind(From_file_ptr); odta = (struct _dta *) Fgetdta(); Fsetdta(&fblk); for (i = 0; i < num_args; i++){ strcpy(ref_list, dir); get_file_spec(buf, file_spec, i); if (*buf != '-'){ int attrib = 0; files_added = 1; strcat(ref_list, (*buf == '\\') ? buf+1 : buf); if (Options & O_COPY_HIDDEN) attrib |= (FA_HIDDEN|FA_SYSTEM); done = Fsfirst(ref_list, attrib); while (!done) { if (find_entry(fblk.dta_name, hash_tab) == NULL) add_entry(&fblk, hash_tab); done = Fsnext(); } } } if (!files_added){ char *all[2] = { "*.*", NULL }; add_to_hash_tab(dir, hash_tab, 0, 1, all); } Fsetdta(odta); } /* * take_from_hash_tab() * * Input: * dir - the directory for which to table will be built * num_args - the number of arguments in the file list * file_spec - the command-line file list * Output: * hash_tab - the hash table containing all the file names * of the directory 'dir' and specified by the file * list, 'file_spec'. * Comments: * takes all the files specified by using the '-' notation * and within the directory 'dir' from the hash table. */ void take_from_hash_tab(char *dir, ENTRY *hash_tab[], int num_args, char *file_spec[]) { int done; struct _dta *odta, fblk; char ref_list[FILENAME_MAX]; int i; char buf[80]; odta = (struct _dta *) Fgetdta(); Fsetdta(&fblk); if (Options & O_FROM_STDIN) Stdin_list_current = Stdin_list_head; if (Options & O_FROM_FILE) rewind(From_file_ptr); for (i = 0; i < num_args; i++){ strcpy(ref_list, dir); get_file_spec(buf, file_spec, i); if (*buf == '-'){ int attrib = 0; strcat(ref_list, buf + 1); if (Options & O_COPY_HIDDEN) attrib |= (FA_HIDDEN|FA_SYSTEM); done = Fsfirst(ref_list, attrib); while (!done){ remove_entry(fblk.dta_name, hash_tab); done = Fsnext(); } } } Fsetdta(odta); } /* * get_file_spec() * * Input: * file_spec - the command-line file list * i - index into the command-line file list * Output: * buf - the next token from the command line file list * Comments: * gets the next token from the command-line file list */ void get_file_spec(char *buf, char *file_spec[], int i) { if (Options & O_FROM_STDIN){ strcpy(buf, Stdin_list_current->string); Stdin_list_current = Stdin_list_current->next; } else if (Options & O_FROM_FILE){ fgets(buf, 79, From_file_ptr); zap_trailing_nl(buf, 79, From_file_ptr); /* clobber newline */ } else { strcpy(buf, file_spec[i]); } } /* * add_entry() * * Input: * fblk - structure containing the file name, date, and time * Output: * hash_tab - add entry to this array of pointers * Comments: * ands a file name along with its date and time to a hash table */ void add_entry(struct _dta *fblk, ENTRY *hash_tab[]) { int bucket; ENTRY *ptr; bucket = hashpjw(fblk->dta_name); if (hash_tab[bucket] == NULL){ if ((hash_tab[bucket] = (ENTRY *)malloc(sizeof(ENTRY))) == NULL){ fprintf(stderr, "out of memory"); exit(-1); } hash_tab[bucket]->next = NULL; } else { if ((ptr = (ENTRY *)malloc(sizeof(ENTRY))) == NULL){ fprintf(stderr, "out of memory"); exit(-1); } ptr->next = hash_tab[bucket]; hash_tab[bucket] = ptr; } strcpy(hash_tab[bucket]->name, fblk->dta_name); hash_tab[bucket]->attr = fblk->dta_attribute; hash_tab[bucket]->copied = 0; hash_tab[bucket]->fdate = fblk->dta_date; hash_tab[bucket]->ftime = fblk->dta_time; } /* * get_first() * * Input: * none * Output: * index - index to first hash table bucket. Each bucket is a * linked-list of structures, one for each file. * Ptr - pointer to first hash table file entry * returns - the name of the first file in the hash table * Comments: * selects the proper hash table and finds its first entry */ char *get_first(int *index) { *index = 0; Ptr = (Options & O_TARGET_DIR) ? Dst_tab[0] : Src_tab[0]; move_Ptr(index); return (Ptr == NULL) ? NULL : Ptr->name; } /* * get_next() * * Input: * index - index to current hash-table bucket. Each bucket is a * linked-list of structures, one for each file. * Ptr - pointer to current hash-table file entry * Output: * index - index to next hash table bucket (may not be different * from current bucket). * Ptr - pointer to next hash table file entry * returns - the name of the next file in the hash table * * Comments: * finds the next entry in the currently selected hash table * of file names */ char *get_next(int *index) { if (Ptr != NULL) Ptr = Ptr->next; move_Ptr(index); return (Ptr == NULL) ? NULL : Ptr->name; } /* * move_Ptr() * * Input: * index - index to current hash-table bucket. Each bucket is a * linked-list of structures, one for each file. * Ptr - pointer to hash table file entry * Output: * index - index to hash-table bucket. * Ptr - pointer to hash table file entry * Comments: * finds the next non-NULL entry, only if the hash-table * pointer is NULL */ void move_Ptr(int *index) { if (Ptr == NULL) for ((*index)++; (*index < HASH_TAB_SIZE); (*index)++) if ((Ptr = ((Options & O_TARGET_DIR) ? Dst_tab[*index] : Src_tab[*index])) != NULL) break; } /* * clear_archive_bits * * given a source directory, go through the hash table (up to the value * of Ptr on entry to this function) and clear the archive bits of all * file entries. This has the desired effect of clearing archive bits * of all source files which have been copied. */ void clear_archive_bits(char *src_dir) { ENTRY *Org_Ptr = Ptr; char source_file[FILENAME_MAX], *cur_name; int index, n; if ((Options & O_ARCHIVE) && (!(Options & O_CHECK))) { n = strlen(src_dir); strcpy(source_file, src_dir); cur_name = get_first(&index); while (Ptr != NULL) { if (Ptr->copied) { strcat(source_file, cur_name); Fattrib(source_file, 1, ((Ptr->copied)&(~FA_CHANGED))); source_file[n] = '\0'; } if (Org_Ptr == Ptr) break; else cur_name = get_next(&index); } } } /* * clear_hash_tab() * * Input: * hash_tab - the hash table to be cleared * Output: * hash_tab - with all its entries cleared and all its * buckets set to NULL * Comments: * removes all entries from a hash table */ void clear_hash_tab(ENTRY *hash_tab[]) { int i; ENTRY *ptr, *temp; for (i = 0; i < HASH_TAB_SIZE; i++){ ptr = hash_tab[i]; while (ptr != NULL){ temp = ptr; ptr = ptr->next; free(temp); } hash_tab[i] = NULL; } } /* * remove_entry() * * Input: * file - the name of the file to be removed * hash_tab - the hash table from which the file is to be * removed * Output: * hash_tab - the hash table with the file removed * Comments: * removes one entry from a hash table */ void remove_entry(char *file, ENTRY *hash_tab[]) { int bucket; ENTRY *ptr, *temp; ENTRY **lastptr; bucket = hashpjw(file); if (hash_tab[bucket] != NULL) { ptr = hash_tab[bucket]; lastptr = &(hash_tab[bucket]); while(ptr != NULL){ if (!strcmp(file, ptr->name)){ *lastptr = ptr->next; free(ptr); break; } lastptr = &(ptr->next); ptr = ptr->next; } } } /* * find_entry() * * Input: * file - the name of the file to be found * hash_tab - the hash table which is to be searched * Output: * returns - a pointer to the entry in the hash table, * or NULL if not found * Comments: * finds an entry in the hash table */ ENTRY *find_entry(char *file, ENTRY *hash_tab[]) { int bucket; ENTRY *ptr, *temp; bucket = hashpjw(file); if (hash_tab[bucket] == NULL){ ptr = NULL; } else { ptr = hash_tab[bucket]; while(ptr != NULL){ if (!strcmp(file, ptr->name)){ break; } ptr = ptr->next; } } return(ptr); } /* * copy_sub_dirs() * * Input: * src_dir - the source directory * dst_dir - the destination directory * file_spec - the command-line file list * num_args - the number of arguments in the file list * file_buf - the buffer for reading and writing files * buf_size - the size of 'file_buf' * Comments: * copies the files in the sub-directories if the recursive * mode is specified */ void copy_sub_dirs(char *src_dir, char *dst_dir, char *file_spec[], int num_args, void *file_buf, unsigned long buf_size) { int done; char ref_list[FILENAME_MAX]; struct _dta *odta, fblk; if (Options & O_RECURSIVE){ strcpy(ref_list, src_dir); strcat(ref_list, "*.*"); odta = (struct _dta *) Fgetdta(); Fsetdta(&fblk); done = Fsfirst(ref_list, FA_DIR); while (!done){ if ((fblk.dta_attribute & FA_DIR) && (!is_special(fblk.dta_name))) { char new_src_dir[FILENAME_MAX], new_dst_dir[FILENAME_MAX]; if (should_dir_copy(src_dir, dst_dir, fblk.dta_name, new_src_dir, new_dst_dir)){ clear_hash_tab(Src_tab); clear_hash_tab(Dst_tab); copy_files(new_src_dir, new_dst_dir, file_spec, num_args, file_buf, buf_size); } } done = Fsnext(); } Fsetdta(odta); } } /* * should_dir_copy() * * Input: * src_dir - the full path of the current source directory * dst_dir - the full path of the current destination directory * name - the name of the sub-directory * Output: * new_src_dir - the full path of the source sub-directory * new_dst_dir - the full path of the destination sub-directory * returns - 1 if sub-directory should be copied * 0 if sub-directory should NOT be copied * Comments: * determines whether a sub-directory should be copied */ int should_dir_copy(char *src_dir, char *dst_dir, char *name, char *new_src_dir, char *new_dst_dir) { struct stat src_buf, dst_buf; int ret_src, ret_dst; int status; int ret_val; strcpy(new_src_dir, src_dir); strcat(new_src_dir, name); strcpy(new_dst_dir, dst_dir); if (Options & O_GATHER) new_dst_dir[strlen(new_dst_dir) - 1] = '\0'; /* chop slash */ else strcat(new_dst_dir, name); ret_src = stat(new_src_dir, &src_buf); ret_dst = stat(new_dst_dir, &dst_buf); if (ret_src == -1) /* sub dir does not exist in source dir */ ret_val = 0; else if (!(src_buf.st_mode & S_IFDIR)) /* src dir is a file */ ret_val = 0; else if (Options & (O_GATHER|O_CHECK)) ret_val = 1; else if (ret_dst == -1) { /* destination dir does not exist */ status = mkdir(new_dst_dir, 0); if (status) { fprintf(stderr, "unable to create directory\n"); exit(-1); } ret_val = 1; } else ret_val = 1; strcat(new_src_dir, "\\"); strcat(new_dst_dir, "\\"); return(ret_val); } /* * should_file_copy() * * Input: * file - name of file to be copied * src_file - full path of file to be copied * Output: * returns: 1 if file should be copied/moved * 0 if file should NOT be copied/moved * Comments: * looks up the file name in the source and destination * hash tables and determines whether a file should be * copied/moved */ int should_file_copy(char *file, char *src_file) { ENTRY *src, *dst; /* source does not exit */ if ((src = find_entry(file, Src_tab)) != NULL) { if (Options & O_DATE_CHECK) if (!(within_date_range(src))) return 0; if (Options & O_ARCHIVE) if (!(src->attr & FA_CHANGED)) return 0; if ((dst = find_entry(file, Dst_tab)) != NULL) { if ((Options & (O_CP_IF_SRC_NEWER|O_COPY_IF_SRC_OLDER)) == (O_CP_IF_SRC_NEWER|O_COPY_IF_SRC_OLDER)) return 0; if (Options & O_CP_IF_SRC_NEWER) if (cmptime_entry(src, dst) <= 0L) return 0; if (Options & O_COPY_IF_SRC_OLDER) if (cmptime_entry(src, dst) >= 0L) return 0; } } else /* source file missing? of course don't copy (something's fishy!) */ return 0; if (Options & O_INTERACTIVE) { char buf[MAXPATH + 20]; sprintf(buf, "copy %s (Y/N/Q) ? ", src_file); return ask_user(buf); /* user has the final say-so */ } return 1; } /* * cmptime_entry * * given two files, return positive if the first has a more recent modification * date/time, zero if the files have the same modification date/time or * negative if the second is more recent. */ long cmptime_entry(ENTRY *a, ENTRY *b) { return (((unsigned long)a->fdate) << 16 | (unsigned long)a->ftime) - (((unsigned long)b->fdate) << 16 | (unsigned long)b->ftime); } int within_date_range(ENTRY *src) { int retval = 1; DATE_NODE *d = Fdate; TIME_NODE *t = Ftime; int saw_after_or_before = 0; /* AND the 'before' and 'after' modes */ while (d != NULL){ if ((d->mode & D_BEFORE) || (d->mode & D_AFTER)) saw_after_or_before = 1; if (((d->mode & D_BEFORE) && (src->fdate >= d->fdate)) || ((d->mode & D_AFTER) && (src->fdate <= d->fdate))){ retval = 0; } d = d->next; } if (!saw_after_or_before) retval = 0; /* OR the 'on' modes */ d = Fdate; while (d != NULL){ if ((d->mode & D_ON) && (src->fdate == d->fdate)) retval = 1; d = d->next; } /* AND the 'before' and 'after' modes */ if (retval) while (t != NULL){ if (((t->mode & D_BEFORE) && (src->ftime >= t->ftime)) || ((t->mode & D_AFTER) && (src->ftime <= t->ftime))) retval = 0; t = t->next; } return retval; } /* * file_exists() * * Input: * name - full path of file * Output: * returns: 1 if file exists * 0 if file does NOT exist */ int file_exists(char *name) { return (access(name, 0) == 0); } /* * get_file_buf() * * Input: * Output: * buf_size - size of buffer * returns - pointer to buffer */ void *get_file_buf(unsigned long *buf_size) { void *buf_mem; *buf_size = MAX_BUF; do { if ((buf_mem = malloc(*buf_size)) != NULL) break; *buf_size /= 2; } while (*buf_size >= 512); if (buf_mem == NULL){ fprintf(stderr, "could not allocate file buffer\n"); exit(-1); } return(buf_mem); } /* * copy_file() * * Input: * ptr - pointer to hash table entry for file * dst_file - full path of destination file * src_file - full path of source file * buf_size - size of buffer for file i/o * File_buf - buffer for file i/o * Buf_ptr - pointer to next available memory in i/o buffer * Reading_flag - indicates whether 'reading:' has been printed * Comments: * copies source file to destination file */ void copy_file(ENTRY *ptr, char *dst_file, char *src_file, unsigned long buf_size) { int src_handle, dst_handle; long bytes; int retval; long bytes_needed, bytes_left; struct stat stat_buf; _DOSTIME ftime_buf; char fattr; if ((Options & O_MOVE) && (*src_file == *dst_file)){ if (!Reading_flag && (Options & O_VERBOSE)){ fprintf(stdout, "renaming file:\n"); fprintf(stdout, "\t%s -> %s\n", src_file, dst_file); Reading_flag = 0; } if (rename_file(ptr, src_file, dst_file)) clean_up_and_exit(); } else { if ((src_handle = open(src_file, O_RDONLY, 0)) < 0){ fprintf(stderr, "unable to open file '%s'\n", src_file); clean_up_and_exit(); } Fdatime(&ftime_buf, src_handle, 0); fstat(src_handle, &stat_buf); fattr = Fattrib(src_file, 0, 0); bytes_needed = sizeof(ENTRY *) + (strlen(src_file) + 1) + (strlen(dst_file) + 1) + sizeof(stat_buf.st_size) + stat_buf.st_size + sizeof(_DOSTIME) + sizeof(fattr); bytes_left = (char *)File_buf - (char *)Buf_ptr + buf_size; if ((bytes_needed > MAX_BUF) || (Options & O_INTERACTIVE)) { if (Buf_ptr != File_buf) write_buffer(File_buf); copy_file_unbuffered(src_handle, ptr, src_file, dst_file, stat_buf.st_size, &ftime_buf, fattr, buf_size); } else { if (bytes_left < bytes_needed) write_buffer(File_buf); if (!Reading_flag && (Options & O_VERBOSE)){ fprintf(stdout, "reading:\n"); Reading_flag = 1; } if (Options & O_VERBOSE) fprintf(stdout, "\t%s\n", src_file); copy_file_to_buffer(src_handle, ptr, src_file, dst_file, &ftime_buf, stat_buf.st_size, fattr); } close(src_handle); } } /* * rename_file() * * Input: * ptr - pointer to hash table entry (so we can mark this as 'copied') * src_file - source file name (full path) * dst_file - destination file name (full path) * Comments: * renames a file. if the destination file already exits, * then it is deleted. */ int rename_file(ENTRY *ptr, char *src_file, char *dst_file) { int old_attrib; if ((old_attrib = Fattrib(src_file, 0, 0)) < 0) { fprintf(stderr, "cannot move file %s\n", src_file); return -1; } if (rename(src_file, dst_file) == -1){ if (file_exists(dst_file)) if (delete_file(dst_file, '\0')){ if ((!(Options & O_BATCH)) && (printf("Target %s protected - ", dst_file), !(ask_user("force move onto it (Y/N/Q)? ")))){ fprintf(stderr, "%s NOT moved to %s\n", src_file, dst_file); return -1; } if (delete_file(dst_file, '\1')){ fprintf(stderr, "could not remove %s\n", dst_file); return -1; } } if (old_attrib & FA_RDONLY) Fattrib(src_file, 1, old_attrib&(~FA_RDONLY)); if (rename(src_file, dst_file) == -1){ if (old_attrib & FA_RDONLY) Fattrib(src_file, 1, old_attrib); fprintf(stderr, "cannot move file %s\n", src_file); return -1; } } Fattrib(dst_file, 1, old_attrib); Copied_a_file = 1; ptr->copied = (old_attrib) ? old_attrib : FA_CHANGED; return 0; } /* * copy_file_to_buffer() * * Input: * src_handle - source file handle * src_file - source file name (full path) * dst_file - destination file name (full path) * st_buf - stat buffer of source file * attrib - file access mode (contains hidden) * Output: * Buf_ptr - pointer to unused position in buffer * Comments: * copies the source file along with a header to memory. * the header contains source-file name, dest-file name, * source-file date, source-file size, and the source-file * modes. */ void copy_file_to_buffer(int src_handle, ENTRY *ptr, char *src_file, char *dst_file, _DOSTIME *ftime_buf, long fsize, char fattr) { memcpy((char *)Buf_ptr, &ptr, sizeof(ENTRY *)); Buf_ptr = (char *)Buf_ptr + sizeof(ENTRY *); strcpy((char *)Buf_ptr, src_file); Buf_ptr = (char *)Buf_ptr + strlen(src_file) + 1; strcpy((char *)Buf_ptr, dst_file); Buf_ptr = (char *)Buf_ptr + strlen(dst_file) + 1; memcpy(Buf_ptr, &fsize, sizeof(fsize)); Buf_ptr = (char *) Buf_ptr + sizeof(long); memcpy(Buf_ptr, ftime_buf, sizeof(_DOSTIME)); Buf_ptr = (char *) Buf_ptr + sizeof(_DOSTIME); memcpy(Buf_ptr, &fattr, sizeof(char)); Buf_ptr = (char *) Buf_ptr + sizeof(char); read(src_handle, Buf_ptr, fsize); Buf_ptr = (char *) Buf_ptr + fsize; } /* * copy_file_unbuffered() * * Input: * src_handle - handle of source file * src_file - name of source file (full path name) * dst_file - name of destination file (full path name) * buf_size - size of file buffer * Reading_flag - indicates whether 'reading:' has been printed * File_buf - buffer for file i/o * Output: * Reading_flag - reset to indicate that the message * 'reading and writing file:' has been printed. * Comments: * copies the source file to the destination file without * buffering (as is done with smaller files). the file modes * and file date are also copied. */ void copy_file_unbuffered(int src_handle, ENTRY *ptr, char *src_file, char *dst_file, long fsize, _DOSTIME *ftime_buf, char fattr, unsigned long buf_size) { unsigned bytes = 0; int dst_handle; int retval; do { Retry = 0; if (open_dest_file(&dst_handle, dst_file, (fattr & FA_RDONLY) ? S_IREAD : S_IREAD | S_IWRITE)) return; if (!Reading_flag && (Options & O_VERBOSE)){ fprintf(stdout, "reading and writing file:\n"); fprintf(stdout, "\t%s -> %s\n", src_file, dst_file); Reading_flag = 0; } if (!(Options & O_LARGEFILES)) lseek(src_handle, 0L, SEEK_SET); copy_file_contents(src_handle, dst_handle, src_file, dst_file, buf_size); } while (Retry); if (Options & O_MOVE) delete_file(src_file, '\0'); Copied_a_file = 1; Fdatime(ftime_buf, dst_handle, 1); close(dst_handle); Fattrib(dst_file, 1, fattr); ptr->copied = (fattr) ? fattr : FA_CHANGED; } /* * ensure_dest_dir_exist * * given a destination file name, first ensure the existence of the directory * which will contain it. This function exists to make a target-disk swap work * together with the recursive option. Return non-zero on success, zero on * failure. */ int ensure_dest_dir_exist(char *dst_file) { int stat_err, ret_val = 1; struct stat statbuf; char *p; if ((p = strrchr(dst_file, '\\')) == NULL) return 0; *p = '\0'; if ((!(stat_err = stat(dst_file, &statbuf))) && (!(statbuf.st_mode & S_IFDIR))) ret_val = 0; else if (stat_err) create_dir(dst_file); *p = '\\'; return ret_val; /* if 'create_dir()' returns, it was successful */ } /* * target_disk_full * * called when the target disk is full. Depending on the options, it may * be possible to change target disks and go on. */ void target_disk_full(char *dst_file, int dst_handle) { close(dst_handle); if (!(Options & O_LARGEFILES)) delete_file(dst_file, '\1'); if (ok_to_retry()) if (ask_user("out of disk space, continue (Y/N/Q) ? ")) { if (Options & O_ZAPTARGET) zap_target(Org_dest_dir, 1); Retry = 1; } else clean_up_and_exit(); else { fprintf(stderr, "out of disk space\n"); clean_up_and_exit(); } } /* * clean_up_and_exit * * error occurred (like write error/target full). Clean up (clear archive * bits of source files already copied) and exit. */ void clean_up_and_exit() { clear_archive_bits(Cur_source_dir); exit(-1); } /* * copy_file_contents() * * Input: * src_handle - handle of source file * src_handle - handle of destination file * src_file - name of source file (full path name) * dst_file - name of destination file (full path name) * buf_size - size of file buffer * File_buf - buffer for file i/o * Comments: * copies the contents of the source file to the destination * file. */ void copy_file_contents(int src_handle, int dst_handle, char *src_file, char *dst_file, unsigned long buf_size) { long last_read_pos, bytes_read, bytes_written; while(1){ last_read_pos = tell(src_handle); bytes_read = read(src_handle, File_buf, buf_size); if (bytes_read == -1L){ fprintf(stderr, "Can't read file '%s'\n", src_file); exit(-1); } if (bytes_read){ bytes_written = write(dst_handle, File_buf, (unsigned long) bytes_read); if (bytes_written == -1L){ fprintf(stderr, "Can't write to file '%s'\n", dst_file); exit(-1); } } else break; if (bytes_read != bytes_written) { if (Options & O_LARGEFILES) lseek(src_handle, last_read_pos+bytes_written, SEEK_SET); target_disk_full(dst_file, dst_handle); break; } } } /* * ok_to_retry() * * Input: * none * Comments: * checks if the target directory was used to determine the file * list. If it was, then the copy is aborted when disk is full */ int ok_to_retry() { return ((!(Options & O_BATCH)) && (Options & O_TARGET_REMOVEABLE) && (!(Options & (O_TARGET_DIR | O_COPY_IF_SRC_OLDER | O_CP_IF_SRC_NEWER)))); } void setftime(char *fname, struct stat *statbuf) { struct utimbuf ftime; ftime.actime = statbuf->st_atime; ftime.modtime = statbuf->st_mtime; utime(fname, &ftime); } /* * write_buffer() * * Input: * file_buf - memory buffer containing file contents along * with their headers * File_buf - starting location of memory buffer * Output: * Reading_flag - reset to indicate recent output of message, * 'writing:' * Buf_ptr - location of available memory in memory buffer * Comments: * copies all the files contained in the memory buffer to their * destination files */ void write_buffer(void *file_buf) { ENTRY *ptr; int dst_handle; unsigned retval; char src_file[FILENAME_MAX]; char dst_file[FILENAME_MAX]; _DOSTIME ftime_buf; long fsize; char fattr; if ((Options & O_VERBOSE) && (file_buf < Buf_ptr)) printf("writing:\n"); Reading_flag = 0; while (file_buf < Buf_ptr){ file_buf = get_header_info(file_buf, &ptr, src_file, dst_file, &ftime_buf, &fsize, &fattr); if (!(Options & O_CHECK)) if (!write_dest_file(&dst_handle, dst_file, file_buf, fsize, fattr)) { if (Options & O_MOVE) delete_file(src_file, '\0'); Copied_a_file = 1; Fdatime(&ftime_buf, dst_handle, 1); close(dst_handle); Fattrib(dst_file, 1, fattr); ptr->copied = (fattr) ? fattr : FA_CHANGED; } file_buf = (char *)file_buf + fsize; } Buf_ptr = File_buf; } /* * get_header_info() * * Input: * file_buf - pointer to memory buffer containing file * contents and header * Output: * src_file - source file name (full path) * dst_file - destination file name (full path) * stat_buf - file status buffer * mode - mode settings of source file * returns - location of next file in memory buffer * Comments: * gets a file's header information from the memory buffer */ void *get_header_info(void *file_buf, ENTRY **ptr, char *src_file, char *dst_file, _DOSTIME * ftimebuf, long *fsize, char *fattr) { memcpy(ptr, (char *)file_buf, sizeof(ENTRY *)); file_buf = (char *)file_buf + sizeof(ENTRY *); strcpy(src_file, (char *)file_buf); file_buf = (char *)file_buf + strlen((char *)file_buf) + 1; strcpy(dst_file, (char *)file_buf); file_buf = (char *)file_buf + strlen((char *)file_buf) + 1; memcpy(fsize, file_buf, sizeof(long)); file_buf = (char *) file_buf + sizeof(long); memcpy(ftimebuf, file_buf, sizeof(_DOSTIME)); file_buf = (char *) file_buf + sizeof(_DOSTIME); memcpy(fattr, file_buf, sizeof(char)); file_buf = (char *) file_buf + sizeof(char); return(file_buf); } /* * write_dest_file() * * Input: * dst_handle - handle of file to be written * dst_file - full-path name of file to be written * file_buf - memory buffer containing file contents * file_size - size of file to be written * fattr - file attributes * Comments: * copies a file's contents from a memory buffer to a disk file. * Returns 0 normally, 1 if the destination couldn't be opened for write. */ int write_dest_file(int *dst_handle, char *dst_file, void *file_buf, long file_size, int fattr) { long bytes_written, written_so_far = 0L; do { Retry = 0; if (Options & O_VERBOSE) printf("\t%s\n", dst_file); if (open_dest_file(dst_handle, dst_file, ((fattr & FA_RDONLY) ? S_IREAD : S_IREAD | S_IWRITE))) break; bytes_written = write(*dst_handle, file_buf+written_so_far, (unsigned long) file_size-written_so_far); if (bytes_written == -1L){ fprintf(stderr, "Can't write to file '%s'\n", dst_file); exit(-1); } if (bytes_written != (file_size-written_so_far)) { if (Options & O_LARGEFILES) written_so_far += bytes_written; target_disk_full(dst_file, *dst_handle); } else return 0; } while (Retry); return 1; } /* * open_dest_file() * * Input: * dst_handle - handle of file to be opened * dst_file - full-path name of file to be opened * mode - mode settings of file to be opened * Comments: * opens a file for write. If not successful because file is read-only, * tries to recover. */ int open_dest_file(int *handle, char *name, unsigned modes) { if ((*handle = open((char *)name, (Options & O_JOIN) ? O_WRONLY | O_APPEND | O_CREAT : O_WRONLY | O_TRUNC | O_CREAT, (unsigned)modes)) < 0){ struct stat statbuf; if (stat(name, &statbuf)) { ensure_dest_dir_exist(name); if ((*handle = open((char *)name, (Options & O_JOIN) ? O_WRONLY | O_APPEND | O_CREAT : O_WRONLY | O_TRUNC | O_CREAT, (unsigned)modes)) < 0){ fprintf(stderr, "unable to open file '%s' for write\n", name); return -1; } } else if ((Options & O_BATCH) || (printf("file '%s' is not writeable.\n", name), ask_user("Attempt to delete and overwrite it (Y/N/Q) ? "))) { delete_file(name, '\1'); if ((*handle = open((char *)name, (Options & O_JOIN) ? O_WRONLY | O_APPEND | O_CREAT : O_WRONLY | O_TRUNC | O_CREAT, (unsigned)modes)) < 0) { fprintf(stderr, "unable to open file '%s' for write\n", name); return -1; } } else { printf("Skipped copy to '%s' (open for write failed).\n", name); return -1; } } return 0; } /* * usage() * * Comments: * outputs brief instructions on the proper use of docp */ void usage() { fprintf(stderr, USAGE_MESS); exit(-1); } /* * get_flags() * * Input: * argv - command line arguments * argc - count of the command line arguments * Output: * From_file_ptr - pointer to file containing file list * Comments: * parses the flags specified on the command line, and sets * the appropriate bit fields in a variable called 'options' */ void get_flags(char *argv[], int argc) { int c; char from_file[FILENAME_MAX]; while ((c = getopt(argc, argv, GET_OPT_LIST)) != EOF) { if (isupper(c)) c = tolower(c); switch(c){ case 'a': /* copy using archive bit */ Options |= O_ARCHIVE; break; case 'b': /* batch mode - don't ask questions */ Options |= O_BATCH; break; case 'c': /* check mode, don't copy files */ Options |= O_VERBOSE | O_X_VERBOSE | O_CHECK; break; case 'd': /* date check */ Options |= O_DATE_CHECK; set_Fdate(optarg); break; case 'f': /* file list from file */ strcpy(from_file, optarg); if (!strcmp(from_file, "-")) Options |= O_FROM_STDIN; else { Options |= O_FROM_FILE; if ((From_file_ptr = fopen(from_file, "r")) == NULL){ fprintf(stderr, "unable to open '-f' file '%s'\n", from_file); exit(-1); } } break; case 'g': /* gather files into one directory */ Options |= O_GATHER | O_RECURSIVE; break; case 'h': /* copy hidden files as well */ Options |= O_COPY_HIDDEN; break; case 'i': /* interactive confirm */ Options |= O_INTERACTIVE; break; case 'j': /* join files */ Options |= O_JOIN; break; case 'l': /* large files (split) */ Options |= O_LARGEFILES; break; case 'm': /* move mode */ Options |= O_MOVE; break; case 'n': /* no action */ Options |= O_CP_IF_SRC_NEWER; break; case 'o': /* copy if source is older */ Options |= O_COPY_IF_SRC_OLDER; break; case 'r': /* update subdirectories */ Options |= O_RECURSIVE; break; case 's': /* reference list from source */ Options |= O_SOURCE_DIR; break; case 't': /* get file list from target */ Options |= O_TARGET_DIR; break; case 'v': /* verbose */ if (Options & O_VERBOSE) Options |= O_X_VERBOSE; else Options |= O_VERBOSE; break; case 'w': /* time check */ Options |= O_TIME_CHECK; set_Ftime(optarg); break; case 'z': /* zap the target before copying */ Options |= O_ZAPTARGET; break; case '?': /* documentation */ show_doc(); break; case '\0': usage(); break; default: break; } } set_defaults(); } /* * set_defaults() * * Input: * none * Output: * none * Comments: * sets the default command-line flags */ void set_defaults() { if (!(Options & (O_ARCHIVE | O_COPY_IF_SRC_OLDER | O_CP_IF_SRC_NEWER | O_DATE_CHECK | O_TIME_CHECK))) Options |= O_COPY_ALL; if (!(Options & (O_SOURCE_DIR | O_TARGET_DIR))) Options |= O_SOURCE_DIR; if ((Options & O_TIME_CHECK) && !(Options & O_DATE_CHECK)){ Options |= O_DATE_CHECK; set_todays_date(); } } /* * check_flags() * * Input: * none * Comments: * terminates if the command-line flags are inconsistent */ void check_flags() { if ((Options & O_SOURCE_DIR) && (Options & O_TARGET_DIR)){ fprintf(stderr, "specify only one of '-s' and '-t' options\n"); exit(-1); } if ((Options & O_BATCH) && (Options & O_INTERACTIVE)) { fprintf(stderr, "specify only one of '-b' and '-i' options\n"); exit(-1); } } /* * show_doc() * * Comments: * outputs expanded description of docp */ void show_doc() { fprintf(stdout, FULL_DOC1); fprintf(stdout, FULL_DOC2); fprintf(stdout, FULL_DOC3); fprintf(stdout, FULL_DOC4); exit(0); } /* * set_Ftime() * * Input: * time_str - time string following '-w' option. includes relation * followed by time (e.g. "a3:15pm") * Output: * Ftime - global with reference time * Time_check_mode - global with relationship to reference time * Comments: * takes time string specified in '-w' option and stores the * time in Ftime and the relationship in Time_check_mode. */ void set_Ftime(char *time_str) { TIME time; int i; char *time_arg = time_str; time.mode = 0; while ((*time_str == 'o') || (*time_str == 'b') || (*time_str == 'a')) switch(*time_str){ case 'o': time_str++; time.mode |= D_ON; break; case 'b': time_str++; time.mode |= D_BEFORE; break; case 'a': time_str++; time.mode |= D_AFTER; break; default: break; } time.hour = atoi(time_str); if ((time_str = get_field(time_str, (int *)&(time.min))) == NULL) bad_time(time_arg); while (*time_str != '\0') if (((*time_str == 'p') || (*time_str == 'P')) && (time.hour < 13)){ time.hour += 12; break; } else time_str++; check_time(&time, time_arg); store_time(&time); } /* * store_time() * * Input: * time - structure containing time * Output: * Ftime - time in form returned by findnext() * Comments: * converts time to form returned by findnext() */ void store_time(TIME *time) { TIME_NODE *d; if ((d = (TIME_NODE *)malloc(sizeof(TIME_NODE))) == NULL){ fprintf(stderr, "out of memory"); exit(-1); } d->ftime = (time->min << 5) | (time->hour << 11); d->mode = time->mode; d->next = Ftime; Ftime = d; } /* * check_time() * * Input: * time - structure containing time * Comments: * does a crude check on the time entered */ void check_time(TIME *time, char *time_arg) { if ((time->hour < 1) || (time->hour > 24) || (time->min < 0) || (time->min > 60)) bad_time(time_arg); } /* * bad_time() * * Comments: * termination routine called when a bad time is found */ void bad_time(char *time_arg) { fprintf(stderr, "bad time specified, '%s'\n", time_arg); exit(-1); } /* * set_Fdate() * * Input: * date_str - date string following '-d' option. includes relation * followed by date (e.g. ">=3/23/91") * Output: * Fdate - global with reference date * Date_check_mode - global with relationship to reference date * Comments: * takes date string specified in '-d' option and stores the * date in Fdate and the relationship in Date_check_mode. */ void set_Fdate(char *date_str) { DATE date; int *p; int i; date.mode = 0; while ((*date_str == 'o') || (*date_str == 'b') || (*date_str == 'a')){ switch(*date_str){ case 'o': date_str++; date.mode |= D_ON; break; case 'b': date_str++; date.mode |= D_BEFORE; break; case 'a': date_str++; date.mode |= D_AFTER; break; default: break; } } convert_date_str(&date, date_str); check_date(&date, date_str); store_date(&date); } /* * convert_date_str() * * Input: * date_str - date string from command line * Output: * date - numeric date structure * Comments: * converts command-line date string into numeric structure */ void convert_date_str(DATE *date, char *date_str) { char *new_date_str; date->mo = atoi(date_str); if ((date_str = get_field(date_str, (int *)&(date->day))) == NULL) return; if ((date_str = get_field(date_str, (int *)&(date->year))) == NULL) return; } /* * get_field() * * Input: * date_str - current position in date string from command line * Output: * date_str - new position in date string from command line * date_field - numeric value of date field (day or year) * Comments: * gets numeric date from field of command-line date string */ char *get_field(char *string, int *field) { char *new_string; new_string = strchr(string, '/'); if (new_string == NULL) new_string = strchr(string, '-'); if (new_string == NULL) new_string = strchr(string, ':'); if (new_string == NULL){ *field = 0; } else { new_string++; *field = atoi(new_string); } return(new_string); } /* * set_todays_date() * * Output: * Fdate - adds today's date in date table with ON option set. * Comments: * stores today's date in date table. Done when user only * specifies time option without date option. */ void set_todays_date() { DATE date; get_todays_date(&date); date.mode = D_ON; store_date(&date); } /* * get_todays_date() * * Output: * date - today's date from the operating system * Comments: * gets the current date from the operating system */ void get_todays_date(DATE *date) { short cur_date = Tgetdate(); date->year = (cur_date >> 9) + 80; date->mo = (cur_date >> 5) & 0xf; date->day = (cur_date) & 0x1f; } /* * store_date() * * Input: * date - structure containing date * Output: * Fdate - date in form returned by findnext() * Comments: * converts date to form returned by findnext() */ void store_date(DATE *date) { DATE_NODE *d; if ((d = (DATE_NODE *)malloc(sizeof(DATE_NODE))) == NULL){ fprintf(stderr, "out of memory"); exit(-1); } d->fdate = date->day | (date->mo << 5) | ((date->year - 80) << 9); d->mode = date->mode; d->next = Fdate; Fdate = d; } /* * check_date() * * Input: * date - structure containing date * date_str - date string from command line * Comments: * does a crude check on the date entered */ void check_date(DATE *date, char *date_str) { if ((date->mo < 1) || (date->mo > 12) || (date->day < 1) || (date->day > 31)) bad_date(date_str); } /* * bad_date() * * Input: * date_str - date string from command line * Comments: * termination routine called when a bad date is found */ void bad_date(char *date_str) { fprintf(stderr, "bad date specified '%s'\n", date_str); exit(-1); } /* * build_stdin_file_list() * * Output: * num_args - the number of args in the stdin file list * Comments: * builds linked list containing file list tokens from stdin. */ void build_stdin_file_list(int *num_args) { char s[80]; while (fgets(s, 79, stdin) != NULL){ zap_trailing_nl(s, 79, stdin); /* clobber newline */ add_to_stdin_list(s); (*num_args)++; } if (*num_args == 0){ add_to_stdin_list("*.*"); *num_args = 1; } } /* * add_to_stdin_list() * * input: * s - the file list token * Output: * Stdin_list_head - first element of file list * Stdin_list_tail - last element of file list * Comments: * adds a file list token to a linked list */ void add_to_stdin_list(char *s) { STDIN_TOKEN *new_token; if ((new_token = (STDIN_TOKEN *)malloc(sizeof(STDIN_TOKEN))) == NULL){ fprintf(stderr, "out of memory"); exit(-1); } if ((new_token->string = (char *)malloc(strlen(s) + 1)) == NULL){ fprintf(stderr, "out of memory"); exit(-1); } strcpy(new_token->string, s); new_token->next = NULL; if (Stdin_list_head == NULL){ Stdin_list_head = Stdin_list_tail = new_token; } else { Stdin_list_tail->next = new_token; Stdin_list_tail = new_token; } }