117049c45SAxel Dörfler // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
217049c45SAxel Dörfler //
3*2ca13760SColdfirex // Copyright (c) 2001-2003, Haiku
417049c45SAxel Dörfler //
5*2ca13760SColdfirex // This software is part of the Haiku distribution and is covered
6b6f76ebeSAugustin 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
usage()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
main(int argc,char * argv[])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
do_chop(char * fname)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
chop_file(int fdin,char * fname,off_t fsize)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