117049c45SAxel Dörfler // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ 217049c45SAxel Dörfler // 317049c45SAxel Dörfler // Copyright (c) 2001-2003, OpenBeOS 417049c45SAxel Dörfler // 517049c45SAxel Dörfler // This software is part of the OpenBeOS distribution and is covered 6*b6f76ebeSAugustin Cavalier // by the MIT License. 717049c45SAxel Dörfler // 817049c45SAxel Dörfler // 917049c45SAxel Dörfler // File: chop.c 1017049c45SAxel Dörfler // Author: Daniel Reinhold (danielre@users.sf.net) 1117049c45SAxel Dörfler // Description: splits one file into a collection of smaller files 1217049c45SAxel Dörfler // 1317049c45SAxel Dörfler // Notes: 1417049c45SAxel Dörfler // This program was written such that it would have identical output as from 1517049c45SAxel Dörfler // the original chop program included with BeOS R5. However, there are a few 1617049c45SAxel Dörfler // minor differences: 1717049c45SAxel Dörfler // 1817049c45SAxel Dörfler // a) using "chop -n" (with no other args) crashes the original version, 1917049c45SAxel Dörfler // but not this one. 2017049c45SAxel Dörfler // 2117049c45SAxel Dörfler // b) filenames are enclosed in single quotes here, but are not in the original. 2217049c45SAxel Dörfler // It is generally better to enquote filenames for error messages so that 2317049c45SAxel Dörfler // problems with the name (e.g extra space chars) can be more easily detected. 2417049c45SAxel Dörfler // 2517049c45SAxel Dörfler // c) this version checks for validity of the input file (including file size) 2617049c45SAxel Dörfler // before there is any attempt to open it -- this changes the error output 2717049c45SAxel Dörfler // slightly from the original in some situations. It can also prevent some 2817049c45SAxel Dörfler // weirdness. For example, the original version will take a command such as: 2917049c45SAxel Dörfler // 3017049c45SAxel Dörfler // chop /dev/ports/serial1 3117049c45SAxel Dörfler // 3217049c45SAxel Dörfler // and attempt to open the device. If serial1 is unused, this will actually 3317049c45SAxel Dörfler // block while waiting for data from the serial port. This version will never 3417049c45SAxel Dörfler // encounter that because the device will be found to have size 0 which will 3517049c45SAxel Dörfler // abort immediately. Since the semantics of chop don't make sense for such 3617049c45SAxel Dörfler // devices as the source, this is really the better behavior (provided that 3717049c45SAxel Dörfler // anyone ever attempts to use such strange arguments, which is unlikely). 3817049c45SAxel Dörfler // 3917049c45SAxel Dörfler // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ 4017049c45SAxel Dörfler 4117049c45SAxel Dörfler #include <OS.h> 4217049c45SAxel Dörfler #include <stdio.h> 4317049c45SAxel Dörfler #include <stdlib.h> 4417049c45SAxel Dörfler #include <string.h> 4517049c45SAxel Dörfler #include <ctype.h> 4617049c45SAxel Dörfler #include <errno.h> 4717049c45SAxel Dörfler #include <fcntl.h> 4817049c45SAxel Dörfler #include <unistd.h> 4917049c45SAxel Dörfler #include <sys/stat.h> 5017049c45SAxel Dörfler 5117049c45SAxel Dörfler 5217049c45SAxel Dörfler void chop_file (int, char *, off_t); 5317049c45SAxel Dörfler void do_chop (char *); 5417049c45SAxel Dörfler void usage (void); 5517049c45SAxel Dörfler 5617049c45SAxel Dörfler 5717049c45SAxel Dörfler // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5817049c45SAxel Dörfler // globals 5917049c45SAxel Dörfler 6017049c45SAxel Dörfler #define BLOCKSIZE 64 * 1024 // file data is read in BLOCKSIZE blocks 6117049c45SAxel Dörfler static char Block[BLOCKSIZE]; // and stored in the global Block array 6217049c45SAxel Dörfler 6317049c45SAxel Dörfler static int KBytesPerChunk = 1400; // determines size of output files 6417049c45SAxel Dörfler 6517049c45SAxel Dörfler 6617049c45SAxel Dörfler void 6717049c45SAxel Dörfler usage() 6817049c45SAxel Dörfler { 6917049c45SAxel Dörfler printf("Usage: chop [-n kbyte_per_chunk] file\n"); 7017049c45SAxel Dörfler printf("Splits file into smaller files named file00, file01...\n"); 7117049c45SAxel Dörfler printf("Default split size is 1400k\n"); 7217049c45SAxel Dörfler } 7317049c45SAxel Dörfler 7417049c45SAxel Dörfler 7517049c45SAxel Dörfler 7617049c45SAxel Dörfler int 7717049c45SAxel Dörfler main(int argc, char *argv[]) 7817049c45SAxel Dörfler { 7917049c45SAxel Dörfler char *arg = NULL; 8017049c45SAxel Dörfler char *first; 8117049c45SAxel Dörfler 8217049c45SAxel Dörfler if ((argc < 2) || (argc > 4)) { 8317049c45SAxel Dörfler usage(); 8417049c45SAxel Dörfler return 0; 8517049c45SAxel Dörfler } 8617049c45SAxel Dörfler 8717049c45SAxel Dörfler first = *++argv; 8817049c45SAxel Dörfler 8917049c45SAxel Dörfler if (strcmp(first, "--help") == 0) { 9017049c45SAxel Dörfler usage(); 9117049c45SAxel Dörfler return 0; 9217049c45SAxel Dörfler } 9317049c45SAxel Dörfler 9417049c45SAxel Dörfler if (strcmp(first, "-n") == 0) { 9517049c45SAxel Dörfler if (--argc > 1) { 9617049c45SAxel Dörfler char *num = *++argv; 9717049c45SAxel Dörfler 9817049c45SAxel Dörfler if (!isdigit(*num)) 9917049c45SAxel Dörfler printf("-n option needs a numeric argument\n"); 10017049c45SAxel Dörfler else { 10117049c45SAxel Dörfler int b = atoi(num); 10217049c45SAxel Dörfler KBytesPerChunk = (b < 1 ? 1 : b); 10317049c45SAxel Dörfler 10417049c45SAxel Dörfler if (--argc > 1) 10517049c45SAxel Dörfler arg = *++argv; 10617049c45SAxel Dörfler else 10717049c45SAxel Dörfler printf("no file specified\n"); 10817049c45SAxel Dörfler } 10917049c45SAxel Dörfler } 11017049c45SAxel Dörfler else 11117049c45SAxel Dörfler printf("-n option needs a numeric argument\n"); 11217049c45SAxel Dörfler } 11317049c45SAxel Dörfler else 11417049c45SAxel Dörfler arg = first; 11517049c45SAxel Dörfler 11617049c45SAxel Dörfler if (arg) 11717049c45SAxel Dörfler do_chop(arg); 11817049c45SAxel Dörfler 11917049c45SAxel Dörfler putchar ('\n'); 12017049c45SAxel Dörfler return 0; 12117049c45SAxel Dörfler } 12217049c45SAxel Dörfler 12317049c45SAxel Dörfler 12417049c45SAxel Dörfler void 12517049c45SAxel Dörfler do_chop(char *fname) 12617049c45SAxel Dörfler { 12717049c45SAxel Dörfler // do some checks for validity 12817049c45SAxel Dörfler // then call chop_file() to do the actual read/writes 12917049c45SAxel Dörfler 13017049c45SAxel Dörfler struct stat e; 13117049c45SAxel Dörfler off_t fsize; 13217049c45SAxel Dörfler int fd; 13317049c45SAxel Dörfler 13417049c45SAxel Dörfler // input file must exist 13517049c45SAxel Dörfler if (stat(fname, &e) == -1) { 13617049c45SAxel Dörfler fprintf(stderr, "'%s': no such file or directory\n", fname); 13717049c45SAxel Dörfler return; 13817049c45SAxel Dörfler } 13917049c45SAxel Dörfler 14017049c45SAxel Dörfler // and it must be not be a directory 14117049c45SAxel Dörfler if (S_ISDIR(e.st_mode)) { 14217049c45SAxel Dörfler fprintf(stderr, "'%s' is a directory\n", fname); 14317049c45SAxel Dörfler return; 14417049c45SAxel Dörfler } 14517049c45SAxel Dörfler 14617049c45SAxel Dörfler // needs to be big enough such that splitting it actually does something 14717049c45SAxel Dörfler fsize = e.st_size; 14817049c45SAxel Dörfler if (fsize < (KBytesPerChunk * 1024)) { 14917049c45SAxel Dörfler fprintf(stderr, "'%s': file is already small enough\n", fname); 15017049c45SAxel Dörfler return; 15117049c45SAxel Dörfler } 15217049c45SAxel Dörfler 15317049c45SAxel Dörfler // also, don't chop up if chunk files are already present 15417049c45SAxel Dörfler { 15517049c45SAxel Dörfler char buf[256]; 15617049c45SAxel Dörfler 15717049c45SAxel Dörfler strcpy(buf, fname); 15817049c45SAxel Dörfler strcat(buf, "00"); 15917049c45SAxel Dörfler 16017049c45SAxel Dörfler if (stat(buf, &e) >= 0) { 16117049c45SAxel Dörfler fprintf(stderr, "'%s' already exists - aborting\n", buf); 16217049c45SAxel Dörfler return; 16317049c45SAxel Dörfler } 16417049c45SAxel Dörfler } 16517049c45SAxel Dörfler 16617049c45SAxel Dörfler // finally! chop up the file 16717049c45SAxel Dörfler fd = open(fname, O_RDONLY); 16817049c45SAxel Dörfler if (fd < 0) 16917049c45SAxel Dörfler fprintf(stderr, "can't open '%s': %s\n", fname, strerror(errno)); 17017049c45SAxel Dörfler else { 17117049c45SAxel Dörfler chop_file(fd, fname, fsize); 17217049c45SAxel Dörfler close(fd); 17317049c45SAxel Dörfler } 17417049c45SAxel Dörfler } 17517049c45SAxel Dörfler 17617049c45SAxel Dörfler 17717049c45SAxel Dörfler void 17817049c45SAxel Dörfler chop_file(int fdin, char *fname, off_t fsize) 17917049c45SAxel Dörfler { 18017049c45SAxel Dörfler const off_t chunk_size = KBytesPerChunk * 1024; // max bytes written to any output file 18117049c45SAxel Dörfler 18217049c45SAxel Dörfler bool open_next_file = true; // when to open a new output file 18317049c45SAxel Dörfler char fnameN[256]; // name of the current output file (file01, file02, etc.) 18417049c45SAxel Dörfler int index = 0; // used to generate the next output file name 18517049c45SAxel Dörfler int fdout = -1; // output file descriptor 18617049c45SAxel Dörfler 18717049c45SAxel Dörfler ssize_t got; // size of the current data block -- i.e. from the last read() 18817049c45SAxel Dörfler ssize_t put; // number of bytes just written -- i.e. from the last write() 18917049c45SAxel Dörfler ssize_t needed; // how many bytes we can safely write to the current output file 19017049c45SAxel Dörfler ssize_t avail; // how many bytes we can safely grab from the current data block 19117049c45SAxel Dörfler off_t curr_written = 0; // number of bytes written to the current output file 19217049c45SAxel Dörfler off_t total_written = 0; // total bytes written out to all output files 19317049c45SAxel Dörfler 19417049c45SAxel Dörfler char *beg = Block; // pointer to the beginning of the block data to be written out 19517049c45SAxel Dörfler char *end = Block; // end of the current block (init to beginning to force first block read) 19617049c45SAxel Dörfler 19717049c45SAxel Dörfler printf("Chopping up %s into %d kbyte chunks\n", fname, KBytesPerChunk); 19817049c45SAxel Dörfler 19917049c45SAxel Dörfler while (total_written < fsize) { 20017049c45SAxel Dörfler if (beg >= end) { 20117049c45SAxel Dörfler // read in another block 20217049c45SAxel Dörfler got = read(fdin, Block, BLOCKSIZE); 20317049c45SAxel Dörfler if (got <= 0) 20417049c45SAxel Dörfler break; 20517049c45SAxel Dörfler 20617049c45SAxel Dörfler beg = Block; 20717049c45SAxel Dörfler end = Block + got - 1; 20817049c45SAxel Dörfler } 20917049c45SAxel Dörfler 21017049c45SAxel Dörfler if (open_next_file) { 21117049c45SAxel Dörfler // start a new output file 21217049c45SAxel Dörfler sprintf(fnameN, "%s%02d", fname, index++); 21317049c45SAxel Dörfler 21417049c45SAxel Dörfler fdout = open(fnameN, O_WRONLY|O_CREAT); 21517049c45SAxel Dörfler if (fdout < 0) { 21617049c45SAxel Dörfler fprintf(stderr, "unable to create chunk file '%s': %s\n", fnameN, strerror(errno)); 21717049c45SAxel Dörfler return; 21817049c45SAxel Dörfler } 21917049c45SAxel Dörfler curr_written = 0; 22017049c45SAxel Dörfler open_next_file = false; 22117049c45SAxel Dörfler } 22217049c45SAxel Dörfler 22317049c45SAxel Dörfler needed = chunk_size - curr_written; 22417049c45SAxel Dörfler avail = end - beg + 1; 22517049c45SAxel Dörfler if (needed > avail) 22617049c45SAxel Dörfler needed = avail; 22717049c45SAxel Dörfler 22817049c45SAxel Dörfler if (needed > 0) { 22917049c45SAxel Dörfler put = write(fdout, beg, needed); 23017049c45SAxel Dörfler beg += put; 23117049c45SAxel Dörfler 23217049c45SAxel Dörfler curr_written += put; 23317049c45SAxel Dörfler total_written += put; 23417049c45SAxel Dörfler } 23517049c45SAxel Dörfler 23617049c45SAxel Dörfler if (curr_written >= chunk_size) { 23717049c45SAxel Dörfler // the current output file is full 23817049c45SAxel Dörfler close(fdout); 23917049c45SAxel Dörfler open_next_file = true; 24017049c45SAxel Dörfler } 24117049c45SAxel Dörfler } 24217049c45SAxel Dörfler 243eda1d21fSPhilippe Saint-Pierre // close up the last output file if it's still open 244eda1d21fSPhilippe Saint-Pierre if (!open_next_file) 24517049c45SAxel Dörfler close(fdout); 24617049c45SAxel Dörfler } 247