Article View: comp.sys.ibm.pc.misc
Article #11940Shatter (Was Re: How can I split a large file???????)
From: Ajay Shah
Date: Mon, 09 Dec 1991 00:50
Date: Mon, 09 Dec 1991 00:50
357 lines
11622 bytes
11622 bytes
With permission from the author (Shuvam Misra, machgrp@dhruv.ernet.in),
I post this source code. It might not work on childrens toys.
/* This is the source code for a program (should compile and run without
any problems) which does basically what split does, but accepts binary
files, and takes size in bytes, not lines. Interestingly, the only
reason to use split in that case is to break files on line
boundaries. This is done by my program too. So you can get rid of
split for almost all applications, and use shatter. Big relief to get
one program with one interface, for both ascii and binary files.
The program is very easy to use, and has no optional arguments. The
usage message will tell you all.
Use the command
shatter 70k bash bash.tar.Z
This will create files called bash.000, bash.001, bash.002, etc. each
of size approximately 70KB, plus-minus 100 bytes.
Here comes the source code. It works. You can always verify the
results by following up the shattering by
cat bash.[0-9]* | cmp bash.tar.Z -
*/
/*
* FILE:
* shatter.c
*
* CONTENTS:
* a simple program to split a binary or ascii file (it doesn't care
* which) into pieces of a given size.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
extern char *malloc();
#define TRUE 1
#define FALSE 0
#define errmsg(msg) fprintf(stderr, "%s: %s\n", argv[0], msg)
#define KILO 'k'
#define MEGA 'm'
#define LINESIZE 100 /* largest line to backtrack */
#define FILENAMEMAX 100 /* largest filename */
#define PIECESMAX 1000 /* so many pieces and not more */
#define print_usage() \
{ \
fprintf(stderr, "usage: %s piecesize prefix filename\n", argv[0]); \
fprintf(stderr, "\tpiecesize can have k, m as suffix\n"); \
fprintf(stderr, "\tfilename can be '-' to indicate stdin\n"); \
}
main(argc, argv)
int argc;
char *argv[];
{
int piecesize, /* size of one piece in bytes */
scalechar, /* character to indicate KB or MB */
infd, /* input file descriptor */
bufoffset, /* offset into buf to use for next read */
is_special = FALSE, /* is the input file descriptor a fifo? */
piecenumber; /* count of the piece being written out */
char *buf; /* buffer for readwrite */
char *infilename; /* input file name: for errmsgs */
if (argc != 4)
{
print_usage();
return 1;
}
if ((piecesize = atoi(argv[1])) <= 0)
{
errmsg("piecesize must be positive integer");
print_usage();
return 1;
}
scalechar = tolower(argv[1][strlen(argv[1]) - 1]);
if (scalechar == KILO) piecesize *= 1024;
if (scalechar == MEGA) piecesize *= (1024*1024);
/*
* As the algorithm stands today, if piece size is less than LINESIZE,
* the algo will not work.
*/
if (piecesize <= (2*LINESIZE)) /* keep a safe margin */
{
fprintf(stderr, "piece size = %d\n", piecesize);
errmsg("piece size too small");
return 1;
}
/*
* Check if stdin is specified for input
*/
if ((argv[3][0] != '-') || (strlen(argv[3]) > 1))
{
infilename = argv[3];
if ((infd = open(infilename, O_RDONLY)) == -1)
{
perror(infilename);
errmsg("error opening input file for read");
return 1;
}
}
else /* i.e. stdin! */
{
infilename = "<stdin>";
infd = 0; /* stdin! */
}
/*
* Now, we must check whether the input is from a problematic
* file type. We have found problems when piping input into this
* program from other commands. We believe this problem can be
* found with sockets and character special devices also.
*
* Our algorithm has been modified to take care of this behaviour
* but the flag must be set for this to be activated.
*/
{
struct stat statbuf;
if (fstat(infd, &statbuf) == -1) /* fstat the input filedes */
{
errmsg("stat on input file descriptor failed.");
return 1;
}
/*
* Check whether the user has given a directory name by mistake
*/
if ((statbuf.st_mode & S_IFDIR) == S_IFDIR)
{
errmsg("on a directory?");
/*
* Always be helpful: Old Jungle Saying
*/
fprintf(stderr, "Consider 'tar cf - %s | shatter nnn pre -'\n",
argv[3]);
return 1;
}
if (((statbuf.st_mode & S_IFIFO) == S_IFIFO) || /* pipe */
((statbuf.st_mode & S_IFCHR) == S_IFCHR) || /* character-spl */
((statbuf.st_mode & S_IFSOCK) == S_IFSOCK)) /* socket */
{
char *file_type;
is_special = TRUE; /* set this critical flag */
#ifndef SILENT
if ((statbuf.st_mode & S_IFIFO) == S_IFIFO) file_type = "pipe";
if ((statbuf.st_mode & S_IFCHR) == S_IFCHR)
file_type = "character device";
if ((statbuf.st_mode & S_IFSOCK) == S_IFSOCK) file_type = "socket";
fprintf(stderr, "Input is from a %s.\n", file_type);
#endif /* SILENT */
}
}
/*
* At this point, piece size has been obtained and adjusted
* for scaling factor, input file has been successfully opened
* or found ready for use (if stdin), and of course, prefix is
* to be used directly.
*
* We shall now go into a "while not eof(infd)" loop and start
* making the pieces. Only, we will use an infinite loop, and
* return out of the loop when we detect end of file.
*/
buf = malloc(piecesize + LINESIZE);
#ifdef DBG
fprintf(stderr, "malloced %d bytes\n", piecesize + LINESIZE);
#endif
bufoffset = 0;
piecenumber = 0;
while (1)
{
int siz = piecesize - bufoffset;
int nbytes_read, outfd, newbufofs;
nbytes_read = read(infd, buf + bufoffset, siz);
/*
* If input is from a problematic file type, then keep reading
* till either read() returns zero bytes, or till you get what
* you wanted: a buffer-full of delicious data.
*
* This is because we have found that reading from a pipe returns
* less than the full amount of data, even though O_NDELAY has
* not been set. We expect that character-special devices and
* sockets will also exhibit this behaviour. See open(2) and
* read(2) for fundaes on O_NDELAY.
*
* So, if input is from special files, we enter the special patch.
*/
if (is_special && (nbytes_read < siz))
{
int fifo_nbytes_read = nbytes_read,
fifo_siz = siz - nbytes_read,
fifo_bufofs = bufoffset + nbytes_read;
int loop_bytes_added = 0; /* total bytes added specially */
#ifdef DBG
fprintf(stderr, "straight read gave %d bytes\n", nbytes_read);
fprintf(stderr, "attempting read(%d, buf+%d, %d)\n",
infd, fifo_bufofs, fifo_siz);
#endif
while ((fifo_nbytes_read > 0) &&
(loop_bytes_added < (siz - nbytes_read)))
{
if ((fifo_nbytes_read =
read(infd, buf + fifo_bufofs, fifo_siz)) == -1)
{
perror(infilename);
errmsg("error reading from input file");
return 1;
}
fifo_bufofs += fifo_nbytes_read; /* forward the ofs */
fifo_siz -= fifo_nbytes_read; /* drop space left */
loop_bytes_added += fifo_nbytes_read; /* add to the total */
#ifdef DBG
fprintf(stderr, "+ %d ", fifo_nbytes_read);
#endif
}
nbytes_read += loop_bytes_added; /* update outer variable */
#ifdef DBG
fprintf(stderr, " = %d bytes added\n", loop_bytes_added);
#endif
}
/*
* Here we proceed as if nothing has happened. It does not matter
* at this point, whether we have gone through the special patch
* or not. If we have, it is possible that we have already
* encountered the zero-byte read there once. In that case, we
* will reattempt the read here once again, and then wind up
* operations.
*
* But that is all right.
*/
if (nbytes_read < 0) /* error reading */
{
perror(infilename);
errmsg("error reading input file");
close(infd);
return 1;
}
else if (nbytes_read == 0) /* end of input */
{
#ifndef SILENT
fprintf(stderr, "Generated %d pieces, named %s.000 to %s.%03d\n",
piecenumber, argv[2], argv[2], piecenumber-1);
#endif /* SILENT */
return 0; /* normal program termination!! */
}
else /* write out the piece */
{
char outfname[FILENAMEMAX];
if (piecenumber == PIECESMAX)
{
errmsg("too many pieces");
return 1;
}
/*
* Build the filename from the prefix and the count
*/
sprintf(outfname, "%s.%03d", argv[2], piecenumber++);
if ((outfd = open(outfname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
{
perror(outfname);
errmsg("error opening output piecefile for writing");
return 1;
}
/*
* Here comes the interesting backtrack attempt.
*
* We go back from the end of the buffer, upto LINESIZE bytes.
* If we find a '\n' anywhere till then, we stop, and write
* only from the start of the buffer upto that point. If we don't
* find any newline, we write the full buffer as it stands.
*
* We achieve this by reducing the value of nbytes_read, and
* storing in newbufofs, the count of the number of bytes
* omitted from the end of the buffer.
*
* Our aim, in this complex exercise, is to try to break
* each piece at a line boundary if possible. Thus, this
* program can be used to break ascii files just as effectively
* as split(1), but without loss of generality for binary
* files.
*/
{
char *p = ((buf + bufoffset) + nbytes_read) - 1;
newbufofs = 0;
while ((*(p--) != '\n') && (newbufofs != LINESIZE))
newbufofs++;
if (*(p+1) != '\n') /* no carriage return found */
newbufofs = 0; /* remove signs of backtrack attempt */
else
nbytes_read -= newbufofs; /* shrink effective bufsize */
}
if (write(outfd, buf, bufoffset + nbytes_read) == -1)
{
perror(outfname);
errmsg("error writing to output piecefile");
return 1;
}
#ifdef DBG
fprintf(stderr, "written %d + %d = %d bytes into %s\n",
bufoffset, nbytes_read,
bufoffset + nbytes_read,
outfname);
#endif
close(outfd);
/*
* Here, we check if we have shrunk the buffer by one or
* more bytes due to backtracking. If we have, we copy
* those bytes to the beginning of the buffer, so that
* they become the first few bytes written out in the next
* piece.
*/
if (newbufofs)
{
memcpy(buf, buf+bufoffset+nbytes_read, newbufofs);
}
bufoffset = newbufofs;
} /* end else */
} /* end while */
} /* end main() */
--
_______________________________________________________________________________
Ajay Shah, (213)734-3930, ajayshah@usc.edu
If you love a pointer, set it free()
_______________________________________________________________________________
Technical Details
Message-ID:
<kk5feqINNbi2@chaph.usc.edu>Path:
arc1-ca.pugleaf.net!wupost!cs.utexas.edu!swrinde!zaphod.mps.ohio-state.edu!usc!chaph.usc.edu!alhena.usc.edu!ajayshahReferences:
<1991Dec04.071514.7373@cavebbs.gen.nz> <oV2LcB3w164w@poorboy.UUCP>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!wupost!cs.utexas.edu!swrinde!zaphod.mps.ohio-state.edu!usc!chaph.usc.edu!alhena.usc.edu!ajayshah From: ajayshah@alhena.usc.edu (Ajay Shah) Newsgroups: comp.sys.ibm.pc.misc,alt.sources Subject: Shatter (Was Re: How can I split a large file???????) Message-ID: <kk5feqINNbi2@chaph.usc.edu> Date: 9 Dec 91 00:50:34 GMT References: <1991Dec04.071514.7373@cavebbs.gen.nz> <oV2LcB3w164w@poorboy.UUCP> Organization: University of Southern California, Los Angeles, CA Lines: 357 NNTP-Posting-Host: alhena.usc.edu