/*
* Copyright (c) 2003 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "argcargv.h"
#include "code.h"
#include "mkdirs.h"
#include "pathcmp.h"
#include "root.h"
int cksum = 1;
int verbose = 0;
int noupload = 0;
int case_sensitive = 1;
extern char *version;
struct node {
char path[ MAXPATHLEN ];
struct node *next;
};
struct node* create_node( char *path );
void free_node( struct node *node );
struct node *
create_node( char *path )
{
struct node *new_node;
if (( new_node = (struct node *) malloc( sizeof( struct node ))) == NULL ) {
perror( "malloc" );
return( NULL );
}
if ( strlen( path ) >= MAXPATHLEN ) {
fprintf( stderr, "%s: path too long\n", path );
return( NULL );
}
strcpy( new_node->path, path );
return( new_node );
}
void
free_node( struct node *node )
{
free( node );
}
struct tran {
struct node *t_next; /* Next tran in list */
FILE *t_fd; /* open file descriptor */
int t_num; /* Tran num from command line */
char *t_path; /* Path from command line */
int t_eof; /* Tran at end of file */
int t_linenum; /* Current line number */
int t_remove; /* Current line has '-' */
char t_prepath[ MAXPATHLEN ]; /* for order check */
char t_tran_root[ MAXPATHLEN ];
char t_file_root[ MAXPATHLEN ];
char t_tran_name[ MAXPATHLEN ];
char *t_line;
char t_tline[ 2 * MAXPATHLEN ];
char t_filepath[ MAXPATHLEN ];
char **t_argv;
int t_tac;
ACAV *t_acav;
};
int getnextline( struct tran *tran );
int
getnextline( struct tran *tran )
{
int len;
char *d_path;
getline:
if ( fgets( tran->t_tline, MAXPATHLEN, tran->t_fd ) == NULL ) {
if ( feof( tran->t_fd )) {
tran->t_eof = 1;
return( 0 );
} else {
perror( tran->t_path );
return( -1 );
}
}
tran->t_linenum++;
if ( tran->t_line != NULL ) {
free( tran->t_line );
tran->t_line = NULL;
}
if ( ( tran->t_line = strdup( tran->t_tline ) ) == NULL ) {
perror( tran->t_tline );
return( -1 );
}
/* Check line length */
len = strlen( tran->t_tline );
if ( ( tran->t_tline[ len - 1 ] ) != '\n' ) {
fprintf( stderr, "%s: %d: %s: line too long\n", tran->t_tran_name,
tran->t_linenum, tran->t_tline );
return( -1 );
}
if ( ( tran->t_tac = acav_parse( tran->t_acav,
tran->t_tline, &(tran->t_argv) ) ) < 0 ) {
fprintf( stderr, "acav_parse\n" );
return( -1 );
}
/* Skip blank lines and comments */
if (( tran->t_tac == 0 ) || ( *tran->t_argv[ 0 ] == '#' )) {
goto getline;
}
if ( *tran->t_argv[ 0 ] == '-' ) {
tran->t_remove = 1;
tran->t_argv++;
} else {
tran->t_remove = 0;
}
/* Decode file path */
if (( d_path = decode( tran->t_argv[ 1 ] )) == NULL ) {
fprintf( stderr, "%s: line %d: path too long\n", tran->t_tran_name,
tran->t_linenum );
return( 1 );
}
if ( strlen( d_path ) >= MAXPATHLEN ) {
fprintf( stderr, "%s: line %d: %s: path too long\n",
tran->t_tran_name, tran->t_linenum, d_path );
return( 1 );
}
strcpy( tran->t_filepath, d_path );
/* Check transcript order */
if ( tran->t_prepath != 0 ) {
if ( pathcasecmp( tran->t_filepath, tran->t_prepath,
case_sensitive ) < 0 ) {
fprintf( stderr, "%s: line %d: bad sort order\n",
tran->t_tran_name, tran->t_linenum );
return( 1 );
}
}
if ( strlen( tran->t_filepath ) >= MAXPATHLEN ) {
fprintf( stderr, "%s: line %d: %s: path too long\n",
tran->t_tran_name, tran->t_linenum, tran->t_filepath );
return( 1 );
}
strcpy( tran->t_prepath, tran->t_filepath );
return( 0 );
}
/*
* exit codes:
* 0 okay
* 2 System error
*/
int
main( int argc, char **argv )
{
int c, i, j, cmpval, err = 0, tcount = 0, candidate = 0;
int force = 0, ofd, fileloc = 0, match = 0;
char *file = NULL;
char npath[ 2 * MAXPATHLEN ];
char opath[ 2 * MAXPATHLEN ];
char *radmind_path = _RADMIND_PATH;
char cwd[ MAXPATHLEN ];
char file_root[ MAXPATHLEN ];
char tran_root[ MAXPATHLEN ];
char tran_name[ MAXPATHLEN ];
char temp[ MAXPATHLEN ];
struct tran **trans = NULL;
struct node *new_node = NULL;
struct node *node = NULL;
struct node *dirlist = NULL;
FILE *ofs;
mode_t mask;
while ( ( c = getopt( argc, argv, "D:fInu:Vv" ) ) != EOF ) {
switch( c ) {
case 'D':
radmind_path = optarg;
break;
case 'f':
force = 1;
break;
case 'I':
case_sensitive = 0;
break;
case 'n':
noupload = 1;
break;
case 'u':
errno = 0;
mask = (mode_t)strtol( optarg, (char **)NULL, 0 );
if ( errno != 0 ) {
err++;
break;
}
umask( mask );
break;
case 'V':
printf( "%s\n", version );
exit( 0 );
case 'v':
verbose = 1;
break;
default:
err++;
break;
}
}
tcount = argc - ( optind + 1 ); /* "+ 1" accounts for dest tran */
if ( noupload && ( tcount > 2 ) ) {
err++;
}
/* make sure there's a second transcript */
if ( force && ( argv[ optind + 1 ] == NULL )) {
err++;
}
if ( force && ( tcount > 1 ) ) {
err++;
}
if ( !force && ( tcount < 2 )) {
err++;
}
if ( err ) {
fprintf( stderr, "Usage: %s [-vIV] [ -D path ] [ -u umask ] ",
argv[ 0 ] );
fprintf( stderr, "transcript... dest\n" );
fprintf( stderr, " %s -f [-vIV] [ -D path ] [ -u umask ] ",
argv[ 0 ] );
fprintf( stderr, "transcript1 transcript2\n" );
fprintf( stderr, " %s -n [-vIV] [ -D path ] [ -u umask ] ",
argv[ 0 ] );
fprintf( stderr, "transcript1 transcript2 dest\n" );
exit( 2 );
}
if ( force ) {
/* Check for write access */
if ( access( argv[ argc - 1 ], W_OK ) != 0 ) {
perror( argv[ argc - 1 ] );
exit( 2 );
}
tcount++; /* add dest to tran merge list */
}
/* Create array of transcripts */
if (( trans = (struct tran**)malloc(
sizeof( struct tran* ) * ( tcount ))) == NULL ) {
perror( "malloc" );
exit( 2 );
}
if ( getcwd( cwd, MAXPATHLEN ) == NULL ) {
perror( "getcwd" );
exit( 2 );
}
/* loop over array of trans */
for ( i = 0; i < tcount; i++ ) {
if ( ( trans[ i ] = (struct tran*)malloc( sizeof( struct tran ) ) )
== NULL ) {
perror( "malloc" );
return( 1 );
}
memset( trans[ i ], 0, sizeof( struct tran ));
trans[ i ]->t_num = i;
trans[ i ]->t_path = argv[ i + optind ];
if ( get_root( radmind_path, trans[ i ]->t_path, trans[ i ]->t_file_root,
trans[ i ]->t_tran_root, trans[ i ]->t_tran_name ) != 0 ) {
exit( 2 );
}
/* open tran */
if (( trans[ i ]->t_fd = fopen( trans[ i ]->t_path, "r" )) == NULL ) {
perror( trans[ i ]->t_path );
return( 1 );
}
if ( ( trans[ i ]->t_acav = acav_alloc() ) == NULL ) {
fprintf( stderr, "acav_malloc\n" );
return( 1 );
}
trans[ i ]->t_line = NULL;
if ( getnextline( trans[ i ] ) < 0 ) {
exit( 2 );
}
}
if ( force ) {
if ( strlen( trans[ 1 ]->t_file_root ) >= MAXPATHLEN ) {
fprintf( stderr, "%s: path too long\n", trans[ 1 ]->t_file_root );
exit( 2 );
}
strcpy( file_root, trans[ 1 ]->t_file_root );
if ( strlen( trans[ 1 ]->t_tran_root ) >= MAXPATHLEN ) {
fprintf( stderr, "%s: path too long\n", trans[ 1 ]->t_tran_root );
exit( 2 );
}
strcpy( tran_root, trans[ 1 ]->t_tran_root );
if ( strlen( trans[ 1 ]->t_tran_name ) >= MAXPATHLEN ) {
fprintf( stderr, "%s: path too long\n", trans[ 1 ]->t_tran_name );
exit( 2 );
}
strcpy( tran_name, trans[ 1 ]->t_tran_name );
} else {
/* Create tran if missing */
if (( ofd = open( argv[ argc - 1 ], O_WRONLY | O_CREAT, 0666 ) ) < 0 ) {
perror( argv[ argc - 1 ] );
exit( 2 );
}
if ( close( ofd ) != 0 ) {
perror( argv[ argc - 1 ] );
exit( 2 );
}
/* Get paths */
if ( *argv[ argc - 1 ] == '/' ) {
if ( strlen( argv[ argc - 1 ] ) >= MAXPATHLEN ) {
fprintf( stderr, "%s: path too long\n", argv[ argc - 1 ] );
exit( 2 );
}
strcpy( cwd, argv[ argc - 1 ] );
} else {
if ( snprintf( temp, MAXPATHLEN, "%s/%s", cwd, argv[ argc - 1 ] )
> MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s: path too long\n", cwd,
argv[ argc - 1 ] );
exit( 2 );
}
strcpy( cwd, temp );
}
if ( get_root( radmind_path, cwd, file_root, tran_root, tran_name ) != 0 ) {
exit( 2 );
}
/* Create file/tname dir */
if ( snprintf( npath, MAXPATHLEN, "%s/%s.%d", file_root, tran_name,
(int)getpid()) > MAXPATHLEN -1 ) {
fprintf( stderr, "%s/%s.%d: path too long\n", file_root, tran_name,
(int)getpid());
exit( 2 );
}
if ( mkdir( npath, (mode_t)0777 ) != 0 ) {
perror( npath );
exit( 2 );
}
}
/* Create temp transcript/tname file */
if ( snprintf( opath, MAXPATHLEN, "%s/%s.%d", tran_root, tran_name,
(int)getpid()) > MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s.%d: path too long\n", tran_root, tran_name,
(int)getpid());
exit( 2 );
}
if (( ofd = open( opath, O_WRONLY | O_CREAT | O_EXCL,
0666 ) ) < 0 ) {
perror( opath );
exit( 2 );
}
if ( ( ofs = fdopen( ofd, "w" ) ) == NULL ) {
perror( opath );
exit( 2 );
}
/* merge */
for ( i = 0; i < tcount; i++ ) {
while ( !(trans[ i ]->t_eof)) {
candidate = i;
fileloc = i;
match = 0;
if ( force && ( candidate == ( tcount - 1 ))) {
match = 1;
goto outputline;
}
/* Compare candidate to other transcripts */
for ( j = i + 1; j < tcount; j++ ) {
if ( trans[ j ]->t_eof ) {
continue;
}
cmpval = pathcasecmp( trans[ candidate ]->t_filepath,
trans[ j ]->t_filepath, case_sensitive );
if ( cmpval == 0 ) {
/* File match */
match = 1;
if (( noupload ) &&
( *trans[ candidate ]->t_argv[ 0 ] == 'f'
|| *trans[ candidate ]->t_argv[ 0 ] == 'a' )) {
/* Use lower precedence path */
trans[ candidate ]->t_path =
trans[ j ]->t_path;
/* Select which file should be linked */
if ( ( strcmp( trans[ candidate ]->t_argv[ 6 ],
trans[ j ]->t_argv[ 6 ] ) == 0 ) &&
( strcmp( trans[ candidate ]->t_argv[ 7 ],
trans[ j ]->t_argv[ 7 ] ) == 0 ) ) {
fileloc = j;
} else {
/* don't print file only in highest tran */
goto skipline;
}
}
if ( ( force ) && ( *trans[ j ]->t_argv[ 0 ] == 'f'
|| *trans[ j ]->t_argv[ 0 ] == 'a' )) {
/* Remove file from lower precedence transcript */
if ( snprintf( opath, MAXPATHLEN, "%s/%s/%s",
trans[ j ]->t_file_root,
trans[ j ]->t_tran_name,
trans[ j ]->t_filepath ) > MAXPATHLEN -1 ) {
fprintf( stderr,
"%s/%s/%s: path too long\n",
trans[ j ]->t_file_root,
trans[ j ]->t_tran_name,
trans[ j ]->t_filepath );
exit( 2 );
}
if ( unlink( opath ) != 0 ) {
perror( opath );
exit( 2 );
}
if ( verbose ) printf( "%s: %s: unlinked\n",
trans[ j ]->t_tran_name, trans[ j ]->t_filepath);
}
/* Advance lower precedence transcript */
if ( getnextline( trans[ j ] ) < 0 ) {
exit( 2 );
}
} else if ( cmpval > 0 ) {
candidate = j;
fileloc = j;
}
}
if ( force && ( candidate == 1 ) ) {
goto outputline;
}
/* skip items to be removed or files not uploaded */
if (( trans[ candidate ]->t_remove ) ||
(( noupload ) && ( candidate == 0 ) && ( fileloc == 0 ))) {
if ( match && force &&
( *trans[ candidate ]->t_argv[ 0 ] == 'd' )) {
new_node = create_node( trans[ candidate ]->t_argv[ 1 ] );
new_node->next = dirlist;
dirlist = new_node;
}
goto skipline;
}
/* output non-files */
if ( *trans[ candidate ]->t_argv[ 0 ] != 'f'
&& *trans[ candidate ]->t_argv[ 0 ] != 'a' ) {
goto outputline;
}
/*
* Assume that directory structure is present so the entire path
* is not recreated for every file. Only if link fails is
* mkdirs() called.
*/
/* Fix for broken mkdir on OS X */
if ( *trans[ candidate ]->t_filepath == '/' ) {
if ( snprintf( opath, MAXPATHLEN, "%s/%s%s",
trans[ candidate ]->t_file_root,
trans[ fileloc ]->t_tran_name,
trans[ candidate ]->t_filepath ) > MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s/%s: path too long\n",
trans[ candidate ]->t_file_root,
trans[ fileloc ]->t_tran_name,
trans[ candidate ]->t_filepath );
exit( 2 );
}
} else {
if ( snprintf( opath, MAXPATHLEN, "%s/%s/%s",
trans[ candidate ]->t_file_root,
trans[ fileloc ]->t_tran_name,
trans[ candidate ]->t_filepath ) > MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s/%s: path too long\n",
trans[ candidate ]->t_file_root,
trans[ fileloc ]->t_tran_name,
trans[ candidate ]->t_filepath );
exit( 2 );
}
}
if ( !force ) {
/* Fix for broken mkdir on OS X */
if ( *trans[ candidate ]->t_filepath == '/' ) {
if ( snprintf( npath, MAXPATHLEN, "%s/%s.%d/%s",
file_root, tran_name, (int)getpid(),
trans[ candidate ]->t_filepath )
> MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s.%d/%s: path too long\n",
file_root, tran_name, (int)getpid(),
trans[ candidate ]->t_filepath );
exit( 2 );
}
} else {
if ( snprintf( npath, MAXPATHLEN, "%s/%s.%d/%s",
file_root, tran_name, (int)getpid(),
trans[ candidate ]->t_filepath ) > MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s.%d/%s: path too long\n",
file_root, tran_name, (int)getpid(),
trans[ candidate ]->t_filepath );
exit( 2 );
}
}
} else {
if ( snprintf( npath, MAXPATHLEN, "%s/%s/%s", file_root,
tran_name, trans[ candidate ]->t_filepath )
> MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s/%s: path too long\n",
file_root, tran_name, trans[ candidate ]->t_filepath );
exit( 2 );
}
}
/* First try to link file */
if ( link( opath, npath ) != 0 ) {
/* If that fails, verify directory structure */
if ( ( file = strrchr( trans[ candidate ]->t_argv[ 1 ], '/' ) )
!= NULL ) {
if ( !force ) {
/* Fix for broken mkdir on OS X */
if ( *trans[ candidate ]->t_filepath == '/' ) {
if ( snprintf( npath, MAXPATHLEN,
"%s/%s.%d%s",
file_root, tran_name, (int)getpid(),
trans[ candidate ]->t_filepath )
> MAXPATHLEN - 1 ) {
fprintf( stderr,
"%s/%s.%d%s: path too long\n",
file_root, tran_name, (int)getpid(),
trans[ candidate ]->t_filepath );
exit( 2 );
}
} else {
if ( snprintf( npath, MAXPATHLEN,
"%s/%s.%d/%s",
file_root, tran_name, (int)getpid(),
trans[ candidate ]->t_filepath )
> MAXPATHLEN - 1 ) {
fprintf( stderr,
"%s/%s.%d/%s: path too long\n",
file_root, tran_name, (int)getpid(),
trans[ candidate ]->t_filepath );
exit( 2 );
}
}
} else {
if ( *trans[ candidate ]->t_filepath == '/' ) {
if ( snprintf( npath, MAXPATHLEN,
"%s/%s%s", file_root, tran_name,
trans[ candidate ]->t_filepath )
> MAXPATHLEN - 1 ) {
fprintf( stderr,
"%s/%s%s: path too long\n", file_root,
tran_name, trans[ candidate ]->t_filepath );
exit( 2 );
}
} else {
if ( snprintf( npath, MAXPATHLEN,
"%s/%s/%s", file_root, tran_name,
trans[ candidate ]->t_filepath )
> MAXPATHLEN - 1 ) {
fprintf( stderr,
"%s/%s/%s: path too long\n", file_root,
tran_name, trans[ candidate ]->t_filepath );
exit( 2 );
}
}
}
if ( mkdirs( npath ) != 0 ) {
fprintf( stderr, "%s: mkdirs failed\n", npath );
exit( 2 );
}
}
/* Try link again */
if ( link( opath, npath ) != 0 ) {
fprintf( stderr, "linking %s -> %s: %s\n",
opath, npath, strerror( errno ));
exit( 2 );
}
}
if ( verbose ) printf( "%s: %s: merged into: %s\n",
trans[ candidate ]->t_tran_name, trans[ candidate ]->t_filepath,
tran_name );
outputline:
/* Output line */
if ( fputs( trans[ candidate ]->t_line, ofs ) == EOF ) {
perror( trans[ candidate ]->t_line );
exit( 2 );
}
skipline:
/* Don't duplicate remove line if it's not a match, or
* we got -f and we're just outputing the last
* transcript.
*/
if (( trans[ candidate ]->t_remove )
&& !match
&& (!( force && ( candidate == 1 )))) {
/* Recreate unmatched "-" line */
if ( fputs( trans[ candidate ]->t_line, ofs ) == EOF ) {
perror( trans[ candidate ]->t_line );
exit( 2 );
}
}
if ( getnextline( trans[ candidate ] ) != 0 ) {
exit( 2 );
}
}
}
if ( force ) {
while ( dirlist != NULL ) {
node = dirlist;
dirlist = node->next;
if ( snprintf( opath, MAXPATHLEN, "%s/%s/%s", file_root,
tran_name, node->path ) >= MAXPATHLEN ) {
fprintf( stderr, "%s/%s/%s: path too long\n",
file_root, tran_name, node->path );
exit( 2 );
}
if ( rmdir( opath ) != 0 ) {
if (( errno == EEXIST ) || ( errno == ENOTEMPTY )) {
fprintf( stderr, "%s: %s: Not empty, continuing...\n",
tran_name, node->path );
} else if ( errno != ENOENT ) {
perror( opath );
exit( 2 );
}
} else {
if ( verbose ) printf( "%s: %s: unlinked\n", tran_name,
node->path );
}
free_node( node );
}
}
/* Rename temp transcript and file structure */
if ( !force ) {
if ( snprintf( opath, MAXPATHLEN, "%s/%s.%d", file_root,
tran_name, (int)getpid()) > MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s.%d: path too long\n",
file_root, tran_name, (int)getpid());
exit( 2 );
}
if ( snprintf( npath, MAXPATHLEN, "%s/%s", file_root, tran_name )
> MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s: path too long\n", file_root, tran_name );
exit( 2 );
}
if ( rename( opath, npath ) != 0 ) {
perror( npath );
exit( 2 );
}
}
if ( snprintf( opath, MAXPATHLEN, "%s/%s.%d", tran_root, tran_name,
(int)getpid()) > MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s.%d: path too long\n", tran_root, tran_name,
(int)getpid());
exit( 2 );
}
if ( snprintf( npath, MAXPATHLEN, "%s/%s", tran_root, tran_name )
> MAXPATHLEN - 1 ) {
fprintf( stderr, "%s/%s: path too long\n", tran_root, tran_name );
exit( 2 );
}
if ( rename( opath, npath ) != 0 ) {
perror( npath );
exit( 2 );
}
exit( 0 );
}
syntax highlighted by Code2HTML, v. 0.9.1