mlm@cad.cs.cmu.edu.ARPA (Michael Mauldin) (10/14/85)
#!/bin/sh
#
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# @ Here is your new automatic Robots player, Rob-O-Matic! @
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#
# To unpack this program, run this message through 'unshar', or
# remove the mail headers and pipe the body through the "sh" command.
# Then "make" should build the program for you.
#
# A tradition of fine game playing programs,
# from the People who brought you Rog-O-Matic.
#
# Michael L. Mauldin (Fuzzy) Department of Computer Science
# Mauldin@CAD.CS.CMU.EDU Carnegie-Mellon University
# (412) 578-3065 Pittsburgh, PA 15213
#
echo 'Start of Rob-O-Matic Distribution:'
echo 'x - robomatic.6'
sed 's/^X//' > robomatic.6 << '/'
X.TH ROBOMATIC 6 10/14/85
X.UC 4
X.SH NAME
Xrobomatic \- Automatically Playing a Game of Logic
X.SH SYNOPSIS
X.I robomatic
X[
X.I -d
X]
X.SH DESCRIPTION
X.PP
X.I Rob-O-Matic
XPlays the "Robots" game written by
XAllan Black of Strathclyde University, Glasgow.
X.SH OPTIONS
X.TP
X.B -d
XTurns on debugging.
X.SH FILES
X.PP
XLooks for the Robots executable in the current directory, then in two
Xplaces specified at compile-time (one of which is /usr/games).
X.SH SEE ALSO
X.PP
Xrobots(6)
X.SH BUGS
X.PP
XDoesn't play an optimum game.
X.SH HISTORY
X.TP
X14-Oct-85 Michael Mauldin (mlm) at Carnegie-Mellon University
XCreated.
/
echo 'x - robomatic.c'
sed 's/^X//' > robomatic.c << '/'
X/* robomatic.c: Rob-O-Matic I (CMU) Mon Oct 14 11:51:01 EDT 1985 - mlm */
X/*****************************************************************
X *
X * Rob-O-Matic: Play the robots game
X * Copyright (C) 1985 by Mauldin
X * The right is granted to any person, university, or company
X * to copy, modify, or distribute (for free) this file, providing
X * that this notice is not removed.
X *
X * HISTORY
X * 11-Oct-85 Michael Mauldin (mlm) at Carnegie-Mellon University
X * Created.
X *
X *****************************************************************/
X
X# include <curses.h>
X
X# define NEWROBOT "/usr/mlm/bin/robots"
X# define ROBOT "/usr/games/robots"
X
X# define READ 0
X# define WRITE 1
X# define abs(A) ((A)>0?(A):-(A))
X# define max(A,B) ((A)>(B)?(A):(B))
X# define sgn(A) ((A)==0?0:((A)>0?1:-1))
X
X/* Define the Rob-O-Matic pseudo-terminal */
X
X# define ROBOTTERM "rb|rterm:am:bs:ce=^E:cl=^L:cm=^F%+ %+ :co#80:li#24:pt:ta=^I:up=^A:do=^B:nd=^C:db:xn:"
X# define ctrl(C) ((C)&037)
X# define BL ctrl('G')
X# define BS ctrl('H')
X# define CE ctrl('E')
X# define CL ctrl('L')
X# define CM ctrl('F')
X# define CR ctrl('M')
X# define DO ctrl('B')
X# define LF ctrl('J')
X# define ND ctrl('C')
X# define TA ctrl('I')
X# define UP ctrl('A')
X
Xint child;
Xint frobot, trobot;
Xint debug=0;
XFILE *trace=NULL;
X/****************************************************************
X * Main routine
X ****************************************************************/
X
Xmain (argc, argv)
Xint argc;
Xchar *argv[];
X
X{ int ptc[2], ctp[2];
X char *rfile=NULL;
X
X /* Get the options from the command line */
X while (--argc > 0 && (*++argv)[0] == '-')
X { while (*++(*argv))
X { switch (**argv)
X { case 'd': debug++; break;
X default: printf ("Usage: robomatic [-d]\n"); exit (1);
X }
X }
X }
X
X /* Open tracing file (if needed) */
X if (debug)
X { if ((trace = fopen ("trace.log", "w")) == NULL)
X { perror ("trace.log");
X exit (1);
X }
X }
X
X /* Find an executable of the robots game */
X if (access ("robots", 1) == 0) rfile = "robots";
X else if (access (NEWROBOT, 1) == 0) rfile = NEWROBOT;
X else if (access (ROBOT, 1) == 0) rfile = ROBOT;
X else
X { perror ("robots");
X exit (1);
X }
X
X /* Get two pipes to attach to the Robots process */
X if ((pipe (ptc) < 0) || (pipe (ctp) < 0))
X { fprintf (stderr, "Cannot get pipes!\n");
X exit (1);
X }
X
X trobot = ptc[WRITE];
X frobot = ctp[READ];
X
X /* Now fork a child process, update the TERMCAP, and exec the Robots */
X if ((child = fork ()) == 0)
X { close (0);
X dup (ptc[READ]);
X close (1);
X dup (ctp[WRITE]);
X
X putenv ("TERMCAP", ROBOTTERM);
X execl (rfile, rfile, 0);
X _exit (1);
X }
X
X /* Call Robomatic as the Parent Process */
X else
X { robomatic (); }
X}
X
X/****************************************************************
X * Robomatic: Play Robots. Read the scrren, choose a move, and send it
X ****************************************************************/
X
X# define ROWS 24
X# define COLS 80
X
Xchar screen[ROWS][COLS];
Xint playing=1;
Xint row=0, col=0, atrow= -1, atcol= -1;
X
Xrobomatic ()
X{ int cmd;
X
X /* Initialize the Curses package */
X initscr (); crmode (); noecho (); clear (); refresh ();
X
X /* Clearn the screen array */
X blankscreen ();
X
X /* Read the first screen of output */
X send (';'); /* Cause robots to send a bell when ready to read cmd */
X getrobot (); /* Read screen updates until a bell */
X
X /* Main loop, send a command and then read the screen */
X while (playing)
X {
X /* If the user types anything, execute his commands */
X while (charsavail (stdin))
X { switch (getchar ())
X { case 'd': debug++;
X break;
X case 'r': clear (); refresh (); drawscreen (); refresh ();
X break;
X default: break;
X }
X }
X
X /* Choose a command and send it */
X cmd = strategy ();
X if (cmd == 0) { debug++; dwait ("command is zero"); }
X dwait ("Sending command '%c'.", cmd);
X send (cmd);
X
X /* Send the semicolon and read screen updates until a bell */
X send (';');
X getrobot ();
X }
X
X /* Clear the scoreboard on some Robots */
X send ('\n');
X
X /* Now wait for the user to type a character before finishing */
X refresh ();
X getchar ();
X
X /* Print termination messages */
X move (ROWS-1, 0); clrtoeol (); refresh ();
X endwin (); nocrmode (); noraw (); echo ();
X
X deadrobot ();
X
X exit (0);
X}
X
X/****************************************************************
X * blankscreen: Fill the screen array with blanks
X ****************************************************************/
X
Xblankscreen ()
X{ register int i, j;
X
X for (i=0; i<ROWS; i++)
X for (j=0; j<COLS; j++)
X screen[i][j] = ' ';
X}
X
X/****************************************************************
X * getrobot: Read the Robot screen, stop when you read a bell
X ****************************************************************/
X
Xgetrobot ()
X{ int r, c, ch, done=0;
X register int i, j;
X char *d, *h, buf[BUFSIZ];
X
X d = "robot food"; /* FSM to check for death */
X h = "scrap heaps"; /* FSM to check for new level */
X
X while (!done)
X {
X /* Read a character from the Robots process */
X if (read (frobot, buf, 1) < 1)
X { ch = EOF; if (trace) fclose (trace); }
X else
X { ch = buf[0]; if (trace) { fputc (ch, trace); fflush (trace); } }
X
X /* Check for the words "robot food" to see if we died */
X if (ch == *d) { if (0 == *++d) { done++; playing=0; } }
X else d = "robot food";
X
X /* Check for the words "scrap heaps" to see if we survived a level */
X if (ch == *h) { if (0 == *++h) { send (';'); } }
X else h = "scrap heaps";
X
X /* Now figure out what the character means */
X switch (ch)
X { case BL: /* Bell */
X done++;
X break;
X
X case BS: /* BackSpace */
X col--;
X break;
X
X case CE: /* Clear to end of line */
X for (i = col; i < COLS; i++)
X screen[row][i] = ' ';
X
X move (row, col);
X clrtoeol ();
X break;
X
X case CL: /* Clear Screen */
X clear ();
X blankscreen ();
X row = col = 0;
X break;
X
X case CM: /* Cursor motion command */
X if (read (frobot, buf, 2) < 2)
X { deadrobot (); }
X else
X { row = buf[0] - ' ';
X col = buf[1] - ' ';
X if (trace) { fprintf (trace, "%c%c", buf[0], buf[1]);
X fflush (trace); }
X }
X break;
X
X case CR: /* Carriage Return */
X col = 0;
X break;
X
X case DO: /* Cursor down */
X row++;
X break;
X
X case LF: /* Line Feed */
X row++;
X col = 0;
X break;
X
X case ND: /* Non-destructive space (cursor right) */
X col++;
X break;
X
X case TA: /* Tab */
X col = 8 * (1 + col / 8);
X break;
X
X case EOF: /* End of file */
X playing = 0;
X return;
X break;
X
X case UP: /* Cursor up */
X row--;
X break;
X
X default: /* Other */
X
X /* Control character */
X if (ch < ' ')
X { fprintf (stderr, "Unknown character '\\%o'\n", ch);
X kill (child, 9);
X exit (1);
X }
X
X /* Printing character */
X if (ch == 'I')
X { atrow = row; atcol = col; }
X mvaddch (row, col, ch);
X screen[row][col++] = ch;
X break;
X }
X }
X
X drawscreen ();
X move (row, col);
X refresh ();
X}
X
X/****************************************************************
X * deadrobot: We died, read our score and print it out
X ****************************************************************/
X
Xdeadrobot ()
X{ int level=0, score=0;
X char junk[BUFSIZ];
X
X sscanf (screen[ROWS-1], "%s level: %d score: %d", junk, &level, &score);
X printf ("Died on level %d with a score of %d.\n", level, score);
X}
X
X/****************************************************************
X * send: Send a command to the Robots process
X ****************************************************************/
X
Xsend (ch)
Xint ch;
X{ char buf[BUFSIZ];
X
X *buf = ch;
X write (trobot, buf, 1);
X}
X
X/****************************************************************
X * drawscreen: Draw the screen array on the user's terminal
X ****************************************************************/
X
Xdrawscreen ()
X{ register int r, c;
X
X for (r=0; r<ROWS; r++)
X for (c=0; c<COLS; c++)
X mvaddch (r, c, screen[r][c]);
X}
X
X/****************************************************************
X * strategy: Five basic rules
X *
X * 1. Find the nearest enemy robot.
X * 2. Can you hide behind a scrap heap and cause him to crash?
X * 3. Can you find another robot close to the first and move in.
X * such a way so as to cause them to collide?
X * 4. Can you make any safe move? Choose randomly.
X * 5. Teleport.
X ****************************************************************/
X
X/* Arrays for finding directions and keys to go that way */
Xint deltar[] = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
Xint deltac[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1};
Xchar *keydir = "ykuh.lbjn";
X
Xstrategy ()
X{ register int k, r, c, ch, cmd;
X int tr=0, tc=0, hr=0, hc=0;
X
X /* Find closest robot and find a heap to block him with */
X if (closest (&tr, &tc, '=') )
X { if (findheap (tr, tc, &hr, &hc) || findcollide (tr, tc, &hr, &hc))
X { if (cmd = makemove (hr, hc)) return (cmd); }
X }
X
X /* Make a random move */
X cmd = 't';
X for (k=0; k<9; k++)
X { r = atrow + deltar[k];
X c = atcol + deltac[k];
X ch = keydir[k];
X if (safe (r, c))
X { if (cmd=='t') cmd = ch;
X else if (time(0) & 1) cmd = ch;
X }
X }
X
X return (cmd);
X}
X
X/****************************************************************
X * safe: Is it safe to move to a given square
X ****************************************************************/
X
Xsafe (r, c)
Xregister int r, c;
X{
X return ((screen[r][c] == ' ' || screen[r][c] == 'I') &&
X screen[r-1][c-1] != '=' && screen[r-1][ c] != '=' &&
X screen[r-1][c+1] != '=' && screen[ r][c-1] != '=' &&
X screen[ r][c+1] != '=' && screen[r+1][c-1] != '=' &&
X screen[r+1][ c] != '=' && screen[r+1][c+1] != '=');
X}
X
X/****************************************************************
X * find the closest object of a given type
X ****************************************************************/
X
Xclosest (rp, cp, type)
Xint *rp, *cp, type;
X{ register int dist=999, newdist, tr=0, tc=0, r, c;
X
X /* Find closest robot */
X for (r=1; r<ROWS-1; r++)
X { for (c=1; c<COLS; c++)
X { if (screen[r][c] == type)
X { newdist = distance (atrow, atcol, r, c);
X if (newdist < dist)
X { tr=r; tc=c; dist=newdist; }
X }
X }
X }
X
X *rp=tr; *cp=tc;
X
X dwait ("Closest %s is at (%d,%d).",
X type == '=' ? "robot" : "scrap heap", tr, tc);
X
X return (dist < 999);
X}
X
X/****************************************************************
X * distance: Calculate the distance between two points
X ****************************************************************/
X
Xdistance (r1, c1, r2, c2)
Xint r1, c1, r2, c2;
X{ register int dr, dc;
X
X if ((dr = r2-r1) < 0) dr = -dr; /* Absolute row distance */
X if ((dc = c2-c1) < 0) dc = -dc; /* Absolute col distance */
X return (max(dr,dc)); /* Max norm */
X}
X
X/****************************************************************
X * findheap: Find a heap to block a given robot
X ****************************************************************/
X
Xfindheap (tr, tc, pr, pc)
Xint tr, tc, *pr, *pc;
X{ int r, c;
X
X /* Check closest heap for blocking first */
X if (closest (&r, &c, '@') && willblock (r, c, tr, tc, pr, pc))
X { dwait ("Heading for nearby safe square at (%d,%d).", *pr, *pc);
X return (1);
X }
X
X /* Look through all heaps on the screen to find a block */
X for (r=1; r<ROWS-1; r++)
X { for (c=1; c<COLS; c++)
X { if (screen[r][c] == '@' && willblock (r, c, tr, tc, pr, pc))
X { dwait ("Heading for safe square at (%d,%d).", *pr, *pc);
X return (1);
X }
X }
X }
X
X /* Lose */
X return (0);
X}
X
X/****************************************************************
X * willblock: returns true if the heap at (hr,hc) will block the enemy robot
X * located at (tr,tc). The square to move to is returned in (pr, pc)
X ****************************************************************/
X
Xwillblock (hr, hc, tr, tc, pr, pc)
Xint hr, hc, tr, tc, *pr, *pc;
X{ register int dr, dc, sr,sc;
X
X /* First find the "shadow" behind the block from the enemy robot */
X dr = hr-tr; dc = hc-tc;
X
X if (abs (dr) == abs (dc))
X { sr = hr+sgn (dr); sc = hc+sgn (dc); }
X else if (abs (dr) > abs (dc))
X { sr = hr+sgn (dr); sc = hc; }
X else
X { sr = hr; sc = hc+sgn (dc); }
X
X /* If we are closer to the shadow square than the robot is, win */
X if (distance (sr, sc, tr, tc) > distance (hr, hc, atrow, atcol))
X { *pr = sr; *pc = sc; return (1); }
X
X /* Lose */
X return (0);
X}
X
X/****************************************************************
X * findcollide: Find a robot to collide with a given robot. The target
X * square will be the nearest safe square to the collision point.
X ****************************************************************/
X
Xfindcollide (tr, tc, pr, pc)
Xint tr, tc, *pr, *pc;
X{ int r=0, c=0, d, cr1, cc1, cr2, cc2;
X
X /* Search from target robot for another on the same row or column */
X for (d=1; d<COLS; d++)
X { if (tc+d < COLS && screen[tr][tc+d] == '=')
X { r = tr; c = tc+d; break; }
X
X if (tc-d > 0 && screen[tr][tc-d] == '=')
X { r = tr; c = tc-d; break; }
X
X if (tr+d < LINES-1 && screen[tr+d][tc] == '=')
X { r = tr+d; c = tc; break; }
X
X if (tr-d > 0 && screen[tr-d][tc] == '=')
X { r = tr-d; c = tc; break; }
X }
X
X if (r || c)
X { dwait ("possible collision between (%d,%d) and (%d,%d)", tr, tc, r, c); }
X
X /* Check for two on same row */
X if (r == tr && r != atrow)
X { /* Check collision up */
X cr1 = tr - (d+1)/2 - 1;
X cr2 = tr + (d+1)/2 + 1;
X cc1 = cc2 = (tc + c) / 2;
X if (distance (atrow, atcol, cr1, cc1) < distance (atrow, atcol, cr2, cc2))
X { *pr = cr1; *pc = cc1; }
X else
X { *pr = cr2; *pc = cc2; }
X
X if (*pr < 1 || *pr >= ROWS || *pc < 1 || *pc > COLS) return (0);
X
X dwait ("predicting safe spot near collision (%d,%d)", *pr, *pc);
X return (1);
X }
X
X /* Check for two on same column */
X if (c == tc && c != atcol)
X { /* Check collision left */
X cr1 = cr2 = (tr + r) / 2;
X cc1 = tc - (d+1)/2 - 1;
X cc2 = tc + (d+1)/2 + 1;
X if (distance (atrow, atcol, cr1, cc1) < distance (atrow, atcol, cr2, cc2))
X { *pr = cr1; *pc = cc1; }
X else
X { *pr = cr2; *pc = cc2; }
X
X if (*pr < 1 || *pr >= ROWS || *pc < 1 || *pc > COLS) return (0);
X
X dwait ("predicting safe spot near collision (%d,%d)", *pr, *pc);
X return (1);
X }
X
X /* Fail */
X return (0);
X}
X
X/****************************************************************
X * makemove: Return the direction to move toward a given square. This
X * routine checks to make sure the move is a safe one. Returns 0 if no
X * move is safe, and the character if one is found.
X ****************************************************************/
X
Xmakemove (r, c)
X{ int dr, dc;
X
X dr = sgn (r-atrow); dc = sgn (c-atcol);
X
X if (dr && dc)
X { if (safe (atrow+dr, atcol+dc)) return (keydir[3*dr + dc + 4]);
X if (safe (atrow, atcol+dc)) return (keydir[ 0 + dc + 4]);
X if (safe (atrow+dr, atcol )) return (keydir[3*dr + 0 + 4]);
X }
X else if (dr == 0)
X { if (safe (atrow, atcol+dc)) return (keydir[ 0 + dc + 4]);
X if (safe (atrow+1, atcol+dc)) return (keydir[ 3 + dc + 4]);
X if (safe (atrow-1, atcol )) return (keydir[-3 + 0 + 4]);
X }
X else if (dc == 0)
X { if (safe (atrow+dr, atcol )) return (keydir[3*dr + 0 + 4]);
X if (safe (atrow, atcol+1)) return (keydir[3*dr + 1 + 4]);
X if (safe (atrow+dr, atcol-1)) return (keydir[3*dr - 1 + 4]);
X }
X
X dwait ("makemove: cannot move to (%d,%d) safely.", r, c);
X return (0);
X}
X
X/*****************************************************************
X * charsavail: How many characters are there at the terminal? If any
X * characters are found, 'noterm' is reset, since there is obviously
X * a terminal around if the user is typing at us.
X *****************************************************************/
X
Xcharsavail ()
X{ long n;
X int retc;
X
X if (retc = ioctl (READ, FIONREAD, &n))
X { fprintf (stderr, "Ioctl returns %d, n=%ld.\n", retc, n);
X n=0;
X }
X
X return ((int) n);
X}
X
X/****************************************************************
X * dwait: Debugging message
X ****************************************************************/
X
Xdwait (f, a1, a2, a3, a4)
Xchar *f;
Xint a1, a2, a3, a4;
X{ char buf[BUFSIZ];
X register int c;
X
X if (debug)
X { mvprintw (0, 0, "[%d,%d] ", atrow, atcol);
X printw (f, a1, a2, a3, a4);
X addch (' ');
X move (row, col);
X refresh ();
X switch (getchar ())
X { case 'd': debug=0; break;
X case 'r': clear (); refresh (); drawscreen (); refresh (); break;
X default: break;
X }
X for (c=0; c<COLS; c++)
X { mvaddch (0, c, screen[0][c]); }
X }
X}
X
X/*
X * putenv -- put value into environment
X *
X * Usage: i = putenv (name,value)
X * int i;
X * char *name, *value;
X *
X * Putenv associates "value" with the environment parameter "name".
X * If "value" is 0, then "name" will be deleted from the environment.
X * Putenv returns 0 normally, -1 on error (not enough core for malloc).
X *
X * Putenv may need to add a new name into the environment, or to
X * associate a value longer than the current value with a particular
X * name. So, to make life simpler, putenv() copies your entire
X * environment into the heap (i.e. malloc()) from the stack
X * (i.e. where it resides when your process is initiated) the first
X * time you call it.
X *
X * HISTORY
X * 14-Oct-85 Michael Mauldin (mlm) at Carnegie-Mellon University
X * Ripped out of CMU lib for Rob-O-Matic portability
X * 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University
X * Created for VAX. Too bad Bell Labs didn't provide this. It's
X * unfortunate that you have to copy the whole environment onto the
X * heap, but the bookkeeping-and-not-so-much-copying approach turns
X * out to be much hairier. So, I decided to do the simple thing,
X * copying the entire environment onto the heap the first time you
X * call putenv(), then doing realloc() uniformly later on.
X * Note that "putenv(name,getenv(name))" is a no-op; that's the reason
X * for the use of a 0 pointer to tell putenv() to delete an entry.
X *
X */
X
X#define EXTRASIZE 5 /* increment to add to env. size */
X
Xchar *index (), *malloc (), *realloc ();
Xint strlen ();
X
Xstatic int envsize = -1; /* current size of environment */
Xextern char **environ; /* the global which is your env. */
X
Xstatic int findenv (); /* look for a name in the env. */
Xstatic int newenv (); /* copy env. from stack to heap */
Xstatic int moreenv (); /* incr. size of env. */
X
Xint putenv (name, value)
Xchar *name, *value;
X{ register int i, j;
X register char *p;
X
X if (envsize < 0)
X { /* first time putenv called */
X if (newenv () < 0) /* copy env. to heap */
X return (-1);
X }
X
X i = findenv (name); /* look for name in environment */
X
X if (value)
X { /* put value into environment */
X if (i < 0)
X { /* name must be added */
X for (i = 0; environ[i]; i++);
X if (i >= (envsize - 1))
X { /* need new slot */
X if (moreenv () < 0)
X return (-1);
X }
X p = malloc (strlen (name) + strlen (value) + 2);
X if (p == 0) /* not enough core */
X return (-1);
X environ[i + 1] = 0; /* new end of env. */
X }
X else
X { /* name already in env. */
X p = realloc (environ[i],
X strlen (name) + strlen (value) + 2);
X if (p == 0)
X return (-1);
X }
X sprintf (p, "%s=%s", name, value);/* copy into env. */
X environ[i] = p;
X }
X else
X { /* delete name from environment */
X if (i >= 0)
X { /* name is currently in env. */
X free (environ[i]);
X for (j = i; environ[j]; j++);
X environ[i] = environ[j - 1];
X environ[j - 1] = 0;
X }
X }
X
X return (0);
X}
X
Xstatic int findenv (name)
Xchar *name;
X{ register char *namechar, *envchar;
X register int i, found;
X
X found = 0;
X for (i = 0; environ[i] && !found; i++)
X { envchar = environ[i];
X namechar = name;
X while (*namechar && (*namechar == *envchar))
X { namechar++;
X envchar++;
X }
X found = (*namechar == '\0' && *envchar == '=');
X }
X return (found ? i - 1 : -1);
X}
X
Xstatic int newenv ()
X{ register char **env, *elem;
X register int i, esize;
X
X for (i = 0; environ[i]; i++);
X esize = i + EXTRASIZE + 1;
X env = (char **) malloc (esize * sizeof (elem));
X if (env == 0)
X return (-1);
X
X for (i = 0; environ[i]; i++)
X { elem = malloc (strlen (environ[i]) + 1);
X if (elem == 0)
X return (-1);
X env[i] = elem;
X strcpy (elem, environ[i]);
X }
X
X env[i] = 0;
X environ = env;
X envsize = esize;
X return (0);
X}
X
Xstatic int moreenv ()
X{ register int esize;
X register char **env;
X
X esize = envsize + EXTRASIZE;
X env = (char **) realloc (environ, esize * sizeof (*env));
X if (env == 0)
X return (-1);
X environ = env;
X envsize = esize;
X return (0);
X}
/
echo 'x - Makefile'
sed 's/^X//' > Makefile << '/'
Xrobomatic: robomatic.c
X cc -O -o robomatic robomatic.c -lcurses -ltermcap
/
echo 'Rob-O-Matic Distribution complete.'
exit