Skip to content

Instantly share code, notes, and snippets.

@RavuAlHemio
Created July 27, 2013 19:38
Show Gist options
  • Save RavuAlHemio/6096015 to your computer and use it in GitHub Desktop.
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.
/**
* 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