[comp.sources.misc] v06i009: xc: a mini-make

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (01/24/89)

Posting-number: Volume 6, Issue 9
Submitted-by: edf@ROCKY2.ROCKEFELLER.EDU (David MacKenzie)
Archive-name: xc

Xc is a program to execute commands within source code files.
It's mainly useful for compiling small programs where there are 
many of them stored in one directory and keeping a lot of makefiles
around would be a pain.  It doesn't do any timestamp-checking
like make does, but it's not really meant for projects that are
composed of multiple source files anyway.

A typical C program source file might start out like

/*
% cc -O foo.c -o foo -ltermcap
%i cc -O foo.c -o foo -ltermcap
%i cp foo /usr/new; chmod 755 /usr/new/foo
*/

So you could ``xc foo.c'' to make it, or ``xc -i foo.c'' to make and
install it.

Xc is distantly descended from an anonymous program of the same name
that I found on a computer at St. Olaf College a couple of years ago.
Not much remains of the original code, though.

-----
David MacKenzie
Environmental Defense Fund
edf@rocky2.rockefeller.edu (...rutgers!cmcl2!rocky2!edf)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  xc.1 xc.c
# Wrapped by dave@zedfdc  on Fri Dec 30 02:09:39 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'xc.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xc.1'\"
else
echo shar: Extracting \"'xc.1'\" \(1327 characters\)
sed "s/^X//" >'xc.1' <<'END_OF_FILE'
X.TH XC 1L
X.SH NAME
Xxc, xcb \- execute commands contained in a file
X.SH SYNOPSIS
X.B xc
Xor
X.B xcb
X[
X.B \-c key_character
X] [
X.B file
X]
X.SH DESCRIPTION
X.I Xc
Xsearches a
X.I file
Xfor a sequence of lines with the form
X.sp
X%c command_line
X.sp
Xor
X.sp
X$c command_line
X.sp
Xwhere 'c' is a single key character given with the
X.I \-c
Xcommand line option.
XIt then executes each
X.I command_line
Xin turn.
X.PP
XIf the
X.I \-c
Xoption is omitted, lines with the form
X.sp
X% command_line
X.sp
Xor
X.sp
X$ command_line
X.sp
Xare executed.  The `%' form echos the command line; the `$' form does not.
X.PP
XWhen invoked as
X.I xcb,
Xthe program automatically runs in the background, redirects the
Xstandar output and standard error output to the file
X.BR ./ERRS ,
Xand makes a short announcement
Xon its controlling terminal when it finishes.
X.PP
X.I Xc
Xsaves the last filename and key character which were used
Xinto the file
X.BR $HOME/.xc ,
Xand uses that file's contents as the defaults if the
X.I file
Xargument is omitted.
X.PP
XIf the
X.I file
Xcontains program source code, the
X.I xc
Xcommand lines should probably be enclosed within comments;
Xhowever, the `%' and `$' must be the first characters on their lines,
Xand be immediately followed by the key characters.
X.SH FILES
X$HOME/.xc
X.PP
XERRS
X.SH "SEE ALSO"
X.BR cc (1),
X.BR sh (1)
X.SH AUTHOR
XDavid MacKenzie (mostly)
END_OF_FILE
if test 1327 -ne `wc -c <'xc.1'`; then
    echo shar: \"'xc.1'\" unpacked with wrong size!
fi
# end of 'xc.1'
fi
if test -f 'xc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xc.c'\"
else
echo shar: Extracting \"'xc.c'\" \(6428 characters\)
sed "s/^X//" >'xc.c' <<'END_OF_FILE'
X/*
X   xc - execute commands contained in a file
X   xcb - same, but in the background
X
X   Usage: xc  [-c key_character] [file]
X          xcb [-c key_character] [file]
X
X   xc -c x file	key: 'x'	file: "file"
X   xc file	key: ' '	file: "file"
X   xc -c x	key: 'x'	file: ~/.xc
X   xc		key: ~/.xc	file: ~/.xc
X
X   Searches a file for a sequence of lines (probably in a comment)
X   with the form
X   %c command_line
X   or
X   $c command_line
X   where 'c' (any single character) in "%c" or "$c" is the same as
X   the key character given with the "-c" option on the command line,
X   and executes the command_lines.
X   If the "-c" option is omitted, lines with the form
X   % command_line
X   or
X   $ command_line
X   are executed.
X   The '%' form echos the command_line; the '$' form does not.
X
X   When invoked as xcb, it automatically runs in the background, redirects
X   stdout and stderr to the file ./ERRS, and announces when it finishes.
X
X   It saves the last filename and key character which were used
X   into $HOME/.xc, and uses that file's contents as the defaults if the
X   filename argument is omitted.
X   $HOME/.xc contains one line of the form:
X   c-filename
X   where 'c' is the key character, or a space if none was given.
X
X   Latest modification: 12/30/88 */
X
X#include <stdio.h>
X
X#define XCFILE      "/.xc"	/* Where default file, key are read from. */
X#define ERRFILE     "ERRS"	/* Where output goes if in background.  */
X
Xvoid perror (), exit ();
Xchar *getenv (), *strcpy (), *strcat (), *strchr (), *strrchr (), *fgets ();
X
Xvoid pfatal (), usage ();
Xvoid read_xc_file (), write_xc_file ();
Xvoid detatch (), notify (), execute ();
Xchar *make_xc_name (), *basename ();
X
Xchar *program_name;		/* Base of program name. */
X
Xmain (argc, argv)
X  int argc;
X  char **argv;
X{
X  extern int optind;		/* Option index. */
X  extern char *optarg;		/* Option argument. */
X  char *xcfile;			/* Expansion of "$HOME/.xc". */
X  char *file_name;		/* File to get commands from. */
X  char key;			/* Command line selector. */
X  int background;		/* Run in the background? */
X  int c;			/* Option character. */
X
X  program_name = basename (argv[0]);
X  file_name = NULL;
X  key = 0;
X  background = !strcmp (program_name, "xcb");
X  xcfile = make_xc_name ();
X
X  while ((c = getopt(argc, argv, "c:")) != EOF)
X    switch (c)
X      {
X      case 'c':
X	if (strlen (optarg) != 1)
X	  {
X	    fprintf (stderr, "%s: %s: Key must be one character long\n",
X	      argv[0], optarg);
X	    usage ();
X	  }
X	key = *optarg;
X	break;
X      default:
X	usage ();
X      }
X
X  if (optind < argc - 1)
X    usage ();
X  else if (optind == argc - 1)
X    file_name = argv[optind];
X  else
X    /* Read filename, and key if not set yet, from XCFILE. */
X    read_xc_file (xcfile, &file_name, &key);
X
X  if (key == 0 || key == '\t')
X    key = ' ';
X
X  if (background)
X    detatch ();
X
X  execute (file_name, key);
X
X  write_xc_file (xcfile, file_name, key);
X
X  if (background)
X    notify ();
X
X  exit (0);
X}
X
X/* Return the path of the file "$HOME/.xc".  */
X
Xchar *make_xc_name ()
X{
X  static char xcbuf[BUFSIZ];	/* "$HOME/XCFILE". */
X  char *home;
X
X  home = getenv ("HOME");
X  if (!home)
X    {
X      fprintf (stderr,
X	"%s: HOME environment variable is not defined\n", program_name);
X      exit (1);
X    }
X  (void) strcpy (xcbuf, home);
X  (void) strcat (xcbuf, XCFILE);
X  return xcbuf;
X}
X
X/* Read from xcfile the filename, and the key if it's not already set.  */
X
Xvoid read_xc_file (xcfile, file_namep, keyp)
X  char *xcfile;
X  char **file_namep;
X  char *keyp;
X{
X  FILE *fp;
X  char *newline;
X  static char line[BUFSIZ];
X
X  fp = fopen (xcfile, "r");
X  if (!fp)
X    pfatal (xcfile);
X  (void) fgets (line, BUFSIZ, fp);
X  newline = strchr (line, '\n');
X  if (newline)
X    *newline = 0;
X  (void) fclose (fp);
X
X  *file_namep = &line[2];
X  if (*keyp == 0)
X    *keyp = *line;
X}
X
X/* Update the xcfile.  */
X
Xvoid write_xc_file (xcfile, file_name, key)
X  char *xcfile;
X  char *file_name;
X  char key;
X{
X  FILE *fp;
X
X  fp = fopen (xcfile, "w");
X  if (!fp)
X    return;
X  fprintf (fp, "%c-%s\n", key, file_name);
X  (void) fclose (fp);
X}
X
Xstatic int save_stdout;		/* Save file descriptor for notification. */
X
X/* Redirect the standard streams and fade into the background.  */
X
Xvoid detatch ()
X{
X  save_stdout = dup (1);
X  if (save_stdout == -1)
X    pfatal ("Can't dup standard output");
X  if (!freopen (ERRFILE, "w", stdout))
X    pfatal (ERRFILE);
X  (void) close (2);
X  if (dup (1) != 2)		/* Could be 0 if stdin was closed. */
X    if (dup (1) == -1)
X      pfatal ("Can't dup standard output");
X  switch (fork ())
X    {
X    case -1:
X      pfatal ("Can't fork");
X    case 0:
X      break;
X    default:
X      exit (0);
X    }
X}
X
Xvoid notify ()
X{
X  (void) write (save_stdout, "\r\n\007[xcb: done]\r\n", 16);
X  printf ("Done\n");
X  (void) fflush (stdout);
X}
X
X/* Read the file and execute command lines that match the key.  */
X
Xvoid execute (file_name, key)
X  char *file_name;
X  char key;
X{
X  FILE *fp;			/* Pointer to file stream. */
X  char line[BUFSIZ];		/* One line of file. */
X  char *command;		/* Command to execute. */
X  int key_matched = 0;		/* Has command line matching key been found? */
X  int status;			/* Return status from system. */
X
X  fp = fopen (file_name, "r");
X  if (!fp)
X    pfatal (file_name);
X
X  while (fgets (line, BUFSIZ, fp))
X    {
X      if (line[1] == '\t')
X	line[1] = ' ';
X      if ((*line != '%' && *line != '$') || line[1] != key)
X	{
X	  if (key_matched)
X	    break;
X	  else
X	    continue;
X	}
X      key_matched = 1;
X      /* Skip leading whitespace in the command for aesthetic reasons. */
X      for (command = &line[2];
X	*command && (*command == ' ' || *command == '\t'); ++command)
X	 /* Do nothing. */ ;
X      if (*line == '%')
X	{
X	  printf ("  %s", command);
X	  (void) fflush (stdout);
X	}
X      status = system (command);
X      if (status)
X	{
X	  fprintf (stderr, "%s: ** Exit status 0%o **\n", program_name, status);
X	  break;
X	}
X    }
X  (void) fclose (fp);
X
X  if (!key_matched)
X    {
X      fprintf (stderr,
X	"%s: %s: No commands for key '%c'\n", program_name, file_name, key);
X      exit (1);
X    }
X}
X
X/* Return name with any leading path stripped off.  */
X
Xchar *basename (name)
X  char *name;
X{
X  char *base;
X
X  base = strrchr (name, '/');
X  return base ? base + 1 : name;
X}
X
Xvoid pfatal (message)
X  char *message;
X{
X  fprintf (stderr, "%s: ", program_name);
X  perror (message);
X  exit (1);
X}
X
Xvoid usage ()
X{
X  fprintf (stderr, "Usage: %s [-c key_character] [file]\n", program_name);
X  exit (1);
X}
END_OF_FILE
if test 6428 -ne `wc -c <'xc.c'`; then
    echo shar: \"'xc.c'\" unpacked with wrong size!
fi
# end of 'xc.c'
fi
echo shar: End of shell archive.