kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit b777ea65cd5dbcdc211bbccb2b5aed7b15658a0d
parent c5ce707a917dbe84bfdd3a8452479c2109e2f33e
Author: Joris Vink <joris@coders.se>
Date:   Wed, 27 Jan 2016 21:32:57 +0100

Long day .. actually add the new build flavor code.

Diffstat:
examples/json_yajl/README.md | 7+++++--
examples/json_yajl/src/json_yajl.c | 1-
examples/tasks/README.md | 6+++---
src/cli.c | 582+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
4 files changed, 457 insertions(+), 139 deletions(-)

diff --git a/examples/json_yajl/README.md b/examples/json_yajl/README.md @@ -3,14 +3,17 @@ This example demonstrates how you can use external libs in your application. In this case we link against yajl (Yet Another JSON library) in order to parse a JSON string that was POSTed to the server. +Take a peek at conf/build.conf for different build flavors and how to +link to other libraries. + Run: ``` - env LDFLAGS="-lyajl" kore run + $ kore run ``` Test: ``` - curl -i -k -d '{"foo":{"bar": "Hello world"}}' https://127.0.0.1:8888 + $ curl -i -k -d '{"foo":{"bar": "Hello world"}}' https://127.0.0.1:8888 ``` The result should echo back the foo.bar JSON path value: Hello world. diff --git a/examples/json_yajl/src/json_yajl.c b/examples/json_yajl/src/json_yajl.c @@ -89,7 +89,6 @@ page(struct http_request *req) /* Release the JSON tree now. */ yajl_tree_free(node); - kore_mem_free(body); /* Respond to the client. */ http_response(req, 200, buf->data, buf->offset); diff --git a/examples/tasks/README.md b/examples/tasks/README.md @@ -6,16 +6,16 @@ before returning to the client. Build: ``` - # env LDFLAGS="-I/path/to/libcurl -lcurl" kore build + $ kore build ``` Run: ``` - # kore run + $ kore run ``` Test: ``` - # curl -i -k https://127.0.0.1:8888/?user=astring + $ curl -i -k https://127.0.0.1:8888/?user=astring The returned data must match what you supplied in user ([a-z] string) ``` diff --git a/src/cli.c b/src/cli.c @@ -60,6 +60,15 @@ #define LD_FLAGS_MAX 30 #define CFLAGS_MAX 30 +struct buildopt { + char *name; + struct kore_buf *cflags; + struct kore_buf *ldflags; + TAILQ_ENTRY(buildopt) list; +}; + +TAILQ_HEAD(buildopt_list, buildopt); + struct cmd { const char *name; const char *descr; @@ -73,7 +82,6 @@ struct filegen { struct cfile { struct stat st; int build; - int cpp; char *name; char *fpath; char *opath; @@ -83,16 +91,18 @@ struct cfile { TAILQ_HEAD(cfile_list, cfile); static void cli_fatal(const char *, ...) __attribute__((noreturn)); - static void cli_file_close(int); static void cli_run_kore(void *); static void cli_generate_certs(void); static void cli_link_library(void *); static void cli_compile_cfile(void *); +static char *cli_trim(char *, size_t); static void cli_mkdir(const char *, int); static int cli_dir_exists(const char *); static int cli_file_exists(const char *); static void cli_cleanup_files(const char *); +static void cli_build_cflags(struct buildopt *); +static void cli_build_ldflags(struct buildopt *); static void cli_file_writef(int, const char *, ...); static void cli_file_open(const char *, int, int *); static void cli_file_remove(char *, struct dirent *); @@ -107,13 +117,24 @@ static int cli_file_requires_build(struct stat *, const char *); static void cli_find_files(const char *, void (*cb)(char *, struct dirent *)); static void cli_add_cfile(char *, char *, char *, - struct stat *, int, int); + struct stat *, int); + +static struct buildopt *cli_buildopt_new(const char *); +static struct buildopt *cli_buildopt_find(const char *); +static void cli_buildopt_cleanup(void); +static void cli_buildopt_parse(const char *); +static void cli_buildopt_cflags(struct buildopt *, const char *); +static void cli_buildopt_ldflags(struct buildopt *, const char *); + +static void cli_flavor_load(void); +static void cli_flavor_change(const char *); static void cli_run(int, char **); static void cli_help(int, char **); static void cli_build(int, char **); static void cli_clean(int, char **); static void cli_create(int, char **); +static void cli_flavor(int, char **); static void file_create_src(void); static void file_create_config(void); @@ -125,6 +146,7 @@ static struct cmd cmds[] = { { "build", "build an application", cli_build }, { "clean", "cleanup the build files", cli_clean }, { "create", "create a new application skeleton", cli_create }, + { "flavor", "switch build flavor", cli_flavor }, { NULL, NULL, NULL } }; @@ -159,7 +181,7 @@ static const char *src_data = "}\n"; static const char *config_data = - "# Placeholder configuration\n" + "# %s configuration\n" "\n" "bind\t\t127.0.0.1 8888\n" "load\t\t./%s.so\n" @@ -175,6 +197,26 @@ static const char *config_data = "\tstatic\t/\tpage\n" "}\n"; +static const char *build_data = + "# %s build config\n" + "# You can switch flavors using: kore flavor [newflavor]\n" + "\n" + "# The cflags below are shared between flavors\n" + "cflags=-Wall -Wmissing-declarations -Wshadow\n" + "cflags=-Wstrict-prototypes -Wmissing-prototypes\n" + "cflags=-Wpointer-arith -Wcast-qual -Wsign-compare\n" + "\n" + "dev {\n" + " # These cflags are added to the shared ones when\n" + " # you build the \"dev\" flavor.\n" + " cflags=-g\n" + "}\n" + "\n" + "#prod {\n" + "# You can specify additional CFLAGS here which are only\n" + "# included if you build with the \"prod\" flavor.\n" + "#}\n"; + #if !defined(KORE_NO_TLS) static const char *dh2048_data = "-----BEGIN DH PARAMETERS-----\n" @@ -187,15 +229,22 @@ static const char *dh2048_data = "-----END DH PARAMETERS-----"; #endif -static const char *gitignore_data = "*.o\n.objs\n%s.so\nassets.h\ncert\n"; +static const char *gitignore = "*.o\n.flavor\n.objs\n%s.so\nassets.h\ncert\n"; static int s_fd = -1; +static int verbose = 0; static char *appl = NULL; static char *rootdir = NULL; static char *compiler = "gcc"; static struct cfile_list source_files; +static struct buildopt_list build_options; static int cfiles_count; static struct cmd *command = NULL; +static int cflags_count = 0; +static int ldflags_count = 0; +static char *flavor = NULL; +static char *cflags[CFLAGS_MAX]; +static char *ldflags[LD_FLAGS_MAX]; void kore_cli_usage(int local) @@ -229,6 +278,9 @@ kore_cli_main(int argc, char **argv) (void)umask(S_IWGRP|S_IWOTH); + if ((flavor = strchr(argv[0], ':')) != NULL) + *(flavor)++ = '\0'; + for (i = 0; cmds[i].name != NULL; i++) { if (!strcmp(argv[0], cmds[i].name)) { argc--; @@ -285,40 +337,96 @@ cli_create(int argc, char **argv) } static void -cli_build(int argc, char **argv) +cli_flavor(int argc, char **argv) { - struct cfile *cf; - struct timeval times[2]; - int requires_relink; - char pwd[PATH_MAX], *src_path, *assets_header; - char *assets_path, *p, *obj_path, *cpath, *config; + struct buildopt *bopt; + char pwd[MAXPATHLEN], *conf; - if (argc == 0) { - if (getcwd(pwd, sizeof(pwd)) == NULL) - cli_fatal("could not get cwd: %s", errno_s); + if (getcwd(pwd, sizeof(pwd)) == NULL) + cli_fatal("could not get cwd: %s", errno_s); - rootdir = "."; - appl = basename(pwd); + appl = basename(pwd); + (void)cli_vasprintf(&conf, "conf/%s.conf", appl); + if (!cli_dir_exists("conf") || !cli_file_exists(conf)) + cli_fatal("%s doesn't appear to be a kore app", appl); + free(conf); + + TAILQ_INIT(&build_options); + (void)cli_buildopt_new("_default"); + cli_buildopt_parse("conf/build.conf"); + + if (argc == 0) { + cli_flavor_load(); + TAILQ_FOREACH(bopt, &build_options, list) { + if (!strcmp(bopt->name, "_default")) + continue; + if (!strcmp(bopt->name, flavor)) { + printf("* %s\n", bopt->name); + } else { + printf(" %s\n", bopt->name); + } + } } else { - appl = argv[0]; - rootdir = appl; + cli_flavor_change(argv[0]); + printf("changed build flavor to: %s\n", argv[0]); } + cli_buildopt_cleanup(); +} + +static void +cli_build(int argc, char **argv) +{ + struct cfile *cf; + struct buildopt *bopt; + struct timeval times[2]; + char *build_path; + int requires_relink, l; + char *sofile, *config, *data; + char *assets_path, *p, *obj_path, *cpath; + char pwd[PATH_MAX], *src_path, *assets_header; + + if (argc == 1 && !strcmp(argv[0], "-v")) + verbose = 1; + + if (getcwd(pwd, sizeof(pwd)) == NULL) + cli_fatal("could not get cwd: %s", errno_s); + + rootdir = "."; + appl = basename(pwd); + if ((p = getenv("CC")) != NULL) compiler = p; cfiles_count = 0; TAILQ_INIT(&source_files); + TAILQ_INIT(&build_options); (void)cli_vasprintf(&src_path, "%s/src", rootdir); (void)cli_vasprintf(&assets_path, "%s/assets", rootdir); (void)cli_vasprintf(&config, "%s/conf/%s.conf", rootdir, appl); (void)cli_vasprintf(&assets_header, "%s/src/assets.h", rootdir); + (void)cli_vasprintf(&build_path, "%s/conf/build.conf", rootdir); + if (!cli_dir_exists(src_path) || !cli_file_exists(config)) cli_fatal("%s doesn't appear to be a kore app", appl); - free(config); + cli_flavor_load(); + bopt = cli_buildopt_new("_default"); + if (!cli_file_exists(build_path)) { + l = cli_vasprintf(&data, build_data, appl); + cli_file_create("conf/build.conf", data, l); + free(data); + } + + cli_buildopt_parse(build_path); + free(build_path); + + printf("building %s (%s)\n", appl, flavor); + cli_build_cflags(bopt); + cli_build_ldflags(bopt); + (void)cli_vasprintf(&obj_path, "%s/.objs", rootdir); if (!cli_dir_exists(obj_path)) cli_mkdir(obj_path, 0755); @@ -342,10 +450,9 @@ cli_build(int argc, char **argv) /* Build all source files. */ cli_find_files(src_path, cli_register_cfile); - free(src_path); - requires_relink = 0; + requires_relink = 0; TAILQ_FOREACH(cf, &source_files, list) { if (cf->build == 0) continue; @@ -373,12 +480,18 @@ cli_build(int argc, char **argv) } free(cpath); + (void)cli_vasprintf(&sofile, "%s.so", appl); + if (!cli_file_exists(sofile)) + requires_relink++; + free(sofile); + if (requires_relink) { cli_spawn_proc(cli_link_library, NULL); printf("%s built successfully!\n", appl); } else { - printf("nothing to be done\n"); + printf("nothing to be done!\n"); } + cli_buildopt_cleanup(); } static void @@ -432,11 +545,14 @@ file_create_config(void) char *name, *data; (void)cli_vasprintf(&name, "conf/%s.conf", appl); - l = cli_vasprintf(&data, config_data, appl); + l = cli_vasprintf(&data, config_data, appl, appl); cli_file_create(name, data, l); - free(name); free(data); + + l = cli_vasprintf(&data, build_data, appl); + cli_file_create("conf/build.conf", data, l); + free(data); } static void @@ -445,7 +561,7 @@ file_create_gitignore(void) int l; char *data; - l = cli_vasprintf(&data, gitignore_data, appl); + l = cli_vasprintf(&data, gitignore, appl); cli_file_create(".gitignore", data, l); free(data); } @@ -617,7 +733,7 @@ cli_build_asset(char *fpath, struct dirent *dp) cli_write_asset(name, ext); *ext = '_'; - cli_add_cfile(name, cpath, opath, &st, 0, 0); + cli_add_cfile(name, cpath, opath, &st, 0); kore_mem_free(name); return; } @@ -676,13 +792,13 @@ cli_build_asset(char *fpath, struct dirent *dp) *--ext = '.'; /* Register the .c file now (cpath is free'd later). */ - cli_add_cfile(name, cpath, opath, &st, 1, 0); + cli_add_cfile(name, cpath, opath, &st, 1); kore_mem_free(name); } static void cli_add_cfile(char *name, char *fpath, char *opath, struct stat *st, - int build, int cpp) + int build) { struct cfile *cf; @@ -691,7 +807,6 @@ cli_add_cfile(char *name, char *fpath, char *opath, struct stat *st, cf->st = *st; cf->build = build; - cf->cpp = cpp; cf->fpath = fpath; cf->opath = opath; cf->name = kore_strdup(name); @@ -704,27 +819,21 @@ cli_register_cfile(char *fpath, struct dirent *dp) { struct stat st; char *ext, *opath; - int cpp; if ((ext = strrchr(fpath, '.')) == NULL || (strcmp(ext, ".c") && strcmp(ext, ".cpp"))) return; - if (!strcmp(ext, ".cpp")) - cpp = 1; - else - cpp = 0; - if (stat(fpath, &st) == -1) cli_fatal("stat(%s): %s", fpath, errno_s); (void)cli_vasprintf(&opath, "%s/.objs/%s.o", rootdir, dp->d_name); if (!cli_file_requires_build(&st, opath)) { - cli_add_cfile(dp->d_name, fpath, opath, &st, 0, cpp); + cli_add_cfile(dp->d_name, fpath, opath, &st, 0); return; } - cli_add_cfile(dp->d_name, fpath, opath, &st, 1, cpp); + cli_add_cfile(dp->d_name, fpath, opath, &st, 1); } static void @@ -876,75 +985,15 @@ cli_generate_certs(void) static void cli_compile_cfile(void *arg) { - int idx, f, i; + int idx, i; struct cfile *cf = arg; - char *flags[CFLAGS_MAX], *p; - char *args[32 + CFLAGS_MAX], *ipath[2], *cppstandard; -#if defined(KORE_USE_PGSQL) - char *ppath; -#endif - - (void)cli_vasprintf(&ipath[0], "-I%s/src", rootdir); - (void)cli_vasprintf(&ipath[1], "-I%s/src/includes", rootdir); + char *args[32 + CFLAGS_MAX]; idx = 0; args[idx++] = compiler; - args[idx++] = ipath[0]; - args[idx++] = ipath[1]; -#if defined(PREFIX) - (void)cli_vasprintf(&args[idx++], "-I%s/include", PREFIX); -#else - args[idx++] = "-I/usr/local/include"; -#endif - -#if defined(__MACH__) - /* Add default openssl include path from homebrew / ports under OSX. */ - args[idx++] = "-I/opt/local/include"; - args[idx++] = "-I/usr/local/opt/openssl/include"; -#endif - - /* Add any user specified flags. */ - if ((p = getenv("CFLAGS")) != NULL) - f = kore_split_string(p, " ", flags, CFLAGS_MAX); - else - f = 0; - for (i = 0; i < f; i++) - args[idx++] = flags[i]; - -#if defined(KORE_USE_PGSQL) - (void)cli_vasprintf(&ppath, "-I%s", PGSQL_INCLUDE_PATH); - args[idx++] = ppath; -#endif - -#if defined(KORE_NO_HTTP) - args[idx++] = "-DKORE_NO_HTTP"; -#endif -#if defined(KORE_NO_TLS) - args[idx++] = "-DKORE_NO_TLS"; -#endif - args[idx++] = "-Wall"; - args[idx++] = "-Wmissing-declarations"; - args[idx++] = "-Wshadow"; - args[idx++] = "-Wpointer-arith"; - args[idx++] = "-Wcast-qual"; - args[idx++] = "-Wsign-compare"; - args[idx++] = "-fPIC"; - args[idx++] = "-g"; - - if (cf->cpp) { - args[idx++] = "-Woverloaded-virtual"; - args[idx++] = "-Wold-style-cast"; - args[idx++] = "-Wnon-virtual-dtor"; - - if ((p = getenv("CXXSTD")) != NULL) { - (void)cli_vasprintf(&cppstandard, "-std=%s", p); - args[idx++] = cppstandard; - } - } else { - args[idx++] = "-Wstrict-prototypes"; - args[idx++] = "-Wmissing-prototypes"; - } + for (i = 0; i < cflags_count; i++) + args[idx++] = cflags[i]; args[idx++] = "-c"; args[idx++] = cf->fpath; @@ -959,47 +1008,20 @@ static void cli_link_library(void *arg) { struct cfile *cf; - int idx, f, i, has_cpp; + int idx, i; + char *libname; char *args[cfiles_count + 11 + LD_FLAGS_MAX]; - char *p, *libname, *flags[LD_FLAGS_MAX], *cpplib; - - if ((p = getenv("LDFLAGS")) != NULL) - f = kore_split_string(p, " ", flags, LD_FLAGS_MAX); - else - f = 0; (void)cli_vasprintf(&libname, "%s/%s.so", rootdir, appl); idx = 0; args[idx++] = compiler; -#if defined(__MACH__) - args[idx++] = "-dynamiclib"; - args[idx++] = "-undefined"; - args[idx++] = "suppress"; - args[idx++] = "-flat_namespace"; -#else - args[idx++] = "-shared"; -#endif - - has_cpp = 0; - TAILQ_FOREACH(cf, &source_files, list) { - if (cf->cpp) - has_cpp = 1; + TAILQ_FOREACH(cf, &source_files, list) args[idx++] = cf->opath; - } - if (has_cpp) { - if ((p = getenv("CXXLIB")) != NULL) { - (void)cli_vasprintf(&cpplib, "-l%s", p); - args[idx++] = cpplib; - } else { - args[idx++] = "-lstdc++"; - } - } - - for (i = 0; i < f; i++) - args[idx++] = flags[i]; + for (i = 0; i < ldflags_count; i++) + args[idx++] = ldflags[i]; args[idx++] = "-o"; args[idx++] = libname; @@ -1024,6 +1046,285 @@ cli_run_kore(void *arg) } static void +cli_buildopt_parse(const char *path) +{ + FILE *fp; + struct buildopt *bopt; + char buf[BUFSIZ], *p, *t, *s; + + if ((fp = fopen(path, "r")) == NULL) + cli_fatal("cli_buildopt_parse: fopen(%s): %s", path, errno_s); + + bopt = NULL; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + p = buf; + buf[strcspn(buf, "\n")] = '\0'; + + while (isspace(*p)) + p++; + if (p[0] == '#' || p[0] == '\0') + continue; + + for (t = p; *t != '\0'; t++) { + if (*t == '\t') + *t = ' '; + } + + if (bopt != NULL && !strcmp(p, "}")) { + bopt = NULL; + continue; + } + + if (bopt == NULL) { + if ((t = strchr(p, '=')) != NULL) + goto parse_option; + if ((t = strchr(p, ' ')) == NULL) + cli_fatal("unexpected '%s'", p); + *(t)++ = '\0'; + if (strcmp(t, "{")) + cli_fatal("expected '{', got '%s'", t); + bopt = cli_buildopt_new(p); + continue; + } + + if ((t = strchr(p, '=')) == NULL) { + printf("bad buildopt line: '%s'\n", p); + continue; + } + +parse_option: + *(t)++ = '\0'; + + cli_trim(p, strlen(p)); + cli_trim(t, strlen(t)); + + while (isspace(*p)) + p++; + s = p + strlen(p) - 1; + while (isspace(*s)) + *(s)-- = '\0'; + + while (isspace(*t)) + t++; + s = t + strlen(t) - 1; + while (isspace(*s)) + *(s)-- = '\0'; + + if (!strcasecmp(p, "cflags")) { + cli_buildopt_cflags(bopt, t); + } else if (!strcasecmp(p, "ldflags")) { + cli_buildopt_ldflags(bopt, t); + } else { + printf("ignoring unknown option %s\n", p); + } + } +} + +static struct buildopt * +cli_buildopt_new(const char *name) +{ + struct buildopt *bopt; + + bopt = kore_malloc(sizeof(*bopt)); + bopt->cflags = NULL; + bopt->ldflags = NULL; + bopt->name = kore_strdup(name); + + TAILQ_INSERT_TAIL(&build_options, bopt, list); + return (bopt); +} + +static struct buildopt * +cli_buildopt_find(const char *name) +{ + struct buildopt *bopt; + + TAILQ_FOREACH(bopt, &build_options, list) { + if (!strcmp(bopt->name, name)) + return (bopt); + } + + return (NULL); +} + +static void +cli_buildopt_cleanup(void) +{ + struct buildopt *bopt, *next; + + for (bopt = TAILQ_FIRST(&build_options); bopt != NULL; bopt = next) { + next = TAILQ_NEXT(bopt, list); + TAILQ_REMOVE(&build_options, bopt, list); + + if (bopt->cflags != NULL) + kore_buf_free(bopt->cflags); + if (bopt->ldflags != NULL) + kore_buf_free(bopt->ldflags); + kore_mem_free(bopt); + } +} + +static void +cli_buildopt_cflags(struct buildopt *bopt, const char *string) +{ + if (bopt == NULL) { + if ((bopt = cli_buildopt_find("_default")) == NULL) + cli_fatal("no _default build options"); + } + + if (bopt->cflags == NULL) + bopt->cflags = kore_buf_create(128); + + kore_buf_appendf(bopt->cflags, "%s ", string); +} + +static void +cli_buildopt_ldflags(struct buildopt *bopt, const char *string) +{ + if (bopt == NULL) { + if ((bopt = cli_buildopt_find("_default")) == NULL) + cli_fatal("no _default build options"); + } + + if (bopt->ldflags == NULL) + bopt->ldflags = kore_buf_create(128); + + kore_buf_appendf(bopt->ldflags, "%s ", string); +} + +static void +cli_build_cflags(struct buildopt *bopt) +{ + struct buildopt *obopt; + char *string; + + if ((obopt = cli_buildopt_find(flavor)) == NULL) + cli_fatal("no such build flavor: %s", flavor); + + if (bopt->cflags == NULL) + bopt->cflags = kore_buf_create(128); + + kore_buf_appendf(bopt->cflags, + "-fPIC -I%s/src -I%s/src/includes ", rootdir, rootdir); +#if defined(PREFIX) + kore_buf_appendf(bopt->cflags, "-I%s/include ", PREFIX); +#else + kore_buf_appendf(bopt->cflags, "-I/usr/local/include "); +#endif +#if defined(__MACH__) + /* Add default openssl include path from homebrew / ports under OSX. */ + kore_buf_appendf(bopt->cflags, "-I/opt/local/include "); + kore_buf_appendf(bopt->cflags, "-I/usr/local/opt/openssl/include "); +#endif +#if defined(KORE_USE_PGSQL) + kore_buf_appendf(bopt->cflags, "-I%s ", PGSQL_INCLUDE_PATH); +#endif +#if defined(KORE_NO_HTTP) + kore_buf_appendf(bopt->cflags, "-DKORE_NO_HTTP "); +#endif +#if defined(KORE_NO_TLS) + kore_buf_appendf(bopt->cflags, "-DKORE_NO_TLS "); +#endif + + if (obopt != NULL && obopt->cflags != NULL) { + kore_buf_append(bopt->cflags, obopt->cflags->data, + obopt->cflags->offset); + } + + string = kore_buf_stringify(bopt->cflags); + if (verbose) + printf("CFLAGS=%s\n", string); + cflags_count = kore_split_string(string, " ", cflags, CFLAGS_MAX); +} + +static void +cli_build_ldflags(struct buildopt *bopt) +{ + struct buildopt *obopt; + char *string; + + if ((obopt = cli_buildopt_find(flavor)) == NULL) + cli_fatal("no such build flavor: %s", flavor); + + if (bopt->ldflags == NULL) + bopt->ldflags = kore_buf_create(128); + +#if defined(__MACH__) + kore_buf_appendf(bopt->ldflags, + "-dynamiclib -undefined suppress -flat_namespace "); +#else + kore_buf_appendf(bopt->ldflags, "-shared "); +#endif + + if (obopt != NULL && obopt->ldflags != NULL) { + kore_buf_append(bopt->ldflags, obopt->ldflags->data, + obopt->ldflags->offset); + } + + string = kore_buf_stringify(bopt->ldflags); + if (verbose) + printf("LDFLAGS=%s\n", string); + ldflags_count = kore_split_string(string, " ", ldflags, LD_FLAGS_MAX); +} + +static void +cli_flavor_load(void) +{ + FILE *fp; + char buf[BUFSIZ], pwd[MAXPATHLEN], *p, *conf; + + if (getcwd(pwd, sizeof(pwd)) == NULL) + cli_fatal("could not get cwd: %s", errno_s); + + appl = basename(pwd); + (void)cli_vasprintf(&conf, "conf/%s.conf", appl); + + if (!cli_dir_exists("conf") || !cli_file_exists(conf)) + cli_fatal("%s doesn't appear to be a kore app", appl); + free(conf); + + if ((fp = fopen(".flavor", "r")) == NULL) { + flavor = kore_strdup("dev"); + return; + } + + if (fgets(buf, sizeof(buf), fp) == NULL) + cli_fatal("failed to read flavor from file"); + + if ((p = strchr(buf, '\n')) != NULL) + *p = '\0'; + + flavor = kore_strdup(buf); + (void)fclose(fp); +} + +static void +cli_flavor_change(const char *name) +{ + FILE *fp; + int ret; + struct buildopt *bopt; + + if ((bopt = cli_buildopt_find(name)) == NULL) + cli_fatal("no such flavor: %s", name); + + if ((fp = fopen(".flavor.tmp", "w")) == NULL) + cli_fatal("failed to open temporary file to save flavor"); + + ret = fprintf(fp, "%s\n", name); + if (ret == -1 || (size_t)ret != (strlen(name) + 1)) + cli_fatal("failed to write new build flavor"); + + (void)fclose(fp); + + if (rename(".flavor.tmp", ".flavor") == -1) + cli_fatal("failed to replace build flavor"); + + cli_clean(0, NULL); +} + +static void cli_spawn_proc(void (*cb)(void *), void *arg) { pid_t pid; @@ -1074,6 +1375,21 @@ cli_cleanup_files(const char *spath) printf("couldn't rmdir %s\n", spath); } +static char * +cli_trim(char *string, size_t len) +{ + char *end; + + end = string + strlen(string) - 1; + while (isspace(*string)) + string++; + + while (isspace(*end) && end > string) + *(end)-- = '\0'; + + return (string); +} + static void cli_fatal(const char *fmt, ...) {