/* *---------------------------------------------------------------------------- * * RESTORE.C : a backup restorer for the Atari ST series * * This code was written to complement the very useful `Turtle' * hard disk backup utility. It essentially does a `cp -r', maintaining * the time stamps on files. It will create directories as needed * on the receiving filesystem. * * This code is copyright (C) by Ross Alexander, Athabasca University, * 1988. It may be freely redistributed and/or modified as long as: * 1) This notice appears, & * 2) modifications are noted as NOT my work. * * I think this programme is OK, but I take no responsibility for * any result (or lack thereof...) of using it. Caveat Emptor. * * Basic Algorithm: * * open source directory * count entries, reserve that much memory * reopen source directory, read into memory * sort alphabetically, moving directories to head * of the list. * conditionally create target directory * call self for all directories in list * copy all files in list to target, correcting time stamps * release memory for list * *---------------------------------------------------------------------------- */ #include #include #include #include #define CANDIDATES ( S_IJRON | S_IJHID | S_IJSYS | S_IJDIR | S_IJWAC ) #define NULLPTR ( (char *) 0 ) extern char * malloc(); extern char * strcpy(); /* this is the stuff at the end of a DMABUFFER */ typedef struct { char f_fattr; /* File attributes */ long f_tandd; /* Time and date words */ long f_fsize; /* File size */ char f_fname[14]; /* File name */ } FILEINFO; /* this is a big enough buffer to do most files in a gulp */ #define BUFFER_SIZE 16384 /* utterly arbitrary */ char track_buffer[ BUFFER_SIZE ]; int v_flag = 0; /* verbosity flag */ /* *---------------------------------------------------------------------------- * * make a (longer) path name from a path-name and {dir,file}-name components * *---------------------------------------------------------------------------- */ char * dircat( path, name ) char * path; char * name; { int path_len = strlen( path ); char path_buf[ 128 ]; /* _should_ be lots... */ char * scan = path_buf; strcpy( path_buf, path ); if ( path_len != 0 && path[ path_len - 1 ] != '\\' ) strcat( path_buf, "\\" ); strcat( path_buf, name ); while ( * scan ) { if ( isupper( * scan ) ) * scan = tolower( * scan ); ++ scan; } return strcpy( malloc( strlen( path_buf ) + 1 ), path_buf ); } /* *---------------------------------------------------------------------------- * * count how many entries exist in a directory. ignores volume labels, ., .. * *---------------------------------------------------------------------------- */ int get_dir_size( from ) char * from; /* source pathname */ { DMABUFFER examine; /* place to hold these for counting */ int status; /* temp to hold Fs{first|next} value*/ DMABUFFER * old_dta = (DMABUFFER *) Fgetdta(); char * globname = dircat( from, "*.*" ); int list_length = 0; Fsetdta( & examine ); /* read'em here, avoid buss faults! */ for ( status = Fsfirst( globname, CANDIDATES ); status == 0; status = Fsnext() ) if ( examine.d_fname[ 0 ] != '.' ) ++ list_length; /* ignore ., .. */ Fsetdta( old_dta ); free( globname ); /* don't need this any more */ return list_length; } /* *---------------------------------------------------------------------------- * * compare two directory entries; subdirectories sort ahead of files * *---------------------------------------------------------------------------- */ static int dir_comp( a, b ) FILEINFO * a; FILEINFO * b; { int attr_a = a->f_fattr & S_IJDIR; int attr_b = b->f_fattr & S_IJDIR; if ( attr_a != attr_b ) return attr_a ? -1 : 1; /* directories-ahead-of-files */ else return strcmp( a->f_fname, b->f_fname ); } /* *---------------------------------------------------------------------------- * * suck in a directory, sort it, return -> entries * *---------------------------------------------------------------------------- */ FILEINFO * read_sorted_dir( path, length ) char * path; int length; { FILEINFO * list = (FILEINFO *) malloc( length * sizeof (FILEINFO) ); FILEINFO * scan = list; char * globname = dircat( path, "*.*" ); DMABUFFER * old_dta = (DMABUFFER *) Fgetdta(); DMABUFFER examine; int status; Fsetdta( & examine ); /* read'em here, avoid buss faults! */ for ( status = Fsfirst( globname, CANDIDATES ); status == 0; status = Fsnext() ) if ( examine.d_fname[ 0 ] != '.' ) { scan->f_fattr = examine.d_fattr; scan->f_tandd = examine.d_tandd; scan->f_fsize = examine.d_fsize; strcpy( scan->f_fname, examine.d_fname ); ++ scan; } qsort( list, length, sizeof (FILEINFO), dir_comp ); free( globname ); Fsetdta( old_dta ); return list; } /* *---------------------------------------------------------------------------- * * copy a file from `from' to `to_path\to_name', in a careful way. * *---------------------------------------------------------------------------- */ char * copy_file( from, to_path, to_name ) char * from; char * to_path; char * to_name; { DMABUFFER * old_dta = (DMABUFFER *) Fgetdta(); char * to = dircat( to_path, to_name ); char * tmp = dircat( to_path, "$$$$$$$$.$$$" ); char * ret_msg = NULLPTR; struct stat stat_from; struct stat stat_to; int from_h = -1; int to_h = -1; int dest_exists; long chunk; int timestamp[ 2 ]; if ( v_flag ) fprintf( stderr, "\t%s ==> %s\n", from, to ); /* check existence & modes of source */ if ( stat( from, & stat_from ) == NODEV ) { ret_msg = "can't stat source"; goto exit; } /* check existence & type of destination */ if ( dest_exists = ( stat( to, & stat_to ) != NODEV ) ) { if ( stat_to.st_mtime > stat_from.st_mtime ) { ret_msg = "source older than destination"; goto exit; } else if ( ( stat_to.st_mode & S_IJDIR ) != 0 ) { ret_msg = "destination is directory"; goto exit; } } /* open source file for reading */ from_h = Fopen( from, 0 ); if ( from_h < 0 ) { ret_msg = "can't open input file"; goto exit; } /* create the temporary output file */ to_h = Fcreate( tmp, stat_from.st_mode ); if ( to_h < 0 ) { ret_msg = "can't create temp file"; goto exit; } /* copy file contents across */ Fsetdta( track_buffer ); while ( stat_from.st_size > 0 ) { chunk = stat_from.st_size > BUFFER_SIZE ? BUFFER_SIZE : stat_from.st_size; if ( chunk != Fread( from_h, chunk, track_buffer ) ) { ret_msg = "file read error"; goto exit; } if ( chunk != Fwrite( to_h, chunk, track_buffer ) ) { ret_msg = "temp file write error"; goto exit; } stat_from.st_size -= chunk; } /* close output, delete destination, rename temp to destination */ Fclose( to_h ); to_h = -1; if ( dest_exists && Fdelete( to ) != 0 ) { ret_msg = "can't delete destination"; goto exit; } if ( Frename( 0, tmp, to ) ) { ret_msg = "rename failed"; goto exit; } /* copy old timestamp to new file */ to_h = Fopen( to, 1 ); Fdatime( timestamp, from_h, 0 ); Fdatime( timestamp, to_h, 1 ); /* sweep up */ exit: if ( from_h >= 0 ) Fclose( from_h ); if ( to_h >= 0 ) Fclose( to_h ); Fsetdta( old_dta ); free( to ); free( tmp ); return ret_msg; } /* *---------------------------------------------------------------------------- * * drive all the other routines ;-) * *---------------------------------------------------------------------------- */ char * cp_hyphen_r( from, to ) char * from; char * to; { struct stat stat_from; struct stat stat_to; int how_many; FILEINFO * list; FILEINFO * scan; char * from_next; char * to_next; char * t; if ( v_flag ) fprintf( stderr, "%s --> %s\n", from, to ); /* guarantee that source exists and is a directory */ if ( stat( from, & stat_from ) == NODEV || ( stat_from.st_mode & S_IJDIR ) == 0 ) return "source not found or not directory"; /* see if destination exists; if not, try to create it */ if ( stat( to, & stat_to ) != NODEV ) { if ( ( stat_to.st_mode & S_IJDIR ) == 0 ) return "destination exists & not directory"; } else if ( Dcreate( to ) ) return "can't create destination directory"; /* now either call self for dir's, or copy files */ scan = list = read_sorted_dir( from, how_many = get_dir_size( from ) ); while ( scan < list + how_many ) { from_next = dircat( from, scan->f_fname ); to_next = dircat( to, scan->f_fname ); if ( ( scan->f_fattr & S_IJDIR ) != 0 ) { if ( ( t = cp_hyphen_r( from_next, to_next ) ) != NULLPTR ) fprintf( stderr, "%s (%s -> %s)\n", t, from_next, to_next ); } else if ( ( t = copy_file( from_next, to, scan->f_fname ) ) != NULLPTR ) fprintf( stderr, "%s (%s -> %s)\n", t, from_next, to_next ); free( from_next ); free( to_next ); ++ scan; } free( list ); return NULLPTR; } /* user interface */ main( argc, argv ) int argc; char * * argv; { char * errmsg; int c; extern int optind; extern char * optarg; while ( ( c = getopt( argc, argv, "v" ) ) != EOF ) if ( c == 'v' ) ++ v_flag; else break; if ( c != EOF || ( argc - optind ) != 2 ) { fprintf( stderr, "%s: usage %s [-v] \n", argv[ 0 ], argv[ 0 ] ); exit( 1 ); } if ( ( errmsg = cp_hyphen_r( argv[ optind ], argv[ optind + 1 ] ) ) != NULLPTR ) { fprintf( stderr, "%s: %s (%s --> %s)\n", argv[ 0 ], errmsg, argv[ optind ], argv[ optind + 1 ] ); exit( 2 ); } exit( 0 ); }