/*
* Copyright (c) 2003, 2007 Regents of The University of Michigan.
* All Rights Reserved. See COPYRIGHT.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#ifdef sun
#include <sys/mkdev.h>
#endif /* sun */
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include "applefile.h"
#include "base64.h"
#include "transcript.h"
#include "argcargv.h"
#include "code.h"
#include "cksum.h"
#include "pathcmp.h"
#include "largefile.h"
#include "list.h"
int read_kfile( char *kfile, int location );
static void t_new( int type, char *fullname, char *shortname, char *kfile );
static void t_remove( int type, char *shortname );
static void t_display( void );
struct transcript *tran_head = NULL;
static struct transcript *prev_tran = NULL;
extern int edit_path;
extern int case_sensitive;
static char *kdir;
static struct list *kfile_list;
struct list *special_list;
char *path_prefix = NULL;
int edit_path;
int skip;
int cksum;
FILE *outtran;
void
transcript_parse( struct transcript *tran )
{
char line[ 2 * MAXPATHLEN ];
int length;
char *epath;
char **argv;
int ac;
/* read in the next line in the transcript, loop through blanks and # */
do {
if (( fgets( line, MAXPATHLEN, tran->t_in )) == NULL ) {
tran->t_eof = 1;
return;
}
tran->t_linenum++;
/* check to see if line contains the whole line */
length = strlen( line );
if ( line[ length - 1 ] != '\n' ) {
fprintf( stderr, "%s: line %d: line too long\n",
tran->t_fullname, tran->t_linenum );
exit( 2 );
}
} while ((( ac = argcargv( line, &argv )) == 0 ) || ( *argv[ 0 ] == '#' ));
if ( ac < 3 ) {
fprintf( stderr, "%s: line %d: minimum 3 arguments, got %d\n",
tran->t_fullname, tran->t_linenum, ac );
exit( 2 );
}
if ( strlen( argv[ 0 ] ) != 1 ) {
fprintf( stderr, "%s: line %d: %s is too long to be a type\n",
tran->t_fullname, tran->t_linenum, argv[ 0 ] );
exit( 2 );
}
if ( argv[ 0 ][ 0 ] == '-' ) {
argv++;
ac--;
tran->t_pinfo.pi_minus = 1;
} else {
tran->t_pinfo.pi_minus = 0;
}
if ( argv[ 0 ][ 0 ] == '+' ) {
argv++;
ac--;
}
tran->t_pinfo.pi_type = argv[ 0 ][ 0 ];
if (( epath = decode( argv[ 1 ] )) == NULL ) {
fprintf( stderr, "%s: line %d: path too long\n",
tran->t_fullname, tran->t_linenum );
exit( 2 );
}
if ( pathcasecmp( epath, tran->t_pinfo.pi_name, case_sensitive ) < 0 ) {
fprintf( stderr, "%s: line %d: bad sort order\n",
tran->t_fullname, tran->t_linenum );
exit( 2 );
}
strcpy( tran->t_pinfo.pi_name, epath );
/* reading and parsing the line */
switch( *argv[ 0 ] ) {
case 'd': /* dir */
#ifdef __APPLE__
if (( ac != 5 ) && ( ac != 6 )) {
fprintf( stderr, "%s: line %d: expected 5 or 6 arguments, got %d\n",
tran->t_fullname, tran->t_linenum, ac );
exit( 2 );
}
#else /* !__APPLE__ */
if ( ac != 5 ) {
fprintf( stderr, "%s: line %d: expected 5 arguments, got %d\n",
tran->t_fullname, tran->t_linenum, ac );
exit( 2 );
}
#endif /* __APPLE__ */
tran->t_pinfo.pi_stat.st_mode = strtol( argv[ 2 ], NULL, 8 );
tran->t_pinfo.pi_stat.st_uid = atoi( argv[ 3 ] );
tran->t_pinfo.pi_stat.st_gid = atoi( argv[ 4 ] );
if ( ac == 6 ) {
base64_d( argv[ 5 ], strlen( argv[ 5 ] ),
(char *)tran->t_pinfo.pi_afinfo.ai.ai_data );
} else {
memset( tran->t_pinfo.pi_afinfo.ai.ai_data, 0, FINFOLEN );
}
break;
case 'p':
case 'D':
case 's':
if ( ac != 5 ) {
fprintf( stderr, "%s: line %d: expected 5 arguments, got %d\n",
tran->t_fullname, tran->t_linenum, ac );
exit( 2 );
}
tran->t_pinfo.pi_stat.st_mode = strtol( argv[ 2 ], NULL, 8 );
tran->t_pinfo.pi_stat.st_uid = atoi( argv[ 3 ] );
tran->t_pinfo.pi_stat.st_gid = atoi( argv[ 4 ] );
break;
case 'b': /* block or char */
case 'c':
if ( ac != 7 ) {
fprintf( stderr, "%s: line %d: expected 7 arguments, got %d\n",
tran->t_fullname, tran->t_linenum, ac );
exit( 2 );
}
tran->t_pinfo.pi_stat.st_mode = strtol( argv[ 2 ], NULL, 8 );
tran->t_pinfo.pi_stat.st_uid = atoi( argv[ 3 ] );
tran->t_pinfo.pi_stat.st_gid = atoi( argv[ 4 ] );
tran->t_pinfo.pi_stat.st_rdev =
makedev( ( unsigned )( atoi( argv[ 5 ] )),
( unsigned )( atoi( argv[ 6 ] )));
break;
case 'l': /* link */
case 'h': /* hard */
if ( ac != 3 ) {
fprintf( stderr, "%s: line %d: expected 3 arguments, got %d\n",
tran->t_fullname, tran->t_linenum, ac );
exit( 2 );
}
if (( epath = decode( argv[ 2 ] )) == NULL ) {
fprintf( stderr, "%s: line %d: target path too long\n",
tran->t_fullname, tran->t_linenum );
exit( 2 );
}
strcpy( tran->t_pinfo.pi_link, epath );
break;
case 'a': /* hfs applefile */
case 'f': /* file */
if ( ac != 8 ) {
fprintf( stderr, "%s: line %d: expected 8 arguments, got %d\n",
tran->t_fullname, tran->t_linenum, ac );
exit( 2 );
}
tran->t_pinfo.pi_stat.st_mode = strtol( argv[ 2 ], NULL, 8 );
tran->t_pinfo.pi_stat.st_uid = atoi( argv[ 3 ] );
tran->t_pinfo.pi_stat.st_gid = atoi( argv[ 4 ] );
tran->t_pinfo.pi_stat.st_mtime = atoi( argv[ 5 ] );
tran->t_pinfo.pi_stat.st_size = strtoofft( argv[ 6 ], NULL, 10 );
if ( tran->t_type != T_NEGATIVE ) {
if (( cksum ) && ( strcmp( "-", argv [ 7 ] ) == 0 )) {
fprintf( stderr, "%s: line %d: no cksums in transcript\n",
tran->t_fullname, tran->t_linenum );
exit( 2 );
}
}
strcpy( tran->t_pinfo.pi_cksum_b64, argv[ 7 ] );
break;
default:
fprintf( stderr,
"%s: line %d: unknown file type '%c'\n",
tran->t_fullname, tran->t_linenum, *argv[ 0 ] );
exit( 2 );
}
return;
}
void
t_print( struct pathinfo *fs, struct transcript *tran, int flag )
{
struct pathinfo *cur;
char *epath;
dev_t dev;
int print_minus = 0;
#ifdef __APPLE__
static char null_buf[ 32 ] = { 0 };
#endif /* __APPLE__ */
if ( edit_path == APPLICABLE ) {
cur = &tran->t_pinfo;
if (( fs != NULL ) && ( fs->pi_type != 'd' ) &&
( fs->pi_type != 'h' ) && ( fs->pi_stat.st_nlink > 1 )) {
hardlink_changed( fs, 1 );
}
} else {
cur = fs; /* What if this is NULL? */
}
/* Print name of transcript if it changed since the last t_print */
if (( edit_path == APPLICABLE )
&& (( flag == PR_TRAN_ONLY ) || ( flag == PR_DOWNLOAD )
|| ( flag == PR_STATUS_NEG ))
&& ( prev_tran != tran )) {
fprintf( outtran, "%s:\n", tran->t_shortname );
prev_tran = tran;
}
/*
* If a file is missing from the edit_path that was chosen, a - is
* printed and then the file name that is missing is printed.
*/
if ( edit_path == APPLICABLE ) {
if ( flag == PR_FS_ONLY ) {
print_minus = 1;
cur = fs;
} else if ( flag == PR_STATUS_MINUS ) {
fprintf( outtran, "- " );
}
} else if (( edit_path == CREATABLE ) &&
(( flag == PR_TRAN_ONLY ) || ( fs->pi_type == 'X' ))) {
print_minus = 1;
cur = &tran->t_pinfo;
}
if ( print_minus ) {
fprintf( outtran, "- " );
}
if (( epath = encode( cur->pi_name )) == NULL ) {
fprintf( stderr, "Filename too long: %s\n", cur->pi_name );
exit( 2 );
}
/* print out info to file based on type */
switch( cur->pi_type ) {
case 's':
case 'D':
case 'p':
fprintf( outtran, "%c %-37s\t%.4lo %5d %5d\n", cur->pi_type, epath,
(unsigned long )( T_MODE & cur->pi_stat.st_mode ),
(int)cur->pi_stat.st_uid, (int)cur->pi_stat.st_gid );
break;
case 'd':
#ifdef __APPLE__
if ( memcmp( cur->pi_afinfo.ai.ai_data, null_buf,
sizeof( null_buf )) != 0 ) {
char finfo_e[ SZ_BASE64_E( FINFOLEN ) ];
base64_e( (char *)cur->pi_afinfo.ai.ai_data, FINFOLEN, finfo_e );
fprintf( outtran, "%c %-37s\t%.4lo %5d %5d %s\n", cur->pi_type,
epath,
(unsigned long)( T_MODE & cur->pi_stat.st_mode ),
(int)cur->pi_stat.st_uid, (int)cur->pi_stat.st_gid,
finfo_e );
break;
}
#endif /* __APPLE__ */
fprintf( outtran, "%c %-37s\t%.4lo %5d %5d\n", cur->pi_type, epath,
(unsigned long )( T_MODE & cur->pi_stat.st_mode ),
(int)cur->pi_stat.st_uid, (int)cur->pi_stat.st_gid );
break;
case 'l':
case 'h':
fprintf( outtran, "%c %-37s\t", cur->pi_type, epath );
if (( epath = encode( cur->pi_link )) == NULL ) {
fprintf( stderr, "Filename too long: %s\n", cur->pi_link );
exit( 2 );
}
fprintf( outtran, "%s\n", epath );
break;
case 'a': /* hfs applesingle file */
case 'f':
if (( edit_path == APPLICABLE ) && (( flag == PR_TRAN_ONLY ) ||
( flag == PR_DOWNLOAD ))) {
fprintf( outtran, "+ " );
}
/*
* If we don't have a checksum yet, and checksums are on, calculate
* it now. Note that this can only be the case if "cur" is the
* filesystem, because transcript_parse() won't read lines without
* checksums if they are enabled. But, don't get the checksum
* if we are just going to remove the file.
*/
if (( *cur->pi_cksum_b64 == '-' ) && cksum && !print_minus ) {
if ( cur->pi_type == 'f' ) {
if ( do_cksum( cur->pi_name, cur->pi_cksum_b64 ) < 0 ) {
perror( cur->pi_name );
exit( 2 );
}
} else if ( cur->pi_type == 'a' ) {
if ( do_acksum( cur->pi_name, cur->pi_cksum_b64,
&cur->pi_afinfo ) < 0 ) {
perror( cur->pi_name );
exit( 2 );
}
}
}
/*
* PR_STATUS_NEG means we've had a permission change on a file,
* but the corresponding transcript is negative, hence, retain
* the file system's mtime. Woof!
*/
fprintf( outtran, "%c %-37s\t%.4lo %5d %5d %9d %7" PRIofft "d %s\n",
cur->pi_type, epath,
(unsigned long)( T_MODE & cur->pi_stat.st_mode ),
(int)cur->pi_stat.st_uid, (int)cur->pi_stat.st_gid,
( flag == PR_STATUS_NEG ) ?
(int)fs->pi_stat.st_mtime : (int)cur->pi_stat.st_mtime,
cur->pi_stat.st_size, cur->pi_cksum_b64 );
break;
case 'c':
case 'b':
dev = cur->pi_stat.st_rdev;
fprintf( outtran, "%c %-37s\t%.4lo %5d %5d %5d %5d\n",
cur->pi_type, epath,
(unsigned long )( T_MODE & cur->pi_stat.st_mode ),
(int)cur->pi_stat.st_uid, (int)cur->pi_stat.st_gid,
(int)major(dev), (int)minor(dev) );
break;
case 'X' :
perror( cur->pi_name );
exit( 2 );
default:
fprintf( stderr, "%s: Unknown type: %c\n", cur->pi_name, cur->pi_type );
exit( 2 );
}
}
static int
t_compare( struct pathinfo *fs, struct transcript *tran )
{
int cmp;
mode_t mode;
mode_t tran_mode;
dev_t dev;
/*
* If the transcript is at EOF, and we've exhausted the filesystem,
* just return T_MOVE_FS, as this will cause transcript() to return.
*/
if (( tran->t_eof ) && ( fs == NULL )) {
return T_MOVE_FS;
}
if ( tran->t_eof ) {
cmp = -1;
} else {
if ( fs == NULL ) {
/*
* If we've exhausted the filesystem, cmp = 1 means that
* name is in tran, but not fs.
*/
cmp = 1;
} else {
cmp = pathcasecmp( fs->pi_name, tran->t_pinfo.pi_name,
case_sensitive );
}
}
if ( cmp > 0 ) {
/* name is in the tran, but not the fs */
t_print( fs, tran, PR_TRAN_ONLY );
return T_MOVE_TRAN;
}
if ( cmp < 0 ) {
/* name is not in the tran */
t_print( fs, tran, PR_FS_ONLY );
return T_MOVE_FS;
}
/* convert the modes */
mode = ( T_MODE & fs->pi_stat.st_mode );
tran_mode = ( T_MODE & tran->t_pinfo.pi_stat.st_mode );
/* the names match so check types */
if ( fs->pi_type != tran->t_pinfo.pi_type ) {
t_print( fs, tran, PR_DOWNLOAD );
return T_MOVE_BOTH;
}
/* compare the other components for each file type */
switch( fs->pi_type ) {
case 'a': /* hfs applefile */
case 'f': /* file */
if ( tran->t_type != T_NEGATIVE ) {
if ( fs->pi_stat.st_size != tran->t_pinfo.pi_stat.st_size ) {
t_print( fs, tran, PR_DOWNLOAD );
break;
}
if ( cksum ) {
if ( fs->pi_type == 'f' ) {
if ( do_cksum( fs->pi_name, fs->pi_cksum_b64 ) < 0 ) {
perror( fs->pi_name );
exit( 2 );
}
} else if ( fs->pi_type == 'a' ) {
if ( do_acksum( fs->pi_name, fs->pi_cksum_b64,
&fs->pi_afinfo ) < 0 ) {
perror( fs->pi_name );
exit( 2 );
}
}
if ( strcmp( fs->pi_cksum_b64, tran->t_pinfo.pi_cksum_b64 ) != 0 ) {
t_print( fs, tran, PR_DOWNLOAD );
break;
}
} else if ( fs->pi_stat.st_mtime != tran->t_pinfo.pi_stat.st_mtime ) {
t_print( fs, tran, PR_DOWNLOAD );
break;
}
if ( fs->pi_stat.st_mtime != tran->t_pinfo.pi_stat.st_mtime ) {
t_print( fs, tran, PR_STATUS );
break;
}
}
if (( fs->pi_stat.st_uid != tran->t_pinfo.pi_stat.st_uid ) ||
( fs->pi_stat.st_gid != tran->t_pinfo.pi_stat.st_gid ) ||
( mode != tran_mode )) {
if (( tran->t_type == T_NEGATIVE ) && ( edit_path == APPLICABLE )) {
t_print( fs, tran, PR_STATUS_NEG );
} else {
t_print( fs, tran, PR_STATUS );
}
}
break;
case 'd': /* dir */
#ifdef __APPLE__
if ( tran->t_type != T_NEGATIVE ) {
if (( fs->pi_stat.st_uid != tran->t_pinfo.pi_stat.st_uid ) ||
( fs->pi_stat.st_gid != tran->t_pinfo.pi_stat.st_gid ) ||
( memcmp( fs->pi_afinfo.ai.ai_data,
tran->t_pinfo.pi_afinfo.ai.ai_data, FINFOLEN ) != 0 ) ||
( mode != tran_mode )) {
t_print( fs, tran, PR_STATUS );
}
break;
}
#endif /* __APPLE__ */
if (( fs->pi_stat.st_uid != tran->t_pinfo.pi_stat.st_uid ) ||
( fs->pi_stat.st_gid != tran->t_pinfo.pi_stat.st_gid ) ||
( mode != tran_mode )) {
t_print( fs, tran, PR_STATUS );
}
break;
case 'D':
case 'p':
case 's':
if (( fs->pi_stat.st_uid != tran->t_pinfo.pi_stat.st_uid ) ||
( fs->pi_stat.st_gid != tran->t_pinfo.pi_stat.st_gid ) ||
( mode != tran_mode )) {
t_print( fs, tran, PR_STATUS );
}
break;
case 'l': /* link */
if ( tran->t_type != T_NEGATIVE ) {
if ( strcmp( fs->pi_link, tran->t_pinfo.pi_link ) != 0 ) {
t_print( fs, tran, PR_STATUS );
}
}
break;
case 'h': /* hard */
if (( strcmp( fs->pi_link, tran->t_pinfo.pi_link ) != 0 ) ||
( hardlink_changed( fs, 0 ) != 0 )) {
t_print( fs, tran, PR_STATUS );
}
break;
case 'c':
/*
* negative character special files only check major and minor
* devices numbers. pseudo ttys can change uid, gid and mode for
* every login and this is normal behavior.
*/
dev = fs->pi_stat.st_rdev;
if ( tran->t_type != T_NEGATIVE ) {
if (( fs->pi_stat.st_uid != tran->t_pinfo.pi_stat.st_uid ) ||
( fs->pi_stat.st_gid != tran->t_pinfo.pi_stat.st_gid ) ||
( mode != tran_mode )) {
t_print( fs, tran, PR_STATUS );
}
}
if ( dev != tran->t_pinfo.pi_stat.st_rdev ) {
t_print( fs, tran, PR_STATUS );
}
break;
case 'b':
dev = fs->pi_stat.st_rdev;
if (( fs->pi_stat.st_uid != tran->t_pinfo.pi_stat.st_uid ) ||
( fs->pi_stat.st_gid != tran->t_pinfo.pi_stat.st_gid ) ||
( dev != tran->t_pinfo.pi_stat.st_rdev ) ||
( mode != tran_mode )) {
t_print( fs, tran, PR_STATUS );
}
break;
default:
fprintf( stderr, "%s: Unknown type: %c\n", fs->pi_name, fs->pi_type );
break;
}
return T_MOVE_BOTH;
}
/*
* Loop through the list of transcripts and compare each
* to find which transcript to start with. Only switch to the
* transcript if it is not at EOF. A transcript at EOF may
* still be returned.
*/
struct transcript *
transcript_select( void )
{
struct transcript *next_tran = NULL;
struct transcript *begin_tran = NULL;
for (;;) {
for ( begin_tran = tran_head, next_tran = tran_head->t_next;
next_tran != NULL; next_tran = next_tran->t_next ) {
if ( begin_tran->t_eof ) {
begin_tran = next_tran;
continue;
}
if ( ! next_tran->t_eof ) {
if ( pathcasecmp( next_tran->t_pinfo.pi_name,
begin_tran->t_pinfo.pi_name, case_sensitive ) < 0 ) {
begin_tran = next_tran;
}
}
}
/* move ahead other transcripts that match */
for ( next_tran = begin_tran->t_next; next_tran != NULL;
next_tran = next_tran->t_next ) {
if ( pathcasecmp( begin_tran->t_pinfo.pi_name,
next_tran->t_pinfo.pi_name, case_sensitive ) == 0 ) {
transcript_parse( next_tran );
}
}
/* This is presumably the NULL transcript. */
if ( !begin_tran->t_eof ) {
/*
* If the highest precedence transcript line has a leading '-',
* then just pretend it's not there.
*/
if ( begin_tran->t_pinfo.pi_minus ) {
transcript_parse( begin_tran );
continue;
}
/* Don't look outside of the initial path. */
if ( !ischildcase( begin_tran->t_pinfo.pi_name, path_prefix,
case_sensitive )) {
transcript_parse( begin_tran );
continue;
}
}
return( begin_tran );
}
}
int
transcript( char *path, struct stat *st, char *type, struct applefileinfo *afinfo )
{
struct pathinfo pi;
int enter = 0;
int len;
char epath[ MAXPATHLEN ];
char *linkpath;
struct transcript *tran = NULL;
/*
* path is NULL when we've been called after the filesystem has been
* exhausted, to consume any remaining transcripts.
*/
if ( path != NULL ) {
strcpy( pi.pi_name, path );
pi.pi_stat = *st;
pi.pi_type = *type;
pi.pi_afinfo = *afinfo;
/* if it's multiply referenced, check if it's a hardlink */
if ( !S_ISDIR( pi.pi_stat.st_mode ) && ( pi.pi_stat.st_nlink > 1 ) &&
(( linkpath = hardlink( &pi )) != NULL )) {
pi.pi_type = 'h';
strcpy( pi.pi_link, linkpath );
} else if ( S_ISLNK( pi.pi_stat.st_mode )) {
len = readlink( pi.pi_name, epath, MAXPATHLEN );
epath[ len ] = '\0';
strcpy( pi.pi_link, epath );
}
/* By default, go into directories */
if ( S_ISDIR( pi.pi_stat.st_mode )) {
enter = 1;
} else {
enter = 0;
}
/* initialize cksum field. */
strcpy( pi.pi_cksum_b64, "-" );
}
for (;;) {
tran = transcript_select();
switch ( t_compare(( path ? &pi : NULL ), tran )) {
case T_MOVE_FS :
return( enter );
case T_MOVE_BOTH :
/* But don't go into negative directories */
if (( tran->t_type == T_NEGATIVE ) &&
( tran->t_pinfo.pi_type == 'd' )) {
enter = 2;
}
transcript_parse( tran );
return( enter );
case T_MOVE_TRAN :
transcript_parse( tran );
break;
default :
fprintf( stderr, "t_compare returned an unexpected value!\n" );
exit( 2 );
}
}
}
static void
t_new( int type, char *fullname, char *shortname, char *kfile )
{
struct transcript *new;
if (( new = (struct transcript *)malloc( sizeof( struct transcript )))
== NULL ) {
perror( "malloc" );
exit( 2 );
}
memset( new, 0, sizeof( struct transcript ));
new->t_type = type;
switch ( type ) {
case T_NULL :
new->t_eof = 1;
break;
case T_POSITIVE :
case T_NEGATIVE :
case T_SPECIAL :
new->t_eof = 0;
new->t_linenum = 0;
strcpy( new->t_shortname, shortname );
strcpy( new->t_fullname, fullname );
strcpy( new->t_kfile, kfile );
if (( new->t_in = fopen( fullname, "r" )) == NULL ) {
perror( fullname );
exit( 2 );
}
transcript_parse( new );
break;
case T_EXCLUDE :
/* read the whole file, keep a list of possibly wild pathnames */
break;
default :
break;
}
new->t_next = tran_head;
if ( tran_head != NULL ) {
tran_head->t_prev = new;
new->t_num = new->t_next->t_num + 1;
}
tran_head = new;
return;
}
static void
t_remove( int type, char *shortname )
{
struct transcript *cur, *next = NULL;
cur = tran_head;
while ( cur->t_type != T_NULL ) {
next = cur->t_next;
if (( cur->t_type == type )
&& ( strcmp( cur->t_shortname, shortname ) == 0 )) {
if ( cur == tran_head ) {
tran_head = cur->t_next;
free( cur );
} else {
cur->t_prev->t_next = cur->t_next;
cur->t_next->t_prev = cur->t_prev;
free( cur );
}
}
cur = next;
}
return;
}
static void
t_display( void )
{
struct transcript *cur = NULL;
for ( cur = tran_head; cur != NULL; cur = cur->t_next ) {
printf( "%d: ", cur->t_num );
switch( cur->t_type ) {
case T_POSITIVE:
printf( "p %s\n", cur->t_shortname );
break;
case T_NEGATIVE:
printf( "n %s\n", cur->t_shortname );
break;
case T_SPECIAL:
printf( "s %s\n", cur->t_shortname );
break;
case T_NULL:
printf( "NULL\n" );
break;
default:
printf( "? %s\n", cur->t_shortname );
break;
}
}
return;
}
void
transcript_init( char *kfile, int location )
{
char *special = "special.T";
char *p;
char fullpath[ MAXPATHLEN ];
/*
* Make sure that there's always a transcript to read, so other code
* doesn't have to check it.
*/
t_new( T_NULL, NULL, NULL, NULL );
if ( skip ) {
return;
}
if (( kdir = strdup( kfile )) == NULL ) {
perror( "strdup failed" );
exit( 2 );
}
if (( p = strrchr( kdir, '/' )) == NULL ) {
/* No '/' in kfile - use working directory */
free( kdir );
kdir = "./";
} else {
p++;
*p = (char)'\0';
}
if (( kfile_list = list_new( )) == NULL ) {
perror( "list_new" );
exit( 2 );
}
if ( list_insert( kfile_list, kfile ) != 0 ) {
perror( "list_insert" );
exit( 2 );
}
if (( special_list = list_new( )) == NULL ) {
perror( "list_new" );
exit( 2 );
}
if ( read_kfile( kfile, location ) != 0 ) {
exit( 2 );
}
if (( list_size( special_list ) > 0 ) && ( location == K_CLIENT )) {
/* open the special transcript if there were any special files */
if ( strlen( kdir ) + strlen( special ) + 2 > MAXPATHLEN ) {
fprintf( stderr,
"special path too long: %s%s\n", kdir, special );
exit( 2 );
}
sprintf( fullpath, "%s%s", kdir, special );
t_new( T_SPECIAL, fullpath, special, "special" );
}
if ( tran_head->t_type == T_NULL && edit_path == APPLICABLE ) {
fprintf( stderr, "-A option requires a non-NULL transcript\n" );
exit( 2 );
}
return;
}
int
read_kfile( char *kfile, int location )
{
int length, ac, linenum = 0, minus = 0;
char line[ MAXPATHLEN ];
char fullpath[ MAXPATHLEN ];
char *subpath;
char **av;
FILE *fp;
if (( fp = fopen( kfile, "r" )) == NULL ) {
perror( kfile );
return( -1 );
}
while ( fgets( line, sizeof( line ), fp ) != NULL ) {
linenum++;
length = strlen( line );
if ( line[ length - 1 ] != '\n' ) {
fprintf( stderr, "command file %s: line %d: line too long\n",
kfile, linenum );
return( -1 );
}
/* skips blank lines and comments */
if ((( ac = argcargv( line, &av )) == 0 ) || ( *av[ 0 ] == '#' )) {
continue;
}
if ( *av[ 0 ] == '-' ) {
minus = 1;
av++;
ac--;
} else {
minus = 0;
}
if ( ac != 2 ) {
fprintf( stderr,
"command file %s: line %d: expected 2 arguments, got %d\n",
kfile, linenum, ac );
return( -1 );
}
switch( location ) {
case K_CLIENT:
if ( snprintf( fullpath, MAXPATHLEN, "%s%s", kdir,
av[ 1 ] ) >= MAXPATHLEN ) {
fprintf( stderr, "comand file %s: line %d: path too long\n",
kfile, linenum );
fprintf( stderr, "command file %s: line %d: %s%s\n",
kfile, linenum, kdir, av[ 1 ] );
return( -1 );
}
break;
case K_SERVER:
if ( *av[ 0 ] == 'k' ) {
subpath = "command";
} else {
subpath = "transcript";
}
if ( snprintf( fullpath, MAXPATHLEN, "%s/%s/%s", _RADMIND_PATH,
subpath, av[ 1 ] ) >= MAXPATHLEN ) {
fprintf( stderr, "command file %s: line %d: path too long\n",
kfile, linenum );
fprintf( stderr, "command file %s: line %d: %s%s\n",
kfile, linenum, kdir, av[ 1 ] );
return( -1 );
}
break;
default:
fprintf( stderr, "unknown location\n" );
return( -1 );
}
switch( *av[ 0 ] ) {
case 'k': /* command file */
if ( minus ) {
/* Error on minus command files for now */
fprintf( stderr, "command file %s: line %d: "
"minus 'k' not supported\n", kfile, linenum );
return( -1 );
} else {
if ( list_check( kfile_list, fullpath )) {
fprintf( stderr,
"command file %s: line %d: command file loop: %s already included\n",
kfile, linenum, av[ 1 ] );
return( -1 );
}
if ( list_insert( kfile_list, fullpath ) != 0 ) {
perror( "list_insert" );
return( -1 );
}
if ( read_kfile( fullpath, location ) != 0 ) {
return( -1 );
}
}
break;
case 'n': /* negative */
if ( minus ) {
t_remove( T_NEGATIVE, av[ 1 ] );
} else {
t_new( T_NEGATIVE, fullpath, av[ 1 ], kfile );
}
break;
case 'p': /* positive */
if ( minus ) {
t_remove( T_POSITIVE, av[ 1 ] );
} else {
t_new( T_POSITIVE, fullpath, av[ 1 ], kfile );
}
break;
case 'x': /* exclude */
if ( minus ) {
t_remove( T_EXCLUDE, av[ 1 ] );
} else {
t_new( T_EXCLUDE, fullpath, av[ 1 ], kfile );
}
break;
case 's': /* special */
if ( minus ) {
if ( list_check( special_list, av[ 1 ] )) {
list_remove( special_list, av[ 1 ] );
}
} else {
if ( !list_check( special_list, av[ 1 ] )) {
if ( list_insert( special_list, av[ 1 ] ) != 0 ) {
perror( "list_insert" );
return( -1 );
}
}
}
break;
default:
fprintf( stderr, "command file %s: line %d: '%s' invalid\n",
kfile, linenum, av[ 0 ] );
return( -1 );
}
}
if ( fclose( fp ) != 0 ) {
perror( kfile );
return( -1 );
}
return( 0 );
}
void
transcript_free( )
{
struct transcript *next;
/*
* Call transcript() with NULL to indicate that we've run out of
* filesystem to compare against.
*/
transcript( NULL, NULL, NULL, NULL );
while ( tran_head != NULL ) {
next = tran_head->t_next;
if ( tran_head->t_in != NULL ) {
fclose( tran_head->t_in );
}
free( tran_head );
tran_head = next;
}
}
syntax highlighted by Code2HTML, v. 0.9.1