Created
August 8, 2014 22:43
-
-
Save joshkunz/a6791ecef6ac0d717921 to your computer and use it in GitHub Desktop.
A simple `qmv` implementation. Given a list of files as arguments, this program will open up a file in $EDITOR with one line per argument. You edit the file names, and then when you quit, the files are re-named according to their corresponding lines in the edited file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.PHONY: clean | |
CFLAGS = -Wall -Wextra -Wpedantic -Werror | |
bin = qmv | |
$(bin): | |
clean: | |
rm $(bin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <stdarg.h> | |
#define ECODE 1 | |
#define GETLINE_INIT 200 | |
void pexit(char * n) { | |
perror(n); exit(ECODE); | |
} | |
void * xmalloc(size_t size) { | |
void * out = malloc(size); | |
if (out == NULL) { pexit("malloc"); } | |
return out; | |
} | |
void * xrealloc(void * ptr, size_t size) { | |
void * out = realloc(ptr, size); | |
if (out == NULL) { free(ptr); pexit("realloc"); } | |
return out; | |
} | |
char * xgetline(FILE * f) { | |
char current = 0; | |
char * line = malloc(GETLINE_INIT); | |
int bufsize = GETLINE_INIT; | |
int csize = 0; | |
for (; fread(¤t, 1, 1, f) > 0; csize++) { | |
if (csize >= bufsize) { | |
bufsize *= 2; | |
line = xrealloc(line, bufsize); | |
} | |
if (current == '\n') { | |
break; | |
} else { | |
line[csize] = current; | |
} | |
} | |
assert(csize < bufsize); | |
line[csize++] = '\0'; | |
line = xrealloc(line, csize); | |
return line; | |
} | |
int subcommand(char * command, ...) { | |
va_list args; | |
va_start(args, command); | |
int bsize = strlen(command) + 1; | |
char * buf = xmalloc(bsize); | |
memcpy(buf, command, bsize); | |
char * next = NULL; | |
while ((next = va_arg(args, char *)) != NULL) { | |
/* +1 for space, +2 for quotes. */ | |
bsize += strlen(next) + 3; | |
buf = realloc(buf, bsize); | |
strcat(buf, " \""); | |
strcat(buf, next); | |
strcat(buf, "\""); | |
} | |
va_end(args); | |
int status = system(buf); | |
free(buf); | |
return status; | |
} | |
int main(int argc, char * argv[]) { | |
char * editor = getenv("EDITOR"); | |
if (editor == NULL) { | |
fprintf(stderr, "$EDITOR is not defined.\n"); | |
exit(ECODE); | |
} | |
char * tname = tmpnam(NULL); | |
FILE * tfile = fopen(tname, "w"); | |
if (tfile == NULL) { pexit("fopen"); } | |
for (int i = 1; i < argc; i++) { | |
if (fwrite(argv[i], strlen(argv[i]), 1, tfile) < 1 || | |
fwrite("\n", 1, 1, tfile) < 1) { | |
pexit("fwrite"); } | |
} | |
fclose(tfile); | |
int status = subcommand(editor, tname, NULL); | |
if (status != 0) { | |
fprintf(stderr, "%s returned non-zero exit code: %d\n", | |
editor, status); | |
remove(tname); | |
exit(ECODE); | |
} | |
tfile = fopen(tname, "r"); | |
if (tfile == NULL) { pexit("fopen"); } | |
char *cline = NULL; | |
for (int i = 1; i < argc; i++) { | |
cline = xgetline(tfile); | |
if (cline[0] == '\0') { | |
fprintf(stderr, | |
"Instruction file had fewer lines than expected.\n"); | |
exit(ECODE); | |
} | |
if (strcmp(argv[i], cline) != 0) { | |
int status = subcommand("mv", argv[i], cline, NULL); | |
if (status != 0) { | |
fprintf(stderr, "'mv' failed with exit code: %d\n", status); | |
exit(ECODE); | |
} | |
} | |
free(cline); | |
} | |
fclose(tfile); | |
remove(tname); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment