#define foobarbaz /* # A shell script within a source file. Ugly, but it works... TRGT=`basename ${0/.c/.exe}` for i in $@ ; do if [ "$i" = "--make-compiled" ] ; then echo "Compiling '$TRGT', please wait..." gcc -W -Wall -O2 -o $TRGT $0 exit $? fi done gcc -W -Wall -Werror -o /tmp/$TRGT "$0" && { "/tmp/$TRGT" $@ ; RET=$? ; rm -f "/tmp/$TRGT" ; exit $RET ; } exit $? */ /* CBuild written by Chris Robinson, copyright 2005-2006 * * Notice of Terms of Use * * CBuild is provided as-is for personal use. You may use and redistribute it * in any manner, with the following terms and conditions: * * * You MAY NOT sell it, or otherwise redistribute it for profit, unless it is * bundled with a seperate commercial product and is used to build said * product. * * You MAY modify it without recompense or notification to the author, as * long as it is clearly marked as a derivitive work. * * You MAY NOT claim it as your own sole work. * * You MAY NOT remove or modify this notice. */ /* Despite what some people may think, no, the name CBuild did not come from my * first name. I got the name from the fact that it uses pure C, and only C. ;) */ #include #include #include #include #include #include #include #include #include #if defined(__unix__) || defined(__MACH__) #include #include #else #define WIFEXITED(x) (1) #define WEXITSTATUS(x) (x) #endif #if defined(__unix__) || defined(__GNUC__) #include #include #endif #ifdef _WIN32 #include #include #ifdef _MSC_VER // MSVC Sucks #define strcasecmp stricmp #define strncasecmp strnicmp #define snprintf _snprintf #define fileno _fileno #define PATH_MAX _MAX_PATH #define lstat slat #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) #define S_IRUSR _S_IREAD #define S_IWUSR _S_IWRITE #define S_IXUSR _S_IEXEC #define S_IRWXU (_S_IREAD | _S_IWRITE | _S_IEXEC) #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 struct dirent { char *d_name; }; typedef struct { long handle; /* -1 for failed rewind */ struct _finddata_t info; struct dirent result; /* d_name null iff first time */ char *name; /* null-terminated char string */ } DIR; DIR *opendir(const char *name) { DIR *dir = 0; if(name && name[0]) { /* search pattern must end with suitable wildcard */ size_t base_length = strlen(name); const char *all = strchr("/\\", name[base_length - 1]) ? "*" : "/*"; if((dir=(DIR*)malloc(sizeof(*dir))) != 0 && (dir->name=(char*)malloc(base_length+strlen(all)+1)) != 0) { strcat(strcpy(dir->name, name), all); if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1) { dir->result.d_name = 0; } else /* rollback */ { free(dir->name); free(dir); dir = NULL; } } else /* rollback */ { free(dir); dir = NULL; errno = ENOMEM; } } else errno = EINVAL; return dir; } int closedir(DIR *dir) { int result = -1; if(dir) { if(dir->handle != -1) result = _findclose(dir->handle); free(dir->name); free(dir); } if(result == -1) /* map all errors to EBADF */ errno = EBADF; return result; } struct dirent *readdir(DIR *dir) { struct dirent *result = 0; if(dir && dir->handle != -1) { if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) { result = &dir->result; result->d_name = dir->info.name; } } else errno = EBADF; return result; } void rewinddir(DIR *dir) { if(dir && dir->handle != -1) { _findclose(dir->handle); dir->handle = (long)_findfirst(dir->name, &dir->info); dir->result.d_name = 0; } else errno = EBADF; } #endif /* _MSC_VER */ static int setenv(const char *env, const char *val, int overwrite) { #if 0 /* This doesn't set anything? */ if(!overwrite) { char buf[2]; if(GetEnvironmentVariable(env, buf, sizeof(buf)) != 0 || GetLastError() != ERROR_ENVVAR_NOT_FOUND) return 0; } return (!SetEnvironmentVariable(env, (val&&*val)?val:NULL)) * -1; #else static char buf[64*1024]; if(!overwrite && getenv(env)) return 0; snprintf(buf, sizeof(buf), "%s=%s", env, (val?val:"")); return _putenv(buf); #endif } static int unsetenv(const char *env) { return setenv(env, "", 1); } #endif /* Win32 */ #define DECL_LIST(type, name) \ type (*name) = NULL; \ size_t name##_size = 0; \ size_t name##_len = 0 #define DECL_STATIC_LIST(type, name) \ static type (*name) = NULL; \ static size_t name##_size = 0; \ static size_t name##_len = 0 #define CLEAR_LIST(name) \ { \ name##_size = 0; \ name##_len = 0; \ free(name); \ name = NULL; \ } typedef struct { FILE *f; char *fname; char *bkp_lbuf; char *bkp_nextline; int bkp_line; int bkp_did_else; int bkp_did_cmds; int bkp_do_level; } INVOKE_BKP; DECL_STATIC_LIST(INVOKE_BKP, invoke_backup); typedef struct { char **argv; size_t argc; } ARGS; DECL_STATIC_LIST(ARGS, arg_backup); static size_t argc; DECL_STATIC_LIST(char*, argv); static FILE *f; static char *fname; static struct stat statbuf; static char linebuf[64*1024]; static char buffer[64*1024]; static char obj[PATH_MAX]; DECL_STATIC_LIST(char*, src_paths); DECL_STATIC_LIST(char*, loaded_files); DECL_STATIC_LIST(char*, sources); static int curr_line = 0; static char *nextline; static jmp_buf jmpbuf; static int stdi_bak = -1; static int stdo_bak = -1; static int stde_bak = -1; static FILE *stde_stream = NULL; // Make a define so we'll always have access to the real stderr #define USEABLE_ERR ((stde_stream)?stde_stream:stderr) typedef struct { char *name; char *val; } DEF; DECL_STATIC_LIST(DEF, defines); static inline void resize_list(void **ptr, size_t count, size_t type_size, size_t *len, size_t *size) { if(count == 0) { *size = 0; *len = 0; free(*ptr); *ptr = NULL; return; } if(count < (*len)/2 || count > *len) { void *tmp; size_t newlen = count-1; size_t i; for(i = 1;i < sizeof(size_t)*8;i*=2) newlen |= newlen>>i; ++newlen; tmp = realloc(*ptr, type_size*newlen); if(!tmp) { fprintf(USEABLE_ERR, "\n\n*** Critical Error ***\n" "Out of memory allocating %u bytes!\n", (unsigned int)(type_size*newlen)); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } *ptr = tmp; *len = newlen; } *size = count; } #define RESIZE_LIST(name, count) \ { \ void *vptr = name; \ resize_list(&vptr, count, sizeof(*name), &name##_len, &name##_size); \ name = vptr; \ } static int libify_name(char *buf, size_t buflen, char *name); /* getvar: Safely gets an environment variable, returning an empty string * instead of NULL */ static const char *getvar(const char *env) { const char *var = getenv(env); return (var?var:""); } /* find_src: Attempts to find the named sourcefile by searching the paths * listed in src_paths. It returns the passed string if the file exists as-is, * or if it couldn't be found. */ static char *find_src(char *src) { static char buf[PATH_MAX]; struct stat statbuf; size_t i; if(stat(src, &statbuf) == 0 || src_paths_size == 0) return src; for(i = 0;i < src_paths_size;++i) { snprintf(buf, sizeof(buf), "%s/%s", src_paths[i], src); if(stat(buf, &statbuf) == 0) return buf; } return src; } #define BUF_SIZE (64*1024) /* expand_string: * The meat of the parser. Reads a string buffer of len bytes, expanding * sub-commands and variables, filling in &entity;'s, and dealing with * escaped characters, until one of the characters denoted in 'stp' is * encountered, non-escaped and outside of quotes, and return a pointer to * that location. If fillmore is non-0, it'll read more data from disk as * needed. */ static char *expand_string(char *str, const char *stp, size_t len, int fillmore) { char *buf, *ptr, *last_pos; int in_quotes = 0; int use_hard_quotes = 0; char *end; int i; if(!(*str) && !fillmore) return str; buf = malloc(BUF_SIZE); if(!buf) { strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } last_pos = str; do { ptr = last_pos; while(1) { if(fillmore) { if(!(*ptr)) { if(nextline) { snprintf(ptr, len+str-ptr, "\n%s", nextline); free(nextline); nextline = NULL; } else { if(fgets(ptr, len+str-ptr, f) == NULL) { free(buf); return ptr; } ++curr_line; } } } if(*ptr == '$') { if(in_quotes != '\'') break; } else if(*ptr == '#') { if(!in_quotes) { char *next = strchr(ptr, '\n'); if(!next) next = ""; memmove(ptr, next, strlen(next)+1); continue; } } if(!in_quotes) { i = 0; do { if((stp[i] == ' ' && isspace(*ptr)) || (stp[i] == '!' && !isspace(*ptr)) || (stp[i] == '^' && !isalpha(*ptr)) || *ptr == stp[i]) { free(buf); return ptr; } } while(stp[i++]); } if(*ptr == '&') { if(in_quotes != '\'') { unsigned int val = '?'; end = NULL; if(ptr[1] != '#') { static struct { char *name; unsigned int val; } val_tab[] = { { "Aacute", 0x00C1 }, { "aacute", 0x00E1 }, { "Acirc", 0x00C2 }, { "acirc", 0x00E2 }, { "acute", 0x00B4 }, { "AElig", 0x00C6 }, { "aelig", 0x00E6 }, { "Agrave", 0x00C0 }, { "agrave", 0x00E0 }, { "alefsym", 0x2135 }, { "Alpha", 0x0391 }, { "alpha", 0x03B1 }, { "amp", 0x0026 }, { "and", 0x2227 }, { "ang", 0x2220 }, { "Aring", 0x00C5 }, { "aring", 0x00E5 }, { "asymp", 0x2248 }, { "Atilde", 0x00C3 }, { "atilde", 0x00E3 }, { "Auml", 0x00C4 }, { "auml", 0x00E4 }, { "bdquo", 0x201E }, { "Beta", 0x0392 }, { "beta", 0x03B2 }, { "brvbar", 0x00A6 }, { "bull", 0x2022 }, { "cap", 0x2229 }, { "Ccedil", 0x00C7 }, { "ccedil", 0x00E7 }, { "cedil", 0x00B8 }, { "cent", 0x00A2 }, { "Chi", 0x03A7 }, { "chi", 0x03C7 }, { "circ", 0x02C6 }, { "clubs", 0x2663 }, { "cong", 0x2245 }, { "copy", 0x00A9 }, { "crarr", 0x21B5 }, { "cup", 0x222A }, { "curren", 0x00A4 }, { "Dagger", 0x2021 }, { "dagger", 0x2020 }, { "dArr", 0x21D3 }, { "darr", 0x2193 }, { "deg", 0x00B0 }, { "Delta", 0x0394 }, { "delta", 0x03B4 }, { "diams", 0x2666 }, { "divide", 0x00F7 }, { "Eacute", 0x00C9 }, { "eacute", 0x00E9 }, { "Ecirc", 0x00CA }, { "ecirc", 0x00EA }, { "Egrave", 0x00C8 }, { "egrave", 0x00E8 }, { "empty", 0x2205 }, { "emsp", 0x2003 }, { "ensp", 0x2002 }, { "Epsilon", 0x0395 }, { "epsilon", 0x03B5 }, { "equiv", 0x2261 }, { "Eta", 0x0397 }, { "eta", 0x03B7 }, { "ETH", 0x00D0 }, { "eth", 0x00F0 }, { "Euml", 0x00CB }, { "euml", 0x00EB }, { "euro", 0x20AC }, { "exist", 0x2203 }, { "fnof", 0x0192 }, { "forall", 0x2200 }, { "frac12", 0x00BD }, { "frac14", 0x00BC }, { "frac34", 0x00BE }, { "frasl", 0x2044 }, { "Gamma", 0x0393 }, { "gamma", 0x03B3 }, { "ge", 0x2265 }, { "gt", 0x003E }, { "hArr", 0x21D4 }, { "harr", 0x2194 }, { "hearts", 0x2665 }, { "hellip", 0x2026 }, { "Iacute", 0x00CD }, { "iacute", 0x00ED }, { "Icirc", 0x00CE }, { "icirc", 0x00EE }, { "iexcl", 0x00A1 }, { "Igrave", 0x00CC }, { "igrave", 0x00EC }, { "image", 0x2111 }, { "infin", 0x221E }, { "int", 0x222B }, { "Iota", 0x0399 }, { "iota", 0x03B9 }, { "iquest", 0x00BF }, { "isin", 0x2208 }, { "Iuml", 0x00CF }, { "iuml", 0x00EF }, { "Kappa", 0x039A }, { "kappa", 0x03BA }, { "Lambda", 0x039B }, { "lambda", 0x03BB }, { "lang", 0x2329 }, { "laquo", 0x00AB }, { "lArr", 0x21D0 }, { "larr", 0x2190 }, { "lceil", 0x2308 }, { "ldquo", 0x201C }, { "le", 0x2264 }, { "lfloor", 0x230A }, { "lowast", 0x2217 }, { "loz", 0x25CA }, { "lrm", 0x200E }, { "lsaquo", 0x2039 }, { "lsquo", 0x2018 }, { "lt", 0x003C }, { "macr", 0x00AF }, { "mdash", 0x2014 }, { "micro", 0x00B5 }, { "middot", 0x00B7 }, { "minus", 0x2212 }, { "Mu", 0x039C }, { "mu", 0x03BC }, { "nabla", 0x2207 }, { "nbsp", 0x00A0 }, { "ndash", 0x2013 }, { "ne", 0x2260 }, { "ni", 0x220B }, { "not", 0x00AC }, { "notin", 0x2209 }, { "nsub", 0x2284 }, { "Ntilde", 0x00D1 }, { "ntilde", 0x00F1 }, { "Nu", 0x039D }, { "nu", 0x03BD }, { "Oacute", 0x00D3 }, { "oacute", 0x00F3 }, { "Ocirc", 0x00D4 }, { "ocirc", 0x00F4 }, { "OElig", 0x0152 }, { "oelig", 0x0153 }, { "Ograve", 0x00D2 }, { "ograve", 0x00F2 }, { "oline", 0x203E }, { "Omega", 0x03A9 }, { "omega", 0x03C9 }, { "Omicron", 0x039F }, { "omicron", 0x03BF }, { "oplus", 0x2295 }, { "or", 0x2228 }, { "ordf", 0x00AA }, { "ordm", 0x00BA }, { "Oslash", 0x00D8 }, { "oslash", 0x00F8 }, { "Otilde", 0x00D5 }, { "otilde", 0x00F5 }, { "otimes", 0x2297 }, { "Ouml", 0x00D6 }, { "ouml", 0x00F6 }, { "para", 0x00B6 }, { "part", 0x2202 }, { "permil", 0x2030 }, { "perp", 0x22A5 }, { "Phi", 0x03A6 }, { "phi", 0x03C6 }, { "Pi", 0x03A0 }, { "pi", 0x03C0 }, { "piv", 0x03D6 }, { "plusmn", 0x00B1 }, { "pound", 0x00A3 }, { "Prime", 0x2033 }, { "prime", 0x2032 }, { "prod", 0x220F }, { "prop", 0x221D }, { "Psi", 0x03A8 }, { "psi", 0x03C8 }, { "quot", 0x0022 }, { "radic", 0x221A }, { "rang", 0x232A }, { "raquo", 0x00BB }, { "rArr", 0x21D2 }, { "rarr", 0x2192 }, { "rceil", 0x2309 }, { "rdquo", 0x201D }, { "real", 0x211C }, { "reg", 0x00AE }, { "rfloor", 0x230B }, { "Rho", 0x03A1 }, { "rho", 0x03C1 }, { "rlm", 0x200F }, { "rsaquo", 0x203A }, { "rsquo", 0x2019 }, { "sbquo", 0x201A }, { "Scaron", 0x0160 }, { "scaron", 0x0161 }, { "sdot", 0x22C5 }, { "sect", 0x00A7 }, { "shy", 0x00AD }, { "Sigma", 0x03A3 }, { "sigma", 0x03C3 }, { "sigmaf", 0x03C2 }, { "sim", 0x223C }, { "spades", 0x2660 }, { "sub", 0x2282 }, { "sube", 0x2286 }, { "sum", 0x2211 }, { "sup", 0x2283 }, { "sup1", 0x00B9 }, { "sup2", 0x00B2 }, { "sup3", 0x00B3 }, { "supe", 0x2287 }, { "szlig", 0x00DF }, { "Tau", 0x03A4 }, { "tau", 0x03C4 }, { "there4", 0x2234 }, { "Theta", 0x0398 }, { "theta", 0x03B8 }, { "thetasym", 0x03D1 }, { "thinsp", 0x2009 }, { "THORN", 0x00DE }, { "thorn", 0x00FE }, { "tilde", 0x02DC }, { "times", 0x00D7 }, { "trade", 0x2122 }, { "Uacute", 0x00DA }, { "uacute", 0x00FA }, { "uArr", 0x21D1 }, { "uarr", 0x2191 }, { "Ucirc", 0x00DB }, { "ucirc", 0x00FB }, { "Ugrave", 0x00D9 }, { "ugrave", 0x00F9 }, { "uml", 0x00A8 }, { "upsih", 0x03D2 }, { "Upsilon", 0x03A5 }, { "upsilon", 0x03C5 }, { "Uuml", 0x00DC }, { "uuml", 0x00FC }, { "weierp", 0x2118 }, { "Xi", 0x039E }, { "xi", 0x03BE }, { "Yacute", 0x00DD }, { "yacute", 0x00FD }, { "yen", 0x00A5 }, { "Yuml", 0x0178 }, { "yuml", 0x00FF }, { "Zeta", 0x0396 }, { "zeta", 0x03B6 }, { "zwj", 0x200D }, { "zwnj", 0x200C }, { NULL, '?' } }; int i; end = expand_string(ptr+1, " ;", len+str-ptr-1, fillmore); if(*end == ';') *(end++) = 0; for(i = 0;val_tab[i].name;++i) { if(strncmp(val_tab[i].name, ptr+1, end-ptr-1) == 0) break; } val = val_tab[i].val; } else { val = strtoul(ptr+2, &end, 0); if(*end == ';') ++end; if(end == ptr+2) val = '?'; } if(val <= 0x7F) { if(val > 0) *(ptr++) = val; } else if(val <= 0x7FF) { *(ptr++) = 0xC0 | (val>>6); *(ptr++) = 0x80 | (val&0x3F); } else if(val <= 0xFFFF) { *(ptr++) = 0xE0 | (val>>12); *(ptr++) = 0x80 | ((val>>6)&0x3F); *(ptr++) = 0x80 | (val&0x3F); } else if(val <= 0x10FFFF) { *(ptr++) = 0xF0 | (val>>18); *(ptr++) = 0x80 | ((val>>12)&0x3F); *(ptr++) = 0x80 | ((val>>6)&0x3F); *(ptr++) = 0x80 | (val&0x3F); } if(end != ptr) memmove(ptr, end, strlen(end)+1); continue; } } else if(*ptr == '"' || *ptr == '\'') { if(!in_quotes || in_quotes == *ptr) { in_quotes ^= *ptr; memmove(ptr, ptr+1, strlen(ptr)); continue; } } else if(*ptr == '\\') memmove(ptr, ptr+1, strlen(ptr)); if(*ptr) ++ptr; } last_pos = ptr; *(ptr++) = 0; use_hard_quotes = 0; /* Run a special command, replacing the section */ if(*ptr == '(') { char *next = NULL; char *opt; ++ptr; if(*ptr == '*') { use_hard_quotes = 1; ++ptr; } opt = expand_string(ptr, " )", len+str-ptr, fillmore); if(!(*opt) || !isspace(*opt)) { *opt = 0; fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Malformed '%s' sub-command!\n\n", fname, curr_line, ptr); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } *(opt++) = 0; opt = expand_string(opt, "!", len+str-opt, fillmore); if(use_hard_quotes) i = snprintf(buf, BUF_SIZE, "%s'", str); else i = snprintf(buf, BUF_SIZE, "%s", str); /* Replaces the section with the specified command line * option's value (in the format 'option=value') */ if(strcasecmp(ptr, "getoptval") == 0) { const char *val = ""; size_t optlen, idx; next = expand_string(opt, ")", len+str-opt, fillmore); if(*next) *(next++) = 0; optlen = strlen(opt); for(idx = 1;idx < argc;++idx) { if(strncasecmp(opt, argv[idx], optlen) == 0) { if(argv[idx][optlen] == '=') val = argv[idx]+optlen+1; else if(argv[idx][optlen] == 0) val = argv[idx+1]; else continue; break; } } snprintf(buf+i, BUF_SIZE-i, "%s", val); } else if(strcasecmp(ptr, "word") == 0) { unsigned long val; char *sep = expand_string(opt, ",)", len+str-opt, fillmore); if(*sep != ',') { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Malformed '%s' sub-command!\n\n", fname, curr_line, ptr); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } *(sep++) = 0; val = strtoul(opt, NULL, 0); opt = expand_string(sep, "!", len+str-sep, fillmore); while(1) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; next = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { next = next_word; if(*next) *(next++) = 0; if(val == 1) snprintf(buf+i, BUF_SIZE-i, "%s", opt); break; } --val; if(val == 0) { snprintf(buf+i, BUF_SIZE-i, "%s", opt); opt = next_word; next = expand_string(opt, ")", len+str-opt, fillmore); if(*next) *(next++) = 0; break; } opt = next_word; } } else if(strcasecmp(ptr, "add")==0 || strcasecmp(ptr, "sub") == 0 || strcasecmp(ptr, "mult")==0 || strcasecmp(ptr, "div") == 0) { long val1, val2; char *sep = expand_string(opt, ",)", len+str-opt, fillmore); if(*sep != ',') { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Malformed '%s' sub-command!\n\n", fname, curr_line, ptr); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } *(sep++) = 0; next = expand_string(sep, ")", len+str-sep, fillmore); if(*next) *(next++) = 0; val1 = atoi(opt); val2 = atoi(sep); if(strcasecmp(ptr, "add") == 0) val1 += val2; else if(strcasecmp(ptr, "sub") == 0) val1 -= val2; else if(strcasecmp(ptr, "mult") == 0) val1 *= val2; else if(strcasecmp(ptr, "div") == 0) { if(!val2) { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Divide-by-0 attempted!\n\n", fname, curr_line); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } val1 /= val2; } snprintf(buf+i, BUF_SIZE-i, "%ld", val1); } /* Returns a library-style name from the specified * filename */ else if(strcasecmp(ptr, "libname") == 0) { next = expand_string(opt, ")", len+str-opt, fillmore); if(*next) *(next++) = 0; libify_name(obj, sizeof(obj), opt); snprintf(buf+i, BUF_SIZE-i, "%s", obj); } /* Returns the full filename for the specified source file by * searching src_path */ else if(strcasecmp(ptr, "findsrc") == 0) { int inc = i; int loop = 1; while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", find_src(opt)); opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } /* Returns the string, lower-cased */ else if(strcasecmp(ptr, "tolower") == 0) { int inc = i; next = expand_string(opt, ")", len+str-opt, fillmore); if(*next) *(next++) = 0; snprintf(buf+i, BUF_SIZE-i, "%s", opt); while(buf[inc]) { buf[inc] = tolower(buf[inc]); ++inc; } } /* Returns the string, upper-cased */ else if(strcasecmp(ptr, "toupper") == 0) { int inc = i; next = expand_string(opt, ")", len+str-opt, fillmore); if(*next) *(next++) = 0; snprintf(buf+i, BUF_SIZE-i, "%s", opt); while(buf[inc]) { buf[inc] = toupper(buf[inc]); ++inc; } } else if(strcasecmp("ifeq", ptr) == 0) { char *var2; char *val; var2 = expand_string(opt, ",)", len+str-opt, fillmore); if(*var2 != ',') { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Malformed 'ifeq' sub-command!\n\n", fname, curr_line); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } *(var2++) = 0; val = expand_string(var2, ",)", len+str-var2, fillmore); if(*val != ',') { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Malformed 'ifeq' sub-command!\n\n", fname, curr_line); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } *(val++) = 0; if(strcmp(opt, var2) == 0) { char *sep = expand_string(val, ",)", len+str-val, fillmore); if(*sep) *(sep++) = 0; next = expand_string(sep, ")", len+str-sep, fillmore); if(*next) *(next++) = 0; } else { val = expand_string(val, ",)", len+str-val, fillmore); if(*val == ',') ++val; next = expand_string(val, ")", len+str-val, fillmore); if(*next) *(next++) = 0; } if(val) snprintf(buf+i, BUF_SIZE-i, "%s", val); } else if(strcasecmp("suffix", ptr) == 0) { char *val; int inc = i; int loop = 1; while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } val = strrchr(opt, '/'); if(!val) val = opt; val = strrchr(val, '.'); if(val) inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", val); opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } else if(strcasecmp("basename", ptr) == 0) { char *val; int inc = i; int loop = 1; while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } val = strrchr(opt, '/'); if(!val) val = opt; val = strrchr(val, '.'); if(val) *val = 0; inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", opt); opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } else if(strcasecmp("addprefix", ptr) == 0) { char *val; int inc = i; int loop = 1; val = opt; opt = expand_string(opt, ",)", len+str-opt, fillmore); if(*opt != ',') { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Malformed 'addprefix' sub-command!\n\n", fname, curr_line); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } *(opt++) = 0; opt = expand_string(opt, "!", len+str-opt, fillmore); while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } inc += snprintf(buf+inc, BUF_SIZE-inc, "%s%s ", val, opt); opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } else if(strcasecmp("addsuffix", ptr) == 0) { char *val; int inc = i; int loop = 1; val = opt; opt = expand_string(opt, ",)", len+str-opt, fillmore); if(*opt != ',') { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Malformed 'addprefix' sub-command!\n\n", fname, curr_line); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } *(opt++) = 0; opt = expand_string(opt, "!", len+str-opt, fillmore); while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } inc += snprintf(buf+inc, BUF_SIZE-inc, "%s%s ", opt, val); opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } else if(strcasecmp("join", ptr) == 0) { int inc = i; size_t p; DECL_LIST(char*, words); do { char *next_word = expand_string(opt, " ,)", len+str-opt, fillmore); if(*next_word == ')') { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Malformed 'join' sub-command!\n\n", fname, curr_line); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } RESIZE_LIST(words, words_size+1); if(*next_word == ',') { *(next_word++) = 0; words[words_size-1] = opt; opt = expand_string(next_word, "!", len+str-next_word, fillmore); break; } *(next_word++) = 0; words[words_size-1] = opt; opt = expand_string(next_word, "!", len+str-next_word, fillmore); if(*opt == ',') { ++opt; break; } } while(1); opt = expand_string(opt, "!", len+str-opt, fillmore); p = 0; do { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(*next_word == ')') { *(next_word++) = 0; if(p < words_size || *opt) { inc += snprintf(buf+inc, BUF_SIZE-inc, "%s%s ", ((p i) buf[inc-1] = 0; CLEAR_LIST(words); next = opt; } else if(strcasecmp("dir", ptr) == 0) { char *val; int inc = i; int loop = 1; while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } val = strrchr(opt, '/'); if(val) { val[1] = 0; inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", opt); } else inc += snprintf(buf+inc, BUF_SIZE-inc, "./ "); opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } else if(strcasecmp("notdir", ptr) == 0) { char *val; int inc = i; int loop = 1; while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } val = strrchr(opt, '/'); if(!val) val = opt; else ++val; opt = next_word; if(val[0]) inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", val); } if(inc > i) buf[inc-1] = 0; next = opt; } /* Finds an executable command by searching the PATH and returns the full absolute path plus the filename, or an empty string if it can't be found */ else if(strcasecmp(ptr, "which") == 0) { char *path = strdup(getvar("PATH")); char *direc; char sep = ':'; next = expand_string(opt, ")", len+str-opt, fillmore); if(*next) *(next++) = 0; if(strchr(path, ';') != NULL || (tolower(path[0]) >= 'a' && tolower(path[0]) <= 'z' && path[1] == ':')) sep = ';'; if(*opt) { direc = path; while(direc && *direc) { struct stat statbuf; char *slash; char *next = strchr(direc, sep); if(next) *(next++) = 0; snprintf(buf+i, BUF_SIZE-i, "%s/%s", direc, opt); while((slash=strchr(buf+i, '\\')) != NULL) *slash = '/'; if(stat(buf+i, &statbuf) == 0) break; buf[i] = 0; direc = next; } } free(path); } else if(strcasecmp("ls", ptr) == 0 || strcasecmp("lsa", ptr) == 0) { struct stat st; int inc = i; int show_hidden = 0; int loop = 1; if(strcasecmp("lsa", ptr) == 0) show_hidden = 1; while(loop) { DIR *dp; char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } dp = opendir(opt); if(dp) { struct dirent *ent; char *slash = strrchr(opt, '/'); if(slash && !slash[1]) *slash = 0; while((ent=readdir(dp)) != NULL) { int i; if(!show_hidden && ent->d_name[0] == '.') continue; i = snprintf(buf+inc, BUF_SIZE-inc, "%s/%s", opt, ent->d_name); if(stat(buf+inc, &st) == 0 && S_ISDIR(st.st_mode)) i += snprintf(buf+inc+i, BUF_SIZE-inc-i, "/ "); else i += snprintf(buf+inc+i, BUF_SIZE-inc-i, " "); inc += i; } closedir(dp); } else { if(show_hidden || opt[0] != '.') { if(stat(opt, &st) == 0) inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", opt); } } opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } else if(strcasecmp("isdir", ptr) == 0) { struct stat st; int inc = i; int loop = 1; while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } if(stat(opt, &st) == 0 && S_ISDIR(st.st_mode)) inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", opt); opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } else if(strcasecmp("isfile", ptr) == 0) { struct stat st; int inc = i; int loop = 1; while(loop) { char *next_word = expand_string(opt, " )", len+str-opt, fillmore); if(next_word == opt) { if(isspace(*opt)) { opt = expand_string(opt, "!", len+str-opt, fillmore); continue; } *(next_word++) = 0; opt = next_word; break; } if(isspace(*next_word)) { *(next_word++) = 0; next_word = expand_string(next_word, "!", len+str-next_word, fillmore); } else { *(next_word++) = 0; loop = 0; } if(stat(opt, &st) == 0 && !S_ISDIR(st.st_mode)) inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", opt); opt = next_word; } if(inc > i) buf[inc-1] = 0; next = opt; } else { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Unknown sub-command '%s'\n\n", fname, curr_line, ptr); free(buf); strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } end = next; } else { /* Insert the named environment var (in the form $FOO or ${FOO}) */ if(*ptr == '{') { ++ptr; if(*ptr == '\'') { use_hard_quotes = 1; ++ptr; end = "'}"; } else end = "}"; end = expand_string(ptr, end, len+str-ptr, fillmore); if(*end == '\'') { *(end++) = 0; end = expand_string(end, "}", len+str-ptr, fillmore); } if(*end) *(end++) = 0; } else end = expand_string(ptr, "^", len+str-ptr, fillmore); if(use_hard_quotes) i = snprintf(buf, BUF_SIZE, "%s'", str); else i = snprintf(buf, BUF_SIZE, "%s", str); if(*ptr >= '0' && *ptr <= '9') { const char *val = ""; size_t idx = atoi(ptr); if(idx < argc) val = argv[idx]; snprintf(buf+i, BUF_SIZE-i, "%s", val); } else if(*ptr == '*' || (use_hard_quotes && *ptr == '@')) { size_t idx = ((ptr+1 >= end || !ptr[1]) ? 1 : atoi(ptr+1)); int inc = i; while(idx < argc) { inc += snprintf(buf+inc, BUF_SIZE-inc, "%s ", argv[idx]); ++idx; } if(inc > i) buf[inc-1] = 0; } else if(*ptr == '@') { size_t idx = ((ptr+1 >= end || !ptr[1]) ? 1 : atoi(ptr+1)); while(idx < argc) { i += snprintf(buf+i, BUF_SIZE-i, "${'%u'}", (unsigned int)idx); ++idx; if(idx < argc) i += snprintf(buf+i, BUF_SIZE-i, " "); } snprintf(buf+i, BUF_SIZE-i, "%s", end); strcpy(str, buf); continue; } else { char c = *end; *end = 0; snprintf(buf+i, BUF_SIZE-i, "%s", getvar(ptr)); *end = c; } } while(buf[i]) { if(buf[i] == '&' || buf[i] == '\'' || buf[i] == '"' || buf[i] == '\\' || buf[i] == '#') { memmove(buf+i+1, buf+i, BUF_SIZE-i-1); buf[i] = '\\'; ++i; } ++i; } if(use_hard_quotes) snprintf(buf+i, BUF_SIZE-i, "'%s", end); else snprintf(buf+i, BUF_SIZE-i, "%s", end); strcpy(str, buf); } while(1); } /* extract_word: Extract a word starting at the string pointed to by 'str'. If * the word begins with a ' or " character, everything until that same * character will be considered part of the word. Otherwise, the word ends at * the first encountered whitespace. Returns the beginning of the next word, * or the end of the string. */ static char *extract_word(char *str, size_t len) { char *end = str; if(!(*end)) return end; end = expand_string(end, " \n", len+str-end, 1); if(*end && *end != '\n') *(end++) = 0; if(*end != '\n') end = expand_string(end, "!\n", len+str-end, 1); if(*end == '\n') { if(end[1] != 0) nextline = strdup(end+1); *end = 0; } return end; } static void extract_line(char *str, size_t len) { char *end = str; if(!(*end)) return; end = expand_string(end, "\n", len+str-end, 1); if(end[0] == '\n' && end[1] != 0) nextline = strdup(end+1); *end = 0; } /* check_obj_deps: Checks a file's dependancy list. The dependancy file is a * file expected to be in dep_dir and with the same name, but with a different * extension. The format of the file is simply: 'file.o: dependancy list...'. A * '\' at the end of the line can be used as a next-line continuation. If the * dependancy file exists, none of the dependancies are missing, and none have * a modification time after 'obj_time', the function will return 0. Otherwise * 1 is returned denoting a rebuild may be required. */ static int check_obj_deps(char *base, char *src, time_t obj_time) { static char dep[PATH_MAX]; char *buf; int bufsize; struct stat statbuf; char *ptr = obj; FILE *df; ptr = strrchr(base, '/'); if(!ptr) ptr = base; ptr = strrchr(ptr, '.'); if(ptr) *ptr = 0; snprintf(dep, sizeof(dep), "${DEP_DIR}/'%s'${DEP_EXT}", base); expand_string(dep, "", sizeof(dep), 0); if(ptr) *ptr = '.'; df = fopen(dep, "r"); if(!df) { if(stat(src, &statbuf) != 0 || statbuf.st_mtime-obj_time > 0) return 1; return 0; } fseek(df, 0, SEEK_END); bufsize = ftell(df)+1; buf = malloc(bufsize); if(!buf) { fclose(df); return 1; } fseek(df, 0, SEEK_SET); bufsize = fread(buf, 1, bufsize, df); if(bufsize >= 0) buf[bufsize] = 0; fclose(df); ptr = strchr(buf, ':'); if(!ptr) { free(buf); return 1; } ++ptr; while(*ptr && *ptr != '\n' && isspace(*ptr)) ++ptr; while(*ptr) { char *stp = ptr; while(*stp && !isspace(*stp)) { if(*stp == '\\') memmove(stp, stp+1, strlen(stp)); if(*stp) ++stp; } if(*stp) *(stp++) = 0; while(*stp && isspace(*stp)) ++stp; if(strcmp(ptr, "\n") != 0 && (stat(ptr, &statbuf) != 0 || statbuf.st_mtime > obj_time)) { free(buf); return 1; } ptr = stp; } free(buf); buf = (char*)getvar("EXTRA_SRC_DEPS"); if(*buf) { char *ptr = malloc(BUF_SIZE); strncpy(ptr, buf, BUF_SIZE); buf = ptr; while(*ptr) { char *next = expand_string(ptr, " ", BUF_SIZE+buf-ptr, 0); if(*next) *(next++) = 0; next = expand_string(next, "!", BUF_SIZE+buf-next, 0); if(stat(ptr, &statbuf) != 0 || statbuf.st_mtime > obj_time) { free(buf); return 1; } ptr = next; } free(buf); } return 0; } /* copy_file: Copies the source file 'sf' to 'df', preserving the source's * file mode and permissions, if possible. */ static int copy_file(const char *sf, const char *df) { struct stat statbuf; FILE *src, *dst; int ret, i; int fd; if(stat(sf, &statbuf) != 0) return 1; #ifdef O_BINARY fd = open(df, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, statbuf.st_mode); #else fd = open(df, O_WRONLY|O_TRUNC|O_CREAT, statbuf.st_mode); #endif if(fd < 0) return 1; dst = fdopen(fd, "wb"); if(!dst) { close(fd); return 1; } src = fopen(sf, "rb"); if(!src) { fclose(dst); return 1; } ret = 0; do { i = fread(buffer, 1, sizeof(buffer), src); if(i > 0) i = fwrite(buffer, 1, i, dst); if(i < 0) ret = 1; } while(i > 0); fclose(src); fclose(dst); return ret; } static int libify_name(char *buf, size_t buflen, char *name) { int i; char *curr = strrchr(name, '/'); if(curr) { *curr = 0; i = snprintf(buf, buflen, "'%s'/${LIB_PRE}'%s'${LIB_EXT}", name, curr+1); *curr = '/'; } else i = snprintf(buf, buflen, "${LIB_PRE}'%s'${LIB_EXT}", name); expand_string(buf, "", buflen, 0); return i; } typedef struct { char *ext; char *cmd; } ASSOC; DECL_LIST(static ASSOC, associations); static void add_association(const char *ext, const char *cmd) { size_t i; for(i = 0;i < associations_size;++i) { if(associations[i].cmd[0] == 0 || strcasecmp(ext, associations[i].ext) == 0) break; } if(i == associations_size) { RESIZE_LIST(associations, i+1); associations[i].ext = NULL; associations[i].cmd = NULL; } free(associations[i].ext); free(associations[i].cmd); associations[i].ext = strdup(ext); associations[i].cmd = strdup(cmd); if(!associations[i].ext || !associations[i].cmd) { strcpy(linebuf, "exit -1\n"); longjmp(jmpbuf, 1); } } static int build_command(char *buffer, size_t bufsize, char *barename, const char *srcname, const char *objname) { char dummy[2] = "."; const char *ptr; char *ext; size_t i; ext = strrchr(barename, '.'); if(strchr(ext, '/') || !ext) ext = dummy; for(i = 0;i < associations_size;++i) { if(strcasecmp(ext+1, associations[i].ext) == 0) break; } if(i == associations_size) return 1; ptr = associations[i].cmd; i = 0; while(*ptr && i+1 < bufsize) { if(strncasecmp(ptr, "<*>", 3) == 0) { *ext = 0; i += snprintf(buffer+i, bufsize-i, "'%s'", barename); *ext = '.'; ptr += 3; } else if(strncasecmp(ptr, "", 3) == 0) { i += snprintf(buffer+i, bufsize-i, "'%s'", srcname); ptr += 3; } else if(strncasecmp(ptr, "<@>", 3) == 0) { i += snprintf(buffer+i, bufsize-i, "'%s'", objname); ptr += 3; } else { if(ptr[0] == '\\' && ptr[1] != 0 && i+2 < bufsize) ptr++; buffer[i++] = *(ptr++); buffer[i] = 0; } } return 0; } /* build_obj_list: Builds a list of object files from the list of sources. If * any of the objects don't exist or have a modification time later than * 'base_time', the variable pointed to by 'do_link' will be set non-zero. */ static int build_obj_list(char *buffer, size_t bufsize, time_t base_time, int *do_link) { static char buf[PATH_MAX]; struct stat statbuf; char *ptr; int i = 0; size_t p; for(p = 0;p < sources_size;++p) { char *ext; ptr = sources[p]; ext = strrchr(ptr, '/'); if(!ext) ext = ptr; ext = strrchr(ext, '.'); if(ext) *ext = 0; snprintf(buf, sizeof(buf), "${OBJ_DIR}/'%s'${OBJ_EXT}", ptr); expand_string(buf, "", sizeof(buf), 0); if(ext) *ext = '.'; if(!(*do_link) && (stat(buf, &statbuf) != 0 || base_time < statbuf.st_mtime)) *do_link = 1; if(ext) *ext = 0; i += snprintf(buffer+i, bufsize-i, " \\\"${OBJ_DIR}/'%s'${OBJ_EXT}\\\"", ptr); if(ext) *ext = '.'; } return i; } void cleanup(void) { size_t i; fflush(stdout); for(i = 1;i < arg_backup_size;++i) { size_t i2; argv = arg_backup[i].argv; for(i2 = 0;i2 < arg_backup[i].argc;++i2) free(argv[i2]); CLEAR_LIST(argv); } CLEAR_LIST(arg_backup); for(i = 0;i < defines_size;++i) { free(defines[i].name); free(defines[i].val); } CLEAR_LIST(defines); for(i = 0;i < src_paths_size;++i) free(src_paths[i]); CLEAR_LIST(src_paths); for(i = 0;i < invoke_backup_size;++i) { fclose(invoke_backup[i].f); free(invoke_backup[i].fname); free(invoke_backup[i].bkp_lbuf); free(invoke_backup[i].bkp_nextline); } CLEAR_LIST(invoke_backup); for(i = 0;i < associations_size;++i) { free(associations[i].ext); free(associations[i].cmd); } CLEAR_LIST(associations); for(i = 0;i < sources_size;++i) free(sources[i]); CLEAR_LIST(sources); for(i = 0;i < loaded_files_size;++i) free(loaded_files[i]); CLEAR_LIST(loaded_files); free(nextline); nextline = NULL; free(fname); fname = NULL; if(f) fclose(f); f = NULL; } unsigned int do_level = 0; unsigned int did_cmds = 0, did_else = 0; unsigned int wait_for_done = 0; int ret = 0, tmp = 0; int ignored_errors = 0; int verbose = 0; int shh = 0; int ignore_err = 0; int has_do = 0; int main(int _argc, char **_argv) { char *ptr; int i; setenv("CC", "gcc", 0); setenv("CXX", "g++", 0); setenv("LD", "gcc", 0); setenv("OUT_OPT", "-o ", 0); setenv("SRC_OPT", "-c ", 0); setenv("DEP_OPT", "-MMD -MF ", 0); setenv("OBJ_EXT", ".o", 0); setenv("OBJ_DIR", ".", 0); setenv("DEP_EXT", ".d", 0); setenv("DEP_DIR", ".", 0); #if (defined __WIN32__ || defined __DOS__) setenv("EXE_EXT", ".exe", 0); #else setenv("EXE_EXT", "", 0); #endif setenv("AR", "ar", 0); setenv("AR_OPT", "", 0); setenv("LIB_PRE", "lib", 0); setenv("LIB_EXT", ".a", 0); setenv("CPPFLAGS", "", 0); setenv("CFLAGS", "", 0); setenv("CXXFLAGS", "", 0); setenv("LDFLAGS", "", 0); atexit(cleanup); /* Check to see if there may be a user-specified script to open */ if(_argc > 1) { char *ext = strrchr(_argv[1], '.'); /* Only files with the .cbd extension are allowed (to reduce chances of * accidently swiping filenames a script may want) */ if(ext && strcasecmp(ext, ".cbd") == 0) { fname = strdup(_argv[1]); f = fopen(fname, "r"); if(!f) { fprintf(USEABLE_ERR, "\n\n*** Critical Error ***\n" "Could not open %s!\n\n", fname); exit(1); } i = 1; while((_argv[i]=_argv[i+1]) != NULL) ++i; --_argc; } } /* Open the default file */ if(!fname) { fname = strdup("default.cbd"); f = fopen(fname, "r"); if(!f) { fprintf(USEABLE_ERR, "\n\n*** Critical Error ***\n" "Could not open %s!\n\n", fname); exit(1); } } setvbuf(stdin, NULL, _IOLBF, 0); argc = _argc; argv = _argv; if(setjmp(jmpbuf)) { free(nextline); nextline = NULL; goto reparse; } main_loop_start: while(1) { int reverse; ignore_err = 0; has_do = 0; if(nextline) { /* If we already have the next line set, go do it */ strncpy(linebuf, nextline, strlen(nextline)+1); free(nextline); nextline = NULL; goto reparse; } /* Grab the next line and increment the line count */ if(fgets(linebuf, sizeof(linebuf), f) == NULL) { /* If end of file, implicitly uninvoke and continue */ snprintf(linebuf, sizeof(linebuf), "uninvoke 0\n"); if(do_level > 0) { fprintf(USEABLE_ERR, "\n\n!!! %s error !!!\n" "Unbalanced do/done pair!\n\n", fname); wait_for_done = 0; did_else = 0; did_cmds = 0; do_level = 0; } goto reparse; } ++curr_line; reparse: reverse = 0; shh = 0; /* Chew up leading whitespace */ ptr = expand_string(linebuf, "!", sizeof(linebuf), 1); if(!*ptr) continue; if(ptr > linebuf) memmove(linebuf, ptr, strlen(ptr)+1); ptr = expand_string(linebuf, " =\n", sizeof(linebuf), 1); if(!linebuf[0] || ptr == linebuf || linebuf[0] == ':') { extract_line(ptr, sizeof(linebuf)+linebuf-ptr); continue; } if(isspace(*ptr)) { if(*ptr != '\n') { *(ptr++) = 0; ptr = expand_string(ptr, "!\n", sizeof(linebuf)+linebuf-ptr, 1); } if(*ptr == '\n') { *ptr = 0; if(ptr[1] != 0) nextline = strdup(ptr+1); } } /* Check for special 'leveling' commands */ /* The 'do' command pushes us up a level, and checks for the next * if-type command, which itself will let us know if we should start * ignoring commands */ if(strcasecmp("do", linebuf) == 0) { ++do_level; if(do_level >= sizeof(int)*8) { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Too many 'do' commands enountered (max: %u)!\n\n", fname, curr_line, (unsigned int)sizeof(int)*8 - 1); snprintf(linebuf, sizeof(linebuf), "exit -1\n"); goto reparse; } if(!*ptr) continue; has_do = 1; memmove(linebuf, ptr, strlen(ptr)+1); goto reparse; } /* 'else' toggles ignoring commands at the current level */ if(strcasecmp("else", linebuf) == 0) { if(do_level <= 0) do_level = 1; /* allow another if-type command to follow an else on the same * level */ if(!(wait_for_done & (1<<(do_level-1)))) { wait_for_done |= 1<<(do_level-1); did_cmds |= 1<<(do_level-1); has_do = 0; } if(!(did_cmds & (1<<(do_level-1)))) { wait_for_done &= ~(1<<(do_level-1)); has_do = 1; } if(!*ptr) continue; memmove(linebuf, ptr, strlen(ptr)+1); goto reparse; } /* 'done' takes us down a level */ if(strcasecmp("done", linebuf) == 0) { if(do_level > 0) { wait_for_done &= ~(1<<(--do_level)); did_else &= ~(1< 300) else if(strcasecmp("i386", ptr) == 0) pass = 1; #endif #if defined(__ia64__) || defined(_M_IA64) else if(strcasecmp("ia64", ptr) == 0) pass = 1; #endif #if defined(__powerpc__) || defined(_M_PPC) else if(strcasecmp("powerpc", ptr) == 0) pass = 1; #endif #if defined(__sparc__) || defined(__sparc) else if(strcasecmp("sparc", ptr) == 0) pass = 1; #endif if((pass != 0) != !!reverse) { memmove(linebuf, next, strlen(next)+1); goto reparse; } if(has_do) wait_for_done |= 1 << (do_level-1); extract_line(next, sizeof(linebuf)+linebuf-next); continue; } if(strcasecmp("ifnplat", linebuf) == 0) { reverse = 1; goto platform_check; } /* Checks if the supplied option name was specified on the command * line */ if(strcasecmp("ifopt", linebuf) == 0) option_check: { char *next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); int pass = 0; size_t i; for(i = 1;i < argc;++i) { size_t len = strlen(argv[i]); char *eq = strrchr(argv[i], '='); if(eq) len = (size_t)(eq-argv[i]); if(strncasecmp(ptr, argv[i], len) == 0) { pass = 1; break; } } if((pass != 0) != !!reverse) { memmove(linebuf, next, strlen(next)+1); goto reparse; } if(has_do) wait_for_done |= 1 << (do_level-1); extract_line(next, sizeof(linebuf)+linebuf-next); continue; } if(strcasecmp("ifnopt", linebuf) == 0) { reverse = 1; goto option_check; } /* Checks if a file or directory exists */ if(strcasecmp("ifexist", linebuf) == 0) file_dir_check: { struct stat statbuf; char *next; next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); if((stat(ptr, &statbuf) == 0) != !!reverse) { memmove(linebuf, next, strlen(next)+1); goto reparse; } if(has_do) wait_for_done |= 1 << (do_level-1); extract_line(next, sizeof(linebuf)+linebuf-next); continue; } if(strcasecmp("ifnexist", linebuf) == 0) { reverse = 1; goto file_dir_check; } /* Checks if we have write permissions to the specified file or * directory */ if(strcasecmp("ifwrite", linebuf) == 0) dir_write_check: { char *next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); int val = 1; #ifdef __unix__ { struct stat stbuf; val = 0; if(stat(ptr, &stbuf) == 0) { if(stbuf.st_uid == getuid()) { if((stbuf.st_mode&S_IWUSR)) val = 1; } else if(stbuf.st_gid == getgid()) { if((stbuf.st_mode&S_IWGRP)) val = 1; } else if((stbuf.st_mode&S_IWOTH)) val = 1; } } #endif if((val != 0) != !!reverse) { memmove(linebuf, next, strlen(next)+1); goto reparse; } if(has_do) wait_for_done |= 1 << (do_level-1); extract_line(next, sizeof(linebuf)+linebuf-next); continue; } if(strcasecmp("ifnwrite", linebuf) == 0) { reverse = 1; goto dir_write_check; } /* Set an environment variable. The value is space sensitive, so if you * wish to use spaces, encapsulate the value in ''s. Using '?=' instead * of '=' will only set the variable if it isn't already set. */ if(strncmp(ptr, "+=", 2) == 0 || strncmp(ptr, "-=", 2) == 0 || strncmp(ptr, "?=", 2) == 0 || ptr[0] == '=') { char *val = ptr; int ovr = 1; if(val[0] != '=') ++val; ptr = linebuf; buffer[0] = 0; if(*(val-1) == '+') { int i; *(val-1) = 0; if(isspace(*(++val))) val = extract_word(val, sizeof(linebuf)+linebuf-val); i = snprintf(buffer, sizeof(buffer), "%s", getvar(ptr)); if(*val) { do { char *next_word = extract_word(val, sizeof(linebuf)+ linebuf-val); i += snprintf(buffer+i, sizeof(buffer)-i, "%s ", val); val = next_word; } while(*val); if(i > 0) buffer[i-1] = 0; } setenv(ptr, buffer, 1); continue; } if(*(val-1) == '-') { char *pos; int len; *(val-1) = 0; if(isspace(*(++val))) val = extract_word(val, sizeof(linebuf)+linebuf-val); extract_line(val, sizeof(linebuf)+linebuf-val); len = strlen(val); if(len <= 0) continue; snprintf(buffer, sizeof(buffer), "%s", getvar(ptr)); while((pos=strstr(buffer, val)) != NULL) { int len = strlen(val); memmove(pos, pos+len, strlen(pos+len)+1); } setenv(ptr, buffer, 1); continue; } if(*(val-1) == '?') { *(val-1) = 0; if(*getvar(ptr)) ovr = 0; } *(val++) = 0; if(isspace(*val)) val = extract_word(val, sizeof(linebuf)+linebuf-val); if(*val) { int i = 0; do { char *next_word = extract_word(val, sizeof(linebuf)+ linebuf-val); i += snprintf(buffer+i, sizeof(buffer)-i, "%s ", val); val = next_word; } while(*val); if(i > 0) buffer[i-1] = 0; } if(buffer[0]) setenv(ptr, buffer, ovr); else if(ovr) unsetenv(ptr); continue; } /* Reset the return value and 'do' status */ has_do = 0; // ret = 0; /* Don't display normal output */ if(linebuf[0] == '@') { shh = 1; memmove(linebuf, linebuf+1, strlen(linebuf)); } /* Set error suppression level */ if(linebuf[0] == '!') { ignore_err = 2; memmove(linebuf, linebuf+1, strlen(linebuf)); } else if(linebuf[0] == '-') { ignore_err = 1; memmove(linebuf, linebuf+1, strlen(linebuf)); } /* Call an external program via the system() function. Parameters are * passed along as-is. */ if(strcasecmp("call", linebuf) == 0) { unsigned int i = 0; char *wrd = ptr; while(*wrd) { char *next = extract_word(wrd, linebuf+sizeof(linebuf)-wrd); #define CHARS_TO_ESC "\\\"%$" #define CHARS_TO_QUOTE "' \t\n()[]<>;&|" if(strpbrk(wrd, CHARS_TO_ESC)) { while(*wrd) { if(strchr(CHARS_TO_ESC CHARS_TO_QUOTE, *wrd) && i < sizeof(buffer)-2) buffer[i++] = '\\'; if(i < sizeof(buffer)-1) buffer[i++] = *(wrd++); } if(i < sizeof(buffer)-1) buffer[i++] = ' '; buffer[i] = 0; } else if(strpbrk(wrd, CHARS_TO_QUOTE)) i += snprintf(buffer+i, sizeof(buffer)-i, "\"%s\" ", wrd); else i += snprintf(buffer+i, sizeof(buffer)-i, "%s ", wrd); wrd = next; } if(i > 0) buffer[i-1] = 0; if(!shh) { printf("%s\n", buffer); fflush(stdout); } ret = system(buffer); if(WIFEXITED(ret)) ret = WEXITSTATUS(ret); else ret = -1; if(ret != 0) { if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) { printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } continue; } /* Invokes an alternate script within the same context, and returns * control when it exits. All variables and command-line options will * persists into and out of invoked scripts. Errors and exit calls will * cause all scripts to abort. To safely exit from an invoked script * before the end of the file, and continue the previous, use * 'uninvoke' */ if(strcasecmp("invoke", linebuf) == 0) { size_t i; FILE *f2; char *fname2; extract_line(ptr, sizeof(linebuf)+linebuf-ptr); f2 = f; fname2 = fname; f = fopen(ptr, "r"); if(!f) { f = f2; ret = 1; if(ignore_err < 2) printf("Could not open script '%s'!\n", ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; fname = strdup(ptr); i = invoke_backup_size; RESIZE_LIST(invoke_backup, i+1); invoke_backup[i].f = f2; invoke_backup[i].fname = fname2; invoke_backup[i].bkp_lbuf = strdup(linebuf); invoke_backup[i].bkp_nextline = nextline; invoke_backup[i].bkp_line = curr_line; invoke_backup[i].bkp_did_else = did_else; invoke_backup[i].bkp_did_cmds = did_cmds; invoke_backup[i].bkp_do_level = do_level; nextline = NULL; curr_line = 0; did_else = 0; did_cmds = 0; do_level = 0; continue; } if(strcasecmp(linebuf, "popfront") == 0) { char *front_word; char *varstr; char *next; next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); extract_word(next, sizeof(linebuf)+linebuf-next); varstr = malloc(65536); if(!varstr) { snprintf(linebuf, sizeof(linebuf), "exit -1\n"); goto reparse; } strncpy(varstr, getvar(ptr), 65536); front_word = varstr; while(*varstr && isspace(*varstr)) ++varstr; if(front_word != varstr) memmove(front_word, varstr, strlen(varstr)+1); varstr = expand_string(front_word, " ", 65536, 0); while(isspace(*varstr)) ++varstr; setenv(ptr, varstr, 1); free(front_word); continue; } if(strcasecmp("define", linebuf) == 0) { char *val = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); extract_line(val, sizeof(linebuf)+linebuf-val); if(*val == 0) { for(i = 0;(size_t)i < defines_size;++i) { if(strcasecmp(ptr, defines[i].name) != 0) continue; free(defines[i].name); free(defines[i].val); if((size_t)i < defines_size) memmove(&defines[i], &defines[i+1], sizeof(defines[0])* (defines_size-i-1)); RESIZE_LIST(defines, defines_size-1); break; } } else { for(i = 0;(size_t)i < defines_size;++i) { if(strcasecmp(ptr, defines[i].name) == 0) break; } if((size_t)i == defines_size) { RESIZE_LIST(defines, i+1); defines[i].val = NULL; defines[i].name = strdup(ptr); if(!defines[i].name) { fprintf(USEABLE_ERR, "\n\n*** Critical Error ***\n" "Out of memory duplicating string " "'%s'!!\n\n", ptr); snprintf(linebuf, sizeof(linebuf), "exit -1\n"); goto reparse; } } free(defines[i].val); defines[i].val = strdup(val); if(!defines[i].val) { fprintf(USEABLE_ERR, "\n\n*** Critical Error ***\n" "Out of memory duplicating string " "'%s'!!\n\n", val); snprintf(linebuf, sizeof(linebuf), "exit -1\n"); goto reparse; } } continue; } /* Compiles a list of source files, and stores them in a list to be * linked later. Compiled files are placed in the 'OBJ_DIR' with the * extension changed to 'OBJ_EXT'. C and C++ files are compiled by the * programs stored in 'CC' and 'CXX' respectively, using the flags * stored in 'CPPFLAGS', 'CFLAGS', and 'CXXFLAGS' as expected. Unknown * file types are silently ignored. */ if(strcasecmp("compile", linebuf) == 0) { size_t i; for(i = 0;i < sources_size;++i) free(sources[i]); CLEAR_LIST(sources); goto compile_more_sources; } /* Adds more source files to the list, and compiles them as above */ if(strcasecmp("compileadd", linebuf) == 0) { compile_more_sources: tmp = 0; while(*ptr) { char *src, *ext, *next; struct stat statbuf; next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); RESIZE_LIST(sources, sources_size+1); sources[sources_size-1] = strdup(ptr); ext = strrchr(ptr, '/'); if(!ext) ext = ptr; ext = strrchr(ext, '.'); if(!ext) goto next_src_file; *ext = 0; snprintf(obj, sizeof(obj), "${OBJ_DIR}/'%s'${OBJ_EXT}", ptr); expand_string(obj, "", sizeof(obj), 0); *ext = '.'; src = find_src(ptr); if(stat(obj, &statbuf) == 0) { if(!check_obj_deps(ptr, src, statbuf.st_mtime)) goto next_src_file; } i = 0; if(build_command(buffer, sizeof(buffer), ptr, src, obj) == 0) goto compile_it; if(strcasecmp(ext+1, "c") == 0) i += snprintf(buffer+i, sizeof(buffer)-i, "${CC} ${CPPFLAGS} ${CFLAGS}"); else if(strcasecmp(ext+1, "cc") == 0 || strcasecmp(ext+1, "cpp") == 0 || strcasecmp(ext+1, "cxx") == 0) i += snprintf(buffer+i, sizeof(buffer)-i, "${CXX} ${CPPFLAGS} ${CXXFLAGS}"); else goto next_src_file; *ext = 0; if(*getvar("DEP_OPT")) i += snprintf(buffer+i, sizeof(buffer)-i, " ${DEP_OPT}\\\"${DEP_DIR}/'%s'${DEP_EXT}\\\"", ptr); i += snprintf(buffer+i, sizeof(buffer)-i, " ${OUT_OPT}\\\"${OBJ_DIR}/'%s'${OBJ_EXT}\\\"", ptr); *ext = '.'; i += snprintf(buffer+i, sizeof(buffer)-i, " ${SRC_OPT}'\"%s\"'", src); compile_it: expand_string(buffer, "", sizeof(buffer), 0); if(!shh) { if(verbose) printf("%s\n", buffer); else printf("Compiling %s...\n", src); fflush(stdout); } ret = system(buffer); if(WIFEXITED(ret)) ret = WEXITSTATUS(ret); else ret = -1; if(ret != 0) { tmp = ret; if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) { printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } next_src_file: ptr = next; } ret = tmp; continue; } /* Links an executable file, using the previously-compiled source * files. The executable will have 'EXE_EXT' appended. */ if(strcasecmp("linkexec", linebuf) == 0) { struct stat statbuf; time_t exe_time = 0; int do_link = 1; if(sources_size == 0) { ret = 1; if(ignore_err < 2) printf("\n!!! %s error, line %d !!!\n" "Trying to build %s with undefined sources!\n\n", fname, curr_line, ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; extract_line(ptr, sizeof(linebuf)+linebuf-ptr); snprintf(obj, sizeof(obj), "'%s'${EXE_EXT}", ptr); expand_string(obj, "", sizeof(obj), 0); if(stat(obj, &statbuf) == 0) { exe_time = statbuf.st_mtime; do_link = 0; } i = 0; i += snprintf(buffer+i, sizeof(buffer)-i, "${LD}"); i += snprintf(buffer+i, sizeof(buffer)-i, " ${OUT_OPT}'\"%s\"'", obj); i += build_obj_list(buffer+i, sizeof(buffer)-i, exe_time, &do_link); if(!do_link) { char *buf = (char*)getvar("EXTRA_LD_DEPS"); if(*buf) { char *ptr = malloc(BUF_SIZE); strncpy(ptr, buf, BUF_SIZE); buf = ptr; while(*ptr) { char *next = expand_string(ptr, " ", BUF_SIZE+ptr-buf, 0); if(*next) *(next++) = 0; next = expand_string(next, "!", BUF_SIZE+ptr-next, 0); if(stat(ptr, &statbuf) != 0 || statbuf.st_mtime > exe_time) { free(buf); buf = NULL; do_link = 1; break; } ptr = next; } free(buf); } if(!do_link) continue; } i += snprintf(buffer+i, sizeof(buffer)-i, " ${LDFLAGS}"); expand_string(buffer, "", sizeof(buffer), 0); if(!shh) { if(verbose) printf("%s\n", buffer); else printf("Linking %s...\n", obj); fflush(stdout); } ret = system(buffer); if(WIFEXITED(ret)) ret = WEXITSTATUS(ret); else ret = -1; if(ret != 0) { if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) { printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } continue; } /* Links a standard archive library, using the previously-compiled * source files. The file will have 'LIB_PRE' prepended, and is * sub-directory-aware, as well as have 'LIB_EXT' appended. */ if(strcasecmp("linklib", linebuf) == 0) { struct stat statbuf; time_t lib_time = 0; int do_link = 1; if(sources_size == 0) { ret = 1; if(ignore_err < 2) printf("!!! %s error, line %d !!!\n" "Trying to build %s with undefined sources!\n", fname, curr_line, ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; extract_line(ptr, sizeof(linebuf)+linebuf-ptr); libify_name(obj, sizeof(obj), ptr); if(stat(obj, &statbuf) == 0) { lib_time = statbuf.st_mtime; do_link = 0; } i = 0; i += snprintf(buffer+i, sizeof(buffer)-i, "${AR} ${AR_OPT}"); i += snprintf(buffer+i, sizeof(buffer)-i, " '\"%s\"'", obj); i += build_obj_list(buffer+i, sizeof(buffer)-i, lib_time, &do_link); if(!do_link) continue; remove(obj); expand_string(buffer, "", sizeof(buffer), 0); if(!shh) { if(verbose) printf("%s\n", buffer); else printf("Linking %s...\n", obj); fflush(stdout); } ret = system(buffer); if(WIFEXITED(ret)) ret = WEXITSTATUS(ret); else ret = -1; if(ret != 0) { if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) { printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } continue; } /* Loads a list of words into a buffer, to later execute an action on * them */ if(strcasecmp("loadlist", linebuf) == 0) { size_t i; for(i = 0;i < loaded_files_size;++i) free(loaded_files[i]); CLEAR_LIST(loaded_files); while(*ptr) { char *next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); i = loaded_files_size; RESIZE_LIST(loaded_files, i+1); loaded_files[i] = strdup(ptr); ptr = next; } continue; } /* Executes a command on the loaded file list */ if(strcasecmp("execlist", linebuf) == 0) { size_t p = 0; if(loaded_files_size == 0) { ret = 1; if(ignore_err < 2) printf("\n\n!!! %s error, line %d !!!\n" "'loadexec' called with no list!\n\n", fname, curr_line); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; tmp = 0; extract_line(ptr, sizeof(linebuf)+linebuf-ptr); while(p < loaded_files_size) { char *curr = loaded_files[p++]; char *cmd_ptr = ptr; int i; i = 0; while(*cmd_ptr && (size_t)i+1 < sizeof(buffer)) { if(strncasecmp(cmd_ptr, "<@>", 3) == 0) { i += snprintf(buffer+i, sizeof(buffer)-i, "%s", curr); cmd_ptr += 3; } else { if(cmd_ptr[0] == '\\' && cmd_ptr[1] != 0 && (size_t)i+2 < sizeof(buffer)) cmd_ptr++; buffer[i++] = *(cmd_ptr++); buffer[i] = 0; } } if(!shh) { printf("%s\n", buffer); fflush(stdout); } ret = system(buffer); if(WIFEXITED(ret)) ret = WEXITSTATUS(ret); else ret = -1; if(ret != 0) { tmp = ret; if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } ret = tmp; continue; } /* Prints a string to the console. If a '.' char is used at the * beginning of string, it will be skipped */ if(strcasecmp("echo", linebuf) == 0) { extract_line(ptr, sizeof(linebuf)+linebuf-ptr); printf("%s\n", ptr); fflush(stdout); continue; } /* Prints a string to the console without a newline. If a '.' char is * used at the beginning of string, it will be skipped */ if(strcasecmp("put", linebuf) == 0) { extract_line(ptr, sizeof(linebuf)+linebuf-ptr); printf("%s", ptr); fflush(stdout); continue; } /* Creates/truncates a file and writes a string to it */ if(strcasecmp("writefile", linebuf) == 0) { char *str; FILE *o; str = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); o = fopen(ptr, "w"); if(!o) { ret = 1; if(ignore_err < 2) printf("Could not create file '%s'!\n", ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; extract_line(str, sizeof(linebuf)+linebuf-str); fprintf(o, "%s\n", str); fclose(o); continue; } /* Appends a string to a file */ if(strcasecmp("appendfile", linebuf) == 0) { char *str; FILE *o; str = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); o = fopen(ptr, "a"); if(!o) { ret = 1; if(ignore_err < 2) printf("Could not create file '%s'!\n", ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; extract_line(str, sizeof(linebuf)+linebuf-str); fprintf(o, "%s\n", str); fclose(o); continue; } /* Jumps to the specified label. A label is denoted by a ':' prepended * to it at the beginning of a line */ if(strcasecmp("goto", linebuf) == 0) { int src_line; char *lbl; extract_word(ptr, sizeof(linebuf)+linebuf-ptr); lbl = strdup(ptr); if(!lbl) { fprintf(USEABLE_ERR, "\n\n** Critical Error **\n" "Out of memory duplicating string " "'%s'\n\n", lbl); snprintf(linebuf, sizeof(linebuf), "exit -1\n"); goto reparse; } rewind(f); src_line = curr_line; curr_line = 0; wait_for_done = 0; did_else = 0; did_cmds = 0; do_level = 0; while(fgets(linebuf, sizeof(linebuf), f) != NULL) { ++curr_line; while(linebuf[0]) { int i; extract_line(linebuf, sizeof(linebuf)); for(i = 0;isspace(linebuf[i]);++i) ; memmove(linebuf, &linebuf[i], strlen(&linebuf[i])+1); if(linebuf[0] == ':' && strcasecmp(linebuf+1, lbl) == 0) { free(lbl); goto main_loop_start; } if(!nextline) break; strcpy(linebuf, nextline); free(nextline); nextline = NULL; } } fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Label '%s' not found!\n\n", fname, src_line, lbl); free(lbl); snprintf(linebuf, sizeof(linebuf), "exit 1\n"); goto reparse; } /* Stores a list of paths to look for source files in. Passing only * '.' clears the list. */ if(strcasecmp("src_paths", linebuf) == 0) { unsigned int count = 0; char *next; while(count < src_paths_size) { free(src_paths[count]); ++count; } CLEAR_LIST(src_paths); count = 0; while(*ptr) { next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); if(count == 0 && !*next && (strcmp(ptr, ".") == 0 || strlen(ptr) == 0)) break; RESIZE_LIST(src_paths, count+1); src_paths[count] = strdup(ptr); if(!src_paths[count]) { fprintf(USEABLE_ERR, "\n\n** Critical Error **\n" "Out of memory duplicating string " "'%s'\n\n", ptr); snprintf(linebuf, sizeof(linebuf), "exit -1\n"); goto reparse; } ++count; ptr = next; } continue; } /* Deletes the specified executables, appending 'EXE_EXT' to the names * as needed */ if(strcasecmp("rmexec", linebuf) == 0) { char *next = ptr; while(*(ptr=next)) { next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); snprintf(buffer, sizeof(buffer), "'%s'${EXE_EXT}", ptr); expand_string(buffer, "", sizeof(buffer), 0); if(stat(buffer, &statbuf) == -1 && errno == ENOENT) continue; if(!shh) { if(verbose) printf("remove(\"%s\");\n", buffer); else printf("Deleting %s...\n", buffer); fflush(stdout); } if((ret=remove(buffer)) != 0) { if(ignore_err < 2) printf("!!! Could not delete '%s' !!!\n", buffer); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } continue; } /* Deletes the specified libraries, prepending 'LIB_PRE' to the * filename portions and appending with 'LIB_EXT' */ if(strcasecmp("rmlib", linebuf) == 0) { char *next = ptr; while(*(ptr=next)) { next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); libify_name(buffer, sizeof(buffer), ptr); if(stat(buffer, &statbuf) == -1 && errno == ENOENT) continue; if(!shh) { if(verbose) printf("remove(\"%s\");\n", buffer); else printf("Deleting %s...\n", buffer); fflush(stdout); } if((ret=remove(buffer)) != 0) { if(ignore_err < 2) printf("!!! Could not delete '%s' !!!\n", buffer); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } while(*(ptr=next)); continue; } /* Removes a list of object files and their associated dependancy * files, replacing the extension of the named file as necesarry */ if(strcasecmp("rmobj", linebuf) == 0) { char *ext; char *next = ptr; while(*(ptr=next)) { next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); ext = strrchr(ptr, '/'); if(!ext) ext = ptr; ext = strrchr(ext, '.'); if(ext) *ext = 0; snprintf(buffer, sizeof(buffer), "${OBJ_DIR}/'%s'${OBJ_EXT}", ptr); expand_string(buffer, "", sizeof(buffer), 0); if(ext) *ext = '.'; if(stat(buffer, &statbuf) == 0 || errno != ENOENT) { if(!shh) { if(verbose) printf("remove(\"%s\");\n", buffer); else printf("Deleting %s...\n", buffer); fflush(stdout); } if((ret=remove(buffer)) != 0) { if(ignore_err < 2) printf("!!! Could not delete '%s' !!!\n", buffer); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); } } if(ext) *ext = 0; snprintf(buffer, sizeof(buffer), "${DEP_DIR}/'%s'${DEP_EXT}", ptr); expand_string(buffer, "", sizeof(buffer), 0); if(ext) *ext = '.'; if(stat(buffer, &statbuf) == -1 && errno == ENOENT) continue; if(!shh) { if(verbose) printf("remove(\"%s\");\n", buffer); else printf("Deleting %s...\n", buffer); fflush(stdout); } if((ret=remove(buffer)) != 0) { if(ignore_err < 2) printf("!!! Could not delete '%s' !!!\n", buffer); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } continue; } /* Removes a list of files or empty directories */ if(strcasecmp("rm", linebuf) == 0) { #ifdef _WIN32 struct stat sbuf; #endif char *next = ptr; while(*(ptr=next)) { next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); if(stat(ptr, &statbuf) == -1 && errno == ENOENT) continue; if(!shh) { if(verbose) printf("remove(\"%s\");\n", ptr); else printf("Deleting %s...\n", ptr); fflush(stdout); } #ifdef _WIN32 if(stat(ptr, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)) ret = rmdir(ptr); else #endif ret = remove(ptr); if(ret != 0) { if(ignore_err < 2) printf("!!! Could not delete '%s' !!!\n", ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } } continue; } /* Creates a directory (with mode 700 in Unix) */ if(strcasecmp("mkdir", linebuf) == 0) { extract_line(ptr, sizeof(linebuf)+linebuf-ptr); if(!shh) { if(!verbose) printf("Creating directory %s/...\n", ptr); #ifdef _WIN32 else printf("mkdir(\"%s\");\n", ptr); fflush(stdout); } if((ret=mkdir(ptr)) != 0) #else else printf("mkdir(\"%s\", S_IRWXU);\n", ptr); fflush(stdout); } if((ret=mkdir(ptr, S_IRWXU)) != 0) #endif { if(ignore_err < 2) printf("!!! Could not create directory !!!\n"); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } continue; } /* Enables/disables command verboseness */ if(strcasecmp("verbose", linebuf) == 0) { extract_line(ptr, sizeof(linebuf)+linebuf-ptr); if(*ptr) verbose = atoi(ptr); continue; } /* Leaves the current script, falling back to the previous if the * current was started with the invoke command. Otherwise, it behaves * like exit */ if(strcasecmp("uninvoke", linebuf) == 0) { size_t i = invoke_backup_size-1; extract_line(ptr, sizeof(linebuf)+linebuf-ptr); ret = atoi(ptr); if(invoke_backup_size == 0) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } fclose(f); f = invoke_backup[i].f; invoke_backup[i].f = NULL; free(fname); fname = invoke_backup[i].fname; strcpy(linebuf, invoke_backup[i].bkp_lbuf); free(invoke_backup[i].bkp_lbuf); free(nextline); nextline = invoke_backup[i].bkp_nextline; curr_line = invoke_backup[i].bkp_line; did_else = invoke_backup[i].bkp_did_else; did_cmds = invoke_backup[i].bkp_did_cmds; do_level = invoke_backup[i].bkp_do_level; RESIZE_LIST(invoke_backup, i); continue; } /* Copies a file */ if(strcasecmp("copy", linebuf) == 0) { char *dfile, *end; struct stat st; dfile = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); if(!*ptr || !*dfile) { ret = 1; if(ignore_err < 2) printf("\n\n!!! %s error, line %d !!!\n" "Improper arguments to 'copy'!\n", fname, curr_line); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; end = extract_word(dfile, sizeof(linebuf)+linebuf-dfile); if(dfile[strlen(dfile)-1] == '/' || (stat(dfile, &st) == 0 && S_ISDIR(st.st_mode))) { char *fn = strrchr(ptr, '/'); snprintf(obj, sizeof(obj), "%s%s%s", dfile, ((dfile[strlen(dfile)-1]=='/')?"":"/"), (fn?(fn+1):ptr)); dfile = obj; } if(!shh) { if(verbose) printf("copy_file(\"%s\", \"%s\");\n", ptr, dfile); else printf("Copying '%s' to '%s'...\n", ptr, dfile); fflush(stdout); } if((ret=copy_file(ptr, dfile)) != 0) { if(ignore_err < 2) printf("!!! Could not copy !!!\n"); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } extract_line(end, sizeof(linebuf)+linebuf-end); continue; } /* Copies a library file, prepending and appending the names as * necesarry */ if(strcasecmp("copylib", linebuf) == 0) { char *dfile, *end; dfile = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); if(!*ptr || !*dfile) { ret = 1; if(ignore_err < 2) printf("\n\n!!! %s error, line %d !!!\n" "Improper arguments to 'copylib'!\n", fname, curr_line); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; end = extract_word(dfile, sizeof(linebuf)+linebuf-dfile); if(dfile[strlen(dfile)-1] == '/') { char *fn = strrchr(ptr, '/'); snprintf(obj, sizeof(obj), "%s%s", dfile, (fn?(fn+1):ptr)); libify_name(buffer, sizeof(buffer), obj); } else libify_name(buffer, sizeof(buffer), dfile); libify_name(obj, sizeof(obj), ptr); if(!shh) { if(verbose) printf("copy_file(\"%s\", \"%s\");\n", obj, buffer); else printf("Copying '%s' to '%s'...\n", obj, buffer); fflush(stdout); } if((ret=copy_file(obj, buffer)) != 0) { if(ignore_err < 2) printf("!!! Could not copy !!!\n"); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } extract_line(end, sizeof(linebuf)+linebuf-end); continue; } if(strcasecmp("chdir", linebuf) == 0) { extract_line(ptr, sizeof(linebuf)+linebuf-ptr); if(!shh) { if(verbose) printf("chdir(\"%s\");\n", ptr); else printf("Moving to directory '%s'\n", ptr); fflush(stdout); } if((ret=chdir(ptr)) != 0) { if(ignore_err < 2) printf("!!! Could not change directory !!!\n"); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); } continue; } /* Creates an association between a file extension and a command to * compile files with that association via the compile command */ if(strcasecmp("associate", linebuf) == 0) { char *cmd = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); extract_line(cmd, sizeof(linebuf)+linebuf-cmd); add_association(ptr, cmd); continue; } /* Yay for DOS/Windows allowing \ as a directory seperator. Modify the * named environment variable to replace \ with / */ if(strcasecmp("fixpath", linebuf) == 0) { char *str, *val; char *end = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); extract_line(end, sizeof(linebuf)+linebuf-ptr); val = strdup(getvar(ptr)); str = val; while((str=strchr(str, '\\')) != NULL) *(str++) = '/'; setenv(ptr, val, 1); free(val); continue; } /* Sets an alternate file for normal output (stdout) */ if(strcasecmp("setoutput", linebuf) == 0) { char *end = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); extract_line(end, sizeof(linebuf)+linebuf-end); ret = 0; if(*ptr) { int fp = open(ptr, O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, S_IRWXU); if(fp == -1) { ret = 1; if(ignore_err < 2) printf("!!! Could not open %s !!!\n", ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } fflush(stdout); if(stdo_bak == -1) stdo_bak = dup(STDOUT_FILENO); close(STDOUT_FILENO); dup2(fp, STDOUT_FILENO); close(fp); continue; } fflush(stdout); if(stdo_bak == -1) continue; close(STDOUT_FILENO); dup2(stdo_bak, STDOUT_FILENO); close(stdo_bak); stdo_bak = -1; continue; } /* Sets an alternate file for error output (stderr) */ if(strcasecmp("seterror", linebuf) == 0) { char *end = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); extract_line(end, sizeof(linebuf)+linebuf-end); ret = 0; if(*ptr) { int fd = open(ptr, O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, S_IRWXU); if(fd == -1) { ret = 1; if(ignore_err < 2) printf("!!! Could not open %s !!!\n", ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } fflush(stderr); if(stde_bak == -1) { stde_bak = dup(STDERR_FILENO); stde_stream = fdopen(stde_bak, "w"); } close(STDERR_FILENO); dup2(fd, STDERR_FILENO); close(fd); continue; } fflush(stderr); if(stde_bak == -1) continue; close(STDERR_FILENO); dup2(stde_bak, STDERR_FILENO); fclose(stde_stream); stde_stream = NULL; stde_bak = -1; continue; } /* Changes the file to read input from. Pass nothing to switch to * stdin. */ if(strcasecmp("setinput", linebuf) == 0) { char *end = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); extract_line(end, sizeof(linebuf)+linebuf-end); ret = 0; if(*ptr) { int fp = open(ptr, O_RDONLY, 0); if(fp == -1) { ret = 1; if(ignore_err < 2) printf("!!! Could not open %s !!!\n", ptr); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } fflush(stdin); if(stdi_bak == -1) stdi_bak = dup(STDIN_FILENO); close(STDIN_FILENO); dup2(fp, STDIN_FILENO); close(fp); continue; } fflush(stdin); if(stdi_bak == -1) continue; close(STDIN_FILENO); dup2(stdi_bak, STDIN_FILENO); close(stdi_bak); stdi_bak = -1; continue; } /* Reads input and stores the string in the named var. The trailing * newline is stripped. */ if(strcasecmp("read", linebuf) == 0) { char *end = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); extract_line(end, sizeof(linebuf)+linebuf-end); if(!(*ptr)) { ret = 1; if(ignore_err < 2) printf("!!! No storage specified for read (line %d) !!!\n", curr_line); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } ret = 0; buffer[0] = 0; if(fgets(buffer, sizeof(buffer), stdin) == NULL) ret = 1; if(buffer[0] && buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = 0; if(!strlen(buffer)) unsetenv(ptr); else ret |= setenv(ptr, buffer, 1); continue; } /* Executes a command via the system shell and redirects its output to * a temp file, which is then read into the named variable. All * trailing new-lines are stripped. */ if(strcasecmp("readexec", linebuf) == 0) { int fd, old_stdout; char *bufptr, *cmd; size_t len; FILE *tf; ret = 0; /* Create a temp file and get its file descriptor (and for Win32, * change it to text mode) */ tf = tmpfile(); if(!tf) { ret = 1; if(ignore_err < 2) printf("!!! Could not create temp file !!!\n"); if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } if(ignore_err < 2) printf("--- Error %d ignored. ---\n\n", ++ignored_errors); fflush(stdout); continue; } fd = fileno(tf); #ifdef _WIN32 _setmode(fd, _O_TEXT); #endif /* Get the var name, and build the command */ len = 0; cmd = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); while(*cmd) { char *next = extract_word(cmd, sizeof(linebuf)+linebuf-cmd); if(strpbrk(cmd, CHARS_TO_ESC)) { while(*cmd) { if(strchr(CHARS_TO_ESC CHARS_TO_QUOTE, *cmd) && len < sizeof(buffer)-2) buffer[len++] = '\\'; if(len < sizeof(buffer)-1) buffer[len++] = *(cmd++); } if(len < sizeof(buffer)-1) buffer[len++] = ' '; buffer[len] = 0; } else if(strpbrk(cmd, CHARS_TO_QUOTE)) len += snprintf(buffer+len, sizeof(buffer)-len, "\"%s\" ", cmd); else len += snprintf(buffer+len, sizeof(buffer)-len, "%s ", cmd); cmd = next; } if(len > 0) buffer[len-1] = 0; if(!shh) printf("%s\n", buffer); fflush(stdout); /* Save stdout, close it, reopen it with the temp file's * descriptor, then run the command */ old_stdout = dup(STDOUT_FILENO); close(STDOUT_FILENO); dup2(fd, STDOUT_FILENO); ret = system(buffer); if(WIFEXITED(ret)) ret = WEXITSTATUS(ret); else ret = -1; if(ret != 0) { if(!ignore_err) { close(fd); close(STDOUT_FILENO); remove(fname); dup2(old_stdout, STDOUT_FILENO); close(old_stdout); snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } } /* Get the size of the out put and read it in */ len = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); bufptr = buffer; len = ((len >= sizeof(buffer)) ? sizeof(buffer)-1 : len); while(len > 0) { size_t b = read(fd, bufptr, len); if(b <= 0) break; len -= b; bufptr += b; } *(bufptr++) = 0; /* Close the temp file, restore stdout, remove trailing newlines * from the program's output, then save it to the var */ fclose(tf); close(STDOUT_FILENO); dup2(old_stdout, STDOUT_FILENO); close(old_stdout); if(buffer[0] && buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = 0; if(*ptr) ret |= setenv(ptr, buffer, 1); if(ret != 0) { if(!ignore_err) { snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } continue; } continue; } /* Exits the script with the specified exitcode */ if(strcasecmp("exit", linebuf) == 0) { static int already_exited = 0; int inc = 0; int retval; if(already_exited) { fprintf(USEABLE_ERR, "\n\n*** Critical error ***\n" "Recursive exit call, aborting now!\n\n"); exit(-1); } already_exited = 1; extract_line(ptr, sizeof(linebuf)+linebuf-ptr); retval = atoi(ptr); for(i = defines_size-1;(size_t)i < defines_size;--i) { if(strncasecmp(defines[i].name, "atexit_", 7) == 0) inc += snprintf(linebuf+inc, sizeof(linebuf)-inc, "%s\n", defines[i].name); } snprintf(linebuf+inc, sizeof(linebuf)-inc, "__really_exit %d\n", retval); free(nextline); nextline = NULL; goto reparse; } /* Does nothing. Ignores the line */ if(strcasecmp("noop", linebuf) == 0) { extract_line(ptr, sizeof(linebuf)+linebuf-ptr); continue; } if(strcasecmp("__reset_cmd_args__", linebuf) == 0) { size_t i = arg_backup_size-1; size_t i2; if(i == ~0u) { fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "__reset_cmd_args__ called too many times!\n" "You didn't call it yourself, did you?", fname, curr_line); snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } extract_line(ptr, sizeof(linebuf)+linebuf-ptr); for(i2 = 0;i2 < argc;++i2) free(argv[i2]); free(argv); argc = arg_backup[i].argc; argv = arg_backup[i].argv; argv_len = 0; argv_size = argc-1; RESIZE_LIST(arg_backup, i); /* If i == 0, then argv is the _argv given to main. We don't want * to be realloc'ing that. */ if(i > 0) RESIZE_LIST(argv, argc+1); continue; } /* Exits the script with the specified exitcode */ if(strcasecmp("__really_exit", linebuf) == 0) { extract_line(ptr, sizeof(linebuf)+linebuf-ptr); exit(atoi(ptr)); } for(i = 0;(size_t)i < defines_size;++i) { char *next; size_t i2; if(strcasecmp(defines[i].name, linebuf) != 0) continue; i2 = arg_backup_size; RESIZE_LIST(arg_backup, i2+1); arg_backup[i2].argv = argv; arg_backup[i2].argc = argc; argc = 0; argv = NULL; argv_len = 0; RESIZE_LIST(argv, 2); argv[argc++] = strdup(linebuf); while(*ptr) { next = extract_word(ptr, sizeof(linebuf)+linebuf-ptr); RESIZE_LIST(argv, argc+2); argv[argc++] = strdup(ptr); ptr = next; } argv[argc] = NULL; snprintf(linebuf, sizeof(linebuf), "%s\n__reset_cmd_args__\n%s\n", defines[i].val, (nextline?nextline:"")); free(nextline); nextline = NULL; goto reparse; } fprintf(USEABLE_ERR, "\n\n!!! %s error, line %d !!!\n" "Unknown command '%s'\n\n", fname, curr_line, linebuf); snprintf(linebuf, sizeof(linebuf), "exit %d\n", ret); goto reparse; } return ret; }