--- /dev/null
+/**
+ * ccp.c - a cp replacement with progress bar
+ *
+ * @version $Id: ccp.c 769 2008-04-26 10:53:11Z bombe $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/timeb.h>
+#include <time.h>
+
+#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;
+}