You are on page 1of 9

/* * 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 #include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <time.h> <fcntl.h> <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 by tes 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, r un 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 witho ut 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 wi thout 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", fil e_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 rel axing 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(c har))) == NULL) { printf("\n\nERROR: Insufficient memory to create name of the output file s.\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++) print f("\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 previ ously 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, stringNumSlic es, 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.tx t"); 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 com mas 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---000000000111111111122222222223333333333444444444455555555556666666 66677777777778 //RULER---123456789012345678901234567890123456789012345678901234567890123456 78901234567890 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 characte r '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 sli ce 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 m ost" \ "\n 123456 bytes each." \ "\n slice 5x -s work.dat Split the file into 5 slices, and suppre ss the" \ "\n progress indicator." \ "\n slice 5x -o work.dat Split the file into 5 slices, and automa tically" \ "\n overwrite existing output files." \ "\n\n"); } /* * Compile notes: * This source file has been compiled successfully using Cygwin's GCC 3.4.4 */

You might also like