Skip to content

Instantly share code, notes, and snippets.

@sug0
Last active October 24, 2022 22:53
Show Gist options
  • Save sug0/163439062be303e1a9ee10b94dd4510c to your computer and use it in GitHub Desktop.
Save sug0/163439062be303e1a9ee10b94dd4510c to your computer and use it in GitHub Desktop.
Small utility program to spin up xbps sub-commands.
// Copyright (C) 2022 Tiago Carvalho <[email protected]>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <stdnoreturn.h>
#ifdef EMIT_WARNINGS
#define warning(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
} while(0)
#else
#define warning(...)
#endif
#define die(...) \
do { \
fprintf(stderr, __VA_ARGS__); \
exit(1); \
} while(0)
noreturn void usage(char *const argv0);
int may_run_subcmd(char *const subcmd, char *const cmd);
int main(int argc, char *argv[]) {
static char *path_buf;
if (argc < 2) {
usage(*argv);
}
char *const env_path = getenv("PATH");
if (!env_path) {
die("%s: Invalid PATH environment variable.\n", *argv);
}
for (
const char *path = strtok_r(env_path, ":", &path_buf);
path != NULL;
path = strtok_r(NULL, ":", &path_buf)
) {
DIR *dir = opendir(path);
if (!dir) {
warning("%s: Failed to open directory: %s\n", *argv, path);
continue;
}
for (struct dirent *p = readdir(dir); p; p = readdir(dir)) {
char arg[4096];
const size_t len_name = strlen(p->d_name);
const size_t end_name =
len_name < sizeof(arg) ? len_name : sizeof(arg) - 1;
memcpy(arg, p->d_name, end_name);
arg[end_name] = 0;
if (may_run_subcmd(argv[1], arg)) {
const size_t len_path = strlen(path);
const int has_trailing_slash = path[len_path - 1] == '/';
const size_t len_total =
len_path + len_name + (has_trailing_slash ? 0 : 1);
if (len_total > sizeof(arg)) {
closedir(dir);
die(
"%s: Path to xbps subcmd is too large: %s\n",
*argv,
p->d_name
);
}
memcpy(arg, path, len_path);
if (has_trailing_slash) {
memcpy(arg + len_path, p->d_name, len_name);
} else {
memcpy(arg + len_path, "/", 1);
memcpy(arg + len_path + 1, p->d_name, len_name);
}
arg[len_total] = 0;
argv[1] = arg;
closedir(dir);
execv(argv[1], argv + 1);
die(
"%s: Exec failed for xbps subcmd: %s: %s\n",
*argv,
argv[1],
strerror(errno)
);
}
}
closedir(dir);
}
die("%s: No xbps subcmd: %s\n", *argv, argv[1]);
}
noreturn void usage(char *const argv0) {
die("Usage: %s <sub-command> [args ...]\n", argv0);
exit(1);
}
int may_run_subcmd(char *const subcmd, char *const cmd) {
static char *subcmd_buf;
const char *part = strtok_r(cmd, "-", &subcmd_buf);
if (!part || strcmp(part, "xbps") != 0) {
return 0;
}
part = strtok_r(NULL, "-", &subcmd_buf);
return part && strcmp(part, subcmd) == 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment