]> Zhao Yanbai Git Server - minix.git/commitdiff
Add libcompat_minix for NBSD libc
authorGianluca Guida <gianluca@minix3.org>
Fri, 18 Mar 2011 16:08:26 +0000 (16:08 +0000)
committerGianluca Guida <gianluca@minix3.org>
Fri, 18 Mar 2011 16:08:26 +0000 (16:08 +0000)
libcompat_minix is a compatibility layer meant to let existing commands to work
with the new libc.

20 files changed:
lib/nbsd_libcompat_minix/Makefile [new file with mode: 0644]
lib/nbsd_libcompat_minix/configfile.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/crypt.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/cuserid.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/fttyslot.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/getpwent.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/include/Makefile.inc [new file with mode: 0644]
lib/nbsd_libcompat_minix/include/compat/a.out.h [new file with mode: 0644]
lib/nbsd_libcompat_minix/include/compat/pwd.h [new file with mode: 0644]
lib/nbsd_libcompat_minix/include/compat/regexp.h [new file with mode: 0644]
lib/nbsd_libcompat_minix/include/compat/syslog.h [new file with mode: 0644]
lib/nbsd_libcompat_minix/include/configfile.h [new file with mode: 0644]
lib/nbsd_libcompat_minix/include/termcap.h [new file with mode: 0644]
lib/nbsd_libcompat_minix/mtab.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/nlist.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/stderr.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/termcap.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/v8regerror.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/v8regexp.c [new file with mode: 0644]
lib/nbsd_libcompat_minix/v8regsub.c [new file with mode: 0644]

diff --git a/lib/nbsd_libcompat_minix/Makefile b/lib/nbsd_libcompat_minix/Makefile
new file mode 100644 (file)
index 0000000..db9425e
--- /dev/null
@@ -0,0 +1,46 @@
+.include <bsd.own.mk>
+
+LIB=   compat_minix
+CPPFLAGS+= -D_MINIX_COMPAT
+
+.PATH:         ${.CURDIR}
+
+# std_err()
+SRCS+= stderr.c
+
+# V8 regexp (incompatible with BSD regexp)
+SRCS+= v8regerror.c   v8regexp.c   v8regsub.c
+
+# Configfile Library.
+SRCS+= configfile.c
+
+# ACK a.out format.
+SRCS+= nlist.c
+
+# Minix 'mtab' handling functions.
+#
+# These should be changed when we switch to
+# NetBSD's 'mtab' format.
+SRCS+= mtab.c
+
+# Termcap. 
+# Included in the Minix libc. 
+#
+# Should be removed once we switch to terminfo.
+SRCS+= termcap.c
+
+# Minix legacy passwd format
+# These should be removed when we switch to
+# NetBSD's 'passwd' db-based format.
+SRCS+= crypt.c getpwent.c
+
+# fttyslot(fd), a Minix-specific extension
+SRCS+= fttyslot.c
+
+# cuserid()
+# Now considered "compat" feature in NetBSD.
+SRCS+= cuserid.c
+
+.include "include/Makefile.inc"
+
+.include <bsd.lib.mk>
diff --git a/lib/nbsd_libcompat_minix/configfile.c b/lib/nbsd_libcompat_minix/configfile.c
new file mode 100644 (file)
index 0000000..4c6300f
--- /dev/null
@@ -0,0 +1,574 @@
+/*     config_read(), _delete(), _length() - Generic config file routines.
+ *                                                     Author: Kees J. Bot
+ *                                                             5 Jun 1999
+ */
+#define nil ((void*)0)
+#if __minix_vmd
+#include <minix/stubs.h>
+#else
+#define fstat _fstat
+#define stat _stat
+#endif
+#include <sys/types.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+#if __minix_vmd
+#include <minix/asciictype.h>
+#else
+#include <ctype.h>
+#endif
+#define _c /* not const */
+#include <configfile.h>
+
+typedef struct configfile {    /* List of (included) configuration files. */
+       struct configfile *next;        /* A list indeed. */
+       time_t          ctime;          /* Last changed time, -1 if no file. */
+       char            name[1];        /* File name. */
+} configfile_t;
+
+/* Size of a configfile_t given a file name of length 'len'. */
+#define configfilesize(len)    (offsetof(configfile_t, name) + 1 + (len))
+
+typedef struct firstconfig {   /* First file and first word share a slot. */
+       configfile_t    *filelist;
+       char            new;            /* Set when created. */
+       config_t        config1;
+} firstconfig_t;
+
+/* Size of a config_t given a word of lenght 'len'.  Same for firstconfig_t. */
+#define config0size()          (offsetof(config_t, word))
+#define configsize(len)                (config0size() + 1 + (len))
+#define firstconfigsize(len)   \
+                       (offsetof(firstconfig_t, config1) + configsize(len))
+
+/* Translate address of first config word to enclosing firstconfig_t and vv. */
+#define cfg2fcfg(p)    \
+    ((firstconfig_t *) ((char *) (p) - offsetof(firstconfig_t, config1)))
+#define fcfg2cfg(p)    (&(p)->config1)
+
+/* Variables used while building data. */
+static configfile_t *c_files;          /* List of (included) config files. */
+static int c_flags;                    /* Flags argument of config_read(). */
+static FILE *c_fp;                     /* Current open file. */
+static char *c_file;                   /* Current open file name. */
+static unsigned c_line;                        /* Current line number. */
+static int c;                          /* Next character. */
+
+static void *allocate(void *mem, size_t size)
+/* Like realloc(), but checked. */
+{
+    if ((mem= realloc(mem, size)) == nil) {
+       fprintf(stderr, "\"%s\", line %u: Out of memory\n", c_file, c_line);
+       exit(1);
+    }
+    return mem;
+}
+
+#define deallocate(mem)        free(mem)
+
+static void delete_filelist(configfile_t *cfgf)
+/* Delete configuration file list. */
+{
+    void *junk;
+
+    while (cfgf != nil) {
+       junk= cfgf;
+       cfgf= cfgf->next;
+       deallocate(junk);
+    }
+}
+
+static void delete_config(config_t *cfg)
+/* Delete configuration file data. */
+{
+    config_t *next, *list, *junk;
+
+    next= cfg;
+    list= nil;
+    for (;;) {
+       if (next != nil) {
+           /* Push the 'next' chain in reverse on the 'list' chain, putting
+            * a leaf cell (next == nil) on top of 'list'.
+            */
+           junk= next;
+           next= next->next;
+           junk->next= list;
+           list= junk;
+       } else
+       if (list != nil) {
+           /* Delete the leaf cell.  If it has a sublist then that becomes
+            * the 'next' chain.
+            */
+           junk= list;
+           next= list->list;
+           list= list->next;
+           deallocate(junk);
+       } else {
+           /* Both chains are gone. */
+           break;
+       }
+    }
+}
+
+void config_delete(config_t *cfg1)
+/* Delete configuration file data, being careful with the odd first one. */
+{
+    firstconfig_t *fcfg= cfg2fcfg(cfg1);
+
+    delete_filelist(fcfg->filelist);
+    delete_config(fcfg->config1.next);
+    delete_config(fcfg->config1.list);
+    deallocate(fcfg);
+}
+
+static void nextc(void)
+/* Read the next character of the current file into 'c'. */
+{
+    if (c == '\n') c_line++;
+    c= getc(c_fp);
+    if (c == EOF && ferror(c_fp)) {
+       fprintf(stderr, "\"%s\", line %u: %s\n",
+           c_file, c_line, strerror(errno));
+       exit(1);
+    }
+}
+
+static void skipwhite(void)
+/* Skip whitespace and comments. */
+{
+    while (isspace(c)) {
+       nextc();
+       if (c == '#') {
+           do nextc(); while (c != EOF && c != '\n');
+       }
+    }
+}
+
+static void parse_err(void)
+/* Tell user that you can't parse past the current character. */
+{
+    char sc[2];
+
+    sc[0]= c;
+    sc[1]= 0;
+    fprintf(stderr, "\"%s\", line %u: parse error at '%s'\n",
+       c_file, c_line, c == EOF ? "EOF" : sc);
+    exit(1);
+}
+
+static config_t *read_word(void)
+/* Read a word or string. */
+{
+    config_t *w;
+    size_t i, len;
+    int q;
+    static char SPECIAL[] = "!#$%&*+-./:<=>?[\\]^_|~";
+
+    i= 0;
+    len= 32;
+    w= allocate(nil, configsize(32));
+    w->next= nil;
+    w->list= nil;
+    w->file= c_file;
+    w->line= c_line;
+    w->flags= 0;
+
+    /* Is it a quoted string? */
+    if (c == '\'' || c == '"') {
+       q= c;   /* yes */
+       nextc();
+    } else {
+       q= -1;  /* no */
+    }
+
+    for (;;) {
+       if (i == len) {
+           len+= 32;
+           w= allocate(w, configsize(len));
+       }
+
+       if (q == -1) {
+           /* A word consists of letters, numbers and a few special chars. */
+           if (!isalnum(c) && c < 0x80 && strchr(SPECIAL, c) == nil) break;
+       } else {
+           /* Strings are made up of anything except newlines. */
+           if (c == EOF || c == '\n') {
+               fprintf(stderr,
+                   "\"%s\", line %u: string at line %u not closed\n",
+                   c_file, c_line, w->line);
+               exit(1);
+               break;
+           }
+           if (c == q) {       /* Closing quote? */
+               nextc();
+               break;
+           }
+       }
+
+       if (c != '\\') {        /* Simply add non-escapes. */
+           w->word[i++]= c;
+           nextc();
+       } else {                /* Interpret an escape. */
+           nextc();
+           if (isspace(c)) {
+               skipwhite();
+               continue;
+           }
+
+           if (c_flags & CFG_ESCAPED) {
+               w->word[i++]= '\\';     /* Keep the \ for the caller. */
+               if (i == len) {
+                   len+= 32;
+                   w= allocate(w, configsize(len));
+               }
+               w->flags |= CFG_ESCAPED;
+           }
+
+           if (isdigit(c)) {           /* Octal escape */
+               int n= 3;
+               int d= 0;
+
+               do {
+                   d= d * 010 + (c - '0');
+                   nextc();
+               } while (--n > 0 && isdigit(c));
+               w->word[i++]= d;
+           } else
+           if (c == 'x' || c == 'X') { /* Hex escape */
+               int n= 2;
+               int d= 0;
+
+               nextc();
+               if (!isxdigit(c)) {
+                   fprintf(stderr, "\"%s\", line %u: bad hex escape\n",
+                       c_file, c_line);
+                   exit(1);
+               }
+               do {
+                   d= d * 0x10 + (islower(c) ? (c - 'a' + 0xa) :
+                                   isupper(c) ? (c - 'A' + 0xA) :
+                                   (c - '0'));
+                   nextc();
+               } while (--n > 0 && isxdigit(c));
+               w->word[i++]= d;
+           } else {
+               switch (c) {
+               case 'a':       c= '\a';        break;
+               case 'b':       c= '\b';        break;
+               case 'e':       c= '\033';      break;
+               case 'f':       c= '\f';        break;
+               case 'n':       c= '\n';        break;
+               case 'r':       c= '\r';        break;
+               case 's':       c= ' ';         break;
+               case 't':       c= '\t';        break;
+               case 'v':       c= '\v';        break;
+               default:        /* Anything else is kept as-is. */;
+               }
+               w->word[i++]= c;
+               nextc();
+           }
+       }
+    }
+    w->word[i]= 0;
+    if (q != -1) {
+       w->flags |= CFG_STRING;
+    } else {
+       int f;
+       char *end;
+       static char base[]= { 0, 010, 10, 0x10 };
+
+       if (i == 0) parse_err();
+
+       /* Can the word be used as a number? */
+       for (f= 0; f < 4; f++) {
+           (void) strtol(w->word, &end, base[f]);
+           if (*end == 0) w->flags |= 1 << (f + 0);
+           (void) strtoul(w->word, &end, base[f]);
+           if (*end == 0) w->flags |= 1 << (f + 4);
+       }
+    }
+    return allocate(w, configsize(i));
+}
+
+static config_t *read_file(const char *file);
+static config_t *read_list(void);
+
+static config_t *read_line(void)
+/* Read and return one line of the config file. */
+{
+    config_t *cline, **pcline, *clist;
+
+    cline= nil;
+    pcline= &cline;
+
+    for (;;) {
+       skipwhite();
+
+       if (c == EOF || c == '}') {
+if(0)      if (cline != nil) parse_err();
+           break;
+       } else
+       if (c == ';') {
+           nextc();
+           if (cline != nil) break;
+       } else
+       if (cline != nil && c == '{') {
+           /* A sublist. */
+           nextc();
+           clist= allocate(nil, config0size());
+           clist->next= nil;
+           clist->file= c_file;
+           clist->line= c_line;
+           clist->list= read_list();
+           clist->flags= CFG_SUBLIST;
+           *pcline= clist;
+           pcline= &clist->next;
+           if (c != '}') parse_err();
+           nextc();
+       } else {
+           *pcline= read_word();
+           pcline= &(*pcline)->next;
+       }
+    }
+    return cline;
+}
+
+static config_t *read_list(void)
+/* Read and return a list of config file commands. */
+{
+    config_t *clist, **pclist, *cline;
+
+    clist= nil;
+    pclist= &clist;
+
+    while ((cline= read_line()) != nil) {
+       if (strcmp(cline->word, "include") == 0) {
+           config_t *file= cline->next;
+           if (file == nil || file->next != nil || !config_isatom(file)) {
+               fprintf(stderr,
+                   "\"%s\", line %u: 'include' command requires an argument\n",
+                   c_file, cline->line);
+               exit(1);
+           }
+           if (file->flags & CFG_ESCAPED) {
+               char *p, *q;
+               p= q= file->word;
+               for (;;) {
+                   if ((*q = *p) == '\\') *q = *++p;
+                   if (*q == 0) break;
+                   p++;
+                   q++;
+               }
+           }
+           file= read_file(file->word);
+           delete_config(cline);
+           *pclist= file;
+           while (*pclist != nil) pclist= &(*pclist)->next;
+       } else {
+           config_t *cfg= allocate(nil, config0size());
+           cfg->next= nil;
+           cfg->list= cline;
+           cfg->file= cline->file;
+           cfg->line= cline->line;
+           cfg->flags= CFG_SUBLIST;
+           *pclist= cfg;
+           pclist= &cfg->next;
+       }
+    }
+    return clist;
+}
+
+static config_t *read_file(const char *file)
+/* Read and return a configuration file. */
+{
+    configfile_t *cfgf;
+    config_t *cfg;
+    struct stat st;
+    FILE *old_fp;      /* old_* variables store current file context. */
+    char *old_file;
+    unsigned old_line;
+    int old_c;
+    size_t n;
+    char *slash;
+
+    old_fp= c_fp;
+    old_file= c_file;
+    old_line= c_line;
+    old_c= c;
+
+    n= 0;
+    if (file[0] != '/' && old_file != nil
+                       && (slash= strrchr(old_file, '/')) != nil) {
+       n= slash - old_file + 1;
+    }
+    cfgf= allocate(nil, configfilesize(n + strlen(file)));
+    memcpy(cfgf->name, old_file, n);
+    strcpy(cfgf->name + n, file);
+    cfgf->next= c_files;
+    c_files= cfgf;
+
+    c_file= cfgf->name;
+    c_line= 0;
+
+    if ((c_fp= fopen(file, "r")) == nil || fstat(fileno(c_fp), &st) < 0) {
+       if (errno != ENOENT) {
+           fprintf(stderr, "\"%s\", line 1: %s\n", file, strerror(errno));
+           exit(1);
+       }
+       cfgf->ctime= -1;
+       c= EOF;
+    } else {
+       cfgf->ctime= st.st_ctime;
+       c= '\n';
+    }
+
+    cfg= read_list();
+    if (c != EOF) parse_err();
+
+    if (c_fp != nil) fclose(c_fp);
+    c_fp= old_fp;
+    c_file= old_file;
+    c_line= old_line;
+    c= old_c;
+    return cfg;
+}
+
+config_t *config_read(const char *file, int flags, config_t *cfg)
+/* Read and parse a configuration file. */
+{
+    if (cfg != nil) {
+       /* First check if any of the involved files has changed. */
+       firstconfig_t *fcfg;
+       configfile_t *cfgf;
+       struct stat st;
+
+       fcfg= cfg2fcfg(cfg);
+       for (cfgf= fcfg->filelist; cfgf != nil; cfgf= cfgf->next) {
+           if (stat(cfgf->name, &st) < 0) {
+               if (errno != ENOENT) break;
+               st.st_ctime= -1;
+           }
+           if (st.st_ctime != cfgf->ctime) break;
+       }
+
+       if (cfgf == nil) return cfg;    /* Everything as it was. */
+       config_delete(cfg);             /* Otherwise delete and reread. */
+    }
+
+    errno= 0;
+    c_files= nil;
+    c_flags= flags;
+    cfg= read_file(file);
+
+    if (cfg != nil) {
+       /* Change first word to have a hidden pointer to a file list. */
+       size_t len= strlen(cfg->word);
+       firstconfig_t *fcfg;
+
+       fcfg= allocate(cfg, firstconfigsize(len));
+       memmove(&fcfg->config1, fcfg, configsize(len));
+       fcfg->filelist= c_files;
+       fcfg->new= 1;
+       return fcfg2cfg(fcfg);
+    }
+    /* Couldn't read (errno != 0) of nothing read (errno == 0). */
+    delete_filelist(c_files);
+    delete_config(cfg);
+    return nil;
+}
+
+int config_renewed(config_t *cfg)
+{
+    int new;
+
+    if (cfg == nil) {
+       new= 1;
+    } else {
+       new= cfg2fcfg(cfg)->new;
+       cfg2fcfg(cfg)->new= 0;
+    }
+    return new;
+}
+
+size_t config_length(config_t *cfg)
+/* Count the number of items on a list. */
+{
+    size_t n= 0;
+
+    while (cfg != nil) {
+       n++;
+       cfg= cfg->next;
+    }
+    return n;
+}
+
+#if TEST
+#include <unistd.h>
+
+static void print_list(int indent, config_t *cfg);
+
+static void print_words(int indent, config_t *cfg)
+{
+    while (cfg != nil) {
+       if (config_isatom(cfg)) {
+           if (config_isstring(cfg)) fputc('"', stdout);
+           printf("%s", cfg->word);
+           if (config_isstring(cfg)) fputc('"', stdout);
+       } else {
+           printf("{\n");
+           print_list(indent+4, cfg->list);
+           printf("%*s}", indent, "");
+       }
+       cfg= cfg->next;
+       if (cfg != nil) fputc(' ', stdout);
+    }
+    printf(";\n");
+}
+
+static void print_list(int indent, config_t *cfg)
+{
+    while (cfg != nil) {
+       if (!config_issub(cfg)) {
+           fprintf(stderr, "Cell at \"%s\", line %u is not a sublist\n");
+           break;
+       }
+       printf("%*s", indent, "");
+       print_words(indent, cfg->list);
+       cfg= cfg->next;
+    }
+}
+
+static void print_config(config_t *cfg)
+{
+    if (!config_renewed(cfg)) {
+       printf("# Config didn't change\n");
+    } else {
+       print_list(0, cfg);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    config_t *cfg;
+    int c;
+
+    if (argc != 2) {
+       fprintf(stderr, "One config file name please\n");
+       exit(1);
+    }
+
+    cfg= nil;
+    do {
+       cfg= config_read(argv[1], CFG_ESCAPED, cfg);
+       print_config(cfg);
+       if (!isatty(0)) break;
+       while ((c= getchar()) != EOF && c != '\n') {}
+    } while (c != EOF);
+    return 0;
+}
+#endif /* TEST */
diff --git a/lib/nbsd_libcompat_minix/crypt.c b/lib/nbsd_libcompat_minix/crypt.c
new file mode 100644 (file)
index 0000000..a82b3b0
--- /dev/null
@@ -0,0 +1,115 @@
+/*     crypt() - one-way password encryption function  Author: Kees J. Bot
+ *                                                             7 Feb 1994
+ * This routine does not encrypt anything, it uses the pwdauth
+ * program to do the hard work.
+ */
+#define nil ((void*)0)
+#define pipe _pipe
+#define fork _fork
+#define close _close
+#define dup2 _dup2
+#define execl _execl
+#define read _read
+#define _exit __exit
+#define write _write
+#define waitpid _waitpid
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/wait.h>
+
+/* Set-uid root program to read /etc/shadow or encrypt passwords. */
+static char PWDAUTH[] = "/usr/lib/pwdauth";
+#define LEN    1024
+
+static void tell(const char *s0, ...)
+{
+       va_list ap;
+       const char *s;
+
+       va_start(ap, s0);
+       s= s0;
+       while (s != nil) {
+               (void) write(2, s, strlen(s));
+               s= va_arg(ap, const char *);
+       }
+       va_end(ap);
+}
+
+char *crypt(const char *key, const char *salt)
+{
+       pid_t pid;
+       int status;
+       int pfd[2];
+       static char pwdata[LEN];
+       char *p= pwdata;
+       const char *k= key;
+       const char *s= salt;
+       int n;
+
+       /* Fill pwdata[] with the key and salt. */
+       while ((*p++ = *k++) != 0) if (p == pwdata+LEN-1) goto fail;
+       while ((*p++ = *s++) != 0) if (p == pwdata+LEN-0) goto fail;
+
+       if (pipe(pfd) < 0) goto fail;
+
+       /* Prefill the pipe. */
+       (void) write(pfd[1], pwdata, p - pwdata);
+
+       switch ((pid= fork())) {
+       case -1:
+               close(pfd[0]);
+               close(pfd[1]);
+               goto fail;
+       case 0:
+               /* Connect both input and output to the pipe. */
+               if (pfd[0] != 0) {
+                       dup2(pfd[0], 0);
+                       close(pfd[0]);
+               }
+               if (pfd[1] != 1) {
+                       dup2(pfd[1], 1);
+                       close(pfd[1]);
+               }
+
+               execl(PWDAUTH, PWDAUTH, (char *) nil);
+
+               tell("crypt(): ", PWDAUTH, ": ", strerror(errno), "\r\n",
+                                                               (char *) nil);
+               /* No pwdauth?  Fail! */
+               (void) read(0, pwdata, LEN);
+               _exit(1);
+       }
+       close(pfd[1]);
+
+       status= -1;
+       while (waitpid(pid, &status, 0) == -1 && errno == EINTR) {}
+       if (status != 0) {
+               close(pfd[0]);
+               goto fail;
+       }
+
+       /* Read and return the result.  Check if it contains exactly one
+        * string.
+        */
+       n= read(pfd[0], pwdata, LEN);
+       close(pfd[0]);
+       if (n < 0) goto fail;
+       p = pwdata + n;
+       n = 0;
+       while (p > pwdata) if (*--p == 0) n++;
+       if (n != 1) goto fail;
+       return pwdata;
+
+fail:
+       pwdata[0] = salt[0] ^ 1;                /* make result != salt */
+       pwdata[1] = 0;
+       return pwdata;
+}
+
+/*
+ * $PchId: crypt.c,v 1.5 1996/04/11 07:46:11 philip Exp $
+ */
diff --git a/lib/nbsd_libcompat_minix/cuserid.c b/lib/nbsd_libcompat_minix/cuserid.c
new file mode 100644 (file)
index 0000000..47640e7
--- /dev/null
@@ -0,0 +1,33 @@
+/*  cuserid(3)
+ *
+ *  Author: Terrence W. Holm          Sept. 1987
+ */
+
+#include <lib.h>
+#include <pwd.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#ifndef  L_cuserid
+#define  L_cuserid   9
+#endif
+
+char *cuserid(user_name)
+char *user_name;
+{
+  PRIVATE char userid[L_cuserid];
+  struct passwd *pw_entry;
+
+  if (user_name == (char *)NULL) user_name = userid;
+
+  pw_entry = getpwuid(geteuid());
+
+  if (pw_entry == (struct passwd *)NULL) {
+       *user_name = '\0';
+       return((char *)NULL);
+  }
+  strcpy(user_name, pw_entry->pw_name);
+
+  return(user_name);
+}
diff --git a/lib/nbsd_libcompat_minix/fttyslot.c b/lib/nbsd_libcompat_minix/fttyslot.c
new file mode 100644 (file)
index 0000000..8afb848
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ttyslot.c
+
+Return the index in the utmp file for the current user's terminal. The 
+current user's terminal is the first file descriptor in the range 0..2
+for which ttyname() returns a name. The index is the line number in the
+/etc/ttytab file. 0 will be returned in case of an error.
+
+Created:       Oct 11, 1992 by Philip Homburg
+*/
+
+#include <sys/types.h>
+#include <ttyent.h>
+#include <string.h>
+#include <unistd.h>
+
+int fttyslot(fd)
+int fd;
+{
+       char *tname;
+       int lineno;
+       struct ttyent *ttyp;
+
+       tname= ttyname(fd);
+       if (tname == NULL) return 0;
+
+       /* Assume that tty devices are in /dev */
+       if (strncmp(tname, "/dev/", 5) != 0)
+               return 0;       /* Malformed tty name. */
+       tname += 5;
+
+       /* Scan /etc/ttytab. */
+       lineno= 1;
+       while ((ttyp= getttyent()) != NULL)
+       {
+               if (strcmp(tname, ttyp->ty_name) == 0)
+               {
+                       endttyent();
+                       return lineno;
+               }
+               lineno++;
+       }
+       /* No match */
+       endttyent();
+       return 0;
+}
+
+/*
+ * $PchHeader: /mount/hd2/minix/lib/misc/RCS/ttyslot.c,v 1.3 1994/12/22 13:49:12 philip Exp $
+ */
diff --git a/lib/nbsd_libcompat_minix/getpwent.c b/lib/nbsd_libcompat_minix/getpwent.c
new file mode 100644 (file)
index 0000000..22598b4
--- /dev/null
@@ -0,0 +1,156 @@
+/*     getpwent(), getpwuid(), getpwnam() - password file routines
+ *
+ *                                                     Author: Kees J. Bot
+ *                                                             31 Jan 1994
+ */
+#include <sys/types.h>
+#include <compat/pwd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define arraysize(a)   (sizeof(a) / sizeof((a)[0]))
+#define arraylimit(a)  ((a) + arraysize(a))
+
+static char PASSWD[]= "/etc/passwd";   /* The password file. */
+static const char *pwfile;             /* Current password file. */
+
+static char buf[1024];                 /* Read buffer. */
+static char pwline[256];               /* One line from the password file. */
+static struct passwd entry;            /* Entry to fill and return. */
+static int pwfd= -1;                   /* Filedescriptor to the file. */
+static char *bufptr;                   /* Place in buf. */
+static ssize_t buflen= 0;              /* Remaining characters in buf. */
+static char *lineptr;                  /* Place in the line. */
+
+void endpwent(void)
+/* Close the password file. */
+{
+       if (pwfd >= 0) {
+               (void) close(pwfd);
+               pwfd= -1;
+               buflen= 0;
+       }
+}
+
+int setpwent(void)
+/* Open the password file. */
+{
+       if (pwfd >= 0) endpwent();
+
+       if (pwfile == NULL) pwfile= PASSWD;
+
+       if ((pwfd= open(pwfile, O_RDONLY)) < 0) return -1;
+       (void) fcntl(pwfd, F_SETFD, fcntl(pwfd, F_GETFD) | FD_CLOEXEC);
+       return 0;
+}
+
+void setpwfile(const char *file)
+/* Prepare for reading an alternate password file. */
+{
+       endpwent();
+       pwfile= file;
+}
+
+static int getline(void)
+/* Get one line from the password file, return 0 if bad or EOF. */
+{
+       lineptr= pwline;
+
+       do {
+               if (buflen == 0) {
+                       if ((buflen= read(pwfd, buf, sizeof(buf))) <= 0)
+                               return 0;
+                       bufptr= buf;
+               }
+
+               if (lineptr == arraylimit(pwline)) return 0;
+               buflen--;
+       } while ((*lineptr++ = *bufptr++) != '\n');
+
+       lineptr= pwline;
+       return 1;
+}
+
+static char *scan_colon(void)
+/* Scan for a field separator in a line, return the start of the field. */
+{
+       char *field= lineptr;
+       char *last;
+
+       for (;;) {
+               last= lineptr;
+               if (*lineptr == 0) return NULL;
+               if (*lineptr == '\n') break;
+               if (*lineptr++ == ':') break;
+       }
+       *last= 0;
+       return field;
+}
+
+struct passwd *getpwent(void)
+/* Read one entry from the password file. */
+{
+       char *p;
+
+       /* Open the file if not yet open. */
+       if (pwfd < 0 && setpwent() < 0) return NULL;
+
+       /* Until a good line is read. */
+       for (;;) {
+               if (!getline()) return NULL;    /* EOF or corrupt. */
+
+               if ((entry.pw_name= scan_colon()) == NULL) continue;
+               if ((entry.pw_passwd= scan_colon()) == NULL) continue;
+               if ((p= scan_colon()) == NULL) continue;
+               entry.pw_uid= strtol(p, NULL, 0);
+               if ((p= scan_colon()) == NULL) continue;
+               entry.pw_gid= strtol(p, NULL, 0);
+               if ((entry.pw_gecos= scan_colon()) == NULL) continue;
+               if ((entry.pw_dir= scan_colon()) == NULL) continue;
+               if ((entry.pw_shell= scan_colon()) == NULL) continue;
+
+               if (*lineptr == 0) return &entry;
+       }
+}
+
+struct passwd *getpwuid(uid_t uid)
+/* Return the password file entry belonging to the user-id. */
+{
+       struct passwd *pw;
+
+       endpwent();
+       while ((pw= getpwent()) != NULL && pw->pw_uid != uid) {}
+       endpwent();
+       return pw;
+}
+
+struct passwd *getpwnam(const char *name)
+/* Return the password file entry belonging to the user name. */
+{
+       struct passwd *pw;
+
+       endpwent();
+       while ((pw= getpwent()) != NULL && strcmp(pw->pw_name, name) != 0) {}
+       endpwent();
+       return pw;
+}
+
+#ifndef  L_cuserid
+#define  L_cuserid   9
+#endif
+
+char *getlogin()
+{
+  static char userid[L_cuserid];
+  struct passwd *pw_entry;
+
+  pw_entry = getpwuid(getuid());
+
+  if (pw_entry == (struct passwd *)NULL) return((char *)NULL);
+
+  strcpy(userid, pw_entry->pw_name);
+
+  return(userid);
+}
diff --git a/lib/nbsd_libcompat_minix/include/Makefile.inc b/lib/nbsd_libcompat_minix/include/Makefile.inc
new file mode 100644 (file)
index 0000000..69c0e11
--- /dev/null
@@ -0,0 +1,8 @@
+.PATH: ${MINIXSRCDIR}/lib/nbsd_libcompat_minix/include
+
+INCSDIR= /usr/netbsd/include
+
+INCS+= configfile.h termcap.h
+INCS+=         compat/a.out.h compat/regexp.h compat/syslog.h \
+       compat/pwd.h
+
diff --git a/lib/nbsd_libcompat_minix/include/compat/a.out.h b/lib/nbsd_libcompat_minix/include/compat/a.out.h
new file mode 100644 (file)
index 0000000..5e66fac
--- /dev/null
@@ -0,0 +1,118 @@
+/* The <a.out> header file describes the format of executable files. */
+
+#ifndef _AOUT_H
+#define _AOUT_H
+
+struct exec {                  /* a.out header */
+  unsigned char        a_magic[2];     /* magic number */
+  unsigned char        a_flags;        /* flags, see below */
+  unsigned char        a_cpu;          /* cpu id */
+  unsigned char        a_hdrlen;       /* length of header */
+  unsigned char        a_unused;       /* reserved for future use */
+  unsigned short a_version;    /* version stamp (not used at present) */
+  long         a_text;         /* size of text segement in bytes */
+  long         a_data;         /* size of data segment in bytes */
+  long         a_bss;          /* size of bss segment in bytes */
+  long         a_entry;        /* entry point */
+  long         a_total;        /* total memory allocated */
+  long         a_syms;         /* size of symbol table */
+
+  /* SHORT FORM ENDS HERE */
+  long         a_trsize;       /* text relocation size */
+  long         a_drsize;       /* data relocation size */
+  long         a_tbase;        /* text relocation base */
+  long         a_dbase;        /* data relocation base */
+};
+
+#define A_MAGIC0      (unsigned char) 0x01
+#define A_MAGIC1      (unsigned char) 0x03
+#define BADMAG(X)     ((X).a_magic[0] != A_MAGIC0 ||(X).a_magic[1] != A_MAGIC1)
+
+/* CPU Id of TARGET machine (byte order coded in low order two bits) */
+#define A_NONE 0x00    /* unknown */
+#define A_I8086        0x04    /* intel i8086/8088 */
+#define A_M68K 0x0B    /* motorola m68000 */
+#define A_NS16K        0x0C    /* national semiconductor 16032 */
+#define A_I80386 0x10  /* intel i80386 */
+#define A_SPARC        0x17    /* Sun SPARC */
+
+#define A_BLR(cputype) ((cputype&0x01)!=0) /* TRUE if bytes left-to-right */
+#define A_WLR(cputype) ((cputype&0x02)!=0) /* TRUE if words left-to-right */
+
+/* Flags. */
+#define A_UZP  0x01    /* unmapped zero page (pages) */
+#define A_PAL  0x02    /* page aligned executable */
+#define A_NSYM 0x04    /* new style symbol table */
+#define A_IMG   0x08   /* image instead of executable (e.g. root FS) */
+#define A_EXEC 0x10    /* executable */
+#define A_SEP  0x20    /* separate I/D */
+#define A_PURE 0x40    /* pure text */         /* not used */
+#define A_TOVLY        0x80    /* text overlay */      /* not used */
+
+/* Offsets of various things. */
+#define A_MINHDR       32
+#define        A_TEXTPOS(X)    ((long)(X).a_hdrlen)
+#define A_DATAPOS(X)   (A_TEXTPOS(X) + (X).a_text)
+#define        A_HASRELS(X)    ((X).a_hdrlen > (unsigned char) A_MINHDR)
+#define A_HASEXT(X)    ((X).a_hdrlen > (unsigned char) (A_MINHDR +  8))
+#define A_HASLNS(X)    ((X).a_hdrlen > (unsigned char) (A_MINHDR + 16))
+#define A_HASTOFF(X)   ((X).a_hdrlen > (unsigned char) (A_MINHDR + 24))
+#define A_TRELPOS(X)   (A_DATAPOS(X) + (X).a_data)
+#define A_DRELPOS(X)   (A_TRELPOS(X) + (X).a_trsize)
+#define A_SYMPOS(X)    (A_TRELPOS(X) + (A_HASRELS(X) ? \
+                       ((X).a_trsize + (X).a_drsize) : 0))
+
+struct reloc {
+  long r_vaddr;                        /* virtual address of reference */
+  unsigned short r_symndx;     /* internal segnum or extern symbol num */
+  unsigned short r_type;       /* relocation type */
+};
+
+/* r_type values: */
+#define R_ABBS         0
+#define R_RELLBYTE     2
+#define R_PCRBYTE      3
+#define R_RELWORD      4
+#define R_PCRWORD      5
+#define R_RELLONG      6
+#define R_PCRLONG      7
+#define R_REL3BYTE     8
+#define R_KBRANCHE     9
+
+/* r_symndx for internal segments */
+#define S_ABS          ((unsigned short)-1)
+#define S_TEXT         ((unsigned short)-2)
+#define S_DATA         ((unsigned short)-3)
+#define S_BSS          ((unsigned short)-4)
+
+struct nlist {                 /* symbol table entry */
+  char n_name[8];              /* symbol name */
+  long n_value;                        /* value */
+  unsigned char        n_sclass;       /* storage class */
+  unsigned char        n_numaux;       /* number of auxiliary entries (not used) */
+  unsigned short n_type;       /* language base and derived type (not used) */
+};
+
+/* Low bits of storage class (section). */
+#define        N_SECT            07    /* section mask */
+#define N_UNDF           00    /* undefined */
+#define N_ABS            01    /* absolute */
+#define N_TEXT           02    /* text */
+#define N_DATA           03    /* data */
+#define        N_BSS             04    /* bss */
+#define N_COMM           05    /* (common) */
+
+/* High bits of storage class. */
+#define N_CLASS                0370    /* storage class mask */
+#define C_NULL
+#define C_EXT          0020    /* external symbol */
+#define C_STAT         0030    /* static */
+
+/* Function prototypes. */
+#ifndef _MINIX_ANSI_H
+#include <minix/ansi.h>
+#endif
+
+_PROTOTYPE( int nlist, (char *_file, struct nlist *_nl)                        );
+
+#endif /* _AOUT_H */
diff --git a/lib/nbsd_libcompat_minix/include/compat/pwd.h b/lib/nbsd_libcompat_minix/include/compat/pwd.h
new file mode 100644 (file)
index 0000000..a534975
--- /dev/null
@@ -0,0 +1,34 @@
+/* The <pwd.h> header defines the items in the password file. */
+
+#ifndef _PWD_H
+#define _PWD_H
+
+#ifndef _TYPES_H
+#include <minix/types.h>
+#endif
+
+struct passwd {
+  char *pw_name;               /* login name */
+  uid_t pw_uid;                        /* uid corresponding to the name */
+  gid_t pw_gid;                        /* gid corresponding to the name */
+  char *pw_dir;                        /* user's home directory */
+  char *pw_shell;              /* name of the user's shell */
+
+  /* The following members are not defined by POSIX. */
+  char *pw_passwd;             /* password information */
+  char *pw_gecos;              /* just in case you have a GE 645 around */
+};
+
+/* Function Prototypes. */
+_PROTOTYPE( struct passwd *getpwnam, (const char *_name)               );
+_PROTOTYPE( struct passwd *getpwuid, (uid_t _uid)                      );
+
+#ifdef _MINIX
+_PROTOTYPE( void endpwent, (void)                                      );
+_PROTOTYPE( struct passwd *getpwent, (void)                            );
+_PROTOTYPE( int setpwent, (void)                                       );
+_PROTOTYPE( void setpwfile, (const char *_file)                                );
+_PROTOTYPE( const char *user_from_uid, (uid_t, int)                    );
+#endif
+
+#endif /* _PWD_H */
diff --git a/lib/nbsd_libcompat_minix/include/compat/regexp.h b/lib/nbsd_libcompat_minix/include/compat/regexp.h
new file mode 100644 (file)
index 0000000..06338f8
--- /dev/null
@@ -0,0 +1,37 @@
+/* The <regexp.h> header is used by the (V8-compatible) regexp(3) routines. */
+/* NOTE: Obsoleted by the POSIX regex(3) library. */
+
+#ifndef _REGEXP_H
+#define _REGEXP_H
+
+#include <sys/cdefs.h>
+
+#define CHARBITS 0377
+#define NSUBEXP  10
+typedef struct regexp {
+       const char *startp[NSUBEXP];
+       const char *endp[NSUBEXP];
+       char regstart;          /* Internal use only. */
+       char reganch;           /* Internal use only. */
+       char *regmust;          /* Internal use only. */
+       int regmlen;            /* Internal use only. */
+       char program[1];        /* Unwarranted chumminess with compiler. */
+} regexp;
+
+/* Keep these functions away from the POSIX versions. */
+#define regcomp _v8_regcomp
+#define regexec _v8_regexec
+#define regsub _v8_regsub
+#define regerror _v8_regerror
+
+/* Function Prototypes. */
+regexp *regcomp(const char *_exp);
+int regexec(regexp *_prog, const char *_string, int _bolflag);
+void regsub(regexp *_prog, char *_source, char *_dest);
+void regerror(const char *_message) ;
+
+#endif /* _REGEXP_H */
+
+/*
+ * $PchId: regexp.h,v 1.4 1996/04/10 21:43:17 philip Exp $
+ */
diff --git a/lib/nbsd_libcompat_minix/include/compat/syslog.h b/lib/nbsd_libcompat_minix/include/compat/syslog.h
new file mode 100644 (file)
index 0000000..990087a
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _COMPAT_SYSLOG_H
+#define _COMPAT_SYSLOG_H
+
+#include <syslog.h>
+#define FacNames facilitynames
+#define PriNames prioritynames
+
+#endif
diff --git a/lib/nbsd_libcompat_minix/include/configfile.h b/lib/nbsd_libcompat_minix/include/configfile.h
new file mode 100644 (file)
index 0000000..f380180
--- /dev/null
@@ -0,0 +1,44 @@
+/*     configfile.h - Generic configuration file format.
+ *                                                     Author: Kees J. Bot
+ *                                                             5 Jun 1999
+ */
+#ifndef _CONFIGFILE_H
+#define _CONFIGFILE_H
+
+/* Data can only be modified inside the library. */
+#ifndef _c
+#define _c     const
+#endif
+
+typedef _c struct config {     /* Contents of a generic configuration file. */
+_c     struct config   *next;          /* Next configuration file thing. */
+_c     struct config   *list;          /* For a { sublist }. */
+       const char      *file;          /* File and line where this is found. */
+       unsigned        line;
+       int             flags;          /* Special flags. */
+       char            word[1];        /* Payload. */
+} config_t;
+
+#define CFG_CLONG      0x0001          /* strtol(word, &end, 0) is valid. */
+#define CFG_OLONG      0x0002          /* strtol(word, &end, 010). */
+#define CFG_DLONG      0x0004          /* strtol(word, &end, 10). */
+#define CFG_XLONG      0x0008          /* strtol(word, &end, 0x10). */
+#define CFG_CULONG     0x0010          /* strtoul(word, &end, 0). */
+#define CFG_OULONG     0x0020          /* strtoul(word, &end, 010). */
+#define CFG_DULONG     0x0040          /* strtoul(word, &end, 10). */
+#define CFG_XULONG     0x0080          /* strtoul(word, &end, 0x10). */
+#define CFG_STRING     0x0100          /* The word is enclosed in quotes. */
+#define CFG_SUBLIST    0x0200          /* This is a sublist, so no word. */
+#define CFG_ESCAPED    0x0400          /* Escapes are still marked with \. */
+
+config_t *config_read(const char *_file, int flags, config_t *_cfg);
+void config_delete(config_t *_cfg);
+int config_renewed(config_t *_cfg);
+size_t config_length(config_t *_cfg);
+#define config_issub(cfg)      (!!((cfg)->flags & CFG_SUBLIST))
+#define config_isatom(cfg)     (!config_issub(cfg))
+#define config_isstring(cfg)   (!!((cfg)->flags & CFG_STRING))
+
+#undef _c
+
+#endif /* _CONFIGFILE_H */
diff --git a/lib/nbsd_libcompat_minix/include/termcap.h b/lib/nbsd_libcompat_minix/include/termcap.h
new file mode 100644 (file)
index 0000000..5dfc50f
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _TERMCAP_H
+#define _TERMCAP_H
+
+#include <minix/ansi.h>
+
+_PROTOTYPE( int tgetent, (char *_bp, char *_name)                      );
+_PROTOTYPE( int tgetflag, (char *_id)                                  );
+_PROTOTYPE( int tgetnum, (char *_id)                                   );
+_PROTOTYPE( char *tgetstr, (char *_id, char **_area)                   );
+_PROTOTYPE( char *tgoto, (char *_cm, int _destcol, int _destline)      );
+_PROTOTYPE( int tputs, (char *_cp, int _affcnt, void (*_outc)(int))    );
+
+#endif /* _TERMCAP_H */
diff --git a/lib/nbsd_libcompat_minix/mtab.c b/lib/nbsd_libcompat_minix/mtab.c
new file mode 100644 (file)
index 0000000..69b91b8
--- /dev/null
@@ -0,0 +1,205 @@
+/* This package consists of 4 routines for handling the /etc/mtab file.
+ * The /etc/mtab file contains information about the root and mounted file
+ * systems as a series of lines, each one with exactly four fields separated 
+ * by one space as follows:
+ *
+ *     special mounted_on version rw_flag
+ *
+ * where 
+ *     special is the name of the block special file
+ *     mounted_on is the directory on which it is mounted
+ *     version is either 1 or 2 for MINIX V1 and V2 file systems
+ *     rw_flag is rw or ro for read/write or read only
+ *
+ * An example /etc/mtab:
+ *
+ *     /dev/ram / 2 rw
+ *     /dev/hd1 /usr 2 rw
+ *     /dev/fd0 /user 1 ro
+ *
+ *
+ * The four routines for handling /etc/mtab are as follows.  They use two
+ * (hidden) internal buffers, mtab_in for input and mtab_out for output.
+ *
+ *     load_mtab(&prog_name)              - read /etc/mtab into mtab_in
+ *     get_mtab_entry(&s1, &s2, &s3, &s4) - arrays that are filled in
+ *     put_mtab_entry(&s1, &s2, &s3, &s4) - append a line to mtab_out
+ *     rewrite_mtab(&prog_name)           - write mtab_out to /etc/mtab
+ *
+ * If load_mtab and rewrite_mtab work, they return 0.  If they fail, they
+ * print their own error messages on stderr and return -1.  When get_mtab_entry
+ * runs out of entries to return, it sets the first pointer to NULL and returns
+ * -1 instead of 0.  Also, rewrite_mtab returns -1 if it fails.
+ */
+#include <sys/types.h>
+#include <minix/minlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#define BUF_SIZE   512           /* size of the /etc/mtab buffer */
+
+char *etc_mtab = "/etc/mtab";    /* name of the /etc/mtab file */
+static char mtab_in[BUF_SIZE+1];  /* holds /etc/mtab when it is read in */
+static char mtab_out[BUF_SIZE+1]; /* buf to build /etc/mtab for output later */
+static char *iptr = mtab_in;     /* pointer to next line to feed out. */
+static char *optr = mtab_out;    /* pointer to place where next line goes */
+
+_PROTOTYPE(int load_mtab, (char *prog_name ));
+_PROTOTYPE(int rewrite_mtab, (char *prog_name ));
+_PROTOTYPE(int get_mtab_entry, (char *special, char *mounted_on, 
+                                       char *version, char *rw_flag));
+_PROTOTYPE(int put_mtab_entry, (char *special, char *mounted_on, 
+                                       char *version, char *rw_flag));
+_PROTOTYPE(void err, (char *prog_name, char *str ));
+
+
+int load_mtab(prog_name)
+char *prog_name;
+{
+/* Read in /etc/mtab and store it in /etc/mtab. */
+
+  int fd, n;
+  char *ptr;
+
+  /* Open the file. */
+  fd = open(etc_mtab, O_RDONLY);
+  if (fd < 0) {
+       err(prog_name, ": cannot open ");
+       return(-1);
+  }
+
+  /* File opened.  Read it in. */
+  n = read(fd, mtab_in, BUF_SIZE);
+  if (n <= 0) {
+       /* Read failed. */
+       err(prog_name, ": cannot read ");
+       return(-1);
+  }
+  if (n == BUF_SIZE) {
+       /* Some nut has mounted 50 file systems or something like that. */
+       std_err(prog_name);
+       std_err(": file too large: ");
+       std_err(etc_mtab);
+       return(-1);
+  }
+
+  close(fd);
+
+  /* Replace all the whitespace by '\0'. */
+  ptr = mtab_in;
+  while (*ptr != '\0') {
+       if (isspace(*ptr)) *ptr = '\0';
+       ptr++;
+  }
+  return(0);
+}
+
+
+int rewrite_mtab(prog_name)
+char *prog_name;
+{
+/* Write mtab_out to /etc/mtab. */
+
+  int fd, n;
+
+  /* Do a creat to truncate the file. */
+  fd = creat(etc_mtab, 0777);
+  if (fd < 0) {
+       err(prog_name, ": cannot overwrite ");
+       return(-1);
+  }
+
+  /* File created.  Write it. */
+  n = write(fd, mtab_out, (unsigned int)(optr - mtab_out));
+  if (n <= 0) {
+       /* Write failed. */
+       err(prog_name, " could not write ");
+       return(-1);
+  }
+
+  close(fd);
+  return(0);
+}
+
+
+int get_mtab_entry(special, mounted_on, version, rw_flag)
+char *special;
+char *mounted_on;
+char *version;
+char *rw_flag;
+{
+/* Return the next entry from mtab_in. */
+
+  if (iptr >= &mtab_in[BUF_SIZE]) {
+       special[0] = '\0';
+       return(-1);
+  }
+
+  strcpy(special, iptr);
+  while (isprint(*iptr)) iptr++;
+  while (*iptr == '\0'&& iptr < &mtab_in[BUF_SIZE]) iptr++;
+
+  strcpy(mounted_on, iptr);
+  while (isprint(*iptr)) iptr++;
+  while (*iptr == '\0'&& iptr < &mtab_in[BUF_SIZE]) iptr++;
+
+  strcpy(version, iptr);
+  while (isprint(*iptr)) iptr++;
+  while (*iptr == '\0'&& iptr < &mtab_in[BUF_SIZE]) iptr++;
+
+  strcpy(rw_flag, iptr);
+  while (isprint(*iptr)) iptr++;
+  while (*iptr == '\0'&& iptr < &mtab_in[BUF_SIZE]) iptr++;
+  return(0);
+}
+
+
+int put_mtab_entry(special, mounted_on, version, rw_flag)
+char *special;
+char *mounted_on;
+char *version;
+char *rw_flag;
+{
+/* Append an entry to the mtab_out buffer. */
+
+  int n1, n2, n3, n4;
+
+  n1 = strlen(special);
+  n2 = strlen(mounted_on);
+  n3 = strlen(version);
+  n4 = strlen(rw_flag);
+
+  if (optr + n1 + n2 + n3 + n4 + 5 >= &mtab_out[BUF_SIZE]) return(-1);
+  strcpy(optr, special);
+  optr += n1;
+  *optr++ = ' ';
+
+  strcpy(optr, mounted_on);
+  optr += n2;
+  *optr++ = ' ';
+
+  strcpy(optr, version);
+  optr += n3;
+  *optr++ = ' ';
+
+  strcpy(optr, rw_flag);
+  optr += n4;
+  *optr++ = '\n';
+  return(0);
+}
+
+
+void
+err(prog_name, str)
+char *prog_name, *str;
+{
+  std_err(prog_name); 
+  std_err(str);
+  std_err(etc_mtab);
+  perror(" ");
+}
diff --git a/lib/nbsd_libcompat_minix/nlist.c b/lib/nbsd_libcompat_minix/nlist.c
new file mode 100644 (file)
index 0000000..4901032
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * "nlist.c", Peter Valkenburg, january 1989.
+ */
+#include <lib.h>
+#include <string.h>
+#include <a.out.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#define fail(fp)       (fclose(fp), -1)        /* ret. exp. when nlist fails */
+
+_PROTOTYPE( int nlist, (char *file, struct nlist nl[]));
+
+/*
+ * Nlist fills fields n_sclass and n_value of array nl with values found in
+ * non-stripped executable file.  Entries that are not found have their
+ * n_value/n_sclass fields set to 0.  Nl ends with a 0 or nul string n_name.
+ * The return value is -1 on failure, else the number of entries not found.
+ */
+int nlist(file, nl)
+char *file;
+struct nlist nl[];
+{
+       int nents, nsrch, nfound, i;
+       struct nlist nlent;
+       FILE *fp;
+       struct exec hd;
+
+       /* open executable with namelist */
+       if ((fp = fopen(file, "r")) == NULL)
+               return -1;
+               
+       /* get header and seek to start of namelist */  
+       if (fread((char *) &hd, sizeof(struct exec), 1, fp) != 1 ||
+           BADMAG(hd) || fseek(fp, A_SYMPOS(hd), SEEK_SET) != 0)
+               return fail(fp);
+       
+       /* determine number of entries searched for & reset fields */
+       nsrch = 0;
+       while (nl[nsrch].n_name != NULL && *(nl[nsrch].n_name) != '\0') {
+               nl[nsrch].n_sclass = 0;
+               nl[nsrch].n_value = 0;
+               nl[nsrch].n_type = 0;           /* for compatability */
+               nsrch++;
+       }
+
+       /* loop through namelist & fill in user array */
+       nfound = 0;
+       for (nents = (hd.a_syms & 0xFFFF) / sizeof(struct nlist);
+            nents > 0; nents--) {
+               if (nsrch == nfound)
+                       break;                  /* no need to look further */
+               if (fread((char *) &nlent, sizeof(struct nlist), 1, fp) != 1)
+                       return fail(fp);          
+               for (i = 0; i < nsrch; i++)
+                       if (nl[i].n_sclass == 0 &&
+                           strncmp(nl[i].n_name, nlent.n_name,
+                                   sizeof(nlent.n_name)) == 0) {
+                               nl[i] = nlent;
+                               nfound++;
+                               break;
+                       }
+       }
+
+       (void) fclose(fp);
+       
+       return nsrch - nfound;
+}
diff --git a/lib/nbsd_libcompat_minix/stderr.c b/lib/nbsd_libcompat_minix/stderr.c
new file mode 100644 (file)
index 0000000..fd7f65b
--- /dev/null
@@ -0,0 +1,11 @@
+#include <lib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+void std_err(const char *s)
+{
+  register const char *p = s;
+
+  while (*p != 0) p++;
+  write(2, s, (int) (p - s));
+}
diff --git a/lib/nbsd_libcompat_minix/termcap.c b/lib/nbsd_libcompat_minix/termcap.c
new file mode 100644 (file)
index 0000000..2f08af2
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ *     termcap.c       V1.1    20/7/87         agc     Joypace Ltd
+ *
+ *     Copyright Joypace Ltd, London, UK, 1987. All rights reserved.
+ *     This file may be freely distributed provided that this notice
+ *     remains attached.
+ *
+ *     A public domain implementation of the termcap(3) routines.
+ *
+ *
+ *
+ *      Klamer Schutte       V1.2    Nov. 1988
+ *
+ *   - Can match multiple terminal names                [tgetent]
+ *   - Removal of **area assignments                    [tgetstr]
+ *
+ *      Terrence W. Holm     V1.3    May, Sep, Oct.  1988
+ *
+ *   - Correct when TERM != name and TERMCAP is defined         [tgetent]
+ *   - Correct the comparison for the terminal name     [tgetent]
+ *   - Correct the value of ^x escapes                  [tgetstr]
+ *   - Added %r to reverse row/column                   [tgoto]
+ *   - Fixed end of definition test                     [tgetnum/flag/str]
+ *
+ *      Terrence W. Holm     V1.4    Jan. 1989
+ *
+ *   - Incorporated Klamer's V1.2 fixes into V1.3
+ *   - Added %d, (old %d is now %2)                     [tgoto]
+ *   - Allow '#' comments in definition file            [tgetent]
+ */
+
+#include <sys/cdefs.h>
+#include <lib.h>
+#include <termcap.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+char *capab = (char *)NULL;            /* the capability itself */
+
+#if 0
+/*  The following are not yet used.  */
+extern short ospeed;           /* output speed */
+extern char PC;                        /* padding character */
+extern char *BC;               /* back cursor movement */
+extern char *UP;               /* up cursor movement */
+#endif
+
+/*
+ *     tgetent - get the termcap entry for terminal name, and put it
+ *     in bp (which must be an array of 1024 chars). Returns 1 if
+ *     termcap entry found, 0 if not found, and -1 if file not found.
+ */
+
+int tgetent(bp, name)
+char *bp;
+char *name;
+{
+  FILE *fp;
+  char *file;
+  char *term;
+  short len = strlen(name);
+
+  capab = bp;
+
+  /* If TERMCAP begins with a '/' then use TERMCAP as the path  */
+  /* Name of the termcap definitions file. If TERMCAP is a      */
+  /* Definition and TERM equals "name" then use TERMCAP as the  */
+  /* Definition. Otherwise use "/etc/termcap" as the path name.         */
+
+  if ((file = getenv("TERMCAP")) == (char *)NULL)
+       file = "/etc/termcap";
+  else if (*file != '/')
+       if ((term = getenv("TERM")) != (char *)NULL && strcmp(term, name) == 0) {
+               *bp = '\0';
+               strncat(bp, file, 1023);
+               return(1);
+       } else
+               file = "/etc/termcap";
+
+  if ((fp = fopen(file, "r")) == (FILE *) NULL) {
+       capab = (char *)NULL;           /* no valid termcap  */
+       return(-1);
+  }
+  for (;;) {
+       /* Read in each definition */
+       int def_len = 0;
+       char *cp = bp;
+
+       do {
+               if (fgets(&bp[def_len], (unsigned int)(1024 - def_len), fp) == (char *)NULL) {
+                       fclose(fp);
+                       capab = (char *)NULL;   /* no valid termcap */
+                       return(0);
+               }
+               def_len = strlen(bp) - 2;
+       } while (bp[def_len] == '\\');
+
+       while (isspace(*cp)) cp++;
+
+       /* Comment lines start with a '#'  */
+       if (*cp == '#') continue;
+
+       /* See if any of the terminal names in this definition */
+       /* Match "name".                                                 */
+
+       do {
+               if (strncmp(name, cp, len) == 0 &&
+                   (cp[len] == '|' || cp[len] == ':')) {
+                       fclose(fp);
+                       return(1);
+               }
+               while ((*cp) && (*cp != '|') && (*cp != ':')) cp++;
+       } while (*cp++ == '|');
+  }
+}
+
+
+/*
+ *     tgetnum - get the numeric terminal capability corresponding
+ *     to id. Returns the value, -1 if invalid.
+ */
+
+int tgetnum(id)
+char *id;
+{
+  register char *cp = capab;
+
+  if (cp == (char *)NULL || id == (char *)NULL) return(-1);
+
+  for (;;) {
+       while (*cp++ != ':')
+               if (cp[-1] == '\0') return(-1);
+
+       while (isspace(*cp)) cp++;
+
+       if (strncmp(cp, id, 2) == 0 && cp[2] == '#') return(atoi(cp + 3));
+  }
+}
+
+
+/*
+ *     tgetflag - get the boolean flag corresponding to id. Returns -1
+ *     if invalid, 0 if the flag is not in termcap entry, or 1 if it is
+ *     present.
+ */
+
+int tgetflag(id)
+char *id;
+{
+  register char *cp = capab;
+
+  if (cp == (char *)NULL || id == (char *)NULL) return(-1);
+
+  for (;;) {
+       while (*cp++ != ':')
+               if (cp[-1] == '\0') return(0);
+
+       while (isspace(*cp)) cp++;
+
+       if (strncmp(cp, id, 2) == 0) return(1);
+  }
+}
+
+
+/*
+ *     tgetstr - get the string capability corresponding to id and place
+ *     it in area (advancing area at same time). Expand escape sequences
+ *     etc. Returns the string, or NULL if it can't do it.
+ */
+
+char *tgetstr(id, area)
+char *id;
+char **area;
+{
+  register char *cp = capab;
+  register char *wsp = *area;  /* workspace pointer  */
+
+  if (cp == (char *)NULL || id == (char *)NULL) return((char *)NULL);
+
+  for (;;) {
+       while (*cp++ != ':')
+               if (cp[-1] == '\0') return((char *)NULL);
+
+       while (isspace(*cp)) cp++;
+
+       if (strncmp(cp, id, 2) == 0 && cp[2] == '=') {
+               for (cp += 3; *cp && *cp != ':'; wsp++, cp++) switch (*cp) {
+                           case '^':
+                               *wsp = *++cp - '@';
+                               break;
+
+                           case '\\':
+                               switch (*++cp) {
+                                   case 'E':
+                                       *wsp = '\033';
+                                       break;
+                                   case 'n':
+                                       *wsp = '\n';
+                                       break;
+                                   case 'r':
+                                       *wsp = '\r';
+                                       break;
+                                   case 't':
+                                       *wsp = '\t';
+                                       break;
+                                   case 'b':
+                                       *wsp = '\b';
+                                       break;
+                                   case 'f':
+                                       *wsp = '\f';
+                                       break;
+                                   case '0':
+                                   case '1':
+                                   case '2':
+                                   case '3':
+                                       {
+                                               int i;
+                                               int t = 0;
+                                               for (i = 0; i < 3 &&
+                                                    isdigit(*cp); ++i, ++cp)
+                                                       t = t * 8 + *cp - '0';
+                                               *wsp = t;
+                                               cp--;
+                                               break;
+                                       }
+                                   default:
+                                       *wsp = *cp;
+                               }
+                               break;
+
+                           default:    *wsp = *cp;
+                       }
+
+               *wsp++ = '\0';
+
+               {
+                       char *ret = *area;
+                       *area = wsp;
+                       return(ret);
+               }
+       }
+  }                            /* end for(;;) */
+}
+
+
+
+/*
+ *     tgoto - given the cursor motion string cm, make up the string
+ *     for the cursor to go to (destcol, destline), and return the string.
+ *     Returns "OOPS" if something's gone wrong, or the string otherwise.
+ */
+
+char *tgoto(cm, destcol, destline)
+char *cm;
+int destcol;
+int destline;
+{
+  PRIVATE char ret[24];
+  char *rp = ret;
+  int incr = 0;
+  int argno = 0;
+  int numval;
+
+  for (; *cm; cm++) {
+       if (*cm == '%') {
+               switch (*++cm) {
+                   case 'i':   incr = 1;                               break;
+
+                   case 'r':   argno = 1;                              break;
+
+                   case '+':
+                       numval = (argno == 0 ? destline : destcol);
+                       *rp++ = numval + incr + *++cm;
+                       argno = 1 - argno;
+                       break;
+
+                   case '2':
+                       numval = (argno == 0 ? destline : destcol);
+                       numval = (numval + incr) % 100;
+                       *rp++ = '0' + (numval / 10);
+                       *rp++ = '0' + (numval % 10);
+                       argno = 1 - argno;
+                       break;
+
+                   case 'd':
+                       numval = (argno == 0 ? destline : destcol);
+                       numval = (numval + incr) % 1000;
+                       if (numval > 99) *rp++ = '0' + (numval / 100);
+                       if (numval > 9) *rp++ = '0' + (numval / 10) % 10;
+                       *rp++ = '0' + (numval % 10);
+                       argno = 1 - argno;
+                       break;
+
+                   case '%':   *rp++ = '%';                            break;
+
+                   default:    return("OOPS");
+               }
+
+       } else
+               *rp++ = *cm;
+  }
+
+  *rp = '\0';
+  return(ret);
+}
+
+
+
+/*
+ *     tputs - put the string cp out onto the terminal, using the function
+ *     outc. This should do padding for the terminal, but I can't find a
+ *     terminal that needs padding at the moment...
+ */
+
+int tputs(cp, affcnt, outc)
+register char *cp;
+int affcnt;
+_PROTOTYPE( void (*outc), (int ch));
+{
+  if (cp == (char *)NULL) return(1);
+  /* Do any padding interpretation - left null for MINIX just now */
+  while (*cp) (*outc) (*cp++);
+  return(1);
+}
diff --git a/lib/nbsd_libcompat_minix/v8regerror.c b/lib/nbsd_libcompat_minix/v8regerror.c
new file mode 100644 (file)
index 0000000..6f4e11f
--- /dev/null
@@ -0,0 +1,15 @@
+/*     regerror() - Default regexp error report        Author: Kees J. Bot
+ *                                                             12 Jun 1999
+ *
+ * A better version of this routine should be supplied by the user in
+ * the program using regexps.
+ */
+#include <stdio.h>
+#define const          /* avoid "const poisoning" */
+#include <compat/regexp.h>
+#undef const
+
+void regerror(char *message)
+{
+       fprintf(stderr, "regexp error: %s\n", message);
+}
diff --git a/lib/nbsd_libcompat_minix/v8regexp.c b/lib/nbsd_libcompat_minix/v8regexp.c
new file mode 100644 (file)
index 0000000..1bed2b5
--- /dev/null
@@ -0,0 +1,1075 @@
+/* regcomp and regexec -- regsub and regerror are elsewhere
+ *
+ *     Copyright (c) 1986 by University of Toronto.
+ *     Written by Henry Spencer.  Not derived from licensed software.
+ *
+ *     Permission is granted to anyone to use this software for any
+ *     purpose on any computer system, and to redistribute it freely,
+ *     subject to the following restrictions:
+ *
+ *     1. The author is not responsible for the consequences of use of
+ *             this software, no matter how awful, even if they arise
+ *             from defects in it.
+ *
+ *     2. The origin of this software must not be misrepresented, either
+ *             by explicit claim or by omission.
+ *
+ *     3. Altered versions must be plainly marked as such, and must not
+ *             be misrepresented as being the original software.
+ *
+ * Beware that some of this code is subtly aware of the way operator
+ * precedence is structured in regular expressions.  Serious changes in
+ * regular-expression syntax might require a total rethink.
+ *
+ *     The third parameter to regexec was added by Martin C. Atkins.
+ *     Andy Tanenbaum also made some changes.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#define const          /* avoid "const poisoning" */
+#include <compat/regexp.h>
+#undef const
+
+/* The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define        MAGIC   0234
+
+/* The "internal use only" fields in regexp.h are present to pass info from
+ * compile to execute that permits the execute phase to run lots faster on
+ * simple cases.  They are:
+ *
+ * regstart    char that must begin a match; '\0' if none obvious
+ * reganch     is the match anchored (at beginning-of-line only)?
+ * regmust     string (pointer into program) that match must include, or NULL
+ * regmlen     length of regmust string
+ *
+ * Regstart and reganch permit very fast decisions on suitable starting points
+ * for a match, cutting down the work a lot.  Regmust permits fast rejection
+ * of lines that cannot possibly match.  The regmust tests are costly enough
+ * that regcomp() supplies a regmust only if the r.e. contains something
+ * potentially expensive (at present, the only such thing detected is * or +
+ * at the start of the r.e., which can involve a lot of backup).  Regmlen is
+ * supplied because the test in regexec() needs it and regcomp() is computing
+ * it anyway.
+ */
+
+/* Structure for regexp "program".  This is essentially a linear encoding
+ * of a nondeterministic finite-state machine (aka syntax charts or
+ * "railroad normal form" in parsing technology).  Each node is an opcode
+ * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
+ * all nodes except BRANCH implement concatenation; a "next" pointer with
+ * a BRANCH on both ends of it is connecting two alternatives.  (Here we
+ * have one of the subtle syntax dependencies:  an individual BRANCH (as
+ * opposed to a collection of them) is never concatenated with anything
+ * because of operator precedence.)  The operand of some types of node is
+ * a literal string; for others, it is a node leading into a sub-FSM.  In
+ * particular, the operand of a BRANCH node is the first node of the branch.
+ * (NB this is *not* a tree structure:  the tail of the branch connects
+ * to the thing following the set of BRANCHes.)  The opcodes are:
+ */
+
+/* Definition  number  opnd?   meaning */
+#define        END     0               /* no   End of program. */
+#define        BOL     1               /* no   Match "" at beginning of line. */
+#define        EOL     2               /* no   Match "" at end of line. */
+#define        ANY     3               /* no   Match any one character. */
+#define        ANYOF   4               /* str  Match any character in this string. */
+#define        ANYBUT  5               /* str  Match any character not in this
+                        * string. */
+#define        BRANCH  6               /* node Match this alternative, or the
+                        * next... */
+#define        BACK    7               /* no   Match "", "next" ptr points backward. */
+#define        EXACTLY 8               /* str  Match this string. */
+#define        NOTHING 9               /* no   Match empty string. */
+#define        STAR    10              /* node Match this (simple) thing 0 or more
+                        * times. */
+#define        PLUS    11              /* node Match this (simple) thing 1 or more
+                        * times. */
+#define        OPEN    20              /* no   Mark this point in input as start of
+                        * #n. */
+ /* OPEN+1 is number 1, etc. */
+#define        CLOSE   30              /* no   Analogous to OPEN. */
+
+/* Opcode notes:
+ *
+ * BRANCH      The set of branches constituting a single choice are hooked
+ *             together with their "next" pointers, since precedence prevents
+ *             anything being concatenated to any individual branch.  The
+ *             "next" pointer of the last BRANCH in a choice points to the
+ *             thing following the whole choice.  This is also where the
+ *             final "next" pointer of each individual branch points; each
+ *             branch starts with the operand node of a BRANCH node.
+ *
+ * BACK                Normal "next" pointers all implicitly point forward; BACK
+ *             exists to make loop structures possible.
+ *
+ * STAR,PLUS   '?', and complex '*' and '+', are implemented as circular
+ *             BRANCH structures using BACK.  Simple cases (one character
+ *             per match) are implemented with STAR and PLUS for speed
+ *             and to minimize recursive plunges.
+ *
+ * OPEN,CLOSE  ...are numbered at compile time.
+ */
+
+/* A node is one char of opcode followed by two chars of "next" pointer.
+ * "Next" pointers are stored as two 8-bit pieces, high order first.  The
+ * value is a positive offset from the opcode of the node containing it.
+ * An operand, if any, simply follows the node.  (Note that much of the
+ * code generation knows about this implicit relationship.)
+ *
+ * Using two bytes for the "next" pointer is vast overkill for most things,
+ * but allows patterns to get big without disasters.
+ */
+#define        OP(p)   (*(p))
+#define        NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+#define        OPERAND(p)      ((p) + 3)
+
+/* Utility definitions.
+ */
+#ifndef CHARBITS
+#define        UCHARAT(p)      ((int)*(unsigned char *)(p))
+#else
+#define        UCHARAT(p)      ((int)*(p)&CHARBITS)
+#endif
+
+#define        CFAIL(m)        { regerror(m); return((char *)NULL); }
+#define        RFAIL(m)        { regerror(m); return((regexp *)NULL); }
+#define        ISMULT(c)       ((c) == '*' || (c) == '+' || (c) == '?')
+#define        META    "^$.[()|?+*\\"
+
+/* Flags to be passed up and down.
+ */
+#define        HASWIDTH        01      /* Known never to match null string. */
+#define        SIMPLE          02      /* Simple enough to be STAR/PLUS operand. */
+#define        SPSTART         04      /* Starts with * or +. */
+#define        WORST           0       /* Worst case. */
+
+/* Global work variables for regcomp().
+ */
+static char *regparse;         /* Input-scan pointer. */
+static int regnpar;            /* () count. */
+static char regdummy;
+static char *regcode;          /* Code-emit pointer; &regdummy = don't. */
+static long regsize;           /* Code size. */
+
+/* Forward declarations for regcomp()'s friends.
+ */
+static char *reg(int paren, int *flagp);
+static char *regbranch(int *flagp);
+static char *regpiece(int *flagp);
+static char *regatom(int *flagp);
+static char *regnode(int op);
+static char *regnext(char *p);
+static void regc(int b);
+static void reginsert(int op, char *opnd);
+static void regtail(char *p, char *val);
+static void regoptail(char *p, char *val);
+
+/*
+ - regcomp - compile a regular expression into internal code
+ *
+ * We can't allocate space until we know how big the compiled form will be,
+ * but we can't compile it (and thus know how big it is) until we've got a
+ * place to put the code.  So we cheat:  we compile it twice, once with code
+ * generation turned off and size counting turned on, and once "for real".
+ * This also means that we don't allocate space until we are sure that the
+ * thing really will compile successfully, and we never have to move the
+ * code and thus invalidate pointers into it.  (Note that it has to be in
+ * one piece because free() must be able to free it all.)
+ *
+ * Beware that the optimization-preparation code in here knows about some
+ * of the structure of the compiled regexp.
+ */
+regexp *regcomp(exp)
+char *exp;
+{
+  register regexp *r;
+  register char *scan;
+  register char *longest;
+  register int len;
+  int flags;
+
+  if (exp == (char *)NULL) RFAIL("NULL argument");
+
+  /* First pass: determine size, legality. */
+  regparse = exp;
+  regnpar = 1;
+  regsize = 0L;
+  regcode = &regdummy;
+  regc(MAGIC);
+  if (reg(0, &flags) == (char *)NULL) return((regexp *)NULL);
+
+  /* Small enough for pointer-storage convention? */
+  if (regsize >= 32767L)       /* Probably could be 65535L. */
+       RFAIL("regexp too big");
+
+  /* Allocate space. */
+  r = (regexp *) malloc(sizeof(regexp) + (unsigned) regsize);
+  if (r == (regexp *)NULL) RFAIL("out of space");
+
+  /* Second pass: emit code. */
+  regparse = exp;
+  regnpar = 1;
+  regcode = r->program;
+  regc(MAGIC);
+  if (reg(0, &flags) == (char *)NULL) return((regexp *)NULL);
+
+  /* Dig out information for optimizations. */
+  r->regstart = '\0';          /* Worst-case defaults. */
+  r->reganch = 0;
+  r->regmust = (char *)NULL;
+  r->regmlen = 0;
+  scan = r->program + 1;       /* First BRANCH. */
+  if (OP(regnext(scan)) == END) {      /* Only one top-level choice. */
+       scan = OPERAND(scan);
+
+       /* Starting-point info. */
+       if (OP(scan) == EXACTLY)
+               r->regstart = *OPERAND(scan);
+       else if (OP(scan) == BOL)
+               r->reganch++;
+
+       /* If there's something expensive in the r.e., find the
+        * longest literal string that must appear and make it the
+        * regmust.  Resolve ties in favor of later strings, since
+        * the regstart check works with the beginning of the r.e.
+        * and avoiding duplication strengthens checking.  Not a
+        * strong reason, but sufficient in the absence of others. */
+       if (flags & SPSTART) {
+               longest = (char *)NULL;
+               len = 0;
+               for (; scan != (char *)NULL; scan = regnext(scan))
+                       if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
+                               longest = OPERAND(scan);
+                               len = strlen(OPERAND(scan));
+                       }
+               r->regmust = longest;
+               r->regmlen = len;
+       }
+  }
+  return(r);
+}
+
+/*
+ - reg - regular expression, i.e. main body or parenthesized thing
+ *
+ * Caller must absorb opening parenthesis.
+ *
+ * Combining parenthesis handling with the base level of regular expression
+ * is a trifle forced, but the need to tie the tails of the branches to what
+ * follows makes it hard to avoid.
+ */
+static char *reg(paren, flagp)
+int paren;                     /* Parenthesized? */
+int *flagp;
+{
+  register char *ret;
+  register char *br;
+  register char *ender;
+  register int parno;
+  int flags;
+
+  *flagp = HASWIDTH;           /* Tentatively. */
+
+  /* Make an OPEN node, if parenthesized. */
+  if (paren) {
+       if (regnpar >= NSUBEXP) CFAIL("too many ()");
+       parno = regnpar;
+       regnpar++;
+       ret = regnode(OPEN + parno);
+  } else {
+       parno = 0;              /* not actually used, keep compiler quiet */
+       ret = (char *)NULL;
+  }
+
+  /* Pick up the branches, linking them together. */
+  br = regbranch(&flags);
+  if (br == (char *)NULL) return((char *)NULL);
+  if (ret != (char *)NULL)
+       regtail(ret, br);       /* OPEN -> first. */
+  else
+       ret = br;
+  if (!(flags & HASWIDTH)) *flagp &= ~HASWIDTH;
+  *flagp |= flags & SPSTART;
+  while (*regparse == '|') {
+       regparse++;
+       br = regbranch(&flags);
+       if (br == (char *)NULL) return((char *)NULL);
+       regtail(ret, br);       /* BRANCH -> BRANCH. */
+       if (!(flags & HASWIDTH)) *flagp &= ~HASWIDTH;
+       *flagp |= flags & SPSTART;
+  }
+
+  /* Make a closing node, and hook it on the end. */
+  ender = regnode((paren) ? CLOSE + parno : END);
+  regtail(ret, ender);
+
+  /* Hook the tails of the branches to the closing node. */
+  for (br = ret; br != (char *)NULL; br = regnext(br)) regoptail(br, ender);
+
+  /* Check for proper termination. */
+  if (paren && *regparse++ != ')') {
+       CFAIL("unmatched ()");
+  } else if (!paren && *regparse != '\0') {
+       if (*regparse == ')') {
+               CFAIL("unmatched ()");
+       } else
+               CFAIL("junk on end");   /* "Can't happen". */
+       /* NOTREACHED */
+  }
+  return(ret);
+}
+
+/*
+ - regbranch - one alternative of an | operator
+ *
+ * Implements the concatenation operator.
+ */
+static char *regbranch(flagp)
+int *flagp;
+{
+  register char *ret;
+  register char *chain;
+  register char *latest;
+  int flags;
+
+  *flagp = WORST;              /* Tentatively. */
+
+  ret = regnode(BRANCH);
+  chain = (char *)NULL;
+  while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
+       latest = regpiece(&flags);
+       if (latest == (char *)NULL) return((char *)NULL);
+       *flagp |= flags & HASWIDTH;
+       if (chain == (char *)NULL)      /* First piece. */
+               *flagp |= flags & SPSTART;
+       else
+               regtail(chain, latest);
+       chain = latest;
+  }
+  if (chain == (char *)NULL)           /* Loop ran zero times. */
+       regnode(NOTHING);
+
+  return(ret);
+}
+
+/*
+ - regpiece - something followed by possible [*+?]
+ *
+ * Note that the branching code sequences used for ? and the general cases
+ * of * and + are somewhat optimized:  they use the same NOTHING node as
+ * both the endmarker for their branch list and the body of the last branch.
+ * It might seem that this node could be dispensed with entirely, but the
+ * endmarker role is not redundant.
+ */
+static char *regpiece(flagp)
+int *flagp;
+{
+  register char *ret;
+  register char op;
+  register char *next;
+  int flags;
+
+  ret = regatom(&flags);
+  if (ret == (char *)NULL) return((char *)NULL);
+
+  op = *regparse;
+  if (!ISMULT(op)) {
+       *flagp = flags;
+       return(ret);
+  }
+  if (!(flags & HASWIDTH) && op != '?') CFAIL("*+ operand could be empty");
+  *flagp = (op != '+') ? (WORST | SPSTART) : (WORST | HASWIDTH);
+
+  if (op == '*' && (flags & SIMPLE))
+       reginsert(STAR, ret);
+  else if (op == '*') {
+       /* Emit x* as (x&|), where & means "self". */
+       reginsert(BRANCH, ret); /* Either x */
+       regoptail(ret, regnode(BACK));  /* and loop */
+       regoptail(ret, ret);    /* back */
+       regtail(ret, regnode(BRANCH));  /* or */
+       regtail(ret, regnode(NOTHING)); /* null. */
+  } else if (op == '+' && (flags & SIMPLE))
+       reginsert(PLUS, ret);
+  else if (op == '+') {
+       /* Emit x+ as x(&|), where & means "self". */
+       next = regnode(BRANCH); /* Either */
+       regtail(ret, next);
+       regtail(regnode(BACK), ret);    /* loop back */
+       regtail(next, regnode(BRANCH)); /* or */
+       regtail(ret, regnode(NOTHING)); /* null. */
+  } else if (op == '?') {
+       /* Emit x? as (x|) */
+       reginsert(BRANCH, ret); /* Either x */
+       regtail(ret, regnode(BRANCH));  /* or */
+       next = regnode(NOTHING);/* null. */
+       regtail(ret, next);
+       regoptail(ret, next);
+  }
+  regparse++;
+  if (ISMULT(*regparse)) CFAIL("nested *?+");
+
+  return(ret);
+}
+
+/*
+ - regatom - the lowest level
+ *
+ * Optimization:  gobbles an entire sequence of ordinary characters so that
+ * it can turn them into a single node, which is smaller to store and
+ * faster to run.  Backslashed characters are exceptions, each becoming a
+ * separate node; the code is simpler that way and it's not worth fixing.
+ */
+static char *regatom(flagp)
+int *flagp;
+{
+  register char *ret;
+  int flags;
+
+  *flagp = WORST;              /* Tentatively. */
+
+  switch (*regparse++) {
+      case '^':        ret = regnode(BOL);             break;
+      case '$':        ret = regnode(EOL);             break;
+      case '.':
+       ret = regnode(ANY);
+       *flagp |= HASWIDTH | SIMPLE;
+       break;
+      case '[':{
+               register int class;
+               register int classend;
+
+               if (*regparse == '^') { /* Complement of range. */
+                       ret = regnode(ANYBUT);
+                       regparse++;
+               } else
+                       ret = regnode(ANYOF);
+               if (*regparse == ']' || *regparse == '-') regc(*regparse++);
+               while (*regparse != '\0' && *regparse != ']') {
+                       if (*regparse == '-') {
+                               regparse++;
+                               if (*regparse == ']' || *regparse == '\0')
+                                       regc('-');
+                               else {
+                                       class = UCHARAT(regparse - 2) + 1;
+                                       classend = UCHARAT(regparse);
+                                       if (class > classend + 1)
+                                               CFAIL("invalid [] range");
+                                       for (; class <= classend; class++)
+                                               regc(class);
+                                       regparse++;
+                               }
+                       } else
+                               regc(*regparse++);
+               }
+               regc('\0');
+               if (*regparse != ']') CFAIL("unmatched []");
+               regparse++;
+               *flagp |= HASWIDTH | SIMPLE;
+       }
+       break;
+      case '(':
+       ret = reg(1, &flags);
+       if (ret == (char *)NULL) return((char *)NULL);
+       *flagp |= flags & (HASWIDTH | SPSTART);
+       break;
+      case '\0':
+      case '|':
+      case ')':
+       CFAIL("internal urp");  /* Supposed to be caught earlier. */
+       break;
+      case '?':
+      case '+':
+      case '*':        CFAIL("?+* follows nothing");           break;
+      case '\\':
+       if (*regparse == '\0') CFAIL("trailing \\");
+       ret = regnode(EXACTLY);
+       regc(*regparse++);
+       regc('\0');
+       *flagp |= HASWIDTH | SIMPLE;
+       break;
+      default:{
+               register int len;
+               register char ender;
+
+               regparse--;
+               len = strcspn(regparse, META);
+               if (len <= 0) CFAIL("internal disaster");
+               ender = *(regparse + len);
+               if (len > 1 && ISMULT(ender))
+                       len--;  /* Back off clear of ?+* operand. */
+               *flagp |= HASWIDTH;
+               if (len == 1) *flagp |= SIMPLE;
+               ret = regnode(EXACTLY);
+               while (len > 0) {
+                       regc(*regparse++);
+                       len--;
+               }
+               regc('\0');
+       }
+       break;
+  }
+
+  return(ret);
+}
+
+/*
+ - regnode - emit a node
+ */
+static char *regnode(op)
+char op;
+{
+  register char *ret;
+  register char *ptr;
+
+  ret = regcode;
+  if (ret == &regdummy) {
+       regsize += 3;
+       return(ret);
+  }
+  ptr = ret;
+  *ptr++ = op;
+  *ptr++ = '\0';               /* Null "next" pointer. */
+  *ptr++ = '\0';
+  regcode = ptr;
+
+  return(ret);
+}
+
+/*
+ - regc - emit (if appropriate) a byte of code
+ */
+static void regc(b)
+char b;
+{
+  if (regcode != &regdummy)
+       *regcode++ = b;
+  else
+       regsize++;
+}
+
+/*
+ - reginsert - insert an operator in front of already-emitted operand
+ *
+ * Means relocating the operand.
+ */
+static void reginsert(op, opnd)
+char op;
+char *opnd;
+{
+  register char *src;
+  register char *dst;
+  register char *place;
+
+  if (regcode == &regdummy) {
+       regsize += 3;
+       return;
+  }
+  src = regcode;
+  regcode += 3;
+  dst = regcode;
+  while (src > opnd) *--dst = *--src;
+
+  place = opnd;                        /* Op node, where operand used to be. */
+  *place++ = op;
+  *place++ = '\0';
+  *place++ = '\0';
+}
+
+/*
+ - regtail - set the next-pointer at the end of a node chain
+ */
+static void regtail(p, val)
+char *p;
+char *val;
+{
+  register char *scan;
+  register char *temp;
+  register int offset;
+
+  if (p == &regdummy) return;
+
+  /* Find last node. */
+  scan = p;
+  for (;;) {
+       temp = (char *)regnext(scan);
+       if (temp == (char *)NULL) break;
+       scan = temp;
+  }
+
+  if (OP(scan) == BACK)
+       offset = scan - val;
+  else
+       offset = val - scan;
+  *(scan + 1) = (offset >> 8) & 0377;
+  *(scan + 2) = offset & 0377;
+}
+
+/*
+ - regoptail - regtail on operand of first argument; nop if operandless
+ */
+static void regoptail(p, val)
+char *p;
+char *val;
+{
+  /* "Operandless" and "op != BRANCH" are synonymous in practice. */
+  if (p == (char *)NULL || p == &regdummy || OP(p) != BRANCH) return;
+  regtail(OPERAND(p), val);
+}
+
+/* regexec and friends
+ */
+
+/* Global work variables for regexec().
+ */
+static char *reginput;         /* String-input pointer. */
+static char *regbol;           /* Beginning of input, for ^ check. */
+static char **regstartp;       /* Pointer to startp array. */
+static char **regendp;         /* Ditto for endp. */
+
+/* Forwards.
+ */
+static int regtry(regexp *prog, char *string);
+static int regmatch(char *prog);
+static int regrepeat(char *p);
+
+#ifdef DEBUG
+int regnarrate = 0;
+void regdump();
+static char *regprop(char *op);
+#endif
+
+/*
+ - regexec - match a regexp against a string
+ */
+int regexec(prog, string, bolflag)
+register regexp *prog;
+register char *string;
+int bolflag;
+{
+  register char *s;
+
+  /* Be paranoid... */
+  if (prog == (regexp *)NULL || string == (char *)NULL) {
+       regerror("NULL parameter");
+       return(0);
+  }
+
+  /* Check validity of program. */
+  if (UCHARAT(prog->program) != MAGIC) {
+       regerror("corrupted program");
+       return(0);
+  }
+
+  /* If there is a "must appear" string, look for it. */
+  if (prog->regmust != (char *)NULL) {
+       s = string;
+       while ((s = strchr(s, prog->regmust[0])) != (char *)NULL) {
+               if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+                       break;  /* Found it. */
+               s++;
+       }
+       if (s == (char *)NULL)          /* Not present. */
+               return(0);
+  }
+
+  /* Mark beginning of line for ^ . */
+  if (bolflag)
+       regbol = string;
+  else
+       regbol = (char *)NULL;
+
+  /* Simplest case:  anchored match need be tried only once. */
+  if (prog->reganch) return(regtry(prog, string));
+
+  /* Messy cases:  unanchored match. */
+  s = string;
+  if (prog->regstart != '\0')  /* We know what char it must start with. */
+       while ((s = strchr(s, prog->regstart)) != (char *)NULL) {
+               if (regtry(prog, s)) return(1);
+               s++;
+       }
+  else
+       /* We don't -- general case. */
+       do {
+               if (regtry(prog, s)) return(1);
+       } while (*s++ != '\0');
+
+  /* Failure. */
+  return(0);
+}
+
+/*
+ - regtry - try match at specific point
+ */
+static int regtry(prog, string)   /* 0 failure, 1 success */
+regexp *prog;
+char *string;
+{
+  register int i;
+  register char **sp;
+  register char **ep;
+
+  reginput = string;
+  regstartp = prog->startp;
+  regendp = prog->endp;
+
+  sp = prog->startp;
+  ep = prog->endp;
+  for (i = NSUBEXP; i > 0; i--) {
+       *sp++ = (char *)NULL;
+       *ep++ = (char *)NULL;
+  }
+  if (regmatch(prog->program + 1)) {
+       prog->startp[0] = string;
+       prog->endp[0] = reginput;
+       return(1);
+  } else
+       return(0);
+}
+
+/*
+ - regmatch - main matching routine
+ *
+ * Conceptually the strategy is simple:  check to see whether the current
+ * node matches, call self recursively to see whether the rest matches,
+ * and then act accordingly.  In practice we make some effort to avoid
+ * recursion, in particular by going through "ordinary" nodes (that don't
+ * need to know whether the rest of the match failed) by a loop instead of
+ * by recursion.
+ */
+static int regmatch(prog)      /* 0 failure, 1 success */ 
+char *prog;
+{
+  register char *scan;         /* Current node. */
+  char *next;                  /* Next node. */
+
+  scan = prog;
+#ifdef DEBUG
+  if (scan != (char *)NULL && regnarrate) fprintf(stderr, "%s(\n", regprop(scan));
+#endif
+  while (scan != (char *)NULL) {
+#ifdef DEBUG
+       if (regnarrate) fprintf(stderr, "%s...\n", regprop(scan));
+#endif
+       next = regnext(scan);
+
+       switch (OP(scan)) {
+           case BOL:
+               if (reginput != regbol) return(0);
+               break;
+           case EOL:
+               if (*reginput != '\0') return(0);
+               break;
+           case ANY:
+               if (*reginput == '\0') return(0);
+               reginput++;
+               break;
+           case EXACTLY:{
+                       register int len;
+                       register char *opnd;
+
+                       opnd = OPERAND(scan);
+                       /* Inline the first character, for speed. */
+                       if (*opnd != *reginput) return(0);
+                       len = strlen(opnd);
+                       if (len > 1 && strncmp(opnd, reginput, len) != 0)
+                               return(0);
+                       reginput += len;
+               }
+               break;
+           case ANYOF:
+               if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == (char *)NULL)
+                       return(0);
+               reginput++;
+               break;
+           case ANYBUT:
+               if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != (char *)NULL)
+                       return(0);
+               reginput++;
+               break;
+           case NOTHING:
+               break;
+           case BACK:
+               break;
+           case OPEN + 1:
+           case OPEN + 2:
+           case OPEN + 3:
+           case OPEN + 4:
+           case OPEN + 5:
+           case OPEN + 6:
+           case OPEN + 7:
+           case OPEN + 8:
+           case OPEN + 9:{
+                       register int no;
+                       register char *save;
+
+                       no = OP(scan) - OPEN;
+                       save = reginput;
+
+                       if (regmatch(next)) {
+                               /* Don't set startp if some later
+                                * invocation of the same parentheses
+                                * already has. */
+                               if (regstartp[no] == (char *)NULL)
+                                       regstartp[no] = save;
+                               return(1);
+                       } else
+                               return(0);
+               }
+               break;
+           case CLOSE + 1:
+           case CLOSE + 2:
+           case CLOSE + 3:
+           case CLOSE + 4:
+           case CLOSE + 5:
+           case CLOSE + 6:
+           case CLOSE + 7:
+           case CLOSE + 8:
+           case CLOSE + 9:{
+                       register int no;
+                       register char *save;
+
+                       no = OP(scan) - CLOSE;
+                       save = reginput;
+
+                       if (regmatch(next)) {
+                               /* Don't set endp if some later
+                                * invocation of the same parentheses
+                                * already has. */
+                               if (regendp[no] == (char *)NULL) regendp[no] = save;
+                               return(1);
+                       } else
+                               return(0);
+               }
+               break;
+           case BRANCH:{
+                       register char *save;
+
+                       if (OP(next) != BRANCH) /* No choice. */
+                               next = OPERAND(scan);   /* Avoid recursion. */
+                       else {
+                               do {
+                                       save = reginput;
+                                       if (regmatch(OPERAND(scan)))
+                                               return(1);
+                                       reginput = save;
+                                       scan = regnext(scan);
+                               } while (scan != (char *)NULL && OP(scan) == BRANCH);
+                               return(0);
+                               /* NOTREACHED */
+                       }
+               }
+               break;
+           case STAR:
+           case PLUS:{
+                       register char nextch;
+                       register int no;
+                       register char *save;
+                       register int min;
+
+                       /* Lookahead to avoid useless match attempts
+                        * when we know what character comes next. */
+                       nextch = '\0';
+                       if (OP(next) == EXACTLY) nextch = *OPERAND(next);
+                       min = (OP(scan) == STAR) ? 0 : 1;
+                       save = reginput;
+                       no = regrepeat(OPERAND(scan));
+                       while (no >= min) {
+                               /* If it could work, try it. */
+                               if (nextch == '\0' || *reginput == nextch)
+                                       if (regmatch(next)) return(1);
+                               /* Couldn't or didn't -- back up. */
+                               no--;
+                               reginput = save + no;
+                       }
+                       return(0);
+               }
+               break;
+           case END:
+               return(1);      /* Success! */
+               break;
+           default:
+               regerror("memory corruption");
+               return(0);
+               break;
+       }
+
+       scan = next;
+  }
+
+  /* We get here only if there's trouble -- normally "case END" is the
+   * terminating point. */
+  regerror("corrupted pointers");
+  return(0);
+}
+
+/*
+ - regrepeat - repeatedly match something simple, report how many
+ */
+static int regrepeat(p)
+char *p;
+{
+  register int count = 0;
+  register char *scan;
+  register char *opnd;
+
+  scan = reginput;
+  opnd = OPERAND(p);
+  switch (OP(p)) {
+      case ANY:
+       count = strlen(scan);
+       scan += count;
+       break;
+      case EXACTLY:
+       while (*opnd == *scan) {
+               count++;
+               scan++;
+       }
+       break;
+      case ANYOF:
+       while (*scan != '\0' && strchr(opnd, *scan) != (char *)NULL) {
+               count++;
+               scan++;
+       }
+       break;
+      case ANYBUT:
+       while (*scan != '\0' && strchr(opnd, *scan) == (char *)NULL) {
+               count++;
+               scan++;
+       }
+       break;
+      default:                 /* Oh dear.  Called inappropriately. */
+       regerror("internal foulup");
+       count = 0;              /* Best compromise. */
+       break;
+  }
+  reginput = scan;
+
+  return(count);
+}
+
+/*
+ - regnext - dig the "next" pointer out of a node
+ */
+static char *regnext(p)
+register char *p;
+{
+  register int offset;
+
+  if (p == &regdummy) return((char *)NULL);
+
+  offset = NEXT(p);
+  if (offset == 0) return((char *)NULL);
+
+  if (OP(p) == BACK)
+       return(p - offset);
+  else
+       return(p + offset);
+}
+
+#ifdef DEBUG
+
+static char *regprop();
+
+/*
+ - regdump - dump a regexp onto stdout in vaguely comprehensible form
+ */
+void regdump(r)
+regexp *r;
+{
+  register char *s;
+  register char op = EXACTLY;  /* Arbitrary non-END op. */
+  register char *next;
+
+  s = r->program + 1;
+  while (op != END) {          /* While that wasn't END last time... */
+       op = OP(s);
+       printf("%2d%s", (int) (s - r->program), regprop(s));    /* Where, what. */
+       next = regnext(s);
+       if (next == (char *)NULL)       /* Next ptr. */
+               printf("(0)");
+       else
+               printf("(%d)", (int) (s - r->program) + (int) (next - s));
+       s += 3;
+       if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+               /* Literal string, where present. */
+               while (*s != '\0') {
+                       putchar(*s);
+                       s++;
+               }
+               s++;
+       }
+       putchar('\n');
+  }
+
+  /* Header fields of interest. */
+  if (r->regstart != '\0') printf("start `%c' ", r->regstart);
+  if (r->reganch) printf("anchored ");
+  if (r->regmust != (char *)NULL) printf("must have \"%s\"", r->regmust);
+  printf("\n");
+}
+
+/*
+ - regprop - printable representation of opcode
+ */
+static char *regprop(op)
+char *op;
+{
+  register char *p;
+  static char buf[50];
+
+  (void) strcpy(buf, ":");
+
+  switch (OP(op)) {
+      case BOL:        p = "BOL";              break;
+      case EOL:        p = "EOL";              break;
+      case ANY:        p = "ANY";              break;
+      case ANYOF:      p = "ANYOF";            break;
+      case ANYBUT:     p = "ANYBUT";           break;
+      case BRANCH:     p = "BRANCH";           break;
+      case EXACTLY:    p = "EXACTLY";          break;
+      case NOTHING:    p = "NOTHING";          break;
+      case BACK:       p = "BACK";             break;
+      case END:        p = "END";              break;
+      case OPEN + 1:
+      case OPEN + 2:
+      case OPEN + 3:
+      case OPEN + 4:
+      case OPEN + 5:
+      case OPEN + 6:
+      case OPEN + 7:
+      case OPEN + 8:
+      case OPEN + 9:
+       sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN);
+       p = (char *)NULL;
+       break;
+      case CLOSE + 1:
+      case CLOSE + 2:
+      case CLOSE + 3:
+      case CLOSE + 4:
+      case CLOSE + 5:
+      case CLOSE + 6:
+      case CLOSE + 7:
+      case CLOSE + 8:
+      case CLOSE + 9:
+       sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE);
+       p = (char *)NULL;
+       break;
+      case STAR:       p = "STAR";             break;
+      case PLUS:       p = "PLUS";             break;
+      default: regerror("corrupted opcode"); p = (char *) NULL; break;
+  }
+  if (p != (char *)NULL) (void) strcat(buf, p);
+  return(buf);
+}
+
+#endif
+
+/*
+ * $PchId: regexp.c,v 1.4 1996/02/22 09:03:07 philip Exp $
+ */
diff --git a/lib/nbsd_libcompat_minix/v8regsub.c b/lib/nbsd_libcompat_minix/v8regsub.c
new file mode 100644 (file)
index 0000000..36f8c03
--- /dev/null
@@ -0,0 +1,90 @@
+/* regsub
+ *
+ *     Copyright (c) 1986 by University of Toronto.
+ *     Written by Henry Spencer.  Not derived from licensed software.
+ *
+ *     Permission is granted to anyone to use this software for any
+ *     purpose on any computer system, and to redistribute it freely,
+ *     subject to the following restrictions:
+ *
+ *     1. The author is not responsible for the consequences of use of
+ *             this software, no matter how awful, even if they arise
+ *             from defects in it.
+ *
+ *     2. The origin of this software must not be misrepresented, either
+ *             by explicit claim or by omission.
+ *
+ *     3. Altered versions must be plainly marked as such, and must not
+ *             be misrepresented as being the original software.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#define const          /* avoid "const poisoning" */
+#include <compat/regexp.h>
+#undef const
+
+/* The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define        MAGIC   0234
+
+#define CHARBITS 0377
+#ifndef CHARBITS
+#define        UCHARAT(p)      ((int)*(unsigned char *)(p))
+#else
+#define        UCHARAT(p)      ((int)*(p)&CHARBITS)
+#endif
+
+/*
+ - regsub - perform substitutions after a regexp match
+ */
+void regsub(prog, source, dest)
+regexp *prog;
+char *source;
+char *dest;
+{
+  register char *src;
+  register char *dst;
+  register char c;
+  register int no;
+  register int len;
+
+  if (prog == (regexp *)NULL || source == (char *)NULL || dest == (char *)NULL) {
+       regerror("NULL parm to regsub");
+       return;
+  }
+  if (UCHARAT(prog->program) != MAGIC) {
+       regerror("damaged regexp fed to regsub");
+       return;
+  }
+  src = source;
+  dst = dest;
+  while ((c = *src++) != '\0') {
+       if (c == '&')
+               no = 0;
+       else if (c == '\\' && '0' <= *src && *src <= '9')
+               no = *src++ - '0';
+       else
+               no = -1;
+
+       if (no < 0) {           /* Ordinary character. */
+               if (c == '\\' && (*src == '\\' || *src == '&')) c = *src++;
+               *dst++ = c;
+       } else
+       if (prog->startp[no] != (char *)NULL && prog->endp[no] != (char *)NULL) {
+               len = (int) (prog->endp[no] - prog->startp[no]);
+               strncpy(dst, prog->startp[no], len);
+               dst += len;
+               if (len != 0 && *(dst - 1) == '\0') {   /* strncpy hit NUL. */
+                       regerror("damaged match string");
+                       return;
+               }
+       }
+  }
+  *dst++ = '\0';
+}
+
+/*
+ * $PchId: regsub.c,v 1.3 1995/11/27 20:18:16 philip Exp $
+ */