Initial revision 1.0.0pre3
authormdw <mdw>
Wed, 2 Jun 1999 16:53:31 +0000 (16:53 +0000)
committermdw <mdw>
Wed, 2 Jun 1999 16:53:31 +0000 (16:53 +0000)
32 files changed:
.cvsignore [new file with mode: 0644]
.links [new file with mode: 0644]
.skelrc [new file with mode: 0644]
.skelrc.sh [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
archtab [new file with mode: 0644]
configure.in [new file with mode: 0644]
setup [new file with mode: 0755]
src/.cvsignore [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/pres_curses.c [new file with mode: 0644]
src/pres_curses.h [new file with mode: 0644]
src/pres_plain.c [new file with mode: 0644]
src/pres_plain.h [new file with mode: 0644]
src/sw.c [new file with mode: 0644]
src/sw.h [new file with mode: 0644]
src/sw_arch.c [new file with mode: 0644]
src/sw_arch.h [new file with mode: 0644]
src/sw_build.c [new file with mode: 0644]
src/sw_build.h [new file with mode: 0644]
src/sw_env.c [new file with mode: 0644]
src/sw_env.h [new file with mode: 0644]
src/sw_info.c [new file with mode: 0644]
src/sw_info.h [new file with mode: 0644]
src/sw_links.c [new file with mode: 0644]
src/sw_links.h [new file with mode: 0644]
src/sw_rsh.c [new file with mode: 0644]
src/sw_rsh.h [new file with mode: 0644]
sw-env [new file with mode: 0644]
sw.1 [new file with mode: 0644]
sw.in [new file with mode: 0755]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..b7fc20a
--- /dev/null
@@ -0,0 +1,11 @@
+Makefile.in
+aclocal.m4
+config.h.in
+configure
+stamp-h.in
+build
+alpha
+sgi
+sparc
+install
+.deps
diff --git a/.links b/.links
new file mode 100644 (file)
index 0000000..54e724f
--- /dev/null
+++ b/.links
@@ -0,0 +1,4 @@
+missing
+mkinstalldirs
+install-sh
+COPYING
diff --git a/.skelrc b/.skelrc
new file mode 100644 (file)
index 0000000..1bb9596
--- /dev/null
+++ b/.skelrc
@@ -0,0 +1,8 @@
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+      (append
+       '((author . "EBI")
+        (full-title . "sw-tools")
+        (program . "sw-tools"))
+       skel-alist))
diff --git a/.skelrc.sh b/.skelrc.sh
new file mode 100644 (file)
index 0000000..05350df
--- /dev/null
@@ -0,0 +1,15 @@
+;;; -*-emacs-lisp-*-
+
+(skelrc-force-mode 'sh-mode)
+
+(setq skel-alist
+      (append
+       '((new-comment . "#")
+        (cont-comment . "#")
+        (end-comment . "#")
+        (block-start . "#")
+        (block-banner-knob . "")
+        (block-banner-end . "")
+        (block-end . "")
+        (preamble . ""))
+       skel-alist))
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..bb85a80
--- /dev/null
@@ -0,0 +1,50 @@
+## -*-makefile-*-
+##
+## $Id: Makefile.am,v 1.1 1999/06/02 16:53:33 mdw Exp $
+##
+## Makefile for sw-tools
+##
+## (c) 1999 EBI
+##
+
+##----- Licensing notice ----------------------------------------------------
+##
+## This file is part of sw-tools.
+##
+## sw-tools is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+## 
+## sw-tools is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with sw-tools; if not, write to the Free Software Foundation,
+## Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+##----- Revision history ----------------------------------------------------
+##
+## $Log: Makefile.am,v $
+## Revision 1.1  1999/06/02 16:53:33  mdw
+## Initial revision
+##
+
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = mLib src
+
+bin_SCRIPTS = sw
+noinst_DATA = archtab sw-env
+EXTRA_DIST = archtab sw-env
+
+install-data-local:
+       $(mkinstalldirs) $(datadir)
+       for i in $(noinst_DATA); do \
+         [ -r $(datadir)/$$i ] || \
+               $(INSTALL_DATA) $(srcdir)/$$i $(datadir)/$$i; \
+       done
+
+##----- That's all, folks ---------------------------------------------------
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..692ae2a
--- /dev/null
@@ -0,0 +1,93 @@
+/* -*-c-*-
+ *
+ * $Id: acconfig.h,v 1.1 1999/06/02 16:53:33 mdw Exp $
+ *
+ * Configuration skeleton
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: acconfig.h,v $
+ * Revision 1.1  1999/06/02 16:53:33  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef ACCONFIG_H
+#define ACCONFIG_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#define _GNU_SOURCE
+
+/*----- Configuration variables -------------------------------------------*/
+@TOP@
+
+/* The package name.  */
+#define PACKAGE "sw-tools"
+
+/* Current version.  */
+#define VERSION "1.0.0"
+
+/* The installed name of my program.  */
+#define SW "sw"
+
+/* Various handy directories.  */
+#define PREFIX "/usr/local"
+#define BINDIR "/usr/local/bin"
+#define DATADIR "/usr/local/share"
+
+/* Derived facts from the above.  */
+#define ARCHTAB DATADIR "/archtab"
+#define PATH_SW BINDIR "/" SW
+
+/* My architecture name.  */
+#define ARCH "i386-linux"
+
+/* Whether to compile in curses support.  */
+#define HAVE_CURSES
+
+/* Whether the `environ' array is already declared somewhere useful.  */
+#undef DECL_ENVIRON
+
+/* Command for starting a remote shell.  */
+#define RSH "/usr/bin/rsh"
+
+/* Return type from `read' and `write'.  */
+#undef ssize_t
+
+/* Define if you have <curses.h>.  */
+#undef HAVE_CURSES_H
+
+@BOTTOM@
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/archtab b/archtab
new file mode 100644 (file)
index 0000000..7a01777
--- /dev/null
+++ b/archtab
@@ -0,0 +1,6 @@
+# archtab
+
+i386-linux     pointy
+sparc-solaris  bach
+alpha-osf1     mozart
+mips-irix      columba
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..fd50b33
--- /dev/null
@@ -0,0 +1,151 @@
+dnl -*-fundamental-*-
+dnl
+dnl $Id: configure.in,v 1.1 1999/06/02 16:53:33 mdw Exp $
+dnl
+dnl Configuration for sw-tools
+dnl
+dnl (c) 1999 EBI
+dnl
+
+dnl ----- Licensing notice --------------------------------------------------
+dnl
+dnl This file is part of sw-tools.
+dnl
+dnl sw-tools is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl 
+dnl sw-tools is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl GNU General Public License for more details.
+dnl 
+dnl You should have received a copy of the GNU General Public License
+dnl along with sw-tools; if not, write to the Free Software Foundation,
+dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+dnl ----- Revision history --------------------------------------------------
+dnl
+dnl $Log: configure.in,v $
+dnl Revision 1.1  1999/06/02 16:53:33  mdw
+dnl Initial revision
+dnl
+
+dnl --- Boring boilerplate ---
+
+AC_INIT(src/sw.c)
+AM_INIT_AUTOMAKE(sw-tools, 1.0.0pre3)
+AM_CONFIG_HEADER(config.h)
+
+dnl --- Work out the architecture name ---
+
+AC_MSG_CHECKING([architecture name])
+arch=`$srcdir/sw.in --archname`
+AC_MSG_RESULT($arch)
+AC_DEFINE_UNQUOTED(ARCH, "$arch")
+AC_SUBST(arch)
+
+dnl --- Set up the C compiler ---
+
+AC_PROG_CC
+mdw_GCC_FLAGS
+
+dnl --- Check for <unistd.h> ---
+
+AC_CHECK_HEADERS(unistd.h)
+
+dnl --- Find an appropriate remote shell program ---
+
+AC_ARG_WITH(rsh,
+[  --with-rsh=PROGRAM      use PROGRAM to start remote shells],
+[RSH=$withval])
+AC_PATH_PROGS(RSH, $RSH remsh rsh ssh, rsh)
+AC_DEFINE_UNQUOTED(RSH, "$RSH")
+
+dnl --- Find out whether I have `strsignal' or `sys_siglist' ---
+
+AC_CHECK_FUNCS(strsignal _sys_siglist, break)
+
+dnl --- Decide whether to declare `environ' ---
+
+AC_CACHE_CHECK([for declaration of \`environ'], sw_cv_environ,
+[AC_EGREP_CPP(environ, 
+[#include <sys/types.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif], [sw_cv_environ=yes], [sw_cv_environ=no])])
+if test $sw_cv_environ = yes; then
+  AC_DEFINE(DECL_ENVIRON)
+fi
+
+dnl --- Find a curses library ---
+dnl
+dnl Try to find one which knows how to resize a terminal.
+
+mdw_CHECK_MANYLIBS(newwin, ncurses curses, AC_DEFINE(HAVE_CURSES))
+
+if test $mdw_cv_lib_newwin != no; then
+AC_CHECK_HEADERS([ncurses.h ncurses/ncurses.h curses.h], [break])
+
+if test "$ac_cv_header_ncurses_h" = "no" &&
+   test "$ac_cv_header_ncurses_ncurses_h" = "no" &&
+   test "$ac_cv_header_curses_h" = "no"; then
+     AC_MSG_WARN([couldn't find a \`curses' header.  Assuming \`curses.h'.])
+     AC_DEFINE(HAVE_CURSES_H)
+fi
+
+AC_CHECK_FUNCS(wresize)
+fi
+
+
+dnl --- Other handy libraries ---
+
+mdw_CHECK_MANYLIBS(socketpair, socket)
+
+dnl --- Various standard types ---
+
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_UID_T
+
+AC_CACHE_CHECK(for ssize_t, sw_cv_ssize_t,
+[AC_EGREP_CPP(ssize_t,
+[#include <sys/types.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif],
+[sw_cv_ssize_t=yes], [sw_cv_ssize_t=no])])
+
+if test $sw_cv_ssize_t = no; then
+  AC_DEFINE(ssize_t, int)
+fi
+
+dnl --- Write in some useful paths ---
+
+mdw_prefix=$prefix mdw_exec_prefix=$exec_prefix
+transform=`echo "$program_transform_name"|sed 's,\\\\\\\\,\\\\,g; s,\\$\\$,$,g'`
+test "$prefix" = "NONE" && prefix=$ac_default_prefix
+test "$exec_prefix" = "NONE" && exec_prefix=$prefix
+SW=`echo sw|sed "$transform"`
+AC_SUBST(SW)
+AC_DEFINE_UNQUOTED(PREFIX, "$mdw_prefix")
+AC_DEFINE_UNQUOTED(BINDIR, "`eval echo $bindir`")
+AC_DEFINE_UNQUOTED(DATADIR, "`eval echo $datadir`")
+AC_DEFINE_UNQUOTED(SW, "$SW")
+prefix=$mdw_prefix exec_prefix=$mdw_exec_prefix
+
+dnl --- Output the configuration ---
+
+AC_CONFIG_SUBDIRS(mLib)
+AC_OUTPUT(Makefile src/Makefile sw)
+
+dnl ----- That's all, folks -------------------------------------------------
diff --git a/setup b/setup
new file mode 100755 (executable)
index 0000000..dd00e3e
--- /dev/null
+++ b/setup
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+set -e
+mklinks
+mkaclocal
+ln -s ../mLib .
+automake
+autoconf
+mkdir build
diff --git a/src/.cvsignore b/src/.cvsignore
new file mode 100644 (file)
index 0000000..39a0668
--- /dev/null
@@ -0,0 +1 @@
+.deps
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..168d0d9
--- /dev/null
@@ -0,0 +1,56 @@
+## -*-makefile-*-
+##
+## $Id: Makefile.am,v 1.1 1999/06/02 16:53:33 mdw Exp $
+##
+## Build the main tool
+##
+## (c) 1999 EBI
+##
+
+##----- Licensing notice ----------------------------------------------------
+##
+## This file is part of sw-tools.
+##
+## sw-tools is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+## 
+## sw-tools is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with sw-tools; if not, write to the Free Software Foundation,
+## Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+##----- Revision history ----------------------------------------------------
+##
+## $Log: Makefile.am,v $
+## Revision 1.1  1999/06/02 16:53:33  mdw
+## Initial revision
+##
+
+AUTOMAKE_OPTIONS = foreign
+
+arch=@arch@
+shareexecdir = $(datadir)/libexec/$(arch)
+LDADD = $(top_builddir)/mLib/libmLib.a
+INCLUDES = -I$(top_srcdir)
+
+shareexec_PROGRAMS = sw
+
+sw_SOURCES = \
+       sw.c sw.h \
+       sw_arch.c sw_arch.h \
+       sw_build.c sw_build.h \
+       sw_env.c sw_env.h \
+       sw_info.c sw_info.h \
+       sw_links.c sw_links.h \
+       sw_rsh.c sw_rsh.h \
+       \
+       pres_plain.c pres_plain.h \
+       pres_curses.c pres_curses.h
+
+##----- That's all, folks ---------------------------------------------------
diff --git a/src/pres_curses.c b/src/pres_curses.c
new file mode 100644 (file)
index 0000000..a134eac
--- /dev/null
@@ -0,0 +1,313 @@
+/* -*-c-*-
+ *
+ * $Id: pres_curses.c,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Curses-based output presentation
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: pres_curses.c,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+#ifdef HAVE_CURSES
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#if defined(HAVE_NCURSES_H)
+#  include <ncurses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+#  include <ncurses/ncurses.h>
+#elif defined(HAVE_CURSES_H)
+#  include <curses.h>
+#else
+#  error "Where's your <curses.h> header?"
+#endif
+#include <term.h>
+
+#include <mLib/alloc.h>
+#include <mLib/report.h>
+
+#include "sw.h"
+#include "sw_arch.h"
+#include "pres_curses.h"
+
+#ifndef SIGWINCH
+#  undef HAVE_WRESIZE
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct cwin {
+  struct cwin *next;
+  WINDOW *w;
+  int top;
+  int height;
+  WINDOW *s;
+  archent *e;
+} cwin;
+
+/*----- Static variables --------------------------------------------------*/
+
+static cwin *cwins = 0;
+static int nwins = 0;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @sizes@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Calculates window sizes for all the windows.  The heights are
+ *             determined in a way which doesn't allow rounding errors to be
+ *             an issue (although there will be up to a line's height
+ *             difference between two windows).  No actual changing of
+ *             curses structures is done.
+ */
+
+static void sizes(void)
+{
+  cwin *c;
+  int t = 0, h = LINES, w = nwins;
+
+  for (c = cwins; c; c = c->next) {
+    int a = h / w;
+    c->top = t;
+    c->height = a - 1;
+    t += a;
+    h -= a;
+    w--;
+  }
+}
+
+/* --- @sig_tstp@ --- */
+
+#ifdef SIGTSTP
+static void sig_tstp(int s)
+{
+  endwin();
+  raise(SIGSTOP);
+}
+#endif
+
+/* --- @sig_cont@ --- */
+
+#ifdef SIGTSTP
+static void sig_cont(int s)
+{
+  wrefresh(curscr);
+}
+#endif
+
+/* --- @sig_winch@ --- */
+
+#ifdef HAVE_WRESIZE
+
+static void sig_winch(int s)
+{
+  cwin *c;
+  endwin();
+  refresh();
+  sizes();
+  for (c = cwins; c; c = c->next) {
+    mvwin(c->w, c->top, 0);
+    wresize(c->w, c->height, COLS);
+    mvwin(c->s, c->top + c->height, 0);
+    wnoutrefresh(c->w);
+    wnoutrefresh(c->s);
+  }
+  doupdate();
+}
+
+#endif
+
+/* --- @curses_ok@ --- */
+
+int curses_ok(void)
+{
+  if (!isatty(STDOUT_FILENO)) {
+    moan("can't use curses: stdout isn't a terminal");
+    return (0);
+  }
+  if (setupterm(0, STDOUT_FILENO, 0) == ERR) {
+    moan("can't use curses: couldn't read terminfo");
+    return (0);
+  }
+  if (!tigetstr("cup")) {
+    moan("can't use curses: terminal insufficiently winning");
+    return (0);
+  }
+  return (1);
+}
+
+/* --- @curses_init@ --- */
+
+int curses_init(archcons *a)
+{
+  cwin **cc = &cwins, *c;
+
+  /* --- Allocate my window structures --- */
+
+  nwins = 0;
+  for (; a; a = a->cdr) {
+    archent *e = a->car;
+    if (!e->r)
+      continue;
+    c = xmalloc(sizeof(cwin));
+    e->pres = c;
+    c->w = 0;
+    c->e = e;
+    nwins++;
+    *cc = c;
+    cc = &c->next;
+  }
+  *cc = 0;
+
+  /* --- Haul curses up by its naughty bits --- */
+
+  initscr();
+  keypad(stdscr, TRUE);
+  cbreak();
+  noecho();
+  nonl();
+  def_prog_mode();
+
+  /* --- Grind through everything setting up windows --- */
+
+  sizes();
+  for (c = cwins; c; c = c->next) {
+    if ((c->w = newwin(c->height, COLS, c->top, 0)) == 0 ||
+       (c->s = newwin(1, COLS, c->top + c->height, 0)) == 0)
+      goto fail_0;
+    scrollok(c->w, TRUE);
+    leaveok(c->w, TRUE);
+    leaveok(c->s, TRUE);
+    wbkgdset(c->s, A_STANDOUT);
+    werase(c->s);
+    mvwprintw(c->s, 0, 0, "  %s  [running]\n", c->e->arch);
+    wnoutrefresh(c->w);
+    wnoutrefresh(c->s);
+  }
+  doupdate();
+
+#ifdef HAVE_WRESIZE
+  signal(SIGWINCH, sig_winch);
+#endif
+#ifdef SIGTSTP
+  signal(SIGTSTP, sig_tstp);
+  signal(SIGCONT, sig_cont);
+#endif
+  return (0);
+
+fail_0:
+  c = cwins;
+  while (c) {
+    cwin *cc = c->next;
+    if (c->w)
+      delwin(c->w);
+    if (c->s)
+      delwin(c->s);
+    free(c);
+    c = cc;
+  }
+  endwin();
+  return (-1);
+}
+
+/* --- @curses_output@ --- */
+
+void curses_output(archent *e, const char *p, size_t sz)
+{
+  cwin *c = e->pres;
+  while (sz) {
+    waddch(c->w, *p);
+    p++;
+    sz--;
+  }
+  wrefresh(c->w);
+}
+
+/* --- @curses_close@ --- */
+
+void curses_close(archent *e, int ok)
+{
+  cwin *c = e->pres;
+  mvwprintw(c->s, 0, 0, "  %s [%s]\n", e->arch, ok ? "finished" : "failed");
+  wrefresh(c->s);
+}
+
+/* --- @curses_done@ --- */
+
+void curses_done(archcons *a)
+{
+  if (opt_flags & optFlag_beep) {
+    beep();
+    doupdate();
+  }
+  wgetch(cwins->w);
+  cwins = 0;
+  for (; a; a = a->cdr) {
+    archent *e = a->car;
+    cwin *c = e->pres;
+    delwin(c->w);
+    delwin(c->s);
+    free(c);
+  }
+  curses_abort(0);
+}
+
+/* --- @curses_abort@ --- */
+
+void curses_abort(archcons *a)
+{
+#ifdef HAVE_WRESIZE
+  signal(SIGWINCH, SIG_DFL);
+#endif
+#ifdef SIGTSTP
+  signal(SIGTSTP, SIG_DFL);
+  signal(SIGCONT, SIG_DFL);
+#endif
+  endwin();
+}
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#else
+  int pres_curses__built = 1;
+#endif
diff --git a/src/pres_curses.h b/src/pres_curses.h
new file mode 100644 (file)
index 0000000..0f13762
--- /dev/null
@@ -0,0 +1,88 @@
+/* -*-c-*-
+ *
+ * $Id: pres_curses.h,v 1.1 1999/06/02 16:53:36 mdw Exp $
+ *
+ * Curses-based output presentation
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: pres_curses.h,v $
+ * Revision 1.1  1999/06/02 16:53:36  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef PRES_CURSES_H
+#define PRES_CURSES_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef CONFIG_H
+#  include "config.h"
+#endif
+
+#ifndef SW_ARCH_H
+#  include "sw_arch.h"
+#endif
+
+#ifdef HAVE_CURSES
+
+/*----- Presentation style ------------------------------------------------*/
+
+extern int curses_ok(void);
+extern int curses_init(archcons */*a*/);
+extern void curses_output(archent */*e*/, const char */*p*/, size_t /*sz*/);
+extern void curses_close(archent */*e*/, int /*ok*/);
+extern void curses_done(archcons */*a*/);
+extern void curses_abort(archcons */*a*/);
+
+#ifdef PRES_LINK
+   static pres pres_curses = {
+     PRES_LINK, "curses",
+     curses_ok, curses_init,
+     curses_output, curses_close,
+     curses_done, curses_abort
+   };
+#  undef PRES_LINK
+#  define PRES_LINK &pres_curses
+#  if PRES_PREFERENCE <= 2
+#    undef PRES_DEFAULT
+#    define PRES_DEFAULT &pres_curses
+#  endif
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#endif
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/src/pres_plain.c b/src/pres_plain.c
new file mode 100644 (file)
index 0000000..3ee170c
--- /dev/null
@@ -0,0 +1,105 @@
+/* -*-c-*-
+ *
+ * $Id: pres_plain.c,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Plain output style for remote builds
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: pres_plain.c,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/alloc.h>
+#include <mLib/lbuf.h>
+
+#include "sw.h"
+#include "sw_arch.h"
+#include "pres_plain.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @line@ --- */
+
+static void line(char *s, void *p)
+{
+  archent *e = p;
+  if (s)
+    printf("%s: %s\n", e->arch, s);
+}
+
+/* --- @plain_init@ --- */
+
+int plain_init(archcons *a)
+{
+  for (; a; a = a->cdr) {
+    archent *e = a->car;
+    lbuf *b;
+    if (!e->r)
+      continue;
+    b = xmalloc(sizeof(lbuf));
+    e->pres = b;
+    lbuf_init(b, line, e);
+  }
+  return (0);
+}
+
+/* --- @plain_output@ --- */
+
+void plain_output(archent *e, const char *p, size_t sz)
+{
+  lbuf_snarf(e->pres, p, sz);
+}
+
+/* --- @plain_close@ --- */
+
+void plain_close(archent *e, int ok)
+{
+  lbuf_close(e->pres);
+}
+
+/* --- @plain_done@ --- */
+
+void plain_done(archcons *a)
+{
+  for (; a; a = a->cdr) {
+    if (!a->car->r)
+      continue;
+    free(a->car->pres);
+  }
+  if (opt_flags & optFlag_beep)
+    putchar('\a');
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/pres_plain.h b/src/pres_plain.h
new file mode 100644 (file)
index 0000000..ab83e06
--- /dev/null
@@ -0,0 +1,76 @@
+/* -*-c-*-
+ *
+ * $Id: pres_plain.h,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Plain output style for remote builds
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: pres_plain.h,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef PRES_PLAIN_H
+#define PRES_PLAIN_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef SW_ARCH_H
+#  include "sw_arch.h"
+#endif
+
+/*----- Presentation style ------------------------------------------------*/
+
+extern int plain_init(archcons */*a*/);
+extern void plain_output(archent */*e*/, const char */*p*/, size_t /*sz*/);
+extern void plain_close(archent */*e*/, int /*ok*/);
+extern void plain_done(archcons */*a*/);
+
+#ifdef PRES_LINK
+   static pres pres_plain = {
+     PRES_LINK, "plain",
+     0, plain_init, plain_output, plain_close, plain_done, 0
+   };
+#  undef PRES_LINK
+#  define PRES_LINK &pres_plain
+#  if PRES_PREFERENCE <= 1
+#    undef PRES_DEFAULT
+#    define PRES_DEFAULT &pres_plain
+#  endif
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/src/sw.c b/src/sw.c
new file mode 100644 (file)
index 0000000..4f7b293
--- /dev/null
+++ b/src/sw.c
@@ -0,0 +1,308 @@
+/* -*-c-*-
+ *
+ * $Id: sw.c,v 1.1 1999/06/02 16:53:33 mdw Exp $
+ *
+ * Main driver code for sw-tools
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw.c,v $
+ * Revision 1.1  1999/06/02 16:53:33  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+
+#include <mLib/alloc.h>
+#include <mLib/dstr.h>
+#include <mLib/exc.h>
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/sub.h>
+
+#define CMD_LINK 0
+#include "sw.h"
+#include "sw_arch.h"
+#include "sw_build.h"
+#include "sw_info.h"
+#include "sw_links.h"
+#include "sw_rsh.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static cmd *cmds = CMD_LINK;
+
+/*----- Global option variables -------------------------------------------*
+ *
+ * It'd be nicer if these could be parsed by the build commands, but alas
+ * this is not to be.  Consider `sw configure --arch=i386-linux
+ * --with-diffutils': how is the program to know which options are for it and
+ * which are for the remote one?  The `configure' command is important,
+ * because it does some hairy stuff under the covers (inserting magical
+ * options, and running `../configure'), so I can't just require `sw run
+ * configure'.  Requiring the user to always put in a `--' to separate the
+ * two lots of options is horrific.  So they get picked up at the main
+ * program and stuffed into global variables.
+ *
+ * On the other hand, there is the advantage that `--output' options can be
+ * put in an environment variable here.
+ */
+
+const char *opt_output = 0;
+const char *opt_arch = 0;
+unsigned int opt_flags;
+
+/*----- Helpful GNUy message routines -------------------------------------*/
+
+/* --- @version@ --- */
+
+static void version(FILE *fp)
+{
+  fprintf(fp, "%s v. " VERSION "\n", QUIS);
+}
+
+/* --- @usage@ --- */
+
+static void usage(FILE *fp)
+{
+  fprintf(fp, "Usage: %s [-fbi] [-a arch,...] [-o style] command [args]\n",
+         QUIS);
+}
+
+/* --- @help@ --- */
+
+static void help(FILE *fp, int full)
+{
+  cmd *p;
+  version(fp);
+  fputc('\n', fp);
+  usage(fp);
+  fputs("\n\
+Performs various handy jobs with multiple-architecture builds.\n\
+\n\
+There are some options which affect a few of the available commands:\n\
+\n\
+-a, --arch=ARCH,...    Only build on the named architectures.\n\
+-b, --beep             Beep when the build is complete.\n\
+-i, --install          Mark architectures as done when build succeeds.\n\
+-f, --force            Run build commands on installed architectures.\n\
+-o, --output=STYLE     Display output in a particular style.  Use style\n\
+                       `help' for a list.\n\
+\n", fp);
+
+  if (full) {
+    fputs("The various commands provided are:\n\n", fp);
+    for (p = cmds; p; p = p->next) {
+      fputs(p->help, fp);
+      fputc('\n', fp);
+    }
+    fprintf(fp,
+"`%s' is an [mdw] production, brought to you in association with the\n\
+European Bioinformatics Institute.\n", QUIS);
+  } else {
+    fputs("The various commands, in summary:\n\n", fp);
+    for (p = cmds; p; p = p->next) {
+      size_t sz = strcspn(p->help, "\n\t");
+      fwrite(p->help, 1, sz, fp);
+      fputc('\n', fp);
+    }
+    fprintf(fp,
+"\nType `%s --help-full' for complete information.  There's a lot of it!\n",
+           QUIS);
+  }
+}
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @main@ --- *
+ *
+ * Arguments:  @int argc@ = number of command line arguments
+ *             @char *argv[]@ = array of command line strings
+ *
+ * Returns:    Zero on success, nonzero on failure.
+ *
+ * Use:                Main program.  Parses some trivial arguments out, and
+ *             dispatches control to one of the subprogram handlers.
+ */
+
+int main(int argc, char *argv[])
+{
+  unsigned f = 0;
+
+  enum {
+    f_bogus = 1
+  };
+
+  /* --- Initialize the support library --- */
+
+  ego(argv[0]);
+  sub_init();
+
+  /* --- Parse command line flags --- */
+
+  for (;;) {
+    static struct option opt[] = {
+
+      /* --- Standard GNUy help options --- */
+
+      { "help",                0,              0,      'h' },
+      { "help-full",   0,              0,      'H' },
+      { "version",     0,              0,      'v' },
+      { "usage",       0,              0,      'u' },
+
+      /* --- Build options --- *
+       *
+       */
+
+      { "arch",                OPTF_ARGREQ,    0,      'a' },
+      { "force",       0,              0,      'f' },
+      { "install",     0,              0,      'i' },
+      { "output",      OPTF_ARGREQ,    0,      'o' },
+      { "beep",                0,              0,      'b' },
+
+      /* --- Internal-use-only magical options --- *
+       *
+       * You get what you deserve if you use this.
+       */
+
+      { "me",          OPTF_ARGREQ,    0,      '=' },
+      { "remote",      OPTF_ARGREQ,    0,      '!' },
+
+      /* --- Termination marker --- */
+
+      { 0,             0,              0,      0 }
+    };
+    int i = mdwopt(argc, argv, "+hHvu a:bfio:", opt, 0, 0, gFlag_envVar);
+    if (i < 0)
+      break;
+
+    switch (i) {
+
+      /* --- GNUy help --- */
+
+      case 'h':
+       help(stdout, 0);
+       exit(0);
+      case 'H':
+       help(stdout, 1);
+       exit(0);
+      case 'v':
+       version(stdout);
+       exit(0);
+      case 'u':
+       usage(stdout);
+       exit(0);
+
+      /* --- Build options --- */
+
+      case 'a':
+       opt_arch = optarg;
+       break;
+      case 'f':
+       opt_flags |= optFlag_force;
+       break;
+      case 'b':
+       opt_flags |= optFlag_beep;
+       break;
+      case 'i':
+       opt_flags |= optFlag_install;
+       break;
+      case 'o':
+       opt_output = optarg;
+       break;
+
+      /* --- Magical options for internal use --- */
+
+      case '=':
+       ego(optarg);
+       break;
+      case '!':
+       swrsh_remote(optarg);
+       _exit(1);
+
+      /* --- The user screwed up --- */
+
+      default:
+       f |= f_bogus;
+       break;
+    }
+  }
+
+  if (f & f_bogus || argc == optind) {
+    usage(stderr);
+    exit(1);
+  }
+
+  /* --- Pick up the operation name --- */
+
+  argc -= optind;
+  argv += optind;
+  optind = 0;
+
+  {
+    cmd *p, *chosen = 0;
+    const char *which = argv[0];
+    size_t sz = strlen(which);
+
+    for (p = cmds; p; p = p->next) {
+      if (strncmp(which, p->name, sz) == 0) {
+       if (p->name[sz] == 0) {
+         chosen = p;
+         break;
+       } else if (chosen)
+         die(1, "ambiguous command name `%s'", which);
+       chosen = p;
+      }
+    }
+
+    if (!chosen)
+      die(1, "unknown command name `%s'", which);
+    signal(SIGPIPE, SIG_IGN);
+    TRY
+      return (chosen->cmd(argc, argv));
+    CATCH switch (exc_type) {
+      case EXC_NOMEM:
+       die(1, "not enough memory");
+      default:
+       RETHROW;
+    } END_TRY;
+  }
+  return (127);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/sw.h b/src/sw.h
new file mode 100644 (file)
index 0000000..4d0ef30
--- /dev/null
+++ b/src/sw.h
@@ -0,0 +1,85 @@
+/* -*-c-*-
+ *
+ * $Id: sw.h,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Interface to main options parser
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw.h,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SW_H
+#define SW_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- About the commands list --- *
+ *
+ * Declare commands in the header file, like this:
+ *@
+ *   #ifdef CMD_LINK
+ *      static cmd cmd_foo = { CMD_LINK, sw_foo, "foo\t\tDoes something." };
+ *   #  undef CMD_LINK
+ *   #  define CMD_LINK &cmd_foo
+ *   #endif
+ *@
+ * This causes all the commands to be linked together at compile time, and is
+ * therefore a cool thing to do.
+ */
+
+typedef struct cmd {
+  struct cmd *next;
+  const char *name;
+  int (*cmd)(int /*argc*/, char */*argv*/[]);
+  const char *help;
+} cmd;
+
+/*----- Global variables --------------------------------------------------*/
+
+extern const char *opt_arch;           /* Value of `--arch' option */
+extern const char *opt_output;         /* Value of `--output' option */
+extern unsigned int opt_flags;         /* Various bitflag options */
+
+enum {
+  optFlag_install = 1,
+  optFlag_force = 2,
+  optFlag_beep = 4
+};
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/src/sw_arch.c b/src/sw_arch.c
new file mode 100644 (file)
index 0000000..5eecd31
--- /dev/null
@@ -0,0 +1,430 @@
+/* -*-c-*-
+ *
+ * $Id: sw_arch.c,v 1.1 1999/06/02 16:53:34 mdw Exp $
+ *
+ * Messing with architectures
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_arch.c,v $
+ * Revision 1.1  1999/06/02 16:53:34  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/alloc.h>
+#include <mLib/dstr.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/sub.h>
+
+#include "sw_arch.h"
+#include "sw_info.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static archcons *tab = 0;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @arch_readtab@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    The address of the archtab list.
+ *
+ * Use:                Reads the archtab file (if necessary) and returns a list of
+ *             its contents.
+ */
+
+archcons *arch_readtab(void)
+{
+  archcons **p;
+  FILE *fp;
+  dstr d = DSTR_INIT;
+
+  /* --- Return the list if I've already got it --- */
+
+  if (tab)
+    return (tab);
+
+  /* --- Open the file --- */
+
+  if ((fp = fopen(ARCHTAB, "r")) == 0)
+    die(1, "couldn't open `%s': %s", ARCHTAB, strerror(errno));
+
+  /* --- Read it in line-by-line --- */
+
+  p = &tab;
+  for (; dstr_putline(&d, fp) != EOF; DRESET(&d)) {
+    archent *a;
+    char *q = d.buf;
+    char *arch, *host;
+
+    /* --- Pick out the architecture name --- */
+
+    while (isspace((unsigned char)*q))
+      q++;
+    if (*q == '#' || *q == 0)
+      continue;
+    arch = q;
+    while (*q && !isspace((unsigned char)*q))
+      q++;
+    if (*q)
+      *q++ = 0;
+
+    /* --- Pick out the hostname --- */
+
+    while (isspace((unsigned char)*q))
+      q++;
+    if (!q)
+      continue;
+    host = q;
+    while (*q && !isspace((unsigned char)*q))
+      q++;
+    if (*q)
+      *q++ = 0;
+
+    /* --- Allocate a new link --- */
+
+    a = xmalloc(sizeof(*a));
+    a->arch = xstrdup(arch);
+    a->host = xstrdup(host);
+    a->cons.car = a;
+    a->r = 0;
+    a->pres = 0;
+    a->flags = 0;
+    if (strcmp(arch, ARCH) == 0)
+      a->flags |= archFlag_home;
+    *p = &a->cons;
+    p = &a->cons.cdr;
+  }
+
+  /* --- Done --- */
+
+  dstr_destroy(&d);
+  fclose(fp);
+  *p = 0;
+  return (tab);
+}
+
+/* --- @arch_lookup@ --- *
+ *
+ * Arguments:  @const char *arch@ = pointer to archtecture name
+ *             @int abbrev@ = whether abbreviations are OK
+ *
+ * Returns:    Pointer to archtab block, or null.
+ *
+ * Use:                Translates an architecture name into the name of a host
+ *             supporting that architecture.
+ */
+
+archent *arch_lookup(const char *arch, int abbrev)
+{
+  size_t sz = strlen(arch);
+  archcons *a;
+  archent *chosen = 0;
+
+  for (a = arch_readtab(); a; a = a->cdr) {
+    archent *e = a->car;
+    if (strncmp(arch, e->arch, sz) == 0) {
+      if (e->arch[sz] == 0)
+       return (e);
+      else if (!abbrev)
+       continue;
+      else if (chosen)
+       return (0);
+      else
+       chosen = e;
+    }
+  }
+  return (chosen);
+}
+
+/* --- @arch_filter@ --- *
+ *
+ * Arguments:  @archcons *a@ = input list to filter
+ *             @const char *p@ = pointer to a textual list of architectures
+ *             @unsigned and@, @unsigned xor@ = flags to look for
+ *
+ * Returns:    A newly constructed architecture list containing only the
+ *             listed architectures.
+ *
+ * Use:                Filters the architecture list down to a few interesting
+ *             architectures.
+ *
+ *             If @p@ is non-null, it is a textual list of architecture
+ *             names (possibly abbreviated), and separated by whitespace
+ *             and/or commas: only the named architectures are included in
+ *             the resulting list.  If @p@ is null, all architectures are
+ *             considered.
+ *
+ *             The list is further trimmed down by examining the flags words
+ *             in each entry.  Only entries with flags @f@ where @(f ^ xor)
+ *             & and@ is zero are left in the resulting list.  (To include
+ *             all entries, clear @and@ to zero.  To require a flag to be
+ *             clear, set the corresponding bit in @and@.  To require a flag
+ *             to be set, set the corresponding bit in both @and@ and @xor@.
+ *
+ *             (Don't try to filter on the @archFlag_touched@ flag.  That
+ *             flag is for the internal use of this routine.)
+ */
+
+archcons *arch_filter(archcons *a, const char *p,
+                     unsigned and, unsigned xor)
+{
+  archcons *h;
+
+  /* --- How this works --- *
+   *
+   * If I have a list of architecture names in @p@, then I scan the list and
+   * set the `touch' bit on matching entries, and clear it on nonmatching
+   * ones.  Then I set the `touch' bit in the @and@ and @xor@ arguments.
+   * Otherwise I skip all of that, and clear the `touch' bit in @and@.  After
+   * all that, I just scoop up the architectures with appropriate flags.
+   */
+
+  and &= ~archFlag_touched;
+  if (p) {
+
+    /* --- Clear all the touch flags --- */
+
+    {
+      archcons *aa;
+      for (aa = a; aa; aa = aa->cdr)
+       aa->car->flags &= ~archFlag_touched;
+    }
+
+    /* --- Set flags for entries in my list --- */
+
+    {
+      char *q = xstrdup(p);
+      for (p = strtok(q, ", \t"); p; p = strtok(0, ", \t")) {
+       archent *e = arch_lookup(p, 1);
+       if (!e)
+         die(1, "architecture `%s' not found", p);
+       e->flags |= archFlag_touched;
+      }
+      free(q);
+    }
+
+    /* --- Only include touched entries --- */
+
+    and |= archFlag_touched;
+    xor |= archFlag_touched;
+  }
+
+  /* --- Now trundle through the list accumulating matching entries --- */
+
+  {
+    archcons **p = &h;
+
+    for (; a; a = a->cdr) {
+      if (((a->car->flags ^ xor) & and) == 0) {
+       archcons *aa = CREATE(archcons);
+       aa->car = a->car;
+       *p = aa;
+       p = &aa->cdr;
+      }
+    }
+    *p = 0;
+  }
+
+  /* --- Done --- */
+
+  return (h);
+}
+
+/* --- @arch_free@ --- *
+ *
+ * Arguments:  @archcons *a@ = pointer to a list
+ *
+ * Returns:    ---
+ *
+ * Use:                Contrary to anything you might have expected from the Lispy
+ *             naming, old architecture lists don't get garbage collected.
+ *             This routine throws away an old list when you don't want it
+ *             any more.  Don't call this on the main list!  It will fail
+ *             miserably, because the cons cells in the main list are
+ *             faked.
+ */
+
+void arch_free(archcons *a)
+{
+  while (a) {
+    archcons *p = a->cdr;
+    DESTROY(a);
+    a = p;
+  }
+}
+
+/* --- @arch_toText@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to dynamic string to build result in
+ *             @archcons *a@ = list to write into the string
+ *             @unsigned and@, @unsigned xor@ = flags to look for
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a textual list of architectures to a string.  This can
+ *             then be used (for example) as the `only-arch' list in the
+ *             info file, by filling it into a skeleton and calling
+ *             @swinfo_update@.  The @and@ and @xor@ arguments work in the
+ *             same way as with @arch_filter@.
+ */
+
+void arch_toText(dstr *d, archcons *a, unsigned and, unsigned xor)
+{
+  int spc = 0;
+
+  for (; a; a = a->cdr) {
+    archent *e = a->car;
+    if (((e->flags ^ xor) & and) == 0) {
+      if (spc)
+       dstr_putc(d, ' ');
+      dstr_puts(d, e->arch);
+      spc = 1;
+    }
+  }
+}
+
+/*----- Subcommands -------------------------------------------------------*/
+
+/* --- @sw_arch@ --- */
+
+int sw_arch(int argc, char *argv[])
+{
+  if (argc != 1)
+    die(1, "Usage: arch");
+  puts(ARCH);
+  return (0);
+}
+
+/* --- @sw_host@ --- */
+
+int sw_host(int argc, char *argv[])
+{
+  archent *a;
+  if (argc != 2)
+    die(1, "Usage: host ARCH");
+  if ((a = arch_lookup(argv[1], 1)) == 0)
+    die(1, "no host for architecture `%s'", argv[1]);
+  puts(a->host);
+  return (0);
+}
+
+/* --- @sw_listarch@ --- */
+
+int sw_listarch(int argc, char *argv[])
+{
+  archcons *a;
+  if (argc != 1)
+    die(1, "Usage: listarch");
+  for (a = arch_readtab(); a; a = a->cdr)
+    puts(a->car->arch);
+  return (0);
+}
+
+/* --- @sw_all@ --- */
+
+int sw_all(int argc, char *argv[])
+{
+  swinfo sw;
+  if (argc != 1)
+    die(1, "Usage: all");
+  if (swinfo_fetch(&sw)) {
+    die(1, "couldn't read build status: %s (try running setup)",
+       strerror(errno));
+  }
+  if (sw.only_arch) {
+    free(sw.only_arch);
+    sw.only_arch = 0;
+    swinfo_put(&sw);
+  }
+  return (0);
+}  
+
+/* --- @sw_only@ --- */
+
+int sw_only(int argc, char *argv[])
+{
+  dstr d = DSTR_INIT;
+
+  /* --- Validate the arguments --- */
+
+  if (argc < 2)
+    die(1, "Usage: only-arch ARCH,...");
+
+  /* --- Gather the arguments into one buffer --- */
+
+  {
+    int i;
+    for (i = 1; i < argc; i++) {
+      dstr_putc(&d, ' ');
+      dstr_puts(&d, argv[i]);
+    }
+  }
+
+  /* --- Convert into a canonical list --- */
+
+  {
+    archcons *a = arch_filter(arch_readtab(), d.buf, 0, 0);
+    DRESET(&d);
+    arch_toText(&d, a, 0, 0);
+    arch_free(a);
+  }
+
+  /* --- Put the list into the information table --- */
+
+  {
+    swinfo sw, skel;
+
+    if (swinfo_fetch(&sw)) {
+      die(1, "couldn't read build status: %s (try running setup)",
+         strerror(errno));
+    }
+
+    swinfo_clear(&skel);
+    skel.only_arch = d.buf;
+    swinfo_update(&sw, &skel);
+
+    swinfo_put(&sw);
+    dstr_destroy(&d);
+  }
+
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/sw_arch.h b/src/sw_arch.h
new file mode 100644 (file)
index 0000000..c301e6e
--- /dev/null
@@ -0,0 +1,224 @@
+/* -*-c-*-
+ *
+ * $Id: sw_arch.h,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Messing with architectures
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_arch.h,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SW_ARCH_H
+#define SW_ARCH_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/dstr.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- @archcons@ --- *
+ *
+ * Links architectures into a list.  It's done like this so that we can have
+ * multiple lists all referring to the same data.
+ */
+
+typedef struct archcons {
+  struct archent *car;
+  struct archcons *cdr;
+} archcons;
+
+/* --- @archent@ --- *
+ *
+ * Records information extracted from the `archtab' file, together with some
+ * other data used at run-time for the parallel build stuff.
+ */
+    
+typedef struct archent {
+  struct archcons cons;                        /* Cons for linking these together */
+  char *arch;                          /* The architecture name */
+  char *host;                          /* The architecture representative */
+  int status;                          /* Exit status from build process */
+  struct sw_remote *r;                 /* Remote command control block */
+  void *pres;                          /* Data for presentation handler */
+  unsigned flags;                      /* Various other flags */
+} archent;
+
+/* --- Flags --- */
+
+enum {
+  archFlag_touched = 1,                        /* Unique flag: see @arch_filter@ */
+  archFlag_built = 2,                  /* This architecture is up-to-date */
+  archFlag_home = 4                    /* This is the current arch */
+};
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @arch_readtab@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    The address of the archtab list.
+ *
+ * Use:                Reads the archtab file (if necessary) and returns a list of
+ *             its contents.
+ */
+
+extern archcons *arch_readtab(void);
+
+/* --- @arch_lookup@ --- *
+ *
+ * Arguments:  @const char *arch@ = pointer to archtecture name
+ *             @int abbrev@ = whether abbreviations are OK
+ *
+ * Returns:    Pointer to archtab block, or null.
+ *
+ * Use:                Translates an architecture name into the name of a host
+ *             supporting that architecture.
+ */
+
+extern archent *arch_lookup(const char */*arch*/, int /*abbrev*/);
+
+/* --- @arch_filter@ --- *
+ *
+ * Arguments:  @archcons *a@ = input list to filter
+ *             @const char *p@ = pointer to a textual list of architectures
+ *             @unsigned and@, @unsigned xor@ = flags to look for
+ *
+ * Returns:    A newly constructed architecture list containing only the
+ *             listed architectures.
+ *
+ * Use:                Filters the architecture list down to a few interesting
+ *             architectures.
+ *
+ *             If @p@ is non-null, it is a textual list of architecture
+ *             names (possibly abbreviated), and separated by whitespace
+ *             and/or commas: only the named architectures are included in
+ *             the resulting list.  If @p@ is null, all architectures are
+ *             considered.
+ *
+ *             The list is further trimmed down by examining the flags words
+ *             in each entry.  Only entries with flags @f@ where @(f ^ xor)
+ *             & and@ is zero are left in the resulting list.  (To include
+ *             all entries, clear @and@ to zero.  To require a flag to be
+ *             clear, set the corresponding bit in @and@.  To require a flag
+ *             to be set, set the corresponding bit in both @and@ and @xor@.
+ *
+ *             (Don't try to filter on the @archFlag_touched@ flag.  That
+ *             flag is for the internal use of this routine.)
+ */
+
+extern archcons *arch_filter(archcons */*a*/, const char */*p*/,
+                            unsigned /*and*/, unsigned /*xor*/);
+
+/* --- @arch_free@ --- *
+ *
+ * Arguments:  @archcons *a@ = pointer to a list
+ *
+ * Returns:    ---
+ *
+ * Use:                Contrary to anything you might have expected from the Lispy
+ *             naming, old architecture lists don't get garbage collected.
+ *             This routine throws away an old list when you don't want it
+ *             any more.  Don't call this on the main list!  It will fail
+ *             miserably, because the cons cells in the main list are
+ *             faked.
+ */
+
+extern void arch_free(archcons */*a*/);
+
+/* --- @arch_toText@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to dynamic string to build result in
+ *             @archcons *a@ = list to write into the string
+ *             @unsigned and@, @unsigned xor@ = flags to look for
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a textual list of architectures to a string.  This can
+ *             then be used (for example) as the `only-arch' list in the
+ *             info file, by filling it into a skeleton and calling
+ *             @swinfo_update@.  The @and@ and @xor@ arguments work in the
+ *             same way as with @arch_filter@.
+ */
+
+extern void arch_toText(dstr */*d*/, archcons */*a*/,
+                       unsigned /*and*/, unsigned /*xor*/);
+
+/* --- Subcommands --- */
+
+extern int sw_arch(int /*argc*/, char */*argv*/[]);
+extern int sw_listarch(int /*argc*/, char */*argv*/[]);
+extern int sw_host(int /*argc*/, char */*argv*/[]);
+extern int sw_only(int /*argc*/, char */*argv*/[]);
+extern int sw_all(int /*argc*/, char */*argv*/[]);
+
+#ifdef CMD_LINK
+   static cmd cmd_only = {
+     CMD_LINK, "only-arch", sw_only,
+"only-arch ARCH,...\n\
+       Restrict builds to a selection of architectures.  (This option is\n\
+       persistent.  An `--arch' command-line option takes precedence,\n\
+       though.)\n"
+   };
+   static cmd cmd_all = {
+     &cmd_only, "all-arch", sw_all,
+"all-arch\n\
+       Clears the `only-arch' list.  Subsequent builds occur across all\n\
+       architectures.\n"
+   };
+   static cmd cmd_host = {
+     &cmd_all, "host", sw_host,
+"host ARCH\n\
+       Display the name of a host supporting architecture ARCH.\n"
+   };
+   static cmd cmd_listarch = {
+     &cmd_host, "listarch", sw_listarch,
+"listarch\n\
+       Display a list of all architectures known.\n"
+   };
+   static cmd cmd_arch = {
+     &cmd_listarch, "arch", sw_arch,
+"arch\tDisplay the architecture name of the current host.\n"
+   };
+#  undef CMD_LINK
+#  define CMD_LINK &cmd_arch
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/src/sw_build.c b/src/sw_build.c
new file mode 100644 (file)
index 0000000..3e8476a
--- /dev/null
@@ -0,0 +1,606 @@
+/* -*-c-*-
+ *
+ * $Id: sw_build.c,v 1.1 1999/06/02 16:53:34 mdw Exp $
+ *
+ * Management of build processes
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_build.c,v $
+ * Revision 1.1  1999/06/02 16:53:34  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#ifndef DECL_ENVIRON
+  extern char **environ;
+#endif
+
+#include <mLib/alloc.h>
+#include <mLib/dstr.h>
+#include <mLib/exc.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+
+#include "sw.h"
+#include "sw_arch.h"
+#include "sw_build.h"
+#include "sw_info.h"
+#include "sw_rsh.h"
+
+#define PRES_LINK 0
+#define PRES_DEFAULT 0
+#define PRES_PREFERENCE 0
+#include "pres_plain.h"
+#include "pres_curses.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+/*----- Static variables --------------------------------------------------*/
+
+static pres *preslist = PRES_LINK;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @swbuild_archlist@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to the build information block
+ *
+ * Returns:    A list of architectures which are to be built.
+ *
+ * Use:                Decides which architectures need building, and returns them
+ *             in a list.
+ */
+
+archcons *swbuild_archlist(swinfo *sw)
+{
+  archcons *a = arch_readtab();
+  const char *only = 0;
+  unsigned and = 0, xor = 0;
+
+  /* --- Restrict the architecture list appropriately --- */
+
+  only = opt_arch ? opt_arch : sw->only_arch;
+
+  /* --- Apply the built flags --- */
+
+  if (!(opt_flags & optFlag_force) && sw->arch) {
+    archcons *aa = arch_filter(a, sw->arch, 0, 0);
+    archcons *c;
+    for (c = aa; c; c = c->cdr)
+      c->car->flags |= archFlag_built;
+    arch_free(aa);
+    and |= archFlag_built;
+  }
+
+  return (arch_filter(a, only, and, xor));
+}
+
+/*----- Main build command ------------------------------------------------*/
+
+/* --- @sw_run@ --- *
+ *
+ * Arguments:  @int argc@ = number of command line arguments
+ *             @char *argv[]@ = array of command line arguments
+ *
+ * Returns:    Zero on success (all builds OK) or nonzero for failure.
+ *
+ * Use:                Runs a multi-architecture build.
+ */
+
+int sw_run(int argc, char *argv[])
+{
+  swinfo sw;
+  archcons *a;
+  pres *p = PRES_DEFAULT;
+  fd_set fdin;
+  int active = 0;
+  int maxfd = 0;
+  int rc = 0;
+
+  /* --- Handle help on output styles --- */
+
+  if (opt_output && strcmp(opt_output, "help") == 0) {
+    printf("Presentation styles supported:");
+    for (p = preslist; p; p = p->next)
+      printf(" %s", p->name);
+    putc('\n', stdout);
+    printf("The default presentation style is %s\n", (PRES_DEFAULT)->name);
+    exit(0);
+  } 
+
+  /* --- Validate arguments --- */
+
+  if (!argv[1])
+    die(1, "Usage: run COMMAND [ARG...]");
+
+  /* --- Choose an output presentation style --- */
+
+  if (opt_output) {
+    pres *q;
+    size_t sz = strlen(opt_output);
+    p = 0;
+
+    for (q = preslist; q; q = q->next) {
+      if (strncmp(opt_output, q->name, sz) == 0) {
+       if (q->name[sz] == 0) {
+         p = q;
+         break;
+       } else if (p)
+         die(1, "ambiguous output style `%s'", opt_output);
+       else
+         p = q;
+      }
+    }
+
+    if (!p)
+      die(1, "unknown output style `%s'", opt_output);
+  }
+
+  if (p->ok && !p->ok()) {
+    moan("output style `%s' can't run; using `plain' instead", p->name);
+    p = &pres_plain;
+  }
+
+  /* --- Decide on an architecture --- */
+
+  if (swinfo_fetch(&sw)) {
+    die(1, "couldn't read build status: %s (try running setup)",
+       strerror(errno));
+  }
+  swinfo_sanity(&sw);
+  a = swbuild_archlist(&sw);
+
+  if (!a) {
+    moan("All desired architectures already built OK.");
+    moan("(Perhaps you forgot `--force', or want to say `%s reset'.)", QUIS);
+    return (0);
+  }
+
+  /* --- Tie on remote context blocks, and crank up the presentation --- */
+
+  {
+    archcons *aa;
+
+    for (aa = a; aa; aa = aa->cdr)
+      aa->car->r = xmalloc(sizeof(sw_remote));
+    if (p->init && p->init(a))
+      die(1, "presentation style refused to start: %s", strerror(errno));
+  }
+
+  signal(SIGINT, SIG_IGN);
+  signal(SIGQUIT, SIG_IGN);
+
+  /* --- Trap any exceptions coming this way --- *
+   *
+   * It's important, for example, that a curses-based presentation system
+   * reset the terminal flags appropriately.
+   */
+
+  TRY {
+    /* --- Run remote build processes on the remote hosts --- */
+
+    {
+      archcons *aa;
+
+      FD_ZERO(&fdin);
+      for (aa = a; aa; aa = aa->cdr) {
+       archent *e = aa->car;
+       sw_remote *r = e->r;
+       if (swrsh(r, e->flags & archFlag_home ? 0 : e->host,
+                 "build", argv + 1)) {
+         dstr d = DSTR_INIT;
+         dstr_putf(&d, "%s: couldn't start build for architecture `%s': %s",
+                   QUIS, e->arch, strerror(errno));
+         p->output(e, d.buf, d.len);
+         free(r);
+         e->r = 0;
+         dstr_destroy(&d);
+       } else {
+         int fd = r->fdin;
+         if (fd > maxfd)
+           maxfd = fd;
+         active++;
+         FD_SET(fd, &fdin);
+       }
+      }
+    }
+
+    /* --- Watch the builds until they do something interesting --- */
+
+    maxfd++;
+    while (active) {
+      fd_set f;
+      int n;
+      archcons *aa;
+
+      /* --- Find out what interesting things are happening --- */
+
+      memcpy(&f, &fdin, sizeof(f));
+      n = select(maxfd, &f, 0, 0, 0);
+      if (n < 0) {
+       if (errno == EINTR || errno == EAGAIN)
+         continue;
+       else
+         THROW(EXC_ERRNO, errno);
+      }
+
+      /* --- Scan through for jobs which need attention --- */
+
+      for (aa = a; aa; aa = aa->cdr) {
+       archent *e = aa->car;
+       sw_remote *r = e->r;
+       int t;
+
+       if (!FD_ISSET(r->fdin, &f))
+         continue;
+
+       switch (t = pkrecv(r)) {
+
+         case PKTYPE_DATA:
+           p->output(e, r->buf, r->sz);
+           break;
+
+         case PKTYPE_STATUS: {
+           dstr d = DSTR_INIT;
+           int ok = 1;
+           if (r->sz != 1) {
+             r->buf[r->sz] = 0;
+             dstr_putf(&d, "\nTerminated by signal: %s.\n", r->buf);
+             ok = 0;
+             rc = 1;
+           } else if (r->buf[0]) {
+             dstr_putf(&d, "\nExited with status %u.\n",
+                       (unsigned char)r->buf[0]);
+             ok = 0;
+             rc = 1;
+           } else if (opt_flags & optFlag_install)
+             e->flags |= archFlag_built;
+           if (d.len)
+             p->output(e, d.buf, d.len);
+           dstr_destroy(&d);
+           if (p->close)
+             p->close(e, ok);
+           FD_CLR(r->fdin, &fdin);
+           close(r->fdin);
+           active--;
+         } break;
+
+         case PKTYPE_EOF: {
+           const static char msg[] = "\nUnexpected exit.\n";
+           p->output(e, msg, sizeof(msg) - 1);
+           if (p->close)
+             p->close(e, 0);
+           rc = 1;
+           FD_CLR(r->fdin, &fdin);
+           close(r->fdin);
+           active--;
+         } break;
+
+         default: {
+           const static char msg[] = "\n[Unexpected packet, type %i]\n";
+           p->output(e, msg, sizeof(msg) - 1);
+         } break;
+       }
+      }
+    }
+  }
+
+  /* --- Handle any exceptions coming this way --- *
+   *
+   * I could do more cleanup here (freeing buffers and so) but it's not worth
+   * it.  Nobody's bothering to catch exceptions anyway.
+   */
+
+  CATCH {
+    if (p->abort)
+      p->abort(a);
+    switch (exc_type) {
+      case EXC_ERRNO:
+       die(1, "unexpected error: %s", exc_i);
+       break;
+      default:
+       RETHROW;
+       break;
+    }
+  } END_TRY;
+
+  /* --- Tell the presentation that everything's done --- */
+
+  if (p->done)
+    p->done(a);
+  else if (opt_flags & optFlag_beep)
+    putchar('\a');
+
+  /* --- Clean up the unwanted remote contexts --- */
+
+  {
+    archcons *aa;
+    for (aa = a; aa; aa = aa->cdr)
+      free(a->car->r);
+  }
+
+  /* --- Tidy away the architecture list --- */
+
+  arch_free(a);
+
+  /* --- Mark built architectures as having been completed now --- */
+
+  if (opt_flags & optFlag_install) {
+    swinfo skel;
+    dstr d = DSTR_INIT;
+    swinfo_clear(&skel);
+    arch_toText(&d, arch_readtab(), archFlag_built, archFlag_built);
+    skel.arch = d.buf;
+    swinfo_update(&sw, &skel);
+    dstr_destroy(&d);
+    if (swinfo_put(&sw))
+      die(1, "error writing build status: %s", strerror(errno));
+  }
+  return (rc);
+}
+
+/*----- Main remote entry point -------------------------------------------*/
+
+/* --- @putf@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = pointer to remote context
+ *             @FILE *fp@ = log file handle
+ *             @const char *fmt@ = format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Reports a string to the log file and the remote controller
+ *             process.
+ */
+
+static void putf(sw_remote *r, FILE *fp, const char *fmt, ...)
+{
+  va_list ap;
+  dstr d = DSTR_INIT;
+  va_start(ap, fmt);
+  dstr_vputf(&d, fmt, ap);
+  va_end(ap);
+  if (r)
+    pksend(r, PKTYPE_DATA, d.buf, d.len);
+  if (fp)
+    fwrite(d.buf, 1, d.len, fp);
+  dstr_destroy(&d);
+}
+
+/* --- @swrsh_build@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = pointer to remote context
+ *             @char *argv[]@ = pointer to argument list
+ *             @char *env[]@ = pointer to environment list
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Runs a remote build command.
+ */
+
+void swrsh_build(sw_remote *r, char *argv[], char *env[])
+{
+  FILE *logfp;
+  int fd[2];
+  pid_t kid;
+
+  /* --- Validate arguments --- */
+
+  if (!argv[0])
+    swdie(r, 1, "Usage: build COMMAND [ARG...]");
+
+  /* --- Change into architecture directory --- */
+
+  if (mkdir(ARCH, 0775) && errno != EEXIST)
+    swdie(r, 1, "mkdir(`%s') failed: %s", ARCH, strerror(errno));
+  if (chdir(ARCH)) {
+    swdie(r, 1, "couldn't change directory to `%s': %s",
+         ARCH, strerror(errno));
+  }
+  if (pipe(fd))
+    swdie(r, 1, "couldn't create pipe: %s", strerror(errno));
+
+  /* --- Open the log file --- */
+
+  {
+    int logfd = open(".build-log", O_WRONLY | O_APPEND | O_CREAT, 0664);
+    time_t t;
+    struct tm *tm;
+    char buf[64];
+    char **p;
+
+    if (logfd < 0)
+      swdie(r, 1, "couldn't open `.build-log' file: %s", strerror(errno));
+    if ((logfp = fdopen(logfd, "a")) == 0) {
+      swdie(r, 1, "couldn't open stream on `.build-log' file: %s",
+           strerror(errno));
+    }
+    t = time(0);
+    tm = localtime(&t);
+    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
+    fprintf(logfp, "\n\n*** %s: started build: %s", buf, argv[0]);
+    for (p = argv + 1; *p; p++)
+      fprintf(logfp, " %s", *p);
+    fputs("\n\n", logfp);
+  }
+
+  /* --- Start off the child process --- */
+
+  kid = fork();
+  if (kid == 0) {
+    int nullfd;
+    close(fd[0]);
+    dup2(fd[1], 1);
+    dup2(fd[1], 2);
+    if (fd[1] > 2)
+      close(fd[1]);
+    close(0);
+    nullfd = open("/dev/null", O_RDONLY);
+    if (nullfd > 0) {
+      dup2(nullfd, 0);
+      close(nullfd);
+    }
+    environ = env;
+    execvp(argv[0], argv);
+    fprintf(stderr, "failed to start `%s': %s\n", argv[0], strerror(errno));
+    _exit(127);
+  }
+
+  /* --- Read from the pipe, and write to the socket and logfile --- */
+
+  close(fd[1]);
+  for (;;) {
+    ssize_t n = read(fd[0], r->buf, PKMAX);
+    if (!n)
+      break;
+    if (n < 0) {
+      putf(r, logfp, "*** error reading from pipe: %s\n", strerror(errno));
+      kill(kid, SIGTERM);
+      break;
+    }
+    fwrite(r->buf, 1, n, logfp);
+    pksend(r, PKTYPE_DATA, r->buf, n);
+  }
+  close(fd[0]);
+
+  /* --- Reap exit status and produce final report --- */
+
+  {
+    int status;
+    if (waitpid(kid, &status, 0) < 0)
+      putf(r, logfp, "*** error reading exit status: %s\n", strerror(errno));
+    else {
+      if (WIFSIGNALED(status))
+       fprintf(logfp, "*** exited on signal %i\n", WTERMSIG(status));
+      else if (WIFEXITED(status))
+       fprintf(logfp, "*** exited with status %i\n", WEXITSTATUS(status));
+      else
+       fprintf(logfp, "*** reaped, but didn't exit.  Strange\n");
+    }
+    fclose(logfp);
+    swwait(r, status);
+  }
+}
+
+/*----- Syntactic sugar ---------------------------------------------------*/
+
+/*
+ * `Syntactic sugar causes cancer of the semicolon.'
+ *             -- Alan Perlis, `Epigrams in Programming'
+ */
+
+/* --- @build@ --- *
+ *
+ * Arguments:  @char **u@ = first segment
+ *             @char **v@ = second segment
+ *
+ * Returns:    Return code from the build.
+ *
+ * Use:                Combines two @argv@-style arrays and then runs a build on the
+ *             result.
+ */
+
+static int build(char **u, char **v)
+{
+  size_t i, j;
+  char **p;
+
+  for (i = 0, p = u; *p; p++, i++) ;
+  for (j = 0, p = v; *p; p++, j++) ;
+  p = xmalloc((i + j + 2) * sizeof(char **));
+  memcpy(p + 1, u, i * sizeof(char **));
+  memcpy(p + i + 1, v, j * sizeof(char **));
+  p[0] = p[i + j + 1] = 0;
+  return (sw_run(i + j + 1, p));
+}
+
+/* --- @sw_make@ --- */
+
+int sw_make(int argc, char *argv[])
+{
+  static char *mk[] = { 0, 0 };
+  if (!mk[0]) {
+    char *m;
+    if ((m = getenv("SW_MAKE")) == 0 &&
+       (m = getenv("MAKE")) == 0)
+      m = "make";
+    mk[0] = m;
+  }
+  return (build(mk, argv + 1));
+}
+
+/* --- @sw_conf@ --- */
+
+int sw_conf(int argc, char *argv[])
+{
+  static char *cf[] = { "../configure", "--prefix=" PREFIX, 0 };
+  return (build(cf, argv + 1));
+}
+
+/*----- Other subcommands -------------------------------------------------*/
+
+/* --- @sw_reset@ --- */
+
+int sw_reset(int argc, char *argv[])
+{
+  swinfo sw, skel;
+  if (argc != 1)
+    die(1, "Usage: reset");
+  if (swinfo_fetch(&sw)) {
+    die(1, "couldn't read build status: %s (try running setup)",
+       strerror(errno));
+  }
+  swinfo_sanity(&sw);
+  swinfo_clear(&skel);
+  skel.arch = "";
+  swinfo_update(&sw, &skel);
+  if (swinfo_put(&sw))
+    die(1, "couldn't write build status: %s", strerror(errno));
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/sw_build.h b/src/sw_build.h
new file mode 100644 (file)
index 0000000..f61b460
--- /dev/null
@@ -0,0 +1,202 @@
+/* -*-c-*-
+ *
+ * $Id: sw_build.h,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Management of build processes
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_build.h,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SW_BUILD_H
+#define SW_BUILD_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef SW_ARCH_H
+#  include "sw_arch.h"
+#endif
+
+#ifndef SW_INFO_H
+#  include "sw_info.h"
+#endif
+
+#ifndef SW_RSH_H
+#  include "sw_rsh.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- Information about a presentation handler --- *
+ *
+ * The @pres@ structure includes everything you need to know about a
+ * presentation handler.
+ *
+ * Presentation handler blocks are linked into a list, in a similar way to
+ * commands.  (See the `sw.h' header for information on this.)  The magic
+ * link macro is @PRES_LINK@.
+ *
+ * The `name' field is used to resolve the `--output=' option argument into a
+ * presentation.  Make the first few characters distinctive: it uses
+ * abbreviation-matching to choose a presentation style.
+ *
+ * The various routines work like this:
+ *
+ *   * @ok@ is used to find out whether the handler can *in principle* run in
+ *     the current environment.  For example, a presentation handler which
+ *     makes use of special terminal features would ensure that standard
+ *     output is a terminal; similarly, an X-based handler would ensure that
+ *     there is a current display set.  If the handler responds `false' to
+ *     this request, the `plain' handler is chosen instead.  A zero value
+ *     means that it's always OK to use this handler.
+ *
+ *  * @init@ is used to initialize the chosen handler.  It returns zero for
+ *    success, nonzero for failure.  A failure at this point is a fatal
+ *    error: the decision about which handler to run has already been made.
+ *    The function is passed the build list as an argument.  Entries with
+ *    null remote contexts are not to be built and can be ignored here.  Note
+ *    that the actual build jobs have not been started yet.  They're left
+ *    until last because they're the most difficult things to stop.  (Indeed,
+ *    if some don't start, the others are left to run anyway.)  A zero value
+ *    means this handler needs no initialization.
+ *
+ *  * @output@ reports new output for a particular build: a pointer to the
+ *    appropriate archtecture entry is passed.  The handler should format the
+ *    text in the buffer in some pleasing and useful manner, and then return.
+ *    Any errors which occur at this point are the handler's own problem to
+ *    sort out.  It is not permitted for a handler to have a null output
+ *    routine.
+ *
+ *  * @close@ informs the handler that a particular build has completed.  The
+ *    @ok@ argument is nonzero if the build was successful, and zero
+ *    otherwise.  (A message describing the failure will have been
+ *    synthetically output before this point, so only a small visual cue is
+ *    needed here.)  A zero value means this handler is not interested in
+ *    close events.
+ *
+ *  * @done@ informs the handler that all builds have completed.  The handler
+ *    should make sure that the display will remain as long as is needed, and
+ *    deallocate any resources it obtained eariler.  A zero value means the
+ *    handler needs no cleanup and its output is persistent anyway.
+ *
+ *  * @abort@ cleans up immediately, because something went horribly wrong.
+ *    This shouldn't happen very often, but when it is, it's normally
+ *    followed by a call to @die@.  A zero value means that this sort of
+ *    thing isn't necessary.
+ *
+ * That's all there is.  Any questions?
+ *
+ * (The current structure doesn't interface very well with X toolkits.  Some
+ * kind of call-tree inverstion would be handy for that, although I don't
+ * want to make all the handlers able to cope with `select' management.  This
+ * needs thought, if I ever decide to add an X interface here.)
+ */
+
+typedef struct pres {
+  struct pres *next;
+  const char *name;
+  int (*ok)(void);
+  int (*init)(archcons */*a*/);
+  void (*output)(archent */*e*/, const char */*p*/, size_t /*sz*/);
+  void (*close)(archent */*e*/, int /*ok*/);
+  void (*done)(archcons */*a*/);
+  void (*abort)(archcons */*a*/);
+} pres;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @swbuild_archlist@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to the build information block
+ *
+ * Returns:    A list of architectures which are to be built.
+ *
+ * Use:                Decides which architectures need building, and returns them
+ *             in a list.
+ */
+
+extern archcons *swbuild_archlist(swinfo */*sw*/);
+
+/*----- Subcommands -------------------------------------------------------*/
+
+extern int sw_run(int /*argc*/, char */*argv*/[]);
+extern int sw_make(int /*argc*/, char */*argv*/[]);
+extern int sw_conf(int /*argc*/, char */*argv*/[]);
+extern int sw_reset(int /*argc*/, char */*argv*/[]);
+extern void swrsh_build(sw_remote */*r*/, char */*argv*/[], char */*env*/[]);
+
+#ifdef CMD_LINK
+   static cmd cmd_reset = {
+     CMD_LINK, "reset", sw_reset,
+"reset\tClears all the architecture build flags, so that all architectures\n\
+       will be built next time.  This is useful just before a call to\n\
+       `sw make clean', for example.\n"
+   };
+   static cmd cmd_run = {
+     &cmd_reset, "run", sw_run,
+"run COMMAND [ARGS...]\n\
+       Run COMMAND across all architectures that haven't been built yet,\n\
+       in architecture-specific subdirectories.  The output is written to\n\
+       logfiles in the subdirectories, and displayed in some way by the\n\
+       selected output style.\n"
+   };
+   static cmd cmd_make = {
+     &cmd_run, "make", sw_make,
+"make [ARGS...]\n\
+       Syntactic sugar for `run make ARGS'.  The `make' program named in\n\
+       the environment variable `SW_MAKE' is used by preference.\n"
+   };
+   static cmd cmd_conf = {
+     &cmd_make, "configure", sw_conf,
+"configure [ARGS...]\n\
+       Syntactic sugar for `run ../configure --prefix=PREFIX'.  Put other\n\
+       autoconf site configuration in the `config.site' script.  The\n\
+       `prefix' value used is " PREFIX ".\n"
+   };
+#  undef CMD_LINK
+#  define CMD_LINK &cmd_conf
+#endif
+
+#ifdef RCMD_LINK
+   static rcmd rcmd_build = { RCMD_LINK, "build", swrsh_build };
+#  undef RCMD_LINK
+#  define RCMD_LINK &rcmd_build
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/src/sw_env.c b/src/sw_env.c
new file mode 100644 (file)
index 0000000..797a4ea
--- /dev/null
@@ -0,0 +1,776 @@
+/* -*-c-*-
+ *
+ * $Id: sw_env.c,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Mangling of environment variables
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_env.c,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <sys/wait.h>
+
+#ifndef DECL_ENVIRON
+  extern char **environ;
+#endif
+
+#include <mLib/alloc.h>
+#include <mLib/dstr.h>
+#include <mLib/report.h>
+#include <mLib/sym.h>
+
+#include "sw_env.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct var {
+  sym_base _base;
+  char *v;
+} var;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @env_get@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to a symbol table
+ *             @const char *name@ = pointer to variable name to look up
+ *
+ * Returns:    Pointer to corresponding value string, or null.
+ *
+ * Use:                Looks up an environment variable in the table and returns its
+ *             value.  If the variable can't be found, a null pointer is
+ *             returned.
+ */
+
+char *env_get(sym_table *t, const char *name)
+{
+  var *e = sym_find(t, name, -1, 0, 0);
+  return (e ? e->v : 0);
+}
+
+/* --- @env_put@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to a symbol table
+ *             @const char *name@ = pointer to variable name to set
+ *             @const char *value@ = pointer to value string to assign
+ *
+ * Returns:    ---
+ *
+ * Use:                Assigns a value to a variable.  If the @name@ contains an
+ *             equals character, then it's assumed to be of the form
+ *             `VAR=VALUE' and @value@ argument is ignored.  Otherwise, if
+ *             @value@ is null, the variable is deleted.  Finally, the
+ *             normal case: @name@ is a plain name, and @value@ is a normal
+ *             string causes the variable to be assigned the value in the
+ *             way you'd expect.
+ */
+
+void env_put(sym_table *t, const char *name, const char *value)
+{
+  char *q = 0;
+
+  /* --- Sort out the mess with `NAME=VALUE' forms --- */
+
+  {
+    size_t eq = strcspn(name, "=");
+    if (name[eq] == '=') {
+      q = xmalloc(eq + 1);
+      memcpy(q, name, eq);
+      q[eq] = 0;
+      value = name + eq + 1;
+      name = q;
+    }
+  }
+
+  /* --- Read the current value --- */
+
+  if (!value) {
+    var *v;
+    if ((v = sym_find(t, name, -1, 0, 0)) != 0) {
+      free(v->v);
+      sym_remove(t, v);
+    }
+  } else {
+    unsigned found;
+    var *v = sym_find(t, name, -1, sizeof(*v), &found);
+    if (found)
+      free(v->v);
+    v->v = xstrdup(value);
+  }
+
+  /* --- Tidying --- */
+
+  if (q)
+    free(q);
+}
+
+/* --- @env_import@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to a symbol table
+ *             @char **env@ = pointer to an environment list
+ *
+ * Returns:    ---
+ *
+ * Use:                Inserts all of the environment variables listed into a symbol
+ *             table for rapid access.  Equivalent to a lot of calls to
+ *             @env_put@.
+ */
+
+void env_import(sym_table *t, char **env)
+{
+  while (*env) {
+    env_put(t, *env, 0);
+    env++;
+  }
+}
+
+/* --- @env_export@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to a symbol table
+ *
+ * Returns:    A big environment list.
+ *
+ * Use:                Extracts an environment table from a symbol table
+ *             representation of an environment.  The table and all of the
+ *             strings are in one big block allocated from the heap.
+ */
+
+char **env_export(sym_table *t)
+{
+  size_t n = 1;
+  size_t sz = 0;
+  sym_iter i;
+  var *v;
+  char **env;
+  char *p, **pp;
+
+  /* --- Work out sizes for everything --- */
+
+  for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) {
+    n++;
+    sz += strlen(SYM_NAME(v)) + strlen(v->v) + 2;
+  }
+
+  /* --- Allocate the big chunk of memory --- */
+
+  env = pp = xmalloc(n * sizeof(char *) + sz);
+  p = (char *)(env + n);
+
+  /* --- Dump the output in the big chunk of memory --- */
+
+  for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) {
+    const char *name = SYM_NAME(v);
+    size_t nlen = strlen(name), vlen = strlen(v->v);
+    *pp++ = p;
+    memcpy(p, name, nlen); p += nlen;
+    *p++ = '=';
+    memcpy(p, v->v, vlen); p += vlen;
+    *p++ = 0;
+  }
+  *pp++ = 0;
+  return (env);
+}
+
+/* --- @env_destroy@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys all the variables in the symbol table.
+ */
+
+void env_destroy(sym_table *t)
+{
+  sym_iter i;
+  var *v;
+
+  for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; )
+    free(v->v);
+  sym_destroy(t);
+}
+
+/* --- @env_error@ --- *
+ *
+ * Arguments:  @int e@ = error code
+ *
+ * Returns:    String representation of error.
+ *
+ * Use:                Transforms an error into something a user can understand.
+ */
+
+const char *env_error(int e)
+{
+  const char *tab[] = {
+    "Everything is fine",
+    "Unexpected end-of-file",
+    "Bad character in variable name",
+    "Bad parameter substitution",
+    "Mismatched quote",
+    "Missing or spurious `}'",
+    "<see errno>",
+    "Internal error"
+  };
+  if (e == ENV_SYSTEM)
+    return (strerror(errno));
+  else
+    return (tab[e]);
+}
+
+/* --- @peek@ --- *
+ *
+ * Arguments:  @FILE *fp@ = stream to read from
+ *
+ * Returns:    Next nonwhitespace character.
+ *
+ * Use:                Advances the file position past whitespace characters, and
+ *             returns the following nonwhitespace character.  The character
+ *             is not `read'.
+ */
+
+static int peek(FILE *fp)
+{
+  int ch;
+
+  do {
+    ch = getc(fp);
+    if (ch == EOF)
+      return (EOF);
+  } while (isspace((unsigned char)ch));
+  ungetc(ch, fp);
+  return (ch);
+}
+
+/* --- @env_var@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @FILE *fp@ = pointer to stream to read from
+ *             @dstr *d@ = pointer to output variable
+ *
+ * Returns:    One of the @ENV_@ constants.
+ *
+ * Use:                Scans a variable name from the input stream.
+ */
+
+int env_var(sym_table *t, FILE *fp, dstr *d)
+{
+  int ch = getc(fp);
+
+  if (ch == EOF)
+    return (ENV_EOF);
+  if (ch != '_' && !isalpha((unsigned char)ch))
+    return (ENV_VCHAR);
+  for (;;) {
+    DPUTC(d, ch);
+    ch = getc(fp);
+    if (ch == EOF || (ch != '_' && !isalnum((unsigned char)ch)))
+      break;
+  }
+  ungetc(ch, fp);
+  DPUTZ(d);
+  return (ENV_OK);
+}
+
+/* --- @cmdsubst@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @FILE *fp@ = pointer to stream to read from
+ *             @dstr *d@ = pointer to output variable
+ *             @unsigned f@ = interesting flags
+ *
+ * Returns:    An @ENV_@ magic code.
+ *
+ * Use:                Rips a command line out of the input stream and writes the
+ *             output of the command to the variable.  The parsing has some
+ *             bizarre artifacts, but it's fairly serviceable.
+ */
+
+static int cmdsubst(sym_table *t, FILE *fp, dstr *d, unsigned f)
+{
+  int term = (f & EVF_BACKTICK) ? '`' : ')';
+  int argc = 1;
+  size_t l = d->len;
+  pid_t kid;
+  int fd[2];
+  int e;
+  char **argv;
+
+  /* --- Snarfle the arguments --- */
+
+  f &= ~EVF_INCSPC;
+  while (peek(fp) != term) {
+    if ((e = env_value(t, fp, d, f)) != ENV_OK)
+      return (e);
+    DPUTC(d, 0);
+    argc++;
+  }
+  getc(fp);
+  if (argc == 1) {
+    d->len = l;
+    return (ENV_OK);
+  }
+
+  /* --- Make the @argv@ array --- */
+
+  {
+    char *p = d->buf + l;
+    char *lim = d->buf + d->len;
+    char **v;
+
+    v = argv = xmalloc(argc * sizeof(char *));
+    while (p < lim) {
+      *v++ = p;
+      while (*p) {
+       p++;
+       if (p >= lim)
+         goto done;
+      }
+      p++;
+    }
+  done:;
+    *v++ = 0;
+  }
+
+  /* --- Do the fork/exec thing --- */
+
+  if (pipe(fd))
+    goto fail_0;
+  if ((kid = fork()) < 0)
+    goto fail_1;
+
+  if (kid == 0) {
+    close(fd[0]);
+    if (fd[1] != 1) {
+      dup2(fd[1], 1);
+      close(fd[1]);
+    }
+    environ = env_export(t);
+    execvp(argv[0], argv);
+    _exit(127);
+  }
+
+  d->len = l;
+  close(fd[1]);
+  for (;;) {
+    char buf[4096];
+    ssize_t n = read(fd[0], buf, sizeof(buf));
+    if (n <= 0)
+      break;
+    DPUTM(d, buf, n);
+  }
+  close(fd[0]);
+  waitpid(kid, 0, 0);
+  l = d->len;
+  while (l > 0 && d->buf[l - 1] == '\n')
+    l--;
+  d->len = l;
+  free(argv);
+  return (ENV_OK);
+
+fail_1:
+  close(fd[0]);
+  close(fd[1]);
+fail_0:
+  free(argv);
+  return (ENV_SYSTEM);
+}
+  
+/* --- @env_value@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @FILE *fp@ = pointer to stream to read from
+ *             @dstr *d@ = pointer to output variable
+ *             @unsigned f@ = various interesting flags
+ *
+ * Returns:    0 if OK, @EOF@ if end-of-file encountered, or >0 on error.
+ *
+ * Use:                Scans a value from the input stream.  The value read may be
+ *             quoted in a Bourne-shell sort of a way, and contain Bourne-
+ *             shell-like parameter substitutions.  Some substitutions
+ *             aren't available because they're too awkward to implement.
+ */
+
+int env_value(sym_table *t, FILE *fp, dstr *d, unsigned f)
+{
+  enum { Q_NONE, Q_SINGLE, Q_DOUBLE, Q_BACK } qt = Q_NONE;
+  int ch;
+
+  do {
+    ch = getc(fp);
+    if (ch == EOF)
+      return (ENV_EOF);
+  } while ((f & EVF_INITSPC) && isspace((unsigned char)ch));
+
+  for (;; ch = getc(fp)) {
+
+    /* --- Sort out the current character --- */
+
+    if (ch == EOF) break;
+
+    /* --- A backslash escapes the next character --- */
+
+    if (ch == '\\') {
+      if ((ch = getc(fp)) == EOF) break;
+      else if (ch != '\n')
+       DPUTC(d, ch);
+      continue;
+    }
+
+    /* --- A single quote starts single-quoting, unless quoted --- *
+     *
+     * Do the single-quoted snarf here rather than fiddling with anything
+     * else.
+     */
+
+    if (ch == '\'' && qt == Q_NONE) {
+      qt = Q_SINGLE;
+      for (;;) {
+       if ((ch = getc(fp)) == EOF) goto done;
+       if (ch == '\'') break;
+       DPUTC(d, ch);
+      }
+      qt = Q_NONE;
+      continue;
+    }
+
+    /* --- A backtick does the obvious thing --- */
+
+    if (ch == '`' && !(f & EVF_BACKTICK)) {
+      int e;
+      if ((e = cmdsubst(t, fp, d, f | EVF_BACKTICK)) != ENV_OK)
+       return (e);
+      continue;
+    }
+
+    /* --- Handle double-quoted text --- */
+
+    if (ch == '\"') {
+      if (qt == Q_DOUBLE)
+       qt = Q_NONE;
+      else if (qt == Q_NONE)
+       qt = Q_DOUBLE;
+      else
+       return (ENV_INTERNAL);
+      continue;
+    }
+
+    /* --- Handle variable references and similar magic --- */
+
+    if (ch == '$') {
+      size_t l = d->len;
+      int e;
+      char *v;
+      char *vn;
+
+      /* --- Read one character ahead --- */
+
+      if ((ch = getc(fp)) == EOF) goto done;
+
+      /* --- An alphabetic means this is a direct reference --- */
+
+      if (ch == '_' || isalpha(ch)) {
+       ungetc(ch, fp);
+       if ((e = env_var(t, fp, d)) != ENV_OK) return (e);
+       d->len = l;
+       if ((v = env_get(t, d->buf + l)) != 0)
+         DPUTS(d, v);
+      }
+
+      /* --- A brace means this is a more complex substitution --- */
+
+      else if (ch == '{') {
+       if ((e = env_var(t, fp, d)) != ENV_OK)
+         return (e);
+       d->len = l;
+       v = env_get(t, d->buf + l);
+
+      again:
+       ch = getc(fp);
+       switch (ch) {
+
+         case EOF:
+           goto done;
+
+         case '}':
+           if (v)
+             DPUTS(d, v);
+           ungetc(ch, fp);
+           break;
+
+         case ':':
+           if (v && !*v)
+             v = 0;
+           goto again;                 /* `::'  and `:}' should be errors */
+
+         case '+':
+           if (v)
+             v = 0;
+           else
+             v = "";
+           /* Drop through hackily */
+
+         case '-':
+           if (v) {
+             DPUTS(d, v);
+             l = d->len;
+           }
+           if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
+             return (e);
+           if (v)
+             d->len = l;
+           break;
+
+         case '=':
+           if (v) {
+             DPUTS(d, v);
+             l = d->len;
+             if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
+               return (e);
+             d->len = l;
+           } else {
+             vn = xstrdup(d->buf + l);
+             if ((e = env_value(t, fp, d, EVF_INCSPC)) != ENV_OK)
+               return (e);
+             if (!(f & EVF_SKIP))
+               env_put(t, vn, d->buf + l);
+             free(vn);
+           }
+           break;
+
+         default:
+           return (ENV_SUBST);
+       }
+       if (getc(fp) != '}')
+         return (ENV_BRACE);
+      }
+
+      /* --- Handle `$(...)'-style command substitution --- */
+
+      else if (ch == '(') {
+       if ((e = cmdsubst(t, fp, d, f & ~EVF_BACKTICK)) != ENV_OK)
+         return (e);
+      }
+
+      /* --- No other `$...' tricks implemented yet --- *
+       *
+       * Other ideas: `$((...))' arithmetic.
+       */
+
+      else
+       return (ENV_SUBST);
+      continue;
+    }
+
+    /* --- At this point, anything else double-quoted is munched --- */
+
+    if (qt == Q_DOUBLE) {
+      DPUTC(d, ch);
+      continue;
+    }
+
+    /* --- Some characters just aren't allowed unquoted --- *
+     *
+     * They're not an error; they just mean I should stop parsing.  They're
+     * probably interesting to the next level up.
+     */
+
+    switch (ch) {
+      case '(': case ')':
+      case '{': case '}':
+      case ';':
+       ungetc(ch, fp);
+       goto done;
+      case '`':
+       if (f & EVF_BACKTICK) {
+         ungetc(ch, fp);
+         goto done;
+       }
+       break;
+    }
+
+    /* --- Whitespace characters --- *
+     *
+     * I might snarf them myself anyway, according to flags.  Or I might
+     * stop, and skip any following whitespace
+     */
+
+    if (isspace((unsigned char)ch) && (f & EVF_INCSPC) == 0) {
+      do
+       ch = getc(fp);
+      while (ch != EOF && isspace((unsigned char)ch));
+      ungetc(ch, fp);
+      break;
+    }
+
+    /* --- Get a new character and go around again --- */
+
+    DPUTC(d, ch);
+  }
+
+  /* --- Tidying --- */
+
+done:
+  DPUTZ(d);
+  return (qt == Q_NONE ? ENV_OK : ENV_QUOTE);
+}
+
+/* --- @env_read@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @FILE *fp@ = file handle to read
+ *             @unsigned f@ = various flags
+ *
+ * Returns:    Zero if OK, @EOF@ for end-of-file, or error code.
+ *
+ * Use:                Reads the environment assignment statements in the file.
+ */
+
+int env_read(sym_table *t, FILE *fp, unsigned f)
+{
+  dstr n = DSTR_INIT, v = DSTR_INIT;
+  int e = ENV_OK, ch;
+
+  for (;;) {
+    ch = peek(fp);
+
+    if (ch == ':') {
+      getc(fp);
+      peek(fp);
+      if ((e = env_value(t, fp, &v, f)) != ENV_OK)
+       goto done;
+    }
+
+    else if (ch == '#')
+      do ch = getc(fp); while (ch != EOF && ch != '\n');
+
+    else if (peek(fp) == '}' ||
+       (e = env_var(t, fp, &n)) != ENV_OK)
+      goto done;
+
+    else if (strcmp(n.buf, "include") == 0) {
+      peek(fp);
+      if ((e = env_value(t, fp, &v, f)) != ENV_OK)
+       goto done;
+      if (!(f & EVF_SKIP))
+       env_file(t, v.buf);
+    }
+
+    else if (strcmp(n.buf, "arch") == 0) {
+      peek(fp);
+      if ((e = env_value(t, fp, &v, f)) != ENV_OK)
+       goto done;
+      if (peek(fp) != '{') {
+       e = ENV_BRACE;
+       goto done;
+      }
+      getc(fp);
+      e = env_read(t, fp, strcmp(v.buf, ARCH) ? f | EVF_SKIP : f);
+      if (e != ENV_OK)
+       goto done;
+      if (getc(fp) != '}') {
+       e = ENV_BRACE;
+       goto done;
+      }
+    }
+
+    else if (strcmp(n.buf, "unset") == 0) {
+      peek(fp);
+      if ((e = env_var(t, fp, &v)) != ENV_OK)
+       goto done;
+      env_put(t, v.buf, 0);
+    }
+
+    else {
+      if (strcmp(n.buf, "set") == 0) {
+       DRESET(&n);
+       peek(fp);
+       if ((e = env_var(t, fp, &n)) != ENV_OK)
+         goto done;
+      }
+      if (peek(fp) == '=') {
+       getc(fp);
+       peek(fp);
+      }
+      if ((e = env_value(t, fp, &v, f)) != ENV_OK)
+       goto done;
+
+      if (!(f & EVF_SKIP))
+       env_put(t, n.buf, v.buf);
+    }
+
+    if (peek(fp) == ';')
+      getc(fp);
+    DRESET(&n);
+    DRESET(&v);
+  }
+
+done:
+  dstr_destroy(&n);
+  dstr_destroy(&v);
+  return (e);
+}
+
+/* --- @env_file@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @const char *name@ = pointer to filename
+ *
+ * Returns:    Zero if OK, or an error code.
+ *
+ * Use:                Reads a named file of environment assignments.
+ */
+
+int env_file(sym_table *t, const char *name)
+{
+  FILE *fp;
+  int e;
+
+  if ((fp = fopen(name, "r")) == 0)
+    return (ENV_SYSTEM);
+  e = env_read(t, fp, 0);
+  fclose(fp);
+  if (e == ENV_EOF)
+    e = ENV_OK;
+  else if (e == ENV_OK)
+    e = ENV_BRACE;
+  return (e);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/sw_env.h b/src/sw_env.h
new file mode 100644 (file)
index 0000000..c14ca1c
--- /dev/null
@@ -0,0 +1,218 @@
+/* -*-c-*-
+ *
+ * $Id: sw_env.h,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Mangling of environment variables
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_env.h,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SW_ENV_H
+#define SW_ENV_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+
+#include <mLib/dstr.h>
+#include <mLib/sym.h>
+
+/*----- Important constants -----------------------------------------------*/
+
+enum {
+  ENV_OK = 0,                          /* Parsed OK */
+  ENV_EOF,                             /* End-of-file encountered */
+  ENV_VCHAR,                           /* Bad character in variable name */
+  ENV_SUBST,                           /* Bad parameter substitution */
+  ENV_QUOTE,                           /* Mismatched quote */
+  ENV_BRACE,                           /* Missing brace character */
+  ENV_SYSTEM,                          /* System error (see @errno@) */
+  ENV_INTERNAL                         /* Internal error */
+};
+
+enum {
+  EVF_INCSPC = 1,                      /* Allow spaces in values */
+  EVF_BACKTICK = 2,                    /* Make backtick self-delimiting */
+  EVF_SKIP = 4,                                /* Skipping this section of text */
+  EVF_INITSPC = 8                      /* Allow and ignore leading space */
+};
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @env_get@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to a symbol table
+ *             @const char *name@ = pointer to variable name to look up
+ *
+ * Returns:    Pointer to corresponding value string, or null.
+ *
+ * Use:                Looks up an environment variable in the table and returns its
+ *             value.  If the variable can't be found, a null pointer is
+ *             returned.
+ */
+
+extern char *env_get(sym_table */*t*/, const char */*name*/);
+
+/* --- @env_put@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to a symbol table
+ *             @const char *name@ = pointer to variable name to set
+ *             @const char *value@ = pointer to value string to assign
+ *
+ * Returns:    ---
+ *
+ * Use:                Assigns a value to a variable.  If the @name@ contains an
+ *             equals character, then it's assumed to be of the form
+ *             `VAR=VALUE' and @value@ argument is ignored.  Otherwise, if
+ *             @value@ is null, the variable is deleted.  Finally, the
+ *             normal case: @name@ is a plain name, and @value@ is a normal
+ *             string causes the variable to be assigned the value in the
+ *             way you'd expect.
+ */
+
+extern void env_put(sym_table */*t*/,
+                   const char */*name*/, const char */*value*/);
+
+/* --- @env_import@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to a symbol table
+ *             @char **env@ = pointer to an environment list
+ *
+ * Returns:    ---
+ *
+ * Use:                Inserts all of the environment variables listed into a symbol
+ *             table for rapid access.  Equivalent to a lot of calls to
+ *             @env_put@.
+ */
+
+extern void env_import(sym_table */*t*/, char **/*env*/);
+
+/* --- @env_export@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to a symbol table
+ *
+ * Returns:    A big environment list.
+ *
+ * Use:                Extracts an environment table from a symbol table
+ *             representation of an environment.  The table and all of the
+ *             strings are in one big block allocated from the heap.
+ */
+
+extern char **env_export(sym_table */*t*/);
+
+/* --- @env_destroy@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys all the variables in the symbol table.
+ */
+
+extern void env_destroy(sym_table */*t*/);
+
+/* --- @env_error@ --- *
+ *
+ * Arguments:  @int e@ = error code
+ *
+ * Returns:    String representation of error.
+ *
+ * Use:                Transforms an error into something a user can understand.
+ */
+
+extern const char *env_error(int /*e*/);
+
+/* --- @env_var@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @FILE *fp@ = pointer to stream to read from
+ *             @dstr *d@ = pointer to output variable
+ *
+ * Returns:    One of the @ENV_@ constants.
+ *
+ * Use:                Scans a variable name from the input stream.
+ */
+
+extern int env_var(sym_table */*t*/, FILE */*fp*/, dstr */*d*/);
+
+/* --- @env_value@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @FILE *fp@ = pointer to stream to read from
+ *             @dstr *d@ = pointer to output variable
+ *             @unsigned f@ = various interesting flags
+ *
+ * Returns:    0 if OK, @EOF@ if end-of-file encountered, or >0 on error.
+ *
+ * Use:                Scans a value from the input stream.  The value read may be
+ *             quoted in a Bourne-shell sort of a way, and contain Bourne-
+ *             shell-like parameter substitutions.  Some substitutions
+ *             aren't available because they're too awkward to implement.
+ */
+
+extern int env_value(sym_table */*t*/, FILE */*fp*/,
+                    dstr */*d*/, unsigned /*f*/);
+
+/* --- @env_read@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @FILE *fp@ = file handle to read
+ *             @unsigned f@ = various flags
+ *
+ * Returns:    Zero if OK, @EOF@ for end-of-file, or error code.
+ *
+ * Use:                Reads the environment assignment statements in the file.
+ */
+
+extern int env_read(sym_table */*t*/, FILE */*fp*/, unsigned /*f*/);
+
+/* --- @env_file@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table
+ *             @const char *name@ = pointer to filename
+ *
+ * Returns:    Zero if OK, or an error code.
+ *
+ * Use:                Reads a named file of environment assignments.
+ */
+
+extern int env_file(sym_table */*t*/, const char */*name*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/src/sw_info.c b/src/sw_info.c
new file mode 100644 (file)
index 0000000..3b4a611
--- /dev/null
@@ -0,0 +1,452 @@
+/* -*-c-*-
+ *
+ * $Id: sw_info.c,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Maintenance of `.sw-info' files
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_info.c,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include <mLib/alloc.h>
+#include <mLib/dstr.h>
+#include <mLib/lock.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+
+#include "sw_arch.h"
+#include "sw_info.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static struct swfield {
+  char *name;
+  size_t off;
+  char *desc;
+} swfields[] = {
+  { "package", offsetof(swinfo, package), "Package" },
+  { "version", offsetof(swinfo, version), "Version" },
+  { "maintainer", offsetof(swinfo, maintainer), "Maintained by" },
+  { "date", offsetof(swinfo, date), "Last modified" },
+  { "only-arch", offsetof(swinfo, only_arch), "Only build for" },
+  { "arch", offsetof(swinfo, arch), "Successfully built" },
+  { 0, 0 }
+};
+typedef struct swfield swfield;
+
+#define SWINFO(sw, off) (*(char **)((char *)sw + off))
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @swinfo_clear@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointe to info block
+ *
+ * Returns:    ---
+ *
+ * Use:                Clears an info block so that it doesn't contain anything.
+ *             This is mainly useful when building skeletons to apply using
+ *             @swinfo_update@.
+ */
+
+void swinfo_clear(swinfo *sw)
+{
+  swfield *f;
+  for (f = swfields; f->name; f++)
+    SWINFO(sw, f->off) = 0;
+} 
+
+/* --- @swinfo_fetch@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to info block to fill in
+ *
+ * Returns:    Zero if OK, else nonzero.
+ *
+ * Use:                Fills in the info block if it can.
+ */
+
+int swinfo_fetch(swinfo *sw)
+{
+  FILE *fp;
+  dstr d = DSTR_INIT;
+  swfield *f;
+  int line = 0;
+
+  /* --- Initialize the various fields --- */
+
+  swinfo_clear(sw);
+
+  /* --- Open the data file --- */
+
+  if ((fp = fopen(".sw-info", "r")) == 0)
+    return (-1);
+
+  /* --- Read the lines from the file --- */
+
+  for (; dstr_putline(&d, fp) != EOF; DRESET(&d)) {
+    char *p = d.buf;
+    char *key, *value;
+    line++;
+
+    /* --- Find the field name --- */
+
+    while (isspace((unsigned char)*p))
+      p++;
+    if (*p == 0 || *p == '#')
+      continue;
+
+    /* --- Find the end of the field name --- */
+
+    key = p;
+    while (*p && *p != '=' && !isspace((unsigned char)*p))
+      p++;
+    
+    /* --- Skip an optional `=' sign --- */
+
+    {
+      char ch = *p;
+      if (ch)
+       *p++ = 0;
+      while (isspace((unsigned char)*p))
+       p++;
+      if (*p == '=' && ch != '=') {
+       p++;
+       while (isspace((unsigned char)*p))
+         p++;
+      }        
+    }
+
+    value = p;
+    
+    /* --- Look up the key in the table --- */
+
+    for (f = swfields; f->name; f++) {
+      if (strcmp(key, f->name) == 0)
+       goto found;
+    }
+    moan("unknown key `%s' at line %i in `.sw-info'", key, line);
+    continue;
+
+    /* --- Put the value in --- */
+
+  found:
+    SWINFO(sw, f->off) = xstrdup(value);
+  }
+
+  fclose(fp);
+  dstr_destroy(&d);
+  return (0);
+}
+
+/* --- @swinfo_sanity@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to an info block
+ *
+ * Returns:    Yes, if the block is OK.
+ *
+ * Use:                Objects if the information in the info block is bad.
+ */
+
+void swinfo_sanity(swinfo *sw)
+{
+  if (!sw->package)
+    die(1, "unknown package name.  Try `%s setup PACKAGE VERSION'.", QUIS);
+  if (!sw->version)
+    die(1, "unknown package version.  Try `%s setup PACKAGE VERSION'.",
+       QUIS);
+  if (!sw->maintainer)
+    die(1, "unknown maintainer.  Try `%s setup PACKAGE VERSION'.", QUIS);
+  if (!sw->date)
+    die(1, "unknown date.  Try `%s setup PACKAGE VERSION'.", QUIS);
+}
+
+/* --- @swinfo_put@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to an info block
+ *
+ * Returns:    Zero if it worked, nonzero if not.
+ *
+ * Use:                Writes an info block to the appropriate file.
+ */
+
+int swinfo_put(swinfo *sw)
+{
+  FILE *fp;
+  swfield *f;
+  int e;
+
+  /* --- Quick sanity check --- */
+
+  swinfo_sanity(sw);
+
+  /* --- Write the new data out --- */
+
+  if ((fp = fopen(".sw-info.new", "w")) == 0)
+    return (-1);
+  for (f = swfields; f->name; f++) {
+    if (!SWINFO(sw, f->off))
+      continue;
+    if (fprintf(fp, "%s = %s\n", f->name, SWINFO(sw, f->off)) == EOF) {
+      e = errno;
+      fclose(fp);
+      goto tidy_0;
+    }
+  }
+  if (fclose(fp) == EOF) {
+    e = errno;
+    goto tidy_0;
+  }
+
+  /* --- Carefully! replace the old one --- *
+   *
+   * Keep an old one around just in case, unless the renames fail because
+   * files don't exist.
+   */
+
+  remove(".sw-info.older");            /* Don't care if this fails */
+  if (rename(".sw-info.old", ".sw-info.older") && errno != ENOENT)
+    { e = errno; goto tidy_0; }
+  if (rename(".sw-info", ".sw-info.old") && errno != ENOENT)
+    { e = errno; goto tidy_1; }
+  if (rename(".sw-info.new", ".sw-info"))
+    { e = errno; goto tidy_2; }
+  remove(".sw-info.older");            /* Don't care if this fails */
+  return (0);
+
+  /* --- Tidy up if anything went tits-up --- */
+
+tidy_2:
+  rename(".sw-info.old", ".sw-info");
+tidy_1:
+  rename(".sw-info.older", ".sw-info.old");
+tidy_0:
+  remove(".sw-info.new");
+  errno = e;
+  return (-1);
+}
+
+/* --- @swinfo_destroy@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to info block
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys an info block when it's not useful any more.
+ */
+
+void swinfo_destroy(swinfo *sw)
+{
+  swfield *f;
+
+  for (f = swfields; f->name; f++) {
+    if (SWINFO(sw, f->off))
+      free(SWINFO(sw, f->off));
+  }
+}
+
+/* --- @swinfo_update@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to info block
+ *             @swinfo *skel@ = pointer to skeleton values
+ *
+ * Returns:    ---
+ *
+ * Use:                Updates the fields in an information block, except for the
+ *             architecture names stuff.  (I'll leave that for later,
+ *             because it's rather more involved.
+ */
+
+void swinfo_update(swinfo *sw, swinfo *skel)
+{
+  char ubuf[20];
+  char dbuf[20];
+  swfield *f;
+
+  /* --- Set up a default maintainer --- */
+
+  if (!skel->maintainer && !sw->maintainer) {
+    char *u = getenv("USER");
+
+    if (!u)
+      u = getenv("LOGNAME");
+    if (!u) {
+      struct passwd *pw = getpwuid(getuid());
+      if (!pw) {
+       moan("you don't seem to exist");
+       sprintf(ubuf, "uid %i", (int)getuid());
+       u = ubuf;
+      } else
+       u = pw->pw_name;
+    }
+    skel->maintainer = u;
+  }
+
+  /* --- Set up a default date --- */
+
+  {
+    time_t t = time(0);
+    struct tm *tm = localtime(&t);
+    strftime(dbuf, sizeof(dbuf), "%Y-%m-%d", tm);
+    skel->date = dbuf;
+  }
+
+  /* --- Set a default architecture list --- */
+
+  if (!skel->arch && !sw->arch)
+    skel->arch = "";
+
+  /* --- Grind through all the fields --- */
+
+  for (f = swfields; f->name; f++) {
+    if (!SWINFO(skel, f->off) || strcmp(SWINFO(skel, f->off), "-") == 0)
+      continue;
+    if (SWINFO(sw, f->off))
+      free(SWINFO(sw, f->off));
+    SWINFO(sw, f->off) = xstrdup(SWINFO(skel, f->off));
+  }
+}
+
+/*----- Subcommands -------------------------------------------------------*/
+
+/* --- @sw_setup@ --- */
+
+int sw_setup(int argc, char *argv[])
+{
+  swinfo sw, skel;
+  if (argc < 3 || argc > 4)
+    die(1, "Usage: setup PACKAGE VERSION [MAINTAINER]");
+  swinfo_fetch(&sw);
+  swinfo_clear(&skel);
+  skel.package = argv[1];
+  skel.version = argv[2];
+  if (argv[3])
+    skel.maintainer = argv[3];
+  swinfo_update(&sw, &skel);
+  swinfo_put(&sw);
+  return (0);
+}
+
+/* --- @sw_status@ --- */
+
+int sw_status(int argc, char *argv[])
+{
+  swinfo sw;
+  swfield *f;
+  if (swinfo_fetch(&sw)) {
+    die(1, "couldn't read build status: %s (try running setup)",
+       strerror(errno));
+  }
+  for (f = swfields; f->name; f++) {
+    if (SWINFO(&sw, f->off))
+      printf("%s: %s\n", f->desc, SWINFO(&sw, f->off));
+  }
+  return (0);
+}
+
+/* --- @sw_commit@ --- */
+
+int sw_commit(int argc, char *argv[])
+{
+  swinfo sw;
+
+  if (argc != 1)
+    die(1, "Usage: commit");
+  if (swinfo_fetch(&sw)) {
+    die(1, "couldn't read build status: %s (try running setup)",
+       strerror(errno));
+  }
+
+  /* --- Make sure everything has been built properly --- */
+
+  {
+    archcons *a, *aa;
+    archcons *all = arch_readtab();
+
+    if (sw.arch) {
+      for (a = aa = arch_filter(all, sw.arch, 0, 0); a; a = a->cdr)
+       a->car->flags |= archFlag_built;
+      arch_free(aa);
+    }
+
+    aa = arch_filter(all, sw.only_arch, archFlag_built, 0);
+    if (aa) {
+      char *sep = " ";
+      fprintf(stderr, "%s: not built for", QUIS);
+      for (a = aa; a; a = a->cdr) {
+       fprintf(stderr, "%s%s", sep, a->car->arch);
+       sep = ", ";
+      }
+      fputc('\n', stderr);
+      exit(1);
+    }
+    arch_free(aa);
+  }
+
+  /* --- Write to the index file --- */
+
+  {
+    FILE *fp = fopen(PREFIX "/sw-index", "a");
+    swfield *f;
+    char *sep = "";
+
+    if (!fp)
+      die(1, "couldn't open index file: %s", strerror(errno));
+    if (lock_file(fileno(fp), LOCK_EXCL))
+      die(1, "couldn't obtain lock on index file: %s", strerror(errno));
+
+    for (f = swfields; f->name; f++) {
+      if (SWINFO(&sw, f->off)) {
+       fprintf(fp, "%s%s = %s", sep, f->name, SWINFO(&sw, f->off));
+       sep = "; ";
+      }
+    }
+    fputc('\n', fp);
+    fclose(fp);
+  }
+
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/sw_info.h b/src/sw_info.h
new file mode 100644 (file)
index 0000000..1d3587d
--- /dev/null
@@ -0,0 +1,162 @@
+/* -*-c-*-
+ *
+ * $Id: sw_info.h,v 1.1 1999/06/02 16:53:35 mdw Exp $
+ *
+ * Maintenance of `.sw-info' files
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_info.h,v $
+ * Revision 1.1  1999/06/02 16:53:35  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SW_INFO_H
+#define SW_INFO_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct swinfo {
+  char *package;
+  char *version;
+  char *maintainer;
+  char *date;
+  char *only_arch;
+  char *arch;
+} swinfo;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @swinfo_clear@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointe to info block
+ *
+ * Returns:    ---
+ *
+ * Use:                Clears an info block so that it doesn't contain anything.
+ *             This is mainly useful when building skeletons to apply using
+ *             @swinfo_update@.
+ */
+
+extern void swinfo_clear(swinfo */*sw*/);
+
+/* --- @swinfo_fetch@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to info block to fill in
+ *
+ * Returns:    Zero if OK, else nonzero.
+ *
+ * Use:                Fills in the info block if it can.
+ */
+
+extern int swinfo_fetch(swinfo */*sw*/);
+
+/* --- @swinfo_sanity@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to an info block
+ *
+ * Returns:    Yes, if the block is OK.
+ *
+ * Use:                Objects if the information in the info block is bad.
+ */
+
+extern void swinfo_sanity(swinfo */*sw*/);
+
+/* --- @swinfo_put@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to an info block
+ *
+ * Returns:    Zero if it worked, nonzero if not.
+ *
+ * Use:                Writes an info block to the appropriate file.
+ */
+
+extern int swinfo_put(swinfo */*sw*/);
+
+/* --- @swinfo_destroy@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to info block
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys an info block when it's not useful any more.
+ */
+
+extern void swinfo_destroy(swinfo */*sw*/);
+
+/* --- @swinfo_update@ --- *
+ *
+ * Arguments:  @swinfo *sw@ = pointer to info block
+ *             @swinfo *skel@ = pointer to skeleton values
+ *
+ * Returns:    ---
+ *
+ * Use:                Updates the fields in an information block, except for the
+ *             architecture names stuff.  (I'll leave that for later,
+ *             because it's rather more involved.
+ */
+
+extern void swinfo_update(swinfo */*sw*/, swinfo */*skel*/);
+
+/* --- Subcommands --- */
+
+extern int sw_commit(int /*argc*/, char */*argv*/[]);
+extern int sw_setup(int /*argc*/, char */*argv*/[]);
+extern int sw_status(int /*argc*/, char */*argv*/[]);
+
+#ifdef CMD_LINK
+   static cmd cmd_commit = {
+     CMD_LINK, "commit", sw_commit,
+"commit\tWrites completed version information to the index file.  This\n\
+       be the last step of an installation.\n"
+   };
+   static cmd cmd_status = {
+     &cmd_commit, "status", sw_status,
+"status\tDisplays information from the package status file.  The output is\n\
+       slightly more readable than `cat .sw-info'.\n"
+   };
+   static cmd cmd_setup = {
+     &cmd_status, "setup", sw_setup,
+"setup PACKAGE VERSION [MAINTAINER]\n\
+       Persistently configures the package and version number.  This\n\
+       information is used by the later build and install steps.  If you\n\
+       omit the maintainer's name, it's assumed to be your username.\n"
+   };
+#  undef CMD_LINK
+#  define CMD_LINK &cmd_setup
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/src/sw_links.c b/src/sw_links.c
new file mode 100644 (file)
index 0000000..17eec9c
--- /dev/null
@@ -0,0 +1,808 @@
+/* -*-c-*-
+ *
+ * $Id: sw_links.c,v 1.1 1999/06/02 16:53:36 mdw Exp $
+ *
+ * Messing with symlink trees
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_links.c,v $
+ * Revision 1.1  1999/06/02 16:53:36  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <utime.h>
+#include <fcntl.h>
+
+#include <mLib/alloc.h>
+#include <mLib/dspool.h>
+#include <mLib/dstr.h>
+#include <mLib/report.h>
+#include <mLib/sub.h>
+
+#include "sw_arch.h"
+#include "sw_build.h"
+#include "sw_links.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct fent {
+  struct fent *next;
+  dstr *name;
+  dstr *link;
+  struct stat st;
+} fent;
+
+/*----- Static variables --------------------------------------------------*/
+
+static dstr down;
+static dstr up;
+static archcons *alist;
+static archcons *all;
+static dspool pool;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @canon@ --- *
+ *
+ * Arguments:  @dstr *d@ = path string to canonify
+ *
+ * Returns:    ---
+ *
+ * Use:                Strips out all `dir/..' pairs in a path string.  Also removes
+ *             any `.' and empty components.
+ */
+
+static void canon(dstr *d)
+{
+#define MAXBUF 16
+  char *b[MAXBUF];
+  int bp;
+  int v, n;
+  int retry;
+  char *path;
+  char *p, *q;
+  int r;
+
+  /* --- Initial stuff --- *
+   *
+   * If @path@ starts with `/' then initial `..' and `.' sequences can be
+   * discarded immediately.  Remember this by remembering where the start of
+   * the string is.
+   */
+
+  DPUTZ(d);
+  p = q = path = d->buf;
+  if (*p == '/') {
+    p++; q++; path++;
+    r = 1;
+  } else
+    r = 0;
+
+  /* --- Now for the main job --- *
+   *
+   * Scan each path component.  If it's a normal name, store @q@ in my
+   * circular buffer, and copy its text from @p@ to @q@.  If it's blank,
+   * or `.' then skip @p@ past it.  If it's `..', and there's an entry in my
+   * buffer, then reset @q@ back to that position and skip @p@ on past;
+   * otherwise, copy it to @q@.
+   *
+   * Complications arise when the buffer gets full.  Old entries are
+   * discarded off the bottom.  If it turns out they were useful (because
+   * @n@ is zero, but @v@ isn't) then @retry@ is set and we go round again
+   * when we're finished.
+   */
+
+again:
+  bp = n = v = 0;
+  retry = 0;
+
+  while (*p) {
+
+    /* --- Skip empty items --- */
+
+    while (*p == '/')
+      p++;
+
+    /* --- Things with `.' in front of them --- */
+
+    if (*p == '.') {
+      if (p[1] == 0)
+       break;
+      else if (p[1] == '/') {
+       p += 2;
+       continue;
+      } else if (p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
+       if (n) {
+         bp = bp ? bp - 1 : MAXBUF - 1;
+         q = b[bp];
+         n--; v--;
+         p += 2;
+         continue;
+       } else {
+         if (v)
+           retry = 1;
+         else if (r) {
+           p += 2;
+           continue;
+         }
+         goto out;
+       }
+      }
+    }
+         
+    /* --- Normal things --- */
+
+    b[bp++] = q;
+    if (bp >= MAXBUF) bp = 0;
+    v++;
+    if (n < MAXBUF) n++;
+  out:
+    while (*p && *p != '/')
+      *q++ = *p++;
+    if (*p == '/')
+      *q++ = *p++;
+  }
+  *q = 0;
+
+  /* --- Tidying up --- */
+
+  if (retry) {
+    p = q = path;
+    goto again;
+  }
+  if (q > path && q[-1] == '/')
+    q[-1] = 0;
+  d->len = q - d->buf;
+}
+
+/* --- @linktree@ --- *
+ *
+ * Arguments:  @int top@ = nonzero if this is toplevel
+ *             @dstr *cont@ = continuation directory
+ *
+ * Returns:    Zero if things are working well, nonzero otherwise.
+ *
+ * Use:                Main recursive tree-linking algorithm.  This makes extensive
+ *             use of the dynamic strings @up@ and @down@ as stacks to
+ *             maintain state.
+ *
+ *             On entry, @down@ contains the name of the path to scan,
+ *             relative to the common root; @up@ is the inverse path, from
+ *             the directory @down@ back up to the root.  As a special
+ *             wrinkle, @up@ has an additional `../' on the front.  The
+ *             current directory is @down@.  On exit, the current directory
+ *             is set to @cont@.
+ *
+ *             See the `description of the algorithm' below to really
+ *             understand what's going on here.  It's not completely
+ *             trivial.
+ */
+
+static int linktree(int top, dstr *cont)
+{
+  int rc = 0;
+  fent *fs;
+  dstr *dd;
+
+  if (!alist)
+    return (0);
+
+  DSGET(&pool, dd);
+
+  /* --- Description of the algorithm ---
+   *
+   * This is the sort of thing which is easy to do badly and hard to do
+   * well.  The current algorithm seeks to minimize the amount of searching
+   * that the operating system has to do around the filesystem, by changing
+   * the current directory fairly often.  But it also tries to avoid using
+   * too much memory, and never visits the same directory twice.
+   *
+   * I start off in the `common root'.  This is the directory that the user
+   * actually wanted to make link trees of, and is the parent of all the link
+   * trees.  The algorithm keeps track of where it's meant to be using two
+   * (static) variables, @down@ and @up@.  The @down@ variable tracks the
+   * current directory relative to the common root, and the @up@ variable
+   * contains enough `../'s to get back to the common root from the current
+   * directory, and one more (for getting back into the main tree from one of
+   * the architecture link trees, which has an extra level of depth).  When a
+   * recursion stage is entered, the current directory is already set
+   * correctly.
+   *
+   * At any stage in the recursion, there is a `continuation' directory.
+   * This is where my caller wants you to go when I've finished my job.  If I
+   * have no subdirectories to cope with, then I just change into the
+   * continuation when I've finished making all my links.  The trick with
+   * continations really works with subdirectories.  I change into the first
+   * subdirectory in my list myself, passing it a continuation for the next
+   * subdirectory in sequence.  The last subdirectory gets given a modified
+   * version of my own contination which keeps my caller happy without me
+   * actually having to do anything.
+   *
+   * The actual work is done by a postorder traversal.  A directory is
+   * processed in four phases, with an interlude between phases three and
+   * four.
+   *
+   *   * Phase one scans the current directory, and stores the names of
+   *    everything in a list.  Uninteresting things like `.' and `..', and
+   *    the architecture trees at toplevel, are filtered out at this stage.
+   *    When this scan is complete, I no longer need a file descriptor open
+   *    on the directory, and I can close it.
+   *
+   *   * Phase two examines each item scanned in phase one, and determines
+   *    how to deal with it.  Directory objects turn into real hard
+   *    directories; symlinks turn into adjusted symlinks to the same
+   *    destination; and files turn into relative symlinks of the right
+   *    kind.
+   *
+   *   * Phase three moves into the corresponding link directory for each
+   *    architecture, and makes the links and directories decided upon in
+   *    phase two.  When phase three is complete, the current directory is
+   *    still the link directory for the last architecture.
+   *
+   *   * The interlude tidies up some internal structures a little, and
+   *    handles early exit.  Firstly, the list of objects is filtered, and
+   *    everything which isn't a directory is removed.  Secondly, if the
+   *    list is now empty, the algorithm does its `early exit': it works out
+   *    how to get to the continuation directory from where it is now, does
+   *    that, and then returns.
+   *
+   *   * Phase four does the recursion step.  There is at least one
+   *    subdirectory to deal with.  Change out of the link tree, and into
+   *    the first subdirectory of my main directory.  Now, for each
+   *    subdirectory, recursively build trees, setting @down@, @up@ and the
+   *    continuation according to the description above.
+   *
+   * That completes the algorithm.
+   */
+
+  {
+    /* --- Phase one: directory scan --- */
+
+    DIR *dp;
+    struct dirent *d;
+    fent **ff, *f;
+    dstr *ds;
+
+    /* --- Open a directory stream --- */
+
+    if ((dp = opendir(".")) == 0) {
+      moan("couldn't read directory `%s': %s", down.buf, strerror(errno));
+      chdir(cont->buf);
+      return (-1);
+    }
+
+    /* --- Read the entries out one by one --- */
+
+    ff = &fs;
+    while ((d = readdir(dp)) != 0) {
+
+      /* --- Skip `.' and `..' directories --- */
+
+      {
+       char *p = d->d_name;
+       if (p[0] == '.' && ((p[1] == '.' && p[2] == 0) || p[1] == 0))
+         goto skip;
+      }
+
+      /* --- If this is toplevel, skip over the symlink trees --- */
+
+      if (top) {
+       archcons *a;
+       for (a = all; a; a = a->cdr) {
+         if (strcmp(d->d_name, a->car->arch) == 0)
+           goto skip;
+       }
+      }
+
+      /* --- Make a little structure with the entries in --- */
+
+      DSGET(&pool, ds);
+      DPUTS(ds, d->d_name);
+      f = CREATE(fent);
+      f->name = ds;
+      *ff = f;
+      ff = &f->next;
+    skip:;
+    }
+
+    closedir(dp);
+    *ff = 0;
+  }
+
+  {
+    /* -- Phase two: read attributes --- */
+
+    fent *f;
+
+    for (f = fs; f; f = f->next) {
+
+      /* --- Read the file information --- */
+
+      if (lstat(f->name->buf, &f->st)) {
+       moan("couldn't stat file `%s%s': %s",
+            down.buf, f->name->buf, strerror(errno));
+       DSPUT(&pool, f->name);
+       f->name = 0;
+       rc = -1;
+      }
+
+      /* --- Handle symbolic links --- *
+       *
+       * I need to canonify relative symbolic links.  (And there shouldn't be
+       * any absolute links in a source distribution!)
+       */
+
+      else if (S_ISLNK(f->st.st_mode)) {
+       dstr *ds;
+       int i;
+       DSGET(&pool, ds);
+       DENSURE(ds, f->st.st_size + 1);
+       if ((i = readlink(f->name->buf, ds->buf, ds->sz)) < 0) {
+         moan("couldn't read symbolic link `%s%s': %s",
+              down.buf, f->name->buf, strerror(errno));
+       } else {
+         ds->buf[i] = 0;
+         ds->len = i;
+         if (ds->buf[0] == '/')
+           f->link = ds;
+         else {
+           dstr *d;
+           DSGET(&pool, d);
+           f->link = d;
+           DPUTD(d, &up);
+           DPUTD(d, &down);
+           DPUTD(d, ds);
+           canon(d);
+           DSPUT(&pool, ds);
+         }
+       }
+      }
+
+      /* --- Directories are easy: they get created the hard way --- */
+
+      else if (S_ISDIR(f->st.st_mode))
+       f->link = 0;
+
+      /* --- Everything else is just a link --- */
+
+      else {
+       dstr *d;
+       DSGET(&pool, d);
+       f->link = d;
+       DPUTD(d, &up);
+       DPUTD(d, &down);
+       DPUTD(d, f->name);
+      }
+    }
+  }
+
+  {
+    /* --- Phase three: output links --- */
+
+    archcons *a;
+
+    /* --- Step 1: change directory --- */
+
+    dd->len = 0;
+    DPUTD(dd, &up);
+    DPUTS(dd, alist->car->arch);
+    DPUTC(dd, '/');
+    DPUTD(dd, &down);
+    if (chdir(dd->buf + 3)) {
+      die(1, "fatal error: couldn't change directory to `%s': %s",
+         dd->buf + 3, strerror(errno));
+    }
+
+    a = alist;
+    for (;;) {
+
+      /* --- Step 2: populate with links --- */
+
+      archent *e = a->car;
+      fent *f;
+
+      for (f = fs; f; f = f->next) {
+       if (f->link) {
+         {
+           struct stat st;
+           if (lstat(f->name->buf, &st) == 0 && S_ISLNK(st.st_mode))
+             unlink(f->name->buf);
+         }
+         if (symlink(f->link->buf, f->name->buf)) {
+           moan("couldn't create link `%s%s/%s': %s",
+                e->arch, down.buf, f->name->buf, strerror(errno));
+           rc = -1;
+         }
+       } else if (f->name) {
+         if (mkdir(f->name->buf, f->st.st_mode & 07777) &&
+             errno != EEXIST) {
+           moan("couldn't create directory `%s%s/%s: %s",
+                e->arch, down.buf, f->name->buf, strerror(errno));
+           rc = -1;
+         }
+       }
+      }
+
+      /* --- Step 3: move along --- */
+
+      a = a->cdr;
+      if (!a)
+       break;
+
+      dd->len = 0;
+      DPUTD(dd, &up);
+      DPUTS(dd, a->car->arch);
+      DPUTC(dd, '/');
+      DPUTD(dd, &down);
+      if (chdir(dd->buf)) {
+       die(1, "fatal error: couldn't change directory to `%s': %s",
+           dd->buf, strerror(errno));
+      }
+    }
+  }
+
+  /* --- Interlude: filter out nondirectories from the file list --- *
+   *
+   * This is a memory-saving exercise, and it makes the subdirectory handling
+   * simpler.
+   */
+
+  {
+    fent **ff = &fs;
+    while (*ff) {
+      fent *f = *ff;
+      if (f->name && !f->link)
+       ff = &f->next;
+      else {
+       if (f->name)
+         DSPUT(&pool, f->name);
+       if (f->link)
+         DSPUT(&pool, f->link);
+       *ff = f->next;
+       DESTROY(f);
+      }
+    }
+  }
+
+  /* --- Interlude: early exit if no directories --- *
+   *
+   * Presumably, a call to @canon@ is cheaper than traversing too many
+   * directories in the kernel.
+   */
+
+  if (!fs) {
+    dd->len = 0;
+    DPUTD(dd, &up);
+    DPUTD(dd, &down);
+    DPUTD(dd, cont);
+    canon(dd);
+    if (chdir(dd->buf)) {
+      die(1, "fatal error: couldn't change directory to `%s': %s",
+         dd->buf, strerror(errno));
+    }
+    DSPUT(&pool, dd);
+    return (rc);
+  }
+
+  {
+    /* --- Phase four: process subdirectories --- */
+
+    fent *f;
+    size_t ulen = up.len, dlen = down.len;
+
+    /* --- Set current directory for first directory --- *
+     *
+     * Subsequent directories do the right thing with the @cont@ argument.
+     * Then just leave this one queued up for the next time around.
+     */
+
+    dd->len = 0;
+    DPUTD(dd, &up);
+    DPUTD(dd, &down);
+    DPUTD(dd, fs->name);
+    if (chdir(dd->buf)) {
+      die(1, "fatal error: couldn't change directory to `%s': %s",
+         dd->buf, strerror(errno));
+    }
+
+    /* --- Now just process all the directories in turn --- */
+
+    f = fs;
+    while (f) {
+
+      /* --- Sort out the new `up' and `down' --- */
+
+      up.len = ulen;
+      down.len = dlen;
+      DPUTS(&up, "../");
+      DPUTD(&down, f->name);
+      DPUTS(&down, "/");
+
+      /* --- Set up the continuation directory --- */
+
+      dd->len = 0;
+      DPUTS(dd, "../");
+      if (f->next)
+       DPUTD(dd, f->next->name);
+      else
+       DPUTD(dd, cont);
+
+      /* --- Clean up this node --- */
+
+      {
+       fent *fnext = f->next;
+       DSPUT(&pool, f->name);
+       DESTROY(f);
+       f = fnext;
+      }
+
+      /* --- Go for it --- */
+
+      linktree(0, dd);
+    }
+  }
+
+  DSPUT(&pool, dd);
+  return (rc);
+}
+
+/* --- @snap@ --- *
+ *
+ * Arguments:  @const char *f@ = filename to snap
+ *
+ * Returns:    Zero if ok, nonzero otherwise.
+ *
+ * Use:                Snaps a symlink in one of the symlink trees into a real file.
+ *             Also (by design) happens to work even if there wasn't a
+ *             symlink there to begin with, in which case any necessary
+ *             directories are created beforehand.
+ */
+
+static int snap(const char *f)
+{
+  int narch;
+  int *fd;
+  int ifd;
+  struct stat st;
+  dstr *d;
+  int rc = 0;
+
+  /* --- Open the input file --- */
+
+  if ((ifd = open(f, O_RDONLY)) < 0) {
+    moan("couldn't open `%s' for reading: %s", f, strerror(errno));
+    return (-1);
+  }
+
+  if (fstat(ifd, &st)) {
+    moan("couldn't read information about `%s': %s", f, strerror(errno));
+    return (-1);
+  }  
+
+  /* --- Count the architectures --- */
+
+  { archcons *a; for (a = alist, narch = 0; a; a = a->cdr, narch++) ; }
+  d = xmalloc(narch * sizeof(dstr));
+  fd = xmalloc(narch * sizeof(int));
+
+  /* --- Make the directories needed, remove the old files, and so on --- */
+
+  {
+    int i;
+    archcons *a;
+    char *p = xstrdup(f);
+    char *q;
+
+    for (i = 0; i < narch; i++)
+      DCREATE(&d[i]);
+    for (i = 0, a = alist; a; i++, a = a->cdr)
+      DPUTS(&d[i], a->car->arch);
+    for (q = strtok(p, "/"); q; q = strtok(0, "/")) {
+      for (i = 0; i < narch; i++) {
+       mkdir(d[i].buf, 0775);
+       DPUTC(&d[i], '/');
+       DPUTS(&d[i], q);
+      }
+    }
+    for (i = 0; i < narch; i++) {
+      unlink(d[i].buf);
+      if ((fd[i] = open(d[i].buf, O_WRONLY | O_TRUNC | O_CREAT,
+                       st.st_mode & 07777)) < 0) {
+       moan("couldn't open `%s' for writing: %s",
+            d[i].buf, strerror(errno));
+       rc = -1;
+      }
+    }
+    free(p);
+  }
+
+  /* --- Main data copy loop --- */
+
+  {
+    int i;
+    char buf[BUFSIZ];
+    ssize_t n;
+
+    for (;;) {
+      n = read(ifd, buf, sizeof(buf));
+      if (n < 0) {
+       moan("error reading `%s': %s", f, strerror(errno));
+       rc = -1;
+       for (i = 0; i < narch; i++) {
+         close(fd[i]);
+         fd[i] = -1;
+         unlink(d[i].buf);
+       }
+       break;
+      }
+      if (!n)
+       break;
+      for (i = 0; i < narch; i++) {
+       if (fd[i] < 0)
+         continue;
+       if (write(fd[i], buf, n) < 0) {
+         moan("error writing `%s', %s", d[i].buf, strerror(errno));
+         close(fd[i]);
+         fd[i] = -1;
+         unlink(d[i].buf);
+         rc = -1;
+       }
+      }
+    }
+  }
+
+  /* --- Set the state on the finished files --- */
+
+  {
+    int i;
+    struct utimbuf u;
+
+    u.actime = st.st_atime;
+    u.modtime = st.st_mtime;
+
+    for (i = 0; i < narch; i++) {
+      if (fd[i] >= 0) {
+       close(fd[i]);
+       chmod(d[i].buf, st.st_mode & 07777);
+       utime(d[i].buf, &u);
+      }
+      DDESTROY(&d[i]);
+    }
+  }
+
+  free(d);
+  free(fd);
+  return (rc);
+}
+
+/*----- Subcommands -------------------------------------------------------*/
+
+/* --- @sw_link@ --- */
+
+int sw_link(int argc, char *argv[])
+{
+  int rc = 0;
+  swinfo sw;
+
+  if (argc != 1)
+    die(1, "Usage: linktree");
+
+  /* --- Initialize the dynamic strings --- */
+
+  dstr_create(&up);
+  dstr_puts(&up, "../");
+  dstr_create(&down);
+  dstr_putz(&down);
+  dspool_create(&pool, 32);
+
+  /* --- Set up the architecture lists --- */
+
+  if (swinfo_fetch(&sw)) {
+    die(1, "couldn't read build status: %s (try running setup)",
+       strerror(errno));
+  }
+  swinfo_sanity(&sw);
+  all = arch_readtab();
+  alist = swbuild_archlist(&sw);
+
+  if (!alist) {
+    moan("All desired architectures already built!");
+    return (0);
+  }
+
+  {
+    archcons *a;
+    for (a = alist; a; a = a->cdr) {
+      if (mkdir(a->car->arch, 0775) && errno != EEXIST) {
+       moan("couldn't create architecture tree `%s': %s",
+            a->car->arch, strerror(errno));
+       rc = -1;
+      }
+    }
+  }
+
+  /* --- Go --- */
+
+  if (rc == 0) {
+    dstr d = DSTR_INIT;
+    rc = linktree(1, &d);
+    DDESTROY(&d);
+  }
+
+  /* --- Clean up the mess --- */
+
+  dspool_destroy(&pool);
+  return (!!rc);
+}
+
+/* --- @sw_snap@ --- */
+
+int sw_snap(int argc, char *argv[])
+{
+  int rc = 0;
+  swinfo sw;
+  int i;
+
+  if (argc < 2)
+    die(1, "Usage: snaplink FILE...");
+
+  /* --- Set up the architecture lists --- */
+
+  if (swinfo_fetch(&sw)) {
+    die(1, "couldn't read build status: %s (try running setup)",
+       strerror(errno));
+  }
+  swinfo_sanity(&sw);
+  alist = swbuild_archlist(&sw);
+
+  if (!alist) {
+    moan("All desired architectures already built!");
+    return (0);
+  }
+
+  for (i = 1; i < argc; i++) {
+    if (snap(argv[i]))
+      rc = 1;
+  }
+
+  return (rc);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/sw_links.h b/src/sw_links.h
new file mode 100644 (file)
index 0000000..52348c4
--- /dev/null
@@ -0,0 +1,82 @@
+/* -*-c-*-
+ *
+ * $Id: sw_links.h,v 1.1 1999/06/02 16:53:36 mdw Exp $
+ *
+ * Messing with symlink trees
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_links.h,v $
+ * Revision 1.1  1999/06/02 16:53:36  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SW_LINKS_H
+#define SW_LINKS_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef SW_H
+#  include "sw.h"
+#endif
+
+/*----- Subcommands -------------------------------------------------------*/
+
+extern int sw_link(int /*argc*/, char */*argv*/[]);
+extern int sw_snap(int /*argc*/, char */*argv*/[]);
+
+#ifdef CMD_LINK
+   static cmd cmd_snap = {
+     CMD_LINK, "snaplink", sw_snap,
+"snaplink FILE...\n\
+       Turns links in link trees into real separate files.  Each file\n\
+       listed becomes a regular file in each of the architecture symlink\n\
+       trees.  Also works even if the trees aren't there, to propagate\n\
+       files.  Honours the `--arch' build flag, and the `only-arch'\n\
+       settings.\n"
+   };
+   static cmd cmd_link = {
+     &cmd_snap, "linktree", sw_link,
+"linktree\n\
+       Creates a symbolic link tree mirroring the current directory and\n\
+       its subdirectories for each architecture which should be built.\n\
+       Honours the `--arch' build flag, and the `only-arch' settings.\n"
+   };
+#  undef CMD_LINK
+#  define CMD_LINK &cmd_link
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/src/sw_rsh.c b/src/sw_rsh.c
new file mode 100644 (file)
index 0000000..3c910df
--- /dev/null
@@ -0,0 +1,909 @@
+/* -*-c-*-
+ *
+ * $Id: sw_rsh.c,v 1.1 1999/06/02 16:53:34 mdw Exp $
+ *
+ * Run remote commands
+ *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_rsh.c,v $
+ * Revision 1.1  1999/06/02 16:53:34  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#ifndef DECL_ENVIRON
+  extern char **environ;
+#endif
+
+#include <mLib/alloc.h>
+#include <mLib/dstr.h>
+#include <mLib/exc.h>
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/sym.h>
+
+#define RCMD_LINK 0
+#include "sw_arch.h"
+#include "sw_build.h"
+#include "sw_env.h"
+#include "sw_rsh.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef unsigned char octet;
+
+#define PKHEADSZ 3
+
+typedef struct pkhead {
+  octet len[2];
+  octet type;
+} pkhead;
+
+/*----- Static variables --------------------------------------------------*/
+
+static int handler = 0;
+static rcmd *rcmds = RCMD_LINK;
+
+/*----- Packet interface --------------------------------------------------*/
+
+/* --- @pksend@ --- *
+ *
+ * Arguments:  @sw_remote@ = pointer to the remote block
+ *             @int type@ = packet type to send
+ *             @const void *p@ = pointer to packet data
+ *             @size_t sz@ = size of data to send
+ *
+ * Returns:    Zero if it worked, nonzero otherwise.
+ *
+ * Use:                Sends a data packet.  If the type is `data', then `sz' may be
+ *             arbitrarily large and is divided into small eenough chunks.
+ *             Otherwise it's an error to send a packet that's too big.
+ */
+
+int pksend(sw_remote *r, int type, const void *p, size_t sz)
+{
+  pkhead h;
+  const char *q = p;
+  size_t chunk;
+
+  /* --- Sort out error conditions --- */
+
+  if (sz > PKMAX && type != PKTYPE_DATA) {
+    errno = E2BIG;
+    return (-1);
+  }
+
+  /* --- Main output loop --- */
+
+  h.type = type;
+  do {
+
+    /* --- Set up the packet header --- */
+
+    chunk = (sz > PKMAX ? PKMAX : sz);
+    h.len[0] = chunk & 0xff;
+    h.len[1] = (chunk >> 8) & 0xff;
+
+    /* --- Write the packet header --- */
+
+  try_again:
+    if (write(r->fdout, &h, PKHEADSZ) < PKHEADSZ) {
+      if (errno == EINTR)
+       goto try_again;
+      return (-1);
+    }
+
+    /* --- Write the payload, if there is one --- *
+     *
+     * Maybe the OS won't want to bite it all off in one go.
+     */
+
+    while (chunk) {
+      ssize_t n = write(r->fdout, q, chunk);
+      if (n < 0 && errno != EINTR)
+       return (-1);
+      if (n > 0) {
+       q += n;
+       chunk -= n;
+       sz -= n;
+      }
+    }
+  } while (sz);
+
+  /* --- Done --- */
+
+  return (0);
+}
+
+/* --- @pkrecv@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote block
+ *
+ * Returns:    Packet type received, or @-1@ for an error.
+ *
+ * Use:                Receives a packet from the remote host.  The packet data is
+ *             placed in the block's buffer, the block's packet length is
+ *             diddled appropriately.
+ */
+
+int pkrecv(sw_remote *r)
+{
+  pkhead h;
+  size_t sz;
+  char *p;
+  ssize_t n;
+
+  /* --- Read the packet header --- */
+
+  sz = PKHEADSZ;
+  p = (char *)&h;
+  while (sz) {
+    n = read(r->fdin, p, sz);
+    if (n < 0 && errno != EINTR)
+      return (-1);
+    if (n == 0)
+      return (PKTYPE_EOF);
+    if (n > 0) {
+      p += n;
+      sz -= n;
+    }
+  }
+
+  /* --- Hack for error messages --- *
+   *
+   * If it doesn't look like a valid packet, read a `chunk' and pretend it's
+   * data.  This isn't too bad, because all the packet types are control
+   * codes, and are unlikely to be in a textual message.
+   *
+   * Normally what happens here is that something sitting before the `sw'
+   * program fails, reports a plain textual error, and exits.  Grabbing the
+   * `last gasp' like this, then, traps that error message and allows
+   * something to report it.  The rest ought to be completely silent, so I
+   * get an `unexpected eof' and then drop everything.
+   *
+   * This is certainly better than the behaviour otherwise, which is an
+   * @E2BIG@ message reported when the packet size is really ASCII
+   * characters.
+   */
+
+  if (h.type >= PKTYPE_BOGUS) {
+    memcpy(r->buf, &h, PKHEADSZ);
+    n = read(r->fdin, r->buf + PKHEADSZ, sizeof(r->buf) - PKHEADSZ);
+    if (n < 0)
+      return (-1);
+    r->sz = n + PKHEADSZ;
+    return (PKTYPE_DATA);
+  }
+
+  /* --- Sort out what's going on --- */
+
+  sz = h.len[0] | (h.len[1] << 8);
+  r->sz = sz;
+  if (!sz)
+    return (h.type);
+  if (sz > PKMAX) {
+    errno = E2BIG;
+    return (-1);
+  }
+
+  /* --- Read the packet payload --- */
+
+  p = r->buf;
+  while (sz) {
+    n = read(r->fdin, p, sz);
+    if (n < 0 && errno != EINTR)
+      return (-1);
+    if (n == 0)
+      return (PKTYPE_EOF);
+    if (n > 0) {
+      p += n;
+      sz -= n;
+    }
+  }
+
+  return (h.type);
+}
+
+/*----- Error reporting and exit statuses --------------------------------*/
+
+/* --- @swexit@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @int status@ = exit status to return
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports the exit status via packet protocol and quits.
+ */
+
+void swexit(sw_remote *r, int status)
+{
+  unsigned char s = status;
+  pksend(r, PKTYPE_STATUS, &s, 1);
+  _exit(status);
+}
+
+/* --- @swsignal@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @int sig@ = signal ocurrence to return
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports a signalled-to-death status via packet protocol and
+ *             quits.
+ */
+
+void swsignal(sw_remote *r, int sig)
+{
+#if defined(HAVE_STRSIGNAL)
+  char *s = strsignal(sig);
+#elif defined(HAVE__SYS_SIGLIST)
+  char *s = _sys_siglist[sig];
+#else
+  char s[16];
+  sprintf(s, "signal %i", sig);
+#endif
+
+  pksend(r, PKTYPE_STATUS, s, strlen(s) + 1);
+  _exit(127);
+}
+
+/* --- @swwait@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @int status@ = status answer from @wait@(2)
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports a child's demise appropriately, and quits.
+ */
+
+void swwait(sw_remote *r, int status)
+{
+  if (WIFEXITED(status))
+    swexit(r, WEXITSTATUS(status));
+  else if (WIFSIGNALED(status))
+    swsignal(r, WTERMSIG(status));
+  else
+    swexit(r, 126);
+}
+
+/* --- @swvprintf@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @const char *format@ = format string
+ *             @va_list ap@ = things to format
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a string to the remote end.  This is the low-level bit
+ *             of @swprintf@.
+ */
+
+void swvprintf(sw_remote *r, const char *format, va_list ap)
+{
+  dstr d = DSTR_INIT;
+  dstr_vputf(&d, format, ap);
+  pksend(r, PKTYPE_DATA, d.buf, d.len);
+  dstr_destroy(&d);
+}
+
+/* --- @swprintf@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @const char *format@ = format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a string to the remote end.
+ */
+
+void swprintf(sw_remote *r, const char *format, ...)
+{
+  va_list ap;
+  va_start(ap, format);
+  swvprintf(r, format, ap);
+  va_end(ap);
+}
+
+/* --- @swdie@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @int status@ = exit status to report
+ *             @const char *format@ = format string to fill in
+ *             @...@ = other arguments
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports a message and quits.
+ */
+
+void swdie(sw_remote *r, int status, const char *format, ...)
+{
+  va_list ap;
+  dstr d = DSTR_INIT;
+
+  va_start(ap, format);
+  dstr_putf(&d, "%s [remote]: ", QUIS);
+  dstr_vputf(&d, format, ap);
+  dstr_putc(&d, '\n');
+  dstr_putz(&d);
+  va_end(ap);
+  pksend(r, PKTYPE_DATA, d.buf, d.len);
+  dstr_destroy(&d);
+  swexit(r, status);
+}
+
+/*----- Command handling and dispatch -------------------------------------*/
+
+/* --- @remote@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = pointer to remote block
+ *             @const char *cmd@ = command to run
+ *             @char *argv[]@ = argument array
+ *             @char *env[]@ = environment variables
+ *
+ * Returns:    Doesn't.  Reports an exit status through packet protocol and
+ *             quits.
+ *
+ * Use:                Dispatches a remote command.  At this point, the two code
+ *             paths for local and remote invokation have joined again.
+ */
+
+static void remote(sw_remote *r, const char *cmd, char *argv[], char *env[])
+{
+  struct rcmd *p, *chosen = 0;
+  size_t sz = strlen(cmd);
+
+  /* --- Make sure that I can get the exit status of children --- */
+
+  signal(SIGCHLD, SIG_DFL);
+
+  /* --- Fix up the environment --- */
+
+  {
+    sym_table t;
+    sym_create(&t);
+    env_import(&t, env);
+    if (env != environ) {
+      free(env);
+      env_import(&t, environ);
+    }
+    env_put(&t, "SW_ARCH", ARCH);
+    env_file(&t, DATADIR "/sw-env");
+    env = env_export(&t);
+  }
+
+  /* --- Dispatch to the command handler --- */
+
+  for (p = rcmds; p; p = p->next) {
+    if (strncmp(cmd, p->name, sz) == 0) {
+      if (p->name[sz] == 0) {
+       chosen = p;
+       break;
+      } else if (chosen)
+       swdie(r, 1, "ambiguous remote command name `%s'", cmd);
+      else
+       chosen = p;
+    }
+  }
+  if (!chosen)
+    swdie(r, 1, "unknown remote command name `%s'", cmd);
+  chosen->rcmd(r, argv, env);
+}
+
+/*----- Remote invocation -------------------------------------------------*/
+
+/* --- @sendargv@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = pointer to the remote context
+ *             @int type@ = packet type to send with
+ *             @char *v[]@ = pointer to the array to send
+ *
+ * Returns:    Zero if OK, nonzero if it failed.
+ *
+ * Use:                Sends something @argv@-shaped; i.e., an array of strings
+ *             terminated by a null pointer.
+ */
+
+static int sendargv(sw_remote *r, int type, char *v[])
+{
+  dstr d = DSTR_INIT;
+  int e;
+
+  while (*v) {
+    dstr_puts(&d, *v);
+    d.len++;                           /* Make the null `real' */
+    v++;
+  }
+  e = pksend(r, type, d.buf, d.len);
+  dstr_destroy(&d);
+  return (e);
+}
+
+/* --- @snarfargv@ --- *
+ *
+ * Arguments:  @const char *buf@ = pointer to buffer
+ *             @size_t sz@ = size of buffer
+ *
+ * Returns:    Pointer to argument array (allocated with @malloc@).
+ *
+ * Use:                Snarfs the null-terminated strings in the buffer and returns
+ *             an array of them.  The whole lot, strings and array, is
+ *             returned in one big chunk allocated from the heap.  Note that
+ *             this means it's OK to throw the initial buffer away.
+ */
+
+static char **snarfargv(const char *buf, size_t sz)
+{
+  /* --- Initial setup --- */
+
+  const char *p, *lim;
+  char *q;
+  size_t c;
+  char **v, **w;
+
+  /* --- Pass one: count the number of arguments --- */
+
+  c = 0;
+  p = buf;
+  lim = p + sz;
+  while (p < lim) {
+    c++;
+    while (*p) {
+      p++;
+      if (p >= lim)
+       goto done_pass1;
+    }
+    p++;
+  }
+done_pass1:
+
+  /* --- Allocate memory for everything --- */
+
+  v = xmalloc((c + 1) * sizeof(char *) + sz + 1);
+  q = (char *)(v + c + 1);
+  memcpy(q, buf, sz);
+
+  /* --- Pass two: set up the arrays --- */
+
+  lim = q + sz;
+  w = v;
+  while (q < lim) {
+    *w++ = q;
+    while (*q) {
+      q++;
+      if (q >= lim)
+       goto done_pass2;
+    }
+    q++;
+  }
+done_pass2:
+  *w++ = 0;
+  *q++ = 0;
+
+  /* --- Done --- */
+
+  return (v);
+}
+
+/* --- @swrsh_remote@ --- *
+ *
+ * Arguments:  @const char *cmd@ = the command to perform
+ *
+ * Returns:    Doesn't.  Reports an exit status through packet protocol and
+ *             quits.
+ *
+ * Use:                Handles the remote end of a remote job invokation.
+ */
+
+void swrsh_remote(const char *cmd)
+{
+  sw_remote r;
+  static char *dummy = 0;
+  char **argv = 0;
+  char **env = 0;
+  char *dir = 0;
+  r.fdin = 0;
+  r.fdout = 1;
+
+ /* --- Read packets from the remote host --- */
+
+  for (;;) {
+    int t = pkrecv(&r);
+    switch (t) {
+      case -1:
+       swdie(&r, 1, "error reading packet: %s", strerror(errno));
+       break;
+      case PKTYPE_EOF:
+       goto done;
+      case PKTYPE_ARGS:
+       if (argv)
+         free(argv);
+       argv = snarfargv(r.buf, r.sz);
+       break;
+      case PKTYPE_ENV:
+       if (env)
+         free(env);
+       env = snarfargv(r.buf, r.sz);
+       break;
+      case PKTYPE_DIR:
+       if (dir)
+         free(dir);
+       r.buf[r.sz] = 0;
+       dir = xstrdup(r.buf);
+       break;
+      case PKTYPE_GO:
+       goto done;
+      case PKTYPE_DATA:
+      case PKTYPE_STATUS:
+       swdie(&r, 1, "internal error: unexpected packet");
+       break;
+    }
+  }
+
+  /* --- Sort out any missing arguments --- */
+
+done:
+  if (!argv)
+    argv = &dummy;
+  if (!env)
+    env = &dummy;
+  if (dir)
+    chdir(dir);
+
+  /* --- Run the command --- */
+
+  TRY
+    remote(&r, cmd, argv, env);
+  CATCH switch (exc_type) {
+    case EXC_NOMEM: {
+      static char msg[] = "\nsw [remote]: not enough memory\n";
+      pksend(&r, PKTYPE_DATA, msg, sizeof(msg) - 1);
+      swexit(&r, 1);
+    } break;
+    default:
+      swdie(&r, 1, "uncaught exception, type = %lx", exc_type);
+  } END_TRY;
+}
+
+/*----- Starting remote jobs ----------------------------------------------*/
+
+/* --- @sigchld@ --- *
+ *
+ * Arguments:  @int sig@ = the signal number
+ *
+ * Returns:    ---
+ *
+ * Use:                Catches @SIGCHLD@ and reaps any children that have lost.
+ */
+
+static void sigchld(int sig)
+{
+#ifdef DEBUG_SIGCHLD
+  int status;
+  while (waitpid(-1, &status, WNOHANG) > 0) {
+    if (WIFEXITED(status)) {
+      fprintf(stderr, "reap child with exit status %i\n",
+             WEXITSTATUS(status));
+    } else if (WIFSIGNALED(status)) {
+      fprintf(stderr, "reap child killed by signal %s\n",
+             strsignal(WTERMSIG(status)));
+    } else
+      fprintf(stderr, "reaped bizarre child which is still alive\n");
+  }
+#else
+  while (waitpid(-1, 0, WNOHANG) > 0)
+    ;
+#endif
+}
+
+/* --- @swrsh@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote process block to look after
+ *             @const char *host@ = host to run on (0 for this one)
+ *             @const char *cmd@ = remote command to run
+ *             @char *argv[]@ = arguments to pass on
+ *
+ * Returns:    Zero if it worked, nonzero if not.
+ *
+ * Use:                Runs a command on a remote host.  The argument array is
+ *             mangled to come out OK at the far end.  The environment and
+ *             current directory are also passed along, and pop out the
+ *             other end unmolested.
+ */
+
+int swrsh(sw_remote *r, const char *host, const char *cmd, char *argv[])
+{
+  int sk[2];
+  pid_t kid;
+
+  /* --- Get a socket pair for communicating with the other end --- */
+
+  if (socketpair(PF_UNIX, SOCK_STREAM, 0, sk))
+    goto tidy_0;
+
+  /* --- Set up a signal handler --- */
+
+  if (!handler) {
+    struct sigaction sa;
+    sa.sa_handler = sigchld;
+    sa.sa_flags = 0;
+    sigemptyset(&sa.sa_mask);
+    sigaction(SIGCHLD, &sa, 0);
+    handler = 1;
+  }
+
+  /* --- Fork off a child to cope with stuff --- */
+
+  kid = fork();
+  if (kid < 0)
+    goto tidy_1;
+
+  /* --- Handle the child process --- *
+   *
+   * If this is a local job, then just loop around inside to handle the
+   * `remote' command.  Otherwise crank up `rsh' and pass the command over to
+   * a remote copy of myself.
+   *
+   * (Why do I need a separate process for local jobs?  I don't really, but
+   * it makes everything much simpler when running multiple jobs at the same
+   * time.)
+   */
+
+  if (kid == 0) {
+    close(sk[0]);
+
+    /* --- Child end of a local job --- */
+
+    if (!host) {
+      r->fdin = r->fdout = sk[1];
+      remote(r, cmd, argv, environ);
+    }
+
+    /* --- Local child end of a remote job --- */
+
+    else {
+      const char *rsh;
+      dup2(sk[1], 0);
+      dup2(sk[1], 1);
+      dup2(sk[1], 2);
+      if (sk[1] > 2)
+       close(sk[1]);
+      rsh = getenv("SW_RSH");
+      if (!rsh)
+       rsh = RSH;
+      execlp(rsh, rsh, host, PATH_SW, "--remote", cmd, (char *)0);
+    }
+
+    /* --- I don't expect either to come back --- */
+
+    _exit(1);
+  }
+
+  /* --- Local sort out of what to do --- *
+   *
+   * Either way, I've now got a socket tied to something which speaks my
+   * communication protocol.  However, if this is a local job, then I can get
+   * going right away; otherwise, I've got to transmit various bits of
+   * information over the protocol.
+   */
+
+  r->fdin = r->fdout = sk[0];
+  close(sk[1]);
+
+  if (host) {
+    char buf[PKMAX];
+    if (!getcwd(buf, sizeof(buf)))
+      goto tidy_1;
+    sendargv(r, PKTYPE_ARGS, argv);
+    sendargv(r, PKTYPE_ENV, environ);
+    pksend(r, PKTYPE_DIR, buf, strlen(buf) + 1);
+    pksend(r, PKTYPE_GO, 0, 0);
+  }
+
+  /* --- Ready to rock'n'roll --- */
+
+  r->sz = 0;
+  return (0);
+
+  /* --- Tidy up if it failed --- */
+
+tidy_1:
+  close(sk[0]);
+  close(sk[1]);
+tidy_0:
+  return (-1);
+}
+
+/*----- Subcommands -------------------------------------------------------*/
+
+/* --- @swrsh_rsh@ --- */
+
+void rsw_rsh(sw_remote *r, char *argv[], char *env[])
+{
+  pid_t kid, k;
+  int pfd[2];
+  int status;
+
+  /* --- Create a pipe --- */
+
+  if (pipe(pfd))
+    swdie(r, 1, "couldn't create pipe: %s", strerror(errno));
+
+  /* --- Start the child process up --- */
+
+  kid = fork();
+  if (kid < 0)
+    swdie(r, 1, "fork failed: %s", strerror(errno));
+  else if (kid == 0) {
+    int fd;
+
+    /* --- Use my new environment --- */
+
+    environ = env; /* Yuk. */
+
+    /* --- Fiddle with pipe file descriptors --- */
+
+    close(pfd[0]);
+    dup2(pfd[1], 1);
+    dup2(pfd[1], 2);
+    if (pfd[1] > 2)
+      close(pfd[1]);
+
+    /* --- Make sure it doesn't get any input --- */
+
+    close(0);
+    fd = open("/dev/null", O_RDONLY);
+    if (fd != 0) {
+      dup2(fd, 0);
+      close(fd);
+    }
+
+    /* --- Run the program --- */
+
+    execvp(argv[0], argv);
+    die(1, "couldn't exec `%s': %s", argv[0], strerror(errno));
+  }
+
+  /* --- Read the data from the pipe until it closes --- */
+
+  close(pfd[1]);
+  for (;;) {
+    ssize_t n = read(pfd[0], r->buf, sizeof(r->buf));
+    if (n < 0)
+      swdie(r, 1, "read error: %s", strerror(errno));
+    else if (!n)
+      break;
+    else
+      pksend(r, PKTYPE_DATA, r->buf, n);
+  }
+  close(pfd[0]);
+
+  /* --- Finally, reap the exit status and pass it on --- */
+
+  for (;;) {
+    k = wait(&status);
+    if (k == kid)
+      break;
+    if (k < 0)
+      swdie(r, 1, "error reaping child: %s", strerror(errno));
+  }
+  swwait(r, status);
+}
+
+/* --- @sw_rsh@ --- */
+
+int sw_rsh(int argc, char *argv[])
+{
+  sw_remote r;
+  char *h;
+  int status = 1;
+
+  /* --- Check the arguments --- */
+
+  if (argc < 3)
+    die(1, "Usage: rsh HOST|ARCH COMMAND [ARGS...]");
+
+  /* --- Translate architecture names into hostnames --- */
+
+  if (strcmp(argv[1], "-") == 0)
+    h = 0;
+  else {
+    archent *a = arch_lookup(argv[1], 0);
+    if (!a)
+      h = argv[1];
+    else if (a->flags & archFlag_home)
+      h = 0;
+    else
+      h = a->host;
+  }
+
+  /* --- Start the remote process --- */
+
+  if (swrsh(&r, h, "rsh", argv + 2))
+    die(1, "remote shell failed: %s", strerror(errno));
+
+  /* --- Cope with packets from the remote process --- */
+
+  fflush(stdout);
+  for (;;) {
+    int t = pkrecv(&r);
+    switch (t) {
+      case -1:
+       die(1, "error reading packet: %s", strerror(errno));
+      case PKTYPE_DATA:
+       write(STDOUT_FILENO, r.buf, r.sz);
+       break;
+      case PKTYPE_STATUS:
+       r.buf[r.sz] = 0;
+       if (r.sz > 1) {
+         status = 127;
+         moan("command exited due to signal: %s", r.buf);
+       } else {
+         status = r.buf[0];
+           moan("command exited with status %i", r.buf[0]);
+       }
+       goto done;
+      case PKTYPE_EOF:
+       moan("command exited unexpectedly");
+       goto done;
+      default:
+       die(1, "unexpected packet type");
+    }
+  }
+
+  /* --- Finished --- */
+
+done:
+  close(r.fdin);
+  return (status);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/src/sw_rsh.h b/src/sw_rsh.h
new file mode 100644 (file)
index 0000000..f44aaa9
--- /dev/null
@@ -0,0 +1,311 @@
+/* -*-c-*-
+ *
+ * $Id: sw_rsh.h,v 1.1 1999/06/02 16:53:33 mdw Exp $
+ *
+ * [Run remote commands *
+ * (c) 1999 EBI
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of sw-tools.
+ *
+ * sw-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * sw-tools is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with sw-tools; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sw_rsh.h,v $
+ * Revision 1.1  1999/06/02 16:53:33  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SW_RSH_H
+#define SW_RSH_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Protocol definition -----------------------------------------------*
+ *
+ * There's a trivial packet protocol used to squeeze various bits of
+ * information down the `rsh' or `ssh' connection.  Each packet has a three
+ * octet header consisting of a two-octet length and a single octet type.
+ * The length is the length of the packet payload, not including the header
+ * (which is fetched separately).  The type determines what happens with the
+ * payload.
+ *
+ * There are six packet types defined so far.  These are:
+ *
+ *   * `args' -- contains a sequence of null-terminated arguments.  The list
+ *     stops when there's no more data in the packet.  (The normal
+ *     null-terminated-list-of-null-terminated-strings doesn't allow empty
+ *     arguments, which are perfectly valid.)
+ *
+ *   * `env' -- contains a sequence of null-terminated environment variable
+ *     assignments of the form `VAR=VALUE'.
+ *
+ *   * `dir' -- contains a null-terminated path to a current directory.
+ *
+ *   * `go' -- contains no data.  Means `execute the command now'.
+ *
+ *   * `data' -- contains data produced by the command.
+ *
+ *   * `status' -- contains command exit status.  If this is a single octet
+ *     then that octet is the exit status; otherwise, it's a null-terminated
+ *     signal name.
+ *
+ * The first four packets are sent from the client to the server; the last
+ * two are sent the other way.  The first three packets may be sent in any
+ * order, and some may be omitted.  (This doesn't happen in the current
+ * implementation.)
+ *
+ * The command was already sent as the `--remote' argument to the remote
+ * `sw'.
+ *
+ * (There's also a fake packet type used for representing eof on the stream.
+ * This should never be seen on the wire -- it's only for internal use.)
+ */
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- Packet type codes --- */
+
+enum {
+  PKTYPE_EOF,                          /* Fake end-of-file condition */
+  PKTYPE_ARGS,                         /* Argument array */
+  PKTYPE_ENV,                          /* Environment array */
+  PKTYPE_DIR,                          /* Current directory */
+  PKTYPE_GO,                           /* You have all you need to know */
+  PKTYPE_DATA,                         /* Here's some data */
+  PKTYPE_STATUS,                       /* My job ended: here's why */
+  PKTYPE_BOGUS                         /* Bogus packet type */
+};
+
+#define PKMAX 4096                     /* Maximum packet size */
+
+/* --- A `remote context' --- *
+ *
+ * Describes everything there is to know about a remotely executing job.
+ *
+ * Note that `fdin' and `fdout' are identical on the local side, and
+ * (potentially) different on the remote side.  Be careful!
+ */
+
+typedef struct sw_remote {
+  int fdin, fdout;                     /* File descriptors */
+  size_t sz;                           /* Size of data in buffer */
+  char buf[PKMAX + 1];                 /* Input buffer (handy for output) */
+} sw_remote;
+
+/* --- A remote command definition --- *
+ *
+ * Like normal user commands, these are linked into a list.  See `sw.h' for
+ * the nitty-gritty.  The magic link macro is called @RCMD_LINK@.
+ */
+
+typedef struct rcmd {
+  struct rcmd *next;
+  const char *name;
+  void (*rcmd)(sw_remote */*r*/, char */*argv*/[], char */*env*/[]);
+} rcmd;
+
+/*----- Packet interface --------------------------------------------------*/
+
+/* --- @pksend@ --- *
+ *
+ * Arguments:  @sw_remote@ = pointer to the remote block
+ *             @int type@ = packet type to send
+ *             @const void *p@ = pointer to packet data
+ *             @size_t sz@ = size of data to send
+ *
+ * Returns:    Zero if it worked, nonzero otherwise.
+ *
+ * Use:                Sends a data packet.  If the type is `data', then `sz' may be
+ *             arbitrarily large and is divided into small eenough chunks.
+ *             Otherwise it's an error to send a packet that's too big.
+ */
+
+extern int pksend(sw_remote */*r*/, int /*type*/,
+                 const void */*p*/, size_t /*sz*/);
+
+/* --- @pkrecv@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote block
+ *
+ * Returns:    Packet type received, or @-1@ for an error.
+ *
+ * Use:                Receives a packet from the remote host.  The packet data is
+ *             placed in the block's buffer, the block's packet length is
+ *             diddled appropriately.
+ */
+
+extern int pkrecv(sw_remote */*r*/);
+
+/*----- Error reporting and exit statuses --------------------------------*/
+
+/* --- @swexit@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @int status@ = exit status to return
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports the exit status via packet protocol and quits.
+ */
+
+extern void swexit(sw_remote */*r*/, int /*status*/);
+
+/* --- @swsignal@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @int sig@ = signal ocurrence to return
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports a signalled-to-death status via packet protocol and
+ *             quits.
+ */
+
+extern void swsignal(sw_remote */*r*/, int /*sig*/);
+
+/* --- @swwait@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @int status@ = status answer from @wait@(2)
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports a child's demise appropriately, and quits.
+ */
+
+extern void swwait(sw_remote */*r*/, int /*status*/);
+
+/* --- @swvprintf@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @const char *format@ = format string
+ *             @va_list ap@ = things to format
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a string to the remote end.  This is the low-level bit
+ *             of @swprintf@.
+ */
+
+extern void swvprintf(sw_remote */*r*/,
+                     const char */*format*/, va_list /*ap*/);
+
+/* --- @swprintf@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @const char *format@ = format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a string to the remote end.
+ */
+
+extern void swprintf(sw_remote */*r*/, const char */*format*/, ...);
+
+/* --- @swdie@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote context
+ *             @int status@ = exit status to report
+ *             @const char *format@ = format string to fill in
+ *             @...@ = other arguments
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports a message and quits.
+ */
+
+extern void swdie(sw_remote */*r*/, int /*status*/,
+                 const char */*format*/, ...);
+
+/*----- Remote invocation -------------------------------------------------*/
+
+/* --- @swrsh_remote@ --- *
+ *
+ * Arguments:  @const char *cmd@ = the command to perform
+ *
+ * Returns:    Doesn't.  Reports an exit status through packet protocol and
+ *             quits.
+ *
+ * Use:                Handles the remote end of a remote job invokation.
+ */
+
+extern void swrsh_remote(const char */*cmd*/);
+
+/*----- Starting remote jobs ----------------------------------------------*/
+
+/* --- @swrsh@ --- *
+ *
+ * Arguments:  @sw_remote *r@ = remote process block to look after
+ *             @const char *host@ = host to run on (0 for this one)
+ *             @const char *cmd@ = remote command to run
+ *             @char *argv[]@ = arguments to pass on
+ *
+ * Returns:    Zero if it worked, nonzero if not.
+ *
+ * Use:                Runs a command on a remote host.  The argument array is
+ *             mangled to come out OK at the far end.  The environment and
+ *             current directory are also passed along, and pop out the
+ *             other end unmolested.
+ */
+
+extern int swrsh(sw_remote */*r*/, const char */*host*/,
+                const char */*cmd*/, char */*argv*/[]);
+
+/*----- Subcommands -------------------------------------------------------*/
+
+extern void rsw_rsh(sw_remote */*r*/, char */*argv*/[], char */*env*/[]);
+extern int sw_rsh(int /*argc*/, char */*argv*/[]);
+
+#ifdef CMD_LINK
+   static cmd cmd_rsh = {
+     CMD_LINK, "rsh", sw_rsh,
+"rsh HOST|ARCH COMMAND [ARGS...]\n\
+       Runs the COMMAND on the remote host HOST (or one which supports\n\
+       architecture ARCH), passing it the given ARGS.  Note that the\n\
+       command and arguments aren't subject to shell expansions on the\n\
+       remote site (unlike with `rsh').  Environment variables are passed\n\
+       on to the remote command, as is the current directory.  The\n\
+       environment variable `SW_RSH' contains the name of a remote-shell\n\
+       program to start up the remote command (`rsh' and `ssh' work\n\
+       well); " RSH " is used by default.  The magic hostname `-'\n\
+       refers to the local machine, as does the current host's\n\
+       architecture name.  In these cases, the command is run directly.\n"
+   };
+#  undef CMD_LINK
+#  define CMD_LINK &cmd_rsh
+#endif
+
+#ifdef RCMD_LINK
+   static rcmd rcmd_rsh = { RCMD_LINK, "rsh", rsw_rsh };
+#  undef RCMD_LINK
+#  define RCMD_LINK &rcmd_rsh
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/sw-env b/sw-env
new file mode 100644 (file)
index 0000000..1851c34
--- /dev/null
+++ b/sw-env
@@ -0,0 +1,43 @@
+# sw-env
+#
+# Default environment for `sw' remote commands
+#
+
+# --- Syntax summary ---
+#
+# This file can contain the following sorts of statements:
+#
+#   arch ARCH { STATEMENTS }
+#   include FILE
+#   [set] VAR [=] VALUE
+#   unset VAR
+#
+# Statements may optionally be terminated by semicolons; this isn't
+# required for the syntax to work properly.  The `set' keyword is only
+# needed when VAR is one of the keywords.  Everything is case sensitive.
+#
+# The `arch' statement groups architecture-specific assignments
+# together.  Variables are only modified if ARCH matches the current
+# architecture.
+
+# --- The state of the world ---
+#
+# The initial environment is the (complete) invoking environment,
+# overridden by the (skeletal) local environment set up by the remote
+# shell.  Additionally, `$SW_ARCH' is the current architecture name.
+
+# --- Set a sensible path ---
+
+PATH = "/usr/bin:/bin:/usr/local/bin";
+arch sparc-solaris { PATH = "$PATH:/usr/ccs/bin:/opt/SUNWspro/bin"; }
+PATH = "$PATH:/sw/common/arch/$SW_ARCH:/ebi/supported/bin";
+
+# --- Remove some dross that will probably break things ---
+
+unset LD_LIBRARY_PATH;
+unset LD_PRELOAD;
+
+# --- Include user preferences, and package-specific definitions ---
+
+include "$HOME/.sw-env";
+include ".sw-env";
diff --git a/sw.1 b/sw.1
new file mode 100644 (file)
index 0000000..f3422a9
--- /dev/null
+++ b/sw.1
@@ -0,0 +1,855 @@
+.\" -*-nroff-*-
+.\"
+.\" $Id: sw.1,v 1.1 1999/06/02 16:53:33 mdw Exp $
+.\"
+.\" Manual page for `sw'
+.\"
+.\" (c) 1999 EBI
+.\"
+.\"----- Licensing notice ---------------------------------------------------
+.\"
+.\" This file is part of sw-tools.
+.\"
+.\" sw-tools is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\" 
+.\" sw-tools is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\" GNU General Public License for more details.
+.\" 
+.\" You should have received a copy of the GNU General Public License
+.\" along with sw-tools; if not, write to the Free Software Foundation,
+.\" Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+.\"
+.\"----- Revision history ---------------------------------------------------
+.\"
+.\" $Log: sw.1,v $
+.\" Revision 1.1  1999/06/02 16:53:33  mdw
+.\" Initial revision
+.\"
+.\"
+.\" --- Useful macro definitions ---
+.\"
+.de VS
+.sp 1
+.in +5n
+.nf
+.ft B
+..
+.de VE
+.ft R
+.fi
+.in -5n
+.sp 1
+..
+.\"
+.\" --- Style hacking for `groff' ---
+.\"
+.ie \n(.g \{\
+.      fam P
+.      de MW
+\fR[\f(BImdw\fR]
+..
+.\}
+.el \{\
+.      de MW
+\fR[\fBmdw\fR]
+..
+.\}
+.\"
+.\" --- Main manual text ---
+.\"
+.TH sw 1 "25 May 1999" "EBI tools"
+.PD 1
+.\"
+.SH NAME
+sw \- tool for convenient software installation
+.\"
+.SH SYNOPSIS
+.nf
+\fBsw --help
+\fBsw --help-full
+\fBsw --version
+\fBsw --archname
+\fBsw --remote \fIcommand
+
+\fBsw all-arch
+\fBsw arch
+\fBsw \fR[\fB\-fbi\fR] [\fB\-a \fIarch\fB,\fIarch\fR...] [\fB\-o \fIstyle\fR] \fBconfigure \fR[\fIconfgure-arg\fR...]
+\fBsw host \fIarch
+\fBsw \fR[\fB\-f\fR] [\fB\-a \fIarch\fB,\fIarch\fR...] \fBlinktree
+\fBsw listarch
+\fBsw \fR[\fB\-fbi\fR] [\fB\-a \fIarch\fB,\fIarch\fR...] [\fB\-o \fIstyle\fR] \fBmake \fR[\fImake-arg\fR...]
+\fBsw only-arch \fIarch \fR[\fIarch\fR...]
+\fBsw reset
+\fBsw rsh \fIhost\fR|\fIarch \fR[\fIcommand \fR[\fIargument\fR...]]
+\fBsw \fR[\fB\-fbi\fR] [\fB\-a \fIarch\fB,\fIarch\fR...] [\fB\-o \fIstyle\fR] \fBrun \fIcommand \fR[\fIargument\fR...]
+\fBsw setup \fIpackage version \fR[\fImaintainer\fR]
+\fBsw \fR[\fB\-f\fR] [\fB\-a \fIarch\fB,\fIarch\fR...] \fBsnaplink \fIfile \fR[\fIfile\fR...]
+\fBsw status
+.ft R
+.fi
+.\"
+.\"
+.SH "INTRODUCTION"
+The
+.B sw
+tool attempts to take a lot of the work out of building and installing
+source packages across multiple architectures.  This section will
+describe how to use
+.BR sw 's
+features to best advantage in a number of common situations.
+.PP
+To keep things concrete, I'll describe how things are done at the EBI,
+although there's nothing EBI-specific about the
+.B sw
+program itself.  For details about how we handle software at EBI, see
+the
+.B Local quirks
+section below.
+.PP
+By the way, this is quite a large manual.  I recommend that you print a
+copy onto paper and peruse it in a leisurely fashion, rather than
+squinting at a monitor.
+.\"
+.\"
+.SH "SUMMARY OF BUILDING PACKAGES"
+First, the
+.B autoconf
+case:
+.VS
+.BI "sw setup " "package version"
+.B "sw only \c"
+.IR "arch " [ arch ...]
+.ft B
+sw configure
+sw make
+sw \-i make install
+sw commit
+.VE
+Secondly, the
+.RB non- autoconf
+case:
+.VS
+.BI "sw setup " "package version"
+.B "sw only \c"
+.IR "arch " [ arch ...]
+.B "sw linktree"
+.BI "sw snaplink \c"
+.IR "file " [ file ...]
+.I [edit the appropriate files]
+.ft B
+sw make
+sw \-i make install
+sw commit
+.VE
+.\"
+.\"
+.SH "8 STEPS TO INSTALLING A PACKAGE"
+The following steps will guide you through your first (and maybe second)
+package installations.  In the description, I'll use
+.RI ` package '
+to refer to the package's name, and
+.RI ` version '
+to refer to its version number.
+.PP
+Not all the important features and options are described in this part of
+the manual.  View it more as a taster for the sorts of things
+.B sw
+can do, and a suggestion 
+.SS "1.  Download the source distribution"
+Download the package's source distribution.  This will normally be in an
+archive called something like
+.IB package - version .tar.gz\c
+\&.  At EBI, we put source archive files in
+.BR /sw/common/tr .
+.SS "2.  Unpack the source tree"
+Unpack the source tree into the standard source directory.  Each source
+tree should have its own directory.  Most well-packaged source
+distributions unpack themselves into a neat directory, but less
+fastidious programmers make archives which scatter files all over the
+current directory.
+.PP
+At EBI, we put the source trees in
+.BR /sw/common/src ,
+so unpacking a well-formed source distribution looks like:
+.VS
+cd /sw/common/src
+.BI "gzip \-dc ../tr/" package \- version ".tar.gz | tar xfv \-"
+.VE
+Ill-formed source distributions involve making the directory for the
+package first, changing into it, and then unpacking into the current
+directory:
+.VS
+cd /sw/common/src
+.BI "mkdir " package \- version
+.BI "cd " package \- version
+.BI "gzip \-dc ../../tr/" package - version ".tar.gz | tar xfv \-"
+.VE
+When you've finished unpacking, make sure that your current directory is
+the top level directory of the source tree you unpacked.
+.SS "3.  Tell sw what you're up to"
+Now you need to tell
+.B sw
+what you're working on.  It will keep track of this and other bits of
+information in a little file and refer to it every now and then.  It
+will also whinge at you and refuse to cooperate if it can't find its
+little file, so it's as well to oblige.
+.PP
+To tell
+.B sw
+to create this little file and initialize it with sensible values, you
+just need to say
+.VS
+.BI "sw setup " "package version"
+.VE
+What could be easier?
+.SS "4.  Restrict the build to particular architectures"
+Some packages don't work on all architectures, either because the author
+wasn't sufficiently good at writing portable software, or because the
+program's doing inherently nonportable things.
+.PP
+If that's the case, then
+you need to tell
+.B sw
+to only build on the architectures that really work.  Do this with the
+.RB ` "sw only" '
+command.  For example, if your package only works on Linux and Solaris,
+say:
+.VS
+sw only i386-linux sparc-solaris
+.VE
+You can get a list of the architecture names that
+.B sw
+understands by typing
+.VS
+sw listarch
+.VE
+With a little bit of luck, these names ought to be self-explanatory.
+.PP
+If your package is properly portable and works everywhere then you don't
+need to do anything for this step.  Skip on to the next one.
+.SS "5.  Configure the package"
+Now it gets complicated.  If the package you're building uses
+.B autoconf
+to configure itself for its current environment then you're in luck.
+You can tell an
+.B autoconf
+package because there's a script called
+.B configure
+in the top source directory, and a file called
+.BR Makefile.in .
+If it
+.I does
+use
+.B autoconf
+then run
+.VS
+sw configure
+.VE
+to configure the package on all the platforms it's meant to be built
+for.  When you've done that, move onto the next step.
+.PP
+If the package
+.I doesn't
+use
+.B autoconf
+then all is not lost (although it may be worthwhile complaining at the
+package's author or maintainers).  You need to make a collection of
+.IR "link trees" ,
+one for each architecture.  These link trees are little replicas of the
+main source tree but with symbolic links instead of the real source
+files.  To make the link trees, run
+.VS
+sw linktree
+.VE
+Now, that's not actually quite what you wanted.  It's made a link for
+.I every
+file in the source tree.  Unfortunately, there are some files you'll
+(probably) have to modify for each architecture in order to configure
+the package to build properly.  You can turn links in the link trees
+into real independently editable files by
+.I snapping
+the links.  Say for example that
+.B Makefile
+and
+.B config.h
+need to be modified for each architecture.  Running the command
+.VS
+sw snaplink Makefile config.h
+.VE
+is sufficient to do the right thing.
+.PP
+Now you must edit the snapped files to configure the package.  Make sure
+that the install directories are correctly set.  At EBI, all the
+software should be configured so that architecture neutral files end up
+under
+.B /sw/common
+and architecture-specific files end up under
+.BI /sw/common/arch/ arch\c
+\&.
+.SS "6.  Build the package"
+Now you've laid the groundwork, everything ought to be easy.  Making the
+program ought to involve simply typing
+.VS
+sw make
+.VE
+and waiting for a while.  If you had the
+.B curses
+library available when
+.B sw
+was built, then your terminal will split itself into little
+independently scrolling windows showing you the progress for each
+architecture.  If you're not privileged enough to have
+.B curses
+then you get the output appropriately tagged with architecture names,
+which is unfortunately fairly hard to read.
+.SS "7.  Install the package"
+Most source packages (and almost certainly all
+.B autoconf
+ones) have a
+.B make
+target
+.RB ` install '
+which installs the program correctly.  You can run this from
+.B sw
+by saying
+.VS
+sw \-i make install
+.VE
+The little
+.RB ` \-i '
+option there tells
+.B sw
+that this is the
+.IR "install step" .
+When an architecture completes this step correctly, it's marked as being
+properly installed, and
+.B sw
+doesn't bother thinking about it again.
+.PP
+If you
+.I don't
+have an
+.RB ` install '
+makefile target, then you have to install things manually.  That's not
+much fun, so moan at the package's author.  When you've finished
+fiddling with installation, run
+.VS
+sw -i run true
+.VE
+just to tell
+.B sw
+that you've installed everything OK.  (This is a bit of a kludge.)
+.SS "8.  Update the index"
+Now that everything's built and installed, there's just one more command
+to type:
+.VS
+sw commit
+.VE
+This makes
+.B sw
+update its main index of installed packages, telling it which
+architectures packages are installed on, and who did it.
+.\"
+.\"
+.SH "REFERENCE INTRODUCTION"
+That was a gentle introduction.  This section contains the complete
+reference to
+.BR sw :
+far more detail that you probably want.  If that's really the case, try
+running
+.VS
+sw \-\-help\-full
+.VE
+to read the available help text.  There's quite a lot of it, and it
+ought to keep you occupied for a while.
+.PP
+The basic
+.B sw
+command line looks a bit like:
+.sp 1
+.RS 5
+.B sw
+.RI [ options ]
+.RI [ command
+.RI [ argument ...]]
+.RE
+.sp 1
+If you just say
+.VS
+sw
+.VE
+at the shell prompt,
+.B sw
+gives you an extremely terse usage summary and quits.  You have to tell
+it to do
+.IR something .
+Most of the time you do this by giving
+.B sw
+a
+.IR command ,
+like
+.RB ` setup '
+or
+.RB ` make '
+so that it knows what to do.  There are some strange command line
+options which cause
+.B sw
+to do more exotic things, though.
+.\"
+.\"
+.SH "IMPLEMENTATION ODDITIES"
+The
+.B sw
+program that users use is really a small architecture-neutral shell
+script, which works out the current architecture and executes the
+appropriate architecture-specific main program.  It's done this way so
+that
+.B sw
+knows that it can use the shell script to start itself up on a remote
+host with a different architecture, something which it does quite a
+lot.  The only feature provided by the front-end shell script is the
+.B \-\-archname
+command line option, which shouldn't be used by anyone except
+.BR sw 's
+build procedure anyway.
+.\"
+.\"
+.SH "COMMAND LINE OPTION REFERENCE"
+Any
+.B sw
+command line options can be put in the
+.B SW
+environment variable.  The
+.B sw
+program will read space-separated options from this variable before it
+reads the command line itself.
+.PP
+The
+.B sw
+program usually understands two different names for each option: a
+traditional Unix single-character name, and a long GNU-style name.  The
+short options behave in the normal Unix way: you can join them together
+into single words with a
+.RB ` \- '
+at the front, for example.  The long names are always preceded by a
+double dash.  You can abbreviate long names as much as you like, as long
+as the resulting abbreviation is unambiguous.  In the descriptions
+below, both the short and long names of the options are shown, but for
+reasons of brevity required arguments are only shown for the long form.
+.PP
+There are conceptually two types of
+.B sw
+command line options: those which, usually for reasons of consistency
+with other programs, cause
+.B sw
+to do something immediately; and those which store some settings for
+particular commands.  The latter type are generally more useful.  It's
+worth bearing in mind, though, that the options are only used by a few
+commands.  The command reference describes exactly which commands use
+which options.
+.PP
+The complete list of command line options understood by the current
+version of
+.B sw
+is as follows:
+.TP
+.B "\-h, \-\-help"
+Writes a fairly brief summary of
+.BR sw 's
+command line options and a usage line for each of
+.BR sw 's
+commands to standard output, and exits successfully.
+.TP
+.B "\-H, \-\-help\-full"
+Writes a summary of
+.BR sw 's
+command line options and a full paragraph of description for each of
+.BR sw 's
+commands to standard output, and exits successfully.  There's a lot of
+text generated by this option.  I recommend you pipe it through a pager
+so that you can actually read it.
+.TP
+.B "\-v, \-\-version"
+Writes
+.BR sw 's
+version number to standard output and exits successfully.  This is handy
+when trying to decide whether your version of
+.B sw
+has a particular feature, for example.
+.TP
+.B "\-u, \-\-usage"
+Writes a usage message so terse as to be nearly useless to standard
+output and exits successfully.  This is different from just running
+.RB ` sw '
+because although both print the same useless message, running
+.B sw
+without any arguments is considered an error, so the message is sent to
+standard error and
+.B sw
+will exit unsuccessfully.
+.TP
+.BI "\-a, \-\-arch " arch , arch\fR...
+For commands which affect multiple architectures: only affect the
+architectures specified.  The architecture names may be separated by
+commas, spaces or both, although clearly commas are most convenient on
+the command line.  This option overrides any other decisions that
+.B sw
+might make about which architectures to process based on the
+.B only-arch
+list and the list of correctly built architectures for the current
+package.
+.TP
+.B "\-f, \-\-force"
+For commands which affect multiple architectures: affect even
+architectures that have been successfully built.  This has no effect if
+there's a
+.RB ` \-a '
+option in force.
+.TP
+.B "\-i, \-\-install"
+For build commands: this is the final install step, so label architectures
+which successfully complete it as having been completely built.  It's
+normal to specify this option on the
+.RB ` "make install" '
+build command.
+.TP
+.BI "\-o, \-\-output " style
+For build commands: select a style for the build output to be displayed
+in.  See the section
+.B "Build commands"
+for more details on output styles.
+.TP
+.B "\-b, \-\-beep"
+For build commands: make a beep noise when the build finishes.  This
+provides a handy reminder if you're getting on with something else while
+waiting for a long build.
+.PP
+The remaining options aren't really intended for users.  They're helpful
+for
+.BR sw 's
+own purposes, though, and described here for completeness' sake.  They
+don't have standard Unix short name equivalents, because they're not
+usually useful for users.
+.TP
+.B "\-\-archname"
+Writes the
+.B sw
+architecture name of the current host to standard output.  This is used
+by
+.BR sw 's
+configuration script to determine the current architecture name.  This
+option is actually handled by a small shell script rather than by being
+passed on to the main program.  You shouldn't use this option yourself:
+use the
+.RB ` arch '
+command instead.  Because this option is handled by the shell script,
+and the script isn't very clever, you can't abbreviate
+.B \-\-archname
+on the command line, and it doesn't conflict with the similarly named
+but completely different
+.B \-\-arch
+option, which you can still abbreviate all the way down to just
+.RB ` \-\-a '.
+.TP
+.BI "\-\-me " name
+Sets
+.BR sw 's
+idea of its program name to
+.IR name .
+This is intended for use by
+.BR sw 's
+front-end shell script, but isn't actually needed at the moment.  I
+can't see why you'd want to play with this option, but it shouldn't do
+any harm.
+.TP
+.BI "\-\-remote " remote-command
+Used by
+.B sw
+when running commands on remote hosts.  Don't use this yourself: it puts
+.B sw
+into a very unfriendly mode and requires that you communicate with it
+using a bizarre binary packet protocol.  If you really must know more
+about this, see the source code: it's quite well documented, really.
+.\"
+.\"
+.SH "COMMAND REFERENCE"
+.SS Terminology
+The descriptions below make use of some technical terms:
+.TP
+.B "architecture restriction"
+A state created by the
+.B only\-arch
+command, restricting the
+.I "default build architectures"
+to those listed as arguments to the command.  An architecture
+restriction may be cleared by
+.B all\-arch
+command.
+.TP
+.B "build architectures"
+The architectures which a
+.I "build command"
+will process.  If the
+.RB ` \-a '
+option is specified on the command line, then its argument specifies the
+build architectures for this command; otherwise, the
+.I "default build architectures"
+are used.
+.TP
+.B "build command"
+A command which executes a process on multiple hosts simultaneously and
+reports the results.  The processes executed usually perform some part
+of the building of a package.  Currently, the build commands are
+.B run
+and its derivatives
+.B configure
+and
+.BR make .
+.TP
+.B "default build architectures"
+The architectures which, in the absence of a
+.RB ` \-a '
+command line option, are affected by a
+.IR "build command" .
+To determine the default build architectures,
+.B sw
+reads the list of all architectures from the
+.B archtab
+file, and filters it: if the
+.RB ` \-f '
+command line option is
+.I not
+specified, then architectures marked as
+.I "successfully built"
+are removed from the list; if there is an
+.I "architecture restriction"
+in force, then the list is further filtered according to the
+restriction.
+.TP
+.B "successfully built"
+A package is considered to be successfully built on a given architecture
+if a build command given the
+.RB ` \-i '
+command-line option succeeds on a host of that architecture.  The list
+of successfully built architectures can be cleared by the
+.B reset
+command.  The
+.RB ` \-f '
+option causes
+.B sw
+to ignore whether architectures have been successfully built when
+determining the
+.IR "default build architectures" .
+.bp
+This section describes all of the available
+.B sw
+commands, in alphabetical order.
+.\"
+.SS all-arch
+.PP
+Clears an architecture restriction set by
+.RB ` only-arch '.
+Subsequent build commands will run across all known architectures not
+yet successfully built, unless overridden by the
+.RB ` \-a '
+command-line option, or a later
+.RB ` only-arch '
+command.
+.\"
+.SS arch
+Writes the name of the local host's architecture to standard output.
+The architecture name is built into
+.B sw
+at compile time.
+.\"
+.SS configure \fR[\fIconfigure-arg\fR...]
+.\"
+.SS host \fIarch\fR
+Writes to standard output the name of a host with requested architecture
+.IR arch .
+The hostname is read from the
+.B archtab
+file.
+.\"
+.SS linktree
+Builds symbolic link trees.  For each of the build architectures, a
+directory with the architecture's name is created containing a symbolic
+link corresponding to each file in the main source tree.  Thus, a `make'
+in the link tree will fetch the source files correctly, but place the
+objects in the link tree rather than the main source tree, so that
+object files from different architectures don't interfere with each
+other.
+.PP
+If the link trees already exist, then rerunning
+.B linkfree
+will update the links.  This might be useful if the links somehow become
+invalid.
+.PP
+To turn some of the links in the link trees into real files, use the
+.B snaplink
+command.
+.\"
+.SS listarch
+Writes a list of all known architecture names to standard output.  The
+list is obtained by reading the
+.B archtab
+file.
+.\"
+.SS make \fR[\fImake-arg\fR...]
+.\"
+.SS only-arch \fIarch arch\fR...
+.\"
+.SS rsh \fIhost\fR|\fIarch \fR[\fIcommand \fR[\fIargument\fR...]]
+.\"
+.SS run \fIcommand \fR[\fIargument\fR...]
+.\"
+.SS setup \fIpackage version \fR[\fImaintainer\fR]
+.\"
+.SS snaplink \fIfile \fR[\fIfile\fR...]
+Creates archtitecture-specific versions of a file.  For each build
+architecture
+.I arch
+and file 
+.IR file ,
+.B sw
+creates a copy
+.IB arch / file\c
+, replacing
+.\"
+.SS status
+foo
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.bp
+.\"
+The style name
+.RB ` plain '
+is always defined: it simply prefixes each line of output with the
+name of the architecture which generated the line, which isn't actually
+particularly easy to read.  Other output styles may have been configured
+into
+.B sw
+when it was compiled.
+.PP
+The set of output styles supported by
+.B sw
+varies according to how it was configured.  In any particular
+.B sw
+program, you might have some of the following:
+.TP
+.B plain
+Simply prefixes each output line with the name of the architecture it
+came from.  This is quite hard to read, but it doesn't require any
+special operating system support or clever terminal.
+.TP
+.B curses
+Splits the terminal into independently scrolling areas, one for each
+architecture, with a status line for each.  Waits for a keypress when
+all architectures are finished building.
+.PP
+The
+.RB ` plain '
+style is always available.  It's used when the selected style doesn't
+work (for example, you don't have a sufficiently capable terminal for
+curses output).
+.PP
+Output style names can be abbreviated as long as the abbreviation is
+unambiguous.
+.PP
+The author has plans to implement an X-based output style, but hasn't
+got around to it yet.
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.\"
+.SH ENVIRONMENT
+The following environment variables are of interest to
+.BR sw :
+.TP
+.B SW
+Contains a space-separated list of default command-line options.  These
+are read before, and overridden by, the actual arguments given on the
+command-line.
+.TP
+.B SW_RSH
+The name of the remote-shell program to use.  By default, something
+similar to
+.B rsh
+is chosen.  I recommend using the excellent
+.B ssh
+program instead.
+.\"
+.SH FILES
+The following files are of interest to
+.BR sw :
+.TP
+.IB prefix /sw-index
+The main index file, containing the list of which packages have been
+installed for which architectures.  See
+.BR sw-info (5)
+for file format details.
+.TP
+.IB prefix /share/archtab
+The architecture-to-host mapping file.  See
+.BR archtab (5)
+for file format details.
+.TP
+.IB prefix /share/sw-env
+Contains global environment variable settings.  See
+.BR sw-env (5)
+for file format details.
+.TP
+.IB package /.sw-info
+Contains the persistent information about a particular package's build
+status.  See
+.BR sw-info (5)
+for file format details.
+.TP
+.IB package /.sw-env
+Contains package-specific environment variable settings.  See
+.BR sw-env (5)
+for file format details.
+.TP
+.IB package / arch /.build-log
+Contains all the build output for a particular architecture.  Usually
+not very interesting, but might be handy one day.
+.\"
+.SH BUGS
+There are no bugs in
+.BR sw ,
+merely unexpected behaviour modes.  Silly you for thinking otherwise.
+.SH AUTHOR
+The
+.B sw
+program, and this manual, are
+.MW
+productions, in association
+with the European Bioinformatics Instutute.  They were written by Mark
+Wooding <mdw@nsict.org>.  Go and ask him if you have problems.
+.\"
+.\"----- That's all, folks --------------------------------------------------
diff --git a/sw.in b/sw.in
new file mode 100755 (executable)
index 0000000..756d9cb
--- /dev/null
+++ b/sw.in
@@ -0,0 +1,84 @@
+#! /bin/sh
+
+# -*-sh-*-
+#
+# $Id: sw.in,v 1.1 1999/06/02 16:53:32 mdw Exp $
+#
+# Determine a canonical `sw' architecture name
+#
+# MAKE SURE I GET UPDATED WHEN NEW ARCHITECTURES ARE ADDED!
+#
+# (c) 1999 EBI
+#
+
+#----- Licensing notice -----------------------------------------------------
+#
+# This file is part of sw-tools.
+#
+# sw-tools is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# sw-tools is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with sw-tools; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#----- Revision history -----------------------------------------------------
+#
+# $Log: sw.in,v $
+# Revision 1.1  1999/06/02 16:53:32  mdw
+# Initial revision
+#
+
+# --- Commentary ---
+#
+# This is deliberately *not* the same as Autoconf's `config.guess'.  The GNU
+# names are too specific for our purposes, because they encode version
+# numbering information that we don't really want to know about.  This
+# operation is deliberately simple, so that new things can be added easily.
+
+# --- Step one: find out about the CPU architecture ---
+
+CPU=`uname -p`; CPU=`echo $CPU | tr A-Z a-z`
+case $CPU in
+  unknown) CPU=`uname -m` ;;
+esac
+
+case $CPU in
+  i?86) CPU=i386 ;;
+esac
+
+# --- Step two: find out about the OS ---
+
+OS=`uname -s`; OS=`echo $OS | tr A-Z a-z`
+
+case $OS in
+  irix*) OS=irix ;;
+  sunos*) OS=solaris ;;
+esac
+
+# --- Write the result ---
+
+arch=$CPU-$OS
+case "$1" in --archname) echo $arch; exit 0;; esac
+
+# --- Run the main `sw' binary ---
+#
+# Ugly hack for broken OSF1 /bin/sh.
+
+prefix=@prefix@
+datadir=@datadir@
+sw=@SW@
+if [ $# = 0 ]; then
+  exec "${datadir}/libexec/${arch}/${sw}"
+else
+  exec "${datadir}/libexec/${arch}/${sw}" "$@"
+fi
+
+#----- That's all, folks ----------------------------------------------------