--- /dev/null
+Makefile.in
+aclocal.m4
+config.h.in
+configure
+stamp-h.in
+build
+alpha
+sgi
+sparc
+install
+.deps
--- /dev/null
+missing
+mkinstalldirs
+install-sh
+COPYING
--- /dev/null
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+ (append
+ '((author . "EBI")
+ (full-title . "sw-tools")
+ (program . "sw-tools"))
+ skel-alist))
--- /dev/null
+;;; -*-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))
--- /dev/null
+## -*-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 ---------------------------------------------------
--- /dev/null
+/* -*-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
--- /dev/null
+# archtab
+
+i386-linux pointy
+sparc-solaris bach
+alpha-osf1 mozart
+mips-irix columba
--- /dev/null
+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 -------------------------------------------------
--- /dev/null
+#! /bin/sh
+
+set -e
+mklinks
+mkaclocal
+ln -s ../mLib .
+automake
+autoconf
+mkdir build
--- /dev/null
+## -*-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 ---------------------------------------------------
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+# 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";
--- /dev/null
+.\" -*-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 --------------------------------------------------
--- /dev/null
+#! /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 ----------------------------------------------------