commit c12dbce62c9a6ea86d9a994caff9e8fdd3bbefe1
parent 744aaf2bf80c8fbb98460b6ee391699835fd4260
Author: vx-clutch <[email protected]>
Date: Sat, 4 Oct 2025 19:28:26 -0400
save point
Diffstat:
21 files changed, 637 insertions(+), 580 deletions(-)
diff --git a/.clangd b/.clangd
@@ -1,5 +1,5 @@
CompileFlags:
- Add: [-x, c, -std=c23]
+ Add: [-x, c, -std=c23, -Ilib, -I.]
Diagnostics:
ClangTidy:
diff --git a/include/yait.h b/include/yait.h
@@ -1,23 +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>
- */
-
-#ifndef YAIT_H
-#define YAIT_H
-
-typedef enum { MIT, GPL, BSD } licence_t;
-
-typedef struct {
- licence_t licence;
- char *project;
- char *author;
- char *editor;
-} manifest_t;
-
-int create_project(manifest_t manifest);
-
-#endif // YAIT_H
diff --git a/lib/flag.c b/lib/flag.c
@@ -0,0 +1,96 @@
+/* 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 <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "flag.h"
+
+static char *nextchar;
+
+int getopt_long(int argc, char *const argv[], const char *optstring,
+ const struct option *longopts, int *longindex)
+{
+ if (nextchar == NULL || *nextchar == '\0') {
+ if (optind >= argc)
+ return -1;
+ if (argv[optind][0] != '-' || argv[optind][1] == '\0')
+ return -1;
+ if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
+ optind++;
+ return -1;
+ }
+ if (argv[optind][1] == '-') {
+ const char *arg = argv[optind] + 2;
+ char *eq = strchr(arg, '=');
+ size_t len = eq ? (size_t)(eq - arg) : strlen(arg);
+ for (int i = 0; longopts[i].name; i++) {
+ if (strncmp(arg, longopts[i].name, len) == 0 &&
+ strlen(longopts[i].name) == len) {
+ if (longindex)
+ *longindex = i;
+ if (longopts[i].has_arg ==
+ required_argument) {
+ if (eq)
+ optarg = (char *)eq + 1;
+ else if (optind + 1 < argc)
+ optarg = argv[++optind];
+ else
+ return '?';
+ } else if (longopts[i].has_arg ==
+ optional_argument)
+ optarg = eq ? (char *)eq + 1 :
+ NULL;
+ else
+ optarg = NULL;
+ optind++;
+ if (longopts[i].flag) {
+ *longopts[i].flag =
+ longopts[i].val;
+ return 0;
+ }
+ return longopts[i].val;
+ }
+ }
+ optind++;
+ return '?';
+ }
+ nextchar = argv[optind] + 1;
+ }
+ char c = *nextchar++;
+ const char *pos = strchr(optstring, c);
+ if (!pos) {
+ optopt = c;
+ if (*nextchar == '\0')
+ optind++;
+ return '?';
+ }
+ if (pos[1] == ':') {
+ if (*nextchar != '\0') {
+ optarg = nextchar;
+ optind++;
+ nextchar = NULL;
+ } else if (optind + 1 < argc) {
+ optarg = argv[++optind];
+ optind++;
+ nextchar = NULL;
+ } else {
+ optopt = c;
+ return '?';
+ }
+ } else {
+ optarg = NULL;
+ if (*nextchar == '\0') {
+ optind++;
+ nextchar = NULL;
+ }
+ }
+ return c;
+}
diff --git a/lib/flag.h b/lib/flag.h
@@ -0,0 +1,15 @@
+/* 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 FLAG_H
+#define FLAG_H
+
+int getopt_long(int argc, char *const argv[], const char *optstring,
+ const struct option *longopts, int *longindex);
+
+#endif
diff --git a/lib/fs.c b/lib/fs.c
@@ -0,0 +1,157 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "err.h"
+
+#include "fs.h"
+
+char *fs_read(const char *path)
+{
+ FILE *fptr = fopen(path, "r");
+ if (!fptr)
+ return NULL;
+
+ size_t cap = 1024;
+ size_t len = 0;
+ char *buf = malloc(cap);
+ if (!buf) {
+ fclose(fptr);
+ return NULL;
+ }
+
+ int c;
+ while ((c = fgetc(fptr)) != EOF) {
+ if (len + 1 >= cap) {
+ cap *= 2;
+ char *tmp = realloc(buf, cap);
+ if (!tmp) {
+ free(buf);
+ fclose(fptr);
+ return NULL;
+ }
+ buf = tmp;
+ }
+ buf[len++] = (char)c;
+ }
+ buf[len] = '\0';
+
+ fclose(fptr);
+ return buf;
+}
+
+bool fs_exists(const char *path)
+{
+ FILE *fptr;
+ bool exists;
+
+ fptr = fopen(path, "r");
+ if (fptr) {
+ exists = true;
+ } else {
+ exists = false;
+ }
+
+ return exists;
+}
+
+int fs_append(const char *path, const char *format, ...)
+{
+ FILE *fp = fopen(path, "a");
+ if (!fp)
+ return -1;
+
+ va_list ap;
+ va_start(ap, format);
+ int ret = vfprintf(fp, format, ap);
+ va_end(ap);
+
+ if (ret < 0) {
+ fclose(fp);
+ return -1;
+ }
+
+ if (fclose(fp) != 0)
+ return -1;
+
+ return ret;
+}
+
+int fs_del(const char *path)
+{
+ return remove(path);
+}
+
+int fs_new(const char *path)
+{
+ size_t len;
+ int fd;
+
+ if (path == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(path);
+ if (len == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (path[len - 1] == '/') {
+ if (mkdir(path, 0777) == -1)
+ return -1;
+ } else {
+ fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd == -1)
+ return -1;
+ close(fd);
+ }
+
+ return 0;
+}
+
+int fs_write(const char *path, const char *format, ...)
+{
+ FILE *fptr = fopen(path, "w");
+ if (!fptr)
+ return -1;
+
+ va_list ap;
+ va_start(ap, format);
+ int ret = vfprintf(fptr, format, ap);
+ va_end(ap);
+
+ if (ret < 0) {
+ fclose(fptr);
+ return -1;
+ }
+
+ if (fclose(fptr) != 0)
+ return -1;
+
+ return ret;
+}
+
+FILE *fs_temp()
+{
+ FILE *fptr = tmpfile();
+
+ if (!fptr) {
+ errorf("tmpfile failed");
+ return NULL;
+ }
+
+ return fptr;
+}
diff --git a/lib/fs.h b/lib/fs.h
@@ -0,0 +1,26 @@
+/* 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 fs_H
+#define fs_H
+
+#include <stdio.h>
+#include <sys/types.h>
+
+char *fs_read(const char *path);
+
+bool fs_exists(const char *path);
+
+int fs_append(const char *path, const char *format, ...);
+int fs_del(const char *path);
+int fs_new(const char *path);
+int fs_write(const char *path, const char *format, ...);
+
+FILE *fs_temp();
+
+#endif
diff --git a/lib/proginfo.c b/lib/proginfo.c
@@ -9,15 +9,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "../config.h"
+#include <libgen.h>
+#include <config.h>
#include "proginfo.h"
const char *prog_name = "";
-void set_prog_name(const char *name)
+void set_prog_name(char *name)
{
- prog_name = prog_name ? name : "";
+ prog_name = prog_name ? basename(name) : "";
}
void emit_try_help()
@@ -36,12 +37,12 @@ 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)())
+int parse_standard_options(int argc, char **argv, void (*print_help)(),
+ void (*print_version)())
{
for (int i = 0; i < argc; ++i) {
if (!strcmp(argv[i], "--help")) {
- usage(0);
+ print_help();
exit(EXIT_SUCCESS);
} else if (!strcmp(argv[i], "--version")) {
emit_version();
diff --git a/lib/proginfo.h b/lib/proginfo.h
@@ -11,12 +11,12 @@
extern const char *prog_name;
-void set_prog_name(const char *name);
+void set_prog_name(char *name);
void emit_try_help();
void emit_version();
-int parse_standard_options(int argc, char **argv, void (*usage)(int),
- void (*version)());
+int parse_standard_options(int argc, char **argv, void (*print_help)(),
+ void (*print_version)());
#endif
diff --git a/lib/str_dup.c b/lib/str_dup.c
@@ -1,12 +0,0 @@
-#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
@@ -1,6 +0,0 @@
-#ifndef STR_DUP_H
-#define STR_DUP_H
-
-char *str_dup(char *s);
-
-#endif
diff --git a/lib/textc.c b/lib/textc.c
@@ -0,0 +1,28 @@
+#include <string.h>
+#include <ctype.h>
+#include "xmem.h"
+
+#include "textc.h"
+
+char *str_dup(char *s)
+{
+ char *new = xmalloc(strlen(s) + 1);
+ strcpy(new, s);
+ return new;
+}
+
+char *tostrupr(char *s)
+{
+ char *new = str_dup(s);
+ for (int i = 0; new[i] != '\0'; ++i)
+ new[i] = toupper((unsigned char)new[i]);
+ return new;
+}
+
+char *tostrlwr(char *s)
+{
+ char *new = str_dup(s);
+ for (int i = 0; new[i] != '\0'; ++i)
+ new[i] = tolower((unsigned char)new[i]);
+ return new;
+}
diff --git a/lib/textc.h b/lib/textc.h
@@ -0,0 +1,8 @@
+#ifndef TEXTC_H
+#define TEXTC_H
+
+char *str_dup(char *s);
+char *tostrupr(char *s);
+char *tostrlwr(char *s);
+
+#endif
diff --git a/src/create_project.c b/src/create_project.c
@@ -1,182 +0,0 @@
-/* Copyright (C) GCK
- *
- * This file is part of yait
- *
- * This project and file is licensed under the BSD-3-Clause licence.
- * <https://opensource.org/licence/bsd-3-clause>
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <stdio.h>
-
-#include "../include/yait.h"
-#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;
-// }
diff --git a/src/file.c b/src/file.c
@@ -1,91 +0,0 @@
-/* Copyright (C) GCK
- *
- * This file is part of yait
- *
- * This project and file is licensed under the BSD-3-Clause licence.
- * <https://opensource.org/license/bsd-3-clause>
- */
-
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "util.h"
-
-int mkdir_p(const char *path)
-{
- char *copypath = strdup(path);
- char *p = copypath;
- int status = 0;
-
- if (!copypath)
- return -1;
-
- if (copypath[0] == '/')
- p++;
-
- for (; *p; p++) {
- if (*p == '/') {
- *p = '\0';
- if (mkdir(copypath, 0777) && errno != EEXIST) {
- status = -1;
- break;
- }
- *p = '/';
- }
- }
-
- if (!status && mkdir(copypath, 0777) && errno != EEXIST)
- status = -1;
-
- free(copypath);
- return status;
-}
-
-int cfprintf(const char *path, const char *format, ...)
-{
- // int lines = atoi(getenv("LINES"));
- char *dirpath;
- const char *slash = strrchr(path, '/');
- if (slash) {
- size_t len = slash - path;
- dirpath = malloc(len + 1);
- if (!dirpath)
- return -1;
- memcpy(dirpath, path, len);
- dirpath[len] = '\0';
- if (mkdir_p(dirpath)) {
- free(dirpath);
- return -1;
- }
- free(dirpath);
- }
-
- FILE *fp = fopen(path, "w");
- if (!fp)
- return -1;
-
- va_list args;
- va_start(args, format);
- if (vfprintf(fp, format, args) < 0) {
- va_end(args);
- fclose(fp);
- return -1;
- }
- va_end(args);
-
- fclose(fp);
-
- if (flast)
- fprintf(stderr, "Created files %d, done.\n", fno);
- else
- fprintf(stderr, "Created files %d\r", fno);
- fno++;
-
- return 0;
-}
diff --git a/src/tests/run_unit_tests.sh b/src/tests/run_unit_tests.sh
@@ -1,20 +0,0 @@
-#!/bin/sh
-
-yait="$(pwd)/bin/yait"
-
-fatal() {
- echo "fatal: $*" >&2
- exit 1
-}
-
-[ -d "./yait" ] && fatal "must be run from parent directory"
-
-{
-$yait --help || fatal "failed on --help"
-$yait --version || fatal "failed on --version"
-
-tmpd=$(mktemp -d)
-cd $tmpd
-
-$yait foo || fatal "failed to create `foo` at ${tmpd}"
-} > build/test.log 2>&1
diff --git a/src/util.c b/src/util.c
@@ -1,125 +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/licence/bsd-3-clause>
- */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <getopt.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "util.h"
-#include "../include/yait.h"
-
-int fno = 1;
-bool flast = false;
-
-char *tostrupr(const char *s)
-{
- char *new = malloc(strlen(s) + 1);
- if (!new)
- return NULL;
- strcpy(new, s);
- for (int i = 0; new[i] != '\0'; ++i)
- new[i] = toupper((unsigned char)new[i]);
- return new;
-}
-
-licence_t TOlicence(char *src)
-{
- char *s = tostrupr(src);
- if (!strcmp(s, "MIT"))
- return MIT;
- if (!strcmp(s, "GPL"))
- return GPL;
- if (!strcmp(s, "BSD"))
- return BSD;
- free(s);
- return BSD;
-}
-
-static char *nextchar;
-
-int getopt_long(int argc, char *const argv[], const char *optstring,
- const struct option *longopts, int *longindex)
-{
- if (nextchar == NULL || *nextchar == '\0') {
- if (optind >= argc)
- return -1;
- if (argv[optind][0] != '-' || argv[optind][1] == '\0')
- return -1;
- if (argv[optind][1] == '-' && argv[optind][2] == '\0') {
- optind++;
- return -1;
- }
- if (argv[optind][1] == '-') {
- const char *arg = argv[optind] + 2;
- char *eq = strchr(arg, '=');
- size_t len = eq ? (size_t)(eq - arg) : strlen(arg);
- for (int i = 0; longopts[i].name; i++) {
- if (strncmp(arg, longopts[i].name, len) == 0 &&
- strlen(longopts[i].name) == len) {
- if (longindex)
- *longindex = i;
- if (longopts[i].has_arg ==
- required_argument) {
- if (eq)
- optarg = (char *)eq + 1;
- else if (optind + 1 < argc)
- optarg = argv[++optind];
- else
- return '?';
- } else if (longopts[i].has_arg ==
- optional_argument)
- optarg = eq ? (char *)eq + 1 :
- NULL;
- else
- optarg = NULL;
- optind++;
- if (longopts[i].flag) {
- *longopts[i].flag =
- longopts[i].val;
- return 0;
- }
- return longopts[i].val;
- }
- }
- optind++;
- return '?';
- }
- nextchar = argv[optind] + 1;
- }
- char c = *nextchar++;
- const char *pos = strchr(optstring, c);
- if (!pos) {
- optopt = c;
- if (*nextchar == '\0')
- optind++;
- return '?';
- }
- if (pos[1] == ':') {
- if (*nextchar != '\0') {
- optarg = nextchar;
- optind++;
- nextchar = NULL;
- } else if (optind + 1 < argc) {
- optarg = argv[++optind];
- optind++;
- nextchar = NULL;
- } else {
- optopt = c;
- return '?';
- }
- } else {
- optarg = NULL;
- if (*nextchar == '\0') {
- optind++;
- nextchar = NULL;
- }
- }
- return c;
-}
diff --git a/src/util.h b/src/util.h
@@ -1,27 +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/licence/bsd-3-clause>
- */
-
-#ifndef UTIL_H
-#define UTIL_H
-
-#include "../include/yait.h"
-
-licence_t TOlicence(char *s);
-
-int getopt_long(int argc, char *const argv[], const char *optstring,
- const struct option *longopts, int *longindex);
-
-char *tostrupr(const char *s);
-
-extern int fno;
-extern bool flast;
-
-int mkdir_p(const char *path);
-int cfprintf(const char *path, const char *format, ...);
-
-#endif
diff --git a/src/yait.c b/src/yait.c
@@ -8,31 +8,33 @@
// Usage: yait [OPTION]... <PROJECT>
+#include <errno.h>
#include <getopt.h>
+#include <limits.h>
#include <pwd.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <limits.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdarg.h>
#include <string.h>
#include <sys/wait.h>
+#include <unistd.h>
-#include <proginfo.h>
-#include <err.h>
-#include <str_dup.h>
-#include <yait.h>
-#include <config.h>
+#include "../config.h"
+#include "../lib/err.h"
+#include "../lib/fs.h"
+#include "../lib/xmem.h"
+#include "../lib/proginfo.h"
+#include "../lib/textc.h"
-#include "name.h"
+typedef enum { MIT, GPL, BSD, UNL } licence_t;
static const struct option longopts[] = {
- { "author", required_argument, 0, 'a' },
- { "licence", required_argument, 0, 'l' },
- { "quiet", no_argument, 0, 'q' },
- { "force", no_argument, 0, 'f' },
- { 0, 0, 0, 0 } };
+ { "author", required_argument, 0, 'a' },
+ { "licence", required_argument, 0, 'l' },
+ { "quiet", no_argument, 0, 'q' },
+ { "force", no_argument, 0, 'f' },
+ { 0, 0, 0, 0 }
+};
static int exit_status;
@@ -88,39 +90,41 @@ int main(int argc, char **argv)
{
int optc;
int lose = 0;
+ char *package;
set_prog_name(argv[0]);
exit_status = EXIT_SUCCESS;
bool quiet = false;
bool force = false;
-
- manifest_t manifest = {
- .author = get_name(),
- .editor = NULL,
- .licence = BSD,
- .project = "Project",
- };
+ bool editor = false;
+ const char *author = get_name();
+ licence_t licence;
parse_standard_options(argc, argv, print_help, print_version);
- while ((optc = getopt_long(argc, argv, "a:l:Eqf", longopts, NULL)) != -1)
+ while ((optc = getopt_long(argc, argv, "a:l:Eqf", longopts, NULL)) !=
+ -1)
switch (optc) {
case 'l':
if (!strcmp(optarg, "list")) {
- puts("BSD\nGPL\nMIT");
+ puts("BSD\nGPL\nMIT\nUNL");
exit(EXIT_SUCCESS);
}
if (!strcmp(optarg, "GPL"))
- manifest.licence = GPL;
+ licence = GPL;
else if (!strcmp(optarg, "MIT"))
- manifest.licence = MIT;
+ licence = MIT;
+ else if (!strcmp(optarg, "BSD"))
+ licence = BSD;
+ else if (!strcmp(optarg, "UNL"))
+ licence = UNL;
else {
- puts("BSD\nGPL\nMIT");
+ puts("BSD\nGPL\nMIT\nUNL");
exit(EXIT_FAILURE);
}
break;
case 'E':
- manifest.editor = getenv("EDITOR");
+ editor = true;
break;
case 'q':
quiet = true;
@@ -131,16 +135,266 @@ int main(int argc, char **argv)
default:
lose = 1;
}
- if (lose || optind < argc) {
- errorf("extra operand: %s", argv[optind]);
- usage();
+ if (lose) {
+ emit_try_help();
+ }
+
+ if (optind >= argc) {
+ fatalf("no project name provided");
}
+ if (optind + 1 < argc) {
+ errorf("extra operand: %s", argv[optind + 1]);
+ emit_try_help();
+ }
+
+ package = str_dup(argv[optind]);
+
+ size_t len = strlen(package);
+ char *pdir = xmalloc(len + 2);
+ memcpy(pdir, package, len);
+ pdir[len] = '/';
+ pdir[len + 1] = '\0';
+
+ fs_write("README", "\
+This is the README for the GCK %s distribution.\n\
+%s does a thing.\n\
+\n\
+ Copyright (C) %d GCK.\n\
+\n\
+ Copying and distribution of this file, with or without modifications\n\
+ are permitted in any medium without royalty provided the copyright\n\
+ notice and this notice are preserved.\n\
+\n\
+See the files ./INSTALL* for building and installation instructions.\n\
+\n\
+Bug reports:\n\
+ Please include enough information for the maintainers to reproduce the\n\
+ problem. Generally speaking, that means:\n\
+- the contents of any input files necessary to reproduce the bug\n\
+ and command line invocations of the program(s) involved (crucial!).\n\
+- a description of the problem and any samples of the erroneous output.\n\
+- the version number of the program(s) involved (use --version).\n\
+- hardware, operating system, and compiler versions (uname -a).\n\
+- unusual options you gave to configure, if any (see config.mak).\n\
+- anything else that you think would be helpful.\n\
+\n\
+See README-dev for information on the development environment -- any\n\
+interested parties are welcome. If you're a programmer and wish to\n\
+contribute, this should get you started. If you're not a programmer,\n\
+your help in writing test cases, checking documentation against the\n\
+implementation, etc., would still be very much appreciated.\n\
+\n\
+GCK %s is free software. See the file COPYING for copying conditions.\n\
+\n",
+ package, package, YEAR, package);
+
+ fs_write("INSTALL", "\
+Installation Instructions\n\
+*************************\n\
+\n\
+Copyright (C) %d GCK.\n\
+\n\
+ Copying and distribution of this file, with or without modification,\n\
+are permitted in any medium without royalty provided the copyright\n\
+notice and this notice are preserved. This file is offered as-is,\n\
+without warranty of any kind.\n\
+\n\
+Basic Installation\n\
+==================\n\
+\n\
+ Briefly, the shell command `./configure && make && make install` should\n\
+configure, build, and install this package. The following more-detailed\n\
+instruction are generic; see the `README` file for instructions specific to\n\
+this package.\n\
+\n\
+ The `configure` shell script attempts to guess correct values for\n\
+various system-dependent variables used during compilation. It uses\n\
+those values within a `Makefile` to build for that POSIX system as\n\
+defined by `config.mak` which was generated by `configure`.\n\
+\n\
+Compilers and Options\n\
+=====================\n\
+\n\
+ Some systems require unusal options for compilation or linking that\n\
+the `configure` script does not know about. If you run into an issue\n\
+run `./configure --help` to figure out what you can do to fix the\n\
+behavoir.\n\
+\n\
+Installation Names\n\
+==================\n\
+\n\
+ By default, `make install` installs the package's command under\n\
+`/usr/local/bin`. You can specify an installation prefix other than `/usr/local/`\n\
+by giving `configure` the option `--prefix=PREFIX` to `configure`, the package uses\n\
+PREFIX as the prefix for installation programs and libraries.\n\
+Documentation and other data files still use the regular prefix.\n\
+\n\
+`configure` Invokation\n\
+======================\n\
+\n\
+ `configure` recongizes the following options to control its operations.\n\
+\n\
+ `--help`\n\
+ Prints a summary of all the options to `configure`, and exits.\n\
+ `--prefix=PREFIX`\n\
+ Sets the installation prefix.\n\
+ `CFLAGS`\n\
+ Sets the flags used during compilation.\n\
+\n\
+`configure` also accepts some other options. Run `configure --help` for more\n\
+details\n\
+",
+ YEAR);
+
+ fs_write("AUTHORS", "\
+Authors of GCK yait.\n\
+\n\
+ Copyright (C) 2025 GCK.\n\
+\n\
+ Copying and distribution of this file, with or without modification,\n\
+ are permitted in any medium without royalty provided the copyright\n\
+ notice and this notice are preserved.\n\
+\n\
+Also see the THANKS files.\n\
+\n\
+%s\n\
+",
+ author);
+
+ fs_write("THANKS", "\
+Additional contributors to GCK %s.\n\
+\n\
+ Copyright (C) %d GCK.\n\
+\n\
+ Copying and distribution of this file, with or without modification,\n\
+ are permitted in any medium without royalty provided the copyright\n\
+ notice and this notice are preserved.\n\
+\n\
+Thanks to:\n\
+\n\
+ GCK yait for project initialization.\n\
+\n\
+See also the AUTHORS file.\n\
+ ",
+ package, YEAR);
+
+ fs_write("config.h", "\
+#ifndef CONFIG_H\n\
+#define CONFIG_H\n\
+\n\
+/* Program information */\n\
+#define PROGRAM \"%s\"\n\
+#define AUTHORS \"GCK\"\n\
+#define VERSION \"beta\"\n\
+#define YEAR %d\n\
+\n\
+#endif\n\
+ ",
+ package, YEAR);
+
+ fs_write("configure", "\
+#!/bin/sh\n\
+\n\
+usage() {\n\
+cat <<EOF\n\
+Usage: $0 [OPTION]... [VAR=VALUE]...\n\
+\n\
+To assign environment variables (e.g., CC, CFLAGS...), specify them as\n\
+VAR=VALUE.\n\
+\n\
+CC C compiler command [detected]\n\
+CFLAGS C compiler flags [-g, ...]\n\
+LDFLAGS C linker flags\n\
+\n\
+--prefix=<path> Set the install path\n\
+--debug Flags for debug build, overrides CFLAGS\n\
+\n\
+EOF\n\
+exit 0\n\
+}\n\
+\n\
+cmdexists() { type \"$1\" >/dev/null 2>&1 ; }\n\
+trycc() { [ -z \"$CC\" ] && cmdexists \"$1\" && CC=$1 ; }\n\
+\n\
+prefix=/usr/local\n\
+CFLAGS=\"-std=c23\"\n\
+LDFLAGS=\n\
+CC=\n\
+\n\
+printf \"checking for C compiler... \"\n\
+trycc gcc\n\
+trycc clang\n\
+trycc cc\n\
+trycc icx\n\
+printf \"%s\n\" \"$CC\"\n\
+\n\
+DEBUG=false\n\
+for arg; do\n\
+case \"$arg\" in\n\
+--help|-h) usage ;;\n\
+--prefix=*) prefix=${arg#*=} ;;\n\
+--debug) DEBUG=true ;;\n\
+CFLAGS=*) CFLAGS=${arg#*=} ;;\n\
+LDFLAGS=*) LDFLAGS=${arg#*=} ;;\n\
+CC=*) CC=${arg#*=} ;;\n\
+*) printf \"Unrecognized option %s\n\" \"$arg\" ;;\n\
+esac\n\
+done\n\
+\n\
+printf \"checking whether C compiler works... \"\n\
+tmpc=\"$(mktemp -d)/test.c\"\n\
+echo \"typedef int x;\" > \"$tmpc\"\n\
+if output=$($CC $CFLAGS -c -o /dev/null \"$tmpc\" 2>&1); then\n\
+printf \"yes\n\"\n\
+else\n\
+printf \"no; %%s\n\" \"$output\"\n\
+exit 1\n\
+fi\n\
+\n\
+GDEBUGCFLAGS=\"-std=c23 -O0 -g3 -Wall -Wextra -Wpedantic -Werror -Wshadow -Wdouble-promotion -Wformat=2 -Wnull-dereference -Wconversion -Wsign-conversion -Wcast-qual -Wcast-align=strict -Wpointer-arith -Wstrict-overflow=5 -Wstrict-aliasing=2 -Wundef -Wunreachable-code -Wswitch-enum -fanalyzer -fsanitize=undefined,address -fstack-protector-strong -D_FORTIFY_SOURCE=3\"\n\
+CDEBUGCFLAGS=\"-std=gnu2x -O0 -g3 -Wall -Wextra -Wpedantic -Werror -Wshadow -Wdouble-promotion -Wformat=2 -Wnull-dereference -Wconversion -Wsign-conversion -Wcast-qual -Wcast-align=strict -Wpointer-arith -Wstrict-overflow=5 -Wstrict-aliasing=2 -Wundef -Wunreachable-code -Wswitch-enum -fanalyzer -fsanitize=undefined,address -fstack-protector-strong -D_FORTIFY_SOURCE=3\"\n\
+\n\
+if [ -z \"$DEBUG\" ]; then\n\
+case \"$CC\" in\n\
+gcc) CFLAGS=\"$GDEBUGFLAGS\";;\n\
+clang) CFLAGS=\"$CDEBUGFLAGS\";;\n\
+*) ;;\n\
+esac\n\
+else\n\
+case \"$CC\" in\n\
+gcc) ;;\n\
+clang) ;;\n\
+*) ;;\n\
+esac\n\
+fi\n\
+\n\
+case \"$OSTYPE\" in\n\
+cygwin|msys) \n\
+echo \"enabling windows specific flags\"\n\
+CFLAGS=\"-v $CFLAGS\"\n\
+;;\n\
+esac\n\
+\n\
+printf \"creating config.mak... \"\n\
+{\n\
+printf \"PREFIX=%%s\n\" \"$prefix\"\n\
+printf \"CFLAGS=%%s\n\" \"$CFLAGS\"\n\
+printf \"LDFLAGS=%%s\n\" \"$LDFLAGS\"\n\
+printf \"CC=%%s\n\" \"$CC\"\n\
+} > config.mak\n\
+printf \"done\n\"\n\
+");
+
char *cwd = getcwd(NULL, 0);
- if (!cwd) {
+ if (cwd == NULL) {
fatalfa(errno);
}
- fprintf(stderr, "Created %s at\n %s\n", manifest.project, cwd);
+
+ if (!quiet) {
+ fprintf(stderr, "Created %s at\n %s\n", package, cwd);
+ }
+
free(cwd);
return exit_status;
@@ -168,15 +422,13 @@ Generates an optionated C project.\n",
exit(exit_status);
}
-/* Print version and copyright information. */
-
static void print_version()
{
- printf("%s %s %d\n", prog_name, VERSION, COMMIT);
-
- printf("Copyright (C) %d GCK.\n", YEAR);
+ printf("%s %s %d\n", prog_name, VERSION, COMMIT);
+
+ printf("Copyright (C) %d GCK.\n", YEAR);
- puts("This is free software: you are free to change and redistribute it.");
- puts("There is NO WARRNTY, to the extent permitted by law.");
- exit(exit_status);
+ puts("This is free software: you are free to change and redistribute it.");
+ puts("There is NO WARRNTY, to the extent permitted by law.");
+ exit(exit_status);
}
diff --git a/tools/tostr b/tools/tostr
@@ -1,43 +0,0 @@
-#!/bin/sh
-
-awk '
-{
- # Start a new string literal
- printf "\""
-
- for (i = 1; i <= length($0); i++) {
- c = substr($0, i, 1)
- if (c == "\\") {
- printf "\\\\"
- } else if (c == "\"") {
- printf "\\\""
- } else if (c == "\t") {
- printf "\\t"
- } else if (c == "\r") {
- printf "\\r"
- } else if (c == "\n") {
- printf "\\n"
- } else if (c == "\f") {
- printf "\\f"
- } else if (c == "\b") {
- printf "\\b"
- } else if (c == "\a") {
- printf "\\a"
- } else if (c == "\v") {
- printf "\\v"
- } else if (c ~ /[[:cntrl:]]/) {
- printf "\\x%02x", ord(c)
- } else {
- printf "%s", c
- }
- }
-
- # Always end each literal with \n inside, close quote, then newline
- printf "\\n\"\n"
-}
-function ord(str, l, r) {
- l = sprintf("%c", 255)
- r = sprintf("%c", 0)
- return index(l str r, str) - 1
-}
-'
diff --git a/tools/update-gcklib b/tools/update-gcklib
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+git submodule update --remote --rebase
diff --git a/yait b/yait
Binary files differ.