From 2992b0e0f987e39dbf578d5c180ce8d62dc6cb50 Mon Sep 17 00:00:00 2001 From: opfez Date: Sun, 30 Jan 2022 11:29:06 +0100 Subject: [PATCH] initial --- Makefile | 5 ++ makros.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ readme | 12 ++++ 3 files changed, 190 insertions(+) create mode 100644 Makefile create mode 100644 makros.c create mode 100644 readme diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d5437db --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +CC = clang +CFLAGS = -Wall -Wextra -std=c99 -pedantic + +makros: makros.c + $(CC) $(CFLAGS) $< -o $@ diff --git a/makros.c b/makros.c new file mode 100644 index 0000000..6d405a6 --- /dev/null +++ b/makros.c @@ -0,0 +1,173 @@ +#define _POSIX_C_SOURCE 2 /* popen */ +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char ident[80]; + char value[256]; // TODO: variable size values +} makro; + +FILE *IN; +FILE *OUT; +size_t makro_num = 0; +makro makros[10] = {0}; // TODO: remove limitation of max 10 makros per file + +void +readn(size_t n, char *buf) +{ + for (size_t i = 0; i < n; i++) + buf[i] = fgetc(IN); +} + +void +die(const char *s, ...) +{ + va_list argp; + va_start(argp, s); + vfprintf(stderr, s, argp); + fputc('\n', stderr); + va_end(argp); + exit(1); +} + +void +shell_output(const char *cmd, char *out) +{ + FILE *shell_out = popen(cmd, "r"); + if (shell_out == NULL) + die("couldn't execute command: %s", cmd); + + char lastc = fgetc(shell_out), c; + size_t i = 0; + if (lastc != EOF) { + while ((c = fgetc(shell_out)) != EOF) { + out[i++] = lastc; + lastc = c; + } + } + + pclose(shell_out); +} + +void +add_special_makros(const char *filename) +{ + makro_num = 2; + + memcpy(makros[0].ident, "FILE_NAME", strlen("FILE_NAME")); + memcpy(makros[0].value, filename, strlen(filename)); + + memcpy(makros[1].ident, "FILE_LAST_CHANGED", strlen("FILE_LAST_CHANGED")); + struct stat attrib; + stat(filename, &attrib); + char date[20]; + strftime(date, 20, "%Y-%m-%d", localtime(&(attrib.st_ctime))); + memcpy(makros[1].value, date, strlen(date)); +} + +void +define_makro(makro *m, char *lastc) +{ + char buf[7]; + readn(6, buf); + buf[6] = '\0'; + if (strcmp(buf, "makro ")) { + fputc('#', OUT); + fseek(IN, -6L, SEEK_CUR); + *lastc = '#'; + return; + } + + *lastc = '\n'; + + char *assops = "=@"; + size_t i = 0; + char c; + while ((c = fgetc(IN)) && strchr(assops, c) == NULL) { + m->ident[i++] = c; + if (c == EOF || c == '\n') + die("makro definition missing assignment operator"); + } + i = 0; + + switch (c) { + case '=': + while ((c = fgetc(IN)) != '\n') { + m->value[i++] = c; + if (c == EOF) + die("makro definition missing assignment operator"); + } + break; + case '@': { + char buf[256] = {0}; + while ((c = fgetc(IN)) != '\n') { + buf[i++] = c; + if (c == EOF) + die("makro definition missing assignment operator"); + } + shell_output(buf, m->value); + break; + } + default: + die("invalid operator: %c", c); + } +} + +int +main(int argc, char *argv[]) +{ + switch (argc) { + case 1: + IN = stdin; + OUT = stdout; + break; + case 2: + IN = fopen(argv[1], "r"); + OUT = stdout; + break; + case 3: + IN = fopen(argv[1], "r"); + OUT = fopen(argv[2], "w"); + break; + default: + die("usage: makros [input-file] [output-file]"); + } + + /* special makros */ + add_special_makros(argv[1] ? argv[1] : "unknown"); + + char lastc = '\n', c; + while ((c = fgetc(IN)) != EOF) { + if (c == '#' && lastc == '\n') { + define_makro(&makros[makro_num++], &lastc); + if (lastc == '\n') + continue; + } + + int found_makro = 0; + for (size_t i = 0; i < makro_num; i++) { + if (c == makros[i].ident[0]) { + char buf[80] = {0}; + long len = strlen(makros[i].ident)-1; + readn(len, buf); + if (!strcmp(buf, makros[i].ident+1)) { + fprintf(OUT, "%s", makros[i].value); + found_makro = 1; + break; + } + else { + fseek(IN, -len, SEEK_CUR); + } + } + } + if (!found_makro) { + fputc(c, OUT); + lastc = c; + } + } +} diff --git a/readme b/readme new file mode 100644 index 0000000..0440352 --- /dev/null +++ b/readme @@ -0,0 +1,12 @@ +#makro TEXT=random text +makros is a simple macro tool, in a similar vein to the C preprocessor. However, +in addition to storing TEXT in macros, it supports storing shell command output +in them as well to allow for more flexibility. This readme file is an example of +a raw file you can feed into makros. Try compiling the program by running +`make' and run the command `./makros readme' to see how it works. + +#makro DATE@date +%s +This readme was compiled at DATE. +We have some *special* macros available, which give us some file reflection: +File last changed FILE_LAST_CHANGED. +This file is named FILE_NAME.