aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sh10
-rw-r--r--out/redo-ifchange.do2
-rw-r--r--out/redo.do2
-rw-r--r--src/build.c135
-rw-r--r--src/build.h5
-rw-r--r--src/filepath.c121
-rw-r--r--src/filepath.h15
-rw-r--r--src/util.c30
-rw-r--r--src/util.h28
9 files changed, 217 insertions, 131 deletions
diff --git a/build.sh b/build.sh
index 6bc4fc4..072ea88 100755
--- a/build.sh
+++ b/build.sh
@@ -1,11 +1,15 @@
-#!/bin/sh -e
+#!/bin/sh -ex
. out/config.sh
$CC $CFLAGS -o out/util.o -c src/util.c
$CC $CFLAGS -o out/build.o -c src/build.c
+$CC $CFLAGS -o out/filepath.o -c src/filepath.c
$CC $CFLAGS -o out/redo.o -c src/redo.c
$CC $CFLAGS -o out/redo-ifchange.o -c src/redo-ifchange.c
-$CC -o out/redo out/redo.o out/util.o out/build.o $LDFLAGS
-$CC -o out/redo-ifchange out/redo-ifchange.o out/util.o out/build.o $LDFLAGS
+$CC -o out/redo out/redo.o out/util.o out/build.o out/filepath.o $LDFLAGS
+$CC -o out/redo-ifchange out/redo-ifchange.o out/util.o out/build.o out/filepath.o $LDFLAGS
+
+# TODO: just for convenience, should be removed as soon as redo can build itself
+sudo install out/redo out/redo-ifchange /usr/bin/
echo "Finished compiling"
diff --git a/out/redo-ifchange.do b/out/redo-ifchange.do
index 84941ce..5a1112b 100644
--- a/out/redo-ifchange.do
+++ b/out/redo-ifchange.do
@@ -1,5 +1,5 @@
. ./config.sh
-DEPS="redo-ifchange.o build.o util.o"
+DEPS="redo-ifchange.o build.o util.o filepath.o"
redo-ifchange $DEPS config.sh
$CC -o $3 $DEPS $LDFLAGS
diff --git a/out/redo.do b/out/redo.do
index dce65d4..08ab81d 100644
--- a/out/redo.do
+++ b/out/redo.do
@@ -1,5 +1,5 @@
. ./config.sh
-DEPS="redo.o build.o util.o"
+DEPS="redo.o build.o util.o filepath.o"
redo-ifchange $DEPS config.sh
$CC -o $3 $DEPS $LDFLAGS
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 <stdbool.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#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 <stdbool.h>
+
+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 <stdlib.h>
-#include <string.h>
-#include <stdint.h>
#include <stdarg.h>
+#include <stdint.h>
#include <assert.h>
#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 <stdio.h>
-#include <stdlib.h>
+#include <stdbool.h>
#include <stddef.h>
-
+/* 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