xref: /haiku/src/bin/chop.c (revision eda1d21fde1e6e1c451bc96ace0610f822111981)
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
617049c45SAxel Dörfler //  by the OpenBeOS 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 
243*eda1d21fSPhilippe Saint-Pierre 	// close up the last output file if it's still open
244*eda1d21fSPhilippe Saint-Pierre 	if (!open_next_file)
24517049c45SAxel Dörfler 		close(fdout);
24617049c45SAxel Dörfler }
247