/* Unix(TM) CD-ROM file copy utility by Matthew B. Hornbeck, Director of Technical Services Copyright 1989, 1990, 1991, 1992 by Young Minds, Incorporated June 30, 1992. File : CD_COPY.C Note : On HP-UX machines, you must link this program using the BSD compatible library (i.e. "cc -o cd_link cd_link.c /usr/lib/libBSD.a") */ #define TRANSLATION_FILE "YMTRANS.TBL;1" #define RRIP_TRANSLATION_FILE "YMTRANS.TBL" #ifdef sun #define REALPATH #endif #include #include #include #include #include #include #include #ifdef REALPATH #include #else #define MAXPATHLEN 4096 #endif #ifdef M_XENIX #define getwd(x) getcwd(x,2047) #endif #define MAX_TRANS_TYPES (4) #ifndef FALSE #define FALSE (0) #define TRUE (!FALSE) #endif /* ** Bit masks for setting flags bit-vector from ** command line args. */ #define RECURSE (1) #define ROCK_RIDGE (2) char* get_program_name( argv0 ) char *argv0; { char *program_name; /* ** Gets the component of the command that ** occurs after the last '/'. */ program_name = strrchr( argv0, '/' ); if (program_name == NULL) program_name = argv0; else program_name++; return( program_name ); } void usage( program_name, arg_list, error_message) char *program_name; char *arg_list; char *error_message; { fprintf( stderr, "Usage: %s %s\n", program_name, arg_list ); fprintf( stderr, "\t%s\n", error_message ); } typedef struct dir_element { struct dir_element *next; char *cd_path; char *new_path; } dir_elem; static dir_elem *head = NULL, *tail = NULL; int push_dir (cd_path, new_path) char *cd_path, *new_path; { dir_elem *tmp; if (head == NULL) { if ((head = tmp = (dir_elem *) malloc (sizeof (dir_elem))) == NULL) { fprintf (stderr, "Unable to allocate dir_element buffer!\n"); return FALSE; } } else { if ((tail->next = tmp = (dir_elem *) malloc (sizeof (dir_elem))) == NULL) { fprintf (stderr, "Unable to allocate dir_element buffer!\n"); return FALSE; } } tmp->next = NULL; if ((tmp->cd_path = (char *) malloc (strlen (cd_path) + 1)) == NULL) { fprintf (stderr, "Unable to allocate cd_path of dir_element!\n"); return FALSE; } strcpy (tmp->cd_path, cd_path); if ((tmp->new_path = (char *) malloc (strlen (new_path) + 1)) == NULL) { fprintf (stderr, "Unable to allocate new_path of dir_element!\n"); return FALSE; } strcpy (tmp->new_path, new_path); tail = tmp; return TRUE; } dir_elem * dequeue_dir() { dir_elem *tmp; if (head == NULL) { tail = NULL; return NULL; } tmp = head; head = tmp->next; tmp->next = NULL; return tmp; } void free_dir (dir) dir_elem *dir; { free (dir->cd_path); free (dir->new_path); free (dir); } void translate_name( name, trans_type) char *name; int trans_type; { int i; /* ** Changes the name given according to one of the algorithms ** below. The algorithm used is selected via the trans_type ** arguement to this function. */ switch( trans_type ) { case 0: /* ** No translation. Use original name. */ break; case 1: /* ** All lower case. */ for (i = 0; i < strlen(name) ; i ++) { if (isupper (name [i])) name [i] = tolower (name [i]); else name [i] = name [i]; } break; case 2: /* ** All lower case. Strip ";version_no". */ for (i = 0; (name[i] != ';') && (i < strlen( name )); i ++) { if (isupper (name [i])) name [i] = tolower (name [i]); else name [i] = name [i]; } name[i] = '\0'; break; case 3: /* ** All lower case. Replace ";version_no" with "-version_no". */ for (i = 0; i < strlen( name ); i ++) { if ( name[i] == ';' ) name[i] = '-'; else if (isupper (name [i])) name [i] = tolower (name [i]); else name [i] = name [i]; } name[i] = '\0'; break; default: fprintf(stderr, "translate_name: Unknown translation type.\n"); exit( 1 ); } } FILE* open_trans( cd_path, trans_name, trans_type ) char *cd_path; char *trans_name; int *trans_type; { FILE *fp; char *new_name; char *name; int i; /* ** Get space for resolved file name which consistes of the cd_path ** and the translated trans_name (new_name) concatinated together. */ if ((name = malloc( strlen( trans_name ) + strlen(cd_path) + 2 )) == NULL) { fprintf(stderr, "Error: Malloc failed.\n"); exit( 1 ); } /* ** Get space to put the translated trans_name. */ if ((new_name = malloc( strlen( trans_name ) + 1 )) == NULL) { fprintf(stderr, "Error: Malloc failed.\n"); exit( 1 ); } /* ** translate the trans_name using the translation type ** that was previously found, or if first time translation ** type defaults to 0. */ strcpy( new_name, trans_name ); translate_name( new_name, *trans_type ); /* ** Concatinate translated name and cd_path to get resolved name. */ sprintf( name, "%s/%s", cd_path, new_name ); /* ** Attempt to open the file. ** If fopen fails then I will try some other translation types on ** on the trans_name. */ if ((fp = fopen (name, "rt")) != NULL) { free( new_name ); free( name ); return( fp ); } /* ** Try translation types on trans_name until I can either ** open the file successfully or I run out of translation ** types. */ for (i = 0; i < MAX_TRANS_TYPES; i++) { strcpy( new_name, trans_name ); translate_name( new_name, i ); sprintf( name, "%s/%s", cd_path, new_name ); if ((fp = fopen (name, "rt")) != NULL) { *trans_type = i; free( new_name ); free( name ); return( fp ); } } /* ** Failed to open the file. ** Return NULL file descriptor to signal error. */ free( new_name ); free( name ); return( NULL ); } int rrip_proc_dir( cd_path, new_path, recurse ) char *cd_path; char *new_path; int recurse; { FILE *fp; char line[MAXPATHLEN]; char file_name[MAXPATHLEN]; char link_buf[MAXPATHLEN]; char trans_name[MAXPATHLEN]; char link_name [MAXPATHLEN]; char new_name [MAXPATHLEN]; char resolved_name [MAXPATHLEN]; char type; int num_fields; char command[MAXPATHLEN]; /* ** For each directory entry. Get its type. ** Depending on its type make a directory or symbolic link. ** If the type is a directory and directory recursion was ** asked for on the command line, then push it onto the ** stack to be proccessed later. */ sprintf( trans_name, "%s/%s", cd_path, RRIP_TRANSLATION_FILE ); if ((fp = fopen( trans_name, "rt" )) == NULL ) { fprintf (stderr, "Unable to open translation file %s!\n", trans_name); return FALSE; } while (fgets( line, sizeof( line ), fp) != NULL) { /* ** Get the type of the file, ** the file name and the link name if this entry is a link. */ strcpy( link_name, "" ); num_fields = sscanf( line, "%c %*s %s %s", &type, file_name, link_name ); if (strcmp( file_name, ".") == 0) continue; if (strcmp( file_name, "..") == 0) continue; sprintf (new_name, "%s/%s", new_path, file_name); switch (type) { case 'F' : sprintf( command, "cp \"%s/%s\" %s", cd_path, file_name, new_name); if (system ( command ) < 0) fprintf (stderr, "Unable to copy %s to %s!\n", link_name, new_name); break; case 'L' : #ifdef M_XENIX fprintf (stderr, "Unable to make link %s to %s!\n", link_name, new_name); #else if (symlink (link_name, new_name) != 0) fprintf (stderr, "Unable to make link %s to %s!\n", link_name, new_name); #endif break; case 'D' : mkdir (new_name, 0777); if (recurse) { sprintf (link_name, "%s/%s", cd_path, file_name); push_dir (link_name, new_name); } break; case 'M' : mkdir (new_name, 0777); if (recurse) { sprintf (link_buf, "%s/%s", cd_path, link_name); #ifdef REALPATH realpath (link_buf, resolved_name); #else strcpy (resolved_name, link_buf); #endif push_dir (resolved_name, new_name); } break; default: fprintf(stderr, "proc_dir: Unknown file type.\n"); exit( 1 ); } } fclose (fp); return TRUE; } int iso9660_proc_dir( cd_path, new_path, recurse ) char *cd_path; char *new_path; int recurse; { FILE *fp; char line [4096], link_name [4096], new_name [4096]; char trans_name [4096], resolved_name [MAXPATHLEN]; char type, elem1 [65], elem2 [2048], elem3 [2048]; int line_cnt, elem_cnt, i, j; static int trans_type = 0; char command[MAXPATHLEN]; sprintf (trans_name, "%s/%s", cd_path, TRANSLATION_FILE); if ((fp = open_trans( cd_path, TRANSLATION_FILE, &trans_type )) == NULL) { fprintf (stderr, "Unable to open file %s!\n", trans_name); return FALSE; } line_cnt = 0; while (fgets (line, sizeof (line), fp) != NULL) { line_cnt ++; if ((strlen (line) < 19) || (line [1] != ' ') || (line [strlen (line) - 1] != '\n')) { fprintf (stderr, "Invalid %s file!?!\n", trans_name); exit (1); } type = line [0]; /* ** Get the ISO name. */ for (i = 2, j = 0; (line [i] != ' ') && (line [i] != '\t'); i ++, j ++) elem1 [j] = line [i]; elem1 [j] = '\0'; /* ** translate name to the same format that was required ** in order to open the "YMTRANS.TBL;1". */ translate_name( elem1, trans_type ); /* ** Skip past white space. */ while ((line [i] == ' ') || (line [i] == '\t')) i ++; /* ** Get the unix name. */ for (j = 0; (line [i] != '\t') && (line [i] != '\n'); i ++, j ++) elem2 [j] = line [i]; elem2 [j] = '\0'; elem_cnt = 2; j = 0; if (line [i] == '\t') { /* ** Get name of file that this name is a link to ** if this is a link. */ for (i ++; line [i] != '\n'; i ++, j ++) elem3 [j] = line [i]; elem_cnt ++; } elem3 [j] = '\0'; if ((line_cnt == 1) && (strcmp (elem1, ".") == 0)) continue; if ((line_cnt == 2) && (strcmp (elem1, "..") == 0)) continue; sprintf (new_name, "%s/%s", new_path, elem2); switch (type) { case 'F' : sprintf( command, "cp \"%s/%s\" %s", cd_path, elem1, new_name); if (system ( command ) < 0) fprintf (stderr, "Unable to copy %s to %s!\n", elem1, elem2); break; case 'L' : #ifdef M_XENIX fprintf (stderr, "Unable to make link %s to %s!\n", elem1, elem3); #else if (symlink (elem3, new_name) != 0) fprintf (stderr, "Unable to make link %s to %s!\n", elem1, elem3); #endif break; case 'D' : mkdir (new_name, 0777); if (recurse) { sprintf (link_name, "%s/%s", cd_path, elem1); push_dir (link_name, new_name); } break; case 'M' : mkdir (new_name, 0777); if (recurse) { sprintf (link_name, "%s/%s", cd_path, elem3); #ifdef REALPATH realpath (link_name, resolved_name); #else strcpy (resolved_name, link_name); #endif push_dir (resolved_name, new_name); } break; default: fprintf(stderr, "proc_dir: Unknown file type.\n"); exit( 1 ); } } fclose (fp); return TRUE; } int proc_dir (cd_path, new_path, flags) char *cd_path, *new_path; int flags; { int recurse; /* ** If command line arguement "-r" was specified then ** recurse down subdirectories. */ if ((flags & RECURSE) != 0) recurse = TRUE; else recurse = FALSE; /* ** If command line arguement "-R" was specified then ** ignore the YMTRANS.TBL and create links with the ** same names as the names that exist in the directory ** entries on the disk. ** ** This is most useful on Rock Ridge disks where the ** name in the directory entry is the name that should ** be used. */ if ((flags & ROCK_RIDGE) != 0 ) rrip_proc_dir( cd_path, new_path, recurse ); else iso9660_proc_dir( cd_path, new_path, recurse ); } int main (argc, argv) int argc; char *argv []; { dir_elem *cur_dir; char cd_pathname [2048]; char target_dirname[2048]; char resolved_cd_name [MAXPATHLEN]; char resolved_dir_name [MAXPATHLEN]; int flags; int this_arg = 1; int switch_count; char *program_name; char error_message[80]; char *arg_list = "[-rR] cd_pathname [target_dir]"; fprintf (stderr, "cd_link : Copyright 1989, 1990, 1991, 1992 By Young Minds, Incoporated\n"); /* Extract program name from first arguement. */ program_name = get_program_name(argv[0]); /* Process arguements */ flags = 0; cd_pathname[0] = '\0'; target_dirname[0] = '\0'; while (this_arg < argc) { /* Process switches */ if (argv[this_arg][0] == '-') { switch_count = 1; while (argv[this_arg][switch_count] != '\0') { switch (argv[this_arg][switch_count]) { case 'r' : /* ** If command line arguement "-r" was specified then ** recurse down subdirectories. */ flags |= RECURSE; break; case 'R' : /* ** If command line arguement "-R" was specified then ** ignore the YMTRANS.TBL and create links with the ** same names as the names that exist in the directory ** entries on the disk. ** ** This is most useful on Rock Ridge disks where the ** name in the directory entry is the name that should ** be used. */ flags |= ROCK_RIDGE; break; default : sprintf(error_message, "Unknown switch: -%c", argv[this_arg][switch_count]); usage( program_name, arg_list, error_message ); exit(1); break; } switch_count++; } } /* Process everything else. */ else { /* Get input file name. */ if (cd_pathname[0] != '\0') { if (target_dirname[0] != '\0') { /* ** If already gotten then an error exists ** in the command line. */ sprintf( error_message, "Invalid arguement: %s", argv[this_arg] ); usage( program_name, arg_list, error_message ); exit(1); } else if (argv [this_arg] [0] == '/') strcpy (target_dirname, argv [this_arg]); else { getwd (target_dirname); strcat (target_dirname, "/"); strcat (target_dirname, argv [this_arg]); } } else { if (argv [this_arg] [0] == '/') strcpy (cd_pathname, argv [this_arg]); else { getwd (cd_pathname); strcat (cd_pathname, "/"); strcat (cd_pathname, argv [this_arg]); } } } this_arg++; } /* ** If there was an input file specified then use that file for ** input. Otherwise, get input from stdin. */ if (cd_pathname[0] == '\0') { sprintf( error_message, "Missing cd_pathname."); usage( program_name, arg_list, error_message ); exit(1); } /* ** If there was an output file specified then use that file for ** output. Otherwise, put output to stdout. */ if (target_dirname[0] == '\0') { getwd (target_dirname); } #ifdef REALPATH realpath (cd_pathname, resolved_cd_name); realpath (target_dirname, resolved_dir_name); #else strcpy (resolved_cd_name, cd_pathname); strcpy (resolved_dir_name, target_dirname); #endif push_dir ( resolved_cd_name, resolved_dir_name ); while ((cur_dir = dequeue_dir ()) != NULL) { proc_dir (cur_dir->cd_path, cur_dir->new_path, flags); free_dir (cur_dir); } return 0; }