2 * ccp.c - a cp replacement with progress bar
4 * @version $Id: ccp.c 769 2008-04-26 10:53:11Z bombe $
11 #include <sys/ioctl.h>
17 #include <sys/timeb.h>
23 int redrawProgressBar = false;
25 void handleWindowResize(int signalNumber) {
26 signal(signalNumber, SIG_IGN);
28 redrawProgressBar = true;
30 signal(signalNumber, handleWindowResize);
34 struct winsize terminalSize;
37 error = ioctl(1, TIOCGWINSZ, &terminalSize);
40 return terminalSize.ws_col;
45 char *extractFilename(char *pathName) {
48 result = strrchr(pathName, '/');
55 int scaleProgress(char* fileName, double progress) {
58 columns = getColumns() - 3 - strlen(fileName);
63 return (int) (columns * progress);
66 void printProgress(char* fileName, int currentProgress) {
72 shortFileName = fileName;
73 totalColumns = getColumns();
74 columns = totalColumns - 3 - strlen(fileName);
77 shortFileName = fileName + (strlen(fileName) - (totalColumns - 8));
81 if (shortFileName != fileName) {
82 printf("...%s", shortFileName);
84 printf("%s", fileName);
87 for (i = 0; i < columns; i++) {
88 printf((i < currentProgress) ? "*" : ".");
94 int copyFileToFile(char* sourceFile, char* destFile) {
101 long long writtenTotal = 0;
107 int currentProgress = 0;
108 int lastProgress = -1;
109 struct timeb startTime, endTime;
110 long startTimeMillis, endTimeMillis, spent;
112 inputFile = fopen(sourceFile, "rb");
114 fprintf(stderr, "can not open \"%s\" for reading!\n", sourceFile);
118 outputFile = fopen(destFile, "wb");
120 fprintf(stderr, "can not open \"%s\" for writing!\n", destFile);
125 fseek(inputFile, 0, SEEK_END);
126 fileSize = ftell(inputFile);
127 fseek(inputFile, 0, SEEK_SET);
129 fileName = extractFilename(sourceFile);
131 bufferSize = 1 << 20;
132 readSize = bufferSize;
133 buffer = (void*) malloc(bufferSize);
135 while (!feof(inputFile)) {
137 read = fread(buffer, 1, readSize, inputFile);
141 written = fwrite(buffer + (read - toWrite), 1, toWrite, outputFile);
143 if (ferror(outputFile)) {
144 fprintf(stderr, "\nerror writing to \"%s\"!\n", destFile);
152 fsync(fileno(outputFile));
154 writtenTotal += written;
155 progress = (double) writtenTotal / fileSize;
156 currentProgress = scaleProgress(fileName, progress);
157 if ((currentProgress != lastProgress) || (redrawProgressBar)) {
158 printProgress(fileName, currentProgress);
159 lastProgress = currentProgress;
160 redrawProgressBar = false;
162 endTimeMillis = endTime.time * 1000 + endTime.millitm;
163 startTimeMillis = startTime.time * 1000 + startTime.millitm;
164 spent = endTimeMillis - startTimeMillis;
166 if (readSize > 2047) {
169 } else if (spent <= 50) {
170 if (readSize < (1 << 19)) {
175 if (ferror(inputFile)) {
176 fprintf(stderr, "\nerror reading from \"%s\"!\n", sourceFile);
191 int copyFileToDirectory(char *sourceFile, char* destDirectory) {
196 fileName = extractFilename(sourceFile);
197 destFile = (char*) calloc(strlen(destDirectory) + 1 + strlen(fileName) + 1, 1);
198 strcat(destFile, destDirectory);
199 strcat(destFile, "/");
200 strcat(destFile, fileName);
202 copyResult = copyFileToFile(sourceFile, destFile);
209 printf("Syntax: ccp SRC [SRC [...]] DEST\n\n");
210 printf("SRC must be a file. If DEST is a directory, an arbitrary amount of SRC\n");
211 printf("may be given. If DEST is an existing file or does not exist at all,\n");
212 printf("only one SRC is allowed.\n");
215 int isWritableFile(char *pathName) {
216 struct stat fileStat;
220 statError = stat(pathName, &fileStat);
221 if (statError == -1) {
222 if (errno == ENOENT) {
223 testFd = fopen(pathName, "w");
231 if (S_ISREG(fileStat.st_mode)) {
232 testFd = fopen(pathName, "ab+");
241 int isDirectory(char *pathName) {
242 struct stat fileStat;
245 statError = stat(pathName, &fileStat);
246 if (statError == -1) {
249 return S_ISDIR(fileStat.st_mode);
252 int checkForDirectoryOrNonExistingFile(char* pathName) {
253 return isDirectory(pathName) || isWritableFile(pathName);
256 int main(int argc, char** argv) {
259 int destinationIsDirectory = false;
261 int moveFiles = false;
267 lastArgument = argv[argc - 1];
268 if (!isDirectory(lastArgument)) {
269 if (isWritableFile(lastArgument)) {
275 fprintf(stderr, "\"%s\" is not a writable file.\n", lastArgument);
279 destinationIsDirectory = true;
282 binaryName = extractFilename(argv[0]);
283 if (!strcmp(binaryName, "cmv")) {
287 signal(SIGWINCH, handleWindowResize);
288 for (fileIndex = 1; fileIndex < (argc - 1); fileIndex++) {
289 if (destinationIsDirectory) {
290 if (copyFileToDirectory(argv[fileIndex], lastArgument) && moveFiles) {
291 unlink(argv[fileIndex]);
294 if (copyFileToFile(argv[fileIndex], lastArgument) && moveFiles) {
295 unlink(argv[fileIndex]);