[net.sources] New release of Visual Empire tool

knutson@ut-ngp.UUCP (Jim Knutson) (10/04/84)

There have been several requests for ve so here is an updated version of
ve done here at the UT.  This version will process ship, radar and 
coastwatch reports as well as the previous map, census, commodity and spy
reports.  Surveys have been fixed so they don't overflow.  Checkpointed
sectors are reported.  Large maps no longer cause segment violations and
other problems.  You no longer are able to run into the edge of the world.
The world map will wrap back on itself.  This version also uses much less
memory making forking the editor much faster.  The editor commands now use
the VISUAL and EDITOR environment variables.  You may now fork a shell
with the ! command (uses the SHELL environment variable).  More commodity,
census, etc. files may be read in without having to enter them on the
command line.  A Naval mode has been added to display navies and ship
information.  Tracing of routes for mov and nav commands may be shown
on the screen using ^P.

Be sure to strip the trailer.
------------------ Cut here -------------------
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting ve.6'
sed 's/^X//' <<'//go.sysin dd *' >ve.6
X.ds v \fIve\fR
X.ds V \fIVe\fR
X.ds ]W "2nd Release"
X.TH VE 6 10/3/84
X.SH NAME
ve - visual empire
X.SH SYNOPSIS
ve [-a|c command_file] census map commodity spy ship radar coastwatch...
X.SH DESCRIPTION
\*V is a screen oriented empire(6) tool to aid in decision
making and command file creation.
It has been used by kings, presidents and petty dictators
in many different countries and worlds with varying degrees of
success.
The program runs independently of empire and can thus be used
as much and whenever you wish.
X.sp
\*V takes as input any number of empire commodity, census, map, spy,
ship, radar and coastwatch reports, combines the information and 
displays a map on the screen
and the census and commodity information for that sector at the bottom.
As you move your cursor from one sector to the next the information
at the bottom is updated.
You can do surveys of the land, mark sectors and save commands in
a separate command file to be executed later in empire.
If the \fI\-a\fR option is used the \fIcommand_file\fR is
appended to whereas the \fI\-c\fR option will create or
truncate the file.
X.sp
The following are the commands that are understood (if not
quite executed) by \*v.
X.sp
X.IP q
Quit \*v.
X.sp 2
X.IP "Entering information:"
Many commands in \*v will prompt you for more information
at the bottom ("last line") of the screen.
The following characters have a special meaning when
entering information.
X.sp
X.IP BS
The backspace key will erase one character using a
backspace-space-backspace sequence.
X.IP @
The @ key will erase the entire line.
X.IP ESC
The ESCape key will move the cursor from the bottom line
to its current map position, wait a second and then jump back.
X.IP .
The period will immediately expand to the x and y coordinates of
the current sector.
A `.' embedded into macros will get expanded when the macro is.
X.IP ^P
Proposed movements may be traced on the screen and allow you to verify
moves and nav's by using the ^P command.
X.IP backslash
The backslash, `\\\' will cause the next character to be interpreted
literally.
This is how `.'s are embedded into macros and how one
keeps a macro from expanding.
X.IP macros
Macros are a single character that will expand to a string
as soon as they are entered.
The commands to set and delete macros are described below.
X.sp 2
X.IP "Movement and display functions:"
The following commands control the movement of the cursor
on the screen and the printing of the status lines and
and display.
X.IP "yugjbn"
Move one sector in the specified direction.
These are the same as in empire (up and left, up and right,
left, right, down and left, down and right respectively).
X.IP ^Y|^U
Move/scroll up.
X.IP ^B|^N
Move/scroll down.
X.IP ^G
Move/scroll left.
X.IP ^J
Move/scroll right.
X.IP l
Leap to a specified sector.
\*V will prompt for the x and y coordinates of the sector.
X.IP P
Toggle the printing of the status lines at the bottom of
the screen (for slow baud rates).
Normally \*v will update the status lines every time the
cursor is moved, but with printing turned off, the status
lines are only printed by the `p' command below.
X.IP p
Print the status lines for that sector.
Only useful if printing has been toggled off.
X.IP ^F
Flip between the survey map and the designation maps.
X.IP ^L
Redraw the screen.
X.sp
X.IP "Marking the map:"
The result of a `?' command is to mark
sectors of the map.
Sectors are marked using the \fIcurrent mark\fR
character. This can be changed without affecting
the screen allowing for multiple marks on the
same screen.
A sector is marked by placing the current mark character
(default is `>') directly to the left of the sector.
As an example of multiple marks,
you can set the current mark to `^' and
do the command "?civ>100" then set the mark to `v'
and do "?civ<5" to get a visual representation of
the high and low civilian population sectors.
X.IP m
Set the current mark character.
X.IP M
Reset the mark character to the default of `>'.
X.IP c
Clear the current marks on the screen.
X.IP C
Clear ALL of the marks on the screen.
X.sp 2
X.IP "Command file:"
The optional command file can be used to store notes
or commands to later be executed by empire.
\*V lets you append to the file, change or create it and
edit it without exiting the program.
X.IP a
Append a line to the command file.
X.IP O
Create or change the current command file.
If there is a current command file it is closed
before the new file is opened.
X.IP V
Invoke the default visual editor (normally \fIvi(1)\fR)
on the current command file.  The shell variable VISUAL will be used if set.
X.IP E
Invoke the default line editor (normally
\fIex(1)\fR) on the current command file.  The shell variable EDITOR will be used if set.
X.sp 2
X.IP "\*V Commands"
X.IP ?
This command will prompt you for an expression similar
to that in empire (see Command Syntax in the empire
manual).
The effect of all `?' commands is to "mark" sectors
that satisfy the requirements of the command.
Unfortunately the command is not as robust as
it is in empire. At most two commands (separated by `&')
can be combined, not all of the items can be checked and
the item name must be on the left while the value is on
the right.
X.sp
Some allowed commands would be
X.in +5
X.nf
?civ>100&mil<5
?country=4
?des=h&lcm<20
?del=food&contract=mil
X.fi
X.in -5
The items that can be accessed are:
X.in +5
X.ta 1.5i
X.nf
civ	civilians
mil	military
foo	food
sh	shells
gun	guns
pl	planes
iro	iron
dus	gold dust
bar	gold bars
oil	oil
lcm	light construction material
hcm	heavy construction material
eff	efficiency
mob	mobility
min	mineral content
gmi	gold mineral content
fer	fertility
pet	petro content
des	designation
cou	country
del	delivery route
X.fi
X.in -5
Note that the above are the minimal abbreviations for the
different items.
X.IP I
The I command allows more report files to be read in.  This is useful
when combined with the ! command to update your empire and look at the
results without ever exiting \*V.
X.IP !
The ! command forks a shell (honors the SHELL variable).  The command
file is flushed before forking to allow its use.
X.sp 2
X.IP Macros:
A macro is a single character that will be expanded
immediately as typed it does not need to be space
delimited.
A `.' may be embedded into a macro and will be expanded
at the time the macro is typed.
For example, a C could be defined as "mov civ \\\.".
Then whenever a C is typed it would expand to "mov civ x, y "
where x,y are the coordinates of the current sector.
X.IP s
Set up a macro.
Note that you must use a `\\\' to embed a `.' into
a macro.
X.IP d
Delete a macro.
X.sp 2
X.IP "Surveys of the land"
It is possible to survey any appropriate item on the
commodity or census list.
A survey has a \fIrange\fR associated with it which
describes the maximum value for the item being surveyed.
When the survey is done each sector is replaced by a
single digit from 0 to 9 which indicates which tenth
of the range the value of that item occurred in.
For values greater than the range capital letters are
used up to `Z'.
After that only a `$' is printed.
The survey map is distinct from the designation map.
You can flip between the two at any time by typing `^F'.
For example, if the range is 100, the item being surveyed
is civilians and the number of civilians in the sector is
25, a 2 will be displayed.
X.IP S
Perform a survey
X.IP R
Set up the range for surveys.
X.sp 2
X.IP "Naval reports"
Normally, your opponents navies as well as your own will not be displayed.
The ship display may be turned on using the 'N' command.  At this point, the
flagship of each sector which contains a ship will be displayed in the sector
in which they reside.
Information about each ship in a sector may be obtained by moving your cursor
over the top of the sector and paging through each ship with the '+', '-',
and '/' keys.
X.IP N
Toggles the ship display mode.
X.IP G
Go to the sector of the specified ship.  \*V will prompt for the ship number.
X.IP +
Display information on the next ship in the sector.  Ships are ordered by
their ship number.
X.IP -
Display information on the previous ship in the sector.
X.IP /
Display information on the flagship in that sector (ship with the lowest
number).
X.sp 2
X.IP "Moving along routes"
You can walk along delivery routes by setting up the
desired route with the `r' command and then typing the `w'
command.
The cursor will then move along the chosen delivery route.
X.IP r
Designate a current route.
X.IP w
Walk along the current route.
X.SH AUTHORS
Matthew Diaz and Michael Baldwin
Modifications by Bill Jones, Jim Knutson, Ken Montgomery, Dan Reynolds
X.SH "SEE ALSO"
empire(6)
X.SH BUGS
Probably lots
X.sp 1
The `?' command should be identical
to that in empire.
X.sp 1
Some contortions of radar scans may not work.
X.sp 1
Surveys with ships may leave survey values on the designation map.
X.sp 1
New ship reports don't replace the old values (read using the I command).
X.sp 1
Command files that are removed in a forked shell will reappear.
X.sp 1
The erase and line kill characters
should be taken from the environment.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 ve.6
	/bin/echo -n '	'; /bin/ls -ld ve.6
fi
/bin/echo 'Extracting ve.c'
sed 's/^X//' <<'//go.sysin dd *' >ve.c
X/* 
 *      ve - visual empire (with due respect to Peter Langston).
 *
 *      Written by Matthew Diaz and Michael Baldwin
 *      Modified by Bill Jones, Jim Knutson, Ken Montgomery and Dan Reynolds
 *
 *      Usage: ve [-a|c] census commodity map ship spy radar coastwatch....
 *
 *      Compile using: cc -o ve ve.c -O -lcurses -ltermcap
 */

#include <ctype.h>
#include <stdio.h>
#include <curses.h>
#include <signal.h>

X/* Things you may want to change */
#define VIPATH      "/usr/ucb/vi"
#define EXPATH      "/usr/ucb/ex"
#define SHPATH      "/bin/sh"
#define TEMPFILE    "/tmp/vetemp"

#define MAPSIZE     128                 /* Empire world size */
#define RADARSIZE   40                  /* Radar scan size */
#define MAXSHIPS    1024                /* Maximum number of ships */
#define VALUESIZE   30                  /* Number of values in sector */
#define DELSIZE     12                  /* Number of deliveries per sector */
#define CNTSIZE     12                  /* Number of contracts per sector */
#define NODISK      -1                  /* No disk address flag */
#define NOSHIPS     -1                  /* No ships present */
#define UNKNOWN     -1                  /* Unknown ship index flag */
#define MCOLS       79                  /* Width of the map window */
#define MLINES      17                  /* Height of the map window */
#define NOX         0                   /* Expansion flags for getline */
#define EX          1
#define NOSU        0                   /* Survey or no survey for mapdr */
#define SURV        1
#define ESC         '\033'

#define ODD(x)      ((x)&01)
#define EVEN(x)     (!((x)&01))
#define MAX(A,B)    ((A) > (B) ? (A) : (B))
#define MIN(A,B)    ((A) < (B) ? (A) : (B))
#define VALID(x,y)  (EVEN(x)&&EVEN(y)||ODD(x)&&ODD(y))
#define CTRL(c)     (('c')-0100)

X/* Offsets into sector arrays for various items */
#define CIV     0
#define MIL     1
#define FOOD    2
#define SH      3
#define GUN     4
#define PL      5
#define IRON    6
#define DUST    7
#define BAR     8
#define OIL     9
#define LCM     10
#define HCM     11
#define EFF     12
#define MOB     13
#define MIN     14
#define GMIN    15
#define FERT    16
#define PET     17
#define DES     18
#define COU     19
#define CONT    20
#define DEL     21
#define CHKPT   22

char    usage[] = "Usage: ve [-c|a out] files...";

X/*
 * If you want accounting, change the filename below (make sure
 * it is 622 at least).  Everytime someone plays that version
 * a line will be entered showing login name, elapsed time and
 * date.
 */
#ifdef ACCTNG
char    acct[] = "/u2/empire/veacct";
#endif

X/*
 * Temporary file related variables
 */
char tfile[30];                     /* temporary file name */
int temp = -1;                      /* temporary file descriptor */

X/*
 * Map sector data structures
 */
struct sector {
        char        surv;           /* Survey value */
        char        mark;           /* Mark character */
        char        own;            /* Do you own or know that sector? */
        char        des;            /* Sector designation */
        int         shp;            /* Index of current ship at x,y */
        long int    diskaddr;       /* Disk address containing values */
} map[MAPSIZE][MAPSIZE];

struct shipentry {
        char        des;            /* Ship designation (type) */
        int         number;         /* Ship number */
        int         x,y;            /* Map coordinates of ship */
        char        fleet;          /* Fleet designation */
        long int    diskaddr;       /* Disk address containing values */
} ships[MAXSHIPS];

struct  value {
        int         val[VALUESIZE]; /* Values as described by items */
        char        del[DELSIZE];   /* Delivery routes */
        char        cnt[CNTSIZE];   /* Contracts */
} values;
                        
struct item {
        char        *nm;            /* Item name - prefix */
        int         len;            /* Length of prefix (for strncmp) */
} items[] = {
        "civ",  3,
        "mil",  3,
        "foo",  3,
        "sh",   2,
        "gun",  3,
        "pl",   2,
        "iro",  3,
        "dus",  3,
        "bar",  3,
        "oil",  3,
        "lcm",  3,
        "hcm",  3,
        "eff",  3,
        "mob",  3,
        "min",  3,
        "gmi",  3,
        "fer",  3,
        "pet",  3,
        "des",  3,
        "cou",  3,
        "con",  3,
        "del",  3,
        "",     0
};


X/* 
 * Vector of ship names
 */
char *shipnames[] = {
    "yacht",                  
    "tender",               
    "minesweep",            
    "destroyer",            
    "submarine",            
    "cargo ship",           
    "battleship",           
    "oil derrick",          
    "patrol boat",          
    "fishing boat",         
    "heavy cruiser",        
    "aircraft carr",     
    "aircraft carrier",     
    0                       
}; /* shipnames */


X/*
 * ve input file function processors 
 */
int     census();                   /* census file processor */
int     readmap();                  /* map file processor */
int     commodities();              /* commodities file processor */
int     spy();                      /* spy report processor */
int     radarscan();                /* naval radar scan processor */
int     ship();                     /* ship report processor */
int	coastwatch();		    /* coastwatch report processor */

struct funsw {                      /* list of prefixes and functions */
        char    *type;
        int     (*func)();
} fsw[] = {
        "cen",  census,
        "map",  readmap,
        "com",  commodities,
        "spy",  spy,
        "rad",  radarscan,
        "shi",  ship,
	"coa",  coastwatch,
        0,      0,
};

char    macros[127][MCOLS];     /* Macro definitions */
char    peekc;                  /* Lets you poke a character */
int     startx = -50000;        /* Leftmost x-coordinate of display window */
int     starty = -50000;        /* Uppermost y-coordinate of display window */
int     minx;                   /* Min and max x (used for map reading) */
int     maxx;
int     miny;
int     curmark = '>';          /* Current marking character */
int     range = 10;             /* Range for survey */
int     curx, cury;             /* Current x and y coordinates in map array */
int     shipcount = 0;          /* Total number of ships in ships array */
int     shipmode = FALSE;       /* True if in ship display mode */
int     surmap = 0;             /* Nonzero if survey map should be displayed */
int     noise = TRUE;           /* True if production messages to be printed */ 

FILE    *inpf;                  /* Input file pointer */
char    iname[BUFSIZ];          /* Input file name */
FILE    *outf;                  /* Output file pointer */
char    oname[BUFSIZ];          /* Output file name */

char    buf[BUFSIZ];            /* Line buffer */

main(argc, argv)
int argc;
char *argv[];
{
        struct funsw *fp;       /* Pointer to input file function processor */
#ifdef ACCTNG
        FILE *actf;             /* Accounting file */
        long sclock;
        long eclock;
#endif

        if (argc == 1) puts(usage), exit(1);

#ifdef ACCTNG
        time(&sclock);
#endif
        opentf();
        presetmap();
        argv++;
        while (*argv)  {
            /* Open/create command file */
            if (**argv == '-' && (argv[0][1] == 'c'||argv[0][1] == 'a')) {
                if ((outf = fopen(*++argv,
                    (argv[0][1] == 'c') ? "w" : "a")) == NULL)
                         perror(*argv), exit(1);
                    strcpy(oname, *argv++);
                    continue;
            } /* if */

            /*
             * Process empire report files.  This is done by looking at
             * the second line of the file which contains a string of
             * the form:
             *
             *            cen ... (e.g. cen # >cen.out)
             *
             * The first word indicates the type of file it is (census,
             * map, etc).
             */
            if ((inpf = fopen(*argv, "r")) == NULL) perror(*argv), exit(1);
            if (fgets(buf, sizeof buf, inpf) == NULL ||
                fgets(buf, sizeof buf, inpf) == NULL) {
                    printf("%s: wrong format\n", *argv);
                    exit(1);
            }

            for (fp = fsw; *fp->type; fp++)
                if (!strncmp(fp->type, buf, strlen(fp->type)))
                    (fp->func)(inpf);
            fclose(inpf);
            argv++;
        } /* while */
        
        if (map[offset(0)][offset(0)].own)
            center (0,0);
        else
            center (minx + MCOLS/2, miny + MLINES/2);
        startx = 0 - MCOLS/2  ; /* Set at Capitol */
        if (ODD(startx)) startx--;
        starty = 0 - MLINES/2 ;
        if (ODD(starty)) starty--;

        signal(SIGINT, SIG_IGN);        /* Ignore common signals */
        signal(SIGQUIT, SIG_IGN);

        initscr();                      /* Start up curses etc. */
        presetty();
        commands();                     /* Process all commands */
        move(LINES-1, 0);               /* Done - move to bottom of screen */
        clrtoeol();
        refresh();

#ifdef ACCTNG
        /* Save accounting information */
        if ((actf = fopen(acct, "a")) != NULL) {
            time(&eclock);
            if (!getpw(getuid(), buf) && (bp = index(buf, ':'))) *bp=0;
                else sprintf(buf, "%d", getuid());
            eclock -= sclock;
            fprintf(actf, "%s\t%2d:%02d\t%s", buf,
                    eclock/60, eclock % 60, ctime(&sclock));
            fclose(actf);
        }
#endif
        endtty();                       /* Reset terminal characteristics */
} /* main */

X/*
 * Input file processing functions
 *
 * These routines parse the information from the input files,
 * many times in a very ugly way.
 *
 * Each routine checks to see if there is a comma in the 4th position.
 * This is to make certain that the line is of the form x,y and not
 * another message (e.g. Bad weather).
 */

X/*
 * census - Read and process census file.
 */
census(inpf)
FILE *inpf;
{
        register int x,y;
        register char *p;
        struct sector *mp;

        while (fgets(buf, sizeof buf, inpf) != NULL) {
            if (buf[3] != ',') {
            /*
             * Look for census lines of the form:
             * 'xxxxx yyy zzz in x,y' (reports of products produced)
             */
                if (p = (char *)rindex(buf, ',')) {
                    if (*++p == '-') p++;
                    if (noise && *p >= '0' && *p <= '9') {
                        fputs(buf, stderr);
                        fflush(stderr);
                    } /* if */
                } /* if */
                continue;
            } /* if */
            x = atoi(buf);
            y = atoi(&buf[4]);
            mp = &map[offset(x)][offset(y)];
            readvalues (offset(x),offset(y));
            mp->own = 1;
            values.del[CIV] = buf[19];
            values.del[MIL] = buf[20];
            values.del[FOOD] = buf[21];
            values.cnt[CIV] = buf[23];
            values.cnt[MIL] = buf[24];
            values.cnt[FOOD] = buf[25];
            values.val[CHKPT] = buf[27];
            values.val[COU] = -1;
            mp->des = (buf[8]=='-') ? '~' : ((buf[8]=='^') ? '&' : buf[8]);
            values.val[EFF] = atoi(&buf[10]);
            values.val[MOB] = atoi(&buf[15]);
            sscanf(&buf[29], "%d%d%d%d%d%d%d",
                   &values.val[CIV], &values.val[MIL], &values.val[FOOD],
                   &values.val[MIN], &values.val[GMIN], &values.val[FERT], 
                   &values.val[PET]);
            writevalues(offset(x),offset(y));
        } /* while */
} /* census */


X/*
 * commodities - Read and process commodities file.
 */
commodities(inpf)
FILE *inpf;
{
        register int x,y;
        register char *p;
        struct sector *mp;

        while (fgets(buf, sizeof buf, inpf) != NULL) {
            if (buf[3] != ',') {
            /*
             * Look for census lines of the form:
             * 'xxxxx yyy zzz in x,y' (reports of products produced)
             */
                if (p = (char *)rindex(buf, ',')) {
                    if (*++p == '-') p++;
                    if (noise && *p >= '0' && *p <= '9') {
                        fputs(buf, stderr);
                        fflush(stderr);
                    } /* if */
                } /* if */
                continue;
            } /* if */
            x = atoi(buf);
            y = atoi(&buf[4]);
            readvalues (x,y);
            mp = &map[offset(x)][offset(y)];
            mp->own = 1;
            sscanf(&buf[14], "%c%c%c%c%c%c%c%c%c",
                   &values.del[SH], &values.del[GUN], &values.del[PL],
                   &values.del[IRON], &values.del[DUST], &values.del[BAR],
                   &values.del[OIL], &values.del[LCM], &values.del[HCM]);
            sscanf(&buf[24], "%c%c%c%c%c%c%c%c%c",
                   &values.cnt[SH], &values.cnt[GUN], &values.cnt[PL],
                   &values.cnt[IRON], &values.cnt[DUST], &values.cnt[BAR],
                   &values.cnt[OIL], &values.cnt[LCM], &values.cnt[HCM]);
            sscanf(&buf[34], "%d%d%d%d%d%d%d%d%d",
                   &values.val[SH], &values.val[GUN], &values.val[PL],
                   &values.val[IRON], &values.val[DUST], &values.val[BAR],
                   &values.val[OIL], &values.val[LCM], &values.val[HCM]);
            writevalues(x,y);   
        } /* while */
} /* commodities */


editradarscan(rm, xr, yr, xp, yp, rp)
char rm[RADARSIZE][RADARSIZE];
int *yr, *xr;
int *xp, *yp;
float *rp;
{
        register int x,y;
        register int xs,ys;
        register int mx,my;

        y = 10.0 * (*rp);
        if (((y%10) == 5) && *yp < 0) ys = (*yr)/2;
        else                          ys = (*yr+1)/2;
        xs = *xr/2;
        if (rm[xs][ys] == ' ') xs++;       
        for (x = 0; x <= *xr; x++)
            for (y = 0; y <= *yr; y++) {
                switch(rm[x][y]) {

                    case 'A':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'F':
                    case 'H':
                    case 'M':
                    case 'O':
                    case 'P':
                    case 'S':
                    case 'T':
                    case 'Y': rm[x][y] = '$';
                              break;
      
                    case '/': rm[x][y] = ' ';
                } /* switch */
                merge(rm[x][y], (*xp-xs+x), (*yp-ys+y));
            } /* for */
} /* editradarscan */


findshipname(inpf)
FILE *inpf;
{
        register char c, *sp;
        register int i;

nomatch:    c = getc(inpf);
            if (c == EOF) return(0);
            for (i = 0; sp = shipnames[i]; i++) {
                while (*sp) {
                    if (c != *sp) {
                        if (sp == shipnames[i]) goto next;
                        if (skipword(inpf)) goto nomatch;
                        return(0);
                    } /* if */
                    c = getc(inpf);
                    sp++;
                } /* while */
                fscanf(inpf," %*d at ");
                return(1);
next:           continue;
            } /* for */
            if (!(c == '-' || (c >= '0' && c <= '9'))) goto nomatch;
            ungetc (c, inpf);
            return(2);
} /* findshipname */

 
X/*
 * getshiptype - Get ship type.
 */
getshiptype(bp)
register char *bp;
{
        register int i;
        register char *sp;

        for (i = 0; sp = shipnames[i]; i++) {
            if (!strncmp (sp, bp, strlen(sp)))
                return (shipnames[i][0] - 'a' + 'A');
        } /* for */
        return (FALSE);

} /* getshiptype */


X/*
 * merge - Update sector designation.
 */
merge(uc, x, y)
register char uc;
register int x,y;
{
        struct sector *mp;

        mp = &map[offset(x)][offset(y)];
        switch(mp->des) {

            case '^':   if (uc == '&') mp->des = uc;
                        /* fall through */

            case '\\':  break;

            case '~':   if (uc == '-') break;
                        /* fall through */

            case '&':   if (uc == '^') break;
                        /* fall through */

            default:
            case '-':
            case '?':   if (uc == ' ' || uc == '$' || uc == '?') break;
                        /* fall through */

            case 0:
            case ' ':   if (uc == '$') mp->des = '.';
                        else mp->des = uc;
                        break;
        } /* switch */
} /* merge */


presetradarmap(rm)
char rm[RADARSIZE][RADARSIZE];
{
        register int x,y;

        for (x = 0; x < RADARSIZE; x++)
            for (y = 0; y < RADARSIZE; y++)
              rm[x][y] = ' ';
} /* presetradarmap */


radarscan(inpf)
FILE *inpf;
{
        int xp, yp;
        float rp;
        int xr, yr;
        char radarmap[RADARSIZE][RADARSIZE];

        while (findshipname(inpf)) {
            if (!readradarlines(inpf, &xp, &yp, &rp)) continue;
            if (rp < 0.6) continue;
            presetradarmap(radarmap);
            if (!readradarscan(inpf, radarmap, &xr, &yr)) continue;
            editradarscan(radarmap, &xr, &yr, &xp, &yp, &rp);
        } /* while */
} /* radarscan */


X/*
 * readmap - Read and process the map file.  This is a real kludge where you 
 *           try to figure out where you are on the map by looking at the
 *           coordinates along the edge of the map.
 */
readmap(inpf)
FILE *inpf;
{
        register int x,y;       /* x,y map coordinates */
        int sign;               /* Sign of number being converted */
        char minb[5];           /* Number buffers for min and max x */
        char maxb[5];
        char *sp = minb;
        char *lp = maxb;
        char *bp;

        fgets(buf, sizeof buf, inpf);
        /* Now determine the minimum x value */
        *sp++ = buf[4];
        *lp++ = buf[strlen(buf) - 2];
        sign = (index(buf, '-')) ? -1 : 1;
        fgets(buf, sizeof buf, inpf);
        *sp++ = buf[4];
        *lp++ = buf[strlen(buf) - 2];
        *sp = 0;
        *lp = 0;

        minx = atoi(minb);
        maxx = atoi(maxb);

        if (sign == -1)  {
            if (minx > 0) minx *= -1;
        } else {
            if (minx > maxx) {
                maxx *= -1;
                minx *= -1;
            } /* if */
        } /* if */

        fgets(buf, sizeof buf, inpf);
        miny = atoi(buf);

        for (y = miny; buf[2] != ' '; y++) {
            for (bp = &buf[4], x = minx; x <= maxx; x++, bp++) {
                merge (*bp, x, y);
            } /* for */
            fgets(buf, sizeof buf, inpf);
        } /* for */
} /* readmap */


readradarlines(inpf, xp, yp, rp)
FILE *inpf;
int *xp, *yp;
float *rp;
{
        static char format[] = 
           "%d,%d efficiency %*d%%, barometer at %*d, max range %f";

        if (fscanf(inpf, format, xp, yp, rp) != 3) return(FALSE);
        pitchline(inpf);
        return(TRUE);
} /* readradarlines */


readradarscan(inpf, rm, xr, yr)
FILE *inpf;
char rm[RADARSIZE][RADARSIZE];
int *xr, *yr;
{
        register char c;
        register int x,y;
        register int blankline;
        register int leadingblanks;

        *xr = x = 0;
        *yr = y = 0;
        leadingblanks = TRUE;
        blankline = FALSE;

        while ((c = getc(inpf)) != EOF) {
            switch (c) {
                case '\n': if (!leadingblanks && blankline)
                               return (*xr > 0 && --(*yr) > 0);
                           if (!leadingblanks)
                               if (++y >= RADARSIZE) return(FALSE);
                           x--;
                           *xr = MAX(*xr, x);
                           *yr = MAX(*yr, y);
                           blankline = TRUE;
                           if (x > 0) {
                               x = 0;
                               continue;
                           } /* if */ 
                           /* fall through */

                default:   if (x <= 0) {
                               ungetc (c, inpf);
                               (*yr)--;
                               return (*xr > 0 && *yr > 0);
                           } /* if */
                           blankline = FALSE;
                           leadingblanks = FALSE;
                           /* fall through */

                case ' ':  rm[x][y] = c;
                           if(++x >= RADARSIZE) return(FALSE);
                       
            } /* switch */
        } /* while */
        (*yr)--;
        return (*xr > 0 && *yr > 0);
} /* readradarscan */


X/*
 * ship - Read and process ship reports.
 */
ship(inpf)
FILE *inpf;
{
        char des;
        register int number;
        register int i;
        struct sector *mp;
        struct shipentry *sp;

        while (fgets (buf, sizeof buf, inpf) != NULL) {
            if (buf[22] != ',') continue;
            if (!(des = getshiptype (&buf[5]))) continue;
            number = atoi (buf);
            if ((i = readship (UNKNOWN, number)) == NOSHIPS) {
                i = shipcount;
                if (++shipcount >= MAXSHIPS) {
                    fputs ("Ship vector overflow!\n", stderr);
                    fflush (stderr);
                    exit(1);
                } /* if */
                sp = &ships[i];
                sp->des = des;
                sp->number = number;
                sp->x = atoi (&buf[19]);
                sp->y = atoi (&buf[23]);
                sp->fleet = buf[27];
                values.val[COU] = -1;
                values.val[EFF] = atoi (&buf[29]);
                sscanf (&buf[34], "%d%d%d%d%d%d%d%d%d%d%d",
                        &values.val[CIV], &values.val[MIL], &values.val[SH],
                        &values.val[GUN], &values.val[PL], &values.val[IRON],
                        &values.val[DUST], &values.val[BAR], &values.val[FOOD],
                        &values.val[OIL], &values.val[MOB]);
                writeship (i, number);

                mp = &map[offset(sp->x)][offset(sp->y)];
                if (mp->shp == NOSHIPS) mp->shp = i;
                mp->own = 1;
            } /* if */
        } /* while */
} /* ship */


coastwatch(inpf)
FILE *inpf;
{
        char des;
        register int number;
        register int i;
	int j,cnum;
        struct sector *mp;
        struct shipentry *sp;

        while (fgets (buf, sizeof buf, inpf) != NULL) {
	    for (i=0; buf[i] != ' '; i++) ;		/* find country */
	    if (buf[++i] != '(' || buf[++i] != '#') continue;
            cnum = atoi(&buf[++i]);
	    for ( ; buf[i] != ' '; i++) ;		/* find ship type */
            if (!(des = getshiptype (&buf[++i]))) continue;
	    for ( ; buf[i] != '#'; i++) ;		/* find ship number */
            number = atoi (&buf[++i]);
            if ((j = readship (UNKNOWN, number)) == NOSHIPS) {
                j = shipcount;
                if (++shipcount >= MAXSHIPS) {
                    fputs ("Ship vector overflow!\n", stderr);
                    fflush (stderr);
                    exit(1);
                } /* if */
                sp = &ships[j];
                sp->des = des;
                sp->number = number;
		for ( ; buf[i] != '@'; i++) ;		/* find coordinates */
                sp->x = atoi (&buf[++i]);
                sp->y = atoi (&buf[i+4]);
                sp->fleet = ' ';
                values.val[COU] = cnum;
                writeship (j, number);

                mp = &map[offset(sp->x)][offset(sp->y)];
                if (mp->shp == NOSHIPS) mp->shp = j;
                mp->own = 1;
            } /* if */
        } /* while */
} /* coastwatch */


X/*
 * spy - Read and process spy reports.
 */
spy(inpf)
FILE *inpf;
{
        register int x,y;
        struct sector *mp;
        register int i;

        while (fgets(buf, sizeof buf, inpf) != NULL) {
            if (buf[3] != ',') continue;
            x = atoi(buf);
            y = atoi(&buf[4]);
            mp = &map[offset(x)][offset(y)];
            readvalues (x,y);
            if (mp->own && values.val[COU] == -1) continue;
            mp->own = 1;
            values.val[COU] = atoi(&buf[8]);
            if (buf[14] == 'N') goto noreport;  /* Ignore "No report... */
            merge (buf[11], x, y);
            values.val[DES] = buf[11];
            values.val[EFF] = atoi(&buf[13]);
            sscanf(&buf[18], "%d%d%d%d%d%d%d",
                   &values.val[CIV], &values.val[MIL], &values.val[SH],
                   &values.val[GUN], &values.val[IRON], &values.val[PL],
                   &values.val[FOOD]);

noreport:   values.val[CHKPT] = ' ';
            for (i = 0; i < DELSIZE; i++) values.del[i] = '.';
            for (i = 0; i < CNTSIZE; i++) values.cnt[i] = '.';
            writevalues(x,y);
        } /* while */
} /* spy */


X/*
 * censusinfo - Display census and commodities info for sector.
 */
censusinfo(x,y)
register int x,y;
{
        register struct sector *mp = &map[offset(x)][offset(y)];
        register struct shipentry *sp;

        censusheader(shipmode && mp->shp != NOSHIPS);
        move(LINES-2, 0);
        clrtoeol();
        move(LINES-4, 0);
        clrtoeol();
        readvalues(x,y);
        printw("%3d,%-3d  %c", x, y, mp->des ? mp->des : ' ');
        if (mp->own) {
            printw("%6d%%%4d %c%c%c %c%c%c   %c %4d%4d%5d%5d%5d%5d%4d",
                   values.val[EFF], values.val[MOB], values.del[CIV],
                   values.del[MIL], values.del[FOOD], values.cnt[CIV],
                   values.cnt[MIL], values.cnt[FOOD], values.val[CHKPT],
                   values.val[CIV], values.val[MIL], values.val[FOOD],
                   values.val[MIN], values.val[GMIN], values.val[FERT],
                   values.val[PET]);
            move(LINES-2, 0);
            if (!shipmode || mp->shp == NOSHIPS) {
                if (values.val[COU] == -1) addstr ("   ");
                    else printw("%3d", values.val[COU]);
                if (values.del[SH])
                    printw(" %c%c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c%c",
                       values.del[SH], values.del[GUN], values.del[PL],
                       values.del[IRON], values.del[DUST], values.del[BAR],
                       values.del[OIL], values.del[LCM], values.del[HCM],
                       values.cnt[SH], values.cnt[GUN], values.cnt[PL],
                       values.cnt[IRON], values.cnt[DUST], values.cnt[BAR],
                       values.cnt[OIL], values.cnt[LCM], values.cnt[HCM]);
                else
                   addstr("                    ");
                        
                printw("%4d%5d%4d%5d%5d%5d%5d%5d%5d",
                   values.val[SH], values.val[GUN], values.val[PL],
                   values.val[IRON], values.val[DUST], values.val[BAR],
                   values.val[OIL], values.val[LCM], values.val[HCM]);
            } else {
                sp = &ships[mp->shp];
                readship(mp->shp, sp->number);
                if (values.val[COU] == -1) addstr ("   ");
                    else printw("%3d", values.val[COU]);
                printw(" %4d  %c  %3d,%-3d %c %3d%%%4d%4d",
                       sp->number, sp->des, x, y, sp->fleet,
                       values.val[EFF], values.val[CIV], values.val[MIL]);
                printw("%4d%4d%4d%4d%4d%4d%4d%4d%4d",
                       values.val[SH], values.val[GUN], values.val[PL],
                       values.val[IRON], values.val[DUST], values.val[BAR],
                       values.val[FOOD], values.val[OIL], values.val[MOB]);
            } /* if */
        } /* if */
} /* censusinfo */


X/*
 * censusheader - Display census header.
 */
censusheader(mode)
register int mode;
{
        move(LINES-5, 0);
        addstr("  sect  des   eff mob cmf cmf % * ");
        addstr(" civ mil food  min gmin fert oil");
        move(LINES-3, 0);
        clrtoeol();
        if (!mode) {
            addstr("cou sgpidbolh sgpidbolh  ");
            addstr("sh  gun  pl iron dust  bar  oil  lcm  hcm");
        } else {
            addstr("cou    # des   x,y   f  ");
            addstr("eff civ mil  sh gun pln irn dst gld food oil mu");
        } /* if */
} /* censusheader */


X/*
 * center - Center display window about (x,y).
 */
center(x,y)
register int x,y;
{
        if (x - 10 < startx || x + 10 > startx + MCOLS ||
            y -  4 < starty || y +  4 > starty + MLINES) {
            startx = x - MCOLS/2;
            starty = y - MLINES/2;
        } /* if */
        return; 
} /* center */


X/*
 * clearmks - Clear sector marks.  If 'all' flag is set, then ALL marks
 *            cleared, otherwise just curmark marks are cleared.
 */
clearmks(all)
int all;
{
        register int x,y;

        for (y = 0; y < MAPSIZE; y++)
            for (x = 0; x < MAPSIZE; x++)
                if (all || map[x][y].mark == curmark) map[x][y].mark = 0;

} /* clearmks */


X/*
 *      closetf - close temporary file.
 */
closetf()
{
        close(temp);
} /* closetf */


X/*
 * commands - Process input commands.
 */
commands()
{
        register char c;                            
        int x,y;                    /* Indexes into map array */
        register int i;
        register struct item *ip;   /* Pointer to current item */
        int tx,ty;                  /* Temporary x,y */
        int crou = -1;              /* Current route */
        int status;                 /* Fork status return */
        int pflg = 1;               /* Print census flag */
        int update = 0;             /* Set if screen update needed */
        struct funsw *fp;           /* Pointer to input file function process */
        char *bp;
        char prbuf[BUFSIZ];
        char *getenv();

        if (map[offset(0)][offset(0)].own)
            center (x = 0, y = 0);
        else {
            x = minx + MCOLS/2 - ODD(minx + MCOLS/2);
            y = miny + MLINES/2 - ODD(miny + MLINES/2);
            center (x,y);
        } /* if */

        mapdr(NOSU);            /* Draw map, census header and census */
        censusinfo(x,y);

        move(y-starty, x-startx);

        while (refresh(), ((c = getac()) != 'q')) {
            update = 0;         /* Initialize variables for next command */
            curx = x;
            cury = y;
            switch(c) {
                /* Movement commands */
                case 'y':       x--;
                                y--;
                                break;

                case 'u':       x++;
                                y--;
                                break;

                case 'j':       x += 2;
                                break;

                case 'n':       x++;
                                y++;
                                break;

                case 'b':       x--;
                                y++;
                                break;

                case 'g':       x -= 2;
                                break;

                case CTRL(B):
                case CTRL(N):   y += 6;
                                break;

                case CTRL(Y):
                case CTRL(U):   y -= 6;
                                break;

                case CTRL(G):   x -= 6;
                                break;

                case CTRL(J):   x += 6;
                                break;

                case '\f':      clear();        /* Redraw the screen */
                                mapdr(surmap);
                                censusinfo(x,y);
                                break;

                case '?':       query();        /* Do query at bottom */
                                mapdr(surmap);
                                censusinfo(x,y);
                                break;

                case '!':                       /* Fork a shell */
                                if ((bp = getenv("SHELL")) == NULL)
                                    strcpy(prbuf, SHPATH);
                                else
                                    strcpy(prbuf, bp);
                                if (outf) fclose(outf);
                                if (fork() == 0) {
                                    move(LINES-1, 0);
                                    clrtoeol();
                                    refresh();
                                    endtty();
                                    execl(prbuf, prbuf, 0);
                                } else
                                    wait(&status);
                                presetty();
                                clear();
                                mapdr(surmap);
                                censusinfo(x,y);
                                if (outf)
                                    if ((outf = fopen(oname, "a")) == NULL)
                                        putline("Cannot reopen %s", oname);
                                break;

                case '+':       nextship(x,y);      /* Advance to next ship */
                                update++;
                                break;

                case '-':       previousship(x,y);  /* Back up to prev ship */
                                update++;
                                break;

                case '/':       firstship(x,y);     /* Go to first ship */
                                update++;
                                break;

                case 'C':
                case 'c':       clearmks(c=='C'); /* Clear marks */
                                mapdr(surmap);
                                break;

                case 'M':       curmark = '>';  /* Reset mark */
                                break;

                case 'm':       getline(buf,"mark: ",NOX); /* Change mark */
                                if (*buf) curmark = *buf;
                                break;

                case 'G':       locateship (&x,&y,surmap); /* Leap to ship */
                                update++;
                                break;

                case 'P':       pflg = (pflg == 0);  /* Toggle census refresh */
                                move(LINES-1, 0);
                                clrtoeol();
                                printw("Printing %s", pflg ? "on" : "off");
                                break;

                case 'N':                           /* Toggle ship display */
                                shipmode = (shipmode == FALSE);
                                mapdr(surmap);
                                censusinfo(x,y);
                                break;

                case 'a':       if (outf) {     /* Append to the file */
                                    getline(buf, "", EX);
                                    x = curx;
                                    y = cury;
                                    if (*buf) {
                                        strcat(buf, "\n");
                                        fputs(buf, outf);
                                    } /* if */
                                } else
                                putline("No output file specified - use O");
                                break;

                case 'i':                   /* Read in input data (map,...) */
                case 'I':
                                getline(buf, "New input file: ", NOX);
                                if ((inpf = fopen(buf, "r")) == NULL) {
                                        putline("%s: cannot open", buf);
                                        break;
                                }
                                strcpy(iname, buf);
                                if (fgets(buf, sizeof buf, inpf) == NULL ||
                                    fgets(buf, sizeof buf, inpf) == NULL) {
                                        putline("%s: wrong format\n", iname);
                                        break;
                                }

                                noise = FALSE;  /* don't report production */
                                for (fp = fsw; *fp->type; fp++)
                                    if (!strncmp(fp->type, buf,
                                        strlen(fp->type))) (fp->func)(inpf);
                                noise = TRUE;
                                fclose(inpf);
                                mapdr(surmap);
                                censusinfo(x,y);
                                break;
                                
                case 'o':                   /* Change/create output file */
                case 'O':
                                getline(buf, "New output file: ", NOX);
                                if (outf) fclose(outf);
                                if ((outf = fopen(buf, "a")) == NULL)
                                    putline("%s: cannot create", buf);
                                else
                                    strcpy(oname, buf);
                                break;

                case 's':                   /* Set a macro */
                                getline(buf, "macro name: ", NOX);
                                getline(prbuf, "define: ", NOX);
                                strcopy(prbuf,macros[*buf]);
                                break;

                case 'd':               /* Delete a macro */
                                getline(buf, "delete macro: ", NOX);
                                *macros[*buf] = 0; 
                                break;

                case 'V':       if ((bp = getenv("VISUAL")) == NULL)
                                    strcpy(prbuf, VIPATH);
                                else
                                    strcpy(prbuf, bp);
                                goto forkeditor;

                case 'E':       if ((bp = getenv("EDITOR")) == NULL)
                                    strcpy(prbuf, EXPATH);
                                else
                                    strcpy(prbuf, bp);

forkeditor:                     if (outf) {
                                    fclose(outf);
                                    move(LINES-1, 0);
                                    clrtoeol();
                                    refresh();
                                    if (fork() == 0) {
                                        endtty();
                                        execl(prbuf, prbuf, oname, 0);
                                    } else
                                        wait(&status);
                                    presetty();
                                    clear();
                                    mapdr(surmap);
                                    censusinfo(x,y);
                                    if ((outf = fopen(oname, "a")) == NULL)
                                        putline("Cannot reopen %s", oname);
                                } else putline("No output file");
                                break;

                case 'S':                       /* Survey */
                                getline(buf, "Survey: ", NOX);
                                survey(buf);
                                mapdr(surmap = SURV);
                                break;

                case 'R':                       /* Range for Survey */
                                sprintf(prbuf, "Range (%d): ", range*10);
                                getline(buf, prbuf, NOX);
                                if (*buf) {
                                    range = atoi(buf)/10;
                                    if (range < 1) {
                                        putline("range should be >= 10");
                                        range = 10;
                                    } /* if */
                                } /* if */
                                break;

                case CTRL(F):                   /* Flip maps */
                                mapdr((surmap = (surmap == NOSU)));
                                break;

                case 'r':                       /* Trace route */
                                getline(buf, "Route: ");
                                for (crou = -1, ip = items; ip->len; ip++)
                                    if (!strncmp(buf, ip->nm, ip->len))
                                        crou = ip - items;
                                if (crou < 0)
                                    putline("I don't know about %s", buf);
                                break;

                case 'w':                       /* Walk along route */
                                readvalues (x,y);
                                if (crou > -1 && values.del[crou] != '.')
                                    peekc = values.del[crou];
                                break;
                                
                case 'p':       break;          /* Print census */

                case 'l':       getline(buf, "Leap to: ");
                                if (*buf && (bp = (char *)index(buf, ','))) {
                                    tx = atoi(buf) ;
                                    ty = atoi(++bp);
                                    if (!VALID(tx, ty)) {
                                        tx -= ODD(tx);
                                        ty -= ODD(ty);
                                    } /* if */
                                    x = tx;
                                    y = ty;
                                    center(x,y);
                                    update++;
                                } /* if */
                                break;
            } /* switch */

            if (x < startx || x > startx+MCOLS) {
                startx = (x < startx) ? x : x-MCOLS;
                update++;
            } /* if */
            if (y < starty || y > starty+MLINES) {
                starty = (y < starty) ? y : y-MLINES;
                update++;
            } /* if */

            if (update) {
                mapdr(surmap);
                censusinfo(x,y);
                touchwin(stdscr);
            } else {
                if (pflg || c == 'p') censusinfo(x,y);
            } /* if */
            move(y-starty, x-startx);
        } /* while */
} /* commands */


X/*
 * endtty - Restore terminal to normal mode.
 */
endtty()
{
        nl();
        echo();
        crmode();
        endwin();
        closetf();
} /* endtty */


X/*
 * findblank - Find next blank character.
 */
char *findblank(addr)
register char *addr;
{
        while ((*addr != 0) && (*addr != ' ')) addr++; 
        return (addr);
} /* findblank */


X/*
 * findchar - Find next non-blank character.
 */
char *findchar(addr)
char *addr;
{
        while ((*addr != 0) && (*addr == ' ')) addr++; 
        return (addr);
} /* findchar */


X/*
 * findship - Find ship or first ship of fleet.
 */
findship(ship,fleet) 
register int ship;
register char fleet;
{
        register int i;

        if (ship == UNKNOWN) {
            for (i = 0; i < shipcount; i++)
                if (ships[i].fleet == fleet) {
                    ship = ships[i].number;
                    break;
                } /* if */
        } /* if */
        if (ship == UNKNOWN) return (NOSHIPS);
        return (readship (UNKNOWN, ship));
} /* findship */        


X/*
 * firstship - Locate first ship at x,y.
 */
firstship(x,y)
int x,y;
{
        register int mx,my;
        register int i;
        struct sector *mp;
        struct shipentry *sp;

        if (!shipmode) return;
        mx = offset(x);
        my = offset(y);
        mp = &map[mx][my];
        if (mp->shp == NOSHIPS) return;
        for (i = 0; i < shipcount; i++) {
            sp = &ships[i];
            if (offset (sp->x) == mx && offset(sp->y) == my) {
                mp->shp = i;
                return;
            } /* if */
        } /* for */
} /* firstship */


X/*
 * getac - Get a character. Return peekc if non-zero,
 *         otherwise read a character from the keyboard.
 */
getac()
{
        register char tc;

        if (peekc) {
            tc = peekc;
            peekc = 0;
            return(tc);
        } else
            return(getch());

} /* getac */


X/*
 * getline - Get input line from the bottom of the screen,
 *           using pr as a prompt if non-zero. If ex is set,
 *           then expand macros.
 */
getline(bp, pr, ex)
char *bp;
char *pr;
int  ex;
{
        register int x,y;
        register char c;
        char *mp;
        char *np;
        char nbuf[10];          /* Number buffer */
        char processmove();
        char *ip = bp;

        move(LINES-1, 0);
        clrtoeol();
        if (*pr) addstr(pr);
        while (refresh(), (c = getch()) != '\r') {
            if (ex && *macros[c]) {         /* check for macros */
                mp = macros[c];
                while (*mp) {
                    if (*mp == '.') {       /* expand . */
                        mp++;
                        sprintf(np = nbuf, "%d,%d ", curx, cury);
                        while (*ip++ = *np++);
                        ip--;
                        addstr(nbuf);
                    } else {
                        addch( *ip++ = *mp++);
                    } /* if */
                } /* while */
                continue;
            } /* if */

            switch(c) {
                case '\b':      if (ip > bp) {  /* backspace */
                                    ip--;
                                    *ip = 0;
                                    addstr("\b \b");
                                } /* if */
                                continue;

                case '\\':      addstr("\\\b"); /* backslash */
                                refresh();
                                c = getch();
                                break;

                case '.':       if (ex) {       /* expand . */
                                    sprintf(np = nbuf, "%d,%d ", curx, cury);
                                    while (*ip++ = *np++);
                                    ip--;
                                    *ip = 0;
                                    addstr(nbuf);
                                    continue;
                                }
                                else break;

                case ESC:       getyx(stdscr,y,x); /* jump to current x,y */
                                move(cury-starty, curx-startx);
                                refresh();
                                sleep(1);
                                move(y, x);
                                continue;

                case '\n':      continue;

                case CTRL(P):   if (ex) c = processmove (bp, &ip);
                                if (c > ' ') break;
                                continue;

                case '@':       move(LINES-1, 0);   /* erase the line */
                                if (*pr) addstr(pr);
                                clrtoeol();
                                *(ip = bp) = 0;
                                continue;
            } /* switch */
            addch(*ip++ = c);
            *ip = 0;
        } /* while */
        *ip = 0;
} /* getline */


X/*
 * getnewxy - Get last postion of MOVE or NAV command.
 */
getnewxy(bp,x,y)
char *bp;
int  *x,*y;
{
        register char *ip = bp;
        char *findblank();
        char *findchar();
        char fleet;
        char xbuf[6];
        char ybuf[6];
        int oldy,oldx;
        int shipnumber;
        register int i;

        switch(*ip) {
            case 'm':   if (index(ip,'mov') == 0) return(0);
                        if (*(ip = findblank(ip)) == 0) return(0);
                        if (*(ip = findchar (ip)) == 0) return(0);
                        if (*(ip = findblank(ip)) == 0) return(0);
                        if (*(ip = findchar (ip)) == 0) return(0);
                        for (i = 0; (i < 5) && (*ip != ',') && (*ip != 0);
                            i++) {
                            xbuf[i] = *ip++;
                            xbuf[i+1] = 0;
                        } /* for */
                        if ((*ip != ',') || (*ip == 0)) return(0);
                        ip++;
                        for (i = 0; (i < 5) && (*ip != ' ') && (*ip != 0);
                            i++) {
                            ybuf[i] = *ip++;
                            ybuf[i+1] = 0;
                        } /* for */
                        if ((*ip != ' ') || (*ip == 0)) return(0);
                        *x = atoi(xbuf);
                        *y = atoi(ybuf);
                        if (*(ip = findchar(ip))  == 0) return(0);
                        if (*(ip = findblank(ip)) == 0) return(0);
                        if (*(ip = findchar(ip))  == 0) return(1);
                        break;

           case 'n':    if (index(ip,'nav') == 0) return(0);
                        if (*(ip = findblank(ip)) == 0 ) return (0);
                        if (*(ip = findchar (ip)) == 0 ) return (0);
                        shipnumber = UNKNOWN;
                        fleet = *ip;
                        if ((fleet >= '0') && (fleet <= '9')) {
                            for (i = 0; (i <5 ) && (*ip != '/') &&
                                        (*ip != ' ') && (*ip != 0);
                                i++) {
                                xbuf[i] = *ip++;
                                xbuf[i+1] = 0;
                            } /* for */
                            shipnumber = atoi(xbuf);
                        } /* if */
                        if ((i = findship(shipnumber,fleet)) == NOSHIPS)
                            return(0);
                        *x = ships[i].x;
                        *y = ships[i].y;
                        if (*ip != ' ') ip = findblank(ip);
                        if (*ip == 0) return(0);
                        shipmode = TRUE;
                        map[offset(*x)][offset(*y)].shp = i;
                        getyx(stdscr,oldy,oldx);
                        curx = *x;
                        cury = *y;
                        center(curx,cury);
                        mapdr(surmap);
                        touchwin(stdscr);
                        censusinfo(curx,cury);
                        move(oldy,oldx);
                        if (*(ip = findchar(ip)) == 0) return(1);
                        break;
        
           default:     return(0);
        } /* switch */

        while (*ip != 0) { 
            switch(*ip) {
                case 'y':       (*x)--;
                                (*y)--;
                                break;

                case 'u':       (*x)++;
                                (*y)--;
                                break;

                case 'j':       *x += 2;
                                break;
                        
                case 'n':       (*x)++;
                                (*y)++;
                                break;

                case 'b':       (*x)--;
                                (*y)++;
                                break;
                             
                case 'g':       *x -= 2;
                                break;
                        
                default:        return(0);
            } /* switch */
        ip++;
        } /* while */
        return(1);
} /* getnewxy */


X/*
 * locateship - Locate arbitrary ship and move display window.
 */
locateship(sx,sy,sflg)
register int *sx,*sy;
int sflg;
{

        register int number;
        register int i;
        char fleet;

        getline (buf, "Ship number: ", NOX);
        if (*buf) {
            fleet = buf[0];
            if (fleet >= '0' && fleet <= '9')
                number = atoi (buf);
            else
                number = UNKNOWN;
            if ((i = findship (number, fleet)) == NOSHIPS) {
                putline("No info on ship");
                return;
            } /* if */
            *sx = ships[i].x;
            *sy = ships[i].y;
            center(*sx,*sy);
            map[offset(*sx)][offset(*sy)].shp = i;
            if (!shipmode) {
                shipmode = TRUE;
                mapdr(sflg);
            } /* if */
            censusinfo(*sx,*sy);
            return;
        } /* if */
} /* locateship */


X/*
 * mapdr - Display map.
 */
mapdr(sflg)
register int sflg;
{
        register int x,y;
        register char des;
        register struct sector *mp;

        for (y = starty; y <= starty + MLINES; y++)
            for (x = startx; x <= startx + MCOLS; x++) {
                mvaddch(y-starty, x-startx, ' ');
                if (!VALID(x, y)) continue;
                mp = &map[offset(x)][offset(y)];
                if (shipmode && mp->shp != NOSHIPS)
                    des = ships[mp->shp].des;
                else
                    if (sflg && mp->surv)
                        des = mp->surv;
                    else
                        des = mp->des;
                mvaddch(y-starty, x-startx-1, (mp->mark)?mp->mark:' ');
                if (des) mvaddch(y-starty, x-startx, des);
            } /* for */
} /* mapdr */


X/*
 * mark - Mark map according to command.
 */
mark(sp, pass)
char *sp;
int pass;
{
        register int itm1;
        register int itm2;
        register int x,y;
        register int num;
        register int val;
        register int markit;
        register struct item *ip;
        register struct item *tp;
        char cmd[20];
        char *cp = cmd;
        struct sector *mp;

        while (isalpha(*cp++ = *sp++));         /* get first word */
        *--cp = 0;
        if (!*--sp) return;
        for (ip = items; ip->len; ip++)         /* check it */
            if (!strncmp(cmd, ip->nm, ip->len)) break;

        if (!ip->len) {
            putline("I don't know about %s\n", cmd);
             return;
        } /* if */

        itm1 = ip - items;
        /*
         * At this point, cmd contains the left side, *sp
         * is the operator and sp+1 is the right side.
         */
        num = (itm1 == DES) ? *(sp+1) : atoi(sp+1);
        if (itm1 == CONT || itm1 == DEL) {
            for (tp = items; tp->len; tp++)
                if (!strncmp(sp+1, tp->nm, tp->len)) break;
            if (!tp->len) {
                putline("I don't know about %s", cmd);
                return;
            } /* if */
            itm2 = tp - items;
        } /* if */
        for (y = 0; y < MAPSIZE; y++)
            for (x = 0; x < MAPSIZE; x++) {
                mp = &map[x][y];
                if (!VALID(x, y) || !mp->own || mp->des == '.') continue;
                readvalues (x,y);
                if (values.val[COU] != -1) continue;
                markit = 0;
                val = values.val[itm1];
                if (itm1 == CONT) {
                    num = '$';
                    val = values.cnt[itm2];
                } else {
                    if (itm1 == DEL) {
                        num = '.';              /* KLUDGE */
                        *sp = '#';
                        val = values.del[itm2];
                    } /* if */
                } /* if */
                switch( *sp) {
                    case '=':   if (val == num) markit++;
                                break;

                    case '#':   if (val != num) markit++;
                                break;

                    case '>':   if (val > num)  markit++;
                                break;

                    case '<':   if (val < num)  markit++;
                                break;
                } /* switch */
                if (markit && (!pass || (pass && mp->mark)))
                    mp->mark = curmark;
                else
                    if (mp->mark == curmark) mp->mark = 0;
            } /* for */
} /* mark */


X/*
 * nextship - Advance ship index to next ship at x,y.
 */
nextship(x,y)
int x,y;
{
        register int mx,my;
        register int i;
        struct sector *mp;
        struct shipentry *sp;

        if (!shipmode) return;
        mx = offset(x);
        my = offset(y);
        mp = &map[mx][my];
        if (mp->shp == NOSHIPS) return;

        for (i = mp->shp+1; i < shipcount; i++) {
            sp = &ships[i];
            if (offset(sp->x) == mx && offset(sp->y) == my) {
                mp->shp = i;
                return;
            } /* if */
        } /* for */

        for (i = 0; i < mp->shp; i++) {
            sp = &ships[i];
            if (offset(sp->x) == mx && offset(sp->y) == my) {
                mp->shp = i;
                readship (i, sp->number);
                return;
            } /* if */
        } /* for */
} /* nextship */


X/*
 * offset - Return transformed coordinate. 
 */
offset(coordinate)
int coordinate;
{       
        register int modulo;
        
        modulo = coordinate%MAPSIZE;
        return  (modulo < 0 ? modulo+MAPSIZE : modulo);

} /* offset */


X/*
 *      opentf - open temporary file.
 */
opentf()
{
        sprintf(tfile, "%s%d", TEMPFILE, getpid());
        temp = creat(tfile, 0600);
        if (temp < 0) {
            write(2, "can't create temp file\n", 23);
            exit(1);
        } /* if */
        /*
         * Re-open tfile in read/write mode. This shouldn't be
         * necessary. It wouldn't be if some blooming idiot hadn't
         * made creat() and open() be two different system calls.
         * (At very least creat() should be able to return a file
         * descriptor which can be used both to read and to write.)
         */
        close(temp);   
        temp = open(tfile, 2);
        if (temp < 0) {
            unlink(tfile);
            write(2, "can't open temp file\n", 22);
            exit(1);
        } /* if */
        unlink(tfile);
} /* opentf */


pitchline(inpf)
FILE *inpf;
{
        register int c;

        while ((c = getc(inpf)) != '\n')
            if (c == EOF) break;

} /* pitchline */


X/*
 * presetmap - Preset map array.
 */
presetmap()
{
        register int x,y;
        register int i;
        struct shipentry *sp;
        
        for (y = 0; y < MAPSIZE ; y++)
            for (x = 0; x < MAPSIZE ; x++) {
                map[x][y].des = 0;
                map[x][y].own = 0;
                map[x][y].shp = NOSHIPS;
                map[x][y].diskaddr = NODISK;
            } /* for */

        for (i = 0; i < MAXSHIPS; i++) {
            sp = &ships[i];
            sp->des = 0;
            sp->number = UNKNOWN;
            sp->diskaddr = NODISK;
        } /* for */

} /* presetmap */


X/*
 * presetty - Set up terminal for display mode.
 */
presetty()
{
        crmode();
        noecho();
        nonl();
} /* presetty /*


X/*
 * previousship - Back up to previous ship at x,y.
 */
previousship(x,y)
int x,y;
{
        register int mx,my;
        register int i;
        struct sector *mp;
        struct shipentry *sp;

        if (!shipmode) return;
        mx = offset(x);
        my = offset(y);
        mp = &map[mx][my];
        if (mp->shp == NOSHIPS) return;

        for (i = mp->shp-1; i >= 0; i--) {
            sp = &ships[i];
            if (offset(sp->x) == mx && offset(sp->y) == my) {
                mp->shp = i;
                return;
            } /* if */
        } /* for */

        for (i = shipcount-1; i > mp->shp; i--) {
            sp = &ships[i];
            if (offset(sp->x) == mx && offset(sp->y) == my) {
                mp->shp = i;
                readship (i, sp->number);
                return;
            } /* if */
        } /* for */
} /* previousship */


X/*
 * processmove - process MOVE or NAV command.
 */
char processmove(bp,ip)
char *bp;
char **ip;
{
        register char c;
        int x,y;
        int oldx,oldy;  

        if (getnewxy(bp,&x,&y) == 0) return(0);
        getyx(stdscr,oldy,oldx);
        updatescreen(x,y);
        while (refresh(),(c = getch()) != -1 ) {
            switch (c) {
                case 'y':       x--;
                                y--;
                                break;
               
                case 'u':       x++;
                                y--;
                                break;

                case 'j':       x += 2;
                                break;

                case 'n':       x++;
                                y++;
                                break;

                case 'b':       x--;
                                y++;
                                break;

                case 'g':       x -= 2;
                                break;


                default:        updatescreen(curx,cury);
                                move(oldy,oldx); 
                                refresh();
                                return(c);
            } /* switch */
            move(oldy,oldx);
            **ip = c;
            (*ip)++;
            **ip = 0;
            addch(c);
            oldx++; 
            updatescreen(x,y);  
        } /* while */
} /* processmove */


X/*
 * putline - Do a printw at the bottom of the screen.
 */
putline(fmt, a1, a2, a3, a4)
{
        move(LINES-1, 0);
        printw(fmt, a1, a2, a3, a4);

} /* putline */


X/*
 * query - Parse ? command.
 */
query()
{
        register int pass = 0;
        char *bp = buf;
        char *tp;

        getline(bp, "?", NOX);
        if (!*bp) return;
        clearmks(0);
        for (;;) {
            if (tp = (char *)index(bp, '&')) {
                *tp++ = 0;
                 mark(bp, pass);
                 bp = tp;
            } else {
                 mark(bp, pass);
                 break;
            } /* if */
            pass++;
        } /* for */
} /* query */


X/*
 * readship - Read ship values from temporary file.
 */
readship(ix, number)
register int ix;                    /* Index into ship vector if known */
register int number;                /* Ship number (used if index unknown) */
{
        register int i;

        for (i = 0; i < VALUESIZE; i++) values.val[i] = 0;
        for (i = 0; i < DELSIZE; i++)   values.del[i] = 0;
        for (i = 0; i < CNTSIZE; i++)   values.cnt[i] = 0;

        if ((i = ix) == UNKNOWN) {
            for (i = 0; i < shipcount; i++)
                if (ships[i].number == number) goto match;
            return (NOSHIPS);
        } /* if */

match:  if (ships[i].diskaddr == NODISK) return (i);
        lseek (temp, ships[i].diskaddr, 0);
        read  (temp, &values, sizeof (struct value));
        return (i);
} /* readship */


X/*
 * readvalues - Read sector values from temporary file.
 */
readvalues (x,y)
int x,y;
{
        register int xt,yt;
        register int i;

        xt = offset(x);
        yt = offset(y);
        
        if (map[xt][yt].diskaddr == NODISK) {
            for (i = 0; i < VALUESIZE; i++) values.val[i] = 0;
            for (i = 0; i < DELSIZE; i++)   values.del[i] = 0;
            for (i = 0; i < CNTSIZE; i++)   values.cnt[i] = 0;
            return;
        } /* if */

        lseek(temp,map[xt][yt].diskaddr,0);
        read(temp,&values,sizeof (struct value));

} /* readvalues */


skipword(inpf)
FILE *inpf;
{
        register char c;

        for (;;) {
            c = getc(inpf);
            if (c == '\n' || c == ' ' || c == '\t') return(1);
            if (c == EOF) return(0);
        } /* for */
} /* skipword */


X/*
 * strcopy - copy string.
 */
strcopy (str,dst)
register char *str,*dst;
{
        while(*dst++ = *str++ );
}


X/*
 * survey - Display survey info for a given item.
 */
survey(sp)
char *sp;
{
        register struct item *ip;
        register int itm;
        register int x,y;
        register struct sector *mp;
        register int surval;

        for (ip = items; ip->len; ip++)
            if (!strncmp(sp, ip->nm, ip->len)) break;

        if (!ip->len) {
            putline("I don't know about %s", sp);
            return;
        } /* if */

        itm = ip - items;
        for (y = 0; y < MAPSIZE; y++)
            for (x = 0; x < MAPSIZE; x++) {
                mp = &map[x][y];
                if (mp->own && mp->des != '.') {
                    readvalues (x,y);
                    surval = values.val[itm]/range;
                    if (surval > 35) mp->surv = '$';
                    else {
                        mp->surv = surval;
                        mp->surv += (mp->surv > 9) ? ('A'-10) : '0';
                    } /* if */
                } /* if */
            } /* for */
} /* survey */


X/*
 * updatescreen - Update screen if display window moved. 
 */
updatescreen(x,y)
register int x,y;
{
        register int update = 0;
        
        if (x < startx || x > startx + MCOLS) {
            startx = (x < startx) ? x : x - MCOLS;
            update++;
        } /* if */
        
        if (y < starty || y > starty + MLINES) {
            starty = (y < starty) ? y : y - MLINES;
            update++;
        } /* if */
        
        if (update) {
            mapdr(surmap);
            touchwin(stdscr);
        } /* if */
        censusinfo(x,y);
        move(y-starty,x-startx);
        refresh();
} /* updatescreen */
                        

X/*
 * writeship - Write ship values to temporary file.
 */
writeship(ix, number)
register int ix;                    /* Index into ship vector if known */
register int number;                /* Ship number (used if index unknown) */
{
        register int i;
        long tell();

        if ((i = ix) == UNKNOWN) {
            for (i = 0; i < shipcount; i++)
                if (ships[i].number == number) goto match;
            return (NOSHIPS);
        } /* if */

match:  if (ships[i].diskaddr == NODISK) {
            lseek (temp, 0L, 2);
            ships[i].diskaddr = tell (temp);
        } else
            lseek (temp, ships[i].diskaddr, 0);
        write (temp, &values, sizeof (struct value));
        return (i);
} /* writeship */


X/*
 * writevalues - write sector values to temporary file.
 */
writevalues(x,y)
int x,y;
{
        register int xt,yt;
        long tell();

        xt = offset(x);
        yt = offset(y);

        if(map[xt][yt].diskaddr == NODISK) {
            lseek(temp,0L,2);
            map[xt][yt].diskaddr = tell(temp);
        } else
            lseek(temp,map[xt][yt].diskaddr,0);
        write(temp,&values,sizeof (struct value));

} /* writevalues */


//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 640 ve.c
	/bin/echo -n '	'; /bin/ls -ld ve.c
fi