blob: 404c6cc65f758472204be060771c9de41f4c2dc1 [file] [log] [blame] [raw]
/*
* Kernel Memory Editor (KME)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
char kme_c_version[] = "@(#)kme.c $Revision$ $Date$";
#include <sys/types.h>
#include <setjmp.h>
#include <ctype.h>
#include <signal.h>
#include <fcntl.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include "config.h"
#if HAVE_SOCKET
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# include <arpa/inet.h>
#endif
#if COFF && NLIST
#error You are hosed. Defined either COFF or NLIST - not both.
#endif
/*
This really should be autoconfiscated, but now that every useful
system on the planet has a perfectly lovely nlist, I'm not convinced
it's useful.
#if COFF
# include <filehdr.h>
# include <ldfcn.h>
# include <syms.h>
#endif */
#if HAVE_GETOPT_H
# include <getopt.h>
#endif
#if HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#if HAVE_SYS_KSYM_H
# include <sys/ksym.h>
#endif
#if HAVE_ELF_H
# include <elf.h>
#endif
#if HAVE_NLIST_H
# include <nlist.h>
#endif
#if HAVE_LIBELF_NLIST_H
# include <libelf/nlist.h>
#endif
#if HAVE_MMAP
# include <sys/mman.h>
#endif
#if HAVE_TERMIOS_H
# include <termios.h>
#endif
#if defined(KEY_HOME)
# define TERMINFO 1
#endif
#if HAVE_LIBNCURSES
# include <ncurses.h>
#endif
#if HAVE_LIBCURSES
# include <curses.h>
#endif
#if HAVE_SYS_PTRACE_H
# include <sys/ptrace.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_STRING_H
# include <string.h>
#endif
#if HAVE_WAIT_H
# include <wait.h>
#endif
#if HAVE_SOCKET
# include <netinet/in.h>
# include <netdb.h>
#endif
#if HAVE_LIBDL
# include <dlfcn.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
/*
* Now try to set up internal typedefs for things that *have* to be
* a fixed size. Use the C99 types if we can.
*/
#if HAVE_INTTYPES_H
typedef uint8_t kme_uint8_t ;
typedef uint16_t kme_uint16_t;
typedef uint32_t kme_uint32_t;
typedef uint64_t kme_uint64_t;
#else
typedef unsigned char kme_uint8_t ;
typedef unsigned short kme_uint16_t;
typedef unsigned long kme_uint32_t;
#if CC_HAS_LONG_LONG
typedef unsigned long long kme_uint64_t;
#endif
#endif
/*
* Unsigned variables. This form makes them impervious
* to similar defs in sys/types.h.
*/
#define uchar unsigned char
#define ulong unsigned long
/*
* This is the (currently incorrect) type of all target addresses
* used internally.
*/
typedef ulong kme_addr_t;
#if HAVE_SOCKET
#if HAVE_STROPTS_H
# include <stropts.h>
#endif /* HAVE_STROPTS_H */
#endif
/*
* Compensate for lame systems that support ptrace, but
* have no <sys/ptrace.h>
*/
#if 1
#if !defined PTRACE_ATTACH
# define PTRACE_ATTACH 10
#endif
#if !defined PTRACE_DETACH
# define PTRACE_DETACH 11
#endif
#if !defined PTRACE_PEEKDATA
# define PTRACE_PEEKDATA 2
#endif
#if !defined PTRACE_POKEDATA
# define PTRACE_POKEDATA 5
#endif
#if !defined PTRACE_CONT
# define PTRACE_CONT 7
#endif
#endif
#if lint
#define malloc(x) 0
#endif
extern int optind;
extern char *optarg;
int insert_mode(void);
int update_line(int);
#include "kme.h"
char kme_h_version[] = KME_H_VERSION;
/*
* OLDCURSE indicates a curses where keypad keys (eg arrow keys)
* do not work when using VMIN=0. Unfortunately, keypad keys
* are broken almost everywhere, so OLDCURSE is now default.
*/
#define OLDCURSE 1
#if OLDCURSE
#define chtype int
#endif
#define ctrl(x) ((x) & 0x1f)
/*
* Default file names.
*/
char *corename = "/dev/kmem";
char *defsname = "kme_defs";
char *symname = 0;
char *defspath = ".";
/*
* Interface to SC vi-edit routines.
*/
#define LINELEN 200 /* Max line size */
extern int linepos; /* Line position */
extern char line[]; /* Line buffer */
extern int (*lineproc)(); /* Line processing procedure */
extern int mode_ind; /* Mode indicator */
/*
* Address field definition.
*/
typedef struct
{
long a_addr; /* Display offset */
char* a_disp; /* Display string */
int a_size; /* Number of bytes */
int a_grow; /* Automatically unobscure itself */
} a_field;
kme_addr_t addr_mask = -1UL; /* All addresses read from a symbol
file are masked with this */
#define NADR 400 /* Max number of addresses */
a_field afield[NADR]; /* Address fields */
a_field bfield[NADR]; /* Address buffer */
/*
* Data field definition.
*/
typedef struct
{
kme_addr_t d_addr; /* Address */
int d_type; /* Type */
} d_field;
#define NDATA (66 * 12) /* Max number of data columns */
d_field dfield[NDATA]; /* Column fields */
#define DFIELD(r,c) (dfield[ncol * ((r) - frow) + (c) - 1])
/*
* Field row/column positioning.
*/
#define FROW(r) ((r) - frow)
/*
* If screen space is plentiful and our addresses may be longer than
* 8 bytes long, we bump the first colum over to make space for them.
*/
#define START_DATA_COL (((sizeof(kme_addr_t) > 4) && COLS > 80) ? 15 : 7)
#define FCOL(c) ((c) ? (8 * (c) + START_DATA_COL) : 5)
int initial_display_row; /* To track order of automatic
insertions */
/*
* Terminal structure.
*/
#if HAVE_TERMIOS_H
struct termios otio; /* Original terminal structure */
struct termios tio; /* Curses terminal structure */
#else /* HAVE_TERMIOS_H */
struct termio otio; /* Original terminal structure */
struct termio tio; /* Curses terminal structure */
#endif /* HAVE_TERMIOS_H */
/*
* Expression parsing variables.
*/
jmp_buf efail; /* Error recovery */
char *str; /* String position */
int inparen = 0; /* In parenthesis */
int ebase; /* Default input base */
/*
* Symbol structure.
*/
typedef struct symbol SYM;
struct symbol
{
SYM* s_next; /* Next item on list */
ulong s_value; /* Symbol value */
char s_name[10]; /* Symbol name */
};
#define NHASH 2909 /* Hash table size (prime) */
SYM* symbol[NHASH]; /* Hash table */
char *symparam; /* Symbol file name parameter */
/*
* Temp variables.
*/
#define TEMP_MAX 10
ulong temp[TEMP_MAX]; /* Temp store variables */
int width = 4;
/*
* Miscellaneous flags.
*/
char *progname; /* Program name */
int sigint; /* Got an interrupt */
int idline; /* Call idlok() */
int quitreq; /* Exit quick */
int debug; /* Debug on */
int depth; /* Recursion depth */
int scount; /* Scroll count */
int uptime = 2; /* Screen update time */
int helpflag = 0; /* Show help information */
int addrflag = 1; /* Show addresses */
int writeflag = 1; /* Open core file for write */
int swapflag = 0; /* Swap "endianness" for
words & longs */
int index_mode = 1; /* display array as
"foo+0x80" or "foo[1]" */
int addrscale = 1; /* Scale displayed addresses */
int rw_max; /* Max readahead */
/*
* Special FEP support variables.
*/
int def_board; /* Default board number */
int def_module; /* Default concentrator number */
int ov_board; /* Overide board number */
int ov_module; /* Overide concentrator number */
#if HAVE_SOCKET
int nsoc; /* Actual number of sockets */
char *fepdev; /* FEP device name */
#endif
#if HAVE_STROPTS_H
int strdev = 0;
#endif
#if HAVE_SOCKET
#define RW_PAKLEN \
((unsigned)&((rw_t *)0)->rw_data[sizeof(((rw_t *)0)->rw_data)])
#define MAXSOC 10 /* Max number of open sockets */
int socfd[MAXSOC]; /* Socket file descriptor */
char *hostname; /* Host name list */
int udp_port; /* UDP port number */
#endif
int fepfd; /* FEP file descriptor */
/*
* Screen variables.
*/
int onscreen; /* This row on the screen */
int brow; /* Number of rows in buffer */
int frow; /* First row on the screen */
int lrow; /* First row on last refresh */
int trow; /* Temporary version of frow */
int crow; /* Current display row */
int erow; /* End of display area */
int irow; /* Input row */
int icol; /* Input column */
int orow; /* Output row */
int ocol; /* Output column */
int nrow; /* Number of rows on screen */
int ncol; /* Number of columns on screen */
/*
* Formatting strings.
*/
char *format[26]; /* Display formatting strings */
char keyformat[26]; /* Format entered from
command/keyboard */
/*
* Symbolic formatting strings.
*/
typedef struct
{
char * name; /* Name entered from command or kbd */
char * format; /* Display formatting strings */
char *(*dlfunc)(kme_addr_t *addrp, int dlarg); /* Function */
int dlarg; /* User defined function argument */
} d_fmt;
int fmt_cnt;
int dl_fmt_cnt;
#define NSYM_FORMATS 512 /* Max number of symbol formattings */
d_fmt dfmt[NSYM_FORMATS];
int readch(void);
char hexnum[] = "0123456789abcdefghijklmnopqrstuvwxyz";
/*
* Core memory access.
*/
char *coredev; /* Core file parameter */
kme_addr_t base; /* Base address */
kme_addr_t addr; /* Memory address being displayed */
kme_addr_t faddr; /* First address this display item */
int lboard; /* Last board number */
int lconc; /* Last conc number */
kme_addr_t laddr; /* Low read address */
kme_addr_t haddr; /* High read address */
int memfd; /* Core file descriptor */
long mem[128]; /* Memory array */
#if HAVE_MMAP
kme_addr_t mempa; /* Memory phys addr */
kme_addr_t memlen; /* Memory length */
uchar *memva; /* Virtual address */
#endif
#define ALIGNED(ADDR,BYTES) (! (((int) ADDR) & (BYTES-1)))
#if HAVE_PTRACE
int pid; /* Ptrace process id */
int pidrun; /* Process is running */
#endif
/*
* Help strings.
*/
char *helptext[] =
{
"+/- (c)hange (d)elete (e)dit (i)nsert (m)acro (p)ut (y)ank (q)uit (z)ero",
"Enter address/format",
"Enter the value to store in memory",
};
/************************************************************************
* catch - catch interrupts gracefully
************************************************************************/
void
catch(int sig)
{
signal(SIGINT, catch);
sigint = 1;
beep();
}
/************************************************************************
* bigendian - Return true if we are on a bigendian host
************************************************************************/
int bigendian()
{
int endian = 1;
return ((char *) &endian)[0] != 1;
}
/************************************************************************
* swaps - Swap short operand.
************************************************************************/
kme_uint16_t
always_swaps(kme_uint16_t mval)
{
mval = (((mval >> 8) & 0xff) | ((mval << 8) & 0xff00));
return (mval);
}
kme_uint16_t
swaps(kme_uint16_t mval)
{
if (swapflag)
{
mval = always_swaps(mval);
}
return (mval);
}
/************************************************************************
* swapl - Swap long operand.
************************************************************************/
kme_uint32_t
always_swapl(kme_uint32_t mval)
{
mval = (((mval >> 24) & 0x000000ff) |
((mval >> 8) & 0x0000ff00) |
((mval << 8) & 0x00ff0000) |
((mval << 24) & 0xff000000));
return(mval);
}
kme_uint32_t
swapl(kme_uint32_t mval)
{
if (swapflag)
mval = always_swapl(mval);
return (mval);
}
#if CC_HAS_LONG_LONG
/************************************************************************
* swapq - Swap quadword operand.
************************************************************************/
kme_uint64_t
always_swapq(kme_uint64_t mval)
{
mval = ( ((mval & 0x00000000000000ffULL) << 56) |
((mval & 0x000000000000ff00ULL) << 40) |
((mval & 0x0000000000ff0000ULL) << 24) |
((mval & 0x00000000ff000000ULL) << 8) |
((mval & 0x000000ff00000000ULL) >> 8) |
((mval & 0x0000ff0000000000ULL) >> 24) |
((mval & 0x00ff000000000000ULL) >> 40) |
((mval & 0xff00000000000000ULL) >> 56)
);
return(mval);
}
kme_uint64_t
swapq(kme_uint64_t mval)
{
if (swapflag)
mval = always_swapq(mval);
return (mval);
}
#endif /* CC_HAS_LONG_LONG */
/************************************************************************
* stralloc - Allocate string.
************************************************************************/
char *
stralloc(char* s)
{
char *d;
if (s == 0) return(0);
d = (char *)malloc((unsigned)strlen(s)+1);
if (d) strcpy(d, s);
return(d);
}
/************************************************************************
* strfree - Free string.
************************************************************************/
void
strfree(char* s)
{
if (s) free((void *)s);
}
/************************************************************************
* Find a display symbol name in the tables
* Returns index in array.
*
* FIXME: Optimization Opportunity. Rather
* than ripping through these sequentially,
* do a hashed lookup. This has to consume
* killer clock cycles, and it's run on each
* screen update.....
*
************************************************************************/
int
find_format(char* s)
{
int x;
for (x=0;x<fmt_cnt;x++)
if (strcmp(dfmt[x].name,s) == 0)
return (x);
return(-1);
}
/************************************************************************
* Add a symbolic format to the table.
* FIXME: should build hash table (index?) hinted at above...
************************************************************************/
void
add_format(char *name, char *format,
char * (*dlfunc)(kme_addr_t *addrp, int dlarg), int dlarg)
{
dfmt[fmt_cnt].name = stralloc(name);
dfmt[fmt_cnt].format = format ? stralloc(format) : NULL;
dfmt[fmt_cnt].dlfunc = dlfunc;
dfmt[fmt_cnt].dlarg = dlarg;
if (debug)
fprintf(stderr,
"Defined full command: (%s)%d = %s\n",
dfmt[fmt_cnt].name, fmt_cnt,
format ? dfmt[fmt_cnt].format : "function");
if (fmt_cnt++ > NSYM_FORMATS)
{
fprintf(stderr, "\nFATAL: exceeded %d symbolic formats\n",
NSYM_FORMATS);
exit(1);
}
}
/************************************************************************
* getbase - Convert the next few characters of an ascii
* string to a number of the specified base.
************************************************************************/
char *
getbase(char* s, ulong* value, int b)
{
ulong v;
int n;
int d;
/* Handle float */
if (b == -4)
{
float f;
char *ss = s;
while ('0' <= *s && *s <= '9')
s++;
if (*s == '.')
{
s++;
while ('0' <= *s && *s <= '9')
s++;
}
if (s == ss)
return 0;
if (*s == 'e')
{
s++;
if (*s == '-' || *s == '+')
s++;
while ('0' <= *s && *s <= '9')
s++;
}
if (sscanf(ss, "%f", &f) != 1)
return 0;
*value = *(ulong*)&f;
return s;
}
/* Handle integer */
if (s[0] == '0')
{
if (s[1] == 't') { b = 10;s += 2;}
else if (s[1] == 'x') { b = 16;s += 2;}
else b = 16;
}
v = 0;
for (n = 0;; n++)
{
if ('0' <= *s && *s <= '9') d = *s - '0';
else if ('a' <= *s && *s <= 'z') d = *s - 'a' + 10;
else if ('A' <= *s && *s <= 'z') d = *s - 'A' + 10;
else break;
if (d >= b) break;
v = b * v + d;
s++;
}
if (n == 0) return(0);
*value = v;
return(s);
}
/************************************************************************
* getdigit - Convert an alphanumeric digit to a number.
************************************************************************/
int
getdigit(int c)
{
return(c <= '9' ? c - '0' : c < 'Z' ? c - 'A' + 10 : c - 'a' + 10);
}
/************************************************************************
* readdefs - Read formatting strings from a file.
************************************************************************/
int readdefs()
{
register char *cp;
register int ch;
register int i;
register int nstring;
register int cmd;
int full_cmd;
int rtn;
int quote;
int bang;
char *s;
char *f;
FILE *file;
char buf[200];
char string[32768];
char fmt_name[200];
char *fmt_name_index;
/*
* Discard old format strings.
*/
for (i = 0;i < 26;i++)
{
if (!keyformat[i])
{
strfree(format[i]);
format[i] = 0;
}
}
fmt_cnt = dl_fmt_cnt;
/*
* Loop to open several filenames separated
* by colons.
*/
rtn = 1;
for (f = defsname;*f;)
{
/*
* Open the file.
*/
s = buf;
while (*f && (ch = *f++) != ':') *s++ = ch;
*s = 0;
if (*buf == '/')
{
file = fopen(buf, "r");
if (file == 0)
{
rtn = 0;
fprintf(stderr, "Warning: could not open ");
perror(buf);
continue;
}
}
else
{
char path[200];
char fullpath[200];
char *p, *ps;
file = 0;
for (p = defspath; *p;)
{
ps = path;
while (*p && (ch = *p++) != ':') *ps++ = ch;
*ps = 0;
if (*path == 0) strcpy(path, ".");
sprintf(fullpath, "%s/%s", path, buf);
file = fopen(fullpath, "r");
if (file)
break;
}
if (file == 0)
{
if (strcmp(buf, "kme_defs"))
{
rtn = 0;
fprintf(stderr, "Warning: could not find ");
perror(buf);
}
continue;
}
}
/*
* Read lines from the file.
*/
cmd = 0;
full_cmd = 0;
nstring = 0;
for (;;)
{
/*
* Get next line from input.
*/
s = fgets(buf, sizeof(buf), file);
/*
* If EOF, or a new command, allocate space for
* the last command.
*/
if (s == 0 || isupper(buf[0]) || (buf[0] == '!'))
{
if (cmd && !keyformat[cmd -= 'A'])
{
strfree(format[cmd]);
string[nstring] = 0;
format[cmd] = stralloc(string);
if (debug)
{
fprintf(stderr, "Defined command: %c = %s\n",
cmd + 'A', string);
}
}
else if (full_cmd)
{
string[nstring] = 0;
add_format(fmt_name, string, NULL, 0);
}
cmd = 0;
full_cmd = 0;
nstring = 0;
}
/*
* Exit on EOF.
*/
if (s == 0) break;
/*
* A line beginning with an upper case letter begins
* a definition. A line who's first non-white space
* is a "#" is a comment. Other lines are continuations.
* Single and double quoted strings are passed verbatim,
* otherwise white space is discarded.
* Behaviour Change 07/02/94 robertl@arnet.com.....
* If the first character is a '!', we take that as
* a "symbolic" format name.
* Symbolic format names are not recursive.
* Symbolic names may contain any printing character.
*/
cp = &buf[0];
/*
* Check for a new format definition name at beginning of line
*/
if (isupper(*cp))
{
/*
* Its an old style definition
*/
cmd = *cp++;
}
else if (*cp == '!')
{
/*
* Its a new style definition
*/
++cp;
fmt_name_index = fmt_name;
while (cp && (isalnum(*cp) || (*cp == '_')))
*fmt_name_index++ = *cp++;
*fmt_name_index = 0;
full_cmd = 1;
}
/*
* Process rest of line
*/
while (isspace(*cp)) cp++;
if ((*cp == '#')) continue;
quote = 0;
bang = 0;
while ((ch = *cp++) != 0)
{
if (quote)
{
if (ch == quote) quote = 0;
}
else
{
if (bang)
{
if (!isalnum(ch) && ch != '_') bang = 0;
if (isspace(ch)) ch = ' ';
}
else if (isspace(ch)) continue;
if (ch == '\'' || ch == '"') quote = ch;
else if (ch == '!') bang = 1;
}
if (nstring < sizeof(string)-2) string[nstring++] = ch;
}
if (quote && nstring < sizeof(string)-2) string[nstring++] = ch;
}
fclose(file);
}
return(rtn);
}
/************************************************************************
* hashname - Hash name to symbol table pointer.
************************************************************************/
SYM**
hashname(char* name)
{
register ulong v;
v = 0;
while (*name)
{
v += *name++;
v *= 23;
}
return symbol + (v % NHASH);
}
/************************************************************************
* readsym - Read namelist file.
************************************************************************/
int
readsym()
{
register SYM **spp;
register SYM *sp;
register char *cp;
char *f;
int ch;
int rtn;
FILE *file;
ulong value;
char *name;
char buf[512];
unsigned n;
#if COFF
LDFILE *ldp;
SYMENT sym;
int i;
unsigned short magic;
#endif
/*
* Clear the current namelist file.
*/
for (spp = symbol;spp < symbol + NHASH;spp++)
{
while ((sp = *spp) != 0)
{
*spp = sp->s_next;
free((void *)sp);
}
}
/*
* Read in the new symbol file.
*/
rtn = 1;
for (f = symname;*f;)
{
/*
* Open the file.
*/
cp = buf;
while (*f && (ch = *f++) != ':') *cp++ = ch;
*cp = 0;
file = fopen(buf, "r");
if (file == 0)
{
rtn = 0;
continue;
}
#if COFF
/*
* See if file is COFF
*/
n = fread(&magic, sizeof(magic), 1, file);
if (n == 1 && ISCOFF(magic))
{
/*
* COFF namelist
*/
fclose(file);
ldp = ldopen(buf, (LDFILE *) NULL);
if (ldp)
{
for (i = 0; ldtbread(ldp, i, &sym) != FAILURE; ++i)
{
i += sym.n_numaux; /* Skip AUX info */
name = ldgetname(ldp, &sym);
if (!name) continue;
if (sym.n_sclass != C_EXT) continue;
spp = hashname(name);
n = (unsigned)&((SYM*)0)->s_name[strlen(name)+1];
sp = (SYM*)malloc(n);
if (sp == 0)
{
fprintf(stderr, "Out of memory");
exit(1);
}
sp->s_next = *spp;
*spp = sp;
sp->s_value = sym.n_value & addr_mask;
strcpy(sp->s_name, name);
if (debug)
{
fprintf(stderr, "Added Symbol %s = %x\n",
sp->s_name, sp->s_value);
}
}
ldclose(ldp);
}
continue;
}
fseek(file, 0L, 0);
#endif
#if HAVE_NLIST
/*
If we're able to use nlist(2), we don't have to
read this whole thing into core. But to allow
the ability for a single kme binary to work with
both native binaries (say, /unix) and binaries
that we provide an ASCII symbol table for (cay,
csfep.sym), let's read some stuff. If any of it
is non-ascii, we'll let the nlister take care of
it.
*/
{
int file_is_binary = 0;
n = fread(&buf, 1, sizeof(buf), file);
while(n--)
{
if (!isascii(buf[n]))
file_is_binary = 1;
}
if (file_is_binary)
{
if (debug)
fprintf(stderr, "File is binary, continuing\n");
fclose(file);
continue;
}
}
#endif
/*
* Read all the symbols in the (ASCII) file.
*/
fseek(file, 0L, 0);
while (fgets(buf, sizeof(buf), file) != 0)
{
cp = buf;
while (isspace(*cp)) cp++;
cp = getbase(cp, &value, 16);
if (cp == 0)
{
rtn = 0;
continue;
}
while (isspace(*cp)) cp++;
name = cp;
while (*cp && !isspace(*cp))
cp++;
*cp = 0;
#if HAVE_PROC_KSYMS
/*
* Strip module Ids from /dev/ksym files.
*/
if (cp - name > 10 && cp[-10] == '_' && cp[-9] == 'R')
{
char *hp = cp - 8;
while (isxdigit(*hp))
hp++;
if (*hp == 0)
cp[-10] = 0;
}
#endif
spp = hashname(name);
n = (unsigned)&((SYM*)0)->s_name[strlen(name)+1];
sp = (SYM*)malloc(n);
if (sp == 0)
{
fprintf(stderr, "Out of memory");
exit(1);
}
sp->s_next = *spp;
*spp = sp;
sp->s_value = value & addr_mask;
strcpy(sp->s_name, name);
if (debug)
{
fprintf(stderr, "Added Symbol %s = %lx\n",
sp->s_name, sp->s_value);
}
}
}
return(rtn);
}
/************************************************************************
* stopchild - Stops ptrace() child for memory inspection.
************************************************************************/
#if HAVE_PTRACE
int
stopchild()
{
int status;
if (!pidrun)
return 1;
pidrun = 0;
if (kill(pid, SIGSTOP) != 0)
{
perror("SIGSTOP");
return 0;
}
if (waitpid(pid, &status, 0) != pid || !WIFSTOPPED(status))
fprintf(stderr, "waitpid()\n");
if (WSTOPSIG(status) != SIGSTOP)
{
ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status));
quitreq = 1;
}
return 1;
}
#endif
/************************************************************************
* startchild - Starts ptrace() child when memory inspection complete.
************************************************************************/
#if HAVE_PTRACE
int
startchild()
{
if (pidrun)
return 0;
pidrun = 1;
if (ptrace(PTRACE_CONT, pid, 0, SIGCONT) != 0)
{
perror("PTRACE_CONT");
return 0;
}
return 1;
}
#endif
/************************************************************************
* getmem - Get memory bytes.
************************************************************************/
int
getmem(unsigned nbyte)
{
int rw_size = rw_max < nbyte ? rw_max : nbyte;
long a = (addr + base) & addr_mask;
static rw_t rw;
static int rtncode;
/*
* If the data is already in the read-ahead buffer,
* there is no need to fetch it.
*/
if (ov_board == lboard &&
ov_module == lconc &&
a >= laddr &&
a + nbyte <= haddr)
{
goto rw_return;
}
#if HAVE_STROPTS_H
if (fepdev && strdev)
{
struct strioctl stio;
/*
* Build the request and do the ioctl.
*/
rw.rw_req = RW_READ;
rw.rw_size = rw_size;
rw.rw_board = lboard = ov_board;
rw.rw_module = lconc = ov_module;
rw.rw_addr = laddr = a;
stio.ic_cmd = DIGI_KME;
stio.ic_timout = 0;
stio.ic_len = sizeof(rw);
stio.ic_dp = (char *) &rw;
if (ioctl(fepfd, I_STR, &stio) == -1 || rw.rw_size < nbyte)
{
haddr = rw.rw_addr + 1024;
rtncode = 0;
}
else
{
haddr = rw.rw_addr + rw.rw_size;
rtncode = 1;
}
goto rw_return;
}
#endif /* HAVE_STROPTS_H */
/*
* Read memory from the DigiBoard special device
* driver hooks.
*/
if (fepdev)
{
/*
* Build the request and do the ioctl.
*/
rw.rw_req = RW_READ;
rw.rw_size = rw_size;
rw.rw_board = lboard = ov_board;
rw.rw_module = lconc = ov_module;
rw.rw_addr = laddr = a;
if (ioctl(fepfd, DIGI_KME, &rw) == -1 || rw.rw_size < nbyte)
{
haddr = rw.rw_addr + 1024;
rtncode = 0;
}
else
{
haddr = rw.rw_addr + rw.rw_size;
rtncode = 1;
}
goto rw_return;
}
#if HAVE_SOCKET
/*
* Read memory from TCP/IP style device.
*/
if (nsoc)
{
int fd;
int rtry;
int wtry;
rw_t w;
struct timeval tv;
static fd_set fdn;
static fd_set fdr;
if (ov_board >= nsoc)
return(0);
/*
* Build the request packet.
*/
w.rw_req = RW_READ;
w.rw_size = rw_size;
w.rw_board = lboard = ov_board;
w.rw_module = lconc = ov_module;
w.rw_addr = laddr = a;
w.rw_size = swaps(w.rw_size);
w.rw_addr = swapl(w.rw_addr);
fd = socfd[w.rw_board];
/*
* Repeatedly request the data with UDP until
* a response is heard from the remote.
*/
rtncode = 0;
for (wtry = 0;!rtncode;wtry++)
{
if (wtry >= 3)
{
haddr = laddr + 1024;
break;
}
write(fd, (char *)&w, RW_PAKLEN);
/*
* Wait for the response packet.
*/
for (rtry = 0; !rtncode && rtry < 3; rtry++)
{
/*
* On timeout, exit with bad return status.
*/
FD_ZERO(&fdr);
FD_SET(fd, &fdr);
tv.tv_sec = 1;
tv.tv_usec = 0;
if (select(FD_SETSIZE, &fdr, &fdn, &fdn, &tv) < 1)
break;
/*
* On response from the correct file descriptor,
* with the response we expect, accept the response.
*
* Note that it is possible to get an irrelevant
* response if on a previous request, the network
* delays exceeded our simplistic 2 second timeout.
*/
if (read(fd, (char *)&rw, RW_PAKLEN) == RW_PAKLEN &&
rw.rw_req == w.rw_req &&
rw.rw_board == w.rw_board &&
rw.rw_module == w.rw_module &&
rw.rw_addr == w.rw_addr)
{
rw.rw_size = swaps(rw.rw_size);
rw.rw_addr = swapl(rw.rw_addr);
if (rw.rw_size < nbyte)
{
haddr = rw.rw_addr + 0x100;
return(0);
}
haddr = rw.rw_addr + rw.rw_size;
rtncode = 1;
break;
}
}
}
goto rw_return;
}
#endif
#if HAVE_MMAP
/*
* Read data from mapped file.
*/
if (memlen)
{
uchar *p = memva + a;
if (a < 0 || a + nbyte > memlen)
return(0);
memcpy((char *) mem, p, (unsigned) nbyte);
return(1);
}
#endif
#if HAVE_PTRACE
/*
* Ptrace memory read.
*/
if (pid)
{
int buf[2];
#if 0
fprintf(stderr, "read(%d, %x, %d)\n", pid, a, nbyte);
#endif
if (!stopchild())
return 0;
/* Fetch our data */
buf[0] = ptrace(PTRACE_PEEKDATA, pid, a & ~3, 0);
if (((a & 3) + nbyte) > 4)
buf[1] = ptrace(PTRACE_PEEKDATA, pid, (a & ~3) + 4, 0);
memcpy((char *)mem, (char*)buf + (a & 3), nbyte);
return 1;
}
#endif
/*
* Plain old memory read.
*/
if (lseek(memfd, a, SEEK_SET) != a ||
read(memfd, (char *)mem, (unsigned) nbyte) != nbyte)
{
return 0;
}
return 1;
/*
* Return data in rw buffer.
*/
rw_return:
if (rtncode)
{
memcpy((char *)mem,
(char *)&rw.rw_data[a-laddr],
nbyte);
}
return rtncode;
}
/************************************************************************
* showhelp - Show help if selected.
************************************************************************/
void showhelp(int index)
{
char *hp;
if (!helpflag) return;
hp = helptext[index];
move(LINES-1, 0);
clrtoeol();
move(LINES-1, (COLS - strlen(hp)) / 2);
printw("%s", hp);
}
/************************************************************************
* clearline - Clear the current line.
************************************************************************/
void clearline()
{
register d_field *dp;
register int i;
assert(frow <= crow && crow < frow + nrow);
/*
* Show line number.
*/
if (lrow != frow)
{
standend();
move(FROW(crow), 0);
printw("%3d| ", crow + 1);
}
/*
* Clear remainder of line.
*/
move(FROW(crow), FCOL(0));
clrtoeol();
/*
* Highlight the selected field.
*/
if (irow == crow)
{
move(FROW(irow), FCOL(icol));
standout();
printw(" ");
}
/*
* Clear data fields this line.
*/
dp = &DFIELD(crow, 1);
for (i = ncol;--i >= 0;)
{
dp->d_type = 0;
dp->d_addr = 0;
dp++;
}
}
/************************************************************************
* insertscreenline - shove everything down 'count' lines
* on the scereen
************************************************************************/
void
insertscreenline(int count)
{
int r;
a_field *ap;
if (count == 0) count = 1;
r = NADR;
ap = &afield[NADR];
while (r > irow)
{
--ap;
--r;
if (r >= NADR-count) strfree(ap[0].a_disp);
if (r >= irow+count)
ap[0] = ap[-count];
else
{
ap[0].a_addr = 0;
ap[0].a_disp = 0;
ap[0].a_size = 0;
}
}
}
/************************************************************************
* initline - Initialize a line prior to writing on it.
************************************************************************/
void initline()
{
clearline();
if (irow == crow && icol == 0) standout();
else standend();
move(FROW(crow), FCOL(0));
crow++;
if (crow < NADR && afield[crow].a_disp)
{
erow = crow;
/* If it's an automatically growing field, and we are
* overlapping, shove everything down. Try to keep out
* cursor in the obvious place.
*/
if (afield[crow].a_grow)
{
int orig_irow = irow;
irow=crow;
insertscreenline(1);
irow = orig_irow;
}
}
}
/************************************************************************
* setup - Setup next field.
************************************************************************/
void setup()
{
/*
* At end-of-line, advance to the next line.
*/
if (ocol == ncol)
{
orow++;
ocol = 0;
}
/*
* Clear screen rows from the last position data was written
* to the current field position. Write the address of each
* line in the first field. Scale the address displayed by
* the scale factor (used when kme'ing things that aren't
* byte addressable, like DSPs).
*/
while (crow <= orow && crow < erow)
{
initline();
printw("%06lx", addr / addrscale);
}
/*
* Position to the next field, and determine if it is
* on the screen.
*/
ocol++;
if (frow <= orow && orow < erow)
{
onscreen = 1;
if (irow == orow && icol == ocol) standout();
else standend();
move(FROW(orow), FCOL(ocol));
}
else
{
onscreen = 0;
}
}
/************************************************************************
* printchar - Unambiguously print character.
************************************************************************/
void printchar(int ch)
{
if (ch < 0x20)
printw("^%c", (ch & 0x1f) + 0x40);
else if (ch < 0x7f)
printw(" %c", ch);
else
printw("%02x", ch);
}
/************************************************************************
* display - Display memory data.
************************************************************************/
void display(char* fp)
{
register int ch;
register int count;
register int n;
register int i;
ulong a;
int c;
static char *endparen;
int orig_swapflag = swapflag;
int orig_width = width;
int have_count;
count = 0;
have_count = 0;
while ((ch = *fp++) != 0)
{
/*
* Get count field.
*/
if ('0' <= ch && ch <= '9')
{
have_count = 1;
count *= 10;
count += ch - '0';
continue;
}
/*
* Process field type.
*/
switch (ch)
{
/*
* Skip whitespace.
*/
case ' ':
break;
/*
* Process parenthesized expression.
*/
case '(':
for (;;)
{
ulong a = faddr;
faddr = addr;
display(fp);
faddr = a;
if (--count <= 0) break;
}
fp = endparen;
break;
case ')':
endparen = fp;
return;
/*
* Negate swapflag. This allows override on specific display
* elements endianness.
*/
case '%':
swapflag ^= 1;
break;
/*
* Process string field.
*/
case '"':
case '\'':
{
int hilite = 0;
setup();
n = 0;
while (*fp)
{
/*
FP is in a very wierd format by this time.
Many things are already quoted for us. That's
why this is a little funny.
FIXME: you should be able to emit a \ in a string,
but you can't. You couldn't do it before I added
this either....
*/
if (*fp == '\\')
{
switch (fp[1])
{
/* Implement only a simple toggle, no nesting */
case 'h':
hilite ^= 1;
if (hilite)
standout();
else
standend();
fp ++;
break;
}
fp ++;
continue;
}
if (*fp == ch)
{
fp++;
break;
}
if (++n == 9)
{
setup();
/* setup may turn off our standout mode... */
if (hilite)
standout();
n = 1;
}
if (onscreen)
addch((chtype) *fp);
fp++;
}
}
break;
/*
* Position to memory relative address.
*/
case '#':
addr = faddr + addrscale * count;
break;
/*
* Save current address in temp variable.
*/
case '$':
if ((unsigned)count < TEMP_MAX) temp[count] = addr;
break;
/*
* Set count from temp variable
*/
case '*':
if ((unsigned)count >= TEMP_MAX)
break;
count = temp[count];
have_count = 1;
continue;
/*
* Save value at current address in a temp variables
*/
case '&':
if ((unsigned)count >= TEMP_MAX)
break;
if (!getmem(width))
*(ulong *) mem = 0;
switch (width)
{
case 1: temp[count] = *(kme_uint8_t *) mem; break;
case 2: temp[count] = *(kme_uint16_t *) mem; break;
case 4: temp[count] = *(kme_uint32_t *) mem; break;
}
break;
/*
* Set width parameter (Used by &, ^)
*/
case '=':
width = count;
break;
/*
* Roundup a value
*/
case '^':
if ((unsigned)count >= TEMP_MAX)
break;
temp[count] = temp[count] + width - 1;
temp[count] /= width;
temp[count] *= width;
break;
/*
* Set readahead max for remainder of this entry.
*/
case ':':
rw_max = ((unsigned)(count - 1) < 128) ? count : 128;
break;
/*
* Skip memory forward.
*/
case '+':
if (have_count == 0) count = 1;
addr += addrscale * count;
break;
/*
* Skip memory reverse.
*/
case '-':
if (have_count == 0) count = 1;
addr -= addrscale * count;
break;
/*
* Blank field.
*/
case '.':
do {
setup();
} while (--count > 0);
break;
/*
* Newline.
*/
case 'n':
orow += count ? count : 1;
ocol = 0;
break;
/*
* String.
*/
case 's':
do {
setup();
n = count <= 8 ? count : 8;
if (!getmem(n))
{
if (onscreen) printw("????");
}
else if (onscreen)
{
for (i = 0;i < n;i++)
{
c = ((uchar *)mem)[i];
if (c == 0) c = ' ';
else if (c < ' ' || c >= 0x7f) c = '.';
addch((chtype) c);
}
}
count -= n;
addr += n;
} while (count > 0);
break;
/*
* Process numeric data fields.
*/
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'i':
case 'l':
case 'q':
case 't':
case 'u':
case 'x':
case 'z':
if (have_count && count == 0)
break;
for (;;)
{
setup();
a = addr;
switch (ch)
{
/* ASCII */
case 'a':
if (onscreen)
{
if (!getmem(sizeof(kme_uint8_t)))
printw("??");
else
{
printchar((int)*(kme_uint8_t *)mem & 0x7f);
}
}
addr += sizeof(kme_uint8_t);
break;
/* Hex byte */
case 'b':
if (onscreen)
{
if (!getmem(sizeof(kme_uint8_t)))
printw("??");
else
printw("%02x", *(kme_uint8_t *)mem);
}
addr += sizeof(kme_uint8_t);
break;
/* Character */
case 'c':
if (onscreen)
{
if (!getmem(sizeof(kme_uint8_t)))
printw("??");
else
{
printchar((int)*(kme_uint8_t *)mem);
}
}
addr += sizeof(kme_uint8_t);
break;
/* Signed decimal word */
case 'd':
if (onscreen)
{
if (!getmem(sizeof(kme_uint16_t)))
printw("????");
else
printw("%d", swaps(*(kme_uint16_t *)mem));
}
addr += sizeof(kme_uint16_t);
break;
/* Decimal longword */
case 'e':
if (onscreen)
{
kme_uint32_t mval;
if (!getmem(sizeof(kme_uint32_t)))
printw("????????");
else
{
mval = swapl(*(kme_uint32_t *)mem);
printw("%ld", mval % 10000000);
if (mval / 10000000) printw("+");
}
}
addr += sizeof(kme_uint32_t);
break;
/* Single precision floating point */
case 'f':
if (onscreen)
{
float fval;
if (!getmem(sizeof(float)))
{
printw("????????");
}
else
{
*(ulong*)&fval = swapl(*(ulong *)&mem);
printw("%.3f", fval);
}
}
addr += sizeof(float);
break;
#if CC_HAS_LONG_LONG
/* Decimal quadword */
case 'g':
if (onscreen)
{
kme_uint64_t mval;
char buf[32];
if (!getmem(sizeof(kme_uint64_t)))
sprintf(buf, "????????????????");
else
{
mval = swapq(*(kme_uint64_t *)mem);
sprintf(buf, "%lld", mval % 1000000000000000);
if (mval / 1000000000000000) strcat(buf, "+");
}
printw("%.8s", buf);
setup();
if (strlen(buf) > 8)
printw("%.8s", buf+8);
}
else
setup();
addr += sizeof(kme_uint64_t);
break;
#endif /* CC_HAS_LONG_LONG */
/* FEP input buffer */
case 'i':
if (onscreen)
{
if (!getmem(sizeof(kme_uint16_t)))
{
printw("?? ??");
}
else
{
printw("%02x ", *((kme_uint8_t *)mem + 1));
printchar((int)*(kme_uint8_t *)mem & 0x7f);
}
}
addr += sizeof(kme_uint16_t);
break;
/* HEX longword */
case 'l':
if (onscreen)
{
kme_uint32_t mval;
if (!getmem(sizeof(kme_uint32_t)))
printw("????????");
else
{
mval = swapl(*(kme_uint32_t *)mem);
printw("%04lx", mval);
}
}
addr += sizeof(kme_uint32_t);
break;
#if CC_HAS_LONG_LONG
/* Hex quadword */
case 'q':
if (onscreen)
{
kme_uint64_t mval;
char buf[32];
if (!getmem(sizeof(kme_uint64_t)))
sprintf(buf, "????????????????");
else
{
mval = swapq(*(kme_uint64_t *)mem);
sprintf(buf, "%016llx", mval);
}
printw("%.8s", buf);
setup();
printw("%.8s", buf+8);
}
else
setup();
addr += sizeof(kme_uint64_t);
break;
#endif
/* Unsigned decimal byte */
case 't':
if (onscreen)
{
if (!getmem(sizeof(kme_uint8_t)))
printw("????");
else
printw("%d", *(kme_uint8_t *)mem);
}
addr += sizeof(kme_uint8_t);
break;
/* Hex word */
case 'x':
if (onscreen)
{
if (!getmem(sizeof(kme_uint16_t)))
printw("????");
else
printw("%04x", swaps(*(kme_uint16_t *)mem));
}
addr += sizeof(kme_uint16_t);
break;
/* Unsigned decimal word */
case 'u':
if (onscreen)
{
if (!getmem(sizeof(short)))
printw("????");
else
{
printw("%d", swaps(*(kme_uint16_t *)mem));
}
}
addr += sizeof(short);
break;
/* Decimal longword */
case 'z':
if (onscreen)
{
if (!getmem(sizeof(kme_uint32_t)))
printw("????????");
else
printw("%ld", swapl(*(kme_uint32_t *)mem));
}
addr += sizeof(kme_uint32_t);
break;
}
if (onscreen)
{
register d_field *dp;
dp = &DFIELD(orow, ocol);
dp->d_type = ch;
dp->d_addr = a;
}
if (--count <= 0) break;
}
break;
/*
* Accept '!' to mean what follows is a symbolic
* format name. Note: These do not recurse.
*/
case '!':
{
char fmt_label[200];
char *flp = fmt_label;
int x;
/* Build null terminated fmt_label */
for (;isalnum(*fp) || (*fp == '_'); *flp++=*fp++);
*flp = 0;
/*
* If this is a legal format, display it,
* respect repeat counts.
*/
if ((x = find_format(fmt_label)) >= 0)
{
do {
if (dfmt[x].format)
display(dfmt[x].format);
else
{
char *fmt;
fmt = (*dfmt[x].dlfunc)(&addr, dfmt[x].dlarg);
if (fmt)
display(fmt);
}
} while (--count > 0);
}
else if (onscreen)
{
setup();
printw("??Count:%d Format Label \"%s\" unknown", count, fmt_label);
}
}
break;
/*
* Handle macro defines & undefined characters.
*
* If we hit a recursion depth of 10, assume
* we are in a recursive loop. Show an error
* backtrace and exit.
*/
default:
if (isupper(ch) && format[ch - 'A'])
{
depth++;
if (depth < 10)
{
do {
display(format[ch - 'A']);
} while (--count > 0);
}
if (depth >= 10)
{
if (onscreen)
{
setup();
printw("**** %c", ch);
}
endparen = "";
return;
}
depth--;
}
else if (onscreen)
{
setup();
printw("?%d%c", count, ch);
}
}
count = 0;
have_count = 0;
}
swapflag = orig_swapflag;
width = orig_width;
endparen = "";
}
/************************************************************************
* inbase - Input base address.
************************************************************************/
int
inbase(char* buf)
{
return getbase(buf, &base, 16) != 0;
}
/************************************************************************
* getvalue - Get numeric or symbol value when parsing expressions.
************************************************************************/
ulong
getname()
{
register SYM *sp;
register char *d;
register char *s;
char name[100];
ulong value;
s = str;
if (*s < '0' || *s > '9')
{
d = name;
while(d < name + sizeof(name) - 1 &&
(isalnum(*s) || *s == '_'))
*d++ = *s++;
*d = 0;
if (d != name)
{
sp = *hashname(name);
#if HAVE_GETKSYM
/*
On UnixWare, give getksym a shot at it before nlist.
This allows symbol detection in dynamically loaded mods.
FIXME: If you suspect this code could benefit from greater
commonality with the NLIST case (or even better, a better
abstraction of the symbol table groper) you would be right.
*/
if (!sp)
{
/*
Must not be in the hash table, let's
look it up, then add it .
*/
register SYM **spp = hashname(name);
unsigned long info;
unsigned long value = 0;
int n;
if (debug)
printw("Looking up `%s' ", name);
if (getksym(name, &value, &info))
goto try_nlist;
/*
This mess is blatantly plagarized from elsewhere in
the code. Sure wish I understood it....
*/
n = (unsigned)&((SYM*)0)->s_name[strlen(name)+1];
if (debug)
printw("n is %d\n", n);
sp = (SYM*)malloc(n);
if (sp == 0)
{
fprintf(stderr, "Out of memory");
exit(1);
}
sp->s_next = *spp;
*spp = sp;
sp->s_value = value & addr_mask;
strcpy(sp->s_name, name);
if (debug)
{
fprintf(stderr, "Added Symbol %s = %lx\n",
sp->s_name, sp->s_value);
}
/*
Tell the parser if we found it, by advancing str
and returning the value.
*/
str = s;
return(sp->s_value);
}
try_nlist:
#endif
#if HAVE_NLIST
if (!sp)
{
/*
Must not be in the hash table, let's
look it up, then add it .
*/
register SYM **spp = hashname(name);
int n;
struct nlist nlist_tbl[2];
if (debug)
printw("Looking up %s\n", name);
/*
Nlist can (efficiently) look up multiple symbols
in one shot. We don't take advantage of this, but
we do attempt to look it up only once....
So, nlist_tbl[0] becomes what we're interested in,
next comes the null terminator....
*/
nlist_tbl[0].n_name = name;
nlist_tbl[1].n_name = NULL;
nlist(symname, nlist_tbl);
if (!nlist_tbl[0].n_value)
{
/* Even though it's not a symbol, it could still
be a number - probably hex . */
goto might_be_a_num;
}
/*
This mess is blatantly plagarized from elsewhere in
the code. Sure wish I understood it....
*/
n = (unsigned)&((SYM*)0)->s_name[strlen(name)+1];
sp = (SYM*)malloc(n);
if (sp == 0)
{
fprintf(stderr, "Out of memory");
exit(1);
}
sp->s_next = *spp;
*spp = sp;
sp->s_value = nlist_tbl[0].n_value & addr_mask;
strcpy(sp->s_name, name);
if (debug)
{
fprintf(stderr, "Added Symbol %s = %lx\n",
sp->s_name, sp->s_value);
}
/*
Tell the parser if we found it, by advancing str
and returning the value.
*/
str = s;
return(sp->s_value);
}
might_be_a_num:
#endif
while (sp)
{
if (strcmp(name, sp->s_name) == 0)
{
str = s;
return(sp->s_value);
}
sp = sp->s_next;
}
}
}
s = getbase(str, &value, ebase);
if (s == 0 || isalpha(*s) || *s == '_') longjmp(efail, 1);
str = s;
return(value);
}
/************************************************************************
* expr - Parse expression by recursive descent.
************************************************************************/
long
expr(int pri)
{
long v, v1, v2;
/*
* Handle UNARY prefix operations.
*/
while (*++str == ' ');
switch (*str)
{
case '-':
v = -expr(10);
break;
case '+':
v = expr(10);
break;
case '~':
v = ~expr(10);
break;
case '*':
addr = expr(10) & addr_mask;
if (!getmem(sizeof(kme_uint32_t))) goto fail;
v = swapl(*(kme_uint32_t *)mem);
break;
case '@':
addr = expr(10) & addr_mask;
if (!getmem(sizeof(kme_uint16_t))) goto fail;
v = swaps(*(kme_uint16_t *)mem);
break;
case '#':
addr = expr(10) & addr_mask;
if (!getmem(sizeof(kme_uint8_t))) goto fail;
v = *(kme_uint8_t *)mem;
break;
case '$':
v = expr(10);
if ((unsigned)v >= TEMP_MAX) goto fail;
v = temp[v];
break;
case '(':
inparen++;
v = expr(0);
inparen--;
if (*str != ')') goto fail;
str++;
break;
/*
* Unary Override endianness on long.
*/
case '%':
v = always_swapl(expr(10));
break;
/*
* Unary Override endianness on short.
*/
case '^':
v = always_swaps(expr(10));
break;
/*
* Unary extended operators
*/
case '<':
if (0)
{}
else if (strncmp(str, "<be32>", 6) == 0)
{
str += 5;
v = bigendian() ? expr(10) : always_swapl(expr(10));
}
else if (strncmp(str, "<le32>", 6) == 0)
{
str += 5;
v = !bigendian() ? expr(10) : always_swapl(expr(10));
}
else if (strncmp(str, "<be16>", 6) == 0)
{
str += 5;
v = bigendian() ? expr(10) : always_swaps(expr(10));
}
else if (strncmp(str, "<le16>", 6) == 0)
{
str += 5;
v = !bigendian() ? expr(10) : always_swaps(expr(10));
}
else
goto fail;
break;
default:
v = getname();
}
/*
* Handle BINARY operators.
*/
for (;;)
{
switch(*str)
{
case ' ':
str++;
break;
case '?':
if (pri > 1) goto rtn;
v1 = expr(0);
if (*str != ':') goto fail;
v2 = expr(1);
v = v ? v1 : v2;
break;
case '|':
if (pri >= 2) goto rtn;
v |= expr(2);
break;
case '^':
if (pri >= 3) goto rtn;
v ^= expr(3);
break;
case '&':
if (pri >= 4) goto rtn;
v &= expr(4);
break;
case '!':
if (str[1] == '=')
{
str++;
if (pri >= 5) goto rtn;
v = v != expr(5);
}
else
goto fail;
break;
case '=':
if (str[1] == '=')
{
str++;
if (pri >= 5) goto rtn;
v = v == expr(5);
}
else
goto fail;
break;
case '>':
if (str[1] == '>')
{
str++;
if (pri >= 7) goto rtn;
v = v >= expr(7);
}
else if (str[1] == '=')
{
str++;
if (pri >= 6) goto rtn;
v = v >= expr(6);
}
else
{
if (pri >= 6) goto rtn;
v = v > expr(6);
}
break;
case '<':
if (str[1] == '<')
{
str++;
if (pri >= 7) goto rtn;
v = v << expr(7);
}
else if (str[1] == '=')
{
str++;
if (pri >= 6) goto rtn;
v = v <= expr(6);
}
else
{
if (pri >= 6) goto rtn;
v = v < expr(6);
}
break;
case '+':
if (pri >= 8) goto rtn;
v += expr(8);
break;
case '-':
if (pri >= 8) goto rtn;
v -= expr(8);
break;
case '*':
if (pri >= 9) goto rtn;
v *= expr(9);
break;
case '/':
if (pri >= 9 || !inparen) goto rtn;
v /= expr(9);
break;
case '%':
if (pri >= 9) goto rtn;
v %= expr(9);
break;
case '.':
v += expr(10);
break;
case '[':
inparen++;
v += expr(0);
inparen--;
if (*str != ']') goto fail;
str++;
addr = v & addr_mask;
if (!getmem(sizeof(kme_uint32_t))) goto fail;
v = swapl(*(kme_uint32_t *)mem);
break;
case '{':
inparen++;
v += expr(0);
inparen--;
if (*str != '}') goto fail;
str++;
addr = v & addr_mask;
if (!getmem(sizeof(kme_uint16_t))) goto fail;
v = swaps(*(kme_uint16_t *)mem);
break;
case ':':
case ')':
case ']':
case '}':
goto rtn;
case 0:
goto rtn;
default:
goto fail;
}
}
fail:
longjmp(efail, 1);
rtn:
return(v);
}
/************************************************************************
* inaddr - Read address string from keyboard.
************************************************************************/
int
inaddr(char* buf)
{
register a_field *ap;
ap = &afield[irow];
ap->a_addr = 0;
/*
* An empty field deletes the entry.
*/
if (*buf == 0)
{
strfree(ap->a_disp);
ap->a_disp = 0;
return(1);
}
strfree(ap->a_disp);
ap->a_disp = stralloc(buf);
if (debug)
fprintf(stderr, "Input %d, %s\n",
irow+1, ap->a_disp ? ap->a_disp : "null");
return(1);
}
/************************************************************************
* inparam - Input command parameter.
************************************************************************/
int
inparam(char* buf)
{
int cmd;
/*
* Handle define parameter.
*/
if (isupper(*buf))
{
cmd = *buf++ - 'A';
if (*buf == '=') buf++;
strfree(format[cmd]);
format[cmd] = stralloc(buf);
keyformat[cmd] = 1;
if (debug) fprintf(stderr,
"Defined %c = %s\n", cmd + 'A', buf);
return(1);
}
/*
* Handle line=display parameter.
*/
irow = 0;
while (isdigit(*buf))
{
irow = 10 * irow + *buf++ - '0';
}
if (*buf++ != '=') return(0);
if (irow == 0 && irow < NADR)
{
irow = ++initial_display_row;
afield[irow].a_grow = 1;
}
if (--irow < 0 || irow >= NADR) return(0);
return(inaddr(buf));
}
/************************************************************************
* indata - Input data.
************************************************************************/
int
indata(char* buf)
{
register ulong v;
register int size;
register d_field *dp;
long a;
/*
* Setup call to expression input routine.
*/
if (setjmp(efail))
return(0);
str = buf - 1;
inparen = 1;
dp = &DFIELD(irow, icol);
switch (dp->d_type)
{
case 'd' :
case 'e' :
case 't' :
case 'u' :
case 'z' :
ebase = 10;
break;
case 'f':
ebase = -4;
break;
default:
ebase = 16;
break;
}
v = expr(0);
if (*str != 0) return(0);
switch (dp->d_type)
{
case 'a':
case 'b':
case 'c':
case 't':
size = sizeof(kme_uint8_t);
*(kme_uint8_t *)mem = v;
break;
case 'd':
case 'i':
case 'x':
case 's':
size = sizeof(kme_uint16_t);
*(kme_uint16_t *)mem = swaps((kme_uint16_t)v);
break;
case 'e':
case 'l':
case 'z':
size = sizeof(kme_uint32_t);
*(kme_uint32_t *)mem = swapl(v);
break;
case 'f':
size = sizeof(float);
*(ulong *)mem = swapl(v);
break;
default:
return(0);
}
a = dp->d_addr + base;
#if HAVE_STROPTS_H
if (fepdev && strdev)
{
static rw_t rw;
register a_field *ap;
struct strioctl stio;
rw.rw_req = RW_WRITE;
rw.rw_size = size;
for (ap = &afield[irow];ap->a_disp == 0;--ap) {}
if (isalnum(ap->a_disp[0]) &&
isalnum(ap->a_disp[1]) &&
ap->a_disp[2] == ':')
{
rw.rw_board = getdigit(ap->a_disp[0]);
rw.rw_module = getdigit(ap->a_disp[1]);
rw.rw_addr = a;
}
else if (isalnum(ap->a_disp[0]) && ap->a_disp[1] == ':')
{
rw.rw_board = getdigit(ap->a_disp[0]);
rw.rw_module = 0;
rw.rw_addr = a;
}
else
{
rw.rw_board = def_board;
rw.rw_module = def_module;
rw.rw_addr = a;
}
memcpy((char *)rw.rw_data, (char *)mem, size);
stio.ic_cmd = DIGI_KME;
stio.ic_timout = 0;
stio.ic_len = sizeof(rw);
stio.ic_dp = (char *) &rw;
if (ioctl(fepfd, I_STR, (char *)&stio) == -1 ||
rw.rw_size != size)
return(0);
return(1);
}
#endif /* HAVE_STROPTS_H */
/*
* Write using DigiBoard special device driver hooks.
*/
if (fepdev)
{
static rw_t rw;
register a_field *ap;
rw.rw_req = RW_WRITE;
rw.rw_size = size;
for (ap = &afield[irow];ap->a_disp == 0;--ap) {}
if (isalnum(ap->a_disp[0]) &&
isalnum(ap->a_disp[1]) &&
ap->a_disp[2] == ':')
{
rw.rw_board = getdigit(ap->a_disp[0]);
rw.rw_module = getdigit(ap->a_disp[1]);
rw.rw_addr = a;
}
else if
(isalnum(ap->a_disp[0]) &&
ap->a_disp[1] == ':')
{
rw.rw_board = getdigit(ap->a_disp[0]);
rw.rw_module = 0;
rw.rw_addr = a;
}
else
{
rw.rw_board = def_board;
rw.rw_module = def_module;
rw.rw_addr = a;
}
memcpy((char *)rw.rw_data, (char *)mem, size);
if (ioctl(fepfd, DIGI_KME, (char *)&rw) == -1 ||
rw.rw_size != size)
return(0);
return(1);
}
#if HAVE_SOCKET
if (nsoc)
{
int fd;
int rtry;
int wtry;
rw_t w;
struct timeval tv;
static fd_set fdn;
static fd_set fdr;
static rw_t r;
register a_field *ap;
if (ov_board >= nsoc) return(0);
/*
* Build the request packet.
*/
w.rw_req = RW_WRITE;
w.rw_size = size;
for (ap = &afield[irow];ap->a_disp == 0;--ap) {}
if (isalnum(ap->a_disp[0]) &&
isalnum(ap->a_disp[1]) &&
ap->a_disp[2] == ':')
{
w.rw_board = getdigit(ap->a_disp[0]);
w.rw_module = getdigit(ap->a_disp[1]);
w.rw_addr = a;
}
else if
(isalnum(ap->a_disp[0]) &&
ap->a_disp[1] == ':')
{
w.rw_board = getdigit(ap->a_disp[0]);
w.rw_module = 0;
w.rw_addr = a;
}
else
{
w.rw_board = def_board;
w.rw_module = def_module;
w.rw_addr = a;
}
w.rw_size = swaps(w.rw_size);
w.rw_addr = swapl(w.rw_addr);
memcpy((char *)w.rw_data, (char *)mem, size);
/*
* Repeatedly request the data with UDP until
* a response is heard from the remote.
*/
fd = socfd[w.rw_board];
for (wtry = 0;wtry < 1;wtry++)
{
write(fd, (char *)&w, RW_PAKLEN);
/*
* Wait for the response packet.
*/
for (rtry = 0;rtry < 3;rtry++)
{
FD_ZERO(&fdr);
FD_SET(fd, &fdr);
tv.tv_sec = 2;
tv.tv_usec = 0;
if (select(FD_SETSIZE, &fdr, &fdn, &fdn, &tv) < 1)
break;
/*
* On response from the correct file descriptor,
* with the response we expect, accept the response.
*
* Note that it is possible to get an irrelevant
* response if on a previous request, the network
* delays exceeded our simplistic 2 second timeout.
*/
if (FD_ISSET(fd, &fdr) &&
read(fd, (char *)&r, RW_PAKLEN) == RW_PAKLEN &&
r.rw_req == w.rw_req &&
r.rw_board == w.rw_board &&
r.rw_module == w.rw_module &&
r.rw_addr == w.rw_addr &&
r.rw_size == w.rw_size)
{
return(1);
}
}
}
return(0);
}
#endif
#if HAVE_MMAP
if (memlen)
{
uchar *addr = memva + a;
switch (size)
{
case 1:
*(char *) addr = *(char *) mem;
break;
case 2:
if (ALIGNED(addr,2) && ALIGNED(mem,2))
*(short *) addr = *(short *) mem;
else
memcpy(addr, (char *) mem, (unsigned) size);
break;
case 4:
if (ALIGNED(addr,4) && ALIGNED(mem,4))
*(long *) addr = *(long *) mem;
else
memcpy(addr, (char *) mem, (unsigned) size);
break;
default:
memcpy(addr, (char *) mem, (unsigned) size);
break;
}
return (1);
}
#endif
#if HAVE_PTRACE
if (pid)
{
int i;
int nw;
int buf[2];
if (!stopchild())
return 0;
/* Read one or two words from the target */
nw = ((a & 3) + size + 3) / 4;
for (i = 0; i < nw; i++)
buf[i] = ptrace(PTRACE_PEEKDATA, pid, (a & ~3) + 4 * i, 0);
/* Copy data into buffer (must be same endian) */
memcpy((char*) buf + (a & 3), (char*) mem, size);
/* Write the data */
for (i = 0; i < nw; i++)
ptrace(PTRACE_POKEDATA, pid, (a & ~3) + 4 * i, buf[i]);
return 1;
}
#endif
/*
* Write to memory device.
*/
if (lseek(memfd, a, SEEK_SET) != a ||
write(memfd, (char *)mem, (unsigned) size) != size)
{
return(0);
}
if (debug)
fprintf(stderr,
"Wrote %lx to %lx (%d bytes)\n",
v, a, size);
return(1);
}
/************************************************************************
* inshell - Input shell.
************************************************************************/
int
inshell(char* buf)
{
register int i;
register char *sh;
register int pid;
int status;
void (*sig[3])();
if (debug) fprintf(stderr, "executing: %s\n", buf);
move(LINES-1, 0);
refresh();
#if OLDCURSE
#if HAVE_TERMIOS_H
tcsetattr(0, TCSANOW, &otio);
#else
ioctl(0, TCSETA, &otio);
#endif
#else
reset_shell_mode();
#endif
putchar('\n');
fflush(stdout);
/*
* Fork off a child process to become the shell.
* If we have problems getting a process, keep
* trying.
*/
sigint = 0;
while ((pid = fork()) == -1)
{
if (sigint)
{
fprintf(stderr, "Couldn't fork!");
sleep(2);
goto done;
}
sleep(1);
}
/*
* With the child process, execute a shell.
*/
if (pid == 0)
{
for (i = 3;i < 10;i++) close(i);
sh = getenv("SHELL");
if (sh == 0) sh = "/bin/sh";
execl(sh, "sh", "-c", buf, (char *)0);
perror(sh);
exit(0xff);
}
/*
* Execute the command.
*/
sig[0] = signal(SIGINT, SIG_IGN);
sig[1] = signal(SIGQUIT, SIG_IGN);
sig[2] = signal(SIGTERM, SIG_IGN);
while (wait(&status) != pid);
signal(SIGINT, sig[0]);
signal(SIGQUIT, sig[1]);
signal(SIGTERM, sig[2]);
/*
* Restore tty modes.
*/
done:
fputc('\r', stdout);
fflush(stdout);
#if OLDCURSE
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
#if HAVE_TERMIOS_H
tcsetattr(0, TCSADRAIN, &tio);
#else
ioctl(0, TCSETAW, &tio);
#endif
#else
reset_prog_mode();
#endif
clrtoeol();
standout();
addstr("[Hit return to continue] ");
standend();
refresh();
readch();
clearok(stdscr, 1);
return(1);
}
/************************************************************************
* inprint - Input print file name, and save screen
************************************************************************/
static char last_print[80] = "| lpr"; /* A good default */
int
inprint(char* buf)
{
register FILE *file;
int oldx, oldy;
int x, y, cols;
int pipe = 0;
int append = 0;
char *obuf = buf;
/*
* Parse filename for append and pipe options
*/
if (buf[0] == '|')
{
pipe = 1;
++buf;
}
else if (buf[0] == '>')
{
++buf;
if (buf[0] == '>')
{
++buf;
append = 1;
}
}
while (*buf == ' ' || *buf == '\t') ++buf;
if (*buf == 0) return(0);
/*
* Open output file or pipe.
*/
if (pipe)
file = popen(buf, "w");
else
file = fopen(buf, append ? "a" : "w");
if (!file) return (0);
strncpy(last_print, obuf, sizeof(last_print) -1);
getyx(stdscr, oldy, oldx);
/*
* Print the screen
*/
for (y = 0; y < LINES-1; ++y)
{
for (cols = COLS-1; cols; --cols)
if ((mvinch(y, cols) & A_CHARTEXT) != ' ')
break;
++cols;
for (x = 0; x < cols; ++x)
fputc(mvinch(y, x) & A_CHARTEXT, file);
fputc('\n', file);
}
move(oldy, oldx);
if (pipe)
return(pclose(file) == 0);
else
return(fclose(file) == 0);
}
/************************************************************************
* insave - Input save file name, and save user-entered
* stuff into a command file.
************************************************************************/
static char last_saved[80]; /* So we can edit later */
int insave(char* buf)
{
register FILE *file;
register a_field *ap;
register int ch;
register int i;
register char *s;
/*
* Open output file.
*/
i = open(buf, O_WRONLY|O_CREAT|O_TRUNC, 0777);
if (i == -1)
return(0);
file = fdopen(i, "w");
if (file == 0)
return(0);
strncpy((char *)last_saved, buf, sizeof(last_saved) -1);
/*
* Output command name and options.
*/
fprintf(file, "exec %s", progname);
if (!addrflag)
fprintf(file, " -a");
if (base)
fprintf(file, " -b %lx", base);
if (coredev)
fprintf(file, " -c %s", coredev);
if (fepdev)
fprintf(file, " -f %s", fepdev);
if (helpflag)
fprintf(file, " -h");
if (idline)
fprintf(file, " -i");
if (def_board || def_module)
{
fprintf(file, " -k %c", hexnum[def_board]);
if (def_module) fprintf(file, "%c", hexnum[def_module]);
}
#if HAVE_SOCKET
if (hostname)
{
fprintf(file, " -m %s", hostname);
}
#endif
#if HAVE_MMAP
if (memlen)
fprintf(file, " -M 0x%lx:0x%lx", mempa, memlen);
#endif
if (symparam)
fprintf(file, " -n %s", symparam);
if (frow)
fprintf(file," -p %d", frow + 1);
#if HAVE_PTRACE
if (pid != 0)
fprintf(file, " -P %d", pid);
#endif
if (writeflag == 0)
fprintf(file, " -r");
if (swapflag)
fprintf(file, " -s");
if (addr_mask != -1)
fprintf(file, " -S 0x%lx", addr_mask);
if (!index_mode)
fprintf(file, " -t");
if (uptime != 2)
fprintf(file, " -u %d", uptime);
#if HAVE_SOCKET
if (udp_port != 0)
fprintf(file, " -U %d", udp_port);
#endif
fprintf(file, " $*");
/*
* Output keyboard entered defines.
*/
for (i = 0;i < 26;i++)
{
if (keyformat[i] && (s = format[i]))
{
fprintf(file, " %c=", i + 'A');
while ((ch = *s++))
{
if (!isalnum(ch)) fputc('\\', file);
fputc(ch, file);
}
}
}
/*
* Output field defines.
*/
ap = &afield[0];
for (i = 0;i < NADR;i++)
{
if (ap->a_disp)
{
fprintf(file, " %d=", i+1);
for (s = ap->a_disp;(ch = *s++);)
{
if (!isalnum(ch)) fputc('\\', file);
fputc(ch, file);
}
}
ap++;
}
fputc('\n', file);
return(fclose(file) == 0);
}
/************************************************************************
* inuptime - Input update time.
************************************************************************/
int
inuptime(char* buf)
{
int u;
if (sscanf(buf, "%d", &u) != 1)
return(0);
uptime = u;
return(1);
}
/************************************************************************
* insearch - Input search string.
*
* NOTE: full semantics of / not yet implemented. This
* version doesn't search format text, nor does it do REs.
************************************************************************/
static char last_search[80];
int search_dir;
int
insearch(char* buf)
{
int i, r;
a_field *ap;
char *p;
strncpy((char *)last_search, buf, sizeof(last_search) - 1);
r = irow + search_dir;
for (i = 0; i < NADR; ++i, r += search_dir)
{
if (r == NADR)
r = 0;
if (r < 0)
r = NADR - 1;
ap = &afield[r];
if (!ap->a_disp)
continue;
/*
* Search in address expression and top level format
*/
if (strstr(ap->a_disp, buf))
break;
/*
* Extremely simplistic seach thru definition of first !format
*
* This is so incomplete and badly handled I'm embarrassed.
* Yet, it solves the immediate problem I have.
*/
p = strchr(ap->a_disp, '!');
if (p)
{
char fbuf[80];
char *fp = fbuf;
char *sp = fbuf;
int x;
int state;
int offset;
++p;
while (isalnum(*p) || (*p == '_'))
*fp++ = *p++;
*fp = 0;
x = find_format(fbuf);
if (x != -1 && dfmt[x].format && (sp = strstr(dfmt[x].format, buf)))
{
state = 0;
offset = 1;
for (fp = dfmt[x].format; fp < sp; ++fp)
{
switch (state)
{
case 0:
if (*fp == '"')
state = 1;
else if (*fp == 'n')
++offset;
break;
case 1:
if (*fp == '"')
state = 0;
break;
}
}
r += offset;
if (r >= NADR)
r = NADR - 1;
break;
}
}
}
if (i != NADR)
{
irow = trow = r;
lrow = -1;
}
else
beep();
return (1);
}
/************************************************************************
* readch - Read an input character in delay mode.
************************************************************************/
int readch(void)
{
char ch;
#if OLDCURSE
read(0, &ch, 1);
return(ch);
#else
for (;;)
{
ch = getch();
if (ch != ERR) return(ch);
}
#endif
}
/************************************************************************
* collect - Collect characters into a field.
************************************************************************/
void
collect(int row, int col, char* buf, int width, int (*proc)())
{
register int i;
register int ch;
register int startpos;
register int len;
for (len = 0;buf && *buf && len < LINELEN;len++)
line[len] = *buf++;
line[len] = 0;
#if 0
/*
* If no edit string is given, go into insert mode with
* a blank line. If an edit string is given, position
* the cursor to the end of line, and go into edit mode
* immediately.
*/
if (buf)
edit_mode();
else
insert_mode();
#else
/*
* It seems to be easier for the unwashed masses to handle
* if you always start in edit mode.
*/
insert_mode();
#endif
linepos = len;
startpos = 0;
lineproc = proc;
/*
* Loop to edit keystroke input.
*/
#if OLDCURSE
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
#if HAVE_TERMIOS_H
tcsetattr(0, TCSADRAIN, &tio);
#else
ioctl(0, TCSETAW, &tio);
#endif
#else
nocbreak();
cbreak();
#endif
for (;;)
{
/*
* Display current field, scrolling if necessary so the
* complete input line can be displayed.
*/
move(row, col);
len = strlen(line);
if (startpos <= linepos - width) startpos = linepos - width + 1;
if (startpos > linepos) startpos = linepos;
for (i = startpos;i < startpos + width;i++)
{
if (i < len) addch((chtype) line[i]);
else addch((chtype) mode_ind);
}
/*
* Read next input character.
*/
move(row, col + linepos - startpos);
refresh();
ch = readch();
switch (ch)
{
/*
* Handle screen refresh special.
*/
#if KEY_REFRESH
case KEY_REFRESH:
#endif
case ctrl('l'):
case ctrl('r'):
clearok(stdscr, 1);
break;
/*
* Everything else is a VI command.
*/
default:
update_line(ch);
break;
}
if (linepos == -1) break;
}
}
/************************************************************************
* docmd - Process command keystrokes.
************************************************************************/
void docmd()
{
register int ch;
register a_field *ap;
register a_field *bp;
int r;
int m;
int n;
static int count;
#if OLDCURSE
int nbuf;
int i;
char buf[100];
#endif
/*
* Position the cursor, and flush output.
*/
move(FROW(irow), FCOL(icol));
refresh();
#if OLDCURSE
tio.c_cc[VMIN] = 0;
tio.c_cc[VTIME] = uptime;
#if HAVE_TERMIOS_H
tcsetattr(0, TCSADRAIN, &tio);
#else
ioctl(0, TCSETAW, &tio);
#endif
nbuf = read(0, buf, sizeof(buf));
i = 0;
#else
cbreak();
#endif
/*
* Process the characters received.
*/
trow = frow;
for (n = 0;; n++)
{
#if OLDCURSE
if (i >= nbuf) goto done;
ch = buf[i++];
#else
halfdelay(uptime);
ch = getch();
if (ch == ERR) goto done;
nocbreak();
cbreak();
#endif
if ((count ? '0' : '1') <= ch && ch <= '9')
{
if (count > 999) count = 999;
count = 10 * count + ch - '0';
continue;
}
if (isupper(ch))
ch = tolower(ch);
switch (ch)
{
/*
* Redraw screen.
*/
#if KEY_REFRESH
case KEY_REFRESH:
#endif
case ctrl('l'):
case ctrl('r'):
clearok(stdscr, 1);
lrow = -1;
break;
/*
* Toggle between index formats (byte and array index)
*/
case ctrl('t'):
index_mode ^= 1;
break;
/*
* Next page.
*/
#if KEY_NPAGE
case KEY_NPAGE:
#endif
case ctrl('F'):
if (count == 0) count = 1;
r = trow + (nrow-1) * count;
if (r > NADR - nrow) r = NADR - nrow;
if (r == trow) beep();
irow += r - trow;
trow = r;
break;
/*
* Scroll down.
*/
#if KEY_SF
case KEY_SF:
#endif
case ctrl('D'):
if (count) scount = count;
r = trow + scount;
if (r > NADR - nrow) r = NADR - nrow;
if (r == trow) beep();
irow += r - trow;
trow = r;
break;
/*
* Previous page.
*/
#if KEY_PPAGE
case KEY_PPAGE:
#endif
case ctrl('B'):
if (count == 0) count = 1;
r = trow - (nrow-1) * count;
if (r < 0) r = 0;
irow += r - trow;
if (r == trow) beep();
trow = r;
break;
/*
* Scroll up.
*/
#if KEY_SR
case KEY_SR:
#endif
case ctrl('U'):
if (count) scount = count;
r = trow - scount;
if (r < 0) r = 0;
irow += r - trow;
if (r == trow) beep();
trow = r;
break;
/*
* Next line.
*/
case '\r':
case '\n':
icol = 0;
if (irow >= NADR - 1) goto error;
irow += count ? count : 1;
if (irow >= NADR) irow = NADR - 1;
if (irow >= trow + nrow) trow = irow - nrow + 1;
break;
/*
* Shell escape.
*/
#if KEY_COMMAND
case KEY_COMMAND:
#endif
case '!':
case ':':
move(LINES-1, 0);
clrtoeol();
printw("shell:");
collect(LINES-1, 7, (char *)0, COLS-8, inshell);
lrow = -1;
goto flush;
/*
* Help on/off.
*/
#if KEY_HELP
case KEY_HELP:
#endif
case '?':
if (helpflag ^= 1)
{
nrow--;
if (irow >= trow + nrow) irow--;
}
else
{
nrow++;
if (trow > NADR - nrow) trow--;
lrow = -1;
}
break;
/*
* Backward in memory.
*/
#if KEY_PREVIOUS
case KEY_PREVIOUS:
#endif
case '-':
ap = &afield[irow];
while (ap->a_disp == 0)
{
if (ap == afield) goto error;
ap--;
}
if (ap->a_size == 0) goto error;
if (count == 0) count = 1;
ap->a_addr -= count * ap->a_size;
break;
/*
* Forward in memory.
*/
#if KEY_NEXT
case KEY_NEXT:
#endif
case '+':
ap = &afield[irow];
while (ap->a_disp == 0)
{
if (ap == afield) goto error;
ap--;
}
if (ap->a_size == 0) goto error;
if (count == 0) count = 1;
ap->a_addr += count * ap->a_size;
break;
/*
* Start of line.
*/
case '0':
case '^':
if (icol == 0) goto error;
icol = 0;
break;
/*
* Toggle address display mode.
*/
case 'a':
addrflag = !addrflag;
break;
/*
* Enter base address.
*/
case 'b':
{
char base_asc[11];
sprintf(base_asc, "0x%lx", base);
move(LINES-1, 0);
clrtoeol();
printw("base address:");
collect(LINES-1, 14, base_asc, 8, inbase);
lrow = -1;
goto flush;
}
/*
* Change field.
*/
#if KEY_REPLACE
case KEY_REPLACE:
#endif
case 'c':
if (icol == 0)
{
showhelp(1);
collect(FROW(irow), FCOL(0), (char *)0,
COLS - FCOL(0) - 1, inaddr);
}
else if (DFIELD(irow, icol).d_type)
{
if (!writeflag) goto error;
showhelp(2);
collect(FROW(irow), FCOL(icol), (char *)0, 8, indata);
}
else goto error;
goto flush;
/*
* Delete rows.
*/
#if KEY_DL
case KEY_DL:
#endif
case 'd':
if (count == 0) count = 1;
r = 0;
bp = &bfield[0];
while (r < brow)
{
strfree(bp[0].a_disp);
bp[0].a_addr = 0;
bp[0].a_disp = 0;
bp[0].a_size = 0;
r++;
bp++;
}
r = irow;
ap = &afield[irow];
bp = &bfield[0];
while (r < NADR)
{
if (r < irow+count) bp[0] = ap[0];
if (r < NADR-count)
ap[0] = ap[count];
else
{
ap[0].a_addr = 0;
ap[0].a_disp = 0;
ap[0].a_size = 0;
}
r++;
ap++;
bp++;
}
brow = count;
break;
/*
* Edit field.
*/
case 'e':
ap = &afield[irow];
if (icol == 0)
{
showhelp(1);
collect(FROW(irow), FCOL(0), ap->a_disp ? ap->a_disp : "",
COLS - FCOL(0) - 1, inaddr);
}
else if (DFIELD(irow, icol).d_type)
{
if (!writeflag) goto error;
showhelp(2);
collect(FROW(irow), FCOL(icol), "", 8, indata);
}
else goto error;
goto flush;
/*
* Go to line number.
*/
#if KEY_FIND
case KEY_FIND:
#endif
#if KEY_REFERENCE
case KEY_REFERENCE:
#endif
case 'g':
if (count == 0) irow = NADR - 1;
else if (count > NADR) goto error;
else irow = count - 1;
if (irow < trow) trow = irow;
if (irow >= trow + nrow) trow = irow - nrow + 1;
break;
/*
* Move left.
*/
#if KEY_LEFT
case KEY_LEFT:
#endif
case 'h':
r = irow * (ncol + 1) + icol;
if (r == 0) goto error;
r -= count ? count : 1;
if (r < 0) r = 0;
irow = r / (ncol + 1);
icol = r % (ncol + 1);
if (trow > irow) trow = irow;
break;
/*
* Insert rows.
*/
#if KEY_OPEN
case KEY_OPEN:
#endif
case 'i':
insertscreenline(count);
break;
/*
* Move down.
*/
#if KEY_DOWN
case KEY_DOWN:
#endif
case 'j':
if (irow >= NADR - 1) goto error;
irow += count ? count : 1;
if (irow >= NADR) irow = NADR - 1;
if (irow >= trow + nrow) trow = irow - nrow + 1;
break;
/*
* Move up.
*/
#if KEY_UP
case KEY_UP:
#endif
case 'k':
if (irow == 0) goto error;
irow -= count ? count : 1;
if (irow < 0) irow = 0;
if (irow < trow) trow = irow;
break;
/*
* Move right.
*/
#if KEY_RIGHT
case KEY_RIGHT:
#endif
case ' ':
case 'l':
r = irow * (ncol + 1) + icol;
m = NADR * (ncol + 1) - 1;
if (r == m) goto error;
r += count ? count : 1;
if (r > m) r = m;
irow = r / (ncol + 1);
icol = r % (ncol + 1);
if (irow >= trow + nrow) trow = irow - nrow + 1;
break;
/*
* Define format macro.
*/
#if KEY_CREATE
case KEY_CREATE:
#endif
case 'm':
move(LINES-1, 0);
clrtoeol();
printw("macro:");
r = irow;
collect(LINES-1, 7, (char *)0, COLS-8, inparam);
irow = r;
lrow = -1;
goto flush;
/*
* Get namelist file.
*/
case 'n':
if (!readsym()) beep();
break;
/*
* Put deleted lines back in.
*/
#if KEY_IL
case KEY_IL:
#endif
case 'p':
if (brow == 0) goto error;
r = NADR;
ap = &afield[NADR];
bp = &bfield[NADR-irow];
while (r > irow)
{
--r;
--ap;
--bp;
if (r >= NADR-brow) strfree(ap[0].a_disp);
if (r >= irow+brow) ap[0] = ap[-brow];
else
{
ap[0].a_addr = bp[0].a_addr;
ap[0].a_disp = stralloc(bp[0].a_disp);
ap[0].a_size = bp[0].a_size;
}
}
break;
/*
* Output screen to a file
*/
case 'o':
move(LINES-1, 0);
clrtoeol();
printw("print file:");
collect(LINES-1, 12, last_print, COLS-13, inprint);
lrow = -1;
goto flush;
#if 0 /* letter already used, waaaaaa */
case '?':
search_dir = -1;
goto search;
#endif
case '/':
search_dir = 1;
goto search;
search:
move(LINES-1, 0);
clrtoeol();
printw("search:");
collect(LINES-1, 8, last_search, COLS-8-1, insearch);
lrow = -1;
goto flush;
/*
* Quit.
*/
#if KEY_EXIT
case KEY_EXIT:
#endif
case 'q':
quitreq = 1;
goto flush;
/*
* Re-read defines file.
*/
#if KEY_REDO
case KEY_REDO:
#endif
case 'r':
readdefs();
break;
/*
* Save configuration.
*/
#if KEY_SAVE
case KEY_SAVE:
#endif
case 's':
move(LINES-1, 0);
clrtoeol();
printw("save file:");
/*
If not already saved, default to "insert/append" mode,
otherwise, let them edit current prompt
*/
if (last_saved[0])
collect(LINES-1, 11, last_saved, COLS-12, insave);
else
collect(LINES-1, 11, (char *)0, COLS-12, insave);
lrow = -1;
goto flush;
/*
* Set update time.
*/
case 'u':
{
char update_asc[8];
sprintf(update_asc, "%d", uptime);
move(LINES-1, 0);
clrtoeol();
printw("update interval (1/10ths of sec):");
collect(LINES-1, 34, update_asc, 4, inuptime);
lrow = -1;
goto flush;
}
/*
* Pause update.
*/
case 'w':
move(LINES-1, 0);
clrtoeol();
move(LINES-1, (COLS-32) / 2);
standout();
printw(" Paused - Hit Return to Continue ");
standend();
refresh();
#if OLDCURSE
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
#if HAVE_TERMIOS_H
tcsetattr(0, TCSADRAIN, &tio);
#else
ioctl(0, TCSETAW, &tio);
#endif
#else
halfdelay(250);
#endif
readch();
lrow = -1;
goto done;
/*
* Yank lines into buffer.
*/
#if KEY_COPY
case KEY_COPY:
#endif
case 'y':
if (count == 0) count = 1;
r = 0;
bp = &bfield[0];
while (r < brow)
{
strfree(bp[0].a_disp);
bp[0].a_addr = 0;
bp[0].a_disp = 0;
bp[0].a_size = 0;
r++;
bp++;
}
r = 0;
ap = &afield[irow];
bp = &bfield[0];
while (r < count)
{
bp[0].a_addr = ap[0].a_addr;
bp[0].a_disp = stralloc(ap[0].a_disp);
bp[0].a_size = ap[0].a_size;
r++;
ap++;
bp++;
}
brow = count;
break;
/*
* Zero field.
*/
case 'z':
if (!writeflag) goto error;
if (count == 0) count = 1;
if (icol == 0) icol = 1;
for (;;)
{
if (DFIELD(irow, icol).d_type)
{
if (count <= 0) break;
indata("0");
count--;
}
if (++icol > ncol)
{
if (++irow >= trow + nrow)
{
irow--;
icol--;
goto error;
}
icol = 1;
}
}
break;
/*
* Others are errors.
*/
default:
goto error;
}
count = 0;
}
error:
beep();
flush:
count = 0;
done:
frow = trow;
if (n == 0)
laddr = haddr = 0;
return;
}
/************************************************************************
* check_symread - See if the symbol table file has changed.
* If so, we silently reread it.
* Optimizatino opportunities abound.
************************************************************************/
void
check_symreread()
{
static struct stat symstat;
static time_t last_time;
/* If the file is gone now, we'll let someone else complain.
* It may show back up on the next poll. */
if (stat(symname, &symstat) < 0)
{
return;
}
if (symstat.st_mtime > last_time)
{
readsym();
last_time = symstat.st_mtime;
}
}
/************************************************************************
* mainloop - Update the screen and read keystroke commands.
************************************************************************/
void mainloop()
{
register a_field *ap;
register int arow;
int err;
int c;
if (frow > NADR - nrow) frow = NADR - nrow;
if (frow < 0) frow = 0;
irow = frow;
lrow = -1;
laddr = haddr = 0;
while (!quitreq)
{
/*
* Find the last row before the start of
* the screen which has data to display.
*/
for (orow = frow;; orow--)
{
if (orow < 0)
{
orow = frow;
break;
}
if (afield[orow].a_disp) break;
}
/*
* Output data over the displayable region
* of the screen only.
*/
crow = frow;
while (crow < frow + nrow)
{
erow = frow + nrow;
arow = crow;
ocol = 0;
/*
* If a field begins on this line, process it, possibly
* filling in subsequent lines until the data runs out,
* the screen ends, or we run into another field.
*/
ap = &afield[orow];
if (ap->a_disp)
{
/*
* Recognize board/concentrator prefix at beginning
* of expression.
*/
str = ap->a_disp;
#if HAVE_SOCKET
if (fepdev || nsoc)
{
if (isalnum(str[0]) &&
isalnum(str[1]) &&
str[2] == ':')
{
ov_board = getdigit(str[0]);
ov_module = getdigit(str[1]);
str += 3;
}
else if
(isalnum(str[0]) &&
str[1] == ':')
{
ov_board = getdigit(str[0]);
ov_module = 0;
str += 2;
}
else
{
ov_board = def_board;
ov_module = def_module;
}
}
#endif
/*
* Initialize the expression parser, to figure
* out the address to display.
*/
--str;
inparen = 0;
err = 0;
if (setjmp(efail))
{
err++;
}
else
{
temp[0] = ap->a_size;
ebase = 16;
addr = (expr(0) + ap->a_addr) & addr_mask;
addr *= addrscale;
if (*str != '/') err++;
}
/*
* On an error, show the incorrect display expression
* in the first field of the line.
*/
if (err)
{
if (orow >= frow)
{
initline();
c = *str;
*str = 0;
printw("%s", ap->a_disp);
*str = c;
printw(" <ERROR> ");
printw("%s", str);
}
orow++;
}
/*
* If the addrflag is set, show the display
* expression alone on the first line.
*
* In any case, display the formatted data on
* the next line.
*/
else
{
if (addrflag)
{
if (orow >= frow)
{
initline();
*str = 0;
printw("%s", ap->a_disp);
if (ap->a_addr < 0)
{
if (index_mode)
{
printw("-%lx", -ap->a_addr);
}
else
{
printw("[-%ld]", -(ap->a_addr/ap->a_size));
}
}
if (ap->a_addr > 0)
{
if (index_mode)
printw("+%lx", ap->a_addr);
else
printw("[%ld]", ap->a_addr/ap->a_size);
}
*str = '/';
printw("%s", str);
}
orow++;
}
depth = 0;
faddr = addr;
rw_max = 128;
display(str+1);
ap->a_size = (addr - faddr) / addrscale;
}
move(FROW(irow), FCOL(icol));
refresh();
}
if (crow == arow)
{
clearline();
crow++;
}
orow = crow;
}
startchild();
lrow = frow;
standend();
showhelp(0);
docmd();
check_symreread();
}
}
/************************************************************************
* beeper - Beep at operator.
************************************************************************/
#if OLDCURSE && 0
void
beeper()
{
static char beepc = '\007';
write(1, &beepc, 1);
}
#endif
/************************************************************************
* opensoc - Open socket interface.
************************************************************************/
#if HAVE_SOCKET
void
opensoc(char* str)
{
int fd;
int i;
int ch;
struct servent *sp;
struct hostent *hp;
struct sockaddr_in sin;
char host[100];
while (*str) {
/*
* Extract the next entry in the colon separated
* list of host names.
*/
for (i = 0;*str && (ch = *str++) != ':';)
{
if (i < sizeof(host)-1) host[i++] = ch;
}
host[i] = 0;
if (nsoc >= MAXSOC)
{
fprintf(stderr, "Too many sockets, increase MAXSOC\n");
exit(2);
}
/*
* Open a socket, and bind it to a non-priveleged port.
*/
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
perror("socket");
exit(2);
}
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(0);
if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("connect");
exit(2);
}
/*
* Get "kme" service port number.
*/
if (udp_port != 0)
{
sin.sin_port = htons(udp_port);
}
else
{
memset((char *)&sin, 0, sizeof(sin));
sp = getservbyname("kme", "udp");
if (sp != 0)
sin.sin_port = sp->s_port;
else
sin.sin_port = htons(UDP_PORT);
}
/*
* Get destination address.
*/
sin.sin_addr.s_addr = inet_addr(host);
if (sin.sin_addr.s_addr != -1)
{
sin.sin_family = AF_INET;
}
else
{
hp = gethostbyname(host);
if (hp == 0)
{
fprintf(stderr, "unknown host: %s\n", host);
exit(2);
}
sin.sin_family = hp->h_addrtype;
memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
}
if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("connect");
exit(2);
}
socfd[nsoc] = fd;
nsoc++;
}
}
#endif
/************************************************************************
* version_info - display confiration information.
************************************************************************/
void
version_info(void)
{
char *vinfo[] = {
PACKAGE " version " VERSION,
#if HAVE_MMAP
"MMAP enabled",
#endif
#if HAVE_SOCKET
"sockets enabled",
#endif
#if HAVE_NLIST
"nlist enabled",
#endif
#if HAVE_PTRACE
"ptrace enabled",
#endif
#if HAVE_LIBELF
"libelf present",
#endif
#if HAVE_LIBDL
"libdl present",
#endif
NULL
};
char **v = vinfo;
while (*v)
{
printf("%s\n", *v);
v++;
}
exit(0);
}
/************************************************************************
* main - main program.
************************************************************************/
int
main(int argc, char** argv)
{
char *cp;
char ch;
int c;
/*
* Unpack options.
*/
progname = argv[0];
if ((cp = getenv("KME_DEFS")) != 0) defsname = cp;
if ((cp = getenv("KME_PATH")) != 0) defspath = cp;
if ((cp = getenv("KME_CORE")) != 0) corename = cp;
if ((cp = getenv("KME_SYMS")) != 0) symname = cp;
fmt_cnt = dl_fmt_cnt = 0;
while ((c = getopt(argc, argv,
"aA:b:c:d:D:f:hik:L:m:M:n:p:P:rsS:tu:U:Vx")) != -1)
{
switch (c)
{
case 'a':
addrflag = !addrflag;
break;
case 'A':
addrscale = atoi(optarg);
break;
case 'b':
if (!inbase(optarg)) goto usage;
break;
case 'c':
coredev = corename = optarg;
break;
case 'd':
defsname = optarg;
break;
case 'D':
defspath = optarg;
break;
case 'f':
fepdev = optarg;
break;
case 'h':
helpflag = 1;
break;
case 'i':
#if TERMINFO
idline ^= 1;
#else
fprintf(stderr,
"Note: insert/delete option not compiled in!\n");
#endif
break;
case 'k':
if (isalnum(optarg[0]))
{
def_board = getdigit(optarg[0]);
def_module = isalnum(optarg[1]) ? getdigit(optarg[1]) : 0;
}
else
{
def_board = 0;
def_module = 0;
}
break;
case 'L':
#if HAVE_LIBDL
{
void *handle;
handle = dlopen(optarg, RTLD_NOW);
if (!handle)
{
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
}
#else
fprintf(stderr, "LIBDL not compiled in!\n");
#endif
break;
case 'm':
#if HAVE_SOCKET
hostname = optarg;
break;
#else
fprintf(stderr, "Networking support not compiled in!\n");
goto usage;
#endif
case 'M': /* -M physaddr:len */
#if HAVE_MMAP
mempa = (off_t) strtoul(optarg, (char **) NULL, 0);
cp = strchr(optarg, ':');
if (!cp) goto usage;
memlen = strtoul(cp+1, (char **) NULL, 0);
break;
#else
fprintf(stderr, "MMAP not compiled in!\n");
goto usage;
#endif
case 'n':
symparam = symname = optarg;
break;
case 'p':
frow = atoi(optarg) - 1;
break;
case 'P':
#if HAVE_PTRACE
pid = atoi(optarg) ? atoi(optarg) : getpid();;
#else
fprintf(stderr, "PTRACE not compiled in!\n");
return(2);
#endif
break;
case 'r':
writeflag = 0;
break;
case 's':
swapflag = !swapflag;
break;
case 'S':
if (sscanf(optarg, "%lx%c", &addr_mask, &ch) != 1 ||
addr_mask < 0x100
)
goto usage;
fprintf(stderr, "address mask = %lx\n", addr_mask);
break;
case 't':
index_mode = 0;
break;
case 'u':
uptime = atoi(optarg);
break;
case 'U':
#if HAVE_SOCKET
if (sscanf(optarg, "%d%c", &udp_port, &ch) != 1)
goto usage;
break;
#else
fprintf(stderr, "Networking not compiled in!\n");
goto usage;
#endif
case 'V':
version_info();
break;
case 'x':
debug++;
break;
default:
goto usage;
}
}
/*
* Default symbol file name if none specified.
*/
/*
* Get define file(s).
*/
dl_fmt_cnt = fmt_cnt;
if (!readdefs())
{
sleep(2);
}
/*
* Read in symbols.
*/
if (symname == 0)
{
#if HAVE_DEV_KSYMS
symname = (strcmp(corename, "/dev/kmem") == 0 ?
"/dev/ksyms" : "kme_syms");
#elif HAVE_PROC_KSYMS
symname = (strcmp(corename, "/dev/kmem") == 0 ?
"/proc/ksyms" : "kme_syms");
#else
symname = "kme_syms";
#endif
}
if (!readsym() && strcmp(symname, "kme_syms"))
{
fprintf(stderr, "Problem with: %s\n", symname);
perror(symname);
sleep(2);
}
/*
* Now that we have symbols available, read in
* the screen configuration.
*/
while (optind != argc)
{
if (!inparam(argv[optind++])) goto usage;
}
/*
* Open memory access file.
*/
if (fepdev)
{
fepfd = open(fepdev, O_WRONLY|O_NDELAY);
if (fepfd < 0)
fepfd = open(fepdev, O_RDONLY|O_NDELAY);
if (fepfd < 0)
{
fprintf(stderr, "Cannot open ");
perror(fepdev);
return(2);
}
#if HAVE_STROPTS_H
/* Test to see if this is a STREAM device */
strdev = ! (ioctl(fepfd, I_FLUSH, FLUSHRW) == -1);
#endif
}
#if HAVE_PTRACE
else if (pid != 0)
{
if (ptrace(PTRACE_ATTACH, pid, 0, 0) != 0)
{
perror("Cannot PTRACE_ATTACH process");
return 2;
}
waitpid(pid, (int*) 0, 0);
if (ptrace(PTRACE_CONT, pid, 0, SIGCONT) != 0)
{
perror("Cannot PTRACE_CONT process");
return 2;
}
pidrun = 1;
}
#endif
#if HAVE_SOCKET
else if (hostname)
{
opensoc(hostname);
}
#endif
else
{
if ((memfd = open(corename, writeflag ? O_RDWR : O_RDONLY)) == -1)
{
perror(corename);
return(2);
}
}
#if HAVE_MMAP
if (memlen)
{
memva = (uchar *) mmap((caddr_t) 0, memlen,
PROT_READ | (writeflag ? PROT_WRITE : 0),
MAP_SHARED, memfd, mempa);
if (memva == (void *) -1)
{
perror("can't mmap corefile");
return(2);
}
}
#endif
/*
* Initialize curses.
*/
#if OLDCURSE
#if HAVE_TERMIOS_H
tcgetattr(0, &otio);
#else
ioctl(0, TCGETA, &otio);
#endif
#endif
initscr();
#if OLDCURSE
#if HAVE_TERMIOS_H
tcgetattr(0, &tio);
#else
ioctl(0, TCGETA, &tio);
#endif
tio.c_lflag &= ~(ECHO|ICANON);
tio.c_iflag &= ~(ICRNL|IGNCR|INLCR);
#else
noecho();
typeahead(-1);
keypad(stdscr, 1);
#endif
#if TERMINFO
idlok(stdscr, idline);
#endif
/*
* Figure number of rows/columns on screen.
*/
nrow = LINES;
if (nrow > NADR) nrow = NADR;
nrow -= helpflag;
scount = nrow / 2;
ncol = (COLS - FCOL(1)) / 8;
if (nrow * ncol > NDATA) ncol = NDATA / nrow;
/*
* Run the main loop.
*/
signal(SIGINT, catch);
irow = frow;
mainloop();
/*
* Exit curses.
*/
move(LINES-1, 0);
refresh();
endwin();
putchar('\n');
/*
* Detach from controlled process.
*/
#if HAVE_PTRACE
if (pid != 0)
ptrace(PTRACE_DETACH, pid, 0, 0);
#endif
return(0);
usage:
fprintf(stderr,
"usage: %s [-ahirstVx] [-A addrscale] [-b base] [-c core]\n"
"\t[-d defs] [-D defspath] [-f tty] [-k node] [-L lib]\n"
"[-m hostlist] [-M addr:len] [-n symfile] [-p row]\n"
"[-u uptime] [-Mstartaddr:size] [-S address mask]\n"
"[-P pid] [-S mask] [-u uptime] [-U port] [param ...]\n",
argv[0]);
return(2);
}