/* Driver for Minix compilers. Written june 1987 by Ceriel J.H. Jacobs, partly derived from old cc-driver, written by Erik Baalbergen. This driver is mostly table-driven, the table being in the form of some global initialized structures. */ /* $Header: /cvsup/minix/src/commands/i86/cc.c,v 1.1.1.1 2005/04/21 14:54:55 beng Exp $ */ #include #include #include #include #include #include #include #include /* Paths. (Executables in /usr are first tried with /usr stripped off.) */ #define SHELL "/bin/sh" #define PP "/usr/lib/ncpp" #define IRREL "/usr/lib/irrel" #define CEM "/usr/lib/ncem" #define M2EM "/usr/lib/nm2em" #define ENCODE "/usr/lib/em_encode" #define OPT "/usr/lib/nopt" #define CG "/usr/lib/ncg" #define AS "/usr/lib/as" #define LD "/usr/lib/ld" #define CV "/usr/lib/cv" #define LIBDIR "/usr/lib" #define CRT "/usr/lib/ncrtso.o" #define PEM "/usr/lib/npem" #define PRT "/usr/lib/nprtso.o" #define M2RT "/usr/lib/nm2rtso.o" #define LIBC "/usr/lib/libd.a", "/usr/lib/libc.a" #define LIBP "/usr/lib/libp.a", "/usr/lib/libc.a" #define LIBM2 "/usr/lib/libm2.a", "/usr/lib/libc.a" #define END "/usr/lib/libe.a", "/usr/lib/end.a" #define M2DEF "-I/usr/lib/m2" /* every pass that this program knows about has associated with it a structure, containing such information as its name, where it resides, the flags it accepts, and the like. */ struct passinfo { char *p_name; /* name of this pass */ char *p_path; /* where is it */ char *p_from; /* suffix of source (comma-separated list) */ char *p_to; /* suffix of destination */ char *p_acceptflags; /* comma separated list; format: flag flag* flag=xxx flag*=xxx[*] where a star matches a, possibly empty, string */ int p_flags; #define INPUT 01 /* needs input file as argument */ #define OUTPUT 02 /* needs output file as argument */ #define LOADER 04 /* this pass is the loader */ #define STDIN 010 /* reads from standard input */ #define STDOUT 020 /* writes on standard output */ #define NOCLEAN 040 /* do not remove target if this pass fails */ #define O_OUTPUT 0100 /* -o outputfile, hack for as */ #define PREPALWAYS 0200 /* always to be preprocessed */ #define PREPCOND 0400 /* preprocessed when starting with '#' */ #define PREPNOLN 01000 /* suppress line number info (cpp -P) */ }; #define MAXHEAD 10 #define MAXTAIL 5 #define MAXPASS 7 /* Every language handled by this program has a "compile" structure associated with it, describing the start-suffix, how the driver for this language is called, which passes must be called, which flags and arguments must be passed to these passes, etc. The language is determined by the suffix of the argument program. However, if this suffix does not determine a language (DEFLANG), the callname is used. Notice that the 's' suffix does not determine a language, because the input file could have been derived from f.i. a C-program. So, if you use "cc x.s", the C-runtime system will be used, but if you use "as x.s", it will not. */ struct compile { char *c_suffix; /* starting suffix of this list of passes */ char *c_callname; /* affects runtime system loaded with program */ struct pass { char *pp_name; /* name of the pass */ char *pp_head[MAXHEAD]; /* args in front of filename */ char *pp_tail[MAXTAIL]; /* args after filename */ } c_passes[MAXPASS]; int c_flags; #define DEFLANG 010 /* this suffix determines a language */ }; struct passinfo passinfo[] = { { "cpp", PP, "CPP", "i", "wo=o,I*,D*,U*,P", INPUT|STDOUT }, { "irrel", IRREL, "i", "i", "m", INPUT}, { "cem", CEM, "i,c", "k", "m=o,p,wa=a,wo=o,ws=s,w,T*", INPUT|OUTPUT|PREPALWAYS }, { "pc", PEM, "i,p", "k", "n=L,w,a,A,R", INPUT|OUTPUT|PREPCOND }, { "m2", M2EM, "i,mod", "k", "n=L,w*,A,R,W*,3,I*", INPUT|OUTPUT|PREPCOND }, { "encode", ENCODE, "i,e", "k", "", INPUT|STDOUT|PREPCOND|PREPNOLN }, { "opt", OPT, "k", "m", "", STDIN|STDOUT }, { "cg", CG, "m", "s", "O=p4", INPUT|OUTPUT }, { "as", AS, "i,s", "o", "T*", INPUT|O_OUTPUT|PREPCOND }, { "ld", LD, "o", "out", "i,s", INPUT|LOADER }, /* changed */ { "cv", CV, "out", 0, "", INPUT|OUTPUT|NOCLEAN }, /* must come after loader */ { 0} }; #define PREP_FLAGS "-D_EM_WSIZE=2", "-D_EM_PSIZE=2", "-D_EM_SSIZE=2", \ "-D_EM_LSIZE=4", "-D_EM_FSIZE=4", "-D_EM_DSIZE=8", \ "-D__ACK__", "-D__minix", "-D__i86" struct pass preprocessor = { "cpp", { PREP_FLAGS } , {0} }; struct pass prepnoln = { "cpp", { PREP_FLAGS, "-P" } , {0} }; struct pass irrel = { "irrel", {0} }; /* The "*" in the arguments for the loader indicates the place where the * fp-emulation library should come. */ struct compile passes[] = { { "c", "cc", { { "cem", {"-L"}, {0} }, /* changed */ { "opt", {0}, {0} }, { "cg", {0}, {0} }, { "as", {"-"}, {0} }, { "ld", {CRT}, /* changed */ {LIBC, "*", END}}, { "cv", {0}, {0} } }, DEFLANG }, { "p", "pc", { { "pc", {0}, {0} }, { "opt", {0}, {0} }, { "cg", {0}, {0} }, { "as", {"-"}, {0} }, { "ld", {PRT}, {LIBP, "*", END}}, { "cv", {0}, {0} } }, DEFLANG }, { "mod", "m2", { { "m2", {M2DEF}, {0} }, { "opt", {0}, {0} }, { "cg", {0}, {0} }, { "as", {"-"}, {0} }, { "ld", {M2RT}, {LIBM2, "*", END}}, { "cv", {0}, {0} } }, DEFLANG }, { "e", "encode", { { "encode", {0}, {0}}, { "opt", {0}, {0} }, { "cg", {0}, {0} }, { "as", {"-"}, {0} }, { "ld", {0}, {"*", END}}, { "cv", {0}, {0} } }, DEFLANG }, { "s", "as", { { "as", {0}, {0}} }, 0 }, { "CPP", "cpp", { { "cpp", {PREP_FLAGS}, {0}} }, DEFLANG }, { 0}, }; #define MAXARGC 150 /* maximum number of arguments allowed in a list */ #define USTR_SIZE 64 /* maximum length of string variable */ typedef char USTRING[USTR_SIZE]; struct arglist { int al_argc; char *al_argv[MAXARGC]; }; struct arglist CALLVEC; int kids = -1; char *o_FILE = "a.out"; /* default name for executable file */ #define init(a) ((a)->al_argc = 1) #define cleanup(str) (str && remove(str)) char *ProgCall = 0; int RET_CODE = 0; char *stopsuffix; int v_flag = 0; int t_flag = 0; int noexec = 0; int fp_lib = 1; int E_flag = 0; int i_flag = 1; USTRING curfil; USTRING newfil; struct arglist SRCFILES; struct arglist LDIRS; struct arglist LDFILES; struct arglist GEN_LDFILES; struct arglist FLAGS; char *tmpdir = "/tmp"; char tmpname[64]; struct compile *compbase; struct pass *loader; struct passinfo *loaderinfo; char *source; int maxLlen; _PROTOTYPE(char *library, (char *nm )); _PROTOTYPE(void trapcc, (int sig )); _PROTOTYPE(int main, (int argc, char *argv [])); _PROTOTYPE(int remove, (char *str )); _PROTOTYPE(char *alloc, (unsigned u )); _PROTOTYPE(int append, (struct arglist *al, char *arg )); _PROTOTYPE(int concat, (struct arglist *al1, struct arglist *al2 )); _PROTOTYPE(char *mkstr, (char *dst, char *arg1, char *arg2, char *arg3 )); _PROTOTYPE(int basename, (char *str, char *dst )); _PROTOTYPE(char *extension, (char *fln )); _PROTOTYPE(int runvec, (struct arglist *vec, struct passinfo *pass, char *in, char *out )); _PROTOTYPE(int prnum, (unsigned x )); _PROTOTYPE(int prs, (char *str )); _PROTOTYPE(int panic, (char *str )); _PROTOTYPE(int pr_vec, (struct arglist *vec )); _PROTOTYPE(int ex_vec, (struct arglist *vec )); _PROTOTYPE(int mktempname, (char *nm )); _PROTOTYPE(int mkbase, (void)); _PROTOTYPE(int mkloader, (void)); _PROTOTYPE(int needsprep, (char *name )); _PROTOTYPE(int cfile, (char *name )); _PROTOTYPE(char *apply, (struct passinfo *pinf, struct compile *cp, char *name, int passindex, int noremove, int first, char *resultname )); _PROTOTYPE(int applicable, (struct passinfo *pinf, char *suffix )); _PROTOTYPE(char *process, (char *name, int noremove )); _PROTOTYPE(int mkvec, (struct arglist *call, char *in, char *out, struct pass *pass, struct passinfo *pinf )); _PROTOTYPE(int callld, (struct arglist *in, char *out, struct pass *pass, struct passinfo *pinf )); _PROTOTYPE(int clean, (struct arglist *c )); _PROTOTYPE(int scanflags, (struct arglist *call, struct passinfo *pinf )); char * library(nm) char *nm; { static char f[512]; int Lcount; for (Lcount = 0; Lcount < LDIRS.al_argc; Lcount++) { mkstr(f, LDIRS.al_argv[Lcount], "/lib", nm); strcat(f, ".a"); if (access(f, 0) != 0) { f[strlen(f)-1] = 'a'; if (access(f, 0) != 0) continue; } return f; } mkstr(f, LIBDIR, "/lib", nm); strcat(f, ".a"); if (access(f, 0) != 0) { int i = strlen(f) - 1; f[i] = 'a'; if (access(f, 0) != 0) f[i] = 'A'; } return f; } void trapcc(sig) int sig; { signal(sig, SIG_IGN); if (kids != -1) kill(kids, sig); cleanup(newfil); cleanup(curfil); exit(1); } main(argc, argv) char *argv[]; { char *str; char **argvec; int count; char *file; maxLlen = strlen(LIBDIR); ProgCall = *argv++; mkbase(); if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, trapcc); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, trapcc); if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) signal(SIGQUIT, trapcc); while (--argc > 0) { if (*(str = *argv++) != '-' || str[1] == 0) { append(&SRCFILES, str); continue; } if (strcmp(str, "-com") == 0) { i_flag = 0; } else if (strcmp(str, "-sep") == 0) { i_flag = 1; } else { switch (str[1]) { case 'c': stopsuffix = "o"; if (str[2] == '.') stopsuffix = str + 3; break; case 'f': fp_lib = (strcmp(str+2, "hard") != 0); break; case 'F': case 'W': /* Ignore. */ break; case 'L': append(&LDIRS, &str[2]); count = strlen(&str[2]); if (count > maxLlen) maxLlen = count; break; case 'l': append(&SRCFILES, library(&str[2])); break; case 'm': /* Use -m, ignore -mxxx. */ if (str[2] == 0) append(&FLAGS, str); break; case 'o': if (argc-- >= 0) o_FILE = *argv++; break; case 'S': stopsuffix = "s"; break; case 'E': E_flag = 1; stopsuffix = "i"; break; case 'P': stopsuffix = "i"; append(&FLAGS, str); break; case 'v': v_flag++; if (str[2] == 'n') noexec = 1; break; case 't': /* save temporaries */ t_flag++; break; case '.': if (str[2] == 'o') { /* no runtime start-off */ loader->pp_head[0] = 0; } break; case 'i': i_flag++; break; case 'T': tmpdir = &str[2]; /*FALLTHROUGH*/ default: append(&FLAGS, str); } } } if (i_flag) append(&FLAGS, "-i"); mktempname(tmpname); count = SRCFILES.al_argc; argvec = &(SRCFILES.al_argv[0]); while (count-- > 0) { file = *argvec++; source = file; file = process(file, 1); if (file && ! stopsuffix) append(&LDFILES, file); } clean(&SRCFILES); /* loader ... */ if (RET_CODE == 0 && LDFILES.al_argc > 0) { register struct passinfo *pp = passinfo; while (!(pp->p_flags & LOADER)) pp++; mkstr(newfil, tmpname, pp->p_to, ""); callld(&LDFILES, !((pp+1)->p_name) ? o_FILE : newfil, loader, pp); if (RET_CODE == 0) { register int i = GEN_LDFILES.al_argc; while (i-- > 0) { remove(GEN_LDFILES.al_argv[i]); free(GEN_LDFILES.al_argv[i]); } if ((++pp)->p_name) { process(newfil, 0); } } } exit(RET_CODE); } remove(str) char *str; { if (t_flag) return; if (v_flag) { prs("rm "); prs(str); prs("\n"); } if (noexec) return; unlink(str); } char * alloc(u) unsigned u; { register char *p = malloc(u); if (p == 0) panic("no space\n"); return p; } append(al, arg) struct arglist *al; char *arg; { char *a = alloc((unsigned) (strlen(arg) + 1)); strcpy(a, arg); if (al->al_argc >= MAXARGC) panic("argument list overflow\n"); al->al_argv[(al->al_argc)++] = a; } concat(al1, al2) struct arglist *al1, *al2; { register i = al2->al_argc; register char **p = &(al1->al_argv[al1->al_argc]); register char **q = &(al2->al_argv[0]); if ((al1->al_argc += i) >= MAXARGC) panic("argument list overflow\n"); while (i-- > 0) *p++ = *q++; } char * mkstr(dst, arg1, arg2, arg3) char *dst, *arg1, *arg2, *arg3; { register char *p; register char *q = dst; p = arg1; while (*q++ = *p++); q--; p = arg2; while (*q++ = *p++); q--; p = arg3; while (*q++ = *p++); q--; return dst; } basename(str, dst) char *str; register char *dst; { register char *p1 = str; register char *p2 = p1; while (*p1) if (*p1++ == '/') p2 = p1; p1--; while (*p1 != '.' && p1 > p2) p1--; if (*p1 == '.') { *p1 = '\0'; while (*dst++ = *p2++); *p1 = '.'; } else while (*dst++ = *p2++); } char * extension(fln) char *fln; { register char *fn = fln; while (*fn) fn++; while (fn > fln && *fn != '.') fn--; if (fn != fln) return fn+1; return (char *)0; } runvec(vec, pass, in, out) struct arglist *vec; struct passinfo *pass; char *in, *out; { int pid, status; int shifted = 0; if ( strncmp(vec->al_argv[1], "/usr/", 5) == 0 && access(vec->al_argv[1] + 4, 1) == 0 ) { vec->al_argv[1] += 4; shifted = 1; } if (v_flag) { pr_vec(vec); if (pass->p_flags & STDIN) { prs(" <"); prs(in); } if (pass->p_flags & STDOUT && !E_flag) { prs(" >"); prs(out); } prs("\n"); } if (noexec) { if (shifted) vec->al_argv[1] -= 4; clean(vec); return 1; } if ((pid = fork()) == 0) { /* start up the process */ if (pass->p_flags & STDIN && strcmp(in, "-") != 0) { /* redirect standard input */ close(0); if (open(in, 0) != 0) panic("cannot open input file\n"); } if (pass->p_flags & STDOUT && !E_flag) { /* redirect standard output */ close(1); if (creat(out, 0666) != 1) panic("cannot create output file\n"); } ex_vec(vec); } if (pid == -1) panic("no more processes\n"); kids = pid; wait(&status); if (status) switch(status & 0177) { case SIGHUP: case SIGINT: case SIGQUIT: case SIGTERM: case 0: break; default: if (E_flag && (status & 0177) == SIGPIPE) break; prs(vec->al_argv[1]); prs(" died with signal "); prnum(status & 0177); prs("\n"); } if (shifted) vec->al_argv[1] -= 4; clean(vec); kids = -1; return status ? ((RET_CODE = 1), 0) : 1; } prnum(x) register unsigned x; { static char numbuf[8]; /* though it prints at most 3 characters */ register char *cp = numbuf + sizeof(numbuf) - 1; *cp = '\0'; while (x >= 10) { *--cp = (x % 10) + '0'; x /= 10; } *--cp = x + '0'; prs(cp); } prs(str) char *str; { if (str && *str) write(2, str, strlen(str)); } panic(str) char *str; { prs(str); trapcc(SIGINT); } pr_vec(vec) register struct arglist *vec; { register char **ap = &vec->al_argv[1]; vec->al_argv[vec->al_argc] = 0; prs(*ap); while (*++ap) { prs(" "); if (strlen(*ap)) prs(*ap); else prs("(empty)"); } } ex_vec(vec) register struct arglist *vec; { extern int errno; vec->al_argv[vec->al_argc] = 0; execv(vec->al_argv[1], &(vec->al_argv[1])); if (errno == ENOEXEC) { /* not an a.out, try it with the SHELL */ vec->al_argv[0] = SHELL; execv(SHELL, &(vec->al_argv[0])); } if (access(vec->al_argv[1], 1) == 0) { /* File is executable. */ prs("Cannot execute "); prs(vec->al_argv[1]); prs(". Not enough memory.\n"); prs("Reduce the memory use of your system and try again\n"); } else { prs(vec->al_argv[1]); prs(" is not executable\n"); } exit(1); } mktempname(nm) register char *nm; { register int i; register int pid = getpid(); mkstr(nm, tmpdir, "/", compbase->c_callname); while (*nm) nm++; for (i = 9; i > 3; i--) { *nm++ = (pid % 10) + '0'; pid /= 10; } *nm++ = '.'; *nm++ = '\0'; /* null termination */ } mkbase() { register struct compile *p = passes; USTRING callname; register int len; basename(ProgCall, callname); len = strlen(callname); while (p->c_suffix) { if (strcmp(p->c_callname, callname+len-strlen(p->c_callname)) == 0) { compbase = p; mkloader(); return; } p++; } /* we should not get here */ panic("internal error\n"); } mkloader() { register struct passinfo *p = passinfo; register struct pass *pass; while (!(p->p_flags & LOADER)) p++; loaderinfo = p; pass = &(compbase->c_passes[0]); while (strcmp(pass->pp_name, p->p_name)) pass++; loader = pass; } needsprep(name) char *name; { int file; char fc; file = open(name,0); if (file <0) return 0; if (read(file, &fc, 1) != 1) fc = 0; close(file); return fc == '#'; } cfile(name) char *name; { while (*name != '\0' && *name != '.') name++; if (*name == '\0') return 0; return (*++name == 'c' && *++name == '\0'); } char * apply(pinf, cp, name, passindex, noremove, first, resultname) register struct passinfo *pinf; register struct compile *cp; char *name, *resultname; { /* Apply a pass, indicated by "pinf", with args in cp->c_passes[passindex], to name "name", leaving the result in a file with name "resultname", concatenated with result suffix. When neccessary, the preprocessor is run first. If "noremove" is NOT set, the file "name" is removed. */ struct arglist *call = &CALLVEC; struct pass *pass = &(cp->c_passes[passindex]); char *outname; if ( /* this pass is the first pass */ first && ( /* preprocessor always needed */ (pinf->p_flags & PREPALWAYS) ||/* or only when "needsprep" says so */ ( (pinf->p_flags & PREPCOND) && needsprep(name)) ) ) { mkstr(newfil, tmpname, passinfo[0].p_to, ""); mkvec(call, name, newfil, (pinf->p_flags & PREPNOLN) ? &prepnoln : &preprocessor, &passinfo[0]); if (! runvec(call, &passinfo[0], name, newfil)) { cleanup(newfil); return 0; } /* A .c file must always be mishandled by irrel. */ if (cfile(name)) { /* newfil is OK */ mkvec(call, newfil, newfil, &irrel, &passinfo[1]); if (! runvec(call, &passinfo[1], newfil, newfil)) { cleanup(newfil); return 0; } } strcpy(curfil, newfil); newfil[0] = '\0'; name = curfil; noremove = 0; } if (pinf->p_to) outname = mkstr(newfil, resultname, pinf->p_to, ""); else outname = o_FILE; mkvec(call, name, outname, pass, pinf); if (! runvec(call, pinf, name, outname)) { if (! (pinf->p_flags & NOCLEAN)) cleanup(outname); if (! noremove) cleanup(name); return 0; } if (! noremove) cleanup(name); strcpy(curfil, newfil); newfil[0] = '\0'; return curfil; } int applicable(pinf, suffix) struct passinfo *pinf; char *suffix; { /* Return one if the pass indicated by "pinfo" is applicable to a file with suffix "suffix". */ register char *sfx = pinf->p_from; int l; if (! suffix) return 0; l = strlen(suffix); while (*sfx) { register char *p = sfx; while (*p && *p != ',') p++; if (l == p - sfx && strncmp(sfx, suffix, l) == 0) { return 1; } if (*p == ',') sfx = p+1; else sfx = p; } return 0; } char * process(name, noremove) char *name; { register struct compile *cp = passes; char *suffix = extension(name); USTRING base; register struct pass *pass; register struct passinfo *pinf; if (E_flag) { /* -E uses the cpp pass. */ suffix = "CPP"; } if (! suffix) return name; basename(name, base); while (cp->c_suffix) { if ((cp->c_flags & DEFLANG) && strcmp(cp->c_suffix, suffix) == 0) break; cp++; } if (! cp->c_suffix) cp = compbase; pass = cp->c_passes; while (pass->pp_name) { int first = 1; for (pinf=passinfo; strcmp(pass->pp_name,pinf->p_name);pinf++) ; if (! (pinf->p_flags & LOADER) && applicable(pinf, suffix)) { int cont = ! stopsuffix || ! pinf->p_to || strcmp(stopsuffix, pinf->p_to) != 0; name = apply(pinf, cp, name, (int) (pass - cp->c_passes), noremove, first, applicable(loaderinfo, pinf->p_to) || !cont ? strcat(base, ".") : tmpname); first = noremove = 0; suffix = pinf->p_to; if (!cont || !name) break; } pass++; } if (!noremove && name) append(&GEN_LDFILES, name); return name; } mkvec(call, in, out, pass, pinf) struct arglist *call; char *in, *out; struct pass *pass; register struct passinfo *pinf; { register int i; init(call); append(call, pinf->p_path); scanflags(call, pinf); if (pass) for (i = 0; i < MAXHEAD; i++) if (pass->pp_head[i]) append(call, pass->pp_head[i]); else break; if (pinf->p_flags & INPUT && strcmp(in, "-") != 0) append(call, in); if (pinf->p_flags & OUTPUT) append(call, out); if (pinf->p_flags & O_OUTPUT) { append(call, "-o"); append(call, out); } if (pass) for (i = 0; i < MAXTAIL; i++) if (pass->pp_tail[i]) append(call, pass->pp_tail[i]); else break; } callld(in, out, pass, pinf) struct arglist *in; char *out; struct pass *pass; register struct passinfo *pinf; { struct arglist *call = &CALLVEC; register int i; init(call); append(call, pinf->p_path); scanflags(call, pinf); append(call, "-o"); append(call, out); for (i = 0; i < MAXHEAD; i++) if (pass->pp_head[i]) append(call, pass->pp_head[i]); else break; if (pinf->p_flags & INPUT) concat(call, in); if (pinf->p_flags & OUTPUT) append(call, out); for (i = 0; i < MAXTAIL; i++) { if (pass->pp_tail[i]) { if (pass->pp_tail[i][0] == '-' && pass->pp_tail[i][1] == 'l') { append(call, library(&(pass->pp_tail[i][2]))); } else if (*(pass->pp_tail[i]) != '*') append(call, pass->pp_tail[i]); else if (fp_lib) append(call, library("fp")); } else break; } if (! runvec(call, pinf, (char *) 0, out)) { cleanup(out); RET_CODE = 1; } } clean(c) register struct arglist *c; { register int i; for (i = 1; i < c->al_argc; i++) { free(c->al_argv[i]); c->al_argv[i] = 0; } c->al_argc = 0; } scanflags(call, pinf) struct arglist *call; struct passinfo *pinf; { /* Find out which flags from FLAGS must be passed to pass "pinf", and how. Append them to "call" */ register int i; USTRING flg; for (i = 0; i < FLAGS.al_argc; i++) { register char *q = pinf->p_acceptflags; while (*q) { register char *p = FLAGS.al_argv[i] + 1; while (*q && *q == *p) { q++; p++; } if (*q == ',' || !*q) { if (! *p) { /* append literally */ append(call, FLAGS.al_argv[i]); } break; } if (*q == '*') { register char *s = flg; if (*++q != '=') { /* append literally */ append(call, FLAGS.al_argv[i]); break; } *s++ = '-'; if (*q) q++; /* skip ',' */ while (*q && *q != ',' && *q != '*') { /* copy replacement flag */ *s++ = *q++; } if (*q == '*') { /* copy rest */ while (*p) *s++ = *p++; } *s = 0; append(call, flg); break; } if (*q == '=') { /* copy replacement */ register char *s = flg; *s++ = '-'; q++; while (*q && *q != ',') *s++ = *q++; *s = 0; append(call, flg); break; } while (*q && *q++ != ',') ; } } }