ulisp/common/genlib.c

220 lines
3.8 KiB
C

/*
* genlib.c: generate unified LispLibrary.h for the various uLisp
* platforms I work on.
*/
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#define MAX_FILENAME 256
#define MAX_STRFTIME 32
#define BUFFER_SIZE 4096
const char lisp_output[] = "LispLibrary.h";
/*
* Determines whether a directory exists.
*/
int
path_exists(const char *path)
{
struct stat st;
int rv;
rv = stat(path, &st);
if (rv == -1) {
return 0;
}
if (st.st_mode & S_IFDIR) {
return 1;
}
else if (st.st_mode & S_IFREG) {
return 1;
}
return 1;
}
/*
* Unlike some other functions, it is an error to call copy_library if the path
* doesn't exist. Other functions (such as copy_platform_library) will validate
* the file exists first and skip it if it doesn't.
*/
static int
copy_library(const char *filename, int source_fd)
{
char buffer[BUFFER_SIZE];
ssize_t bytes_read = 0;
ssize_t bytes_written = 0;
int fd = -1;
fd = open(filename, O_RDONLY);
if (fd == -1) {
return -1;
}
while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(source_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
close(fd);
return -1;
}
}
if (bytes_read == -1) {
close(fd);
return -1;
}
close(fd);
return 0;
}
static int
copy_platform_library(const char *platform, int dest_fd) {
char filename[MAX_FILENAME];
if (!platform || !*platform) {
errno = EINVAL;
return -1;
}
printf("[+] attempting to load %s-specific library...\n",
platform);
if (snprintf(filename, sizeof(filename), "%s.lsp", platform) >=
(int)sizeof(filename)) {
errno = ENAMETOOLONG;
return -1;
}
if (!path_exists(filename)) {
printf("\t[+] %s-specific library not found; skipping.\n",
platform);
return 0;
}
dprintf(dest_fd, "(when (eq (platform) :%s)", platform);
if (copy_library(filename, dest_fd) != 0) {
perror("[!] copying failed");
return -1;
}
dprintf(dest_fd, ")\n");
return 0;
}
static void
write_preamble(int dest_fd)
{
char timestamp[MAX_STRFTIME+1];
struct tm tm;
time_t now = time(NULL);
localtime_r(&now, &tm);
dprintf(dest_fd,
"/*\n"
" * LispLibrary.h - builtin library of additional uLisp functions\n"
" *\n"
" * NOTE:\n"
" * this file was automatically generated on %s\n"
" *\n"
" * Changes will not be persisted across builds or platforms.\n"
" */\n\n"
"const char LispLibrary[] PROGMEM = R\"lisplibrary(\n\n",
timestamp);
}
static void
write_epilogue(int dest_fd)
{
dprintf(dest_fd, "\n)lisplibrary\";\n");
}
static void
usage(int status)
{
FILE *output = stdout;
if (status != 0) {
output = stderr;
}
fprintf(output, "Usage: genlib [-h]\n"
""
"Generates a unified LispLibrary.h for the various platforms.\n");
exit(status);
}
int
main(int argc, const char *argv[])
{
int fd = -1;
int ch;
while ((ch = getopt(argc, (char **)argv, "h")) != -1) {
switch (ch) {
case 'h':
usage(0);
break;
default:
usage(1);
}
}
argc -= optind;
argv += optind;
fd = open(lisp_output, O_WRONLY|O_CREAT, 0644);
if (fd == -1) {
perror("[!] failed to open library for write");
exit(1);
}
write_preamble(fd);
if (copy_library("library.lsp", fd) != 0) {
fprintf(stderr, "[!] failed to generate %s.\n", lisp_output);
exit(1);
}
if (copy_platform_library("picocalc", fd) != 0) {
fprintf(stderr, "[!] failed to generate the picocalc component.\n");
exit(1);
}
if (copy_platform_library("t-deck", fd) != 0) {
fprintf(stderr, "[!] failed to generate the picocalc component.\n");
exit(1);
}
if (copy_platform_library("teensy", fd) != 0) {
fprintf(stderr, "[!] failed to generate the picocalc component.\n");
exit(1);
}
write_epilogue(fd);
close(fd);
}