/* this is 'yet another shell archiver' the rub is that it retains permissions (usually dangerious) and that it can execute a program once done unpacking. i use this to build transport packages when i need to deploy a fix onto several machines very quickly. this is not for public use; meaning other people would not want to run archives made with this program. then again, shell archives are pretty dumb to begin with. -mrs.brisby@nimh.org */ #include #include #include #include #include #include #include #define LINE_SIZE 1024 #define SCAN_SIZE 2048 FILE *arch = stdout; void archive(char *file); void archive_dir(char* file, struct stat *info); void archive_file(char* file, struct stat *info); void archive_link(char *file, struct stat *info); void archive_char_dev(char *file, struct stat *info); void archive_block_dev(char *file, struct stat *info); void archive_pipe(char *file, struct stat *info); int is_bin(char *file); static int a_bin = 0, silent_ex = 0; static char *pre_dir = 0; static char *auto_run = 0; int main( int argc, char **argv ) { int i = 1; while( argv[i] && argv[i][0] == '-' ) { switch( argv[i][1] ) { case 'r': ++i; if ( argv[i] == 0) exit( 1 ); auto_run = argv[i]; break; case 'o': ++i; if( argv[i] == 0 ) exit( 1 ); arch = fopen( argv[i], "w" ); fchmod( fileno( arch ), 0755 ); break; case 's': silent_ex = 1; break; case 'p': ++i; if ( argv[i] == 0) exit( 1 ); pre_dir = argv[i]; break; case 'a': a_bin = -1; break; case 'b': a_bin = 1; break; case 'h': default: fprintf( stderr, "usage: sha [-o output-file] [-a] [-b] [-r auto-run] [-p prefix] files\n"); exit( 0 ); } ++i; } fputs("#!/bin/sh\n",arch); if (pre_dir) { fprintf(arch,"mkdir %s\ncd %s\n", pre_dir, pre_dir); } while( i < argc ) archive( argv[i++] ); if (auto_run) { fprintf(arch,"%s\n", auto_run); } fputs("exit 0\n", arch); exit(0); } void archive( char *file ) { struct stat info; if( lstat( file, &info ) < 0 ) { perror( file ); return; } fprintf( stderr, "archiving %s\n", file ); if (silent_ex == 0) { fprintf( arch, "echo extracting %s%s%s\n", pre_dir?pre_dir:"", pre_dir?"/":"", file ); } if( S_ISDIR( info.st_mode ) ) archive_dir( file, &info ); else if( S_ISREG( info.st_mode ) ) archive_file( file, &info ); else if( S_ISFIFO( info.st_mode ) ) archive_pipe( file, &info ); else if( S_ISBLK( info.st_mode ) ) archive_block_dev( file, &info ); else if( S_ISCHR( info.st_mode ) ) archive_char_dev( file, &info ); else if( S_ISLNK( info.st_mode ) ) archive_link( file, &info ); else fprintf( stderr, "file mode not recognized: %s\n", file ); } /* We will only be able to store MAX_FILES_OPEN sub-directory levels ... */ void archive_dir( char* file, struct stat *info ) { char path[LINE_SIZE+1]; struct dirent *ent; DIR *dir; if( strcmp( file, "." ) && strcmp( file, ".." ) ) fprintf( arch, "mkdir %s\n", file ); dir = opendir( file ); if( dir == NULL ) { perror( file ); return; } while( ent = readdir( dir ) ) { ent->d_name[ent->d_reclen] = '\0'; if( strcmp( ent->d_name, "." ) && strcmp( ent->d_name, ".." ) ) { sprintf( path, "%s/%s", file, ent->d_name ); archive( path ); } } closedir( dir ); fprintf( arch, "chmod %o %s\n", 07777&info->st_mode, file ); } /* Maybe a Bug: If a text file doesn't end with a '\n', it will be appended */ /* but then again, if it IS a text file, this probably doesn't matter */ void archive_file( char* file, struct stat *info ) { char cmd[LINE_SIZE+1]; FILE *fptr; int d, n, lc = '\n'; unsigned char c; if( is_bin( file ) ) { sprintf( cmd, "uuencode %s %s", file, file ); fptr = popen( cmd, "r" ); if( fptr == NULL ) { perror( cmd ); return; } fprintf( arch, "uudecode << '\\\\__END__OF__%s__FILE\\\\'\n", file ); } else { fptr = fopen( file, "r" ); if( fptr == NULL ) { perror( file ); return; } fprintf(arch, "cat > %s << '\\\\__END__OF__%s__FILE\\\\'\n",file,file); } for(;;) { c = getc( fptr ); if( feof( fptr ) ) break; putc( c, arch ); lc = c; } if( lc != '\n' ) putc( '\n', arch ); fprintf( arch, "\\\\__END__OF__%s__FILE\\\\\n", file ); fprintf( arch, "chmod %o %s\n", 07777&info->st_mode, file ); fclose( fptr ); } void archive_pipe( char *file, struct stat *info ) { fprintf( arch, "mknod -m %o %s p\n", file, 07777&info->st_mode ); } void archive_block_dev( char *file, struct stat *info ) { fprintf( arch, "mknod -m %o %s b %d %d\n", 07777&info->st_mode, file, (info->st_rdev&0xff00)>>8, info->st_rdev & 0xff ); } void archive_char_dev( char *file, struct stat *info ) { fprintf( arch, "mknod -m %o %s c %d %d\n", 07777 & info->st_mode, file, (info->st_rdev&0xff00)>>8, info->st_rdev & 0xff ); } void archive_link( char *file, struct stat *info ) { char path[LINE_SIZE+1]; int n; n = readlink( file, path, LINE_SIZE ); if( n <= 0 ) { perror( file ); return; } path[n] = '\0'; fprintf( arch, "ln -s %s %s\n", path, file ); } int is_bin( char *file ) { int n, d; char buff[SCAN_SIZE+1]; if (a_bin == -1) return 0; if (a_bin == 1) return 1; d = open( file, O_RDONLY ); if( d == -1 ) { perror( file ); return; } n = read( d, buff, SCAN_SIZE ); close( d ); while( --n >= 0 ) if( buff[n] != '\0' && !isascii( buff[n] ) ) return 1; return 0; }