Created
July 27, 2013 19:38
-
-
Save RavuAlHemio/6096015 to your computer and use it in GitHub Desktop.
Pounce-and-write. Waits until a file is accessible, then either writes a set of lines into it or executes a command.
This file contains hidden or 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
/** | |
* Pounce-and-write. Waits until a file is accessible, then either writes a set | |
* of lines into it or executes a command. | |
* | |
* Released into the public domain. | |
* http://creativecommons.org/publicdomain/zero/1.0/ | |
*/ | |
#include <errno.h> | |
#include <stdbool.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
/** Options specifying how the program should run. */ | |
typedef struct paw_options_s | |
{ | |
/** | |
* The program will wait for the existence of this file before | |
* continuing. NULL if the program shouldn't wait for any file. | |
*/ | |
const char *file_to_wait_for; | |
/** | |
* If set, the program will write its positional arguments line by line | |
* into this file (potentially after waiting for the file specified in | |
* file_to_wait_for to come into existence). If not set, the program | |
* will execute the command specified in its positional arguments. | |
*/ | |
const char *file_to_write_into; | |
/** | |
* Positional arguments -- a command and its arguments, or lines to | |
* write into a file. | |
*/ | |
char **positional_args; | |
} paw_options; | |
/** Name of the program, taken from argv[0] if possible. */ | |
static const char *progname = "pounceandwrite"; | |
/** | |
* Complain about an unexpected situation in the environment (something failed | |
* that cannot). | |
*/ | |
__attribute__((noreturn, format(printf, 1, 2))) | |
static void explode(const char * const fmt, ...) | |
{ | |
va_list vl; | |
va_start(vl, fmt); | |
vfprintf(stderr, fmt, vl); | |
va_end(vl); | |
abort(); | |
} | |
/** | |
* Complain about an error returned by a function. | |
* @param funcname Name of the function which failed. | |
* @param e Error number (mostly taken from errno). | |
*/ | |
__attribute__((noreturn)) | |
static void bail_out_errno(const char * const funcname, int e) | |
{ | |
fprintf(stderr, "%s: %s failed: [%d] %s\n", progname, funcname, e, strerror(e)); | |
exit(1); | |
} | |
/** | |
* Output program usage and exit. | |
*/ | |
__attribute__((noreturn)) | |
static void usage(void) | |
{ | |
fprintf(stderr, | |
"Usage: %s [-e FILE] CMD ARG...\n" | |
" %s [-e FILE] [-w FILE] LINE...\n" | |
"\n" | |
" -e FILE File for whose existence to wait before continuing.\n" | |
" -w FILE Instead of executing a command, write into this file.\n", | |
progname, progname | |
); | |
exit(1); | |
} | |
/** | |
* Parse the command-line arguments. | |
* @param argc Number of arguments. | |
* @param argv Array of argument strings. | |
*/ | |
static void parse_args(int argc, char * const *argv, paw_options *opts) | |
{ | |
int i, c, pos_count; | |
/* rational defaults */ | |
opts->file_to_wait_for = NULL; | |
opts->file_to_write_into = NULL; | |
/* set program name */ | |
if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0') | |
{ | |
progname = argv[0]; | |
} | |
while ((c = getopt(argc, argv, "e:w:")) != -1) | |
{ | |
switch (c) | |
{ | |
case 'e': | |
opts->file_to_wait_for = optarg; | |
break; | |
case 'w': | |
opts->file_to_write_into = optarg; | |
break; | |
case '?': | |
usage(); | |
break; | |
default: | |
explode("getopt default case"); | |
break; | |
} | |
} | |
pos_count = argc - optind; | |
if (pos_count == 0) | |
{ | |
/* no positional arguments */ | |
fprintf(stderr, "%s: no positional arguments specified\n", progname); | |
usage(); | |
} | |
else if (pos_count < 0) | |
{ | |
explode("your getopt is broken (positional arg count is %d)\n", pos_count); | |
} | |
opts->positional_args = malloc((pos_count + 1) * sizeof(*(opts->positional_args))); | |
if (opts->positional_args == NULL) | |
{ | |
explode("malloc failed"); | |
} | |
for (i = 0; i < pos_count; ++i) | |
{ | |
opts->positional_args[i] = argv[optind + i]; | |
} | |
opts->positional_args[pos_count] = NULL; | |
} | |
/** | |
* Wait for the given file to exist. | |
* @param fn Filename to wait for. | |
*/ | |
static void wait_for_file(const char * const fn) | |
{ | |
int a; | |
while ((a = access(fn, F_OK)) == -1 && errno == ENOENT) | |
{ | |
sleep(1); | |
} | |
if (a == -1) | |
{ | |
bail_out_errno("access", errno); | |
} | |
} | |
/** | |
* Write a few lines into a file. | |
* @param fn Filename to write into. | |
* @param lines Lines to write, terminated by NULL. | |
*/ | |
static void write_into_file(const char * const fn, char * const * const lines) | |
{ | |
size_t i; | |
FILE *f; | |
f = fopen(fn, "w"); | |
if (f == NULL) | |
{ | |
bail_out_errno("fopen", errno); | |
} | |
for (i = 0; lines[i] != NULL; ++i) | |
{ | |
fprintf(f, "%s\n", lines[i]); | |
} | |
if (fclose(f) == EOF) | |
{ | |
bail_out_errno("fclose", errno); | |
} | |
} | |
/** | |
* Execute a command as specified by the given array. | |
* @param args Command name and arguments, terminated by NULL. | |
*/ | |
__attribute__((noreturn)) | |
static void execute_command(char * const * const args) | |
{ | |
if (args[0] == NULL) | |
{ | |
explode("no arguments"); | |
} | |
execvp(args[0], args); | |
bail_out_errno("execvp", errno); | |
} | |
int main(int argc, char **argv) | |
{ | |
paw_options opts; | |
parse_args(argc, argv, &opts); | |
if (opts.file_to_wait_for != NULL) | |
{ | |
wait_for_file(opts.file_to_wait_for); | |
} | |
if (opts.file_to_write_into != NULL) | |
{ | |
write_into_file(opts.file_to_write_into, opts.positional_args); | |
} | |
else | |
{ | |
execute_command(opts.positional_args); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment