diff options
Diffstat (limited to 'src/lib/libast/misc')
48 files changed, 21096 insertions, 0 deletions
diff --git a/src/lib/libast/misc/astintercept.c b/src/lib/libast/misc/astintercept.c new file mode 100644 index 0000000..c4e84cb --- /dev/null +++ b/src/lib/libast/misc/astintercept.c @@ -0,0 +1,53 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +#include "intercepts.h" + +/* + * NOTE: the "intercepts" definition is in getenv.c because some static linkers + * miss lone references to "intercepts" without "astintercept()" + */ + +/* + * set/clear ast intercept callouts + */ + +int +astintercept(Shbltin_t* call, int set) +{ + if (call->shgetenv) + { + if (set) + intercepts.intercept_getenv = call->shgetenv; + else + intercepts.intercept_getenv = 0; + } + if (call->shsetenv) + { + if (set) + intercepts.intercept_setenviron = call->shsetenv; + else + intercepts.intercept_setenviron = 0; + } + return 0; +} diff --git a/src/lib/libast/misc/cmdarg.c b/src/lib/libast/misc/cmdarg.c new file mode 100644 index 0000000..38d247b --- /dev/null +++ b/src/lib/libast/misc/cmdarg.c @@ -0,0 +1,382 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2012 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * xargs/tw command arg list support + */ + +#define _AST_API_H 1 + +#include <ast.h> +#include <cmdarg.h> + +Cmdarg_t* +cmdopen(char** argv, int argmax, int size, const char* argpat, int flags) +{ + Error_f ef; + + if (flags & CMD_SILENT) + ef = 0; + else + { + ef = errorf; + flags |= CMD_EXIT; + } + return cmdopen_20110505(argv, argmax, size, argpat, flags, ef); +} + +#undef _AST_API_H + +#include <ast_api.h> + +#include <ctype.h> +#include <proc.h> + +#ifndef ARG_MAX +#define ARG_MAX (64*1024) +#endif +#ifndef EXIT_QUIT +#define EXIT_QUIT 255 +#endif + +static const char* echo[] = { "echo", 0 }; + +/* + * open a cmdarg stream + * initialize the command for execution + * argv[-1] is reserved for procrun(PROC_ARGMOD) + */ + +Cmdarg_t* +cmdopen_20110505(char** argv, int argmax, int size, const char* argpat, int flags, Error_f errorf) +{ + register Cmdarg_t* cmd; + register int n; + register char** p; + register char* s; + char* sh; + char* exe; + int c; + int m; + int argc; + long x; + + char** post = 0; + + n = sizeof(char**); + if (*argv) + { + for (p = argv + 1; *p; p++) + { + if ((flags & CMD_POST) && argpat && streq(*p, argpat)) + { + *p = 0; + post = p + 1; + argpat = 0; + } + else + n += strlen(*p) + 1; + } + argc = p - argv; + } + else + argc = 0; + for (p = environ; *p; p++) + n += sizeof(char**) + strlen(*p) + 1; + if ((x = strtol(astconf("ARG_MAX", NiL, NiL), NiL, 0)) <= 0) + x = ARG_MAX; + if (size <= 0 || size > x) + size = x; + sh = pathshell(); + m = n + (argc + 4) * sizeof(char**) + strlen(sh) + 1; + m = roundof(m, sizeof(char**)); + if (size < m) + { + if (errorf) + (*errorf)(NiL, sh, 2, "size must be at least %d", m); + return 0; + } + if ((m = x / 10) > 2048) + m = 2048; + if (size > (x - m)) + size = x - m; + n = size - n; + m = ((flags & CMD_INSERT) && argpat) ? (strlen(argpat) + 1) : 0; + if (!(cmd = newof(0, Cmdarg_t, 1, n + m))) + { + if (errorf) + (*errorf)(NiL, sh, ERROR_SYSTEM|2, "out of space"); + return 0; + } + cmd->errorf = errorf; + c = n / sizeof(char**); + if (argmax <= 0 || argmax > c) + argmax = c; + s = cmd->buf; + if (!(exe = argv[0])) + { + exe = *(argv = (char**)echo); + cmd->echo = 1; + } + else if (streq(exe, echo[0])) + { + cmd->echo = 1; + flags &= ~CMD_NEWLINE; + } + else if (!(flags & CMD_CHECKED)) + { + if (!pathpath(exe, NiL, PATH_REGULAR|PATH_EXECUTE, s, n + m)) + { + n = EXIT_NOTFOUND; + if (errorf) + (*cmd->errorf)(NiL, cmd, ERROR_SYSTEM|2, "%s: command not found", exe); + if (flags & CMD_EXIT) + (*error_info.exit)(n); + free(cmd); + return 0; + } + exe = s; + } + s += strlen(s) + 1; + if (m) + { + cmd->insert = strcpy(s, argpat); + cmd->insertlen = m - 1; + s += m; + } + s += sizeof(char**) - (s - cmd->buf) % sizeof(char**); + p = (char**)s; + n -= strlen(*p++ = sh) + 1; + cmd->argv = p; + *p++ = exe; + while (*p = *++argv) + p++; + if (m) + { + argmax = 1; + *p++ = 0; + cmd->insertarg = p; + argv = cmd->argv; + c = *cmd->insert; + while (s = *argv) + { + while ((s = strchr(s, c)) && strncmp(cmd->insert, s, cmd->insertlen)) + s++; + *p++ = s ? *argv : (char*)0; + argv++; + } + *p++ = 0; + } + cmd->firstarg = cmd->nextarg = p; + cmd->laststr = cmd->nextstr = cmd->buf + n; + cmd->argmax = argmax; + cmd->flags = flags; + cmd->offset = ((cmd->postarg = post) ? (argc - (post - argv)) : 0) + 3; + return cmd; +} + +/* + * flush outstanding command file args + */ + +int +cmdflush(register Cmdarg_t* cmd) +{ + register char* s; + register char** p; + register int n; + + if (cmd->flags & CMD_EMPTY) + cmd->flags &= ~CMD_EMPTY; + else if (cmd->nextarg <= cmd->firstarg) + return 0; + if ((cmd->flags & CMD_MINIMUM) && cmd->argcount < cmd->argmax) + { + if (cmd->errorf) + (*cmd->errorf)(NiL, cmd, 2, "%d arg command would be too long", cmd->argcount); + return -1; + } + cmd->total.args += cmd->argcount; + cmd->total.commands++; + cmd->argcount = 0; + if (p = cmd->postarg) + while (*cmd->nextarg++ = *p++); + else + *cmd->nextarg = 0; + if (s = cmd->insert) + { + char* a; + char* b; + char* e; + char* t; + char* u; + int c; + int m; + + a = cmd->firstarg[0]; + b = (char*)&cmd->nextarg[1]; + e = cmd->nextstr; + c = *s; + m = cmd->insertlen; + for (n = 1; cmd->argv[n]; n++) + if (t = cmd->insertarg[n]) + { + cmd->argv[n] = b; + for (;;) + { + if (!(u = strchr(t, c))) + { + b += sfsprintf(b, e - b, "%s", t); + break; + } + if (!strncmp(s, u, m)) + { + b += sfsprintf(b, e - b, "%-.*s%s", u - t, t, a); + t = u + m; + } + else if (b >= e) + break; + else + { + *b++ = *u++; + t = u; + } + } + if (b < e) + *b++ = 0; + } + if (b >= e) + { + if (cmd->errorf) + (*cmd->errorf)(NiL, cmd, 2, "%s: command too large after insert", a); + return -1; + } + } + cmd->nextarg = cmd->firstarg; + cmd->nextstr = cmd->laststr; + if (cmd->flags & (CMD_QUERY|CMD_TRACE)) + { + p = cmd->argv; + sfprintf(sfstderr, "+ %s", *p); + while (s = *++p) + sfprintf(sfstderr, " %s", s); + if (!(cmd->flags & CMD_QUERY)) + sfprintf(sfstderr, "\n"); + else if (astquery(1, "? ")) + return 0; + } + if (cmd->echo) + { + n = (cmd->flags & CMD_NEWLINE) ? '\n' : ' '; + for (p = cmd->argv + 1; s = *p++;) + sfputr(sfstdout, s, *p ? n : '\n'); + n = 0; + } + else if ((n = procrun(*cmd->argv, cmd->argv, PROC_ARGMOD|PROC_IGNOREPATH)) == -1) + { + n = EXIT_NOTFOUND - 1; + if (cmd->errorf) + (*cmd->errorf)(NiL, cmd, ERROR_SYSTEM|2, "%s: command exec error", *cmd->argv); + if (cmd->flags & CMD_EXIT) + (*error_info.exit)(n); + return n; + } + else if (n >= EXIT_NOTFOUND - 1) + { + if (cmd->flags & CMD_EXIT) + (*error_info.exit)(n); + } + else if (!(cmd->flags & CMD_IGNORE)) + { + if (n == EXIT_QUIT && (cmd->flags & CMD_EXIT)) + (*error_info.exit)(2); + if (n) + error_info.errors++; + } + return n; +} + +/* + * add file to the command arg list + */ + +int +cmdarg(register Cmdarg_t* cmd, const char* file, register int len) +{ + int i; + int r; + + r = 0; + if (len) + { + while ((cmd->nextstr -= len + 1) < (char*)(cmd->nextarg + cmd->offset)) + { + if (cmd->nextarg == cmd->firstarg) + { + if (cmd->errorf) + (*cmd->errorf)(NiL, cmd, 2, "%s: path too long for exec args", file); + return -1; + } + if (i = cmdflush(cmd)) + { + if (r < i) + r = i; + if (!(cmd->flags & CMD_IGNORE)) + return r; + } + } + *cmd->nextarg++ = cmd->nextstr; + memcpy(cmd->nextstr, file, len); + cmd->nextstr[len] = 0; + cmd->argcount++; + if (cmd->argcount >= cmd->argmax && (i = cmdflush(cmd)) > r) + r = i; + } + return r; +} + +/* + * close a cmdarg stream + */ + +int +cmdclose(Cmdarg_t* cmd) +{ + int n; + + if ((cmd->flags & CMD_EXACT) && cmd->argcount < cmd->argmax) + { + if (cmd->errorf) + (*cmd->errorf)(NiL, cmd, 2, "only %d arguments for last command", cmd->argcount); + n = -1; + } + else + { + cmd->flags &= ~CMD_MINIMUM; + n = cmdflush(cmd); + } + free(cmd); + return n; +} diff --git a/src/lib/libast/misc/conformance.c b/src/lib/libast/misc/conformance.c new file mode 100644 index 0000000..12ee2ca --- /dev/null +++ b/src/lib/libast/misc/conformance.c @@ -0,0 +1,151 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +#include <ast.h> +#include <ctype.h> + +static char** ids; + +static const char* dflt[] = { "ast", "standard", 0 }; + +/* + * initialize the conformance() id list + */ + +static char** +initconformance(void) +{ + char* m; + char** p; + char* t; + int h; + int i; + int j; + int c; + Sfio_t* sp; + + static const char* conf[] = { "CONFORMANCE", "HOSTTYPE", "UNIVERSE" }; + + p = 0; + if (sp = sfstropen()) + { + for (i = h = 0, j = 1; i < elementsof(conf); i++) + if (*(m = astconf(conf[i], NiL, NiL)) && (h |= (1<<i)) || !i && (m = "ast")) + { + t = m; + while ((c = *m++) && c != '.') + { + if (isupper(c)) + c = tolower(c); + sfputc(sp, c); + } + sfputc(sp, 0); + j++; + if ((c = (m - t)) == 6 && strneq(t, "linux", 5)) + { + sfputr(sp, "gnu", 0); + j++; + } + else if (c > 3 && strneq(t, "bsd", 3) || c == 7 && strneq(t, "debian", 7)) + { + sfputr(sp, "bsd", 0); + j++; + } + if (h & 1) + break; + } + i = sfstrtell(sp); + sfstrseek(sp, 0, SEEK_SET); + if (p = newof(0, char*, j, i)) + { + m = (char*)(p + j--); + memcpy(m, sfstrbase(sp), i); + i = 0; + p[i++] = m; + while (i < j) + { + while (*m++); + p[i++] = m; + } + p[i] = 0; + } + sfstrclose(sp); + } + if (!p) + p = (char**)dflt; + return ids = p; +} + +/* + * return conformance id if s size n is in conformance + * prefix match of s on the conformance id table + * s==0 => "standard" + */ + +char* +conformance(const char* s, size_t n) +{ + char** p; + char** q; + char* m; + const char* e; + const char* t; + + static uint32_t serial = ~(uint32_t)0; + + if (!(p = ids) || serial != ast.env_serial) + { + serial = ast.env_serial; + if (ids) + { + if (ids != (char**)dflt) + free(ids); + ids = 0; + } + p = initconformance(); + } + if (!s) + s = dflt[1]; + if (!n) + n = strlen(s); + e = s + n; + if (*s == '(') + s++; + do + { + while (s < e && (isspace(*s) || *s == ',' || *s == '|')) + s++; + if (*s == ')') + break; + for (t = s; s < e && !isspace(*s) && *s != ',' && *s != '|' && *s != ')'; s++); + if (s == t) + break; + q = p; + while (m = *q++) + if (strneq(t, m, s - t)) + return m; + if (s < e) + s++; + } while (s < e); + return 0; +} diff --git a/src/lib/libast/misc/debug.c b/src/lib/libast/misc/debug.c new file mode 100644 index 0000000..791973c --- /dev/null +++ b/src/lib/libast/misc/debug.c @@ -0,0 +1,66 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * <debug.h> support + */ + +#include <ast.h> +#include <error.h> +#include <debug.h> + +void +debug_fatal(const char* file, int line) +{ + error(2, "%s:%d: debug error", file, line); + abort(); +} + +#if _sys_times + +#include <times.h> +#include <sys/resource.h> + +double +debug_elapsed(int set) +{ + double tm; + struct rusage ru; + + static double prev; + + getrusage(RUSAGE_SELF, &ru); + tm = (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec/1000000.0; + if (set) + return prev = tm; + return tm - prev; +} + +#else + +double +debug_elapsed(int set) +{ + return 0; +} + +#endif diff --git a/src/lib/libast/misc/error.c b/src/lib/libast/misc/error.c new file mode 100644 index 0000000..8ac8c60 --- /dev/null +++ b/src/lib/libast/misc/error.c @@ -0,0 +1,659 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * error and message formatter + * + * level is the error level + * level >= error_info.core!=0 dumps core + * level >= ERROR_FATAL calls error_info.exit + * level < 0 is for debug tracing + * + * NOTE: id && ERROR_NOID && !ERROR_USAGE implies format=id for errmsg() + */ + +#include "lclib.h" + +#include <ctype.h> +#include <ccode.h> +#include <namval.h> +#include <sig.h> +#include <stk.h> +#include <times.h> +#include <regex.h> + +/* + * 2007-03-19 move error_info from _error_info_ to (*_error_infop_) + * to allow future Error_info_t growth + * by 2009 _error_info_ can be static + */ + +#if _BLD_ast && defined(__EXPORT__) +#define extern extern __EXPORT__ +#endif + +extern Error_info_t _error_info_; + +Error_info_t _error_info_ = +{ + 2, exit, write, + 0,0,0,0,0,0,0,0, + 0, /* version */ + 0, /* auxilliary */ + 0,0,0,0,0,0,0, /* top of old context stack */ + 0,0,0,0,0,0,0, /* old empty context */ + 0, /* time */ + translate, + 0 /* catalog */ +}; + +#undef extern + +__EXTERN__(Error_info_t, _error_info_); + +__EXTERN__(Error_info_t*, _error_infop_); + +Error_info_t* _error_infop_ = &_error_info_; + +/* + * these should probably be in error_info + */ + +static struct State_s +{ + char* prefix; + Sfio_t* tty; + unsigned long count; + int breakpoint; + regex_t* match; +} error_state; + +#undef ERROR_CATALOG +#define ERROR_CATALOG (ERROR_LIBRARY<<1) + +#define OPT_BREAK 1 +#define OPT_CATALOG 2 +#define OPT_CORE 3 +#define OPT_COUNT 4 +#define OPT_FD 5 +#define OPT_LIBRARY 6 +#define OPT_MASK 7 +#define OPT_MATCH 8 +#define OPT_PREFIX 9 +#define OPT_SYSTEM 10 +#define OPT_TIME 11 +#define OPT_TRACE 12 + +static const Namval_t options[] = +{ + "break", OPT_BREAK, + "catalog", OPT_CATALOG, + "core", OPT_CORE, + "count", OPT_COUNT, + "debug", OPT_TRACE, + "fd", OPT_FD, + "library", OPT_LIBRARY, + "mask", OPT_MASK, + "match", OPT_MATCH, + "prefix", OPT_PREFIX, + "system", OPT_SYSTEM, + "time", OPT_TIME, + "trace", OPT_TRACE, + 0, 0 +}; + +/* + * called by stropt() to set options + */ + +static int +setopt(void* a, const void* p, register int n, register const char* v) +{ + NoP(a); + if (p) + switch (((Namval_t*)p)->value) + { + case OPT_BREAK: + case OPT_CORE: + if (n) + switch (*v) + { + case 'e': + case 'E': + error_state.breakpoint = ERROR_ERROR; + break; + case 'f': + case 'F': + error_state.breakpoint = ERROR_FATAL; + break; + case 'p': + case 'P': + error_state.breakpoint = ERROR_PANIC; + break; + default: + error_state.breakpoint = strtol(v, NiL, 0); + break; + } + else + error_state.breakpoint = 0; + if (((Namval_t*)p)->value == OPT_CORE) + error_info.core = error_state.breakpoint; + break; + case OPT_CATALOG: + if (n) + error_info.set |= ERROR_CATALOG; + else + error_info.clear |= ERROR_CATALOG; + break; + case OPT_COUNT: + if (n) + error_state.count = strtol(v, NiL, 0); + else + error_state.count = 0; + break; + case OPT_FD: + error_info.fd = n ? strtol(v, NiL, 0) : -1; + break; + case OPT_LIBRARY: + if (n) + error_info.set |= ERROR_LIBRARY; + else + error_info.clear |= ERROR_LIBRARY; + break; + case OPT_MASK: + if (n) + error_info.mask = strtol(v, NiL, 0); + else + error_info.mask = 0; + break; + case OPT_MATCH: + if (error_state.match) + regfree(error_state.match); + if (n) + { + if ((error_state.match || (error_state.match = newof(0, regex_t, 1, 0))) && regcomp(error_state.match, v, REG_EXTENDED|REG_LENIENT)) + { + free(error_state.match); + error_state.match = 0; + } + } + else if (error_state.match) + { + free(error_state.match); + error_state.match = 0; + } + break; + case OPT_PREFIX: + if (n) + error_state.prefix = strdup(v); + else if (error_state.prefix) + { + free(error_state.prefix); + error_state.prefix = 0; + } + break; + case OPT_SYSTEM: + if (n) + error_info.set |= ERROR_SYSTEM; + else + error_info.clear |= ERROR_SYSTEM; + break; + case OPT_TIME: + error_info.time = n ? 1 : 0; + break; + case OPT_TRACE: + if (n) + error_info.trace = -strtol(v, NiL, 0); + else + error_info.trace = 0; + break; + } + return 0; +} + +/* + * print a name with optional delimiter, converting unprintable chars + */ + +static void +print(register Sfio_t* sp, register char* name, char* delim) +{ + if (mbwide()) + sfputr(sp, name, -1); + else + { +#if CC_NATIVE != CC_ASCII + register int c; + register unsigned char* n2a; + register unsigned char* a2n; + register int aa; + register int as; + + n2a = ccmap(CC_NATIVE, CC_ASCII); + a2n = ccmap(CC_ASCII, CC_NATIVE); + aa = n2a['A']; + as = n2a[' ']; + while (c = *name++) + { + c = n2a[c]; + if (c & 0200) + { + c &= 0177; + sfputc(sp, '?'); + } + if (c < as) + { + c += aa - 1; + sfputc(sp, '^'); + } + c = a2n[c]; + sfputc(sp, c); + } +#else + register int c; + + while (c = *name++) + { + if (c & 0200) + { + c &= 0177; + sfputc(sp, '?'); + } + if (c < ' ') + { + c += 'A' - 1; + sfputc(sp, '^'); + } + sfputc(sp, c); + } +#endif + } + if (delim) + sfputr(sp, delim, -1); +} + +/* + * print error context FIFO stack + */ + +#define CONTEXT(f,p) (((f)&ERROR_PUSH)?((Error_context_t*)&(p)->context->context):((Error_context_t*)(p))) + +static void +context(register Sfio_t* sp, register Error_context_t* cp) +{ + if (cp->context) + context(sp, CONTEXT(cp->flags, cp->context)); + if (!(cp->flags & ERROR_SILENT)) + { + if (cp->id) + print(sp, cp->id, NiL); + if (cp->line > ((cp->flags & ERROR_INTERACTIVE) != 0)) + { + if (cp->file) + sfprintf(sp, ": \"%s\", %s %d", cp->file, ERROR_translate(NiL, NiL, ast.id, "line"), cp->line); + else + sfprintf(sp, "[%d]", cp->line); + } + sfputr(sp, ": ", -1); + } +} + +/* + * debugging breakpoint + */ + +extern void +error_break(void) +{ + char* s; + + if (error_state.tty || (error_state.tty = sfopen(NiL, "/dev/tty", "r+"))) + { + sfprintf(error_state.tty, "error breakpoint: "); + if (s = sfgetr(error_state.tty, '\n', 1)) + { + if (streq(s, "q") || streq(s, "quit")) + exit(0); + stropt(s, options, sizeof(*options), setopt, NiL); + } + } +} + +void +error(int level, ...) +{ + va_list ap; + + va_start(ap, level); + errorv(NiL, level, ap); + va_end(ap); +} + +void +errorv(const char* id, int level, va_list ap) +{ + register int n; + int fd; + int flags; + char* s; + char* t; + char* format; + char* library; + const char* catalog; + + int line; + char* file; + +#if !_PACKAGE_astsa + unsigned long d; + struct tms us; +#endif + + if (!error_info.init) + { + error_info.init = 1; + stropt(getenv("ERROR_OPTIONS"), options, sizeof(*options), setopt, NiL); + } + if (level > 0) + { + flags = level & ~ERROR_LEVEL; + level &= ERROR_LEVEL; + } + else + flags = 0; + if ((flags & (ERROR_USAGE|ERROR_NOID)) == ERROR_NOID) + { + format = (char*)id; + id = 0; + } + else + format = 0; + if (id) + { + catalog = (char*)id; + if (!*catalog || *catalog == ':') + { + catalog = 0; + library = 0; + } + else if ((library = strchr(catalog, ':')) && !*++library) + library = 0; + } + else + { + catalog = 0; + library = 0; + } + if (catalog) + id = 0; + else + { + id = (const char*)error_info.id; + catalog = error_info.catalog; + } + if (level < error_info.trace || (flags & ERROR_LIBRARY) && !(((error_info.set | error_info.flags) ^ error_info.clear) & ERROR_LIBRARY) || level < 0 && error_info.mask && !(error_info.mask & (1<<(-level - 1)))) + { + if (level >= ERROR_FATAL) + (*error_info.exit)(level - 1); + return; + } + if (error_info.trace < 0) + flags |= ERROR_LIBRARY|ERROR_SYSTEM; + flags |= error_info.set | error_info.flags; + flags &= ~error_info.clear; + if (!library) + flags &= ~ERROR_LIBRARY; + fd = (flags & ERROR_OUTPUT) ? va_arg(ap, int) : error_info.fd; + if (error_info.write) + { + long off; + char* bas; + + bas = stkptr(stkstd, 0); + if (off = stktell(stkstd)) + stkfreeze(stkstd, 0); + file = error_info.id; + if (error_state.prefix) + sfprintf(stkstd, "%s: ", error_state.prefix); + if (flags & ERROR_USAGE) + { + if (flags & ERROR_NOID) + sfprintf(stkstd, " "); + else + sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "Usage")); + if (file || opt_info.argv && (file = opt_info.argv[0])) + print(stkstd, file, " "); + } + else + { + if (level && !(flags & ERROR_NOID)) + { + if (error_info.context && level > 0) + context(stkstd, CONTEXT(error_info.flags, error_info.context)); + if (file) + print(stkstd, file, (flags & ERROR_LIBRARY) ? " " : ": "); + if (flags & (ERROR_CATALOG|ERROR_LIBRARY)) + { + sfprintf(stkstd, "["); + if (flags & ERROR_CATALOG) + sfprintf(stkstd, "%s %s%s", + catalog ? catalog : ERROR_translate(NiL, NiL, ast.id, "DEFAULT"), + ERROR_translate(NiL, NiL, ast.id, "catalog"), + (flags & ERROR_LIBRARY) ? ", " : ""); + if (flags & ERROR_LIBRARY) + sfprintf(stkstd, "%s %s", + library, + ERROR_translate(NiL, NiL, ast.id, "library")); + sfprintf(stkstd, "]: "); + } + } + if (level > 0 && error_info.line > ((flags & ERROR_INTERACTIVE) != 0)) + { + if (error_info.file && *error_info.file) + sfprintf(stkstd, "\"%s\", ", error_info.file); + sfprintf(stkstd, "%s %d: ", ERROR_translate(NiL, NiL, ast.id, "line"), error_info.line); + } + } +#if !_PACKAGE_astsa + if (error_info.time) + { + if ((d = times(&us)) < error_info.time || error_info.time == 1) + error_info.time = d; + sfprintf(stkstd, " %05lu.%05lu.%05lu ", d - error_info.time, (unsigned long)us.tms_utime, (unsigned long)us.tms_stime); + } +#endif + switch (level) + { + case 0: + flags &= ~ERROR_SYSTEM; + break; + case ERROR_WARNING: + sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "warning")); + break; + case ERROR_PANIC: + sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "panic")); + break; + default: + if (level < 0) + { + s = ERROR_translate(NiL, NiL, ast.id, "debug"); + if (error_info.trace < -1) + sfprintf(stkstd, "%s%d:%s", s, level, level > -10 ? " " : ""); + else + sfprintf(stkstd, "%s: ", s); + for (n = 0; n < error_info.indent; n++) + { + sfputc(stkstd, ' '); + sfputc(stkstd, ' '); + } + } + break; + } + if (flags & ERROR_SOURCE) + { + /* + * source ([version], file, line) message + */ + + file = va_arg(ap, char*); + line = va_arg(ap, int); + s = ERROR_translate(NiL, NiL, ast.id, "line"); + if (error_info.version) + sfprintf(stkstd, "(%s: \"%s\", %s %d) ", error_info.version, file, s, line); + else + sfprintf(stkstd, "(\"%s\", %s %d) ", file, s, line); + } + if (format || (format = va_arg(ap, char*))) + { + if (!(flags & ERROR_USAGE)) + format = ERROR_translate(NiL, id, catalog, format); + sfvprintf(stkstd, format, ap); + } + if (!(flags & ERROR_PROMPT)) + { + /* + * level&ERROR_OUTPUT on return means message + * already output + */ + + if ((flags & ERROR_SYSTEM) && errno && errno != error_info.last_errno) + { + sfprintf(stkstd, " [%s]", fmterror(errno)); + if (error_info.set & ERROR_SYSTEM) + errno = 0; + error_info.last_errno = (level >= 0) ? 0 : errno; + } + if (error_info.auxilliary && level >= 0) + level = (*error_info.auxilliary)(stkstd, level, flags); + sfputc(stkstd, '\n'); + } + if (level > 0) + { + if ((level & ~ERROR_OUTPUT) > 1) + error_info.errors++; + else + error_info.warnings++; + } + if (level < 0 || !(level & ERROR_OUTPUT)) + { + n = stktell(stkstd); + s = stkptr(stkstd, 0); + if (t = memchr(s, '\f', n)) + { + n -= ++t - s; + s = t; + } +#if HUH_19980401 /* nasty problems if sfgetr() is in effect! */ + sfsync(sfstdin); +#endif + sfsync(sfstdout); + sfsync(sfstderr); + if (fd == sffileno(sfstderr) && error_info.write == write) + { + sfwrite(sfstderr, s, n); + sfsync(sfstderr); + } + else + (*error_info.write)(fd, s, n); + } + else + { + s = 0; + level &= ERROR_LEVEL; + } + stkset(stkstd, bas, off); + } + else + s = 0; + if (level >= error_state.breakpoint && error_state.breakpoint && (!error_state.match || !regexec(error_state.match, s ? s : format, 0, NiL, 0)) && (!error_state.count || !--error_state.count)) + { + if (error_info.core) + { +#ifndef SIGABRT +#ifdef SIGQUIT +#define SIGABRT SIGQUIT +#else +#ifdef SIGIOT +#define SIGABRT SIGIOT +#endif +#endif +#endif +#ifdef SIGABRT + signal(SIGABRT, SIG_DFL); + kill(getpid(), SIGABRT); + pause(); +#else + abort(); +#endif + } + else + error_break(); + } + if (level >= ERROR_FATAL) + (*error_info.exit)(level - ERROR_FATAL + 1); +} + +/* + * error_info context control + */ + +static Error_info_t* freecontext; + +Error_info_t* +errorctx(Error_info_t* p, int op, int flags) +{ + if (op & ERROR_POP) + { + if (!(_error_infop_ = p->context)) + _error_infop_ = &_error_info_; + if (op & ERROR_FREE) + { + p->context = freecontext; + freecontext = p; + } + p = _error_infop_; + } + else + { + if (!p) + { + if (p = freecontext) + freecontext = freecontext->context; + else if (!(p = newof(0, Error_info_t, 1, 0))) + return 0; + *p = *_error_infop_; + p->errors = p->flags = p->line = p->warnings = 0; + p->catalog = p->file = 0; + } + if (op & ERROR_PUSH) + { + p->flags = flags; + p->context = _error_infop_; + _error_infop_ = p; + } + p->flags |= ERROR_PUSH; + } + return p; +} diff --git a/src/lib/libast/misc/errorf.c b/src/lib/libast/misc/errorf.c new file mode 100644 index 0000000..0a67e97 --- /dev/null +++ b/src/lib/libast/misc/errorf.c @@ -0,0 +1,41 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * generic error discipline function + */ + +#include <error.h> + +int +errorf(void* handle, void* discipline, int level, ...) +{ + va_list ap; + + va_start(ap, level); + errorv((discipline && handle) ? *((char**)handle) : (char*)handle, (discipline || level < 0) ? level : (level | ERROR_LIBRARY), ap); + va_end(ap); + return 0; +} diff --git a/src/lib/libast/misc/errormsg.c b/src/lib/libast/misc/errormsg.c new file mode 100644 index 0000000..555d4f1 --- /dev/null +++ b/src/lib/libast/misc/errormsg.c @@ -0,0 +1,41 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * error function with specific dictionary + */ + +#include <error.h> + +int +errormsg(const char* dictionary, int level, ...) +{ + va_list ap; + + va_start(ap, level); + errorv(dictionary, level, ap); + va_end(ap); + return 0; +} diff --git a/src/lib/libast/misc/errorx.c b/src/lib/libast/misc/errorx.c new file mode 100644 index 0000000..70466de --- /dev/null +++ b/src/lib/libast/misc/errorx.c @@ -0,0 +1,50 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +#include "lclib.h" + +/* + * low level for ERROR_translate() + * this fills in NiL arg defaults and calls error_info.translate + */ + +char* +errorx(const char* loc, const char* cmd, const char* cat, const char* msg) +{ + char* s; + + if (!error_info.translate) + error_info.translate = translate; /* 2007-03-19 OLD_Error_info_t workaround */ + if (ERROR_translating()) + { + if (!loc) + loc = (const char*)locales[AST_LC_MESSAGES]->code; + if (!cmd) + cmd = (const char*)error_info.id; + if (!cat) + cat = (const char*)error_info.catalog; + if (s = (*error_info.translate)(loc, cmd, cat, msg)) + return s; + } + return (char*)msg; +} diff --git a/src/lib/libast/misc/fastfind.c b/src/lib/libast/misc/fastfind.c new file mode 100644 index 0000000..04d54d9 --- /dev/null +++ b/src/lib/libast/misc/fastfind.c @@ -0,0 +1,1282 @@ +#pragma prototyped +/* + * original code + * + * James A. Woods, Informatics General Corporation, + * NASA Ames Research Center, 6/81. + * Usenix ;login:, February/March, 1983, p. 8. + * + * discipline/method interface + * + * Glenn Fowler + * AT&T Research + * modified from the original BSD source + * + * 'fastfind' scans a file list for the full pathname of a file + * given only a piece of the name. The list is processed with + * with "front-compression" and bigram coding. Front compression reduces + * space by a factor of 4-5, bigram coding by a further 20-25%. + * + * there are 4 methods: + * + * FF_old original with 7 bit bigram encoding (no magic) + * FF_gnu 8 bit clean front compression (FF_gnu_magic) + * FF_dir FF_gnu with sfgetl/sfputl and trailing / on dirs (FF_dir_magic) + * FF_typ FF_dir with (mime) types (FF_typ_magic) + * + * the bigram encoding steals the eighth bit (that's why its FF_old) + * maybe one day we'll limit it to readonly: + * + * 0-2*FF_OFF likeliest differential counts + offset to make nonnegative + * FF_ESC 4 byte big-endian out-of-range count+FF_OFF follows + * FF_MIN-FF_MAX ascii residue + * >=FF_MAX bigram codes + * + * a two-tiered string search technique is employed + * + * a metacharacter-free subpattern and partial pathname is matched + * backwards to avoid full expansion of the pathname list + * + * then the actual shell glob-style regular expression (if in this form) + * is matched against the candidate pathnames using the slower regexec() + * + * The original BSD code is covered by the BSD license: + * + * Copyright (c) 1985, 1993, 1999 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +static const char id[] = "\n@(#)$Id: fastfind (AT&T Research) 2002-10-02 $\0\n"; + +static const char lib[] = "libast:fastfind"; + +#include "findlib.h" + +#define FIND_MATCH "*/(find|locate)/*" + +/* + * this db could be anywhere + * findcodes[] directories are checked for findnames[i] + */ + +static char* findcodes[] = +{ + 0, + 0, + FIND_CODES, + "/usr/local/share/lib", + "/usr/local/lib", + "/usr/share/lib", + "/usr/lib", + "/var/spool", + "/usr/local/var", + "/var/lib", + "/var/lib/slocate", + "/var/db", +}; + +static char* findnames[] = +{ + "find/codes", + "find/find.codes", + "locate/locatedb", + "locatedb", + "locate.database", + "slocate.db", +}; + +/* + * convert t to lower case and drop leading x- and x- after / + * converted value copied to b of size n + */ + +char* +typefix(char* buf, size_t n, register const char* t) +{ + register int c; + register char* b = buf; + + if ((*t == 'x' || *t == 'X') && *(t + 1) == '-') + t += 2; + while (c = *t++) + { + if (isupper(c)) + c = tolower(c); + if ((*b++ = c) == '/' && (*t == 'x' || *t == 'X') && *(t + 1) == '-') + t += 2; + } + *b = 0; + return buf; +} + +/* + * return a fastfind stream handle for pattern + */ + +Find_t* +findopen(const char* file, const char* pattern, const char* type, Finddisc_t* disc) +{ + register Find_t* fp; + register char* p; + register char* s; + register char* b; + register int i; + register int j; + char* path; + int brace = 0; + int paren = 0; + int k; + int q; + int fd; + int uid; + Vmalloc_t* vm; + Type_t* tp; + struct stat st; + + + if (!(vm = vmopen(Vmdcheap, Vmbest, 0))) + goto nospace; + + /* + * NOTE: searching for FIND_CODES would be much simpler if we + * just stuck with our own, but we also support GNU + * locate codes and have to search for the one of a + * bazillion possible names for that file + */ + + if (!findcodes[1]) + findcodes[1] = getenv(FIND_CODES_ENV); + if (disc->flags & FIND_GENERATE) + { + if (!(fp = (Find_t*)vmnewof(vm, 0, Find_t, 1, sizeof(Encode_t) - sizeof(Code_t)))) + goto nospace; + fp->vm = vm; + fp->id = lib; + fp->disc = disc; + fp->generate = 1; + if (file && (!*file || streq(file, "-"))) + file = 0; + uid = geteuid(); + j = (findcodes[0] = (char*)file) && *file == '/' ? 1 : elementsof(findcodes); + + /* + * look for the codes file, but since it may not exist yet, + * also look for the containing directory if i<2 or if + * it is sufficiently qualified (FIND_MATCH) + */ + + for (i = 0; i < j; i++) + if (path = findcodes[i]) + { + if (*path == '/') + { + if (!stat(path, &st)) + { + if (S_ISDIR(st.st_mode)) + { + for (k = 0; k < elementsof(findnames); k++) + { + sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%s/%s", path, findnames[k]); + if (!eaccess(fp->encode.file, R_OK|W_OK)) + { + path = fp->encode.file; + break; + } + if (strchr(findnames[k], '/') && (b = strrchr(fp->encode.file, '/'))) + { + *b = 0; + if (!stat(fp->encode.file, &st) && st.st_uid == uid && (st.st_mode & S_IWUSR)) + { + *b = '/'; + path = fp->encode.file; + break; + } + } + } + if (k < elementsof(findnames)) + break; + } + else if (st.st_uid == uid && (st.st_mode & S_IWUSR)) + { + sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%s", path); + path = fp->encode.file; + break; + } + } + else if (i < 2 || strmatch(path, FIND_MATCH)) + { + sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%s", path); + if (b = strrchr(fp->encode.file, '/')) + { + *b = 0; + if (!stat(fp->encode.file, &st) && st.st_uid == uid && (st.st_mode & S_IWUSR)) + { + *b = '/'; + path = fp->encode.file; + break; + } + } + } + } + else if (pathpath(path, "", PATH_REGULAR|PATH_READ|PATH_WRITE, fp->encode.file, sizeof(fp->encode.file))) + { + path = fp->encode.file; + break; + } + else if (b = strrchr(path, '/')) + { + sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%-.*s", b - path, path); + if (pathpath(fp->encode.file, "", PATH_EXECUTE|PATH_READ|PATH_WRITE, fp->encode.temp, sizeof(fp->encode.temp)) && + !stat(fp->encode.temp, &st) && st.st_uid == uid && (st.st_mode & S_IWUSR)) + { + sfsprintf(fp->encode.file, sizeof(fp->encode.file), "%s%s", fp->encode.temp, b); + path = fp->encode.file; + break; + } + } + } + if (i >= j) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot locate codes", file ? file : findcodes[2]); + goto drop; + } + if (fp->disc->flags & FIND_OLD) + { + /* + * FF_old generates temp data that is read + * in a second pass to generate the real codes + */ + + fp->method = FF_old; + if (!(fp->fp = sftmp(32 * PATH_MAX))) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "cannot create tmp file"); + goto drop; + } + } + else + { + /* + * the rest generate into a temp file that + * is simply renamed on completion + */ + + if (s = strrchr(path, '/')) + { + *s = 0; + p = path; + } + else + p = "."; + if (!pathtemp(fp->encode.temp, sizeof(fp->encode.temp), p, "ff", &fd)) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "%s: cannot create tmp file in this directory", p ? p : "."); + goto drop; + } + if (s) + *s = '/'; + if (!(fp->fp = sfnew(NiL, NiL, (size_t)SF_UNBOUND, fd, SF_WRITE))) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "%s: cannot open tmp file", fp->encode.temp); + close(fd); + goto drop; + } + if (fp->disc->flags & FIND_TYPE) + { + fp->method = FF_typ; + fp->encode.namedisc.key = offsetof(Type_t, name); + fp->encode.namedisc.link = offsetof(Type_t, byname); + fp->encode.indexdisc.key = offsetof(Type_t, index); + fp->encode.indexdisc.size = sizeof(unsigned long); + fp->encode.indexdisc.link = offsetof(Type_t, byindex); + s = "system/dir"; + if (!(fp->encode.namedict = dtopen(&fp->encode.namedisc, Dtoset)) || !(fp->encode.indexdict = dtopen(&fp->encode.indexdisc, Dtoset)) || !(tp = newof(0, Type_t, 1, strlen(s) + 1))) + { + if (fp->encode.namedict) + dtclose(fp->encode.namedict); + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "cannot allocate type table"); + goto drop; + } + + /* + * type index 1 is always system/dir + */ + + tp->index = ++fp->types; + strcpy(tp->name, s); + dtinsert(fp->encode.namedict, tp); + dtinsert(fp->encode.indexdict, tp); + } + else if (fp->disc->flags & FIND_GNU) + { + fp->method = FF_gnu; + sfputc(fp->fp, 0); + sfputr(fp->fp, FF_gnu_magic, 0); + } + else + { + fp->method = FF_dir; + sfputc(fp->fp, 0); + sfputr(fp->fp, FF_dir_magic, 0); + } + } + } + else + { + i = sizeof(Decode_t) + sizeof(Code_t); + if (!pattern || !*pattern) + pattern = "*"; + i += (j = 2 * (strlen(pattern) + 1)); + if (!(fp = (Find_t*)vmnewof(vm, 0, Find_t, 1, i))) + { + vmclose(vm); + return 0; + } + fp->vm = vm; + fp->id = lib; + fp->disc = disc; + if (disc->flags & FIND_ICASE) + fp->decode.ignorecase = 1; + j = (findcodes[0] = (char*)file) && *file == '/' ? 1 : elementsof(findcodes); + for (i = 0; i < j; i++) + if (path = findcodes[i]) + { + if (*path == '/') + { + if (!stat(path, &st)) + { + if (S_ISDIR(st.st_mode)) + { + for (k = 0; k < elementsof(findnames); k++) + { + sfsprintf(fp->decode.path, sizeof(fp->decode.path), "%s/%s", path, findnames[k]); + if (fp->fp = sfopen(NiL, fp->decode.path, "r")) + { + path = fp->decode.path; + break; + } + } + if (fp->fp) + break; + } + else if (fp->fp = sfopen(NiL, path, "r")) + break; + } + } + else if ((path = pathpath(path, "", PATH_REGULAR|PATH_READ, fp->decode.path, sizeof(fp->decode.path))) && (fp->fp = sfopen(NiL, path, "r"))) + break; + } + if (!fp->fp) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot locate codes", file ? file : findcodes[2]); + goto drop; + } + if (fstat(sffileno(fp->fp), &st)) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot stat codes", path); + goto drop; + } + if (fp->secure = ((st.st_mode & (S_IRGRP|S_IROTH)) == S_IRGRP) && st.st_gid == getegid() && getegid() != getgid()) + setgid(getgid()); + fp->stamp = st.st_mtime; + b = (s = fp->decode.temp) + 1; + for (i = 0; i < elementsof(fp->decode.bigram1); i++) + { + if ((j = sfgetc(fp->fp)) == EOF) + goto invalid; + if (!(*s++ = fp->decode.bigram1[i] = j) && i) + { + i = -i; + break; + } + if ((j = sfgetc(fp->fp)) == EOF) + goto invalid; + if (!(*s++ = fp->decode.bigram2[i] = j) && (i || fp->decode.bigram1[0] >= '0' && fp->decode.bigram1[0] <= '1')) + break; + } + if (streq(b, FF_typ_magic)) + { + if (type) + { + type = (const char*)typefix(fp->decode.bigram2, sizeof(fp->decode.bigram2), type); + memset(fp->decode.bigram1, 0, sizeof(fp->decode.bigram1)); + } + fp->method = FF_typ; + for (j = 0, i = 1;; i++) + { + if (!(s = sfgetr(fp->fp, 0, 0))) + goto invalid; + if (!*s) + break; + if (type && strmatch(s, type)) + { + FF_SET_TYPE(fp, i); + j++; + } + } + if (type && !j) + goto drop; + fp->types = j; + } + else if (streq(b, FF_dir_magic)) + fp->method = FF_dir; + else if (streq(b, FF_gnu_magic)) + fp->method = FF_gnu; + else if (!*b && *--b >= '0' && *b <= '1') + { + fp->method = FF_gnu; + while (j = sfgetc(fp->fp)) + { + if (j == EOF || fp->decode.count >= sizeof(fp->decode.path)) + goto invalid; + fp->decode.path[fp->decode.count++] = j; + } + } + else + { + fp->method = FF_old; + if (i < 0) + { + if ((j = sfgetc(fp->fp)) == EOF) + goto invalid; + fp->decode.bigram2[i = -i] = j; + } + while (++i < elementsof(fp->decode.bigram1)) + { + if ((j = sfgetc(fp->fp)) == EOF) + goto invalid; + fp->decode.bigram1[i] = j; + if ((j = sfgetc(fp->fp)) == EOF) + goto invalid; + fp->decode.bigram2[i] = j; + } + if ((fp->decode.peek = sfgetc(fp->fp)) != FF_OFF) + goto invalid; + } + + /* + * set up the physical dir table + */ + + if (disc->version >= 19980301L) + { + fp->verifyf = disc->verifyf; + if (disc->dirs && *disc->dirs) + { + for (k = 0; disc->dirs[k]; k++); + if (k == 1 && streq(disc->dirs[0], "/")) + k = 0; + if (k) + { + if (!(fp->dirs = vmnewof(fp->vm, 0, char*, 2 * k + 1, 0))) + goto drop; + if (!(fp->lens = vmnewof(fp->vm, 0, int, 2 * k, 0))) + goto drop; + p = 0; + b = fp->decode.temp; + j = fp->method == FF_old || fp->method == FF_gnu; + + /* + * fill the dir list with logical and + * physical names since we don't know + * which way the db was encoded (it + * could be *both* ways) + */ + + for (i = q = 0; i < k; i++) + { + if (*(s = disc->dirs[i]) == '/') + sfsprintf(b, sizeof(fp->decode.temp) - 1, "%s", s); + else if (!p && !(p = getcwd(fp->decode.path, sizeof(fp->decode.path)))) + goto nospace; + else + sfsprintf(b, sizeof(fp->decode.temp) - 1, "%s/%s", p, s); + s = pathcanon(b, sizeof(fp->decode.temp), 0); + *s = '/'; + *(s + 1) = 0; + if (!(fp->dirs[q] = vmstrdup(fp->vm, b))) + goto nospace; + if (j) + (fp->dirs[q])[s - b] = 0; + q++; + *s = 0; + s = pathcanon(b, sizeof(fp->decode.temp), PATH_PHYSICAL); + *s = '/'; + *(s + 1) = 0; + if (!strneq(b, fp->dirs[q - 1], s - b)) + { + if (!(fp->dirs[q] = vmstrdup(fp->vm, b))) + goto nospace; + if (j) + (fp->dirs[q])[s - b] = 0; + q++; + } + } + strsort(fp->dirs, q, strcasecmp); + for (i = 0; i < q; i++) + fp->lens[i] = strlen(fp->dirs[i]); + } + } + } + if (fp->verifyf || (disc->flags & FIND_VERIFY)) + { + if (fp->method != FF_dir && fp->method != FF_typ) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: %s code format does not support directory verification", path, fp->method == FF_gnu ? FF_gnu_magic : "OLD-BIGRAM"); + goto drop; + } + fp->verify = 1; + } + + /* + * extract last glob-free subpattern in name for fast pre-match + * prepend 0 for backwards match + */ + + if (p = s = (char*)pattern) + { + b = fp->decode.pattern; + for (;;) + { + switch (*b++ = *p++) + { + case 0: + break; + case '\\': + s = p; + if (!*p++) + break; + continue; + case '[': + if (!brace) + { + brace++; + if (*p == ']') + p++; + } + continue; + case ']': + if (brace) + { + brace--; + s = p; + } + continue; + case '(': + if (!brace) + paren++; + continue; + case ')': + if (!brace && paren > 0 && !--paren) + s = p; + continue; + case '|': + case '&': + if (!brace && !paren) + { + s = ""; + break; + } + continue; + case '*': + case '?': + s = p; + continue; + default: + continue; + } + break; + } + if (s != pattern && !streq(pattern, "*")) + { + fp->decode.match = 1; + if (i = regcomp(&fp->decode.re, pattern, REG_SHELL|REG_AUGMENTED|(fp->decode.ignorecase?REG_ICASE:0))) + { + if (disc->errorf) + { + regerror(i, &fp->decode.re, fp->decode.temp, sizeof(fp->decode.temp)); + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: %s", pattern, fp->decode.temp); + } + goto drop; + } + } + if (*s) + { + *b++ = 0; + while (i = *s++) + *b++ = i; + *b-- = 0; + fp->decode.end = b; + if (fp->decode.ignorecase) + for (s = fp->decode.pattern; s <= b; s++) + if (isupper(*s)) + *s = tolower(*s); + } + } + } + return fp; + nospace: + if (disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "out of space"); + if (!vm) + return 0; + if (!fp) + { + vmclose(vm); + return 0; + } + goto drop; + invalid: + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: invalid codes", path); + drop: + if (!fp->generate && fp->decode.match) + regfree(&fp->decode.re); + if (fp->fp) + sfclose(fp->fp); + vmclose(fp->vm); + return 0; +} + +/* + * return the next fastfind path + * 0 returned when list exhausted + */ + +char* +findread(register Find_t* fp) +{ + register char* p; + register char* q; + register char* s; + register char* b; + register char* e; + register int c; + register int n; + register int m; + int ignorecase; + int t; + unsigned char w[4]; + struct stat st; + + if (fp->generate) + return 0; + if (fp->decode.restore) + { + *fp->decode.restore = '/'; + fp->decode.restore = 0; + } + ignorecase = fp->decode.ignorecase ? STR_ICASE : 0; + c = fp->decode.peek; + next: + for (;;) + { + switch (fp->method) + { + case FF_dir: + t = 0; + n = sfgetl(fp->fp); + goto grab; + case FF_gnu: + if ((c = sfgetc(fp->fp)) == EOF) + return 0; + if (c == 0x80) + { + if ((c = sfgetc(fp->fp)) == EOF) + return 0; + n = c << 8; + if ((c = sfgetc(fp->fp)) == EOF) + return 0; + n |= c; + if (n & 0x8000) + n = (n - 0xffff) - 1; + } + else if ((n = c) & 0x80) + n = (n - 0xff) - 1; + t = 0; + goto grab; + case FF_typ: + t = sfgetu(fp->fp); + n = sfgetl(fp->fp); + grab: + p = fp->decode.path + (fp->decode.count += n); + do + { + if ((c = sfgetc(fp->fp)) == EOF) + return 0; + } while (*p++ = c); + p -= 2; + break; + case FF_old: + if (c == EOF) + { + fp->decode.peek = c; + return 0; + } + if (c == FF_ESC) + { + if (sfread(fp->fp, w, sizeof(w)) != sizeof(w)) + return 0; + if (fp->decode.swap >= 0) + { + c = (int32_t)((w[0] << 24) | (w[1] << 16) | (w[2] << 8) | w[3]); + if (!fp->decode.swap) + { + /* + * the old format uses machine + * byte order; this test uses + * the smallest magnitude of + * both byte orders on the + * first encoded path motion + * to determine the original + * byte order + */ + + m = c; + if (m < 0) + m = -m; + n = (int32_t)((w[3] << 24) | (w[2] << 16) | (w[1] << 8) | w[0]); + if (n < 0) + n = -n; + if (m < n) + fp->decode.swap = 1; + else + { + fp->decode.swap = -1; + c = (int32_t)((w[3] << 24) | (w[2] << 16) | (w[1] << 8) | w[0]); + } + } + } + else + c = (int32_t)((w[3] << 24) | (w[2] << 16) | (w[1] << 8) | w[0]); + } + fp->decode.count += c - FF_OFF; + for (p = fp->decode.path + fp->decode.count; (c = sfgetc(fp->fp)) > FF_ESC;) + if (c & (1<<(CHAR_BIT-1))) + { + *p++ = fp->decode.bigram1[c & ((1<<(CHAR_BIT-1))-1)]; + *p++ = fp->decode.bigram2[c & ((1<<(CHAR_BIT-1))-1)]; + } + else + *p++ = c; + *p-- = 0; + t = 0; + break; + } + b = fp->decode.path; + if (fp->decode.found) + fp->decode.found = 0; + else + b += fp->decode.count; + if (fp->dirs) + for (;;) + { + if (!*fp->dirs) + return 0; + + /* + * use the ordering and lengths to prune + * comparison function calls + * (*fp->dirs)[*fp->lens]=='/' if its + * already been matched + */ + + if ((n = p - fp->decode.path + 1) > (m = *fp->lens)) + { + if (!(*fp->dirs)[m]) + goto next; + if (!strncasecmp(*fp->dirs, fp->decode.path, m)) + break; + } + else if (n == m) + { + if (!(*fp->dirs)[m]) + { + if (!(n = strcasecmp(*fp->dirs, fp->decode.path)) && (ignorecase || !strcmp(*fp->dirs, fp->decode.path))) + { + if (m > 0) + { + (*fp->dirs)[m] = '/'; + if ((*fp->dirs)[m - 1] != '/') + (*fp->dirs)[++(*fp->lens)] = '/'; + } + break; + } + if (n >= 0) + goto next; + } + } + else if (!(*fp->dirs)[m]) + goto next; + fp->dirs++; + fp->lens++; + } + if (fp->verify && (*p == '/' || t == 1)) + { + if ((n = p - fp->decode.path)) + *p = 0; + else + n = 1; + if (fp->verifyf) + n = (*fp->verifyf)(fp, fp->decode.path, n, fp->disc); + else if (stat(fp->decode.path, &st)) + n = -1; + else if ((unsigned long)st.st_mtime > fp->stamp) + n = 1; + else + n = 0; + *p = '/'; + + /* + * n<0 skip this subtree + * n==0 keep as is + * n>0 read this dir now + */ + + /* NOT IMPLEMENTED YET */ + } + if (FF_OK_TYPE(fp, t)) + { + if (fp->decode.end) + { + if (*(s = p) == '/') + s--; + if (*fp->decode.pattern == '/' && b > fp->decode.path) + b--; + for (; s >= b; s--) + if (*s == *fp->decode.end || ignorecase && tolower(*s) == *fp->decode.end) + { + if (ignorecase) + for (e = fp->decode.end - 1, q = s - 1; *e && (*q == *e || tolower(*q) == *e); e--, q--); + else + for (e = fp->decode.end - 1, q = s - 1; *e && *q == *e; e--, q--); + if (!*e) + { + fp->decode.found = 1; + if (!fp->decode.match || strgrpmatch(fp->decode.path, fp->decode.pattern, NiL, 0, STR_MAXIMAL|STR_LEFT|STR_RIGHT|ignorecase)) + { + fp->decode.peek = c; + if (*p == '/') + *(fp->decode.restore = p) = 0; + if (!fp->secure || !access(fp->decode.path, F_OK)) + return fp->decode.path; + } + break; + } + } + } + else if (!fp->decode.match || !(n = regexec(&fp->decode.re, fp->decode.path, 0, NiL, 0))) + { + fp->decode.peek = c; + if (*p == '/' && p > fp->decode.path) + *(fp->decode.restore = p) = 0; + if (!fp->secure || !access(fp->decode.path, F_OK)) + return fp->decode.path; + } + else if (n != REG_NOMATCH) + { + if (fp->disc->errorf) + { + regerror(n, &fp->decode.re, fp->decode.temp, sizeof(fp->decode.temp)); + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: %s", fp->decode.pattern, fp->decode.temp); + } + return 0; + } + } + } +} + +/* + * add path to the code table + * paths are assumed to be in sort order + */ + +int +findwrite(register Find_t* fp, const char* path, size_t len, const char* type) +{ + register unsigned char* s; + register unsigned char* e; + register unsigned char* p; + register int n; + register int d; + register Type_t* x; + register unsigned long u; + + if (!fp->generate) + return -1; + if (type && fp->method == FF_dir) + { + len = sfsprintf(fp->encode.mark, sizeof(fp->encode.mark), "%-.*s/", len, path); + path = fp->encode.mark; + } + s = (unsigned char*)path; + if (len <= 0) + len = strlen(path); + if (len < sizeof(fp->encode.path)) + e = s + len++; + else + { + len = sizeof(fp->encode.path) - 1; + e = s + len; + } + p = (unsigned char*)fp->encode.path; + while (s < e) + { + if (*s != *p++) + break; + s++; + } + n = s - (unsigned char*)path; + switch (fp->method) + { + case FF_gnu: + d = n - fp->encode.prefix; + if (d >= -127 && d <= 127) + sfputc(fp->fp, d & 0xff); + else + { + sfputc(fp->fp, 0x80); + sfputc(fp->fp, (d >> 8) & 0xff); + sfputc(fp->fp, d & 0xff); + } + fp->encode.prefix = n; + sfputr(fp->fp, (char*)s, 0); + break; + case FF_old: + sfprintf(fp->fp, "%ld", n - fp->encode.prefix + FF_OFF); + fp->encode.prefix = n; + sfputc(fp->fp, ' '); + p = s; + while (s < e) + { + n = *s++; + if (s >= e) + break; + fp->encode.code[n][*s++]++; + } + while (p < e) + { + if ((n = *p++) < FF_MIN || n >= FF_MAX) + n = '?'; + sfputc(fp->fp, n); + } + sfputc(fp->fp, 0); + break; + case FF_typ: + if (type) + { + type = (const char*)typefix((char*)fp->encode.bigram, sizeof(fp->encode.bigram), type); + if (x = (Type_t*)dtmatch(fp->encode.namedict, type)) + u = x->index; + else if (!(x = newof(0, Type_t, 1, strlen(type) + 1))) + u = 0; + else + { + u = x->index = ++fp->types; + strcpy(x->name, type); + dtinsert(fp->encode.namedict, x); + dtinsert(fp->encode.indexdict, x); + } + } + else + u = 0; + sfputu(fp->fp, u); + /*FALLTHROUGH...*/ + case FF_dir: + d = n - fp->encode.prefix; + sfputl(fp->fp, d); + fp->encode.prefix = n; + sfputr(fp->fp, (char*)s, 0); + break; + } + memcpy(fp->encode.path, path, len); + return 0; +} + +/* + * findsync() helper + */ + +static int +finddone(register Find_t* fp) +{ + int r; + + if (sfsync(fp->fp)) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: write error [sfsync]", fp->encode.file); + return -1; + } + if (sferror(fp->fp)) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: write error [sferror]", fp->encode.file); + return -1; + } + r = sfclose(fp->fp); + fp->fp = 0; + if (r) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: write error [sfclose]", fp->encode.file); + return -1; + } + return 0; +} + +/* + * finish the code table + */ + +static int +findsync(register Find_t* fp) +{ + register char* s; + register int n; + register int m; + register int d; + register Type_t* x; + char* t; + int b; + long z; + Sfio_t* sp; + + switch (fp->method) + { + case FF_dir: + case FF_gnu: + /* + * replace the real file with the temp file + */ + + if (finddone(fp)) + goto bad; + remove(fp->encode.file); + if (rename(fp->encode.temp, fp->encode.file)) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "%s: cannot rename from tmp file %s", fp->encode.file, fp->encode.temp); + remove(fp->encode.temp); + return -1; + } + break; + case FF_old: + /* + * determine the top FF_MAX bigrams + */ + + for (n = 0; n < FF_MAX; n++) + for (m = 0; m < FF_MAX; m++) + fp->encode.hits[fp->encode.code[n][m]]++; + fp->encode.hits[0] = 0; + m = 1; + for (n = USHRT_MAX; n >= 0; n--) + if (d = fp->encode.hits[n]) + { + fp->encode.hits[n] = m; + if ((m += d) > FF_MAX) + break; + } + while (--n >= 0) + fp->encode.hits[n] = 0; + for (n = FF_MAX - 1; n >= 0; n--) + for (m = FF_MAX - 1; m >= 0; m--) + if (fp->encode.hits[fp->encode.code[n][m]]) + { + d = fp->encode.code[n][m]; + b = fp->encode.hits[d] - 1; + fp->encode.code[n][m] = b + FF_MAX; + if (fp->encode.hits[d]++ >= FF_MAX) + fp->encode.hits[d] = 0; + fp->encode.bigram[b *= 2] = n; + fp->encode.bigram[b + 1] = m; + } + else + fp->encode.code[n][m] = 0; + + /* + * commit the real file + */ + + if (sfseek(fp->fp, (Sfoff_t)0, SEEK_SET)) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "cannot rewind tmp file"); + return -1; + } + if (!(sp = sfopen(NiL, fp->encode.file, "w"))) + goto badcreate; + + /* + * dump the bigrams + */ + + sfwrite(sp, fp->encode.bigram, sizeof(fp->encode.bigram)); + + /* + * encode the massaged paths + */ + + while (s = sfgetr(fp->fp, 0, 0)) + { + z = strtol(s, &t, 0); + s = t; + if (z < 0 || z > 2 * FF_OFF) + { + sfputc(sp, FF_ESC); + sfputc(sp, (z >> 24)); + sfputc(sp, (z >> 16)); + sfputc(sp, (z >> 8)); + sfputc(sp, z); + } + else + sfputc(sp, z); + while (n = *s++) + { + if (!(m = *s++)) + { + sfputc(sp, n); + break; + } + if (d = fp->encode.code[n][m]) + sfputc(sp, d); + else + { + sfputc(sp, n); + sfputc(sp, m); + } + } + } + sfclose(fp->fp); + fp->fp = sp; + if (finddone(fp)) + goto bad; + break; + case FF_typ: + if (finddone(fp)) + goto bad; + if (!(fp->fp = sfopen(NiL, fp->encode.temp, "r"))) + { + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, ERROR_SYSTEM|2, "%s: cannot read tmp file", fp->encode.temp); + remove(fp->encode.temp); + return -1; + } + + /* + * commit the output file + */ + + if (!(sp = sfopen(NiL, fp->encode.file, "w"))) + goto badcreate; + + /* + * write the header magic + */ + + sfputc(sp, 0); + sfputr(sp, FF_typ_magic, 0); + + /* + * write the type table in index order starting with 1 + */ + + for (x = (Type_t*)dtfirst(fp->encode.indexdict); x; x = (Type_t*)dtnext(fp->encode.indexdict, x)) + sfputr(sp, x->name, 0); + sfputc(sp, 0); + + /* + * append the front compressed strings + */ + + if (sfmove(fp->fp, sp, SF_UNBOUND, -1) < 0 || !sfeof(fp->fp)) + { + sfclose(sp); + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot append codes", fp->encode.file); + goto bad; + } + sfclose(fp->fp); + fp->fp = sp; + if (finddone(fp)) + goto bad; + remove(fp->encode.temp); + break; + } + return 0; + badcreate: + if (fp->disc->errorf) + (*fp->disc->errorf)(fp, fp->disc, 2, "%s: cannot write codes", fp->encode.file); + bad: + if (fp->fp) + { + sfclose(fp->fp); + fp->fp = 0; + } + remove(fp->encode.temp); + return -1; +} + +/* + * close an open fastfind stream + */ + +int +findclose(register Find_t* fp) +{ + int n = 0; + + if (!fp) + return -1; + if (fp->generate) + { + n = findsync(fp); + if (fp->encode.indexdict) + dtclose(fp->encode.indexdict); + if (fp->encode.namedict) + dtclose(fp->encode.namedict); + } + else + { + if (fp->decode.match) + regfree(&fp->decode.re); + n = 0; + } + if (fp->fp) + sfclose(fp->fp); + vmclose(fp->vm); + return n; +} diff --git a/src/lib/libast/misc/findlib.h b/src/lib/libast/misc/findlib.h new file mode 100644 index 0000000..e42e619 --- /dev/null +++ b/src/lib/libast/misc/findlib.h @@ -0,0 +1,123 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * fast find private interface + */ + +#ifndef _FINDLIB_H +#define _FINDLIB_H + +#include <ast.h> +#include <cdt.h> +#include <ctype.h> +#include <error.h> +#include <ls.h> +#include <regex.h> +#include <vmalloc.h> + +#define FF_old 1 /* old format - 7 bit bigram */ +#define FF_gnu 2 /* gnu 8 bit no bigram */ +#define FF_dir 3 /* FF_gnu, dirs have trailing / */ +#define FF_typ 4 /* FF_dir with types */ + +#define FF_gnu_magic "LOCATE02" +#define FF_dir_magic "FIND-DIR-02" +#define FF_typ_magic "FIND-DIR-TYPE-03" + +#define FF_ESC 0036 +#define FF_MAX 0200 +#define FF_MIN 0040 +#define FF_OFF 0016 + +#define FF_SET_TYPE(p,i) ((p)->decode.bigram1[((i)>>3)&((1<<CHAR_BIT)-1)]|=(1<<((i)&07))) +#define FF_OK_TYPE(p,i) (!(p)->types||((p)->decode.bigram1[((i)>>3)&((1<<CHAR_BIT)-1)]&(1<<((i)&07)))) + +typedef struct +{ + char* end; + char* type; + char* restore; + int count; + int found; + int ignorecase; + int match; + int peek; + int swap; + regex_t re; + char bigram1[(1<<(CHAR_BIT-1))]; + char bigram2[(1<<(CHAR_BIT-1))]; + char path[PATH_MAX]; + char temp[PATH_MAX]; + char pattern[1]; +} Decode_t; + +typedef struct +{ + Dtdisc_t namedisc; + Dtdisc_t indexdisc; + Dt_t* namedict; + Dt_t* indexdict; + int prefix; + unsigned char bigram[2*FF_MAX]; + unsigned short code[FF_MAX][FF_MAX]; + unsigned short hits[USHRT_MAX+1]; + char path[PATH_MAX]; + char mark[PATH_MAX]; + char file[PATH_MAX]; + char temp[PATH_MAX]; +} Encode_t; + +typedef union +{ + Decode_t code_decode; + Encode_t code_encode; +} Code_t; + +typedef struct +{ + Dtlink_t byname; + Dtlink_t byindex; + unsigned long index; + char name[1]; +} Type_t; + +#define _FIND_PRIVATE_ \ + Finddisc_t* disc; \ + Vmalloc_t* vm; \ + char** dirs; \ + int* lens; \ + Sfio_t* fp; \ + Findverify_f verifyf; \ + int generate; \ + int method; \ + int secure; \ + int types; \ + int verify; \ + Code_t code; + +#define decode code.code_decode +#define encode code.code_encode + +#include <find.h> + +#endif diff --git a/src/lib/libast/misc/fmtrec.c b/src/lib/libast/misc/fmtrec.c new file mode 100644 index 0000000..36b2cd5 --- /dev/null +++ b/src/lib/libast/misc/fmtrec.c @@ -0,0 +1,102 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * return the record format string given a format descriptor + */ + +#include <recfmt.h> +#include <ctype.h> + +char* +fmtrec(Recfmt_t f, int fs) +{ + char* b; + char* e; + char* s; + long n; + char del[2]; + + b = s = fmtbuf(n = 32); + e = b + n; + switch (RECTYPE(f)) + { + case REC_delimited: + *s++ = 'd'; + if ((del[0] = REC_D_DELIMITER(f)) != '\n') + { + del[1] = 0; + if (fs) + sfsprintf(s, e - s, "0x%02x", *(unsigned char*)del); + else + sfsprintf(s, e - s, "%s", fmtquote(del, NiL, NiL, 1, 0)); + } + else + *s = 0; + break; + case REC_fixed: + if (!fs) + *s++ = 'f'; + sfsprintf(s, e - s, "%lu", REC_F_SIZE(f)); + break; + case REC_variable: + *s++ = 'v'; + if (n = REC_V_SIZE(f)) + s += sfsprintf(s, e - s, "%lu", n); + if (REC_V_HEADER(f) != 4) + s += sfsprintf(s, e - s, "h%u", REC_V_HEADER(f)); + if (REC_V_OFFSET(f) != 0) + s += sfsprintf(s, e - s, "o%u", REC_V_OFFSET(f)); + if (REC_V_LENGTH(f) != 2) + s += sfsprintf(s, e - s, "z%u", REC_V_LENGTH(f)); + if (REC_V_LITTLE(f) != 0) + *s++ = 'l'; + if (REC_V_INCLUSIVE(f) == 0) + *s++ = 'n'; + *s = 0; + break; + case REC_method: + *s++ = 'm'; + switch (n = REC_M_INDEX(f)) + { + case REC_M_data: + sfsprintf(s, e - s, "data"); + break; + case REC_M_path: + sfsprintf(s, e - s, "path"); + break; + default: + sfsprintf(s, e - s, "%lu", n); + break; + } + break; + case REC_none: + *s++ = 'n'; + *s = 0; + break; + default: + sfsprintf(s, e - s, "u%u.0x%07x", RECTYPE(f), REC_U_ATTRIBUTES(f)); + break; + } + return b; +} diff --git a/src/lib/libast/misc/fs3d.c b/src/lib/libast/misc/fs3d.c new file mode 100644 index 0000000..cc708f2 --- /dev/null +++ b/src/lib/libast/misc/fs3d.c @@ -0,0 +1,116 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Bell Laboratories + * + * 3d fs operations + * only active for non-shared 3d library + */ + +#define mount ______mount + +#include <ast.h> + +#undef mount + +#include <fs3d.h> + +int +fs3d(register int op) +{ + register int cur; + register char* v; + char val[sizeof(FS3D_off) + 8]; + + static int fsview; + static char on[] = FS3D_on; + static char off[] = FS3D_off; + + if (fsview < 0) + return 0; + + /* + * get the current setting + */ + + if (!fsview && (!getenv("LD_PRELOAD") || mount("", "", 0, NiL))) + goto nope; + if (FS3D_op(op) == FS3D_OP_INIT && mount(FS3D_init, NiL, FS3D_VIEW, NiL)) + goto nope; + if (mount(on, val, FS3D_VIEW|FS3D_GET|FS3D_SIZE(sizeof(val)), NiL)) + goto nope; + if (v = strchr(val, ' ')) + v++; + else + v = val; + if (!strcmp(v, on)) + cur = FS3D_ON; + else if (!strncmp(v, off, sizeof(off) - 1) && v[sizeof(off)] == '=') + cur = FS3D_LIMIT((int)strtol(v + sizeof(off) + 1, NiL, 0)); + else + cur = FS3D_OFF; + if (cur != op) + { + switch (FS3D_op(op)) + { + case FS3D_OP_OFF: + v = off; + break; + case FS3D_OP_ON: + v = on; + break; + case FS3D_OP_LIMIT: + sfsprintf(val, sizeof(val), "%s=%d", off, FS3D_arg(op)); + v = val; + break; + default: + v = 0; + break; + } + if (v && mount(v, NiL, FS3D_VIEW, NiL)) + goto nope; + } + fsview = 1; + return cur; + nope: + fsview = -1; + return 0; +} + +/* + * user code that includes <fs3d.h> will have mount() mapped to fs3d_mount() + * this restricts the various "standard" mount prototype conflicts to this spot + * this means that code that includes <fs3d.h> cannot access the real mount + * (at least without some additional macro hackery + */ + +#undef mount + +extern int mount(const char*, char*, int, void*); + +int +fs3d_mount(const char* source, char* target, int flags, void* data) +{ + return mount(source, target, flags, data); +} diff --git a/src/lib/libast/misc/fts.c b/src/lib/libast/misc/fts.c new file mode 100644 index 0000000..d8bb34d --- /dev/null +++ b/src/lib/libast/misc/fts.c @@ -0,0 +1,1605 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Phong Vo + * Glenn Fowler + * AT&T Research + * + * fts implementation unwound from the kpv ftwalk() of 1988-10-30 + */ + +#include <ast.h> +#include <ast_dir.h> +#include <error.h> +#include <fs3d.h> +#include <ls.h> + +struct Ftsent; + +typedef int (*Compar_f)(struct Ftsent* const*, struct Ftsent* const*); +typedef int (*Stat_f)(const char*, struct stat*); + +#define _fts_status status +#define _fts_statb statb + +#define _FTS_PRIVATE_ \ + FTSENT* parent; /* top parent */ \ + FTSENT* todo; /* todo list */ \ + FTSENT* top; /* top element */ \ + FTSENT* root; \ + FTSENT* bot; /* bottom element */ \ + FTSENT* free; /* free element */ \ + FTSENT* diroot; \ + FTSENT* curdir; \ + FTSENT* current; /* current element */ \ + FTSENT* previous; /* previous current */ \ + FTSENT* dotdot; \ + FTSENT* link; /* real current fts_link*/ \ + FTSENT* pwd; /* pwd parent */ \ + DIR* dir; /* current dir stream */ \ + Compar_f comparf; /* node comparison func */ \ + size_t baselen; /* current strlen(base) */ \ + size_t homesize; /* sizeof(home) */ \ + int cd; /* chdir status */ \ + int cpname; \ + int flags; /* fts_open() flags */ \ + int nd; \ + unsigned char children; \ + unsigned char fs3d; \ + unsigned char nostat; \ + unsigned char state; /* fts_read() state */ \ + char* base; /* basename in path */ \ + char* name; \ + char* path; /* path workspace */ \ + char* home; /* home/path buffer */ \ + char* endbase; /* space to build paths */ \ + char* endbuf; /* space to build paths */ \ + char* pad[2]; /* $0.02 to splain this */ + +/* + * NOTE: <ftwalk.h> relies on status and statb being the first two elements + */ + +#define _FTSENT_PRIVATE_ \ + int nd; /* popdir() count */ \ + FTSENT* left; /* left child */ \ + FTSENT* right; /* right child */ \ + FTSENT* pwd; /* pwd parent */ \ + FTSENT* stack; /* getlist() stack */ \ + long nlink; /* FTS_D link count */ \ + unsigned char must; /* must stat */ \ + unsigned char type; /* DT_* type */ \ + unsigned char symlink; /* originally a symlink */ \ + char name[sizeof(int)]; /* fts_name data */ + +#include <fts.h> + +#ifndef ENOSYS +#define ENOSYS EINVAL +#endif + + +#if MAXNAMLEN > 16 +#define MINNAME 32 +#else +#define MINNAME 16 +#endif + +#define drop(p,f) (((f)->fts_namelen < MINNAME) ? ((f)->fts_link = (p)->free, (p)->free = (f)) : (free(f), (p)->free)) + +#define ACCESS(p,f) ((p)->cd==0?(f)->fts_name:(f)->fts_path) +#define PATH(f,p,l) ((!((f)->flags&FTS_SEEDOTDIR)&&(l)>0&&(p)[0]=='.'&&(p)[1]=='/')?((p)+2):(p)) +#define SAME(one,two) ((one)->st_ino==(two)->st_ino&&(one)->st_dev==(two)->st_dev) +#define SKIPLINK(p,f) ((f)->fts_parent->nlink == 0) + +#ifdef D_TYPE +#define ISTYPE(f,t) ((f)->type == (t)) +#define TYPE(f,t) ((f)->type = (t)) +#define SKIP(p,f) ((f)->fts_parent->must == 0 && (((f)->type == DT_UNKNOWN) ? SKIPLINK(p,f) : ((f)->type != DT_DIR && ((f)->type != DT_LNK || ((p)->flags & FTS_PHYSICAL))))) +#else +#undef DT_UNKNOWN +#define DT_UNKNOWN 0 +#undef DT_LNK +#define DT_LNK 1 +#define ISTYPE(f,t) ((t)==DT_UNKNOWN) +#define TYPE(f,d) +#define SKIP(p,f) ((f)->fts_parent->must == 0 && SKIPLINK(p,f)) +#endif + +#ifndef D_FILENO +#define D_FILENO(d) (1) +#endif + +/* + * NOTE: a malicious dir rename() could change .. underfoot so we + * must always verify; undef verify to enable the unsafe code + */ + +#define verify 1 + +/* + * FTS_NOSTAT requires a dir with + * D_TYPE(&dirent_t)!=DT_UNKNOWN + * OR + * st_nlink>=2 + */ + +#define FTS_children_resume 1 +#define FTS_children_return 2 +#define FTS_error 3 +#define FTS_popstack 4 +#define FTS_popstack_resume 5 +#define FTS_popstack_return 6 +#define FTS_preorder 7 +#define FTS_preorder_resume 8 +#define FTS_preorder_return 9 +#define FTS_readdir 10 +#define FTS_terminal 11 +#define FTS_todo 12 +#define FTS_top_return 13 + +typedef int (*Notify_f)(FTS*, FTSENT*, void*); + +typedef struct Notify_s +{ + struct Notify_s* next; + Notify_f notifyf; + void* context; +} Notify_t; + +static Notify_t* notify; + +/* + * allocate an FTSENT node + */ + +static FTSENT* +node(FTS* fts, FTSENT* parent, register char* name, register size_t namelen) +{ + register FTSENT* f; + register size_t n; + + if (fts->free && namelen < MINNAME) + { + f = fts->free; + fts->free = f->fts_link; + } + else + { + n = (namelen < MINNAME ? MINNAME : namelen + 1) - sizeof(int); + if (!(f = newof(0, FTSENT, 1, n))) + { + fts->fts_errno = errno; + fts->state = FTS_error; + return 0; + } + f->fts = fts; + } + TYPE(f, DT_UNKNOWN); + f->status = 0; + f->symlink = 0; + f->fts_level = (f->fts_parent = parent)->fts_level + 1; +#if __OBSOLETE__ < 20140101 + f->_fts_level = (short)f->fts_level; +#endif + f->fts_link = 0; + f->fts_pointer = 0; + f->fts_number = 0; + f->fts_errno = 0; + f->fts_namelen = namelen; +#if __OBSOLETE__ < 20140101 + f->_fts_namelen = (unsigned short)f->fts_namelen; +#endif + f->fts_name = f->name; + f->fts_statp = &f->statb; + memcpy(f->fts_name, name, namelen + 1); + return f; +} + +/* + * compare directories by device/inode + */ + +static int +statcmp(FTSENT* const* pf1, FTSENT* const* pf2) +{ + register const FTSENT* f1 = *pf1; + register const FTSENT* f2 = *pf2; + + if (f1->statb.st_ino < f2->statb.st_ino) + return -1; + if (f1->statb.st_ino > f2->statb.st_ino) + return 1; + if (f1->statb.st_dev < f2->statb.st_dev) + return -1; + if (f1->statb.st_dev > f2->statb.st_dev) + return 1; + + /* + * hack for NFS where <dev,ino> may not uniquely identify objects + */ + + if (f1->statb.st_mtime < f2->statb.st_mtime) + return -1; + if (f1->statb.st_mtime > f2->statb.st_mtime) + return 1; + return 0; +} + +/* + * search trees with top-down splaying (a la Tarjan and Sleator) + * when used for insertion sort, this implements a stable sort + */ + +#define RROTATE(r) (t = r->left, r->left = t->right, t->right = r, r = t) +#define LROTATE(r) (t = r->right, r->right = t->left, t->left = r, r = t) + +static FTSENT* +search(FTSENT* e, FTSENT* root, int(*comparf)(FTSENT* const*, FTSENT* const*), int insert) +{ + register int cmp; + register FTSENT* t; + register FTSENT* left; + register FTSENT* right; + register FTSENT* lroot; + register FTSENT* rroot; + + left = right = lroot = rroot = 0; + while (root) + { + if (!(cmp = (*comparf)(&e, &root)) && !insert) + break; + if (cmp < 0) + { + /* + * this is the left zig-zig case + */ + + if (root->left && (cmp = (*comparf)(&e, &root->left)) <= 0) + { + RROTATE(root); + if (!cmp && !insert) + break; + } + + /* + * stick all things > e to the right tree + */ + + if (right) + right->left = root; + else + rroot = root; + right = root; + root = root->left; + right->left = 0; + } + else + { + /* + * this is the right zig-zig case + */ + + if (root->right && (cmp = (*comparf)(&e, &root->right)) >= 0) + { + LROTATE(root); + if (!cmp && !insert) + break; + } + + /* + * stick all things <= e to the left tree + */ + + if (left) + left->right = root; + else + lroot = root; + left = root; + root = root->right; + left->right = 0; + } + } + if (!root) + root = e; + else + { + if (right) + right->left = root->right; + else + rroot = root->right; + if (left) + left->right = root->left; + else + lroot = root->left; + } + root->left = lroot; + root->right = rroot; + return root; +} + +/* + * delete the root element from the tree + */ + +static FTSENT* +deleteroot(register FTSENT* root) +{ + register FTSENT* t; + register FTSENT* left; + register FTSENT* right; + + right = root->right; + if (!(left = root->left)) + root = right; + else + { + while (left->right) + LROTATE(left); + left->right = right; + root = left; + } + return root; +} + +/* + * generate ordered fts_link list from binary tree at root + * FTSENT.stack instead of recursion to avoid blowing the real + * stack on big directories + */ + +static void +getlist(register FTSENT** top, register FTSENT** bot, register FTSENT* root) +{ + register FTSENT* stack = 0; + + for (;;) + { + if (root->left) + { + root->stack = stack; + stack = root; + root = root->left; + } + else + { + for (;;) + { + if (*top) + *bot = (*bot)->fts_link = root; + else + *bot = *top = root; + if (root->right) + { + root = root->right; + break; + } + if (!(root = stack)) + { + (*bot)->fts_link = 0; + return; + } + stack = stack->stack; + } + } + } +} + +/* + * set directory when curdir is lost in space + */ + +static int +setdir(register char* home, register char* path) +{ + register int cdrv; + + if (path[0] == '/') + cdrv = pathcd(path, NiL); + else + { + /* + * note that path and home are in the same buffer + */ + + path[-1] = '/'; + cdrv = pathcd(home, NiL); + path[-1] = 0; + } + if (cdrv < 0) + pathcd(home, NiL); + return cdrv; +} + +/* + * set to parent dir + */ + +static int +setpdir(register char* home, register char* path, register char* base) +{ + register int c; + register int cdrv; + + if (base > path) + { + c = base[0]; + base[0] = 0; + cdrv = setdir(home, path); + base[0] = c; + } + else + cdrv = pathcd(home, NiL); + return cdrv; +} + +/* + * pop a set of directories + */ +static int +popdirs(FTS* fts) +{ + register FTSENT*f; + register char* s; + register char* e; +#ifndef verify + register int verify; +#endif + struct stat sb; + char buf[PATH_MAX]; + + if (!(f = fts->curdir) || f->fts_level < 0) + return -1; + e = buf + sizeof(buf) - 4; +#ifndef verify + verify = 0; +#endif + while (fts->nd > 0) + { + for (s = buf; s < e && fts->nd > 0; fts->nd--) + { + if (fts->pwd) + { +#ifndef verify + verify |= fts->pwd->symlink; +#endif + fts->pwd = fts->pwd->pwd; + } + *s++ = '.'; + *s++ = '.'; + *s++ = '/'; + } + *s = 0; + if (chdir(buf)) + return -1; + } + return (verify && (stat(".", &sb) < 0 || !SAME(&sb, f->fts_statp))) ? -1 : 0; +} + +/* + * initialize st from path and fts_info from st + */ + +static int +info(FTS* fts, register FTSENT* f, const char* path, struct stat* sp, int flags) +{ + if (path) + { +#ifdef S_ISLNK + if (!f->symlink && (ISTYPE(f, DT_UNKNOWN) || ISTYPE(f, DT_LNK))) + { + if (lstat(path, sp) < 0) + goto bad; + } + else +#endif + if (stat(path, sp) < 0) + goto bad; + } +#ifdef S_ISLNK + again: +#endif + if (S_ISDIR(sp->st_mode)) + { + if ((flags & FTS_NOSTAT) && !fts->fs3d) + { + f->fts_parent->nlink--; +#ifdef D_TYPE + if ((f->nlink = sp->st_nlink) < 2) + { + f->must = 2; + f->nlink = 2; + } + else + f->must = 0; +#else + if ((f->nlink = sp->st_nlink) >= 2) + f->must = 1; + else + f->must = 2; +#endif + } + else + f->must = 2; + TYPE(f, DT_DIR); + f->fts_info = FTS_D; + } +#ifdef S_ISLNK + else if (S_ISLNK((sp)->st_mode)) + { + struct stat sb; + + f->symlink = 1; + if (flags & FTS_PHYSICAL) + { + TYPE(f, DT_LNK); + f->fts_info = FTS_SL; + } + else if (stat(path, &sb) >= 0) + { + *sp = sb; + flags = FTS_PHYSICAL; + goto again; + } + else + { + TYPE(f, DT_LNK); + f->fts_info = FTS_SLNONE; + } + } +#endif + else + { + TYPE(f, DT_REG); + f->fts_info = FTS_F; + } + return 0; + bad: + TYPE(f, DT_UNKNOWN); + f->fts_info = FTS_NS; + return -1; +} + +/* + * get top list of elements to process + * ordering delayed until first fts_read() + * to give caller a chance to set fts->handle + */ + +static FTSENT* +toplist(FTS* fts, register char* const* pathnames) +{ + register char* path; + register FTSENT* f; + register FTSENT* top; + register FTSENT* bot; + int physical; + int metaphysical; + char* s; + struct stat st; + + if (fts->flags & FTS_NOSEEDOTDIR) + fts->flags &= ~FTS_SEEDOTDIR; + physical = (fts->flags & FTS_PHYSICAL); + metaphysical = (fts->flags & (FTS_META|FTS_PHYSICAL)) == (FTS_META|FTS_PHYSICAL); + top = bot = 0; + while (path = *pathnames++) + { + /* + * make elements + */ + + if (!(f = node(fts, fts->parent, path, strlen(path)))) + break; + path = f->fts_name; + if (!physical) + f->fts_namelen = (fts->flags & FTS_SEEDOTDIR) ? strlen(path) : (pathcanon(path, strlen(path) + 1, 0) - path); + else if (*path != '.') + { + f->fts_namelen = strlen(path); + fts->flags |= FTS_SEEDOTDIR; + } + else + { + if (fts->flags & FTS_NOSEEDOTDIR) + { + fts->flags &= ~FTS_SEEDOTDIR; + s = path; + while (*s++ == '.' && *s++ == '/') + { + while (*s == '/') + s++; + if (!*s) + break; + path = f->fts_name; + while (*path++ = *s++); + path = f->fts_name; + } + } + else + fts->flags |= FTS_SEEDOTDIR; + for (s = path + strlen(path); s > path && *(s - 1) == '/'; s--); + *s = 0; + f->fts_namelen = s - path; + } +#if __OBSOLETE__ < 20140101 + f->_fts_namelen = (unsigned short)f->fts_namelen; +#endif + if (!*path) + { + errno = ENOENT; + f->fts_info = FTS_NS; + } + else + info(fts, f, path, f->fts_statp, fts->flags); +#ifdef S_ISLNK + + /* + * don't let any standards committee get + * away with calling your idea a hack + */ + + if (metaphysical && f->fts_info == FTS_SL) + { + if (stat(path, &st) >= 0) + { + *f->fts_statp = st; + info(fts, f, NiL, f->fts_statp, 0); + } + else + f->fts_info = FTS_SLNONE; + } +#endif + if (bot) + { + bot->fts_link = f; + bot = f; + } + else + top = bot = f; + } + return top; +} + +/* + * order fts->todo if fts->comparf != 0 + */ + +static void +order(FTS* fts) +{ + register FTSENT* f; + register FTSENT* root; + FTSENT* top; + FTSENT* bot; + + top = bot = root = 0; + for (f = fts->todo; f; f = f->fts_link) + root = search(f, root, fts->comparf, 1); + getlist(&top, &bot, root); + fts->todo = top; +} + +/* + * resize the path buffer + * note that free() is not used because we may need to chdir(fts->home) + * if there isn't enough space to continue + */ + +static int +resize(register FTS* fts, size_t inc) +{ + register char* old; + register char* newp; + register size_t n_old; + + /* + * add space for "/." used in testing FTS_DNX + */ + + n_old = fts->homesize; + fts->homesize = ((fts->homesize + inc + 4) / PATH_MAX + 1) * PATH_MAX; + if (!(newp = newof(0, char, fts->homesize, 0))) + { + fts->fts_errno = errno; + fts->state = FTS_error; + return -1; + } + old = fts->home; + fts->home = newp; + memcpy(newp, old, n_old); + if (fts->endbuf) + fts->endbuf = newp + fts->homesize - 4; + if (fts->path) + fts->path = newp + (fts->path - old); + if (fts->base) + fts->base = newp + (fts->base - old); + free(old); + return 0; +} + +/* + * open a new fts stream on pathnames + */ + +FTS* +fts_open(char* const* pathnames, int flags, int (*comparf)(FTSENT* const*, FTSENT* const*)) +{ + register FTS* fts; + + if (!(fts = newof(0, FTS, 1, sizeof(FTSENT)))) + return 0; + fts->flags = flags; + fts->cd = (flags & FTS_NOCHDIR) ? 1 : -1; + fts->comparf = comparf; + fts->fs3d = fs3d(FS3D_TEST); + + /* + * set up the path work buffer + */ + + fts->homesize = 2 * PATH_MAX; + for (;;) + { + if (!(fts->home = newof(fts->home, char, fts->homesize, 0))) + { + free(fts); + return 0; + } + if (fts->cd > 0 || getcwd(fts->home, fts->homesize)) + break; + if (errno == ERANGE) + fts->homesize += PATH_MAX; + else + fts->cd = 1; + } + fts->endbuf = fts->home + fts->homesize - 4; + + /* + * initialize the tippity-top + */ + + fts->parent = (FTSENT*)(fts + 1); + fts->parent->fts_info = FTS_D; + memcpy(fts->parent->fts_accpath = fts->parent->fts_path = fts->parent->fts_name = fts->parent->name, ".", 2); + fts->parent->fts_level = -1; +#if __OBSOLETE__ < 20140101 + fts->parent->_fts_level = (short)fts->parent->fts_level; +#endif + fts->parent->fts_statp = &fts->parent->statb; + fts->parent->must = 2; + fts->parent->type = DT_UNKNOWN; + fts->path = fts->home + strlen(fts->home) + 1; + + /* + * make the list of top elements + */ + + if (!pathnames || (flags & FTS_ONEPATH) || !*pathnames) + { + char* v[2]; + + v[0] = pathnames && (flags & FTS_ONEPATH) ? (char*)pathnames : "."; + v[1] = 0; + fts->todo = toplist(fts, v); + } + else + fts->todo = toplist(fts, pathnames); +#if _HUH_1997_01_07 + if (!fts->todo || fts->todo->fts_info == FTS_NS && !fts->todo->fts_link) +#else + if (!fts->todo) +#endif + { + fts_close(fts); + return 0; + } + return fts; +} + +/* + * return the next FTS entry + */ + +FTSENT* +fts_read(register FTS* fts) +{ + register char* s; + register int n; + register FTSENT* f; + struct dirent* d; + size_t i; + FTSENT* t; + Notify_t* p; +#ifdef verify + struct stat sb; +#endif + + for (;;) + switch (fts->state) + { + + case FTS_top_return: + + f = fts->todo; + t = 0; + while (f) + if (f->status == FTS_SKIP) + { + if (t) + { + t->fts_link = f->fts_link; + drop(fts, f); + f = t->fts_link; + } + else + { + fts->todo = f->fts_link; + drop(fts, f); + f = fts->todo; + } + } + else + { + t = f; + f = f->fts_link; + } + /*FALLTHROUGH*/ + + case 0: + + if (!fts->state && fts->comparf) + order(fts); + if (!(f = fts->todo)) + return 0; + /*FALLTHROUGH*/ + + case FTS_todo: + + /* + * process the top object on the stack + */ + + fts->root = fts->top = fts->bot = 0; + + /* + * initialize the top level + */ + + if (f->fts_level == 0) + { + fts->parent->fts_number = f->fts_number; + fts->parent->fts_pointer = f->fts_pointer; + fts->parent->fts_statp = f->fts_statp; + fts->parent->statb = *f->fts_statp; + f->fts_parent = fts->parent; + fts->diroot = 0; + if (fts->cd == 0) + pathcd(fts->home, NiL); + else if (fts->cd < 0) + fts->cd = 0; + fts->pwd = f->fts_parent; + fts->curdir = fts->cd ? 0 : f->fts_parent; + *(fts->base = fts->path) = 0; + } + + /* + * chdir to parent if asked for + */ + + if (fts->cd < 0) + { + fts->cd = setdir(fts->home, fts->path); + fts->pwd = f->fts_parent; + fts->curdir = fts->cd ? 0 : f->fts_parent; + } + + /* + * add object's name to the path + */ + + if ((fts->baselen = f->fts_namelen) >= (fts->endbuf - fts->base) && resize(fts, fts->baselen)) + return 0; + memcpy(fts->base, f->name, fts->baselen + 1); + fts->name = fts->cd ? fts->path : fts->base; + /*FALLTHROUGH*/ + + case FTS_preorder: + + /* + * check for cycle and open dir + */ + + if (f->fts_info == FTS_D) + { + if ((fts->diroot = search(f, fts->diroot, statcmp, 0)) != f || f->fts_level > 0 && (t = f) && statcmp(&t, &f->fts_parent) == 0) + { + f->fts_info = FTS_DC; + f->fts_cycle = fts->diroot; + } + else if (!(fts->flags & FTS_TOP) && (!(fts->flags & FTS_XDEV) || f->statb.st_dev == f->fts_parent->statb.st_dev)) + { + /* + * buffer is known to be large enough here! + */ + + if (fts->base[fts->baselen - 1] != '/') + memcpy(fts->base + fts->baselen, "/.", 3); + if (!(fts->dir = opendir(fts->name))) + f->fts_info = FTS_DNX; + fts->base[fts->baselen] = 0; + if (!fts->dir && !(fts->dir = opendir(fts->name))) + f->fts_info = FTS_DNR; + } + } + f->nd = f->fts_info & ~FTS_DNX; + if (f->nd || !(fts->flags & FTS_NOPREORDER)) + { + fts->current = f; + fts->link = f->fts_link; + f->fts_link = 0; + f->fts_path = PATH(fts, fts->path, f->fts_level); + f->fts_pathlen = (fts->base - f->fts_path) + fts->baselen; + f->fts_accpath = ACCESS(fts, f); + fts->state = FTS_preorder_return; + goto note; + } + /*FALLTHROUGH*/ + + case FTS_preorder_resume: + + /* + * prune + */ + + if (!fts->dir || f->nd || f->status == FTS_SKIP) + { + if (fts->dir) + { + closedir(fts->dir); + fts->dir = 0; + } + fts->state = FTS_popstack; + continue; + } + + /* + * FTS_D or FTS_DNX, about to read children + */ + + if (fts->cd == 0) + { + if ((fts->cd = chdir(fts->name)) < 0) + pathcd(fts->home, NiL); + else if (fts->pwd != f) + { + f->pwd = fts->pwd; + fts->pwd = f; + } + fts->curdir = fts->cd < 0 ? 0 : f; + } + fts->nostat = fts->children > 1 || f->fts_info == FTS_DNX; + fts->cpname = fts->cd && !fts->nostat || !fts->children && !fts->comparf; + fts->dotdot = 0; + fts->endbase = fts->base + fts->baselen; + if (fts->endbase[-1] != '/') + *fts->endbase++ = '/'; + fts->current = f; + /*FALLTHROUGH*/ + + case FTS_readdir: + + while (d = readdir(fts->dir)) + { + s = d->d_name; + if (s[0] == '.') + { + if (s[1] == 0) + { + fts->current->nlink--; + if (!(fts->flags & FTS_SEEDOT)) + continue; + n = 1; + } + else if (s[1] == '.' && s[2] == 0) + { + fts->current->nlink--; + if (fts->current->must == 1) + fts->current->must = 0; + if (!(fts->flags & FTS_SEEDOT)) + continue; + n = 2; + } + else + n = 0; + } + else + n = 0; + + /* + * make a new entry + */ + + i = D_NAMLEN(d); + if (!(f = node(fts, fts->current, s, i))) + return 0; + TYPE(f, D_TYPE(d)); + + /* + * check for space + */ + + if (i >= fts->endbuf - fts->endbase) + { + if (resize(fts, i)) + return 0; + fts->endbase = fts->base + fts->baselen; + if (fts->endbase[-1] != '/') + fts->endbase++; + } + if (fts->cpname) + { + memcpy(fts->endbase, s, i + 1); + if (fts->cd) + s = fts->path; + } + if (n) + { + /* + * don't recurse on . and .. + */ + + if (n == 1) + f->fts_statp = fts->current->fts_statp; + else + { + if (f->fts_info != FTS_NS) + fts->dotdot = f; + if (fts->current->fts_parent->fts_level < 0) + { + f->fts_statp = &fts->current->fts_parent->statb; + info(fts, f, s, f->fts_statp, 0); + } + else + f->fts_statp = fts->current->fts_parent->fts_statp; + } + f->fts_info = FTS_DOT; + } + else if ((fts->nostat || SKIP(fts, f)) && (f->fts_info = FTS_NSOK) || info(fts, f, s, &f->statb, fts->flags)) + f->statb.st_ino = D_FILENO(d); + if (fts->comparf) + fts->root = search(f, fts->root, fts->comparf, 1); + else if (fts->children || f->fts_info == FTS_D || f->fts_info == FTS_SL) + { + if (fts->top) + fts->bot = fts->bot->fts_link = f; + else + fts->top = fts->bot = f; + } + else + { + /* + * terminal node + */ + + f->fts_path = PATH(fts, fts->path, 1); + f->fts_pathlen = fts->endbase - f->fts_path + f->fts_namelen; + f->fts_accpath = ACCESS(fts, f); + fts->previous = fts->current; + fts->current = f; + fts->state = FTS_terminal; + goto note; + } + } + + /* + * done with the directory + */ + + closedir(fts->dir); + fts->dir = 0; + if (fts->root) + getlist(&fts->top, &fts->bot, fts->root); + if (fts->children) + { + /* + * try moving back to parent dir + */ + + fts->base[fts->baselen] = 0; + if (fts->cd <= 0) + { + f = fts->current->fts_parent; + if (fts->cd < 0 + || f != fts->curdir + || !fts->dotdot + || !SAME(f->fts_statp, fts->dotdot->fts_statp) + || fts->pwd && fts->pwd->symlink + || (fts->cd = chdir("..")) < 0 +#ifdef verify + || stat(".", &sb) < 0 + || !SAME(&sb, fts->dotdot->fts_statp) +#endif + ) + fts->cd = setpdir(fts->home, fts->path, fts->base); + if (fts->pwd) + fts->pwd = fts->pwd->pwd; + fts->curdir = fts->cd ? 0 : f; + } + f = fts->current; + fts->link = f->fts_link; + f->fts_link = fts->top; + f->fts_path = PATH(fts, fts->path, f->fts_level); + f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen; + f->fts_accpath = ACCESS(fts, f); + fts->state = FTS_children_return; + goto note; + } + /*FALLTHROUGH*/ + + case FTS_children_resume: + + fts->base[fts->baselen] = 0; + if (fts->top) + { + fts->bot->fts_link = fts->todo; + fts->todo = fts->top; + fts->top = 0; + } + /*FALLTHROUGH*/ + + case FTS_popstack: + + /* + * pop objects completely processed + */ + + fts->nd = 0; + f = fts->current; + /*FALLTHROUGH*/ + + case FTS_popstack_resume: + + while (fts->todo && f == fts->todo) + { + t = f->fts_parent; + if ((f->fts_info & FTS_DP) == FTS_D) + { + /* + * delete from <dev,ino> tree + */ + + if (f != fts->diroot) + fts->diroot = search(f, fts->diroot, statcmp, 0); + fts->diroot = deleteroot(fts->diroot); + if (f == fts->curdir) + { + fts->nd++; + fts->curdir = t; + } + + /* + * perform post-order processing + */ + + if (!(fts->flags & FTS_NOPOSTORDER) && + f->status != FTS_SKIP && + f->status != FTS_NOPOSTORDER) + { + /* + * move to parent dir + */ + + if (fts->nd > 0) + fts->cd = popdirs(fts); + if (fts->cd < 0) + fts->cd = setpdir(fts->home, fts->path, fts->base); + fts->curdir = fts->cd ? 0 : t; + f->fts_info = FTS_DP; + f->fts_path = PATH(fts, fts->path, f->fts_level); + f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen; + f->fts_accpath = ACCESS(fts, f); + + /* + * re-stat to update nlink/times + */ + + stat(f->fts_accpath, f->fts_statp); + fts->link = f->fts_link; + f->fts_link = 0; + fts->state = FTS_popstack_return; + goto note; + } + } + + /* + * reset base + */ + + if (fts->base > fts->path + t->fts_namelen) + fts->base--; + *fts->base = 0; + fts->base -= t->fts_namelen; + + /* + * try again or delete from top of stack + */ + + if (f->status == FTS_AGAIN) + { + f->fts_info = FTS_D; + f->status = 0; + } + else + { + fts->todo = fts->todo->fts_link; + drop(fts, f); + } + f = t; + } + + /* + * reset current directory + */ + + if (fts->nd > 0 && popdirs(fts) < 0) + { + pathcd(fts->home, NiL); + fts->curdir = 0; + fts->cd = -1; + } + if (fts->todo) + { + if (*fts->base) + fts->base += f->fts_namelen; + if (*(fts->base - 1) != '/') + *fts->base++ = '/'; + *fts->base = 0; + f = fts->todo; + fts->state = FTS_todo; + continue; + } + return 0; + + case FTS_children_return: + + f = fts->current; + f->fts_link = fts->link; + + /* + * chdir down again + */ + + i = f->fts_info != FTS_DNX; + n = f->status == FTS_SKIP; + if (!n && fts->cd == 0) + { + if ((fts->cd = chdir(fts->base)) < 0) + pathcd(fts->home, NiL); + else if (fts->pwd != f) + { + f->pwd = fts->pwd; + fts->pwd = f; + } + fts->curdir = fts->cd ? 0 : f; + } + + /* + * prune + */ + + if (fts->base[fts->baselen - 1] != '/') + fts->base[fts->baselen] = '/'; + for (fts->bot = 0, f = fts->top; f; ) + if (n || f->status == FTS_SKIP) + { + if (fts->bot) + fts->bot->fts_link = f->fts_link; + else + fts->top = f->fts_link; + drop(fts, f); + f = fts->bot ? fts->bot->fts_link : fts->top; + } + else + { + if (fts->children > 1 && i) + { + if (f->status == FTS_STAT) + info(fts, f, NiL, f->fts_statp, 0); + else if (f->fts_info == FTS_NSOK && !SKIP(fts, f)) + { + s = f->fts_name; + if (fts->cd) + { + memcpy(fts->endbase, s, f->fts_namelen + 1); + s = fts->path; + } + info(fts, f, s, f->fts_statp, fts->flags); + } + } + fts->bot = f; + f = f->fts_link; + } + fts->children = 0; + fts->state = FTS_children_resume; + continue; + + case FTS_popstack_return: + + f = fts->todo; + f->fts_link = fts->link; + f->fts_info = f->status == FTS_AGAIN ? FTS_DP : 0; + fts->state = FTS_popstack_resume; + continue; + + case FTS_preorder_return: + + f = fts->current; + f->fts_link = fts->link; + + /* + * follow symlink if asked to + */ + + if (f->status == FTS_FOLLOW) + { + f->status = 0; + if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK) + { + info(fts, f, f->fts_accpath, f->fts_statp, 0); + if (f->fts_info != FTS_SL) + { + fts->state = FTS_preorder; + continue; + } + } + } + + /* + * about to prune this f and already at home + */ + + if (fts->cd == 0 && f->fts_level == 0 && f->nd) + fts->cd = -1; + fts->state = FTS_preorder_resume; + continue; + + case FTS_terminal: + + f = fts->current; + if (f->status == FTS_FOLLOW) + { + f->status = 0; + if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK) + { + info(fts, f, f->fts_accpath, f->fts_statp, 0); + if (f->symlink && f->fts_info != FTS_SL) + { + if (!(f->fts_link = fts->top)) + fts->bot = f; + fts->top = f; + fts->current = fts->previous; + fts->state = FTS_readdir; + continue; + } + } + } + f = f->fts_parent; + drop(fts, fts->current); + fts->current = f; + fts->state = FTS_readdir; + continue; + + case FTS_error: + + return 0; + + default: + + fts->fts_errno = EINVAL; + fts->state = FTS_error; + return 0; + + } + note: +#if __OBSOLETE__ < 20140101 + f->_fts_pathlen = (unsigned short)f->fts_pathlen; +#endif + for (p = notify; p; p = p->next) + if ((n = (*p->notifyf)(fts, f, p->context)) > 0) + break; + else if (n < 0) + { + fts->fts_errno = EINVAL; + fts->state = FTS_error; + return 0; + } + return f; +} + +/* + * set stream or entry flags + */ + +int +fts_set(register FTS* fts, register FTSENT* f, int status) +{ + if (fts || !f || f->fts->current != f) + return -1; + switch (status) + { + case FTS_AGAIN: + break; + case FTS_FOLLOW: + if (!(f->fts_info & FTS_SL)) + return -1; + break; + case FTS_NOPOSTORDER: + break; + case FTS_SKIP: + if ((f->fts_info & (FTS_D|FTS_P)) != FTS_D) + return -1; + break; + default: + return -1; + } + f->status = status; + return 0; +} + +/* + * return the list of child entries + */ + +FTSENT* +fts_children(register FTS* fts, int flags) +{ + register FTSENT* f; + + switch (fts->state) + { + + case 0: + + if (fts->comparf) + order(fts); + fts->state = FTS_top_return; + return fts->todo; + + case FTS_preorder_return: + + fts->children = ((flags | fts->flags) & FTS_NOSTAT) ? 2 : 1; + if (f = fts_read(fts)) + f = f->fts_link; + return f; + + } + return 0; +} + +/* + * return default (FTS_LOGICAL|FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR) flags + * conditioned by astconf() + */ + +int +fts_flags(void) +{ + register char* s; + + s = astconf("PATH_RESOLVE", NiL, NiL); + if (streq(s, "logical")) + return FTS_LOGICAL; + if (streq(s, "physical")) + return FTS_PHYSICAL|FTS_SEEDOTDIR; + return FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR; +} + +/* + * return 1 if ent is mounted on a local filesystem + */ + +int +fts_local(FTSENT* ent) +{ +#ifdef ST_LOCAL + struct statvfs fs; + + return statvfs(ent->fts_path, &fs) || (fs.f_flag & ST_LOCAL); +#else + return !strgrpmatch(fmtfs(ent->fts_statp), "([an]fs|samb)", NiL, 0, STR_LEFT|STR_ICASE); +#endif +} + +/* + * close an open fts stream + */ + +int +fts_close(register FTS* fts) +{ + register FTSENT* f; + register FTSENT* x; + + if (fts->dir) + closedir(fts->dir); + if (fts->cd == 0) + pathcd(fts->home, NiL); + free(fts->home); + if (fts->state == FTS_children_return) + fts->current->fts_link = fts->link; + if (fts->top) + { + fts->bot->fts_link = fts->todo; + fts->todo = fts->top; + } + for (f = fts->todo; f; f = x) + { + x = f->fts_link; + free(f); + } + for (f = fts->free; f; f = x) + { + x = f->fts_link; + free(f); + } + free(fts); + return 0; +} + +/* + * register function to be called for each fts_read() entry + * context==0 => unregister notifyf + */ + +int +fts_notify(Notify_f notifyf, void* context) +{ + register Notify_t* np; + register Notify_t* pp; + + if (context) + { + if (!(np = newof(0, Notify_t, 1, 0))) + return -1; + np->notifyf = notifyf; + np->context = context; + np->next = notify; + notify = np; + } + else + { + for (np = notify, pp = 0; np; pp = np, np = np->next) + if (np->notifyf == notifyf) + { + if (pp) + pp->next = np->next; + else + notify = np->next; + free(np); + return 0; + } + return -1; + } + return 0; +} diff --git a/src/lib/libast/misc/ftwalk.c b/src/lib/libast/misc/ftwalk.c new file mode 100644 index 0000000..30c293f --- /dev/null +++ b/src/lib/libast/misc/ftwalk.c @@ -0,0 +1,156 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * ftwalk on top of fts + */ + +#include <ast.h> +#include <ftwalk.h> + +static struct +{ + int (*comparf)(Ftw_t*, Ftw_t*); +} state; + +/* + * why does fts take FTSENT** instead of FTSENT* + */ + +static int +ftscompare(Ftw_t* const* pf1, Ftw_t* const* pf2) +{ + return (*state.comparf)(*pf1, *pf2); +} + +/* + * the real thing -- well it used to be + */ + +int +ftwalk(const char* path, int (*userf)(Ftw_t*), int flags, int (*comparf)(Ftw_t*, Ftw_t*)) +{ + register FTS* f; + register FTSENT* e; + register int children; + register int rv; + int oi; + int ns; + int os; + int nd; + FTSENT* x; + FTSENT* dd[2]; + + flags ^= FTS_ONEPATH; + if (flags & FTW_TWICE) + flags &= ~(FTS_NOPREORDER|FTS_NOPOSTORDER); + else if (flags & FTW_POST) + flags |= FTS_NOPREORDER; + else + flags |= FTS_NOPOSTORDER; + if (children = flags & FTW_CHILDREN) + flags |= FTS_SEEDOT; + state.comparf = comparf; + if (!(f = fts_open((char* const*)path, flags, comparf ? ftscompare : 0))) + { + if (!path || !(flags & FTS_ONEPATH) && !(path = (const char*)(*((char**)path)))) + return -1; + ns = strlen(path) + 1; + if (!(e = newof(0, FTSENT, 1, ns))) + return -1; + e->fts_accpath = e->fts_name = e->fts_path = strcpy((char*)(e + 1), path); + e->fts_namelen = e->fts_pathlen = ns; + e->fts_info = FTS_NS; + e->parent = e; + e->parent->link = e; + rv = (*userf)((Ftw_t*)e); + free(e); + return rv; + } + rv = 0; + if (children && (e = fts_children(f, 0))) + { + nd = 0; + for (x = e; x; x = x->link) + if (x->info & FTS_DD) + { + x->statb = *x->fts_statp; + x->info &= ~FTS_DD; + dd[nd++] = x; + if (nd >= elementsof(dd)) + break; + } + e->parent->link = e; + rv = (*userf)((Ftw_t*)e->parent); + e->parent->link = 0; + while (nd > 0) + dd[--nd]->info |= FTS_DD; + for (x = e; x; x = x->link) + if (!(x->info & FTS_D)) + x->status = FTS_SKIP; + } + while (!rv && (e = fts_read(f))) + { + oi = e->info; + os = e->status; + ns = e->status = e->path == e->fts_accpath ? FTW_PATH : FTW_NAME; + nd = 0; + switch (e->info) + { + case FTS_D: + case FTS_DNX: + if (children) + for (x = fts_children(f, 0); x; x = x->link) + if (x->info & FTS_DD) + { + x->statb = *x->fts_statp; + x->info &= ~FTS_DD; + dd[nd++] = x; + if (nd >= elementsof(dd)) + break; + } + break; + case FTS_DOT: + continue; + case FTS_ERR: + e->info = FTS_NS; + break; + case FTS_NSOK: + e->info = FTS_NSOK; + break; + case FTS_SLNONE: + e->info = FTS_SL; + break; + } + rv = (*userf)((Ftw_t*)e); + e->info = oi; + if (e->status == ns) + e->status = os; + while (nd > 0) + dd[--nd]->info |= FTS_DD; + } + fts_close(f); + return rv; +} diff --git a/src/lib/libast/misc/ftwflags.c b/src/lib/libast/misc/ftwflags.c new file mode 100644 index 0000000..179a366 --- /dev/null +++ b/src/lib/libast/misc/ftwflags.c @@ -0,0 +1,35 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * return default FTW_* flags conditioned by astconf() + */ + +#include <ast.h> +#include <ftwalk.h> + +int +ftwflags(void) +{ + return fts_flags(); +} diff --git a/src/lib/libast/misc/getcwd.c b/src/lib/libast/misc/getcwd.c new file mode 100644 index 0000000..c509648 --- /dev/null +++ b/src/lib/libast/misc/getcwd.c @@ -0,0 +1,334 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * pwd library support + */ + +#include <ast.h> + +#if _WINIX + +NoN(getcwd) + +#else + +#include "FEATURE/syscall" + +#if defined(SYSGETCWD) + +#include <error.h> + +#define ERROR(e) { errno = e; return 0; } + +char* +getcwd(char* buf, size_t len) +{ + size_t n; + size_t r; + int oerrno; + + if (buf) + return SYSGETCWD(buf, len) < 0 ? 0 : buf; + oerrno = errno; + n = PATH_MAX; + for (;;) + { + if (!(buf = newof(buf, char, n, 0))) + ERROR(ENOMEM); + if (SYSGETCWD(buf, n) >= 0) + { + if ((r = strlen(buf) + len + 1) != n && !(buf = newof(buf, char, r, 0))) + ERROR(ENOMEM); + break; + } + if (errno != ERANGE) + { + free(buf); + return 0; + } + n += PATH_MAX / 4; + } + errno = oerrno; + return buf; +} + +#else + +#include <ast_dir.h> +#include <error.h> +#include <fs3d.h> + +#ifndef ERANGE +#define ERANGE E2BIG +#endif + +#define ERROR(e) { errno = e; goto error; } + +struct dirlist /* long path chdir(2) component */ +{ + struct dirlist* next; /* next component */ + int index; /* index from end of buf */ +}; + +/* + * pop long dir component chdir stack + */ + +static int +popdir(register struct dirlist* d, register char* end) +{ + register struct dirlist* dp; + int v; + + v = 0; + while (dp = d) + { + d = d->next; + if (!v) + { + if (d) *(end - d->index - 1) = 0; + v = chdir(end - dp->index); + if (d) *(end - d->index - 1) = '/'; + } + free(dp); + } + return v; +} + +/* + * push long dir component onto stack + */ + +static struct dirlist* +pushdir(register struct dirlist* d, char* dots, char* path, char* end) +{ + register struct dirlist* p; + + if (!(p = newof(0, struct dirlist, 1, 0)) || chdir(dots)) + { + if (p) free(p); + if (d) popdir(d, end); + return 0; + } + p->index = end - path; + p->next = d; + return p; +} + +/* + * return a pointer to the absolute path name of . + * this path name may be longer than PATH_MAX + * + * a few environment variables are checked before the search algorithm + * return value is placed in buf of len chars + * if buf is 0 then space is allocated via malloc() with + * len extra chars after the path name + * 0 is returned on error with errno set as appropriate + */ + +char* +getcwd(char* buf, size_t len) +{ + register char* d; + register char* p; + register char* s; + DIR* dirp = 0; + int n; + int x; + size_t namlen; + ssize_t extra = -1; + struct dirent* entry; + struct dirlist* dirstk = 0; + struct stat* cur; + struct stat* par; + struct stat* tmp; + struct stat curst; + struct stat parst; + struct stat tstst; + char dots[PATH_MAX]; + + static struct + { + char* name; + char* path; + dev_t dev; + ino_t ino; + } env[] = + { + { /*previous*/0 }, + { "PWD" }, + { "HOME" }, + }; + + if (buf && !len) ERROR(EINVAL); + if (fs3d(FS3D_TEST) && (namlen = mount(".", dots, FS3D_GET|FS3D_VIEW|FS3D_SIZE(sizeof(dots)), NiL)) > 1 && namlen < sizeof(dots)) + { + p = dots; + easy: + namlen++; + if (buf) + { + if (len < namlen) ERROR(ERANGE); + } + else if (!(buf = newof(0, char, namlen, len))) ERROR(ENOMEM); + return (char*)memcpy(buf, p, namlen); + } + cur = &curst; + par = &parst; + if (stat(".", par)) ERROR(errno); + for (n = 0; n < elementsof(env); n++) + { + if ((env[n].name && (p = getenv(env[n].name)) || (p = env[n].path)) && *p == '/' && !stat(p, cur)) + { + env[n].path = p; + env[n].dev = cur->st_dev; + env[n].ino = cur->st_ino; + if (cur->st_ino == par->st_ino && cur->st_dev == par->st_dev) + { + namlen = strlen(p); + goto easy; + } + } + } + if (!buf) + { + extra = len; + len = PATH_MAX; + if (!(buf = newof(0, char, len, extra))) ERROR(ENOMEM); + } + d = dots; + p = buf + len - 1; + *p = 0; + n = elementsof(env); + for (;;) + { + tmp = cur; + cur = par; + par = tmp; + if ((d - dots) > (PATH_MAX - 4)) + { + if (!(dirstk = pushdir(dirstk, dots, p, buf + len - 1))) ERROR(ERANGE); + d = dots; + } + *d++ = '.'; + *d++ = '.'; + *d = 0; + if (!(dirp = opendir(dots))) ERROR(errno); +#if !_dir_ok || _mem_dd_fd_DIR + if (fstat(dirp->dd_fd, par)) ERROR(errno); +#else + if (stat(dots, par)) ERROR(errno); +#endif + *d++ = '/'; + if (par->st_dev == cur->st_dev) + { + if (par->st_ino == cur->st_ino) + { + closedir(dirp); + *--p = '/'; + pop: + if (p != buf) + { + d = buf; + while (*d++ = *p++); + len = d - buf; + if (extra >= 0 && !(buf = newof(buf, char, len, extra))) ERROR(ENOMEM); + } + if (dirstk && popdir(dirstk, buf + len - 1)) + { + dirstk = 0; + ERROR(errno); + } + if (env[0].path) + free(env[0].path); + env[0].path = strdup(buf); + return buf; + } +#ifdef D_FILENO + while (entry = readdir(dirp)) + if (D_FILENO(entry) == cur->st_ino) + { + namlen = D_NAMLEN(entry); + goto found; + } +#endif + + /* + * this fallthrough handles logical naming + */ + + rewinddir(dirp); + } + do + { + if (!(entry = readdir(dirp))) ERROR(ENOENT); + namlen = D_NAMLEN(entry); + if ((d - dots) > (PATH_MAX - 1 - namlen)) + { + *d = 0; + if (namlen >= PATH_MAX || !(dirstk = pushdir(dirstk, dots + 3, p, buf + len - 1))) ERROR(ERANGE); + d = dots + 3; + } + memcpy(d, entry->d_name, namlen + 1); + } while (stat(dots, &tstst) || tstst.st_ino != cur->st_ino || tstst.st_dev != cur->st_dev); + found: + if (*p) *--p = '/'; + while ((p -= namlen) <= (buf + 1)) + { + x = (buf + len - 1) - (p += namlen); + s = buf + len; + if (extra < 0 || !(buf = newof(buf, char, len += PATH_MAX, extra))) ERROR(ERANGE); + p = buf + len; + while (p > buf + len - 1 - x) *--p = *--s; + } + if (n < elementsof(env)) + { + memcpy(p, env[n].path, namlen); + goto pop; + } + memcpy(p, entry->d_name, namlen); + closedir(dirp); + dirp = 0; + for (n = 0; n < elementsof(env); n++) + if (env[n].ino == par->st_ino && env[n].dev == par->st_dev) + { + namlen = strlen(env[n].path); + goto found; + } + } + error: + if (buf) + { + if (dirstk) popdir(dirstk, buf + len - 1); + if (extra >= 0) free(buf); + } + if (dirp) closedir(dirp); + return 0; +} + +#endif + +#endif diff --git a/src/lib/libast/misc/getenv.c b/src/lib/libast/misc/getenv.c new file mode 100644 index 0000000..7e24817 --- /dev/null +++ b/src/lib/libast/misc/getenv.c @@ -0,0 +1,113 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +#if _UWIN && __STDPP__ +__STDPP__directive pragma pp:hide getenv +#endif + +#include "intercepts.h" + +#if _UWIN && __STDPP__ +__STDPP__directive pragma pp:nohide getenv +#endif + +/* + * NOTE: the "intercepts" definition is here instead of astintercept.c because some + * static linkers miss lone references to "intercepts" without "astintercept()" + * ALSO: { 0 } definition required by some dynamic linkers averse to common symbols + * UWIN: no _ast_getenv macro map to maintain ast54 compatibility + */ + +Intercepts_t intercepts +#if _BLD_3d + ; +#else + = { 0 }; +#endif + +#if _UWIN && !defined(getenv) + +#include <windows.h> + +extern char** environ; + +static char* +default_getenv(const char* name) +{ + register char** av; + register const char* cp; + register const char* sp; + register char c0; + register char c1; + + av = environ; + if (!av || !name || !(c0 = *name)) + return 0; + if (!(c1 = *++name)) + c1 = '='; + while (cp = *av++) + { + if (cp[0] != c0 || cp[1] != c1) + continue; + sp = name; + cp++; + while (*sp && *sp++ == *cp++); + if (*(sp-1) != *(cp-1)) + continue; + if (*sp == 0 && *cp == '=') + return (char*)(cp+1); + } + return 0; +} + +#endif + +/* + * get name from the environment + */ + +#if defined(__EXPORT__) && defined(getenv) +#define extern __EXPORT__ +#endif + +extern char* +getenv(const char* name) +{ +#if _UWIN && !defined(getenv) /* for ast54 compatibility */ + HANDLE dll; + + static char* (*posix_getenv)(const char*); + + if (!posix_getenv) + { + if (dll = GetModuleHandle("posix.dll")) + posix_getenv = (char*(*)(const char*))GetProcAddress(dll, "getenv"); + if (!posix_getenv) + posix_getenv = default_getenv; + } + return intercepts.intercept_getenv ? (*intercepts.intercept_getenv)(name) : (*posix_getenv)(name); +#else +#undef getenv + return intercepts.intercept_getenv ? (*intercepts.intercept_getenv)(name) : getenv(name); +#endif +} diff --git a/src/lib/libast/misc/glob.c b/src/lib/libast/misc/glob.c new file mode 100644 index 0000000..f05b40e --- /dev/null +++ b/src/lib/libast/misc/glob.c @@ -0,0 +1,829 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * file name expansion - posix.2 glob with gnu and ast extensions + * + * David Korn + * Glenn Fowler + * AT&T Research + */ + +#include <ast.h> +#include <ls.h> +#include <stak.h> +#include <ast_dir.h> +#include <error.h> +#include <ctype.h> +#include <regex.h> + +#define GLOB_MAGIC 0xaaaa0000 + +#define MATCH_RAW 1 +#define MATCH_MAKE 2 +#define MATCH_META 4 + +#define MATCHPATH(g) (offsetof(globlist_t,gl_path)+(g)->gl_extra) + +typedef int (*GL_error_f)(const char*, int); +typedef void* (*GL_opendir_f)(const char*); +typedef struct dirent* (*GL_readdir_f)(void*); +typedef void (*GL_closedir_f)(void*); +typedef int (*GL_stat_f)(const char*, struct stat*); + +#define _GLOB_PRIVATE_ \ + GL_error_f gl_errfn; \ + int gl_error; \ + char* gl_nextpath; \ + globlist_t* gl_rescan; \ + globlist_t* gl_match; \ + Stak_t* gl_stak; \ + int re_flags; \ + int re_first; \ + regex_t* gl_ignore; \ + regex_t* gl_ignorei; \ + regex_t re_ignore; \ + regex_t re_ignorei; \ + unsigned long gl_starstar; \ + char* gl_opt; \ + char* gl_pat; \ + char* gl_pad[4]; + +#include <glob.h> + +/* + * default gl_diropen + */ + +static void* +gl_diropen(glob_t* gp, const char* path) +{ + return (*gp->gl_opendir)(path); +} + +/* + * default gl_dirnext + */ + +static char* +gl_dirnext(glob_t* gp, void* handle) +{ + struct dirent* dp; + + while (dp = (struct dirent*)(*gp->gl_readdir)(handle)) + { +#ifdef D_TYPE + if (D_TYPE(dp) != DT_UNKNOWN && D_TYPE(dp) != DT_DIR && D_TYPE(dp) != DT_LNK) + gp->gl_status |= GLOB_NOTDIR; +#endif + return dp->d_name; + } + return 0; +} + +/* + * default gl_dirclose + */ + +static void +gl_dirclose(glob_t* gp, void* handle) +{ + (gp->gl_closedir)(handle); +} + +/* + * default gl_type + */ + +static int +gl_type(glob_t* gp, const char* path, int flags) +{ + register int type; + struct stat st; + + if ((flags & GLOB_STARSTAR) ? (*gp->gl_lstat)(path, &st) : (*gp->gl_stat)(path, &st)) + type = 0; + else if (S_ISDIR(st.st_mode)) + type = GLOB_DIR; + else if (!S_ISREG(st.st_mode)) + type = GLOB_DEV; + else if (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) + type = GLOB_EXE; + else + type = GLOB_REG; + return type; +} + +/* + * default gl_attr + */ + +static int +gl_attr(glob_t* gp, const char* path, int flags) +{ + return strchr(astconf("PATH_ATTRIBUTES", path, NiL), 'c') ? GLOB_ICASE : 0; +} + +/* + * default gl_nextdir + */ + +static char* +gl_nextdir(glob_t* gp, char* dir) +{ + if (!(dir = gp->gl_nextpath)) + dir = gp->gl_nextpath = stakcopy(pathbin()); + switch (*gp->gl_nextpath) + { + case 0: + dir = 0; + break; + case ':': + while (*gp->gl_nextpath == ':') + gp->gl_nextpath++; + dir = "."; + break; + default: + while (*gp->gl_nextpath) + if (*gp->gl_nextpath++ == ':') + { + *(gp->gl_nextpath - 1) = 0; + break; + } + break; + } + return dir; +} + +/* + * error intercept + */ + +static int +errorcheck(register glob_t* gp, const char* path) +{ + int r = 1; + + if (gp->gl_errfn) + r = (*gp->gl_errfn)(path, errno); + if (gp->gl_flags & GLOB_ERR) + r = 0; + if (!r) + gp->gl_error = GLOB_ABORTED; + return r; +} + +/* + * remove backslashes + */ + +static void +trim(register char* sp, register char* p1, int* n1, register char* p2, int* n2) +{ + register char* dp = sp; + register int c; + + if (p1) + *n1 = 0; + if (p2) + *n2 = 0; + do + { + if ((c = *sp++) == '\\') + c = *sp++; + if (sp == p1) + { + p1 = 0; + *n1 = sp - dp - 1; + } + if (sp == p2) + { + p2 = 0; + *n2 = sp - dp - 1; + } + } while (*dp++ = c); +} + +static void +addmatch(register glob_t* gp, const char* dir, const char* pat, register const char* rescan, char* endslash, int meta) +{ + register globlist_t* ap; + int offset; + int type; + + stakseek(MATCHPATH(gp)); + if (dir) + { + stakputs(dir); + stakputc(gp->gl_delim); + } + if (endslash) + *endslash = 0; + stakputs(pat); + if (rescan) + { + if ((*gp->gl_type)(gp, stakptr(MATCHPATH(gp)), 0) != GLOB_DIR) + return; + stakputc(gp->gl_delim); + offset = staktell(); + /* if null, reserve room for . */ + if (*rescan) + stakputs(rescan); + else + stakputc(0); + stakputc(0); + rescan = stakptr(offset); + ap = (globlist_t*)stakfreeze(0); + ap->gl_begin = (char*)rescan; + ap->gl_next = gp->gl_rescan; + gp->gl_rescan = ap; + } + else + { + if (!endslash && (gp->gl_flags & GLOB_MARK) && (type = (*gp->gl_type)(gp, stakptr(MATCHPATH(gp)), 0))) + { + if ((gp->gl_flags & GLOB_COMPLETE) && type != GLOB_EXE) + { + stakseek(0); + return; + } + else if (type == GLOB_DIR && (gp->gl_flags & GLOB_MARK)) + stakputc(gp->gl_delim); + } + ap = (globlist_t*)stakfreeze(1); + ap->gl_next = gp->gl_match; + gp->gl_match = ap; + gp->gl_pathc++; + } + ap->gl_flags = MATCH_RAW|meta; + if (gp->gl_flags & GLOB_COMPLETE) + ap->gl_flags |= MATCH_MAKE; +} + +/* + * this routine builds a list of files that match a given pathname + * uses REG_SHELL of <regex> to match each component + * a leading . must match explicitly + */ + +static void +glob_dir(glob_t* gp, globlist_t* ap, int re_flags) +{ + register char* rescan; + register char* prefix; + register char* pat; + register char* name; + register int c; + char* dirname; + void* dirf; + char first; + regex_t* ire; + regex_t* pre; + regex_t rec; + regex_t rei; + int notdir; + int t1; + int t2; + int bracket; + + int anymeta = ap->gl_flags & MATCH_META; + int complete = 0; + int err = 0; + int meta = ((gp->re_flags & REG_ICASE) && *ap->gl_begin != '/') ? MATCH_META : 0; + int quote = 0; + int savequote = 0; + char* restore1 = 0; + char* restore2 = 0; + regex_t* prec = 0; + regex_t* prei = 0; + char* matchdir = 0; + int starstar = 0; + + if (*gp->gl_intr) + { + gp->gl_error = GLOB_INTR; + return; + } + pat = rescan = ap->gl_begin; + prefix = dirname = ap->gl_path + gp->gl_extra; + first = (rescan == prefix); +again: + bracket = 0; + for (;;) + { + switch (c = *rescan++) + { + case 0: + if (meta) + { + rescan = 0; + break; + } + if (quote) + { + trim(ap->gl_begin, rescan, &t1, NiL, NiL); + rescan -= t1; + } + if (!first && !*rescan && *(rescan - 2) == gp->gl_delim) + { + *(rescan - 2) = 0; + c = (*gp->gl_type)(gp, prefix, 0); + *(rescan - 2) = gp->gl_delim; + if (c == GLOB_DIR) + addmatch(gp, NiL, prefix, NiL, rescan - 1, anymeta); + } + else if ((anymeta || !(gp->gl_flags & GLOB_NOCHECK)) && (*gp->gl_type)(gp, prefix, 0)) + addmatch(gp, NiL, prefix, NiL, NiL, anymeta); + return; + case '[': + if (!bracket) + { + bracket = MATCH_META; + if (*rescan == '!' || *rescan == '^') + rescan++; + if (*rescan == ']') + rescan++; + } + continue; + case ']': + meta |= bracket; + continue; + case '(': + if (!(gp->gl_flags & GLOB_AUGMENTED)) + continue; + case '*': + case '?': + meta = MATCH_META; + continue; + case '\\': + if (!(gp->gl_flags & GLOB_NOESCAPE)) + { + quote = 1; + if (*rescan) + rescan++; + } + continue; + default: + if (c == gp->gl_delim) + { + if (meta) + break; + pat = rescan; + bracket = 0; + savequote = quote; + } + continue; + } + break; + } + anymeta |= meta; + if (matchdir) + goto skip; + if (pat == prefix) + { + prefix = 0; + if (!rescan && (gp->gl_flags & GLOB_COMPLETE)) + { + complete = 1; + dirname = 0; + } + else + dirname = "."; + } + else + { + if (pat == prefix + 1) + dirname = "/"; + if (savequote) + { + quote = 0; + trim(ap->gl_begin, pat, &t1, rescan, &t2); + pat -= t1; + if (rescan) + rescan -= t2; + } + *(restore1 = pat - 1) = 0; + } + if (!complete && (gp->gl_flags & GLOB_STARSTAR)) + while (pat[0] == '*' && pat[1] == '*' && (pat[2] == '/' || pat[2]==0)) + { + matchdir = pat; + if (pat[2]) + { + pat += 3; + while (*pat=='/') + pat++; + if (*pat) + continue; + } + rescan = *pat?0:pat; + pat = "*"; + goto skip; + } + if (matchdir) + { + rescan = pat; + goto again; + } +skip: + if (rescan) + *(restore2 = rescan - 1) = 0; + if (rescan && !complete && (gp->gl_flags & GLOB_STARSTAR)) + { + register char* p = rescan; + + while (p[0] == '*' && p[1] == '*' && (p[2] == '/' || p[2]==0)) + { + rescan = p; + if (starstar = (p[2]==0)) + break; + p += 3; + while (*p=='/') + p++; + if (*p==0) + { + starstar = 2; + break; + } + } + } + if (matchdir) + gp->gl_starstar++; + if (gp->gl_opt) + pat = strcpy(gp->gl_opt, pat); + for (;;) + { + if (complete) + { + if (!(dirname = (*gp->gl_nextdir)(gp, dirname))) + break; + prefix = streq(dirname, ".") ? (char*)0 : dirname; + } + if ((!starstar && !gp->gl_starstar || (*gp->gl_type)(gp, dirname, GLOB_STARSTAR) == GLOB_DIR) && (dirf = (*gp->gl_diropen)(gp, dirname))) + { + if (!(gp->re_flags & REG_ICASE) && ((*gp->gl_attr)(gp, dirname, 0) & GLOB_ICASE)) + { + if (!prei) + { + if (err = regcomp(&rei, pat, gp->re_flags|REG_ICASE)) + break; + prei = &rei; + if (gp->re_first) + { + gp->re_first = 0; + gp->re_flags = regstat(prei)->re_flags & ~REG_ICASE; + } + } + pre = prei; + } + else + { + if (!prec) + { + if (err = regcomp(&rec, pat, gp->re_flags)) + break; + prec = &rec; + if (gp->re_first) + { + gp->re_first = 0; + gp->re_flags = regstat(prec)->re_flags; + } + } + pre = prec; + } + if ((ire = gp->gl_ignore) && (gp->re_flags & REG_ICASE)) + { + if (!gp->gl_ignorei) + { + if (regcomp(&gp->re_ignorei, gp->gl_fignore, re_flags|REG_ICASE)) + { + gp->gl_error = GLOB_APPERR; + break; + } + gp->gl_ignorei = &gp->re_ignorei; + } + ire = gp->gl_ignorei; + } + if (restore2) + *restore2 = gp->gl_delim; + while ((name = (*gp->gl_dirnext)(gp, dirf)) && !*gp->gl_intr) + { + if (notdir = (gp->gl_status & GLOB_NOTDIR)) + gp->gl_status &= ~GLOB_NOTDIR; + if (ire && !regexec(ire, name, 0, NiL, 0)) + continue; + if (matchdir && (name[0] != '.' || name[1] && (name[1] != '.' || name[2])) && !notdir) + addmatch(gp, prefix, name, matchdir, NiL, anymeta); + if (!regexec(pre, name, 0, NiL, 0)) + { + if (!rescan || !notdir) + addmatch(gp, prefix, name, rescan, NiL, anymeta); + if (starstar==1 || (starstar==2 && !notdir)) + addmatch(gp, prefix, name, starstar==2?"":NiL, NiL, anymeta); + } + errno = 0; + } + (*gp->gl_dirclose)(gp, dirf); + if (err || errno && !errorcheck(gp, dirname)) + break; + } + else if (!complete && !errorcheck(gp, dirname)) + break; + if (!complete) + break; + if (*gp->gl_intr) + { + gp->gl_error = GLOB_INTR; + break; + } + } + if (restore1) + *restore1 = gp->gl_delim; + if (restore2) + *restore2 = gp->gl_delim; + if (prec) + regfree(prec); + if (prei) + regfree(prei); + if (err == REG_ESPACE) + gp->gl_error = GLOB_NOSPACE; +} + +int +glob(const char* pattern, int flags, int (*errfn)(const char*, int), register glob_t* gp) +{ + register globlist_t* ap; + register char* pat; + globlist_t* top; + Stak_t* oldstak; + char** argv; + char** av; + size_t skip; + unsigned long f; + int n; + int x; + int re_flags; + + const char* nocheck = pattern; + int optlen = 0; + int suflen = 0; + int extra = 1; + unsigned char intr = 0; + + gp->gl_rescan = 0; + gp->gl_error = 0; + gp->gl_errfn = errfn; + if (flags & GLOB_APPEND) + { + if ((gp->gl_flags |= GLOB_APPEND) ^ (flags|GLOB_MAGIC)) + return GLOB_APPERR; + if (((gp->gl_flags & GLOB_STACK) == 0) == (gp->gl_stak == 0)) + return GLOB_APPERR; + if (gp->gl_starstar > 1) + gp->gl_flags |= GLOB_STARSTAR; + else + gp->gl_starstar = 0; + } + else + { + gp->gl_flags = (flags&0xffff)|GLOB_MAGIC; + gp->re_flags = REG_SHELL|REG_NOSUB|REG_LEFT|REG_RIGHT|((flags&GLOB_AUGMENTED)?REG_AUGMENTED:0); + gp->gl_pathc = 0; + gp->gl_ignore = 0; + gp->gl_ignorei = 0; + gp->gl_starstar = 0; + if (!(flags & GLOB_DISC)) + { + gp->gl_fignore = 0; + gp->gl_suffix = 0; + gp->gl_intr = 0; + gp->gl_delim = 0; + gp->gl_handle = 0; + gp->gl_diropen = 0; + gp->gl_dirnext = 0; + gp->gl_dirclose = 0; + gp->gl_type = 0; + gp->gl_attr = 0; + gp->gl_nextdir = 0; + gp->gl_stat = 0; + gp->gl_lstat = 0; + gp->gl_extra = 0; + } + if (!(flags & GLOB_ALTDIRFUNC)) + { + gp->gl_opendir = (GL_opendir_f)opendir; + gp->gl_readdir = (GL_readdir_f)readdir; + gp->gl_closedir = (GL_closedir_f)closedir; + if (!gp->gl_stat) + gp->gl_stat = (GL_stat_f)pathstat; + } + if (!gp->gl_lstat) + gp->gl_lstat = (GL_stat_f)lstat; + if (!gp->gl_intr) + gp->gl_intr = &intr; + if (!gp->gl_delim) + gp->gl_delim = '/'; + if (!gp->gl_diropen) + gp->gl_diropen = gl_diropen; + if (!gp->gl_dirnext) + gp->gl_dirnext = gl_dirnext; + if (!gp->gl_dirclose) + gp->gl_dirclose = gl_dirclose; + if (!gp->gl_type) + gp->gl_type = gl_type; + if (!gp->gl_attr) + gp->gl_attr = gl_attr; + if (flags & GLOB_GROUP) + gp->re_flags |= REG_SHELL_GROUP; + if (flags & GLOB_ICASE) + gp->re_flags |= REG_ICASE; + if (!gp->gl_fignore) + gp->re_flags |= REG_SHELL_DOT; + else if (*gp->gl_fignore) + { + if (regcomp(&gp->re_ignore, gp->gl_fignore, gp->re_flags)) + return GLOB_APPERR; + gp->gl_ignore = &gp->re_ignore; + } + if (gp->gl_flags & GLOB_STACK) + gp->gl_stak = 0; + else if (!(gp->gl_stak = stakcreate(0))) + return GLOB_NOSPACE; + if ((gp->gl_flags & GLOB_COMPLETE) && !gp->gl_nextdir) + gp->gl_nextdir = gl_nextdir; + } + skip = gp->gl_pathc; + if (gp->gl_stak) + oldstak = stakinstall(gp->gl_stak, 0); + if (flags & GLOB_DOOFFS) + extra += gp->gl_offs; + if (gp->gl_suffix) + suflen = strlen(gp->gl_suffix); + if (*(pat = (char*)pattern) == '~' && *(pat + 1) == '(') + { + f = gp->gl_flags; + n = 1; + x = 1; + pat += 2; + for (;;) + { + switch (*pat++) + { + case 0: + case ':': + break; + case '-': + n = 0; + continue; + case '+': + n = 1; + continue; + case 'i': + if (n) + f |= GLOB_ICASE; + else + f &= ~GLOB_ICASE; + continue; + case 'M': + if (n) + f |= GLOB_BRACE; + else + f &= ~GLOB_BRACE; + continue; + case 'N': + if (n) + f &= ~GLOB_NOCHECK; + else + f |= GLOB_NOCHECK; + continue; + case 'O': + if (n) + f |= GLOB_STARSTAR; + else + f &= ~GLOB_STARSTAR; + continue; + case ')': + flags = (gp->gl_flags = f) & 0xffff; + if (f & GLOB_ICASE) + gp->re_flags |= REG_ICASE; + else + gp->re_flags &= ~REG_ICASE; + if (x) + optlen = pat - (char*)pattern; + break; + default: + x = 0; + continue; + } + break; + } + } + top = ap = (globlist_t*)stakalloc((optlen ? 2 : 1) * strlen(pattern) + sizeof(globlist_t) + suflen + gp->gl_extra); + ap->gl_next = 0; + ap->gl_flags = 0; + ap->gl_begin = ap->gl_path + gp->gl_extra; + pat = strcopy(ap->gl_begin, pattern + optlen); + if (suflen) + pat = strcopy(pat, gp->gl_suffix); + if (optlen) + strlcpy(gp->gl_pat = gp->gl_opt = pat + 1, pattern, optlen); + else + gp->gl_pat = 0; + suflen = 0; + if (!(flags & GLOB_LIST)) + gp->gl_match = 0; + re_flags = gp->re_flags; + gp->re_first = 1; + do + { + gp->gl_rescan = ap->gl_next; + glob_dir(gp, ap, re_flags); + } while (!gp->gl_error && (ap = gp->gl_rescan)); + gp->re_flags = re_flags; + if (gp->gl_pathc == skip) + { + if (flags & GLOB_NOCHECK) + { + gp->gl_pathc++; + top->gl_next = gp->gl_match; + gp->gl_match = top; + strcopy(top->gl_path + gp->gl_extra, nocheck); + } + else + gp->gl_error = GLOB_NOMATCH; + } + if (flags & GLOB_LIST) + gp->gl_list = gp->gl_match; + else + { + argv = (char**)stakalloc((gp->gl_pathc + extra) * sizeof(char*)); + if (gp->gl_flags & GLOB_APPEND) + { + skip += --extra; + memcpy(argv, gp->gl_pathv, skip * sizeof(char*)); + av = argv + skip; + } + else + { + av = argv; + while (--extra > 0) + *av++ = 0; + } + gp->gl_pathv = argv; + argv = av; + ap = gp->gl_match; + while (ap) + { + *argv++ = ap->gl_path + gp->gl_extra; + ap = ap->gl_next; + } + *argv = 0; + if (!(flags & GLOB_NOSORT) && (argv - av) > 1) + { + strsort(av, argv - av, strcoll); + if (gp->gl_starstar > 1) + av[gp->gl_pathc = struniq(av, argv - av)] = 0; + gp->gl_starstar = 0; + } + } + if (gp->gl_starstar > 1) + gp->gl_flags &= ~GLOB_STARSTAR; + if (gp->gl_stak) + stakinstall(oldstak, 0); + return gp->gl_error; +} + +void +globfree(glob_t* gp) +{ + if ((gp->gl_flags & GLOB_MAGIC) == GLOB_MAGIC) + { + gp->gl_flags &= ~GLOB_MAGIC; + if (gp->gl_stak) + stkclose(gp->gl_stak); + if (gp->gl_ignore) + regfree(gp->gl_ignore); + if (gp->gl_ignorei) + regfree(gp->gl_ignorei); + } +} diff --git a/src/lib/libast/misc/intercepts.h b/src/lib/libast/misc/intercepts.h new file mode 100644 index 0000000..15959ea --- /dev/null +++ b/src/lib/libast/misc/intercepts.h @@ -0,0 +1,40 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +#ifndef _INTERCEPTS_H +#define _INTERCEPTS_H 1 + +#include <ast.h> +#include <shcmd.h> + +typedef struct Intercepts_s +{ + char* (*intercept_getenv)(const char*); + char* (*intercept_setenviron)(const char*); +} Intercepts_t; + +#define intercepts _ast_intercepts + +extern Intercepts_t intercepts; + +#endif diff --git a/src/lib/libast/misc/magic.c b/src/lib/libast/misc/magic.c new file mode 100644 index 0000000..aa2706e --- /dev/null +++ b/src/lib/libast/misc/magic.c @@ -0,0 +1,2497 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * library interface to file + * + * the sum of the hacks {s5,v10,planix} is _____ than the parts + */ + +static const char id[] = "\n@(#)$Id: magic library (AT&T Research) 2011-03-09 $\0\n"; + +static const char lib[] = "libast:magic"; + +#include <ast.h> +#include <ctype.h> +#include <ccode.h> +#include <dt.h> +#include <modex.h> +#include <error.h> +#include <regex.h> +#include <swap.h> + +#define T(m) (*m?ERROR_translate(NiL,NiL,lib,m):m) + +#define match(s,p) strgrpmatch(s,p,NiL,0,STR_LEFT|STR_RIGHT|STR_ICASE) + +#define MAXNEST 10 /* { ... } nesting limit */ +#define MINITEM 4 /* magic buffer rounding */ + +typedef struct /* identifier dictionary entry */ +{ + const char name[16]; /* identifier name */ + int value; /* identifier value */ + Dtlink_t link; /* dictionary link */ +} Info_t; + +typedef struct Edit /* edit substitution */ +{ + struct Edit* next; /* next in list */ + regex_t* from; /* from pattern */ +} Edit_t; + +struct Entry; + +typedef struct /* loop info */ +{ + struct Entry* lab; /* call this function */ + int start; /* start here */ + int size; /* increment by this amount */ + int count; /* dynamic loop count */ + int offset; /* dynamic offset */ +} Loop_t; + +typedef struct Entry /* magic file entry */ +{ + struct Entry* next; /* next in list */ + char* expr; /* offset expression */ + union + { + unsigned long num; + char* str; + struct Entry* lab; + regex_t* sub; + Loop_t* loop; + } value; /* comparison value */ + char* desc; /* file description */ + char* mime; /* file mime type */ + unsigned long offset; /* offset in bytes */ + unsigned long mask; /* mask before compare */ + char cont; /* continuation operation */ + char type; /* datum type */ + char op; /* comparison operation */ + char nest; /* { or } nesting operation */ + char swap; /* forced swap order */ +} Entry_t; + +#define CC_BIT 5 + +#if (CC_MAPS*CC_BIT) <= (CHAR_BIT*2) +typedef unsigned short Cctype_t; +#else +typedef unsigned long Cctype_t; +#endif + +#define CC_text 0x01 +#define CC_control 0x02 +#define CC_latin 0x04 +#define CC_binary 0x08 +#define CC_utf_8 0x10 + +#define CC_notext CC_text /* CC_text is flipped before checking */ + +#define CC_MASK (CC_binary|CC_latin|CC_control|CC_text) + +#define CCTYPE(c) (((c)>0240)?CC_binary:((c)>=0200)?CC_latin:((c)<040&&(c)!=007&&(c)!=011&&(c)!=012&&(c)!=013&&(c)!=015)?CC_control:CC_text) + +#define ID_NONE 0 +#define ID_ASM 1 +#define ID_C 2 +#define ID_COBOL 3 +#define ID_COPYBOOK 4 +#define ID_CPLUSPLUS 5 +#define ID_FORTRAN 6 +#define ID_HTML 7 +#define ID_INCL1 8 +#define ID_INCL2 9 +#define ID_INCL3 10 +#define ID_MAM1 11 +#define ID_MAM2 12 +#define ID_MAM3 13 +#define ID_NOTEXT 14 +#define ID_PL1 15 +#define ID_YACC 16 + +#define ID_MAX ID_YACC + +#define INFO_atime 1 +#define INFO_blocks 2 +#define INFO_ctime 3 +#define INFO_fstype 4 +#define INFO_gid 5 +#define INFO_mode 6 +#define INFO_mtime 7 +#define INFO_name 8 +#define INFO_nlink 9 +#define INFO_size 10 +#define INFO_uid 11 + +#define _MAGIC_PRIVATE_ \ + Magicdisc_t* disc; /* discipline */ \ + Vmalloc_t* vm; /* vmalloc region */ \ + Entry_t* magic; /* parsed magic table */ \ + Entry_t* magiclast; /* last entry in magic */ \ + char* mime; /* MIME type */ \ + unsigned char* x2n; /* CC_ALIEN=>CC_NATIVE */ \ + char fbuf[SF_BUFSIZE + 1]; /* file data */ \ + char xbuf[SF_BUFSIZE + 1]; /* indirect file data */ \ + char nbuf[256]; /* !CC_NATIVE data */ \ + char mbuf[64]; /* mime string */ \ + char sbuf[64]; /* type suffix string */ \ + char tbuf[2 * PATH_MAX]; /* type string */ \ + Cctype_t cctype[UCHAR_MAX + 1]; /* char code types */ \ + unsigned int count[UCHAR_MAX + 1]; /* char frequency count */ \ + unsigned int multi[UCHAR_MAX + 1]; /* muti char count */ \ + int keep[MAXNEST]; /* ckmagic nest stack */ \ + char* cap[MAXNEST]; /* ckmagic mime stack */ \ + char* msg[MAXNEST]; /* ckmagic text stack */ \ + Entry_t* ret[MAXNEST]; /* ckmagic return stack */ \ + int fbsz; /* fbuf size */ \ + int fbmx; /* fbuf max size */ \ + int xbsz; /* xbuf size */ \ + int swap; /* swap() operation */ \ + unsigned long flags; /* disc+open flags */ \ + long xoff; /* xbuf offset */ \ + int identifier[ID_MAX + 1]; /* Info_t identifier */ \ + Sfio_t* fp; /* fbuf fp */ \ + Sfio_t* tmp; /* tmp string */ \ + regdisc_t redisc; /* regex discipline */ \ + Dtdisc_t dtdisc; /* dict discipline */ \ + Dt_t* idtab; /* identifier dict */ \ + Dt_t* infotab; /* info keyword dict */ + +#include <magic.h> + +static Info_t dict[] = /* keyword dictionary */ +{ + { "COMMON", ID_FORTRAN }, + { "COMPUTE", ID_COBOL }, + { "COMP", ID_COPYBOOK }, + { "COMPUTATIONAL",ID_COPYBOOK }, + { "DCL", ID_PL1 }, + { "DEFINED", ID_PL1 }, + { "DIMENSION", ID_FORTRAN }, + { "DIVISION", ID_COBOL }, + { "FILLER", ID_COPYBOOK }, + { "FIXED", ID_PL1 }, + { "FUNCTION", ID_FORTRAN }, + { "HTML", ID_HTML }, + { "INTEGER", ID_FORTRAN }, + { "MAIN", ID_PL1 }, + { "OPTIONS", ID_PL1 }, + { "PERFORM", ID_COBOL }, + { "PIC", ID_COPYBOOK }, + { "REAL", ID_FORTRAN }, + { "REDEFINES", ID_COPYBOOK }, + { "S9", ID_COPYBOOK }, + { "SECTION", ID_COBOL }, + { "SELECT", ID_COBOL }, + { "SUBROUTINE", ID_FORTRAN }, + { "TEXT", ID_ASM }, + { "VALUE", ID_COPYBOOK }, + { "attr", ID_MAM3 }, + { "binary", ID_YACC }, + { "block", ID_FORTRAN }, + { "bss", ID_ASM }, + { "byte", ID_ASM }, + { "char", ID_C }, + { "class", ID_CPLUSPLUS }, + { "clr", ID_NOTEXT }, + { "comm", ID_ASM }, + { "common", ID_FORTRAN }, + { "data", ID_ASM }, + { "dimension", ID_FORTRAN }, + { "done", ID_MAM2 }, + { "double", ID_C }, + { "even", ID_ASM }, + { "exec", ID_MAM3 }, + { "extern", ID_C }, + { "float", ID_C }, + { "function", ID_FORTRAN }, + { "globl", ID_ASM }, + { "h", ID_INCL3 }, + { "html", ID_HTML }, + { "include", ID_INCL1 }, + { "int", ID_C }, + { "integer", ID_FORTRAN }, + { "jmp", ID_NOTEXT }, + { "left", ID_YACC }, + { "libc", ID_INCL2 }, + { "long", ID_C }, + { "make", ID_MAM1 }, + { "mov", ID_NOTEXT }, + { "private", ID_CPLUSPLUS }, + { "public", ID_CPLUSPLUS }, + { "real", ID_FORTRAN }, + { "register", ID_C }, + { "right", ID_YACC }, + { "sfio", ID_INCL2 }, + { "static", ID_C }, + { "stdio", ID_INCL2 }, + { "struct", ID_C }, + { "subroutine", ID_FORTRAN }, + { "sys", ID_NOTEXT }, + { "term", ID_YACC }, + { "text", ID_ASM }, + { "tst", ID_NOTEXT }, + { "type", ID_YACC }, + { "typedef", ID_C }, + { "u", ID_INCL2 }, + { "union", ID_YACC }, + { "void", ID_C }, +}; + +static Info_t info[] = +{ + { "atime", INFO_atime }, + { "blocks", INFO_blocks }, + { "ctime", INFO_ctime }, + { "fstype", INFO_fstype }, + { "gid", INFO_gid }, + { "mode", INFO_mode }, + { "mtime", INFO_mtime }, + { "name", INFO_name }, + { "nlink", INFO_nlink }, + { "size", INFO_size }, + { "uid", INFO_uid }, +}; + +/* + * return pointer to data at offset off and size siz + */ + +static char* +getdata(register Magic_t* mp, register long off, register int siz) +{ + register long n; + + if (off < 0) + return 0; + if (off + siz <= mp->fbsz) + return mp->fbuf + off; + if (off < mp->xoff || off + siz > mp->xoff + mp->xbsz) + { + if (off + siz > mp->fbmx) + return 0; + n = (off / (SF_BUFSIZE / 2)) * (SF_BUFSIZE / 2); + if (sfseek(mp->fp, n, SEEK_SET) != n) + return 0; + if ((mp->xbsz = sfread(mp->fp, mp->xbuf, sizeof(mp->xbuf) - 1)) < 0) + { + mp->xoff = 0; + mp->xbsz = 0; + return 0; + } + mp->xbuf[mp->xbsz] = 0; + mp->xoff = n; + if (off + siz > mp->xoff + mp->xbsz) + return 0; + } + return mp->xbuf + off - mp->xoff; +} + +/* + * @... evaluator for strexpr() + */ + +static long +indirect(const char* cs, char** e, void* handle) +{ + register char* s = (char*)cs; + register Magic_t* mp = (Magic_t*)handle; + register long n = 0; + register char* p; + + if (s) + { + if (*s == '@') + { + n = *++s == '(' ? strexpr(s, e, indirect, mp) : strtol(s, e, 0); + switch (*(s = *e)) + { + case 'b': + case 'B': + s++; + if (p = getdata(mp, n, 1)) + n = *(unsigned char*)p; + else + s = (char*)cs; + break; + case 'h': + case 'H': + s++; + if (p = getdata(mp, n, 2)) + n = swapget(mp->swap, p, 2); + else + s = (char*)cs; + break; + case 'q': + case 'Q': + s++; + if (p = getdata(mp, n, 8)) + n = swapget(mp->swap, p, 8); + else + s = (char*)cs; + break; + default: + if (isalnum(*s)) + s++; + if (p = getdata(mp, n, 4)) + n = swapget(mp->swap, p, 4); + else + s = (char*)cs; + break; + } + } + *e = s; + } + else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "%s in indirect expression", *e); + return n; +} + +/* + * emit regex error message + */ + +static void +regmessage(Magic_t* mp, regex_t* re, int code) +{ + char buf[128]; + + if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) + { + regerror(code, re, buf, sizeof(buf)); + (*mp->disc->errorf)(mp, mp->disc, 3, "regex: %s", buf); + } +} + +/* + * decompose vcodex(3) method composition + */ + +static char* +vcdecomp(char* b, char* e, unsigned char* m, unsigned char* x) +{ + unsigned char* map; + const char* o; + int c; + int n; + int i; + int a; + + map = CCMAP(CC_ASCII, CC_NATIVE); + a = 0; + i = 1; + for (;;) + { + if (i) + i = 0; + else + *b++ = '^'; + if (m < (x - 1) && !*(m + 1)) + { + /* + * obsolete indices + */ + + if (!a) + { + a = 1; + o = "old, "; + while (b < e && (c = *o++)) + *b++ = c; + } + switch (*m) + { + case 0: o = "delta"; break; + case 1: o = "huffman"; break; + case 2: o = "huffgroup"; break; + case 3: o = "arith"; break; + case 4: o = "bwt"; break; + case 5: o = "rle"; break; + case 6: o = "mtf"; break; + case 7: o = "transpose"; break; + case 8: o = "table"; break; + case 9: o = "huffpart"; break; + case 50: o = "map"; break; + case 100: o = "recfm"; break; + case 101: o = "ss7"; break; + default: o = "UNKNOWN"; break; + } + m += 2; + while (b < e && (c = *o++)) + *b++ = c; + } + else + while (b < e && m < x && (c = *m++)) + { + if (map) + c = map[c]; + *b++ = c; + } + if (b >= e) + break; + n = 0; + while (m < x) + { + n = (n<<7) | (*m & 0x7f); + if (!(*m++ & 0x80)) + break; + } + if (n >= (x - m)) + break; + m += n; + } + return b; +} + +/* + * check for magic table match in buf + */ + +static char* +ckmagic(register Magic_t* mp, const char* file, char* buf, char* end, struct stat* st, unsigned long off) +{ + register Entry_t* ep; + register char* p; + register char* b; + register int level = 0; + int call = -1; + int all = 0; + int c; + int str; + char* q; + char* t; + char* cur; + char* base = 0; + unsigned long num; + unsigned long mask; + regmatch_t matches[10]; + + mp->swap = 0; + b = mp->msg[0] = cur = buf; + mp->mime = mp->cap[0] = 0; + mp->keep[0] = 0; + for (ep = mp->magic; ep; ep = ep->next) + { + fun: + if (ep->nest == '{') + { + if (++level >= MAXNEST) + { + call = -1; + level = 0; + mp->keep[0] = 0; + b = mp->msg[0]; + mp->mime = mp->cap[0]; + continue; + } + mp->keep[level] = mp->keep[level - 1] != 0; + mp->msg[level] = b; + mp->cap[level] = mp->mime; + } + switch (ep->cont) + { + case '#': + if (mp->keep[level] && b > cur) + { + if ((mp->flags & MAGIC_ALL) && b < (end - 3)) + { + all = 1; + *b++ = '\n'; + cur = b; + continue; + } + *b = 0; + return buf; + } + mp->swap = 0; + b = mp->msg[0] = cur; + mp->mime = mp->cap[0] = 0; + if (ep->type == ' ') + continue; + break; + case '$': + if (mp->keep[level] && call < (MAXNEST - 1)) + { + mp->ret[++call] = ep; + ep = ep->value.lab; + goto fun; + } + continue; + case ':': + ep = mp->ret[call--]; + if (ep->op == 'l') + goto fun; + continue; + case '|': + if (mp->keep[level] > 1) + goto checknest; + /*FALLTHROUGH*/ + default: + if (!mp->keep[level]) + { + b = mp->msg[level]; + mp->mime = mp->cap[level]; + goto checknest; + } + break; + } + p = ""; + num = 0; + if (!ep->expr) + num = ep->offset + off; + else + switch (ep->offset) + { + case 0: + num = strexpr(ep->expr, NiL, indirect, mp) + off; + break; + case INFO_atime: + num = st->st_atime; + ep->type = 'D'; + break; + case INFO_blocks: + num = iblocks(st); + ep->type = 'N'; + break; + case INFO_ctime: + num = st->st_ctime; + ep->type = 'D'; + break; + case INFO_fstype: + p = fmtfs(st); + ep->type = toupper(ep->type); + break; + case INFO_gid: + if (ep->type == 'e' || ep->type == 'm' || ep->type == 's') + { + p = fmtgid(st->st_gid); + ep->type = toupper(ep->type); + } + else + { + num = st->st_gid; + ep->type = 'N'; + } + break; + case INFO_mode: + if (ep->type == 'e' || ep->type == 'm' || ep->type == 's') + { + p = fmtmode(st->st_mode, 0); + ep->type = toupper(ep->type); + } + else + { + num = modex(st->st_mode); + ep->type = 'N'; + } + break; + case INFO_mtime: + num = st->st_ctime; + ep->type = 'D'; + break; + case INFO_name: + if (!base) + { + if (base = strrchr(file, '/')) + base++; + else + base = (char*)file; + } + p = base; + ep->type = toupper(ep->type); + break; + case INFO_nlink: + num = st->st_nlink; + ep->type = 'N'; + break; + case INFO_size: + num = st->st_size; + ep->type = 'N'; + break; + case INFO_uid: + if (ep->type == 'e' || ep->type == 'm' || ep->type == 's') + { + p = fmtuid(st->st_uid); + ep->type = toupper(ep->type); + } + else + { + num = st->st_uid; + ep->type = 'N'; + } + break; + } + switch (ep->type) + { + + case 'b': + if (!(p = getdata(mp, num, 1))) + goto next; + num = *(unsigned char*)p; + break; + + case 'h': + if (!(p = getdata(mp, num, 2))) + goto next; + num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 2); + break; + + case 'd': + case 'l': + case 'v': + if (!(p = getdata(mp, num, 4))) + goto next; + num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 4); + break; + + case 'q': + if (!(p = getdata(mp, num, 8))) + goto next; + num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 8); + break; + + case 'e': + if (!(p = getdata(mp, num, 0))) + goto next; + /*FALLTHROUGH*/ + case 'E': + if (!ep->value.sub) + goto next; + if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches))) + { + c = mp->fbsz; + if (c >= sizeof(mp->nbuf)) + c = sizeof(mp->nbuf) - 1; + p = (char*)memcpy(mp->nbuf, p, c); + p[c] = 0; + ccmapstr(mp->x2n, p, c); + if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches))) + { + if (c != REG_NOMATCH) + regmessage(mp, ep->value.sub, c); + goto next; + } + } + p = ep->value.sub->re_sub->re_buf; + q = T(ep->desc); + t = *q ? q : p; + if (mp->keep[level]++ && b > cur && b < end && *(b - 1) != ' ' && *t && *t != ',' && *t != '.' && *t != '\b') + *b++ = ' '; + b += sfsprintf(b, end - b, *q ? q : "%s", p + (*p == '\b')); + if (ep->mime) + mp->mime = ep->mime; + goto checknest; + + case 's': + if (!(p = getdata(mp, num, ep->mask))) + goto next; + goto checkstr; + case 'm': + if (!(p = getdata(mp, num, 0))) + goto next; + /*FALLTHROUGH*/ + case 'M': + case 'S': + checkstr: + for (;;) + { + if (*ep->value.str == '*' && !*(ep->value.str + 1) && isprint(*p)) + break; + if ((ep->type == 'm' || ep->type == 'M') ? strmatch(p, ep->value.str) : !memcmp(p, ep->value.str, ep->mask)) + break; + if (p == mp->nbuf || ep->mask >= sizeof(mp->nbuf)) + goto next; + p = (char*)memcpy(mp->nbuf, p, ep->mask); + p[ep->mask] = 0; + ccmapstr(mp->x2n, p, ep->mask); + } + q = T(ep->desc); + if (mp->keep[level]++ && b > cur && b < end && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b') + *b++ = ' '; + for (t = p; (c = *t) >= 0 && c <= 0177 && isprint(c) && c != '\n'; t++); + *t = 0; + b += sfsprintf(b, end - b, q + (*q == '\b'), p); + *t = c; + if (ep->mime) + mp->mime = ep->mime; + goto checknest; + + } + if (mask = ep->mask) + num &= mask; + switch (ep->op) + { + + case '=': + case '@': + if (num == ep->value.num) + break; + if (ep->cont != '#') + goto next; + if (!mask) + mask = ~mask; + if (ep->type == 'h') + { + if ((num = swapget(mp->swap = 1, p, 2) & mask) == ep->value.num) + { + if (!(mp->swap & (mp->swap + 1))) + mp->swap = 7; + goto swapped; + } + } + else if (ep->type == 'l') + { + for (c = 1; c < 4; c++) + if ((num = swapget(mp->swap = c, p, 4) & mask) == ep->value.num) + { + if (!(mp->swap & (mp->swap + 1))) + mp->swap = 7; + goto swapped; + } + } + else if (ep->type == 'q') + { + for (c = 1; c < 8; c++) + if ((num = swapget(mp->swap = c, p, 8) & mask) == ep->value.num) + goto swapped; + } + goto next; + + case '!': + if (num != ep->value.num) + break; + goto next; + + case '^': + if (num ^ ep->value.num) + break; + goto next; + + case '>': + if (num > ep->value.num) + break; + goto next; + + case '<': + if (num < ep->value.num) + break; + goto next; + + case 'l': + if (num > 0 && mp->keep[level] && call < (MAXNEST - 1)) + { + if (!ep->value.loop->count) + { + ep->value.loop->count = num; + ep->value.loop->offset = off; + off = ep->value.loop->start; + } + else if (!--ep->value.loop->count) + { + off = ep->value.loop->offset; + goto next; + } + else + off += ep->value.loop->size; + mp->ret[++call] = ep; + ep = ep->value.loop->lab; + goto fun; + } + goto next; + + case 'm': + c = mp->swap; + t = ckmagic(mp, file, b + (b > cur), end, st, num); + mp->swap = c; + if (t) + { + if (b > cur && b < end) + *b = ' '; + b += strlen(b); + } + else if (ep->cont == '&') + goto next; + break; + + case 'r': +#if _UWIN + { + char* e; + Sfio_t* rp; + Sfio_t* gp; + + if (!(t = strrchr(file, '.'))) + goto next; + sfprintf(mp->tmp, "/reg/classes_root/%s", t); + if (!(t = sfstruse(mp->tmp)) || !(rp = sfopen(NiL, t, "r"))) + goto next; + *ep->desc = 0; + *ep->mime = 0; + gp = 0; + while (t = sfgetr(rp, '\n', 1)) + { + if (strneq(t, "Content Type=", 13)) + { + ep->mime = vmnewof(mp->vm, ep->mime, char, sfvalue(rp), 0); + strcpy(ep->mime, t + 13); + if (gp) + break; + } + else + { + sfprintf(mp->tmp, "/reg/classes_root/%s", t); + if ((e = sfstruse(mp->tmp)) && (gp = sfopen(NiL, e, "r"))) + { + ep->desc = vmnewof(mp->vm, ep->desc, char, strlen(t), 1); + strcpy(ep->desc, t); + if (*ep->mime) + break; + } + } + } + sfclose(rp); + if (!gp) + goto next; + if (!*ep->mime) + { + t = T(ep->desc); + if (!strncasecmp(t, "microsoft", 9)) + t += 9; + while (isspace(*t)) + t++; + e = "application/x-ms-"; + ep->mime = vmnewof(mp->vm, ep->mime, char, strlen(t), strlen(e)); + e = strcopy(ep->mime, e); + while ((c = *t++) && c != '.' && c != ' ') + *e++ = isupper(c) ? tolower(c) : c; + *e = 0; + } + while (t = sfgetr(gp, '\n', 1)) + if (*t && !streq(t, "\"\"")) + { + ep->desc = vmnewof(mp->vm, ep->desc, char, sfvalue(gp), 0); + strcpy(ep->desc, t); + break; + } + sfclose(gp); + if (!*ep->desc) + goto next; + if (!t) + for (t = T(ep->desc); *t; t++) + if (*t == '.') + *t = ' '; + if (!mp->keep[level]) + mp->keep[level] = 2; + mp->mime = ep->mime; + break; + } +#else + if (ep->cont == '#' && !mp->keep[level]) + mp->keep[level] = 1; + goto next; +#endif + + case 'v': + if (!(p = getdata(mp, num, 4))) + goto next; + c = 0; + do + { + num++; + c = (c<<7) | (*p & 0x7f); + } while (*p++ & 0x80); + if (!(p = getdata(mp, num, c))) + goto next; + if (mp->keep[level]++ && b > cur && b < (end - 1) && *(b - 1) != ' ') + { + *b++ = ','; + *b++ = ' '; + } + b = vcdecomp(b, cur + PATH_MAX, (unsigned char*)p, (unsigned char*)p + c); + goto checknest; + + } + swapped: + q = T(ep->desc); + if (mp->keep[level]++ && b > cur && b < end && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b') + *b++ = ' '; + if (*q == '\b') + q++; + str = 0; + for (t = q; *t; t++) + if (*t == '%' && (c = *(t + 1))) + { + if (c == '%') + t++; + else + while (c && c != '%') + { + if (c == 's') + { + str = 1; + break; + } + else if (c == 'c' || c == 'd' || c == 'i' || c == 'u' || c == 'x' || c == 'X') + goto format; + t++; + c = *(t + 1); + } + } + format: + if (!str) + b += sfsprintf(b, end - b, q, num, num == 1 ? "" : "s", 0, 0, 0, 0, 0, 0); + else if (ep->type == 'd' || ep->type == 'D') + b += sfsprintf(b, end - b, q, fmttime("%?%QL", (time_t)num), 0, 0, 0, 0, 0, 0, 0); + else if (ep->type == 'v') + b += sfsprintf(b, end - b, q, fmtversion(num), 0, 0, 0, 0, 0, 0, 0); + else + b += sfsprintf(b, end - b, q, fmtnum(num, 0), num == 1 ? "" : "s", 0, 0, 0, 0, 0, 0); + if (ep->mime && *ep->mime) + mp->mime = ep->mime; + checknest: + if (ep->nest == '}') + { + if (!mp->keep[level]) + { + b = mp->msg[level]; + mp->mime = mp->cap[level]; + } + else if (level > 0) + mp->keep[level - 1] = mp->keep[level]; + if (--level < 0) + { + level = 0; + mp->keep[0] = 0; + } + } + continue; + next: + if (ep->cont == '&') + mp->keep[level] = 0; + goto checknest; + } + if (all && b-- || mp->keep[level] && b > cur) + { + *b = 0; + return buf; + } + return 0; +} + +/* + * check english language stats + */ + +static int +ckenglish(register Magic_t* mp, int pun, int badpun) +{ + register char* s; + register int vowl = 0; + register int freq = 0; + register int rare = 0; + + if (5 * badpun > pun) + return 0; + if (2 * mp->count[';'] > mp->count['E'] + mp->count['e']) + return 0; + if ((mp->count['>'] + mp->count['<'] + mp->count['/']) > mp->count['E'] + mp->count['e']) + return 0; + for (s = "aeiou"; *s; s++) + vowl += mp->count[toupper(*s)] + mp->count[*s]; + for (s = "etaion"; *s; s++) + freq += mp->count[toupper(*s)] + mp->count[*s]; + for (s = "vjkqxz"; *s; s++) + rare += mp->count[toupper(*s)] + mp->count[*s]; + return 5 * vowl >= mp->fbsz - mp->count[' '] && freq >= 10 * rare; +} + +/* + * check programming language stats + */ + +static char* +cklang(register Magic_t* mp, const char* file, char* buf, char* end, struct stat* st) +{ + register int c; + register unsigned char* b; + register unsigned char* e; + register int q; + register char* s; + char* t; + char* base; + char* suff; + char* t1; + char* t2; + char* t3; + int n; + int badpun; + int code; + int pun; + Cctype_t flags; + Info_t* ip; + + b = (unsigned char*)mp->fbuf; + e = b + mp->fbsz; + memzero(mp->count, sizeof(mp->count)); + memzero(mp->multi, sizeof(mp->multi)); + memzero(mp->identifier, sizeof(mp->identifier)); + + /* + * check character coding + */ + + flags = 0; + while (b < e) + flags |= mp->cctype[*b++]; + b = (unsigned char*)mp->fbuf; + code = 0; + q = CC_ASCII; + n = CC_MASK; + for (c = 0; c < CC_MAPS; c++) + { + flags ^= CC_text; + if ((flags & CC_MASK) < n) + { + n = flags & CC_MASK; + q = c; + } + flags >>= CC_BIT; + } + flags = n; + if (!(flags & (CC_binary|CC_notext))) + { + if (q != CC_NATIVE) + { + code = q; + ccmaps(mp->fbuf, mp->fbsz, q, CC_NATIVE); + } + if (b[0] == '#' && b[1] == '!') + { + for (b += 2; b < e && isspace(*b); b++); + for (s = (char*)b; b < e && isprint(*b); b++); + c = *b; + *b = 0; + if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) || match(s, "/*bin*/*") || !access(s, F_OK)) + { + if (t = strrchr(s, '/')) + s = t + 1; + for (t = s; *t; t++) + if (isspace(*t)) + { + *t = 0; + break; + } + sfsprintf(mp->mbuf, sizeof(mp->mbuf), "application/x-%s", *s ? s : "sh"); + mp->mime = mp->mbuf; + if (match(s, "*sh")) + { + t1 = T("command"); + if (streq(s, "sh")) + *s = 0; + else + { + *b++ = ' '; + *b = 0; + } + } + else + { + t1 = T("interpreter"); + *b++ = ' '; + *b = 0; + } + sfsprintf(mp->sbuf, sizeof(mp->sbuf), T("%s%s script"), s, t1); + s = mp->sbuf; + goto qualify; + } + *b = c; + b = (unsigned char*)mp->fbuf; + } + badpun = 0; + pun = 0; + q = 0; + s = 0; + t = 0; + while (b < e) + { + c = *b++; + mp->count[c]++; + if (c == q && (q != '*' || *b == '/' && b++)) + { + mp->multi[q]++; + q = 0; + } + else if (c == '\\') + { + s = 0; + b++; + } + else if (!q) + { + if (isalpha(c) || c == '_') + { + if (!s) + s = (char*)b - 1; + } + else if (!isdigit(c)) + { + if (s) + { + if (s > mp->fbuf) + switch (*(s - 1)) + { + case ':': + if (*b == ':') + mp->multi[':']++; + break; + case '.': + if (((char*)b - s) == 3 && (s == (mp->fbuf + 1) || *(s - 2) == '\n')) + mp->multi['.']++; + break; + case '\n': + case '\\': + if (*b == '{') + t = (char*)b + 1; + break; + case '{': + if (s == t && *b == '}') + mp->multi['X']++; + break; + } + if (!mp->idtab) + { + if (mp->idtab = dtnew(mp->vm, &mp->dtdisc, Dtset)) + for (q = 0; q < elementsof(dict); q++) + dtinsert(mp->idtab, &dict[q]); + else if (mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 3, "out of space"); + q = 0; + } + if (mp->idtab) + { + *(b - 1) = 0; + if (ip = (Info_t*)dtmatch(mp->idtab, s)) + mp->identifier[ip->value]++; + *(b - 1) = c; + } + s = 0; + } + switch (c) + { + case '\t': + if (b == (unsigned char*)(mp->fbuf + 1) || *(b - 2) == '\n') + mp->multi['\t']++; + break; + case '"': + case '\'': + q = c; + break; + case '/': + if (*b == '*') + q = *b++; + else if (*b == '/') + q = '\n'; + break; + case '$': + if (*b == '(' && *(b + 1) != ' ') + mp->multi['$']++; + break; + case '{': + case '}': + case '[': + case ']': + case '(': + mp->multi[c]++; + break; + case ')': + mp->multi[c]++; + goto punctuation; + case ':': + if (*b == ':' && isspace(*(b + 1)) && b > (unsigned char*)(mp->fbuf + 1) && isspace(*(b - 2))) + mp->multi[':']++; + goto punctuation; + case '.': + case ',': + case '%': + case ';': + case '?': + punctuation: + pun++; + if (*b != ' ' && *b != '\n') + badpun++; + break; + } + } + } + } + } + else + while (b < e) + mp->count[*b++]++; + base = (t1 = strrchr(file, '/')) ? t1 + 1 : (char*)file; + suff = (t1 = strrchr(base, '.')) ? t1 + 1 : ""; + if (!flags) + { + if (match(suff, "*sh|bat|cmd")) + goto id_sh; + if (match(base, "*@(mkfile)")) + goto id_mk; + if (match(base, "*@(makefile|.mk)")) + goto id_make; + if (match(base, "*@(mamfile|.mam)")) + goto id_mam; + if (match(suff, "[cly]?(pp|xx|++)|cc|ll|yy")) + goto id_c; + if (match(suff, "f")) + goto id_fortran; + if (match(suff, "htm+(l)")) + goto id_html; + if (match(suff, "cpy")) + goto id_copybook; + if (match(suff, "cob|cbl|cb2")) + goto id_cobol; + if (match(suff, "pl[1i]")) + goto id_pl1; + if (match(suff, "tex")) + goto id_tex; + if (match(suff, "asm|s")) + goto id_asm; + if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) && (!suff || suff != strchr(suff, '.'))) + { + id_sh: + s = T("command script"); + mp->mime = "application/sh"; + goto qualify; + } + if (strmatch(mp->fbuf, "From * [0-9][0-9]:[0-9][0-9]:[0-9][0-9] *")) + { + s = T("mail message"); + mp->mime = "message/rfc822"; + goto qualify; + } + if (match(base, "*@(mkfile)")) + { + id_mk: + s = "mkfile"; + mp->mime = "application/mk"; + goto qualify; + } + if (match(base, "*@(makefile|.mk)") || mp->multi['\t'] >= mp->count[':'] && (mp->multi['$'] > 0 || mp->multi[':'] > 0)) + { + id_make: + s = "makefile"; + mp->mime = "application/make"; + goto qualify; + } + if (mp->multi['.'] >= 3) + { + s = T("nroff input"); + mp->mime = "application/x-troff"; + goto qualify; + } + if (mp->multi['X'] >= 3) + { + s = T("TeX input"); + mp->mime = "application/x-tex"; + goto qualify; + } + if (mp->fbsz < SF_BUFSIZE && + (mp->multi['('] == mp->multi[')'] && + mp->multi['{'] == mp->multi['}'] && + mp->multi['['] == mp->multi[']']) || + mp->fbsz >= SF_BUFSIZE && + (mp->multi['('] >= mp->multi[')'] && + mp->multi['{'] >= mp->multi['}'] && + mp->multi['['] >= mp->multi[']'])) + { + c = mp->identifier[ID_INCL1]; + if (c >= 2 && mp->identifier[ID_INCL2] >= c && mp->identifier[ID_INCL3] >= c && mp->count['.'] >= c || + mp->identifier[ID_C] >= 5 && mp->count[';'] >= 5 || + mp->count['='] >= 20 && mp->count[';'] >= 20) + { + id_c: + t1 = ""; + t2 = "c "; + t3 = T("program"); + switch (*suff) + { + case 'c': + case 'C': + mp->mime = "application/x-cc"; + break; + case 'l': + case 'L': + t1 = "lex "; + mp->mime = "application/x-lex"; + break; + default: + t3 = T("header"); + if (mp->identifier[ID_YACC] < 5 || mp->count['%'] < 5) + { + mp->mime = "application/x-cc"; + break; + } + /*FALLTHROUGH*/ + case 'y': + case 'Y': + t1 = "yacc "; + mp->mime = "application/x-yacc"; + break; + } + if (mp->identifier[ID_CPLUSPLUS] >= 3) + { + t2 = "c++ "; + mp->mime = "application/x-c++"; + } + sfsprintf(mp->sbuf, sizeof(mp->sbuf), "%s%s%s", t1, t2, t3); + s = mp->sbuf; + goto qualify; + } + } + if (mp->identifier[ID_MAM1] >= 2 && mp->identifier[ID_MAM3] >= 2 && + (mp->fbsz < SF_BUFSIZE && mp->identifier[ID_MAM1] == mp->identifier[ID_MAM2] || + mp->fbsz >= SF_BUFSIZE && mp->identifier[ID_MAM1] >= mp->identifier[ID_MAM2])) + { + id_mam: + s = T("mam program"); + mp->mime = "application/x-mam"; + goto qualify; + } + if (mp->identifier[ID_FORTRAN] >= 8) + { + id_fortran: + s = T("fortran program"); + mp->mime = "application/x-fortran"; + goto qualify; + } + if (mp->identifier[ID_HTML] > 0 && mp->count['<'] >= 8 && (c = mp->count['<'] - mp->count['>']) >= -2 && c <= 2) + { + id_html: + s = T("html input"); + mp->mime = "text/html"; + goto qualify; + } + if (mp->identifier[ID_COPYBOOK] > 0 && mp->identifier[ID_COBOL] == 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2) + { + id_copybook: + s = T("cobol copybook"); + mp->mime = "application/x-cobol"; + goto qualify; + } + if (mp->identifier[ID_COBOL] > 0 && mp->identifier[ID_COPYBOOK] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2) + { + id_cobol: + s = T("cobol program"); + mp->mime = "application/x-cobol"; + goto qualify; + } + if (mp->identifier[ID_PL1] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2) + { + id_pl1: + s = T("pl1 program"); + mp->mime = "application/x-pl1"; + goto qualify; + } + if (mp->count['{'] >= 6 && (c = mp->count['{'] - mp->count['}']) >= -2 && c <= 2 && mp->count['\\'] >= mp->count['{']) + { + id_tex: + s = T("TeX input"); + mp->mime = "text/tex"; + goto qualify; + } + if (mp->identifier[ID_ASM] >= 4) + { + id_asm: + s = T("as program"); + mp->mime = "application/x-as"; + goto qualify; + } + if (ckenglish(mp, pun, badpun)) + { + s = T("english text"); + mp->mime = "text/plain"; + goto qualify; + } + } + else if (streq(base, "core")) + { + mp->mime = "x-system/core"; + return T("core dump"); + } + if (flags & (CC_binary|CC_notext)) + { + b = (unsigned char*)mp->fbuf; + e = b + mp->fbsz; + n = 0; + for (;;) + { + c = *b++; + q = 0; + while (c & 0x80) + { + c <<= 1; + q++; + } + switch (q) + { + case 4: + if (b < e && (*b++ & 0xc0) != 0x80) + break; + case 3: + if (b < e && (*b++ & 0xc0) != 0x80) + break; + case 2: + if (b < e && (*b++ & 0xc0) != 0x80) + break; + n = 1; + case 0: + if (b >= e) + { + if (n) + { + flags &= ~(CC_binary|CC_notext); + flags |= CC_utf_8; + } + break; + } + continue; + } + break; + } + } + if (flags & (CC_binary|CC_notext)) + { + unsigned long d = 0; + + if ((q = mp->fbsz / UCHAR_MAX) >= 2) + { + /* + * compression/encryption via standard deviation + */ + + + for (c = 0; c < UCHAR_MAX; c++) + { + pun = mp->count[c] - q; + d += pun * pun; + } + d /= mp->fbsz; + } + if (d <= 0) + s = T("binary"); + else if (d < 4) + s = T("encrypted"); + else if (d < 16) + s = T("packed"); + else if (d < 64) + s = T("compressed"); + else if (d < 256) + s = T("delta"); + else + s = T("data"); + mp->mime = "application/octet-stream"; + return s; + } + mp->mime = "text/plain"; + if (flags & CC_utf_8) + s = (flags & CC_control) ? T("utf-8 text with control characters") : T("utf-8 text"); + else if (flags & CC_latin) + s = (flags & CC_control) ? T("latin text with control characters") : T("latin text"); + else + s = (flags & CC_control) ? T("text with control characters") : T("text"); + qualify: + if (!flags && mp->count['\n'] >= mp->count['\r'] && mp->count['\n'] <= (mp->count['\r'] + 1) && mp->count['\r']) + { + t = "dos "; + mp->mime = "text/dos"; + } + else + t = ""; + if (code) + { + if (code == CC_ASCII) + sfsprintf(buf, end - buf, "ascii %s%s", t, s); + else + { + sfsprintf(buf, end - buf, "ebcdic%d %s%s", code - 1, t, s); + mp->mime = "text/ebcdic"; + } + s = buf; + } + else if (*t) + { + sfsprintf(buf, end - buf, "%s%s", t, s); + s = buf; + } + return s; +} + +/* + * return the basic magic string for file,st in buf,size + */ + +static char* +type(register Magic_t* mp, const char* file, struct stat* st, char* buf, char* end) +{ + register char* s; + register char* t; + + mp->mime = 0; + if (!S_ISREG(st->st_mode)) + { + if (S_ISDIR(st->st_mode)) + { + mp->mime = "x-system/dir"; + return T("directory"); + } + if (S_ISLNK(st->st_mode)) + { + mp->mime = "x-system/lnk"; + s = buf; + s += sfsprintf(s, end - s, T("symbolic link to ")); + if (pathgetlink(file, s, end - s) < 0) + return T("cannot read symbolic link text"); + return buf; + } + if (S_ISBLK(st->st_mode)) + { + mp->mime = "x-system/blk"; + sfsprintf(buf, PATH_MAX, T("block special (%s)"), fmtdev(st)); + return buf; + } + if (S_ISCHR(st->st_mode)) + { + mp->mime = "x-system/chr"; + sfsprintf(buf, end - buf, T("character special (%s)"), fmtdev(st)); + return buf; + } + if (S_ISFIFO(st->st_mode)) + { + mp->mime = "x-system/fifo"; + return "fifo"; + } +#ifdef S_ISSOCK + if (S_ISSOCK(st->st_mode)) + { + mp->mime = "x-system/sock"; + return "socket"; + } +#endif + } + if (!(mp->fbmx = st->st_size)) + s = T("empty"); + else if (!mp->fp) + s = T("cannot read"); + else + { + mp->fbsz = sfread(mp->fp, mp->fbuf, sizeof(mp->fbuf) - 1); + if (mp->fbsz < 0) + s = fmterror(errno); + else if (mp->fbsz == 0) + s = T("empty"); + else + { + mp->fbuf[mp->fbsz] = 0; + mp->xoff = 0; + mp->xbsz = 0; + if (!(s = ckmagic(mp, file, buf, end, st, 0))) + s = cklang(mp, file, buf, end, st); + } + } + if (!mp->mime) + mp->mime = "application/unknown"; + else if ((t = strchr(mp->mime, '%')) && *(t + 1) == 's' && !*(t + 2)) + { + register char* b; + register char* be; + register char* m; + register char* me; + + b = mp->mime; + me = (m = mp->mime = mp->fbuf) + sizeof(mp->fbuf) - 1; + while (m < me && b < t) + *m++ = *b++; + b = t = s; + for (;;) + { + if (!(be = strchr(t, ' '))) + { + be = b + strlen(b); + break; + } + if (*(be - 1) == ',' || strneq(be + 1, "data", 4) || strneq(be + 1, "file", 4)) + break; + b = t; + t = be + 1; + } + while (m < me && b < be) + if ((*m++ = *b++) == ' ') + *(m - 1) = '-'; + *m = 0; + } + return s; +} + +/* + * low level for magicload() + */ + +static int +load(register Magic_t* mp, char* file, register Sfio_t* fp) +{ + register Entry_t* ep; + register char* p; + register char* p2; + char* p3; + char* next; + int n; + int lge; + int lev; + int ent; + int old; + int cont; + Info_t* ip; + Entry_t* ret; + Entry_t* first; + Entry_t* last = 0; + Entry_t* fun['z' - 'a' + 1]; + + memzero(fun, sizeof(fun)); + cont = '$'; + ent = 0; + lev = 0; + old = 0; + ret = 0; + error_info.file = file; + error_info.line = 0; + first = ep = vmnewof(mp->vm, 0, Entry_t, 1, 0); + while (p = sfgetr(fp, '\n', 1)) + { + error_info.line++; + for (; isspace(*p); p++); + + /* + * nesting + */ + + switch (*p) + { + case 0: + case '#': + cont = '#'; + continue; + case '{': + if (++lev < MAXNEST) + ep->nest = *p; + else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 1, "{ ... } operator nesting too deep -- %d max", MAXNEST); + continue; + case '}': + if (!last || lev <= 0) + { + if (mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "`%c': invalid nesting", *p); + } + else if (lev-- == ent) + { + ent = 0; + ep->cont = ':'; + ep->offset = ret->offset; + ep->nest = ' '; + ep->type = ' '; + ep->op = ' '; + ep->desc = "[RETURN]"; + last = ep; + ep = ret->next = vmnewof(mp->vm, 0, Entry_t, 1, 0); + ret = 0; + } + else + last->nest = *p; + continue; + default: + if (*(p + 1) == '{' || *(p + 1) == '(' && *p != '+' && *p != '>' && *p != '&' && *p != '|') + { + n = *p++; + if (n >= 'a' && n <= 'z') + n -= 'a'; + else + { + if (mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n); + n = 0; + } + if (ret && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a'); + if (*p == '{') + { + ent = ++lev; + ret = ep; + ep->desc = "[FUNCTION]"; + } + else + { + if (*(p + 1) != ')' && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function call argument list", n + 'a'); + ep->desc = "[CALL]"; + } + ep->cont = cont; + ep->offset = n; + ep->nest = ' '; + ep->type = ' '; + ep->op = ' '; + last = ep; + ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0); + if (ret) + fun[n] = last->value.lab = ep; + else if (!(last->value.lab = fun[n]) && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a'); + continue; + } + if (!ep->nest) + ep->nest = (lev > 0 && lev != ent) ? ('0' + lev - !!ent) : ' '; + break; + } + + /* + * continuation + */ + + cont = '$'; + switch (*p) + { + case '>': + old = 1; + if (*(p + 1) == *p) + { + /* + * old style nesting push + */ + + p++; + old = 2; + if (!lev && last) + { + lev = 1; + last->nest = '{'; + if (last->cont == '>') + last->cont = '&'; + ep->nest = '1'; + } + } + /*FALLTHROUGH*/ + case '+': + case '&': + case '|': + ep->cont = *p++; + break; + default: + if ((mp->flags & MAGIC_VERBOSE) && !isalpha(*p) && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 1, "`%c': invalid line continuation operator", *p); + /*FALLTHROUGH*/ + case '*': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + ep->cont = (lev > 0) ? '&' : '#'; + break; + } + switch (old) + { + case 1: + old = 0; + if (lev) + { + /* + * old style nesting pop + */ + + lev = 0; + if (last) + last->nest = '}'; + ep->nest = ' '; + if (ep->cont == '&') + ep->cont = '#'; + } + break; + case 2: + old = 1; + break; + } + if (isdigit(*p)) + { + /* + * absolute offset + */ + + ep->offset = strton(p, &next, NiL, 0); + p2 = next; + } + else + { + for (p2 = p; *p2 && !isspace(*p2); p2++); + if (!*p2) + { + if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p); + continue; + } + + /* + * offset expression + */ + + *p2++ = 0; + ep->expr = vmstrdup(mp->vm, p); + if (isalpha(*p)) + ep->offset = (ip = (Info_t*)dtmatch(mp->infotab, p)) ? ip->value : 0; + else if (*p == '(' && ep->cont == '>') + { + /* + * convert old style indirection to @ + */ + + p = ep->expr + 1; + for (;;) + { + switch (*p++) + { + case 0: + case '@': + case '(': + break; + case ')': + break; + default: + continue; + } + break; + } + if (*--p == ')') + { + *p = 0; + *ep->expr = '@'; + } + } + } + for (; isspace(*p2); p2++); + for (p = p2; *p2 && !isspace(*p2); p2++); + if (!*p2) + { + if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p); + continue; + } + *p2++ = 0; + + /* + * type + */ + + if ((*p == 'b' || *p == 'l') && *(p + 1) == 'e') + { + ep->swap = ~(*p == 'l' ? 7 : 0); + p += 2; + } + if (*p == 's') + { + if (*(p + 1) == 'h') + ep->type = 'h'; + else + ep->type = 's'; + } + else if (*p == 'a') + ep->type = 's'; + else + ep->type = *p; + if (p = strchr(p, '&')) + { + /* + * old style mask + */ + + ep->mask = strton(++p, NiL, NiL, 0); + } + for (; isspace(*p2); p2++); + if (ep->mask) + *--p2 = '='; + + /* + * comparison operation + */ + + p = p2; + if (p2 = strchr(p, '\t')) + *p2++ = 0; + else + { + int qe = 0; + int qn = 0; + + /* + * assume balanced {}[]()\\""'' field + */ + + for (p2 = p;;) + { + switch (n = *p2++) + { + case 0: + break; + case '{': + if (!qe) + qe = '}'; + if (qe == '}') + qn++; + continue; + case '(': + if (!qe) + qe = ')'; + if (qe == ')') + qn++; + continue; + case '[': + if (!qe) + qe = ']'; + if (qe == ']') + qn++; + continue; + case '}': + case ')': + case ']': + if (qe == n && qn > 0) + qn--; + continue; + case '"': + case '\'': + if (!qe) + qe = n; + else if (qe == n) + qe = 0; + continue; + case '\\': + if (*p2) + p2++; + continue; + default: + if (!qe && isspace(n)) + break; + continue; + } + if (n) + *(p2 - 1) = 0; + else + p2--; + break; + } + } + lge = 0; + if (ep->type == 'e' || ep->type == 'm' || ep->type == 's') + ep->op = '='; + else + { + if (*p == '&') + { + ep->mask = strton(++p, &next, NiL, 0); + p = next; + } + switch (*p) + { + case '=': + case '>': + case '<': + case '*': + ep->op = *p++; + if (*p == '=') + { + p++; + switch (ep->op) + { + case '>': + lge = -1; + break; + case '<': + lge = 1; + break; + } + } + break; + case '!': + case '@': + ep->op = *p++; + if (*p == '=') + p++; + break; + case 'x': + p++; + ep->op = '*'; + break; + default: + ep->op = '='; + if (ep->mask) + ep->value.num = ep->mask; + break; + } + } + if (ep->op != '*' && !ep->value.num) + { + if (ep->type == 'e') + { + if (ep->value.sub = vmnewof(mp->vm, 0, regex_t, 1, 0)) + { + ep->value.sub->re_disc = &mp->redisc; + if (!(n = regcomp(ep->value.sub, p, REG_DELIMITED|REG_LENIENT|REG_NULL|REG_DISCIPLINE))) + { + p += ep->value.sub->re_npat; + if (!(n = regsubcomp(ep->value.sub, p, NiL, 0, 0))) + p += ep->value.sub->re_npat; + } + if (n) + { + regmessage(mp, ep->value.sub, n); + ep->value.sub = 0; + } + else if (*p && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 1, "invalid characters after substitution: %s", p); + } + } + else if (ep->type == 'm') + { + ep->mask = stresc(p) + 1; + ep->value.str = vmnewof(mp->vm, 0, char, ep->mask + 1, 0); + memcpy(ep->value.str, p, ep->mask); + if ((!ep->expr || !ep->offset) && !strmatch(ep->value.str, "\\!\\(*\\)")) + ep->value.str[ep->mask - 1] = '*'; + } + else if (ep->type == 's') + { + ep->mask = stresc(p); + ep->value.str = vmnewof(mp->vm, 0, char, ep->mask, 0); + memcpy(ep->value.str, p, ep->mask); + } + else if (*p == '\'') + { + stresc(p); + ep->value.num = *(unsigned char*)(p + 1) + lge; + } + else if (strmatch(p, "+([a-z])\\(*\\)")) + { + char* t; + + t = p; + ep->type = 'V'; + ep->op = *p; + while (*p && *p++ != '('); + switch (ep->op) + { + case 'l': + n = *p++; + if (n < 'a' || n > 'z') + { + if (mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n); + } + else if (!fun[n -= 'a']) + { + if (mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a'); + } + else + { + ep->value.loop = vmnewof(mp->vm, 0, Loop_t, 1, 0); + ep->value.loop->lab = fun[n]; + while (*p && *p++ != ','); + ep->value.loop->start = strton(p, &t, NiL, 0); + while (*t && *t++ != ','); + ep->value.loop->size = strton(t, &t, NiL, 0); + } + break; + case 'm': + case 'r': + ep->desc = vmnewof(mp->vm, 0, char, 32, 0); + ep->mime = vmnewof(mp->vm, 0, char, 32, 0); + break; + case 'v': + break; + default: + if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 1, "%-.*s: unknown function", p - t, t); + break; + } + } + else + { + ep->value.num = strton(p, NiL, NiL, 0) + lge; + if (ep->op == '@') + ep->value.num = swapget(0, (char*)&ep->value.num, sizeof(ep->value.num)); + } + } + + /* + * file description + */ + + if (p2) + { + for (; isspace(*p2); p2++); + if (p = strchr(p2, '\t')) + { + /* + * check for message catalog index + */ + + *p++ = 0; + if (isalpha(*p2)) + { + for (p3 = p2; isalnum(*p3); p3++); + if (*p3++ == ':') + { + for (; isdigit(*p3); p3++); + if (!*p3) + { + for (p2 = p; isspace(*p2); p2++); + if (p = strchr(p2, '\t')) + *p++ = 0; + } + } + } + } + stresc(p2); + ep->desc = vmstrdup(mp->vm, p2); + if (p) + { + for (; isspace(*p); p++); + if (*p) + ep->mime = vmstrdup(mp->vm, p); + } + } + else + ep->desc = ""; + + /* + * get next entry + */ + + last = ep; + ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0); + } + if (last) + { + last->next = 0; + if (mp->magiclast) + mp->magiclast->next = first; + else + mp->magic = first; + mp->magiclast = last; + } + vmfree(mp->vm, ep); + if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf) + { + if (lev < 0) + (*mp->disc->errorf)(mp, mp->disc, 1, "too many } operators"); + else if (lev > 0) + (*mp->disc->errorf)(mp, mp->disc, 1, "not enough } operators"); + if (ret) + (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a'); + } + error_info.file = 0; + error_info.line = 0; + return 0; +} + +/* + * load a magic file into mp + */ + +int +magicload(register Magic_t* mp, const char* file, unsigned long flags) +{ + register char* s; + register char* e; + register char* t; + int n; + int found; + int list; + Sfio_t* fp; + + mp->flags = mp->disc->flags | flags; + found = 0; + if (list = !(s = (char*)file) || !*s || (*s == '-' || *s == '.') && !*(s + 1)) + { + if (!(s = getenv(MAGIC_FILE_ENV)) || !*s) + s = MAGIC_FILE; + } + for (;;) + { + if (!list) + e = 0; + else if (e = strchr(s, ':')) + { + /* + * ok, so ~ won't work for the last list element + * we do it for MAGIC_FILES_ENV anyway + */ + + if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME"))) + { + sfputr(mp->tmp, t, -1); + s += n - 1; + } + sfwrite(mp->tmp, s, e - s); + if (!(s = sfstruse(mp->tmp))) + goto nospace; + } + if (!*s || streq(s, "-")) + s = MAGIC_FILE; + if (!(fp = sfopen(NiL, s, "r"))) + { + if (list) + { + if (!(t = pathpath(s, "", PATH_REGULAR|PATH_READ, mp->fbuf, sizeof(mp->fbuf))) && !strchr(s, '/')) + { + strcpy(mp->fbuf, s); + sfprintf(mp->tmp, "%s/%s", MAGIC_DIR, mp->fbuf); + if (!(s = sfstruse(mp->tmp))) + goto nospace; + if (!(t = pathpath(s, "", PATH_REGULAR|PATH_READ, mp->fbuf, sizeof(mp->fbuf)))) + goto next; + } + if (!(fp = sfopen(NiL, t, "r"))) + goto next; + } + else + { + if (mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 3, "%s: cannot open magic file", s); + return -1; + } + } + found = 1; + n = load(mp, s, fp); + sfclose(fp); + if (n && !list) + return -1; + next: + if (!e) + break; + s = e + 1; + } + if (!found) + { + if (mp->flags & MAGIC_VERBOSE) + { + if (mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 2, "cannot find magic file"); + } + return -1; + } + return 0; + nospace: + if (mp->disc->errorf) + (*mp->disc->errorf)(mp, mp->disc, 3, "out of space"); + return -1; +} + +/* + * open a magic session + */ + +Magic_t* +magicopen(Magicdisc_t* disc) +{ + register Magic_t* mp; + register int i; + register int n; + register int f; + register int c; + register Vmalloc_t* vm; + unsigned char* map[CC_MAPS + 1]; + + if (!(vm = vmopen(Vmdcheap, Vmbest, 0))) + return 0; + if (!(mp = vmnewof(vm, 0, Magic_t, 1, 0))) + { + vmclose(vm); + return 0; + } + mp->id = lib; + mp->disc = disc; + mp->vm = vm; + mp->flags = disc->flags; + mp->redisc.re_version = REG_VERSION; + mp->redisc.re_flags = REG_NOFREE; + mp->redisc.re_errorf = (regerror_t)disc->errorf; + mp->redisc.re_resizef = (regresize_t)vmgetmem; + mp->redisc.re_resizehandle = (void*)mp->vm; + mp->dtdisc.key = offsetof(Info_t, name); + mp->dtdisc.link = offsetof(Info_t, link); + if (!(mp->tmp = sfstropen()) || !(mp->infotab = dtnew(mp->vm, &mp->dtdisc, Dtoset))) + goto bad; + for (n = 0; n < elementsof(info); n++) + dtinsert(mp->infotab, &info[n]); + for (i = 0; i < CC_MAPS; i++) + map[i] = ccmap(i, CC_ASCII); + mp->x2n = ccmap(CC_ALIEN, CC_NATIVE); + for (n = 0; n <= UCHAR_MAX; n++) + { + f = 0; + i = CC_MAPS; + while (--i >= 0) + { + c = ccmapchr(map[i], n); + f = (f << CC_BIT) | CCTYPE(c); + } + mp->cctype[n] = f; + } + return mp; + bad: + magicclose(mp); + return 0; +} + +/* + * close a magicopen() session + */ + +int +magicclose(register Magic_t* mp) +{ + if (!mp) + return -1; + if (mp->tmp) + sfstrclose(mp->tmp); + if (mp->vm) + vmclose(mp->vm); + return 0; +} + +/* + * return the magic string for file with optional stat info st + */ + +char* +magictype(register Magic_t* mp, Sfio_t* fp, const char* file, register struct stat* st) +{ + off_t off; + char* s; + + mp->flags = mp->disc->flags; + mp->mime = 0; + if (!st) + s = T("cannot stat"); + else + { + if (mp->fp = fp) + off = sfseek(mp->fp, (off_t)0, SEEK_CUR); + s = type(mp, file, st, mp->tbuf, &mp->tbuf[sizeof(mp->tbuf)-1]); + if (mp->fp) + sfseek(mp->fp, off, SEEK_SET); + if (!(mp->flags & (MAGIC_MIME|MAGIC_ALL))) + { + if (S_ISREG(st->st_mode) && (st->st_size > 0) && (st->st_size < 128)) + sfprintf(mp->tmp, "%s ", T("short")); + sfprintf(mp->tmp, "%s", s); + if (!mp->fp && (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) + sfprintf(mp->tmp, ", %s", S_ISDIR(st->st_mode) ? T("searchable") : T("executable")); + if (st->st_mode & S_ISUID) + sfprintf(mp->tmp, ", setuid=%s", fmtuid(st->st_uid)); + if (st->st_mode & S_ISGID) + sfprintf(mp->tmp, ", setgid=%s", fmtgid(st->st_gid)); + if (st->st_mode & S_ISVTX) + sfprintf(mp->tmp, ", sticky"); + if (!(s = sfstruse(mp->tmp))) + s = T("out of space"); + } + } + if (mp->flags & MAGIC_MIME) + s = mp->mime; + if (!s) + s = T("error"); + return s; +} + +/* + * list the magic table in mp on sp + */ + +int +magiclist(register Magic_t* mp, register Sfio_t* sp) +{ + register Entry_t* ep = mp->magic; + register Entry_t* rp = 0; + + mp->flags = mp->disc->flags; + sfprintf(sp, "cont\toffset\ttype\top\tmask\tvalue\tmime\tdesc\n"); + while (ep) + { + sfprintf(sp, "%c %c\t", ep->cont, ep->nest); + if (ep->expr) + sfprintf(sp, "%s", ep->expr); + else + sfprintf(sp, "%ld", ep->offset); + sfprintf(sp, "\t%s%c\t%c\t%lo\t", ep->swap == (char)~3 ? "L" : ep->swap == (char)~0 ? "B" : "", ep->type, ep->op, ep->mask); + switch (ep->type) + { + case 'm': + case 's': + sfputr(sp, fmtesc(ep->value.str), -1); + break; + case 'V': + switch (ep->op) + { + case 'l': + sfprintf(sp, "loop(%d,%d,%d,%d)", ep->value.loop->start, ep->value.loop->size, ep->value.loop->count, ep->value.loop->offset); + break; + case 'v': + sfprintf(sp, "vcodex()"); + break; + default: + sfprintf(sp, "%p", ep->value.str); + break; + } + break; + default: + sfprintf(sp, "%lo", ep->value.num); + break; + } + sfprintf(sp, "\t%s\t%s\n", ep->mime ? ep->mime : "", fmtesc(ep->desc)); + if (ep->cont == '$' && !ep->value.lab->mask) + { + rp = ep; + ep = ep->value.lab; + } + else + { + if (ep->cont == ':') + { + ep = rp; + ep->value.lab->mask = 1; + } + ep = ep->next; + } + } + return 0; +} diff --git a/src/lib/libast/misc/magic.tab b/src/lib/libast/misc/magic.tab new file mode 100644 index 0000000..00df074 --- /dev/null +++ b/src/lib/libast/misc/magic.tab @@ -0,0 +1,1721 @@ +# +# @(#)magic (AT&T Research) 2011-11-02 +# +# magic number database for file(1) and magic(3) +# +# the tab separated fields are: +# +# [op]offset type [mask]operator description mime +# +# + previous fields must match, current optional +# & previous and current fields must match +# { start nesting block +# } end nesting block +# s{ function declaration and call +# } function return +# s() function call +# +# offset byte offset for magic number test or (@offset) expr +# or file meta-data from { atime blocks ctime fstype +# gid mode mtime name nlink size uid } +# type { byte short long quad date edit match } +# mask optional &number before operator +# operator comparison operator { < <= > >= != == (default) } +# description file description for magic number match +# mime optional mime type +# +# numeric values may be decimal, octal, or hex +# the description string may have one printf format spec for the +# matched magic number +# + +0 short 070707 binary cpio archive application/pax +0 string 070707 cpio archive application/pax ++76 edit %!PAX!C!\([^!]*\).*%\1% , compressed, version %s ++76 edit %!PAX!D!\([^!]*\).*%\1% , delta, version %s ++76 string DELTA!!! , delta, version 88 ++76 match !(*!*) , [ %s ... ] +0 string 070701 System V asc cpio archive application/pax ++110 string * , [ %s ... ] +0 string 070702 System V aschk cpio archive application/pax ++110 string * , [ %s ... ] +0 long 0177555 System III ar archive application/x-ar +0 short 0177545 pdp11 ar archive application/x-ar +0 long 0x04034b50 zip archive application/zip ++2 byte >0 , version %d +&3 byte * .%d +0 long 0x223e9f78 ms outlook tnef archive application/pax +0 string MSCF ms cabinet archive application/pax +&4 long 0 ++25 byte * , version %d ++24 byte * .%d +0 string \x52\x61\x72\x21\x1a\x07 rar archive application/pax +0 long 0x0d010b05 make object application/x-nmake +o{ ++4 byte <037 (version %ld) ++4 edit %.*\(..\)/\(..\)/\(..\).*%19\3-\1-\2% , version %s ++4 edit %.*\(....-..-..\).*%\1% , version %s +} +0 long 0177535 make object, old magic application/x-nmake +o() +0 long 0x090f0301 jmake project db application/x-jmake ++4 string * , version %s +0 long 0x0b130800 ksh binary script application/ksh ++4 byte * , version %ld +0 string vkda delta application/x-vdelta ++4 byte >0 (version %ld) +0 long 0x03040200 cql db application/x-cql ++4 byte * , version %d ++5 byte * .%d ++6 string * , %s +0 string !<cdb- ++6 edit %\([^-]*\)-\([0-9.]*\)>.*%cql db, \1 format, version \2% %s application/x-cql +0 long 0x08091800 ++32 string * %s application/x-cql ++0 byte * hashed index ++4 long >0 , %d record%s ++8 long >0 , %d max ++12 date >0 , stamp %s +0 string \1S\1B\1C\1S sbcs delta application/x-sbcs +0 long 0100554 apl workspace application/x-apl +0 short 017037 packed data application/zip +0 string <ar> System V 1.0 ar archive application/x-ar +0 string !<arch>\n/ ar library application/x-ar ++68 long 0x020b0619 , hp s800 relocatable ++68 long 0x02100619 , hp pa-risc 1.1 relocatable ++68 long 0x02110619 , hp pa-risc 1.2 relocatable ++68 long 0x02140619 , hp pa-risc 2.0 relocatable +0 string !<arch>\n__.SYMDEF ar library, ranlib application/x-ar +0 string !<arch>\n__________E ar library, hybrid application/x-ar +0 string !<arch>\n_______[0-9_][0-9_][0-9_]E[BL]E[BL]_ ar library, hybrid application/x-ar +o{ ++22 byte 'X' , out of date ++20 byte 'U' , ucode members ++21 byte >='A' , %c-endian members ++19 byte >='A' , %c-endian hash +} +0 string !<arch>\n________64E ar library, 64 bit hybrid application/x-ar +o() +0 string !<arch> ar archive application/x-ar +0 string <aiaff>\n aix ar library application/x-ar +0 string <bigaf>\n aix ar library, big application/x-ar +20 short 0xa7dc zoo archive application/x-zoo +&22 short 0xfdc4 ++32 byte * , version %ld ++33 byte * .%ld + +0 string \326\303\304\330 vcodex data application/x-vczip ++5 void vcodex() + +0 long 0x080456 +{ +85 byte <10 sun ++85 byte <3 m680%d0 ++85 byte 3 sparc ++85 byte >3 *unknown* +} ++85 byte * core dump x-system/core ++128 string * from `%s' ++132 string * from `%s' +0 long 050632 core dump x-system/core +0 long &0xfff00000==0xe8c00000 Alliant core dump ++160 string * from `%s' + +0 long 0x02100106 hp pa-risc 1.1 object x-system/obj +0 long 0x02100107 hp pa-risc 1.1 executable x-system/exe +o{ ++(@144) long 0x054ef630 , dynamically linked ++96 long >0 , not stripped +} +0 long 0x02100108 hp pa-risc 1.1 executable, shared x-system/exe +o() +0 long 0x0210010b hp pa-risc 1.1 executable, demand-load x-system/exe +o() +0 long 0x0210010e hp pa-risc 1.1 shared library x-system/dll +o() +0 long 0x0210010d hp pa-risc 1.1 shared library x-system/dll +s{ ++96 long >0 , not stripped +} + +0 long 0x02140106 hp pa-risc 2.0 object x-system/obj +0 long 0x02140107 hp pa-risc 2.0 executable x-system/exe +o() +0 long 0x02140108 hp pa-risc 2.0 executable, shared x-system/exe +o() +0 long 0x0214010b hp pa-risc 2.0 executable, demand-load x-system/exe +o() +0 long 0x0214010e hp pa-risc 2.0 shared library x-system/dll +o() +0 long 0x0214010d hp pa-risc 2.0 shared library x-system/dll +s() + +0 long 0x020b0106 hp s800 object x-system/obj +0 long 0x020b0107 hp s800 executable x-system/exe +o() +0 long 0x020b0108 hp s800 executable, shared x-system/exe +o() +0 long 0x020b010b hp s800 executable, demand-load x-system/exe +o() +0 long 0x020b010d hp s800 shared library x-system/dll +s() +0 long 0x020b010e hp s800 shared library x-system/dll +s() + +0 long 0x02080108 hp s500 executable, pure x-system/exe +o{ ++16 long >0 , version %ld +} +0 long 0x02080107 hp s500 executable x-system/exe +o() +0 long 0x02080106 hp s500 executable, relocatable x-system/obj +o() +0 long 0x020c0108 hp s200 executable, pure x-system/exe +o{ ++36 long >0 , not stripped ++4 short >0 , version %ld +} +0 long 0x020c0107 hp s200 executable x-system/exe +o() +0 long 0x020c010b hp s200 executable, demand-load x-system/exe +o() +0 long 0x020a0108 hp s200 2.x executable, pure x-system/exe +o() +0 long 0x020a0107 hp s200 2.x executable x-system/exe +o() +0 long 0x020c0106 hp s200 executable, relocatable x-system/exe ++4 short >0 , version %ld +0 long 0x0208ff65 hp s500 old archive application/x-ar +0 long 0x020cff65 hp s200 old archive application/x-ar +0 long 0x020aff65 hp s200 old archive application/x-ar +0 short 0x8000 hp lif file +0 long 0x020c010c hp compiled Lisp +0 long 0x4da7eee8 hp windows font ++8 byte >0 , version %ld + +0 string Joy!peffpwpc PowerPC executable +0 short 0x01df PowerPC object x-system/obj +&3 byte 3 + +0 long 0x50900107 pyramid 90x executable x-system/exe +o{ ++0 long &0x7=0x3 , paged ++0 long &0x8 , pure ++16 long >0 , not stripped +} +0 long 0x50900108 pyramid 90x object x-system/obj +0 long 0x5090010b pyramid 90x executable x-system/exe +o() + +0 long 0x000001EB plan9 386 executable x-system/exe +0 long 0x00000107 plan9 68020 executable x-system/exe +&mode long &0111!=0 +0 long 0x00000197 plan9 hobbit executable x-system/exe +0 long 0x00000407 plan9 mips executable x-system/exe +0 long 0x000002AB plan9 sparc executable x-system/exe + +0 long 0x7E004501 plan9 386 object x-system/obj +0 long 0x4D013201 plan9 68020 object x-system/obj +0 long 0x430D013C plan9 hobbit object x-system/obj +0 long 0x3A11013C plan9 mips object x-system/obj +0 long 0x7410013C plan9 sparc object x-system/obj + +0 long &0x0030FFFF==0x00000064 linux 386 +&0 long &0x000F0000>0 ++20 long &0xEFDFFFFF==0 executable x-system/exe ++20 long &0xEFDFFFFF!=0 shared library x-system/dll ++0 long 0x01080064 , pure ++0 long 0x010B0064 , paged ++0 long 0x00CC0064 , paged, no page 0 +{ +20 long &0xEFDFFFFF==0 +&16 long >0 , not stripped +} +216 long 0421 linux core dump x-system/core + +0 long 0x00cc0064 linux 386 kernel code x-system/exe ++size long <600000 , compressed +510 short 0xaa55 linux 386 kernel image x-system/exe ++size long <600000 , compressed +0 long 0x03010410 minix 386 executable x-system/exe +0 long 0x000186a3 minix 386 object x-system/obj + +0 long 0314 bsd 386 executable, paged, no page 0 x-system/exe ++16 long >0 , not stripped +0 long 0407 bsd 386 executable x-system/exe +&mode long &0111!=0 ++16 long >0 , not stripped +0 long 0407 bsd-or-linux 386 object x-system/obj +0 long 0410 bsd 386 executable, pure x-system/exe ++16 long >0 , not stripped +0 long 0413 bsd 386 executable, paged x-system/exe ++16 long >0 , not stripped + +0 belong 0xcafebabe java object x-java/obj +&4 belong >30 ++6 beshort * version %d ++6 beshort * .%d + +a{ ++4 long &0x00ffffff=1 vax ++4 long &0x00ffffff=2 romp ++4 long &0x00ffffff=3 architecture=%ld ++4 long &0x00ffffff=4 ns32032 ++4 long &0x00ffffff=5 ns32332 ++4 long &0x00ffffff=6 m68k ++4 long &0x00ffffff=7 i386 ++4 long &0x00ffffff=8 mips ++4 long &0x00ffffff=9 ns32532 ++4 long &0x00ffffff=10 architecture=%ld ++4 long &0x00ffffff=11 hp pa-risc ++4 long &0x00ffffff=12 acorn ++4 long &0x00ffffff=13 m88k ++4 long &0x00ffffff=14 sparc ++4 long &0x00ffffff=15 i860-big ++4 long &0x00ffffff=16 i860 ++4 long &0x00ffffff=17 rs6000 ++4 long &0x00ffffff=18 powerpc ++4 long &0x00ffffff>=19 architecture=%ld ++4 long &0x01000000!=0 \b-64 +} + +0 belong 0xcafebabe universal binary [ ++4 belong loop(a,4,20) ++4 belong * ] ++010000 void magic() ++0 void * ... + +0 long &0xfffffffe=0xfeedface mach-o ++0 long &0x00000001!=0 64-bit +a() ++12 long <=0 filetype=%ld +{ +12 long 1 relocatable x-system/obj +&mode long &0111!=0 +} +{ +12 long 1 object x-system/obj +&mode long &0111==0 +} ++12 long 2 executable x-system/exe ++12 long 3 fixed vm shared library x-system/dll ++12 long 4 core x-system/core ++12 long 5 preload executable x-system/exe ++12 long 6 shared library x-system/dll ++12 long 7 dynamic link editor x-system/exe ++12 long 8 bundle x-system/dll ++12 long >=9 filetype=%ld + +2 short 0407 +m{ +1 byte <10 sun ++1 byte <3 m680%d0 ++1 byte 3 sparc ++1 byte >3 *unknown* +} ++0 byte !=0xffffffff object x-system/obj +2 short 0410 +m() ++0 byte !=0xffffffff executable, pure x-system/exe +o{ +{ +0 byte &0200 +&20 long >=0x2000 , dynamically linked +} ++16 long >0 , not stripped +} +2 short 0413 +m() +{ +0 byte &0200 +&20 long <0x2000 shared library x-system/dll +} ++0 byte !=0xffffffff executable, paged x-system/exe +o() + +0 short 0420 Alliant virtual executable x-system/exe ++16 long >0 , not stripped +o{ ++2 short &0x0001 , 68020 only ++2 short &0x0002 , vector instructions ++2 short &0x0008 , IP only ++2 short &0x0010 , CE only ++2 short &0x0020 , common library ++2 short &0x0200 , no complex +} +0 short 0421 Alliant object x-system/obj ++16 long 0 , no symbols +o() + +0 short 0x01df aix RISC +{ +18 short &0x2002==0x0002 executable x-system/exe ++18 short &0x1000 , dynamically linked ++12 long >0 , not stripped +} ++18 short &0x2002==0 object x-system/obj ++18 short &0x2000 shared library x-system/dll +0 short 0x0103 aix RT executable x-system/exe ++2 byte 0x50 , pure ++28 long >0 , not stripped ++6 short >0 , version %ld +0 short 0x0104 aix shared library x-system/dll +0 short 0x0105 aix ctab data +0 short 0xfe04 aix structured file + +0 short 0401 unix-rt ldp +0 short 0405 old overlay +0 short 0437 pdp11 kernel overlay + +0 short 0407 System III executable x-system/exe +o{ ++16 long >0 , not stripped ++2 short >0 , version %ld +} +0 short 0410 System III executable, pure x-system/exe +o() +0 short 0411 System III executable, separate I&D x-system/exe +o() + +0 long 0407 vax object x-system/obj +0 long 0410 vax executable, pure x-system/exe +o{ ++16 long >0 , not stripped +} +0 long 0413 vax executable, paged x-system/exe +o() + +0 short 0413 vax executable, pure x-system/exe ++8 short >0 , not stripped ++15 byte >0 , version %ld + +0 short 0570 vax +o{ +{ +16 short >0 executable x-system/exe ++12 long >0 , not stripped ++22 short >0 , version %ld +} ++16 short 0 object x-system/obj +} +0 short 0575 vax +o() +0 short 0502 basic-16 +o() +0 short 0503 basic-16 (TV) +o() +0 short 0510 x86 +o() +0 short 0511 x86 (TV) +o() +0 short 0550 3b20 +o() +0 short 0551 3b20d (TV) +o() + +0 long 0x464c457f elf +&4 byte <2 +&5 byte 1 +o{ +{ ++18 short 0 machine=UNKNOWN ++18 short 1 3b ++18 short 2 sparc ++18 short 3 i386 ++18 short 4 m68k ++18 short 5 m88k ++18 short 6 i486 ++18 short 7 i860 +{ +18 short 8 +{ +36 long &0xf00000f0==0x00000000 ++4 byte 1 mips2 ++4 byte 2 mips4 +} ++36 long &0xf0000000==0x10000000 mips2 ++36 long &0xf0000000==0x20000000 mips3 ++36 long &0xf0000000==0x30000000 mips4 ++36 long &0xf00000f0==0x00000040 mips4 ++36 long &0xf0000000==0x40000000 mips5 ++36 long &0xf0000000==0x50000000 mips6 ++36 long &0xf0000000==0x60000000 mips7 ++36 long &0xf0000000==0x70000000 mips8 ++36 long &0xf0000000==0x80000000 mips9 +} ++18 short 9 amdahl ++18 short 10 mips_le ++18 short 11 rs6000 ++18 short 15 pa ++18 short 16 n-cube ++18 short 17 fujitsu500 ++18 short 18 sparc32+ ++18 short 20 powerpc ++18 short 21 powerpc-64 ++18 short 22 s390 ++18 short 23 cell-BE ++18 short 36 nec-v800 ++18 short 37 fujitsu-fr20 ++18 short 38 trw-rh32 ++18 short 39 fujitsu-mma ++18 short 40 arm ++18 short 41 alpha ++18 short 42 hitachi-sh ++18 short 43 sparc64-v9 ++18 short 44 siemens-tricore ++18 short 45 argonaut ++18 short 46 hitachi-h8/300 ++18 short 47 hitachi-h8/300h ++18 short 48 hitachi-h8s ++18 short 49 hitachi-h8/500 ++18 short 50 itanium ++18 short 51 mips-x ++18 short 52 motorola-coldfire ++18 short 53 motorola-m68hc12 ++18 short 54 fujutsu-mma ++18 short 55 siemens-pcp ++18 short 56 sony-ncpu ++18 short 57 denso-ndr1 ++18 short 58 motorola-startcore ++18 short 59 toyota-me16 ++18 short 60 stm-st100 ++18 short 61 alc-tinyj ++18 short 62 x86-64 ++18 short 63 sony-dsp ++18 short 66 siemens-fx66 ++18 short 67 stm-st9-16 ++18 short 68 stm-st7-8 ++18 short 69 motorola-mc68hc16 ++18 short 70 motorola-mc68hc11 ++18 short 71 motorola-mc68hc08 ++18 short 72 motorola-mc68hc05 ++18 short 73 sgi-svx ++18 short 74 stm-st19-8 ++18 short 75 vax ++18 short 76 axis, 32-bit ++18 short 77 infineon-javelin-32 ++18 short 78 element-14-firepath-64 ++18 short 79 lsi-zsp-16 ++18 short 80 knuth-mmix-64 ++18 short 81 harvard-huany ++18 short 82 sitera-prism ++18 short 83 amtel-avr-8 ++18 short 84 fujitsu-fr30 ++18 short 85 mitsubishi-d10v ++18 short 86 mitsubishi-d30v ++18 short 87 nec-v850 ++18 short 88 matsushita-m32r ++18 short 89 matsushita-mn10300 ++18 short 90 matsushita-mn10200 ++18 short 91 picojava ++18 short 92 openrisc-32 ++18 short 93 arc-tangent-a5 ++18 short 94 tensilica-xtensa ++18 short 0x9026 alpha ++18 short 0xa390 s390 +|18 short * machine=0x%04lX +} ++16 short 0 type=UNKNOWN ++16 short 1 object x-system/obj ++16 short 2 executable x-system/exe ++16 short 3 shared library x-system/dll +{ +16 short 4 core dump x-system/core ++(@28+@42H*@44H+104) string * from `%s' +} ++16 short >4 type=%d ++4 byte 0 , 16-bit ++4 byte 1 , 32-bit ++5 byte 1 , little-endian +{ +18 short ==8 +&36 long &0x000000f0==0x00000000 +&4 byte 1 , old +} +{ +18 short 1 ++36 long 1 , mau +} ++20 long >1 , version %ld +{ +16 short 2 +&(@28+4*32) long 2 , dynamically linked +} +} + +0 long 0x7f454c46 elf +&4 byte 1 +&5 byte 2 +o() + +0 long 0x464c457f elf +&4 byte 2 +&5 byte 1 +o{ +{ ++18 short 0 machine=UNKNOWN ++18 short 1 3b ++18 short 2 sparc ++18 short 3 i386 ++18 short 4 m68k ++18 short 5 m88k ++18 short 6 i486 ++18 short 7 i860 +{ +18 short 8 +{ +48 long &0xf00000f0==0x00000000 ++4 byte 1 mips2 ++4 byte 2 mips4 +} ++48 long &0xf0000000==0x10000000 mips2 ++48 long &0xf0000000==0x20000000 mips3 ++48 long &0xf0000000==0x30000000 mips4 ++48 long &0xf00000f0==0x00000040 mips4 ++48 long &0xf0000000==0x40000000 mips5 ++48 long &0xf0000000==0x50000000 mips6 ++48 long &0xf0000000==0x60000000 mips7 ++48 long &0xf0000000==0x70000000 mips8 ++48 long &0xf0000000==0x80000000 mips9 +} ++18 short 9 amdahl ++18 short 10 mips_le ++18 short 11 rs6000 ++18 short 15 pa ++18 short 16 n-cube ++18 short 17 fujitsu500 ++18 short 18 sparc32+ ++18 short 20 powerpc ++18 short 21 powerpc-64 ++18 short 22 s390 ++18 short 23 cell-BE ++18 short 36 nec-v800 ++18 short 37 fujitsu-fr20 ++18 short 38 trw-rh32 ++18 short 39 fujitsu-mma ++18 short 40 arm ++18 short 41 alpha ++18 short 42 hitachi-sh ++18 short 43 sparc64-v9 ++18 short 44 siemens-tricore ++18 short 45 argonaut ++18 short 46 hitachi-h8/300 ++18 short 47 hitachi-h8/300h ++18 short 48 hitachi-h8s ++18 short 49 hitachi-h8/500 ++18 short 50 itanium ++18 short 51 mips-x ++18 short 52 motorola-coldfire ++18 short 53 motorola-m68hc12 ++18 short 54 fujutsu-mma ++18 short 55 siemens-pcp ++18 short 56 sony-ncpu ++18 short 57 denso-ndr1 ++18 short 58 motorola-startcore ++18 short 59 toyota-me16 ++18 short 60 stm-st100 ++18 short 61 alc-tinyj ++18 short 62 x86-64 ++18 short 63 sony-dsp ++18 short 66 siemens-fx66 ++18 short 67 stm-st9-16 ++18 short 68 stm-st7-8 ++18 short 69 motorola-mc68hc16 ++18 short 70 motorola-mc68hc11 ++18 short 71 motorola-mc68hc08 ++18 short 72 motorola-mc68hc05 ++18 short 73 sgi-svx ++18 short 74 stm-st19-8 ++18 short 75 vax ++18 short 76 axis-32 ++18 short 77 infineon-javelin-32 ++18 short 78 element-14-firepath-64 ++18 short 79 lsi-zsp-16 ++18 short 80 knuth-mmix-64 ++18 short 81 harvard-huany ++18 short 82 sitera-prism ++18 short 83 amtel-avr-8 ++18 short 84 fujitsu-fr30 ++18 short 85 mitsubishi-d10v ++18 short 86 mitsubishi-d30v ++18 short 87 nec-v850 ++18 short 88 matsushita-m32r ++18 short 89 matsushita-mn10300 ++18 short 90 matsushita-mn10200 ++18 short 91 picojava ++18 short 92 openrisc-32 ++18 short 93 arc-tangent-a5 ++18 short 94 tensilica-xtensa ++18 short 0x9026 alpha ++18 short 0xa390 s390 +|18 short * machine=0x%04lX +} ++16 short 0 type=UNKNOWN ++16 short 1 object x-system/obj ++16 short 2 executable x-system/exe ++16 short 3 shared library x-system/dll +{ +16 short 4 core dump x-system/core ++(@28+@42H*@44H+104) string * from `%s' +} ++16 short >4 type=%ld ++4 byte 2 , 64-bit ++5 byte 1 , little-endian ++20 long >1 , version %ld +{ +16 short 2 ++(@32Q+3*56) long 2 , dynamically linked ++(@32Q+4*56) long 2 , dynamically linked +} +} + +0 long 0x7f454c46 elf +&4 byte 2 +&5 byte 2 +o() + +0 lelong 000000407 netbsd little-endian object x-system/obj ++16 lelong 0 , stripped +0 belong 000000407 netbsd big-endian object x-system/obj ++16 belong 0 , stripped + +0 belong&0377777777 041400413 netbsd i386 +d{ +0 byte &0x80 ++20 lelong <4096 shared library x-system/dll ++20 lelong >=4096 executable, dynamically linked x-system/exe +} ++0 byte ^0x80 executable x-system/exe ++16 lelong 0 , stripped +0 belong&0377777777 041400410 netbsd i386 pure +p{ ++0 byte &0x80 executable, dynamically linked x-system/exe ++0 byte ^0x80 executable x-system/exe ++16 lelong 0 , stripped +} +0 belong&0377777777 041400407 netbsd i386 +n{ ++0 byte &0x80 executable, dynamically linked, impure x-system/exe +{ +0 byte ^0x80 ++0 byte &0x40 position independent ++20 lelong !0 executable x-system/exe ++20 lelong =0 object x-system/obj +} ++16 lelong 0 , stripped +} +0 belong&0377777777 041400507 netbsd i386 core +c{ ++12 string * from '%s' x-system/core +} + +0 belong&0377777777 042000413 netbsd m68k4k +d() +0 belong&0377777777 042000410 netbsd m68k4k pure +p() +0 belong&0377777777 042000407 netbsd m68k4k +n() +0 belong&0377777777 042000507 netbsd m68k4k core +c() + +0 belong&0377777777 042200413 netbsd ns32532 +d() +0 belong&0377777777 042200410 netbsd ns32532 pure +p() +0 belong&0377777777 042200407 netbsd ns32532 +n() +0 belong&0377777777 042200507 netbsd ns32532 core +c() + +0 belong&0377777777 042600413 netbsd pmax +d() +0 belong&0377777777 042600410 netbsd pmax pure +p() +0 belong&0377777777 042600407 netbsd pmax +n() +0 belong&0377777777 042600507 netbsd pmax core +c() + +0 belong&0377777777 043000413 netbsd vax 1k +d() +0 belong&0377777777 043000410 netbsd vax 1k pure +p() +0 belong&0377777777 043000407 netbsd vax 1k +n() +0 belong&0377777777 043000507 netbsd vax 1k core +c() + +0 belong&0377777777 045400413 netbsd vax 4k +d() +0 belong&0377777777 045400410 netbsd vax 4k pure +p() +0 belong&0377777777 045400407 netbsd vax 4k +n() +0 belong&0377777777 045400507 netbsd vax 4k core +c() + +0 belong&0377777777 041600413 netbsd m68k +d{ +0 byte &0x80 ++20 belong <8192 shared library x-system/dll ++20 belong >=8192 executable, dynamically linked x-system/exe +} ++0 byte ^0x80 executable x-system/exe ++16 belong 0 , stripped +0 belong&0377777777 041600410 netbsd m68k pure +p() +0 belong&0377777777 041600407 netbsd m68k +n() +0 belong&0377777777 041600507 netbsd m68k core +c() +0 belong&0377777777 042400413 netbsd sparc +d() + +0 belong&0377777777 042400410 netbsd sparc pure +p() +0 belong&0377777777 042400407 netbsd sparc +n() +0 belong&0377777777 042400507 netbsd sparc core +c() + +0 belong&0377777777 043400413 netbsd mips +d() +0 belong&0377777777 043400410 netbsd mips pure +p() +0 belong&0377777777 043400407 netbsd mips +n() +0 belong&0377777777 043400507 netbsd mips core +c() + +0 belong&0377777777 043600413 netbsd arm32 +d() +0 belong&0377777777 043600410 netbsd arm32 pure +p() +0 belong&0377777777 043600407 netbsd arm32 +n() +0 belong&0377777777 043600507 netbsd arm32 core +c() + +0 lelong 0x00070185 ECOFF netbsd alpha binary ++10 leshort 0x0000 , stripped x-system/obj +0 belong&0377777777 043200507 netbsd alpha core +c() + +0 short 0560 3b +{ +16 short >0 executable x-system/exe ++12 long >0 , not stripped +} ++16 short 0 object x-system/obj ++18 short &010000 , paging 3b2/300 ++18 short &020000 , 32100 ++18 short &040000 , mau +{ +16 short >0 ++20 short 0443 , shared library x-system/dll ++20 short 0410 , swapped ++20 short 0413 , paged ++22 short >0 , version %ld +} +0 short 0561 3b (TV) +{ +16 short >0 executable x-system/exe ++12 long >0 , not stripped +} ++16 short 0 object x-system/obj ++18 short &020000 , 32100 required ++18 short &040000 , mau hardware required + +0 short 0512 pc 286 small model (COFF) +o{ +{ +16 short >0 executable x-system/exe ++12 long >0 , not stripped +} ++16 short 0 object x-system/obj +{ +16 short >0 +&22 short >0 , version %ld +} +} +0 short 0522 pc 286 large model (COFF) +o() + +0 short 0514 pc 386 +o{ +{ +16 short >0 executable x-system/exe ++12 long >0 , not stripped +} ++16 short 0 object x-system/obj +{ +16 short >0 +&22 short >0 , version %ld +} ++0 short !=0x8664 , 32 bit ++0 short 0x8664 , 64 bit +} +0 short 0x8664 pc 386 +o() +0 short 0524 pc 386 +o() +0 short 0604 pc alpha +o() + +0 short 0520 m68k +{ +16 short >0 executable x-system/exe ++12 long >0 , not stripped +} ++16 short 0 object x-system/obj +{ +16 short >0 ++20 short 0410 , pure ++20 short 0413 , paged ++22 short >0 , version %ld +} + +0 short 0521 m68k executable, shared x-system/exe +o{ ++12 long >0 , not stripped +} +0 short 0522 m68k executable, shared, paged x-system/exe +o() + +0 long 0x02c5e2c4 mvs OpenEdition object x-system/obj +&4 long 0x40404040 +0 long 0xc9c5e6d7 mvs OpenEdition executable x-system/exe +&4 long 0xd3d4c840 + +0 short 0530 u370 5.2/5.0 +o{ +{ +20 long !=0440 +&18 short &01 executable x-system/exe ++0 short &01==0 , pure ++12 long >0 , not stripped +} ++20 long 0440 shared library x-system/dll ++18 short &01==0 object x-system/obj ++18 byte &0x40 , BIG ++49 byte &0xf!=0 , pre-XA +{ +18 short &01 ++24 long >0 , version %ld +} +} +0 short 0531 amdahl 5.2 +o() +0 short 0534 amdahl 5.2 +o() +0 short 0535 u370 5.2 +o() + +0 short 0700 ncr tower32 +o{ ++18 short &0040000 68000 ++18 short &0040000==0 68020 ++18 short &0020000 \b+68881 +{ +18 short &0000001==0 object x-system/obj ++22 short >0 , version %ld +} +{ +18 short &0001 executable x-system/exe ++20 short 0413 , paged ++20 short 0443 shared library x-system/dll ++20 short 0410 , pure, swapped ++20 short 0407 , swapped ++12 long >0 , not stripped +} +} +0 short 0720 ncr towe32r/600 +o() +0 short 0740 ncr tower32/800 +o() +0 short 0610 ncr tower/XP rel 2 +o() +0 short 0615 ncr tower/XP rel 2 +o() +0 short 0620 ncr tower/XP rel 3 +o() +0 short 0625 ncr tower/XP rel 3 +o() +0 short 0630 ncr tower32/600/400 +o() +0 short 0640 ncr tower32/800 ++18 short &00040000 compatible +o() +0 short 0645 ncr tower32/800 68010 +o() + +0 short 0457 DG MV pure executable x-system/exe ++40 long >0 , not stripped ++2 short >0 , version %ld +0 short 0460 DG MV object x-system/obj ++2 short >0 , version %ld +0 short 0541 m88k pure executable x-system/exe ++12 long >0 , not stripped ++22 short >0 , version %ld +0 short 0555 m88k object x-system/obj ++22 short >0 , version %ld + +0 short &0xfffd==0x0160 mips +{ +18 short &02 executable x-system/exe ++20 short 0410 , pure ++20 short 0413 , paged ++20 short 0443 , shared library x-system/dll ++8 long !=0 , not stripped +} ++18 short &02==0 object x-system/obj +o{ ++0 short &0x2 , little-endian ++22 byte * , version %ld ++23 byte * .%ld +} +0 short &0xfffd==0x180 mips ucode x-system/obj +o() +0 long 0xdeadadb0 mips core dump x-system/core +f{ ++4 long 1 +&16 string * from `%s' ++0 long &0xff==0xb0 , 32-bit, old ++0 long &0xff==0xbb , 32-bit ++0 long &0xff==0x40 , 64-bit +} +0 long 0xbabec0bb mips core dump x-system/core +f() +0 long 0xdeadad40 mips core dump x-system/core +f() + +0 short 0603 alpha +{ +22 short &02 +&22 short &030000!=020000 executable x-system/exe ++24 short 0410 , pure ++24 short 0413 , paged ++22 short &020000 , dynamically linked ++16 long !=0 , not stripped +} ++22 short &030000==020000 shared library x-system/dll +{ +22 short &030002==0 ++24 short 0407 object x-system/obj +} ++27 byte * , version %ld ++26 byte * .%ld + +0 short 0432 compiled terminfo entry +0 short 0433 curses screen image +0 short 0434 curses screen image + +0 long 0x14031008 tcpdump cons headers application/x-tcpdump +v{ ++4 long >0 , version %d +&8 long * .%d +} +0 long 0x14031004 tcpdump cons data application/x-tcpdump +v() + +257 string ustar pax archive application/pax +&156 match [gx] +o{ +&99 byte 0 +&100 match +([ 0-7])? +} + +257 string ustar\ \ gnu tar archive application/pax +o{ +&99 byte 0 +&100 match +([ 0-7])? ++0 match ???* , [ %s ... ] +} + +257 string ustar tar archive application/pax +o() + +99 byte 0 old tar archive application/pax +o() + +0 string \301\304\331\100\323\311\302\331\306 ca librarian archive application/pax + +0 match <[hH][tT][mM][lL]> html input text/html +0 match <!?(--)[Dd][Oo][Cc][Tt][Yy][Pp][Ee] [Hh][Tt][Mm][Ll] html input text/html + +0 long 0x02f78301 ++16 string TeX TeX dvi output application/x-dvi + +0 byte 0201 shell history application/sh +&1 byte <07 version %d + +0 byte 1 +&1 byte 0150 +&2 match [0-9][0-9][0-9][0-9][0-9] sccs application/x-sccs + +0 short 0x5a4d +o{ +&24 short 0x0040 +{ ++(@60H) short 0x454c os/2 linear ++(@60H) short 0x454e os/2 ++(@60H) short 0x4550 win32 ++(@60H+4) short 0x014c 386 ++(@60H+4) short 0x0150 powerpc ++(@60H+4) short 0x0162 mips ++(@60H+4) short 0x0166 mips, big endian? ++(@60H+4) short 0x0184 alpha ++(@60H+4) short 0x8664 386 +|(@60H+4) short * machine=0x%04lX +} ++(@60H+22) short &0x2002==0x0002 executable x-system/exe ++(@60H+22) short &0x2000 shared library x-system/dll ++(@60H+22) short &0x0120==0 , 16 bit ++(@60H+22) short &0x0120==0x0100 , 32 bit ++(@60H+22) short &0x0120==0x0020 , 64 bit ++(@60H+92) short 0 , unknown subsystem ++(@60H+92) short 1 , native ++(@60H+92) short 2 , windows gui ++(@60H+92) short 5 , os2 ++(@60H+92) short 7 , posix ++(@60H+92) short >7 , subsystem %d ++50 string PKWARE , self extracting zip ++36 string LHA's , self extracting lha ++233 string PKSFX2 , self extracting zip +} + +0 short 0x5a4c +o() + +0 long 0x4c000000 +&4 long 0x01140200 windows shortcut application/x-windows-lnk + +0 string PMCC windows GRP application/dos-grp +369 string MICROSOFT windows PIF application/dos-pif +0 long 0xffffffff dos device driver application/dos-drv +&name match *.(SYS|sys) +0 string LZ dos builtin +0 byte 0xe9 dos executable, COM application/x-dos +0 byte 0xeb dos executable, COM application/x-dos +0 byte 0xf0 dos library application/x-ar +0 byte 0x80 dos object, OMF application/dos-omf + +0 match x[ ]T[ ] ditroff application/x-ditroff +&4 string * for %s +0 string %! postscript input application/postscript +{ +2 string PS-Adobe- , conforming ++11 match +([0-9]).+([0-9]) , version %s +} +0 string %PDF adobe acrobat file application/x-pdf ++5 match +([0-9]).+([0-9]) , version %s +0 string @document( imagen input + +0 long 0x2e736e64 audio data audio/basic +o{ ++12 long 1 , 8-bit u-law ++12 long 2 , 8-bit linear pcm ++12 long 3 , 16-bit linear pcm ++12 long 4 , 24-bit linear pcm ++12 long 5 , 32-bit linear pcm ++12 long 6 , 32-bit floating point ++12 long 7 , 64-bit floating point ++12 long 23 , compressed (G.721 ADPCM) ++20 long =1 , mono ++20 long =2 , stereo ++20 short =3 , 3 channels ++20 short =4 , quad ++20 short >4 , %d channel%s ++16 long * , %d hz +} +0 long 0x0064732E dec audio data audio/x-dec +o() + +0 string Creative\ Voice\ File soundblaster audio data audio/x-soundblaster +0 long 0x4e54524b multitrack audio data file audio/x-multitrack ++4 long * , version %ld + +0 string MThd standard midi data audio/midi ++9 byte >0 , format %d ++11 byte >1 , %d channel%s +0 string CTMF creative music data +0 string SBI soundblaster instrument data +0 string Creative\ Voice\ File creative labs voice data +&19 byte 0x1A ++23 byte >0 , version %d ++22 byte >0 \b.%d + +0 string RIFF riff audio data audio/riff ++8 string AIFF aiff format ++8 string AIFC aiff-c format ++8 string WAVE , wave format ++8 string 8SVX 8svx format ++34 leshort >0 , %d bit ++22 leshort =1 , mono ++22 leshort =2 , stereo ++22 leshort =3 , 3 channels ++22 leshort =4 , quad ++22 leshort >4 , %d channel%s ++24 lelong >0 , %d hz + +8 long 0x41494646 aiff audio data audio/aiff +8 long 0x41494643 aiff-C audio data audio/aiff +0 long 0x4e54524b multitrack audio data audio/multitrack + +0 string ;vdb;ciao ciao virtual database application/x-ciao +0 string ;vdb; vdb archive application/pax ++5 string * , %s + +0 string #pragma pp:checkpoint preprocessor checkpoint application/x-libpp ++22 edit %"\([^"]*\)".*%\1% , version %s + +# +# pc application files +# + +0 string HDR*PowerBuilder power builder library application/x-powerbuilder ++18 edit %\([0-9][0-9]\)\([0-9][0-9]\).*%\1.\2% , version %s + +# +# database files +# + +0 long 0x13579ace dbm 1.x database application/x-dbm +0 string GDBM gnu dbm 2.x database application/x-gdbm + +12 long 0x00042253 bsd db queue ++16 long >0 version %d ++12 belong 0x00042253 , big-endian ++12 lelong 0x00042253 , little-endian + +0 long 0x00053162 bsd db btree application/x-bsd-db ++4 long >2 1.86 ++4 long <3 1.85 ++4 long >0 , version %d ++0 belong 0x00053162 , big-endian ++0 lelong 0x00053162 , little-endian ++16 long * , %d record%s ++20 long * , flags 0x%x + +12 long 0x00053162 bsd db btree ++16 long >0 version %d ++12 belong 0x00053162 , big-endian ++12 lelong 0x00053162 , little-endian + +0 long 0x00061561 bsd db hash application/x-bsd-db ++4 long >2 1.86 ++4 long <3 1.85 ++4 long >0 version %d ++8 long 4321 , big-endian ++8 long 1234 , little-endian ++56 long * , %d key%s + +12 long 0x00061561 bsd db hash ++16 long >0 version %d ++12 belong 0x00061561 , big-endian ++12 lelong 0x00061561 , little-endian + +0 long 0x950412de gnu message catalog application/x-locale ++4 long * , revision %d ++8 long * , %d message%s + +# +# from the net +# + +1 string # This is a shell archive. shar archive application/x-shar +81 string # This is a shell archive. shar archive application/x-shar + +0 short 0x1f9d compressed data application/zip +{ +2 byte &0200 +&2 byte &037>0 , %d bit%s +} +0 short 017436 packed data application/zip +0 short 0x9d1f compressed data application/zip ++2 byte &0200 , blocked ++2 byte &037>0 , with %d bit%s + +0 short 0x1f10 pzip compressed data application/pzip +&2 byte >0 , version %d +&3 byte <10 .%d + +0 short 0x1f8b pzip compressed data application/pzip +&10 short 0x9217 + +0 short 0x1f8b gzip compressed data application/gzip ++9 byte 0 , dos ++9 byte 1 , amiga ++9 byte 2 , vms ++9 byte 3 , unix ++9 byte 5 , atari ++9 byte 6 , os/2 ++9 byte 7 , mac ++9 byte 10 , tops/20 ++9 byte 11 , win/32 ++2 byte <8 , reserved ++2 byte 8 , deflate ++3 byte &0x1 , ascii ++3 byte &0x2 , continuation ++3 byte &0x4 , extra field ++3 byte &0x8 , original name ++3 byte &0x10 , comment ++3 byte &0x20 , encrypted ++8 byte 2 , max compression ++8 byte 4 , max speed ++4 ledate >0 , %s + +0 string BZh bzip compressed data application/zip ++3 byte >='0'&<='9' , %c00k blocks +0 long 0x0000abcd NOC newbridge raw stats +v{ ++4 short * , version %d ++6 short * .%d +} +0 long 0x0e130414 NOC switch stats +v() +0 long 0x0e13130d NOC switch summary stats +v() + +0 leshort 0 windows icon resource application/x-ms-icon +&2 leshort 1 ++4 leshort x , %d icon%s + +0 string begin 0 uuencoded data application/x-uuencode +0 string \x89PNG PNG image data image/png +&4 belong 0x0d0a1a0a ++16 belong x , %ld x ++20 belong x %ld ++24 byte x , %d-bit ++25 byte 0 , grayscale ++25 byte 2 , color RGB ++25 byte 3 , colormap ++25 byte 4 , gray+alpha ++25 byte 6 , color RGBA +#+26 byte 0 , deflate/32K ++28 byte 0 , non-interlaced ++28 byte 1 , interlaced +0 string \377\330\377 JPEG image image/jpeg +0 string GIF GIF image image/gif ++3 string * , version %-.3s +{ +6 leshort >0 , %d +&8 leshort >0 x %d +} ++10 byte &0x40 , interlaced ++10 byte &0x03==0x00 , 2 colors ++10 byte &0x03==0x01 , 4 colors ++10 byte &0x03==0x02 , 8 colors ++10 byte &0x03==0x03 , 16 colors ++10 byte &0x03==0x04 , 32 colors ++10 byte &0x03==0x05 , 64 colors ++10 byte &0x03==0x06 , 128 colors ++10 byte &0x03==0x07 , 256 colors +0 short 0x4d4d TIFF image, big-endian image/tiff ++2 short >0 , version %d +0 short 0x4949 TIFF image, little-endian image/tiff ++2 short >0 , version %d +0 short 000732 sgi imagelib image image/x-imagelib ++6 short * , %d ++8 short * x %d + +0 string gimp xcf gimp XCF image image/x-gimp ++9 string file , version 0 +{ +9 string v , version +&10 string * %s +} ++14 belong x , %lu x ++18 belong x %lu ++22 belong 0 , rgb color ++22 belong 1 , greyscale ++22 belong 2 , indexed color + +0 string MOVI sgi movie video/x-sgi + +0 byte 0 +&4 string moov quicktime movie video/quicktime +0 byte 0 +&4 string mdat quicktime movie video/quicktime +8 string AVI avi movie video/avi +0 long 0x000001BA mpeg movie video/mpeg +0 long 0x000001B3 mpeg movie video/mpeg + +0 string <MakerFile frame maker file application/framemaker +0 string {\\rtf rich text application/rtf + +0 long 0xd0cf11e0 ms powerpoint document application/x-powerpoint + +0 string ms C/C++ program database ms program database application/x-dbx ++33 string * , version %s + +0 string \377WPC corel wordperfect document application/x-wordperfect + +0 beshort 0xedab +&2 beshort 0xeedb red hat package manager ++4 byte * v%d ++8 beshort 1 i386 ++8 beshort 2 alpha ++8 beshort 3 sparc ++8 beshort 4 mips ++8 beshort 5 powerpc ++8 beshort 6 68k ++8 beshort 7 sgi ++8 beshort >7 unknown ++6 beshort 0 binary ++6 beshort 1 source ++10 string * , %s + +0 short 0x9900 pgp key public ring application/pgp +0 short 0x9501 pgp key security ring application/pgp +0 short 0x9500 pgp key security ring application/pgp +0 string -----BEGIN\040PGP pgp armored data application/pgp ++15 string PUBLIC\040KEY\040BLOCK- , public key block ++15 string MESSAGE- , message ++15 string SIGNED\040MESSAGE- , signed message ++15 string PGP\040SIGNATURE- , signature + +0 string Core osf unknown core dump x-system/core +&name match core* ++24 string * from `%s' + +0 match From[ ] mail message message/rfc822 +0 match (BABYL|From|Received|Return-Path|To)?(:)[ ] mail message message/partial + +0 string \001fcp X11 portable compiled font x-X11/font + +0 string \357\273\277 utf-8 encoded text application/x-iconv +0 string \376\377 utf-16 encoded text application/x-iconv +0 string \377\376 utf-16 encoded text, little-endian application/x-iconv + +32769 string CD001 ISO 9660 CD-ROM filesystem image data/x-filesystem ++32808 string * , '%s' ++34816 string \000CD001\001EL\ TORITO\ SPECIFICATION , bootable +37633 string CD001 ISO 9660 CD-ROM filesystem image, raw 2352 byte sectors data/x-filesystem +32776 string CDROM High Sierra CD-ROM filesystem image data/x-filesystem + +# +# front compression data +# + +0 byte 0 +&1 edit %^\([A-Z_][A-Z_]*\)-\([^0-9]*\)-\([0-9][0-9]\)%\1 data, with \2, version \3%l %s application/x-%s +0 byte 0 +&1 edit %^\([A-Z_][A-Z_]*\)\([0-9][0-9]\)%\1 data, version \2%l %s application/x-%s + +# +# generic binary magic +# + +0 long 0x00010203 +&4 string * %s application/x-%s +&12 string * %s data +&24 version * , version %s ++28 long >0 , size %u +{ +&28 long >=4 +&32 long >0 , %u +} +{ +&28 long >=8 +&36 long >0 , %u +} + +# +# local additions +# + +0 match info mam mam program application/x-mam +0 edit %^!<\([^>]*\)>.*%\1%l %s data application/x-%s +0 string \015\023\007\000 ast message catalog application/x-locale ++4 string * , %s + +# +# last chance +# + +name match *.(o|obj) unknown object x-system/obj +name match core unknown core dump x-system/core +name match core.* unknown core dump x-system/core + +# +# we resisted til now +# + +0 void registry() +|name match *.acp Office.ActorPreview application/x-ms-office +|name match *.act Office.Actor application/x-ms-office +|name match *.ade Microsoft Access project extension application/x-ms +|name match *.adp Microsoft Access project application/x-ms +|name match *.aif AIFF Audio audio/x-aiff +|name match *.aifc AIFF Audio audio/aiff +|name match *.aiff AIFF Audio audio/aiff +|name match *.aim AOL Instant Messenger Launch application/x-aim +|name match *.ani Animated Cursor application/x-ms-anifile +|name match *.app Application file application/x-ms +|name match *.arc WinZip File application/x-ms-winzip +|name match *.arj WinZip File application/x-ms-winzip +|name match *.art ART Image image/x-jg +|name match *.asp Active Server Page application/x-ms +|name match *.asx Windows Media Audio / Video application/x-ms +|name match *.au Sound Clip audio/basic +|name match *.avi Video Clip video/avi +|name match *.awx Custom AppWizard application/x-ms-awxfile +|name match *.b64 WinZip File application/x-ms-winzip +|name match *.bas Microsoft Visual Basic class module application/x-ms +|name match *.bat MS-DOS Batch File application/x-ms-batfile +|name match *.bfc Briefcase application/x-ms-briefcase +|name match *.bhx WinZip File application/x-ms-winzip +|name match *.bmp Bitmap Image image/bmp +|name match *.bpg Borland Project Group application/x-ms-borlandprojectgroup +|name match *.bpk C++Builder Package application/x-ms-bcbpackage +|name match *.bpr C++Builder Project application/x-ms-bcbproject +|name match *.bsc Browser Information application/x-ms-bscfile +|name match *.cda CD Audio Track application/x-ms-cdafile +|name match *.cdf Channel File application/x-netcdf +|name match *.cer Internet Security Certificate application/x-x509-ca-cert +|name match *.cfg CFG File application/x-ms-cfg_auto_file +|name match *.chm Compiled HTML Help file application/x-ms-help +|name match *.cil Clip Gallery Download Package application/x-ms-clipgallerydownloadpackage +|name match *.class Java class file application/x-java +|name match *.clp Clipboard Clip application/x-ms-clpfile +|name match *.cmd Windows Command Script application/x-ms-cmdfile +|name match *.com MS-DOS Application application/x-ms-comfile +|name match *.cpl Control Panel extension application/x-ms-cplfile +|name match *.cpp C++ Source File application/x-c++ +|name match *.crt Internet Security Certificate application/x-x509-ca-cert +|name match *.css HyperText Style Sheet text/css +|name match *.csv Microsoft Excel Comma Separated Values File application/x-ms-excel +|name match *.cur Cursor application/x-ms-curfile +|name match *.cxx C++ Source File application/x-c++ +|name match *.dcx DCX Image Document application/x-ms-dcximage +|name match *.der Internet Security Certificate application/x-x509-ca-cert +|name match *.dfm C++Builder Form application/x-ms-bcbform +|name match *.dic Text Document application/x-ms-txtfile +|name match *.dif DV video/x-dv +|name match *.dll Windows dynamic link library application/x-ms-dll +|name match *.doc Microsoft Word Document application/x-ms-word +|name match *.dot Microsoft Word Template application/x-ms-word +|name match *.drv Device driver application/x-ms-drvfile +|name match *.dsm Developer Studio Macro File application/x-ms-dsmfile +|name match *.dsn Microsoft OLE DB Provider for ODBC Drivers application/x-ms-msdasql +|name match *.dsp Project File application/x-ms-dspfile +|name match *.dsw Project Workspace application/x-ms-dswfile +|name match *.dv DV video/x-dv +|name match *.ebh Ebasic Files application/x-ms-hclebasich +|name match *.ebx Ebrun Files application/x-ms-hclebrun +|name match *.exc Text Document application/x-ms-txtfile +|name match *.exe Application application/x-msdownload +|name match *.fav Outlook Bar Shortcuts application/x-ms-outlook +|name match *.fdf Adobe Acrobat Forms Document application/x-ms-acroexch +|name match *.fnd Saved Search application/x-ms-fndfile +|name match *.fon Font file application/x-ms-fonfile +|name match *.fs Ftp Files application/x-ms-hclftp +|name match *.fxp Microsoft Visual FoxPro compiled program application/x-ms-foxpro +|name match *.gfi Genigraphics GraphicsLink application/x-ms-graphicslink +|name match *.gfx Genigraphics GraphicsLink application/x-ms-graphicslink +|name match *.gif GIF Image image/gif +|name match *.gim Genigraphics GraphicsLink application/x-ms-graphicslink +|name match *.gix Genigraphics GraphicsLink application/x-ms-graphicslink +|name match *.gna Genigraphics GraphicsLink application/x-ms-graphicslink +|name match *.gnx Genigraphics GraphicsLink application/x-ms-graphicslink +|name match *.gra Microsoft Graph 97 Chart application/x-ms-msgraph +|name match *.grp Microsoft Program Group application/x-ms-msprogramgroup +|name match *.gst MSMap.Datainst.8 application/x-ms-msmap +|name match *.gwx Genigraphics GraphicsLink application/x-ms-graphicslink +|name match *.gwz Genigraphics GraphicsLink application/x-ms-graphicslink +|name match *.gz WinZip File application/gzip +|name match *.hep HostExplorer Session Profile application/x-ms-hostexplorer +|name match *.hlp Help File application/x-ms-help +|name match *.hpp C++ Header File application/x-c++ +|name match *.hqx WinZip File application/mac-binhex40 +|name match *.hs3 HostExplorer Hotspot Definition application/x-ms-hostexplorer +|name match *.hs5 HostExplorer Hotspot Definition application/x-ms-hostexplorer +|name match *.hsv HostExplorer Hotspot Definition application/x-ms-hostexplorer +|name match *.ht HyperTerminal File application/x-ms-htfile +|name match *.hta HTML program application/x-ms +|name match *.htm html source text/html +|name match *.hts Hummingbird Telnet Program v6.0.0.0 application/x-ms-hummingbird +|name match *.htt HyperText Template text/webviewhtml +|name match *.htw HTML Document application/x-ms-htmlfile +|name match *.htx HTML Document text/html +|name match *.hxx C++ Header File application/x-c++ +|name match *.ico Icon application/x-ms-icon +|name match *.idb Intermediate File application/x-ms-mdpxfile +|name match *.ilk Intermediate File application/x-ms-mdpxfile +|name match *.inf Setup Information application/x-ms-setup +|name match *.ini Configuration Settings application/x-ms-config +|name match *.ins Internet Communication Settings application/x-internet-signup +|name match *.iqy Microsoft Excel Web Query File application/x-ms-iqyfile +|name match *.isp Internet Communication Settings application/x-internet-signup +|name match *.its Internet Document Set application/x-ms-its +|name match *.ivt InfoViewer Title application/x-ms-ivt +|name match *.jfif JPEG Image image/jpeg +|name match *.job Scheduler Job Object application/x-ms-jobobject +|name match *.jod Microsoft.Jet.OLEDB.3.51 application/x-ms-microsoft +|name match *.jpe JPEG Image image/jpeg +|name match *.jpeg JPEG Image image/jpeg +|name match *.jpg JPEG Image image/jpeg +|name match *.js JavaScript file application/x-java +|name match *.jse JavaScript Encoded Script file application/x-ms +|name match *.jsp JavaScript Page application/x-ms +|name match *.km3 HostExplorer KeyMap Definition application/x-ms-hostexplorer +|name match *.km5 HostExplorer KeyMap Definition application/x-ms-hostexplorer +|name match *.kmv HostExplorer KeyMap Definition application/x-ms-hostexplorer +|name match *.lam LAMDocument application/x-ms-lamdocument +|name match *.ldb Microsoft Access Record-Locking Information application/x-ms-access +|name match *.lnk Shortcut application/x-ms +|name match *.log Text Document application/x-text +|name match *.lzh WinZip File application/x-ms-winzip +|name match *.m1v Movie Clip video/mpeg +|name match *.mac MacPaint Image image/x-macpaint +|name match *.mad Microsoft Access Module Shortcut application/x-ms-access +|name match *.maf Microsoft Access Form Shortcut application/x-ms-access +|name match *.mam Microsoft Access Macro Shortcut application/x-ms-access +|name match *.maq Microsoft Access Query Shortcut application/x-ms-access +|name match *.mar Microsoft Access Report Shortcut application/x-ms-access +|name match *.mat Microsoft Access Table Shortcut application/x-ms-access +|name match *.mda Microsoft Access Add-in application/x-ms-access +|name match *.mdb Microsoft Access Database application/x-ms-access +|name match *.mdb Microsoft Access program application/x-ms +|name match *.mde Microsoft Access MDE Database application/x-ms-access +|name match *.mdn Microsoft Access Blank Database Template application/x-ms-access +|name match *.mdp Project Workspace application/x-ms-mdpfile +|name match *.mdt Microsoft Access Add-in Data application/x-ms-access +|name match *.mdw Microsoft Access Workgroup Information application/x-ms-access +|name match *.mdz Microsoft Access Database Wizard Template application/x-ms-access +|name match *.mht Microsoft MHTML Document 4.0 message/rfc822 +|name match *.mhtml Microsoft MHTML Document 4.0 message/rfc822 +|name match *.mid MIDI Sequence audio/mid +|name match *.mmm Media Clip application/x-ms-mplayer +|name match *.mov QuickTime Movie video/quicktime +|name match *.mp2 Movie Clip video/mpeg +|name match *.mpa Movie Clip video/mpeg +|name match *.mpe Movie Clip video/mpeg +|name match *.mpeg Movie Clip video/mpeg +|name match *.mpg Movie Clip video/mpeg +|name match *.msc Microsoft Common Console Document application/x-ms-mmc +|name match *.msg Outlook Item application/x-ms-msgfile +|name match *.msi Microsoft Windows Installer package application/x-ms +|name match *.msp Windows Installer patch application/x-ms +|name match *.mst Visual Test source files application/x-ms +|name match *.nsc Netscape Conference Call File application/x-conference +|name match *.obd Microsoft Office Binder application/x-ms-office +|name match *.obt Microsoft Office Binder Template application/x-ms-office +|name match *.obz Microsoft Office Binder Wizard application/x-ms-office +|name match *.odl Object Definition Language File application/x-ms-odlfile +|name match *.ofn Other Office Documents... application/x-ms-office +|name match *.oft Outlook Item Template application/x-ms-outlook +|name match *.ops Microsoft Office profile settings file application/x-ms +|name match *.opx MS Organization Chart 2.0 application/x-ms-orgpluswopx +|name match *.oss Office Search application/x-ms-ossfile +|name match *.pcd Photo CD Image application/x-ms-pcdfile +|name match *.pch Intermediate File application/x-ms-mdpxfile +|name match *.pct PICT Image image/pict +|name match *.pcx PCX Image application/x-ms-pcxfile +|name match *.pdb Intermediate File application/x-ms-mdpxfile +|name match *.pdf Adobe Acrobat Document application/pdf +|name match *.pfm Type 1 Font file application/x-ms-pfmfile +|name match *.php Perl CGI Script File application/x-perl +|name match *.pic PICT Image image/pict +|name match *.pict PICT Image image/pict +|name match *.pif Shortcut to MS-DOS Program application/x-ms-piffile +|name match *.pif Shortcut to MS-DOS program application/x-ms +|name match *.pkg Microsoft Developer Extension application/x-ms-pkgfile +|name match *.pma Performance Monitor File application/x-ms-perffile +|name match *.pmc Performance Monitor File application/x-ms-perffile +|name match *.pml Performance Monitor File application/x-ms-perffile +|name match *.pmr Performance Monitor File application/x-ms-perffile +|name match *.pmw Performance Monitor File application/x-ms-perffile +|name match *.pnf Precompiled Setup Information application/x-ms-pnffile +|name match *.png PNG Image application/x-ms-pngfile +|name match *.pntg MacPaint Image image/x-macpaint +|name match *.pop HostExplorer Poppad Definition application/x-ms-hostexplorer +|name match *.pot Microsoft PowerPoint Template application/vnd.ms-powerpoint +|name match *.ppa Microsoft PowerPoint Addin application/vnd.ms-powerpoint +|name match *.pps Microsoft PowerPoint SlideShow application/vnd.ms-powerpoint +|name match *.ppt Microsoft PowerPoint Presentation application/vnd.ms-powerpoint +|name match *.prf System file application/x-ms +|name match *.prg Program source file application/x-ms +|name match *.psd Photoshop Image image/x-photoshop +|name match *.pwz Microsoft PowerPoint Wizard application/vnd.ms-powerpoint +|name match *.qif QuickTime Image image/x-quicktime +|name match *.qk3 HostExplorer QuickKeys application/x-ms-hostexplorer +|name match *.qk5 HostExplorer QuickKeys application/x-ms-hostexplorer +|name match *.qkv HostExplorer QuickKeys application/x-ms-hostexplorer +|name match *.qt QuickTime Movie video/quicktime +|name match *.qti QuickTime Image image/x-quicktime +|name match *.qtif QuickTime Image image/x-quicktime +|name match *.qtp QuickTime Preferences application/x-ms-quicktimepreferences +|name match *.qts QuickTime application/x-ms-quicktimesystem +|name match *.qtx QuickTime Extension application/x-ms-quicktimeextension +|name match *.que Scheduler Queue Object application/x-ms-queueobject +|name match *.rc Resource Template application/x-ms-rcfile +|name match *.rct Resource Template application/x-ms-rcfile +|name match *.reg Registration Entries application/x-ms-regfile +|name match *.res Intermediate File application/x-ms-mdpxfile +|name match *.rmi MIDI Sequence audio/mid +|name match *.rnk Dial-Up Shortcut application/x-ms-rnkfile +|name match *.rtf Rich Text Format application/x-ms-word +|name match *.rx XRX Files application/x-ms-hclbroadway +|name match *.sbr Intermediate File application/x-ms-mdpxfile +|name match *.sc2 Microsoft Schedule+ 7.0 Application application/x-ms-scheduleplus +|name match *.scd Microsoft Schedule+ 7.0 Application application/x-ms-scheduleplus +|name match *.scf Windows Explorer Command application/x-ms-explorer +|name match *.sch Microsoft Schedule+ 7.0 Application application/x-ms-scheduleplus +|name match *.scp Text Document application/x-ms-txtfile +|name match *.scr Screen Saver application/x-ms-scrfile +|name match *.sct Windows Script Component application/x-ms +|name match *.sd2 Sound Designer 2 audio/x-sd2 +|name match *.ses Xsession Files application/x-ms-hclxsession +|name match *.shb Shortcut into a document application/x-ms-docshortcut +|name match *.shs Scrap object application/x-ms-shellscrap +|name match *.shtml Netscape Hypertext Document application/x-ms-netscapemarkup +|name match *.slk Microsoft Excel SLK Data Import Format application/x-ms-excel +|name match *.snd Sound Clip audio/basic +|name match *.stm HTML Document text/html +|name match *.sys System file application/x-ms-sysfile +|name match *.taz WinZip File application/x-ms-winzip +|name match *.tga TGA Image application/x-ms-tgafile +|name match *.tif TIF Image Document image/tiff +|name match *.tlb Type Library application/x-ms-tlbfile +|name match *.ttf TrueType Font file application/x-ms-ttffile +|name match *.txt Text Document text/plain +|name match *.tz WinZip File application/x-ms-winzip +|name match *.udl Microsoft Data Link application/x-ms-msdasc +|name match *.url Internet Shortcut application/x-ms-internetshortcut +|name match *.uue WinZip File application/x-ms-winzip +|name match *.vb Microsoft Visual Basic Scripting Edition (VBScript) file application/x-ms +|name match *.vbe VBScript Encoded Script file application/x-ms +|name match *.vbs VBScript file application/x-ms +|name match *.vir Virus Infected File application/x-ms-virus +|name match *.wav Wave Sound audio/x-wav +|name match *.wbk Microsoft Word Backup Document application/x-ms-word +|name match *.wiz Microsoft Word Wizard application/x-ms-word +|name match *.wll Microsoft Word Addin application/x-ms-word +|name match *.wpd corel wordperfect document application/x-wordperfect +|name match *.wri Write Document application/x-ms-wrifile +|name match *.wrl SGI.CosmoPlayer.1 application/x-ms-sgi +|name match *.wrz SGI.CosmoPlayer.1 application/x-ms-sgi +|name match *.ws Wstart Files application/x-ms-hclwstart +|name match *.wsc Windows Script Component application/x-ms +|name match *.wsf Windows Script file application/x-ms +|name match *.wsh Windows Script Host Settings file application/x-ms +|name match *.wtx Text Document application/x-ms-txtfile +|name match *.xbm Netscape Hypertext Document image/x-xbitmap +|name match *.xif XIF Image Document application/x-ms-xifimage +|name match *.xla Microsoft Excel Add-In application/x-ms-excel +|name match *.xlb Microsoft Excel Worksheet application/x-ms-excel +|name match *.xlc Microsoft Excel Chart application/x-ms-excel +|name match *.xld Microsoft Excel 5.0 DialogSheet application/x-ms-excel +|name match *.xlk Microsoft Excel Backup File application/x-ms-excel +|name match *.xll Microsoft Excel XLL Add-In application/x-ms-excel +|name match *.xlm Microsoft Excel 4.0 Macro application/x-ms-excel +|name match *.xls Microsoft Excel Worksheet application/vnd.ms-excel +|name match *.xlt Microsoft Excel Template application/x-ms-excel +|name match *.xlv Microsoft Excel VBA Module application/x-ms-excel +|name match *.xlw Microsoft Excel Workspace application/x-ms-excel +|name match *.xnk Microsoft Exchange Shortcut application/x-ms-exchange +|name match *.xs Microsoft Exchange start Files application/x-ms-exchange +|name match *.xxe WinZip File application/x-ms-winzip +|name match *.zip WinZip File application/x-zip-compressed diff --git a/src/lib/libast/misc/mime.c b/src/lib/libast/misc/mime.c new file mode 100644 index 0000000..49cfaa1 --- /dev/null +++ b/src/lib/libast/misc/mime.c @@ -0,0 +1,839 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * Glenn Fowler + * AT&T Research + * + * mime/mailcap support library + */ + +static const char id[] = "\n@(#)$Id: mime library (AT&T Research) 2002-10-29 $\0\n"; + +static const char lib[] = "libast:mime"; + +#include "mimelib.h" + +typedef struct Att_s +{ + struct Att_s* next; + char* name; + char* value; +} Att_t; + +typedef struct Cap_s +{ + struct Cap_s* next; + unsigned long flags; + Att_t att; + char* test; + char data[1]; +} Cap_t; + +typedef struct +{ + Dtlink_t link; + Cap_t* cap; + Cap_t* pac; + char name[1]; +} Ent_t; + +typedef struct +{ + char* data; + int size; +} String_t; + +typedef struct +{ + char* next; + String_t name; + String_t value; +} Parse_t; + +typedef struct +{ + const char* pattern; + int prefix; + Sfio_t* fp; + int hit; +} Walk_t; + +/* + * convert c to lower case + */ + +static int +lower(register int c) +{ + return isupper(c) ? tolower(c) : c; +} + +/* + * Ent_t case insensitive comparf + */ + +static int +order(Dt_t* dt, void* a, void* b, Dtdisc_t* disc) +{ + return strcasecmp(a, b); +} + +/* + * Cap_t free + */ + +static void +dropcap(register Cap_t* cap) +{ + register Att_t* att; + + while (att = cap->att.next) + { + cap->att.next = att->next; + free(att); + } + free(cap); +} + +/* + * Ent_t freef + */ + +static void +drop(Dt_t* dt, void* object, Dtdisc_t* disc) +{ + register Ent_t* ent = (Ent_t*)object; + register Cap_t* cap; + + while (cap = ent->cap) + { + ent->cap = cap->next; + dropcap(cap); + } + free(ent); +} + +/* + * add mime type entry in s to mp + */ + +int +mimeset(Mime_t* mp, register char* s, unsigned long flags) +{ + register Ent_t* ent; + register Cap_t* cap; + register Att_t* att; + register char* t; + register char* v; + register char* k; + char* x; + Att_t* tta; + int q; + + for (; isspace(*s); s++); + if (*s && *s != '#') + { + cap = 0; + for (v = s; *v && *v != ';'; v++) + if (isspace(*v) || *v == '/' && *(v + 1) == '*') + *v = 0; + if (*v) + { + *v++ = 0; + do + { + for (; isspace(*v); v++); + if (cap) + { + for (t = v; *t && !isspace(*t) && *t != '='; t++); + for (k = t; isspace(*t); t++); + if (!*t || *t == '=' || *t == ';') + { + if (*t) + while (isspace(*++t)); + *k = 0; + k = v; + v = t; + } + else + k = 0; + } + if (*v == '"') + q = *v++; + else + q = 0; + for (t = v; *t; t++) + if (*t == '\\') + { + switch (*(t + 1)) + { + case 0: + case '\\': + case '%': + *t = *(t + 1); + break; + default: + *t = ' '; + break; + } + if (!*++t) + break; + } + else if (*t == q) + { + *t = ' '; + q = 0; + } + else if (*t == ';' && !q) + { + *t = ' '; + break; + } + for (; t > v && isspace(*(t - 1)); t--); + if (t <= v && (!cap || !k)) + break; + if (!cap) + { + if (!(cap = newof(0, Cap_t, 1, strlen(v) + 1))) + return -1; + if (*t) + *t++ = 0; + tta = &cap->att; + tta->name = "default"; + x = strcopy(tta->value = cap->data, v) + 1; + } + else if (k) + { + if (*t) + *t++ = 0; + if (!(att = newof(0, Att_t, 1, 0))) + return -1; + x = strcopy(att->name = x, k) + 1; + x = strcopy(att->value = x, v) + 1; + tta = tta->next = att; + if (!strcasecmp(k, "test")) + cap->test = att->value; + } + } while (*(v = t)); + } + ent = (Ent_t*)dtmatch(mp->cap, s); + if (cap) + { + if (ent) + { + register Cap_t* dup; + register Cap_t* pud; + + for (pud = 0, dup = ent->cap; dup; pud = dup, dup = dup->next) + if (!cap->test && !dup->test || cap->test && dup->test && streq(cap->test, dup->test)) + { + if (flags & MIME_REPLACE) + { + if (pud) + pud->next = cap; + else + ent->cap = cap; + if (!(cap->next = dup->next)) + ent->pac = cap; + cap = dup; + } + dropcap(cap); + return 0; + } + ent->pac = ent->pac->next = cap; + } + else if (!(ent = newof(0, Ent_t, 1, strlen(s) + 1))) + return -1; + else + { + strcpy(ent->name, s); + ent->cap = ent->pac = cap; + dtinsert(mp->cap, ent); + } + } + else if (ent && (flags & MIME_REPLACE)) + dtdelete(mp->cap, ent); + } + return 0; +} + +/* + * load mime type files into mp + */ + +int +mimeload(Mime_t* mp, const char* file, unsigned long flags) +{ + register char* s; + register char* t; + register char* e; + register int n; + Sfio_t* fp; + + if (!(s = (char*)file)) + { + flags |= MIME_LIST; + if (!(s = getenv(MIME_FILES_ENV))) + s = MIME_FILES; + } + for (;;) + { + if (!(flags & MIME_LIST)) + e = 0; + else if (e = strchr(s, ':')) + { + /* + * ok, so ~ won't work for the last list element + * we do it for MIME_FILES_ENV anyway + */ + + if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME"))) + { + sfputr(mp->buf, t, -1); + s += n - 1; + } + sfwrite(mp->buf, s, e - s); + if (!(s = sfstruse(mp->buf))) + return -1; + } + if (fp = tokline(s, SF_READ, NiL)) + { + while (t = sfgetr(fp, '\n', 1)) + if (mimeset(mp, t, flags)) + break; + sfclose(fp); + } + else if (!(flags & MIME_LIST)) + return -1; + if (!e) + break; + s = e + 1; + } + return 0; +} + +/* + * mimelist walker + */ + +static int +list(Dt_t* dt, void* object, void* context) +{ + register Walk_t* wp = (Walk_t*)context; + register Ent_t* ent = (Ent_t*)object; + register Cap_t* cap; + register Att_t* att; + + if (!wp->pattern || !strncasecmp(ent->name, wp->pattern, wp->prefix) && (!ent->name[wp->prefix] || ent->name[wp->prefix] == '/')) + { + wp->hit++; + for (cap = ent->cap; cap; cap = cap->next) + { + sfprintf(wp->fp, "%s", ent->name); + for (att = &cap->att; att; att = att->next) + { + sfprintf(wp->fp, "\n\t"); + if (att != &cap->att) + { + sfprintf(wp->fp, "%s", att->name); + if (*att->value) + sfprintf(wp->fp, " = "); + } + sfputr(wp->fp, att->value, -1); + } + sfprintf(wp->fp, "\n"); + } + } + return 0; +} + +/* + * find entry matching type + * if exact match fails then left and right x- and right version number + * permutations are attempted + */ + +static Ent_t* +find(Mime_t* mp, const char* type) +{ + register char* lp; + register char* rp; + register char* rb; + register char* rv; + register int rc; + register int i; + char* s; + Ent_t* ent; + char buf[256]; + + static const char* prefix[] = { "", "", "x-", "x-", "" }; + + if ((ent = (Ent_t*)dtmatch(mp->cap, type)) || + !(rp = strchr(lp = (char*)type, '/')) || + strlen(lp) >= sizeof(buf)) + return ent; + strcpy(buf, type); + rp = buf + (rp - lp); + *rp++ = 0; + if (*rp == 'x' && *(rp + 1) == '-') + rp += 2; + lp = buf; + if (*lp == 'x' && *(lp + 1) == '-') + lp += 2; + rb = rp; + for (rv = rp + strlen(rp); rv > rp && (isdigit(*(rv - 1)) || *(rv - 1) == '.'); rv--); + rc = *rv; + do + { + rp = rb; + do + { + for (i = 0; i < elementsof(prefix) - 1; i++) + { + sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp); + if (!(s = sfstruse(mp->buf))) + return 0; + if (ent = (Ent_t*)dtmatch(mp->cap, s)) + return ent; + if (rc) + { + *rv = 0; + sfprintf(mp->buf, "%s%s/%s%s", prefix[i], lp, prefix[i + 1], rp); + if (!(s = sfstruse(mp->buf))) + return 0; + if (ent = (Ent_t*)dtmatch(mp->cap, s)) + return ent; + *rv = rc; + } + } + while (*rp && *rp++ != '-'); + } while (*rp); + while (*lp && *lp++ != '-'); + } while (*lp); + return (Ent_t*)dtmatch(mp->cap, buf); +} + +/* + * list mime <type,data> for pat on fp + */ + +int +mimelist(Mime_t* mp, Sfio_t* fp, register const char* pattern) +{ + Ent_t* ent; + Walk_t ws; + + ws.fp = fp; + ws.hit = 0; + ws.prefix = 0; + if (ws.pattern = pattern) + { + while (*pattern && *pattern++ != '/'); + if (!*pattern || *pattern == '*' && !*(pattern + 1)) + ws.prefix = pattern - ws.pattern; + else if (ent = find(mp, ws.pattern)) + { + ws.pattern = 0; + list(mp->cap, ent, &ws); + return ws.hit; + } + } + dtwalk(mp->cap, list, &ws); + return ws.hit; +} + +/* + * get next arg in pp + * 0 returned if no more args + */ + +static int +arg(register Parse_t* pp, int first) +{ + register char* s; + register int c; + register int q; + int x; + + for (s = pp->next; isspace(*s) && *s != '\n'; s++); + if (!*s || *s == '\n') + { + pp->next = s; + return 0; + } + pp->name.data = s; + pp->value.data = 0; + q = 0; + x = 0; + while ((c = *s++) && c != ';' && c != '\n') + { + if (c == '"') + { + q = 1; + if (pp->value.data) + { + pp->value.data = s; + if (x) + x = -1; + else + x = 1; + } + else if (!x && pp->name.data == (s - 1)) + { + x = 1; + pp->name.data = s; + } + do + { + if (!(c = *s++) || c == '\n') + { + s--; + break; + } + } while (c != '"'); + if (first < 0 || x > 0) + { + c = ';'; + break; + } + } + else if (c == '=' && !first) + { + first = 1; + pp->name.size = s - pp->name.data - 1; + pp->value.data = s; + } + else if (first >= 0 && isspace(c)) + break; + } + pp->next = s - (c != ';'); + if (first >= 0 || !q) + for (s--; s > pp->name.data && isspace(*(s - 1)); s--); + if (pp->value.data) + pp->value.size = s - pp->value.data - (q && first < 0); + else + { + pp->value.size = 0; + pp->name.size = s - pp->name.data - (q && first < 0); + } + if (first >= 0 && pp->name.size > 0 && pp->name.data[pp->name.size - 1] == ':') + return 0; + return pp->name.size > 0; +} + +/* + * low level for mimeview() + */ + +static char* +expand(Mime_t* mp, register char* s, const char* name, const char* type, const char* opts) +{ + register char* t; + register char* v; + register int c; + register int e; + register int n; + Parse_t pp; + + mp->disc->flags |= MIME_PIPE; + for (;;) + { + switch (c = *s++) + { + case 0: + case '\n': + break; + case '%': + if ((c = *s++) == '{' && (e = '}') || c == '(' && (e = ')')) + { + for (t = s; *s && *s != e; s++); + n = s - t; + switch (*s) + { + case '}': + s++; + c = '{'; + break; + case ')': + s++; + if (c = *s) + s++; + break; + } + } + else + t = 0; + switch (c) + { + case 's': + v = (char*)name; + mp->disc->flags &= ~MIME_PIPE; + break; + case 't': + v = (char*)type; + break; + case '{': + for (t = s; *s && *s != '}'; s++); + if (*s && (c = s++ - t) && (pp.next = (char*)opts)) + while (arg(&pp, 0)) + if (pp.name.size == c && !strncasecmp(pp.name.data, t, c)) + { + if (pp.value.size) + sfwrite(mp->buf, pp.value.data, pp.value.size); + break; + } + continue; + default: + sfputc(mp->buf, c); + continue; + } + if (v && *v) + n = strlen(v); + else if (t) + v = t; + else + continue; + sfputr(mp->buf, fmtquote(v, 0, 0, n, FMT_SHELL), -1); + continue; + default: + sfputc(mp->buf, c); + continue; + } + break; + } + return sfstruse(mp->buf); +} + +/* + * return expanded command/path/value for <view,name,type,opts> + * return value valid until next mime*() call + */ + +char* +mimeview(Mime_t* mp, const char* view, const char* name, const char* type, const char* opts) +{ + register Ent_t* ent; + register Cap_t* cap; + register Att_t* att; + register char* s; + int c; + + if (ent = find(mp, type)) + { + cap = ent->cap; + if (!view || strcasecmp(view, "test")) + while (s = cap->test) + { + if (s = expand(mp, s, name, type, opts)) + { + Parse_t a1; + Parse_t a2; + Parse_t a3; + Parse_t a4; + + /* + * try to do a few common cases here + * mailcap consistency is a winning + * strategy + */ + + a1.next = s; + if (arg(&a1, -1)) + { + if ((c = *a1.name.data == '!') && --a1.name.size <= 0 && !arg(&a1, -1)) + goto lose; + if (a1.name.size == 6 && strneq(a1.name.data, "strcmp", 6) || a1.name.size == 10 && strneq(a1.name.data, "strcasecmp", 10)) + { + a2.next = a1.next; + if (!arg(&a2, -1)) + goto lose; + a3.next = a2.next; + if (!arg(&a3, -1)) + goto lose; + if (a2.name.size != a3.name.size) + c ^= 0; + else c ^= (a1.name.size == 6 ? strncmp : strncasecmp)(a2.name.data, a3.name.data, a2.name.size) == 0; + if (c) + break; + goto skip; + } + else if (a1.name.size == 4 && strneq(a1.name.data, "test", 4)) + { + if (!arg(&a1, -1)) + goto lose; + a2.next = a1.next; + if (!arg(&a2, -1) || a2.name.size > 2 || a2.name.size == 1 && *a2.name.data != '=' || a2.name.size == 2 && (!strneq(a1.name.data, "!=", 2) || !strneq(a2.name.data, "==", 2))) + goto lose; + a3.next = a2.next; + if (!arg(&a3, -1)) + goto lose; + if (*a3.name.data == '`' && *(a3.name.data + a3.name.size - 1) == '`') + { + a4 = a3; + a3 = a1; + a1 = a4; + } + if (*a1.name.data == '`' && *(a1.name.data + a1.name.size - 1) == '`') + { + a1.next = a1.name.data + 1; + if (!arg(&a1, -1) || a1.name.size != 4 || !strneq(a1.name.data, "echo", 4) || !arg(&a1, -1)) + goto lose; + a4.next = a1.next; + if (!arg(&a4, 1) || a4.name.size < 21 || !strneq(a4.name.data, "| tr '[A-Z]' '[a-z]'`", 21)) + goto lose; + } + else + a4.name.size = 0; + c = *a2.name.data == '!'; + if (a1.name.size != a3.name.size) + c ^= 0; + else c ^= (a4.name.size ? strncasecmp : strncmp)(a1.name.data, a3.name.data, a1.name.size) == 0; + if (c) + break; + goto skip; + } + } + lose: + if (!system(s)) + break; + } + skip: + if (!(cap = cap->next)) + return 0; + } + att = &cap->att; + if (view && *view && !streq(view, "-")) + while (strcasecmp(view, att->name)) + if (!(att = att->next)) + return 0; + return expand(mp, att->value, name, type, opts); + } + return 0; +} + +/* + * lower case identifier prefix strcmp + * if e!=0 then it will point to the next char after the match + */ + +int +mimecmp(register const char* s, register const char* v, char** e) +{ + register int n; + + while (isalnum(*v) || *v == *s && (*v == '_' || *v == '-' || *v == '/')) + if (n = lower(*s++) - lower(*v++)) + return n; + if (!isalnum(*s) && *s != '_' && *s != '-') + { + if (e) + *e = (char*)s; + return 0; + } + return lower(*s) - lower(*v); +} + +/* + * parse mime headers in strsearch(tab,num,siz) from s + * return >0 if mime header consumed + */ + +int +mimehead(Mime_t* mp, void* tab, size_t num, size_t siz, register char* s) +{ + register void* p; + char* e; + Parse_t pp; + Mimevalue_f set; + + set = mp->disc->valuef; + if (!strncasecmp(s, "original-", 9)) + s += 9; + if (!strncasecmp(s, "content-", 8)) + { + s += 8; + if ((p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, s, &e)) && *e == ':') + { + pp.next = e + 1; + if (arg(&pp, 1)) + { + if ((*set)(mp, p, pp.name.data, pp.name.size, mp->disc)) + return 0; + while (arg(&pp, 0)) + if (pp.value.size && + (p = strsearch(tab, num, siz, (Strcmp_f)mimecmp, pp.name.data, &e)) && + (*set)(mp, p, pp.value.data, pp.value.size, mp->disc)) + return 0; + return 1; + } + } + else if (strchr(s, ':')) + return 1; + } + return !strncasecmp(s, "x-", 2); +} + +/* + * open a mime library handle + */ + +Mime_t* +mimeopen(Mimedisc_t* disc) +{ + register Mime_t* mp; + + if (!(mp = newof(0, Mime_t, 1, 0))) + return 0; + mp->id = lib; + mp->disc = disc; + mp->dict.key = offsetof(Ent_t, name); + mp->dict.comparf = order; + mp->dict.freef = drop; + if (!(mp->buf = sfstropen()) || !(mp->cap = dtopen(&mp->dict, Dtoset))) + { + mimeclose(mp); + return 0; + } + return mp; +} + +/* + * close a mimeopen() handle + */ + +int +mimeclose(Mime_t* mp) +{ + if (mp) + { + if (mp->buf) + sfclose(mp->buf); + if (mp->cap) + dtclose(mp->cap); + if (mp->freef) + (*mp->freef)(mp); + free(mp); + } + return 0; +} diff --git a/src/lib/libast/misc/mimelib.h b/src/lib/libast/misc/mimelib.h new file mode 100644 index 0000000..ebfa343 --- /dev/null +++ b/src/lib/libast/misc/mimelib.h @@ -0,0 +1,52 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * mime/mailcap internal interface + */ + +#ifndef _MIMELIB_H +#define _MIMELIB_H 1 + +#include <ast.h> +#include <cdt.h> +#include <magic.h> +#include <tok.h> + +struct Mime_s; + +typedef void (*Free_f)(struct Mime_s*); + +#define _MIME_PRIVATE_ \ + Mimedisc_t* disc; /* mime discipline */ \ + Dtdisc_t dict; /* cdt discipline */ \ + Magicdisc_t magicd; /* magic discipline */ \ + Dt_t* cap; /* capability tree */ \ + Sfio_t* buf; /* string buffer */ \ + Magic_t* magic; /* mimetype() magic handle */ \ + Free_f freef; /* avoid magic lib if possible */ \ + +#include <mime.h> +#include <ctype.h> + +#endif diff --git a/src/lib/libast/misc/mimetype.c b/src/lib/libast/misc/mimetype.c new file mode 100644 index 0000000..63fdd0b --- /dev/null +++ b/src/lib/libast/misc/mimetype.c @@ -0,0 +1,69 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * mime/mailcap to magic support + */ + +#include "mimelib.h" + +/* + * close magic handle + * done this way so that magic is only pulled in + * if mimetype() is used + */ + +static void +drop(Mime_t* mp) +{ + if (mp->magic) + { + magicclose(mp->magic); + mp->magic = 0; + } +} + +/* + * return mime type for file + */ + +char* +mimetype(Mime_t* mp, Sfio_t* fp, const char* file, struct stat* st) +{ + if (mp->disc->flags & MIME_NOMAGIC) + return 0; + if (!mp->magic) + { + mp->magicd.version = MAGIC_VERSION; + mp->magicd.flags = MAGIC_MIME; + mp->magicd.errorf = mp->disc->errorf; + if (!(mp->magic = magicopen(&mp->magicd))) + { + mp->disc->flags |= MIME_NOMAGIC; + return 0; + } + mp->freef = drop; + magicload(mp->magic, NiL, 0); + } + return magictype(mp->magic, fp, file, st); +} diff --git a/src/lib/libast/misc/optctx.c b/src/lib/libast/misc/optctx.c new file mode 100644 index 0000000..1dbdf71 --- /dev/null +++ b/src/lib/libast/misc/optctx.c @@ -0,0 +1,70 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * _opt_infop_ context control + * + * allocate new context: + * new_context = optctx(0, 0); + * free new context: + * optctx(0, new_context); + * switch to new_context: + * old_context = optctx(new_context, 0); + * switch to old_context and free new_context: + * optctx(old_context, new_context); + */ + +#include <optlib.h> + +static Opt_t* freecontext; + +Opt_t* +optctx(Opt_t* p, Opt_t* o) +{ + if (o) + { + if (freecontext) + free(o); + else + freecontext = o; + if (!p) + return 0; + } + if (p) + { + o = _opt_infop_; + _opt_infop_ = p; + } + else + { + if (o = freecontext) + freecontext = 0; + else if (!(o = newof(0, Opt_t, 1, 0))) + return 0; + memset(o, 0, sizeof(Opt_t)); + o->state = _opt_infop_->state; + } + return o; +} diff --git a/src/lib/libast/misc/optesc.c b/src/lib/libast/misc/optesc.c new file mode 100644 index 0000000..8b023c5 --- /dev/null +++ b/src/lib/libast/misc/optesc.c @@ -0,0 +1,93 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * escape optget() special chars in s and write to sp + * esc == '?' or ':' also escaped + */ + +#include <optlib.h> +#include <ctype.h> + +int +optesc(Sfio_t* sp, register const char* s, int esc) +{ + register const char* m; + register int c; + + if (*s == '[' && *(s + 1) == '+' && *(s + 2) == '?') + { + c = strlen(s); + if (s[c - 1] == ']') + { + sfprintf(sp, "%-.*s", c - 4, s + 3); + return 0; + } + } + if (esc != '?' && esc != ':') + esc = 0; + while (c = *s++) + { + if (isalnum(c)) + { + for (m = s - 1; isalnum(*s); s++); + if (isalpha(c) && *s == '(' && isdigit(*(s + 1)) && *(s + 2) == ')') + { + sfputc(sp, '\b'); + sfwrite(sp, m, s - m); + sfputc(sp, '\b'); + sfwrite(sp, s, 3); + s += 3; + } + else + sfwrite(sp, m, s - m); + } + else if (c == '-' && *s == '-' || c == '<') + { + m = s - 1; + if (c == '-') + s++; + else if (*s == '/') + s++; + while (isalnum(*s)) + s++; + if (c == '<' && *s == '>' || isspace(*s) || *s == 0 || *s == '=' || *s == ':' || *s == ';' || *s == '.' || *s == ',') + { + sfputc(sp, '\b'); + sfwrite(sp, m, s - m); + sfputc(sp, '\b'); + } + else + sfwrite(sp, m, s - m); + } + else + { + if (c == ']' || c == esc) + sfputc(sp, c); + sfputc(sp, c); + } + } + return 0; +} diff --git a/src/lib/libast/misc/optget.c b/src/lib/libast/misc/optget.c new file mode 100644 index 0000000..bd4df04 --- /dev/null +++ b/src/lib/libast/misc/optget.c @@ -0,0 +1,5751 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2012 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * command line option parser and usage formatter + * its a monster but its all in one place + * widen your window while you're at it + */ + +#include <optlib.h> +#include <debug.h> +#include <ccode.h> +#include <ctype.h> +#include <errno.h> + +#define KEEP "*[A-Za-z][A-Za-z]*" +#define OMIT "*@(\\[[-+]*\\?*\\]|\\@\\(#\\)|Copyright \\(c\\)|\\$\\I\\d\\: )*" + +#define GO '{' /* group nest open */ +#define OG '}' /* group nest close */ + +#define OPT_WIDTH 80 /* default help text width */ +#define OPT_MARGIN 10 /* default help text margin */ +#define OPT_USAGE 7 /* usage continuation indent */ + +#define OPT_flag 0x001 /* flag ( 0 or 1 ) */ +#define OPT_hidden 0x002 /* remaining are hidden */ +#define OPT_ignorecase 0x004 /* arg match ignores case */ +#define OPT_invert 0x008 /* flag inverts long sense */ +#define OPT_listof 0x010 /* arg is ' ' or ',' list */ +#define OPT_number 0x020 /* arg is strtonll() number */ +#define OPT_oneof 0x040 /* arg may be set once */ +#define OPT_optional 0x080 /* arg is optional */ +#define OPT_string 0x100 /* arg is string */ + +#define OPT_preformat 0001 /* output preformat string */ +#define OPT_proprietary 0002 /* proprietary docs */ + +#define OPT_TYPE (OPT_flag|OPT_number|OPT_string) + +#define STYLE_posix 0 /* posix getopt usage */ +#define STYLE_short 1 /* [default] short usage */ +#define STYLE_long 2 /* long usage */ +#define STYLE_match 3 /* long description of matches */ +#define STYLE_options 4 /* short and long descriptions */ +#define STYLE_man 5 /* pretty details */ +#define STYLE_html 6 /* html details */ +#define STYLE_nroff 7 /* nroff details */ +#define STYLE_api 8 /* program details */ +#define STYLE_keys 9 /* translation key strings */ +#define STYLE_usage 10 /* escaped usage string */ + +#define FONT_BOLD 1 +#define FONT_ITALIC 2 +#define FONT_LITERAL 4 + +#define HELP_head 0x01 +#define HELP_index 0x02 + +#define TAG_NONE 0 +#define TAG_DIV 1 +#define TAG_DL 2 + +#define SEP(c) ((c)=='-'||(c)=='_') + +typedef struct Attr_s +{ + const char* name; + int flag; +} Attr_t; + +typedef struct Help_s +{ + const char* match; /* builtin help match name */ + const char* name; /* builtin help name */ + int style; /* STYLE_* */ + const char* text; /* --? text */ + unsigned int size; /* strlen text */ +} Help_t; + +typedef struct Font_s +{ + const char* html[2]; + const char* nroff[2]; + const char* term[2]; +} Font_t; + +typedef struct List_s +{ + int type; /* { - + : } */ + const char* name; /* list name */ + const char* text; /* help text */ +} List_t; + +typedef struct Msg_s +{ + const char* text; /* default message text */ + Dtlink_t link; /* cdt link */ +} Msg_t; + +typedef struct Save_s +{ + Dtlink_t link; /* cdt link */ + char text[1]; /* saved text text */ +} Save_t; + +typedef struct Push_s +{ + struct Push_s* next; /* next string */ + char* ob; /* next char in old string */ + char* oe; /* end of old string */ + char* nb; /* next char in new string */ + char* ne; /* end of new string */ + int ch; /* localize() translation */ +} Push_t; + +typedef struct Tag_s +{ + unsigned char level; /* indent level */ + unsigned char id; /* TAG_* id */ +} Tag_t; + +typedef struct Indent_s +{ + int stop; /* tab column position */ +} Indent_t; + +static Indent_t indent[] = +{ + 0,2, 4,10, 12,18, 20,26, 28,34, 36,42, 44,50, 0,0 +}; + +static const char* end[] = +{ + "", "</DIV>\n", "</DL>\n" +}; + +static const char term_off[] = {CC_esc,'[','0','m',0}; +static const char term_B_on[] = {CC_esc,'[','1','m',0}; +static const char term_I_on[] = {CC_esc,'[','1',';','4','m',0}; + +static const Font_t fonts[] = +{ + "", "", "", "", "", "", + "</B>", "<B>", "\\fP", "\\fB", &term_off[0], &term_B_on[0], + "</I>", "<I>", "\\fP", "\\fI", &term_off[0], &term_I_on[0], + "", "", "", "", "", "", + "</TT>","<TT>","\\fP", "\\f5", "", "", +}; + +static char native[] = ""; + +static unsigned char map[UCHAR_MAX]; + +static Optstate_t state; + +#if !_PACKAGE_astsa + +#define ID ast.id + +#define C(s) ERROR_catalog(s) +#define D(s) (state.msgdict && dtmatch(state.msgdict, (s))) +#define T(i,c,m) (X(c)?translate(i,c,C(m)):(m)) +#define X(c) (ERROR_translating()&&(c)!=native) +#define Z(x) C(x),sizeof(x)-1 + +/* + * translate with C_LC_MESSAGES_libast[] check + */ + +static char* +translate(const char* cmd, const char* cat, const char* msg) +{ + if (!X(cat)) + return (char*)msg; + if (cat != (const char*)ID && D(msg)) + cat = (const char*)ID; + return errorx(NiL, cmd, cat, msg); +} + +#else + +static char ID[] = "ast"; + +#define C(s) s +#define D(s) (state.msgdict && dtmatch(state.msgdict, (s))) +#define T(i,c,m) m +#define X(c) 0 +#define Z(x) C(x),sizeof(x)-1 + +#endif + +static const List_t help_head[] = +{ + '-', 0, + 0, + '+', C("NAME"), + C("options available to all \bast\b commands"), + '+', C("DESCRIPTION"), + C("\b-?\b and \b--?\b* options are the same \ +for all \bast\b commands. For any \aitem\a below, if \b--\b\aitem\a is not \ +supported by a given command then it is equivalent to \b--\?\?\b\aitem\a. The \ +\b--\?\?\b form should be used for portability. All output is written to the \ +standard error."), +}; + +static const Help_t styles[] = +{ + C("about"), "-", STYLE_match, + Z("List all implementation info."), + C("api"), "?api", STYLE_api, + Z("List detailed info in program readable form."), + C("help"), "", -1, + Z("List detailed help option info."), + C("html"), "?html", STYLE_html, + Z("List detailed info in html."), + C("keys"), "?keys", STYLE_keys, + Z("List the usage translation key strings with C style escapes."), + C("long"), "?long", STYLE_long, + Z("List long option usage."), + C("man"), "?man", STYLE_man, + Z("List detailed info in displayed man page form."), + C("nroff"), "?nroff", STYLE_nroff, + Z("List detailed info in nroff."), + C("options"), "?options", STYLE_options, + Z("List short and long option details."), + C("posix"), "?posix", STYLE_posix, + Z("List posix getopt usage."), + C("short"), "?short", STYLE_short, + Z("List short option usage."), + C("usage"), "?usage", STYLE_usage, + Z("List the usage string with C style escapes."), +}; + +static const List_t help_tail[] = +{ + ':', C("\?\?-\alabel\a"), + C("List implementation info matching \alabel\a*."), + ':', C("\?\?\aname\a"), + C("Equivalent to \b--help=\b\aname\a."), + ':', C("\?\?"), + C("Equivalent to \b--\?\?options\b."), + ':', C("\?\?\?\?"), + C("Equivalent to \b--\?\?man\b."), + ':', C("\?\?\?\?\?\?"), + C("Equivalent to \b--\?\?help\b."), + ':', C("\?\?\?\?\?\?\aitem\a"), + C("If the next argument is \b--\b\aoption\a then list \ +the \aoption\a output in the \aitem\a style. Otherwise print \ +\bversion=\b\an\a where \an\a>0 if \b--\?\?\b\aitem\a is supported, \b0\b \ +if not."), + ':', C("\?\?\?\?\?\?ESC"), + C("Emit escape codes even if output is not a terminal."), + ':', C("\?\?\?\?\?\?MAN[=\asection\a]]"), + C("List the \bman\b(1) section title for \asection\a [the \ +current command]]."), + ':', C("\?\?\?\?\?\?SECTION"), + C("List the \bman\b(1) section number for the current command."), + ':', C("\?\?\?\?\?\?TEST"), + C("Massage the output for regression testing."), +}; + +static const Attr_t attrs[] = +{ + "flag", OPT_flag, + "hidden", OPT_hidden, + "ignorecase", OPT_ignorecase, + "invert", OPT_invert, + "listof", OPT_listof, + "number", OPT_number, + "oneof", OPT_oneof, + "optional", OPT_optional, + "string", OPT_string, +}; + +static const char unknown[] = C("unknown option or attribute"); + +static const char* heading[] = +{ + C("INDEX"), + C("USER COMMANDS"), + C("SYSTEM LIBRARY"), + C("USER LIBRARY"), + C("FILE FORMATS"), + C("MISCELLANEOUS"), + C("GAMES and DEMOS"), + C("SPECIAL FILES"), + C("ADMINISTRATIVE COMMANDS"), + C("GUIs"), +}; + +/* + * list of common man page strings + * NOTE: add but do not delete from this table + */ + +static Msg_t C_LC_MESSAGES_libast[] = +{ + { C("APPLICATION USAGE") }, + { C("ASYNCHRONOUS EVENTS") }, + { C("BUGS") }, + { C("CAVEATS") }, + { C("CONSEQUENCES OF ERRORS") }, + { C("DESCRIPTION") }, + { C("ENVIRONMENT VARIABLES") }, + { C("EXAMPLES") }, + { C("EXIT STATUS") }, + { C("EXTENDED DESCRIPTION") }, + { C("INPUT FILES") }, + { C("LIBRARY") }, + { C("NAME") }, + { C("OPERANDS") }, + { C("OPTIONS") }, + { C("OUTPUT FILES") }, + { C("PLUGIN") }, + { C("SEE ALSO") }, + { C("STDERR") }, + { C("STDIN") }, + { C("STDOUT") }, + { C("SYNOPSIS") }, + { C("author") }, + { C("copyright") }, + { C("license") }, + { C("name") }, + { C("path") }, + { C("version") }, +}; + +/* + * 2007-03-19 move opt_info from _opt_info_ to (*_opt_data_) + * to allow future Opt_t growth + * by 2009 _opt_info_ can be static + */ + +#if _BLD_ast && defined(__EXPORT__) +#define extern extern __EXPORT__ +#endif + +extern Opt_t _opt_info_; + +Opt_t _opt_info_ = { 0,0,0,0,0,0,0,{0},{0},0,0,0,{0},{0},&state }; + +#undef extern + +__EXTERN__(Opt_t, _opt_info_); + +__EXTERN__(Opt_t*, _opt_infop_); + +Opt_t* _opt_infop_ = &_opt_info_; + +Optstate_t* +optstate(Opt_t* p) +{ + return &state; +} + +#if DEBUG || _BLD_DEBUG + +/* + * debug usage string segment format + */ + +static char* +show(register char* s) +{ + register int c; + register char* t; + register char* e; + + static char buf[32]; + + if (!s) + return "(null)"; + t = buf; + e = buf + sizeof(buf) - 2; + while (t < e) + { + switch (c = *s++) + { + case 0: + goto done; + case '\a': + *t++ = '\\'; + c = 'a'; + break; + case '\b': + *t++ = '\\'; + c = 'b'; + break; + case '\f': + *t++ = '\\'; + c = 'f'; + break; + case '\n': + *t++ = '\\'; + c = 'n'; + break; + case '\t': + *t++ = '\\'; + c = 't'; + break; + case '\v': + *t++ = '\\'; + c = 'v'; + break; + } + *t++ = c; + } + done: + *t = 0; + return buf; +} + +#endif + +typedef struct Section_s +{ + const char section[4]; + const char* name; +} Section_t; + +static const Section_t sections[] = +{ + "1M", "MAKE ASSERTION OPERATORS AND RULES", + "1", "USER COMMANDS", + "2", "SYSTEM CALLS", + "3F", "FORTRAN LIBRARY ROUTINES", + "3K", "KERNEL VM LIBRARY FUNCTIONS", + "3L", "LIGHTWEIGHT PROCESSES LIBRARY", + "3M", "MATHEMATICAL LIBRARY", + "3N", "NETWORK FUNCTIONS", + "3R", "RPC SERVICES LIBRARY", + "3S", "STANDARD I/O FUNCTIONS", + "3V", "SYSTEM V LIBRARY", + "3", "C LIBRARY FUNCTIONS", + "4F", "PROTOCOL FAMILIES", + "4P", "PROTOCOLS", + "4", "DEVICES AND NETWORK INTERFACES", + "5P", "PLUGINS", + "5", "FILE FORMATS", + "6", "GAMES AND DEMOS", + "7", "PUBLIC FILES AND TABLES", + "8", "ADMINISTRATIVE COMMANDS", + "L", "LOCAL COMMANDS", +}; + +/* + * return section name given abbreviation + */ + +static char* +secname(char* section) +{ + int i; + char* b; + char* t; + const char* s; + + b = t = fmtbuf(64); + if (section[1]) + { + switch (section[2] ? section[2] : section[1]) + { + case 'C': + s = "COMPATIBILITY "; + break; + case 'U': + s = "UWIN "; + break; + case 'X': + s = "MISCELLANEOUS "; + break; + default: + s = 0; + break; + } + if (s) + t = strcopy(t, s); + } + s = 0; + for (i = 0; i < elementsof(sections); i++) + if (section[0] == sections[i].section[0] && (section[1] == sections[i].section[1] || !sections[i].section[1])) + { + s = sections[i].name; + break; + } + if (!s) + { + t = strcopy(t, "SECTION "); + s = section; + } + strcopy(t, s); + return b; +} + +/* + * pop the push stack + */ + +static Push_t* +pop(register Push_t* psp) +{ + register Push_t* tsp; + + while (tsp = psp) + { + psp = psp->next; + free(tsp); + } + return 0; +} + +/* + * skip over line space to the next token + */ + +static char* +next(register char* s, int version) +{ + register char* b; + + while (*s == '\t' || *s == '\r' || version >= 1 && *s == ' ') + s++; + if (*s == '\n') + { + b = s; + while (*++s == ' ' || *s == '\t' || *s == '\r'); + if (*s == '\n') + return b; + } + return s; +} + +/* + * skip to t1 or t2 or t3, whichever first, in s + * n==0 outside [...] + * n==1 inside [...] before ? + * n==2 inside [...] after ? + * b==0 outside {...} + * b==1 inside {...} + * past skips past the terminator to the next token + * otherwise a pointer to the terminator is returned + * + * ]] for ] inside [...] + * ?? for ? inside [...] before ? + * :: for : inside [...] before ? + */ + +static char* +skip(register char* s, register int t1, register int t2, register int t3, register int n, register int b, int past, int version) +{ + register int c; + register int on = n; + register int ob = b; + + if (version < 1) + { + n = n >= 1; + for (;;) + { + switch (*s++) + { + case 0: + break; + case '[': + n++; + continue; + case ']': + if (--n <= 0) + break; + continue; + default: + continue; + } + break; + } + } + else while (c = *s++) + { + message((-22, "optget: skip t1=%c t2=%c t3=%c n=%d b=%d `%s'", t1 ? t1 : '@', t2 ? t2 : '@', t3 ? t3 : '@', n, b, show(s - 1))); + if (c == '[') + { + if (!n) + n = 1; + } + else if (c == ']') + { + if (n) + { + if (*s == ']') + s++; + else if (on == 1) + break; + else + n = 0; + } + } + else if (c == GO) + { + if (n == 0) + b++; + } + else if (c == OG) + { + if (n == 0 && b-- == ob) + break; + } + else if (c == '?') + { + if (n == 1) + { + if (*s == '?') + s++; + else + { + if (n == on && (c == t1 || c == t2 || c == t3)) + break; + n = 2; + } + } + } + else if (n == on && (c == t1 || c == t2 || c == t3)) + { + if (n == 1 && c == ':' && *s == c) + s++; + else + break; + } + } + return past && *(s - 1) ? next(s, version) : s - 1; +} + +/* + * *s points to '(' on input + * return is one past matching ')' + */ + +static char* +nest(register char* s) +{ + int n; + + n = 0; + for (;;) + { + switch (*s++) + { + case '(': + n++; + continue; + case ')': + if (!--n) + break; + continue; + default: + continue; + } + break; + } + return s; +} + +/* + * match s with t + * t translated if possible + * embedded { - _ ' } ignored + * * separates required prefix from optional suffix + * otherwise prefix match + */ + +static int +match(char* s, char* t, int version, const char* id, const char* catalog) +{ + register char* w; + register char* x; + char* xw; + char* ww; + int n; + int v; + int j; + + for (n = 0; n < 2; n++) + { + if (n) + x = t; + else + { + if (catalog) + { + w = skip(t, ':', '?', 0, 1, 0, 0, version); + w = sfprints("%-.*s", w - t, t); + x = T(id, catalog, w); + if (x == w) + continue; + } + x = T(NiL, ID, t); + if (x == t) + continue; + } + do + { + v = 0; + xw = x; + w = ww = s; + while (*x && *w) + { + if (isupper(*x)) + xw = x; + if (isupper(*w)) + ww = w; + if (*x == '*' && !v++ || *x == '\a') + { + if (*x == '\a') + do + { + if (!*++x) + { + x--; + break; + } + } while (*x != '\a'); + j = *(x + 1); + if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0) + while (*w) + w++; + } + else if (SEP(*x)) + xw = ++x; + else if (SEP(*w) && w != s) + ww = ++w; + else if (*x == *w) + { + x++; + w++; + } + else if (w == ww && x == xw) + break; + else + { + if (x != xw) + { + while (*x && !SEP(*x) && !isupper(*x)) + x++; + if (!*x) + break; + if (SEP(*x)) + x++; + xw = x; + } + while (w > ww && *w != *x) + w--; + } + } + if (!*w) + { + if (!v) + { + for (;;) + { + switch (*x++) + { + case 0: + case ':': + case '|': + case '?': + case ']': + return 1; + case '*': + break; + default: + continue; + } + break; + } + break; + } + return 1; + } + } while (*(x = skip(x, '|', 0, 0, 1, 0, 0, version)) == '|' && x++); + } + return 0; +} + +/* + * prefix search for s in tab with num elements of size + * with optional translation + */ + +static void* +search(const void* tab, size_t num, size_t siz, char* s) +{ + register char* p; + register char* e; + + for (e = (p = (char*)tab) + num * siz; p < e; p += siz) + if (match(s, *((char**)p), -1, NiL, NiL)) + return (void*)p; + return 0; +} + +/* + * save ap+bp+cp and return the saved pointer + */ + +static char* +save(const char* ap, size_t az, const char* bp, size_t bz, const char* cp, size_t cz) +{ + char* b; + char* e; + const char* ep; + Save_t* p; + Dtdisc_t* d; + char buf[1024]; + + static Dt_t* dict; + + if (!dict) + { + if (!(d = newof(0, Dtdisc_t, 1, 0))) + return (char*)ap; + d->key = offsetof(Save_t, text); + if (!(dict = dtopen(d, Dtset))) + return (char*)ap; + } + b = buf; + e = b + sizeof(buf) - 1; + for (ep = ap + az; b < e && ap < ep; *b++ = *ap++); + if (bp) + { + for (ep = bp + bz; b < e && bp < ep; *b++ = *bp++); + if (cp) + for (ep = cp + cz; b < e && cp < ep; *b++ = *cp++); + } + *b = 0; + if (!(p = (Save_t*)dtmatch(dict, buf))) + { + if (!(p = newof(0, Save_t, 1, b - buf))) + return (char*)ap; + strcpy(p->text, buf); + dtinsert(dict, p); + } + return p->text; +} + +/* + * expand \f...\f info + * *p set to next char after second \f + * expanded value returned + */ + +static char* +expand(register char* s, register char* e, char** p, Sfio_t* ip, char* id) +{ + register int c; + register char* b = s; + int n; + + n = sfstrtell(ip); + c = 1; + while ((!e || s < e) && (c = *s++) && c != '\f'); + sfwrite(ip, b, s - b - 1); + sfputc(ip, 0); + b = sfstrbase(ip) + n; + n = sfstrtell(ip); + if (!c) + s--; + if (*b == '?') + { + if (!*++b || streq(b, "NAME")) + { + if (!(b = id)) + b = "command"; + sfstrseek(ip, 0, SEEK_SET); + sfputr(ip, b, -1); + n = 0; + } + else + n = 1; + } + else if (!opt_info.disc || !opt_info.disc->infof || (*opt_info.disc->infof)(&opt_info, ip, b, opt_info.disc) < 0) + n = 0; + *p = s; + if (s = sfstruse(ip)) + s += n; + else + s = "error"; + return s; +} + +/* + * initialize the translation dictionary and flag maps + */ + +static void +initdict(void) +{ + register int n; + + state.vp = sfstropen(); + state.msgdisc.key = offsetof(Msg_t, text); + state.msgdisc.size = -1; + state.msgdisc.link = offsetof(Msg_t, link); + if (state.msgdict = dtopen(&state.msgdisc, Dtset)) + for (n = 0; n < elementsof(C_LC_MESSAGES_libast); n++) + dtinsert(state.msgdict, C_LC_MESSAGES_libast + n); +} + +/* + * initialize the attributes for pass p from opt string s + */ + +static int +init(register char* s, Optpass_t* p) +{ + register char* t; + register char* u; + register int c; + register int a; + register int n; + char* e; + int l; + + if (!state.localized) + { + state.localized = 1; +#if !_PACKAGE_astsa + if (!ast.locale.serial) + setlocale(LC_ALL, ""); +#endif + state.xp = sfstropen(); + if (!map[OPT_FLAGS[0]]) + for (n = 0, t = OPT_FLAGS; *t; t++) + map[*t] = ++n; + } +#if _BLD_DEBUG + error(-1, "optget debug"); +#endif + p->oopts = s; + p->version = 0; + p->prefix = 2; + p->section[0] = '1'; + p->section[1] = 0; + p->flags = 0; + p->id = error_info.id; + p->catalog = 0; + s = next(s, 0); + if (*s == ':') + s++; + if (*s == '+') + s++; + s = next(s, 0); + if (*s++ == '[') + { + if (*s == '+') + p->version = 1; + else if (*s++ == '-') + { + if (*s == '?' || *s == ']') + p->version = 1; + else + { + if (!isdigit(*s)) + p->version = 1; + else + while (isdigit(*s)) + p->version = p->version * 10 + (*s++ - '0'); + while (*s && *s != ']') + { + if ((c = *s++) == '?') + { + p->release = s; + while (*s && *s != ']') + if (isspace(*s++)) + p->release = s; + break; + } + else if (!isdigit(*s)) + n = 1; + else + { + n = 0; + while (isdigit(*s)) + n = n * 10 + (*s++ - '0'); + } + switch (c) + { + case '+': + p->flags |= OPT_plus; + break; + case 'a': + p->flags |= OPT_append; + break; + case 'c': + p->flags |= OPT_cache; + break; + case 'i': + p->flags |= OPT_ignore; + break; + case 'l': + p->flags |= OPT_long; + break; + case 'm': + p->flags |= OPT_module; + break; + case 'n': + p->flags |= OPT_numeric; + break; + case 'o': + p->flags |= OPT_old; + break; + case 'p': + p->prefix = n; + break; + case 's': + if (n > 1 && n < 5) + { + p->flags |= OPT_functions; + p->prefix = 0; + } + p->section[0] = '0' + (n % 10); + n = 1; + if (isupper(*s)) + p->section[n++] = *s++; + if (isupper(*s)) + p->section[n++] = *s++; + p->section[n] = 0; + break; + } + } + } + } + while (*s) + if (*s++ == ']') + { + while (isspace(*s)) + s++; + if (*s++ == '[') + { + if (*s++ != '-') + { + l = 0; + if (strneq(s - 1, "+NAME?", 6) && (s += 5) || strneq(s - 1, "+LIBRARY?", 9) && (s += 8) && (l = 1) || strneq(s - 1, "+PLUGIN?", 8) && (s += 7) && (l = 1)) + { + for (; *s == '\a' || *s == '\b' || *s == '\v' || *s == ' '; s++); + if (*s == '\f') + { + if (*(s + 1) == '?' && *(s + 2) == '\f') + break; + s = expand(s + 1, NiL, &e, state.xp, p->id); + } + for (t = s; *t && *t != ' ' && *t != ']'; t++); + if (t > s) + { + u = t; + if (*(t - 1) == '\a' || *(t - 1) == '\b' || *(t - 1) == '\v') + t--; + if (t > s) + { + while (*u == ' ' || *u == '\\') + u++; + if (*u == '-' || *u == ']') + { + if (!l) + p->id = save(s, t - s, 0, 0, 0, 0); + else if ((a = strlen(p->id)) <= (n = t - s) || strncmp(p->id + a - n, s, n) || *(p->id + a - n - 1) != ':') + p->id = save(p->id, strlen(p->id), "::", 2, s, t - s); + } + } + } + } + break; + } + if (*s == '-') + s++; + if (strneq(s, "catalog?", 8)) + p->catalog = s += 8; + } + } + } + if (!error_info.id) + { + if (!(error_info.id = p->id)) + p->id = "command"; + } + else if (p->id == error_info.id) + p->id = save(p->id, strlen(p->id), 0, 0, 0, 0); + if (s = p->catalog) + p->catalog = ((t = strchr(s, ']')) && (!p->id || (t - s) != strlen(p->id) || !strneq(s, p->id, t - s))) ? save(s, t - s, 0, 0, 0, 0) : (char*)0; + if (!p->catalog) + { + if (opt_info.disc && opt_info.disc->catalog && (!p->id || !streq(opt_info.disc->catalog, p->id))) + p->catalog = opt_info.disc->catalog; + else + p->catalog = ID; + } + s = p->oopts; + if (*s == ':') + s++; + if (*s == '+') + { + s++; + p->flags |= OPT_plus; + } + s = next(s, 0); + if (*s != '[') + for (t = s, a = 0; *t; t++) + if (!a && *t == '-') + { + p->flags |= OPT_minus; + break; + } + else if (*t == '[') + a++; + else if (*t == ']') + a--; + if (!p->version && (t = strchr(s, '(')) && strchr(t, ')') && (state.cp || (state.cp = sfstropen()))) + { + /* + * solaris long option compatibility + */ + + p->version = 1; + for (t = p->oopts; t < s; t++) + sfputc(state.cp, *t); + n = t - p->oopts; + sfputc(state.cp, '['); + sfputc(state.cp, '-'); + sfputc(state.cp, ']'); + c = *s++; + while (c) + { + sfputc(state.cp, '['); + sfputc(state.cp, c); + if (a = (c = *s++) == ':') + c = *s++; + if (c == '(') + { + sfputc(state.cp, ':'); + for (;;) + { + while ((c = *s++) && c != ')') + sfputc(state.cp, c); + if (!c || (c = *s++) != '(') + break; + sfputc(state.cp, '|'); + } + } + sfputc(state.cp, ']'); + if (a) + sfputr(state.cp, ":[string]", -1); + } + if (!(p->oopts = s = sfstruse(state.cp))) + return -1; + s += n; + } + p->opts = s; + message((-1, "version=%d prefix=%d section=%s flags=%04x id=%s catalog=%s oopts=%p", p->version, p->prefix, p->section, p->flags, p->id, p->catalog, p->oopts)); + return 0; +} + +/* + * return the bold set/unset sequence for style + */ + +static const char* +font(int f, int style, int set) +{ + switch (style) + { + case STYLE_html: + return fonts[f].html[set]; + case STYLE_nroff: + return fonts[f].nroff[set]; + case STYLE_short: + case STYLE_long: + case STYLE_posix: + case STYLE_api: + break; + default: + if (state.emphasis > 0) + return fonts[f].term[set]; + break; + } + return ""; +} + +/* + * push \f...\f info + */ + +static Push_t* +info(Push_t* psp, char* s, char* e, Sfio_t* ip, char* id) +{ + register char* b; + int n; + Push_t* tsp; + + static Push_t push; + + b = expand(s, e, &s, ip, id); + n = strlen(b); + if (tsp = newof(0, Push_t, 1, n + 1)) + { + tsp->nb = (char*)(tsp + 1); + tsp->ne = tsp->nb + n; + strcpy(tsp->nb, b); + } + else + tsp = &push; + tsp->next = psp; + tsp->ob = s; + tsp->oe = e; + return tsp; +} + +/* + * push translation + */ + +static Push_t* +localize(Push_t* psp, char* s, char* e, int term, int n, Sfio_t* ip, int version, char* id, char* catalog) +{ + char* t; + char* u; + Push_t* tsp; + int c; + + t = skip(s, term, 0, 0, n, 0, 0, version); + if (e && t > e) + t = e; + while (s < t) + { + switch (c = *s++) + { + case ':': + case '?': + if (term && *s == c) + s++; + break; + case ']': + if (*s == c) + s++; + break; + } + sfputc(ip, c); + } + if (!(s = sfstruse(ip)) || (u = T(id, catalog, s)) == s) + return 0; + n = strlen(u); + if (tsp = newof(0, Push_t, 1, n + 1)) + { + tsp->nb = (char*)(tsp + 1); + tsp->ne = tsp->nb + n; + strcpy(tsp->nb, u); + tsp->ob = t; + tsp->oe = e; + tsp->ch = 1; + } + tsp->next = psp; + return tsp; +} + +/* + * output label s from [ ...label...[?...] ] to sp + * 1 returned if the label was translated + */ + +static int +label(register Sfio_t* sp, int sep, register char* s, int about, int z, int level, int style, int f, Sfio_t* ip, int version, char* id, char* catalog) +{ + register int c; + register char* t; + register char* e; + int ostyle; + int a; + int i; + char* p; + char* q; + char* w; + char* y; + int va; + Push_t* tsp; + + int r = 0; + int n = 1; + Push_t* psp = 0; + + if ((ostyle = style) > (STYLE_nroff - (sep <= 0)) && f != FONT_LITERAL && f >= 0) + style = 0; + if (z < 0) + e = s + strlen(s); + else + e = s + z; + if (sep > 0) + { + if (sep == ' ' && style == STYLE_nroff) + sfputc(sp, '\\'); + sfputc(sp, sep); + } + sep = !sep || z < 0; + va = 0; + y = 0; + if (about) + sfputc(sp, '('); + if (version < 1) + { + a = 0; + for (;;) + { + if (s >= e) + return r; + switch (c = *s++) + { + case '[': + a++; + break; + case ']': + if (--a < 0) + return r; + break; + } + sfputc(sp, c); + } + } + else if (level && (*(p = skip(s, 0, 0, 0, 1, level, 1, version)) == ':' || *p == '#')) + { + va = 0; + if (*++p == '?' || *p == *(p - 1)) + { + p++; + va |= OPT_optional; + } + if (*(p = next(p, version)) == '[') + y = p + 1; + } + if (X(catalog) && (!level || *s == '\a' || *(s - 1) != '+') && + (tsp = localize(psp, s, e, (sep || level) ? '?' : 0, sep || level, ip, version, id, catalog))) + { + psp = tsp; + s = psp->nb; + e = psp->ne; + r = psp->ch > 0; + } + switch (*s) + { + case '\a': + if (f == FONT_ITALIC || f < 0) + s++; + if (f > 0) + f = 0; + break; + case '\b': + if (f == FONT_BOLD || f < 0) + s++; + if (f > 0) + f = 0; + break; + case '\v': + if (f == FONT_LITERAL || f < 0) + s++; + if (f > 0) + f = 0; + break; + default: + if (f > 0) + sfputr(sp, font(f, style, 1), -1); + break; + } + for (;;) + { + if (s >= e) + { + if (!(tsp = psp)) + goto restore; + s = psp->ob; + e = psp->oe; + psp = psp->next; + free(tsp); + continue; + } + switch (c = *s++) + { + case '(': + if (n) + { + n = 0; + if (f > 0) + { + sfputr(sp, font(f, style, 0), -1); + f = 0; + } + } + break; + case '?': + case ':': + case ']': + if (psp && psp->ch) + break; + if (y) + { + if (va & OPT_optional) + sfputc(sp, '['); + sfputc(sp, '='); + label(sp, 0, y, 0, -1, 0, style, f >= 0 ? FONT_ITALIC : f, ip, version, id, catalog); + if (va & OPT_optional) + sfputc(sp, ']'); + y = 0; + } + switch (c) + { + case '?': + if (*s == '?') + s++; + else if (*s == ']' && *(s + 1) != ']') + continue; + else if (sep) + goto restore; + else if (X(catalog) && (tsp = localize(psp, s, e, 0, 1, ip, version, id, catalog))) + { + psp = tsp; + s = psp->nb; + e = psp->ne; + } + break; + case ']': + if (sep && *s++ != ']') + goto restore; + break; + case ':': + if (sep && *s++ != ':') + goto restore; + break; + } + break; + case '\a': + a = FONT_ITALIC; + setfont: + if (f >= 0) + { + if (f & ~a) + { + sfputr(sp, font(f, style, 0), -1); + f = 0; + } + if (!f && style == STYLE_html) + { + for (t = s; t < e && !isspace(*t) && !iscntrl(*t); t++); + if (*t == c && *++t == '(') + { + w = t; + if (++t < e && isdigit(*t)) + while (++t < e && isupper(*t)); + if (t < e && *t == ')' && t > w + 1) + { + sfprintf(sp, "<NOBR><A href=\"../man%-.*s/" + , t - w - 1, w + 1 + ); + for (q = s; q < w - 1; q++) + if (*q == ':' && q < w - 2 && *(q + 1) == ':') + { + sfputc(sp, '-'); + q++; + } + else + sfputc(sp, *q); + sfprintf(sp, ".html\">%s%-.*s%s</A>%-.*s</NOBR>" + , font(a, style, 1) + , w - s - 1, s + , font(a, style, 0) + , t - w + 1, w + ); + s = t + 1; + continue; + } + } + } + sfputr(sp, font(a, style, !!(f ^= a)), -1); + } + continue; + case '\b': + a = FONT_BOLD; + goto setfont; + case '\f': + psp = info(psp, s, e, ip, id); + if (psp->nb) + { + s = psp->nb; + e = psp->ne; + } + else + { + s = psp->ob; + psp = psp->next; + } + continue; + case '\n': + sfputc(sp, c); + for (i = 0; i < level; i++) + sfputc(sp, '\t'); + continue; + case '\v': + a = FONT_LITERAL; + goto setfont; + case '<': + if (style == STYLE_html) + { + sfputr(sp, "<", -1); + c = 0; + for (t = s; t < e; t++) + if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-') + { + if (*t == '@') + { + if (c) + break; + c = 1; + } + else if (*t == '>') + { + if (c) + { + sfprintf(sp, "<A href=\"mailto:%-.*s>%-.*s</A>>", t - s, s, t - s, s); + s = t + 1; + } + break; + } + else + break; + } + continue; + } + break; + case '>': + if (style == STYLE_html) + { + sfputr(sp, ">", -1); + continue; + } + break; + case '&': + if (style == STYLE_html) + { + sfputr(sp, "&", -1); + continue; + } + break; + case '"': + if (style == STYLE_html) + { + sfputr(sp, """, -1); + continue; + } + break; + case '-': + if (ostyle == STYLE_nroff) + sfputc(sp, '\\'); + break; + case '.': + if (ostyle == STYLE_nroff) + { + sfputc(sp, '\\'); + sfputc(sp, '&'); + } + break; + case '\\': + if (ostyle == STYLE_nroff) + { + c = 'e'; + sfputc(sp, '\\'); + } + break; + case ' ': + if (ostyle == STYLE_nroff) + sfputc(sp, '\\'); + break; + } + sfputc(sp, c); + } + restore: + if (f > 0) + sfputr(sp, font(f, style, 0), -1); + if (about) + sfputc(sp, ')'); + if (psp) + pop(psp); + return r; +} + +/* + * output args description to sp from p of length n + */ + +static void +args(register Sfio_t* sp, register char* p, register int n, int flags, int style, Sfio_t* ip, int version, char* id, char* catalog) +{ + register int i; + register char* t; + register char* o; + register char* a = 0; + char* b; + int sep; + + if (flags & OPT_functions) + sep = '\t'; + else + { + sep = ' '; + o = T(NiL, ID, "options"); + b = style == STYLE_nroff ? "\\ " : " "; + for (;;) + { + t = (char*)memchr(p, '\n', n); + if (style >= STYLE_man) + { + if (!(a = id)) + a = "..."; + sfprintf(sp, "\t%s%s%s%s[%s%s%s%s%s]", font(FONT_BOLD, style, 1), a, font(FONT_BOLD, style, 0), b, b, font(FONT_ITALIC, style, 1), o, font(FONT_ITALIC, style, 0), b); + } + else if (a) + sfprintf(sp, "%*.*s%s%s%s[%s%s%s]", OPT_USAGE - 1, OPT_USAGE - 1, T(NiL, ID, "Or:"), b, a, b, b, o, b); + else + { + if (!(a = error_info.id) && !(a = id)) + a = "..."; + if (!sfstrtell(sp)) + sfprintf(sp, "[%s%s%s]", b, o, b); + } + if (!t) + break; + i = ++t - p; + if (i) + { + sfputr(sp, b, -1); + if (X(catalog)) + { + sfwrite(ip, p, i); + if (b = sfstruse(ip)) + sfputr(sp, T(id, catalog, b), -1); + else + sfwrite(sp, p, i); + } + else + sfwrite(sp, p, i); + } + if (style == STYLE_html) + sfputr(sp, "<BR>", '\n'); + else if (style == STYLE_nroff) + sfputr(sp, ".br", '\n'); + else if (style == STYLE_api) + sfputr(sp, ".BR", '\n'); + p = t; + n -= i; + while (n > 0 && (*p == ' ' || *p == '\t')) + { + p++; + n--; + } + } + } + if (n) + label(sp, sep, p, 0, n, 0, style, 0, ip, version, id, catalog); +} + +/* + * output [+-...label...?...] label s to sp + * according to {...} level and style + * return 0:header 1:paragraph + */ + +static int +item(Sfio_t* sp, char* s, int about, int level, int style, Sfio_t* ip, int version, char* id, char* catalog, int* hflags) +{ + register char* t; + int n; + int par; + + sfputc(sp, '\n'); + if (*s == '\n') + { + par = 0; + if (style >= STYLE_nroff) + sfprintf(sp, ".DS\n"); + else + { + if (style == STYLE_html) + sfprintf(sp, "<PRE>\n"); + else + sfputc(sp, '\n'); + for (n = 0; n < level; n++) + sfputc(sp, '\t'); + } + label(sp, 0, s + 1, about, -1, level, style, FONT_LITERAL, ip, version, id, catalog); + sfputc(sp, '\n'); + if (style >= STYLE_nroff) + sfprintf(sp, ".DE"); + else if (style == STYLE_html) + sfprintf(sp, "</PRE>"); + } + else if (*s != ']' && (*s != '?' || *(s + 1) == '?')) + { + par = 0; + if (level) + { + if (style >= STYLE_nroff) + sfprintf(sp, ".H%d ", (level - (level > 2)) / 2); + else + for (n = 0; n < level; n++) + sfputc(sp, '\t'); + } + if (style == STYLE_html) + { + if (!level) + { + if (*hflags & HELP_head) + sfputr(sp, "</DIV>", '\n'); + else + *hflags |= HELP_head; + sfputr(sp, "<H4>", -1); + } + sfputr(sp, "<A name=\"", -1); + if (s[-1] == '-' && s[0] == 'l' && s[1] == 'i' && s[2] == 'c' && s[3] == 'e' && s[4] == 'n' && s[5] == 's' && s[6] == 'e' && s[7] == '?') + for (t = s + 8; *t && *t != ']'; t++) + if (t[0] == 'p' && (!strncmp(t, "proprietary", 11) || !strncmp(t, "private", 7)) || t[0] == 'n' && !strncmp(t, "noncommercial", 13)) + { + state.flags |= OPT_proprietary; + break; + } + label(sp, 0, s, about, -1, level, style, -1, ip, version, id, catalog); + sfputr(sp, "\">", -1); + label(sp, 0, s, about, -1, level, style, level ? FONT_BOLD : 0, ip, version, id, catalog); + sfputr(sp, "</A>", -1); + if (!level) + { + if (!strncmp(s, C("SYNOPSIS"), strlen(C("SYNOPSIS")))) + sfputr(sp, "</H4>\n<DIV class=SY>", -1); + else + { + sfputr(sp, "</H4>\n<DIV class=SH>", -1); + if (!strncmp(s, C("NAME"), strlen(C("NAME"))) || !strncmp(s, C("PLUGIN"), strlen(C("PLUGIN")))) + *hflags |= HELP_index; + } + } + } + else + { + if (!level) + { + if (style >= STYLE_nroff) + sfprintf(sp, ".SH "); + else if (style == STYLE_man) + sfputc(sp, '\n'); + else if (style != STYLE_options && style != STYLE_match || *s == '-' || *s == '+') + sfputc(sp, '\t'); + } + label(sp, 0, s, about, -1, level, style, FONT_BOLD, ip, version, id, catalog); + } + } + else + { + par = 1; + if (style >= STYLE_nroff) + sfputr(sp, level ? ".SP" : ".PP", -1); + } + if (style >= STYLE_nroff || !level) + sfputc(sp, '\n'); + if (par && style < STYLE_nroff) + for (n = 0; n < level; n++) + sfputc(sp, '\t'); + return par; +} + +/* + * output text to sp from p according to style + */ + +#if _BLD_DEBUG + +static char* textout(Sfio_t*, char*, char*, int, int, int, int, Sfio_t*, int, char*, char*, int*); + +static char* +trace_textout(Sfio_t* sp, register char* p, char* conform, int conformlen, int style, int level, int bump, Sfio_t* ip, int version, char* id, char* catalog, int* hflags, int line) +{ + static int depth = 0; + + message((-21, "opthelp: txt#%d +++ %2d \"%s\" style=%d level=%d bump=%d", line, ++depth, show(p), style, level, bump)); + p = textout(sp, p, conform, conformlen, style, level, bump, ip, version, id, catalog, hflags); + message((-21, "opthelp: txt#%d --- %2d \"%s\"", line, depth--, show(p))); + return p; +} + +#endif + +static char* +textout(Sfio_t* sp, register char* s, char* conform, int conformlen, int style, int level, int bump, Sfio_t* ip, int version, char* id, char* catalog, int* hflags) +{ +#if _BLD_DEBUG +#define textout(sp,s,conform,conformlen,style,level,bump,ip,version,id,catalog,hflags) trace_textout(sp,s,conform,conformlen,style,level,bump,ip,version,id,catalog,hflags,__LINE__) +#endif + register char* t; + register int c; + register int n; + char* w; + char* q; + int a; + int f; + int par; + int about; + Push_t* tsp; + + int ident = 0; + int lev = level; + Push_t* psp = 0; + + again: + about = 0; + if ((c = *s) == GO) + { + for (;;) + { + while (*(s = next(s + 1, version)) == '\n'); + if (*s == GO) + { + if (level > 1) + level++; + level++; + } + else if (*s != OG) + { + if (level <= 1 || *s != '[' || *(s + 1) != '-' || style == STYLE_man && *(s + 2) == '?' || isalpha(*(s + 2))) + break; + s = skip(s, 0, 0, 0, 1, level, 0, version); + } + else if ((level -= 2) <= lev) + return s + 1; + } + if (*s == '\f') + { + psp = info(psp, s + 1, NiL, ip, id); + if (psp->nb) + s = psp->nb; + else + { + s = psp->ob; + psp = psp->next; + } + } + if (*s != '[') + return s; + c = *++s; + if (level > 1) + level++; + level++; + } + if (c == '-' && level > 1) + { + if (style == STYLE_man) + { + about = 1; + if (*(s + 1) == '-') + s++; + } + else + for (;;) + { + s = skip(s, 0, 0, 0, 1, level, 0, version); + while (*(s = next(s + 1, version)) == '\n'); + if (*s == '[') + { + if ((c = *++s) != '-') + break; + } + else if (*s == GO) + goto again; + else if (*s == OG) + return s + 1; + } + } + if (c == '+' || c == '-' && (bump = 3) || c != ' ' && level > 1) + { + s = skip(t = s + 1, '?', 0, 0, 1, level, 0, version); + if (c == '-' && (*t == '?' || isdigit(*t) || *s == '?' && *(s + 1) == '\n')) + { + if ((c = *s) != '?') + return skip(s, 0, 0, 0, 1, level, 1, version); + w = C("version"); + par = item(sp, w, about, level, style, ip, version, id, ID, hflags); + for (;;) + { + while (isspace(*(s + 1))) + s++; + w = s; + if (w[1] == '@' && w[2] == '(' && w[3] == '#' && w[4] == ')') + s = w + 4; + else if (w[1] == '$' && w[2] == 'I' && w[3] == 'd' && w[4] == ':' && w[5] == ' ') + { + s = w + 5; + ident = 1; + } + else + break; + } + } + else + { + if (isdigit(c) && isdigit(*t)) + { + while (isdigit(*t)) + t++; + if (*t == ':') + t++; + } + else if (isalnum(c) && *t-- == ':') + { + if (X(catalog) || *t == *(t + 2)) + t += 2; + else + { + sfprintf(ip, "%s", t); + if (w = sfstruse(ip)) + *((t = w) + 1) = '|'; + } + } + par = item(sp, t, about, level, style, ip, version, id, catalog, hflags); + c = *s; + } + if (!about && level) + par = 0; + } + else + { + if (style >= STYLE_nroff) + sfputc(sp, '\n'); + else if (c == '?') + for (n = 0; n < level; n++) + sfputc(sp, '\t'); + par = 0; + } + if (c == ':') + c = *(s = skip(s, '?', 0, 0, 1, 0, 0, version)); + if ((c == ']' || c == '?' && *(s + 1) == ']' && *(s + 2) != ']' && s++) && (c = *(s = next(s + 1, version))) == GO) + { + s = textout(sp, s, conform, conformlen, style, level + bump + par + 1, 0, ip, version, id, catalog, hflags); + if (level > lev && *s && *(s = next(s, version)) == '[') + { + s++; + message((-21, "textout#%d s=%s", __LINE__, show(s))); + goto again; + } + } + else if (c == '?' || c == ' ') + { + s++; + if (c == ' ') + sfputc(sp, c); + else + { + if (X(catalog) && (tsp = localize(psp, s, NiL, 0, 1, ip, version, id, catalog))) + { + psp = tsp; + s = psp->nb; + } + if (style < STYLE_nroff) + for (n = 0; n < bump + 1; n++) + sfputc(sp, '\t'); + } + if (conform) + { + sfprintf(sp, "[%-.*s %s] ", conformlen, conform, T(NiL, ID, "conformance")); + conform = 0; + } + if (*hflags & HELP_index) + { + *hflags &= ~HELP_index; + sfputr(sp, "<!--MAN-INDEX-->", -1); + } + f = 0; + for (;;) + { + switch (c = *s++) + { + case 0: + if (!(tsp = psp)) + { + if (f) + sfputr(sp, font(f, style, 0), -1); + return s - 1; + } + s = psp->ob; + psp = psp->next; + free(tsp); + continue; + case ']': + if (psp && psp->ch) + break; + if (*s != ']') + { + if (f) + { + sfputr(sp, font(f, style, 0), -1); + f = 0; + } + for (;;) + { + if ((*s == '#' || *s == ':') && level > lev) + { + char* o; + char* v; + int j; + int m; + int ol; + int vl; + + a = 0; + o = 0; + v = 0; + if (*++s == '?' || *s == *(s - 1)) + { + s++; + a |= OPT_optional; + } + if (*(s = next(s, version)) == '[') + { + s = skip(s + 1, ':', '?', 0, 1, 0, 0, version); + while (*s == ':') + { + s = skip(t = s + 1, ':', '?', 0, 1, 0, 0, version); + m = s - t; + if (*t == '!') + { + o = t + 1; + ol = m - 1; + } + else if (*t == '=') + { + v = t + 1; + vl = m - 1; + } + else + for (j = 0; j < elementsof(attrs); j++) + if (strneq(t, attrs[j].name, m)) + { + a |= attrs[j].flag; + break; + } + } + } + if (a & OPT_optional) + { + if (o) + { + sfprintf(sp, " %s ", T(NiL, ID, "If the option value is omitted then")); + sfputr(sp, font(FONT_BOLD, style, 1), -1); + t = o + ol; + while (o < t) + { + if (((c = *o++) == ':' || c == '?') && *o == c) + o++; + sfputc(sp, c); + } + sfputr(sp, font(FONT_BOLD, style, 0), -1); + sfprintf(sp, " %s.", T(NiL, ID, "is assumed")); + } + else + sfprintf(sp, " %s", T(NiL, ID, "The option value may be omitted.")); + } + if (v) + { + sfprintf(sp, " %s ", T(NiL, ID, "The default value is")); + sfputr(sp, font(FONT_BOLD, style, 1), -1); + t = v + vl; + while (v < t) + { + if (((c = *v++) == ':' || c == '?') && *v == c) + v++; + sfputc(sp, c); + } + sfputr(sp, font(FONT_BOLD, style, 0), -1); + sfputc(sp, '.'); + } + s = skip(s, 0, 0, 0, 1, 0, 1, version); + } + if (*(s = next(s, version)) == GO) + { + s = textout(sp, s, 0, 0, style, level + bump + !level, 0, ip, version, id, catalog, hflags); + if (*s && *(s = next(s, version)) == '[' && !isalnum(*(s + 1))) + { + s++; + message((-21, "textout#%d s=%s", __LINE__, show(s))); + goto again; + } + } + else if (*s == '[' && level > lev) + { + s++; + goto again; + } + else if (*s == '\f') + { + s++; + if (style != STYLE_keys) + { + psp = info(psp, s, NiL, ip, id); + if (psp->nb) + s = psp->nb; + else + { + s = psp->ob; + psp = psp->next; + } + } + } + else if (!*s) + { + if (!(tsp = psp)) + break; + s = psp->ob; + psp = psp->next; + free(tsp); + } + else if (*s != OG) + break; + else + { + s++; + if ((level -= 2) <= lev) + break; + } + } + return s; + } + s++; + break; + case '\a': + a = FONT_ITALIC; + setfont: + if (f & ~a) + { + sfputr(sp, font(f, style, 0), -1); + f = 0; + } + if (!f && style == STYLE_html) + { + for (t = s; *t && !isspace(*t) && !iscntrl(*t); t++); + if (*t == c && *++t == '(') + { + w = t; + if (isdigit(*++t)) + while (isupper(*++t)); + if (*t == ')' && t > w + 1) + { + sfprintf(sp, "<NOBR><A href=\"../man%-.*s/" + , t - w - 1, w + 1 + ); + for (q = s; q < w - 1; q++) + if (*q == ':' && q < w - 2 && *(q + 1) == ':') + { + sfputc(sp, '-'); + q++; + } + else + sfputc(sp, *q); + sfprintf(sp, ".html\">%s%-.*s%s</A>%-.*s</NOBR>" + , font(a, style, 1) + , w - s - 1, s + , font(a, style, 0) + , t - w + 1, w + ); + s = t + 1; + continue; + } + } + } + sfputr(sp, font(a, style, !!(f ^= a)), -1); + continue; + case '\b': + a = FONT_BOLD; + goto setfont; + case '\f': + if (style != STYLE_keys) + { + psp = info(psp, s, NiL, ip, id); + if (psp->nb) + s = psp->nb; + else + { + s = psp->ob; + psp = psp->next; + } + } + continue; + case '\v': + a = FONT_LITERAL; + goto setfont; + case ' ': + if (ident && *s == '$') + { + while (*++s) + if (*s == ']') + { + if (*(s + 1) != ']') + break; + s++; + } + continue; + } + case '\n': + case '\r': + case '\t': + while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') + s++; + if (*s == ']' && *(s + 1) != ']' && (!psp || !psp->ch)) + continue; + c = ' '; + break; + case '<': + if (style == STYLE_html) + { + sfputr(sp, "<", -1); + c = 0; + for (t = s; *t; t++) + if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-') + { + if (*t == '@') + { + if (c) + break; + c = 1; + } + else if (*t == '>') + { + if (c) + { + sfprintf(sp, "<A href=\"mailto:%-.*s\">%-.*s</A>>", t - s, s, t - s, s); + s = t + 1; + } + break; + } + else + break; + } + continue; + } + break; + case '>': + if (style == STYLE_html) + { + sfputr(sp, ">", -1); + continue; + } + break; + case '&': + if (style == STYLE_html) + { + sfputr(sp, "&", -1); + continue; + } + break; + case '-': + if (style == STYLE_nroff) + sfputc(sp, '\\'); + break; + case '.': + if (style == STYLE_nroff) + { + sfputc(sp, '\\'); + sfputc(sp, '&'); + } + break; + case '\\': + if (style == STYLE_nroff) + { + sfputc(sp, c); + c = 'e'; + } + break; + } + sfputc(sp, c); + } + } + else if (c == '[' && level > lev) + { + s++; + goto again; + } + return s; +} + +/* + * generate optget() help [...] list from lp + */ + +static void +list(Sfio_t* sp, register const List_t* lp) +{ + sfprintf(sp, "[%c", lp->type); + if (lp->name) + { + sfprintf(sp, "%s", lp->name); + if (lp->text) + sfprintf(sp, "?%s", lp->text); + } + sfputc(sp, ']'); +} + +/* + * return pointer to help message sans `Usage: command' + * if oopts is 0 then state.pass is used + * what: + * 0 ?short by default, ?long if any long options used + * * otherwise see help_text[] (--???) + * external formatter: + * \a...\a italic + * \b...\b bold + * \f...\f discipline infof callback on ... + * \v...\v literal + * internal formatter: + * \t indent + * \n newline + * margin flush pops to previous indent + */ + +char* +opthelp(const char* oopts, const char* what) +{ + register Sfio_t* sp; + register Sfio_t* mp; + register int c; + register char* p; + register Indent_t* ip; + char* t; + char* x; + char* w; + char* u; + char* y; + char* s; + char* d; + char* v; + char* cb; + char* dt; + char* ov; + char* pp; + char* rb; + char* re; + int f; + int i; + int j; + int m; + int n; + int a; + int cl; + int sl; + int vl; + int ol; + int wl; + int xl; + int rm; + int ts; + int co; + int z; + int style; + int head; + int margin; + int mode; + int mutex; + int prefix; + int version; + long tp; + char* id; + char* catalog; + Optpass_t* o; + Optpass_t* q; + Optpass_t* e; + Optpass_t one; + Optpass_t top; + Help_t* hp; + Tag_t ptstk[elementsof(indent) + 2]; + Tag_t* pt; + Sfio_t* vp; + Push_t* tsp; + + char* opts = (char*)oopts; + char* section = "1"; + int flags = 0; + int bflags = 0; + int dflags = 0; + int hflags = 0; + int sflags = 0; + int matched = 0; + int paragraph = 0; + Push_t* psp = 0; + Sfio_t* sp_help = 0; + Sfio_t* sp_text = 0; + Sfio_t* sp_plus = 0; + Sfio_t* sp_head = 0; + Sfio_t* sp_body = 0; + Sfio_t* sp_info = 0; + Sfio_t* sp_misc = 0; + + if (!(mp = state.mp) && !(mp = state.mp = sfstropen())) + goto nospace; + if (!what) + style = state.style; + else if (!*what) + style = STYLE_options; + else if (*what != '?') + style = STYLE_match; + else if (!*(what + 1)) + style = STYLE_man; + else if ((hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what + 1)) && hp->style >= 0) + { + style = hp->style; + if (*hp->name != '?') + what = hp->name; + } + else + { + if ((style = state.force) < STYLE_man) + style = STYLE_man; + if (!(sp_help = sfstropen())) + goto nospace; + for (i = 0; i < elementsof(help_head); i++) + list(sp_help, &help_head[i]); + for (i = 0; i < elementsof(styles); i++) + sfprintf(sp_help, "[:%s?%s]", styles[i].match, styles[i].text); + for (i = 0; i < elementsof(help_tail); i++) + list(sp_help, &help_tail[i]); + if (!(opts = sfstruse(sp_help))) + goto nospace; + } + + /* + * this is a workaround for static optjoin() data + * clobbered by plugins/builtins that may be called + * evaluating \f...\f -- it would be good to hide + * optjoin() interactions a bit more ... + */ + + top = state.pass[0]; + again: + if (opts) + { + for (i = 0; i < state.npass; i++) + if (state.pass[i].oopts == opts) + { + o = &state.pass[i]; + break; + } + if (i >= state.npass) + { + o = &one; + if (init((char*)opts, o)) + goto nospace; + } + e = o + 1; + } + else + { + if (state.npass > 0) + { + o = state.pass; + e = o + state.npass; + } + else if (state.npass < 0) + { + o = &state.cache->pass; + e = o + 1; + } + else + return T(NiL, ID, "[* call optget() before opthelp() *]"); + oopts = (const char*)state.pass[0].oopts; + } + if (style <= STYLE_usage) + { + if (!(sp_text = sfstropen()) || !(sp_info = sfstropen())) + goto nospace; + if (style >= STYLE_match && style < STYLE_keys && !(sp_body = sfstropen())) + goto nospace; + } + switch (style) + { + case STYLE_api: + case STYLE_html: + case STYLE_nroff: + state.emphasis = 0; + break; + case STYLE_usage: + case STYLE_keys: + for (q = o; q < e; q++) + if (!(q->flags & OPT_ignore) && !streq(q->catalog, o->catalog)) + o = q; + /*FALLTHROUGH*/ + case STYLE_posix: + sfputc(mp, '\f'); + break; + default: + if (!state.emphasis) + { + if (x = getenv("ERROR_OPTIONS")) + { + if (strmatch(x, "*noemphasi*")) + break; + if (strmatch(x, "*emphasi*")) + { + state.emphasis = 1; + break; + } + } + if ((x = getenv("TERM")) && strmatch(x, "(ansi|vt100|xterm)*") && isatty(sffileno(sfstderr))) + state.emphasis = 1; + } + break; + } + x = ""; + xl = 0; + for (q = o; q < e; q++) + { + if (q->flags & OPT_ignore) + continue; + section = q->section; + flags |= q->flags; + p = q->opts; + prefix = q->prefix; + version = q->version; + id = q->id; + catalog = q->catalog; + switch (style) + { + case STYLE_usage: + if (xl) + sfputc(mp, '\n'); + else + xl = 1; + psp = 0; + for (;;) + { + switch (c = *p++) + { + case 0: + if (!(tsp = psp)) + goto style_usage; + p = psp->ob; + psp = psp->next; + free(tsp); + continue; + case '\a': + c = 'a'; + break; + case '\b': + c = 'b'; + break; + case '\f': + psp = info(psp, p, NiL, sp_info, id); + if (psp->nb) + p = psp->nb; + else + { + p = psp->ob; + psp = psp->next; + } + continue; + case '\n': + c = 'n'; + break; + case '\r': + c = 'r'; + break; + case '\t': + c = 't'; + break; + case '\v': + c = 'v'; + break; + case '"': + c = '"'; + break; + case '\'': + c = '\''; + break; + case '\\': + c = '\\'; + break; + default: + sfputc(mp, c); + continue; + } + sfputc(mp, '\\'); + sfputc(mp, c); + } + style_usage: + continue; + case STYLE_keys: + a = 0; + psp = 0; + vl = 0; + for (;;) + { + if (!(c = *p++)) + { + if (!(tsp = psp)) + break; + p = psp->ob; + psp = psp->next; + free(tsp); + continue; + } + if (c == '\f') + { + psp = info(psp, p, NiL, sp_info, id); + if (psp->nb) + p = psp->nb; + else + { + p = psp->ob; + psp = psp->next; + } + continue; + } + f = z = 1; + t = 0; + if (a == 0 && (c == ' ' || c == '\n' && *p == '\n')) + { + if (c == ' ' && *p == ']') + { + p++; + continue; + } + if (*p == '\n') + p++; + a = c; + } + else if (c == '\n') + { + if (a == ' ') + a = -1; + else if (a == '\n' || *p == '\n') + { + a = -1; + p++; + } + continue; + } + else if ((c == ':' || c == '#') && (*p == '[' || *p == '?' && *(p + 1) == '[' && p++)) + p++; + else if (c != '[') + { + if (c == GO) + vl++; + else if (c == OG) + vl--; + continue; + } + else if (*p == ' ') + { + p++; + continue; + } + else if (*p == '-') + { + z = 0; + if (*++p == '-') + { + p = skip(p, 0, 0, 0, 1, 0, 1, version); + continue; + } + } + else if (*p == '+') + { + p++; + if (vl > 0 && *p != '\a') + { + f = 0; + p = skip(p, '?', 0, 0, 1, 0, 0, version); + if (*p == '?') + p++; + } + } + else + { + if (*(p + 1) == '\f' && (vp = state.vp)) + p = expand(p + 2, NiL, &t, vp, id); + p = skip(p, ':', '?', 0, 1, 0, 0, version); + if (*p == ':') + p++; + } + if (f && *p == '?' && *(p + 1) != '?') + { + f = 0; + if (z) + p++; + else + p = skip(p, 0, 0, 0, 1, 0, 0, version); + } + if (*p == ']' && *(p + 1) != ']') + { + p++; + continue; + } + if (!*p) + { + if (!t) + break; + p = t; + t = 0; + } + m = sfstrtell(mp); + sfputc(mp, '"'); + xl = 1; + /*UNDENT...*/ + + for (;;) + { + if (!(c = *p++)) + { + if (t) + { + p = t; + t = 0; + } + if (!(tsp = psp)) + { + p--; + break; + } + p = psp->ob; + psp = psp->next; + free(tsp); + continue; + } + if (a > 0) + { + if (c == '\n') + { + if (a == ' ') + { + a = -1; + break; + } + if (a == '\n' || *p == '\n') + { + a = -1; + p++; + break; + } + } + } + else if (c == ']') + { + if (*p != ']') + { + sfputc(mp, 0); + y = sfstrbase(mp) + m + 1; + if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT)) + { + sfstrseek(mp, m, SEEK_SET); + xl = 0; + } + else + sfstrseek(mp, -1, SEEK_CUR); + break; + } + sfputc(mp, *p++); + continue; + } + switch (c) + { + case '?': + if (f) + { + if (*p == '?') + { + p++; + sfputc(mp, c); + } + else + { + f = 0; + sfputc(mp, 0); + y = sfstrbase(mp) + m + 1; + if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT)) + { + sfstrseek(mp, m, SEEK_SET); + xl = 0; + } + else + sfstrseek(mp, -1, SEEK_CUR); + if (z && (*p != ']' || *(p + 1) == ']')) + { + if (xl) + { + sfputc(mp, '"'); + sfputc(mp, '\n'); + } + m = sfstrtell(mp); + sfputc(mp, '"'); + xl = 1; + } + else + { + p = skip(p, 0, 0, 0, 1, 0, 0, version); + if (*p == '?') + p++; + } + } + } + else + sfputc(mp, c); + continue; + case ':': + if (f && *p == ':') + p++; + sfputc(mp, c); + continue; + case '\a': + c = 'a'; + break; + case '\b': + c = 'b'; + break; + case '\f': + c = 'f'; + break; + case '\n': + c = 'n'; + break; + case '\r': + c = 'r'; + break; + case '\t': + c = 't'; + break; + case '\v': + c = 'v'; + break; + case '"': + c = '"'; + break; + case '\\': + c = '\\'; + break; + case CC_esc: + c = 'E'; + break; + default: + sfputc(mp, c); + continue; + } + sfputc(mp, '\\'); + sfputc(mp, c); + } + + /*...INDENT*/ + if (xl) + { + sfputc(mp, '"'); + sfputc(mp, '\n'); + } + } + continue; + } + z = 0; + head = 0; + mode = 0; + mutex = 0; + if (style > STYLE_short && style < STYLE_nroff && version < 1) + { + style = STYLE_short; + if (sp_body) + { + sfclose(sp_body); + sp_body = 0; + } + } + else if (style == STYLE_short && prefix < 2) + style = STYLE_long; + if (*p == ':') + p++; + if (*p == '+') + { + p++; + if (!(sp = sp_plus) && !(sp = sp_plus = sfstropen())) + goto nospace; + } + else if (style >= STYLE_match) + sp = sp_body; + else + sp = sp_text; + psp = 0; + for (;;) + { + if (!(*(p = next(p, version)))) + { + if (!(tsp = psp)) + break; + p = psp->ob; + psp = psp->next; + free(tsp); + continue; + } + if (*p == '\f') + { + psp = info(psp, p + 1, NiL, sp_info, id); + if (psp->nb) + p = psp->nb; + else + { + p = psp->ob; + psp = psp->next; + } + continue; + } + if (*p == '\n' || *p == ' ') + { + if (*(x = p = next(p + 1, version))) + while (*++p) + if (*p == '\n') + { + while (*++p == ' ' || *p == '\t' || *p == '\r'); + if (*p == '\n') + break; + } + xl = p - x; + if (!*p) + break; + continue; + } + if (*p == OG) + { + p++; + continue; + } + message((-20, "opthelp: opt %s", show(p))); + if (z < 0) + z = 0; + a = 0; + f = 0; + w = 0; + d = 0; + s = 0; + rb = re = 0; + sl = 0; + vl = 0; + if (*p == '[') + { + if ((c = *(p = next(p + 1, version))) == '(') + { + p = nest(cb = p); + cl = p - cb; + c = *p; + } + else + cb = 0; + if (c == '-') + { + if (style >= STYLE_man) + { + if (*(p + 1) != '-') + { + if (!sp_misc && !(sp_misc = sfstropen())) + goto nospace; + else + p = textout(sp_misc, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags); + continue; + } + } + else if (style == STYLE_match && *what == '-') + { + if (*(p + 1) == '?' || *(s = skip(p + 1, ':', '?', 0, 1, 0, 0, version)) == '?' && isspace(*(s + 1))) + s = C("version"); + else + s = p + 1; + w = (char*)what; + if (*s != '-' || *(w + 1) == '-') + { + if (*s == '-') + s++; + if (*(w + 1) == '-') + w++; + if (match(w + 1, s, version, id, catalog)) + { + if (*(p + 1) == '-') + p++; + p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags); + matched = -1; + continue; + } + } + } + if (!z) + z = -1; + } + else if (c == '+') + { + if (style >= STYLE_man) + { + p = textout(sp_body, p, cb, cl, style, 0, 0, sp_info, version, id, catalog, &bflags); + if (!sp_head) + { + sp_head = sp_body; + hflags = dflags = bflags; + if (!(sp_body = sfstropen())) + goto nospace; + } + continue; + } + else if (style == STYLE_match && *what == '+') + { + if (paragraph) + { + if (p[1] == '?') + { + p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags); + continue; + } + paragraph = 0; + } + if (match((char*)what + 1, p + 1, version, id, catalog)) + { + p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags); + matched = -1; + paragraph = 1; + continue; + } + } + if (!z) + z = -1; + } + else if (c == '[' || version < 1) + { + mutex++; + continue; + } + else + { + if (c == '!') + { + a |= OPT_invert; + p++; + } + rb = p; + if (*p != ':') + { + s = p; + if (*(p + 1) == '|') + { + while (*++p && *p != '=' && *p != '!' && *p != ':' && *p != '?'); + if ((p - s) > 1) + sl = p - s; + if (*p == '!') + a |= OPT_invert; + } + if (*(p + 1) == '\f') + p++; + else + p = skip(p, ':', '?', 0, 1, 0, 0, version); + if (sl || (p - s) == 1 || *(s + 1) == '=' || *(s + 1) == '!' && (a |= OPT_invert) || *(s + 1) == '|') + f = *s; + } + re = p; + if (style <= STYLE_short) + { + if (!z && !f) + z = -1; + } + else + { + if (*p == '\f' && (vp = state.vp)) + p = expand(p + 1, NiL, &t, vp, id); + else + t = 0; + if (*p == ':') + { + p = skip(w = p + 1, ':', '?', 0, 1, 0, 0, version); + if (!(wl = p - w)) + w = 0; + } + else + wl = 0; + if (*p == ':' || *p == '?') + { + d = p; + p = skip(p, 0, 0, 0, 1, 0, 0, version); + } + else + d = 0; + if (style == STYLE_match) + { + if (wl && !match((char*)what, w, version, id, catalog)) + wl = 0; + if ((!wl || *w == ':' || *w == '?') && (what[1] || sl && !memchr(s, what[0], sl) || !sl && what[0] != f)) + { + w = 0; + if (!z) + z = -1; + } + else + matched = 1; + } + if (t) + { + p = t; + if (*p == ':' || *p == '?') + { + d = p; + p = skip(p, 0, 0, 0, 1, 0, 0, version); + } + } + } + } + p = skip(p, 0, 0, 0, 1, 0, 1, version); + if (*p == GO) + p = skip(p + 1, 0, 0, 0, 0, 1, 1, version); + } + else if (*p == ']') + { + if (mutex) + { + if (style >= STYLE_nroff) + sfputr(sp_body, "\n.OP - - anyof", '\n'); + if (!(mutex & 1)) + { + mutex--; + if (style <= STYLE_long) + { + sfputc(sp_body, ' '); + sfputc(sp_body, ']'); + } + } + mutex--; + } + p++; + continue; + } + else if (*p == '?') + { + if (style < STYLE_match) + z = 1; + mode |= OPT_hidden; + p++; + continue; + } + else if (*p == '\\' && style==STYLE_posix) + { + if (*++p) + p++; + continue; + } + else + { + f = *p++; + s = 0; + if (style == STYLE_match && !z) + z = -1; + } + if (!z) + { + if (style == STYLE_long || prefix < 2 || (q->flags & OPT_long)) + f = 0; + else if (style <= STYLE_short) + w = 0; + if (!f && !w) + z = -1; + } + ov = 0; + u = v = y = 0; + if (*p == ':' && (a |= OPT_string) || *p == '#' && (a |= OPT_number)) + { + message((-21, "opthelp: arg %s", show(p))); + if (*++p == '?' || *p == *(p - 1)) + { + p++; + a |= OPT_optional; + } + if (*(p = next(p, version)) == '[') + { + if (!z) + { + p = skip(y = p + 1, ':', '?', 0, 1, 0, 0, version); + while (*p == ':') + { + p = skip(t = p + 1, ':', '?', 0, 1, 0, 0, version); + m = p - t; + if (*t == '!') + { + ov = t + 1; + ol = m - 1; + } + else if (*t == '=') + { + v = t + 1; + vl = m - 1; + } + else + for (j = 0; j < elementsof(attrs); j++) + if (strneq(t, attrs[j].name, m)) + { + a |= attrs[j].flag; + break; + } + } + if (*p == '?') + u = p; + p = skip(p, 0, 0, 0, 1, 0, 1, version); + } + else + p = skip(p + 1, 0, 0, 0, 1, 0, 1, version); + } + else + y = (a & OPT_number) ? T(NiL, ID, "#") : T(NiL, ID, "arg"); + } + else + a |= OPT_flag; + if (!z) + { + if (style <= STYLE_short && !y && !mutex || style == STYLE_posix) + { + if (style != STYLE_posix && !sfstrtell(sp)) + { + sfputc(sp, '['); + if (sp == sp_plus) + sfputc(sp, '+'); + sfputc(sp, '-'); + } + if (!sl) + sfputc(sp, f); + else + for (c = 0; c < sl; c++) + if (s[c] != '|') + sfputc(sp, s[c]); + if (style == STYLE_posix && y) + sfputc(sp, ':'); + } + else + { + if (style >= STYLE_match) + { + sfputc(sp_body, '\n'); + if (!head) + { + head = 1; + item(sp_body, (flags & OPT_functions) ? C("FUNCTIONS") : C("OPTIONS"), 0, 0, style, sp_info, version, id, ID, &bflags); + } + if (style >= STYLE_nroff) + { + if (mutex & 1) + { + mutex++; + sfputr(sp_body, "\n.OP - - oneof", '\n'); + } + } + else + sfputc(sp_body, '\t'); + } + else + { + if (sp_body) + sfputc(sp_body, ' '); + else if (!(sp_body = sfstropen())) + goto nospace; + if (mutex) + { + if (mutex & 1) + { + mutex++; + sfputc(sp_body, '['); + } + else + sfputc(sp_body, '|'); + sfputc(sp_body, ' '); + } + else + sfputc(sp_body, '['); + } + if (style >= STYLE_nroff) + { + if (flags & OPT_functions) + { + sfputr(sp_body, ".FN", ' '); + if (re > rb) + sfwrite(sp_body, rb, re - rb); + else + sfputr(sp, "void", -1); + if (w) + label(sp_body, ' ', w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog); + } + else + { + sfputr(sp_body, ".OP", ' '); + if (sl) + sfwrite(sp_body, s, sl); + else + sfputc(sp_body, f ? f : '-'); + sfputc(sp_body, ' '); + if (w) + { + if (label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, catalog)) + { + sfputc(sp_body, '|'); + label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, native); + } + } + else + sfputc(sp_body, '-'); + sfputc(sp_body, ' '); + m = a & OPT_TYPE; + for (j = 0; j < elementsof(attrs); j++) + if (m & attrs[j].flag) + { + sfputr(sp_body, attrs[j].name, -1); + break; + } + if (m = (a & ~m) | mode) + for (j = 0; j < elementsof(attrs); j++) + if (m & attrs[j].flag) + { + sfputc(sp_body, ':'); + sfputr(sp_body, attrs[j].name, -1); + } + sfputc(sp_body, ' '); + if (y) + label(sp_body, 0, y, 0, -1, 0, style, 0, sp_info, version, id, catalog); + else + sfputc(sp_body, '-'); + if (v) + sfprintf(sp_body, " %-.*s", vl, v); + } + } + else + { + if (f) + { + if (sp_body == sp_plus) + sfputc(sp_body, '+'); + sfputc(sp_body, '-'); + sfputr(sp_body, font(FONT_BOLD, style, 1), -1); + if (!sl) + { + sfputc(sp_body, f); + if (f == '-' && y) + { + y = 0; + sfputr(sp_body, C("long-option[=value]"), -1); + } + } + else + sfwrite(sp_body, s, sl); + sfputr(sp_body, font(FONT_BOLD, style, 0), -1); + if (w) + { + sfputc(sp_body, ','); + sfputc(sp_body, ' '); + } + } + else if ((flags & OPT_functions) && re > rb) + { + sfwrite(sp_body, rb, re - rb); + sfputc(sp_body, ' '); + } + if (w) + { + if (prefix > 0) + { + sfputc(sp_body, '-'); + if (prefix > 1) + sfputc(sp_body, '-'); + } + if (label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog)) + { + sfputc(sp_body, '|'); + label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, native); + } + } + if (y) + { + if (a & OPT_optional) + sfputc(sp_body, '['); + else if (!w) + sfputc(sp_body, ' '); + if (w) + sfputc(sp_body, prefix == 1 ? ' ' : '='); + label(sp_body, 0, y, 0, -1, 0, style, FONT_ITALIC, sp_info, version, id, catalog); + if (a & OPT_optional) + sfputc(sp_body, ']'); + } + } + if (style >= STYLE_match) + { + if (d) + { + textout(sp_body, d, cb, cl, style, 0, 3, sp_info, version, id, catalog, &bflags); + cb = 0; + } + if (u) + textout(sp_body, u, cb, cl, style, 0, 3, sp_info, version, id, catalog, &bflags); + if ((a & OPT_invert) && w && (d || u)) + { + u = skip(w, ':', '?', 0, 1, 0, 0, version); + if (f) + sfprintf(sp_info, " %s; -\b%c\b %s --\bno%-.*s\b.", T(NiL, ID, "On by default"), f, T(NiL, ID, "means"), u - w, w); + else + sfprintf(sp_info, " %s %s\bno%-.*s\b %s.", T(NiL, ID, "On by default; use"), "--"+2-prefix, u - w, w, T(NiL, ID, "to turn off")); + if (!(t = sfstruse(sp_info))) + goto nospace; + textout(sp_body, t, 0, 0, style, 0, 0, sp_info, version, NiL, NiL, &bflags); + } + if (*p == GO) + { + p = u ? skip(p + 1, 0, 0, 0, 0, 1, 1, version) : textout(sp_body, p, 0, 0, style, 4, 0, sp_info, version, id, catalog, &bflags); + y = "+?"; + } + else + y = " "; + if (a & OPT_optional) + { + if (ov) + { + sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "If the option value is omitted then")); + t = ov + ol; + while (ov < t) + { + if (((c = *ov++) == ':' || c == '?') && *ov == c) + ov++; + sfputc(sp_info, c); + } + sfprintf(sp_info, "\b %s.", T(NiL, ID, "is assumed")); + } + else + sfprintf(sp_info, "%s%s", y, T(NiL, ID, "The option value may be omitted.")); + if (!(t = sfstruse(sp_info))) + goto nospace; + textout(sp_body, t, 0, 0, style, 4, 0, sp_info, version, NiL, NiL, &bflags); + y = " "; + } + if (v) + { + sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "The default value is")); + t = v + vl; + while (v < t) + { + if (((c = *v++) == ':' || c == '?') && *v == c) + v++; + sfputc(sp_info, c); + } + sfputc(sp_info, '\b'); + sfputc(sp_info, '.'); + if (!(t = sfstruse(sp_info))) + goto nospace; + textout(sp_body, t, 0, 0, style, 4, 0, sp_info, version, NiL, NiL, &bflags); + } + } + else if (!mutex) + sfputc(sp_body, ']'); + } + if (*p == GO) + { + if (style >= STYLE_match) + p = textout(sp_body, p, 0, 0, style, 4, 0, sp_info, version, id, catalog, &bflags); + else + p = skip(p + 1, 0, 0, 0, 0, 1, 1, version); + } + } + else if (*p == GO) + p = skip(p + 1, 0, 0, 0, 0, 1, 1, version); + } + psp = pop(psp); + if (sp_misc) + { + if (!(p = sfstruse(sp_misc))) + goto nospace; + for (t = p; *t == '\t' || *t == '\n'; t++); + if (*t) + { + item(sp_body, C("IMPLEMENTATION"), 0, 0, style, sp_info, version, id, ID, &bflags); + sfputr(sp_body, p, -1); + } + } + } + if (oopts != o->oopts && oopts == top.oopts) + state.pass[0] = top; + version = o->version; + id = o->id; + catalog = o->catalog; + if (style >= STYLE_keys) + { + if (sp_info) + sfclose(sp_info); + if (style == STYLE_keys && sfstrtell(mp) > 1) + sfstrseek(mp, -1, SEEK_CUR); + if (!(p = sfstruse(mp))) + goto nospace; + return opt_info.msg = p; + } + sp = sp_text; + if (sfstrtell(sp) && style != STYLE_posix) + sfputc(sp, ']'); + if (style == STYLE_nroff) + { + char rd[64]; + char ud[64]; + + s = o->id; + t = ud; + while (t < &ud[sizeof(ud)-2] && (c = *s++)) + { + if (islower(c)) + c = toupper(c); + *t++ = c; + } + *t = 0; + t = rd; + if (s = o->release) + { + *t++ = ' '; + while (t < &rd[sizeof(rd)-2] && (c = *s++) && c != ']') + *t++ = c; + } + *t = 0; + sfprintf(sp, "\ +.\\\" format with nroff|troff|groff -man\n\ +.TH %s %s%s\n\ +.fp 5 CW\n\ +.nr mH 5\n\ +.de H0\n\ +.nr mH 0\n\ +.in 5n\n\ +\\fB\\\\$1\\fP\n\ +.in 7n\n\ +..\n\ +.de H1\n\ +.nr mH 1\n\ +.in 7n\n\ +\\fB\\\\$1\\fP\n\ +.in 9n\n\ +..\n\ +.de H2\n\ +.nr mH 2\n\ +.in 11n\n\ +\\fB\\\\$1\\fP\n\ +.in 13n\n\ +..\n\ +.de H3\n\ +.nr mH 3\n\ +.in 15n\n\ +\\fB\\\\$1\\fP\n\ +.in 17n\n\ +..\n\ +.de H4\n\ +.nr mH 4\n\ +.in 19n\n\ +\\fB\\\\$1\\fP\n\ +.in 21n\n\ +..\n\ +.de OP\n\ +.nr mH 0\n\ +.ie !'\\\\$1'-' \\{\n\ +.ds mO \\\\fB\\\\-\\\\$1\\\\fP\n\ +.ds mS ,\\\\0\n\ +.\\}\n\ +.el \\{\n\ +.ds mO \\\\&\n\ +.ds mS \\\\&\n\ +.\\}\n\ +.ie '\\\\$2'-' \\{\n\ +.if !'\\\\$4'-' .as mO \\\\0\\\\fI\\\\$4\\\\fP\n\ +.\\}\n\ +.el \\{\n\ +.as mO \\\\*(mS\\\\fB%s\\\\$2\\\\fP\n\ +.if !'\\\\$4'-' .as mO =\\\\fI\\\\$4\\\\fP\n\ +.\\}\n\ +.in 5n\n\ +\\\\*(mO\n\ +.in 9n\n\ +..\n\ +.de SP\n\ +.if \\\\n(mH==2 .in 9n\n\ +.if \\\\n(mH==3 .in 13n\n\ +.if \\\\n(mH==4 .in 17n\n\ +..\n\ +.de FN\n\ +.nr mH 0\n\ +.in 5n\n\ +\\\\$1 \\\\$2\n\ +.in 9n\n\ +..\n\ +.de DS\n\ +.in +3n\n\ +.ft 5\n\ +.nf\n\ +..\n\ +.de DE\n\ +.fi\n\ +.ft R\n\ +.in -3n\n\ +..\n\ +" +, ud +, section +, rd +, o->prefix == 2 ? "\\\\-\\\\-" : o->prefix == 1 ? "\\\\-" : "" +); + } + if (style == STYLE_match) + { + if (!matched) + { + if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what)) + { + if (!sp_help && !(sp_help = sfstropen())) + goto nospace; + sfprintf(sp_help, "[-][:%s?%s]", hp->match, hp->text); + if (!(opts = sfstruse(sp_help))) + goto nospace; + goto again; + } + s = (char*)unknown; + goto nope; + } + else if (matched < 0) + x = 0; + } + if (sp_plus) + { + if (sfstrtell(sp_plus)) + { + if (sfstrtell(sp)) + sfputc(sp, ' '); + if (!(t = sfstruse(sp_plus))) + goto nospace; + sfputr(sp, t, ']'); + } + sfclose(sp_plus); + } + if (style >= STYLE_man) + { + if (sp_head) + { + if (!(t = sfstruse(sp_head))) + goto nospace; + for (; *t == '\n'; t++); + sfputr(sp, t, '\n'); + sfclose(sp_head); + sp_head = 0; + } + if (x) + item(sp, C("SYNOPSIS"), 0, 0, style, sp_info, version, id, ID, &hflags); + } + if (x) + { + for (t = x + xl; t > x && (*(t - 1) == '\n' || *(t - 1) == '\r'); t--); + xl = t - x; + if (style >= STYLE_match) + { + u = id; + if (o->flags & OPT_functions) + t = 0; + else if (t = strchr(u, ':')) + { + if ((o->flags & OPT_module) && *(t + 1) == ':' && *(t + 2)) + { + u = t + 2; + t = 0; + } + else + *t = 0; + } + args(sp, x, xl, o->flags, style, sp_info, version, u, catalog); + if (t) + *t = ':'; + x = 0; + } + } + if (sp_body) + { + if (sfstrtell(sp_body)) + { + if (style < STYLE_match && sfstrtell(sp)) + sfputc(sp, ' '); + if (!(t = sfstruse(sp_body))) + goto nospace; + if (style == STYLE_html && !(dflags & HELP_head) && (bflags & HELP_head)) + sfputr(sp, "\n</DIV>", '\n'); + sfputr(sp, t, -1); + } + sfclose(sp_body); + sp_body = 0; + } + if (x && style != STYLE_posix) + args(sp, x, xl, flags, style, sp_info, version, id, catalog); + if (sp_info) + { + sfclose(sp_info); + sp_info = 0; + } + if (sp_misc) + { + sfclose(sp_misc); + sp_misc = 0; + } + if (!(p = sfstruse(sp))) + goto nospace; + astwinsize(1, NiL, &state.width); + if (state.width < 20) + state.width = OPT_WIDTH; + m = strlen((style <= STYLE_long && error_info.id && !strchr(error_info.id, '/')) ? error_info.id : id) + 1; + margin = style == STYLE_api ? (8 * 1024) : (state.width - 1); + if (!(state.flags & OPT_preformat)) + { + if (style >= STYLE_man || matched < 0) + { + sfputc(mp, '\f'); + ts = 0; + } + else + ts = OPT_USAGE + m; + if (style == STYLE_html) + { + char ud[64]; + + s = id; + t = ud; + while (t < &ud[sizeof(ud)-2] && (c = *s++)) + { + if (islower(c)) + c = toupper(c); + *t++ = c; + } + *t = 0; + sfprintf(mp, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n<HTML>\n<HEAD>\n<META name=\"generator\" content=\"optget (AT&T Research) 2011-11-11\">\n%s<TITLE>%s man document</TITLE>\n<STYLE type=\"text/css\">\ndiv.SH { padding-left:2em; text-indent:0em; }\ndiv.SY { padding-left:4em; text-indent:-2em; }\ndt { float:left; clear:both; }\ndd { margin-left:3em; }\n</STYLE>\n</HEAD>\n<BODY bgcolor=white>\n", (state.flags & OPT_proprietary) ? "<!--INTERNAL-->\n" : "", id); + sfprintf(mp, "<H4><TABLE width=100%%><TR><TH align=left>%s ( %s ) <TH align=center><A href=\".\" title=\"Index\">%s</A><TH align=right>%s ( %s )</TR></TABLE></H4>\n<HR>\n", ud, section, T(NiL, ID, secname(section)), ud, section); + co = 2; + pt = ptstk; + pt->level = 0; + pt->id = TAG_DIV; + } + else + co = 0; + if ((rm = margin - ts) < OPT_MARGIN) + rm = OPT_MARGIN; + ip = indent; + ip->stop = (ip+1)->stop = style >= STYLE_html ? 0 : 2; + tp = 0; + n = 0; + head = 1; + while (*p == '\n') + p++; + while (c = *p++) + { + if (c == '\n') + { + ip = indent; + n = 0; + tp = 0; + sfputc(mp, '\n'); + co = 0; + rm = margin; + ts = ip->stop; + if (*p == '\n') + { + while (*++p == '\n'); + if ((style == STYLE_man || style == STYLE_html) && (!head || *p != ' ' && *p != '\t')) + { + if (style == STYLE_man) + p--; + else + sfprintf(mp, "<P>\n"); + } + } + head = *p != ' ' && *p != '\t'; + if (style == STYLE_html && (*p != '<' || !strneq(p, "<BR>", 4) && !strneq(p, "<P>", 3))) + { + y = p; + while (*p == '\t') + p++; + if (*p == '\n') + continue; + j = p - y; + if (j > pt->level) + { + pt++; + pt->level = j; + pt->id = TAG_NONE; + for (y = p; *y && *y != '\n'; y++) + if (*y == '\t') + { + pt->id = TAG_DL; + sfprintf(mp, "<DL>\n"); + break; + } + } + else + while (j < pt->level && pt > ptstk) + { + sfprintf(mp, "%s", end[pt->id]); + pt--; + } + if (pt->id == TAG_DL) + { + dt = p; + sfprintf(mp, "<DT>"); + } + else + dt = 0; + } + } + else if (c == '\t') + { + if (style == STYLE_html) + { + while (*p == '\t') + p++; + if (*p != '\n') + { + co += sfprintf(mp, "<DD>"); + if (dt) + { + c = 0; + m = 0; + for (;;) + { + switch (*dt++) + { + case '\t': + break; + case '<': + c = '>'; + continue; + case '>': + if (c == '>') + c = 0; + else + m++; + continue; + case '&': + c = ';'; + continue; + case ';': + if (c == ';') + c = 0; + m++; + continue; + default: + if (!c) + m++; + continue; + } + break; + } + if (m >= 5) + co += sfprintf(mp, "<BR>"); + } + } + } + else + { + if ((ip+1)->stop) + { + do + { + ip++; + if (*p != '\t') + break; + p++; + } while ((ip+1)->stop); + if (*p == '\n') + continue; + ts = ip->stop; + if (co >= ts) + { + sfputc(mp, '\n'); + co = 0; + rm = margin; + ts = ip->stop; + } + } + while (co < ts) + { + sfputc(mp, ' '); + co++; + } + } + } + else + { + if (c == ' ' && !n) + { + if (co >= rm) + tp = 0; + else + { + tp = sfstrtell(mp); + pp = p; + } + if (style == STYLE_nroff && !co) + continue; + } + else if (style == STYLE_html) + { + if (c == '<') + { + if (strneq(p, "NOBR>", 5)) + n++; + else if (n && strneq(p, "/NOBR>", 6) && !--n) + { + for (y = p += 6; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++) + if (c == '[') + sfputr(mp, "[", -1); + else if (c == ']') + sfputr(mp, "]", -1); + else + sfputc(mp, c); + sfwrite(mp, "</NOBR", 6); + c = '>'; + co += p - y + 6; + } + } + else if (c == '>' && !n) + { + for (y = --p; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++) + if (c == '[') + sfputr(mp, "[", -1); + else if (c == ']') + sfputr(mp, "]", -1); + else + sfputc(mp, c); + c = *sfstrseek(mp, -1, SEEK_CUR); + if (p > y + 1) + { + tp = 0; + co += p - y - 1; + } + if (co >= rm) + tp = 0; + else + { + tp = sfstrtell(mp); + pp = p; + } + } + else if (c == '[') + { + sfputr(mp, "[", -1); + c = ';'; + } + else if (c == ']') + { + sfputr(mp, "]", -1); + c = ';'; + } + else if (c == 'h') + { + y = p; + if (*y++ == 't' && *y++ == 't' && *y++ == 'p' && (*y == ':' || *y++ == 's' && *y == ':') && *y++ == ':' && *y++ == '/' && *y++ == '/') + { + while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.') + y++; + if (*y == '?') + while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.' || *y == '?' || *y == '=' || *y == '%' || *y == '&' || *y == ';' || *y == '#') + y++; + if (*(y - 1) == '.') + y--; + p--; + sfprintf(mp, "<A href=\"%-.*s\">%-.*s</A", y - p, p, y - p, p); + p = y; + c = '>'; + } + } + else if (c == 'C') + { + y = p; + if (*y++ == 'o' && *y++ == 'p' && *y++ == 'y' && *y++ == 'r' && *y++ == 'i' && *y++ == 'g' && *y++ == 'h' && *y++ == 't' && *y++ == ' ' && *y++ == '(' && (*y++ == 'c' || *(y - 1) == 'C') && *y++ == ')') + { + sfputr(mp, "Copyright ©", -1); + p = y; + c = ';'; + } + } + } + else if (c == ']') + { + if (n) + n--; + } + else if (c == '[') + n++; + if (c == CC_esc) + { + sfputc(mp, c); + do + { + if (!(c = *p++)) + { + p--; + break; + } + sfputc(mp, c); + } while (c < 'a' || c > 'z'); + } + else if (co++ >= rm && !n) + { + if (tp) + { + if (*sfstrseek(mp, tp, SEEK_SET) != ' ') + sfstrseek(mp, 1, SEEK_CUR); + tp = 0; + p = pp; + n = 0; + } + else if (c != ' ' && c != '\n') + sfputc(mp, c); + if (*p == ' ') + p++; + if (*p != '\n') + { + sfputc(mp, '\n'); + for (co = 0; co < ts; co++) + sfputc(mp, ' '); + rm = margin; + } + } + else + sfputc(mp, c); + } + } + for (d = sfstrbase(mp), t = sfstrseek(mp, 0, SEEK_CUR); t > d && ((c = *(t - 1)) == '\n' || c == '\r' || c == ' ' || c == '\t'); t--); + sfstrseek(mp, t - d, SEEK_SET); + if (style == STYLE_html) + { + sfprintf(mp, "\n"); + while (pt > ptstk) + { + sfprintf(mp, "%s", end[pt->id]); + pt--; + } + sfprintf(mp, "</DIV>\n</BODY>\n</HTML>"); + } + } + else + sfputr(mp, p, 0); + if (!(p = sfstruse(mp))) + goto nospace; + if (sp) + sfclose(sp); + return opt_info.msg = p; + nospace: + s = T(NiL, ID, "[* out of space *]"); + nope: + if (psp) + pop(psp); + if (sp_help) + sfclose(sp_help); + if (sp_text) + sfclose(sp_text); + if (sp_plus) + sfclose(sp_plus); + if (sp_info) + sfclose(sp_info); + if (sp_head) + sfclose(sp_head); + if (sp_body) + sfclose(sp_body); + if (sp_misc) + sfclose(sp_misc); + return s; +} + +/* + * compatibility wrapper to opthelp() + */ + +char* +optusage(const char* opts) +{ + return opthelp(opts, NiL); +} + +/* + * convert number using strtonll() *except* that + * 0*[[:digit:]].* is treated as [[:digit:]].* + * i.e., it looks octal but isn't, to meet + * posix Utility Argument Syntax -- use + * 0x.* or <base>#* for alternate bases + */ + +static intmax_t +optnumber(const char* s, char** t, int* e) +{ + intmax_t n; + int oerrno; + + while (*s == '0' && isdigit(*(s + 1))) + s++; + oerrno = errno; + errno = 0; + n = strtonll(s, t, NiL, 0); + if (e) + *e = errno; + errno = oerrno; + return n; +} + +/* + * point opt_info.arg to an error/info message for opt_info.name + * p points to opts location for opt_info.name + * optget() return value is returned + */ + +static int +opterror(register char* p, int err, int version, char* id, char* catalog) +{ + register Sfio_t* mp; + register Sfio_t* tp; + register char* s; + register int c; + + if (opt_info.num != LONG_MIN) + opt_info.num = (long)(opt_info.number = 0); + if (!p || !(mp = state.mp) && !(mp = state.mp = sfstropen())) + goto nospace; + s = *p == '-' ? p : opt_info.name; + if (*p == '!') + { + while (*s == '-') + sfputc(mp, *s++); + sfputc(mp, 'n'); + sfputc(mp, 'o'); + } + sfputr(mp, s, ':'); + sfputc(mp, ' '); + if (*p == '#' || *p == ':') + { + if (*p == '#') + { + s = T(NiL, ID, "numeric"); + sfputr(mp, s, ' '); + } + if (*(p = next(p + 1, version)) == '[') + { + p = skip(s = p + 1, ':', '?', 0, 1, 0, 0, version); + tp = X(catalog) ? state.xp : mp; + while (s < p) + { + if ((c = *s++) == '?' || c == ']') + s++; + sfputc(tp, c); + } + if (!X(catalog)) + sfputc(mp, ' '); + else if (p = sfstruse(tp)) + sfputr(mp, T(id, catalog, p), ' '); + else + goto nospace; + } + p = opt_info.name[2] ? C("value expected") : C("argument expected"); + } + else if (*p == '*' || *p == '&') + { + sfputr(mp, opt_info.arg, ':'); + sfputc(mp, ' '); + p = *p == '&' ? C("ambiguous option argument value") : C("unknown option argument value"); + } + else if (*p == '=' || *p == '!') + p = C("value not expected"); + else if (*p == '?') + p = *(p + 1) == '?' ? C("optget: option not supported") : C("ambiguous option"); + else if (*p == '+') + p = C("section not found"); + else + { + if (opt_info.option[0] != '?' && opt_info.option[0] != '-' || opt_info.option[1] != '?' && opt_info.option[1] != '-') + opt_info.option[0] = 0; + p = C("unknown option"); + } + p = T(NiL, ID, p); + sfputr(mp, p, -1); + if (err) + sfputr(mp, " -- out of range", -1); + if (opt_info.arg = sfstruse(mp)) + return ':'; + nospace: + opt_info.arg = T(NiL, ID, "[* out of space *]"); + return ':'; +} + +/* + * argv: command line argv where argv[0] is command name + * + * opts: option control string + * + * '[' [flag][=][index][:<long-name>[|<alias-name>...]['?'description]] ']' + * long option name, index, description; -index returned + * ':' option takes string arg + * '#' option takes numeric arg (concat option may follow) + * '?' (option) following options not in usage + * (following # or :) optional arg + * '[' '[' ... ] ... '[' ... ']' ']' + * mutually exclusive option grouping + * '[' name [:attr]* [?description] ']' + * (following # or :) optional option arg description + * '\n'[' '|'\t']* ignored for legibility + * ' ' ... optional argument(s) description (to end of string) + * or after blank line + * ']]' literal ']' within '[' ... ']' + * + * return: + * 0 no more options + * '?' usage: opt_info.arg points to message sans + * `Usage: command ' + * ':' error: opt_info.arg points to message sans `command: ' + * + * ':' '#' ' ' '[' ']' + * invalid option chars + * + * -- terminates option list and returns 0 + * + * + as first opts char makes + equivalent to - + * + * if any # option is specified then numeric options (e.g., -123) + * are associated with the leftmost # option in opts + * + * usage info in placed opt_info.arg when '?' returned + * see help_text[] (--???) for more info + */ + +int +optget(register char** argv, const char* oopts) +{ + register int c; + register char* s; + char* a; + char* b; + char* e; + char* f; + char* g; + char* v; + char* w; + char* p; + char* q; + char* t; + char* y; + char* numopt; + char* opts; + char* id; + char* catalog; + int n; + int m; + int k; + int j; + int x; + int err; + int no; + int nov; + int num; + int numchr; + int prefix; + int version; + Help_t* hp; + Push_t* psp; + Push_t* tsp; + Sfio_t* vp; + Sfio_t* xp; + Optcache_t* cache; + Optcache_t* pcache; + Optpass_t* pass; + +#if !_PACKAGE_astsa && !_YOU_FIGURED_OUT_HOW_TO_GET_ALL_DLLS_TO_DO_THIS_ + /* + * these are not initialized by all dlls! + */ + + extern Error_info_t _error_info_; + extern Opt_t _opt_info_; + + if (!_error_infop_) + _error_infop_ = &_error_info_; + if (!_opt_infop_) + _opt_infop_ = &_opt_info_; +#endif + if (!oopts) + return 0; + state.pindex = opt_info.index; + state.poffset = opt_info.offset; + if (!opt_info.index) + { + opt_info.index = 1; + opt_info.offset = 0; + if (state.npass) + { + state.npass = 0; + state.join = 0; + } + } + if (!argv) + cache = 0; + else + for (pcache = 0, cache = state.cache; cache; pcache = cache, cache = cache->next) + if (cache->pass.oopts == (char*)oopts) + break; + if (cache) + { + if (pcache) + { + pcache->next = cache->next; + cache->next = state.cache; + state.cache = cache; + } + pass = &cache->pass; + state.npass = -1; + } + else + { + if (!argv) + n = state.npass ? state.npass : 1; + else if ((n = state.join - 1) < 0) + n = 0; + if (n >= state.npass || state.pass[n].oopts != (char*)oopts) + { + for (m = 0; m < state.npass && state.pass[m].oopts != (char*)oopts; m++); + if (m < state.npass) + n = m; + else + { + if (n >= elementsof(state.pass)) + n = elementsof(state.pass) - 1; + init((char*)oopts, &state.pass[n]); + if (state.npass <= n) + state.npass = n + 1; + } + } + if (!argv) + return 0; + pass = &state.pass[n]; + } + opts = pass->opts; + prefix = pass->prefix; + version = pass->version; + id = pass->id; + if (!(xp = state.xp) || (catalog = pass->catalog) && !X(catalog)) + catalog = 0; + else /* if (!error_info.catalog) */ + error_info.catalog = catalog; + again: + psp = 0; + + /* + * check if any options remain and determine if the + * next option is short or long + */ + + opt_info.assignment = 0; + num = 1; + w = v = 0; + x = 0; + for (;;) + { + if (!opt_info.offset) + { + /* + * finished with the previous arg + */ + + if (opt_info.index == 1 && opt_info.argv != state.strv) + { + opt_info.argv = 0; + state.argv[0] = 0; + if (argv[0] && (state.argv[0] = save(argv[0], strlen(argv[0]), 0, 0, 0, 0))) + opt_info.argv = state.argv; + state.style = STYLE_short; + } + if (!(s = argv[opt_info.index])) + return 0; + if (!prefix) + { + /* + * long with no prefix (dd style) + */ + + n = 2; + if ((c = *s) != '-' && c != '+') + c = '-'; + else if (*++s == c) + { + if (!*++s) + { + opt_info.index++; + return 0; + } + else if (*s == c) + return 0; + } + else if (*s == '?') + n = 1; + } + else if ((c = *s++) != '-' && (c != '+' || !(pass->flags & OPT_plus) && (!(pass->flags & OPT_numeric) || !isdigit(*s)))) + { + if (!(pass->flags & OPT_old) || !isalpha(c)) + return 0; + s--; + n = 1; + opt_info.offset--; + } + else if (*s == c) + { + if (!*++s) + { + /* + * -- or ++ end of options + */ + + opt_info.index++; + return 0; + } + else if (*s == c) + { + /* + * ---* or +++* are operands + */ + + return 0; + } + if (version || *s == '?' || !(pass->flags & OPT_minus)) + { + /* + * long with double prefix + */ + + n = 2; + } + else + { + /* + * short option char '-' + */ + + s--; + n = 1; + } + } + else if (prefix == 1 && *s != '?') + { + /* + * long with single prefix (find style) + */ + + n = 2; + } + else + { + /* + * short (always with single prefix) + */ + + n = 1; + } + + /* + * just a prefix is an option (e.g., `-' == stdin) + */ + + if (!*s) + return 0; + if (c == '+') + opt_info.arg = 0; + if (n == 2) + { + x = 0; + state.style = STYLE_long; + opt_info.option[0] = opt_info.name[0] = opt_info.name[1] = c; + w = &opt_info.name[prefix]; + if ((*s == 'n' || *s == 'N') && (*(s + 1) == 'o' || *(s + 1) == 'O') && *(s + 2) && *(s + 2) != '=') + no = *(s + 2) == '-' ? 3 : 2; + else + no = 0; + for (c = *s; *s; s++) + { + if (*s == '=') + { + if (*(s + 1) == '=') + s++; + if (!isalnum(*(s - 1)) && *(w - 1) == (opt_info.assignment = *(s - 1))) + w--; + v = ++s; + break; + } + if (w < &opt_info.name[elementsof(opt_info.name) - 1] && *s != ':' && *s != '|' && *s != '[' && *s != ']') + *w++ = *s; + } + *w = 0; + w = &opt_info.name[prefix]; + c = *w; + opt_info.offset = 0; + opt_info.index++; + break; + } + opt_info.offset++; + } + if (!argv[opt_info.index]) + return 0; + if (c = argv[opt_info.index][opt_info.offset++]) + { + if ((k = argv[opt_info.index][0]) != '-' && k != '+') + k = '-'; + opt_info.option[0] = opt_info.name[0] = k; + opt_info.option[1] = opt_info.name[1] = c; + opt_info.option[2] = opt_info.name[2] = 0; + break; + } + opt_info.offset = 0; + opt_info.index++; + } + + /* + * at this point: + * + * c the first character of the option + * w long option name if != 0, otherwise short + * v long option value (via =) if w != 0 + */ + + if (c == '?') + { + /* + * ? always triggers internal help + */ + + if (!state.msgdict) + initdict(); + if (w) + { + if (!v && (*(w + 1) || !(v = argv[opt_info.index]) || !++opt_info.index)) + v = w + 1; + else if (w[0] != '?' || w[1]) + { + s = w; + w = v; + v = s + 1; + } + } + opt_info.option[1] = c; + opt_info.option[2] = 0; + if (!w) + { + opt_info.name[1] = c; + opt_info.name[2] = 0; + } + goto help; + } + else if (w && !state.msgdict) + initdict(); + numopt = 0; + f = 0; + s = opts; + + /* + * no option can start with these characters + */ + + if (c == ':' || c == '#' || c == ' ' || c == '[' || c == ']') + { + if (c != *s) + s = ""; + } + else + { + a = 0; + if (!w && (pass->flags & OPT_cache)) + { + if (cache) + { + if (k = cache->flags[map[c]]) + { + opt_info.arg = 0; + + /* + * this is a ksh getopts workaround + */ + + if (opt_info.num != LONG_MIN) + opt_info.num = (long)(opt_info.number = !(k & OPT_cache_invert)); + if (!(k & (OPT_cache_string|OPT_cache_numeric))) + return c; + if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset])) + { + if (!(k & OPT_cache_numeric)) + { + opt_info.offset = 0; + return c; + } + opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); + if (err || e == opt_info.arg) + { + if (!err && (k & OPT_cache_optional)) + { + opt_info.arg = 0; + opt_info.index--; + return c; + } + } + else if (*e) + { + opt_info.offset += e - opt_info.arg; + opt_info.index--; + return c; + } + else + { + opt_info.offset = 0; + return c; + } + } + else if (opt_info.arg = argv[opt_info.index]) + { + opt_info.index++; + if ((k & OPT_cache_optional) && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1)) + { + opt_info.arg = 0; + opt_info.index--; + opt_info.offset = 0; + return c; + } + if (k & OPT_cache_string) + { + opt_info.offset = 0; + return c; + } + opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); + if (!err) + { + if (!*e) + { + opt_info.offset = 0; + return c; + } + if (k & OPT_cache_optional) + { + opt_info.arg = 0; + opt_info.index--; + opt_info.offset = 0; + return c; + } + } + } + else if (k & OPT_cache_optional) + { + opt_info.offset = 0; + return c; + } + opt_info.index--; + } + cache = 0; + } + else if (cache = newof(0, Optcache_t, 1, 0)) + { + cache->caching = c; + c = 0; + cache->pass = *pass; + cache->next = state.cache; + state.cache = cache; + } + } + else + cache = 0; + for (;;) + { + if (!(*(s = next(s, version))) || *s == '\n' || *s == ' ') + { + if (!(tsp = psp)) + { + if (cache) + { + /* + * the first loop pass + * initialized the cache + * so one more pass to + * check the cache or + * bail for a full scan + */ + + cache->flags[0] = 0; + c = cache->caching; + cache->caching = 0; + cache = 0; + s = opts; + continue; + } + if (!x && catalog) + { + /* + * the first loop pass + * translated long + * options and there + * were no matches so + * one more pass for C + * locale + */ + + catalog = 0; + s = opts; + continue; + } + s = ""; + break; + } + s = psp->ob; + psp = psp->next; + free(tsp); + continue; + } + if (*s == '\f') + { + psp = info(psp, s + 1, NiL, xp, id); + if (psp->nb) + s = psp->nb; + else + { + s = psp->ob; + psp = psp->next; + } + continue; + } + message((-20, "optget: opt %s c %c w %s num %ld", show(s), c, w, num)); + if (*s == c && !w) + break; + else if (*s == '[') + { + s = next(s + 1, version); + if (*s == '(') + { + s = nest(f = s); + if (!conformance(f, s - f)) + goto disable; + } + k = *(f = s); + if (k == '+' || k == '-') + /* ignore */; + else if (k == '[' || version < 1) + continue; + else if (w && !cache) + { + nov = no; + if (*(s + 1) == '\f' && (vp = state.vp)) + { + sfputc(vp, k); + s = expand(s + 2, NiL, &t, vp, id); + if (*s) + *(f = s - 1) = k; + else + { + f = sfstrbase(vp); + if (s = strrchr(f, ':')) + f = s - 1; + else + s = f + 1; + } + } + else + t = 0; + if (*s != ':') + s = skip(s, ':', '?', 0, 1, 0, 0, version); + if (*s == ':') + { + if (catalog) + { + p = skip(s + 1, '?', 0, 0, 1, 0, 0, version); + e = sfprints("%-.*s", p - (s + 1), s + 1); + g = T(id, catalog, e); + if (g == e) + p = 0; + else + { + sfprintf(xp, ":%s|%s?", g, e); + if (!(s = sfstruse(xp))) + goto nospace; + } + } + else + p = 0; + y = w; + for (;;) + { + n = m = 0; + e = s + 1; + while (*++s) + { + if (*s == '*' || *s == '\a') + { + if (*s == '\a') + do + { + if (!*++s) + { + s--; + break; + } + } while (*s != '\a'); + j = *(s + 1); + if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0) + { + while (*w) + w++; + m = 0; + break; + } + m = 1; + } + else if (*s == *w || SEP(*s) && SEP(*w)) + w++; + else if (*w == 0) + break; + else if (!SEP(*s)) + { + if (SEP(*w)) + { + if (*++w == *s) + { + w++; + continue; + } + } + else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w)) + break; + for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++); + if (!SEP(*q)) + break; + for (s = q; w > y && *w != *(s + 1); w--); + } + else if (*w != *(s + 1)) + break; + } + if (!*w) + { + nov = 0; + break; + } + if (n = no) + { + m = 0; + s = e - 1; + w = y + n; + while (*++s) + { + if (*s == '*' || *s == '\a') + { + if (*s == '\a') + do + { + if (!*++s) + { + s--; + break; + } + } while (*s != '\a'); + j = *(s + 1); + if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0) + { + while (*w) + w++; + m = 0; + break; + } + m = 1; + } + else if (*s == *w || SEP(*s) && SEP(*w)) + w++; + else if (*w == 0) + break; + else if (!SEP(*s)) + { + if (SEP(*w)) + { + if (*++w == *s) + { + w++; + continue; + } + } + else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w)) + break; + for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++); + if (!SEP(*q)) + break; + for (s = q; w > y && *w != *(s + 1); w--); + } + else if (*w != *(s + 1)) + break; + } + if (!*w) + break; + } + if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|') + break; + w = y; + } + if (p) + s = p; + if (!*w) + { + if (n) + num = 0; + if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']' || *s == 0)) && x) + { + psp = pop(psp); + return opterror("?", 0, version, id, catalog); + } + for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2); + if (*f == ':') + { + x = -1; + opt_info.option[1] = '-'; + opt_info.option[2] = 0; + } + else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':') + { + opt_info.option[1] = x; + opt_info.option[2] = 0; + } + else + { + a = f; + if (*a == '=') + a++; + else + { + if (*(a + 1) == '!') + a++; + if (*(a + 1) == '=') + a += 2; + } + x = -strtol(a, &b, 0); + if ((b - a) > sizeof(opt_info.option) - 2) + b = a + sizeof(opt_info.option) - 2; + memcpy(&opt_info.option[1], a, b - a); + opt_info.option[b - a + 1] = 0; + } + b = e; + if (t) + { + s = t; + t = 0; + } + a = s = skip(s, 0, 0, 0, 1, 0, 0, version); + if (n) + { + w = y; + break; + } + } + w = y; + } + else if (k == c && prefix == 1) + { + w = 0; + opt_info.name[1] = c; + opt_info.name[2] = 0; + opt_info.offset = 2; + opt_info.index--; + break; + } + if (t) + { + s = t; + if (a) + a = t; + } + } + disable: + s = skip(s, 0, 0, 0, 1, 0, 1, version); + if (*s == GO) + s = skip(s + 1, 0, 0, 0, 0, 1, 1, version); + if (cache) + { + m = OPT_cache_flag; + v = s; + if (*v == '#') + { + v++; + m |= OPT_cache_numeric; + } + else if (*v == ':') + { + v++; + m |= OPT_cache_string; + } + if (*v == '?') + { + v++; + m |= OPT_cache_optional; + } + else if (*v == *(v - 1)) + v++; + if (*(v = next(v, version)) == '[') + v = skip(v + 1, 0, 0, 0, 1, 0, 1, version); + if (*v != GO) + { + v = f; + for (;;) + { + if (isdigit(*f) && isdigit(*(f + 1))) + while (isdigit(*(f + 1))) + f++; + else if (*(f + 1) == '=') + break; + else + cache->flags[map[*f]] = m; + j = 0; + while (*(f + 1) == '|') + { + f += 2; + if (!(j = *f) || j == '!' || j == '=' || j == ':' || j == '?' || j == ']') + break; + cache->flags[map[j]] = m; + } + if (j != '!' || (m & OPT_cache_invert)) + break; + f = v; + m |= OPT_cache_invert; + } + } + } + else + { + m = 0; + if (!w) + { + if (isdigit(*f) && isdigit(*(f + 1))) + k = -1; + if (c == k) + m = 1; + while (*(f + 1) == '|') + { + f += 2; + if (!(j = *f)) + { + m = 0; + break; + } + else if (j == c) + m = 1; + else if (j == '!' || j == '=' || j == ':' || j == '?' || j == ']') + break; + } + } + if (m) + { + s--; + if (*++f == '!') + { + f++; + num = 0; + } + if (*f == '=') + { + c = -strtol(++f, &b, 0); + if ((b - f) > sizeof(opt_info.option) - 2) + b = f + sizeof(opt_info.option) - 2; + memcpy(&opt_info.option[1], f, b - f); + opt_info.option[b - f + 1] = 0; + } + else + c = k; + break; + } + } + if (*s == '#') + { + if (!numopt && s > opts) + { + numopt = s - 1; + numchr = k; + if (*f == ':') + numchr = -1; + else if (*(f + 1) != ':' && *(f + 1) != '!' && *(f + 1) != ']') + { + a = f; + if (*a == '=') + a++; + else + { + if (*(a + 1) == '!') + a++; + if (*(a + 1) == '=') + a += 2; + } + numchr = -strtol(a, NiL, 0); + } + } + } + else if (*s != ':') + continue; + } + else if (*s == ']') + { + s++; + continue; + } + else if (*s == '#') + { + if (!numopt && s > opts) + numchr = *(numopt = s - 1); + } + else if (*s != ':') + { + if (cache) + { + m = OPT_cache_flag; + if (*(s + 1) == '#') + { + m |= OPT_cache_numeric; + if (*(s + 2) == '?') + m |= OPT_cache_optional; + } + else if (*(s + 1) == ':') + { + m |= OPT_cache_string; + if (*(s + 2) == '?') + m |= OPT_cache_optional; + } + cache->flags[map[*s]] = m; + } + s++; + continue; + } + message((-21, "optget: opt %s", show(s))); + if (*++s == '?' || *s == *(s - 1)) + s++; + if (*(s = next(s, version)) == '[') + { + s = skip(s + 1, 0, 0, 0, 1, 0, 1, version); + if (*s == GO) + s = skip(s + 1, 0, 0, 0, 0, 1, 1, version); + } + message((-21, "optget: opt %s", show(s))); + } + if (w && x) + { + s = skip(b, '|', '?', 0, 1, 0, 0, version); + if (v && (a == 0 || *a == 0 || *(a + 1) != ':' && *(a + 1) != '#') && (*v == '0' || *v == '1') && !*(v + 1)) + { + if (*v == '0') + num = !num; + v = 0; + } + if ((s - b) >= elementsof(opt_info.name)) + s = b + elementsof(opt_info.name) - 1; + for (;;) + { + if (b >= s) + { + *w = 0; + break; + } + if (*b == '*') + break; + *w++ = *b++; + } + if (!num && v) + return opterror(no ? "!" : "=", 0, version, id, catalog); + w = &opt_info.name[prefix]; + c = x; + s = a; + } + } + if (!*s) + { + if (w) + { + if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), w)) + { + if (!v) + v = (char*)hp->name; + goto help; + } + if (!v) + { + v = opt_info.name; + goto help; + } + } + if (w || !isdigit(c) || !numopt || !(pass->flags & OPT_numeric)) + { + pop(psp); + return opterror("", 0, version, id, catalog); + } + s = numopt; + c = opt_info.option[1] = numchr; + opt_info.offset--; + } + opt_info.arg = 0; + + /* + * this is a ksh getopts workaround + */ + + if (opt_info.num != LONG_MIN) + opt_info.num = (long)(opt_info.number = num); + if ((n = *++s == '#') || *s == ':' || w && !nov && v && (optnumber(v, &e, NiL), n = !*e)) + { + if (w) + { + if (nov) + { + if (v) + { + pop(psp); + return opterror("!", 0, version, id, catalog); + } + opt_info.num = (long)(opt_info.number = 0); + } + else + { + if (!v && *(s + 1) != '?' && (v = argv[opt_info.index])) + { + opt_info.index++; + opt_info.offset = 0; + } + if (!(opt_info.arg = v) || (*v == '0' || *v == '1') && !*(v + 1)) + { + if (*(s + 1) != '?') + { + if (!opt_info.arg) + { + pop(psp); + return opterror(s, 0, version, id, catalog); + } + } + else if (*(t = next(s + 2, version)) == '[') + while (*(t = skip(t, ':', 0, 0, 1, 0, 0, version)) == ':') + if (*++t == '!') + { + if (!v || *v == '1') + { + e = skip(t, ':', '?', ']', 1, 0, 0, version); + opt_info.arg = sfprints("%-.*s", e - t - 1, t + 1); + } + else + { + opt_info.arg = 0; + opt_info.num = (long)(opt_info.number = 0); + } + break; + } + } + if (opt_info.arg && n) + { + opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); + if (err || e == opt_info.arg) + { + pop(psp); + return opterror(s, err, version, id, catalog); + } + } + } + goto optarg; + } + else if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset])) + { + if (*s == '#') + { + opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); + if (err || e == opt_info.arg) + { + if (!err && *(s + 1) == '?') + { + opt_info.arg = 0; + opt_info.index--; + } + else + { + opt_info.offset = 0; + c = opterror(s, err, version, id, catalog); + } + pop(psp); + return c; + } + else if (*e) + { + opt_info.offset += e - opt_info.arg; + opt_info.index--; + pop(psp); + return c; + } + } + } + else if (opt_info.arg = argv[opt_info.index]) + { + opt_info.index++; + if (*(s + 1) == '?' && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1)) + { + opt_info.index--; + opt_info.arg = 0; + } + else if (*s == '#') + { + opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); + if (err || *e) + { + if (!err && *(s + 1) == '?') + { + opt_info.arg = 0; + opt_info.index--; + } + else + { + pop(psp); + opt_info.offset = 0; + return opterror(s, err, version, id, catalog); + } + } + } + } + else if (*(s + 1) != '?') + { + opt_info.index--; + pop(psp); + return opterror(s, 0, version, id, catalog); + } + opt_info.offset = 0; + optarg: + if (*s == ':' && *(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == GO && *(s = next(s + 1, version)) == '[' && isalnum(*(s + 1))) + { + x = 0; + if (opt_info.arg) + { + do + { + w = y = opt_info.arg; + f = s = next(s + 1, version); + k = *f; + if (k == *w && isalpha(k) && !*(w + 1)) + { + x = k; + break; + } + if (*s == '+' || *s == '-') + continue; + else if (*s == '[' || version < 1) + continue; + else + { + if (*s != ':') + s = skip(s, ':', '?', 0, 1, 0, 0, version); + if (*s == ':') + { + if (catalog) + { + p = skip(s + 1, '?', 0, 0, 1, 0, 0, version); + e = sfprints("%-.*s", p - (s + 1), s + 1); + b = T(id, catalog, e); + if (b == e) + p = 0; + else + { + sfprintf(xp, ":%s|%s?", b, e); + if (!(s = sfstruse(xp))) + goto nospace; + } + } + else + p = 0; + for (;;) + { + n = m = 0; + e = s + 1; + while (*++s) + { + if (*s == '*' || *s == '\a') + { + if (*s == '\a') + do + { + if (!*++s) + { + s--; + break; + } + } while (*s != '\a'); + j = *(s + 1); + if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0) + { + while (*w) + w++; + m = 0; + break; + } + m = 1; + } + else if (*s == *w || SEP(*s) && SEP(*w)) + w++; + else if (*w == 0) + break; + else if (!SEP(*s)) + { + if (SEP(*w)) + { + if (*++w == *s) + { + w++; + continue; + } + } + else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w)) + break; + for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++); + if (!SEP(*q)) + break; + for (s = q; w > y && *w != *(s + 1); w--); + } + else if (*w != *(s + 1)) + break; + } + if (!*w) + { + nov = 0; + break; + } + if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|') + break; + w = y; + } + if (p) + s = p; + if (!*w) + { + if (n) + num = 0; + if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']')) && x) + { + pop(psp); + return opterror("&", 0, version, id, catalog); + } + for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2); + if (*f == ':') + x = -1; + else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':') + /* ok */; + else + { + a = f; + if (*a == '=') + a++; + else + { + if (*(a + 1) == '!') + a++; + if (*(a + 1) == '=') + a += 2; + } + x = -strtol(a, &b, 0); + } + b = e; + a = s = skip(s, 0, 0, 0, 1, 0, 0, version); + if (n) + break; + } + } + } + } while (*(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == '['); + if (!(opt_info.num = (long)(opt_info.number = x))) + { + pop(psp); + return opterror("*", 0, version, id, catalog); + } + } + } + } + else if (w && v) + { + pop(psp); + return opterror("=", 0, version, id, catalog); + } + else + { + opt_info.num = (long)(opt_info.number = num); + if (!w && !argv[opt_info.index][opt_info.offset]) + { + opt_info.offset = 0; + opt_info.index++; + } + } + pop(psp); + return c; + help: + if (v && *v == '?' && *(v + 1) == '?' && *(v + 2)) + { + s = v + 2; + if ((s[0] == 'n' || s[0] == 'N') && (s[1] == 'o' || s[1] == 'O')) + { + s += 2; + n = -1; + } + else + n = 1; + if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), s)) + { + if (hp->style < STYLE_man || !(s = argv[opt_info.index]) || s[0] != '-' || s[1] != '-' || !s[2]) + { + opt_info.arg = sfprints("\fversion=%d", version); + pop(psp); + return '?'; + } + state.force = hp->style; + } + else if (match(s, "CONFORMANCE", -1, ID, NiL)) + { + opt_info.arg = sfprints("\f%s", conformance(w, 0)); + pop(psp); + return '?'; + } + else if (match(s, "ESC", -1, ID, NiL) || match(s, "EMPHASIS", -1, ID, NiL)) + state.emphasis = n; + else if (match(s, "MAN", -1, ID, NiL)) + { + opt_info.arg = sfprints("\f%s", secname(*w != '?' ? w : pass->section)); + pop(psp); + return '?'; + } + else if (match(s, "PREFORMAT", -1, ID, NiL)) + state.flags |= OPT_preformat; + else if (match(s, "SECTION", -1, ID, NiL)) + { + opt_info.arg = sfprints("\f%s", pass->section); + pop(psp); + return '?'; + } + else if (match(s, "TEST", -1, ID, NiL)) + { + state.width = OPT_WIDTH; + state.emphasis = 1; + } + else + { + pop(psp); + return opterror(v, 0, version, id, catalog); + } + psp = pop(psp); + if (argv == state.strv) + return '#'; + goto again; + } + if ((opt_info.arg = opthelp(NiL, v)) == (char*)unknown) + { + pop(psp); + return opterror(v, 0, version, id, catalog); + } + pop(psp); + return '?'; + nospace: + pop(psp); + return opterror(NiL, 0, 0, NiL, NiL); +} + +/* + * parse long options with 0,1,2 leading '-' or '+' from string and pass to optget() + * syntax is the unquoted + * + * <length> [-|+|--|++]<name>[[-+:|&=]=<value>\n (or \0 for the last) + * + * or the quoted + * + * [-|+|--|++][no]name[[-+:|&=]=['"{(]value[)}"']][, ]... + * + * with \x escapes passed to chresc() + * + * return '#' for `label:', with opt_info.name==label + * str[opt_info.offset] next arg + * + * optstr(s, 0) + * return '-' if arg, 0 otherwise + * optstr(0, opts) + * use previous parsed str + */ + +int +optstr(const char* str, const char* opts) +{ + register char* s = (char*)str; + register Sfio_t* mp; + register int c; + register int ql; + register int qr; + register int qc; + int v; + char* e; + + again: + if (s) + { + if (!(mp = state.strp) && !(mp = state.strp = sfstropen())) + return 0; + if (state.str != s) + state.str = s; + else if (opt_info.index == 1) + s += opt_info.offset; + while (*s == ',' || *s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') + s++; + if (!*s) + { + state.str = 0; + return 0; + } + if (*s == '-' || *s == '+') + { + c = *s++; + sfputc(mp, c); + if (*s == c) + { + sfputc(mp, c); + s++; + } + } + else + { + sfputc(mp, '-'); + sfputc(mp, '-'); + } + if (isdigit(*s) && (v = (int)strtol(s, &e, 10)) > 1 && isspace(*e) && --v <= strlen(s) && (s[v] == 0 || s[v] == '\n')) + { + s += v; + while (isspace(*++e)); + sfwrite(mp, e, s - e); + } + else + { + while (*s && *s != ',' && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' && *s != '=' && *s != ':') + sfputc(mp, *s++); + if ((c = *s) == ':' && *(s + 1) != '=') + { + opt_info.index = 1; + opt_info.offset = ++s - (char*)str; + if (!(s = sfstruse(mp))) + goto nospace; + s += 2; + e = opt_info.name; + while (e < &opt_info.name[sizeof(opt_info.name)-1] && (*e++ = *s++)); + opt_info.arg = 0; + opt_info.num = (long)(opt_info.number = 0); + opt_info.option[0] = ':'; + opt_info.option[1] = 0; + return '#'; + } + if (c == ':' || c == '=') + { + sfputc(mp, c); + ql = qr = 0; + while (c = *++s) + { + if (c == '\\') + { + sfputc(mp, chresc(s, &e)); + s = e - 1; + } + else if (c == qr) + { + if (qr != ql) + sfputc(mp, c); + if (--qc <= 0) + qr = ql = 0; + } + else if (c == ql) + { + sfputc(mp, c); + qc++; + } + else if (qr) + sfputc(mp, c); + else if (c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r') + break; + else if (c == '"' || c == '\'') + { + ql = qr = c; + qc = 1; + } + else + { + sfputc(mp, c); + if (c == GO) + { + ql = c; + qr = OG; + qc = 1; + } + else if (c == '(') + { + ql = c; + qr = ')'; + qc = 1; + } + } + } + } + } + opt_info.argv = state.strv; + state.strv[0] = T(NiL, ID, "option"); + if (!(state.strv[1] = sfstruse(mp))) + goto nospace; + state.strv[2] = 0; + opt_info.offset = s - (char*)str; + } + if (opts) + { + if (!state.strv[1]) + { + state.str = 0; + return 0; + } + opt_info.index = 1; + v = opt_info.offset; + opt_info.offset = 0; + c = optget(state.strv, opts); + opt_info.index = 1; + opt_info.offset = v; + if (c == '#') + { + s = state.str; + goto again; + } + if ((c == '?' || c == ':') && (opt_info.arg[0] == '-' && opt_info.arg[1] == '-')) + opt_info.arg += 2; + s = opt_info.name; + if (*s++ == '-' && *s++ == '-' && *s) + { + e = opt_info.name; + while (*e++ = *s++); + } + } + else + c = '-'; + return c; + nospace: + return opterror(NiL, 0, 0, NiL, NiL); +} diff --git a/src/lib/libast/misc/optjoin.c b/src/lib/libast/misc/optjoin.c new file mode 100644 index 0000000..1648dec --- /dev/null +++ b/src/lib/libast/misc/optjoin.c @@ -0,0 +1,129 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * multi-pass commmand line option parse assist + * + * int fun(char** argv, int last) + * + * each fun() argument parses as much of argv as + * possible starting at (opt_info.index,opt_info.offset) using + * optget() + * + * if last!=0 then fun is the last pass to view + * the current arg, otherwise fun sets opt_info.again=1 + * and another pass will get a crack at it + * + * 0 fun() return causes immediate optjoin() 0 return + * + * optjoin() returns non-zero if more args remain + * to be parsed at opt_info.index + */ + +#include <optlib.h> + +typedef int (*Optpass_f)(char**, int); + +int +optjoin(char** argv, ...) +{ + va_list ap; + register Optpass_f fun; + register Optpass_f rep; + Optpass_f err; + Optstate_t* state; + int r; + int more; + int user; + int last_index; + int last_offset; + int err_index; + int err_offset; + + state = optstate(&opt_info); + err = rep = 0; + r = -1; + while (r < 0) + { + va_start(ap, argv); + state->join = 0; + while (fun = va_arg(ap, Optpass_f)) + { + last_index = opt_info.index; + last_offset = opt_info.offset; + state->join++; + user = (*fun)(argv, 0); + more = argv[opt_info.index] != 0; + if (!opt_info.again) + { + if (!more) + { + state->join = 0; + r = 0; + break; + } + if (!user) + { + if (*argv[opt_info.index] != '+') + { + state->join = 0; + r = 1; + break; + } + opt_info.again = -1; + } + else + err = 0; + } + if (opt_info.again) + { + if (opt_info.again > 0 && (!err || err_index < opt_info.index || err_index == opt_info.index && err_offset < opt_info.offset)) + { + err = fun; + err_index = opt_info.index; + err_offset = opt_info.offset; + } + opt_info.again = 0; + opt_info.index = state->pindex ? state->pindex : 1; + opt_info.offset = state->poffset; + } + if (!rep || opt_info.index != last_index || opt_info.offset != last_offset) + rep = fun; + else if (fun == rep) + { + if (!err) + { + state->join = 0; + r = 1; + break; + } + (*err)(argv, 1); + opt_info.offset = 0; + } + } + va_end(ap); + } + return r; +} diff --git a/src/lib/libast/misc/optlib.h b/src/lib/libast/misc/optlib.h new file mode 100644 index 0000000..5cafed9 --- /dev/null +++ b/src/lib/libast/misc/optlib.h @@ -0,0 +1,115 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * command line option parser and usage formatter private definitions + */ + +#ifndef _OPTLIB_H +#define _OPTLIB_H + +#include <ast.h> +#include <cdt.h> + +#define OPT_append 0x001 +#define OPT_cache 0x002 +#define OPT_functions 0x004 +#define OPT_ignore 0x008 +#define OPT_long 0x010 +#define OPT_minus 0x020 +#define OPT_module 0x040 +#define OPT_numeric 0x080 +#define OPT_old 0x100 +#define OPT_plus 0x200 + +#define OPT_cache_flag 0x001 +#define OPT_cache_invert 0x002 +#define OPT_cache_numeric 0x004 +#define OPT_cache_optional 0x008 +#define OPT_cache_string 0x010 + +#define OPT_CACHE 128 +#define OPT_FLAGS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +struct Optdisc_s; + +typedef struct Optpass_s +{ + char* opts; + char* oopts; + char* id; + char* catalog; + char* release; + char section[4]; + unsigned char version; + unsigned char prefix; + unsigned short flags; +} Optpass_t; + +typedef struct Optcache_s +{ + struct Optcache_s* next; + Optpass_t pass; + int caching; + unsigned char flags[sizeof(OPT_FLAGS)]; +} Optcache_t; + +typedef struct Optstate_s +{ + Sfio_t* mp; /* opt_info.msg string stream */ + Sfio_t* vp; /* translation string stream */ + Sfio_t* xp; /* translation string stream */ + Sfio_t* cp; /* compatibility string stream */ + Optpass_t pass[8]; /* optjoin() list */ + char* argv[2]; /* initial argv copy */ + char* strv[3]; /* optstr() argv */ + char* str; /* optstr() string */ + Sfio_t* strp; /* optstr() stream */ + int force; /* force this style */ + int pindex; /* prev index for backup */ + int poffset; /* prev offset for backup */ + int npass; /* # optjoin() passes */ + int join; /* optjoin() pass # */ + int plus; /* + ok */ + int style; /* default opthelp() style */ + int width; /* format line width */ + int flags; /* display flags */ + int emphasis; /* ansi term emphasis ok */ + int localized; /* locale initialized */ + Dtdisc_t msgdisc; /* msgdict discipline */ + Dt_t* msgdict; /* default ast.id catalog msgs */ + Optcache_t* cache; /* OPT_cache cache */ + char** conformance; /* conformance id vector */ +} Optstate_t; + +#define _OPT_PRIVATE_ \ + char pad[2*sizeof(void*)]; \ + Optstate_t* state; + +#include <error.h> + +extern Optstate_t* optstate(Opt_t*); + +#endif diff --git a/src/lib/libast/misc/procclose.c b/src/lib/libast/misc/procclose.c new file mode 100644 index 0000000..a492454 --- /dev/null +++ b/src/lib/libast/misc/procclose.c @@ -0,0 +1,98 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * close a proc opened by procopen() + * otherwise exit() status of process is returned + */ + +#include "proclib.h" + +int +procclose(register Proc_t* p) +{ + int pid; + int flags = 0; + int status = -1; + + if (p) + { + if (p->rfd >= 0) + close(p->rfd); + if (p->wfd >= 0 && p->wfd != p->rfd) + close(p->wfd); + if (p->flags & PROC_ORPHAN) + status = 0; + else + { + if (p->flags & PROC_ZOMBIE) + { + /* + * process may leave a zombie behind + * give it a chance to do that but + * don't hang waiting for it + */ + + flags |= WNOHANG; + sleep(1); + } + if (!(p->flags & PROC_FOREGROUND)) + sigcritical(SIG_REG_EXEC|SIG_REG_PROC); + while ((pid = waitpid(p->pid, &status, flags)) == -1 && errno == EINTR); + if (pid != p->pid && (flags & WNOHANG)) + status = 0; + if (!(p->flags & PROC_FOREGROUND)) + sigcritical(0); + else + { + if (p->sigint != SIG_IGN) + signal(SIGINT, p->sigint); + if (p->sigquit != SIG_IGN) + signal(SIGQUIT, p->sigquit); +#if defined(SIGCHLD) +#if _lib_sigprocmask + sigprocmask(SIG_SETMASK, &p->mask, NiL); +#else +#if _lib_sigsetmask + sigsetmask(p->mask); +#else + if (p->sigchld != SIG_DFL) + signal(SIGCHLD, p->sigchld); +#endif +#endif +#endif + } + status = status == -1 ? + EXIT_QUIT : + WIFSIGNALED(status) ? + EXIT_TERM(WTERMSIG(status)) : + EXIT_CODE(WEXITSTATUS(status)); + } + procfree(p); + } + else + status = errno == ENOENT ? EXIT_NOTFOUND : EXIT_NOEXEC; + return status; +} diff --git a/src/lib/libast/misc/procfree.c b/src/lib/libast/misc/procfree.c new file mode 100644 index 0000000..d14ff35 --- /dev/null +++ b/src/lib/libast/misc/procfree.c @@ -0,0 +1,43 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * free a proc opened by procopen() + * skipping wait() and close() + */ + +#include "proclib.h" + +int +procfree(register Proc_t* p) +{ + if (!p) + return -1; + if (p == &proc_default) + p->pid = -1; + else + free(p); + return 0; +} diff --git a/src/lib/libast/misc/proclib.h b/src/lib/libast/misc/proclib.h new file mode 100644 index 0000000..45fc242 --- /dev/null +++ b/src/lib/libast/misc/proclib.h @@ -0,0 +1,64 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * process library definitions + */ + +#ifndef _PROCLIB_H +#define _PROCLIB_H + +#include <ast.h> +#include <errno.h> +#include <sig.h> +#include <wait.h> + +#if _lib_sigprocmask +typedef sigset_t Sig_mask_t; +#else +typedef unsigned long Sig_mask_t; +#endif + +struct Mods_s; + +#define _PROC_PRIVATE_ \ + struct Mod_s* mods; /* process modification state */ \ + long flags; /* original PROC_* flags */ \ + Sig_mask_t mask; /* original blocked sig mask */ \ + Sig_handler_t sigchld; /* PROC_FOREGROUND SIG_DFL */ \ + Sig_handler_t sigint; /* PROC_FOREGROUND SIG_IGN */ \ + Sig_handler_t sigquit; /* PROC_FOREGROUND SIG_IGN */ + +#include <proc.h> + +#define proc_default _proc_info_ /* hide external symbol */ + +extern Proc_t proc_default; /* first proc */ + +#ifndef errno +extern int errno; +#endif + +#endif diff --git a/src/lib/libast/misc/procopen.c b/src/lib/libast/misc/procopen.c new file mode 100644 index 0000000..aa8240a --- /dev/null +++ b/src/lib/libast/misc/procopen.c @@ -0,0 +1,941 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * common process execution support with + * proper sfio, signal and wait() syncronization + * + * _ contains the process path name and is + * placed at the top of the environment + */ + +#include "proclib.h" + +#include <ls.h> +#include <ast_tty.h> + +/* + * not quite ready for _use_spawnveg + */ + +#if _use_spawnveg +#if _lib_fork +#undef _use_spawnveg +#else +#if _WINIX +#define _lib_fork 1 +#endif +#endif +#endif + +#ifndef DEBUG_PROC +#define DEBUG_PROC 1 +#endif + +#if _lib_socketpair +#if _sys_socket +#include <sys/types.h> +#include <sys/socket.h> +#else +#undef _lib_socketpair +#endif +#endif + +Proc_t proc_default = { -1 }; + +#if DEBUG_PROC + +#include <namval.h> + +#define PROC_ENV_OPTIONS "PROC_OPTIONS" + +#define PROC_OPT_ENVIRONMENT (1<<0) +#define PROC_OPT_EXEC (1<<1) +#define PROC_OPT_TRACE (1<<2) +#define PROC_OPT_VERBOSE (1<<3) + +static const Namval_t options[] = +{ + "debug", PROC_OPT_VERBOSE, + "environment", PROC_OPT_ENVIRONMENT, + "exec", PROC_OPT_EXEC, + "trace", PROC_OPT_TRACE, + "verbose", PROC_OPT_VERBOSE, + 0, 0 +}; + +/* + * called by stropt() to set options + */ + +static int +setopt(register void* a, register const void* p, register int n, const char* v) +{ + NoP(v); + if (p) + { + if (n) + *((int*)a) |= ((Namval_t*)p)->value; + else + *((int*)a) &= ~((Namval_t*)p)->value; + } + return 0; +} + +#endif + +#if _use_spawnveg + +typedef struct Fd_s +{ + short fd; + short flag; +} Fd_t; + +typedef struct Mod_s +{ + struct Mod_s* next; + short op; + short save; + + union + { + + struct + { + Fd_t parent; + Fd_t child; + } fd; + + Handler_t handler; + + } arg; + +} Modify_t; + +#endif + +#ifdef SIGPIPE + +/* + * catch but ignore sig + * avoids SIG_IGN being passed to children + */ + +static void +ignoresig(int sig) +{ + signal(sig, ignoresig); +} + +#endif + +/* + * do modification op and save previous state for restore() + */ + +static int +modify(Proc_t* proc, int forked, int op, long arg1, long arg2) +{ +#if _lib_fork + if (forked) + { + int i; + int k; +#ifndef TIOCSCTTY + char* s; +#endif + + switch (op) + { + case PROC_fd_dup: + case PROC_fd_dup|PROC_FD_PARENT: + case PROC_fd_dup|PROC_FD_CHILD: + case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD: + if (arg1 != arg2) + { + if (arg2 != PROC_ARG_NULL) + { + close(arg2); + if (fcntl(arg1, F_DUPFD, arg2) != arg2) + return -1; + } + if (op & PROC_FD_CHILD) + close(arg1); + } + break; + case PROC_fd_ctty: + setsid(); + for (i = 0; i <= 2; i++) + if (arg1 != i) + close(i); + arg2 = -1; +#ifdef TIOCSCTTY + if (ioctl(arg1, TIOCSCTTY, NiL) < 0) + return -1; +#else + if (!(s = ttyname(arg1))) + return -1; + if ((arg2 = open(s, O_RDWR)) < 0) + return -1; +#endif + for (i = 0; i <= 2; i++) + if (arg1 != i && arg2 != i && (k = fcntl(arg1, F_DUPFD, i)) != i) + return -1; + if (arg1 > 2) + close(arg1); + if (arg2 > 2) + close(arg2); + break; + case PROC_sig_dfl: + signal(arg1, SIG_DFL); + break; + case PROC_sig_ign: + signal(arg1, SIG_IGN); + break; + case PROC_sys_pgrp: + if (arg1 < 0) + setsid(); + else if (arg1 > 0) + { + if (arg1 == 1) + arg1 = 0; + if (setpgid(0, arg1) < 0 && arg1 && errno == EPERM) + setpgid(0, 0); + } + break; + case PROC_sys_umask: + umask(arg1); + break; + default: + return -1; + } + } +#if _use_spawnveg + else +#endif +#else + NoP(forked); +#endif +#if _use_spawnveg + { + register Modify_t* m; + + if (!(m = newof(NiL, Modify_t, 1, 0))) + return -1; + m->next = proc->mods; + proc->mods = m; + switch (m->op = op) + { + case PROC_fd_dup: + case PROC_fd_dup|PROC_FD_PARENT: + case PROC_fd_dup|PROC_FD_CHILD: + case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD: + m->arg.fd.parent.fd = (short)arg1; + m->arg.fd.parent.flag = fcntl(arg1, F_GETFD, 0); + if ((m->arg.fd.child.fd = (short)arg2) != arg1) + { + if (arg2 != PROC_ARG_NULL) + { + m->arg.fd.child.flag = fcntl(arg2, F_GETFD, 0); + if ((m->save = fcntl(arg2, F_DUPFD, 3)) < 0) + { + m->op = 0; + return -1; + } + fcntl(m->save, F_SETFD, FD_CLOEXEC); + close(arg2); + if (fcntl(arg1, F_DUPFD, arg2) != arg2) + return -1; + if (op & PROC_FD_CHILD) + close(arg1); + } + else if (op & PROC_FD_CHILD) + { + if (m->arg.fd.parent.flag) + break; + fcntl(arg1, F_SETFD, FD_CLOEXEC); + } + else if (!m->arg.fd.parent.flag) + break; + else + fcntl(arg1, F_SETFD, 0); + return 0; + } + break; + case PROC_sig_dfl: + if ((m->arg.handler = signal(arg1, SIG_DFL)) == SIG_DFL) + break; + m->save = (short)arg1; + return 0; + case PROC_sig_ign: + if ((m->arg.handler = signal(arg1, SIG_IGN)) == SIG_IGN) + break; + m->save = (short)arg1; + return 0; + case PROC_sys_pgrp: + proc->pgrp = arg1; + break; + case PROC_sys_umask: + if ((m->save = (short)umask(arg1)) == arg1) + break; + return 0; + default: + proc->mods = m->next; + free(m); + return -1; + } + proc->mods = m->next; + free(m); + } +#else + NoP(proc); +#endif + return 0; +} + +#if _use_spawnveg + +/* + * restore modifications + */ + +static void +restore(Proc_t* proc) +{ + register Modify_t* m; + register Modify_t* p; + int oerrno; + + NoP(proc); + oerrno = errno; + m = proc->mods; + proc->mods = 0; + while (m) + { + switch (m->op) + { + case PROC_fd_dup: + case PROC_fd_dup|PROC_FD_PARENT: + case PROC_fd_dup|PROC_FD_CHILD: + case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD: + if (m->op & PROC_FD_PARENT) + close(m->arg.fd.parent.fd); + if (m->arg.fd.child.fd != m->arg.fd.parent.fd && m->arg.fd.child.fd != PROC_ARG_NULL) + { + if (!(m->op & PROC_FD_PARENT)) + { + if (m->op & PROC_FD_CHILD) + { + close(m->arg.fd.parent.fd); + fcntl(m->arg.fd.child.fd, F_DUPFD, m->arg.fd.parent.fd); + } + fcntl(m->arg.fd.parent.fd, F_SETFD, m->arg.fd.parent.flag); + } + close(m->arg.fd.child.fd); + fcntl(m->save, F_DUPFD, m->arg.fd.child.fd); + close(m->save); + if (m->arg.fd.child.flag) + fcntl(m->arg.fd.child.fd, F_SETFD, FD_CLOEXEC); + } + else if ((m->op & (PROC_FD_PARENT|PROC_FD_CHILD)) == PROC_FD_CHILD) + fcntl(m->arg.fd.parent.fd, F_SETFD, 0); + break; + case PROC_sig_dfl: + case PROC_sig_ign: + signal(m->save, m->arg.handler); + break; + case PROC_sys_umask: + umask(m->save); + break; + } + p = m; + m = m->next; + free(p); + } + errno = oerrno; +} + +#else + +#define restore(p) + +#endif + +/* + * fork and exec or spawn proc(argv) and return a Proc_t handle + * + * pipe not used when PROC_READ|PROC_WRITE omitted + * argv==0 duplicates current process if possible + * cmd==0 names the current shell + * cmd=="" does error cleanup + * envv is the child environment + * modv is the child modification vector of PROC_*() ops + */ + +Proc_t* +procopen(const char* cmd, char** argv, char** envv, long* modv, int flags) +{ + register Proc_t* proc = 0; + register int procfd; + register char** p; + char** v; + int i; + int forked = 0; + int signalled = 0; + long n; + char path[PATH_MAX]; + char env[PATH_MAX + 2]; + int pio[2]; +#if _lib_fork + int pop[2]; +#endif +#if !_pipe_rw && !_lib_socketpair + int poi[2]; +#endif +#if defined(SIGCHLD) && ( _lib_sigprocmask || _lib_sigsetmask ) + Sig_mask_t mask; +#endif +#if _use_spawnveg + int newenv = 0; +#endif +#if DEBUG_PROC + int debug = PROC_OPT_EXEC; +#endif + +#if _lib_fork + if (!argv && (flags & (PROC_ORPHAN|PROC_OVERLAY))) +#else + if (!argv || (flags & PROC_ORPHAN)) +#endif + { + errno = ENOEXEC; + return 0; + } + pio[0] = pio[1] = -1; +#if _lib_fork + pop[0] = pop[1] = -1; +#endif +#if !_pipe_rw && !_lib_socketpair + poi[0] = poi[1] = -1; +#endif + if (cmd && (!*cmd || !pathpath(cmd, NiL, PATH_REGULAR|PATH_EXECUTE, path, sizeof(path)))) + goto bad; + switch (flags & (PROC_READ|PROC_WRITE)) + { + case 0: + procfd = -1; + break; + case PROC_READ: + procfd = 1; + break; + case PROC_WRITE: + procfd = 0; + break; + case PROC_READ|PROC_WRITE: + procfd = 2; + break; + } + if (proc_default.pid == -1) + proc = &proc_default; + else if (!(proc = newof(0, Proc_t, 1, 0))) + goto bad; + proc->pid = -1; + proc->pgrp = 0; + proc->rfd = -1; + proc->wfd = -1; + proc->flags = flags; + sfsync(NiL); + if (environ && envv != (char**)environ && (envv || (flags & PROC_PARANOID) || argv && (environ[0][0] != '_' || environ[0][1] != '='))) + { + if (!setenviron(NiL)) + goto bad; +#if _use_spawnveg + if (!(flags & PROC_ORPHAN)) + newenv = 1; +#endif + } + if (procfd >= 0) + { +#if _pipe_rw + if (pipe(pio)) + goto bad; +#else + if (procfd > 1) + { +#if _lib_socketpair + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pio)) + goto bad; +#else + if (pipe(pio) || pipe(poi)) + goto bad; +#endif + } + else if (pipe(pio)) + goto bad; +#endif + } + if (flags & PROC_OVERLAY) + { + proc->pid = 0; + forked = 1; + } +#if _use_spawnveg + else if (argv && !(flags & PROC_ORPHAN)) + proc->pid = 0; +#endif +#if _lib_fork + else + { + if (!(flags & PROC_FOREGROUND)) + sigcritical(SIG_REG_EXEC|SIG_REG_PROC); + else + { + signalled = 1; + proc->sigint = signal(SIGINT, SIG_IGN); + proc->sigquit = signal(SIGQUIT, SIG_IGN); +#if defined(SIGCHLD) +#if _lib_sigprocmask + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &proc->mask); +#else +#if _lib_sigsetmask + mask = sigmask(SIGCHLD); + proc->mask = sigblock(mask); +#else + proc->sigchld = signal(SIGCHLD, SIG_DFL); +#endif +#endif +#endif + } + if ((flags & PROC_ORPHAN) && pipe(pop)) + goto bad; + proc->pid = fork(); + if (!(flags & PROC_FOREGROUND)) + sigcritical(0); + else if (!proc->pid) + { + if (proc->sigint != SIG_IGN) + { + proc->sigint = SIG_DFL; + signal(SIGINT, proc->sigint); + } + if (proc->sigquit != SIG_IGN) + { + proc->sigquit = SIG_DFL; + signal(SIGQUIT, proc->sigquit); + } +#if defined(SIGCHLD) +#if _lib_sigprocmask + sigprocmask(SIG_SETMASK, &proc->mask, NiL); +#else +#if _lib_sigsetmask + sigsetmask(proc->mask); +#else + if (proc->sigchld != SIG_IGN) + signal(SIGCHLD, SIG_DFL); +#endif +#endif +#endif + } + else if (proc->pid == -1) + goto bad; + forked = 1; + } +#endif + if (!proc->pid) + { +#if _use_spawnveg + char** oenviron = 0; + char* oenviron0 = 0; + + v = 0; +#endif +#if _lib_fork + if (flags & PROC_ORPHAN) + { + if (!(proc->pid = fork())) + { + close(pop[0]); + close(pop[1]); + } + else + { + if (proc->pid > 0) + write(pop[1], &proc->pid, sizeof(proc->pid)); + _exit(EXIT_NOEXEC); + } + } +#endif +#if DEBUG_PROC + stropt(getenv(PROC_ENV_OPTIONS), options, sizeof(*options), setopt, &debug); +#if _lib_fork + if (debug & PROC_OPT_TRACE) + { + if (!fork()) + { + sfsprintf(path, sizeof(path), "%d", getppid()); + execlp("trace", "trace", "-p", path, NiL); + _exit(EXIT_NOTFOUND); + } + sleep(2); + } +#endif +#endif + if (flags & PROC_DAEMON) + { +#ifdef SIGHUP + modify(proc, forked, PROC_sig_ign, SIGHUP, 0); +#endif + modify(proc, forked, PROC_sig_dfl, SIGTERM, 0); +#ifdef SIGTSTP + modify(proc, forked, PROC_sig_ign, SIGTSTP, 0); +#endif +#ifdef SIGTTIN + modify(proc, forked, PROC_sig_ign, SIGTTIN, 0); +#endif +#ifdef SIGTTOU + modify(proc, forked, PROC_sig_ign, SIGTTOU, 0); +#endif + } + if (flags & (PROC_BACKGROUND|PROC_DAEMON)) + { + modify(proc, forked, PROC_sig_ign, SIGINT, 0); +#ifdef SIGQUIT + modify(proc, forked, PROC_sig_ign, SIGQUIT, 0); +#endif + } + if (flags & (PROC_DAEMON|PROC_SESSION)) + modify(proc, forked, PROC_sys_pgrp, -1, 0); + if (forked || (flags & PROC_OVERLAY)) + { + if ((flags & PROC_PRIVELEGED) && !geteuid()) + { + setuid(geteuid()); + setgid(getegid()); + } + if (flags & (PROC_PARANOID|PROC_GID)) + setgid(getgid()); + if (flags & (PROC_PARANOID|PROC_UID)) + setuid(getuid()); + } + if (procfd > 1) + { + if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[0], PROC_ARG_NULL)) + goto cleanup; + if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[1], 1)) + goto cleanup; +#if _pipe_rw || _lib_socketpair + if (modify(proc, forked, PROC_fd_dup, 1, 0)) + goto cleanup; +#else + if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, poi[0], 0)) + goto cleanup; + if (poi[1] != 0 && modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, poi[1], PROC_ARG_NULL)) + goto cleanup; +#endif + } + else if (procfd >= 0) + { + if (modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[!!procfd], !!procfd)) + goto cleanup; + if (pio[!procfd] != !!procfd && modify(proc, forked, PROC_fd_dup|PROC_FD_CHILD, pio[!procfd], PROC_ARG_NULL)) + goto cleanup; + } + if (modv) + for (i = 0; n = modv[i]; i++) + switch (PROC_OP(n)) + { + case PROC_fd_dup: + case PROC_fd_dup|PROC_FD_PARENT: + case PROC_fd_dup|PROC_FD_CHILD: + case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD: + if (modify(proc, forked, PROC_OP(n), PROC_ARG(n, 1), PROC_ARG(n, 2))) + goto cleanup; + break; + default: + if (modify(proc, forked, PROC_OP(n), PROC_ARG(n, 1), 0)) + goto cleanup; + break; + } +#if _lib_fork + if (forked && (flags & PROC_ENVCLEAR)) + environ = 0; +#if _use_spawnveg + else +#endif +#endif +#if _use_spawnveg + if (newenv) + { + p = environ; + while (*p++); + if (!(oenviron = (char**)memdup(environ, (p - environ) * sizeof(char*)))) + goto cleanup; + } +#endif + if (argv && envv != (char**)environ) + { +#if _use_spawnveg + if (!newenv && environ[0][0] == '_' && environ[0][1] == '=') + oenviron0 = environ[0]; +#endif + env[0] = '_'; + env[1] = '='; + env[2] = 0; + if (!setenviron(env)) + goto cleanup; + } + if ((flags & PROC_PARANOID) && setenv("PATH", astconf("PATH", NiL, NiL), 1)) + goto cleanup; + if ((p = envv) && p != (char**)environ) + while (*p) + if (!setenviron(*p++)) + goto cleanup; + p = argv; +#if _lib_fork + if (forked && !p) + return proc; +#endif +#if DEBUG_PROC + if (!(debug & PROC_OPT_EXEC) || (debug & PROC_OPT_VERBOSE)) + { + if ((debug & PROC_OPT_ENVIRONMENT) && (p = environ)) + while (*p) + sfprintf(sfstderr, "%s\n", *p++); + sfprintf(sfstderr, "+ %s", cmd ? path : "sh"); + if ((p = argv) && *p) + while (*++p) + sfprintf(sfstderr, " %s", *p); + sfprintf(sfstderr, "\n"); +sfsync(sfstderr); + if (!(debug & PROC_OPT_EXEC)) + _exit(0); + p = argv; + } +#endif + if (cmd) + { + strcpy(env + 2, path); + if (forked || (flags & PROC_OVERLAY)) + execve(path, p, environ); +#if _use_spawnveg + else if ((proc->pid = spawnveg(path, p, environ, proc->pgrp)) != -1) + goto cleanup; +#endif + if (errno != ENOEXEC) + goto cleanup; + + /* + * try cmd as a shell script + */ + + if (!(flags & PROC_ARGMOD)) + { + while (*p++); + if (!(v = newof(0, char*, p - argv + 2, 0))) + goto cleanup; + p = v + 2; + if (*argv) + argv++; + while (*p++ = *argv++); + p = v + 1; + } + *p = path; + *--p = "sh"; + } + strcpy(env + 2, (flags & PROC_PARANOID) ? astconf("SH", NiL, NiL) : pathshell()); + if (forked || (flags & PROC_OVERLAY)) + execve(env + 2, p, environ); +#if _use_spawnveg + else + proc->pid = spawnveg(env + 2, p, environ, proc->pgrp); +#endif + cleanup: + if (forked) + { + if (!(flags & PROC_OVERLAY)) + _exit(errno == ENOENT ? EXIT_NOTFOUND : EXIT_NOEXEC); + goto bad; + } +#if _use_spawnveg + if (v) + free(v); + if (p = oenviron) + { + environ = 0; + while (*p) + if (!setenviron(*p++)) + goto bad; + free(oenviron); + } + else if (oenviron0) + environ[0] = oenviron0; + restore(proc); + if (flags & PROC_OVERLAY) + exit(0); +#endif + } + if (proc->pid != -1) + { + if (!forked) + { + if (flags & PROC_FOREGROUND) + { + signalled = 1; + proc->sigint = signal(SIGINT, SIG_IGN); + proc->sigquit = signal(SIGQUIT, SIG_IGN); +#if defined(SIGCHLD) +#if _lib_sigprocmask + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &proc->mask); +#else +#if _lib_sigsetmask + mask = sigmask(SIGCHLD); + proc->mask = sigblock(mask); +#else + proc->sigchld = signal(SIGCHLD, SIG_DFL); +#endif +#endif +#endif + } + } + else if (modv) + for (i = 0; n = modv[i]; i++) + switch (PROC_OP(n)) + { + case PROC_fd_dup|PROC_FD_PARENT: + case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD: + close(PROC_ARG(n, 1)); + break; + case PROC_sys_pgrp: + if (proc->pgrp < 0) + proc->pgrp = proc->pid; + else if (proc->pgrp > 0) + { + if (proc->pgrp == 1) + proc->pgrp = proc->pid; + if (setpgid(proc->pid, proc->pgrp) < 0 && proc->pid != proc->pgrp && errno == EPERM) + setpgid(proc->pid, proc->pid); + } + break; + } + if (procfd >= 0) + { +#ifdef SIGPIPE + if ((flags & (PROC_WRITE|PROC_IGNORE)) == (PROC_WRITE|PROC_IGNORE)) + { + Handler_t handler; + + if ((handler = signal(SIGPIPE, ignoresig)) != SIG_DFL && handler != ignoresig) + signal(SIGPIPE, handler); + } +#endif + switch (procfd) + { + case 0: + proc->wfd = pio[1]; + close(pio[0]); + break; + default: +#if _pipe_rw || _lib_socketpair + proc->wfd = pio[0]; +#else + proc->wfd = poi[1]; + close(poi[0]); +#endif + /*FALLTHROUGH*/ + case 1: + proc->rfd = pio[0]; + close(pio[1]); + break; + } + if (proc->rfd > 2) + fcntl(proc->rfd, F_SETFD, FD_CLOEXEC); + if (proc->wfd > 2) + fcntl(proc->wfd, F_SETFD, FD_CLOEXEC); + } + if (!proc->pid) + proc->pid = getpid(); + else if (flags & PROC_ORPHAN) + { + while (waitpid(proc->pid, &i, 0) == -1 && errno == EINTR); + if (read(pop[0], &proc->pid, sizeof(proc->pid)) != sizeof(proc->pid)) + goto bad; + close(pop[0]); + } + return proc; + } + bad: + if (signalled) + { + if (proc->sigint != SIG_IGN) + signal(SIGINT, proc->sigint); + if (proc->sigquit != SIG_IGN) + signal(SIGQUIT, proc->sigquit); +#if defined(SIGCHLD) +#if _lib_sigprocmask + sigprocmask(SIG_SETMASK, &proc->mask, NiL); +#else +#if _lib_sigsetmask + sigsetmask(proc->mask); +#else + if (proc->sigchld != SIG_DFL) + signal(SIGCHLD, proc->sigchld); +#endif +#endif +#endif + } + if ((flags & PROC_CLEANUP) && modv) + for (i = 0; n = modv[i]; i++) + switch (PROC_OP(n)) + { + case PROC_fd_dup: + case PROC_fd_dup|PROC_FD_PARENT: + case PROC_fd_dup|PROC_FD_CHILD: + case PROC_fd_dup|PROC_FD_PARENT|PROC_FD_CHILD: + if (PROC_ARG(n, 2) != PROC_ARG_NULL) + close(PROC_ARG(n, 1)); + break; + } + if (pio[0] >= 0) + close(pio[0]); + if (pio[1] >= 0) + close(pio[1]); + if (pop[0] >= 0) + close(pop[0]); + if (pop[1] >= 0) + close(pop[1]); +#if !_pipe_rw && !_lib_socketpair + if (poi[0] >= 0) + close(poi[0]); + if (poi[1] >= 0) + close(poi[1]); +#endif + procfree(proc); + return 0; +} diff --git a/src/lib/libast/misc/procrun.c b/src/lib/libast/misc/procrun.c new file mode 100644 index 0000000..a37e067 --- /dev/null +++ b/src/lib/libast/misc/procrun.c @@ -0,0 +1,49 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * procopen() + procclose() + * no env changes + * no modifications + * effective=real + * parent ignores INT & QUIT + */ + +#include "proclib.h" + +int +procrun(const char* path, char** argv, int flags) +{ +#if __OBSOLETE__ < 20090101 + flags &= argv ? PROC_ARGMOD : PROC_CHECK; +#endif + if (flags & PROC_CHECK) + { + char buf[PATH_MAX]; + + return pathpath(path, NiL, PATH_REGULAR|PATH_EXECUTE, buf, sizeof(buf)) ? 0 : -1; + } + return procclose(procopen(path, argv, NiL, NiL, flags|PROC_FOREGROUND|PROC_GID|PROC_UID)); +} diff --git a/src/lib/libast/misc/recfmt.c b/src/lib/libast/misc/recfmt.c new file mode 100644 index 0000000..09c3220 --- /dev/null +++ b/src/lib/libast/misc/recfmt.c @@ -0,0 +1,165 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * determine record format by sampling data in <buf,size> + * total is the total file size, <=0 if not available + * return r: + * -1 could not determine + * RECTYPE(r)==REC_fixed fixed length REC_F_SIZE(r) + * RECTYPE(r)==REC_delimited variable length delimiter=REC_D_DELIMITER(r) + * RECTYPE(r)==REC_variable variable length + */ + +#include <recfmt.h> + +typedef struct +{ + unsigned int rep[4 * 1024]; + unsigned int hit[UCHAR_MAX + 1]; +} Sample_t; + +Recfmt_t +recfmt(const void* buf, size_t size, off_t total) +{ + register unsigned char* s; + register unsigned char* t; + register Sample_t* q; + register unsigned int* h; + register unsigned int i; + unsigned int j; + unsigned int k; + unsigned int n; + unsigned int m; + unsigned int x; + unsigned long f; + unsigned long g; + + static unsigned char terminators[] = { '\n', 0x15, 0x25 }; + + /* + * check for V format + */ + + s = (unsigned char*)buf; + t = s + size; + while ((k = (t - s)) >= 4 && !s[2] && !s[3]) + { + if ((i = (s[0]<<8)|s[1]) > k) + break; + s += i; + } + if (!k || size > 2 * k) + return REC_V_TYPE(4, 0, 2, 0, 1); + s = (unsigned char*)buf; + + /* + * check for terminated records + */ + + for (i = 0; i < elementsof(terminators); i++) + if ((t = (unsigned char*)memchr((void*)s, k = terminators[i], size / 2)) && (n = t - s + 1) > 1 && (total <= 0 || !(total % n))) + { + for (j = n - 1; j < size; j += n) + if (s[j] != k) + { + n = 0; + break; + } + if (n) + return REC_D_TYPE(terminators[i]); + } + + /* + * check fixed length record frequencies + */ + + if (!(q = newof(0, Sample_t, 1, 0))) + return REC_N_TYPE(); + x = 0; + for (i = 0; i < size; i++) + { + h = q->hit + s[i]; + m = i - *h; + *h = i; + if (m < elementsof(q->rep)) + { + if (m > x) + x = m; + q->rep[m]++; + } + } + n = 0; + m = 0; + f = ~0; + for (i = x; i > 1; i--) + { + if ((total <= 0 || !(total % i)) && q->rep[i] > q->rep[n]) + { + m++; + g = 0; + for (j = i; j < size - i; j += i) + for (k = 0; k < i; k++) + if (s[j + k] != s[j + k - i]) + g++; + g = (((g * 100) / i) * 100) / q->rep[i]; + if (g <= f) + { + f = g; + n = i; + } + } + } + if (m <= 1 && n <= 2 && total > 1 && total < 256) + { + n = 0; + for (i = 0; i < size; i++) + for (j = 0; j < elementsof(terminators); j++) + if (s[i] == terminators[j]) + n++; + n = n ? 0 : total; + } + free(q); + return n ? REC_F_TYPE(n) : REC_N_TYPE(); +} + +#if MAIN + +main() +{ + void* s; + size_t size; + off_t total; + + if (!(s = sfreserve(sfstdin, SF_UNBOUND, 0))) + { + sfprintf(sfstderr, "read error\n"); + return 1; + } + size = sfvalue(sfstdin); + total = sfsize(sfstdin); + sfprintf(sfstdout, "%d\n", recfmt(s, size, total)); + return 0; +} + +#endif diff --git a/src/lib/libast/misc/reclen.c b/src/lib/libast/misc/reclen.c new file mode 100644 index 0000000..ec927f8 --- /dev/null +++ b/src/lib/libast/misc/reclen.c @@ -0,0 +1,71 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * return the length of the current record at b, size n, according to f + * -1 returned on error + * 0 returned if more data is required + */ + +#include <recfmt.h> +#include <ctype.h> + +ssize_t +reclen(Recfmt_t f, const void* b, size_t n) +{ + register unsigned char* s = (unsigned char*)b; + register unsigned char* e; + size_t h; + size_t z; + + switch (RECTYPE(f)) + { + case REC_delimited: + if (e = (unsigned char*)memchr(s, REC_D_DELIMITER(f), n)) + return e - s + 1; + return 0; + case REC_fixed: + return REC_F_SIZE(f); + case REC_variable: + h = REC_V_HEADER(f); + if (n < h) + return 0; + z = 0; + s += REC_V_OFFSET(f); + e = s + REC_V_LENGTH(f); + if (REC_V_LITTLE(f)) + while (e > s) + z = (z<<8)|*--e; + else + while (s < e) + z = (z<<8)|*s++; + if (!REC_V_INCLUSIVE(f)) + z += h; + else if (z < h) + z = h; + return z; + case REC_method: + return -1; + } + return -1; +} diff --git a/src/lib/libast/misc/recstr.c b/src/lib/libast/misc/recstr.c new file mode 100644 index 0000000..8ba8337 --- /dev/null +++ b/src/lib/libast/misc/recstr.c @@ -0,0 +1,206 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * return the record format descriptor given a format string + * e!=0 set to the first unrecognized char after the format + * REC_N_TYPE() returned on error + * + * d[0xNN|delimiter] (delimited, newline default) + * [f][+]size (fixed length) + * v hN oN zN b|l i|n (variable length with size header) + * h header size in bytes (ibm V 4) + * o size offset in bytes (ibm V 0) + * z size length in bytes (ibm V 2) + * l|b little-endian or big-endian size (ibm V b (0)) + * i|n header included/not-included in size (ibm V i (1)) + */ + +#include <recfmt.h> +#include <ctype.h> + +Recfmt_t +recstr(register const char* s, char** e) +{ + char* t; + int n; + long v; + int a[6]; + + while (*s == ' ' || *s == '\t' || *s == ',') + s++; + switch (*s) + { + case 'd': + case 'D': + if (!*++s) + n = '\n'; + else + { + if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X')) + n = (int)strtol(s, &t, 0); + else + n = chresc(s, &t); + s = (const char*)t; + } + if (e) + *e = (char*)s; + return REC_D_TYPE(n); + case 'f': + case 'F': + while (*++s == ' ' || *s == '\t' || *s == ','); + /*FALLTHROUGH*/ + case '+': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + n = strton(s, &t, NiL, 0); + if (n > 0 && t > (char*)s) + { + if (e) + *e = t; + return REC_F_TYPE(n); + } + break; + case 'm': + case 'M': + while (*++s == ' ' || *s == '\t' || *s == ','); + for (t = (char*)s; *t && *t != ' ' && *t != '\t' && *t != ','; t++); + if ((t - s) == 4) + { + if (strneq(s, "data", 4)) + { + if (e) + *e = t; + return REC_M_TYPE(REC_M_data); + } + else if (strneq(s, "path", 4)) + { + if (e) + *e = t; + return REC_M_TYPE(REC_M_path); + } + } + + /* + * TBD: look up name in method libraries + * and assign an integer index + */ + + break; + case 'u': + case 'U': + while (*++s == ' ' || *s == '\t' || *s == ','); + n = strtol(s, &t, 0); + if (n < 0 || n > 15 || *t++ != '.') + break; + v = strtol(t, &t, 0); + if (*t) + break; + if (e) + *e = t; + return REC_U_TYPE(n, v); + case 'v': + case 'V': + a[0] = 0; + a[1] = 4; + a[2] = 0; + a[3] = 2; + a[4] = 0; + a[5] = 1; + n = 0; + for (;;) + { + switch (*++s) + { + case 0: + break; + case 'm': + case 'M': + n = 0; + continue; + case 'h': + case 'H': + n = 1; + continue; + case 'o': + case 'O': + n = 2; + continue; + case 'z': + case 'Z': + n = 3; + continue; + case 'b': + case 'B': + n = 4; + a[n++] = 0; + continue; + case 'l': + case 'L': + n = 4; + a[n++] = 1; + continue; + case 'n': + case 'N': + n = 0; + a[5] = 0; + continue; + case 'i': + case 'I': + n = 0; + a[5] = 1; + continue; + case ' ': + case '\t': + case ',': + case '-': + case '+': + continue; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + v = 0; + a[n++] = strtol(s, &t, 0); + s = (const char*)t - 1; + continue; + } + break; + } + if (e) + *e = (char*)s; + if (a[3] > (a[1] - a[2])) + a[3] = a[1] - a[2]; + return REC_V_RECORD(REC_V_TYPE(a[1], a[2], a[3], a[4], a[5]), a[0]); + case '%': + if (e) + *e = (char*)s + 1; + return REC_M_TYPE(REC_M_path); + case '-': + case '?': + if (e) + *e = (char*)s + 1; + return REC_M_TYPE(REC_M_data); + } + if (e) + *e = (char*)s; + return REC_N_TYPE(); +} diff --git a/src/lib/libast/misc/setenviron.c b/src/lib/libast/misc/setenviron.c new file mode 100644 index 0000000..c67477e --- /dev/null +++ b/src/lib/libast/misc/setenviron.c @@ -0,0 +1,147 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +#include "intercepts.h" + +#include <fs3d.h> + +/* + * put name=value in the environment + * pointer to value returned + * environ==0 is ok + * + * setenviron("N=V") add N=V + * setenviron("N") delete N + * setenviron(0) expect more (pre-fork optimization) + * + * _ always placed at the top + */ + +#define INCREMENT 16 /* environ increment */ + +char* +setenviron(const char* akey) +{ +#undef setenviron + static char** envv; /* recorded environ */ + static char** next; /* next free slot */ + static char** last; /* last free slot (0) */ + static char ok[] = ""; /* delete/optimization ok return*/ + + char* key = (char*)akey; + register char** v = environ; + register char** p = envv; + register char* s; + register char* t; + int n; + + ast.env_serial++; + if (intercepts.intercept_setenviron) + return (*intercepts.intercept_setenviron)(akey); + if (p && !v) + { + environ = next = p; + *++next = 0; + } + else if (p != v || !v) + { + if (v) + { + while (*v++); + n = v - environ + INCREMENT; + v = environ; + } + else + n = INCREMENT; + if (!p || (last - p + 1) < n) + { + if (!p && fs3d(FS3D_TEST)) + { + /* + * kick 3d initialization + */ + + close(open(".", O_RDONLY)); + v = environ; + } + if (!(p = newof(p, char*, n, 0))) + return 0; + last = p + n - 1; + } + envv = environ = p; + if (v && v[0] && v[0][0] == '_' && v[0][1] == '=') + *p++ = *v++; + else + *p++ = "_="; + if (!v) + *p = 0; + else + while (*p = *v++) + if (p[0][0] == '_' && p[0][1] == '=') + envv[0] = *p; + else + p++; + next = p; + p = envv; + } + else if (next == last) + { + n = last - v + INCREMENT + 1; + if (!(p = newof(p, char*, n, 0))) + return 0; + last = p + n - 1; + next = last - INCREMENT; + envv = environ = p; + } + if (!key) + return ok; + for (; s = *p; p++) + { + t = key; + do + { + if (!*t || *t == '=') + { + if (*s == '=') + { + if (!*t) + { + v = p++; + while (*v++ = *p++); + next--; + return ok; + } + *p = key; + return (s = strchr(key, '=')) ? s + 1 : (char*)0; + } + break; + } + } while (*t++ == *s++); + } + if (!(s = strchr(key, '='))) + return ok; + p = next; + *++next = 0; + *p = key; + return s + 1; +} diff --git a/src/lib/libast/misc/sigcrit.c b/src/lib/libast/misc/sigcrit.c new file mode 100644 index 0000000..243c478 --- /dev/null +++ b/src/lib/libast/misc/sigcrit.c @@ -0,0 +1,199 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * signal critical region support + */ + +#include <ast.h> +#include <sig.h> + +static struct +{ + int sig; + int op; +} +signals[] = /* held inside critical region */ +{ + SIGINT, SIG_REG_EXEC, +#ifdef SIGPIPE + SIGPIPE, SIG_REG_EXEC, +#endif +#ifdef SIGQUIT + SIGQUIT, SIG_REG_EXEC, +#endif +#ifdef SIGHUP + SIGHUP, SIG_REG_EXEC, +#endif +#if defined(SIGCHLD) && ( !defined(SIGCLD) || SIGCHLD != SIGCLD || _lib_sigprocmask || _lib_sigsetmask ) + SIGCHLD, SIG_REG_PROC, +#endif +#ifdef SIGTSTP + SIGTSTP, SIG_REG_TERM, +#endif +#ifdef SIGTTIN + SIGTTIN, SIG_REG_TERM, +#endif +#ifdef SIGTTOU + SIGTTOU, SIG_REG_TERM, +#endif +}; + +#ifndef SIG_SETMASK +#undef _lib_sigprocmask +#endif + +#if !_lib_sigprocmask && !_lib_sigsetmask + +static long hold; /* held signal mask */ + +/* + * hold last signal for later delivery + */ + +static void +interrupt(int sig) +{ + signal(sig, interrupt); + hold |= sigmask(sig); +} + +#endif + +/* + * critical signal region handler + * + * op>0 new region according to SIG_REG_*, return region level + * op==0 pop region, return region level + * op<0 return non-zero if any signals held in current region + * + * signals[] held until region popped + */ + +int +sigcritical(int op) +{ + register int i; + static int region; + static int level; +#if _lib_sigprocmask + static sigset_t mask; + sigset_t nmask; +#else +#if _lib_sigsetmask + static long mask; +#else + static Sig_handler_t handler[elementsof(signals)]; +#endif +#endif + + if (op > 0) + { + if (!level++) + { + region = op; + if (op & SIG_REG_SET) + level--; +#if _lib_sigprocmask + sigemptyset(&nmask); + for (i = 0; i < elementsof(signals); i++) + if (op & signals[i].op) + sigaddset(&nmask, signals[i].sig); + sigprocmask(SIG_BLOCK, &nmask, &mask); +#else +#if _lib_sigsetmask + mask = 0; + for (i = 0; i < elementsof(signals); i++) + if (op & signals[i].op) + mask |= sigmask(signals[i].sig); + mask = sigblock(mask); +#else + hold = 0; + for (i = 0; i < elementsof(signals); i++) + if ((op & signals[i].op) && (handler[i] = signal(signals[i].sig, interrupt)) == SIG_IGN) + { + signal(signals[i].sig, handler[i]); + hold &= ~sigmask(signals[i].sig); + } +#endif +#endif + } + return level; + } + else if (op < 0) + { +#if _lib_sigprocmask + sigpending(&nmask); + for (i = 0; i < elementsof(signals); i++) + if (region & signals[i].op) + { + if (sigismember(&nmask, signals[i].sig)) + return 1; + } + return 0; +#else +#if _lib_sigsetmask + /* no way to get pending signals without installing handler */ + return 0; +#else + return hold != 0; +#endif +#endif + } + else + { + /* + * a vfork() may have intervened so we + * allow apparent nesting mismatches + */ + + if (--level <= 0) + { + level = 0; +#if _lib_sigprocmask + sigprocmask(SIG_SETMASK, &mask, NiL); +#else +#if _lib_sigsetmask + sigsetmask(mask); +#else + for (i = 0; i < elementsof(signals); i++) + if (region & signals[i].op) + signal(signals[i].sig, handler[i]); + if (hold) + { + for (i = 0; i < elementsof(signals); i++) + if (region & signals[i].op) + { + if (hold & sigmask(signals[i].sig)) + kill(getpid(), signals[i].sig); + } + pause(); + } +#endif +#endif + } + return level; + } +} diff --git a/src/lib/libast/misc/sigdata.c b/src/lib/libast/misc/sigdata.c new file mode 100644 index 0000000..657aeed --- /dev/null +++ b/src/lib/libast/misc/sigdata.c @@ -0,0 +1,40 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +/* + * Glenn Fowler + * AT&T Research + * + * signal name and text data + */ + +#include <ast.h> +#include <sig.h> + +#include "FEATURE/signal" + +Sig_info_t _sig_info_ = { (char**)sig_name, (char**)sig_text, SIG_MAX }; + +__EXTERN__(Sig_info_t, _sig_info_); + +#ifdef NoF +NoF(sigdata) +#endif diff --git a/src/lib/libast/misc/signal.c b/src/lib/libast/misc/signal.c new file mode 100644 index 0000000..0d5fe9d --- /dev/null +++ b/src/lib/libast/misc/signal.c @@ -0,0 +1,136 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * signal that disables syscall restart on interrupt with clear signal mask + * fun==SIG_DFL also unblocks signal + */ + +#if !_UWIN + +#undef signal +#define signal ______signal + +#endif + +#include <ast.h> +#include <sig.h> + +#if !_UWIN + +#undef signal + +#undef _def_map_ast +#include <ast_map.h> + +#if defined(__EXPORT__) +#define extern __EXPORT__ +#endif + +#endif + +#if defined(SV_ABORT) +#undef SV_INTERRUPT +#define SV_INTERRUPT SV_ABORT +#endif + +#if !_std_signal && (_lib_sigaction && defined(SA_NOCLDSTOP) || _lib_sigvec && defined(SV_INTERRUPT)) + +#if !defined(SA_NOCLDSTOP) || !defined(SA_INTERRUPT) && defined(SV_INTERRUPT) +#undef SA_INTERRUPT +#define SA_INTERRUPT SV_INTERRUPT +#undef sigaction +#define sigaction sigvec +#undef sigemptyset +#define sigemptyset(p) (*(p)=0) +#undef sa_flags +#define sa_flags sv_flags +#undef sa_handler +#define sa_handler sv_handler +#undef sa_mask +#define sa_mask sv_mask +#endif + +extern Sig_handler_t +signal(int sig, Sig_handler_t fun) +{ + struct sigaction na; + struct sigaction oa; + int unblock; +#ifdef SIGNO_MASK + unsigned int flags; +#endif + + if (sig < 0) + { + sig = -sig; + unblock = 0; + } + else + unblock = fun == SIG_DFL; +#ifdef SIGNO_MASK + flags = sig & ~SIGNO_MASK; + sig &= SIGNO_MASK; +#endif + memzero(&na, sizeof(na)); + na.sa_handler = fun; +#if defined(SA_INTERRUPT) || defined(SA_RESTART) + switch (sig) + { +#if defined(SIGIO) || defined(SIGTSTP) || defined(SIGTTIN) || defined(SIGTTOU) +#if defined(SIGIO) + case SIGIO: +#endif +#if defined(SIGTSTP) + case SIGTSTP: +#endif +#if defined(SIGTTIN) + case SIGTTIN: +#endif +#if defined(SIGTTOU) + case SIGTTOU: +#endif +#if defined(SA_RESTART) + na.sa_flags = SA_RESTART; +#endif + break; +#endif + default: +#if defined(SA_INTERRUPT) + na.sa_flags = SA_INTERRUPT; +#endif + break; + } +#endif + if (sigaction(sig, &na, &oa)) + return 0; + if (unblock) + sigunblock(sig); + return oa.sa_handler; +} + +#else + +NoN(signal) + +#endif diff --git a/src/lib/libast/misc/stack.c b/src/lib/libast/misc/stack.c new file mode 100644 index 0000000..fdad3ff --- /dev/null +++ b/src/lib/libast/misc/stack.c @@ -0,0 +1,172 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * pointer stack routines + */ + +static const char id_stack[] = "\n@(#)$Id: stack (AT&T Bell Laboratories) 1984-05-01 $\0\n"; + +#include <ast.h> +#include <stack.h> + +/* + * create a new stack + */ + +STACK +stackalloc(register int size, void* error) +{ + register STACK stack; + register struct stackblock *b; + + if (size <= 0) size = 100; + if (!(stack = newof(0, struct stacktable, 1, 0))) return(0); + if (!(b = newof(0, struct stackblock, 1, 0))) + { + free(stack); + return(0); + } + if (!(b->stack = newof(0, void*, size, 0))) + { + free(b); + free(stack); + return(0); + } + stack->blocks = b; + stack->size = size; + stack->error = error; + stack->position.block = b; + stack->position.index = -1; + b->next = 0; + b->prev = 0; + return(stack); +} + +/* + * remove a stack + */ + +void +stackfree(register STACK stack) +{ + register struct stackblock* b; + register struct stackblock* p; + + b = stack->blocks; + while (p = b) + { + b = p->next; + free(p->stack); + free(p); + } + free(stack); +} + +/* + * clear stack + */ + +void +stackclear(register STACK stack) +{ + stack->position.block = stack->blocks; + stack->position.index = -1; +} + +/* + * get value on top of stack + */ + +void* +stackget(register STACK stack) +{ + if (stack->position.index < 0) return(stack->error); + else return(stack->position.block->stack[stack->position.index]); +} + +/* + * push value on to stack + */ + +int +stackpush(register STACK stack, void* value) +{ + register struct stackblock *b; + + if (++stack->position.index >= stack->size) + { + b = stack->position.block; + if (b->next) b = b->next; + else + { + if (!(b->next = newof(0, struct stackblock, 1, 0))) + return(-1); + b = b->next; + if (!(b->stack = newof(0, void*, stack->size, 0))) + return(-1); + b->prev = stack->position.block; + b->next = 0; + } + stack->position.block = b; + stack->position.index = 0; + } + stack->position.block->stack[stack->position.index] = value; + return(0); +} + +/* + * pop value off stack + */ + +int +stackpop(register STACK stack) +{ + /* + * return: + * + * -1 if stack empty before pop + * 0 if stack empty after pop + * 1 if stack not empty before & after pop + */ + + if (stack->position.index < 0) return(-1); + else if (--stack->position.index < 0) + { + if (!stack->position.block->prev) return(0); + stack->position.block = stack->position.block->prev; + stack->position.index = stack->size - 1; + return(1); + } + else return(1); +} + +/* + * set|get stack position + */ + +void +stacktell(register STACK stack, int set, STACKPOS* position) +{ + if (set) stack->position = *position; + else *position = stack->position; +} diff --git a/src/lib/libast/misc/state.c b/src/lib/libast/misc/state.c new file mode 100644 index 0000000..0e73be4 --- /dev/null +++ b/src/lib/libast/misc/state.c @@ -0,0 +1,42 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +static const char id[] = "\n@(#)$Id: ast (AT&T Research) 2010-01-02 $\0\n"; + +#include <ast.h> + +#undef strcmp + +_Ast_info_t _ast_info = +{ + "libast", /* id */ + { 0 }, + 0,0,0,0,0, + strcmp, /* collate */ + 0,0, + 1, /* mb_cur_max */ + 0,0,0,0,0,0,0, + 20100102 /* version */ +}; + +__EXTERN__(_Ast_info_t, _ast_info); diff --git a/src/lib/libast/misc/stk.c b/src/lib/libast/misc/stk.c new file mode 100644 index 0000000..1abc28d --- /dev/null +++ b/src/lib/libast/misc/stk.c @@ -0,0 +1,553 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Routines to implement a stack-like storage library + * + * A stack consists of a link list of variable size frames + * The beginning of each frame is initialized with a frame structure + * that contains a pointer to the previous frame and a pointer to the + * end of the current frame. + * + * This is a rewrite of the stk library that uses sfio + * + * David Korn + * AT&T Research + * dgk@research.att.com + * + */ + +#include <sfio_t.h> +#include <ast.h> +#include <align.h> +#include <stk.h> + +/* + * A stack is a header and a linked list of frames + * The first frame has structure + * Sfio_t + * Sfdisc_t + * struct stk + * Frames have structure + * struct frame + * data + */ + +#define STK_ALIGN ALIGN_BOUND +#define STK_FSIZE (1024*sizeof(char*)) +#define STK_HDRSIZE (sizeof(Sfio_t)+sizeof(Sfdisc_t)) + +typedef char* (*_stk_overflow_)(int); + +static int stkexcept(Sfio_t*,int,void*,Sfdisc_t*); +static Sfdisc_t stkdisc = { 0, 0, 0, stkexcept }; + +Sfio_t _Stak_data = SFNEW((char*)0,0,-1,SF_STATIC|SF_WRITE|SF_STRING,&stkdisc,0); + +__EXTERN__(Sfio_t, _Stak_data); + +struct frame +{ + char *prev; /* address of previous frame */ + char *end; /* address of end this frame */ + char **aliases; /* address aliases */ + int nalias; /* number of aliases */ +}; + +struct stk +{ + _stk_overflow_ stkoverflow; /* called when malloc fails */ + short stkref; /* reference count; */ + short stkflags; /* stack attributes */ + char *stkbase; /* beginning of current stack frame */ + char *stkend; /* end of current stack frame */ +}; + +static size_t init; /* 1 when initialized */ +static struct stk *stkcur; /* pointer to current stk */ +static char *stkgrow(Sfio_t*, size_t); + +#define stream2stk(stream) ((stream)==stkstd? stkcur:\ + ((struct stk*)(((char*)(stream))+STK_HDRSIZE))) +#define stk2stream(sp) ((Sfio_t*)(((char*)(sp))-STK_HDRSIZE)) +#define stkleft(stream) ((stream)->_endb-(stream)->_data) + + +#ifdef STKSTATS + static struct + { + int create; + int delete; + int install; + int alloc; + int copy; + int puts; + int seek; + int set; + int grow; + int addsize; + int delsize; + int movsize; + } _stkstats; +# define increment(x) (_stkstats.x++) +# define count(x,n) (_stkstats.x += (n)) +#else +# define increment(x) +# define count(x,n) +#endif /* STKSTATS */ + +static const char Omsg[] = "malloc failed while growing stack\n"; + +/* + * default overflow exception + */ +static char *overflow(int n) +{ + NoP(n); + write(2,Omsg, sizeof(Omsg)-1); + exit(2); + /* NOTREACHED */ + return(0); +} + +/* + * initialize stkstd, sfio operations may have already occcured + */ +static void stkinit(size_t size) +{ + register Sfio_t *sp; + init = size; + sp = stkopen(0); + init = 1; + stkinstall(sp,overflow); +} + +static int stkexcept(register Sfio_t *stream, int type, void* val, Sfdisc_t* dp) +{ + NoP(dp); + NoP(val); + switch(type) + { + case SF_CLOSING: + { + register struct stk *sp = stream2stk(stream); + register char *cp = sp->stkbase; + register struct frame *fp; + if(--sp->stkref<=0) + { + increment(delete); + if(stream==stkstd) + stkset(stream,(char*)0,0); + else + { + while(1) + { + fp = (struct frame*)cp; + if(fp->prev) + { + cp = fp->prev; + free(fp); + } + else + { + free(fp); + break; + } + } + } + } + stream->_data = stream->_next = 0; + } + return(0); + case SF_FINAL: + free(stream); + return(1); + case SF_DPOP: + return(-1); + case SF_WRITE: + case SF_SEEK: + { + long size = sfvalue(stream); + if(init) + { + Sfio_t *old = 0; + if(stream!=stkstd) + old = stkinstall(stream,NiL); + if(!stkgrow(stkstd,size-(stkstd->_endb-stkstd->_data))) + return(-1); + if(old) + stkinstall(old,NiL); + } + else + stkinit(size); + } + return(1); + case SF_NEW: + return(-1); + } + return(0); +} + +/* + * create a stack + */ +Sfio_t *stkopen(int flags) +{ + register size_t bsize; + register Sfio_t *stream; + register struct stk *sp; + register struct frame *fp; + register Sfdisc_t *dp; + register char *cp; + if(!(stream=newof((char*)0,Sfio_t, 1, sizeof(*dp)+sizeof(*sp)))) + return(0); + increment(create); + count(addsize,sizeof(*stream)+sizeof(*dp)+sizeof(*sp)); + dp = (Sfdisc_t*)(stream+1); + dp->exceptf = stkexcept; + sp = (struct stk*)(dp+1); + sp->stkref = 1; + sp->stkflags = (flags&STK_SMALL); + if(flags&STK_NULL) sp->stkoverflow = 0; + else sp->stkoverflow = stkcur?stkcur->stkoverflow:overflow; + bsize = init+sizeof(struct frame); +#ifndef USE_REALLOC + if(flags&STK_SMALL) + bsize = roundof(bsize,STK_FSIZE/16); + else +#endif /* USE_REALLOC */ + bsize = roundof(bsize,STK_FSIZE); + bsize -= sizeof(struct frame); + if(!(fp=newof((char*)0,struct frame, 1,bsize))) + { + free(stream); + return(0); + } + count(addsize,sizeof(*fp)+bsize); + cp = (char*)(fp+1); + sp->stkbase = (char*)fp; + fp->prev = 0; + fp->nalias = 0; + fp->aliases = 0; + fp->end = sp->stkend = cp+bsize; + if(!sfnew(stream,cp,bsize,-1,SF_STRING|SF_WRITE|SF_STATIC|SF_EOF)) + return((Sfio_t*)0); + sfdisc(stream,dp); + return(stream); +} + +/* + * return a pointer to the current stack + * if <stream> is not null, it becomes the new current stack + * <oflow> becomes the new overflow function + */ +Sfio_t *stkinstall(Sfio_t *stream, _stk_overflow_ oflow) +{ + Sfio_t *old; + register struct stk *sp; + if(!init) + { + stkinit(1); + if(oflow) + stkcur->stkoverflow = oflow; + return((Sfio_t*)0); + } + increment(install); + old = stkcur?stk2stream(stkcur):0; + if(stream) + { + sp = stream2stk(stream); + while(sfstack(stkstd, SF_POPSTACK)); + if(stream!=stkstd) + sfstack(stkstd,stream); + stkcur = sp; +#ifdef USE_REALLOC + /*** someday ***/ +#endif /* USE_REALLOC */ + } + else + sp = stkcur; + if(oflow) + sp->stkoverflow = oflow; + return(old); +} + +/* + * increase the reference count on the given <stack> + */ +int stklink(register Sfio_t* stream) +{ + register struct stk *sp = stream2stk(stream); + return(sp->stkref++); +} + +/* + * terminate a stack and free up the space + * >0 returned if reference decremented but still > 0 + * 0 returned on last close + * <0 returned on error + */ +int stkclose(Sfio_t* stream) +{ + register struct stk *sp = stream2stk(stream); + if(sp->stkref>1) + { + sp->stkref--; + return(1); + } + return(sfclose(stream)); +} + +/* + * returns 1 if <loc> is on this stack + */ +int stkon(register Sfio_t * stream, register char* loc) +{ + register struct stk *sp = stream2stk(stream); + register struct frame *fp; + for(fp=(struct frame*)sp->stkbase; fp; fp=(struct frame*)fp->prev) + if(loc>=((char*)(fp+1)) && loc< fp->end) + return(1); + return(0); +} +/* + * reset the bottom of the current stack back to <loc> + * if <loc> is not in this stack, then the stack is reset to the beginning + * otherwise, the top of the stack is set to stkbot+<offset> + * + */ +char *stkset(register Sfio_t * stream, register char* loc, size_t offset) +{ + register struct stk *sp = stream2stk(stream); + register char *cp; + register struct frame *fp; + register int frames = 0; + int n; + if(!init) + stkinit(offset+1); + increment(set); + while(1) + { + fp = (struct frame*)sp->stkbase; + cp = sp->stkbase + roundof(sizeof(struct frame), STK_ALIGN); + n = fp->nalias; + while(n-->0) + { + if(loc==fp->aliases[n]) + { + loc = cp; + break; + } + } + /* see whether <loc> is in current stack frame */ + if(loc>=cp && loc<=sp->stkend) + { + if(frames) + sfsetbuf(stream,cp,sp->stkend-cp); + stream->_data = (unsigned char*)(cp + roundof(loc-cp,STK_ALIGN)); + stream->_next = (unsigned char*)loc+offset; + goto found; + } + if(fp->prev) + { + sp->stkbase = fp->prev; + sp->stkend = ((struct frame*)(fp->prev))->end; + free((void*)fp); + } + else + break; + frames++; + } + /* set stack back to the beginning */ + cp = (char*)(fp+1); + if(frames) + sfsetbuf(stream,cp,sp->stkend-cp); + else + stream->_data = stream->_next = (unsigned char*)cp; +found: + return((char*)stream->_data); +} + +/* + * allocate <n> bytes on the current stack + */ +char *stkalloc(register Sfio_t *stream, register size_t n) +{ + register unsigned char *old; + if(!init) + stkinit(n); + increment(alloc); + n = roundof(n,STK_ALIGN); + if(stkleft(stream) <= (int)n && !stkgrow(stream,n)) + return(0); + old = stream->_data; + stream->_data = stream->_next = old+n; + return((char*)old); +} + +/* + * begin a new stack word of at least <n> bytes + */ +char *_stkseek(register Sfio_t *stream, register ssize_t n) +{ + if(!init) + stkinit(n); + increment(seek); + if(stkleft(stream) <= n && !stkgrow(stream,n)) + return(0); + stream->_next = stream->_data+n; + return((char*)stream->_data); +} + +/* + * advance the stack to the current top + * if extra is non-zero, first add a extra bytes and zero the first + */ +char *stkfreeze(register Sfio_t *stream, register size_t extra) +{ + register unsigned char *old, *top; + if(!init) + stkinit(extra); + old = stream->_data; + top = stream->_next; + if(extra) + { + if(extra > (stream->_endb-stream->_next)) + { + if (!(top = (unsigned char*)stkgrow(stream,extra))) + return(0); + old = stream->_data; + } + *top = 0; + top += extra; + } + stream->_next = stream->_data += roundof(top-old,STK_ALIGN); + return((char*)old); +} + +/* + * copy string <str> onto the stack as a new stack word + */ +char *stkcopy(Sfio_t *stream, const char* str) +{ + register unsigned char *cp = (unsigned char*)str; + register size_t n; + register int off=stktell(stream); + char buff[40], *tp=buff; + if(off) + { + if(off > sizeof(buff)) + { + if(!(tp = malloc(off))) + { + struct stk *sp = stream2stk(stream); + if(!sp->stkoverflow || !(tp = (*sp->stkoverflow)(off))) + return(0); + } + } + memcpy(tp, stream->_data, off); + } + while(*cp++); + n = roundof(cp-(unsigned char*)str,STK_ALIGN); + if(!init) + stkinit(n); + increment(copy); + if(stkleft(stream) <= n && !stkgrow(stream,n)) + cp = 0; + else + { + strcpy((char*)(cp=stream->_data),str); + stream->_data = stream->_next = cp+n; + if(off) + { + _stkseek(stream,off); + memcpy(stream->_data, tp, off); + } + } + if(tp!=buff) + free((void*)tp); + return((char*)cp); +} + +/* + * add a new stack frame of size >= <n> to the current stack. + * if <n> > 0, copy the bytes from stkbot to stktop to the new stack + * if <n> is zero, then copy the remainder of the stack frame from stkbot + * to the end is copied into the new stack frame + */ + +static char *stkgrow(register Sfio_t *stream, size_t size) +{ + register size_t n = size; + register struct stk *sp = stream2stk(stream); + register struct frame *fp= (struct frame*)sp->stkbase; + register char *cp, *dp=0; + register size_t m = stktell(stream); + char *end=0; + int nn=0,add=1; + n += (m + sizeof(struct frame)+1); + if(sp->stkflags&STK_SMALL) +#ifndef USE_REALLOC + n = roundof(n,STK_FSIZE/16); + else +#endif /* !USE_REALLOC */ + n = roundof(n,STK_FSIZE); + /* see whether current frame can be extended */ + if(stkptr(stream,0)==sp->stkbase+sizeof(struct frame)) + { + nn = fp->nalias+1; + dp=sp->stkbase; + sp->stkbase = ((struct frame*)dp)->prev; + end = fp->end; + } + cp = newof(dp, char, n, nn*sizeof(char*)); + if(!cp && (!sp->stkoverflow || !(cp = (*sp->stkoverflow)(n)))) + return(0); + increment(grow); + count(addsize,n - (dp?m:0)); + if(dp && cp==dp) + { + nn--; + add=0; + } + fp = (struct frame*)cp; + fp->prev = sp->stkbase; + sp->stkbase = cp; + sp->stkend = fp->end = cp+n; + cp = (char*)(fp+1); + cp = sp->stkbase + roundof((cp-sp->stkbase),STK_ALIGN); + if(fp->nalias=nn) + { + fp->aliases = (char**)fp->end; + if(end) + memmove(fp->aliases,end,nn*sizeof(char*)); + if(add) + fp->aliases[nn-1] = dp + roundof(sizeof(struct frame),STK_ALIGN); + } + if(m && !dp) + { + memcpy(cp,(char*)stream->_data,m); + count(movsize,m); + } + sfsetbuf(stream,cp,sp->stkend-cp); + return((char*)(stream->_next = stream->_data+m)); +} diff --git a/src/lib/libast/misc/systrace.c b/src/lib/libast/misc/systrace.c new file mode 100644 index 0000000..a084c29 --- /dev/null +++ b/src/lib/libast/misc/systrace.c @@ -0,0 +1,68 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * trace systems calls if possible + */ + +#include <ast.h> +#include <error.h> +#include <proc.h> +#include <debug.h> + +void +systrace(const char* id) +{ + register int n; + register char* out; + char* s; + char buf[PATH_MAX]; + char* av[7]; + long ov[2]; + + static char* trace[] = { "trace", "truss", "strace", "traces" }; + + if (!(s = getenv("HOME"))) + return; + if (!id && !(id = (const char*)error_info.id)) + id = (const char*)trace[0]; + out = buf; + out += sfsprintf(out, sizeof(buf), "%s/.%s/%s", s, trace[0], id); + if (access(buf, F_OK)) + return; + av[1] = trace[0]; + av[2] = "-o"; + av[3] = buf; + av[4] = "-p"; + av[5] = out + 1; + av[6] = 0; + ov[0] = PROC_FD_DUP(open("/dev/null", O_WRONLY), 2, PROC_FD_PARENT|PROC_FD_CHILD); + ov[1] = 0; + sfsprintf(out, &buf[sizeof(buf)] - out, ".%d", getpid()); + for (n = 0; n < elementsof(trace); n++) + if (!procfree(procopen(trace[n], av + 1, NiL, ov, PROC_ARGMOD|PROC_GID|PROC_UID|(n == (elementsof(trace) - 1) ? PROC_CLEANUP : 0)))) + { + sleep(1); + break; + } +} diff --git a/src/lib/libast/misc/translate.c b/src/lib/libast/misc/translate.c new file mode 100644 index 0000000..0c9fbe3 --- /dev/null +++ b/src/lib/libast/misc/translate.c @@ -0,0 +1,437 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped + +/* + * AT&T Research and SCO + * ast l10n message translation + */ + +#include "lclib.h" + +#include <cdt.h> +#include <error.h> +#include <mc.h> +#include <nl_types.h> + +#ifndef DEBUG_trace +#define DEBUG_trace 0 +#endif + +#define NOCAT ((nl_catd)-1) +#define GAP 100 + +typedef struct +{ + Dtlink_t link; /* dictionary link */ + Dt_t* messages; /* message dictionary handle */ + nl_catd cat; /* message catalog handle */ + int debug; /* special debug locale */ + const char* locale; /* message catalog locale */ + const char* nlspath; /* message catalog NLSPATH */ + char name[1]; /* catalog name */ +} Catalog_t; + +typedef struct +{ + Dtlink_t link; /* dictionary link */ + Catalog_t* cat; /* current catalog pointer */ + int set; /* set number */ + int seq; /* sequence number */ + char text[1]; /* message text */ +} Message_t; + +typedef struct +{ + Sfio_t* sp; /* temp string stream */ + int off; /* string base offset */ +} Temp_t; + +typedef struct +{ + Dtdisc_t message_disc; /* message dict discipline */ + Dtdisc_t catalog_disc; /* catalog dict discipline */ + Dt_t* catalogs; /* catalog dictionary handle */ + Sfio_t* tmp; /* temporary string stream */ + int error; /* no dictionaries! */ + char null[1]; /* null string */ +} State_t; + +static State_t state = +{ + { offsetof(Message_t, text), 0, 0 }, + { offsetof(Catalog_t, name), 0, 0 }, +}; + +static int +tempget(Sfio_t* sp) +{ + if (sfstrtell(sp) > sfstrsize(sp) / 2) + sfstrseek(sp, 0, SEEK_SET); + return sfstrtell(sp); +} + +static char* +tempuse(Sfio_t* sp, int off) +{ + sfputc(sp, 0); + return sfstrbase(sp) + off; +} + +/* + * add msg to dict + */ + +static int +entry(Dt_t* dict, int set, int seq, const char* msg) +{ + Message_t* mp; + + if (!(mp = newof(0, Message_t, 1, strlen(msg)))) + return 0; + strcpy(mp->text, msg); + mp->set = set; + mp->seq = seq; + if (!dtinsert(dict, mp)) + { + free(mp); + return 0; + } +#if DEBUG_trace > 1 +sfprintf(sfstderr, "AHA#%d:%s set %d seq %d msg `%s'\n", __LINE__, __FILE__, set, seq, msg); +#endif + return 1; +} + +/* + * find catalog in locale and return catopen() descriptor + */ + +static nl_catd +find(const char* locale, const char* catalog) +{ + char* o; + nl_catd d; + char path[PATH_MAX]; + + if (!mcfind(locale, catalog, LC_MESSAGES, 0, path, sizeof(path)) || (d = catopen(path, NL_CAT_LOCALE)) == NOCAT) + { + if (locale == (const char*)lc_categories[AST_LC_MESSAGES].prev) + o = 0; + else if (o = setlocale(LC_MESSAGES, NiL)) + { + ast.locale.set |= AST_LC_internal; + setlocale(LC_MESSAGES, locale); + } + d = catopen(catalog, NL_CAT_LOCALE); + if (o) + { + setlocale(LC_MESSAGES, o); + ast.locale.set &= ~AST_LC_internal; + } + } + return d; +} + +/* + * initialize the catalog s by loading in the default locale messages + */ + +static Catalog_t* +init(register char* s) +{ + register Catalog_t* cp; + register char* u; + register int n; + register int m; + register int set; + nl_catd d; + + static const int sets[] = { AST_MESSAGE_SET, 1 }; + + /* + * insert into the catalog dictionary + */ + + if (!(cp = newof(0, Catalog_t, 1, strlen(s)))) + return 0; + strcpy(cp->name, s); + if (!dtinsert(state.catalogs, cp)) + { + free(cp); + return 0; + } + cp->cat = NOCAT; + + /* + * locate the default locale catalog + */ + + if ((d = find("C", s)) != NOCAT) + { + /* + * load the default locale messages + * this assumes one mesage set for ast (AST_MESSAGE_SET or fallback to 1) + * different packages can share the same message catalog + * name by using different message set numbers + * see <mc.h> mcindex() + * + * this method requires a scan of each catalog, and the + * catalogs do not advertise the max message number, so + * we assume there are no messages after a gap of GAP + * missing messages + */ + + if (cp->messages = dtopen(&state.message_disc, Dtset)) + { + n = m = 0; + for (;;) + { + n++; + if (((s = catgets(d, set = AST_MESSAGE_SET, n, state.null)) && *s || (s = catgets(d, set = 1, n, state.null)) && *s) && entry(cp->messages, set, n, s)) + m = n; + else if ((n - m) > GAP) + break; + } + if (!m) + { + dtclose(cp->messages); + cp->messages = 0; + } + } + catclose(d); + } + return cp; +} + +/* + * return the C locale message pointer for msg in cat + * cat may be a : separated list of candidate names + */ + +static Message_t* +match(const char* cat, const char* msg) +{ + register char* s; + register char* t; + Catalog_t* cp; + Message_t* mp; + size_t n; + + char buf[1024]; + + s = (char*)cat; + for (;;) + { + if (t = strchr(s, ':')) + { + if (s == (char*)cat) + { + if ((n = strlen(s)) >= sizeof(buf)) + n = sizeof(buf) - 1; + s = (char*)memcpy(buf, s, n); + s[n] = 0; + t = strchr(s, ':'); + } + *t = 0; + } + if (*s && ((cp = (Catalog_t*)dtmatch(state.catalogs, s)) || (cp = init(s))) && cp->messages && (mp = (Message_t*)dtmatch(cp->messages, msg))) + { + mp->cat = cp; + return mp; + } + if (!t) + break; + s = t + 1; + } + return 0; +} + +/* + * translate() is called with four arguments: + * + * loc the LC_MESSAGES locale name + * cmd the calling command name + * cat the catalog name, possibly a : separated list + * "libFOO" FOO library messages + * "libshell" ksh command messages + * "SCRIPT" script SCRIPT application messages + * msg message text to be translated + * + * the translated message text is returned on success + * otherwise the original msg is returned + * + * The first time translate() is called (for a non-C locale) + * it creates the state.catalogs dictionary. A dictionary entry + * (Catalog_t) is made each time translate() is called with a new + * cmd:cat argument. + * + * The X/Open interface catgets() is used to obtain a translated + * message. Its arguments include the message catalog name + * and the set/sequence numbers within the catalog. An additional + * dictionary, with entries of type Message_t, is needed for + * mapping untranslated message strings to the set/sequence numbers + * needed by catgets(). A separate Message_t dictionary is maintained + * for each Catalog_t. + */ + +char* +translate(const char* loc, const char* cmd, const char* cat, const char* msg) +{ + register char* r; + char* t; + int p; + int oerrno; + Catalog_t* cp; + Message_t* mp; + + static uint32_t serial; + static char* nlspath; + + oerrno = errno; + r = (char*)msg; + + /* + * quick out + */ + + if (!cmd && !cat) + goto done; + if (cmd && (t = strrchr(cmd, '/'))) + cmd = (const char*)(t + 1); + + /* + * initialize the catalogs dictionary + */ + + if (!state.catalogs) + { + if (state.error) + goto done; + if (!(state.tmp = sfstropen())) + { + state.error = 1; + goto done; + } + if (!(state.catalogs = dtopen(&state.catalog_disc, Dtset))) + { + sfclose(state.tmp); + state.error = 1; + goto done; + } + } + + /* + * get the message + * or do we have to spell it out for you + */ + + if ((!cmd || !(mp = match(cmd, msg))) && + (!cat || !(mp = match(cat, msg))) && + (!error_info.catalog || !(mp = match(error_info.catalog, msg))) && + (!ast.id || !(mp = match(ast.id, msg))) || + !(cp = mp->cat)) + { +#if DEBUG_trace > 1 +sfprintf(sfstderr, "AHA#%d:%s cmd %s cat %s:%s id %s msg `%s'\n", __LINE__, __FILE__, cmd, cat, error_info.catalog, ast.id, msg); +#endif + cp = 0; + goto done; + } + + /* + * adjust for the current locale + */ + +#if DEBUG_trace +sfprintf(sfstderr, "AHA#%d:%s cp->locale `%s' %p loc `%s' %p\n", __LINE__, __FILE__, cp->locale, cp->locale, loc, loc); +#endif + if (serial != ast.env_serial) + { + serial = ast.env_serial; + nlspath = getenv("NLSPATH"); + } + if (cp->locale != loc || cp->nlspath != nlspath) + { + cp->locale = loc; + cp->nlspath = nlspath; + if (cp->cat != NOCAT) + catclose(cp->cat); + if ((cp->cat = find(cp->locale, cp->name)) == NOCAT) + cp->debug = streq(cp->locale, "debug"); + else + cp->debug = 0; +#if DEBUG_trace +sfprintf(sfstderr, "AHA#%d:%s cp->cat %p cp->debug %d NOCAT %p\n", __LINE__, __FILE__, cp->cat, cp->debug, NOCAT); +#endif + } + if (cp->cat == NOCAT) + { + if (cp->debug) + { + p = tempget(state.tmp); + sfprintf(state.tmp, "(%s,%d,%d)", cp->name, mp->set, mp->seq); + r = tempuse(state.tmp, p); + } + else if (ast.locale.set & AST_LC_debug) + { + p = tempget(state.tmp); + sfprintf(state.tmp, "(%s,%d,%d)%s", cp->name, mp->set, mp->seq, r); + r = tempuse(state.tmp, p); + } + } + else + { + /* + * get the translated message + */ + + r = catgets(cp->cat, mp->set, mp->seq, msg); + if (r != (char*)msg) + { + if (streq(r, (char*)msg)) + r = (char*)msg; + else if (strcmp(fmtfmt(r), fmtfmt(msg))) + { + sfprintf(sfstderr, "locale %s catalog %s message %d.%d \"%s\" does not match \"%s\"\n", cp->locale, cp->name, mp->set, mp->seq, r, msg); + r = (char*)msg; + } + } + if (ast.locale.set & AST_LC_debug) + { + p = tempget(state.tmp); + sfprintf(state.tmp, "(%s,%d,%d)%s", cp->name, mp->set, mp->seq, r); + r = tempuse(state.tmp, p); + } + } + if (ast.locale.set & AST_LC_translate) + sfprintf(sfstderr, "translate locale=%s catalog=%s set=%d seq=%d \"%s\" => \"%s\"\n", cp->locale, cp->name, mp->set, mp->seq, msg, r == (char*)msg ? "NOPE" : r); + done: + if (r == (char*)msg && (!cp && streq(loc, "debug") || cp && cp->debug)) + { + p = tempget(state.tmp); + sfprintf(state.tmp, "(%s,%s,%s,%s)", loc, cmd, cat, r); + r = tempuse(state.tmp, p); + } + errno = oerrno; + return r; +} diff --git a/src/lib/libast/misc/univdata.c b/src/lib/libast/misc/univdata.c new file mode 100644 index 0000000..0cc04df --- /dev/null +++ b/src/lib/libast/misc/univdata.c @@ -0,0 +1,58 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Bell Laboratories + * + * universe common data + */ + +#include "univlib.h" + +#ifndef UNIV_MAX + +char univ_env[] = "__UNIVERSE__"; + +#else + +#ifndef NUMUNIV + +#if !_lib_universe +#undef U_GET +#endif + +#ifdef U_GET +char* univ_name[] = { "ucb", "att" }; +#else +char* univ_name[] = { "att", "ucb" }; +#endif + +int univ_max = sizeof(univ_name) / sizeof(univ_name[0]); + +#endif + +char univ_cond[] = "$(UNIVERSE)"; + +int univ_size = sizeof(univ_cond) - 1; + +#endif diff --git a/src/lib/libast/misc/univlib.h b/src/lib/libast/misc/univlib.h new file mode 100644 index 0000000..ba319c5 --- /dev/null +++ b/src/lib/libast/misc/univlib.h @@ -0,0 +1,93 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1985-2011 AT&T Intellectual Property * +* and is licensed under the * +* Eclipse Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.eclipse.org/org/documents/epl-v10.html * +* (with md5 checksum b35adb5213ca9657e911e9befb180842) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* Glenn Fowler <gsf@research.att.com> * +* David Korn <dgk@research.att.com> * +* Phong Vo <kpv@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * Glenn Fowler + * AT&T Research + * + * universe support + * + * symbolic link external representation has trailing '\0' and $(...) style + * conditionals where $(...) corresponds to a kernel object (i.e., probably + * not environ) + * + * universe symlink conditionals use $(UNIVERSE) + */ + +#ifndef _UNIVLIB_H +#define _UNIVLIB_H + +#if defined(__STDPP__directive) && defined(__STDPP__hide) +__STDPP__directive pragma pp:hide getuniverse readlink setuniverse symlink universe +#else +#define getuniverse ______getuniverse +#define readlink ______readlink +#define setuniverse ______setuniverse +#define symlink ______symlink +#define universe ______universe +#endif + +#include <ast.h> +#include <ls.h> +#include <errno.h> + +#define UNIV_SIZE 9 + +#if _cmd_universe && _sys_universe +#include <sys/universe.h> +#endif + +#if defined(__STDPP__directive) && defined(__STDPP__hide) +__STDPP__directive pragma pp:nohide getuniverse readlink setuniverse symlink universe +#else +#undef getuniverse +#undef readlink +#undef setuniverse +#undef symlink +#undef universe +#endif + +#if _cmd_universe +#ifdef NUMUNIV +#define UNIV_MAX NUMUNIV +#else +#define UNIV_MAX univ_max +extern char* univ_name[]; +extern int univ_max; +#endif + +extern char univ_cond[]; +extern int univ_size; + +#else + +extern char univ_env[]; + +#endif + +extern int getuniverse(char*); +extern int readlink(const char*, char*, int); +extern int setuniverse(int); +extern int symlink(const char*, const char*); +extern int universe(int); + +#endif |