示例#1
0
static int
intercept(Sfio_t* sp, int level, int flags)
{
	register Rule_t*	r;
	char*			m;
	char*			s;
	char*			t;
	char*			e;
	int			n;
	int			i;
	Sfio_t*			tmp;

	NoP(sp);
	NoP(flags);
	if ((state.mam.level = level) > 0 && !state.hold && (r = internal.error) && (r->property & (P_functional|P_target)) == (P_functional|P_target) && !state.compileonly && !state.interrupt && (m = stakptr(0)) && (n = staktell()) > 0)
	{
		/*
		 * make the error trap
		 */

		state.hold = m;
		while (*m && *m != ':')
			m++;
		while (isspace(*++m));
		n -= m - state.hold;
		tmp = sfstropen();
		sfprintf(tmp, "%d %-.*s", level, n, m);
		s = sfstruse(tmp);

		/*
		 * return [ level | - ] [ message ]
		 * level is new level or - to retain old
		 * omitted message means it has been printed
		 */

		if (t = call(r, s))
		{
			i = strtol(t, &e, 0);
			if (e > t)
			{
				t = e;
				level = i;
			}
			else if (*t == '-')
				t++;
			while (isspace(*t))
				t++;
		}
		if (!t || !*t)
		{
			level |= ERROR_OUTPUT;
			message((-1, "suppressed %s message: `%-.*s'", level == 1 ? "warning" : "error", n, m));
		}
		sfstrclose(tmp);
		state.hold = 0;
	}
	return level;
}
示例#2
0
static void
commit(Joblist_t* job, register char* s)
{
	register char*		t;
	register char*		v;
	register Rule_t*	r;
	Stat_t			st;

	if (t = strrchr(s, '/'))
	{
		*t = 0;
		if (r = bindfile(NiL, s, 0))
		{
			if (!r->view)
			{
				*t = '/';
				return;
			}
			if (*(v = r->name) != '/')
			{
				sfprintf(internal.nam, "%s/%s", state.view[r->view].root, v);
				v = sfstruse(internal.nam);
			}
			if (stat(v, &st))
				r = 0;
		}
		if (r || state.targetcontext && (!r || !r->time) && (st.st_mode = (S_IRWXU|S_IRWXG|S_IRWXO)) && tmxsetmtime(&st, state.start))
		{
			/*
			 * why not mkdir -p here?
			 */

			commit(job, s);
			if (((job->flags & CO_ALWAYS) || state.exec && state.touch) && (mkdir(s, st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) || stat(s, &st)))
				error(1, "%s: cannot create target directory %s", job->target->name, s);
			if (state.mam.out)
			{
				Sfio_t*	tmp = sfstropen();

				sfprintf(tmp, "mkdir %s", s);
				dumpaction(state.mam.out, MAMNAME(job->target), sfstruse(tmp), NiL);
				sfstrclose(tmp);
			}
			r = makerule(s);
			if (r->dynamic & D_alias)
				oldname(r);
			r->view = 0;
			r->time = tmxgetmtime(&st);
			if (r->dynamic & D_scanned)
				unbind(NiL, (char*)r, NiL);
		}
		*t = '/';
	}
}
示例#3
0
Time_t
tmxduration(const char* s, char** e)
{
	Time_t		ns;
	Time_t		ts;
	Time_t		now;
	char*		last;
	char*		t;
	char*		x;
	Sfio_t*		f;
	int		i;

	now = TMX_NOW;
	while (isspace(*s))
		s++;
	if (*s == 'P' || *s == 'p')
		ns = tmxdate(s, &last, now) - now;
	else
	{
		ns = strtod(s, &last) * TMX_RESOLUTION;
		if (*last && (f = sfstropen()))
		{
			sfprintf(f, "exact %s", s);
			t = sfstruse(f);
			ts = tmxdate(t, &x, now);
			if ((i = x - t - 6) > (last - s))
			{
				last = (char*)s + i;
				ns = ts - now;
			}
			else
			{
				sfprintf(f, "p%s", s);
				t = sfstruse(f);
				ts = tmxdate(t, &x, now);
				if ((i = x - t - 1) > (last - s))
				{
					last = (char*)s + i;
					ns = ts - now;
				}
			}
			sfstrclose(f);
		}
	}
	if (e)
		*e = last;
	return ns;
}
示例#4
0
static int
print(Expr_t* ex, Exnode_t* expr, void* env, Sfio_t* sp)
{
	register Print_t*	x;
	Extype_t		v;
	Fmt_t			fmt;

	if (!sp)
	{
		v = eval(ex, expr->data.print.descriptor, env);
		if (v.integer < 0 || v.integer >= elementsof(ex->file) || !(sp = ex->file[v.integer]) && !(sp = ex->file[v.integer] = sfnew(NiL, NiL, SF_UNBOUND, v.integer, SF_READ|SF_WRITE)))
		{
			exerror("printf: %d: invalid descriptor", v.integer);
			return 0;
		}
	}
	memset(&fmt, 0, sizeof(fmt));
	fmt.fmt.version = SFIO_VERSION;
	fmt.fmt.extf = prformat;
	fmt.expr = ex;
	fmt.env = env;
	x = expr->data.print.args;
	if (x->format)
		do
		{
			if (x->arg)
			{
				fmt.fmt.form = x->format;
				fmt.args = x;
				sfprintf(sp, "%!", &fmt);
			}
			else
				sfputr(sp, x->format, -1);
		} while (x = x->next);
	else
	{
		v = eval(ex, x->arg->data.operand.left, env);
		fmt.fmt.form = v.string;
		fmt.actuals = x->arg;
		sfprintf(sp, "%!", &fmt);
		if (fmt.actuals->data.operand.right)
			exerror("printf: \"%s\": too many arguments", fmt.fmt.form);
	}
	if (fmt.tmp)
		sfstrclose(fmt.tmp);
	return 1;
}
示例#5
0
int
vasprintf(char** s, const char* fmt, va_list args)
{
	Sfio_t*	f;
	int	v;

	if (f = sfstropen())
	{
		v = sfvprintf(f, fmt, args);
		if (!(*s = strdup(sfstruse(f))))
			v = -1;
		sfstrclose(f);
	}
	else
	{
		*s = 0;
		v = -1;
	}
	return v;
}
示例#6
0
文件: msggen.c 项目: alhazred/onarm
static int
ccsfprintf(int from, int to, Sfio_t* sp, const char* format, ...)
{
	va_list		ap;
	Sfio_t*		tp;
	char*		s;
	int		n;

	va_start(ap, format);
	if (from == to)
		n = sfvprintf(sp, format, ap);
	else if (tp = sfstropen())
	{
		n = sfvprintf(tp, format, ap);
		s = sfstrbase(tp);
		ccmaps(s, n, from, to);
		n = sfwrite(sp, s, n);
		sfstrclose(tp);
	}
	else
		n = -1;
	return n;
}
示例#7
0
void
ppbuiltin(void)
{
	register int		c;
	register char*		p;
	register char*		a;

	int			n;
	int			op;
	char*			token;
	char*			t;
	long			number;
	long			onumber;
	struct ppinstk*		in;
	struct pplist*		list;
	struct ppsymbol*	sym;
	Sfio_t*			sp;

	number = pp.state;
	pp.state |= DISABLE|FILEPOP|NOSPACE;
	token = pp.token;
	p = pp.token = pp.tmpbuf;
	*(a = pp.args) = 0;
	if ((c = pplex()) != T_ID)
	{
		error(2, "%s: #(<identifier>...) expected", p);
		*p = 0;
	}
	switch (op = (int)hashget(pp.strtab, p))
	{
	case V_DEFAULT:
		n = 0;
		p = pp.token = pp.valbuf;
		if ((c = pplex()) == ',')
		{
			op = -1;
			c = pplex();
		}
		pp.state &= ~NOSPACE;
		for (;;)
		{
			if (!c)
			{
				error(2, "%s in #(...) argument", pptokchr(c));
				break;
			}
			if (c == '(') n++;
			else if (c == ')' && !n--) break;
			else if (c == ',' && !n && op > 0) op = 0;
			if (op) pp.token = pp.toknxt;
			c = pplex();
		}
		*pp.token = 0;
		pp.token = token;
		pp.state = number;
		break;
	case V_EMPTY:
		p = pp.valbuf;
		if ((c = pplex()) == ')') *p = '1';
		else
		{
			*p = '0';
			n = 0;
			for (;;)
			{
				if (!c)
				{
					error(2, "%s in #(...) argument", pptokchr(c));
					break;
				}
				if (c == '(') n++;
				else if (c == ')' && !n--) break;
				c = pplex();
			}
		}
		*(p + 1) = 0;
		pp.token = token;
		pp.state = number;
		break;
	case V_ITERATE:
		n = 0;
		pp.token = pp.valbuf;
		if ((c = pplex()) != T_ID || !(sym = ppsymref(pp.symtab, pp.token)) || !sym->macro || sym->macro->arity != 1 || (c = pplex()) != ',')
		{
			error(2, "#(%s <macro(x)>, ...) expected", p);
			for (;;)
			{
				if (!c)
				{
					error(2, "%s in #(...) argument", pptokchr(c));
					break;
				}
				if (c == '(') n++;
				else if (c == ')' && !n--) break;
				c = pplex();
			}
			*pp.valbuf = 0;
		}
		else while (c != ')')
		{
			p = pp.token;
			if (pp.token > pp.valbuf) *pp.token++ = ' ';
			STRCOPY(pp.token, sym->name, a);
			*pp.token++ = '(';
			if (!c || !(c = pplex()))
			{
				pp.token = p;
				error(2, "%s in #(...) argument", pptokchr(c));
				break;
			}
			pp.state &= ~NOSPACE;
			while (c)
			{
				if (c == '(') n++;
				else if (c == ')' && !n--) break;
				else if (c == ',' && !n) break;
				pp.token = pp.toknxt;
				c = pplex();
			}
			*pp.token++ = ')';
			pp.state |= NOSPACE;
		}
		p = pp.valbuf;
		pp.token = token;
		pp.state = number;
		break;
	default:
		pp.token = token;
		while (c != ')')
		{
			if (!c)
			{
				error(2, "%s in #(...) argument", pptokchr(c));
				break;
			}
			if ((c = pplex()) == T_ID && !*a)
				strcpy(a, pp.token);
		}
		pp.state = number;
		switch (op)
		{
		case V_ARGC:
			c = -1;
			for (in = pp.in; in; in = in->prev)
				if ((in->type == IN_MACRO || in->type == IN_MULTILINE) && (in->symbol->flags & SYM_FUNCTION))
				{
					c = *((unsigned char*)(pp.macp->arg[0] - 2));
					break;
				}
			sfsprintf(p = pp.valbuf, MAXTOKEN, "%d", c);
			break;
		case V_BASE:
			p = (a = strrchr(error_info.file, '/')) ? a + 1 : error_info.file;
			break;
		case V_DATE:
			if (!(p = pp.date))
			{
				time_t	tm;

				time(&tm);
				a = p = ctime(&tm) + 4;
				*(p + 20) = 0;
				for (p += 7; *p = *(p + 9); p++);
				pp.date = p = strdup(a);
			}
			break;
		case V_FILE:
			p = error_info.file;
			break;
		case V_LINE:
			sfsprintf(p = pp.valbuf, MAXTOKEN, "%d", error_info.line);
			break;
		case V_PATH:
			p = pp.path;
			break;
		case V_SOURCE:
			p = error_info.file;
			for (in = pp.in; in->prev; in = in->prev)
				if (in->prev->type == IN_FILE && in->file)
					p = in->file;
			break;
		case V_STDC:
			p = pp.valbuf;
			p[0] = ((pp.state & (COMPATIBILITY|TRANSITION)) || (pp.mode & (HOSTED|HOSTEDTRANSITION)) == (HOSTED|HOSTEDTRANSITION)) ? '0' : '1';
			p[1] = 0;
			break;
		case V_TIME:
			if (!(p = pp.time))
			{
				time_t	tm;

				time(&tm);
				p = ctime(&tm) + 11;
				*(p + 8) = 0;
				pp.time = p = strdup(p);
			}
			break;
		case V_VERSION:
			p = (char*)pp.version;
			break;
		case V_DIRECTIVE:
			pp.state |= NEWLINE;
			pp.mode |= RELAX;
			strcpy(p = pp.valbuf, "#");
			break;
		case V_GETENV:
			if (!(p = getenv(a))) p = "";
			break;
		case V_GETMAC:
			p = (sym = pprefmac(a, REF_NORMAL)) ? sym->macro->value : "";
			break;
		case V_GETOPT:
			sfsprintf(p = pp.valbuf, MAXTOKEN, "%ld", ppoption(a));
			break;
		case V_GETPRD:
			p = (list = (struct pplist*)hashget(pp.prdtab, a)) ? list->value : "";
			break;
		case V__PRAGMA:
			if ((c = pplex()) == '(')
			{
				number = pp.state;
				pp.state |= NOSPACE|STRIP;
				c = pplex();
				pp.state = number;
				if (c == T_STRING || c == T_WSTRING)
				{
					if (!(sp = sfstropen()))
						error(3, "temporary buffer allocation error");
					sfprintf(sp, "#%s %s\n", dirname(PRAGMA), pp.token);
					a = sfstruse(sp);
					if ((c = pplex()) == ')')
					{
						pp.state |= NEWLINE;
						PUSH_BUFFER(p, a, 1);
					}
					sfstrclose(sp);
				}
			}
			if (c != ')')
				error(2, "%s: (\"...\") expected", p);
			return;
		case V_FUNCTION:

#define BACK(a,p)	((a>p)?*--a:(number++?0:((p=pp.outbuf+PPBUFSIZ),(a=pp.outbuf+2*PPBUFSIZ),*--a)))
#define PEEK(a,p)	((a>p)?*(a-1):(number?0:*(pp.outbuf+2*PPBUFSIZ-1)))

			number = pp.outbuf != pp.outb;
			a = pp.outp;
			p = pp.outb;
			op = 0;
			while (c = BACK(a, p))
			{
				if (c == '"' || c == '\'')
				{
					op = 0;
					while ((n = BACK(a, p)) && n != c || PEEK(a, p) == '\\');
				}
				else if (c == '\n')
				{
					token = a;
					while (c = BACK(a, p))
						if (c == '\n')
						{
							a = token;
							break;
						}
						else if (c == '#' && PEEK(a, p) == '\n')
							break;
				}
				else if (c == ' ')
					/*ignore*/;
				else if (c == '{') /* '}' */
					op = 1;
				else if (op == 1)
				{
					if (c == ')')
					{
						op = 2;
						n = 1;
					}
					else
						op = 0;
				}
				else if (op == 2)
				{
					if (c == ')')
						n++;
					else if (c == '(' && !--n)
						op = 3;
				}
				else if (op == 3)
				{
					if (ppisidig(c))
					{
						for (t = p, token = a, onumber = number; ppisidig(PEEK(a, p)) && a >= p; BACK(a, p));
						p = pp.valbuf + 1;
						if (a > token)
						{
							for (; a < pp.outbuf+2*PPBUFSIZ; *p++ = *a++);
							a = pp.outbuf;
						}
						for (; a <= token; *p++ = *a++);
						*p = 0;
						p = pp.valbuf + 1;
						if (streq(p, "for") || streq(p, "if") || streq(p, "switch") || streq(p, "while"))
						{
							op = 0;
							p = t;
							number = onumber;
							continue;
						}
					}
					else
						op = 0;
					break;
				}
			}
			if (op == 3)
				p = strncpy(pp.funbuf, p, sizeof(pp.funbuf) - 1);
			else if (*pp.funbuf)
				p = pp.funbuf;
			else
				p = "__FUNCTION__";
			break;
		default:
			if (pp.builtin && (a = (*pp.builtin)(pp.valbuf, p, a)))
				p = a;
			break;
		}
		break;
	}
	if (strchr(p, MARK))
	{
		a = pp.tmpbuf;
		strcpy(a, p);
		c = p != pp.valbuf;
		p = pp.valbuf + c;
		for (;;)
		{
			if (p < pp.valbuf + MAXTOKEN - 2)
				switch (*p++ = *a++)
				{
				case 0:
					break;
				case MARK:
					*p++ = MARK;
					/*FALLTHROUGH*/
				default:
					continue;
				}
			break;
		}
		p = pp.valbuf + c;
	}
	if (p == pp.valbuf)
		PUSH_STRING(p);
	else
	{
		if (p == pp.valbuf + 1)
			*pp.valbuf = '"';
		else
		{
			if (strlen(p) > MAXTOKEN - 2)
				error(1, "%-.16s: builtin value truncated", p);
			sfsprintf(pp.valbuf, MAXTOKEN, "\"%-.*s", MAXTOKEN - 2, p);
		}
		PUSH_QUOTE(pp.valbuf, 1);
	}
}
示例#8
0
int
main(int argc, char** argv)
{
	register char*		s;
	register Rule_t*	r;
	register List_t*	p;
	int			i;
	int			args;
	int			trace;
	char*			t;
	char*			buf;
	char*			tok;
	Var_t*			v;
	Stat_t			st;
	Stat_t			ds;
	Sfio_t*			tmp;

	/*
	 * initialize dynamic globals
	 */

	version = strdup(fmtident(version));
	setlocale(LC_ALL, "");
	error_info.id = idname;
	error_info.version = version;
	error_info.exit = finish;
	error_info.auxilliary = intercept;
	if (pathcheck(PATHCHECK, error_info.id, NiL))
		return 1;
	error(-99, "startup");
	settypes("*?[]", C_MATCH);
	settypes("+-|=", C_OPTVAL);
	settypes(" \t\n", C_SEP);
	settype(0, C_SEP);
	settypes(" \t\v\n:+&=;\"\\", C_TERMINAL);
	settype(0, C_TERMINAL);
	settypes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", C_ID1|C_ID2|C_VARIABLE1|C_VARIABLE2);
	settypes(".", C_VARIABLE1|C_VARIABLE2);
	settypes("0123456789", C_ID2|C_VARIABLE2);

	/*
	 * close garbage fd's -- we'll be tidy from this point on
	 * 3 may be /dev/tty on some systems
	 * 0..9 for user redirection in shell
	 * 10..19 left open by bugs in some shells
	 * error_info.fd interited from parent
	 * any close-on-exec fd's must have been done on our behalf
	 */

	i = 3;
	if (isatty(i))
		i++;
	for (; i < 20; i++)
		if (i != error_info.fd && !fcntl(i, F_GETFD, 0))
			close(i);

	/*
	 * allocate the very temporary buffer streams
	 */

	internal.met = sfstropen();
	internal.nam = sfstropen();
	internal.tmp = sfstropen();
	internal.val = sfstropen();
	internal.wrk = sfstropen();
	tmp = sfstropen();
	sfstrrsrv(tmp, 2 * MAXNAME);

	/*
	 * initialize the code and hash tables
	 */

	initcode();
	inithash();

	/*
	 * set the default state
	 */

	state.alias = 1;
	state.exec = 1;
	state.global = 1;
	state.init = 1;
#if DEBUG
	state.intermediate = 1;
#endif
	state.io[0] = sfstdin;
	state.io[1] = sfstdout;
	state.io[2] = sfstderr;
	state.jobs = 1;
	state.pid = getpid();
	state.readstate = MAXVIEW;
	state.scan = 1;
	state.start = CURTIME;
	state.stateview = -1;
	state.tabstops = 8;
	state.targetview = -1;
#if BINDINDEX
	state.view[0].path = makerule(".");
#else
	state.view[0].path = ".";
#endif
	state.view[0].pathlen = 1;
	state.writeobject = state.writestate = "-";

	/*
	 * pwd initialization
	 *
	 * for project management, if . is group|other writeable
	 * then change umask() for similar write protections
	 */

	buf = sfstrbase(tmp);
	internal.pwd = (s = getcwd(buf, MAXNAME)) ? strdup(s) : strdup(".");
	internal.pwdlen = strlen(internal.pwd);
	if (stat(".", &st))
		error(3, "cannot stat .");
	if (S_ISDIR(st.st_mode) && (st.st_mode & (S_IWGRP|S_IWOTH)))
		umask(umask(0) & ~(st.st_mode & (S_IWGRP|S_IWOTH)));

	/*
	 * set some variable default values
	 */

	hashclear(table.var, HASH_ALLOCATE);
	setvar(external.make, argv[0], V_import);
	t = "lib/make";
	setvar(external.lib, strdup((s = pathpath(t, argv[0], PATH_EXECUTE, buf, SF_BUFSIZE)) ? s : t), V_import);
	setvar(external.pwd, internal.pwd, V_import);
	setvar(external.version, version, V_import);
	hashset(table.var, HASH_ALLOCATE);

	/*
	 * read the environment
	 */

	readenv();
	if (v = getvar(external.nproc))
		state.jobs = (int)strtol(v->value, NiL, 0);
	if ((v = getvar(external.pwd)) && !streq(v->value, internal.pwd))
	{
		if (!stat(v->value, &st) && !stat(internal.pwd, &ds) && st.st_ino == ds.st_ino && st.st_dev == ds.st_dev)
		{
			free(internal.pwd);
			internal.pwd = strdup(v->value);
			internal.pwdlen = strlen(v->value);
		}
		else
		{
			v->property &= ~V_import;
			v->property |= V_free;
			v->value = strdup(internal.pwd);
		}
	}

	/*
	 * initialize the internal rule pointers
	 */

	initrule();

	/*
	 * read the static initialization script
	 */

	sfputr(tmp, initstatic, -1);
	parse(NiL, sfstruse(tmp), "initstatic", NiL);

	/*
	 * check and read the args file
	 */

	if (s = colonlist(tmp, external.args, 1, ' '))
	{
		i = fs3d(0);
		tok = tokopen(s, 1);
		while (s = tokread(tok))
			if (vecargs(vecfile(s), &argc, &argv) >= 0)
				break;
			else if (errno != ENOENT)
				error(1, "cannot read args file %s", s);
		tokclose(tok);
		fs3d(i);
	}
	state.argf = newof(0, int, argc, 0);

	/*
	 * set the command line options
	 * read the command line assignments
	 * mark the command line scripts and targets
	 */

	state.init = 0;
	state.readonly = 1;
	state.argv = argv;
	state.argc = argc;
	if ((args = scanargs(state.argc, state.argv, state.argf)) < 0)
		return 1;
	state.readonly = 0;
	state.init = 1;
	if (state.base)
		state.readstate = 0;
	if (state.compileonly)
	{
		state.forceread = 1;
		state.virtualdot = 0;
	}

	/*
	 * tone down the bootstrap noise
	 */

	if ((trace = error_info.trace) == -1)
		error_info.trace = 0;

	/*
	 * check explicit environment overrides
	 */

	if (s = colonlist(tmp, external.import, 1, ' '))
	{
		tok = tokopen(s, 1);
		while (s = tokread(tok))
		{
			if (i = *s == '!')
				s++;
			if (v = getvar(s))
			{
				if (i)
					v->property &= ~V_import;
				else
					v->property |= V_readonly;
			}
		}
		tokclose(tok);
	}

	/*
	 * set up the traps
	 */

	inittrap();

	/*
	 * announce the version
	 */

	if (error_info.trace < 0)
	{
		errno = 0;
		error(error_info.trace, "%s [%d %s]", version, state.pid, timestr(state.start));
	}

	/*
	 * initialize the views
	 */

	state.global = 0;
	state.user = 1;
	initview();

	/*
	 * check for mam
	 */

	if (state.mam.out)
	{
		if (!state.mam.statix || *state.mam.label)
			error_info.write = mamerror;
		if (state.mam.regress || state.regress)
		{
			sfprintf(state.mam.out, "%sinfo mam %s %05d\n", state.mam.label, state.mam.type, state.mam.parent);
			if (state.mam.regress)
				sfprintf(state.mam.out, "%sinfo start regression\n", state.mam.label);
		}
		else
		{
			sfprintf(state.mam.out, "%sinfo mam %s %05d 1994-07-17 %s\n", state.mam.label, state.mam.type, state.mam.parent, version);
			if (!state.mam.statix || *state.mam.label)
			{
				sfprintf(state.mam.out, "%sinfo start %lu\n", state.mam.label, CURTIME);
				if (!state.mam.root || streq(state.mam.root, internal.pwd))
					sfprintf(state.mam.out, "%sinfo pwd %s\n", state.mam.label, internal.pwd);
				else
					sfprintf(state.mam.out, "%sinfo pwd %s %s\n", state.mam.label, state.mam.root, mamname(makerule(internal.pwd)));
				buf = sfstrbase(tmp);
				if (state.fsview && !mount(NiL, buf, FS3D_GET|FS3D_ALL|FS3D_SIZE(sfstrsize(tmp)), NiL))
					sfprintf(state.mam.out, "%sinfo view %s\n", state.mam.label, buf);
			}
		}
	}

	/*
	 * read the dynamic initialization script
	 */

	if ((i = error_info.trace) > -20)
		error_info.trace = 0;
	sfputr(tmp, initdynamic, -1);
	parse(NiL, sfstruse(tmp), "initdynamic", NiL);
	error_info.trace = i;
	state.user = 0;
	state.init = 0;

	/*
	 * read the explicit makefiles
	 * readfile() handles the base and global rules
	 *
	 * NOTE: internal.tmplist is used to handle the effects of
	 *	 load() on internal list pointers
	 */

	compref(NiL, 0);
	if (p = internal.makefiles->prereqs)
	{
		p = internal.tmplist->prereqs = listcopy(p);
		for (; p; p = p->next)
			readfile(p->rule->name, COMP_FILE, NiL);
		freelist(internal.tmplist->prereqs);
		internal.tmplist->prereqs = 0;
	}

	/*
	 * if no explicit makefiles then try external.{convert,files}
	 */

	if (!state.makefile)
	{
		int	sep;
		Sfio_t*	exp;
		Sfio_t*	imp;

		exp = 0;
		imp = sfstropen();
		sep = 0;
		s = 0;
		if (*(t = getval(external.convert, VAL_PRIMARY)))
		{
			sfputr(tmp, t, 0);
			sfstrrsrv(tmp, MAXNAME);
			tok = tokopen(sfstrbase(tmp), 0);
			while (s = tokread(tok))
			{
				if (!exp)
					exp = sfstropen();
				if (t = colonlist(exp, s, 0, ' '))
				{
					t = tokopen(t, 0);
					while (s = tokread(t))
					{
						if (readfile(s, COMP_INCLUDE|COMP_DONTCARE, NiL))
							break;
						if (sep)
							sfputc(imp, ',');
						else
							sep = 1;
						sfputr(imp, s, -1);
					}
					tokclose(t);
					if (s)
						break;
				}
				if (!(s = tokread(tok)))
					break;
			}
			tokclose(tok);
			sfstrseek(tmp, 0, SEEK_SET);
		}
		if (!s && (s = colonlist(tmp, external.files, 1, ' ')))
		{
			tok = tokopen(s, 1);
			while (s = tokread(tok))
			{
				if (readfile(s, COMP_INCLUDE|COMP_DONTCARE, NiL))
					break;
				if (sep)
					sfputc(imp, ',');
				else
					sep = 1;
				sfputr(imp, s, -1);
			}
			tokclose(tok);
		}
		if (!s)
		{
			/*
			 * this readfile() pulls in the default base rules
			 * that might resolve any delayed self-documenting
			 * options in optcheck()
			 */

			if (readfile("-", COMP_FILE, NiL))
				optcheck(1);
			if (*(s = sfstruse(imp)))
				error(state.errorid ? 1 : 3, "a makefile must be specified when %s omitted", s);
			else
				error(state.errorid ? 1 : 3, "a makefile must be specified");
		}
		sfstrclose(imp);
		if (exp)
			sfstrclose(exp);
	}

	/*
	 * validate external command line options
	 */

	optcheck(1);

	/*
	 * check for listing of variable and rule definitions
	 */

	if (state.list)
	{
		dump(sfstdout, 0);
		return 0;
	}

	/*
	 * check if makefiles to be compiled
	 */

	if (state.compile && !state.virtualdot && state.writeobject)
	{
		/*
		 * make the compinit trap
		 */

		if (r = getrule(external.compinit))
		{
			state.reading = 1;
			maketop(r, P_dontcare|P_foreground, NiL);
			state.reading = 0;
		}
		if (state.exec && state.objectfile)
		{
			message((-2, "compiling makefile input into %s", state.objectfile));
			compile(state.objectfile, NiL);
		}

		/*
		 * make the compdone trap
		 */

		if (r = getrule(external.compdone))
		{
			state.reading = 1;
			maketop(r, P_dontcare|P_foreground, NiL);
			state.reading = 0;
		}
	}

	/*
	 * makefile read cleanup
	 */

	if (state.compileonly)
		return 0;
	compref(NiL, 0);
	sfstrclose(tmp);
	state.compile = COMPILED;
	if (state.believe)
	{
		if (!state.maxview)
			state.believe = 0;
		else if (state.fsview)
			error(3, "%s: option currently works in 2d only", optflag(OPT_believe)->name);
	}

	/*
	 * read the state file
	 */

	readstate();

	/*
	 * place the command line targets in internal.args
	 */

	if (internal.main->dynamic & D_dynamic)
		dynamic(internal.main);
	internal.args->prereqs = p = 0;
	for (i = args; i < state.argc; i++)
		if (state.argf[i] & ARG_TARGET)
		{
			List_t*		q;

			q = cons(makerule(state.argv[i]), NiL);
			if (p)
				p = p->next = q;
			else
				internal.args->prereqs = p = q;
		}

	/*
	 * the engine bootstrap is complete -- start user activities
	 */

	state.user = 1;

	/*
	 * make the makeinit trap
	 */

	if (r = getrule(external.makeinit))
		maketop(r, P_dontcare|P_foreground, NiL);

	/*
	 * read the command line scripts
	 */

	for (i = args; i < state.argc; i++)
		if (state.argf[i] & ARG_SCRIPT)
		{
			state.reading = 1;
			parse(NiL, state.argv[i], "command line script", NiL);
			state.reading = 0;
		}

	/*
	 * freeze the parameter files and candidate state variables
	 */

	state.user = 2;
	candidates();

	/*
	 * make the init trap
	 */

	if (r = getrule(external.init))
		maketop(r, P_dontcare|P_foreground, NiL);

	/*
	 * internal.args default to internal.main
	 */

	if (!internal.args->prereqs && internal.main->prereqs)
		internal.args->prereqs = listcopy(internal.main->prereqs);

	/*
	 * turn up the volume again
	 */

	if (!error_info.trace)
		error_info.trace = trace;

	/*
	 * make the prerequisites of internal.args
	 */

	if (internal.args->prereqs)
		while (internal.args->prereqs)
		{
			/*
			 * we explicitly allow internal.args modifications
			 */

			r = internal.args->prereqs->rule;
			internal.making->prereqs = internal.args->prereqs;
			internal.args->prereqs = internal.args->prereqs->next;
			internal.making->prereqs->next = 0;
			maketop(r, 0, NiL);
		}
	else if (state.makefile)
		error(3, "%s: a main target must be specified", state.makefile);

	/*
	 * finish up
	 */

	finish(0);
	return 0;
}
示例#9
0
static void
execute(register Joblist_t* job)
{
	register List_t*	p;
	char*			s;
	char*			t;
	int			flags;
	Rule_t*			r;
	Var_t*			v;
	Sfio_t*			tmp;
	Sfio_t*			att;
	Sfio_t*			sp;

	att = sfstropen();
	tmp = sfstropen();
	restore(job, tmp, att);
	job->status = RUNNING;
	job->target->mark &= ~M_waiting;
	if (state.targetcontext || state.maxview && !state.fsview && *job->target->name != '/' && (!(job->target->dynamic & D_regular) || job->target->view))
		commit(job, job->target->name);
	if ((state.mam.dynamic || state.mam.regress) && state.user && !(job->target->property & (P_after|P_before|P_dontcare|P_make|P_state|P_virtual)))
		sfprintf(state.mam.out, "%sinit %s %s\n", state.mam.label, mamname(job->target), timefmt(NiL, CURTIME));
	t = sfstruse(tmp);
	if (!(job->flags & CO_ALWAYS))
	{
		if (state.touch)
		{
			if (state.virtualdot)
			{
				state.virtualdot = 0;
				lockstate(1);
			}
			if (!(job->target->property & (P_attribute|P_virtual)))
			{
				acceptrule(job->target);
				if ((job->target->property & (P_joint|P_target)) == (P_joint|P_target))
					for (p = job->target->prereqs->rule->prereqs; p; p = p->next)
						if (p->rule != job->target)
							acceptrule(p->rule);
			}
		}
		else if (*t && (!state.silent || state.mam.regress))
			dumpaction(state.mam.out ? state.mam.out : sfstdout, NiL, t, NiL);
		done(job, 0, NiL);
	}
	else
	{
		if (state.virtualdot && !notfile(job->target))
		{
			state.virtualdot = 0;
			lockstate(1);
		}
		if (!state.coshell)
		{
			sp = sfstropen();
			sfprintf(sp, "label=%s", idname);
			expand(sp, " $(" CO_ENV_OPTIONS ")");
			flags = CO_ANY;
			if (state.cross)
				flags |= CO_CROSS;
			if (state.serialize && state.jobs > 1)
				flags |= CO_SERIALIZE;
			if (!(state.coshell = coopen(getval(CO_ENV_SHELL, VAL_PRIMARY), flags, sfstruse(sp))))
				error(ERROR_SYSTEM|3, "coshell open error");
			sfstrclose(sp);
		}
		if (p = internal.exports->prereqs)
		{
			Sfio_t*	exp;

			exp = sfstropen();
			do
			{
				if (v = getvar(p->rule->name))
				{
					expand(exp, v->value);
					coexport(state.coshell, p->rule->name, sfstruse(exp));
				}
				else if (s = strchr(p->rule->name, '='))
				{
					*s = 0;
					expand(exp, s + 1);
					coexport(state.coshell, p->rule->name, sfstruse(exp));
					*s = '=';
				}
			} while (p = p->next);
			sfstrclose(exp);
#if 0
			freelist(internal.exports->prereqs);
#endif
			internal.exports->prereqs = 0;
		}
		if (job->flags & CO_DATAFILE)
		{
			static char*	dot;
			static char*	tmp;

			if (job->target->property & P_read)
			{
				if (!dot)
					dot = pathtemp(NiL, 0, null, idname, NiL);
				state.tmpfile = dot;
			}
			else
			{
				if (!tmp)
					tmp = pathtemp(NiL, 0, NiL, idname, NiL);
				state.tmpfile = tmp;
			}
		}
#if !_HUH_1992_02_29 /* i386 and ftx m68k dump without this statement -- help */
		message((-99, "execute: %s: t=0x%08x &t=0x%08x", job->target->name, t, &t));
#endif
		if (state.mam.out)
			dumpaction(state.mam.out, MAMNAME(job->target), t, NiL);
		if (r = getrule(external.makerun))
			maketop(r, P_dontcare|P_foreground, NiL);
		if (!(job->cojob = coexec(state.coshell, t, job->flags, state.tmpfile, NiL, sfstruse(att))))
			error(3, "%s: cannot send action to coshell", job->target->name);
		job->cojob->local = (void*)job;

		/*
		 * grab semaphores
		 */

		if (job->target->dynamic & D_hassemaphore)
		{
			job->flags |= CO_SEMAPHORES;
			for (p = job->prereqs; p; p = p->next)
				if (p->rule->semaphore && --p->rule->semaphore == 1)
					p->rule->status = MAKING;
		}

		/*
		 * check status and sync
		 */

		if (job->target->dynamic & D_hasafter)
			save(job);
		if (job->flags & (CO_DATAFILE|CO_FOREGROUND))
		{
			complete(job->target, NiL, NiL, 0);
			if (job->target->property & (P_functional|P_read))
			{
				if (sp = sfopen(NiL, state.tmpfile, "r"))
				{
					remove(state.tmpfile);
					if (job->target->property & P_read)
						parse(sp, NiL, job->target->name, NiL);
					else
					{
						char*	e;

						sfmove(sp, tmp, SF_UNBOUND, -1);
						t = sfstrbase(tmp);
						e = sfstrseek(tmp, 0, SEEK_CUR);
						while (e > t && *(e - 1) == '\n')
							e--;
						sfstrseek(tmp, e - t, SEEK_SET);
						setvar(job->target->name, sfstruse(tmp), 0);
					}
					sfclose(sp);
				}
				else
					error(2, "%s: cannot read temporary data output file %s", job->target->name, state.tmpfile);
				state.tmpfile = 0;
			}
		}
	}
	sfstrclose(att);
	sfstrclose(tmp);
}
示例#10
0
static void
restore(register Joblist_t* job, Sfio_t* buf, Sfio_t* att)
{
	register char*	s;
	register char*	b;
	char*		u;
	char*		down;
	char*		back;
	char*		sep;
	int		downlen;
	int		localview;
	void*		pos;
	Var_t*		v;
	Sfio_t*		opt;
	Sfio_t*		tmp;
	Sfio_t*		context;

	push(job);
	localview = state.localview;
	state.localview = state.mam.statix && !state.expandview && state.user && !(job->flags & CO_ALWAYS);
	if ((job->flags & CO_LOCALSTACK) || (job->target->dynamic & D_hasscope))
	{
		register Rule_t*	r;
		register List_t*	p;

		job->flags |= CO_LOCALSTACK;
		pos = pushlocal();
		opt = sfstropen();
		if (job->target->dynamic & D_hasscope)
			for (p = job->prereqs; p; p = p->next)
				if ((r = p->rule)->dynamic & D_scope)
				{
					if (*r->name == '-')
						set(r->name, 1, opt);
					else
						parse(NiL, r->name, r->name, opt);
				}
				else if ((r->property & (P_make|P_local|P_use)) == (P_make|P_local) && r->action)
					parse(NiL, r->action, r->name, opt);
	}
	context = state.context;
	if (state.targetcontext && *(u = unbound(job->target)) != '/' && (s = strrchr(u, '/')))
	{
		size_t	n;
		int	c;

		tmp = sfstropen();
		downlen = s - u;
		*s = 0;
		sfprintf(tmp, "%s%c", u, 0);
		n = sfstrtell(tmp);
		c = '/';
		do
		{
			if (u = strchr(u, '/'))
				u++;
			else
				c = 0;
			sfputr(tmp, "..", c);
		} while (c);
		*s = '/';
		back = (down = sfstrbase(tmp)) + n;
		state.context = buf;
		buf = sfstropen();
		state.localview++;
	}
	else
		state.context = 0;
	if (job->action)
		expand(buf, job->action);
	if (state.context)
	{
		s = sfstruse(buf);
		sep = strchr(s, '\n') ? "\n" : "; ";
		sfprintf(state.context, "{ cd %s%s", down, sep);
		while (b = strchr(s, MARK_CONTEXT))
		{
			sfwrite(state.context, s, b - s);
			if (!(s = strchr(++b, MARK_CONTEXT)))
				error(PANIC, "unbalanced MARK_CONTEXT");
			*s++ = 0;
			if (*b == '/' || (u = getbound(b)) && *u == '/')
				sfputr(state.context, b, -1);
			else if (*b)
			{
				if (strneq(b, down, downlen))
					switch (*(b + downlen))
					{
					case 0:
						sfputc(state.context, '.');
						continue;
					case '/':
						sfputr(state.context, b + downlen + 1, -1);
						continue;
					}
				if (streq(b, "."))
					sfputr(state.context, back, -1);
				else if (isspace(*b))
					sfputr(state.context, b, -1);
				else
					sfprintf(state.context, "%s/%s", back, b);
			}
		}
		sfprintf(state.context, "%s%s}", s, sep);
		sfstrclose(tmp);
		sfstrclose(buf);
	}
	state.context = context;
	sfprintf(att, "label=%s", job->target->name);
	if ((v = getvar(CO_ENV_ATTRIBUTES)) && !(v->property & V_import))
		sfprintf(att, ",%s", v->value);
	if (job->flags & CO_LOCALSTACK)
	{
		poplocal(pos);
		if (*(s = sfstruse(opt)))
			set(s, 1, NiL);
		sfclose(opt);
	}
	state.localview = localview;
	pop(job);
}
示例#11
0
void
trigger(register Rule_t* r, Rule_t* a, char* action, Flags_t flags)
{
	register Joblist_t*	job;
	register List_t*	p;
	List_t*			prereqs;
	int			n;

	/*
	 * update flags
	 */

	if (!a)
		a = r;
	if (state.exec && !state.touch || (a->property & P_always) && (!state.never || (flags & CO_URGENT)))
		flags |= CO_ALWAYS;
	if ((a->property | r->property) & P_local)
		flags |= CO_LOCAL;
	if (!state.jobs || (r->property & P_foreground) || (r->property & (P_make|P_functional)) == P_functional || (r->dynamic & D_hasmake))
		flags |= CO_FOREGROUND|CO_LOCAL;
	if (state.keepgoing || state.unwind)
		flags |= CO_KEEPGOING;
	if (state.silent)
		flags |= CO_SILENT;
	if (state.ignore)
		flags |= CO_IGNORE;
	if (r->property & (P_functional|P_read))
		flags |= CO_DATAFILE;
	if (action)
	{
		message((-1, "triggering %s action%s%s", r->name, r == a ? null : " using ", r == a ? null : a->name));
		if (state.exec)
			jobs.triggered = r;
		r->dynamic |= D_triggered;
		if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
			for (p = r->prereqs->rule->prereqs; p; p = p->next)
				p->rule->dynamic |= D_triggered;
		if (!*action)
			action = 0;
	}
	if (state.coshell && (action && !(r->property & P_make) || (flags & CO_FOREGROUND)))
	{
		/*
		 * the make thread blocks when too many jobs are outstanding
		 */

		n = (flags & CO_FOREGROUND) ? 0 : (state.jobs - 1);
		while ((cozombie(state.coshell) || cojobs(state.coshell) > n) && block(0));
		if ((flags & CO_FOREGROUND) && r->active && r->active->parent && r->active->parent->prereqs && copending(state.coshell) > cojobs(state.coshell))
			serial(r, r->active->parent->prereqs);
	}
	prereqs = r->prereqs;
	if (r->active && r->active->primary)
	{
		prereqs = cons(getrule(r->active->primary), prereqs);
		flags |= CO_PRIMARY;
	}
	if (r->property & P_make)
	{
		if (r->property & P_local)
		{
			r->status = EXISTS;
			return;
		}

		/*
		 * make actions are done immediately, bypassing the job queue
		 */

		if (prereqs && complete(NiL, prereqs, NiL, 0))
			r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
		else
		{
			if (action && cancel(r, prereqs))
				r->status = EXISTS;
			else if ((r->dynamic & (D_hasbefore|D_triggered)) == (D_hasbefore|D_triggered) && (makebefore(r) || complete(NiL, prereqs, NiL, 0)))
				r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
			else
			{
				if (r->property & P_functional)
					setvar(r->name, null, 0);
				if (action)
					switch (parse(NiL, action, r->name, NiL))
					{
					case EXISTS:
						if (!(r->property & (P_state|P_virtual)))
							statetime(r, 0);
						break;
					case FAILED:
						r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
						break;
					case TOUCH:
						r->time = internal.internal->time;
						break;
					case UPDATE:
						if ((r->property & (P_state|P_virtual)) != (P_state|P_virtual))
							r->time = CURTIME;
						break;
					}
				if (r->status == UPDATE)
					r->status = EXISTS;
			}
		}
		if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
			for (p = r->prereqs->rule->prereqs; p; p = p->next)
				if (p->rule != r)
				{
					p->rule->status = r->status;
					p->rule->time = r->time;
				}
		if ((r->dynamic & (D_hasafter|D_triggered)) == (D_hasafter|D_triggered))
		{
			if (r->status == FAILED)
			{
				if (hasafter(r, P_failure) && !makeafter(r, P_failure) && !complete(NiL, prereqs, NiL, 0))
					r->status = EXISTS;
			}
			else if (hasafter(r, P_after) && (makeafter(r, P_after) || complete(NiL, prereqs, NiL, 0)))
				r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
		}
	}
	else
	{
		/*
		 * only one repeat action at a time
		 */

		if ((r->property & P_repeat) && (r->property & (P_before|P_after)) && !(r->dynamic & D_hassemaphore))
		{
			a = catrule(internal.semaphore->name, ".", r->name, 1);
			a->semaphore = 2;
			r->prereqs = append(r->prereqs, cons(a, NiL));
			r->dynamic |= D_hassemaphore;
		}

		/*
		 * check if any prerequisites are blocking execution
		 * FAILED prerequisites cause the target to fail too
		 */

		n = READY;
		for (;;)
		{
			for (p = prereqs; p; p = p->next)
			{
				if ((a = p->rule)->dynamic & D_alias)
					a = makerule(a->name);
				if (a->property & P_after)
					continue;
				switch (a->status)
				{
				case FAILED:
					if (a->property & P_repeat)
						continue;
					r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
					if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
						for (p = r->prereqs->rule->prereqs; p; p = p->next)
							if (p->rule != r)
								p->rule->status = (p->rule->property & P_dontcare) ? IGNORE : FAILED;
					return;
				case MAKING:
					if (a->active)
						error(1, "%s: prerequisite %s is active", r->name, a->name);
					else
						n = BLOCKED;
					break;
				}
			}
			if (n != READY)
				break;
			if (action)
			{
				if (cancel(r, prereqs))
					return;
				if ((r->dynamic & D_intermediate) && r->must == 1)
				{
					n = INTERMEDIATE;
					jobs.intermediate++;
					break;
				}
			}
			if ((r->dynamic & (D_hasbefore|D_triggered)) != (D_hasbefore|D_triggered))
				break;
			if (makebefore(r))
			{
				r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
				if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
					for (p = r->prereqs->rule->prereqs; p; p = p->next)
						if (p->rule != r)
							p->rule->status = (p->rule->property & P_dontcare) ? IGNORE : FAILED;
				return;
			}
		}
		if (action || n != READY)
		{
			/*
			 * allocate a job cell and add to job list
			 * the first READY job from the top is executed next
			 */

			if (job = jobs.freejob)
				jobs.freejob = jobs.freejob->next;
			else
				job = newof(0, Joblist_t, 1, 0);
			if (flags & CO_URGENT)
			{
				job->prev = 0;
				if (job->next = jobs.firstjob)
					jobs.firstjob->prev = job;
				else
					jobs.lastjob = job;
				jobs.firstjob = job;
			}
			else
			{
				job->next = 0;
				if (job->prev = jobs.lastjob)
					jobs.lastjob->next = job;
				else
					jobs.firstjob = job;
				jobs.lastjob = job;
			}

			/*
			 * fill in the info
			 */

			job->target = r;
			job->prereqs = prereqs;
			job->status = n;
			job->flags = flags;
			job->action = action;
			r->status = MAKING;
			if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
				for (p = r->prereqs->rule->prereqs; p; p = p->next)
					if (p->rule != r)
						p->rule->status = r->status;
			if (n == READY)
			{
				execute(job);
				if (r->dynamic & D_hasafter)
					save(job);
			}
			else
				save(job);
			jobstatus();
		}
		else
		{
			if (r->status == UPDATE)
				r->status = EXISTS;
			if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
				for (p = r->prereqs->rule->prereqs; p; p = p->next)
					if (p->rule->status == UPDATE)
						p->rule->status = EXISTS;
			if ((r->dynamic & (D_hasafter|D_triggered)) == (D_hasafter|D_triggered))
			{
				if (r->status == FAILED)
				{
					if (hasafter(r, P_failure) && !makeafter(r, P_failure) && !complete(NiL, prereqs, NiL, 0))
						r->status = EXISTS;
				}
				else if (hasafter(r, P_after) && (makeafter(r, P_after) || complete(NiL, prereqs, NiL, 0)))
					r->status = (r->property & P_dontcare) ? IGNORE : FAILED;
				if (r->status == EXISTS)
				{
					char*	t;
					Sfio_t*	tmp;

					tmp = sfstropen();
					edit(tmp, r->name, KEEP, DELETE, DELETE);
					if (*(t = sfstruse(tmp)))
						newfile(r, t, r->time);
					sfstrclose(tmp);
				}
			}
		}
		if (r->dynamic & D_triggered)
		{
			r->time = CURTIME;
			if ((r->property & (P_joint|P_target)) == (P_joint|P_target))
				for (p = r->prereqs->rule->prereqs; p; p = p->next)
					p->rule->time = r->time;
		}
	}
}
示例#12
0
static int
golly(int argc, char* const* argv, const char* optstring, const struct option* longopts, int* longindex, int flags)
{
	register char*			s;
	register const struct option*	o;
	register int			c;
	char*				t;

	if (!up || optstring != lastoptstring || longopts != lastlongopts)
	{
		if (!up && !(up = sfstropen()) || !(t = strdup(optstring)))
			return -1;
		sfprintf(up, "[-1p%d]", flags);
		for (o = longopts; o->name; o++)
		{
			if (o->flag || o->val <= 0 || o->val > UCHAR_MAX || !isalnum(o->val))
				sfprintf(up, "\n[%d:%s]", UCHAR_MAX + 1 + (o - longopts), o->name);
			else
			{
				sfprintf(up, "\n[%c:%s]", o->val, o->name);
				if (s = strchr(t, o->val))
				{
					*s++ = ' ';
					if (*s == ':')
					{
						*s++ = ' ';
						if (*s == ':')
							*s = ' ';
					}
				}
			}
			if (o->has_arg)
			{
				sfputc(up, ':');
				if (o->has_arg == optional_argument)
					sfputc(up, '?');
				sfprintf(up, "[string]");
			}
		}
		s = t;
		while (c = *s++)
			if (c != ' ')
			{
				sfprintf(up, "\n[%c]", c);
				if (*s == ':')
				{
					sfputc(up, *s);
					if (*++s == ':')
					{
						sfputc(up, '?');
						s++;
					}
					sfputc(up, '[');
					sfputc(up, ']');
				}
			}
		sfputc(up, '\n');
		free(t);
		if (!(usage = sfstruse(up)))
			return -1;
		lastoptstring = optstring;
		lastlongopts = longopts;
	}
	opt_info.index = (optind > 1 || optind == lastoptind) ? optind : 0;
	if (opt_info.index >= argc || !(c = optget((char**)argv, usage)))
	{
		sfstrclose(up);
		up = 0;
		c = -1;
	}
	else
	{
		if (c == ':' || c == '?')
		{
			if (opterr && (!optstring || *optstring != ':'))
			{
				if (!error_info.id)
					error_info.id = argv[0];
				errormsg(NiL, c == '?' ? (ERROR_USAGE|4) : 2, "%s", opt_info.arg);
			}
			optopt = opt_info.option[1];
			c = '?';
		}
		optarg = opt_info.arg;
		if (c < 0)
		{
			o = longopts - c - UCHAR_MAX - 1;
			if (o->flag)
			{
				*o->flag = o->val;
				c = 0;
			}
			else
				c = o->val;
		}
	}
	lastoptind = optind = opt_info.index;
	return c;
}