commit d847c2fcb6e0a488d1342fd75bfbe1a3135c7ebf
parent 90fadf3980c5c531e105edb1843a84abb2e2bea5
Author: vx-clutch <[email protected]>
Date: Sun, 28 Sep 2025 22:06:39 -0400
save
Diffstat:
20 files changed, 630 insertions(+), 444 deletions(-)
diff --git a/.gitmodules b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "gcklib"]
+ path = gcklib
+ url = https://github.com/gck-org/gcklib
diff --git a/Makefile b/Makefile
@@ -1,11 +1,11 @@
PREFIX = /usr/bin/
-YAIT_SRCS := $(wildcard src/*.c)
+YAIT_SRCS := $(wildcard src/*.c) $(wildcard lib/*.c)
YAIT_OBJS := $(patsubst src/%.c,build/obj/%.o,$(YAIT_SRCS))
YAIT := bin/yait
-ALLOWED_DIRS = doc include man src tools
+ALLOWED_DIRS = doc include man src tools lib
DISTDIRS := $(sort $(shell find . -maxdepth 1 -type d -not -name '.' -printf '%f\n'))
-include config.mak
@@ -23,10 +23,10 @@ build:
mkdir -p build/obj
build/obj/%.o: src/%.c config.mak
- $(CC) $(CFLAGS) -DCOMMIT=$(shell git rev-list --count --all) -Iinclude -c $< -o $@
+ $(CC) $(CFLAGS) -DCOMMIT=$(shell git rev-list --count --all) -Iinclude -Ilib -c $< -o $@
$(YAIT): $(YAIT_OBJS)
- $(CC) $(CFLAGS) -Iinclude -DCOMMIT=$(shell git rev-list --count --all) $^ -o $@
+ $(CC) $(CFLAGS) -Iinclude -Ilib -DCOMMIT=$(shell git rev-list --count --all) $^ -o $@
endif
diff --git a/TODO b/TODO
@@ -3,7 +3,7 @@ GCK yait - TODO
Todo:
* Refactor to more GNU style main
- * Implement the extra option
+ * Factor out str_dup
* Build tarball with Makefile
* Polish for 1.0
diff --git a/gcklib b/gcklib
@@ -0,0 +1 @@
+Subproject commit 432788eb5f78afb08b7c011b81118c7f9bc520d9
diff --git a/include/yait.h b/include/yait.h
@@ -9,25 +9,10 @@
#ifndef YAIT_H
#define YAIT_H
-typedef enum { MIT, GPL, BSD, UNL, LCOUNT } licence_t;
-typedef enum { MAKE, CMAKE, AUTOTOOLS, BARE, BCOUNT } built_t;
+typedef enum { MIT, GPL, BSD } licence_t;
typedef struct {
licence_t licence;
- built_t build;
-
- bool lib;
-
- bool git;
- bool flat;
- bool open_editor;
-
- struct {
- bool build_nob;
- bool tools_format;
- bool tools_Cleanup;
- } extra;
-
char *project;
char *author;
char *editor;
diff --git a/lib/err.c b/lib/err.c
@@ -0,0 +1,151 @@
+/* Copyright (C) GCK
+ *
+ * This file is part of gcklib
+ *
+ * This project and file is licensed under the BSD-3-Clause license.
+ * <https://opensource.org/licenses/BSD-3-Clause>
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "err.h"
+
+#define RESET "\e[0m"
+#define ERROR "\e[1;91m"
+#define WARN "\e[1;95m"
+#define NOTE "\e[1;94m"
+#define HINT "\e[38;5;166m"
+
+static bool __check(void)
+{
+ static int done = 0;
+ static bool cached = false;
+
+ if (!done) {
+ const char *term = getenv("TERM");
+ cached = isatty(STDOUT_FILENO) && term != NULL &&
+ (strstr(term, "color") != NULL ||
+ strstr(term, "ansi") != NULL ||
+ strstr(term, "xterm") != NULL);
+ done = 1;
+ }
+
+ return cached;
+}
+
+void errorf(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ if (__check()) {
+ fprintf(stderr, "%serror%s: ", ERROR, RESET);
+ } else {
+ fputs("error: ", stderr);
+ }
+
+ vfprintf(stderr, format, args);
+ fputc('\n', stderr);
+
+ va_end(args);
+}
+void fatalf(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ if (__check()) {
+ fprintf(stderr, "%sfatal%s: ", ERROR, RESET);
+ } else {
+ fputs("fatal: ", stderr);
+ }
+
+ vfprintf(stderr, format, args);
+ fputc('\n', stderr);
+
+ va_end(args);
+
+ exit(EXIT_FAILURE);
+}
+void warnf(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ if (__check()) {
+ fprintf(stderr, "%swarning%s: ", WARN, RESET);
+ } else {
+ fputs("warning: ", stderr);
+ }
+
+ vfprintf(stderr, format, args);
+ fputc('\n', stderr);
+
+ va_end(args);
+}
+void notef(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ if (__check()) {
+ fprintf(stderr, "%snote%s: ", NOTE, RESET);
+ } else {
+ fputs("note: ", stderr);
+ }
+
+ vfprintf(stderr, format, args);
+ fputc('\n', stderr);
+
+ va_end(args);
+}
+void hintf(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+ if (__check()) {
+ fprintf(stderr, "%shint: ", HINT);
+ } else {
+ fputs("hint: ", stderr);
+ }
+
+ vfprintf(stderr, format, args);
+
+ if (__check()) {
+ fprintf(stderr, "%s\n", RESET);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ va_end(args);
+}
+
+void errorfa(int code)
+{
+ errorf(strerror(code));
+}
+
+void fatalfa(int code)
+{
+ fatalf(strerror(code));
+}
+
+void notefa(int code)
+{
+ notef(strerror(code));
+}
+
+void warnfa(int code)
+{
+ warnf(strerror(code));
+}
+
+void hintfa(int code)
+{
+ hintf(strerror(code));
+}
diff --git a/lib/err.h b/lib/err.h
@@ -0,0 +1,24 @@
+/* Copyright (C) GCK
+ *
+ * This file is part of gcklib
+ *
+ * This project and file is licensed under the BSD-3-Clause licence.
+ * <https://opensource.org/licence/bsd-3-clause>
+ */
+
+#ifndef ERR_H
+#define ERR_H
+
+void errorf(const char *format, ...);
+void fatalf(const char *format, ...);
+void notef(const char *format, ...);
+void warnf(const char *format, ...);
+void hintf(const char *format, ...);
+
+void errorfa(int code);
+void fatalfa(int code);
+void notefa(int code);
+void warnfa(int code);
+void hintfa(int code);
+
+#endif
diff --git a/lib/proginfo.c b/lib/proginfo.c
@@ -0,0 +1,52 @@
+/* Copyright (C) GCK
+ *
+ * This file is part of gcklib
+ *
+ * This project and file is licensed under the BSD-3-Clause licence.
+ * <https://opensource.org/licence/bsd-3-clause>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../config.h"
+
+#include "proginfo.h"
+
+const char *prog_name = "";
+
+void set_prog_name(const char *name)
+{
+ prog_name = prog_name ? name : "";
+}
+
+void emit_try_help()
+{
+ printf("Try '%s --help' for more information\n", prog_name);
+}
+
+void emit_version()
+{
+ printf("\
+%s %s %d\n\
+Copyright (C) %d GCK.\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRNTY, to the extent permitted by law.\n\
+",
+ prog_name, VERSION, COMMIT, YEAR);
+}
+
+int parse_standard_options(int argc, char **argv, void (*usage)(int),
+ void (*version)())
+{
+ for (int i = 0; i < argc; ++i) {
+ if (!strcmp(argv[i], "--help")) {
+ usage(0);
+ exit(EXIT_SUCCESS);
+ } else if (!strcmp(argv[i], "--version")) {
+ emit_version();
+ exit(EXIT_SUCCESS);
+ }
+ }
+ return 0;
+}
diff --git a/lib/proginfo.h b/lib/proginfo.h
@@ -0,0 +1,22 @@
+/* Copyright (C) GCK
+ *
+ * This file is part of gcklib
+ *
+ * This project and file is licensed under the BSD-3-Clause licence.
+ * <https://opensource.org/licence/bsd-3-clause>
+ */
+
+#ifndef proginfo_H
+#define proginfo_H
+
+extern const char *prog_name;
+
+void set_prog_name(const char *name);
+
+void emit_try_help();
+void emit_version();
+
+int parse_standard_options(int argc, char **argv, void (*usage)(int),
+ void (*version)());
+
+#endif
diff --git a/lib/str_dup.c b/lib/str_dup.c
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+#include <string.h>
+#include "xmem.h"
+
+#include "str_dup.h"
+
+char *str_dup(char *s)
+{
+ char *new = xmalloc(strlen(s) + 1);
+ strcpy(new, s);
+ return new;
+}
diff --git a/lib/str_dup.h b/lib/str_dup.h
@@ -0,0 +1,6 @@
+#ifndef STR_DUP_H
+#define STR_DUP_H
+
+char *str_dup(char *s);
+
+#endif
diff --git a/lib/xmem.c b/lib/xmem.c
@@ -0,0 +1,36 @@
+/* Copyright (C) GCK
+ *
+ * This file is part of gcklib
+ *
+ * This project and file is licensed under the BSD-3-Clause licence.
+ * <https://opensource.org/licence/bsd-3-clause>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "err.h"
+
+#include "xmem.h"
+
+void *ensure_nonnull(void *ptr)
+{
+ if (ptr == NULL)
+ fatalf("memory exhausted");
+ return ptr;
+}
+
+void *xmalloc(size_t size)
+{
+ return ensure_nonnull(malloc(size));
+}
+
+void *xrealloc(void *ptr, size_t size)
+{
+ return ensure_nonnull(realloc(ptr, size));
+}
+
+void *xcalloc(size_t nmemb, size_t size)
+{
+ return ensure_nonnull(calloc(nmemb, size));
+}
diff --git a/lib/xmem.h b/lib/xmem.h
@@ -0,0 +1,18 @@
+/* Copyright (C) GCK
+ *
+ * This file is part of gcklib
+ *
+ * This project and file is licensed under the BSD-3-Clause licence.
+ * <https://opensource.org/licence/bsd-3-clause>
+ */
+
+#ifndef xmem_H
+#define xmem_H
+
+#include <stdlib.h>
+
+void *xmalloc(size_t size);
+void *xrealloc(void *ptr, size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+
+#endif
diff --git a/src/create_project.c b/src/create_project.c
@@ -17,166 +17,166 @@
#include "licences/licences.h"
#include "util.h"
-int create_project(manifest_t manifest)
-{
- int status;
- char buffer[BUFSIZ], *main_source;
-
- status = mkdir_p(manifest.project);
- if (status)
- return 1;
-
- status = chdir(manifest.project);
- if (status)
- return 1;
-
- cfprintf(
- "README",
- "%s ( short description )\n\nThis cool project actions adverbly.\n",
- manifest.project);
-
- if (manifest.build != BARE) {
- main_source = manifest.flat ? "main.c" : "src/main.c";
- cfprintf(main_source, "#include <stdio.h>\n"
- "\n"
- "int main()\n"
- "{\n"
- "\tputs(\"Hello, World!\");\n"
- "\treturn 0;\n"
- "}\n");
- }
- char *upr_name = tostrupr(manifest.project);
- switch (manifest.build) {
- case MAKE:
- cfprintf(
- "Makefile",
- "PREFIX = /usr/bin\n"
- "\n"
- "%s_SRCS := $(wildcard src/*.c)\n"
- "%s_OBJS := $(patsubst src/%.c,build/obj/%.o,$(%s_SRCS))"
- "\n"
- "%s := bin/%s"
- "\n"
- "-include config.mak\n"
- "\n"
- "ifeq ($(wildcard config.mak),)\n",
- upr_name, upr_name, upr_name, upr_name,
- manifest.project);
- break;
- case CMAKE:
- cfprintf("CMakeLists.txt",
- "cmake_minimum_required(VERSION 3.16)\n"
- "\n"
- "project(%s\n"
- " VERSION 0.1.0\n"
- " LANGUAGES C)\n"
- "\n"
- "set(CMAKE_C_STANDARD 23)\n"
- "set(CMAKE_C_STANDARD_REQUIRED ON)\n"
- "set(CMAKE_C_EXTENSIONS OFF)\n"
- "\n"
- "include(GNUInstallDirs)\n"
- "\n"
- "add_executable(%s\n"
- " src/main.c\n"
- ")\n"
- "\n"
- "install(TARGETS %s\n"
- " RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n"
- ")\n",
- manifest.project, manifest.project, manifest.project);
- break;
- case AUTOTOOLS:
- cfprintf("configure.ac",
- "AC_PREREQ([2.69])\n"
- "AC_INIT([%s], [0.1], [[email protected]])\n"
- "AM_INIT_AUTOMAKE([foreign -Wall])\n"
- "AC_CONFIG_SRCDIR([src/main.c])\n"
- "AC_CONFIG_HEADERS([config.h])"
- "AC_PROG_CC\n"
- "AC_CONFIG_FILES([Makefile src/Makefile])\n"
- "AC_OUTPUT\n",
- manifest.project);
- cfprintf("Makefile.am", "SUBDIRS = src\n");
- cfprintf("src/Makefile.am",
- "bin_PROGRAMS = %s\n"
- "%s_SOURCES = main.c\n",
- manifest.project, manifest.project);
- cfprintf("bootstrap",
- "#!/bin/sh\n"
- "set -e\n"
- "\n"
- "autoreconf --install --verbose --force\n");
- break;
- case BARE:
- snprintf(buffer, BUFSIZ, "%s.c", manifest.project);
- cfprintf(buffer, "");
- main_source = str_dup(buffer);
- cfprintf("Makefile",
- ".POSIX:\n"
- "CC ::= gcc\n"
- "CFLAGS ::= -std=c23 -Wall -Wextra -Wpedantic\n"
- "\n"
- "all: %s\n"
- "\n"
- "clean:\n"
- "\t$(RM) %s",
- manifest.project, manifest.project);
- break;
- case BCOUNT:
- default:
- abort();
- }
-
- flast = true;
- switch (manifest.licence) {
- case MIT:
- cfprintf("COPYING", "%s", MIT_txt);
- break;
- case GPL:
- cfprintf("COPYING", "%s", GPL_3_0_txt);
- break;
- case BSD:
- cfprintf("COPYING", "%s", BSD_3_Clause_txt);
- break;
- case UNL:
- cfprintf("COPYING", "%s", UNLICENSE_txt);
- break;
- case LCOUNT:
- default:
- abort();
- }
-
- if (!manifest.git) {
- fprintf(stderr, "Initializing git reposity");
- status = system("git init --quiet");
- if (status)
- fprintf(stderr, ", failed.\n");
- else
- fprintf(stderr, ", done.\n");
- }
-
- if (manifest.build == AUTOTOOLS) {
- fprintf(stderr, "Changing files permissions 1");
- struct stat st;
- if (stat("bootstrap", &st) == -1) {
- perror("stat failed");
- return 1;
- }
- mode_t new_mode = st.st_mode | S_IXUSR;
- if (chmod("bootstrap", new_mode) == -1) {
- perror("chmod failed");
- return 1;
- }
- fprintf(stderr, ", done.\n");
- }
-
- if (manifest.open_editor) {
- snprintf(buffer, BUFSIZ, "$EDITOR %s", main_source);
- status = system(buffer);
- if (status)
- fprintf(stderr, "Could not open editor");
- }
-
- return 0;
-}
+// int create_project(manifest_t manifest)
+// {
+// int status;
+// char buffer[BUFSIZ], *main_source;
+//
+// status = mkdir_p(manifest.project);
+// if (status)
+// return 1;
+//
+// status = chdir(manifest.project);
+// if (status)
+// return 1;
+//
+// cfprintf(
+// "README",
+// "%s ( short description )\n\nThis cool project actions adverbly.\n",
+// manifest.project);
+//
+// if (manifest.build != BARE) {
+// main_source = manifest.flat ? "main.c" : "src/main.c";
+// cfprintf(main_source, "#include <stdio.h>\n"
+// "\n"
+// "int main()\n"
+// "{\n"
+// "\tputs(\"Hello, World!\");\n"
+// "\treturn 0;\n"
+// "}\n");
+// }
+// char *upr_name = tostrupr(manifest.project);
+// switch (manifest.build) {
+// case MAKE:
+// cfprintf(
+// "Makefile",
+// "PREFIX = /usr/bin\n"
+// "\n"
+// "%s_SRCS := $(wildcard src/*.c)\n"
+// "%s_OBJS := $(patsubst src/%.c,build/obj/%.o,$(%s_SRCS))"
+// "\n"
+// "%s := bin/%s"
+// "\n"
+// "-include config.mak\n"
+// "\n"
+// "ifeq ($(wildcard config.mak),)\n",
+// upr_name, upr_name, upr_name, upr_name,
+// manifest.project);
+// break;
+// case CMAKE:
+// cfprintf("CMakeLists.txt",
+// "cmake_minimum_required(VERSION 3.16)\n"
+// "\n"
+// "project(%s\n"
+// " VERSION 0.1.0\n"
+// " LANGUAGES C)\n"
+// "\n"
+// "set(CMAKE_C_STANDARD 23)\n"
+// "set(CMAKE_C_STANDARD_REQUIRED ON)\n"
+// "set(CMAKE_C_EXTENSIONS OFF)\n"
+// "\n"
+// "include(GNUInstallDirs)\n"
+// "\n"
+// "add_executable(%s\n"
+// " src/main.c\n"
+// ")\n"
+// "\n"
+// "install(TARGETS %s\n"
+// " RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n"
+// ")\n",
+// manifest.project, manifest.project, manifest.project);
+// break;
+// case AUTOTOOLS:
+// cfprintf("configure.ac",
+// "AC_PREREQ([2.69])\n"
+// "AC_INIT([%s], [0.1], [[email protected]])\n"
+// "AM_INIT_AUTOMAKE([foreign -Wall])\n"
+// "AC_CONFIG_SRCDIR([src/main.c])\n"
+// "AC_CONFIG_HEADERS([config.h])"
+// "AC_PROG_CC\n"
+// "AC_CONFIG_FILES([Makefile src/Makefile])\n"
+// "AC_OUTPUT\n",
+// manifest.project);
+// cfprintf("Makefile.am", "SUBDIRS = src\n");
+// cfprintf("src/Makefile.am",
+// "bin_PROGRAMS = %s\n"
+// "%s_SOURCES = main.c\n",
+// manifest.project, manifest.project);
+// cfprintf("bootstrap",
+// "#!/bin/sh\n"
+// "set -e\n"
+// "\n"
+// "autoreconf --install --verbose --force\n");
+// break;
+// case BARE:
+// snprintf(buffer, BUFSIZ, "%s.c", manifest.project);
+// cfprintf(buffer, "");
+// main_source = str_dup(buffer);
+// cfprintf("Makefile",
+// ".POSIX:\n"
+// "CC ::= gcc\n"
+// "CFLAGS ::= -std=c23 -Wall -Wextra -Wpedantic\n"
+// "\n"
+// "all: %s\n"
+// "\n"
+// "clean:\n"
+// "\t$(RM) %s",
+// manifest.project, manifest.project);
+// break;
+// case BCOUNT:
+// default:
+// abort();
+// }
+//
+// flast = true;
+// switch (manifest.licence) {
+// case MIT:
+// cfprintf("COPYING", "%s", MIT_txt);
+// break;
+// case GPL:
+// cfprintf("COPYING", "%s", GPL_3_0_txt);
+// break;
+// case BSD:
+// cfprintf("COPYING", "%s", BSD_3_Clause_txt);
+// break;
+// case UNL:
+// cfprintf("COPYING", "%s", UNLICENSE_txt);
+// break;
+// case LCOUNT:
+// default:
+// abort();
+// }
+//
+// if (!manifest.git) {
+// fprintf(stderr, "Initializing git reposity");
+// status = system("git init --quiet");
+// if (status)
+// fprintf(stderr, ", failed.\n");
+// else
+// fprintf(stderr, ", done.\n");
+// }
+//
+// if (manifest.build == AUTOTOOLS) {
+// fprintf(stderr, "Changing files permissions 1");
+// struct stat st;
+// if (stat("bootstrap", &st) == -1) {
+// perror("stat failed");
+// return 1;
+// }
+// mode_t new_mode = st.st_mode | S_IXUSR;
+// if (chmod("bootstrap", new_mode) == -1) {
+// perror("chmod failed");
+// return 1;
+// }
+// fprintf(stderr, ", done.\n");
+// }
+//
+// if (manifest.open_editor) {
+// snprintf(buffer, BUFSIZ, "$EDITOR %s", main_source);
+// status = system(buffer);
+// if (status)
+// fprintf(stderr, "Could not open editor");
+// }
+//
+// return 0;
+// }
diff --git a/src/main.c b/src/main.c
@@ -8,258 +8,101 @@
// Usage: yait [OPTION]... <PROJECT>
-#define _POSIX_C_SOURCE 200809L // popen extention
#include <getopt.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
-#include <string.h>
+#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
+#include <string.h>
+#include "../lib/proginfo.h"
+#include "../lib/err.h"
#include "../include/yait.h"
-#include "standard.h"
-#include "util.h"
#include "../config.h"
+#include "name.h"
-#define print_option(option, description) \
- printf(" %-20s %-20s\n", option, description)
+static struct option longopts[] = { { "author", required_argument, 0, 'a' },
+ { "licence", required_argument, 0, 'l' },
+ { 0, 0, 0, 0 } };
-static void usage(int status)
-{
- if (status != 0) {
- puts("Try 'yait --help' for more information.");
- return;
- }
+static int exit_status;
- printf("Usage: %s [OPTION]... [project-name]\n", PROGRAM);
- fputs("\
-Generates an optionated C project.\n",
- stdout);
- puts("");
- fputs("\
- --help display this help and exit\n\
- --version display version information and eixt\n",
- stdout);
- puts("");
- fputs("\
- --git Initialize git repository (default)\n\
- --no-git Do not initialize git repository\n\
- --lib Make this a library\n\
- -l <licence> Set licence. This list can be found by passing 'list'\n\
- -E Open $EDITOR after project creation\n\
- --autotools Use the autotools build system\n\
- --cmake Use the cmake build system\n\
- --make Use the GNU make build system (default)\n\
- --bare Minimal C project structure\n\
- --flat All files in project root.\n\
- --extras=<arg1>,<arg2> Extra build options, Pass list to list out options.\n",
- stdout);
-}
+static void usage(int status);
-void print_lines(const char *first, ...)
+int main(int argc, char **argv)
{
- va_list args;
- const char *s;
-
- va_start(args, first);
-
- for (s = first; s != NULL; s = va_arg(args, const char *))
- printf("%s\n", s);
+ int optc;
+ int lose = 0;
+ set_prog_name(argv[0]);
- va_end(args);
-}
-
-static inline int parse_extras_token(manifest_t *conf, const char *s)
-{
- if (!strcmp(s, "list")) {
- print_lines("nob", "Cleanup", "format", NULL);
- exit(EXIT_SUCCESS);
- }
+ exit_status = EXIT_SUCCESS;
- if (!strcmp(s, "nob"))
- return conf->extra.build_nob = true, 0;
- if (!strcmp(s, "Cleanup"))
- return conf->extra.tools_Cleanup = true, 0;
- if (!strcmp(s, "format"))
- return conf->extra.tools_format = true, 0;
- fprintf(stderr, "Option '%s' is not valid. See %s --extras=list\n", s,
- PROGRAM);
- return 1;
-}
+ manifest_t manifest = {
+ .author = get_name(),
+ .editor = NULL,
+ .licence = BSD,
+ .project = "Project",
+ };
-static int parse_arguments(manifest_t *conf, int argc, char **argv)
-{
- int opt, option_index;
+ parse_standard_options(argc, argv, usage, emit_version);
- // clang-format off
- static struct option long_opts[] = {
- { "git", no_argument, 0, 'g' },
- { "no-git", no_argument, 0, 'G' },
- { "lib", no_argument, 0, 'L' },
- { "autotools", no_argument, 0, 'a' },
- { "cmake", no_argument, 0, 'c' },
- { "make", no_argument, 0, 'm' },
- { "bare", no_argument, 0, 'B' },
- { "flat", no_argument, 0, 'f' },
- { "author", required_argument, 0, 'A' },
- { "extras", required_argument, 0, 0 },
- { 0, 0, 0, 0 } };
- // clang-format on
+ while ((optc = getopt_long(argc, argv, "a:l:E", longopts, NULL)) != -1)
- while ((opt = getopt_long(argc, argv, "gGLbacmBfAl:E", long_opts,
- &option_index)) != -1) {
- if (opt == 0 &&
- strcmp(long_opts[option_index].name, "extras") == 0) {
- int err;
- char *arg = optarg;
- char *token = strtok(arg, ",");
- while (token) {
- err = parse_extras_token(conf, token);
- if (err)
- exit(err);
- token = strtok(NULL, ",");
- }
- }
- switch (opt) {
- case 'g':
- conf->git = true;
- break;
- case 'G':
- conf->git = false;
- break;
- case 'L':
- conf->lib = true;
- break;
- case 'a':
- conf->build = AUTOTOOLS;
- break;
- case 'c':
- conf->build = CMAKE;
- break;
- case 'm':
- conf->build = MAKE;
- break;
- case 'B':
- conf->build = BARE;
- break;
- case 'f':
- conf->flat = true;
- break;
- case 'A':
- conf->author = optarg;
- break;
+ switch (optc) {
case 'l':
if (!strcmp(optarg, "list")) {
- print_lines("MIT", "BSD", "GPL", "UNL", NULL);
+ printf("BSD\nGPL\nMIT\n");
exit(EXIT_SUCCESS);
}
- conf->licence = TOlicence(optarg);
+ if (!strcmp(optarg, "GPL"))
+ manifest.licence = GPL;
+ else if (!strcmp(optarg, "MIT"))
+ manifest.licence = MIT;
+ else {
+ printf("BSD\nGPL\nMIT\n");
+ exit(EXIT_FAILURE);
+ }
break;
case 'E':
- conf->open_editor = true;
- conf->editor = getenv("EDITOR");
+ manifest.editor = getenv("EDITOR");
break;
default:
- return 1;
+ lose = 1;
}
- }
- if (optind >= argc) {
- return HELP_REQUESTED;
+ char *cwd = getcwd(NULL, 0);
+ if (!cwd) {
+ fatalfa(errno);
}
- conf->project = str_dup(argv[optind]);
-
- return 0;
-}
-
-int get_name(char **output)
-{
- FILE *fp;
- char buf[256];
- char *res = NULL;
- struct passwd *pw;
-
- fp = popen("git config --get user.name", "r");
- if (fp) {
- if (fgets(buf, sizeof buf, fp)) {
- size_t len = strlen(buf);
- if (len > 0 && buf[len - 1] == '\n')
- buf[len - 1] = '\0';
- res = strdup(buf);
- }
- pclose(fp);
- }
-
- if (!res) {
- pw = getpwuid(getuid());
- if (!pw || !pw->pw_name)
- return -1;
- res = strdup(pw->pw_name);
- }
-
- if (!res)
- return -1;
+ fprintf(stderr, "Created %s at\n %s\n", manifest.project, cwd);
+ free(cwd);
- *output = res;
- return 0;
+ return exit_status;
}
-int main(int argc, char **argv)
+static void usage(int status)
{
- int status;
- manifest_t manifest = {
- .project = "Project",
- .author = "author",
- .editor = "vim",
-
- .licence = UNL,
-
- .git = true,
- .build = MAKE,
- .flat = false,
- .open_editor = false,
- .lib = false,
-
- .extra.build_nob = false,
- .extra.tools_format = false,
- .extra.tools_Cleanup = false,
- };
-
- status = parse_standard_options(usage, argc, argv);
- if (status != 0 && status != HELP_REQUESTED)
- return fprintf(stderr, "error: %s\n", strerror(status)),
- EXIT_FAILURE;
-
- status = parse_arguments(&manifest, argc, argv);
- if (status == HELP_REQUESTED) {
- usage(0);
- }
-
- if (status) {
- return EXIT_FAILURE;
- }
-
- get_name(&manifest.author);
- status = create_project(manifest);
-
- if (status) {
- fprintf(stderr, "%s\n", strerror(status));
- return EXIT_FAILURE;
- }
-
- char *cwd;
-
- cwd = getcwd(NULL, 0);
- if (cwd == NULL) {
- perror("getcwd");
- exit(EXIT_FAILURE);
+ if (status != 0) {
+ puts("Try 'yait --help' for more information.");
+ return;
}
- fprintf(stderr, "Created %s at\n %s\n", manifest.project, cwd);
-
- free(cwd);
-
- return EXIT_SUCCESS;
+ printf("Usage: %s [OPTION]... [project-name]\n", PROGRAM);
+ fputs("\
+Generates an optionated C project.\n",
+ stdout);
+ puts("");
+ fputs("\
+ --help display this help and exit\n\
+ --version display version information and eixt\n",
+ stdout);
+ puts("");
+ fputs("\
+ -E Open $EDITOR after project creation (default false)\n\
+ --author=NAME Set the program author (default git username)\n\
+ --licence=LICENCE Set the program licence (default BSD)\n",
+ stdout);
}
diff --git a/src/name.c b/src/name.c
@@ -0,0 +1,61 @@
+/* Copyright (C) GCK
+ *
+ * This file is part of yait
+ *
+ * This project and file is licenced under the BSD-3-Clause licence.
+ * <https://opensource.org/licence/bsd-3-clause>
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <pwd.h>
+
+#include "../lib/str_dup.h"
+
+#include "name.h"
+
+char *get_name()
+{
+ int fds[2];
+ if (pipe(fds) == -1)
+ goto sysuser;
+
+ pid_t pid = fork();
+ if (pid == -1) {
+ close(fds[0]);
+ close(fds[1]);
+ goto sysuser;
+ }
+
+ if (pid == 0) {
+ dup2(fds[1], STDOUT_FILENO);
+ close(fds[0]);
+ close(fds[1]);
+ execlp("git", "git", "config", "--get", "user.name",
+ (char *)NULL);
+ _exit(127);
+ }
+
+ close(fds[1]);
+ char buf[256];
+ ssize_t n = read(fds[0], buf, sizeof buf - 1);
+ close(fds[0]);
+ int status;
+ waitpid(pid, &status, 0);
+ if (n > 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ buf[n] = 0;
+ buf[strcspn(buf, "\n")] = 0;
+ return str_dup(buf);
+ }
+
+sysuser: {
+ char *name = getlogin();
+ if (name)
+ return str_dup(name);
+ struct passwd *pw = getpwuid(getuid());
+ if (pw && pw->pw_name)
+ return str_dup(pw->pw_name);
+}
+ return "author";
+}
diff --git a/src/name.h b/src/name.h
@@ -0,0 +1,14 @@
+/* Copyright (C) GCK
+ *
+ * This file is part of yait
+ *
+ * This project and file is licenced under the BSD-3-Clause licence.
+ * <https://opensource.org/licence/bsd-3-clause>
+ */
+
+#ifndef NAME_H
+#define NAME_H
+
+char *get_name();
+
+#endif
diff --git a/src/standard.c b/src/standard.c
@@ -1,34 +0,0 @@
-/* Copyright (C) GCK
- *
- * This file is part of yait
- *
- * This project and file is licenced under the BSD-3-Clause licence.
- * <https://opensource.org/license/bsd-3-clause>
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../config.h"
-#include "standard.h"
-
-int parse_standard_options(void (*usage)(int), int argc, char **argv)
-{
- for (int i = 0; i < argc; ++i) {
- if (strcmp(argv[i], "--help") == 0) {
- usage(0);
- exit(EXIT_SUCCESS);
- } else if (strcmp(argv[i], "--version") == 0) {
- printf("%s %s %d\nCopyright (C) %d %s.\n%s\nThis is "
- "free software: "
- "you are free to change and redistribute "
- "it.\nThere is NO "
- "WARRNTY, to the extent permitted by law.\n",
- PROGRAM, VERSION, COMMIT, YEAR, AUTHORS,
- LICENSE_LINE);
- exit(EXIT_SUCCESS);
- }
- }
- return HELP_REQUESTED;
-}
diff --git a/src/standard.h b/src/standard.h
@@ -1,8 +0,0 @@
-#ifndef STANDARD_H
-#define STANDARD_H
-
-#define HELP_REQUESTED 2
-
-int parse_standard_options(void (*usage)(int), int argc, char **argv);
-
-#endif
diff --git a/src/util.c b/src/util.c
@@ -28,7 +28,7 @@ licence_t TOlicence(char *src)
if (!strcmp(s, "BSD"))
return BSD;
free(s);
- return UNL;
+ return BSD;
}
char *str_dup(const char *s)