From: David ‘Bombe’ Roden Date: Tue, 1 Dec 2009 20:07:45 +0000 (+0100) Subject: Version 0.1. X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=41d281b9fd8f2629103b3867e6277cd1ee8a4b4b;p=ccp.git Version 0.1. --- 41d281b9fd8f2629103b3867e6277cd1ee8a4b4b diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ed17d66 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ + +PREFIX=$(HOME) +BINDIR=$(PREFIX)/bin + +all: ccp cmv + +cmv: ccp + ln -s ccp cmv + +ccp: ccp.o + gcc -o $@ $^ + +ccp.o: ccp.c + gcc -Wall -c -o $@ $^ + +install: all + cp ccp "$(BINDIR)/ccp" + rm -f "$(BINDIR)/cmv" + ln -s ccp "$(BINDIR)/cmv" + +clean: + rm -f ccp cmv + rm -f ccp.o diff --git a/ccp.c b/ccp.c new file mode 100644 index 0000000..6a40186 --- /dev/null +++ b/ccp.c @@ -0,0 +1,300 @@ +/** + * ccp.c - a cp replacement with progress bar + * + * @version $Id: ccp.c 769 2008-04-26 10:53:11Z bombe $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define false 0 +#define true !false + +int redrawProgressBar = false; + +void handleWindowResize(int signalNumber) { + signal(signalNumber, SIG_IGN); + + redrawProgressBar = true; + + signal(signalNumber, handleWindowResize); +} + +int getColumns() { + struct winsize terminalSize; + int error; + + error = ioctl(1, TIOCGWINSZ, &terminalSize); + + if (!error) { + return terminalSize.ws_col; + } + return 80; +} + +char *extractFilename(char *pathName) { + char* result; + + result = strrchr(pathName, '/'); + if (result) { + return result + 1; + } + return pathName; +} + +int scaleProgress(char* fileName, double progress) { + int columns; + + columns = getColumns() - 3 - strlen(fileName); + if (columns < 2) { + columns = 2; + } + + return (int) (columns * progress); +} + +void printProgress(char* fileName, int currentProgress) { + char* shortFileName; + int totalColumns; + int columns; + int i; + + shortFileName = fileName; + totalColumns = getColumns(); + columns = totalColumns - 3 - strlen(fileName); + + if (columns < 2) { + shortFileName = fileName + (strlen(fileName) - (totalColumns - 8)); + columns = 2; + } + + if (shortFileName != fileName) { + printf("...%s", shortFileName); + } else { + printf("%s", fileName); + } + printf(" ["); + for (i = 0; i < columns; i++) { + printf((i < currentProgress) ? "*" : "."); + } + printf("]\r"); + fflush(stdout); +} + +int copyFileToFile(char* sourceFile, char* destFile) { + FILE* inputFile; + FILE* outputFile; + char* fileName; + long long fileSize; + size_t read; + size_t toWrite; + long long writtenTotal = 0; + size_t written; + int readSize; + int bufferSize; + void* buffer; + double progress; + int currentProgress = 0; + int lastProgress = -1; + struct timeb startTime, endTime; + long startTimeMillis, endTimeMillis, spent; + + inputFile = fopen(sourceFile, "rb"); + if (!inputFile) { + fprintf(stderr, "can not open \"%s\" for reading!\n", sourceFile); + return false; + } + + outputFile = fopen(destFile, "wb"); + if (!outputFile) { + fprintf(stderr, "can not open \"%s\" for writing!\n", destFile); + fclose(inputFile); + return false; + } + + fseek(inputFile, 0, SEEK_END); + fileSize = ftell(inputFile); + fseek(inputFile, 0, SEEK_SET); + + fileName = extractFilename(sourceFile); + + bufferSize = 1 << 20; + readSize = bufferSize; + buffer = (void*) malloc(bufferSize); + + while (!feof(inputFile)) { + ftime(&startTime); + read = fread(buffer, 1, readSize, inputFile); + if (read) { + toWrite = read; + do { + written = fwrite(buffer + (read - toWrite), 1, toWrite, outputFile); + if (!written) { + if (ferror(outputFile)) { + fprintf(stderr, "\nerror writing to \"%s\"!\n", destFile); + fclose(inputFile); + fclose(outputFile); + return false; + } + } + toWrite -= written; + } while (toWrite); + ftime(&endTime); + writtenTotal += written; + progress = (double) writtenTotal / fileSize; + currentProgress = scaleProgress(fileName, progress); + if ((currentProgress != lastProgress) || (redrawProgressBar)) { + printProgress(fileName, currentProgress); + lastProgress = currentProgress; + redrawProgressBar = false; + } + endTimeMillis = endTime.time * 1000 + endTime.millitm; + startTimeMillis = startTime.time * 1000 + startTime.millitm; + spent = endTimeMillis - startTimeMillis; + if (spent >= 200) { + if (readSize > 2047) { + readSize >>= 1; + } + } else if (spent <= 50) { + if (readSize < (1 << 19)) { + readSize <<= 1; + } + } + } else { + if (ferror(inputFile)) { + fprintf(stderr, "\nerror reading from \"%s\"!\n", sourceFile); + fclose(inputFile); + fclose(outputFile); + return false; + } + } + } + printf("\n"); + fflush(stdout); + fclose(inputFile); + fclose(outputFile); + + return true; +} + +int copyFileToDirectory(char *sourceFile, char* destDirectory) { + char* fileName; + char* destFile; + int copyResult; + + fileName = extractFilename(sourceFile); + destFile = (char*) calloc(strlen(destDirectory) + 1 + strlen(fileName) + 1, 1); + strcat(destFile, destDirectory); + strcat(destFile, "/"); + strcat(destFile, fileName); + + copyResult = copyFileToFile(sourceFile, destFile); + + free(destFile); + return copyResult; +} + +void printHelp() { + printf("Syntax: ccp SRC [SRC [...]] DEST\n\n"); + printf("SRC must be a file. If DEST is a directory, an arbitrary amount of SRC\n"); + printf("may be given. If DEST is an existing file or does not exist at all,\n"); + printf("only one SRC is allowed.\n"); +} + +int isWritableFile(char *pathName) { + struct stat fileStat; + int statError; + FILE* testFd; + + statError = stat(pathName, &fileStat); + if (statError == -1) { + if (errno == ENOENT) { + testFd = fopen(pathName, "w"); + if (testFd) { + fclose(testFd); + return true; + } + } + return false; + } + if (S_ISREG(fileStat.st_mode)) { + testFd = fopen(pathName, "ab+"); + if (testFd) { + fclose(testFd); + return true; + } + } + return false; +} + +int isDirectory(char *pathName) { + struct stat fileStat; + int statError; + + statError = stat(pathName, &fileStat); + if (statError == -1) { + return false; + } + return S_ISDIR(fileStat.st_mode); +} + +int checkForDirectoryOrNonExistingFile(char* pathName) { + return isDirectory(pathName) || isWritableFile(pathName); +} + +int main(int argc, char** argv) { + char* lastArgument; + char* binaryName; + int destinationIsDirectory = false; + int fileIndex; + int moveFiles = false; + + if (argc < 3) { + printHelp(); + return 1; + } + lastArgument = argv[argc - 1]; + if (!isDirectory(lastArgument)) { + if (isWritableFile(lastArgument)) { + if (argc != 3) { + printHelp(); + return 1; + } + } else { + fprintf(stderr, "\"%s\" is not a writable file.\n", lastArgument); + return 2; + } + } else { + destinationIsDirectory = true; + } + + binaryName = extractFilename(argv[0]); + if (!strcmp(binaryName, "cmv")) { + moveFiles = true; + } + + signal(SIGWINCH, handleWindowResize); + for (fileIndex = 1; fileIndex < (argc - 1); fileIndex++) { + if (destinationIsDirectory) { + if (copyFileToDirectory(argv[fileIndex], lastArgument) && moveFiles) { + unlink(argv[fileIndex]); + } + } else { + if (copyFileToFile(argv[fileIndex], lastArgument) && moveFiles) { + unlink(argv[fileIndex]); + } + } + } + + return 0; +}