/*
* Slice
* v 1.0 (2007-03-28)
* File splitter
*
* Copyright 2006 Zach Scrivena
* Email: zachscrivena@gmail.com
* Webpage: http://zs.freeshell.org/
*
* Splits a file into several 'slices' that can be easily concatenated
* to recover the original file. Free software written in C.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/stat.h>
#define BUFFER_SIZE 1048576 // 1 Mb
#define STRING_SIZE 256
/* Function prototypes */
void strcombine(char *target, char *source1, char *source2, char *source3);
long long int getFileSize(char *filename);
int fileExists(char *filename);
void printNumComma(long long l);
void printUsage();
/* Main entry point for Slice program */
int main(int argc, char *argv[])
{
FILE *fp_in; // input file pointer
FILE *fp_out; // output file pointer
char *command; // first command-line argument ("command")
char *file_in; // second command-line argument ("file")
char *file_out; // output file name
long long commandInteger; // integer associated with the "command"
long long size; // input file size in bytes
long long sliceSize; // size of each slice (except the last slice) in bytes
long long lastSliceSize; // size of the last slice in bytes
long long numSlices; // number of slices
long long sliceIndex;
long long thisSliceSize;
long long bytesWritten;
long long totalBytesWritten;
int numDigits;
int bytesToWrite;
int bytesRead;
char formatString[STRING_SIZE];
char buffer[BUFFER_SIZE];
char stringNumSlices[STRING_SIZE];
char stringSliceIndex[STRING_SIZE];
long int sliceStartTime;
long int sliceDuration;
char indicator[] = "/-\\|";
int indicatorMaxIndex = strlen(indicator) - 1;
int indicatorIndex;
// program settings
int showProgress = 1; // show progress by default
int overwriteOutputFiles = 0; // do not overwrite output files by default
// temp vars
char a[STRING_SIZE];
int i;
printf("\nSlice 1.0 Copyright 2007 Zach Scrivena 2007-03-28");
/* Process command-line arguments */
if (argc == 1)
{
printUsage();
exit(0);
}
else if (argc < 3)
{
printf("\n\nERROR: Insufficient number of arguments.\nTo display help, run Slice without any command-line arguments.\n");
exit(1);
}
// correct number of arguments supplied
command = argv[1]; // first argument
file_in = argv[argc - 1]; // last argument
// extract the integer part of the command
if ((commandInteger = strtoll(command, (char **) NULL, 10)) <= 0)
{
printf("\n\nERROR: Invalid command %s.\nTo display help, run Slice without any command-line arguments.\n", command);
exit(1);
}
// process switches
for (i = 2; i < argc - 1; i++)
{
if (strcmp("-s", argv[i]) == 0)
{
// suppress progress indicator
showProgress = 0;
}
else if (strcmp("-o", argv[i]) == 0)
{
// overwrite output files
overwriteOutputFiles = 1;
}
else
{
// invalid switch
printf("\n\nERROR: Invalid switch %s.\nTo display help, run Slice without any command-line arguments.\n", argv[i]);
exit(1);
}
}
// check if input file exists
if (!fileExists(file_in))
{
printf("\n\nERROR: Input file %s does not exist.\nSlice aborted.\n", file_in);
exit(1);
}
// print file to be sliced
printf("\n\nFile to be sliced: %s ", file_in);
// get size of input file
if ((size = getFileSize(file_in)) == -1)
{
printf("\nSlice aborted.\n");
exit(1);
}
printf("(");
printNumComma(size);
printf(" bytes)");
/* Compute slice sizes */
if (command[strlen(command) - 1] == 'x')
{
// split file into specified number of slices
sliceSize = size / commandInteger;
numSlices = commandInteger;
}
else
{
// split file into slices not exceeding specified size in bytes
sliceSize = commandInteger;
numSlices = (size + commandInteger - 1) / commandInteger;
}
if (numSlices < 1 ||
sliceSize < 1)
{
printf("\n\nERROR: Unable to slice file to given specification.\nTry relaxing the specification.\nSlice aborted.\n");
exit(1);
}
if (numSlices == 1)
{
printf("\n\nNothing to do; original file already satisfies specification.\n");
exit(0);
}
if (sliceSize > size)
{
sliceSize = size;
}
// compute size of the last slice
lastSliceSize = size - (numSlices - 1) * sliceSize;
// display size of slices
printf("\nCreating ");
printNumComma(numSlices);
printf(" slices ");
if (lastSliceSize == sliceSize)
{
printf("(");
printNumComma(numSlices);
printf(" x ");
printNumComma(sliceSize);
printf(" bytes)");
}
else
{
printf("(");
printNumComma(numSlices - 1);
printf(" x ");
printNumComma(sliceSize);
printf(" bytes + 1 x ");
printNumComma(lastSliceSize);
printf(" bytes)");
}
if (showProgress) printf("...");
fflush(stdout);
/* Allocate memory for output file names */
// compute number of digits required to display the maximum slice index
numDigits = sprintf(a, "%lld", numSlices);
// create format string used in creating the file extension of each slice
sprintf(formatString, "%%0%dd", numDigits);
// allocate memory for the name of the output files
if ((file_out = (char *) malloc((strlen(file_in) + 1 + numDigits) * sizeof(char))) == NULL)
{
printf("\n\nERROR: Insufficient memory to create name of the output files.\nSlice aborted.\n");
exit(1);
}
// string representation of the number of the slices
sprintf(stringNumSlices, formatString, numSlices);
/* Check if the output files already exist */
if (!overwriteOutputFiles)
{
for (sliceIndex = 1; sliceIndex <= numSlices; sliceIndex++)
{
// string representation of the slice index
sprintf(stringSliceIndex, formatString, sliceIndex);
// create the name of the output file, e.g. work.dat.001
strcombine(file_out, file_in, ".", stringSliceIndex);
if (fileExists(file_out))
{
char stringOne[STRING_SIZE]; // string representation of number 1 (e.g. "0001")
sprintf(stringOne, formatString, 1);
printf("\n\nERROR: Output file %s already exists.", file_out);
strcombine(file_out, file_in, ".", stringOne);
printf("\nPlease ensure that files");
printf("\n %s, through", file_out);
strcombine(file_out, file_in, ".", stringNumSlices);
printf("\n %s\ndo not already exist.", file_out);
printf("\nSlice aborted.\n");
exit(1);
}
}
}
// start timer
sliceStartTime = time(NULL);
/* Open the input file for binary read-only */
if ((fp_in = fopen(file_in, "rb")) == 0)
{
printf("\n\nERROR: Unable to open file for reading.\nSlice aborted.\n", file_in);
exit(1);
}
/* Write output files */
if (showProgress) printf("\nWriting slice ");
totalBytesWritten = 0;
// for progress indicator /-\|
indicatorIndex = 0;
for (sliceIndex = 1; sliceIndex <= numSlices; sliceIndex++)
{
// string representation of the slice index
sprintf(stringSliceIndex, formatString, sliceIndex);
// create the name of the output file, e.g. work.dat.001
strcombine(file_out, file_in, ".", stringSliceIndex);
// print slice status
if (showProgress)
{
if (sliceIndex > 1) for (i = 0; i < (2 * numDigits + 25); i++) printf("\b"); // erase previously printed line
printf("%s of %s: %3d%% - [Total %3d%%]",
stringSliceIndex,
stringNumSlices,
0,
(int) (100.0 * totalBytesWritten / size)); // e.g. "001 of 123: 0% - [Total 0%]"
}
else
{
printf(".");
}
fflush(stdout);
// open output file for binary write-only
if ((fp_out = fopen(file_out, "wb")) == NULL)
{
printf("\n\nERROR: Unable to create output file %s.\nSlice aborted.\n", file_out);
exit(1);
}
// get size of the current slice (output file)
thisSliceSize = (sliceIndex == numSlices) ? lastSliceSize : sliceSize;
/* Read input file and write the current slice */
// reset total bytes written for the current slice
bytesWritten = 0;
while (bytesWritten < thisSliceSize)
{
if (thisSliceSize - bytesWritten < BUFFER_SIZE)
{
bytesToWrite = (int) (thisSliceSize - bytesWritten);
}
else
{
bytesToWrite = BUFFER_SIZE;
}
// read bytes from input file into the buffer
bytesRead = fread(buffer, 1, bytesToWrite, fp_in);
if (bytesRead != bytesToWrite)
{
printf("\n\nERROR: Unable to read input file.\nSlice aborted.\n");
exit(1);
}
// write bytes from buffer to the current slice (output file)
fwrite(buffer, 1, bytesRead, fp_out);
// increment total bytes written for the current slice
bytesWritten += bytesToWrite;
// update progress bar
if (showProgress)
{
indicatorIndex++;
if (indicatorIndex > indicatorMaxIndex) indicatorIndex = 0;
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); // erase previously printed line
printf("%3d%% %c [Total %3d%%]",
(int) (100.0 * bytesWritten / thisSliceSize),
indicator[indicatorIndex],
(int) (100.0 * (totalBytesWritten + bytesWritten) / size));
fflush(stdout);
}
}
// close the current slice (output file)
fclose(fp_out);
// update total bytes written
totalBytesWritten += thisSliceSize;
}
// close the input file
fclose(fp_in);
// free previously allocated memory
free(file_out);
// done!
sliceDuration = time(NULL) - sliceStartTime;
if (sliceDuration <= 0) sliceDuration = 1;
if (showProgress)
{
if (sliceIndex > 1) for (i = 0; i < (2 * numDigits + 25); i++) printf("\b"); // erase previously printed line
printf("%s of %s: %3d%% [Total %3d%%]", stringNumSlices, stringNumSlices, 100, 100); // e.g. "123 of 123: 100% [Total 100%]"
}
printf("\nSlice is done!");
printf("\nAverage slicing speed: ");
printNumComma((long long int) (size / sliceDuration));
printf(" bytes/second");
printf("\n\nTo recover the original file, simply concatenate the file slices sequentially\nin binary mode. For example,");
printf("\n Windows: copy /b file.txt.1 + file.txt.2 + file.txt.3 file.txt");
printf("\n Unix : cat file.txt.1 file.txt.2 file.txt.3 > file.txt");
printf("\n");
// successful termination
return 0;
}
/* Concatenate multiple strings (e.g. target = source1 + source2 + source3) */
void strcombine(char *target, char *source1, char *source2, char *source3)
{
strcpy(target, source1);
strcat(target, source2);
strcat(target, source3);
}
/* Get size in bytes of the specified file */
long long int getFileSize(char *filename)
{
struct stat statBuffer; // file attributes
if (stat(filename, &statBuffer) != 0)
{
printf("\n\nERROR: Unable to obtain attributes of file %s.", filename);
return -1;
}
return (long long int) statBuffer.st_size;
}
/* Check if specified file exists */
int fileExists(char *filename)
{
struct stat statBuffer; // file attributes
return (stat(filename, &statBuffer) == 0) ? 1 : 0;
}
/* Prints a given long long int as a comma-grouped numeral */
void printNumComma(long long num)
{
char stringNoComma[STRING_SIZE]; // string representation of num without commas
char stringComma[STRING_SIZE]; // string representation of num with commas
int stringNoComma_len;
int i;
int j;
sprintf(stringNoComma, "%lld", num);
stringNoComma_len = strlen(stringNoComma);
j = 0;
for (i = 0; i < stringNoComma_len; i++)
{
stringComma[j++] = stringNoComma[i];
if (((stringNoComma_len - i) % 3 == 1) && (i < stringNoComma_len - 1))
{
stringComma[j++] = ','; // insert a comma
}
}
stringComma[j] = '\0'; // null-terminate the string
printf("%s", stringComma);
}
/* Prints usage for Slice */
void printUsage()
{
//RULER---00000000011111111112222222222333333333344444444445555555555666666666677777777778
//RULER---12345678901234567890123456789012345678901234567890123456789012345678901234567890
printf("\n" \
"\nSplits a file into several 'slices' that can be easily concatenated to recover" \
"\nthe original file. By default, Slice displays a progress indicator, and will" \
"\nnot overwrite existing output files." \
"\n" \
"\nUSAGE: slice [command] <switches> [\"File\"]" \
"\n" \
"\n [command] is a positive integer followed possibly by the character 'x'." \
"\n With the 'x', the integer gives the total number of slices;" \
"\n without the 'x', the integer gives the maximum size of each slice in bytes." \
"\n" \
"\n <Switches>:" \
"\n -s Suppress progress indicator (improves speed)" \
"\n -o Overwrite existing output files" \
"\n" \
"\n [\"File\"] is the name of the file to be split." \
"\n" \
"\nEXAMPLES:" \
"\n slice 5x work.dat Split the file into 5 slices." \
"\n slice 123456 work.dat Split the file into slices that are at most" \
"\n 123456 bytes each." \
"\n slice 5x -s work.dat Split the file into 5 slices, and suppress the" \
"\n progress indicator." \
"\n slice 5x -o work.dat Split the file into 5 slices, and automatically" \
"\n overwrite existing output files." \
"\n\n");
}
/*
* Compile notes:
* This source file has been compiled successfully using Cygwin's GCC 3.4.4
*/