home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk1.iso / ym_utils / cd_copy.c < prev    next >
C/C++ Source or Header  |  1994-11-08  |  15KB  |  659 lines

  1. /* Unix(TM) CD-ROM file copy utility  by
  2.         Matthew B. Hornbeck, Director of Technical Services
  3.         Copyright 1989, 1990, 1991, 1992 by Young Minds, Incorporated
  4.         June 30, 1992.
  5. File : CD_COPY.C
  6.  
  7. Note : On HP-UX machines, you must link this program using the BSD
  8.     compatible library (i.e. "cc -o cd_link cd_link.c /usr/lib/libBSD.a")
  9. */
  10.  
  11. #define TRANSLATION_FILE    "YMTRANS.TBL;1"
  12. #define RRIP_TRANSLATION_FILE    "YMTRANS.TBL"
  13.  
  14. #ifdef sun
  15. #define REALPATH
  16. #endif
  17.  
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <ctype.h>
  21. #include <malloc.h>
  22. #include <sys/types.h>
  23. #include <sys/dir.h>
  24. #include <sys/stat.h>
  25.  
  26. #ifdef REALPATH
  27. #include <sys/param.h>
  28. #else
  29. #define MAXPATHLEN    4096
  30. #endif
  31.  
  32. #ifdef M_XENIX
  33. #define getwd(x)    getcwd(x,2047)
  34. #endif
  35.  
  36. #define MAX_TRANS_TYPES    (4)
  37. #ifndef FALSE
  38. #define FALSE    (0)
  39. #define TRUE    (!FALSE)
  40. #endif
  41.  
  42. /*
  43. **    Bit masks for setting flags bit-vector from
  44. **    command line args.
  45. */
  46. #define RECURSE        (1)
  47. #define ROCK_RIDGE    (2)
  48.  
  49. char*
  50. get_program_name( argv0 )
  51.     char    *argv0;
  52. {
  53.     char    *program_name;
  54.  
  55.     /*
  56.     **    Gets the component of the command that
  57.     **    occurs after the last '/'.
  58.     */
  59.     program_name = strrchr( argv0, '/' );
  60.     if (program_name == NULL)
  61.         program_name = argv0;
  62.     else
  63.         program_name++;
  64.     return( program_name );
  65. }
  66.  
  67. void
  68. usage( program_name, arg_list, error_message)
  69.     char    *program_name;
  70.     char    *arg_list;
  71.     char    *error_message;
  72. {
  73.     fprintf( stderr, "Usage: %s %s\n", program_name, arg_list );
  74.     fprintf( stderr, "\t%s\n", error_message );
  75. }
  76.  
  77. typedef struct dir_element {
  78.     struct dir_element *next;
  79.     char *cd_path;
  80.     char *new_path;
  81. } dir_elem;
  82.  
  83. static dir_elem *head = NULL, *tail = NULL;
  84.  
  85. int 
  86. push_dir (cd_path, new_path)
  87.     char *cd_path, *new_path;
  88.  
  89. {
  90.     dir_elem *tmp;
  91.  
  92.     if (head == NULL)
  93.       {    if ((head = tmp = (dir_elem *) malloc (sizeof (dir_elem))) == NULL)
  94.           {    fprintf (stderr, "Unable to allocate dir_element buffer!\n");
  95.             return FALSE;
  96.           }
  97.       }
  98.     else
  99.       {    if ((tail->next = tmp = (dir_elem *) malloc (sizeof (dir_elem))) == NULL)
  100.           {    fprintf (stderr, "Unable to allocate dir_element buffer!\n");
  101.             return FALSE;
  102.           }
  103.       }
  104.     tmp->next = NULL;
  105.     if ((tmp->cd_path = (char *) malloc (strlen (cd_path) + 1)) == NULL)
  106.       {    fprintf (stderr, "Unable to allocate cd_path of dir_element!\n");
  107.         return FALSE;
  108.       }
  109.     strcpy (tmp->cd_path, cd_path);
  110.     if ((tmp->new_path = (char *) malloc (strlen (new_path) + 1)) == NULL)
  111.       {    fprintf (stderr, "Unable to allocate new_path of dir_element!\n");
  112.         return FALSE;
  113.       }
  114.     strcpy (tmp->new_path, new_path);
  115.     tail = tmp;
  116.     return TRUE;
  117. }
  118.  
  119. dir_elem *
  120. dequeue_dir()
  121.  
  122. {    
  123.     dir_elem *tmp;
  124.  
  125.     if (head == NULL)
  126.       {    tail = NULL;
  127.         return NULL;
  128.       }
  129.     tmp = head;
  130.     head = tmp->next;
  131.     tmp->next = NULL;
  132.     return tmp;
  133. }
  134.  
  135. void
  136. free_dir (dir)
  137.     dir_elem *dir;
  138.  
  139. {
  140.     free (dir->cd_path);
  141.     free (dir->new_path);
  142.     free (dir);
  143. }
  144.  
  145. void
  146. translate_name( name, trans_type)
  147.     char    *name;
  148.     int        trans_type;
  149. {
  150.     int    i;
  151.  
  152.     /*
  153.     **    Changes the name given according to one of the algorithms
  154.     **    below.  The algorithm used is selected via the trans_type
  155.     **    arguement to this function.
  156.     */
  157.     switch( trans_type ) {
  158.         case 0:
  159.             /*
  160.             **    No translation.  Use original name.
  161.             */
  162.             break;
  163.  
  164.         case 1:
  165.             /*
  166.             **    All lower case.
  167.             */
  168.             for (i = 0; i < strlen(name) ; i ++) {
  169.                 if (isupper (name [i]))
  170.                     name [i] = tolower (name [i]);
  171.                 else
  172.                     name [i] = name [i];
  173.             }
  174.             break;
  175.         case 2:
  176.             /*
  177.             **    All lower case.  Strip ";version_no".
  178.             */
  179.             for (i = 0; (name[i] != ';') && (i < strlen( name )); i ++) {
  180.                 if (isupper (name [i]))
  181.                     name [i] = tolower (name [i]);
  182.                 else 
  183.                     name [i] = name [i];
  184.             }
  185.             name[i] = '\0';
  186.             break;
  187.         case 3:
  188.             /*
  189.             **    All lower case.  Replace ";version_no" with "-version_no".
  190.             */
  191.             for (i = 0; i < strlen( name ); i ++) {
  192.                 if ( name[i] == ';' )
  193.                     name[i] = '-';
  194.                 else if (isupper (name [i]))
  195.                     name [i] = tolower (name [i]);
  196.                 else 
  197.                     name [i] = name [i];
  198.             }
  199.             name[i] = '\0';
  200.             break;
  201.         default:
  202.             fprintf(stderr, "translate_name: Unknown translation type.\n");
  203.             exit( 1 );
  204.     }
  205. }
  206.  
  207. FILE*
  208. open_trans( cd_path, trans_name, trans_type )
  209.     char    *cd_path;
  210.     char    *trans_name;
  211.     int        *trans_type;
  212. {
  213.     FILE    *fp;
  214.     char    *new_name;
  215.     char    *name;
  216.     int        i;
  217.  
  218.     /*
  219.     **    Get space for resolved file name which consistes of the cd_path
  220.     **    and the translated trans_name (new_name) concatinated together.
  221.     */
  222.     if ((name = malloc( strlen( trans_name ) + strlen(cd_path) + 2 )) == NULL) {
  223.         fprintf(stderr, "Error: Malloc failed.\n");
  224.         exit( 1 );
  225.     }
  226.     /*
  227.     **    Get space to put the translated trans_name.
  228.     */
  229.     if ((new_name = malloc( strlen( trans_name ) + 1 )) == NULL) {
  230.         fprintf(stderr, "Error: Malloc failed.\n");
  231.         exit( 1 );
  232.     }
  233.     /*
  234.     **    translate the trans_name using the translation type
  235.     **    that was previously found, or if first time translation
  236.     **    type defaults to 0.
  237.     */
  238.     strcpy( new_name, trans_name );
  239.     translate_name( new_name, *trans_type );
  240.     /*
  241.     **    Concatinate translated name and cd_path to get resolved name.
  242.     */
  243.     sprintf( name, "%s/%s", cd_path, new_name );
  244.     /*
  245.     **    Attempt to open the file.
  246.     **    If fopen fails then I will try some other translation types on
  247.     **    on the trans_name.
  248.     */
  249.     if ((fp = fopen (name, "rt")) != NULL) {
  250.         free( new_name );
  251.         free( name );
  252.         return( fp );
  253.     }
  254.     /*
  255.     **    Try translation types on trans_name until I can either
  256.     **    open the file successfully or I run out of translation
  257.     **    types.
  258.     */
  259.     for (i = 0; i < MAX_TRANS_TYPES; i++) {
  260.         strcpy( new_name, trans_name );
  261.         translate_name( new_name, i );
  262.         sprintf( name, "%s/%s", cd_path, new_name );
  263.         if ((fp = fopen (name, "rt")) != NULL) {
  264.             *trans_type = i;
  265.             free( new_name );
  266.             free( name );
  267.             return( fp );
  268.         }
  269.     }
  270.     /*
  271.     **    Failed to open the file.
  272.     **    Return NULL file descriptor to signal error.
  273.     */
  274.     free( new_name );
  275.     free( name );
  276.     return( NULL );
  277. }
  278.  
  279. int
  280. rrip_proc_dir( cd_path, new_path, recurse )
  281.     char    *cd_path;
  282.     char    *new_path;
  283.     int        recurse;
  284. {
  285.     FILE            *fp;
  286.     char            line[MAXPATHLEN];
  287.     char            file_name[MAXPATHLEN];
  288.     char            link_buf[MAXPATHLEN];
  289.     char            trans_name[MAXPATHLEN];
  290.     char            link_name [MAXPATHLEN];
  291.     char            new_name [MAXPATHLEN];
  292.     char            resolved_name [MAXPATHLEN];
  293.     char            type;
  294.     int                num_fields;
  295.     char            command[MAXPATHLEN];
  296.  
  297.     /*
  298.     **    For each directory entry.  Get its type.  
  299.     **    Depending on its type make a directory or symbolic link.
  300.     **    If the type is a directory and directory recursion was
  301.     **    asked for on the command line, then push it onto the
  302.     **    stack to be proccessed later.
  303.     */
  304.     sprintf( trans_name, "%s/%s", cd_path, RRIP_TRANSLATION_FILE );
  305.     if ((fp = fopen( trans_name, "rt" )) == NULL ) {
  306.         fprintf (stderr, "Unable to open translation file %s!\n", trans_name);
  307.         return FALSE;
  308.     }
  309.     while (fgets( line, sizeof( line ), fp) != NULL) {
  310.         /*
  311.         **    Get the type of the file,
  312.         **    the file name and the link name if this entry is a link.
  313.         */
  314.         strcpy( link_name, "" );
  315.         num_fields = sscanf( line, "%c %*s %s %s", 
  316.                                             &type, file_name, link_name );
  317.  
  318.         if (strcmp( file_name, ".") == 0)
  319.             continue;
  320.         if (strcmp( file_name, "..") == 0)
  321.             continue;
  322.         sprintf (new_name, "%s/%s", new_path, file_name);
  323.         switch (type) {
  324.             case 'F' :    
  325.                 sprintf( command, "cp \"%s/%s\" %s", cd_path, 
  326.                                                         file_name, new_name);
  327.                 if (system ( command ) < 0)
  328.                     fprintf (stderr, "Unable to copy %s to %s!\n", 
  329.                                                 link_name, new_name);
  330.                     break;
  331.  
  332.             case 'L' :    
  333. #ifdef M_XENIX
  334.                 fprintf (stderr, "Unable to make link %s to %s!\n", 
  335.                     link_name, new_name);
  336. #else
  337.                 if (symlink (link_name, new_name) != 0)
  338.                     fprintf (stderr, "Unable to make link %s to %s!\n", 
  339.                         link_name, new_name);
  340. #endif
  341.                     break;
  342.  
  343.             case 'D' :    
  344.                 mkdir (new_name, 0777);
  345.                 if (recurse) {
  346.                     sprintf (link_name, "%s/%s", cd_path, file_name);
  347.                     push_dir (link_name, new_name);
  348.                 }
  349.                 break;
  350.  
  351.             case 'M' :    
  352.                 mkdir (new_name, 0777);
  353.                 if (recurse) {    
  354.                     sprintf (link_buf, "%s/%s", cd_path, link_name);
  355. #ifdef REALPATH
  356.                     realpath (link_buf, resolved_name);
  357. #else
  358.                     strcpy (resolved_name, link_buf);
  359. #endif
  360.                     push_dir (resolved_name, new_name);
  361.                 }
  362.                 break;
  363.             default:
  364.                 fprintf(stderr, "proc_dir:    Unknown file type.\n");
  365.                 exit( 1 );
  366.         }
  367.     }
  368.     fclose (fp);
  369.     return TRUE;
  370. }
  371.  
  372. int
  373. iso9660_proc_dir( cd_path, new_path, recurse )
  374.     char    *cd_path;
  375.     char    *new_path;
  376.     int        recurse;
  377. {
  378.     FILE            *fp;
  379.     char            line [4096], link_name [4096], new_name [4096];
  380.     char            trans_name [4096], resolved_name [MAXPATHLEN];
  381.     char            type, elem1 [65], elem2 [2048], elem3 [2048];
  382.     int             line_cnt, elem_cnt, i, j;
  383.     static int        trans_type = 0;
  384.     char            command[MAXPATHLEN];
  385.  
  386.     sprintf (trans_name, "%s/%s", cd_path, TRANSLATION_FILE);
  387.     if ((fp = open_trans( cd_path, TRANSLATION_FILE, &trans_type )) == NULL)
  388.       {    
  389.         fprintf (stderr, "Unable to open file %s!\n", trans_name);
  390.         return FALSE;
  391.       }
  392.     line_cnt = 0;
  393.     while (fgets (line, sizeof (line), fp) != NULL) {
  394.         line_cnt ++;
  395.         if ((strlen (line) < 19) || (line [1] != ' ') || (line [strlen (line) - 1] != '\n'))
  396.           {    fprintf (stderr, "Invalid %s file!?!\n", trans_name);
  397.             exit (1);
  398.           }
  399.         type = line [0];
  400.  
  401.         /*
  402.         **    Get the ISO name.
  403.         */
  404.         for (i = 2, j = 0; (line [i] != ' ') && (line [i] != '\t'); i ++, j ++)
  405.             elem1 [j] = line [i];
  406.  
  407.         elem1 [j] = '\0';
  408.  
  409.         /*
  410.         **    translate name to the same format that was required
  411.         **    in order to open the "YMTRANS.TBL;1".
  412.         */
  413.         translate_name( elem1, trans_type );
  414.         /*
  415.         **    Skip past white space.
  416.         */
  417.         while ((line [i] == ' ') || (line [i] == '\t'))
  418.             i ++;
  419.  
  420.         /*
  421.         **    Get the unix name.
  422.         */
  423.         for (j = 0; (line [i] != '\t') && (line [i] != '\n'); i ++, j ++)
  424.             elem2 [j] = line [i];
  425.         elem2 [j] = '\0';
  426.  
  427.         elem_cnt = 2;
  428.         j = 0;
  429.         if (line [i] == '\t') {
  430.             /*
  431.             **    Get name of file that this name is a link to 
  432.             **    if this is a link.
  433.             */
  434.             for (i ++; line [i] != '\n'; i ++, j ++)
  435.                 elem3 [j] = line [i];
  436.             elem_cnt ++;
  437.         }
  438.         elem3 [j] = '\0';
  439.         if ((line_cnt == 1) && (strcmp (elem1, ".") == 0))
  440.             continue;
  441.         if ((line_cnt == 2) && (strcmp (elem1, "..") == 0))
  442.             continue;
  443.         sprintf (new_name, "%s/%s", new_path, elem2);
  444.         switch (type) {
  445.             case 'F' :    
  446.                 sprintf( command, "cp \"%s/%s\" %s", cd_path, elem1, new_name);
  447.                 if (system ( command ) < 0)
  448.                     fprintf (stderr, "Unable to copy %s to %s!\n", 
  449.                                                                 elem1, elem2);
  450.                     break;
  451.  
  452.             case 'L' :    
  453. #ifdef M_XENIX
  454.                 fprintf (stderr, "Unable to make link %s to %s!\n", 
  455.                     elem1, elem3);
  456. #else
  457.                 if (symlink (elem3, new_name) != 0)
  458.                     fprintf (stderr, "Unable to make link %s to %s!\n", 
  459.                         elem1, elem3);
  460. #endif
  461.                     break;
  462.  
  463.             case 'D' :    
  464.                 mkdir (new_name, 0777);
  465.                 if (recurse) {
  466.                     sprintf (link_name, "%s/%s", cd_path, elem1);
  467.                     push_dir (link_name, new_name);
  468.                 }
  469.                 break;
  470.  
  471.             case 'M' :    
  472.                 mkdir (new_name, 0777);
  473.                 if (recurse) {    
  474.                     sprintf (link_name, "%s/%s", cd_path, elem3);
  475. #ifdef REALPATH
  476.                     realpath (link_name, resolved_name);
  477. #else
  478.                     strcpy (resolved_name, link_name);
  479. #endif
  480.                     push_dir (resolved_name, new_name);
  481.                 }
  482.                 break;
  483.             default:
  484.                 fprintf(stderr, "proc_dir:    Unknown file type.\n");
  485.                 exit( 1 );
  486.         }
  487.     }
  488.     fclose (fp);
  489.     return TRUE;
  490. }
  491.  
  492. int 
  493. proc_dir (cd_path, new_path, flags)
  494.     char *cd_path, *new_path;
  495.     int flags;
  496.  
  497. {    
  498.     int    recurse;
  499.  
  500.     /*
  501.     **    If command line arguement "-r" was specified then
  502.     **    recurse down subdirectories.
  503.     */
  504.     if ((flags & RECURSE) != 0)
  505.         recurse = TRUE;
  506.     else
  507.         recurse = FALSE;
  508.  
  509.     /*
  510.     **    If command line arguement "-R" was specified then
  511.     **    ignore the YMTRANS.TBL and create links with the 
  512.     **    same names as the names that exist in the directory
  513.     **    entries on the disk.
  514.     **
  515.     **    This is most useful on Rock Ridge disks where the
  516.     **    name in the directory entry is the name that should
  517.     **    be used.
  518.     */
  519.     if ((flags & ROCK_RIDGE) != 0 )
  520.         rrip_proc_dir( cd_path, new_path, recurse );
  521.     else
  522.         iso9660_proc_dir( cd_path, new_path, recurse );
  523. }
  524.  
  525. int main (argc, argv)
  526. int argc;
  527. char *argv [];
  528.  
  529. {    dir_elem    *cur_dir;
  530.     char        cd_pathname [2048];
  531.     char        target_dirname[2048];
  532.     char        resolved_cd_name [MAXPATHLEN];
  533.     char        resolved_dir_name [MAXPATHLEN];
  534.     int            flags;
  535.  
  536.     int        this_arg = 1;
  537.     int        switch_count;
  538.     char    *program_name;
  539.     char    error_message[80];
  540.     char    *arg_list = "[-rR] cd_pathname [target_dir]";
  541.  
  542.     fprintf (stderr, 
  543.         "cd_link : Copyright 1989, 1990, 1991, 1992 By Young Minds, Incoporated\n");
  544.  
  545.     /*    Extract program name from first arguement.    */
  546.     program_name = get_program_name(argv[0]);
  547.  
  548.     /*    Process arguements    */
  549.     flags = 0;
  550.     cd_pathname[0] = '\0';
  551.     target_dirname[0] = '\0';
  552.     while (this_arg < argc) {
  553.         /*    Process switches    */
  554.         if (argv[this_arg][0] == '-') {
  555.             switch_count = 1;
  556.             while (argv[this_arg][switch_count] != '\0') {
  557.                 switch (argv[this_arg][switch_count]) {
  558.                     case 'r' :
  559.                         /*
  560.                         **    If command line arguement "-r" was specified then
  561.                         **    recurse down subdirectories.
  562.                         */
  563.                         flags |= RECURSE;
  564.                         break;
  565.                     case 'R' :
  566.                         /*
  567.                         **    If command line arguement "-R" was specified then
  568.                         **    ignore the YMTRANS.TBL and create links with the 
  569.                         **    same names as the names that exist in the directory
  570.                         **    entries on the disk.
  571.                         **
  572.                         **    This is most useful on Rock Ridge disks where the
  573.                         **    name in the directory entry is the name that should
  574.                         **    be used.
  575.                         */
  576.                         flags |= ROCK_RIDGE;
  577.                         break;
  578.                     default :
  579.                         sprintf(error_message, "Unknown switch: -%c", 
  580.                                                 argv[this_arg][switch_count]);
  581.                         usage( program_name, arg_list, error_message );
  582.                         exit(1);
  583.                         break;
  584.                 }
  585.                 switch_count++;
  586.             }
  587.         }
  588.         /*    Process everything else.    */
  589.         else {
  590.             /*    Get input file name.    */
  591.             if (cd_pathname[0] != '\0') {
  592.                 if (target_dirname[0] != '\0') {
  593.                     /*    
  594.                     **    If already gotten then an error exists 
  595.                     **    in the command line.
  596.                     */
  597.                     sprintf( error_message, "Invalid arguement: %s", 
  598.                                                             argv[this_arg] );
  599.                     usage( program_name, arg_list, error_message );
  600.                     exit(1);
  601.                 }
  602.                 else
  603.                     if (argv [this_arg] [0] == '/')
  604.                         strcpy (target_dirname, argv [this_arg]);
  605.                     else {    
  606.                         getwd (target_dirname);
  607.                         strcat (target_dirname, "/");
  608.                         strcat (target_dirname, argv [this_arg]);
  609.                       }
  610.             }
  611.             else {
  612.                 if (argv [this_arg] [0] == '/')
  613.                     strcpy (cd_pathname, argv [this_arg]);
  614.                 else {    
  615.                     getwd (cd_pathname);
  616.                     strcat (cd_pathname, "/");
  617.                     strcat (cd_pathname, argv [this_arg]);
  618.                   }
  619.             }
  620.         }
  621.         this_arg++;
  622.     }
  623.  
  624.     /*
  625.     **    If there was an input file specified then use that file for
  626.     **    input.  Otherwise, get input from stdin.
  627.     */ 
  628.     if (cd_pathname[0] == '\0') {
  629.         sprintf( error_message, "Missing cd_pathname.");
  630.         usage( program_name, arg_list, error_message );
  631.         exit(1);
  632.     }
  633.  
  634.     /*
  635.     **    If there was an output file specified then use that file for
  636.     **    output.  Otherwise, put output to stdout.
  637.     */
  638.     if (target_dirname[0] == '\0') {
  639.         getwd (target_dirname);
  640.     }
  641.  
  642.  
  643. #ifdef REALPATH
  644.     realpath (cd_pathname, resolved_cd_name);
  645.     realpath (target_dirname, resolved_dir_name);
  646. #else
  647.     strcpy (resolved_cd_name, cd_pathname);
  648.     strcpy (resolved_dir_name, target_dirname);
  649. #endif
  650.  
  651.     push_dir ( resolved_cd_name, resolved_dir_name );
  652.     while ((cur_dir = dequeue_dir ()) != NULL)
  653.       {    
  654.         proc_dir (cur_dir->cd_path, cur_dir->new_path, flags);
  655.         free_dir (cur_dir);
  656.       }
  657.     return 0;
  658. }
  659.