Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / bin / chop.c
blob964307afdd27963155ab2ad7a1ecd605852009fd
1 // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
2 //
3 // Copyright (c) 2001-2003, OpenBeOS
4 //
5 // This software is part of the OpenBeOS distribution and is covered
6 // by the OpenBeOS license.
7 //
8 //
9 // File: chop.c
10 // Author: Daniel Reinhold (danielre@users.sf.net)
11 // Description: splits one file into a collection of smaller files
13 // Notes:
14 // This program was written such that it would have identical output as from
15 // the original chop program included with BeOS R5. However, there are a few
16 // minor differences:
18 // a) using "chop -n" (with no other args) crashes the original version,
19 // but not this one.
21 // b) filenames are enclosed in single quotes here, but are not in the original.
22 // It is generally better to enquote filenames for error messages so that
23 // problems with the name (e.g extra space chars) can be more easily detected.
25 // c) this version checks for validity of the input file (including file size)
26 // before there is any attempt to open it -- this changes the error output
27 // slightly from the original in some situations. It can also prevent some
28 // weirdness. For example, the original version will take a command such as:
30 // chop /dev/ports/serial1
32 // and attempt to open the device. If serial1 is unused, this will actually
33 // block while waiting for data from the serial port. This version will never
34 // encounter that because the device will be found to have size 0 which will
35 // abort immediately. Since the semantics of chop don't make sense for such
36 // devices as the source, this is really the better behavior (provided that
37 // anyone ever attempts to use such strange arguments, which is unlikely).
39 // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
41 #include <OS.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <sys/stat.h>
52 void chop_file (int, char *, off_t);
53 void do_chop (char *);
54 void usage (void);
57 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
58 // globals
60 #define BLOCKSIZE 64 * 1024 // file data is read in BLOCKSIZE blocks
61 static char Block[BLOCKSIZE]; // and stored in the global Block array
63 static int KBytesPerChunk = 1400; // determines size of output files
66 void
67 usage()
69 printf("Usage: chop [-n kbyte_per_chunk] file\n");
70 printf("Splits file into smaller files named file00, file01...\n");
71 printf("Default split size is 1400k\n");
76 int
77 main(int argc, char *argv[])
79 char *arg = NULL;
80 char *first;
82 if ((argc < 2) || (argc > 4)) {
83 usage();
84 return 0;
87 first = *++argv;
89 if (strcmp(first, "--help") == 0) {
90 usage();
91 return 0;
94 if (strcmp(first, "-n") == 0) {
95 if (--argc > 1) {
96 char *num = *++argv;
98 if (!isdigit(*num))
99 printf("-n option needs a numeric argument\n");
100 else {
101 int b = atoi(num);
102 KBytesPerChunk = (b < 1 ? 1 : b);
104 if (--argc > 1)
105 arg = *++argv;
106 else
107 printf("no file specified\n");
110 else
111 printf("-n option needs a numeric argument\n");
113 else
114 arg = first;
116 if (arg)
117 do_chop(arg);
119 putchar ('\n');
120 return 0;
124 void
125 do_chop(char *fname)
127 // do some checks for validity
128 // then call chop_file() to do the actual read/writes
130 struct stat e;
131 off_t fsize;
132 int fd;
134 // input file must exist
135 if (stat(fname, &e) == -1) {
136 fprintf(stderr, "'%s': no such file or directory\n", fname);
137 return;
140 // and it must be not be a directory
141 if (S_ISDIR(e.st_mode)) {
142 fprintf(stderr, "'%s' is a directory\n", fname);
143 return;
146 // needs to be big enough such that splitting it actually does something
147 fsize = e.st_size;
148 if (fsize < (KBytesPerChunk * 1024)) {
149 fprintf(stderr, "'%s': file is already small enough\n", fname);
150 return;
153 // also, don't chop up if chunk files are already present
155 char buf[256];
157 strcpy(buf, fname);
158 strcat(buf, "00");
160 if (stat(buf, &e) >= 0) {
161 fprintf(stderr, "'%s' already exists - aborting\n", buf);
162 return;
166 // finally! chop up the file
167 fd = open(fname, O_RDONLY);
168 if (fd < 0)
169 fprintf(stderr, "can't open '%s': %s\n", fname, strerror(errno));
170 else {
171 chop_file(fd, fname, fsize);
172 close(fd);
177 void
178 chop_file(int fdin, char *fname, off_t fsize)
180 const off_t chunk_size = KBytesPerChunk * 1024; // max bytes written to any output file
182 bool open_next_file = true; // when to open a new output file
183 char fnameN[256]; // name of the current output file (file01, file02, etc.)
184 int index = 0; // used to generate the next output file name
185 int fdout = -1; // output file descriptor
187 ssize_t got; // size of the current data block -- i.e. from the last read()
188 ssize_t put; // number of bytes just written -- i.e. from the last write()
189 ssize_t needed; // how many bytes we can safely write to the current output file
190 ssize_t avail; // how many bytes we can safely grab from the current data block
191 off_t curr_written = 0; // number of bytes written to the current output file
192 off_t total_written = 0; // total bytes written out to all output files
194 char *beg = Block; // pointer to the beginning of the block data to be written out
195 char *end = Block; // end of the current block (init to beginning to force first block read)
197 printf("Chopping up %s into %d kbyte chunks\n", fname, KBytesPerChunk);
199 while (total_written < fsize) {
200 if (beg >= end) {
201 // read in another block
202 got = read(fdin, Block, BLOCKSIZE);
203 if (got <= 0)
204 break;
206 beg = Block;
207 end = Block + got - 1;
210 if (open_next_file) {
211 // start a new output file
212 sprintf(fnameN, "%s%02d", fname, index++);
214 fdout = open(fnameN, O_WRONLY|O_CREAT);
215 if (fdout < 0) {
216 fprintf(stderr, "unable to create chunk file '%s': %s\n", fnameN, strerror(errno));
217 return;
219 curr_written = 0;
220 open_next_file = false;
223 needed = chunk_size - curr_written;
224 avail = end - beg + 1;
225 if (needed > avail)
226 needed = avail;
228 if (needed > 0) {
229 put = write(fdout, beg, needed);
230 beg += put;
232 curr_written += put;
233 total_written += put;
236 if (curr_written >= chunk_size) {
237 // the current output file is full
238 close(fdout);
239 open_next_file = true;
243 // close up the last output file if it's still open
244 if (!open_next_file)
245 close(fdout);