/* Unix(TM) CD-ROM symbolic link utility by Matthew B. Hornbeck, Director of Technical Services Copyright 1989, 1990, 1991 by Young Minds, Incorporated June 6, 1991. File : CD←LINK.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 <stdio.h> #include <string.h> #include <ctype.h> #include <malloc.h> #include <sys/types.h> #include <sys/dir.h> #include <sys/stat.h> #ifdef REALPATH #include <sys/param.h> #else #define MAXPATHLEN 4096 #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; /* ** 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 (link←name, "%s/%s", cd←path, file←name); if (symlink (link←name, new←name) != 0) fprintf (stderr, "Unable to make link %s to %s!\n", link←name, new←name); break; case 'L' : if (symlink (link←name, new←name) != 0) fprintf (stderr, "Unable to make link %s to %s!\n", link←name, new←name); 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; 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 (link←name, "%s/%s", cd←path, elem1); if (symlink (link←name, new←name) != 0) fprintf (stderr, "Unable to make link %s to %s!\n", elem1, elem2); break; case 'L' : if (symlink (elem3, new←name) != 0) fprintf (stderr, "Unable to make link %s to %s!\n", elem1, elem3); 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 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; }