/* [ʞ] smake.c <c.hale.su/lexi/util>
* ~ lexi hale <lexi@hale.su>
* $ cc -Ofast smake.c -osmake
* © affero general public license
* I N C O M P L E T E *
| basic functionality is still |
| heavily under development |
* a replacement for sassc --watch, smake maintains an
* in-memory dependency graph for the specified files
* and automatically re-compiles them when they or a
* dependency of theirs changes
TODO add switch to generate makefile from constructed
graph and exit */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
enum e { ok, badname, nofile };
enum kind { css, sass, scss, bad };
typedef enum { false, true } bool;
#define corebufsz 1024
char corebuf[corebufsz];
void* extbuf, * bufptr;
size_t run = 0;
#define max(a,b) (a > b ? a : b)
void* alloc(size_t sz) {
if((bufptr + sz) < ((void*)corebuf + corebufsz)) goto mkptr;
else {
if (run == 0) {
run = corebufsz;
extbuf = malloc(run);
bufptr = extbuf + sz;
return extbuf;
} else {
if ((bufptr + sz) < (extbuf + run)) goto mkptr;
else {
run += max(512, sz+128);
size_t rsz = bufptr-extbuf;
extbuf = realloc(extbuf, run);
bufptr = extbuf + rsz + sz;
return bufptr - sz;
}
}
}
// this point should never be reached
mkptr: {
void* ret = bufptr;
bufptr += sz;
return ret;
}
}
struct file {
char* src;
char* out;
enum kind kind;
struct dep* deplink;
};
struct dep { struct file* f; struct dep* nextdep; };
void readfile(struct file* f, char* src) {
f->src = src;
int fd = open(src, O_RDONLY);
int sz = lseek(fd,0,SEEK_END); lseek(fd,0,SEEK_SET);
char* file = mmap(0,sz,PROT_READ,MAP_PRIVATE,fd,0);
close(fd);
char namebuf[256];
char* namecur = namebuf;
char* cur = file;
char strqt = 0;
read_start:
if (*cur == 0) goto read_done;
if (*cur == '"') { ++ cur; strqt = '"'; goto read_string; }
if (*cur == '\'') { ++ cur; strqt = '\''; goto read_string; }
if (*cur == '(') { ++ cur; strqt = ')'; goto read_string; }
if (*cur == '[') { ++ cur; strqt = ']'; goto read_string; }
if (*cur == '/') {
switch(cur[1]) {
case '/': goto skip_line;
case '*': cur += 2; goto read_ml_comment;
case 0: goto read_done;
}
++cur; goto read_start;
}
if (*cur == '@') {
if (strncmp(cur+1, "import", 6) == 0) {
cur += 7; goto read_import;
}
}
++cur; goto read_start;
read_import:
if (*cur == ' ' || *cur == '\t' || *cur == '\n') {
++cur; goto read_import;
} else if (*cur == 'u' && cur[1] == 'r' && cur[2] == 'l') {
strqt=')'; goto read_string;
} else if (*cur == '"' || *cur=='\'') {
strqt=*cur;
while(*++cur != strqt) {
if (*cur == '\\' && cur[1] == strqt) {
*namecur++=strqt; cur += 2;
// FIXME support other escapes
} else {
*namecur++=*cur;
}
}
*namecur = 0;
printf("found import to %s;\n", namebuf);
namecur = namebuf;
}
++cur; goto read_start;
read_string:
if (*cur == 0) goto read_done; //unterminated string!?
if (*cur++ == strqt) goto read_start;
goto read_string;
read_ml_comment:
if (*cur == 0) goto read_done; //unterminated comment!
if (*cur == '*' && cur[1] == '/') {
cur += 2;
goto read_start;
}
++cur; goto read_ml_comment;
skip_line:
if (*cur == 0) goto read_done;
if (*cur++ == '\n') goto read_start;
goto skip_line;
read_done:
munmap(file,sz);
}
enum e parsename(struct file* f) {
char* ext = NULL;
char* path = f->src;
while(*path++!=0) {
if(*path == '.') ext = path + 1;
}
if(ext == NULL) return badname;
// quickly determine file type
if (ext[0] == 's'
&& ext[2] == 's'
&& ext[3] == 's') {
if (ext[1] == 'c') f -> kind = scss;
else if (ext[1] == 'a') f -> kind = sass;
else f -> kind = bad;
} else if (ext[0] == 'c'
&& ext[1] == 's'
&& ext[2] == 's' ) f -> kind = css;
else f -> kind = bad;
if(f -> kind == sass || f -> kind == scss) {
size_t sz = (ext - f->src);
f->out = alloc(sz + 4);
strncpy(f->out, f->src, sz);
f->out[sz+0] = 'c';
f->out[sz+1] = 's';
f->out[sz+2] = 's';
f->out[sz+3] = 0;
} else f -> out = f -> src;
printf("src %s; dst %s; ext %s;\n", f->src,f->out,ext);
}
int main(int argc, char** argv) {
bufptr = corebuf;
struct file top;
readfile(&top, argv[1]);
parsename(&top);
}