/* * esplit -- enhanced split * (tarアーカイブを分割、出力をパイプに) * * revision history: * 0.0: Dec. 23, 2006 by Dai ISHIJIMA (as psplit.c) * 0.1: Jun. 3, 2007 (as nsplit.c) * 0.2: Jun. 10, 2007 (as esplit.c) * * usage: * esplit -bytes -d# -c# -o file/pipe * * options: * -bytes 分割するバイト数を指定 * -o 出力 出力先を指定。'|' で始まる場合はパイプに食わす * -d# 出力先の '%' が数字#桁に * -c# 出力先の '%' がアルファベット#桁に * -t tar形式を無視 (tarアーカイブのファイル境界で分割しない) * * example: * % tar cf - . | gzip --fast | esplit -512m -c2 -o 'hoge.%.tgz' * カレントディレクトリをtarで固めて圧縮して512Mバイト単位に分割 * (tar cf - . | gzip --fast | split -b 512m - とほぼ等価) * * % tar cf - . | esplit -512m -d3 -o '| gzip --fast > hoge.%.tgz' * カレントディレクトリをtarで固めて、512Mバイト単位に分割して * 圧縮する * * split(1)コマンドは、ファイルを一定のサイズに分割することはできます。 * しかし、分割後のデータをパイプに食わしたりすることはできません。 * また、tarアーカイブの構造を認識して、ファイル境界で分割することも * できません。このプログラムは、出力をパイプに食わしたり、アーカイブ * の構造を認識して、適当な位置でファイルを分割することができます。 * * [1]% tar cf - . | gzip --fast | split -b 100k - foo.tgz. * * とすると、カレントディレクトリをtarで固めて圧縮し、foo.tgz.aa,... * というファイルに分割することができます。しかし、分割されたアーカイブ * が壊れてしまった場合などは、そのアーカイブ以降のデータを復元できません。 * * [2]% tar cf - . | esplit -100k -c2 -o '| gzip --fast > foo.tgz.%' * * とすると、分割されたアーカイブを個別に扱うことができます。つまり、 * [1] で作成した foo.tgz.ab は foo.tgz.aa なしに抽出 (解凍) すること * ができませんが、[2] で作成したファイルは tar tzvf foo.tgz.ab など * で処理することが可能になります。 */ #include #include #define YES 1 #define NO 0 #define EOS '\0' #define shift --argc; ++argv char *prog; #define IS_PIPE '|' #define IS_TAR 't' #define UNKNOWN 'u' #define NOT_TAR 0 #define SEQUENCE '%' #define D_BASE 10 #define C_BASE 26 #define TBLKSIZ 512 #define NAMSIZ 100 #define MODSIZ 8 #define UIDSIZ 8 #define GIDSIZ 8 #define SIZSIZ 12 #define MTMSIZ 12 #define SUMSIZ 8 /* tar形式のヘッダ情報 (古い? ;^^) */ typedef union { unsigned char buf[TBLKSIZ]; struct header { char name[NAMSIZ]; char mode[MODSIZ]; char uid[UIDSIZ]; char gid[GIDSIZ]; char size[SIZSIZ]; char mtime[MTMSIZ]; char cksum[SUMSIZ]; char linkflag; char linknum[NAMSIZ]; } dbuf; } tblock_t; /* 新しい出力先の名称 */ char *newname(int n, char *s, char *format) { static char t[BUFSIZ]; int nseq; int pos, m; int radix, start; nseq = 2; if (isdigit(format[1])) { nseq = format[1] - '0'; } pos = 0; while ((*s != EOS) && (pos - nseq < BUFSIZ - 1)) { if (*s == SEQUENCE) { if (*(s + 1) != SEQUENCE) { break; } else { ++s; } } t[pos] = *s; ++pos; ++s; } m = 0; if (*s == SEQUENCE) { ++s; radix = D_BASE; start = '0'; if (format[0] == 'c') { radix = C_BASE; start = 'a'; } while (m < nseq) { t[pos + nseq - m - 1] = (n % radix) + start; n /= radix; ++m; } } pos += m; while ((*s != EOS) && (pos < BUFSIZ - 1)) { t[pos] = *s; ++pos; ++s; } t[pos] = EOS; return(t); } /* 新しい出力先を開いてポインタを返す */ FILE *openfp(char *name) { FILE *fp; if (*name == IS_PIPE) { ++name; while (isspace(*name)) { ++name; } if ((fp = popen(name, "w")) == NULL) { fprintf(stderr, "%s: can't open pipe: %s\n", prog, name); exit(1); } } else { if ((fp = fopen(name, "w")) == NULL) { fprintf(stderr, "%s: can't open file: %s\n", prog, name); exit(1); } } return(fp); } /* 出力先を閉じる */ void closefp(FILE *fp, char *name) { if (name[0] == IS_PIPE) { ++name; while (isspace(*name)) { ++name; } if (pclose(fp) != 0) { fprintf(stderr, "%s: can't close pipe: %s\n", prog, name); } } else { if (fclose(fp) != 0) { fprintf(stderr, "%s: can't close file: %s\n", prog, name); } } } /* 入力はtar形式か? */ int istar(tblock_t *tblock) { int i; int cksum; int calc; tblock_t bufcopy; cksum = 0; for (i = 0; i < SUMSIZ; i++) { if (tblock->dbuf.cksum[i] == EOS) { break; } cksum = cksum * 8 + tblock->dbuf.cksum[i] - '0'; } for (i = 0; i < TBLKSIZ; i++) { bufcopy.buf[i] = tblock->buf[i]; } for (i = 0; i < SUMSIZ; i++) { bufcopy.dbuf.cksum[i] = ' '; } calc = 0; for (i = 0; i < TBLKSIZ; i++) { calc += bufcopy.buf[i]; } #ifdef EBUG fprintf(stderr, "%s: cksum %8o, %8o\n", prog, cksum, calc); #endif if (calc == cksum) { return(IS_TAR); } return(NOT_TAR); } /* 読み込んだブロックがNULLでない */ int nonull(unsigned char *buf) { int i; i = 0; for (i = 0; i < TBLKSIZ - 1; i++) { if (buf[i] != 0) { return(buf[i]); } } return(buf[i]); } /* 読み込んだブロックはNULLか */ int nullblk(unsigned char *buf) { int i; i = 0; for (i = 0; i < TBLKSIZ - 1; i++) { if (buf[i] != 0) { return(0); } } return(1); } /* tar形式のファイル長さを返す */ unsigned long tarlength(tblock_t *tblock) { int i; unsigned long length; length = 0; for (i = 0; i < SIZSIZ; i++) { if (tblock->dbuf.size[i] == EOS) { break; } length = length * 8 + tblock->dbuf.size[i] - '0'; } return(length); } /* ファイルを分割する */ void split(FILE *fp, unsigned long length, char *output, char *format, int mode, int verbose) { static int nfil = 0; char *filnam; FILE *ofp; unsigned long osiz; unsigned long rsiz; unsigned long wsiz; unsigned long tlen; unsigned long tout; tblock_t tblock; nfil = 0; ofp = NULL; osiz = 0; tout = 0; rsiz = 0; while (!feof(fp)) { if (rsiz == 0) { /* 入力バッファが空なら読み込み */ rsiz = fread(tblock.buf, 1, TBLKSIZ, fp); if (verbose > 3) { fprintf(stderr, "%s: read %ld bytes\n", prog, rsiz); } } if (ofp == NULL) { /* オープンされていなければ出力を開く */ filnam = newname(nfil, output, format); ofp = openfp(filnam); osiz = 0; } if ((mode != NOT_TAR) && (istar(&tblock))) { /* tarモードならファイル一つ分を読み書き */ mode = IS_TAR; tlen = tarlength(&tblock); if (verbose > 2) { fprintf(stderr, "%s: tar: %s (%ld)\n", prog, tblock.dbuf.name, tlen); } /* とりあえずヘッダ部分を出力 */ if ((wsiz = fwrite(tblock.buf, 1, rsiz, ofp)) != rsiz) { fprintf(stderr, "%s: write to %d-th output failed at %ld,\n", prog, nfil, osiz); fprintf(stderr, " (wrote: %ld != read: %ld)\n", wsiz, rsiz); exit(1); } tout = 0; while (tout < tlen) { /* ファイルの実体があれば読み書き */ rsiz = fread(tblock.buf, 1, TBLKSIZ, fp); if (rsiz <= 0) { break; } if (verbose > 3) { fprintf(stderr, "%s: read %ld bytes\n", prog, rsiz); } if ((wsiz = fwrite(tblock.buf, 1, rsiz, ofp)) != rsiz) { fprintf(stderr, "%s: write to %d-th output failed", prog, nfil); fprintf(stderr, " at %ld,\n", osiz); fprintf(stderr, " (wrote: %ld != read: %ld)\n", wsiz, rsiz); exit(1); } osiz += wsiz; tout += wsiz; } /* ファイルの後ろにNULLブロックが続いていれば読み書き */ rsiz = fread(tblock.buf, 1, TBLKSIZ, fp); while (nullblk(tblock.buf)) { if ((wsiz = fwrite(tblock.buf, 1, rsiz, ofp)) != rsiz) { fprintf(stderr, "%s: write to %d-th output failed", prog, nfil); fprintf(stderr, " at %ld,\n", osiz); fprintf(stderr, " (wrote: %ld != read: %ld)\n", wsiz, rsiz); exit(1); } osiz += wsiz; tout += wsiz; if ((rsiz = fread(tblock.buf, 1, TBLKSIZ, fp)) <= 0) { break; } } mode = UNKNOWN; } else { /* tarモードでないときはブロックごとに読み書き */ if ((wsiz = fwrite(tblock.buf, 1, rsiz, ofp)) != rsiz) { fprintf(stderr, "%s: write to %d-th output failed at %ld,\n", prog, nfil, osiz); fprintf(stderr, " (wrote: %ld != read: %ld)\n", wsiz, rsiz); exit(1); } osiz += wsiz; rsiz = 0; } if (osiz >= length) { /* 出力済みサイズが指定長さを越えていたらファイルを閉じる */ if (verbose > 0) { fprintf(stderr, "%s: wrote %ld bytes on %s\n", prog, osiz, filnam); } closefp(ofp, filnam); ofp = NULL; ++nfil; } } if (ofp != NULL) { if (verbose > 0) { fprintf(stderr, "%s: wrote %ld bytes on %s\n", prog, osiz, filnam); } closefp(ofp, filnam); ++nfil; } } /* 出力サイズを得る */ unsigned long getfilsiz(char *s) { char *t; unsigned long n; t = s; n = 0; while (isdigit(*s)) { n = n * 10; n += (*s - '0'); ++s; } if ((*s == 'k') || (*s == 'K')) { n *= 1024L; } else if ((*s == 'm') || (*s == 'M')) { n *= (1024L * 1024L); } else if ((*s == 'g') || (*s == 'G')) { n *= (1024L * 1024L * 1024L); } #if 0 else if ((*s == 't') || (*s == 'T')) { n *= (1024L * 1024L * 1024L * 1024L); } #endif else if (*s != EOS) { fprintf(stderr, "%s: suffix '%c' unknown in '%s'\n", prog, *s, t); } return(n); } /* 使いかたの表示 */ void usage(void) { fprintf(stderr, "Usage: %s -size -o output -d# -c# -v\n", prog); exit(1); } int main(int argc, char *argv[]) { char *output; unsigned long length; char *format; int verbose; int mode; FILE *fp; prog = *argv; shift; length = 1024; verbose = 0; output = "| gzip --fast > %.gz"; format = "c2"; verbose = 0; mode = UNKNOWN; while ((argc > 0) && (argv[0][0] == '-') && (argv[0][1] != EOS)) { if (isdigit(argv[0][1])) { length = getfilsiz(&argv[0][1]); if (length <= 0) { fprintf(stderr, "%s: length must be lager than 0 (%ld)\n", prog, length); exit(1); } } else if (argv[0][1] == 'o') { shift; output = *argv; } else if (argv[0][1] == 'v') { if (isdigit(argv[0][2])) { verbose = argv[0][2] - '0'; } else { ++verbose; } } else if (argv[0][1] == 't') { mode = NOT_TAR; } else if (argv[0][1] == 'c') { if (isdigit(argv[0][2])) { format = &argv[0][1]; } else { format = "c2"; } } else if (argv[0][1] == 'd') { if (isdigit(argv[0][2])) { format = &argv[0][1]; } else { format = "d3"; } } else { usage(); } shift; } if (argc <= 0) { split(stdin, length, output, format, mode, verbose); } else { while (argc > 0) { if ((fp = fopen(*argv, "r")) == NULL) { fprintf(stderr, "%s: can't open %s\n", prog, *argv); exit(1); } split(fp, length, output, format, mode, verbose); shift; } } exit(0); } /* Local Variables: */ /* compile-command:"gcc -Wall -o esplit esplit.c" */ /* End: */