summaryrefslogtreecommitdiff
path: root/src/lib/libast/misc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libast/misc')
-rw-r--r--src/lib/libast/misc/astintercept.c53
-rw-r--r--src/lib/libast/misc/cmdarg.c382
-rw-r--r--src/lib/libast/misc/conformance.c151
-rw-r--r--src/lib/libast/misc/debug.c66
-rw-r--r--src/lib/libast/misc/error.c659
-rw-r--r--src/lib/libast/misc/errorf.c41
-rw-r--r--src/lib/libast/misc/errormsg.c41
-rw-r--r--src/lib/libast/misc/errorx.c50
-rw-r--r--src/lib/libast/misc/fastfind.c1282
-rw-r--r--src/lib/libast/misc/findlib.h123
-rw-r--r--src/lib/libast/misc/fmtrec.c102
-rw-r--r--src/lib/libast/misc/fs3d.c116
-rw-r--r--src/lib/libast/misc/fts.c1605
-rw-r--r--src/lib/libast/misc/ftwalk.c156
-rw-r--r--src/lib/libast/misc/ftwflags.c35
-rw-r--r--src/lib/libast/misc/getcwd.c334
-rw-r--r--src/lib/libast/misc/getenv.c113
-rw-r--r--src/lib/libast/misc/glob.c829
-rw-r--r--src/lib/libast/misc/intercepts.h40
-rw-r--r--src/lib/libast/misc/magic.c2497
-rw-r--r--src/lib/libast/misc/magic.tab1721
-rw-r--r--src/lib/libast/misc/mime.c839
-rw-r--r--src/lib/libast/misc/mimelib.h52
-rw-r--r--src/lib/libast/misc/mimetype.c69
-rw-r--r--src/lib/libast/misc/optctx.c70
-rw-r--r--src/lib/libast/misc/optesc.c93
-rw-r--r--src/lib/libast/misc/optget.c5751
-rw-r--r--src/lib/libast/misc/optjoin.c129
-rw-r--r--src/lib/libast/misc/optlib.h115
-rw-r--r--src/lib/libast/misc/procclose.c98
-rw-r--r--src/lib/libast/misc/procfree.c43
-rw-r--r--src/lib/libast/misc/proclib.h64
-rw-r--r--src/lib/libast/misc/procopen.c941
-rw-r--r--src/lib/libast/misc/procrun.c49
-rw-r--r--src/lib/libast/misc/recfmt.c165
-rw-r--r--src/lib/libast/misc/reclen.c71
-rw-r--r--src/lib/libast/misc/recstr.c206
-rw-r--r--src/lib/libast/misc/setenviron.c147
-rw-r--r--src/lib/libast/misc/sigcrit.c199
-rw-r--r--src/lib/libast/misc/sigdata.c40
-rw-r--r--src/lib/libast/misc/signal.c136
-rw-r--r--src/lib/libast/misc/stack.c172
-rw-r--r--src/lib/libast/misc/state.c42
-rw-r--r--src/lib/libast/misc/stk.c553
-rw-r--r--src/lib/libast/misc/systrace.c68
-rw-r--r--src/lib/libast/misc/translate.c437
-rw-r--r--src/lib/libast/misc/univdata.c58
-rw-r--r--src/lib/libast/misc/univlib.h93
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, "&lt;", -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>&gt;", t - s, s, t - s, s);
+ s = t + 1;
+ }
+ break;
+ }
+ else
+ break;
+ }
+ continue;
+ }
+ break;
+ case '>':
+ if (style == STYLE_html)
+ {
+ sfputr(sp, "&gt;", -1);
+ continue;
+ }
+ break;
+ case '&':
+ if (style == STYLE_html)
+ {
+ sfputr(sp, "&amp;", -1);
+ continue;
+ }
+ break;
+ case '"':
+ if (style == STYLE_html)
+ {
+ sfputr(sp, "&quot;", -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, "&lt;", -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>&gt;", t - s, s, t - s, s);
+ s = t + 1;
+ }
+ break;
+ }
+ else
+ break;
+ }
+ continue;
+ }
+ break;
+ case '>':
+ if (style == STYLE_html)
+ {
+ sfputr(sp, "&gt;", -1);
+ continue;
+ }
+ break;
+ case '&':
+ if (style == STYLE_html)
+ {
+ sfputr(sp, "&amp;", -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&nbsp;(&nbsp;%s&nbsp;)&nbsp;<TH align=center><A href=\".\" title=\"Index\">%s</A><TH align=right>%s&nbsp;(&nbsp;%s&nbsp;)</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, "&#0091;", -1);
+ else if (c == ']')
+ sfputr(mp, "&#0093;", -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, "&#0091;", -1);
+ else if (c == ']')
+ sfputr(mp, "&#0093;", -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, "&#0091", -1);
+ c = ';';
+ }
+ else if (c == ']')
+ {
+ sfputr(mp, "&#0093", -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 &copy", -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