This commit is contained in:
opfez 2022-01-30 11:29:06 +01:00
commit 2992b0e0f9
3 changed files with 190 additions and 0 deletions

5
Makefile Normal file
View File

@ -0,0 +1,5 @@
CC = clang
CFLAGS = -Wall -Wextra -std=c99 -pedantic
makros: makros.c
$(CC) $(CFLAGS) $< -o $@

173
makros.c Normal file
View File

@ -0,0 +1,173 @@
#define _POSIX_C_SOURCE 2 /* popen */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <sys/stat.h>
#include <time.h>
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;
}
}
}

12
readme Normal file
View File

@ -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.