From 754ab792e43ab4b95350fee2f5e841186af13653 Mon Sep 17 00:00:00 2001 From: Tharre Date: Tue, 29 Jul 2014 00:51:25 +0200 Subject: Add filepath.c, refactor out parse_shebang() and rewrite most of the error checking code to use predefined error macros --- src/build.c | 135 +++++++++++++++++++-------------------------------------- src/build.h | 5 +-- src/filepath.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/filepath.h | 15 +++++++ src/util.c | 30 ++++--------- src/util.h | 28 ++++++++---- 6 files changed, 208 insertions(+), 126 deletions(-) create mode 100644 src/filepath.c create mode 100644 src/filepath.h (limited to 'src') diff --git a/src/build.c b/src/build.c index 3bd0abd..4f5d142 100644 --- a/src/build.c +++ b/src/build.c @@ -12,6 +12,7 @@ #include "build.h" #include "util.h" +#include "filepath.h" #define _FILENAME "build.c" #include "dbg.h" @@ -49,7 +50,7 @@ int build_target(const char *target) { pid_t pid = fork(); if (pid == -1) { /* failure */ - fatal("redo: failed to fork() new process"); + fatal(ERRM_FORK); } else if (pid == 0) { /* child */ @@ -57,7 +58,7 @@ int build_target(const char *target) { char *dirc = safe_strdup(target); char *dtarget = dirname(dirc); if (chdir(dtarget) == -1) - fatal("redo: failed to change directory to %s", dtarget); + fatal(ERRM_CHDIR, dtarget); free(dirc); @@ -66,34 +67,7 @@ int build_target(const char *target) { char *bdo_file = xbasename(do_file); char *btemp_output = xbasename(temp_output); - /* read and parse shebang */ - FILE *fp = safe_fopen(bdo_file, "rb+"); - - const size_t bufsize = 1024; - char buf[bufsize]; - - buf[ fread(buf, 1, sizeof(buf)-1, fp) ] = '\0'; - if (ferror(fp)) - fatal("redo: failed to read from %s", bdo_file); - - fclose(fp); - - char **argv; - size_t i = 0; - if (buf[0] == '#' && buf[1] == '!') { - argv = parsecmd(&buf[2], &i, 5); - } else { - argv = safe_malloc(7 * sizeof(char*)); - argv[i++] = "/bin/sh"; - argv[i++] = "-e"; - } - - argv[i++] = bdo_file; - argv[i++] = (char*) btarget; - char *basename = remove_ext(btarget); - argv[i++] = basename; - argv[i++] = btemp_output; - argv[i] = NULL; + char **argv = parse_shebang(btarget, bdo_file, btemp_output); /* excelp() has nearly everything we want: automatic parsing of the shebang line through execve() and fallback to /bin/sh if no valid @@ -103,7 +77,7 @@ int build_target(const char *target) { execv(argv[0], argv); /* execv should never return */ - fatal("redo: failed to replace the child process with %s", argv[0]); + fatal(ERRM_EXEC, argv[0]); } /* parent */ @@ -119,11 +93,7 @@ int build_target(const char *target) { /* successful */ /* if the file is 0 bytes long we delete it */ - off_t f_size = fsize(temp_output); - if (f_size == -1) { - if (errno != ENOENT) - fatal("redo: failed to determine the size of %s", temp_output); - } else if (f_size) + if (fsize(temp_output) > 0) remove_temp = false; } } else { @@ -134,10 +104,10 @@ int build_target(const char *target) { if (remove_temp) { if (remove(temp_output)) if (errno != ENOENT) - fatal("redo: failed to remove %s", temp_output); + fatal(ERRM_REMOVE, temp_output); } else { if (rename(temp_output, target)) - fatal("redo: failed to rename %s to %s", temp_output, target); + fatal(ERRM_RENAME, temp_output, target); } free(temp_output); @@ -147,6 +117,42 @@ int build_target(const char *target) { return retval; } +/* Read and parse shebang and return an argv-like pointer array containing the + arguments. If no valid shebang could be found, assume "/bin/sh -e" instead */ +char **parse_shebang(char *target, char *dofile, char *temp_output) { + FILE *fp = fopen(dofile, "rb+"); + if (!fp) + fatal(ERRM_FOPEN, dofile) + + const size_t bufsize = 1024; + char buf[bufsize]; + + buf[ fread(buf, 1, sizeof(buf)-1, fp) ] = '\0'; + if (ferror(fp)) + fatal(ERRM_FREAD, dofile); + + fclose(fp); + + char **argv; + size_t i = 0; + if (buf[0] == '#' && buf[1] == '!') { + argv = parsecmd(&buf[2], &i, 5); + } else { + argv = safe_malloc(7 * sizeof(char*)); + argv[i++] = "/bin/sh"; + argv[i++] = "-e"; + } + + argv[i++] = dofile; + argv[i++] = (char*) target; + char *basename = remove_ext(target); + argv[i++] = basename; + argv[i++] = temp_output; + argv[i] = NULL; + + return argv; +} + /* Returns the right do-file for target */ char *get_do_file(const char *target) { assert(target); @@ -171,49 +177,6 @@ char *get_do_file(const char *target) { return NULL; } -/* Returns the extension of the target or the empty string if none was found */ -char *take_extension(const char *target) { - assert(target); - char *temp = strrchr(target, '.'); - if (temp) - return temp; - else { - return ""; - } -} - -/* Checks if target exists and prints a debug message if access() failed - except if it failed with ENOENT. */ -bool fexists(const char *target) { - assert(target); - if (!access(target, F_OK)) - return true; - if (errno != ENOENT) - debug("Failed to access %s: %s\n", target, strerror(errno)); - return false; -} - -/* Returns a new copy of str with the extension removed, where the extension is - everything behind the last dot, including the dot. */ -char *remove_ext(const char *str) { - assert(str); - size_t len; - char *ret, *dot = NULL; - - for (len = 0; str[len]; ++len) - if (str[len] == '.') - dot = (char*) &str[len]; - - if (dot) /* recalculate length to only reach just before the last dot */ - len = dot - str; - - ret = safe_malloc(len+1); - memcpy(ret, str, len); - ret[len] = '\0'; - - return ret; -} - /* Breaks cmd at spaces and stores a pointer to each argument in the returned array. The index i is incremented to point to the next free pointer. The returned array is guaranteed to have at least keep_free entries left */ @@ -250,13 +213,3 @@ char **parsecmd(char *cmd, size_t *i, size_t keep_free) { } } } - -/* Returns the size of fn */ -off_t fsize(const char *fn) { - struct stat st; - - if (stat(fn, &st)) - return -1; - - return st.st_size; -} diff --git a/src/build.h b/src/build.h index 4eda46d..70bc8fd 100644 --- a/src/build.h +++ b/src/build.h @@ -7,10 +7,7 @@ extern char *get_do_file(const char *target); extern int build_target(const char *target); -extern bool fexists(const char *target); -extern char *take_extension(const char *target); -extern char *remove_ext(const char *str); +extern char **parse_shebang(char *target, char *dofile, char *temp_output); extern char **parsecmd(char *cmd, size_t *i, size_t keep_free); -extern off_t fsize(const char *fn); #endif diff --git a/src/filepath.c b/src/filepath.c new file mode 100644 index 0000000..b8b217a --- /dev/null +++ b/src/filepath.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include +#include + +#include "util.h" +#define _FILENAME "build.c" +#include "dbg.h" + + +/* Check if the given path is absolute */ +bool is_absolute(const char* path) { + return path[0] == '/'; +} + +/* Returns a new copy of str with the extension removed, where the extension is + everything behind the last dot, including the dot. */ +char *remove_ext(const char *str) { + assert(str); + size_t len; + char *ret, *dot = NULL; + + for (len = 0; str[len]; ++len) + if (str[len] == '.') + dot = (char*) &str[len]; + + if (dot) /* recalculate length to only reach just before the last dot */ + len = dot - str; + + ret = safe_malloc(len+1); + memcpy(ret, str, len); + ret[len] = '\0'; + + return ret; +} + +/* Returns the extension of the target or the empty string if none was found */ +char *take_extension(const char *target) { + assert(target); + char *temp = strrchr(target, '.'); + if (temp) + return temp; + else + return ""; +} + +/* Make one path relative to another i.e. some/path to some/path/a/b/c would + yield a/b/c as result. Prints '.' if the 2 paths match */ +// TODO: nameing, requires absolute paths, doesn't MALLOC +const char *make_relative(const char *target, const char *to) { + int i; + for (i = 0; target[i] && to[i]; ++i) + if (target[i] != to[i]) { + /* the paths do not match */ + return to; + } + + if (!target[i] && !to[i]) { + /* both paths match completely */ + return "."; + } + + /* skip any leading seperators */ + while (to[i] == '/') + ++i; + + return &to[i]; +} + +/* Transforms target into a safe filename, replacing all '/' with '!' */ +char *transform_path(const char *target) { + char *ptr = (char*) target; + size_t escape = 0, i = 0; + while (*ptr++) + if (*ptr == '!') escape++; + + ptr = safe_malloc((ptr-target) + escape + 1); + do { + if (*target == '/') + ptr[i++] = '!'; + else if (*target == '!') { + ptr[i++] = '!'; + ptr[i++] = '!'; + } else + ptr[i++] = *target; + } while (*target++); + + ptr[i] = '\0'; + return ptr; +} + +/* Sane and portable basename implementation */ +char *xbasename(const char *path) { + assert(path); + char *ptr = strrchr(path, '/'); + return ptr? ptr+1 : (char*) path; +} + +/* Checks if target exists and prints a debug message if access() failed + except if it failed with ENOENT. */ +bool fexists(const char *target) { + assert(target); + if (!access(target, F_OK)) + return true; + if (errno != ENOENT) + debug("Failed to access %s: %s\n", target, strerror(errno)); + return false; +} + +/* Returns the size of fn, or -1 if the file doesn't exist. */ +off_t fsize(const char *fn) { + struct stat st; + if (stat(fn, &st)) { + if (errno != ENOENT) + fatal(ERRM_STAT, fn); + return -1; + } + + return st.st_size; +} diff --git a/src/filepath.h b/src/filepath.h new file mode 100644 index 0000000..91f4623 --- /dev/null +++ b/src/filepath.h @@ -0,0 +1,15 @@ +#ifndef __FILEPATH_H__ +#define __FILEPATH_H__ + +#include + +extern bool is_absolute(const char* path); +extern char *remove_ext(const char *str); +extern char *take_extension(const char *target); +extern const char *make_relative(const char *target, const char *to); +extern char *transform_path(const char *target); +extern char *xbasename(const char *path); +extern bool fexists(const char *target); +extern off_t fsize(const char *fn); + +#endif diff --git a/src/util.c b/src/util.c index d2e18af..77df646 100644 --- a/src/util.c +++ b/src/util.c @@ -1,7 +1,5 @@ -#include -#include -#include #include +#include #include #include "util.h" @@ -10,38 +8,33 @@ void *safe_malloc_(size_t size, const char *file, unsigned line) { + assert(size > 0); void *ptr = malloc(size); if (!ptr) - fatal_(file, line, _PROGNAME": cannot allocate %zu bytes", size); + fatal_(file, line, ERRM_MALLOC, size); return ptr; } void *safe_realloc_(void *ptr, size_t size, const char *file, unsigned line) { + assert(size > 0 && ptr); void *ptr2 = realloc(ptr, size); if (!ptr2) - fatal_(file, line, _PROGNAME": cannot reallocate %zu bytes", size); + fatal_(file, line, ERRM_REALLOC, size); return ptr2; } char *safe_strdup_(const char *str, const char *file, unsigned line) { + assert(str); size_t len = strlen(str) + 1; char *ptr = malloc(len); if (!ptr) - fatal_(file, line, _PROGNAME": failed to duplicate string"); + fatal_(file, line, ERRM_MALLOC, len); - return memcpy(ptr, str, len);; + return memcpy(ptr, str, len); } -FILE *safe_fopen_(const char *path, const char *mode, const char *file, - unsigned line) { - FILE *temp = fopen(path, mode); - if (!temp) - fatal_(file, line, _PROGNAME": failed to open %s", path); - - return temp; -} /* For concating multiple strings into a single larger one. */ char *concat(size_t count, ...) { @@ -66,10 +59,3 @@ char *concat(size_t count, ...) { va_end(ap2); return result; } - -/* Sane and portable basename implementation */ -char *xbasename(const char *path) { - assert(path); - char *ptr = strrchr(path, '/'); - return ptr? ptr+1 : (char*) path; -} diff --git a/src/util.h b/src/util.h index 86310fc..789a404 100644 --- a/src/util.h +++ b/src/util.h @@ -1,24 +1,34 @@ #ifndef __RUTIL_H__ #define __RUTIL_H__ -#include -#include +#include #include - +/* standard error messages */ #define _PROGNAME "redo" +#define ERRM_MALLOC _PROGNAME": cannot allocate %zu bytes" +#define ERRM_REALLOC _PROGNAME": cannot reallocate %zu bytes" +#define ERRM_FOPEN _PROGNAME": failed to open %s" +#define ERRM_FREAD _PROGNAME": failed to read from %s" +#define ERRM_FCLOSE _PROGNAME": failed to close %s" +#define ERRM_WRITE _PROGNAME": failed to write to %s" +#define ERRM_CHDIR _PROGNAME": failed to change directory to %s" +#define ERRM_SETENV _PROGNAME": failed to setenv %s to %s" +#define ERRM_EXEC _PROGNAME": failed to replace child process with %s" +#define ERRM_REMOVE _PROGNAME": failed to remove %s" +#define ERRM_RENAME _PROGNAME": failed to rename %s to %s" +#define ERRM_FORK _PROGNAME": failed to fork() new process" +#define ERRM_REALPATH _PROGNAME": failed to get realpath() of %s" +#define ERRM_STAT _PROGNAME": failed to aquire stat() information about %s" + #define safe_malloc(size) safe_malloc_(size, _FILENAME, __LINE__) #define safe_realloc(ptr, size) safe_realloc_(ptr, size, _FILENAME, __LINE__) -#define safe_fopen(path, mode) safe_fopen_(path, mode, _FILENAME, __LINE__) #define safe_strdup(str) safe_strdup_(str, _FILENAME, __LINE__) -extern void *safe_malloc_(size_t size, const char *file, unsigned int line); -extern void *safe_realloc_(void *ptr, size_t size, const char *file, unsigned int line); +extern void *safe_malloc_(size_t size, const char *file, unsigned line); +extern void *safe_realloc_(void *ptr, size_t size, const char *file, unsigned line); extern char *safe_strdup_(const char *str, const char *file, unsigned line); -extern FILE *safe_fopen_(const char *path, const char *mode, - const char *file, unsigned int line); extern char *concat(size_t count, ...); -extern char *xbasename(const char *path); #endif -- cgit v1.2.3-70-g09d2