Skip to content

Instantly share code, notes, and snippets.

@phlummox
Last active July 26, 2023 13:58
Show Gist options
  • Save phlummox/c2c14b30f7185ee49ab76baf4924da9f to your computer and use it in GitHub Desktop.
Save phlummox/c2c14b30f7185ee49ab76baf4924da9f to your computer and use it in GitHub Desktop.

Revisions

  1. phlummox revised this gist Jul 18, 2023. No changes.
  2. phlummox revised this gist Jul 18, 2023. No changes.
  3. phlummox created this gist Jul 18, 2023.
    9 changes: 9 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@

    CFLAGS = -pedantic -Wall -Wextra -std=c11 -O2

    rel: rel.o

    rel.o:

    clean:
    -rm *.o rel
    67 changes: 67 additions & 0 deletions rel.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,67 @@
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <libgen.h>
    #include <unistd.h>

    // Variable naming:
    //
    // - Lengths of NUL-terminated strings are called <something>_len
    // (and don't include the NUL in the length)
    // - Sizes of buffers are called <something>_size
    // (and represent the exact size of the buffer)
    //
    // We assume a Linux with memory overcommit enabled, and lazily don't bother to
    // check the return value of calls to malloc(). (But we should. See
    // <https://ewontfix.com/3/>.)


    // Wrapper around POSIX dirname which works on a copy
    // of `path` (since `dirname()` may try to alter its
    // argument).
    //
    // `dir` should point to memory which can hold the full result
    // of `dirname(path)`.
    // `dir_len` should point to memory which can hold a size_t; the
    // length of `dir` will be written to it.
    void dirname_(char *dir, size_t *dir_len, const char *path) {
    const size_t path_len = strlen(path);
    char *path_cp = malloc(path_len+1);
    strncpy(path_cp, path, path_len+1);

    char *tmp = dirname(path_cp);
    *dir_len = strlen(tmp);
    strncpy(dir, tmp, (*dir_len)+1);
    free(path_cp);
    }

    int main(int argc, char* argv[]) {
    if (argc < 3) {
    fprintf(stderr, "expected at least 2 args: <INTERPRETER> <SCRIPT> ...\n");
    exit(EXIT_FAILURE);
    }
    const char * interpreter = argv[1];
    const char * orig_script = argv[2];

    const size_t interpreter_len = strlen(interpreter);

    // max buf needed: + 1 for a "/" char between the two, +1 more for NUL.
    const size_t buf_size = strlen(orig_script) + 1 + interpreter_len + 1;

    char * buf = malloc(buf_size);
    size_t dirname_len;
    dirname_(buf, &dirname_len, orig_script);
    buf[dirname_len] = '/';
    strncpy(buf + dirname_len + 1, interpreter, interpreter_len+1);

    // adjust argv for passing to execv:
    // argv[0] <- buf, all others shift back one place.

    argv[0] = buf;
    for (int i = 1; i < argc - 1; i++) {
    argv[i] = argv[i+1];
    }
    argv[argc-1] = NULL;

    execv(buf, argv);
    }