From: mdw Date: Wed, 2 Jun 1999 16:53:31 +0000 (+0000) Subject: Initial revision X-Git-Tag: 1.0.2~56 X-Git-Url: https://git.distorted.org.uk/~mdw/sw-tools/commitdiff_plain/3315e8b31a4707ef2c5491d0c9a9c9a09816bcb2 Initial revision --- 3315e8b31a4707ef2c5491d0c9a9c9a09816bcb2 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 ----------------------------------------------------