/* ** main.c - POSIX 1003.2 "ar" command ** ** This isn't a pure POSIX 1003.2 ar; it only manipulates Metrowerks ** Library files, not general-purpose POSIX 1003.2 format archives. ** ** Dec. 14, 1997 Chris Herborth (chrish@kagi.com) ** ** This code is donated to the PUBLIC DOMAIN. You can use, abuse, modify, ** redistribute, steal, or otherwise manipulate this code. No restrictions ** at all. If you laugh at this code, you can't use it. ** ** This "ar" was implemented using IEEE Std 1003.2-1992 as the basis for ** the interface, and Metrowerk's published docs detailing their library ** format. Look inside for clues about how reality differs from MW's ** documentation on BeOS... */ #include #include #include #include #include "commands.h" static const char *rcs_version_id = "$Id$"; static const char *ar_version_id = "1.0 " __DATE__; /* ---------------------------------------------------------------------- */ typedef enum { delete_cmd, print_cmd, replace_cmd, table_cmd, extract_cmd, no_cmd = -1 } command; /* ---------------------------------------------------------------------- ** Prototypes */ void usage( void ); void version( void ); void check_command( command *cmd, int arg ); /* ---------------------------------------------------------------------- ** Print a usage message and exit. */ void usage( void ) { printf( "ar [dprtx][cuv] archive [file ...]\n" ); exit( EXIT_FAILURE ); } /* ---------------------------------------------------------------------- ** Print a version message and exit. */ void version( void ) { printf( "ar (POSIX 1003.2-1992), version %s\n", ar_version_id ); printf( "by Chris Herborth (chrish@qnx.com)\n" ); printf( "This code has been donated to the BeOS developer community.\n" ); return; } /* ---------------------------------------------------------------------- ** Set *cmd to the appropriate command enum if it isn't already set. */ void check_command( command *cmd, int arg ) { if( *cmd == no_cmd ) { switch( arg ) { case 'd': *cmd = delete_cmd; break; case 'p': *cmd = print_cmd; break; case 'r': *cmd = replace_cmd; break; case 't': *cmd = table_cmd; break; case 'x': *cmd = extract_cmd; break; } } else { printf( "ar: you can only specify one command at a time\n" ); usage(); } } /* ---------------------------------------------------------------------- ** Mainline */ int main( int argc, char **argv ) { command cmd = no_cmd; int verbose_flag = 0; int create_flag = 0; /* these two only apply to replace_cmd */ int update_flag = 0; int c = 0; char *archive_name; char **files_list; int num_files; int idx; status_t retval; /* The argument parsing is a little hairier than usual; the idea is ** to support the POSIX 1003.2 style of arguments, and the much more ** common traditional argument style. */ if( argc < 3 ) { printf( "ar: invalid number of arguments\n" ); usage(); } /* Do we have traditional or POSIX-style args? */ if( argv[1][0] == '-' ) { while( ( c = getopt( argc, argv, "dprtxcuvV" ) ) != EOF ) { switch( c ) { case 'd': /* fall-through */ case 'p': /* fall-through */ case 'r': /* fall-through */ case 't': /* fall-through */ case 'x': /* fall-through */ check_command( &cmd, c ); break; case 'v': verbose_flag = 1; break; case 'c': if( cmd != no_cmd && cmd != replace_cmd ) { printf( "ar: invalid option, -c\n" ); usage(); } else { create_flag = 1; } break; case 'u': if( cmd != no_cmd && cmd != replace_cmd ) { printf( "ar: invalid option, -u\n" ); usage(); } else { update_flag = 1; } break; case 'V': version(); break; default: printf( "ar: invalid option, -%c\n", c ); usage(); break; } idx = optind; } } else { /* In the traditional way, arguments ar: ** ** argv[1] = [dprtx][cuv] ** argv[2] = archive ** argv[...] = file ... **/ char *ptr; idx = 1; ptr = argv[idx++]; while( *ptr != '\0' ) { switch( *ptr ) { case 'd': /* fall-through */ case 'p': /* fall-through */ case 'r': /* fall-through */ case 't': /* fall-through */ case 'x': /* fall-through */ check_command( &cmd, *ptr ); break; case 'v': verbose_flag = 1; break; case 'c': if( cmd != no_cmd && cmd != replace_cmd ) { printf( "ar: invalid option, -c\n" ); usage(); } else { create_flag = 1; } break; case 'u': if( cmd != no_cmd && cmd != replace_cmd ) { printf( "ar: invalid option, -u\n" ); usage(); } else { update_flag = 1; } break; case 'V': version(); break; default: printf( "ar: invalid option, -%c\n", c ); usage(); break; } ptr++; } } /* Next arg is the archive. */ archive_name = argv[idx++]; /* Next are the files. */ num_files = argc - idx; if( num_files == 0 ) { files_list = NULL; } else { int ctr = 0; files_list = (char **)malloc( ( num_files + 1 ) * sizeof( char * ) ); while( idx < argc ) { files_list[ctr++] = argv[idx++]; } files_list[idx] = NULL; } /* Now we can attempt to manipulate the archive. */ switch( cmd ) { case delete_cmd: retval = do_delete( archive_name, files_list, verbose_flag ); break; case print_cmd: retval = do_print( archive_name, files_list, verbose_flag ); break; case replace_cmd: retval = do_replace( archive_name, files_list, verbose_flag, create_flag, update_flag ); break; case table_cmd: retval = do_table( archive_name, files_list, verbose_flag ); break; case extract_cmd: retval = do_extract( archive_name, files_list, verbose_flag ); break; default: printf( "ar: you must specify a command\n" ); usage(); break; } /* Check the return value. */ switch( retval ) { case B_OK: break; case B_FILE_NOT_FOUND: printf( "can't open the file %s\n", archive_name ); return EXIT_FAILURE; break; case B_IO_ERROR: printf( "can't read from %s\n", archive_name ); return EXIT_FAILURE; break; case B_BAD_VALUE: printf( "invalid magic word\n" ); return EXIT_FAILURE; break; case B_MISMATCHED_VALUES: printf( "invalid processor value, or magicflags, or version\n" ); return EXIT_FAILURE; break; case B_NO_MEMORY: printf( "unable to allocate memory\n" ); return EXIT_FAILURE; break; case B_ERROR: printf( "error during processing\n" ); return EXIT_FAILURE; default: printf( "unknown error: %ld\n", retval ); return EXIT_FAILURE; break; } return EXIT_SUCCESS; }