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