/* [ʞ] smake.c * ~ lexi hale * $ 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 #include #include #include #include #include #include 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; } } } assert(false); /* 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); }