Skip to content

Instantly share code, notes, and snippets.

@joshkunz
Created August 8, 2014 22:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshkunz/a6791ecef6ac0d717921 to your computer and use it in GitHub Desktop.
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.
.PHONY: clean
CFLAGS = -Wall -Wextra -Wpedantic -Werror
bin = qmv
$(bin):
clean:
rm $(bin)
#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(&current, 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