/* 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;
}