#include #include #include #include #include #define steq(x,y) (!strcmp(x,y)) #define stneq(x,y) (!steq(x,y)) enum { FN_SPLIT, FN_MERGE }; static char *Argv0; static char *DefaultOutName = "split_get.out"; static size_t BlockSize = 0; static int SuffixWidth = 2; static char *OutputFilename = NULL; static int Function = FN_SPLIT; static int OnlyBlockN = 0; void do_error(const char *fmat, ...); void do_help(int); int main(int argc, char **argv) { int i, j; Argv0 = argv[0]; if (argc <= 1) do_help(0); for (i=1; i < argc; ++i) { if (argv[i][0] != '-') break; if (argv[i][1] == '\0') { do_error("This program does not read from stdin.\n" "Use %s -- - to read from a file named -.\n", Argv0); } if (steq(argv[i]+1, "-")) { ++i; break; } else if (steq(argv[i]+1, "?")) do_help(0); else if (steq(argv[i]+1, "-help")) do_help(0); else if (steq(argv[i]+1, "-man")) do_help(1); else if (steq(argv[i], "-o") || steq(argv[i], "-O")) { if (i >= argc-1) { do_error("Need output filename with -o\n"); } OutputFilename = argv[++i]; } else if (steq(argv[i], "-b") || steq(argv[i], "-B")) { long temp; char *end; if (i >= argc-1) { do_error("Need integer block size with -B\n"); } temp = strtol(argv[++i], &end, 10); if ((temp <= 0) || (end == argv[i])) { do_error("Invalid block size: '%s'\n", argv[i]); } if (*end == '\0') ; /* do nothing */ else if (steq(end, "K") || steq(end, "k")) temp *= 1000; else if (steq(end, "Ki") || steq(end, "ki")) temp *= 1024; else if (steq(end, "M") || steq(end, "m")) temp *= 1000*1000; else if (steq(end, "Mi") || steq(end, "mi")) temp *= 1024*1024; else do_error("Invalid block size: '%s' is not an integer\n", argv[i]); BlockSize = temp; } else if (steq(argv[i], "-w") || steq(argv[i], "-W")) { if (i >= argc-1) { do_error("Need integer suffix width with -W\n"); } SuffixWidth = atoi(argv[++i]); if (SuffixWidth < 1 || SuffixWidth > 10) do_error("Invalid suffix width: out of range [1..10]\n"); } else if (steq(argv[i], "-n") || steq(argv[i], "-N")) { if (i >= argc-1) { do_error("Need integer block number with -N\n"); } OnlyBlockN = atoi(argv[++i]); if (OnlyBlockN < 1) do_error("Invalid block number: <= 0\n"); } else { for (j=1; argv[i][j]; ++j) { if (argv[i][j] == 'd' || argv[i][j] == 'D') Function = FN_MERGE; else do_error("Unrecognized option(s) %s\n", argv[i]); } } } if (i == argc) do_error("No files supplied; %s --help for usage\n", Argv0); if (Function == FN_MERGE) { FILE *outfp = fopen((OutputFilename? OutputFilename: DefaultOutName), "wb"); if (outfp == NULL) do_error("Error opening file '%s' for output\n", OutputFilename); for (; i < argc; ++i) { int c; FILE *infp = fopen(argv[i], "rb"); if (infp == NULL) do_error("Error opening file '%s' for input\n", argv[i]); while ((c=getc(infp)) != EOF) putc(c, outfp); fclose(infp); } fclose(outfp); } else if (Function == FN_SPLIT) { for (; i < argc; ++i) { static char name[1000]; FILE *infp = fopen(argv[i], "rb"); FILE *outfp = NULL; int bn; size_t bi; int c; if (infp == NULL) do_error("Error opening file '%s' for input\n", argv[i]); if (OnlyBlockN > 0) { size_t target = (OnlyBlockN-1)*BlockSize; if (OutputFilename) { outfp = fopen(OutputFilename, "wb"); } else { sprintf(name, "%.990s.%0*d", argv[i], SuffixWidth, OnlyBlockN); outfp = fopen(name, "wb"); } if (outfp == NULL) do_error("Error opening output file '%s'\n", (OutputFilename? OutputFilename: name)); for (bi=0; (c=getc(infp)) != EOF; ++bi) if (bi == target) break; if (c != EOF) { for (bi=0; bi < BlockSize; ++bi) { putc(c, outfp); if ((c=getc(infp)) == EOF) break; } } } else { bi = BlockSize; for (bn=0; (c=getc(infp)) != EOF; ++bi) { if (bi == BlockSize) { if (outfp) fclose(outfp); ++bn; bi = 0; sprintf(name, "%.990s.%0*d", argv[i], SuffixWidth, bn); outfp = fopen(name, "wb"); if (outfp == NULL) do_error("Error opening output file '%s'\n", name); } putc(c, outfp); } } fclose(outfp); fclose(infp); } } return 0; } void do_error(const char *fmat, ...) { va_list ap; printf("%s: ", Argv0); va_start(ap, fmat); vprintf(fmat, ap); va_end(ap); exit(EXIT_FAILURE); } void do_help(int man) { if (man) goto man; puts("split_get [-?] [-Dd] [-o name] [-B n] [-W n] [-N n] filenames"); puts("Splits binary files into small chunks and re-merges them."); puts(" -d: Decode; merge named chunks into output file"); printf(" -o: Specify an output file (default '%s')\n", DefaultOutName); puts(" -B n: Split into n-byte chunks (suffixed .1, .2,...)"); puts(" -w n: Use n-character suffixes (e.g. -n 3 gives .001,...)"); puts(" -n n: Extract n-th block only."); exit(0); man: puts("split_get: Splits and merges binary files for transport."); puts(""); puts(" This program can be used to split a binary file into several"); puts(" chunks for transport across a slow network connection or"); puts(" a number of floppy disks. It can then re-assemble the"); puts(" file at the other end of the line."); puts(" The -D option tells the program to merge the supplied files"); puts(" into one big file, in the order the files are specified on"); puts(" the command line. Note that most operating systems will"); puts(" alphabetize the results of using wildcard syntax like"); puts(" \"-D bigfile.*\", thus performing the merge correctly."); puts(" Without the -D option, the default mode of action is to split"); puts(" the supplied file(s) into several smaller files."); puts(" The -B parameter controls the \"block size\" of the program's"); puts(" output. For example, \"-B 100\" tells the program to"); puts(" split the input file(s) into 100-byte chunks. The -B"); puts(" parameter can also be an integer followed by the suffix"); puts(" Ki or Mi, indicating kibibytes (1024 bytes) or mebibytes"); puts(" (1048576 bytes); or K or M, indicating decimal kilobytes"); puts(" or megabytes (1000 and 1000000 bytes, respectively)."); puts(" The -W parameter controls the width of the suffix attached"); puts(" to each output block. Normally, the width of the suffix"); puts(" is 2, which produces output files named infile.01,"); puts(" infile.02, and so on up to infile.99. At that point the"); puts(" number no longer fits in two digits, so the suffix expands"); puts(" to infile.100, infile.101,... infile.999. This"); puts(" will cause problems with alphabetizing the blocks, since"); puts(" in a traditional string comparison, \"infile.100\" sorts"); puts(" between \"infile.10\" and \"infile.11\". Setting -W 3 or"); puts(" larger will solve this problem."); puts(" The -N parameter can be used to tell the program to extract"); puts(" only one specific block from the file; e.g., -N 2 -B 10"); puts(" will extract the second 10-byte block from the input file"); puts(" and place it in an output file named \"infile.02\". This"); puts(" option may be useful when splitting a large file on a"); puts(" system with limited disk space -- you can extract and"); puts(" transport each block individually without having to store"); puts(" two copies of the entire file on disk at once."); puts(" The -o parameter tells the program where to store the output"); puts(" of its -D or -N modes. If no -o filename is provided,"); puts(" the program will use its own default filename. -o has no"); puts(" effect on the program if used without either -D or -N."); puts(""); exit(0); }