Mark Williams MWC v3.1.1

This commit is contained in:
davidly 2024-07-01 06:16:06 -07:00
parent 78276e2457
commit deca789689
73 changed files with 8188 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,4 @@
-xcc:\bin\
-xlc:\lib\
-xtc:\tmp\
-Ic:\include\

View File

@ -0,0 +1,18 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Assertion verifier.
*/
#ifndef ASSERT_H
#define ASSERT_H
#if NDEBUG
#define assert(p)
#else
#define assert(p) if(!(p)){printf("%s: %d: assert(%s) failed.\n",\
__FILE__, __LINE__, "p");exit(1);}
#endif
#endif

View File

@ -0,0 +1,41 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Character type classification routines.
* This is implemented by table lookup.
*/
#ifndef CTYPE_H
#define CTYPE_H
extern char _ctype[];
/* Bits classifications */
#define _U 01 /* Upper case alphabetic */
#define _L 02 /* Lower case alphabetic */
#define _A (_U|_L) /* Alphabetic */
#define _D 010 /* Digit */
#define _S 020 /* White space character */
#define _P 040 /* Punctuation character */
#define _C 0100 /* Control character */
#define _X 0200 /* Printable but nothing else */
#define isalpha(c) (_ctype[(c)+1]&_A)
#define isupper(c) (_ctype[(c)+1]&_U)
#define islower(c) (_ctype[(c)+1]&_L)
#define isdigit(c) (_ctype[(c)+1]&_D)
#define isalnum(c) (_ctype[(c)+1]&(_A|_D))
#define isspace(c) (_ctype[(c)+1]&_S)
#define ispunct(c) (_ctype[(c)+1]&_P)
#define isprint(c) (_ctype[(c)+1]&(_P|_X|_A|_D))
#define iscntrl(c) (_ctype[(c)+1]&_C)
#define isascii(c) (((c)&~0177)==0)
#define _tolower(c) ((c)|('a'-'A'))
#define _toupper(c) ((c)&~('a'-'A'))
#define toascii(c) ((c)&0177)
#endif

View File

@ -0,0 +1,186 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Definitions for accessing MS-DOS and 8086 functions.
*/
#ifndef DOS_H
#define DOS_H
#define L2RECSZ 9 /* log base 2 of record size */
#define RECSIZ (1<<L2RECSZ) /* Record size */
#define TTYSIZ 128 /* # of bytes, CON: line buffer */
#define BAD 0xFF /* Bad status, open, make */
#define SOK 0x00 /* All ok */
#define SNOREC 0x01 /* Reading unwritten data */
#define SOVER 0x02 /* Disc buffer overflows segment */
#define SPART 0x03 /* Partial record read */
/*
* DOS functions.
*/
#define EXIT 0x0000 /* Program terminate */
#define CONIN 0x0100 /* Console input */
#define CONOUT 0x0200 /* Console output */
#define RDRIN 0x0300 /* Get byte from reader */
#define AUXIN 0x0300 /* Alternative name */
#define PUNOUT 0x0400 /* Put byte to punch */
#define AUXOUT 0x0400 /* Alternative name */
#define LSTOUT 0x0500 /* Put byte to printer */
#define CONDIO 0x0600 /* Direct console I/O */
#define DIRCON 0x0600 /* Old synonym */
#define CONRAW 0x0700 /* Console input, no echo, no int */
#define CON_E 0x0800 /* Console input, no echo */
#define PUTSTR 0x0900 /* Put `$' delimited string */
#define BUFCON 0x0A00 /* Buffered console read */
#define CONSTAT 0x0B00 /* Console status */
#define CLRIN 0x0C01 /* Console clear then input */
#define CLRDIO 0x0C06 /* Console clear then direct I/O */
#define CLRRAW 0x0C07 /* Console clear then raw input */
#define CLR_E 0x0C08 /* Console clear then input no echo */
#define CLRBUF 0x0C0A /* Console clear then buffered input*/
#define RESDSK 0x0D00 /* Reset disc system */
#define SELDSK 0x0E00 /* Select disc */
#define OPENF 0x0F00 /* Open file */
#define CLOSEF 0x1000 /* Close file */
#define FFIRST 0x1100 /* Find first */
#define FNEXT 0x1200 /* Find next */
#define DELETEF 0x1300 /* Delete file */
#define READS 0x1400 /* Read sequential */
#define WRITES 0x1500 /* Write sequential */
#define MAKEF 0x1600 /* Make file */
#define CREATEF 0x1600 /* Create file (synonym) */
#define RENAMEF 0x1700 /* Rename file */
#define GETDISK 0x1900 /* Get current disk */
#define SETDMAO 0x1A00 /* Set DMA offset */
#define SETDTA 0x1A00 /* Set disk transfer address */
#define GETALTI 0x1B00 /* Get allocation table info */
#define READR 0x2100 /* Read random */
#define WRITER 0x2200 /* Write random */
#define SIZEF 0x2300 /* Compute file size */
#define SETRREC 0x2400 /* Set random record */
#define SETINT 0x2500 /* Set interrupt vector */
#define PROGSEG 0x2600 /* Create program segment */
#define READB 0x2700 /* Random block read */
#define WRITEB 0x2800 /* Random block write */
#define GETDATE 0x2A00 /* Get date */
#define SETDATE 0x2B00 /* Set date */
#define GETTIME 0x2C00 /* Get time */
#define SETTIME 0x2D00 /* Set time */
#define VERIFY 0x2E00 /* Disk write verification */
#define GETDTA 0x2F00 /* Get address of disk transfer adr */
#define GETVER 0x3000 /* Get DOS version number */
#define TERMRES 0x3100 /* Terminate and remain resident */
#define CTLBCHK 0x3300 /* Ctrl-break check */
#define GETVEC 0x3500 /* Get interrupt vector */
#define GETFREE 0x3600 /* Get free disk space */
#define GETCDI 0x3800 /* Get country dependent info */
#define MKDIR 0x3900 /* Create a sub-directory */
#define RMDIR 0x3A00 /* Remove a sub-directory */
#define CHDIR 0x3B00 /* Change the current directory */
#define CREATH 0x3C00 /* Create a file */
#define OPENH 0x3D00 /* Open a file */
#define CLOSEH 0x3E00 /* Close a file */
#define READH 0x3F00 /* Read from a file or device */
#define WRITEH 0x4000 /* Write to a file or device */
#define DELETE 0x4100 /* Delete a file */
#define LSEEKH 0x4200 /* Move file read/write pointer */
#define CHMOD 0x4300 /* Change file mode */
#define IOCTLH 0x4400 /* I/O control for devices */
#define DUPH 0x4500 /* Duplicate a file handle */
#define FDUPH 0x4600 /* Force a duplicate of handle */
#define GETCDIR 0x4700 /* Get current directory */
#define ALLOC 0x4800 /* Allocate memory */
#define FREE 0x4900 /* Free allocated memory */
#define SETBLK 0x4A00 /* Modify allocated memory blocks */
#define EXEC 0x4B00 /* Load or execute a program */
#define NEXIT 0x4C00 /* Terminate a process */
#define WAIT 0x4D00 /* Get return code of subprocess */
#define NFFIRST 0x4E00 /* DOS 2.0 find first */
#define NFNEXT 0x4F00 /* DOS 2.0 find next */
#define GETVST 0x5400 /* Get verify state */
#define RENAME 0x5600 /* Rename a file */
#define GSDT 0x5700 /* Get/set a file's date and time */
#define ON 1 /* for verify */
#define OFF 0 /* for verify */
/*
* Function declarations for DOS functions.
* Functions dos(), dosb(), dosc(), and dosd() have been replaced
* by the more generalized function intcall(). The old functions
* are retained in the library but will be retracted in a future release.
* If you use an old DOS call, you must declare it.
*/
#define DOSINT 0x21 /* DOS interrupt */
/*
extern int dos();
extern int dosb();
extern long dosc();
extern long dosd();
*/
/*
* Type definition for DOS file control block.
* Assumes all elements get byte alignment.
* FCBs are no longer necessary with DOS 2.0;
* they are replaced by file handles.
*/
typedef struct fcb_t {
unsigned char f_drive; /* drive code */
char f_name[8], /* name */
f_ext[3]; /* extension */
unsigned short f_block; /* current block (=128 records) */
unsigned short f_recsz; /* record size in bytes (=1) */
unsigned long f_size; /* file size in bytes (system) */
unsigned int f_date; /* modification date (system) */
char f_sys[10]; /* for system use */
unsigned char f_rec; /* current record in block */
unsigned long f_seek; /* random record position */
} fcb_t;
/*
* The following masks test the state of the processor carry or zero flags.
*/
#define F_CF 0x0001 /* Carry flag mask */
#define F_ZF 0x0040 /* Zero flag mask */
/*
* A structure of this type is passed to intcall().
*/
#ifndef CSD
struct reg {
unsigned r_ax;
unsigned r_bx;
unsigned r_cx;
unsigned r_dx;
unsigned r_si;
unsigned r_di;
unsigned r_ds;
unsigned r_es;
unsigned r_flags;
};
#endif
/*
* These macros can be used to store or extract pointers
* from a reg structure (defined above).
* These are LARGE/SMALL model independent.
*/
#ifdef LARGE
#define PTR(x) (x)
#define ptoreg(t, off, seg, ptr) ((off)=(long)(ptr),(seg)=((long)ptr)>>16)
#define regtop(off, seg, ptr) ((ptr)=(char *)((long)(off)+((long)(seg)<<16)))
#else
extern unsigned dsreg(), esreg(), csreg(), ssreg();
#define PTR(x) (x), dsreg()
#define ptoreg(t, off, seg, ptr) ((off)=(int)(ptr),(seg)=(t)())
#define regtop(off, seg, ptr) ((ptr)=(char *)(off))
#endif
#endif

View File

@ -0,0 +1,90 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Definitions for math functions.
*/
#ifndef MATH_H
#define MATH_H
#define I 1e+37 /* Infinity */
#define L2I 127.0 /* log2(infinity) */
#define L10P 17 /* log10(precision) */
#define L2L2P 6 /* log2(log2(precision)) */
/*
* Error return values.
*/
#define EBON 0 /* Succesful */
#define EDOM 33 /* Domain error */
#define ERANGE 34 /* Result too large */
/*
* Constants.
*/
#define PI 0.31415926535897932e+01
#define SQRT2 0.14142135623730950e+01
#define LOG2B10 0.30102999566398119e+00
#define LOG10BE 0.23025850929940456e+01
#define LOG10B2 0.33219280948873623e+01
#define LOGEB2 0.14426950408889634e+01
/*
* Complex variables.
*/
typedef struct cpx {
double z_r;
double z_i;
} CPX;
/*
* Status from routines.
*/
extern int errno;
/*
* Internal functions.
*/
double _pol();
double _two();
/*
* Function definitions.
*/
double acos();
double asin();
double atan();
double atan2();
double atof();
double cabs();
double ceil();
double cos();
double cosh();
double exp();
double fabs();
double floor();
double flt();
double frexp();
double hypot();
double j0();
double j1();
double jn();
double y0();
double y1();
double yn();
double ldexp();
double log();
double log10();
double modf();
double pow();
double sin();
double sinh();
double sqrt();
double tan();
double tanh();
double two();
#endif

View File

@ -0,0 +1,26 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* MSDOS setjmp.h.
*/
#ifndef SETJMP_H
#define SETJMP_H
/*
* This header defines the type used to
* save state for setjmp() and longjmp().
* Saves sp, bp and the return pc. The
* return PC is 2 words long in large model.
*/
#ifdef LARGE
typedef int jmp_buf[4];
#else
typedef int jmp_buf[3];
#endif
#endif

View File

@ -0,0 +1,79 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Standard I/O library, MSDOS.
*/
#ifndef STDIO_H
#define STDIO_H
typedef struct FILE {
unsigned char *_cp, /* current character ptr */
*_dp, /* ptr to start of data in buffer */
*_bp; /* buffer pointer */
int _cc; /* character count */
int (*_gt)(), /* getc function */
(*_pt)(); /* putc function */
int _ff; /* flags; see below */
char _fd; /* file descriptor (reqd by reopen) */
int _uc; /* ungot char */
} FILE;
#endif
#define NULL ((char *)0)
#define EOF (-1)
#define BUFSIZ (1<<9)
#define CTRLZ 26
#define _NFILE 30
extern FILE _stdin, _stdout, _stderr, _stdaux, _stdprn, *_fp[_NFILE];
/* Flags in _ff */
#define _FINUSE 0x01
#define _FSTBUF 0x02 /* setbuf was called */
#define _FUNGOT 0x04 /* ungotten char present */
#define _FEOF 0x08
#define _FERR 0x10
#define _FASCII 0x20 /* Ascii mode (default) */
#define _FWRITE 0x40 /* File is opened for writing */
#define _FDONTC 0x80 /* Don't close */
#define _ep(fp) ((fp)->_bp+BUFSIZ)
#define _isdev(fd) (_devinfo(fd)&0x80)
#define _setbinary(fp) ((fp)->_ff &= ~_FASCII)
char *gets();
char *fgets();
FILE *fopen();
FILE *freopen();
FILE *fdopen();
FILE *popen();
FILE *_stropen();
long ftell();
void puts();
void fputs();
void setbuf();
char *malloc();
char *sbrk();
#define getchar() getc(stdin)
#define getc(fp) ((*(fp)->_gt)(fp))
#define putchar(c) putc(c, stdout)
#define putc(c,fp) ((*(fp)->_pt)(c,fp))
#define getw(fp) (fgetw(fp))
#define putw(w,fp) (fputw(w,fp))
#define feof(fp) ((fp)->_ff&(_FEOF|_FERR))
#define ferror(fp) ((fp)->_ff&_FERR)
#define clearerr(fp) ((fp)->_ff &= ~(_FERR|_FEOF))
#define fileno(fp) ((fp)->_fd)
#define wdleng() (16)
#define stdin (&_stdin)
#define stdout (&_stdout)
#define stderr (&_stderr)
#define stdaux (&_stdaux)
#define stdprn (&_stdprn)

View File

@ -0,0 +1,32 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Header file for localtime()
*/
#ifndef TIME_H
#define TIME_H
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
struct tm *localtime();
struct tm *gmtime();
char *asctime();
char *ctime();
extern long timezone;
extern char tzname[2][16];
#endif

View File

@ -0,0 +1,23 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* MSDOS timeb.h.
* Time buffer.
*/
#ifndef TIMEB_H
#define TIMEB_H
typedef long time_t;
struct timeb {
time_t time; /* Time since 1970. */
unsigned short millitm; /* Milliseconds. */
short timezone; /* Time zone -- always 0. */
short dstflag; /* DST applies -- always 0. */
};
#endif

View File

@ -0,0 +1,4 @@
-xcC:\bin\
-xlC:\lib\
-xtC:\tmp\
-IC:\include\

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,66 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
#include <ctype.h>
#define ERROR 0x10 /* largest input base */
/*
* atod() converts the string 'num' to a double and returns its value.
* If there is a non-digit in the string, or if there is an overflow,
* then atod() exits with an appropriate error message.
* atod() accepts leading zero for octal and leading 0x for hexidecimal;
* in the latter case, 'a'-'f' and 'A'-'F' are accepted as digits.
*/
double
atod(num)
char *num;
{
register char *str;
register int i;
double res = 0,
base = 10;
str = num;
i = *str++;
if (i == '0')
if ((i = *str++) == 'x') {
i = *str++;
base = 0x10;
} else
base = 010;
for (; i != '\0'; i = *str++) {
i = todigit(i);
if (i >= base)
die("bad number '%s'", num);
res = res * base + i;
if (res+1 == res)
die("number too big '%s'", num);
}
return (res);
}
/*
* todigit() converts character 'ch' to an integer equivalent,
* assuming that 'ch' is a digit or 'a'-'f' or 'A'-'F'.
* If this is not true, then it returns ERROR.
*/
todigit(ch)
register int ch;
{
if (!isascii(ch))
return (ERROR);
if (isdigit(ch))
return (ch - '0' + 0);
if (isupper(ch))
ch = tolower(ch);
if ('a' <= ch && ch <= 'f')
return (ch - 'a' + 0xA);
return (ERROR);
}

View File

@ -0,0 +1,138 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Banner -- print in large type
* td 80.09.24
*/
#include <stdio.h>
char font[96][9]={
0000,0000,0000,0000,0000,0000,0000,0000,0000, /* sp */
0010,0010,0010,0010,0010,0000,0010,0000,0000, /* ! */
0024,0024,0024,0000,0000,0000,0000,0000,0000, /* " */
0024,0024,0076,0024,0076,0024,0024,0000,0000, /* # */
0010,0036,0050,0034,0012,0074,0010,0000,0000, /* $ */
0060,0062,0004,0010,0020,0046,0006,0000,0000, /* % */
0010,0024,0024,0030,0052,0044,0032,0000,0000, /* & */
0010,0010,0020,0000,0000,0000,0000,0000,0000, /* ' */
0004,0010,0020,0020,0020,0010,0004,0000,0000, /* ( */
0020,0010,0004,0004,0004,0010,0020,0000,0000, /* ) */
0000,0010,0052,0034,0052,0010,0000,0000,0000, /* * */
0000,0010,0010,0076,0010,0010,0000,0000,0000, /* + */
0000,0000,0000,0000,0000,0030,0030,0010,0020, /* , */
0000,0000,0000,0076,0000,0000,0000,0000,0000, /* - */
0000,0000,0000,0000,0000,0030,0030,0000,0000, /* . */
0001,0002,0004,0010,0020,0040,0100,0000,0000, /* / */
0034,0042,0046,0052,0062,0042,0034,0000,0000, /* 0 */
0010,0030,0010,0010,0010,0010,0034,0000,0000, /* 1 */
0034,0042,0002,0004,0010,0020,0076,0000,0000, /* 2 */
0076,0004,0010,0004,0002,0042,0034,0000,0000, /* 3 */
0004,0014,0024,0044,0076,0004,0004,0000,0000, /* 4 */
0076,0040,0074,0002,0002,0042,0034,0000,0000, /* 5 */
0014,0020,0040,0074,0042,0042,0034,0000,0000, /* 6 */
0076,0002,0004,0010,0020,0020,0020,0000,0000, /* 7 */
0034,0042,0042,0034,0042,0042,0034,0000,0000, /* 8 */
0034,0042,0042,0036,0002,0004,0030,0000,0000, /* 9 */
0000,0030,0030,0000,0030,0030,0000,0000,0000, /* : */
0000,0030,0030,0000,0030,0030,0010,0020,0000, /* ; */
0002,0004,0010,0020,0010,0004,0002,0000,0000, /* < */
0000,0000,0076,0000,0076,0000,0000,0000,0000, /* = */
0040,0020,0010,0004,0010,0020,0040,0000,0000, /* > */
0034,0042,0002,0004,0010,0000,0010,0000,0000, /* ? */
0014,0022,0056,0052,0056,0040,0036,0000,0000, /* @ */
0034,0042,0042,0076,0042,0042,0042,0000,0000, /* A */
0074,0042,0042,0074,0042,0042,0074,0000,0000, /* B */
0034,0042,0040,0040,0040,0042,0034,0000,0000, /* C */
0070,0044,0042,0042,0042,0044,0070,0000,0000, /* D */
0076,0040,0040,0074,0040,0040,0076,0000,0000, /* E */
0076,0040,0040,0074,0040,0040,0040,0000,0000, /* F */
0036,0040,0040,0046,0042,0042,0036,0000,0000, /* G */
0042,0042,0042,0076,0042,0042,0042,0000,0000, /* H */
0034,0010,0010,0010,0010,0010,0034,0000,0000, /* I */
0034,0010,0010,0010,0010,0050,0020,0000,0000, /* J */
0042,0044,0050,0060,0050,0044,0042,0000,0000, /* K */
0040,0040,0040,0040,0040,0040,0076,0000,0000, /* L */
0042,0066,0052,0052,0042,0042,0042,0000,0000, /* M */
0042,0042,0062,0052,0046,0042,0042,0000,0000, /* N */
0034,0042,0042,0042,0042,0042,0034,0000,0000, /* O */
0074,0042,0042,0074,0040,0040,0040,0000,0000, /* P */
0034,0042,0042,0042,0052,0044,0032,0000,0000, /* Q */
0074,0042,0042,0074,0050,0044,0042,0000,0000, /* R */
0034,0042,0040,0034,0002,0042,0034,0000,0000, /* S */
0076,0010,0010,0010,0010,0010,0010,0000,0000, /* T */
0042,0042,0042,0042,0042,0042,0034,0000,0000, /* U */
0042,0042,0042,0024,0024,0010,0010,0000,0000, /* V */
0042,0042,0042,0052,0052,0052,0024,0000,0000, /* W */
0042,0042,0024,0010,0024,0042,0042,0000,0000, /* X */
0042,0042,0024,0010,0010,0010,0010,0000,0000, /* Y */
0076,0002,0004,0010,0020,0040,0076,0000,0000, /* Z */
0034,0020,0020,0020,0020,0020,0034,0000,0000, /* [ */
0100,0040,0020,0010,0004,0002,0001,0000,0000, /* \ */
0034,0004,0004,0004,0004,0004,0034,0000,0000, /* ] */
0010,0024,0042,0000,0000,0000,0000,0000,0000, /* ^ */
0000,0000,0000,0000,0000,0000,0076,0000,0000, /* _ */
0010,0010,0004,0000,0000,0000,0000,0000,0000, /* ` */
0000,0000,0034,0002,0036,0042,0036,0000,0000, /* a */
0040,0040,0074,0042,0042,0042,0074,0000,0000, /* b */
0000,0000,0030,0044,0040,0044,0030,0000,0000, /* c */
0002,0002,0036,0042,0042,0042,0036,0000,0000, /* d */
0000,0000,0034,0042,0076,0040,0034,0000,0000, /* e */
0014,0022,0070,0020,0020,0020,0020,0000,0000, /* f */
0000,0000,0032,0046,0042,0046,0032,0002,0034, /* g */
0040,0040,0074,0042,0042,0042,0042,0000,0000, /* h */
0010,0000,0030,0010,0010,0010,0034,0000,0000, /* i */
0004,0000,0004,0004,0004,0004,0004,0044,0030, /* j */
0040,0040,0044,0050,0064,0042,0042,0000,0000, /* k */
0030,0010,0010,0010,0010,0010,0034,0000,0000, /* l */
0000,0000,0064,0052,0052,0052,0052,0000,0000, /* m */
0000,0000,0074,0042,0042,0042,0042,0000,0000, /* n */
0000,0000,0034,0042,0042,0042,0034,0000,0000, /* o */
0000,0000,0054,0062,0042,0062,0054,0040,0040, /* p */
0000,0000,0032,0046,0042,0046,0032,0002,0002, /* q */
0000,0000,0054,0062,0040,0040,0040,0000,0000, /* r */
0000,0000,0036,0040,0034,0002,0074,0000,0000, /* s */
0020,0020,0070,0020,0020,0022,0014,0000,0000, /* t */
0000,0000,0042,0042,0042,0046,0032,0000,0000, /* u */
0000,0000,0042,0042,0042,0024,0010,0000,0000, /* v */
0000,0000,0042,0042,0052,0052,0024,0000,0000, /* w */
0000,0000,0042,0024,0010,0024,0042,0000,0000, /* x */
0000,0000,0042,0042,0042,0046,0032,0002,0034, /* y */
0000,0000,0076,0004,0010,0020,0076,0000,0000, /* z */
0014,0020,0020,0040,0020,0020,0014,0000,0000, /* { */
0010,0010,0010,0000,0010,0010,0010,0000,0000, /* | */
0030,0004,0004,0002,0004,0004,0030,0000,0000, /* } */
0020,0052,0004,0000,0000,0000,0000,0000,0000, /* ~ */
0177,0177,0177,0177,0177,0177,0177,0177,0177, /* del */
};
char line[512];
main(argc, argv)
char *argv[];
{
register i;
if(argc>1) for(i=1;i!=argc;i++){
if(i!=1)
putchar('\n');
banner(argv[i]);
}
else while(gets(line)!=NULL){
putchar('\n');
banner(line);
}
}
banner(line)
char *line;
{
register i, j;
register char *s;
for(i=0;i!=9;i++){
for(s=line;*s;s++) for(j=0200;j!=0;j>>=1)
if(' '<=*s && *s<=0177 && font[*s-' '][i]&j)
putchar('x');
else
putchar(' ');
putchar('\n');
}
}

View File

@ -0,0 +1,111 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Factor prints out the prime factorization of numbers.
* If there are arguments, it factors them.
* If there are no arguments, it reads stdin until
* either EOF or the number zero or a non-numeric
* non-white-space character. Since factor does all of
* its calculations in double format, the largest number
* which can be handled is quite large.
*/
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#define NUL '\0'
#define ERROR 0x10 /* largest input base */
#define MAXNUM 200 /* max number of chars in number */
main(argc, argv)
int argc;
register char *argv[];
{
register char *chp;
double n;
double atod();
char *getnum();
if (argc != 1)
while ((chp=*++argv) != NULL &&
(n=atod(chp)) != 0)
factor(n);
else
while ((chp=getnum()) != NULL &&
(n=atod(chp)) != 0)
factor(n);
return (0);
}
die(str)
char *str;
{
fprintf(stderr, "%r\n", &str);
exit(1);
}
usage()
{
die("Usage: factor [number number ...]");
}
char *
getnum()
{
register char *chp,
ch;
static char res[MAXNUM+1];
do {
ch = getchar();
} while (isascii(ch) && isspace(ch));
if (!isascii(ch) || todigit(ch) == ERROR)
return (NULL);
for (chp=res; isascii(ch) && !isspace(ch);
ch=getchar())
if (chp < &res[MAXNUM])
*chp++ = ch;
if (chp >= &res[MAXNUM])
die("number too big");
*chp++ = NUL;
return (res);
}
/*
* Factor is the routine that actually factors the double `n'.
* It writes the prime factors to standard output.
*/
factor(n)
double n;
{
double temp,
limit,
try;
while (n > 1 && modf(n/2, &temp) == 0) {
printf("2 ");
n = temp;
}
limit = sqrt(n);
for (try=3; try <= limit; try += 2) {
if (modf(n/try, &temp) != 0)
continue;
do {
printf("%.0f ", try);
n = temp;
} while (modf(n/try, &temp) == 0);
limit = sqrt(n);
}
if (n > 1)
printf("%.0f", n);
putchar('\n');
}

View File

@ -0,0 +1,321 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* fdir.c
* 6/5/86
*
* This program prints a formatted directory of its command line arguments.
* Example command lines:
* fdir
* fdir *.c
* fdir \test\*.c \source\*.*
* It demonstrates some advanced features of MSDOS MWC86:
* (1) The intcall() function, used to call MSDOS.
* (2) The ptoreg() macro, used to pass a pointer to MSDOS
* with code which is independent of LARGE or SMALL model.
* (3) Bit fields, used to reference the dates of the files.
* (4) The qsort() function, used to sort files and directories.
* (5) The ctype macros.
* (6) Dynamic memory allocation.
* Files within each directory are sorted alphabetically.
* Directories are listed before other files.
* The output is in the following format for each file:
* <attribute bits> <file length> <last modification date> <filename>
*/
/*
* Header files.
*/
#include <stdio.h> /* Standard I/O definitions */
#include <ctype.h> /* Character type macros */
#include <dos.h> /* MSDOS definitions */
/*
* Predefined limits.
*/
#define FNLEN 80 /* Maximum filename length */
#define MAXFILES 512 /* Max number of files/directory */
/*
* This macro returns the number of elements of its array argument.
*/
#define SIZE(x) (sizeof(x)/sizeof(x[0]))
/*
* File attributes.
* Defined on page C-4 of the IBM-PC DOS 2.0 manual.
*/
#define A_RONLY 0x01 /* Read only file */
#define A_HIDDEN 0x02 /* Hidden file */
#define A_SYSTEM 0x04 /* System file */
#define A_DIRECTORY 0x10 /* Directory file */
#define A_ARCHIVE 0x20 /* Archive bit */
/*
* Attribute structure, associating a name with each mask.
*/
static struct atr {
int a_mask;
int a_name;
} atr[] = {
A_DIRECTORY, 'd', /* Directory file */
A_RONLY, 'r', /* Read only file */
A_HIDDEN, 'h', /* Hidden file */
A_SYSTEM, 's', /* System file */
A_ARCHIVE, 'a' /* Archive bit */
};
/*
* Structure of MSDOS date/time bit fields.
* Defined on page C-5 of the IBM-PC DOS 2.0 manual.
*/
struct date {
unsigned d_sec : 5; /* Time, 2 second intervals */
unsigned d_min : 6; /* Time, minutes */
unsigned d_hour : 5; /* Time, hours */
unsigned d_day : 5; /* Date, day of month */
unsigned d_month : 4; /* Date, month of year */
unsigned d_year : 7; /* Date, year since 1980 */
};
/*
* Structure filled in by MSDOS for FINDFIRST and FINDNEXT calls.
* Defined of page D-49 of the IBM-PC DOS 2.0 manual.
*/
#define FNDINFO 21
#define FNDNAME 13
struct find {
char fnd_dosinfo[FNDINFO]; /* Reserved for dos */
char fnd_attr; /* File attribute */
struct date fnd_date; /* Date structure */
long fnd_size; /* File size */
char fnd_name[FNDNAME]; /* File name less path */
};
/*
* Routines which return values other than int.
* All such routines should be declared before they are used.
* Failure to do so could sacrifice portability between machines
* or between LARGE and SMALL model.
*/
extern char *basename();
extern char *date();
extern void fatal();
extern char *malloc();
extern int qscmp();
extern char *rindex();
extern char *strcpy();
extern char *strlower();
/*
* Global data.
*/
int filecount = 0; /* Total number of files */
int dircount = 0; /* Total number of directories */
long bytecount = 0L; /* Total length of files in bytes */
char path[FNLEN]; /* Filename path */
struct find *fnd[MAXFILES]; /* Pointers to file structures */
main(argc, argv)
int argc; /* Number of command line arguments */
char *argv[]; /* Array of pointers to arguments */
{
register int i;
register int count; /* Number of files found */
register char *file; /* Command line argument */
register int status; /* Exit status */
status = 0;
/* If no command line arguments, i.e. "fdir", fake argument "*.*". */
if (argc == 1) {
argc = 2;
argv[1] = "*.*";
}
while (--argc) { /* Loop for each argument */
file = *++argv; /* Grab next argument */
i = basename(file) - file; /* Find pathname length */
if (i + FNDNAME > FNLEN)
fatal("filename \"%s\" too long", file);
strcpy(path, file); /* Copy file to path[] */
path[i] = '\0'; /* NUL-terminate pathname */
strlower(path); /* Convert pathname to lower case */
count = dosfind(file); /* Find matching files */
if (!count) { /* Issue error if no match */
++status;
fprintf(stderr, "fdir: cannot find \"%s\"\n", file);
}
qsort(fnd, count, sizeof(struct find *), qscmp); /* Sort */
for (i=0; i < count; i++) /* Loop for each matching file */
printinfo(fnd[i]); /* Print information */
}
/* Print totals. */
printf("\n");
if (dircount)
printf("Directories: %d\n", dircount);
if (filecount)
printf("Other files: %d\n", filecount);
if (bytecount)
printf("Total bytes: %D\n", bytecount);
exit(status);
}
/*
* Return a pointer to the beginning of the filename (past pathname, if any).
*/
char *basename(name) char *name;
{
register char *p;
if ((p = rindex(name, '\\')) != NULL || (p = rindex(name, ':')) != NULL)
return(++p);
return(name);
}
/*
* Given a pointer to a date structure, return a pointer to an ASCII date.
* The buffer for the return value is statically allocated.
*/
char *date(dp) register struct date *dp;
{
static char ad[21];
static char *month[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
sprintf(ad, "%s %2d %02d:%02d:%02d %4d",
month[dp->d_month - 1], dp->d_day,
dp->d_hour, dp->d_min, dp->d_sec * 2,
dp->d_year + 1980);
return(ad);
}
/*
* Expand filename 'file' possibly containing wildcard characters.
* Initialize global array fnd[] with pointers to matching filenames and
* return the number of matches found.
*/
int dosfind(file) char *file;
{
register int count; /* Number of files found */
struct find f; /* Filename return data from MSDOS */
struct reg r; /* Register values for intcall() */
/*
* Call MSDOS to set the disk transfer address (DTA) to &f.
* Macro ptoreg() stores the segment:offset of f
* in r.r_ds and r.r_dx in a model independent manner.
*/
r.r_ax = SETDTA; /* Function code to AX */
ptoreg(dsreg, r.r_dx, r.r_ds, &f); /* Seg:off of f to DS:DX */
intcall(&r, &r, DOSINT); /* Call MSDOS */
/* Call MSDOS to find the first matching filename. */
r.r_ax = NFFIRST; /* Function code to AX*/
r.r_cx = A_HIDDEN | A_SYSTEM | A_DIRECTORY | A_RONLY | A_ARCHIVE;
/* Attributes to CX */
ptoreg(dsreg, r.r_dx, r.r_ds, file); /* Seg:off of file to DS:DX */
intcall(&r, &r, DOSINT); /* Call MSDOS */
/*
* Loop for each matching filename.
* Carry flag set indicates no more matching files.
*/
for (count = 0; (r.r_flags & F_CF) == 0; count++) {
/* Do not exceed maximum number of matching files. */
if (count >= MAXFILES)
fatal("too many matching files");
/* Dynamically allocate memory for a find structure. */
if ((fnd[count] = (struct find *)malloc(sizeof(struct find))) == NULL)
fatal("out of memory");
/* Copy the structure to the allocated space. */
*fnd[count] = f; /* Structure assignment. */
/* Find the next matching filename. */
r.r_ax = NFNEXT; /* Find next function */
intcall(&r, &r, DOSINT); /* Call MSDOS */
}
return(count);
}
/*
* Fatal error.
* The "%r" (recursive) format specifier is non-portable.
*/
void fatal(p) char *p;
{
fprintf(stderr, "fdir: %r\n", &p);
exit(1);
}
/*
* Print a line of information about a file.
* Update running totals.
*/
printinfo(fndp) register struct fnd *fndp;
{
register int i, attr;
attr = fndp->fnd_attr;
if (attr&A_DIRECTORY)
++dircount;
else
++filecount;
bytecount += fndp->fnd_size;
/* Print attributes (name if bit is set, '-' if not). */
for (i = 0; i < SIZE(atr); i++)
putchar((atr[i].a_mask & attr) ? atr[i].a_name : '-');
/* Print size, date, name. */
printf(" %10D %s %s%s\n",
fndp->fnd_size, date(&(fndp->fnd_date)), path, strlower(fndp->fnd_name));
/* Free allocated structure. */
free(fndp);
}
/*
* Compare routine for qsort().
* Order: directories alphabetically first, then other files alphabetically.
* Called by qsort(), passed two pointers to find structure pointers.
* Only the pointers to the find structures are actually sorted.
*/
int qscmp(fpp1, fpp2) struct find **fpp1, **fpp2;
{
register struct find *f1;
register struct find *f2;
register int i;
f1 = *fpp1;
f2 = *fpp2;
if (i = (f2->fnd_attr&A_DIRECTORY) - (f1->fnd_attr&A_DIRECTORY))
return(i); /* Exactly one is directory */
return (strcmp(f1->fnd_name, f2->fnd_name));
}
/*
* Convert string 'str' to lower case (in-place) and return a pointer to it.
*/
char *strlower(str) char *str;
{
register int c;
register char *s;
s = str;
while (c = *s)
*s++ = tolower(c);
return(str);
}
/* end of fdir.c */

View File

@ -0,0 +1,4 @@
main()
{
printf("hello world\n");
}

View File

@ -0,0 +1,254 @@
/*
* MWC86 CPS Version 3.1.1.
* Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* These routines provide an interface between
* the MSDOS hardware interrupt system and MWC86 C.
* Works only with SMALL model as of now.
* Example compilation:
* cc count.c int.c intdis.s
*/
#include <stdio.h>
#include <dos.h>
#define SIZE(x) (sizeof(x)/sizeof(x[0])) /* Size in elements */
#define END(x) (&x[SIZE(x)]) /* Address of end */
/*
* The interrupts are set to jump to these assembly entry points first.
*/
extern intdis1();
extern intdis2();
extern intdis3();
extern intdis4();
extern intdis5();
extern intdis6();
extern intdis7();
extern intdis8();
extern intdis9();
extern intdis10();
extern intdis11();
extern intdis12();
extern intdis13();
extern intdis14();
extern intdis15();
extern intdis16();
/*
* Interrupt information.
* Knowledge of this structure is hardcoded in intdis.s.
*/
struct INTINFO {
int (*i_cfunc)(); /* C function to be called */
int i_stacksize; /* Stack needed by C func */
void (*i_intdis)(); /* Assembly dispatch routine */
int i_intnum; /* 8086 int number (-1 if free) */
unsigned i_oldoff; /* Old vector offset */
unsigned i_oldseg; /* Old vector segment */
char *i_stack; /* Pointer to start of stack */
char *i_curstack; /* Current sp (grows down) */
char *i_endstack; /* Pointer to end of stack */
} intinfo[] = {
{ NULL, 0, &intdis1, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis2, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis3, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis4, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis5, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis6, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis7, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis8, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis9, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis10, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis11, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis12, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis13, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis14, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis15, -1, 0, 0, NULL, NULL, NULL },
{ NULL, 0, &intdis16, -1, 0, 0, NULL, NULL, NULL }
};
static char iovermsg[] = "Interrupt nesting capacity exceeded\r\n";
/*
* Link a c function with an 8086 interrupt.
* Up to 16 C functions can be linked to interrupts at once.
*
* Parameters:
*
* intnum -- 8086 interrupt number.
* func -- C function pointer for interrupt service routine.
* stacksize -- Amount of stack (in bytes) that C function needs.
* level -- Maximum number of levels that interrupt will be nested.
*
* Returns:
*
* The C function should return 1 if it wants the old interrupt
* service routine to be called too. Otherwise, it should return 0.
* This is useful for tapping an interrupt but not affecting its
* old functionality.
* This function returns a number from 0 to 15 on success. This
* unique interrupt identifier is passed to clearint when
* you want to dereference the C routine from the interrupt.
* Returns -1 on failure (Bad intnum, all available ints in use, bad
* level value, not enough memory for stack).
*
* Notes:
*
* (1) If the interrupt service routine returns 0 and the interrupt
* is a real hardware interrupt (eg. interrupts 0x8 to 0xF on the
* IBM-PC), the C service routine must output an EOI to the
* interrupt controller.
*
* (2) All C service routines are called with interrupts disabled. You
* may call cli or sti to clear or set the interrupt enable bit
* respectively.
*
* (3) Most library functions cannot be called by an interrupt service
* routine.
*
* (4) The use of floating point arithmetic within
* an interrupt service routine is not recommended.
*
* (5) Caution should be exercised when interrupt routine(s)
* and the main program access the same writeable data structures.
*
* (6) Interrupts can be linked. That is, setint may be called
* several times with the same interrupt number. When that
* interrupt occurs, if all C service routines for that interrupt
* return 1, all such routines will be executed. When you link
* interrupts, it is vital that setint/clearint calls are not crossed.
* That is, between a setint and its corresponding clearint, no other
* setints on that interrupt should have been performed which
* weren't cleared.
*
* (7) No stack checking is performed. If stacksize is too small, the
* system will crash. If level is too small (the interrupt
* nests more than level levels), the system will print
* an error message and hang with interrupts disabled.
* The stack size (in bytes) needed by one function is equal to
* 6 + sizeof parameters + sizeof automatics.
* Allow at least 256 bytes excess stack for a safety margin.
*
* (8) It's ok to use this system after calling MS-DOS function 0x31
* (Terminate and remain resident). The routine termres, provided
* here, does this.
*/
setint(intnum, func, stacksize, level)
register int intnum;
int (*func)();
unsigned stacksize;
int level;
{
register struct INTINFO *ip;
register int i = 0;
register unsigned totalstack = stacksize * level;
struct reg r;
char *malloc();
if (intnum < 0 || intnum >= 256 || level < 0)
return(-1);
savedsreg();
for (ip = intinfo; ip < END(intinfo); ++ip, ++i)
if (ip->i_intnum == -1) { /* Free spot? */
if ((ip->i_stack = malloc(totalstack)) == NULL)
return(-1);
r.r_ax = GETVEC | intnum;
intcall(&r, &r, DOSINT); /* Get old vector */
ip->i_oldseg = r.r_es; /* Save it */
ip->i_oldoff = r.r_bx;
ip->i_intnum = intnum;
ip->i_cfunc = func;
ip->i_stacksize = stacksize;
ip->i_curstack = ip->i_endstack = ip->i_stack +
totalstack;
ptoreg(csreg, r.r_dx, r.r_ds, ip->i_intdis);
r.r_ax = SETINT | intnum;
intcall(&r, &r, DOSINT); /* Set new int */
return(i);
}
return(-1); /* Table full */
}
/*
* Clear an interrupt previously set by setint.
* id -- unique identifier which setint returned.
* Returns 0 on success.
* Returns -1 on failure (id out of range, interrupt never set).
*/
clearint(id)
register int id;
{
if (id < 0 || id >= SIZE(intinfo))
return(-1);
return(_clearint(&intinfo[id]));
}
/*
* Clear all interrupts.
* You should not call this routine if you have linked several
* interrupt service routines to the same interrupt with setint.
*/
doneint()
{
register struct INTINFO *ip;
for (ip = intinfo; ip < END(intinfo); ++ip)
_clearint(ip);
}
/*
* Internal routine to restore an interrupt to its old value.
* Returns 0 if ok, -1 if interrupt is clear.
*/
static
_clearint(ip)
register struct INTINFO *ip;
{
struct reg r;
if (ip->i_intnum != -1) {
r.r_ax = SETINT | ip->i_intnum;
r.r_ds = ip->i_oldseg;
r.r_dx = ip->i_oldoff;
intcall(&r, &r, DOSINT);
ip->i_intnum = -1;
free(ip->i_stack);
return(0);
}
return(-1);
}
/*
* Internal routine called by the assembly interrupt linkage
* on interrupt nesting overflow. Called in interrupt context.
* At this point, system may be foobared.
*/
isover()
{
write(1, iovermsg, sizeof(iovermsg) - 1);
}
/*
* Terminate process and remain resident.
* This routine exits, but allocates all program and data memory
* before doing so. You must call this routine if you want
* to handle interrupts after normal program exit.
* Basically figure out the total number of paragraphs that program,
* data, stack, and malloc arena occupy and pass it to the MS-DOS
* TERMRES function.
* status -- Byte exit code.
*/
termres(status)
int status;
{
struct reg r;
extern int _pspbase;
extern char *__end;
r.r_ax = TERMRES | (status & 0xFF);
r.r_dx = dsreg() + ((int)__end >> 4) + 1 - _pspbase;
intcall(&r, &r, DOSINT);
}

View File

@ -0,0 +1,163 @@
/
/ MWC86 CPS Version 3.1.1.
/ Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
/ All rights reserved. May not be copied or disclosed without permission.
/
/
/ Assembly language interrupt dispatcher.
/ To be used with int.c.
/
.shri
.globl intdis1_ / Interrupt entry points
.globl intdis2_
.globl intdis3_
.globl intdis4_
.globl intdis5_
.globl intdis6_
.globl intdis7_
.globl intdis8_
.globl intdis9_
.globl intdis10_
.globl intdis11_
.globl intdis12_
.globl intdis13_
.globl intdis14_
.globl intdis15_
.globl intdis16_
iisize = 18 / sizeof(struct INTINFO)
i_cfunc = 0 / Member offsets in intinfo
i_stacksize = 2
i_oldoff = 8
i_stack = 12
i_curstack = 14
i_endstack = 16
.globl intinfo_ / External references
.globl isover_
.globl cli_ / External definitions
.globl sti_
.globl savedsreg_
dsreg:
.word 0 / Saved ds goes here
cli_:
cli
ret
sti_:
sti
ret
savedsreg_: / Called by initint()
mov cs:dsreg, ds
ret
intdis1_: / Entry points
push bx
mov bx, $0 * iisize
jmp dispatch
intdis2_:
push bx
mov bx, $1 * iisize
jmp dispatch
intdis3_:
push bx
mov bx, $2 * iisize
jmp dispatch
intdis4_:
push bx
mov bx, $3 * iisize
jmp dispatch
intdis5_:
push bx
mov bx, $4 * iisize
jmp dispatch
intdis6_:
push bx
mov bx, $5 * iisize
jmp dispatch
intdis7_:
push bx
mov bx, $6 * iisize
jmp dispatch
intdis8_:
push bx
mov bx, $7 * iisize
jmp dispatch
intdis9_:
push bx
mov bx, $8 * iisize
jmp dispatch
intdis10_:
push bx
mov bx, $9 * iisize
jmp dispatch
intdis11_:
push bx
mov bx, $10 * iisize
jmp dispatch
intdis12_:
push bx
mov bx, $11 * iisize
jmp dispatch
intdis13_:
push bx
mov bx, $12 * iisize
jmp dispatch
intdis14_:
push bx
mov bx, $13 * iisize
jmp dispatch
intdis15_:
push bx
mov bx, $14 * iisize
jmp dispatch
intdis16_:
push bx
mov bx, $15 * iisize
dispatch:
push ax / Save machine state
push cx
push dx
push ds
push es
mov cx, ss
mov dx, sp
mov ax, cs:dsreg / Load seg registers for C
mov ss, ax
mov ds, ax
mov es, ax
mov sp, intinfo_+i_curstack(bx) / Get interrupt sp
mov ax, sp
cmp ax, intinfo_+i_stack(bx) / Nesting overflow check
je overflow
sub ax, intinfo_+i_stacksize(bx) / Make room on stack
mov intinfo_+i_curstack(bx), ax
push dx
push cx
push bx
icall intinfo_+i_cfunc(bx) / Call C routine
pop bx / Pop intinfo offset
pop cx / Pop old ss, sp
pop dx
test ax, ax / Call old interrupt?
jz done / No
pushf / Simulate int to old function
xcall intinfo_+i_oldoff(bx) / Indirect intersegment call
done:
mov intinfo_+i_curstack(bx), sp / Deallocate stack
mov ss, cx / Restore machine state
mov sp, dx
pop es
pop ds
pop dx
pop cx
pop ax
pop bx
iret
overflow:
mov sp, intinfo_+i_endstack(bx)
call isover_ / Nesting overflow
death:
jmp death

View File

@ -0,0 +1,6 @@
main()
{
int i;
for (i=0; i<11; i++)
printf("%2d %4d\n", i, i*i);
}

View File

@ -0,0 +1,16 @@
From "Life on the Mississippi":
I know how a prize watermelon looks when it is sunning
its fat rotundity among pumpkin vines; I know how to tell
when it ripe without "plugging" it; I know how inviting
it looks when it is cooling itself in a tub of water under
the bed, waiting; I know how it looks when it lies on the
table in the sheltered great floor space between house and
kitchen, and the children gathered for the sacrifice and
their mouths watering; I know the crackling sound it makes
when the carving knife enters its end, and I can see the
split fly along in front of the blade as the knife cleaves
its way to the other end; I can see its halves fall apart
and display the rich red meat and the black seeds, and the
heart standing up, a luxury fit the elect; I know how a
boy looks behind a yard-long slice of that melon, and I
know how he feels; for I have been there.

View File

@ -0,0 +1,11 @@
From the "Devil's Dictionary":
A penny saved is a penny to squander.
A man is known by the company he organizes.
A bird in the hand is worth what it will bring.
Think twice before you speak to a friend in need.
He laughs best who laughs least.
Least said is soonest disavowed.
Speak of the Devil and he will hear about it.
Of two evils choose to be the least.
Strike while your employer has a big contract.
Where there's a will there's a won't.

View File

@ -0,0 +1,92 @@
From "Gulliver's Travels":
I said there was a society of men among us,
bred up from their youth in the art of proving
by words multiplied for the purpose, that
white is black, and black is white, according
as they are paid. To this society all the rest
of the people are slaves.
"For example. If my neighbor hath a mind to my
cow, he hires a lawyer to prove that he ought to
have my cow from me. I must then hire another to
defend my right; it being against all rules of law
that any man should be allowed to speak for himself.
Now in this case, I who am the true owner lie under
two great disadvantages. First, my lawyer being
practiced almost from his cradle in defending
falsehood is quite out of his element when he would
be an advocate for justice, which as an office
unnatural, he always attempts with great awkwardness,
if not ill-will. The second disadvantage is that my
lawyer must proceed with great caution, or else he
will be reprimanded by the judges, and abhorred by
his brethren, as one who would lessen the practice
of the law. And therefore I have but two methods
to preserve my cow. The first is to gain over my
adversary's lawyer with a double fee; who will then
betray his client, by insinuating that he hath
justice on his side. The second way is for my
lawyer to make my cause appear as unjust as he can;
by allowing the cow to belong to my adversary; and
this if it be skillfully done, will certainly
bespeak the favor of the bench.
"Now, these judges are persons appointed to
decide all controversies of property, as well as
for the trial of criminals; and picked out from the
most dexterous lawyers who are grown old or lazy;
and having been biased all their lives against truth
and equity, lie under such a fatal necessity of
favoring fraud, perjury, and oppression, that I
have known some of them to have refused a large
bribe from the side where justice lay, rather than
injure the faculty, by doing anything unbecoming
their nature or their office.
"It is a maxim among these lawyers, that whatever
hath been done before may legally be done again; and
therefore they take special care to record all
decisions formerly made against common justice and the
general reason of mankind. These, under the name of
'precedents', they produce as authorities to justify
the most iniquitous opinions; and the judges never
fail of directing accordingly.
"In pleading, they studiously avoid entering
into the merits of the cause; but are loud, violent,
and tedious in dwelling upon all circumstances
which are not to the purpose. For instance, in the
case already mentioned, they never desire to know
what claim or title my adversary hath to my cow;
but whether the said cow were red or black; her horns
long or short; whether the field I graze her in be
round or square; whether she were milked at home
or abroad; what diseases she is subject to, and the
like. After which they consult precedents, adjourn
the cause, from time to time, and in ten, twenty,
or thirty years come to an issue.
"It is likewise to be observed, that this society
hath a peculiar cant and jargon of their own, that
no other mortal can understand, and wherein all
their laws are written, which they take special
care to multiply; whereby they have wholly confounded
the very essence of truth and falsehood, of right and
wrong; so that it will take thirty years to decide
whether the field, left me by my ancestors for six
generations, belong to me, or to a stranger three
hundred miles off.
"In the trial of persons accused of crimes against
the state, the method is much more short and
commendable: the judge first sends to sound the
disposition of those in power; after which he can
easily hang or save the criminal, preserving all
the forms of law."
Here my master interposing said it was a pity
that creatures endowed with such prodigious abilities
of mind as these lawyers, by the description I gave
of them must certainly be, were not rather encouraged
to be instructors of others in wisdom and knowledge.
In answer to which, I assured his honor that in all
points out of their own trade, they were usually the
most ignorant and stupid generation among us, the
most despicable in common conversation, avowed
enemies to all knowledge and learning; and equally
disposed to pervert the general reason of mankind,
in every other subject of discourse as in that of
their own profession.

View File

@ -0,0 +1,72 @@
/
/ MWC86 CPS Version 3.1.1.
/ Copyright (c) 1982-1986 by Mark Williams Company, Chicago.
/ All rights reserved. May not be copied or disclosed without permission.
/
////////
/
/ PC DOS runtime startoff.
/ Set up data segment registers.
/ Clear the block segment.
/ Make sure the stack is word aligned.
/ Call the second level of run time startoff to
/ chop up the command line, open files and call the
/ user's main routine.
/ This routine is the first file in the load.
/
////////
.globl _pspbase_
.globl _exit_
.globl edata_
.globl end_
.globl __end_;
.globl _main_
mov ax, ss
mov ds, ax / DS = SS
mov es, ax / ES = DS
mov di, $edata_ / Get base address of BSS.
mov cx, $end_ / Get top of load.
sub cx, di / Figure out BSS size.
subb al, al / Get a 0.
cld / Incrementing.
rep
stosb / Clear segment.
sub bp, bp / For setjmp and longjmp.
test sp, $01 / Is the stack word aligned?
jz 1f / Yep, ok.
dec sp / Nope, make it word aligned.
1: mov ax, $end_ / For sbrk.
inc ax
and ax, $-2 / Align on word boundary.
mov __end_, ax
mov _pspbase_, cs / Save CS.
call _main_ / Call _main which calls main.
push ax / Save return status,
push ax / push phoney return address
/ and fall through to _exit.
////////
/
/ void
/ _exit(s);
/ int s;
/
/ Low level exit.
/ Return to caller.
/ Status in AX.
/
////////
_exit_: cld / For DOS.
pop ax / Discard return address and
pop ax / get return status in AL.
movb ah, $0x4C / Load return exit status function code
int 0x21 / and do it.

View File

@ -0,0 +1,107 @@
/*
* The routines in this file
* provide support for ANSI style terminals
* over a serial line. The serial I/O services are
* provided by routines in "termio.c". It compiles
* into nothing if not an ANSI device.
*/
#include <stdio.h>
#include "ed.h"
#if ANSI
#define NROW 24 /* Screen size. */
#define NCOL 80 /* Edit if you want to. */
#define BEL 0x07 /* BEL character. */
#define ESC 0x1B /* ESC character. */
extern int ttopen(); /* Forward references. */
extern int ttgetc();
extern int ttputc();
extern int ttflush();
extern int ttclose();
extern int ansimove();
extern int ansieeol();
extern int ansieeop();
extern int ansibeep();
extern int ansiopen();
/*
* Standard terminal interface
* dispatch table. Most of the fields
* point into "termio" code.
*/
TERM term = {
NROW-1,
NCOL,
&ansiopen,
&ttclose,
&ttgetc,
&ttputc,
&ttflush,
&ansimove,
&ansieeol,
&ansieeop,
&ansibeep
};
ansimove(row, col)
{
ttputc(ESC);
ttputc('[');
ansiparm(row+1);
ttputc(';');
ansiparm(col+1);
ttputc('H');
}
ansieeol()
{
ttputc(ESC);
ttputc('[');
ttputc('K');
}
ansieeop()
{
ttputc(ESC);
ttputc('[');
ttputc('J');
}
ansibeep()
{
ttputc(BEL);
ttflush();
}
ansiparm(n)
register int n;
{
register int q;
q = n/10;
if (q != 0)
ansiparm(q);
ttputc((n%10) + '0');
}
#endif
ansiopen()
{
#if V7
register char *cp;
char *getenv();
if ((cp = getenv("TERM")) == NULL) {
puts("Shell variable TERM not defined!");
exit(1);
}
if (strcmp(cp, "vt100") != 0) {
puts("Terminal type not 'vt100'!");
exit(1);
}
#endif
ttopen();
}

View File

@ -0,0 +1,311 @@
/*
* The routines in this file
* move the cursor around on the screen.
* They compute a new value for the cursor, then
* adjust ".". The display code always updates the
* cursor location, so only moves between lines,
* or functions that adjust the top line in the window
* and invalidate the framing, are hard.
*/
#include <stdio.h>
#include "ed.h"
/*
* Move the cursor to the
* beginning of the current line.
* Trivial.
*/
gotobol(f, n)
{
curwp->w_doto = 0;
return (TRUE);
}
/*
* Move the cursor backwards by
* "n" characters. If "n" is less than
* zero call "forwchar" to actually do the
* move. Otherwise compute the new cursor
* location. Error if you try and move
* out of the buffer. Set the flag if the
* line pointer for dot changes.
*/
backchar(f, n)
register int n;
{
register LINE *lp;
if (n < 0)
return (forwchar(f, -n));
while (n--) {
if (curwp->w_doto == 0) {
if ((lp=lback(curwp->w_dotp)) == curbp->b_linep)
return (FALSE);
curwp->w_dotp = lp;
curwp->w_doto = llength(lp);
curwp->w_flag |= WFMOVE;
} else
curwp->w_doto--;
}
return (TRUE);
}
/*
* Move the cursor to the end
* of the current line. Trivial.
* No errors.
*/
gotoeol(f, n)
{
curwp->w_doto = llength(curwp->w_dotp);
return (TRUE);
}
/*
* Move the cursor forwwards by
* "n" characters. If "n" is less than
* zero call "backchar" to actually do the
* move. Otherwise compute the new cursor
* location, and move ".". Error if you
* try and move off the end of the
* buffer. Set the flag if the line pointer
* for dot changes.
*/
forwchar(f, n)
register int n;
{
if (n < 0)
return (backchar(f, -n));
while (n--) {
if (curwp->w_doto == llength(curwp->w_dotp)) {
if (curwp->w_dotp == curbp->b_linep)
return (FALSE);
curwp->w_dotp = lforw(curwp->w_dotp);
curwp->w_doto = 0;
curwp->w_flag |= WFMOVE;
} else
curwp->w_doto++;
}
return (TRUE);
}
/*
* Goto the beginning of the buffer.
* Massive adjustment of dot. This is considered
* to be hard motion; it really isn't if the original
* value of dot is the same as the new value of dot.
* Normally bound to "M-<".
*/
gotobob(f, n)
{
curwp->w_dotp = lforw(curbp->b_linep);
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* Move to the end of the buffer.
* Dot is always put at the end of the
* file (ZJ). The standard screen code does
* most of the hard parts of update. Bound to
* "M->".
*/
gotoeob(f, n)
{
curwp->w_dotp = curbp->b_linep;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* Move forward by full lines.
* If the number of lines to move is less
* than zero, call the backward line function to
* actually do it. The last command controls how
* the goal column is set. Bound to "C-N". No
* errors are possible.
*/
forwline(f, n)
{
register LINE *dlp;
if (n < 0)
return (backline(f, -n));
if ((lastflag&CFCPCN) == 0) /* Reset goal if last */
curgoal = curcol; /* not C-P or C-N */
thisflag |= CFCPCN;
dlp = curwp->w_dotp;
while (n-- && dlp!=curbp->b_linep)
dlp = lforw(dlp);
curwp->w_dotp = dlp;
curwp->w_doto = getgoal(dlp);
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* This function is like "forwline", but
* goes backwards. The scheme is exactly the same.
* Check for arguments that are less than zero and
* call your alternate. Figure out the new line and
* call "movedot" to perform the motion. No errors
* are possible. Bound to "C-P".
*/
backline(f, n)
{
register LINE *dlp;
if (n < 0)
return (forwline(f, -n));
if ((lastflag&CFCPCN) == 0) /* Reset goal if the */
curgoal = curcol; /* last isn't C-P, C-N */
thisflag |= CFCPCN;
dlp = curwp->w_dotp;
while (n-- && lback(dlp)!=curbp->b_linep)
dlp = lback(dlp);
curwp->w_dotp = dlp;
curwp->w_doto = getgoal(dlp);
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* This routine, given a pointer to
* a LINE, and the current cursor goal
* column, return the best choice for the
* offset. The offset is returned.
* Used by "C-N" and "C-P".
*/
getgoal(dlp)
register LINE *dlp;
{
register int c;
register int col;
register int newcol;
register int dbo;
col = 0;
dbo = 0;
while (dbo != llength(dlp)) {
c = lgetc(dlp, dbo);
newcol = col;
if (c == '\t')
newcol |= 0x07;
else if (c<0x20 || c==0x7F)
++newcol;
++newcol;
if (newcol > curgoal)
break;
col = newcol;
++dbo;
}
return (dbo);
}
/*
* Scroll forward by a specified number
* of lines, or by a full page if no argument.
* Bound to "C-V". The "2" in the arithmetic on
* the window size is the overlap; this value is
* the default overlap value in ITS EMACS.
* Because this zaps the top line in the display
* window, we have to do a hard update.
*/
forwpage(f, n)
register int n;
{
register LINE *lp;
if (f == FALSE) {
n = curwp->w_ntrows - 2; /* Default scroll. */
if (n <= 0) /* Forget the overlap */
n = 1; /* if tiny window. */
} else if (n < 0)
return (backpage(f, -n));
#if CVMVAS
else /* Convert from pages */
n *= curwp->w_ntrows; /* to lines. */
#endif
lp = curwp->w_linep;
while (n-- && lp!=curbp->b_linep)
lp = lforw(lp);
curwp->w_linep = lp;
curwp->w_dotp = lp;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* This command is like "forwpage",
* but it goes backwards. The "2", like above,
* is the overlap between the two windows. The
* value is from the ITS EMACS manual. Bound
* to "M-V". We do a hard update for exactly
* the same reason.
*/
backpage(f, n)
register int n;
{
register LINE *lp;
if (f == FALSE) {
n = curwp->w_ntrows - 2; /* Default scroll. */
if (n <= 0) /* Don't blow up if the */
n = 1; /* window is tiny. */
} else if (n < 0)
return (forwpage(f, -n));
#if CVMVAS
else /* Convert from pages */
n *= curwp->w_ntrows; /* to lines. */
#endif
lp = curwp->w_linep;
while (n-- && lback(lp)!=curbp->b_linep)
lp = lback(lp);
curwp->w_linep = lp;
curwp->w_dotp = lp;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* Set the mark in the current window
* to the value of "." in the window. No errors
* are possible. Bound to "M-.".
*/
setmark(f, n)
{
curwp->w_markp = curwp->w_dotp;
curwp->w_marko = curwp->w_doto;
mlwrite("[Mark set]");
return (TRUE);
}
/*
* Swap the values of "." and "mark" in
* the current window. This is pretty easy, bacause
* all of the hard work gets done by the standard routine
* that moves the mark about. The only possible error is
* "no mark". Bound to "C-X C-X".
*/
swapmark(f, n)
{
register LINE *odotp;
register int odoto;
if (curwp->w_markp == NULL) {
mlwrite("No mark in this window");
return (FALSE);
}
odotp = curwp->w_dotp;
odoto = curwp->w_doto;
curwp->w_dotp = curwp->w_markp;
curwp->w_doto = curwp->w_marko;
curwp->w_markp = odotp;
curwp->w_marko = odoto;
curwp->w_flag |= WFMOVE;
return (TRUE);
}

View File

@ -0,0 +1,362 @@
/*
* Buffer management.
* Some of the functions are internal,
* and some are actually attached to user
* keys. Like everyone else, they set hints
* for the display system.
*/
#include <stdio.h>
#include "ed.h"
/*
* Attach a buffer to a window. The
* values of dot and mark come from the buffer
* if the use count is 0. Otherwise, they come
* from some other window.
*/
usebuffer(f, n)
{
register BUFFER *bp;
register WINDOW *wp;
register int s;
char bufn[NBUFN];
if ((s=mlreply("Use buffer: ", bufn, NBUFN)) != TRUE)
return (s);
if ((bp=bfind(bufn, TRUE, 0)) == NULL)
return (FALSE);
if (--curbp->b_nwnd == 0) { /* Last use. */
curbp->b_dotp = curwp->w_dotp;
curbp->b_doto = curwp->w_doto;
curbp->b_markp = curwp->w_markp;
curbp->b_marko = curwp->w_marko;
}
curbp = bp; /* Switch. */
curwp->w_bufp = bp;
curwp->w_linep = bp->b_linep; /* For macros, ignored. */
curwp->w_flag |= WFMODE|WFFORCE|WFHARD; /* Quite nasty. */
if (bp->b_nwnd++ == 0) { /* First use. */
curwp->w_dotp = bp->b_dotp;
curwp->w_doto = bp->b_doto;
curwp->w_markp = bp->b_markp;
curwp->w_marko = bp->b_marko;
return (TRUE);
}
wp = wheadp; /* Look for old. */
while (wp != NULL) {
if (wp!=curwp && wp->w_bufp==bp) {
curwp->w_dotp = wp->w_dotp;
curwp->w_doto = wp->w_doto;
curwp->w_markp = wp->w_markp;
curwp->w_marko = wp->w_marko;
break;
}
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* Dispose of a buffer, by name.
* Ask for the name. Look it up (don't get too
* upset if it isn't there at all!). Get quite upset
* if the buffer is being displayed. Clear the buffer (ask
* if the buffer has been changed). Then free the header
* line and the buffer header. Bound to "C-X K".
*/
killbuffer(f, n)
{
register BUFFER *bp;
register BUFFER *bp1;
register BUFFER *bp2;
register int s;
char bufn[NBUFN];
if ((s=mlreply("Kill buffer: ", bufn, NBUFN)) != TRUE)
return (s);
if ((bp=bfind(bufn, FALSE, 0)) == NULL) /* Easy if unknown. */
return (TRUE);
if (bp->b_nwnd != 0) { /* Error if on screen. */
mlwrite("Buffer is being displayed");
return (FALSE);
}
if ((s=bclear(bp)) != TRUE) /* Blow text away. */
return (s);
free((char *) bp->b_linep); /* Release header line. */
bp1 = NULL; /* Find the header. */
bp2 = bheadp;
while (bp2 != bp) {
bp1 = bp2;
bp2 = bp2->b_bufp;
}
bp2 = bp2->b_bufp; /* Next one in chain. */
if (bp1 == NULL) /* Unlink it. */
bheadp = bp2;
else
bp1->b_bufp = bp2;
free((char *) bp); /* Release buffer block */
return (TRUE);
}
/*
* List all of the active
* buffers. First update the special
* buffer that holds the list. Next make
* sure at least 1 window is displaying the
* buffer list, splitting the screen if this
* is what it takes. Lastly, repaint all of
* the windows that are displaying the
* list. Bound to "C-X C-B".
*/
listbuffers(f, n)
{
register WINDOW *wp;
register BUFFER *bp;
register int s;
if ((s=makelist()) != TRUE)
return (s);
if (blistp->b_nwnd == 0) { /* Not on screen yet. */
if ((wp=wpopup()) == NULL)
return (FALSE);
bp = wp->w_bufp;
if (--bp->b_nwnd == 0) {
bp->b_dotp = wp->w_dotp;
bp->b_doto = wp->w_doto;
bp->b_markp = wp->w_markp;
bp->b_marko = wp->w_marko;
}
wp->w_bufp = blistp;
++blistp->b_nwnd;
}
wp = wheadp;
while (wp != NULL) {
if (wp->w_bufp == blistp) {
wp->w_linep = lforw(blistp->b_linep);
wp->w_dotp = lforw(blistp->b_linep);
wp->w_doto = 0;
wp->w_markp = NULL;
wp->w_marko = 0;
wp->w_flag |= WFMODE|WFHARD;
}
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* This routine rebuilds the
* text in the special secret buffer
* that holds the buffer list. It is called
* by the list buffers command. Return TRUE
* if everything works. Return FALSE if there
* is an error (if there is no memory).
*/
makelist()
{
register char *cp1;
register char *cp2;
register int c;
register BUFFER *bp;
register LINE *lp;
register int nbytes;
register int s;
register int type;
char b[6+1];
char line[128];
blistp->b_flag &= ~BFCHG; /* Don't complain! */
if ((s=bclear(blistp)) != TRUE) /* Blow old text away */
return (s);
strcpy(blistp->b_fname, "");
if (addline("C Size Buffer File") == FALSE
|| addline("- ---- ------ ----") == FALSE)
return (FALSE);
bp = bheadp; /* For all buffers */
while (bp != NULL) {
if ((bp->b_flag&BFTEMP) != 0) { /* Skip magic ones. */
bp = bp->b_bufp;
continue;
}
cp1 = &line[0]; /* Start at left edge */
if ((bp->b_flag&BFCHG) != 0) /* "*" if changed */
*cp1++ = '*';
else
*cp1++ = ' ';
*cp1++ = ' '; /* Gap. */
nbytes = 0; /* Count bytes in buf. */
lp = lforw(bp->b_linep);
while (lp != bp->b_linep) {
nbytes += llength(lp)+1;
lp = lforw(lp);
}
itoa(b, 6, nbytes); /* 6 digit buffer size. */
cp2 = &b[0];
while ((c = *cp2++) != 0)
*cp1++ = c;
*cp1++ = ' '; /* Gap. */
cp2 = &bp->b_bname[0]; /* Buffer name */
while ((c = *cp2++) != 0)
*cp1++ = c;
cp2 = &bp->b_fname[0]; /* File name */
if (*cp2 != 0) {
while (cp1 < &line[1+1+6+1+NBUFN+1])
*cp1++ = ' ';
while ((c = *cp2++) != 0) {
if (cp1 < &line[128-1])
*cp1++ = c;
}
}
*cp1 = 0; /* Add to the buffer. */
if (addline(line) == FALSE)
return (FALSE);
bp = bp->b_bufp;
}
return (TRUE); /* All done */
}
itoa(buf, width, num)
register char buf[];
register int width;
register int num;
{
buf[width] = 0; /* End of string. */
while (num >= 10) { /* Conditional digits. */
buf[--width] = (num%10) + '0';
num /= 10;
}
buf[--width] = num + '0'; /* Always 1 digit. */
while (width != 0) /* Pad with blanks. */
buf[--width] = ' ';
}
/*
* The argument "text" points to
* a string. Append this line to the
* buffer list buffer. Handcraft the EOL
* on the end. Return TRUE if it worked and
* FALSE if you ran out of room.
*/
addline(text)
char *text;
{
register LINE *lp;
register int i;
register int ntext;
ntext = strlen(text);
if ((lp=lalloc(ntext)) == NULL)
return (FALSE);
for (i=0; i<ntext; ++i)
lputc(lp, i, text[i]);
blistp->b_linep->l_bp->l_fp = lp; /* Hook onto the end */
lp->l_bp = blistp->b_linep->l_bp;
blistp->b_linep->l_bp = lp;
lp->l_fp = blistp->b_linep;
if (blistp->b_dotp == blistp->b_linep) /* If "." is at the end */
blistp->b_dotp = lp; /* move it to new line */
return (TRUE);
}
/*
* Look through the list of
* buffers. Return TRUE if there
* are any changed buffers. Buffers
* that hold magic internal stuff are
* not considered; who cares if the
* list of buffer names is hacked.
* Return FALSE if no buffers
* have been changed.
*/
anycb()
{
register BUFFER *bp;
bp = bheadp;
while (bp != NULL) {
if ((bp->b_flag&BFTEMP)==0 && (bp->b_flag&BFCHG)!=0)
return (TRUE);
bp = bp->b_bufp;
}
return (FALSE);
}
/*
* Find a buffer, by name. Return a pointer
* to the BUFFER structure associated with it. If
* the named buffer is found, but is a TEMP buffer (like
* the buffer list) conplain. If the buffer is not found
* and the "cflag" is TRUE, create it. The "bflag" is
* the settings for the flags in in buffer.
*/
BUFFER *
bfind(bname, cflag, bflag)
register char *bname;
{
register BUFFER *bp;
register LINE *lp;
bp = bheadp;
while (bp != NULL) {
if (strcmp(bname, bp->b_bname) == 0) {
if ((bp->b_flag&BFTEMP) != 0) {
mlwrite("Cannot select builtin buffer");
return (NULL);
}
return (bp);
}
bp = bp->b_bufp;
}
if (cflag != FALSE) {
if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL)
return (NULL);
if ((lp=lalloc(0)) == NULL) {
free((char *) bp);
return (NULL);
}
bp->b_bufp = bheadp;
bheadp = bp;
bp->b_dotp = lp;
bp->b_doto = 0;
bp->b_markp = NULL;
bp->b_marko = 0;
bp->b_flag = bflag;
bp->b_nwnd = 0;
bp->b_linep = lp;
strcpy(bp->b_fname, "");
strcpy(bp->b_bname, bname);
lp->l_fp = lp;
lp->l_bp = lp;
}
return (bp);
}
/*
* This routine blows away all of the text
* in a buffer. If the buffer is marked as changed
* then we ask if it is ok to blow it away; this is
* to save the user the grief of losing text. The
* window chain is nearly always wrong if this gets
* called; the caller must arrange for the updates
* that are required. Return TRUE if everything
* looks good.
*/
bclear(bp)
register BUFFER *bp;
{
register LINE *lp;
register int s;
if ((bp->b_flag&BFTEMP) == 0 /* Not scratch buffer. */
&& (bp->b_flag&BFCHG) != 0 /* Something changed */
&& (s=mlyesno("Discard changes")) != TRUE)
return (s);
bp->b_flag &= ~BFCHG; /* Not changed */
while ((lp=lforw(bp->b_linep)) != bp->b_linep)
lfree(lp);
bp->b_dotp = bp->b_linep; /* Fix "." */
bp->b_doto = 0;
bp->b_markp = NULL; /* Invalidate "mark" */
bp->b_marko = 0;
return (TRUE);
}

View File

@ -0,0 +1,722 @@
/*
* The functions in this file
* handle redisplay. There are two halves,
* the ones that update the virtual display
* screen, and the ones that make the physical
* display screen the same as the virtual
* display screen. These functions use hints
* that are left in the windows by the
* commands.
*/
#include <stdio.h>
#include "ed.h"
#define WFDEBUG 0 /* Window flag debug. */
typedef struct VIDEO {
short v_flag; /* Flags */
char v_text[]; /* Screen data. */
} VIDEO;
#define VFCHG 0x0001 /* Changed. */
int sgarbf = TRUE; /* TRUE if screen is garbage */
int mpresf = FALSE; /* TRUE if message in last line */
int vtrow = 0; /* Row location of SW cursor */
int vtcol = 0; /* Column location of SW cursor */
int ttrow = HUGE; /* Row location of HW cursor */
int ttcol = HUGE; /* Column location of HW cursor */
VIDEO **vscreen; /* Virtual screen. */
VIDEO **pscreen; /* Physical screen. */
/*
* Initialize the data structures used
* by the display code. The edge vectors used
* to access the screens are set up. The operating
* system's terminal I/O channel is set up. All the
* other things get initialized at compile time.
* The original window has "WFCHG" set, so that it
* will get completely redrawn on the first
* call to "update".
*/
vtinit()
{
register int i;
register VIDEO *vp;
(*term.t_open)();
vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
if (vscreen == NULL)
abort();
pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
if (pscreen == NULL)
abort();
for (i=0; i<term.t_nrow; ++i) {
vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
if (vp == NULL)
abort();
vscreen[i] = vp;
vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
if (vp == NULL)
abort();
pscreen[i] = vp;
}
}
/*
* Clean up the virtual terminal
* system, in anticipation for a return to the
* operating system. Move down to the last line and
* clear it out (the next system prompt will be
* written in the line). Shut down the channel
* to the terminal.
*/
vttidy()
{
movecursor(term.t_nrow, 0);
(*term.t_eeol)();
(*term.t_close)();
}
/*
* Set the virtual cursor to
* the specified row and column on the
* virtual screen. There is no checking for
* nonsense values; this might be a good
* idea during the early stages.
*/
vtmove(row, col)
{
vtrow = row;
vtcol = col;
}
/*
* Write a character to the
* virtual screen. The virtual row and
* column are updated. If the line is too
* long put a "$" in the last column.
* This routine only puts printing characters
* into the virtual terminal buffers.
* Only column overflow is checked.
*/
vtputc(c)
register int c;
{
register VIDEO *vp;
vp = vscreen[vtrow];
if (vtcol >= term.t_ncol)
vp->v_text[term.t_ncol-1] = '$';
else if (c == '\t') {
do {
vtputc(' ');
} while ((vtcol&0x07) != 0);
} else if (c<0x20 || c==0x7F) {
vtputc('^');
vtputc(c ^ 0x40);
} else
vp->v_text[vtcol++] = c;
}
/*
* Erase from the end of the
* software cursor to the end of the
* line on which the software cursor is
* located.
*/
vteeol()
{
register VIDEO *vp;
vp = vscreen[vtrow];
while (vtcol < term.t_ncol)
vp->v_text[vtcol++] = ' ';
}
/*
* Make sure that the display is
* right. This is a three part process. First,
* scan through all of the windows looking for dirty
* ones. Check the framing, and refresh the screen.
* Second, make sure that "currow" and "curcol" are
* correct for the current window. Third, make the
* virtual and physical screens the same.
*/
update()
{
register LINE *lp;
register WINDOW *wp;
register VIDEO *vp1;
register VIDEO *vp2;
register int i;
register int j;
register int c;
wp = wheadp;
while (wp != NULL) {
/* Look at any window with update flags set on. */
if (wp->w_flag != 0) {
/* If not force reframe, check the framing. */
if ((wp->w_flag&WFFORCE) == 0) {
lp = wp->w_linep;
for (i=0; i<wp->w_ntrows; ++i) {
if (lp == wp->w_dotp)
goto out;
if (lp == wp->w_bufp->b_linep)
break;
lp = lforw(lp);
}
}
/* Not acceptable, better compute a new value */
/* for the line at the top of the window. Then */
/* set the "WFHARD" flag to force full redraw. */
i = wp->w_force;
if (i > 0) {
--i;
if (i >= wp->w_ntrows)
i = wp->w_ntrows-1;
} else if (i < 0) {
i += wp->w_ntrows;
if (i < 0)
i = 0;
} else
i = wp->w_ntrows/2;
lp = wp->w_dotp;
while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) {
--i;
lp = lback(lp);
}
wp->w_linep = lp;
wp->w_flag |= WFHARD; /* Force full. */
out:
/* Try to use reduced update. Mode line update */
/* has its own special flag. The fast update is */
/* used if the only thing to do is within the */
/* line editing. */
lp = wp->w_linep;
i = wp->w_toprow;
if ((wp->w_flag&~WFMODE) == WFEDIT) {
while (lp != wp->w_dotp) {
++i;
lp = lforw(lp);
}
vscreen[i]->v_flag |= VFCHG;
vtmove(i, 0);
for (j=0; j<llength(lp); ++j)
vtputc(lgetc(lp, j));
vteeol();
} else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0) {
while (i < wp->w_toprow+wp->w_ntrows) {
vscreen[i]->v_flag |= VFCHG;
vtmove(i, 0);
if (lp != wp->w_bufp->b_linep) {
for (j=0; j<llength(lp); ++j)
vtputc(lgetc(lp, j));
lp = lforw(lp);
}
vteeol();
++i;
}
}
#if !WFDEBUG
if ((wp->w_flag&WFMODE) != 0)
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
#endif
}
#if WFDEBUG
modeline(wp);
wp->w_flag = 0;
wp->w_force = 0;
#endif
wp = wp->w_wndp;
}
/* Always recompute the row and column number of the hardware */
/* cursor. This is the only update for simple moves. */
lp = curwp->w_linep;
currow = curwp->w_toprow;
while (lp != curwp->w_dotp) {
++currow;
lp = lforw(lp);
}
curcol = 0;
i = 0;
while (i < curwp->w_doto) {
c = lgetc(lp, i++);
if (c == '\t')
curcol |= 0x07;
else if (c<0x20 || c==0x7F)
++curcol;
++curcol;
}
if (curcol >= term.t_ncol) /* Long line. */
curcol = term.t_ncol-1;
/* Special hacking if the screen is garbage. Clear the hardware */
/* screen, and update your copy to agree with it. Set all the */
/* virtual screen change bits, to force a full update. */
if (sgarbf != FALSE) {
for (i=0; i<term.t_nrow; ++i) {
vscreen[i]->v_flag |= VFCHG;
vp1 = pscreen[i];
for (j=0; j<term.t_ncol; ++j)
vp1->v_text[j] = ' ';
}
movecursor(0, 0); /* Erase the screen. */
(*term.t_eeop)();
sgarbf = FALSE; /* Erase-page clears */
mpresf = FALSE; /* the message area. */
}
/* Make sure that the physical and virtual displays agree. */
/* Unlike before, the "updateline" code is only called with a */
/* line that has been updated for sure. */
for (i=0; i<term.t_nrow; ++i) {
vp1 = vscreen[i];
if ((vp1->v_flag&VFCHG) != 0) {
vp1->v_flag &= ~VFCHG;
vp2 = pscreen[i];
updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
}
}
/* Finally, update the hardware cursor and flush out buffers. */
movecursor(currow, curcol);
(*term.t_flush)();
}
/*
* Update a single line. This
* does not know how to use insert
* or delete character sequences; we are
* using VT52 functionality. Update the physical
* row and column variables. It does try an
* exploit erase to end of line. The RAINBOW version
* of this routine uses fast video.
*/
updateline(row, vline, pline)
char vline[];
char pline[];
{
#if RAINBOW
register char *cp1;
register char *cp2;
register int nch;
cp1 = &vline[0]; /* Use fast video. */
cp2 = &pline[0];
putline(row+1, 1, cp1);
nch = term.t_ncol;
do {
*cp2 = *cp1;
++cp2;
++cp1;
} while (--nch);
#else
register char *cp1;
register char *cp2;
register char *cp3;
register char *cp4;
register char *cp5;
register int nbflag;
cp1 = &vline[0]; /* Compute left match. */
cp2 = &pline[0];
while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0]) {
++cp1;
++cp2;
}
/* This can still happen, even though we only call this routine */
/* on changed lines. A hard update is always done when a line */
/* splits, a massive change is done, or a buffer is displayed */
/* twice. This optimizes out most of the excess updating. A lot */
/* of computes are used, but these tend to be hard operations */
/* that do a lot of update, so I don't really care. */
if (cp1 == &vline[term.t_ncol]) /* All equal. */
return;
nbflag = FALSE;
cp3 = &vline[term.t_ncol]; /* Compute right match. */
cp4 = &pline[term.t_ncol];
while (cp3[-1] == cp4[-1]) {
--cp3;
--cp4;
if (cp3[0] != ' ') /* Note if any nonblank */
nbflag = TRUE; /* in right match. */
}
cp5 = cp3;
if (nbflag == FALSE) { /* Erase to EOL ? */
while (cp5!=cp1 && cp5[-1]==' ')
--cp5;
if (cp3-cp5 <= 3) /* Use only if erase is */
cp5 = cp3; /* fewer characters. */
}
movecursor(row, cp1-&vline[0]); /* Go to start of line. */
while (cp1 != cp5) { /* Ordinary. */
(*term.t_putchar)(*cp1);
++ttcol;
*cp2++ = *cp1++;
}
if (cp5 != cp3) { /* Erase. */
(*term.t_eeol)();
while (cp1 != cp3)
*cp2++ = *cp1++;
}
#endif
}
/*
* Redisplay the mode line for
* the window pointed to by the "wp".
* This is the only routine that has any idea
* of how the modeline is formatted. You can
* change the modeline format by hacking at
* this routine. Called by "update" any time
* there is a dirty window.
*/
modeline(wp)
register WINDOW *wp;
{
register char *cp;
register int c;
register int n;
register BUFFER *bp;
n = wp->w_toprow+wp->w_ntrows; /* Location. */
vscreen[n]->v_flag |= VFCHG; /* Redraw next time. */
vtmove(n, 0); /* Seek to right line. */
vtputc('-');
bp = wp->w_bufp;
if ((bp->b_flag&BFCHG) != 0) /* "*" if changed. */
vtputc('*');
else
vtputc('-');
n = 2;
cp = " MicroEMACS -- "; /* Buffer name. */
while ((c = *cp++) != 0) {
vtputc(c);
++n;
}
cp = &bp->b_bname[0];
while ((c = *cp++) != 0) {
vtputc(c);
++n;
}
vtputc(' ');
++n;
if (bp->b_fname[0] != 0) { /* File name. */
cp = "-- File: ";
while ((c = *cp++) != 0) {
vtputc(c);
++n;
}
cp = &bp->b_fname[0];
while ((c = *cp++) != 0) {
vtputc(c);
++n;
}
vtputc(' ');
++n;
}
#if WFDEBUG
vtputc('-');
vtputc((wp->w_flag&WFMODE)!=0 ? 'M' : '-');
vtputc((wp->w_flag&WFHARD)!=0 ? 'H' : '-');
vtputc((wp->w_flag&WFEDIT)!=0 ? 'E' : '-');
vtputc((wp->w_flag&WFMOVE)!=0 ? 'V' : '-');
vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
n += 6;
#endif
while (n < term.t_ncol) { /* Pad to full width. */
vtputc('-');
++n;
}
}
/*
* Send a command to the terminal
* to move the hardware cursor to row "row"
* and column "col". The row and column arguments
* are origin 0. Optimize out random calls.
* Update "ttrow" and "ttcol".
*/
movecursor(row, col)
{
if (row!=ttrow || col!=ttcol) {
ttrow = row;
ttcol = col;
(*term.t_move)(row, col);
}
}
/*
* Erase the message line.
* This is a special routine because
* the message line is not considered to be
* part of the virtual screen. It always works
* immediately; the terminal buffer is flushed
* via a call to the flusher.
*/
mlerase()
{
movecursor(term.t_nrow, 0);
(*term.t_eeol)();
(*term.t_flush)();
mpresf = FALSE;
}
/*
* Ask a yes or no question in
* the message line. Return either TRUE,
* FALSE, or ABORT. The ABORT status is returned
* if the user bumps out of the question with
* a ^G. Used any time a confirmation is
* required.
*/
mlyesno(prompt)
char *prompt;
{
register int s;
char buf[64];
for (;;) {
strcpy(buf, prompt);
strcat(buf, " [y/n]? ");
s = mlreply(buf, buf, sizeof(buf));
if (s == ABORT)
return (ABORT);
if (s != FALSE) {
if (buf[0]=='y' || buf[0]=='Y')
return (TRUE);
if (buf[0]=='n' || buf[0]=='N')
return (FALSE);
}
}
}
/*
* Write a prompt into the message
* line, then read back a response. Keep
* track of the physical position of the cursor.
* If we are in a keyboard macro throw the prompt
* away, and return the remembered response. This
* lets macros run at full speed. The reply is
* always terminated by a carriage return. Handle
* erase, kill, and abort keys.
*/
mlreply(prompt, buf, nbuf)
char *prompt;
char *buf;
{
register int cpos;
register int i;
register int c;
cpos = 0;
if (kbdmop != NULL) {
while ((c = *kbdmop++) != '\0')
buf[cpos++] = c;
buf[cpos] = 0;
if (buf[0] == 0)
return (FALSE);
return (TRUE);
}
mlwrite(prompt);
for (;;) {
c = (*term.t_getchar)();
switch (c) {
case 0x0D: /* Return, end of line */
buf[cpos++] = 0;
if (kbdmip != NULL) {
if (kbdmip+cpos > &kbdm[NKBDM-3]) {
ctrlg(FALSE, 0);
(*term.t_flush)();
return (ABORT);
}
for (i=0; i<cpos; ++i)
*kbdmip++ = buf[i];
}
(*term.t_putchar)('\r');
ttcol = 0;
(*term.t_flush)();
if (buf[0] == 0)
return (FALSE);
return (TRUE);
case 0x07: /* Bell, abort */
(*term.t_putchar)('^');
(*term.t_putchar)('G');
ttcol += 2;
ctrlg(FALSE, 0);
(*term.t_flush)();
return (ABORT);
case 0x7F: /* Rubout, erase */
case 0x08: /* Backspace, erase */
if (cpos != 0) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
if (buf[--cpos] < 0x20) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
}
(*term.t_flush)();
}
break;
case 0x15: /* C-U, kill */
while (cpos != 0) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
if (buf[--cpos] < 0x20) {
(*term.t_putchar)('\b');
(*term.t_putchar)(' ');
(*term.t_putchar)('\b');
--ttcol;
}
}
(*term.t_flush)();
break;
default:
if (cpos < nbuf-1) {
buf[cpos++] = c;
if (c < ' ') {
(*term.t_putchar)('^');
++ttcol;
c ^= 0x40;
}
(*term.t_putchar)(c);
++ttcol;
(*term.t_flush)();
}
}
}
}
/*
* Write a message into the message
* line. Keep track of the physical cursor
* position. A small class of printf like format
* items is handled. Assumes the stack grows
* down; this assumption is made by the "++"
* in the argument scan loop. Set the "message
* line" flag TRUE.
*/
mlwrite(fmt, arg)
char *fmt;
{
register int c;
register char *ap;
movecursor(term.t_nrow, 0);
ap = (char *) &arg;
while ((c = *fmt++) != 0) {
if (c != '%') {
(*term.t_putchar)(c);
++ttcol;
} else {
c = *fmt++;
switch (c) {
case 'd':
mlputi(*(int *)ap, 10);
ap += sizeof(int);
break;
case 'o':
mlputi(*(int *)ap, 8);
ap += sizeof(int);
break;
case 'x':
mlputi(*(int *)ap, 16);
ap += sizeof(int);
break;
case 'D':
mlputli(*(long *)ap, 10);
ap += sizeof(long);
break;
case 's':
mlputs(*(char **)ap);
ap += sizeof(char *);
break;
default:
(*term.t_putchar)(c);
++ttcol;
}
}
}
(*term.t_eeol)();
(*term.t_flush)();
mpresf = TRUE;
}
/*
* Write out a string.
* Update the physical cursor position.
* This assumes that the characters in the
* string all have width "1"; if this is
* not the case things will get screwed up
* a little.
*/
mlputs(s)
register char *s;
{
register int c;
while ((c = *s++) != 0) {
(*term.t_putchar)(c);
++ttcol;
}
}
/*
* Write out an integer, in
* the specified radix. Update the physical
* cursor position. This will not handle any
* negative numbers; maybe it should.
*/
mlputi(i, r)
{
register int q;
static char hexdigits[] = "0123456789ABCDEF";
if (i < 0) {
i = -i;
(*term.t_putchar)('-');
}
q = i/r;
if (q != 0)
mlputi(q, r);
(*term.t_putchar)(hexdigits[i%r]);
++ttcol;
}
/*
* do the same except as a long integer.
*/
mlputli(l, r)
long l;
{
register long q;
if (l < 0) {
l = -l;
(*term.t_putchar)('-');
}
q = l/r;
if (q != 0)
mlputli(q, r);
(*term.t_putchar)((int)(l%r)+'0');
++ttcol;
}

View File

@ -0,0 +1,193 @@
/*
* This file is the general header file for
* all parts of the MicroEMACS display editor. It contains
* definitions used by everyone, and it contains the stuff
* you have to edit to create a version of the editor for
* a specific operating system and terminal.
*/
#define V7 0 /* V7 UN*X or Coherent */
#define VMS 0 /* VAX/VMS */
#define CPM 0 /* CP/M-86 */
#define MSDOS 1 /* MS-DOS */
#define ANSI 1
#define VT52 0 /* VT52 terminal (Zenith). */
#define VT100 0 /* Handle VT100 style keypad. */
#define LK201 0 /* Handle LK201 style keypad. */
#define RAINBOW 0 /* Use Rainbow fast video. */
#define TERMCAP 0 /* Use TERMCAP */
#define CVMVAS 1 /* C-V, M-V arg. in screens. */
#define NFILEN 80 /* # of bytes, file name */
#define NBUFN 16 /* # of bytes, buffer name */
#define NLINE 256 /* # of bytes, line */
#define NKBDM 256 /* # of strokes, keyboard macro */
#define NPAT 80 /* # of bytes, pattern */
#define HUGE 1000 /* Huge number */
#define AGRAVE 0x60 /* M- prefix, Grave (LK201) */
#define METACH 0x1B /* M- prefix, Control-[, ESC */
#define CTMECH 0x1C /* C-M- prefix, Control-\ */
#define EXITCH 0x1D /* Exit level, Control-] */
#define CTRLCH 0x1E /* C- prefix, Control-^ */
#define HELPCH 0x1F /* Help key, Control-_ */
#define CTRL 0x0100 /* Control flag, or'ed in */
#define META 0x0200 /* Meta flag, or'ed in */
#define CTLX 0x0400 /* ^X flag, or'ed in */
#define FALSE 0 /* False, no, bad, etc. */
#define TRUE 1 /* True, yes, good, etc. */
#define ABORT 2 /* Death, ^G, abort, etc. */
#define FIOSUC 0 /* File I/O, success. */
#define FIOFNF 1 /* File I/O, file not found. */
#define FIOEOF 2 /* File I/O, end of file. */
#define FIOERR 3 /* File I/O, error. */
#define CFCPCN 0x0001 /* Last command was C-P, C-N */
#define CFKILL 0x0002 /* Last command was a kill */
/*
* There is a window structure allocated for
* every active display window. The windows are kept in a
* big list, in top to bottom screen order, with the listhead at
* "wheadp". Each window contains its own values of dot and mark.
* The flag field contains some bits that are set by commands
* to guide redisplay; although this is a bit of a compromise in
* terms of decoupling, the full blown redisplay is just too
* expensive to run for every input character.
*/
typedef struct WINDOW {
struct WINDOW *w_wndp; /* Next window */
struct BUFFER *w_bufp; /* Buffer displayed in window */
struct LINE *w_linep; /* Top line in the window */
struct LINE *w_dotp; /* Line containing "." */
short w_doto; /* Byte offset for "." */
struct LINE *w_markp; /* Line containing "mark" */
short w_marko; /* Byte offset for "mark" */
char w_toprow; /* Origin 0 top row of window */
char w_ntrows; /* # of rows of text in window */
char w_force; /* If NZ, forcing row. */
char w_flag; /* Flags. */
} WINDOW;
#define WFFORCE 0x01 /* Window needs forced reframe */
#define WFMOVE 0x02 /* Movement from line to line */
#define WFEDIT 0x04 /* Editing within a line */
#define WFHARD 0x08 /* Better to a full display */
#define WFMODE 0x10 /* Update mode line. */
/*
* Text is kept in buffers. A buffer header, described
* below, exists for every buffer in the system. The buffers are
* kept in a big list, so that commands that search for a buffer by
* name can find the buffer header. There is a safe store for the
* dot and mark in the header, but this is only valid if the buffer
* is not being displayed (that is, if "b_nwnd" is 0). The text for
* the buffer is kept in a circularly linked list of lines, with
* a pointer to the header line in "b_linep".
*/
typedef struct BUFFER {
struct BUFFER *b_bufp; /* Link to next BUFFER */
struct LINE *b_dotp; /* Link to "." LINE structure */
short b_doto; /* Offset of "." in above LINE */
struct LINE *b_markp; /* The same as the above two, */
short b_marko; /* but for the "mark" */
struct LINE *b_linep; /* Link to the header LINE */
char b_nwnd; /* Count of windows on buffer */
char b_flag; /* Flags */
char b_fname[NFILEN]; /* File name */
char b_bname[NBUFN]; /* Buffer name */
} BUFFER;
#define BFTEMP 0x01 /* Internal temporary buffer */
#define BFCHG 0x02 /* Changed since last write */
/*
* The starting position of a
* region, and the size of the region in
* characters, is kept in a region structure.
* Used by the region commands.
*/
typedef struct {
struct LINE *r_linep; /* Origin LINE address. */
short r_offset; /* Origin LINE offset. */
short r_size; /* Length in characters. */
} REGION;
/*
* All text is kept in circularly linked
* lists of "LINE" structures. These begin at the
* header line (which is the blank line beyond the
* end of the buffer). This line is pointed to by
* the "BUFFER". Each line contains a the number of
* bytes in the line (the "used" size), the size
* of the text array, and the text. The end of line
* is not stored as a byte; it's implied. Future
* additions will include update hints, and a
* list of marks into the line.
*/
typedef struct LINE {
struct LINE *l_fp; /* Link to the next line */
struct LINE *l_bp; /* Link to the previous line */
short l_size; /* Allocated size */
short l_used; /* Used size */
char l_text[]; /* A bunch of characters. */
} LINE;
#define lforw(lp) ((lp)->l_fp)
#define lback(lp) ((lp)->l_bp)
#define lgetc(lp, n) ((lp)->l_text[(n)]&0xFF)
#define lputc(lp, n, c) ((lp)->l_text[(n)]=(c))
#define llength(lp) ((lp)->l_used)
/*
* The editor communicates with the display
* using a high level interface. A "TERM" structure
* holds useful variables, and indirect pointers to
* routines that do useful operations. The low level get
* and put routines are here too. This lets a terminal,
* in addition to having non standard commands, have
* funny get and put character code too. The calls
* might get changed to "termp->t_field" style in
* the future, to make it possible to run more than
* one terminal type.
*/
typedef struct {
short t_nrow; /* Number of rows. */
short t_ncol; /* Number of columns. */
int (*t_open)(); /* Open terminal at the start. */
int (*t_close)(); /* Close terminal at end. */
int (*t_getchar)(); /* Get character from keyboard. */
int (*t_putchar)(); /* Put character to display. */
int (*t_flush)(); /* Flush output buffers. */
int (*t_move)(); /* Move the cursor, origin 0. */
int (*t_eeol)(); /* Erase to end of line. */
int (*t_eeop)(); /* Erase to end of page. */
int (*t_beep)(); /* Beep. */
} TERM;
extern int fillcol; /* Fill column */
extern int currow; /* Cursor row */
extern int curcol; /* Cursor column */
extern int thisflag; /* Flags, this command */
extern int lastflag; /* Flags, last command */
extern int curgoal; /* Goal for C-P, C-N */
extern int mpresf; /* Stuff in message line */
extern int sgarbf; /* State of screen unknown */
extern WINDOW *curwp; /* Current window */
extern BUFFER *curbp; /* Current buffer */
extern WINDOW *wheadp; /* Head of list of windows */
extern BUFFER *bheadp; /* Head of list of buffers */
extern BUFFER *blistp; /* Buffer for C-X C-B */
extern short kbdm[]; /* Holds kayboard macro data */
extern short *kbdmip; /* Input pointer for above */
extern short *kbdmop; /* Output pointer for above */
extern char pat[]; /* Search pattern */
extern TERM term; /* Terminal information. */
extern BUFFER *bfind(); /* Lookup a buffer by name */
extern WINDOW *wpopup(); /* Pop up window creation */
extern LINE *lalloc(); /* Allocate a line */

View File

@ -0,0 +1,353 @@
/*
* The routines in this file
* handle the reading and writing of
* disk files. All of details about the
* reading and writing of the disk are
* in "fileio.c".
*/
#include <stdio.h>
#include "ed.h"
/*
* Read a file into the current
* buffer. This is really easy; all you do it
* find the name of the file, and call the standard
* "read a file into the current buffer" code.
* Bound to "C-X C-R".
*/
fileread(f, n)
{
register int s;
char fname[NFILEN];
if ((s=mlreply("Read file: ", fname, NFILEN)) != TRUE)
return (s);
return (readin(fname));
}
/*
* Select a file for editing.
* Look around to see if you can find the
* fine in another buffer; if you can find it
* just switch to the buffer. If you cannot find
* the file, create a new buffer, read in the
* text, and switch to the new buffer.
* Bound to C-X C-V.
*/
filevisit(f, n)
{
register BUFFER *bp;
register WINDOW *wp;
register LINE *lp;
register int i;
register int s;
char bname[NBUFN];
char fname[NFILEN];
if ((s=mlreply("Visit file: ", fname, NFILEN)) != TRUE)
return (s);
for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
if ((bp->b_flag&BFTEMP)==0 && strcmp(bp->b_fname, fname)==0) {
if (--curbp->b_nwnd == 0) {
curbp->b_dotp = curwp->w_dotp;
curbp->b_doto = curwp->w_doto;
curbp->b_markp = curwp->w_markp;
curbp->b_marko = curwp->w_marko;
}
curbp = bp;
curwp->w_bufp = bp;
if (bp->b_nwnd++ == 0) {
curwp->w_dotp = bp->b_dotp;
curwp->w_doto = bp->b_doto;
curwp->w_markp = bp->b_markp;
curwp->w_marko = bp->b_marko;
} else {
wp = wheadp;
while (wp != NULL) {
if (wp!=curwp && wp->w_bufp==bp) {
curwp->w_dotp = wp->w_dotp;
curwp->w_doto = wp->w_doto;
curwp->w_markp = wp->w_markp;
curwp->w_marko = wp->w_marko;
break;
}
wp = wp->w_wndp;
}
}
lp = curwp->w_dotp;
i = curwp->w_ntrows/2;
while (i-- && lback(lp)!=curbp->b_linep)
lp = lback(lp);
curwp->w_linep = lp;
curwp->w_flag |= WFMODE|WFHARD;
mlwrite("[Old buffer]");
return (TRUE);
}
}
makename(bname, fname); /* New buffer name. */
while ((bp=bfind(bname, FALSE, 0)) != NULL) {
s = mlreply("Buffer name: ", bname, NBUFN);
if (s == ABORT) /* ^G to just quit */
return (s);
if (s == FALSE) { /* CR to clobber it */
makename(bname, fname);
break;
}
}
if (bp==NULL && (bp=bfind(bname, TRUE, 0))==NULL) {
mlwrite("Cannot create buffer");
return (FALSE);
}
if (--curbp->b_nwnd == 0) { /* Undisplay. */
curbp->b_dotp = curwp->w_dotp;
curbp->b_doto = curwp->w_doto;
curbp->b_markp = curwp->w_markp;
curbp->b_marko = curwp->w_marko;
}
curbp = bp; /* Switch to it. */
curwp->w_bufp = bp;
curbp->b_nwnd++;
return (readin(fname)); /* Read it in. */
}
/*
* Read file "fname" into the current
* buffer, blowing away any text found there. Called
* by both the read and visit commands. Return the final
* status of the read. Also called by the mainline,
* to read in a file specified on the command line as
* an argument.
*/
readin(fname)
char fname[];
{
register LINE *lp1;
register LINE *lp2;
register int i;
register WINDOW *wp;
register BUFFER *bp;
register int s;
register int nbytes;
register int nline;
char line[NLINE];
bp = curbp; /* Cheap. */
if ((s=bclear(bp)) != TRUE) /* Might be old. */
return (s);
bp->b_flag &= ~(BFTEMP|BFCHG);
strcpy(bp->b_fname, fname);
if ((s=ffropen(fname)) == FIOERR) /* Hard file open. */
goto out;
if (s == FIOFNF) { /* File not found. */
mlwrite("[New file]");
goto out;
}
mlwrite("[Reading file]");
nline = 0;
while ((s=ffgetline(line, NLINE)) == FIOSUC) {
nbytes = strlen(line);
if ((lp1=lalloc(nbytes)) == NULL) {
s = FIOERR; /* Keep message on the */
break; /* display. */
}
lp2 = lback(curbp->b_linep);
lp2->l_fp = lp1;
lp1->l_fp = curbp->b_linep;
lp1->l_bp = lp2;
curbp->b_linep->l_bp = lp1;
for (i=0; i<nbytes; ++i)
lputc(lp1, i, line[i]);
++nline;
}
ffclose(); /* Ignore errors. */
if (s == FIOEOF) { /* Don't zap message! */
if (nline == 1)
mlwrite("[Read 1 line]");
else
mlwrite("[Read %d lines]", nline);
}
out:
for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
if (wp->w_bufp == curbp) {
wp->w_linep = lforw(curbp->b_linep);
wp->w_dotp = lforw(curbp->b_linep);
wp->w_doto = 0;
wp->w_markp = NULL;
wp->w_marko = 0;
wp->w_flag |= WFMODE|WFHARD;
}
}
if (s == FIOERR) /* False if error. */
return (FALSE);
return (TRUE);
}
/*
* Take a file name, and from it
* fabricate a buffer name. This routine knows
* about the syntax of file names on the target system.
* I suppose that this information could be put in
* a better place than a line of code.
*/
makename(bname, fname)
char bname[];
char fname[];
{
register char *cp1;
register char *cp2;
cp1 = &fname[0];
while (*cp1 != 0)
++cp1;
#if VMS
while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!=']')
--cp1;
#endif
#if CPM
while (cp1!=&fname[0] && cp1[-1]!=':')
--cp1;
#endif
#if MSDOS
while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\')
--cp1;
#endif
#if V7
while (cp1!=&fname[0] && cp1[-1]!='/')
--cp1;
#endif
cp2 = &bname[0];
while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
*cp2++ = *cp1++;
*cp2 = 0;
}
/*
* Ask for a file name, and write the
* contents of the current buffer to that file.
* Update the remembered file name and clear the
* buffer changed flag. This handling of file names
* is different from the earlier versions, and
* is more compatable with Gosling EMACS than
* with ITS EMACS. Bound to "C-X C-W".
*/
filewrite(f, n)
{
register WINDOW *wp;
register int s;
char fname[NFILEN];
if ((s=mlreply("Write file: ", fname, NFILEN)) != TRUE)
return (s);
if ((s=writeout(fname)) == TRUE) {
strcpy(curbp->b_fname, fname);
curbp->b_flag &= ~BFCHG;
wp = wheadp; /* Update mode lines. */
while (wp != NULL) {
if (wp->w_bufp == curbp)
wp->w_flag |= WFMODE;
wp = wp->w_wndp;
}
}
return (s);
}
/*
* Save the contents of the current
* buffer in its associatd file. No nothing
* if nothing has changed (this may be a bug, not a
* feature). Error if there is no remembered file
* name for the buffer. Bound to "C-X C-S". May
* get called by "C-Z".
*/
filesave(f, n)
{
register WINDOW *wp;
register int s;
if ((curbp->b_flag&BFCHG) == 0) /* Return, no changes. */
return (TRUE);
if (curbp->b_fname[0] == 0) { /* Must have a name. */
mlwrite("No file name");
return (FALSE);
}
if ((s=writeout(curbp->b_fname)) == TRUE) {
curbp->b_flag &= ~BFCHG;
wp = wheadp; /* Update mode lines. */
while (wp != NULL) {
if (wp->w_bufp == curbp)
wp->w_flag |= WFMODE;
wp = wp->w_wndp;
}
}
return (s);
}
/*
* This function performs the details of file
* writing. Uses the file management routines in the
* "fileio.c" package. The number of lines written is
* displayed. Sadly, it looks inside a LINE; provide
* a macro for this. Most of the grief is error
* checking of some sort.
*/
writeout(fn)
char *fn;
{
register int s;
register LINE *lp;
register int nline;
if ((s=ffwopen(fn)) != FIOSUC) /* Open writes message. */
return (FALSE);
lp = lforw(curbp->b_linep); /* First line. */
nline = 0; /* Number of lines. */
while (lp != curbp->b_linep) {
if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
break;
++nline;
lp = lforw(lp);
}
if (s == FIOSUC) { /* No write error. */
s = ffclose();
if (s == FIOSUC) { /* No close error. */
if (nline == 1)
mlwrite("[Wrote 1 line]");
else
mlwrite("[Wrote %d lines]", nline);
}
} else /* Ignore close error */
ffclose(); /* if a write error. */
if (s != FIOSUC) /* Some sort of error. */
return (FALSE);
return (TRUE);
}
/*
* The command allows the user
* to modify the file name associated with
* the current buffer. It is like the "f" command
* in UNIX "ed". The operation is simple; just zap
* the name in the BUFFER structure, and mark the windows
* as needing an update. You can type a blank line at the
* prompt if you wish.
*/
filename(f, n)
{
register WINDOW *wp;
register int s;
char fname[NFILEN];
if ((s=mlreply("Name: ", fname, NFILEN)) == ABORT)
return (s);
if (s == FALSE)
strcpy(curbp->b_fname, "");
else
strcpy(curbp->b_fname, fname);
wp = wheadp; /* Update mode lines. */
while (wp != NULL) {
if (wp->w_bufp == curbp)
wp->w_flag |= WFMODE;
wp = wp->w_wndp;
}
return (TRUE);
}

View File

@ -0,0 +1,121 @@
/*
* The routines in this file
* read and write ASCII files from the
* disk. All of the knowledge about files
* are here. A better message writing
* scheme should be used.
*/
#include <stdio.h>
#include "ed.h"
FILE *ffp; /* File pointer, all functions. */
/*
* Open a file for reading.
*/
ffropen(fn)
char *fn;
{
if ((ffp=fopen(fn, "r")) == NULL)
return (FIOFNF);
return (FIOSUC);
}
/*
* Open a file for writing.
* Return TRUE if all is well, and
* FALSE on error (cannot create).
*/
ffwopen(fn)
char *fn;
{
#if VMS
register int fd;
if ((fd=creat(fn, 0666, "rfm=var", "rat=cr")) < 0
|| (ffp=fdopen(fd, "w")) == NULL) {
#else
if ((ffp=fopen(fn, "w")) == NULL) {
#endif
mlwrite("Cannot open file for writing");
return (FIOERR);
}
return (FIOSUC);
}
/*
* Close a file.
* Should look at the status in all systems.
*/
ffclose()
{
#if V7
if (fclose(ffp) != FALSE) {
mlwrite("Error closing file");
return(FIOERR);
}
return(FIOSUC);
#endif
fclose(ffp);
return (FIOSUC);
}
/*
* Write a line to the already
* opened file. The "buf" points to the
* buffer, and the "nbuf" is its length, less
* the free newline. Return the status.
* Check only at the newline.
*/
ffputline(buf, nbuf)
register char buf[];
{
register int i;
for (i=0; i<nbuf; ++i)
putc(buf[i]&0xFF, ffp);
putc('\n', ffp);
if (ferror(ffp) != FALSE) {
mlwrite("Write I/O error");
return (FIOERR);
}
return (FIOSUC);
}
/*
* Read a line from a file,
* and store the bytes in the supplied
* buffer. The "nbuf" is the length of the
* buffer. Complain about long lines and lines
* at the end of the file that don't have a
* newline present. Check for I/O errors
* too. Return status.
*/
ffgetline(buf, nbuf)
register char buf[];
{
register int c;
register int i;
i = 0;
while ((c=getc(ffp))!=EOF && c!='\n') {
if (i >= nbuf-1) {
mlwrite("File has long line");
return (FIOERR);
}
buf[i++] = c;
}
if (c == EOF) {
if (ferror(ffp) != FALSE) {
mlwrite("File read error");
return (FIOERR);
}
if (i != 0) {
mlwrite("File has funny line at EOF");
return (FIOERR);
}
return (FIOEOF);
}
buf[i] = 0;
return (FIOSUC);
}

View File

@ -0,0 +1,492 @@
/*
* The functions in this file
* are a general set of line management
* utilities. They are the only routines that
* touch the text. They also touch the buffer
* and window structures, to make sure that the
* necessary updating gets done. There are routines
* in this file that handle the kill buffer too.
* It isn't here for any good reason.
*
* Note that this code only updates the dot and
* mark values in the window list. Since all the code
* acts on the current window, the buffer that we
* are editing must be being displayed, which means
* that "b_nwnd" is non zero, which means that the
* dot and mark values in the buffer headers are
* nonsense.
*/
#include <stdio.h>
#include "ed.h"
#define NBLOCK 16 /* Line block chunk size */
#define KBLOCK 256 /* Kill buffer block size */
char *kbufp = NULL; /* Kill buffer data */
int kused = 0; /* # of bytes used in KB */
int ksize = 0; /* # of bytes allocated in KB */
/*
* This routine allocates a block
* of memory large enough to hold a LINE
* containing "used" characters. The block is
* always rounded up a bit. Return a pointer
* to the new block, or NULL if there isn't
* any memory left. Print a message in the
* message line if no space.
*/
LINE *
lalloc(used)
register int used;
{
register LINE *lp;
register int size;
size = (used+NBLOCK-1) & ~(NBLOCK-1);
if (size == 0) /* Assume that an empty */
size = NBLOCK; /* line is for type-in. */
if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
mlwrite("Cannot allocate %d bytes", size);
return (NULL);
}
lp->l_size = size;
lp->l_used = used;
return (lp);
}
/*
* Delete line "lp". Fix all of the
* links that might point at it (they are
* moved to offset 0 of the next line.
* Unlink the line from whatever buffer it
* might be in. Release the memory. The
* buffers are updated too; the magic conditions
* described in the above comments don't hold
* here.
*/
lfree(lp)
register LINE *lp;
{
register BUFFER *bp;
register WINDOW *wp;
wp = wheadp;
while (wp != NULL) {
if (wp->w_linep == lp)
wp->w_linep = lp->l_fp;
if (wp->w_dotp == lp) {
wp->w_dotp = lp->l_fp;
wp->w_doto = 0;
}
if (wp->w_markp == lp) {
wp->w_markp = lp->l_fp;
wp->w_marko = 0;
}
wp = wp->w_wndp;
}
bp = bheadp;
while (bp != NULL) {
if (bp->b_nwnd == 0) {
if (bp->b_dotp == lp) {
bp->b_dotp = lp->l_fp;
bp->b_doto = 0;
}
if (bp->b_markp == lp) {
bp->b_markp = lp->l_fp;
bp->b_marko = 0;
}
}
bp = bp->b_bufp;
}
lp->l_bp->l_fp = lp->l_fp;
lp->l_fp->l_bp = lp->l_bp;
free((char *) lp);
}
/*
* This routine gets called when
* a character is changed in place in the
* current buffer. It updates all of the required
* flags in the buffer and window system. The flag
* used is passed as an argument; if the buffer is being
* displayed in more than 1 window we change EDIT to
* HARD. Set MODE if the mode line needs to be
* updated (the "*" has to be set).
*/
lchange(flag)
register int flag;
{
register WINDOW *wp;
if (curbp->b_nwnd != 1) /* Ensure hard. */
flag = WFHARD;
if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
flag |= WFMODE; /* update mode lines. */
curbp->b_flag |= BFCHG;
}
wp = wheadp;
while (wp != NULL) {
if (wp->w_bufp == curbp)
wp->w_flag |= flag;
wp = wp->w_wndp;
}
}
/*
* Insert "n" copies of the character "c"
* at the current location of dot. In the easy case
* all that happens is the text is stored in the line.
* In the hard case, the line has to be reallocated.
* When the window list is updated, take special
* care; I screwed it up once. You always update dot
* in the current window. You update mark, and a
* dot in another window, if it is greater than
* the place where you did the insert. Return TRUE
* if all is well, and FALSE on errors.
*/
linsert(n, c)
{
register char *cp1;
register char *cp2;
register LINE *lp1;
register LINE *lp2;
register LINE *lp3;
register int doto;
register int i;
register WINDOW *wp;
lchange(WFEDIT);
lp1 = curwp->w_dotp; /* Current line */
if (lp1 == curbp->b_linep) { /* At the end: special */
if (curwp->w_doto != 0) {
mlwrite("bug: linsert");
return (FALSE);
}
if ((lp2=lalloc(n)) == NULL) /* Allocate new line */
return (FALSE);
lp3 = lp1->l_bp; /* Previous line */
lp3->l_fp = lp2; /* Link in */
lp2->l_fp = lp1;
lp1->l_bp = lp2;
lp2->l_bp = lp3;
for (i=0; i<n; ++i)
lp2->l_text[i] = c;
curwp->w_dotp = lp2;
curwp->w_doto = n;
return (TRUE);
}
doto = curwp->w_doto; /* Save for later. */
if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */
if ((lp2=lalloc(lp1->l_used+n)) == NULL)
return (FALSE);
cp1 = &lp1->l_text[0];
cp2 = &lp2->l_text[0];
while (cp1 != &lp1->l_text[doto])
*cp2++ = *cp1++;
cp2 += n;
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
lp1->l_bp->l_fp = lp2;
lp2->l_fp = lp1->l_fp;
lp1->l_fp->l_bp = lp2;
lp2->l_bp = lp1->l_bp;
free((char *) lp1);
} else { /* Easy: in place */
lp2 = lp1; /* Pretend new line */
lp2->l_used += n;
cp2 = &lp1->l_text[lp1->l_used];
cp1 = cp2-n;
while (cp1 != &lp1->l_text[doto])
*--cp2 = *--cp1;
}
for (i=0; i<n; ++i) /* Add the characters */
lp2->l_text[doto+i] = c;
wp = wheadp; /* Update windows */
while (wp != NULL) {
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1) {
wp->w_dotp = lp2;
if (wp==curwp || wp->w_doto>doto)
wp->w_doto += n;
}
if (wp->w_markp == lp1) {
wp->w_markp = lp2;
if (wp->w_marko > doto)
wp->w_marko += n;
}
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* Insert a newline into the buffer
* at the current location of dot in the current
* window. The funny ass-backwards way it does things
* is not a botch; it just makes the last line in
* the file not a special case. Return TRUE if everything
* works out and FALSE on error (memory allocation
* failure). The update of dot and mark is a bit
* easier then in the above case, because the split
* forces more updating.
*/
lnewline()
{
register char *cp1;
register char *cp2;
register LINE *lp1;
register LINE *lp2;
register int doto;
register WINDOW *wp;
lchange(WFHARD);
lp1 = curwp->w_dotp; /* Get the address and */
doto = curwp->w_doto; /* offset of "." */
if ((lp2=lalloc(doto)) == NULL) /* New first half line */
return (FALSE);
cp1 = &lp1->l_text[0]; /* Shuffle text around */
cp2 = &lp2->l_text[0];
while (cp1 != &lp1->l_text[doto])
*cp2++ = *cp1++;
cp2 = &lp1->l_text[0];
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
lp1->l_used -= doto;
lp2->l_bp = lp1->l_bp;
lp1->l_bp = lp2;
lp2->l_bp->l_fp = lp2;
lp2->l_fp = lp1;
wp = wheadp; /* Windows */
while (wp != NULL) {
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1) {
if (wp->w_doto < doto)
wp->w_dotp = lp2;
else
wp->w_doto -= doto;
}
if (wp->w_markp == lp1) {
if (wp->w_marko < doto)
wp->w_markp = lp2;
else
wp->w_marko -= doto;
}
wp = wp->w_wndp;
}
return (TRUE);
}
/*
* This function deletes "n" bytes,
* starting at dot. It understands how do deal
* with end of lines, etc. It returns TRUE if all
* of the characters were deleted, and FALSE if
* they were not (because dot ran into the end of
* the buffer. The "kflag" is TRUE if the text
* should be put in the kill buffer.
*/
ldelete(n, kflag)
{
register char *cp1;
register char *cp2;
register LINE *dotp;
register int doto;
register int chunk;
register WINDOW *wp;
while (n != 0) {
dotp = curwp->w_dotp;
doto = curwp->w_doto;
if (dotp == curbp->b_linep) /* Hit end of buffer. */
return (FALSE);
chunk = dotp->l_used-doto; /* Size of chunk. */
if (chunk > n)
chunk = n;
if (chunk == 0) { /* End of line, merge. */
lchange(WFHARD);
if (ldelnewline() == FALSE
|| (kflag!=FALSE && kinsert('\n')==FALSE))
return (FALSE);
--n;
continue;
}
lchange(WFEDIT);
cp1 = &dotp->l_text[doto]; /* Scrunch text. */
cp2 = cp1 + chunk;
if (kflag != FALSE) { /* Kill? */
while (cp1 != cp2) {
if (kinsert(*cp1) == FALSE)
return (FALSE);
++cp1;
}
cp1 = &dotp->l_text[doto];
}
while (cp2 != &dotp->l_text[dotp->l_used])
*cp1++ = *cp2++;
dotp->l_used -= chunk;
wp = wheadp; /* Fix windows */
while (wp != NULL) {
if (wp->w_dotp==dotp && wp->w_doto>=doto) {
wp->w_doto -= chunk;
if (wp->w_doto < doto)
wp->w_doto = doto;
}
if (wp->w_markp==dotp && wp->w_marko>=doto) {
wp->w_marko -= chunk;
if (wp->w_marko < doto)
wp->w_marko = doto;
}
wp = wp->w_wndp;
}
n -= chunk;
}
return (TRUE);
}
/*
* Delete a newline. Join the current line
* with the next line. If the next line is the magic
* header line always return TRUE; merging the last line
* with the header line can be thought of as always being a
* successful operation, even if nothing is done, and this makes
* the kill buffer work "right". Easy cases can be done by
* shuffling data around. Hard cases require that lines be moved
* about in memory. Return FALSE on error and TRUE if all
* looks ok. Called by "ldelete" only.
*/
ldelnewline()
{
register char *cp1;
register char *cp2;
register LINE *lp1;
register LINE *lp2;
register LINE *lp3;
register WINDOW *wp;
lp1 = curwp->w_dotp;
lp2 = lp1->l_fp;
if (lp2 == curbp->b_linep) { /* At the buffer end. */
if (lp1->l_used == 0) /* Blank line. */
lfree(lp1);
return (TRUE);
}
if (lp2->l_used <= lp1->l_size-lp1->l_used) {
cp1 = &lp1->l_text[lp1->l_used];
cp2 = &lp2->l_text[0];
while (cp2 != &lp2->l_text[lp2->l_used])
*cp1++ = *cp2++;
wp = wheadp;
while (wp != NULL) {
if (wp->w_linep == lp2)
wp->w_linep = lp1;
if (wp->w_dotp == lp2) {
wp->w_dotp = lp1;
wp->w_doto += lp1->l_used;
}
if (wp->w_markp == lp2) {
wp->w_markp = lp1;
wp->w_marko += lp1->l_used;
}
wp = wp->w_wndp;
}
lp1->l_used += lp2->l_used;
lp1->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp1;
free((char *) lp2);
return (TRUE);
}
if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
return (FALSE);
cp1 = &lp1->l_text[0];
cp2 = &lp3->l_text[0];
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
cp1 = &lp2->l_text[0];
while (cp1 != &lp2->l_text[lp2->l_used])
*cp2++ = *cp1++;
lp1->l_bp->l_fp = lp3;
lp3->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp3;
lp3->l_bp = lp1->l_bp;
wp = wheadp;
while (wp != NULL) {
if (wp->w_linep==lp1 || wp->w_linep==lp2)
wp->w_linep = lp3;
if (wp->w_dotp == lp1)
wp->w_dotp = lp3;
else if (wp->w_dotp == lp2) {
wp->w_dotp = lp3;
wp->w_doto += lp1->l_used;
}
if (wp->w_markp == lp1)
wp->w_markp = lp3;
else if (wp->w_markp == lp2) {
wp->w_markp = lp3;
wp->w_marko += lp1->l_used;
}
wp = wp->w_wndp;
}
free((char *) lp1);
free((char *) lp2);
return (TRUE);
}
/*
* Delete all of the text
* saved in the kill buffer. Called by commands
* when a new kill context is being created. The kill
* buffer array is released, just in case the buffer has
* grown to immense size. No errors.
*/
kdelete()
{
if (kbufp != NULL) {
free((char *) kbufp);
kbufp = NULL;
kused = 0;
ksize = 0;
}
}
/*
* Insert a character to the kill buffer,
* enlarging the buffer if there isn't any room. Always
* grow the buffer in chunks, on the assumption that if you
* put something in the kill buffer you are going to put
* more stuff there too later. Return TRUE if all is
* well, and FALSE on errors.
*/
kinsert(c)
{
register char *nbufp;
register int i;
if (kused == ksize) {
if ((nbufp=malloc(ksize+KBLOCK)) == NULL)
return (FALSE);
for (i=0; i<ksize; ++i)
nbufp[i] = kbufp[i];
if (kbufp != NULL)
free((char *) kbufp);
kbufp = nbufp;
ksize += KBLOCK;
}
kbufp[kused++] = c;
return (TRUE);
}
/*
* This function gets characters from
* the kill buffer. If the character index "n" is
* off the end, it returns "-1". This lets the caller
* just scan along until it gets a "-1" back.
*/
kremove(n)
{
if (n >= kused)
return (-1);
else
return (kbufp[n] & 0xFF);
}

View File

@ -0,0 +1,669 @@
/*
* This program is in public domain; written by Dave G. Conroy.
* This file contains the
* main driving routine, and some
* keyboard processing code, for the
* MicroEMACS screen editor.
*/
#include <stdio.h>
#include "ed.h"
#if VMS
#include <ssdef.h>
#define GOOD (SS$_NORMAL)
#endif
#ifndef GOOD
#define GOOD 0
#endif
int currow; /* Working cursor row */
int curcol; /* Working cursor column */
int fillcol; /* Current fill column */
int thisflag; /* Flags, this command */
int lastflag; /* Flags, last command */
int curgoal; /* Goal column */
BUFFER *curbp; /* Current buffer */
WINDOW *curwp; /* Current window */
BUFFER *bheadp; /* BUFFER listhead */
WINDOW *wheadp; /* WINDOW listhead */
BUFFER *blistp; /* Buffer list BUFFER */
short kbdm[NKBDM] = CTLX|')'; /* Macro */
short *kbdmip; /* Input for above */
short *kbdmop; /* Output for above */
char pat[NPAT]; /* Pattern */
typedef struct {
short k_code; /* Key code */
int (*k_fp)(); /* Routine to handle it */
} KEYTAB;
extern int ctrlg(); /* Abort out of things */
extern int quit(); /* Quit */
extern int ctlxlp(); /* Begin macro */
extern int ctlxrp(); /* End macro */
extern int ctlxe(); /* Execute macro */
extern int fileread(); /* Get a file, read only */
extern int filevisit(); /* Get a file, read write */
extern int filewrite(); /* Write a file */
extern int filesave(); /* Save current file */
extern int filename(); /* Adjust file name */
extern int getccol(); /* Get current column */
extern int gotobol(); /* Move to start of line */
extern int forwchar(); /* Move forward by characters */
extern int gotoeol(); /* Move to end of line */
extern int backchar(); /* Move backward by characters */
extern int forwline(); /* Move forward by lines */
extern int backline(); /* Move backward by lines */
extern int forwpage(); /* Move forward by pages */
extern int backpage(); /* Move backward by pages */
extern int gotobob(); /* Move to start of buffer */
extern int gotoeob(); /* Move to end of buffer */
extern int setfillcol(); /* Set fill column. */
extern int setmark(); /* Set mark */
extern int swapmark(); /* Swap "." and mark */
extern int forwsearch(); /* Search forward */
extern int backsearch(); /* Search backwards */
extern int showcpos(); /* Show the cursor position */
extern int nextwind(); /* Move to the next window */
extern int prevwind(); /* Move to the previous window */
extern int onlywind(); /* Make current window only one */
extern int splitwind(); /* Split current window */
extern int mvdnwind(); /* Move window down */
extern int mvupwind(); /* Move window up */
extern int enlargewind(); /* Enlarge display window. */
extern int shrinkwind(); /* Shrink window. */
extern int listbuffers(); /* Display list of buffers */
extern int usebuffer(); /* Switch a window to a buffer */
extern int killbuffer(); /* Make a buffer go away. */
extern int reposition(); /* Reposition window */
extern int refresh(); /* Refresh the screen */
extern int twiddle(); /* Twiddle characters */
extern int tab(); /* Insert tab */
extern int newline(); /* Insert CR-LF */
extern int indent(); /* Insert CR-LF, then indent */
extern int openline(); /* Open up a blank line */
extern int deblank(); /* Delete blank lines */
extern int quote(); /* Insert literal */
extern int backword(); /* Backup by words */
extern int forwword(); /* Advance by words */
extern int forwdel(); /* Forward delete */
extern int backdel(); /* Backward delete */
extern int kill(); /* Kill forward */
extern int yank(); /* Yank back from killbuffer. */
extern int upperword(); /* Upper case word. */
extern int lowerword(); /* Lower case word. */
extern int upperregion(); /* Upper case region. */
extern int lowerregion(); /* Lower case region. */
extern int capword(); /* Initial capitalize word. */
extern int delfword(); /* Delete forward word. */
extern int delbword(); /* Delete backward word. */
extern int killregion(); /* Kill region. */
extern int copyregion(); /* Copy region to kill buffer. */
extern int spawncli(); /* Run CLI in a subjob. */
extern int spawn(); /* Run a command in a subjob. */
extern int quickexit(); /* low keystroke style exit. */
extern char *strcpy(); /* copy string */
/*
* Command table.
* This table is *roughly* in ASCII
* order, left to right across the characters
* of the command. This expains the funny
* location of the control-X commands.
*/
KEYTAB keytab[] = {
CTRL|'@', &setmark,
CTRL|'A', &gotobol,
CTRL|'B', &backchar,
CTRL|'C', &spawncli, /* Run CLI in subjob. */
CTRL|'D', &forwdel,
CTRL|'E', &gotoeol,
CTRL|'F', &forwchar,
CTRL|'G', &ctrlg,
CTRL|'H', &backdel,
CTRL|'I', &tab,
CTRL|'J', &indent,
CTRL|'K', &kill,
CTRL|'L', &refresh,
CTRL|'M', &newline,
CTRL|'N', &forwline,
CTRL|'O', &openline,
CTRL|'P', &backline,
CTRL|'Q', &quote, /* Often unreachable */
CTRL|'R', &backsearch,
CTRL|'S', &forwsearch, /* Often unreachable */
CTRL|'T', &twiddle,
CTRL|'V', &forwpage,
CTRL|'W', &killregion,
CTRL|'Y', &yank,
CTRL|'Z', &quickexit, /* quick save and exit */
CTLX|CTRL|'B', &listbuffers,
CTLX|CTRL|'C', &quit, /* Hard quit. */
CTLX|CTRL|'F', &filename,
CTLX|CTRL|'L', &lowerregion,
CTLX|CTRL|'O', &deblank,
CTLX|CTRL|'N', &mvdnwind,
CTLX|CTRL|'P', &mvupwind,
CTLX|CTRL|'R', &fileread,
CTLX|CTRL|'S', &filesave, /* Often unreachable */
CTLX|CTRL|'U', &upperregion,
CTLX|CTRL|'V', &filevisit,
CTLX|CTRL|'W', &filewrite,
CTLX|CTRL|'X', &swapmark,
CTLX|CTRL|'Z', &shrinkwind,
CTLX|'!', &spawn, /* Run 1 command. */
CTLX|'=', &showcpos,
CTLX|'(', &ctlxlp,
CTLX|')', &ctlxrp,
CTLX|'1', &onlywind,
CTLX|'2', &splitwind,
CTLX|'B', &usebuffer,
CTLX|'E', &ctlxe,
CTLX|'F', &setfillcol,
CTLX|'K', &killbuffer,
CTLX|'N', &nextwind,
CTLX|'P', &prevwind,
CTLX|'Z', &enlargewind,
META|CTRL|'H', &delbword,
META|'!', &reposition,
META|'.', &setmark,
META|'>', &gotoeob,
META|'<', &gotobob,
META|'B', &backword,
META|'C', &capword,
META|'D', &delfword,
META|'F', &forwword,
META|'L', &lowerword,
META|'Q', &quote,
META|'R', &backsearch,
META|'S', &forwsearch,
META|'U', &upperword,
META|'V', &backpage,
META|'W', &copyregion,
META|0x7F, &delbword,
0x7F, &backdel
};
#define NKEYTAB (sizeof(keytab)/sizeof(keytab[0]))
#if LK201
/*
* Mapping table for all of the funny
* keys with the numeric parameters on the LK201.
* Indexed by the code, which is between 0 (unused) and
* 34 (F20). An entry of 0 means no mapping. The map
* goes to command keys. If I had a "special" bit,
* I could use the code in the escape sequence as a
* key code, and return (for example) "do" as
* SPECIAL + 29. Then the dispatch would be
* done by the default keymap. This is probably a
* better way to go.
*/
short lkmap[] = {
0,
CTRL|'S', /* 1 Find */
CTRL|'Y', /* 2 Insert here */
CTRL|'W', /* 3 Remove */
CTRL|'@', /* 4 Select */
META|'V', /* 5 Previous screen */
CTRL|'V', /* 6 Next screen */
0,
0,
0,
0, /* 10 Compose */
0,
0, /* 12 Print screen */
0,
0, /* 14 F4 */
0,
0,
0, /* 17 F6 */
0, /* 18 F7 */
0, /* 19 F8 */
0, /* 20 F9 */
0, /* 21 F10 */
0,
0,
0,
0,
0, /* 26 F14 */
0,
0, /* 28 Help */
CTLX|'E', /* 29 Do C-X E */
0,
CTLX|'P', /* 31 F17 C-X P */
CTLX|'N', /* 32 F18 C-X N */
CTLX|'Z', /* 33 F19 C-X Z */
CTLX|CTRL|'Z' /* 34 F20 C-X C-Z */
};
#endif
main(argc, argv)
char *argv[];
{
register int c;
register int f;
register int n;
register int mflag;
char bname[NBUFN];
strcpy(bname, "main"); /* Work out the name of */
if (argc > 1) /* the default buffer. */
makename(bname, argv[1]);
edinit(bname); /* Buffers, windows. */
vtinit(); /* Displays. */
if (argc > 1) {
update(); /* You have to update */
readin(argv[1]); /* in case "[New file]" */
}
lastflag = 0; /* Fake last flags. */
loop:
update(); /* Fix up the screen */
c = getkey();
if (mpresf != FALSE) {
mlerase();
update();
if (c == ' ') /* ITS EMACS does this */
goto loop;
}
f = FALSE;
n = 1;
if (c == (CTRL|'U')) { /* ^U, start argument */
f = TRUE;
n = 4; /* with argument of 4 */
mflag = 0; /* that can be discarded. */
mlwrite("Arg: 4");
while ((c=getkey()) >='0' && c<='9' || c==(CTRL|'U') || c=='-'){
if (c == (CTRL|'U'))
n = n*4;
/*
* If dash, and start of argument string, set arg.
* to -1. Otherwise, insert it.
*/
else if (c == '-') {
if (mflag)
break;
n = 0;
mflag = -1;
}
/*
* If first digit entered, replace previous argument
* with digit and set sign. Otherwise, append to arg.
*/
else {
if (!mflag) {
n = 0;
mflag = 1;
}
n = 10*n + c - '0';
}
mlwrite("Arg: %d", (mflag >=0) ? n : (n ? -n : -1));
}
/*
* Make arguments preceded by a minus sign negative and change
* the special argument "^U -" to an effective "^U -1".
*/
if (mflag == -1) {
if (n == 0)
n++;
n = -n;
}
}
if (c == (CTRL|'X')) /* ^X is a prefix */
c = CTLX | getctl();
if (kbdmip != NULL) { /* Save macro strokes. */
if (c!=(CTLX|')') && kbdmip>&kbdm[NKBDM-6]) {
ctrlg(FALSE, 0);
goto loop;
}
if (f != FALSE) {
*kbdmip++ = (CTRL|'U');
*kbdmip++ = n;
}
*kbdmip++ = c;
}
execute(c, f, n); /* Do it. */
goto loop;
}
/*
* Initialize all of the buffers
* and windows. The buffer name is passed down as
* an argument, because the main routine may have been
* told to read in a file by default, and we want the
* buffer name to be right.
*/
edinit(bname)
char bname[];
{
register BUFFER *bp;
register WINDOW *wp;
bp = bfind(bname, TRUE, 0); /* First buffer */
blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer */
wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */
if (bp==NULL || wp==NULL || blistp==NULL)
abort();
curbp = bp; /* Make this current */
wheadp = wp;
curwp = wp;
wp->w_wndp = NULL; /* Initialize window */
wp->w_bufp = bp;
bp->b_nwnd = 1; /* Displayed. */
wp->w_linep = bp->b_linep;
wp->w_dotp = bp->b_linep;
wp->w_doto = 0;
wp->w_markp = NULL;
wp->w_marko = 0;
wp->w_toprow = 0;
wp->w_ntrows = term.t_nrow-1; /* "-1" for mode line. */
wp->w_force = 0;
wp->w_flag = WFMODE|WFHARD; /* Full. */
}
/*
* This is the general command execution
* routine. It handles the fake binding of all the
* keys to "self-insert". It also clears out the "thisflag"
* word, and arranges to move it to the "lastflag", so that
* the next command can look at it. Return the status of
* command.
*/
execute(c, f, n)
{
register KEYTAB *ktp;
register int status;
ktp = &keytab[0]; /* Look in key table. */
while (ktp < &keytab[NKEYTAB]) {
if (ktp->k_code == c) {
thisflag = 0;
status = (*ktp->k_fp)(f, n);
lastflag = thisflag;
return (status);
}
++ktp;
}
/*
* If a space was typed, fill column is defined, the argument is non-
* negative, and we are now past fill column, perform word wrap.
*/
if (c == ' ' && fillcol > 0 && n>=0 && getccol(FALSE) > fillcol)
wrapword();
if ((c>=0x20 && c<=0x7E) /* Self inserting. */
|| (c>=0xA0 && c<=0xFE)) {
if (n <= 0) { /* Fenceposts. */
lastflag = 0;
return (n<0 ? FALSE : TRUE);
}
thisflag = 0; /* For the future. */
status = linsert(n, c);
lastflag = thisflag;
return (status);
}
lastflag = 0; /* Fake last flags. */
return (FALSE);
}
/*
* Read in a key.
* Do the standard keyboard preprocessing.
* Convert the keys to the internal character set. On
* the LK201, which lacks a reasonable ESC key, make the
* grave accent a meta key too; this is a fairly common
* customization around Digital. Also read and decode
* the arrow keys, and other special keys. This is
* done in Rainbow mode; does this work on all
* the terminals with LK201 keyboards?
*/
getkey()
{
register int c;
#if LK201
register int n;
loop:
c = (*term.t_getchar)();
if (c == AGRAVE) { /* Alternate M- prefix. */
c = getctl();
return (META | c);
}
if (c == METACH) { /* M-, or special key. */
c = (*term.t_getchar)();
if (c == '[') { /* Arrows and extras. */
c = (*term.t_getchar)();
if (c == 'A')
return (CTRL | 'P');
if (c == 'B')
return (CTRL | 'N');
if (c == 'C')
return (CTRL | 'F');
if (c == 'D')
return (CTRL | 'B');
if (c>='0' && c<='9') {
n = 0;
do {
n = 10*n + c - '0';
c = (*term.t_getchar)();
} while (c>='0' && c<='9');
if (c=='~' && n<=34 && (c=lkmap[n])!=0)
return (c);
}
goto loop;
}
if (c == 'O') {
c = (*term.t_getchar)();
if (c == 'P') /* PF1 => M-X (Future) */
return (META | 'X');
if (c == 'Q') /* PF2 => C-Q */
return (CTRL | 'Q');
if (c == 'R') /* PF3 => C-S */
return (CTRL | 'S');
if (c == 'S') /* PF4 => C-R */
return (CTRL | 'R');
goto loop;
}
if (c>='a' && c<='z') /* Force to upper */
c -= 0x20;
if (c>=0x00 && c<=0x1F) /* C0 control -> C- */
c = CTRL | (c+'@');
return (META | c);
}
#endif
#if VT100
loop:
c = (*term.t_getchar)();
if (c == METACH) { /* Apply M- prefix */
c = (*term.t_getchar)();
if (c == '[') { /* Arrow keys. */
c = (*term.t_getchar)();
if (c == 'A')
return (CTRL | 'P');
if (c == 'B')
return (CTRL | 'N');
if (c == 'C')
return (CTRL | 'F');
if (c == 'D')
return (CTRL | 'B');
goto loop;
}
if (c == 'O') {
c = (*term.t_getchar)();
if (c == 'P') /* PF1 => M-X (Future) */
return (META | 'X');
if (c == 'Q') /* PF2 => C-Q */
return (CTRL | 'Q');
if (c == 'R') /* PF3 => C-S */
return (CTRL | 'S');
if (c == 'S') /* PF4 => C-R */
return (CTRL | 'R');
goto loop;
}
if (c>='a' && c<='z') /* Force to upper */
c -= 0x20;
if (c>=0x00 && c<=0x1F) /* C0 control -> C- */
c = CTRL | (c+'@');
return (META | c);
}
#endif
#if !LK201 && !VT100
c = (*term.t_getchar)();
if (c == METACH) { /* Apply M- prefix */
c = getctl();
return (META | c);
}
#endif
if (c == CTRLCH) { /* Apply C- prefix */
c = getctl();
return (CTRL | c);
}
if (c == CTMECH) { /* Apply C-M- prefix */
c = getctl();
return (CTRL | META | c);
}
if (c>=0x00 && c<=0x1F) /* C0 control -> C- */
c = CTRL | (c+'@');
return (c);
}
/*
* Get a key.
* Apply control modifications
* to the read key.
*/
getctl()
{
register int c;
c = (*term.t_getchar)();
if (c>='a' && c<='z') /* Force to upper */
c -= 0x20;
if (c>=0x00 && c<=0x1F) /* C0 control -> C- */
c = CTRL | (c+'@');
return (c);
}
/*
* Fancy quit command, as implemented
* by Norm. If the current buffer has changed
* do a write current buffer and exit emacs,
* otherwise simply exit.
*/
quickexit(f, n)
{
if ((curbp->b_flag&BFCHG) != 0 /* Changed. */
&& (curbp->b_flag&BFTEMP) == 0) /* Real. */
filesave(f, n);
quit(f, n); /* conditionally quit */
}
/*
* Quit command. If an argument, always
* quit. Otherwise confirm if a buffer has been
* changed and not written out. Normally bound
* to "C-X C-C".
*/
quit(f, n)
{
register int s;
if (f != FALSE /* Argument forces it. */
|| anycb() == FALSE /* All buffers clean. */
|| (s=mlyesno("Quit")) == TRUE) { /* User says it's OK. */
vttidy();
exit(GOOD);
}
return (s);
}
/*
* Begin a keyboard macro.
* Error if not at the top level
* in keyboard processing. Set up
* variables and return.
*/
ctlxlp(f, n)
{
if (kbdmip!=NULL || kbdmop!=NULL) {
mlwrite("Not now");
return (FALSE);
}
mlwrite("[Start macro]");
kbdmip = &kbdm[0];
return (TRUE);
}
/*
* End keyboard macro. Check for
* the same limit conditions as the
* above routine. Set up the variables
* and return to the caller.
*/
ctlxrp(f, n)
{
if (kbdmip == NULL) {
mlwrite("Not now");
return (FALSE);
}
mlwrite("[End macro]");
kbdmip = NULL;
return (TRUE);
}
/*
* Execute a macro.
* The command argument is the
* number of times to loop. Quit as
* soon as a command gets an error.
* Return TRUE if all ok, else
* FALSE.
*/
ctlxe(f, n)
{
register int c;
register int af;
register int an;
register int s;
if (kbdmip!=NULL || kbdmop!=NULL) {
mlwrite("Not now");
return (FALSE);
}
if (n <= 0)
return (TRUE);
do {
kbdmop = &kbdm[0];
do {
af = FALSE;
an = 1;
if ((c = *kbdmop++) == (CTRL|'U')) {
af = TRUE;
an = *kbdmop++;
c = *kbdmop++;
}
s = TRUE;
} while (c!=(CTLX|')') && (s=execute(c, af, an))==TRUE);
kbdmop = NULL;
} while (s==TRUE && --n);
return (s);
}
/*
* Abort.
* Beep the beeper.
* Kill off any keyboard macro,
* etc., that is in progress.
* Sometimes called as a routine,
* to do general aborting of
* stuff.
*/
ctrlg(f, n)
{
(*term.t_beep)();
if (kbdmip != NULL) {
kbdm[0] = (CTLX|')');
kbdmip = NULL;
}
return (ABORT);
}

View File

@ -0,0 +1,12 @@
: makefile.bat 3/11/86
: MSDOS batch file to build executable MicroEMACS me.exe.
: Remember to put MicroEMACS source files into a separate directory!
: Uses "tempfile" to avoid MSDOS command line length restrictions.
: Requires header "ed.h" in addition to files listed below.
echo ansi.c basic.c buffer.c display.c file.c fileio.c >tempfile
echo line.c main.c random.c region.c search.c spawn.c >>tempfile
echo tcap.c termio.c vt52.c window.c word.c >>tempfile
cc -VQUIET -o me @tempfile
del tempfile

View File

@ -0,0 +1,73 @@
/ High speed screen update for Rainbow.
/ MWC-86 has this in CP/M-86 but not in MS-DOS.
/ putline(row, col, buf);
/ Row and column are origin 1.
/ The buf is a pointer to an array of characters.
/ It won't write past the end of the line in
/ the video RAM; it looks for the FF at the end
/ of the line.
/ This routine by Bob McNamara.
.globl putline_
Scr_Seg = 0xEE00
ScrPtr = 3
putline_:
push si
push di
push es
mov ax,$Scr_Seg /point our extra segment into screen RAM
mov es,ax
mov di,es:ScrPtr+1 /di <- base line address
and di,$0x0fff
movb al,$0xff
cld
mov bx,sp
mov dx,8(bx) /dx = row number to write
mov si,12(bx) /si = string to be moved
mov bx,10(bx) /bx = column number to start at
dec bx /column number starts at 1
dec dx /row number starts at 1 too
jz 2f
1:
mov cx,$140
repnz scasb
mov di,es:(di) /di = pointer to next line
and di,$0x0fff
dec dx
jnz 1b
2:
add di,bx /di -> offset in row
3:
cmpb al,es:(di)
jz 4f
movsb
cmpb al,es:(di)
jz 4f
movsb
cmpb al,es:(di)
jz 4f
movsb
cmpb al,es:(di)
jz 4f
movsb
cmpb al,es:(di)
jz 4f
movsb
cmpb al,es:(di)
jz 4f
movsb
cmpb al,es:(di)
jz 4f
movsb
cmpb al,es:(di)
jz 4f
movsb
jmp 3b
4:
pop es
pop di
pop si
ret

View File

@ -0,0 +1,417 @@
/*
* This file contains the
* command processing functions for
* a number of random commands. There is
* no functional grouping here, for
* sure.
*/
#include <stdio.h>
#include "ed.h"
int tabsize; /* Tab size (0: use real tabs) */
/*
* Set fill column to n.
*/
setfillcol(f, n)
{
fillcol = n;
return(TRUE);
}
/*
* Display the current position of the cursor,
* in origin 1 X-Y coordinates, the character that is
* under the cursor (in octal), and the fraction of the
* text that is before the cursor. The displayed column
* is not the current column, but the column that would
* be used on an infinite width display. Normally this
* is bound to "C-X =".
*/
showcpos(f, n)
{
register LINE *clp;
register long nch;
register int cbo;
register long nbc;
register int cac;
register int ratio;
register int col;
register int i;
register int c;
clp = lforw(curbp->b_linep); /* Grovel the data. */
cbo = 0;
nch = 0;
for (;;) {
if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
nbc = nch;
if (cbo == llength(clp))
cac = '\n';
else
cac = lgetc(clp, cbo);
}
if (cbo == llength(clp)) {
if (clp == curbp->b_linep)
break;
clp = lforw(clp);
cbo = 0;
} else
++cbo;
++nch;
}
col = getccol(FALSE); /* Get real column. */
ratio = 0; /* Ratio before dot. */
if (nch != 0)
ratio = (100L*nbc) / nch;
mlwrite("X=%d Y=%d CH=0x%x .=%D (%d%% of %D)",
col+1, currow+1, cac, nbc, ratio, nch);
return (TRUE);
}
/*
* Return current column. Stop at first non-blank given TRUE argument.
*/
getccol(bflg)
int bflg;
{
register int c, i, col;
col = 0;
for (i=0; i<curwp->w_doto; ++i) {
c = lgetc(curwp->w_dotp, i);
if (c!=' ' && c!='\t' && bflg)
break;
if (c == '\t')
col |= 0x07;
else if (c<0x20 || c==0x7F)
++col;
++col;
}
return(col);
}
/*
* Twiddle the two characters on either side of
* dot. If dot is at the end of the line twiddle the
* two characters before it. Return with an error if dot
* is at the beginning of line; it seems to be a bit
* pointless to make this work. This fixes up a very
* common typo with a single stroke. Normally bound
* to "C-T". This always works within a line, so
* "WFEDIT" is good enough.
*/
twiddle(f, n)
{
register LINE *dotp;
register int doto;
register int cl;
register int cr;
dotp = curwp->w_dotp;
doto = curwp->w_doto;
if (doto==llength(dotp) && --doto<0)
return (FALSE);
cr = lgetc(dotp, doto);
if (--doto < 0)
return (FALSE);
cl = lgetc(dotp, doto);
lputc(dotp, doto+0, cr);
lputc(dotp, doto+1, cl);
lchange(WFEDIT);
return (TRUE);
}
/*
* Quote the next character, and
* insert it into the buffer. All the characters
* are taken literally, with the exception of the newline,
* which always has its line splitting meaning. The character
* is always read, even if it is inserted 0 times, for
* regularity. Bound to "M-Q" (for me) and "C-Q" (for Rich,
* and only on terminals that don't need XON-XOFF).
*/
quote(f, n)
{
register int s;
register int c;
c = (*term.t_getchar)();
if (n < 0)
return (FALSE);
if (n == 0)
return (TRUE);
if (c == '\n') {
do {
s = lnewline();
} while (s==TRUE && --n);
return (s);
}
return (linsert(n, c));
}
/*
* Set tab size if given non-default argument (n <> 1). Otherwise, insert a
* tab into file. If given argument, n, of zero, change to true tabs.
* If n > 1, simulate tab stop every n-characters using spaces.
* This has to be done in this slightly funny way because the
* tab (in ASCII) has been turned into "C-I" (in 10
* bit code) already. Bound to "C-I".
*/
tab(f, n)
{
if (n < 0)
return (FALSE);
if (n == 0 || n > 1) {
tabsize = n;
return(TRUE);
}
if (! tabsize)
return(linsert(1, '\t'));
return(linsert(tabsize - (getccol(FALSE) % tabsize), ' '));
}
/*
* Open up some blank space. The basic plan
* is to insert a bunch of newlines, and then back
* up over them. Everything is done by the subcommand
* procerssors. They even handle the looping. Normally
* this is bound to "C-O".
*/
openline(f, n)
{
register int i;
register int s;
if (n < 0)
return (FALSE);
if (n == 0)
return (TRUE);
i = n; /* Insert newlines. */
do {
s = lnewline();
} while (s==TRUE && --i);
if (s == TRUE) /* Then back up overtop */
s = backchar(f, n); /* of them all. */
return (s);
}
/*
* Insert a newline. Bound to "C-M".
* If you are at the end of the line and the
* next line is a blank line, just move into the
* blank line. This makes "C-O" and "C-X C-O" work
* nicely, and reduces the ammount of screen
* update that has to be done. This would not be
* as critical if screen update were a lot
* more efficient.
*/
newline(f, n)
{
int nicol;
register LINE *lp;
register int s;
if (n < 0)
return (FALSE);
while (n--) {
lp = curwp->w_dotp;
if (llength(lp) == curwp->w_doto
&& lp != curbp->b_linep
&& llength(lforw(lp)) == 0) {
if ((s=forwchar(FALSE, 1)) != TRUE)
return (s);
} else if ((s=lnewline()) != TRUE)
return (s);
}
return (TRUE);
}
/*
* Delete blank lines around dot.
* What this command does depends if dot is
* sitting on a blank line. If dot is sitting on a
* blank line, this command deletes all the blank lines
* above and below the current line. If it is sitting
* on a non blank line then it deletes all of the
* blank lines after the line. Normally this command
* is bound to "C-X C-O". Any argument is ignored.
*/
deblank(f, n)
{
register LINE *lp1;
register LINE *lp2;
register int nld;
lp1 = curwp->w_dotp;
while (llength(lp1)==0 && (lp2=lback(lp1))!=curbp->b_linep)
lp1 = lp2;
lp2 = lp1;
nld = 0;
while ((lp2=lforw(lp2))!=curbp->b_linep && llength(lp2)==0)
++nld;
if (nld == 0)
return (TRUE);
curwp->w_dotp = lforw(lp1);
curwp->w_doto = 0;
return (ldelete(nld));
}
/*
* Insert a newline, then enough
* tabs and spaces to duplicate the indentation
* of the previous line. Assumes tabs are every eight
* characters. Quite simple. Figure out the indentation
* of the current line. Insert a newline by calling
* the standard routine. Insert the indentation by
* inserting the right number of tabs and spaces.
* Return TRUE if all ok. Return FALSE if one
* of the subcomands failed. Normally bound
* to "C-J".
*/
indent(f, n)
{
register int nicol;
register int c;
register int i;
if (n < 0)
return (FALSE);
while (n--) {
nicol = 0;
for (i=0; i<llength(curwp->w_dotp); ++i) {
c = lgetc(curwp->w_dotp, i);
if (c!=' ' && c!='\t')
break;
if (c == '\t')
nicol |= 0x07;
++nicol;
}
if (lnewline() == FALSE
|| ((i=nicol/8)!=0 && linsert(i, '\t')==FALSE)
|| ((i=nicol%8)!=0 && linsert(i, ' ')==FALSE))
return (FALSE);
}
return (TRUE);
}
/*
* Delete forward. This is real
* easy, because the basic delete routine does
* all of the work. Watches for negative arguments,
* and does the right thing. If any argument is
* present, it kills rather than deletes, to prevent
* loss of text if typed with a big argument.
* Normally bound to "C-D".
*/
forwdel(f, n)
{
if (n < 0)
return (backdel(f, -n));
if (f != FALSE) { /* Really a kill. */
if ((lastflag&CFKILL) == 0)
kdelete();
thisflag |= CFKILL;
}
return (ldelete(n, f));
}
/*
* Delete backwards. This is quite easy too,
* because it's all done with other functions. Just
* move the cursor back, and delete forwards.
* Like delete forward, this actually does a kill
* if presented with an argument. Bound to both
* "RUBOUT" and "C-H".
*/
backdel(f, n)
{
register int s;
if (n < 0)
return (forwdel(f, -n));
if (f != FALSE) { /* Really a kill. */
if ((lastflag&CFKILL) == 0)
kdelete();
thisflag |= CFKILL;
}
if ((s=backchar(f, n)) == TRUE)
s = ldelete(n, f);
return (s);
}
/*
* Kill text. If called without an argument,
* it kills from dot to the end of the line, unless it
* is at the end of the line, when it kills the newline.
* If called with an argument of 0, it kills from the
* start of the line to dot. If called with a positive
* argument, it kills from dot forward over that number
* of newlines. If called with a negative argument it
* kills backwards that number of newlines. Normally
* bound to "C-K".
*/
kill(f, n)
{
register int chunk;
register LINE *nextp;
if ((lastflag&CFKILL) == 0) /* Clear kill buffer if */
kdelete(); /* last wasn't a kill. */
thisflag |= CFKILL;
if (f == FALSE) {
chunk = llength(curwp->w_dotp)-curwp->w_doto;
if (chunk == 0)
chunk = 1;
} else if (n == 0) {
chunk = curwp->w_doto;
curwp->w_doto = 0;
} else if (n > 0) {
chunk = llength(curwp->w_dotp)-curwp->w_doto+1;
nextp = lforw(curwp->w_dotp);
while (--n) {
if (nextp == curbp->b_linep)
return (FALSE);
chunk += llength(nextp)+1;
nextp = lforw(nextp);
}
} else {
mlwrite("neg kill");
return (FALSE);
}
return (ldelete(chunk, TRUE));
}
/*
* Yank text back from the kill buffer. This
* is really easy. All of the work is done by the
* standard insert routines. All you do is run the loop,
* and check for errors. Bound to "C-Y". The blank
* lines are inserted with a call to "newline"
* instead of a call to "lnewline" so that the magic
* stuff that happens when you type a carriage
* return also happens when a carriage return is
* yanked back from the kill buffer.
*/
yank(f, n)
{
register int c;
register int i;
extern int kused;
if (n < 0)
return (FALSE);
while (n--) {
i = 0;
while ((c=kremove(i)) >= 0) {
if (c == '\n') {
if (newline(FALSE, 1) == FALSE)
return (FALSE);
} else {
if (linsert(1, c) == FALSE)
return (FALSE);
}
++i;
}
}
return (TRUE);
}

View File

@ -0,0 +1,199 @@
/*
* The routines in this file
* deal with the region, that magic space
* between "." and mark. Some functions are
* commands. Some functions are just for
* internal use.
*/
#include <stdio.h>
#include "ed.h"
/*
* Kill the region. Ask "getregion"
* to figure out the bounds of the region.
* Move "." to the start, and kill the characters.
* Bound to "C-W".
*/
killregion(f, n)
{
register int s;
REGION region;
if ((s=getregion(&region)) != TRUE)
return (s);
if ((lastflag&CFKILL) == 0) /* This is a kill type */
kdelete(); /* command, so do magic */
thisflag |= CFKILL; /* kill buffer stuff. */
curwp->w_dotp = region.r_linep;
curwp->w_doto = region.r_offset;
return (ldelete(region.r_size, TRUE));
}
/*
* Copy all of the characters in the
* region to the kill buffer. Don't move dot
* at all. This is a bit like a kill region followed
* by a yank. Bound to "M-W".
*/
copyregion(f, n)
{
register LINE *linep;
register int loffs;
register int s;
REGION region;
if ((s=getregion(&region)) != TRUE)
return (s);
if ((lastflag&CFKILL) == 0) /* Kill type command. */
kdelete();
thisflag |= CFKILL;
linep = region.r_linep; /* Current line. */
loffs = region.r_offset; /* Current offset. */
while (region.r_size--) {
if (loffs == llength(linep)) { /* End of line. */
if ((s=kinsert('\n')) != TRUE)
return (s);
linep = lforw(linep);
loffs = 0;
} else { /* Middle of line. */
if ((s=kinsert(lgetc(linep, loffs))) != TRUE)
return (s);
++loffs;
}
}
return (TRUE);
}
/*
* Lower case region. Zap all of the upper
* case characters in the region to lower case. Use
* the region code to set the limits. Scan the buffer,
* doing the changes. Call "lchange" to ensure that
* redisplay is done in all buffers. Bound to
* "C-X C-L".
*/
lowerregion(f, n)
{
register LINE *linep;
register int loffs;
register int c;
register int s;
REGION region;
if ((s=getregion(&region)) != TRUE)
return (s);
lchange(WFHARD);
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
linep = lforw(linep);
loffs = 0;
} else {
c = lgetc(linep, loffs);
if (c>='A' && c<='Z')
lputc(linep, loffs, c+'a'-'A');
++loffs;
}
}
return (TRUE);
}
/*
* Upper case region. Zap all of the lower
* case characters in the region to upper case. Use
* the region code to set the limits. Scan the buffer,
* doing the changes. Call "lchange" to ensure that
* redisplay is done in all buffers. Bound to
* "C-X C-L".
*/
upperregion(f, n)
{
register LINE *linep;
register int loffs;
register int c;
register int s;
REGION region;
if ((s=getregion(&region)) != TRUE)
return (s);
lchange(WFHARD);
linep = region.r_linep;
loffs = region.r_offset;
while (region.r_size--) {
if (loffs == llength(linep)) {
linep = lforw(linep);
loffs = 0;
} else {
c = lgetc(linep, loffs);
if (c>='a' && c<='z')
lputc(linep, loffs, c-'a'+'A');
++loffs;
}
}
return (TRUE);
}
/*
* This routine figures out the
* bounds of the region in the current window, and
* fills in the fields of the "REGION" structure pointed
* to by "rp". Because the dot and mark are usually very
* close together, we scan outward from dot looking for
* mark. This should save time. Return a standard code.
* Callers of this routine should be prepared to get
* an "ABORT" status; we might make this have the
* conform thing later.
*/
getregion(rp)
register REGION *rp;
{
register LINE *flp;
register LINE *blp;
register int fsize;
register int bsize;
if (curwp->w_markp == NULL) {
mlwrite("No mark set in this window");
return (FALSE);
}
if (curwp->w_dotp == curwp->w_markp) {
rp->r_linep = curwp->w_dotp;
if (curwp->w_doto < curwp->w_marko) {
rp->r_offset = curwp->w_doto;
rp->r_size = curwp->w_marko-curwp->w_doto;
} else {
rp->r_offset = curwp->w_marko;
rp->r_size = curwp->w_doto-curwp->w_marko;
}
return (TRUE);
}
blp = curwp->w_dotp;
bsize = curwp->w_doto;
flp = curwp->w_dotp;
fsize = llength(flp)-curwp->w_doto+1;
while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
if (flp != curbp->b_linep) {
flp = lforw(flp);
if (flp == curwp->w_markp) {
rp->r_linep = curwp->w_dotp;
rp->r_offset = curwp->w_doto;
rp->r_size = fsize+curwp->w_marko;
return (TRUE);
}
fsize += llength(flp)+1;
}
if (lback(blp) != curbp->b_linep) {
blp = lback(blp);
bsize += llength(blp)+1;
if (blp == curwp->w_markp) {
rp->r_linep = blp;
rp->r_offset = curwp->w_marko;
rp->r_size = bsize - curwp->w_marko;
return (TRUE);
}
}
}
mlwrite("Bug: lost mark");
return (FALSE);
}

View File

@ -0,0 +1,206 @@
/*
* The functions in this file
* implement commands that search in the
* forward and backward directions. There are
* no special characters in the search strings.
* Probably should have a regular expression
* search, or something like that.
*/
#include <stdio.h>
#include "ed.h"
/*
* Search forward.
* Get a search string from the
* user, and search, beginning at ".",
* for the string. If found, reset the
* "." to be just after the match string,
* and [perhaps] repaint the display.
* Bound to "M-S".
*/
forwsearch(f, n)
{
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register int c;
register char *pp;
register int s;
if ((s=readpattern("Search")) != TRUE)
return (s);
clp = curwp->w_dotp;
cbo = curwp->w_doto;
while (clp != curbp->b_linep) {
if (cbo == llength(clp)) {
clp = lforw(clp);
cbo = 0;
c = '\n';
} else
c = lgetc(clp, cbo++);
if (eq(c, pat[0]) != FALSE) {
tlp = clp;
tbo = cbo;
pp = &pat[1];
while (*pp != 0) {
if (tlp == curbp->b_linep)
goto fail;
if (tbo == llength(tlp)) {
tlp = lforw(tlp);
tbo = 0;
c = '\n';
} else
c = lgetc(tlp, tbo++);
if (eq(c, *pp++) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
fail: ;
}
mlwrite("Not found");
return (FALSE);
}
/*
* Reverse search.
* Get a search string from the
* user, and search, starting at "."
* and proceeding toward the front of
* the buffer. If found "." is left
* pointing at the first character of
* the pattern [the last character that
* was matched]. Bound to "M-R".
*/
backsearch(f, n)
{
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register int c;
register char *epp;
register char *pp;
register int s;
if ((s=readpattern("Reverse search")) != TRUE)
return (s);
for (epp = &pat[0]; epp[1] != 0; ++epp)
;
clp = curwp->w_dotp;
cbo = curwp->w_doto;
for (;;) {
if (cbo == 0) {
clp = lback(clp);
if (clp == curbp->b_linep) {
mlwrite("Not found");
return (FALSE);
}
cbo = llength(clp)+1;
}
if (--cbo == llength(clp))
c = '\n';
else
c = lgetc(clp, cbo);
if (eq(c, *epp) != FALSE) {
tlp = clp;
tbo = cbo;
pp = epp;
while (pp != &pat[0]) {
if (tbo == 0) {
tlp = lback(tlp);
if (tlp == curbp->b_linep)
goto fail;
tbo = llength(tlp)+1;
}
if (--tbo == llength(tlp))
c = '\n';
else
c = lgetc(tlp, tbo);
if (eq(c, *--pp) == FALSE)
goto fail;
}
curwp->w_dotp = tlp;
curwp->w_doto = tbo;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
fail: ;
}
}
/*
* Compare two characters.
* The "bc" comes from the buffer.
* It has it's case folded out. The
* "pc" is from the pattern.
*/
eq(bc, pc)
register int bc;
register int pc;
{
if (bc>='a' && bc<='z')
bc -= 0x20;
if (pc>='a' && pc<='z')
pc -= 0x20;
if (bc == pc)
return (TRUE);
return (FALSE);
}
/*
* Read a pattern.
* Stash it in the external
* variable "pat". The "pat" is
* not updated if the user types in
* an empty line. If the user typed
* an empty line, and there is no
* old pattern, it is an error.
* Display the old pattern, in the
* style of Jeff Lomicka. There is
* some do-it-yourself control
* expansion.
*/
readpattern(prompt)
char *prompt;
{
register char *cp1;
register char *cp2;
register int c;
register int s;
char tpat[NPAT+20];
cp1 = &tpat[0]; /* Copy prompt */
cp2 = prompt;
while ((c = *cp2++) != '\0')
*cp1++ = c;
if (pat[0] != '\0') { /* Old pattern */
*cp1++ = ' ';
*cp1++ = '[';
cp2 = &pat[0];
while ((c = *cp2++) != 0) {
if (cp1 < &tpat[NPAT+20-6]) { /* "??]: \0" */
if (c<0x20 || c==0x7F) {
*cp1++ = '^';
c ^= 0x40;
} else if (c == '%') /* Map "%" to */
*cp1++ = c; /* "%%". */
*cp1++ = c;
}
}
*cp1++ = ']';
}
*cp1++ = ':'; /* Finish prompt */
*cp1++ = ' ';
*cp1++ = '\0';
s = mlreply(tpat, tpat, NPAT); /* Read pattern */
if (s == TRUE) /* Specified */
strcpy(pat, tpat);
else if (s==FALSE && pat[0]!=0) /* CR, but old one */
s = TRUE;
return (s);
}

View File

@ -0,0 +1,181 @@
/*
* The routines in this file
* are called to create a subjob running
* a command interpreter. This code is a big
* fat nothing on CP/M-86. You lose.
*/
#include <stdio.h>
#include "ed.h"
#if VMS
#define EFN 0 /* Event flag. */
#include <ssdef.h> /* Random headers. */
#include <stsdef.h>
#include <descrip.h>
#include <iodef.h>
extern int oldmode[]; /* In "termio.c" */
extern int newmode[]; /* In "termio.c" */
extern short iochan; /* In "termio.c" */
#endif
#if MSDOS
#include <dos.h>
#endif
#if V7
#include <signal.h>
#endif
/*
* Create a subjob with a copy
* of the command intrepreter in it. When the
* command interpreter exits, mark the screen as
* garbage so that you do a full repaint. Bound
* to "C-C". The message at
* the start in VMS puts out a newline. Under
* some (unknown) condition, you don't get one
* free when DCL starts up.
*/
spawncli(f, n)
{
#if V7
register char *cp;
char *getenv();
#endif
#if VMS
movecursor(term.t_nrow, 0); /* In last line. */
mlputs("[Starting DCL]\r\n");
(*term.t_flush)(); /* Ignore "ttcol". */
sgarbf = TRUE;
return (sys(NULL)); /* NULL => DCL. */
#endif
#if CPM
mlwrite("Not in CP/M-86");
#endif
#if MSDOS
movecursor(term.t_nrow, 0); /* Seek to last line. */
(*term.t_flush)();
ttclose();
execall("\\command.com", ""); /* Run CLI. */
ttopen();
sgarbf = TRUE;
return(TRUE);
#endif
#if V7
movecursor(term.t_nrow, 0); /* Seek to last line. */
(*term.t_flush)();
ttclose(); /* stty to old settings */
if ((cp = getenv("SHELL")) != NULL && *cp != '\0')
system(cp);
else
system("exec /bin/sh");
sgarbf = TRUE;
sleep(2);
ttopen();
return(TRUE);
#endif
}
/*
* Run a one-liner in a subjob.
* When the command returns, wait for a single
* character to be typed, then mark the screen as
* garbage so a full repaint is done.
* Bound to "C-X !".
*/
spawn(f, n)
{
register int s;
char line[NLINE];
#if VMS
if ((s=mlreply("DCL command: ", line, NLINE)) != TRUE)
return (s);
(*term.t_putchar)('\n'); /* Already have '\r' */
(*term.t_flush)();
s = sys(line); /* Run the command. */
mlputs("\r\n\n[End]"); /* Pause. */
(*term.t_flush)();
while ((*term.t_getchar)() != '\r')
;
sgarbf = TRUE;
return (s);
#endif
#if CPM
mlwrite("Not in CP/M-86");
return (FALSE);
#endif
#if MSDOS
if ((s=mlreply("MS-DOS command: ", line, NLINE)) != TRUE)
return (s);
ttclose();
system(line);
ttopen();
mlputs("\n[End]");
while ((*term.t_getchar)() != '\r') /* Pause. */
;
sgarbf = TRUE;
return (TRUE);
#endif
#if V7
if ((s=mlreply("! ", line, NLINE)) != TRUE)
return (s);
(*term.t_putchar)('\n'); /* Already have '\r' */
(*term.t_flush)();
ttclose(); /* stty to old modes */
system(line);
sleep(2);
ttopen();
mlputs("[End]"); /* Pause. */
(*term.t_flush)();
while ((s = (*term.t_getchar)()) != '\r' && s != ' ')
;
sgarbf = TRUE;
return (TRUE);
#endif
}
#if VMS
/*
* Run a command. The "cmd" is a pointer
* to a command string, or NULL if you want to run
* a copy of DCL in the subjob (this is how the standard
* routine LIB$SPAWN works. You have to do wierd stuff
* with the terminal on the way in and the way out,
* because DCL does not want the channel to be
* in raw mode.
*/
sys(cmd)
register char *cmd;
{
struct dsc$descriptor cdsc;
struct dsc$descriptor *cdscp;
long status;
long substatus;
long iosb[2];
status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
oldmode, sizeof(oldmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
return (FALSE);
cdscp = NULL; /* Assume DCL. */
if (cmd != NULL) { /* Build descriptor. */
cdsc.dsc$a_pointer = cmd;
cdsc.dsc$w_length = strlen(cmd);
cdsc.dsc$b_dtype = DSC$K_DTYPE_T;
cdsc.dsc$b_class = DSC$K_CLASS_S;
cdscp = &cdsc;
}
status = LIB$SPAWN(cdscp, 0, 0, 0, 0, 0, &substatus, 0, 0, 0);
if (status != SS$_NORMAL)
substatus = status;
status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
newmode, sizeof(newmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
return (FALSE);
if ((substatus&STS$M_SUCCESS) == 0) /* Command failed. */
return (FALSE);
return (TRUE);
}
#endif

View File

@ -0,0 +1,126 @@
#include <stdio.h>
#include "ed.h"
#if TERMCAP
#define NROW 24
#define NCOL 80
#define BEL 0x07
#define ESC 0x1B
extern int ttopen();
extern int ttgetc();
extern int ttputc();
extern int ttflush();
extern int ttclose();
extern int tcapmove();
extern int tcapeeol();
extern int tcapeeop();
extern int tcapbeep();
extern int tcapopen();
extern int tput();
extern char *tgoto();
#define TCAPSLEN 315
char tcapbuf[TCAPSLEN];
char PC,
*CM,
*CL,
*CE,
*UP,
*CD;
TERM term = {
NROW-1,
NCOL,
&tcapopen,
&ttclose,
&ttgetc,
&ttputc,
&ttflush,
&tcapmove,
&tcapeeol,
&tcapeeop,
&tcapbeep
};
tcapopen()
{
char *getenv();
char *t, *p, *tgetstr();
char tcbuf[1024];
char *tv_stype;
char err_str[72];
if ((tv_stype = getenv("TERM")) == NULL)
{
puts("Environment variable TERM not defined!");
exit(1);
}
if((tgetent(tcbuf, tv_stype)) != 1)
{
sprintf(err_str, "Unknown terminal type %s!", tv_stype);
puts(err_str);
exit(1);
}
p = tcapbuf;
t = tgetstr("pc", &p);
if(t)
PC = *t;
CD = tgetstr("cd", &p);
CM = tgetstr("cm", &p);
CE = tgetstr("ce", &p);
UP = tgetstr("up", &p);
if(CD == NULL || CM == NULL || CE == NULL || UP == NULL)
{
puts("Incomplete termcap entry\n");
exit(1);
}
if (p >= &tcapbuf[TCAPSLEN])
{
puts("Terminal description too big!\n");
exit(1);
}
ttopen();
}
tcapmove(row, col)
register int row, col;
{
putpad(tgoto(CM, col, row));
}
tcapeeol()
{
putpad(CE);
}
tcapeeop()
{
putpad(CD);
}
tcapbeep()
{
ttputc(BEL);
}
putpad(str)
char *str;
{
tputs(str, 1, ttputc);
}
putnpad(str, n)
char *str;
{
tputs(str, n, ttputc);
}
#endif TERMCAP

View File

@ -0,0 +1,285 @@
/*
* The functions in this file
* negotiate with the operating system
* for characters, and write characters in
* a barely buffered fashion on the display.
* All operating systems.
*/
#include <stdio.h>
#include "ed.h"
#if VMS
#include <stsdef.h>
#include <ssdef.h>
#include <descrip.h>
#include <iodef.h>
#include <ttdef.h>
#define NIBUF 128 /* Input buffer size */
#define NOBUF 1024 /* MM says bug buffers win! */
#define EFN 0 /* Event flag */
char obuf[NOBUF]; /* Output buffer */
int nobuf; /* # of bytes in above */
char ibuf[NIBUF]; /* Input buffer */
int nibuf; /* # of bytes in above */
int ibufi; /* Read index */
int oldmode[2]; /* Old TTY mode bits */
int newmode[2]; /* New TTY mode bits */
short iochan; /* TTY I/O channel */
#endif
#if CPM
#include <bdos.h>
#endif
#if MSDOS
#include <dos.h>
#endif
#if V7
#include <sgtty.h> /* for stty/gtty functions */
struct sgttyb ostate; /* saved tty state */
struct sgttyb nstate; /* values for editor mode */
#endif
/*
* This function is called once
* to set up the terminal device streams.
* On VMS, it translates SYS$INPUT until it
* finds the terminal, then assigns a channel to it
* and sets it raw. On CPM it is a no-op.
*/
ttopen()
{
#if VMS
struct dsc$descriptor idsc;
struct dsc$descriptor odsc;
char oname[40];
int iosb[2];
int status;
odsc.dsc$a_pointer = "SYS$INPUT";
odsc.dsc$w_length = strlen(odsc.dsc$a_pointer);
odsc.dsc$b_dtype = DSC$K_DTYPE_T;
odsc.dsc$b_class = DSC$K_CLASS_S;
idsc.dsc$b_dtype = DSC$K_DTYPE_T;
idsc.dsc$b_class = DSC$K_CLASS_S;
do {
idsc.dsc$a_pointer = odsc.dsc$a_pointer;
idsc.dsc$w_length = odsc.dsc$w_length;
odsc.dsc$a_pointer = &oname[0];
odsc.dsc$w_length = sizeof(oname);
status = LIB$SYS_TRNLOG(&idsc, &odsc.dsc$w_length, &odsc);
if (status!=SS$_NORMAL && status!=SS$_NOTRAN)
exit(status);
if (oname[0] == 0x1B) {
odsc.dsc$a_pointer += 4;
odsc.dsc$w_length -= 4;
}
} while (status == SS$_NORMAL);
status = SYS$ASSIGN(&odsc, &iochan, 0, 0);
if (status != SS$_NORMAL)
exit(status);
status = SYS$QIOW(EFN, iochan, IO$_SENSEMODE, iosb, 0, 0,
oldmode, sizeof(oldmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
exit(status);
newmode[0] = oldmode[0];
newmode[1] = oldmode[1] | TT$M_PASSALL | TT$M_NOECHO;
status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
newmode, sizeof(newmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
exit(status);
#endif
#if CPM
#endif
#if MSDOS
/*
* Redefine cursor keys (as described in DOS Technical Reference Manual
* p. 2-11, DOS BASIC Manual p. G-6) to mean what the user might expect.
*/
static char *control[] = {
"\033[0;72;16p", /* up = <ctrl-p> */
"\033[0;77;6p", /* right = <ctrl-f> */
"\033[0;75;2p", /* left = <ctrl-b> */
"\033[0;80;14p", /* down = <ctrl-n> */
"\033[0;81;22p", /* pg dn = <ctrl-v> */
"\033[0;73;27;86p", /* pg up = <esc>V */
"\033[0;71;27;60p", /* home = <esc>< */
"\033[0;79;27;62p", /* end = <esc>> */
"\033[0;83;127p", /* del = del */
"\033[0;3;27;46p" /* <ctrl-@> = <esc>. */
};
register char *cp;
register int i;
for (i = 0; i < sizeof(control)/sizeof(char *); i++) {
for (cp = control[i]; *cp; )
ttputc(*cp++);
}
#endif
#if V7
gtty(1, &ostate); /* save old state */
gtty(1, &nstate); /* get base of new state */
nstate.sg_flags |= RAW;
nstate.sg_flags &= ~(ECHO|CRMOD); /* no echo for now... */
stty(1, &nstate); /* set mode */
#endif
}
/*
* This function gets called just
* before we go back home to the command interpreter.
* On VMS it puts the terminal back in a reasonable state.
* Another no-operation on CPM.
*/
ttclose()
{
#if VMS
int status;
int iosb[1];
ttflush();
status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
oldmode, sizeof(oldmode), 0, 0, 0, 0);
if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
exit(status);
status = SYS$DASSGN(iochan);
if (status != SS$_NORMAL)
exit(status);
#endif
#if CPM
#endif
#if MSDOS
/* Redefine cursor keys to default values. */
static char *control[] = {
"\033[0;72;0;72p",
"\033[0;77;0;77p",
"\033[0;75;0;75p",
"\033[0;80;0;80p",
"\033[0;81;0;81p",
"\033[0;73;0;73p",
"\033[0;71;0;71p",
"\033[0;79;0;79p",
"\033[0;83;0;83p",
"\033[0;3;0;3p"
};
register char *cp;
register int i;
for (i = 0; i < sizeof(control)/sizeof(char *); i++) {
for (cp = control[i]; *cp; )
ttputc(*cp++);
}
#endif
#if V7
stty(1, &ostate);
#endif
}
/*
* Write a character to the display.
* On VMS, terminal output is buffered, and
* we just put the characters in the big array,
* after cheching for overflow. On CPM terminal I/O
* unbuffered, so we just write the byte out.
* Ditto on MS-DOS (use the very very raw console
* output routine).
*/
ttputc(c)
{
#if VMS
if (nobuf >= NOBUF)
ttflush();
obuf[nobuf++] = c;
#endif
#if CPM
bios(BCONOUT, c, 0);
#endif
#if MSDOS
dosb(CONDIO, c, 0);
#endif
#if V7
fputc(c, stdout);
#endif
}
/*
* Flush terminal buffer. Does real work
* where the terminal output is buffered up. A
* no-operation on systems where byte at a time
* terminal I/O is done.
*/
ttflush()
{
#if VMS
int status;
int iosb[2];
status = SS$_NORMAL;
if (nobuf != 0) {
status = SYS$QIOW(EFN, iochan, IO$_WRITELBLK|IO$M_NOFORMAT,
iosb, 0, 0, obuf, nobuf, 0, 0, 0, 0);
if (status == SS$_NORMAL)
status = iosb[0] & 0xFFFF;
nobuf = 0;
}
return (status);
#endif
#if CPM
#endif
#if MSDOS
#endif
#if V7
fflush(stdout);
#endif
}
/*
* Read a character from the terminal,
* performing no editing and doing no echo at all.
* More complex in VMS that almost anyplace else, which
* figures. Very simple on CPM, because the system can
* do exactly what you want.
*/
ttgetc()
{
#if VMS
int status;
int iosb[2];
int term[2];
while (ibufi >= nibuf) {
ibufi = 0;
term[0] = 0;
term[1] = 0;
status = SYS$QIOW(EFN, iochan, IO$_READLBLK|IO$M_TIMED,
iosb, 0, 0, ibuf, NIBUF, 0, term, 0, 0);
if (status != SS$_NORMAL)
exit(status);
status = iosb[0] & 0xFFFF;
if (status!=SS$_NORMAL && status!=SS$_TIMEOUT)
exit(status);
nibuf = (iosb[0]>>16) + (iosb[1]>>16);
if (nibuf == 0) {
status = sys$qiow(EFN, iochan, IO$_READLBLK,
iosb, 0, 0, ibuf, 1, 0, term, 0, 0);
if (status != SS$_NORMAL
|| (status = (iosb[0]&0xFFFF)) != SS$_NORMAL)
exit(status);
nibuf = (iosb[0]>>16) + (iosb[1]>>16);
}
}
return (ibuf[ibufi++] & 0xFF); /* Allow multinational */
#endif
#if CPM
return (biosb(BCONIN, 0, 0));
#endif
#if MSDOS
return (dosb(CONRAW, 0, 0));
#endif
#if V7
return(fgetc(stdin));
#endif
}

View File

@ -0,0 +1,97 @@
/*
* The routines in this file
* provide support for VT52 style terminals
* over a serial line. The serial I/O services are
* provided by routines in "termio.c". It compiles
* into nothing if not a VT52 style device. The
* bell on the VT52 is terrible, so the "beep"
* routine is conditionalized on defining BEL.
*/
#include <stdio.h>
#include "ed.h"
#if VT52
#define NROW 24 /* Screen size. */
#define NCOL 80 /* Edit if you want to. */
#define BIAS 0x20 /* Origin 0 coordinate bias. */
#define ESC 0x1B /* ESC character. */
#define BEL 0x07 /* ascii bell character */
extern int ttopen(); /* Forward references. */
extern int ttgetc();
extern int ttputc();
extern int ttflush();
extern int ttclose();
extern int vt52move();
extern int vt52eeol();
extern int vt52eeop();
extern int vt52beep();
extern int vt52open();
/*
* Dispatch table. All the
* hard fields just point into the
* terminal I/O code.
*/
TERM term = {
NROW-1,
NCOL,
&vt52open,
&ttclose,
&ttgetc,
&ttputc,
&ttflush,
&vt52move,
&vt52eeol,
&vt52eeop,
&vt52beep
};
vt52move(row, col)
{
ttputc(ESC);
ttputc('Y');
ttputc(row+BIAS);
ttputc(col+BIAS);
}
vt52eeol()
{
ttputc(ESC);
ttputc('K');
}
vt52eeop()
{
ttputc(ESC);
ttputc('J');
}
vt52beep()
{
#ifdef BEL
ttputc(BEL);
ttflush();
#endif
}
#endif
vt52open()
{
#if V7
register char *cp;
char *getenv();
if ((cp = getenv("TERM")) == NULL) {
puts("Shell variable TERM not defined!");
exit(1);
}
if (strcmp(cp, "vt52") != 0 && strcmp(cp, "z19") != 0) {
puts("Terminal type not 'vt52'or 'z19' !");
exit(1);
}
#endif
ttopen();
}

View File

@ -0,0 +1,388 @@
/*
* Window management.
* Some of the functions are internal,
* and some are attached to keys that the
* user actually types.
*/
#include <stdio.h>
#include "ed.h"
/*
* Reposition dot in the current
* window to line "n". If the argument is
* positive, it is that line. If it is negative it
* is that line from the bottom. If it is 0 the window
* is centered (this is what the standard redisplay code
* does). With no argument it defaults to 1. Bound to
* M-!. Because of the default, it works like in
* Gosling.
*/
reposition(f, n)
{
curwp->w_force = n;
curwp->w_flag |= WFFORCE;
return (TRUE);
}
/*
* Refresh the screen. With no
* argument, it just does the refresh. With an
* argument it recenters "." in the current
* window. Bound to "C-L".
*/
refresh(f, n)
{
if (f == FALSE)
sgarbf = TRUE;
else {
curwp->w_force = 0; /* Center dot. */
curwp->w_flag |= WFFORCE;
}
return (TRUE);
}
/*
* The command make the next
* window (next => down the screen)
* the current window. There are no real
* errors, although the command does
* nothing if there is only 1 window on
* the screen. Bound to "C-X C-N".
*/
nextwind(f, n)
{
register WINDOW *wp;
if ((wp=curwp->w_wndp) == NULL)
wp = wheadp;
curwp = wp;
curbp = wp->w_bufp;
return (TRUE);
}
/*
* This command makes the previous
* window (previous => up the screen) the
* current window. There arn't any errors,
* although the command does not do a lot
* if there is 1 window.
*/
prevwind(f, n)
{
register WINDOW *wp1;
register WINDOW *wp2;
wp1 = wheadp;
wp2 = curwp;
if (wp1 == wp2)
wp2 = NULL;
while (wp1->w_wndp != wp2)
wp1 = wp1->w_wndp;
curwp = wp1;
curbp = wp1->w_bufp;
return (TRUE);
}
/*
* This command moves the current
* window down by "arg" lines. Recompute
* the top line in the window. The move up and
* move down code is almost completely the same;
* most of the work has to do with reframing the
* window, and picking a new dot. We share the
* code by having "move down" just be an interface
* to "move up". Magic. Bound to "C-X C-N".
*/
mvdnwind(f, n)
register int n;
{
return (mvupwind(f, -n));
}
/*
* Move the current window up by "arg"
* lines. Recompute the new top line of the window.
* Look to see if "." is still on the screen. If it is,
* you win. If it isn't, then move "." to center it
* in the new framing of the window (this command does
* not really move "."; it moves the frame). Bound
* to "C-X C-P".
*/
mvupwind(f, n)
register int n;
{
register LINE *lp;
register int i;
lp = curwp->w_linep;
if (n < 0) {
while (n++ && lp!=curbp->b_linep)
lp = lforw(lp);
} else {
while (n-- && lback(lp)!=curbp->b_linep)
lp = lback(lp);
}
curwp->w_linep = lp;
curwp->w_flag |= WFHARD; /* Mode line is OK. */
for (i=0; i<curwp->w_ntrows; ++i) {
if (lp == curwp->w_dotp)
return (TRUE);
if (lp == curbp->b_linep)
break;
lp = lforw(lp);
}
lp = curwp->w_linep;
i = curwp->w_ntrows/2;
while (i-- && lp!=curbp->b_linep)
lp = lforw(lp);
curwp->w_dotp = lp;
curwp->w_doto = 0;
return (TRUE);
}
/*
* This command makes the current
* window the only window on the screen.
* Bound to "C-X 1". Try to set the framing
* so that "." does not have to move on
* the display. Some care has to be taken
* to keep the values of dot and mark
* in the buffer structures right if the
* distruction of a window makes a buffer
* become undisplayed.
*/
onlywind(f, n)
{
register WINDOW *wp;
register LINE *lp;
register int i;
while (wheadp != curwp) {
wp = wheadp;
wheadp = wp->w_wndp;
if (--wp->w_bufp->b_nwnd == 0) {
wp->w_bufp->b_dotp = wp->w_dotp;
wp->w_bufp->b_doto = wp->w_doto;
wp->w_bufp->b_markp = wp->w_markp;
wp->w_bufp->b_marko = wp->w_marko;
}
free((char *) wp);
}
while (curwp->w_wndp != NULL) {
wp = curwp->w_wndp;
curwp->w_wndp = wp->w_wndp;
if (--wp->w_bufp->b_nwnd == 0) {
wp->w_bufp->b_dotp = wp->w_dotp;
wp->w_bufp->b_doto = wp->w_doto;
wp->w_bufp->b_markp = wp->w_markp;
wp->w_bufp->b_marko = wp->w_marko;
}
free((char *) wp);
}
lp = curwp->w_linep;
i = curwp->w_toprow;
while (i!=0 && lback(lp)!=curbp->b_linep) {
--i;
lp = lback(lp);
}
curwp->w_toprow = 0;
curwp->w_ntrows = term.t_nrow-1;
curwp->w_linep = lp;
curwp->w_flag |= WFMODE|WFHARD;
return (TRUE);
}
/*
* Split the current window. A window
* smaller than 3 lines cannot be split.
* The only other error that is possible is
* a "malloc" failure allocating the structure
* for the new window. Bound to "C-X 2".
*/
splitwind(f, n)
{
register WINDOW *wp;
register LINE *lp;
register int ntru;
register int ntrl;
register int ntrd;
register WINDOW *wp1;
register WINDOW *wp2;
if (curwp->w_ntrows < 3) {
mlwrite("Cannot split a %d line window", curwp->w_ntrows);
return (FALSE);
}
if ((wp = (WINDOW *) malloc(sizeof(WINDOW))) == NULL) {
mlwrite("Cannot allocate WINDOW block");
return (FALSE);
}
++curbp->b_nwnd; /* Displayed twice. */
wp->w_bufp = curbp;
wp->w_dotp = curwp->w_dotp;
wp->w_doto = curwp->w_doto;
wp->w_markp = curwp->w_markp;
wp->w_marko = curwp->w_marko;
wp->w_flag = 0;
wp->w_force = 0;
ntru = (curwp->w_ntrows-1) / 2; /* Upper size */
ntrl = (curwp->w_ntrows-1) - ntru; /* Lower size */
lp = curwp->w_linep;
ntrd = 0;
while (lp != curwp->w_dotp) {
++ntrd;
lp = lforw(lp);
}
lp = curwp->w_linep;
if (ntrd <= ntru) { /* Old is upper window. */
if (ntrd == ntru) /* Hit mode line. */
lp = lforw(lp);
curwp->w_ntrows = ntru;
wp->w_wndp = curwp->w_wndp;
curwp->w_wndp = wp;
wp->w_toprow = curwp->w_toprow+ntru+1;
wp->w_ntrows = ntrl;
} else { /* Old is lower window */
wp1 = NULL;
wp2 = wheadp;
while (wp2 != curwp) {
wp1 = wp2;
wp2 = wp2->w_wndp;
}
if (wp1 == NULL)
wheadp = wp;
else
wp1->w_wndp = wp;
wp->w_wndp = curwp;
wp->w_toprow = curwp->w_toprow;
wp->w_ntrows = ntru;
++ntru; /* Mode line. */
curwp->w_toprow += ntru;
curwp->w_ntrows = ntrl;
while (ntru--)
lp = lforw(lp);
}
curwp->w_linep = lp; /* Adjust the top lines */
wp->w_linep = lp; /* if necessary. */
curwp->w_flag |= WFMODE|WFHARD;
wp->w_flag |= WFMODE|WFHARD;
return (TRUE);
}
/*
* Enlarge the current window.
* Find the window that loses space. Make
* sure it is big enough. If so, hack the window
* descriptions, and ask redisplay to do all the
* hard work. You don't just set "force reframe"
* because dot would move. Bound to "C-X Z".
*/
enlargewind(f, n)
{
register WINDOW *adjwp;
register LINE *lp;
register int i;
if (n < 0)
return (shrinkwind(f, -n));
if (wheadp->w_wndp == NULL) {
mlwrite("Only one window");
return (FALSE);
}
if ((adjwp=curwp->w_wndp) == NULL) {
adjwp = wheadp;
while (adjwp->w_wndp != curwp)
adjwp = adjwp->w_wndp;
}
if (adjwp->w_ntrows <= n) {
mlwrite("Impossible change");
return (FALSE);
}
if (curwp->w_wndp == adjwp) { /* Shrink below. */
lp = adjwp->w_linep;
for (i=0; i<n && lp!=adjwp->w_bufp->b_linep; ++i)
lp = lforw(lp);
adjwp->w_linep = lp;
adjwp->w_toprow += n;
} else { /* Shrink above. */
lp = curwp->w_linep;
for (i=0; i<n && lback(lp)!=curbp->b_linep; ++i)
lp = lback(lp);
curwp->w_linep = lp;
curwp->w_toprow -= n;
}
curwp->w_ntrows += n;
adjwp->w_ntrows -= n;
curwp->w_flag |= WFMODE|WFHARD;
adjwp->w_flag |= WFMODE|WFHARD;
return (TRUE);
}
/*
* Shrink the current window.
* Find the window that gains space. Hack at
* the window descriptions. Ask the redisplay to
* do all the hard work. Bound to "C-X C-Z".
*/
shrinkwind(f, n)
{
register WINDOW *adjwp;
register LINE *lp;
register int i;
if (n < 0)
return (enlargewind(f, -n));
if (wheadp->w_wndp == NULL) {
mlwrite("Only one window");
return (FALSE);
}
if ((adjwp=curwp->w_wndp) == NULL) {
adjwp = wheadp;
while (adjwp->w_wndp != curwp)
adjwp = adjwp->w_wndp;
}
if (curwp->w_ntrows <= n) {
mlwrite("Impossible change");
return (FALSE);
}
if (curwp->w_wndp == adjwp) { /* Grow below. */
lp = adjwp->w_linep;
for (i=0; i<n && lback(lp)!=adjwp->w_bufp->b_linep; ++i)
lp = lback(lp);
adjwp->w_linep = lp;
adjwp->w_toprow -= n;
} else { /* Grow above. */
lp = curwp->w_linep;
for (i=0; i<n && lp!=curbp->b_linep; ++i)
lp = lforw(lp);
curwp->w_linep = lp;
curwp->w_toprow += n;
}
curwp->w_ntrows -= n;
adjwp->w_ntrows += n;
curwp->w_flag |= WFMODE|WFHARD;
adjwp->w_flag |= WFMODE|WFHARD;
return (TRUE);
}
/*
* Pick a window for a pop-up.
* Split the screen if there is only
* one window. Pick the uppermost window that
* isn't the current window. An LRU algorithm
* might be better. Return a pointer, or
* NULL on error.
*/
WINDOW *
wpopup()
{
register WINDOW *wp;
if (wheadp->w_wndp == NULL /* Only 1 window */
&& splitwind(FALSE, 0) == FALSE) /* and it won't split */
return (NULL);
wp = wheadp; /* Find window to use */
while (wp!=NULL && wp==curwp)
wp = wp->w_wndp;
return (wp);
}

View File

@ -0,0 +1,294 @@
/*
* The routines in this file
* implement commands that work word at
* a time. There are all sorts of word mode
* commands. If I do any sentence and/or paragraph
* mode commands, they are likely to be put in
* this file.
*/
#include <stdio.h>
#include "ed.h"
/* Word wrap on n-spaces.
* Back-over whatever precedes the point on the current line and
* stop on the first word-break or the beginning of the line.
* If we reach the beginning of the line, jump back to the end of the
* word and start a new line. Otherwise, break the line at the
* word-break, eat it, and jump back to the end of the word.
* NOTE: This function may leaving trailing blanks.
* Returns TRUE on success, FALSE on errors.
*/
wrapword(n)
int n;
{
register int cnt;
register char *oldp;
oldp = curwp->w_dotp;
cnt = -1;
do {
cnt++;
if (! backchar(NULL, 1))
return(FALSE);
}
while (! inword());
if (! backword(NULL, 1))
return(FALSE);
if (oldp == curwp->w_dotp && curwp->w_doto) {
if (! backdel(NULL, 1))
return(FALSE);
if (! newline(NULL, 1))
return(FALSE);
}
return(forwword(NULL, 1) && forwchar(NULL, cnt));
}
/*
* Move the cursor backward by
* "n" words. All of the details of motion
* are performed by the "backchar" and "forwchar"
* routines. Error if you try to move beyond
* the buffers.
*/
backword(f, n)
{
if (n < 0)
return (forwword(f, -n));
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
while (n--) {
while (inword() == FALSE) {
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
}
while (inword() != FALSE) {
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
return (forwchar(FALSE, 1));
}
/*
* Move the cursor forward by
* the specified number of words. All of the
* motion is done by "forwchar". Error if you
* try and move beyond the buffer's end.
*/
forwword(f, n)
{
if (n < 0)
return (backword(f, -n));
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
while (inword() != FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
return (TRUE);
}
/*
* Move the cursor forward by
* the specified number of words. As you move,
* convert any characters to upper case. Error
* if you try and move beyond the end of the
* buffer. Bound to "M-U".
*/
upperword(f, n)
{
register int c;
if (n < 0)
return (FALSE);
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
while (inword() != FALSE) {
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='a' && c<='z') {
c -= 'a'-'A';
lputc(curwp->w_dotp, curwp->w_doto, c);
lchange(WFHARD);
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
return (TRUE);
}
/*
* Move the cursor forward by
* the specified number of words. As you move
* convert characters to lower case. Error if you
* try and move over the end of the buffer.
* Bound to "M-L".
*/
lowerword(f, n)
{
register int c;
if (n < 0)
return (FALSE);
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
while (inword() != FALSE) {
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='A' && c<='Z') {
c += 'a'-'A';
lputc(curwp->w_dotp, curwp->w_doto, c);
lchange(WFHARD);
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
return (TRUE);
}
/*
* Move the cursor forward by
* the specified number of words. As you move
* convert the first character of the word to upper
* case, and subsequent characters to lower case. Error
* if you try and move past the end of the buffer.
* Bound to "M-C".
*/
capword(f, n)
{
register int c;
if (n < 0)
return (FALSE);
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
if (inword() != FALSE) {
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='a' && c<='z') {
c -= 'a'-'A';
lputc(curwp->w_dotp, curwp->w_doto, c);
lchange(WFHARD);
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
while (inword() != FALSE) {
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='A' && c<='Z') {
c += 'a'-'A';
lputc(curwp->w_dotp, curwp->w_doto, c);
lchange(WFHARD);
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
}
}
}
return (TRUE);
}
/*
* Kill forward by "n" words.
* Remember the location of dot. Move forward
* by the right number of words. Put dot back where
* it was and issue the kill command for the
* right number of characters. Bound to "M-D".
*/
delfword(f, n)
{
register int size;
register LINE *dotp;
register int doto;
if (n < 0)
return (FALSE);
dotp = curwp->w_dotp;
doto = curwp->w_doto;
size = 0;
while (n--) {
while (inword() == FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
++size;
}
while (inword() != FALSE) {
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
++size;
}
}
curwp->w_dotp = dotp;
curwp->w_doto = doto;
return (ldelete(size, TRUE));
}
/*
* Kill backwards by "n" words.
* Move backwards by the desired number of
* words, counting the characters. When dot is
* finally moved to its resting place, fire off
* the kill command. Bound to "M-Rubout" and
* to "M-Backspace".
*/
delbword(f, n)
{
register int size;
if (n < 0)
return (FALSE);
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
size = 0;
while (n--) {
while (inword() == FALSE) {
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
++size;
}
while (inword() != FALSE) {
if (backchar(FALSE, 1) == FALSE)
return (FALSE);
++size;
}
}
if (forwchar(FALSE, 1) == FALSE)
return (FALSE);
return (ldelete(size, TRUE));
}
/*
* Return TRUE if the character at dot
* is a character that is considered to be
* part of a word. The word character list is hard
* coded. Should be setable.
*/
inword()
{
register int c;
if (curwp->w_doto == llength(curwp->w_dotp))
return (FALSE);
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (c>='a' && c<='z')
return (TRUE);
if (c>='A' && c<='Z')
return (TRUE);
if (c>='0' && c<='9')
return (TRUE);
if (c=='$' || c=='_') /* For identifiers */
return (TRUE);
return (FALSE);
}

View File

@ -0,0 +1,4 @@
-xcc:\bin\
-xlc:\lib\
-xtc:\tmp\
-Ic:\include\

View File

@ -0,0 +1,35 @@
#include <stdio.h>
#ifndef MWC
#include <string.h>
#include <stdlib.h>
#endif
#define DIGITS_TO_FIND 200 /*9009*/
int main() {
int N = DIGITS_TO_FIND;
int x = 0;
int a[ DIGITS_TO_FIND ];
int n;
for (n = N - 1; n > 0; --n) {
a[n] = 1;
}
a[1] = 2, a[0] = 0;
while (N > 9) {
n = N--;
while (--n) {
a[n] = x % n;
x = 10 * a[n-1] + x/n;
}
printf("%d", x);
}
printf( "\ndone\n" );
return 0;
}

View File

@ -0,0 +1,35 @@
/* sieve.c
/* Eratosthenes Sieve Prime Number Program in C from Byte Jan 1983
to compare the speed. */
#include <stdio.h>
#define TRUE 1
#define FALSE 0
#define SIZE 8190
typedef int bool;
char flags[SIZE+1];
int main()
{
int i,k;
int prime,count,iter;
for (iter = 1; iter <= 10; iter++) { /* do program 10 times */
count = 0; /* initialize prime counter */
for (i = 0; i <= SIZE; i++) /* set all flags TRUE */
flags[i] = TRUE;
for (i = 0; i <= SIZE; i++) {
if (flags[i]) { /* found a prime */
prime = i + i + 3; /* twice index + 3 */
for (k = i + prime; k <= SIZE; k += prime)
flags[k] = FALSE; /* kill all multiples */
count++; /* primes found */
}
}
}
printf("%d primes.\n",count); /*primes found in 10th pass */
return 0;
}

View File

@ -0,0 +1,165 @@
#include <stdio.h>
#ifdef AZTEC86
#include <stdlib.h>
#endif
#ifdef HISOFTC
#include <stdlib.h>
#endif
#ifdef WATCOM
#include <malloc.h>
#include <process.h>
#endif
#ifdef powerc
#define allocs 50
#else
#ifdef HISOFTC
#define allocs 66 /* not enough RAM with hisoft to go higher */
#else
/* most c runtimes work up to 69, but use 66 to have a consistent benchmark */
#define allocs 66
#endif
#endif
int logging = 1;
char * memset_x( p, v, c ) char * p; int v; int c;
{
unsigned char * pc = (unsigned char *) p;
unsigned char val = (unsigned char) ( v & 0xff );
int i;
if ( 0 == p )
{
printf( "request to memset a null pointer\n" );
exit( 1 );
}
if ( logging )
#ifdef CPMTIME
printf( " memset p %u, v %d, val %x, c %d\n", p, v, val, c );
#else
#ifdef HISOFTC
printf( " memset p %u, v %d, val %x, c %d\n", p, v, val, c );
#else
printf( " memset p %p, v %d, val %x, c %d\n", p, v, val, c );
#endif
#endif
for ( i = 0; i < c; i++ )
*pc++ = val;
return p;
}
void chkmem( p, v, c ) char * p; int v; int c;
{
unsigned char * pc = (unsigned char *) p;
unsigned char val = (unsigned char) ( v & 0xff );
int i;
if ( 0 == p )
{
printf( "request to chkmem a null pointer\n" );
exit( 1 );
}
for ( i = 0; i < c; i++ )
{
if ( *pc != val )
{
#ifdef CPMTIME
printf( "memory isn't as expected! p %u, v %d, c %d, *pc %d\n",p, v, c, *pc );
#else
printf( "memory isn't as expected! p %p, v %d, c %d, *pc %d\n",p, v, c, *pc );
#endif
exit( 1 );
}
pc++;
}
}
int main( argc, argv ) int argc; char * argv[];
{
int i, cb, c_cb, j;
char * pc;
char * ap[ allocs ];
logging = ( argc > 1 );
pc = argv[ 0 ]; /* evade compiler warning */
for ( j = 0; j < 10; j++ )
{
if ( logging )
printf( "in alloc mode\n" );
for ( i = 0; i < allocs; i++ )
{
cb = 8 + ( i * 10 );
c_cb = cb + 5;
if ( logging )
printf( " i, cb: %d %d\n", i, cb );
pc = (char *) calloc( c_cb, 1 );
chkmem( pc, 0, c_cb );
memset_x( pc, 0xcc, c_cb );
ap[ i ] = (char *) malloc( cb );
memset_x( ap[ i ], 0xaa, cb );
chkmem( pc, 0xcc, c_cb );
free( pc );
}
if ( logging )
printf( "in free mode, even first\n" );
for ( i = 0; i < allocs; i += 2 )
{
cb = 8 + ( i * 10 );
c_cb = cb + 3;
if ( logging )
printf( " i, cb: %d %d\n", i, cb );
pc = (char *) calloc( c_cb, 1 );
chkmem( pc, 0, c_cb );
memset_x( pc, 0xcc, c_cb );
chkmem( ap[ i ], 0xaa, cb );
memset_x( ap[ i ], 0xff, cb );
free( ap[ i ] );
chkmem( pc, 0xcc, c_cb );
free( pc );
}
if ( logging )
printf( "in free mode, now odd\n" );
for ( i = 1; i < allocs; i += 2 )
{
cb = 8 + ( i * 10 );
c_cb = cb + 7;
if ( logging )
printf( " i, cb: %d %d\n", i, cb );
pc = (char *) calloc( c_cb, 1 );
chkmem( pc, 0, c_cb );
memset_x( pc, 0xcc, c_cb );
chkmem( ap[ i ], 0xaa, cb );
memset_x( ap[ i ], 0xff, cb );
free( ap[ i ] );
chkmem( pc, 0xcc, c_cb );
free( pc );
}
}
printf( "success\n" );
return 0;
}

View File

@ -0,0 +1,55 @@
#include <stdio.h>
#define HIGH_MARK 500 /* 2800 */
static long r[HIGH_MARK + 1];
int main() {
long i, k, c;
long b, d, v;
long iteration;
int iterations = 1;
for ( iteration = 0; iteration < iterations; iteration++ ) {
c = 0;
for (i = 0; i < HIGH_MARK; i++) {
r[i] = 2000;
}
for (k = HIGH_MARK; k > 0; k -= 14) {
d = 0;
i = k;
for (;;) {
d += r[i] * 10000;
b = 2 * i - 1;
r[i] = d % b;
d /= b;
i--;
if (i == 0) break;
d *= i;
}
if ( iteration == ( iterations - 1 ) )
{
#ifdef MWC
v = c + d / 10000;
if ( v < 10 )
printf( "000" );
else if ( v < 100 )
printf( "00" );
else if ( v < 1000 )
printf( "0" );
#endif
printf( "%.4d", c + d / 10000 );
fflush( stdout );
}
c = d % 10000;
}
}
printf( "\n" );
return 0;
}

View File

@ -0,0 +1,527 @@
/*
This version builds with old compilers including:
Aztec C 1.06 for 8080 & Z80 on CP/M.
Microsoft C Compiler V1.04 for 8086 on DOS. (This is Lattice C)
Microsoft C Compiler V2.03 for 8086 on DOS. (Still Lattice C)
Microsoft C Compiler V3.00 for 8086 on DOS.
QuickC 1.0
Turbo C 2.0
The syntax is old and reminds me of 7th grade summer vacation.
Much of this code is awkward to satisfy the lowest common denominator of many compilers.
unsigned long isn't supported in many older compilers, so long is used instead.
Early DOS and CP/M require register variabes to be int, not char or other types.
The perf improvement of using register-int instead of stack-char is worth it.
*/
#define LINT_ARGS
#include <stdio.h>
#ifdef DOSTIME
#include <time.h>
#include <dos.h>
#endif
#define true 1
#define false 0
/* Function Pointers are the fastest implementation for almost every compiler */
#define UseFunPointers 1
#define UseWinner2 2
#define UseLookForWinner 3
#define WinMethod UseFunPointers
#define ABPrune true /* alpha beta pruning */
#define WinLosePrune true /* stop early on win/lose */
#define ScoreWin 6
#define ScoreTie 5
#define ScoreLose 4
#define ScoreMax 9
#define ScoreMin 2
#define DefaultIterations 100
#define PieceX 1
#define PieceO 2
#define PieceBlank 0
typedef char ttype; /* 8-bit and 16-bit cpus do best with char aside from register in locals */
int g_Iterations = DefaultIterations;
ttype g_board[ 9 ];
#if WinMethod == UseFunPointers
ttype pos0func()
{
/* using "register int" instead of "ttype" for x is faster on 8086 and Z80 */
register int x = g_board[0];
if ( ( x == g_board[1] && x == g_board[2] ) ||
( x == g_board[3] && x == g_board[6] ) ||
( x == g_board[4] && x == g_board[8] ) )
return x;
return PieceBlank;
}
ttype pos1func()
{
register int x = g_board[1];
if ( ( x == g_board[0] && x == g_board[2] ) ||
( x == g_board[4] && x == g_board[7] ) )
return x;
return PieceBlank;
}
ttype pos2func()
{
register int x = g_board[2];
if ( ( x == g_board[0] && x == g_board[1] ) ||
( x == g_board[5] && x == g_board[8] ) ||
( x == g_board[4] && x == g_board[6] ) )
return x;
return PieceBlank;
}
ttype pos3func()
{
register int x = g_board[3];
if ( ( x == g_board[4] && x == g_board[5] ) ||
( x == g_board[0] && x == g_board[6] ) )
return x;
return PieceBlank;
}
ttype pos4func()
{
register int x = g_board[4];
if ( ( x == g_board[0] && x == g_board[8] ) ||
( x == g_board[2] && x == g_board[6] ) ||
( x == g_board[1] && x == g_board[7] ) ||
( x == g_board[3] && x == g_board[5] ) )
return x;
return PieceBlank;
}
ttype pos5func()
{
register int x = g_board[5];
if ( ( x == g_board[3] && x == g_board[4] ) ||
( x == g_board[2] && x == g_board[8] ) )
return x;
return PieceBlank;
}
ttype pos6func()
{
register int x = g_board[6];
if ( ( x == g_board[7] && x == g_board[8] ) ||
( x == g_board[0] && x == g_board[3] ) ||
( x == g_board[4] && x == g_board[2] ) )
return x;
return PieceBlank;
}
ttype pos7func()
{
register int x = g_board[7];
if ( ( x == g_board[6] && x == g_board[8] ) ||
( x == g_board[1] && x == g_board[4] ) )
return x;
return PieceBlank;
}
ttype pos8func()
{
register int x = g_board[8];
if ( ( x == g_board[6] && x == g_board[7] ) ||
( x == g_board[2] && x == g_board[5] ) ||
( x == g_board[0] && x == g_board[4] ) )
return x;
return PieceBlank;
}
typedef ttype pfunc_t();
pfunc_t * winner_functions[9] =
{
pos0func,
pos1func,
pos2func,
pos3func,
pos4func,
pos5func,
pos6func,
pos7func,
pos8func,
};
#endif
#if WinMethod == UseWinner2
ttype winner2( move ) ttype move;
{
register int x; /* faster than ttype x on the stack */
switch( move ) /* msc v3 from 1985 generates a jump table! */
{
case 0:
{
x = g_board[ 0 ];
if ( ( ( x == g_board[1] ) && ( x == g_board[2] ) ) ||
( ( x == g_board[3] ) && ( x == g_board[6] ) ) ||
( ( x == g_board[4] ) && ( x == g_board[8] ) ) )
return x;
break;
}
case 1:
{
x = g_board[ 1 ];
if ( ( ( x == g_board[0] ) && ( x == g_board[2] ) ) ||
( ( x == g_board[4] ) && ( x == g_board[7] ) ) )
return x;
break;
}
case 2:
{
x = g_board[ 2 ];
if ( ( ( x == g_board[0] ) && ( x == g_board[1] ) ) ||
( ( x == g_board[5] ) && ( x == g_board[8] ) ) ||
( ( x == g_board[4] ) && ( x == g_board[6] ) ) )
return x;
break;
}
case 3:
{
x = g_board[ 3 ];
if ( ( ( x == g_board[4] ) && ( x == g_board[5] ) ) ||
( ( x == g_board[0] ) && ( x == g_board[6] ) ) )
return x;
break;
}
case 4:
{
x = g_board[ 4 ];
if ( ( ( x == g_board[0] ) && ( x == g_board[8] ) ) ||
( ( x == g_board[2] ) && ( x == g_board[6] ) ) ||
( ( x == g_board[1] ) && ( x == g_board[7] ) ) ||
( ( x == g_board[3] ) && ( x == g_board[5] ) ) )
return x;
break;
}
case 5:
{
x = g_board[ 5 ];
if ( ( ( x == g_board[3] ) && ( x == g_board[4] ) ) ||
( ( x == g_board[2] ) && ( x == g_board[8] ) ) )
return x;
break;
}
case 6:
{
x = g_board[ 6 ];
if ( ( ( x == g_board[7] ) && ( x == g_board[8] ) ) ||
( ( x == g_board[0] ) && ( x == g_board[3] ) ) ||
( ( x == g_board[4] ) && ( x == g_board[2] ) ) )
return x;
break;
}
case 7:
{
x = g_board[ 7 ];
if ( ( ( x == g_board[6] ) && ( x == g_board[8] ) ) ||
( ( x == g_board[1] ) && ( x == g_board[4] ) ) )
return x;
break;
}
case 8:
{
x = g_board[ 8 ];
if ( ( ( x == g_board[6] ) && ( x == g_board[7] ) ) ||
( ( x == g_board[2] ) && ( x == g_board[5] ) ) ||
( ( x == g_board[0] ) && ( x == g_board[4] ) ) )
return x;
break;
}
}
return PieceBlank;
} /*winner2*/
#endif
#if WinMethod == UseLookForWinner
ttype LookForWinner()
{
register int p = g_board[0]; /* faster as register int than ttype on 8086 and Z80 */
if ( PieceBlank != p )
{
if ( p == g_board[1] && p == g_board[2] )
return p;
if ( p == g_board[3] && p == g_board[6] )
return p;
}
p = g_board[3];
if ( PieceBlank != p && p == g_board[4] && p == g_board[5] )
return p;
p = g_board[6];
if ( PieceBlank != p && p == g_board[7] && p == g_board[8] )
return p;
p = g_board[1];
if ( PieceBlank != p && p == g_board[4] && p == g_board[7] )
return p;
p = g_board[2];
if ( PieceBlank != p && p == g_board[5] && p == g_board[8] )
return p;
p = g_board[4];
if ( PieceBlank != p )
{
if ( ( p == g_board[0] ) && ( p == g_board[8] ) )
return p;
if ( ( p == g_board[2] ) && ( p == g_board[6] ) )
return p;
}
return PieceBlank;
} /*LookForWinner*/
#endif
int g_IMoves = 0;
ttype MinMax( alpha, beta, depth, move ) ttype alpha; ttype beta; ttype depth; ttype move;
{
ttype pieceMove, score; /* better perf with char than int. out of registers so use stack */
register int p, value; /* better perf with these as an int on Z80, 8080, and 8086 */
g_IMoves++;
if ( depth >= 4 )
{
#if WinMethod == UseFunPointers
p = ( * winner_functions[ move ] )();
#endif
#if WinMethod == UseWinner2
p = winner2( move );
#endif
#if WinMethod == UseLookForWinner
p = LookForWinner();
#endif
if ( PieceBlank != p )
{
if ( PieceX == p )
return ScoreWin;
return ScoreLose;
}
if ( 8 == depth )
return ScoreTie;
}
if ( depth & 1 )
{
value = ScoreMin;
pieceMove = PieceX;
}
else
{
value = ScoreMax;
pieceMove = PieceO;
}
for ( p = 0; p < 9; p++ )
{
if ( PieceBlank == g_board[ p ] )
{
g_board[p] = pieceMove;
score = MinMax( alpha, beta, depth + 1, p );
g_board[p] = PieceBlank;
if ( depth & 1 )
{
#if WinLosePrune /* #if statements must be in first column for MS C 1.0 */
if ( ScoreWin == score )
return ScoreWin;
#endif
if ( score > value )
{
value = score;
#if ABPrune
if ( value >= beta )
return value;
if ( value > alpha )
alpha = value;
#endif
}
}
else
{
#if WinLosePrune
if ( ScoreLose == score )
return ScoreLose;
#endif
if ( score < value )
{
value = score;
#if ABPrune
if ( value <= alpha )
return value;
if ( value < beta )
beta = value;
#endif
}
}
}
}
return value;
} /*MinMax*/
long g_Moves = 0;
int FindSolution( position ) ttype position;
{
register int i;
for ( i = 0; i < 9; i++ )
g_board[ i ] = PieceBlank;
g_board[ position ] = PieceX;
for ( i = 0; i < g_Iterations; i++ )
{
g_IMoves = 0;
MinMax( ScoreMin, ScoreMax, 0, position );
g_Moves += g_IMoves; /* do the 4-byte long addition once per loop to save work */
}
return 0;
} /*FindSolution*/
#ifdef CPMTIME
struct CPMTimeValue
{
int h, m, s, l;
};
void print_time_now()
{
/* This CP/M BDOS call of 105 is only implemented in NTVCM -- it's not a standard CP/M 2.2 call */
struct CPMTimeValue t;
t.h = t.m = t.s = t.l = 0;
bdos( 105, &t );
printf( "current time: %02d:%02d:%02d.%02d\n", t.h, t.m, t.s, t.l );
} /*print_time_now*/
long get_ms()
{
/* This CP/M BDOS call of 105 is only implemented in NTVCM -- it's not a standard CP/M 2.2 call */
long h, m, s, l;
struct CPMTimeValue t;
t.h = t.m = t.s = t.l = 0;
bdos( 105, &t );
h = t.h;
m = t.m;
s = t.s;
l = t.l;
return h * 3600000 + m * 60000 + s * 1000 + l * 10;
} /*get_ms*/
#else /* no elif with old compilers */
#ifdef DOSTIME
void print_time_now()
{
/* Make a DOS interrupt call to get the time */
union REGS wrIn, wrOut;
wrIn.h.ah = 0x2c;
intdos( &wrIn, &wrOut );
printf( "current time: %02d:%02d:%02d.%02d\n", wrOut.h.ch, wrOut.h.cl, wrOut.h.dh, wrOut.h.dl );
fflush( stdout );
} /*print_time_now*/
long get_ms()
{
/* this function takes about 3 milliseconds on the original IBM PC */
long h, m, s, l;
union REGS wrIn, wrOut;
wrIn.h.ah = 0x2c;
intdos( &wrIn, &wrOut );
h = wrOut.h.ch;
m = wrOut.h.cl;
s = wrOut.h.dh;
l = wrOut.h.dl;
return h * 3600000 + m * 60000 + s * 1000 + l * 10;
} /*get_ms*/
#else
/* must do this on actual CP/M machines */
int print_time_now() { return 0; }
long get_ms() { return 0; }
#endif
#endif
int main( argc, argv ) int argc; char * argv[];
{
long start_time, end_time;
if ( 2 == argc )
sscanf( argv[ 1 ], "%d", &g_Iterations ); /* no atoi in MS C 1.0 */
start_time = get_ms();
FindSolution( 0 );
FindSolution( 1 );
FindSolution( 4 );
end_time = get_ms();
printf( "runtime in ms: %ld\n", end_time - start_time );
printf( "move count: %ld\n", g_Moves ); /* 6493 * g_Iterations */
printf( "iteration count: %d\n", g_Iterations );
printf( "method: %s\n",
( WinMethod == UseFunPointers ) ? "function pointers" :
( WinMethod == UseWinner2 ) ? "winner2" :
( WinMethod == UseLookForWinner ) ? "look for winner" :
"invalid method" );
return 0;
} /*main*/

View File

@ -0,0 +1,2 @@
ntvdm -r:.. "-e:CCHEAD=@C:\lib\cc -f -DMWC" ..\bin\cc %1.c -lm