Mark Williams C v4.0.12

This commit is contained in:
davidly 2024-07-01 16:08:53 -07:00
parent 3448f09847
commit 2a21533966
113 changed files with 10956 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.

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,21 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Modes for the access
* system call.
*/
#define AREAD 04 /* Test for read */
#define AWRITE 02 /* Test for write */
#define AEXEC 01 /* Test for execute */
#define AAPPND AWRITE /* Test for append */
/* Dummy directory modes */
#define ALIST AREAD /* List directory */
#define ADEL AWRITE /* Delete directory entry */
#define ASRCH AEXEC /* Search directory */
#define ACREAT AAPPND /* Create directory entry */

View File

@ -0,0 +1,24 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* assert.h
* 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
/* end of assert.h */

View File

@ -0,0 +1,41 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 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+1)[(c)]&_A)
#define isupper(c) ((_ctype+1)[(c)]&_U)
#define islower(c) ((_ctype+1)[(c)]&_L)
#define isdigit(c) ((_ctype+1)[(c)]&_D)
#define isalnum(c) ((_ctype+1)[(c)]&(_A|_D))
#define isspace(c) ((_ctype+1)[(c)]&_S)
#define ispunct(c) ((_ctype+1)[(c)]&_P)
#define isprint(c) ((_ctype+1)[(c)]&(_P|_X|_A|_D))
#define iscntrl(c) ((_ctype+1)[(c)]&_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 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 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,52 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* MSDOS dosfind.h.
*/
#ifndef DOSFIND_H
#define DOSFIND_H
#define MAXDOSDIR 64 /* Max directory path length */
/*
* MSDOS time and date.
* See DOS Technical Reference Manual, pp. 4-6, 4-7.
*/
struct dostime {
unsigned int dos_twosec:5; /* Seconds/2 0-29 */
unsigned int dos_minute:6; /* Minute 0-59 */
unsigned int dos_hour :5; /* Hour 0-23 */
unsigned int dos_day :5; /* Day 1-31 */
unsigned int dos_month :4; /* Month 1-12 */
unsigned int dos_year :7; /* Year-1980 0-199 */
};
/*
* Structure returned by DOS calls NFFIRST and NFNEXT.
* See DOS Technical Reference Manual, pp. 5-46, 4-6.
*/
struct dosfind {
char fnd_info[21]; /* Reserved for DOS */
char fnd_attr; /* File attribute */
struct dostime fnd_time; /* MSDOS time and date */
long fnd_size; /* File size */
char fnd_name[13]; /* Filename without dir */
};
/*
* MSDOS mode bits (attributes).
* See DOS Technical Reference Manual, pp. 4-5, 4-6.
*/
#define S_RDONLY 0x0001 /* Readonly */
#define S_HIDDEN 0x0002 /* Hidden */
#define S_SYSTEM 0x0004 /* System */
#define S_VOLUME 0x0008 /* Volume label */
#define S_SUBDIR 0x0010 /* Subdirectory */
#define S_ARCHIV 0x0020 /* Archive bit */
#endif

View File

@ -0,0 +1,69 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/* larges.h */
/* Model-dependent definitions for i8086 assembler sources. */
#ifndef LARGES_H
#define LARGES_H
#if LARGE
#define LARGECODE 1
#define LARGEDATA 1
#endif
#if LARGECODE
/* LARGE code: calls, jumps and returns are far, code pointer is 4 bytes. */
#define Gcall xcall
#define Gicall xicall
#define Gijmp xijmp
#define Gjmp xjmp
#define Gptr .dword
#define Gret xret
#define CPL 4
#else
/* SMALL code: calls, jumps and returns are near, code pointer is 2 bytes. */
#define Gcall call
#define Gicall icall
#define Gijmp ijmp
#define Gjmp jmp
#define Gptr .word
#define Gret ret
#define CPL 2
#endif
#if LARGEDATA
/* LARGE data: segment registers must be loaded to map data, escapes required. */
/* Data pointer is 4 bytes. */
#define Gdptr .dword
#define Lds lds
#define Les les
#define Map(sreg, temp, val) mov temp, val; mov sreg, temp
#define Pes es:
#define Pss ss:
#define DPL 4
#else
/* SMALL data: DS==ES==SS, no escapes required. */
/* Data pointer is 2 bytes. */
#define Gdptr .word
#define Lds mov
#define Les mov
#define Map(sreg, temp, val)
#define Pes
#define Pss
#define DPL 2
#endif
/* Function entry and exit: preserve C register variables, initialize BP. */
/* No automatics allocated. */
#define Enter(x) .globl x; x: push si; push di; push bp; mov bp, sp
#define Leave pop bp; pop di; pop si; Gret
#define LEFTARG CPL+6
#define RASIZE CPL
#define PTRSIZE DPL
#endif

View File

@ -0,0 +1,89 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Definitions for math library.
*/
#ifndef MATH_H
#define MATH_H
#define HUGE_VAL 1.79769313486231e+308 /* Infinity */
#define L2I 1023.0 /* log2(infinity) */
#define L10P 16 /* 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();
/*
* Math library functions.
*/
double acos();
double asin();
double atan();
double atan2();
double cabs();
double ceil();
double cos();
double cosh();
double exp();
double fabs();
double floor();
double hypot();
double j0();
double j1();
double jn();
double log();
double log10();
double pow();
double sin();
double sinh();
double sqrt();
double tan();
double tanh();
/*
* C library floating point functions.
*/
double atof();
double frexp();
double ldexp();
double modf();
#endif

View File

@ -0,0 +1,29 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* Machine types.
*/
#ifndef MTYPE_H
#define MTYPE_H
/*
* Type definitions.
*/
#define M_PDP11 1 /* PDP-11 */
#define M_VAX 2 /* VAX */
#define M_360 3 /* IBM 360 */
#define M_Z8001 4 /* Zilog 8001 (segmented) */
#define M_Z8002 5 /* Zilog 8002 (unsegmented) */
#define M_8086 6 /* Intel 8086 */
#define M_8080 7 /* Intel 8080 */
#define M_6800 8 /* Motorola 6800 */
#define M_6809 9 /* Motorola 6809 */
#define M_68000 10 /* Motorola 68000 */
#define M_NS16000 11 /* National Semiconductor 16000 */
#define M_LRG8086 12 /* Intel 8086 Large Model */
#endif

View File

@ -0,0 +1,42 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* path.h
* For COHERENT, GEMDOS, MSDOS.
*/
#include <access.h>
#if COHERENT
#define PATHSEP '/' /* Path name component separator */
#define PATHSEPSTRING "/" /* PATHSEP as a string */
#define LISTSEP ':' /* Search list component separator */
#define DEFPATH ":/bin:/usr/bin"
#define DEFLIBPATH "/lib:/usr/lib"
#define DEFSHELL "sh"
#endif
#if GEMDOS
#define PATHSEP '\\'
#define PATHSEPSTRING "\\"
#define LISTSEP ','
#define DEFPATH ",\\bin,\\usr\\bin"
#define DEFLIBPATH "\\lib,,\\usr\\lib"
#define DEFSHELL "msh.prg"
#endif
#if MSDOS
#define PATHSEP '\\'
#define PATHSEPSTRING "\\"
#define LISTSEP ';'
#define DEFPATH ";\\bin;\\usr\\bin"
#define DEFLIBPATH "\\lib;\\usr\\lib"
#define DEFSHELL "command.com"
#endif
#define MAXPATH 128 /* Size of static pathname buffer */
extern char *path(); /* (char *path, *file; int mode;) */

View File

@ -0,0 +1,26 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 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,38 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* MSDOS stat.h.
*/
#ifndef STAT_H
#define STAT_H
#include <dosfind.h>
#include <time.h>
/*
* Structure returned by stat.
* The field names are compatible with COHERENT stat.h.
* COHERENT stat.h fields which are meaningless under MSDOS are not included.
* The st_dostime of the root is always 00:00 of 1/1/80.
*/
struct stat {
unsigned short st_mode; /* Mode */
long st_size; /* Size */
struct dostime st_dostime; /* MSDOS time and date */
time_t st_mtime; /* COHERENT modification time */
};
/*
* COHERENT-compatible mode bits.
*/
#define S_IFMT 0x0300 /* Type */
#define S_IFDIR 0x0100 /* Directory */
#define S_IFREG 0x0200 /* Regular */
#define S_IREAD 0x0400 /* Read permission, always 1 */
#define S_IWRITE 0x0800 /* Write permission */
#endif

View File

@ -0,0 +1,83 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 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)
/* The following definitions are for S-V style tmpnam() and tempnam(). */
#define L_tmpnam 64
#define P_tmpdir "\\tmp"

View File

@ -0,0 +1,169 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* time.h -- time and date services.
*/
#ifndef TIME_H
#define TIME_H
/*
* Clock function for process timing.
* This returns raw ticks, CLK_TCK per second,
* for the process's personal interpretation.
*/
#if GEMDOS
#define CLK_TCK 200 /* 200 hz timer */
typedef unsigned long clock_t; /* 200 hz timer ticks */
#endif
#if MSDOS
#define CLK_TCK 18.20648193359375 /* 1193180/65536 counts/sec */
typedef unsigned long clock_t; /* Timer ticks */
#endif
clock_t clock(); /* (void) */
/*
* Time difference as a double.
*/
double difftime(); /* (time_t time1, time_t time2) */
/*
* Time function for calendar time.
* Draft ANSI standard leaves time_t implementation-dependent:
* ours is time_t is 32 bits of seconds,
* beginning January 1, 1970, 0h00m00s GMT.
*/
typedef unsigned long time_t;
time_t time(); /* (time_t *timer) */
/*
* Time fields structure: struct "tm",
* aliased to "tm_t" for neatness.
* This structure is filled in by localtime() and gmtime().
* Note that "tm_mon" is 0 through 11,
* and "tm_year" is number of years since 1900.
*/
typedef struct tm {
int tm_sec; /* Seconds (0-59) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23); 0 = midnight */
int tm_mday; /* Day of month (1..28,29,30,31) */
int tm_mon; /* Month (0-11); 0 = January */
int tm_year; /* Year AD since 1900 */
int tm_wday; /* Day of week (0-6): 0 = Sunday */
int tm_yday; /* Day of year (0-365,366) */
int tm_isdst; /* Daylight savings time flag: */
/* Non-standard, they make negative==NA */
} tm_t;
/*
* Calendar time conversion functions:
*/
char *asctime(); /* tm_t fields to ascii string */
char *ctime(); /* time_t * to ascii string */
tm_t *gmtime(); /* time_t * to standard time fields */
tm_t *localtime(); /* time_t * to local time fields */
/*
* Mark Williams extensions to the draft ANSI standard:
*/
/*
* Timezone and daylight savings time implementation.
* localtime() parses the TIMEZONE environmental parameter
* or default values to set timezone, tzname[][], and daylight
* savings time parameters.
*/
extern long timezone; /* Seconds subtracted from standard time */
extern int dstadjust; /* Seconds added to local standard */
extern char tzname[2][32]; /* Names of local standard and daylight zone */
/*
* Calendar utilities:
* Gregorian calendar counted from October 1582; Julian calendar
* before that.
* The month of Gregorian adjustment isn't right.
*/
int isleapyear(); /* (year) AD */
int dayspermonth(); /* (month, year) [1..12], AD */
/*
* Julian day structure consists of the days and seconds since
* Greenwich mean noon of January 1st 4713 BC.
* COHERENT time_t is a variation of Julian time:
* it counts seconds from Julian day 2,440,587.5 (January 1, 1970).
*/
typedef struct { long j_d, j_s; } jday_t;
#define COHEPOCH 2440587L /* Julian day 1969.12.31 12h00m00s */
jday_t time_to_jday(); /* COHERENT time into Julian date */
time_t jday_to_time(); /* Julian date to coherent time */
jday_t tm_to_jday(); /* tm_t structure into Julian date */
tm_t *jday_to_tm(); /* Julian date into tm_t structure */
#if GEMDOS
/*
* Atari ST support.
* TOS time & date: packed binary date and time
* A "tetd_t" object is the object of xbios functions Gettime() and Settime().
* The halves are the objects of gemdos Tgettime(), Tgetdate(), Tsettime(),
* Tsetdate().
* The file time and date stamps that Fdatime(), Fsfirst(),
* and Fsnext() maintain are rtetd_t format:
* they reverse the order of the time
* and date fields from the utetd_t format used by Gettime() and Settime().
*/
typedef unsigned long tetd_t;
typedef struct { unsigned g_date, g_time; } utetd_t;
typedef struct { unsigned g_rtime, g_rdate; } rtetd_t;
tetd_t tm_to_tetd(); /* tm_t structure into TOS time and date */
tm_t *tetd_to_tm(); /* TOS time and date into tm_t */
#define tetd_to_jday(td) tm_to_jday(tetd_to_tm(td))
#define jday_to_tetd(jd) tm_to_tetd(jday_to_tm(jd))
#define tetd_to_time(td) jday_to_time(tetd_to_jday(td))
#define time_to_tetd(t) jday_to_tetd(time_to_jday(t))
/*
* The intelligent keyboard keeps time to the second
* which neither xbios or gemdos support.
* These two functions operate on tm_t objects.
*/
tm_t *Kgettime(); /* (void) */
int Ksettime(); /* (tm_t *timep) */
#endif
#if MSDOS
/* MSDOS support. */
/* Information returned by DOS calls GETDATE and GETTIME. */
/* See DOS Technical Reference Manual, p. 5-30. */
#define Year(r) (r.r_cx)
#define Month(r) (r.r_dx >> 8)
#define Day(r) (r.r_dx & 0xFF)
#define Hour(r) (r.r_cx >> 8)
#define Minute(r) (r.r_cx & 0xFF)
#define Second(r) (r.r_dx >> 8)
#define Hundredth(r) (r.r_dx & 0xFF)
/* COHERENT-compatible time buffer. */
struct timeb {
time_t time; /* Time since 1970. */
unsigned short millitm; /* Milliseconds. */
short timezone; /* Time zone -- obsolete. */
short dstflag; /* DST applies -- obsolete. */
};
tm_t *gettime(); /* (void) */
int settime(); /* (tm_t *timep) */
int stime(); /* (time_t *timep) */
#endif
#endif
/* end of time.h */

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,180 @@
#shell_main
The Mark Williams Shell (MWS) provides an integrated environment for
editing, compiling, debugging and executing your programs. To select
one of these functions, use the arrow keys to position the cursor bar
over the desired choice, then press return.
To leave the MWS, press the <esc> key. Short descriptions of
each command follow.
EDIT: Create a new file or edit an existing one. Edit invokes the
MicroEMACS editor.
COMPILE: Compile an existing C program. In the Compile menu, you
may choose whatever compiler options you need, or simply execute a
prior command line.
RUN: Use RUN to execute a compiled program.
DEBUG: Invoke the C Source Debugger (csd) to begin a debugging
session. The entire dialogue in csd will reference your C source code.
MAKE: Create a script for complex compilation sequences.
Speeds compile time by eliminating unnecessary compilation.
BUFFERS: Tailor the in-RAM compilation/accelerator to your needs.
Allocate more memory, or add expanded memory, for even faster compilation.
QUICK DOS: Accepts one DOS command and returns to MWS.
!DOS ESCAPE: Exit to DOS for unlimited DOS commands.
In-RAM compilations and accelerator are still active. Type 'exit'
to return to MWS.
NEW DIRECTORY: Changes the current working directory.
#shell_main_edit
Edit invokes the MicroEMACS editor.
EXECUTE: Invokes the editor for the file named in the menu box.
FILES: Lists files in current directory available for editing.
Follow with the execute command to invoke the editor.
NEW FILE: Lets you name a new file to edit.
Follow with the Execute command to invoke the editor.
#shell_main_edit_file
Name a new file to edit. Follow with the Execute command to
to invoke the editor.
#shell_main_edit_files
Select file(s) for editing. Then, use the Execute command to begin edit.
#shell_main_compile
EXECUTE: Invoke compiler and linker based on command line in command box.
OPTIONS: Choose options for the compile command line.
End list with END key.
Backspace key deletes an option from the command line.
FILES: List files in current directory with '.C' or '.OBJ' suffix.
Use to add appropriate file(s) to command line. Follow with Execute
command to compile.
#shell_main_compile_files
List files in current directory with '.C' or '.OBJ' suffix.
Use to add appropriate file(s) to command line. Follow with Execute
command to compile.
#shell_main_compile_options
Choose an option by moving the highlight bar to the option and pressing return.
Press the <end> key once all desired options are selected.
Delete an option by moving the highlight bar to the desired option
and pressing backspace.
For detailed information on each option, see the Lexicon entry for 'cc'
or the introduction to the manual.
#shell_main_run
EXECUTE: Run a compiled program listed in the command box.
ARGUMENTS: After file is selected, use Arguments to enter the desired
run time arguments in the command box.
FILES: Lists '.EXE' files(s) in current directory. Choose one, then
select Execute to run.
#shell_main_run_arguments
ARGUMENTS: After file is selected, use Arguments to enter the desired
runtime arguments in the command box.
#shell_main_quickDOS
Accepts and executes one DOS command, then returns to MWS.
#shell_main_directory
Enter desired directory pathname, followed by return.
#shell_main_debug
Debug your programs with the csd C Source-Level Debugger.
EXECUTE: Invokes the debugger for the file and options displayed in
the command box.
OPTIONS: Choose options for command line. End list with <end> key.
Backspace key deletes an option from the command line.
ARGUMENTS: After selecting file to debug, use Arguments to
enter command line parameters.
FILES: Lists '.exe' files in current directory. Note: files must have
been compiled using the '-vcsd' option for csd to debug your program.
#shell_main_debug_options
Choose an option by moving the highlight bar to the option and
pressing backspace.
For detailed information on each option, see the Commands Reference
section in the csd C Source Debugger manual.
#shell_main_debug_arguments
Select file to debug, then use Arguments to enter the
target program's command line parameters.
#shell_main_debug_files
Choose the .EXE file to debug.
In order to use csd on a .EXE file,
the program must have been compiled using the -VCSD option.
#shell_main_make
MAKE provides a systematic and efficient procedure for compiling
a program comprised of several source files.
EXECUTE: Invoke the makefile in the current directory based on
the information in the command box.
OPTIONS: Choose options for the make command. End list with <end> key.
Backspace key deletes an option from command line.
MACROS: Create macros used to eliminate repetitive typing or allow
special option at run time.
TARGETS: Enter name(s) of target file(s) to be made.
#shell_main_make_options
Choose an option by moving the highlight bar to the option and pressing
return.
Press the <end> key once all desired options are selected.
Delete an option by moving the highlight bar to the option and
pressing backspace.
For detailed information on each option, see the Lexicon entry for
Make or the tutorial for Make in the manual.
#shell_main_make_macros
Macros should be of the form 'a=b'. Macros can increase the flexibility
of your makefiles.
#shell_main_make_targets
Enter name(s) of file(s) to be made.
#shell_main_make_Alternate makefile
Enter the name of the makefile if its name is not 'makefile'
#shell_main_buffers
Tailor the in-RAM compilation accelerator to your needs.
Allocate memory or add expanded memory for faster compilation.
LOAD/UNLOAD: Tell MWS to load or unload the accelerator.
DISK DRIVES: Select disk drives to accelerate. The default is
'*', or all drives accelerated.
BUFFER SIZE: Change the size of the buffer reserved for the accelerator.
Default size is 128K.
WRITE OPTIONS: Choose how data will be saved to disk: memory, timed save
or disk. For details on write options, see the Let's C manual.
SAVE DATA: Write to disk all data stored in the accelerator's buffer.
CHANGE DISK: Write to disk all data in the buffer, then clear the buffer for
a new disk.
EXPANDED MEMORY: Tell MWS to create buffers in the expanded memory rather
than in main memory.
#shell_main_buffers_load/unload
Load or unload the accelerator.
#shell_main_buffers_drive
Select the disk drives you want to accelerate. The default is '*',
or all drives accelerated.
#shell_main_buffers_size
Change the size of the buffer reserved for the accelerator.
Default size is 128K.
#shell_main_buffers_write
Choose how data will be written to disk: Memory, Timed Save, or
disk. For complete information on the write options, see
the Let's C manual.
#shell_main_buffers_save
Write to disk all data stored in the accelerator's buffer.
#shell_main_buffers_change
Write to disk all data stored in the accelerator's buffer, then clear
the buffer for a new disk.
#shell_main_buffers_expanded
Creates buffers in the expanded memory rather that in main memory.

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,12 @@
#
# Let's C Version 4.0.4.
# Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
# All rights reserved. May not be copied or disclosed without permission.
#
# Default actions.
.c.o .c.obj .s.o .s.obj .m.o .m.obj .asm.obj:
$(CC) $(CFLAGS) -c $<
.o.exe .obj.exe:
$(CC) $(LDFLAGS) -o $@ $<

View File

@ -0,0 +1,14 @@
#
# Let's C Version 4.0.4.
# Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
# All rights reserved. May not be copied or disclosed without permission.
#
# Default macro definitions.
.SUFFIXES: .exe .o .obj .c .m .s .asm
AS = as
ASFLAGS = -gx
CC = cc
CFLAGS =
LDFLAGS =

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,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,66 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 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 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 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,367 @@
/*
* 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 forwards 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.
* 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.
* Bound to "C-V".
*/
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. We do a hard update for exactly
* the same reason.
* Bound to "M-V".
*/
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, because 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);
}
/*
* Go to a specific line in the buffer, mostly for
* looking up errors in C programs, which give the
* error a line number. If an argument is present, then
* it is the line number, else prompt for a line number
* to use.
*/
gotoline(f, n)
register int n;
{
register LINE *clp;
register int s;
char buf[32];
if (f == FALSE) {
if ((s=mlreply("Goto line: ", buf, sizeof(buf))) != TRUE)
return (s);
n = atoi(buf);
}
if (n <= 0) {
mlwrite("Bad line");
return (FALSE);
}
clp = lforw(curbp->b_linep); /* "clp" is first line */
while (n != 1) {
if (clp == curbp->b_linep) {
mlwrite("Line too large");
return (FALSE);
}
clp = lforw(clp);
--n;
}
curwp->w_dotp = clp;
curwp->w_doto = 0;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* Go to a specific line from the original file, mostly for
* looking up errors in C programs, which give the
* error a line number. If an argument is present, then
* it is the line number, else prompt for a line number
* to use.
*/
gotofline(f, n)
register int n;
{
register LINE *clp;
register int s;
char buf[32];
if (f == FALSE) {
if ((s=mlreply("Goto line: ", buf, sizeof(buf))) != TRUE)
return (s);
n = atoi(buf);
}
if (n <= 0) {
mlwrite("Bad line");
return (FALSE);
}
clp = lforw(curbp->b_linep); /* "clp" is first line */
while (n != l_number(clp)) {
if (clp == curbp->b_linep) {
mlwrite("Line not in buffer");
return (FALSE);
}
clp = lforw(clp);
}
curwp->w_dotp = clp;
curwp->w_doto = 0;
curwp->w_flag |= WFMOVE;
return (TRUE);
}

View File

@ -0,0 +1,153 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* bios.h
* IBM PC ROM BIOS information.
* Cf. "I.B.M. PC Technical Reference", Appendix A, pp. 2-4.
* Using this information guarantees that your program will NOT be portable.
*/
/*
* Standard system pointer.
*/
struct seg {
unsigned short offs;
unsigned short segv;
};
/*
* Item in keyboard buffer.
*/
struct creg {
char scan_code;
char ascii;
};
/*
* ROM BIOS data area starts at location 0.
*/
struct rom_data { /* emulated ROM BIOS data area */
struct seg int_vect[0x100]; /* interrupt vectors */
short rs232_base[4]; /* addresses of rs232 adapters */
short printer_base[4]; /* addresses of printers */
short equip_flag; /* installed hardware */
char mfg_tst; /* initialization flag */
short memory_size; /* memory size in Kbytes */
short io_ram_size; /* memory in I/O channel */
/*
* Keyboard data areas.
*/
char kb_flag; /* shift flags */
char kb_flag_1; /* second byte of kb status */
char alt_input; /* alternate keypad */
short buffer_head; /* pointer to head of kbd buff */
short buffer_tail; /* pointer to tail of kbd buff */
struct creg kb_buffer[16];
/*
* Diskette data areas.
*/
char seek_status; /* drive recalibration status */
char motor_status; /* bit 3-0 drive 3-0 currently running */
char motor_count; /* timeout counter for drive turn off */
char diskette_status;
char nec_status[7]; /* status bytes from NEC */
/*
* Video display area.
*/
char crt_mode;
short crt_cols; /* number of cols on screen */
short crt_len; /* length of CRT region in bytes */
short crt_start; /* start of crt buffer (SEG ADDR) */
short cursor_posn[8]; /* cursor position for 8 pages */
short cursor_mode; /* current cursor mode setting */
char active_page; /* current page being displayed */
short addr_6845; /* base addr of active display card */
char crt_mode_set; /* current setting of 3*8 register */
char crt_palette; /* current palette setting color card */
/*
* Cassette data area.
*/
short edge_cnt;
short crc_reg;
char last_val;
/*
* Timer data area.
*/
short timer_low; /* low word of timer count */
short timer_hi; /* hi word of timer count */
char timer_ofl; /* timer has rolled over since last read */
/*
* System data area.
*/
char bios_break; /* bit 7 = 1 if break key depressed */
short reset_flag; /* 0x1234 if kb reset underway */
/*
* Fixed disk data area.
*/
short fixed_disk_data[2];
/*
* Timeout counters.
*/
char prt_tim_out;
char rs232_tim_out;
};
/*
* Shift flag equates within kb_flag.
*/
#define INS_STATE 0x80
#define CAPS_STATE 0x40
#define NUM_STATE 0x20
#define SCROLL_STATE 0x10
#define ALT_SHIFT 0x08
#define CTL_SHIFT 0x04
#define LEFT_SHIFT 0x02
#define RIGHT_SHIFT 0x01
/*
* Shift flag equates within kb_flag1.
*/
#define INS_SHIFT 0x80
#define CAPS_SHIFT 0x40
#define NUM_SHIFT 0x20
#define SCROLL_SHIFT 0x10
#define HOLD_STATE 0x08 /* hold state has been toggled */
/*
* Diskette_status flags.
*/
#define TIME_OUT 0x80
#define BAD_SEEK 0x40
#define BAD_NEC 0x20 /* NEC Controller has failed */
#define BAD_CRC 0x10 /* bad CRC on diskette read */
#define DMA_BOUNDARY 0x09 /* DMA across DMA boundary */
#define BAD_DMA 0x08 /* DMA Overrun */
#define RECORD_NOT_FND 0x04 /* requested sector not found */
#define WRITE_PROTECT 0x03 /* write protect violation */
#define BAD_ADDR_MARK 0x02 /* address mark not found */
#define BAD_COMMAND 0x01 /* bad command to diskette I/O */
/*
* Timer equates.
* Approximations of 1193180/65536 counts/sec.
*/
#define COUNTS_SEC 18
#define COUNTS_MIN 1092
#define COUNTS_HOUR 65543L
#define COUNTS_DAY 0x1800B0L
/*
* Port addresses.
*/
#define KB_DATA 0x60 /* keyboard scan code port */
#define KB_CTL 0x61 /* control bits for kbd sense data */
#define TIMER 0x40 /* 8253/timer counter */
#define TIM_CTL 0x43 /* 8253/timer control port */
#define DMA08 0x08 /* DMA status reg port */
#define DMA 0x00 /* DMA channel 0 addr reg port */
/* end of bios.h */

View File

@ -0,0 +1,109 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* biosdata.c
* Print snapshot of ROM BIOS data area.
*/
#include <stdio.h>
#include <dos.h>
#include "bios.h"
struct rom_data d;
main()
{
register int i;
/* Copy ROM BIOS data. */
_copy(PTR(&d), 0, 0, sizeof(d));
/* Interrupt vectors. */
printf("Interrupt vectors:\n");
for(i = 0; i<0x100; )
{
printf("%02x %04x:%04x",
i, d.int_vect[i].offs, d.int_vect[i].segv);
if(++i % 5)
printf(" ");
else
printf("\n");
}
printf("\n\nRS-232 adapter addrs: %04x %04x %04x %04x\n",
d.rs232_base[0], d.rs232_base[1],
d.rs232_base[2], d.rs232_base[3]);
printf("Printer addresses: %04x %04x %04x %04x\n",
d.printer_base[0], d.printer_base[1],
d.printer_base[2], d.printer_base[3]);
/* Machine flags. */
printf("\nMachine flags:\n");
printf("equip_flag\t%04x\n", d.equip_flag);
printf("mfg_tst\t\t%02x\n", d.mfg_tst);
printf("memory_size\t%04x\n", d.memory_size);
printf("io_ram_size\t%04x\n", d.io_ram_size);
/* Keyboard data areas. */
printf("\nKeyboard data areas:\n");
printf("kb_flag\t\t%02x\n", d.kb_flag);
printf("kb_flag_1\t%02x\n", d.kb_flag_1);
printf("alt_input\t%02x\n", d.alt_input);
printf("buffer_head\t%04x\n", d.buffer_head);
printf("buffer_tail\t%04x\n", d.buffer_tail);
printf("buffer scan codes ");
for(i = 0; i < 16; i++)
printf("%02x ", d.kb_buffer[i].scan_code);
printf("\nbuffer ascii ");
for(i = 0; i < 16; i++)
printf("%02x ", d.kb_buffer[i].ascii);
/* Diskette data areas. */
printf("\n\nDiskette data areas:\n");
printf("seek_status\t%02x\n", d.seek_status);
printf("motor_status\t%02x\n", d.motor_status);
printf("motor_count\t%02x\n", d.motor_count);
printf("diskette_status\t%02x\n", d.diskette_status);
printf("NEC status bytes ");
for(i = 0; i < 7; i++)
printf("%02x ", d.nec_status[i]);
/* Video display area. */
printf("\n\nVideo display area:\n");
printf("crt_mode\t%02x\n", d.crt_mode);
printf("crt_cols\t%04x\n", d.crt_cols);
printf("crt_len\t\t%04x\n", d.crt_len);
printf("crt_start\t%04x\n", d.crt_start);
printf("cursor_posn\t%04x\n", d.cursor_posn);
printf("cursor_mode\t%04x\n", d.cursor_mode);
printf("active_page\t%02x\n", d.active_page);
printf("addr_6845\t%04x\n", d.addr_6845);
printf("crt_mode_set\t%02x\n", d.crt_mode_set);
printf("crt_palette\t%02x\n", d.crt_palette);
/* Cassette data area. */
printf("\nCassette data area:\n");
printf("edge_cnt\t%04x\n", d.edge_cnt);
printf("crc_reg\t\t%04x\n", d.crc_reg);
printf("last_val\t%02x\n", d.last_val);
/* Timer data area. */
printf("\nTimer data area:\n");
printf("timer_low\t%04x\n", d.timer_low);
printf("timer_hi\t%04x\n", d.timer_hi);
printf("timer_ofl\t%02x\n", d.timer_ofl);
/* System data area. */
printf("\nSystem data area:\n");
printf("bios_break\t%02x\n", d.bios_break);
printf("reset_flag\t%04x\n", d.reset_flag);
/* Timeout counters. */
printf("\nTimeout counters:\n");
printf("prt_tim_out\t%02x\n", d.prt_tim_out);
printf("rs232_tim_out\t%02x\n", d.rs232_tim_out);
}
/* end of biosdata.c */

View File

@ -0,0 +1,358 @@
/*
* 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 GEM
fixname(bufn);
#endif
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 GEM
fixname(bufn);
#endif
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 BUFFER *bp;
register LINE *lp;
register long nlines;
register long nbytes;
register int c;
char b[6+1];
char line[128];
blistp->b_flag &= ~BFCHG; /* Don't complain! */
if ((c=bclear(blistp)) != TRUE) /* Blow old text away */
return (c);
strcpy(blistp->b_fname, "");
if (addline("C Size Lines 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. */
nlines = 0; /* Count lines in buf. */
lp = lforw(bp->b_linep);
while (lp != bp->b_linep) {
nlines++;
nbytes += llength(lp)+1;
lp = lforw(lp);
}
ltoa(b, 6, nbytes); /* 6 digit buffer size. */
cp2 = &b[0];
while ((c = *cp2++) != 0)
*cp1++ = c;
*cp1++ = ' '; /* Gap. */
ltoa(b, 6, nlines); /* 6 digit # of lines */
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+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 */
}
ltoa(buf, width, num)
register char buf[];
register int width;
register long 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|BFNOWRT|BFERROR))==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,98 @@
;+
; Let's C Version 4.0.C.
; Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
; All rights reserved. May not be copied or disclosed without permission.
;-
name crts0xl
;+
; DOS LARGE model C runtime startoff.
;-
;+
; At compile time:
; Declare the classes 'code', 'data', 'stack', 'memory' and 'debug'
; in the right order so that LINK gets it right.
; Set up a minimal stack of 2048 bytes.
; Set up public label '_mbase_' in the memory segment
; so C knows where the program ends.
; At run time:
; Save the program segment prefix base address in '_pspbase_'
; so C knows where the program begins.
; Free all memory past the end of the program for 'malloc_()'.
; Initialize BP to NULL for 'longjmp_()'.
; Call '_main_()'.
; Provide low level C callable '_exit_()'.
;-
extrn _main_:far
extrn _pspbase_:word
extrn __end_:word
code segment public 'code'
code ends
data segment public 'data'
data ends
stack segment stack 'stack'
db 2048 dup (?)
stack ends
memory segment memory 'memory'
public _mbase_
_mbase_ label word
memory ends
debug segment byte public 'debug'
debug ends
code segment public 'code'
public _exit_
assume cs:code
crts0 proc far
mov ax, seg _pspbase_ ; Set DS to map the
mov ds, ax ; _pspbase_ segment
assume ds:seg _pspbase_ ; and tell the assembler.
mov _pspbase_, es ; Initialize _pspbase.
mov ax, seg __end_ ; Set DS to map the
mov ds, ax ; __end segment
assume ds:seg __end_ ; and tell the assembler.
mov bx, memory ; BX gets end of memory segment
sub ax, ax ; and AX gets offset.
mov __end_, ax ; Initialize __end offset
mov __end_+2, bx ; and segment.
sub bp, bp ; Clear BP for longjmp.
call _main_ ; Call _main which calls main.
push ax ; Save return status,
push ax ; push fake far return address
push ax ; and fall through to _exit.
;+
; void _exit(s) int s;
;
; Low level exit.
; Return to caller with exit status in AL.
; Since CSD catches end of execution at this label,
; all program terminations should pass through _exit_.
;-
_exit_ label far
cld ; For DOS.
add sp, 4 ; Discard return address and
pop ax ; get exit status in AL.
mov ah, 4Ch ; Load return exit status function code
int 21h ; and do it.
crts0 endp
code ends
end crts0

View File

@ -0,0 +1,106 @@
;+
; Let's C Version 4.0.C.
; Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
; All rights reserved. May not be copied or disclosed without permission.
;-
name crts0xs
;+
; DOS SMALL model C runtime startoff.
;-
;+
; At compile time:
; Declare the classes 'CODE', 'DATA', 'CONST', 'STACK', 'MEMORY'
; and 'DEBUG' in the right order so that LINK gets it right.
; Set up a minimal stack of 2048 bytes.
; Set up public label 'end_' in the MEMORY segment
; so C knows where the program ends.
; At run time:
; Set up segment registers.
; Save the program segment prefix base address in '_pspbase_'
; so C knows where the program begins.
; Initialize BP to NULL for 'longjmp_()'.
; Call '_main_()'.
; Provide low level C callable '_exit_()'.
;-
extrn _main_:near
code segment public 'code'
code ends
data segment public 'data'
; The following declaration prevents LINK from putting data at address DS:0.
dw 0
extrn __end_:word
extrn _pspbase_:word
data ends
const segment public 'const'
const ends
stack segment stack 'stack'
db 2048 dup (?)
stack ends
memory segment memory 'memory'
public end_
end_ label word
memory ends
debug segment byte public 'debug'
debug ends
cgroup group code
dgroup group const, data, stack, memory
code segment public 'code'
public _exit_
assume cs:cgroup
crts0 proc near
mov ax, dgroup ; Initialize SS and DS
mov ss, ax ; with the
mov ds, ax ; base of dgroup
assume ds:dgroup ; and tell
assume ss:dgroup ; the assembler.
mov _pspbase_, es ; Initialize _pspbase.
mov es, ax ; Initialize ES
assume es:dgroup ; and tell the assembler.
lea ax, end_ ; Initialize
mov __end_, ax ; __end_.
mov sp, ax ; Initialize stack pointer.
sub bp, bp ; Clear BP for longjmp.
call _main_ ; Call _main which calls main.
push ax ; Save return status,
push ax ; push fake return address
; and fall through to _exit.
;+
; void _exit(s) int s;
;
; Low level exit.
; Return to caller with exit status in AL.
; Since CSD catches end of execution at this label,
; all program terminations should pass through _exit_.
;-
_exit_ label near
cld ; For DOS.
pop ax ; Discard return address and
pop ax ; get exit status in AL.
mov ah, 4Ch ; Load return exit status function code
int 21h ; and do it.
crts0 endp
code ends
end crts0

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|IBM
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, (int)(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 = PROMPT; /* 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,263 @@
/*
* 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.
*/
#ifdef COHERENT
#define V7 1 /* V7 UN*X or Coherent */
#define VMS 0 /* VAX/VMS */
#define CPM 0 /* CP/M-86 */
#define MSDOS 0 /* MS-DOS */
#define GEM 0 /* GEMDOS */
#define PROMPT " Coherent MicroEMACS -- "
#endif
#ifdef GEMDOS
#define V7 0 /* V7 UN*X or Coherent */
#define VMS 0 /* VAX/VMS */
#define CPM 0 /* CP/M-86 */
#define MSDOS 0 /* MS-DOS */
#define GEM 1 /* GEMDOS */
#define UPPERNM 0 /* if 0 names in all lower case */
#define PROMPT " ST MicroEMACS V1.6 -- "
#endif
#ifdef MSDOS
#define V7 0 /* V7 UN*X or Coherent */
#define VMS 0 /* VAX/VMS */
#define CPM 0 /* CP/M-86 */
#define GEM 0 /* GEMDOS */
#define PROMPT " DOS MicroEMACS V4.0.12 -- "
#endif
#ifdef COHERENT
#define ANSI 0
#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 1 /* Use TERMCAP */
#endif
#ifdef GEMDOS
#define ANSI 0
#define VT52 1 /* 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 */
#endif
#ifdef MSDOS
#ifndef IBM
#define ANSI 1 /* Use ANSI.SYS */
#else
#define ANSI 0
#endif
#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 */
#endif
#ifndef PROMPT
#define PROMPT " MicroEMACS -- "
#endif
#define CVMVAS 1 /* C-V, M-V arg. in screens. */
#define NCFILES 5 /* Max # of files on command line */
#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 NSRCH 128 /* undoable search command len */
#define ERRLINES 3 /* error window lines displayed */
#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 */
#define BFERROR 0x04 /* Error file buffer */
#define BFNOWRT 0x08 /* Don't write this buffer */
#define BFBACKP 0x10 /* Rename file before write */
#define BFWRITE 0x20 /* Written since read */
#define BFTRUNC 0x40 /* File truncated */
/*
* 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 */
long l_lnumber; /* Line number in original file */
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)
#define l_number(lp) ((lp)->l_lnumber)
/*
* 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;
/* Command line switch flags */
#define CF_ERROR (0x0001) /* Error edit switch specified */
#define CF_BACKUP (0x0002) /* Rename file before write sw */
#define CF_WARN (0x0004) /* Warn user before overwrite */
#define CF_DEBUG (0x8000) /* Debugging flag */
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 int runswitch; /* Switch flags */
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 BUFFER *errbp; /* Error file buffer */
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 char *ufiles[]; /* command-line specified files */
extern int ffold; /* Fold in search flag. */
extern char errfile[]; /* error file name */
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,205 @@
#define ERRLINES 3 /* Number of lines in error window. */
/*
* Micro-EMACS extension for MWC -- Search for next/previous error in
* error buffer. This module must "know" the format of each type of
* error message produced by the compiler that the error message is
* generated by.
*
*/
#include <stdio.h>
#include "ed.h"
/*
* Find a window associated with the buffer list...
*/
WINDOW *wfind(bp)
BUFFER *bp;
{
register WINDOW *wp;
wp = wheadp;
while (wp != NULL) {
if (wp->w_bufp == bp)
return wp;
wp = wp->w_wndp;
}
return NULL;
}
/*
* Search forward in the error buffer for an error message.
*/
forwerr(dir)
{
register LINE *lp;
register WINDOW *wp;
register char *tp;
register int c;
register int error_line;
char error_text[81];
char *et;
if ((wp = wfind(errbp)) == NULL)
return FALSE;
error_line = 0;
lp = wp->w_dotp;
if (wp->w_doto) {
if (dir)
lp = lp->l_bp;
else
lp = lp->l_fp;
}
while (lp != errbp->b_linep) {
if((tp = &lp->l_text[0]) == NULL)
goto next_line;
while (((c = *tp++ - '0') >= 0) && (c <= 9)) {
error_line *= 10;
error_line += c;
}
et = error_text;
while ((*et++ = *tp++) != 0) {
if((et - error_text) > (term.t_ncol - 15))
break;
if((tp - lp->l_text) > (lp->l_used - 1))
break;
}
*et = 0;
mlwrite("[%d:%s]",error_line, error_text); /* Debug! */
if (error_line != 0)
break;
next_line:
if (dir)
lp = lp->l_bp; /* Get the prev line... */
else
lp = lp->l_fp; /* Get the next line... */
}
if (error_line == 0) {
mlwrite("[No more errors.]");
return FALSE;
}
wp->w_dotp = lp; /* set dot to this line */
wp->w_doto = 1;
wp->w_force = 1;
wp->w_flag |= WFFORCE | WFMOVE;
gotofline(TRUE, error_line);
return TRUE;
}
/*
* make sure we have an error window...
*/
errwind()
{
register BUFFER *bp;
register BUFFER *obp;
register WINDOW *wp;
register int r;
if ((runswitch & CF_ERROR) == 0) {
mlwrite("[no error buffer]");
return FALSE;
}
if ((bp = errbp) == NULL) {
mlwrite( "[No error buffer]" );
return FALSE;
}
if (bp->b_nwnd == 0) { /* If this buffer is not */
if ((wp=wpopup()) == NULL) { /* in a window, display it */
mlwrite("[Can't open window for error buffer]");
return FALSE;
}
obp = wp->w_bufp; /* Get the old buffer ptr */
if (--obp->b_nwnd == 0) {
obp->b_dotp = wp->w_dotp;
obp->b_doto = wp->w_doto;
obp->b_markp = wp->w_markp;
obp->b_marko = wp->w_marko;
}
wp->w_bufp = bp;
++bp->b_nwnd;
wp->w_linep = lback(bp->b_dotp);
wp->w_dotp = bp->b_dotp;
wp->w_doto = bp->b_doto;
wp->w_markp = bp->b_markp;
wp->w_marko = bp->b_marko;
wp->w_flag |= WFMODE|WFHARD;
if ((r = wp->w_ntrows - ERRLINES) > 0) {
BUFFER *owp;
owp = curwp;
curwp = wp;
shrinkwind(0,r);
curwp = owp;
}
}
return TRUE;
}
/*
* Function to find the next error line in the error file
*/
nexterr(f,n)
{
if (n < 0)
return preverr(f, -n);
if (errwind() == FALSE)
return FALSE;
return forwerr(0);
}
/*
* Function to find the previous error line in the error buffer.
*/
preverr(f,n)
{
if (n < 0)
return nexterr(f, -n);
if (errwind() == FALSE)
return FALSE;
return forwerr(-1);
}
/*
* Read in the error file, giving it a buffer...
*/
readerr()
{
register BUFFER *bp;
char bname[NBUFN];
makename(bname, "error-buffer"); /* New buffer name. */
#if GEM
fixname(bname);
fixname(errfile);
#endif
if ((errbp = bp = bfind(bname, TRUE, 0)) == NULL) {
mlwrite("Cannot create error 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;
}
bp->b_flag |= BFNOWRT|BFERROR; /* Mark the buffer... */
curbp = bp; /* Switch to it. */
curwp->w_bufp = bp;
curbp->b_nwnd++;
return (readin(errfile)); /* Read it in. */
}

View File

@ -0,0 +1,99 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* example.c
* This program demonstrates the use of MS-DOS interrupt routines.
* It works in either LARGE or SMALL model.
* See also: int.c, intdis.m.
* To compile: cc example.c int.c intdis.m -ns -na
*/
#define INT_BREAK 0x1B /* Keyboard ctrl-break interrupt */
#define INT_TICK 0x1C /* System timer tick interrupt */
#define STACKSIZE 0x100 /* Small stack for locals */
#define TRUE 1
#define FALSE 0
int breakflag = FALSE;
int timerflag = FALSE;
main()
{
int breaktrp(), timertrp();
int breakid, timerid;
/* Initialize the interrupts. */
if ( (breakid=setint(INT_BREAK, breaktrp, STACKSIZE, 1)) == -1 )
fatal("Cannot set Ctrl-Break interrupt.");
else
puts("Ctrl-Break interrupt set.");
if ( (timerid=setint(INT_TICK, timertrp, STACKSIZE, 1)) == -1 )
fatal("Cannot set timer tick interrupt.");
else
puts("Timer tick interrupt set.");
/* Wait for Ctrl-Break key. */
puts("Type Ctrl-Break to exit.");
for (;;) {
if ( breakflag == TRUE )
break;
if ( timerflag == FALSE )
continue;
puts("Five seconds have passed.");
timerflag = FALSE;
}
puts("Got the Ctrl-Break key.");
/* Reset the interrupts. */
if ( clearint(breakid) != 0 )
fatal("Cannot reset Ctrl-Break interrupt.");
else
puts("Ctrl-Break interrupt reset.");
if ( clearint(timerid) != 0 )
fatal("Cannot reset timer tick interrupt.");
else
puts("Timer tick interrupt reset.");
exit(0);
}
/*
* Fatal error routine.
*/
fatal(s) register char *s;
{
puts(s);
exit(1);
}
/*
* Service routine for the Ctrl-Break interrupt (0x1B).
* Simply sets the breakflag to TRUE.
*/
breaktrp()
{
breakflag = TRUE;
return(0);
}
/*
* Service routine for timer tick interrupt (0x1C).
* This comes from the 8253-5 programmable interval timer at a rate of 18.2 Hz.
* Every 91 (=18.2*5 ) interrupts (five seconds), set the timerflag to TRUE.
*/
timertrp()
{
static counter = 0;
if ( ++counter == 91 ) {
timerflag = TRUE;
counter = 0;
}
return(1); /* Link in case interrupt 0x1C did something already */
}

View File

@ -0,0 +1,29 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/* This a simple C program that computes the results of three
* different rates of inflation over a span of ten years.
* Use this text file to learn how to use MicroEMACS commands
* to make creating and editing text files fast, efficient, and
* easy.
*/
#include <stdio.h>
main()
{
int i; /* count ten years */
float w1, w2, w3; /* three inflated quantities */
char *msg = " %2d\t%f %f\n"; /* printf string */
i = 0;
w1 = 1.0;
w2 = 1.0;
w3 = 1.0;
for (i = 1; i<= 10; i++) {
w1 *= 1.07; /* apply inflation */
w2 *= 1.08;
w3 *= 1.10;
printf (msg, i, w1, w2, w3);
}
}

View File

@ -0,0 +1,27 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*Use this program to get better aquainted with the MicroEMACS
*interactive screen editor. You can use this text to learn
*some of the more advanced editing features of MicroEMACS.
*/
#include <stdio.h>
main()
{
FILE *fp;
int ch;
int filename[20];
printf("Enter file name: ");
gets(filename);
if ((fp =fopen(filename,"r")) !=NULL) {
while ((ch = fgetc(fp)) != EOF)
putc(ch, stdout);
}
else
printf("Cannot open %s.\n", filename);
fclose(fp);
}

View File

@ -0,0 +1,129 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 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
* any arguments, then it factors these. If there are no arguments,
* then 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);
}
/*
* Todigit converts the char `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);
}
/*
* 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,111 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 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,353 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* fdir.c
* 5/8/87
*
* This program prints a formatted directory of its command line arguments.
* Example command lines:
* fdir
* fdir -t
* 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
* using 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.
* The -t option sorts files by time, -s sorts by size;
* otherwise, 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 aflag = 0; /* Set if "-a" specified. */
int howsort = 0;
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;
/* "fdir -a" lists "." and "..", which are otherwise suppressed. */
if (argc > 1 && *argv[1] == '-') { /* options */
while ((i = toupper(*++argv[1])) != '\0')
if (i == 'A')
aflag = 1;
else
howsort = i;
++argv;
--argc;
}
/* 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. */
if (dircount != 0 || filecount != 0 || bytecount != 0L)
printf("\n");
if (dircount != 0)
printf("Directories: %6d\n", dircount);
if (filecount != 0)
printf("Files: %6d\n", filecount);
if (bytecount != 0L)
printf("Total bytes: %6ld\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) {
/* Suppress "." and ".." unless "-a" specified. */
if (!aflag && (strcmp(fndp->fnd_name, ".") == 0
|| strcmp(fndp->fnd_name, "..") == 0))
return;
++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().
* 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 */
if (howsort == 'T') { /* take advantage of form of date */
if (*(unsigned long *)&f1->fnd_date
< *(unsigned long *)&f2->fnd_date)
return(1);
else
return(-1);
} else if (howsort == 'S') {
if( f1->fnd_size < f2->fnd_size)
return(1);
else
return(-1);
} else
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,382 @@
/*
* 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);
#if GEM
fixname(fname);
#endif
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)
{
char fname[NFILEN];
int s;
if ((s=mlreply("Visit file: ", fname, NFILEN)) != TRUE)
return (s);
#if GEM
fixname(fname);
#endif
return visitfile(fname);
}
/* Real file visit routine... */
visitfile(fname)
char fname[];
{
register BUFFER *bp;
register WINDOW *wp;
register LINE *lp;
register int i;
register int s;
char bname[NBUFN];
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 GEM
fixname(bname);
#endif
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];
mlwrite(" ");
bp = curbp; /* Cheap. */
if ((s=bclear(bp)) != TRUE) /* Might be old. */
return (s);
bp->b_flag &= ~(BFTEMP|BFCHG|BFTRUNC);
strcpy(bp->b_fname, fname);
if ((s=ffropen(fname)) == FIOERR) /* Hard file open. */
goto out;
if (s == FIOFNF) { /* File not found. */
mlwrite("[New file: %s]", fname);
goto out;
}
mlwrite("[Reading file %s]", fname);
nline = 0;
while ((s=ffgetline(line, NLINE)) == FIOSUC) {
nbytes = strlen(line);
if ((lp1=lalloc(nbytes)) == NULL) {
mlwrite("File too large for available memory!");
bp->b_flag |= BFTRUNC; /* Mark buffer truncated */
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;
lp1->l_lnumber = ++nline;
for (i=0; i<nbytes; ++i)
lputc(lp1, i, line[i]);
}
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 || GEM
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 GEM
fixname(fname);
#endif
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 ((curbp->b_flag & BFTRUNC) != 0) { /* If file was truncated... */
if (mlyesno("File was truncated -- write it anyway") != TRUE)
return (FALSE);
}
if ((s=writeout(curbp->b_fname)) == TRUE) {
curbp->b_flag &= ~(BFCHG|BFTRUNC);
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. */
curbp->b_flag |= BFWRITE; /* Written... */
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 GEM
fixname(fname);
#endif
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,131 @@
/*
* 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");
#if GEM
buf[i] = 0;
ungetc(c, ffp);
return (FIOSUC); /* Make long lines load */
#endif
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");
#if GEM
buf[i]=0; /* Make funny line show up. */
return (FIOSUC);
#else
return (FIOERR);
#endif
}
return (FIOEOF);
}
buf[i] = 0;
return (FIOSUC);
}

View File

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

View File

@ -0,0 +1,84 @@
//////////
/ ibmbios.m
/ IBM PC ROM BIOS support for MicroEMACS.
/ The routines in this file use IBM PC ROM BIOS interrupts 0x10 and 0x16
/ to control the screen and keyboard;
/ they will not work on some IBM-compatible (non-IBM) systems.
/ They make no assumptions about screen type.
//////////
#if IBM
#include <larges.h>
RBVIDEO = 0x10 / IBM PC ROM BIOS video driver interrupt
RBKEYB = 0x16 / IBM PC ROM BIOS keyboard interrupt
NROW = 24 / Screen size.
NCOL = 80 / Edit if you want to; also in ibmpc.c.
//////////
/ int ibmrbkey(i) int i;
/ ROM bios keyboard service interrupt.
//////////
i = LEFTARG
Enter(ibmrbkey_)
mov ax, i(bp) / Parameter to AX
int RBKEYB / perform the interrupt
Leave / and return
//////////
/ ibmmove(row, col)
/ Move the cursor to row, col.
//////////
row = LEFTARG
col = row + 2
Enter(ibmmove_)
movb dh, row(bp) / row to DH
movb dl, col(bp) / col to DL
0:
movb ah, $2 / set cursor to AH
movb bh, $0 / current page to BH
1:
int RBVIDEO
Leave
//////////
/ ibmeol()
/ Erase to end of line.
//////////
Enter(ibmeeol_)
movb ah, $3 / get cursor to AH
movb bh, $0 / current page to BH
int RBVIDEO
mov ax, $0x0600 / init window to AH, erase to AL
movb bh, $7 / normal video to BH
mov cx, dx / upper x = current x, upper y = current y
movb dl, $NCOL-1 / lower x = end of line
jmp 1b
//////////
/ ibmeeop()
/ Erase to end of page.
/////////
Enter(ibmeeop_)
mov ax, $0x0600 / init window to AH, erase to AL
movb bh, $7 / normal video to BH
sub cx, cx / upper left
movb dh, $NROW / lower right
movb dl, $NCOL-1
int RBVIDEO
sub dx, dx / home
jmp 0b / set cursor
#endif
/ end of ibmbios.m

View File

@ -0,0 +1,52 @@
/*
* The routines referenced in this file
* are defined in the assembly language source "ibmbios.m".
* Because they use IBM PC ROM BIOS interrupt 0x10 to control the screen,
* they will not work on some IBM-compatible (non-IBM) systems.
* They make no assumptions about screen type.
*/
#include <stdio.h>
#include "ed.h"
#if IBM
#define NROW 24 /* Screen size. */
#define NCOL 80 /* Edit if you want to; also in ibmbios.m. */
#define BEL 0x07 /* BEL character. */
extern int ttopen(); /* Forward references. */
extern int ttgetc();
extern int ttputc();
extern int ttflush();
extern int ttclose();
extern int ibmmove();
extern int ibmeeol();
extern int ibmeeop();
extern int ibmbeep();
/*
* Standard terminal interface dispatch table.
* Most of the fields point into "termio" code.
*/
TERM term = {
NROW-1,
NCOL,
&ttopen,
&ttclose,
&ttgetc,
&ttputc,
&ttflush,
&ibmmove,
&ibmeeol,
&ibmeeop,
&ibmbeep
};
/* Beep the terminal. */
ibmbeep()
{
ttputc(BEL);
ttflush();
}
#endif

View File

@ -0,0 +1,253 @@
/*
* Let's C Version 4.0.C.
* Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
* All rights reserved. May not be copied or disclosed without permission.
*/
/*
* int.c
* Let's C interface routines for MS-DOS hardware interrupt handling.
* These work in either LARGE or SMALL model.
* See also: intdis.m, example.c.
*/
#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.m.
*/
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 trapping 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 (e.g., 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);
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,208 @@
/
/ Lets C Version 4.0.C.
/ Copyright (c) 1982-1987 by Mark Williams Company, Chicago.
/ All rights reserved. May not be copied or disclosed without permission.
/
/*
* intdis.m
* Assembly language interrupt dispatcher.
* Works in either LARGE or SMALL model.
* See also: int.c, example.c.
*/
#if LARGE
#define LARGECODE 1
#define LARGEDATA 1
#endif
/*
* The following byte size parameters are used to determine the
* actual offsets for the members of the structure INTINFO, which
* is defined in int.c, independent of the data model LARGE or SMALL.
*/
#define INTSIZE 2
#if LARGEDATA
#define PTRSIZE 4
#else
#define PTRSIZE 2
#endif
/*
* The following are used to get the correct 8086 mnemonics, independent
* of the execution model LARGE or SMALL.
*/
#if LARGECODE
#define Gicall xicall
#define Gret xret
#else
#define Gicall icall
#define Gret ret
#endif
.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 = 5*PTRSIZE + 4*INTSIZE / sizeof(struct INTINFO)
i_cfunc = 0 / Member offsets in intinfo
i_stacksize = PTRSIZE
i_oldoff = 2*PTRSIZE + 2*INTSIZE
i_stack = 2*PTRSIZE + 4*INTSIZE
i_curstack = 3*PTRSIZE + 4*INTSIZE
i_endstack = 4*PTRSIZE + 4*INTSIZE
.globl intinfo_ / External references
.globl isover_
.globl cli_ / External definitions
.globl sti_
cli_:
cli
Gret
sti_:
sti
Gret
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, $@intinfo_ / Segment of intinfo[]
mov ds, ax / for data references
mov es, ax
/*
* The following is used to load the SS and SP registers to the
* correct value for the interrupt processing routine. With
* LARGEDATA model, we must get SS from the intinfo[]; otherwise,
* simply set it the same as DS and ES.
*/
#if LARGEDATA
mov ss, intinfo_+INTSIZE+i_curstack(bx)
#else
mov ss, ax
#endif
mov sp, intinfo_+i_curstack(bx)
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
Gicall 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
mov ax, $@intinfo_ / Segment of intinfo[]
mov ds, ax / for data reference
xicall intinfo_+i_oldoff(bx) / Indirect intersegment call
done:
mov ax, $@intinfo_ / Segment of intinfo[]
mov ds, ax / for data reference
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,467 @@
/*
* 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;
lp->l_lnumber = 0; /* This is a new line... */
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 to 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; /* This line... */
lp2 = lp1->l_fp; /* The next line... */
if (lp2 == curbp->b_linep) { /* At the buffer end. */
if (lp1->l_used == 0) /* Blank line. */
lfree(lp1);
return (TRUE);
}
/* Fix file-line numbers. */
if ((lp1->l_used == 0) || (lp1->l_lnumber == 0))
lp1->l_lnumber = lp2->l_lnumber;
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,863 @@
/*
* 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)
#define BAD (SS$_ABORT)
#endif
#ifndef GOOD
#define GOOD 0
#define BAD 1
#endif
#define ERRLINES 3 /* Number of lines to display */
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 */
int ffold; /* Fold Flag */
BUFFER *curbp; /* Current buffer */
WINDOW *curwp; /* Current window */
BUFFER *bheadp; /* BUFFER listhead */
WINDOW *wheadp; /* WINDOW listhead */
BUFFER *blistp; /* Buffer list BUFFER */
BUFFER *errbp; /* Error file BUFFER */
short kbdm[NKBDM] = CTLX|')'; /* Macro */
short *kbdmip; /* Input for above */
short *kbdmop; /* Output for above */
char pat[NPAT]; /* Pattern */
char errfile[NFILEN]; /* Error file name */
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 forwisearch(); /* forward incremental search */
extern int backisearch(); /* backward incremental search */
extern int queryrepl(); /* query/replace */
extern int searchagain(); /* find next occurrance */
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 int gotoline(); /* goto line given as arg */
extern int gotofline(); /* goto line in file. */
extern int setfold(); /* set case-fold on search flag */
extern int nexterr(); /* Seek the next error */
extern int preverr(); /* Seek the previous error */
extern char *strcpy(); /* copy string */
/*
* File-name list for command line...
*/
char *cfiles[NCFILES]; /* Command line specified files */
int cfilecnt; /* File count... */
/*
* Command line switch flags...
*/
int runswitch;
/*
* 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', &backisearch,
CTRL|'S', &forwisearch,
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|'<', &preverr, /* Seek previous error */
CTLX|'=', &showcpos,
CTLX|'>', &nexterr, /* Seek next error */
CTLX|'(', &ctlxlp,
CTLX|')', &ctlxrp,
CTLX|'1', &onlywind,
CTLX|'2', &splitwind,
CTLX|'B', &usebuffer,
CTLX|'E', &ctlxe,
CTLX|'F', &setfillcol,
CTLX|'G', &gotoline,
CTLX|'K', &killbuffer,
CTLX|'N', &nextwind,
CTLX|'P', &prevwind,
CTLX|'Z', &enlargewind,
#ifdef VT52KEYS
META|CTRL|'B', &backword,
META|CTRL|'C', &capword,
META|CTRL|'D', &delfword,
#endif
META|CTRL|'H', &delbword,
META|CTRL|'R', &queryrepl,
META|'!', &reposition,
META|'.', &setmark,
META|'>', &gotoeob,
META|'<', &gotobob,
META|'%', &queryrepl,
META|'/', &searchagain,
META|'@', &setfold,
#ifdef VT52KEYS
META|'A', &backline, /* VT52 arrow keys */
META|'B', &forwline,
META|'C', &backchar,
META|'D', &forwchar,
#else
META|'B', &backword,
META|'C', &capword,
META|'D', &delfword,
#endif
META|'F', &forwword,
META|'G', &gotofline,
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];
#if MSDOS
setkeys();
#endif
#if IBM
vidnit();
#endif
runswitch = 0; /* Initialize the switches */
ffold = TRUE; /* initialize the fold flg */
argproc(argc, argv); /* Parse the arg list */
strcpy(bname, "main"); /* Work out the name of */
if (cfilecnt > 0) /* the default buffer. */
makename(bname, cfiles[0]); /* Make a buffer name */
edinit(bname); /* Buffers, windows. */
vtinit(); /* Displays. */
if (cfilecnt > 0) {
update(); /* You have to update */
readin(cfiles[0]); /* in case "[New file]" */
}
if (cfilecnt > 1) {
n = (term.t_nrow - cfilecnt - 1) / cfilecnt;
for (c = 1; c < cfilecnt ; c++) { /* For all other files... */
splitwind(0,0); /* Split this window... */
if ((f=curwp->w_ntrows-n) != 0)
shrinkwind(0,f); /* Even out the windows */
nextwind(0,0); /* Go on to the next one */
visitfile(cfiles[c]); /* Read in that file */
}
}
if ((runswitch & CF_ERROR) != 0) {
splitwind(0,0); /* Split this window */
f = curwp->w_ntrows - ERRLINES; /* Make error window small */
if (f > 0)
shrinkwind(0,f);
readerr();
nextwind();
mlerase();
update();
nexterr(0,1);
update();
}
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|BFERROR|BFNOWRT)) == 0)
/* Real and not R/O... */
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 = FALSE;
if (f != FALSE /* Argument forces it. */
|| anycb() == FALSE /* All buffers clean. */
|| (s=mlyesno("Quit")) == TRUE) { /* User says it's OK. */
vttidy();
#if MSDOS
resetkeys();
#endif
if (f != FALSE || s != FALSE)
exit(BAD);
else
exit(GOOD);
}
#if MSDOS
setkeys();
#endif
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);
}
#if MSDOS
setkeys() /* redefine cursor keys */
/* so that they make */
/* sense to microEMACS */
/* as described in IBM */
/* DOS tech. reference */
/* manual */
{
#if !IBM
static char *ctlseq[] = {
"\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-@> = <exc>. */
NULL
};
register char **ctlp;
for(ctlp = ctlseq; NULL != *ctlp; ctlp++)
mlwrite(*ctlp);
#endif
}
resetkeys() /* redefine cursor keys */
/* to default values */
{
#if !IBM
static char *ctlseq[] = {
"\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",
NULL
};
register char **ctlp;
for(ctlp = ctlseq; NULL != *ctlp; ctlp++)
mlwrite(*ctlp);
#endif
}
#endif
argproc(argc, argv)
int argc;
char **argv;
{
int i;
char *ptr;
cfilecnt = 0;
for (i = 1; i < argc; i++) {
ptr = argv[i]; /* Get this argument... */
if (*ptr == '-') { /* Is this a switch? */
switch (ptr[1]) {
case 'b':
runswitch |= CF_BACKUP;
break;
case 'd':
runswitch |= CF_DEBUG;
break;
case 'e':
runswitch |= CF_ERROR;
if (ptr[2] == 0) {
if (++i == argc) {
runswitch &= ~CF_ERROR;
continue;
}
strcpy(errfile, argv[i]);
} else {
strcpy(errfile, &ptr[2]);
}
break;
case 'w':
runswitch |= CF_WARN;
break;
default:
break;
}
/* Process this switch */
} else { /* Otherwise... */
if (cfilecnt >= NCFILES)
continue;
cfiles[cfilecnt++] = ptr; /* This is a file. */
#if GEM
fixname(cfiles[cfilecnt-1]);
#endif
}
}
}

View File

@ -0,0 +1,64 @@
# Makefile 10/13/87
# Make command file to build MSDOS executable MicroEMACS me.exe.
# Source file directory.
# This allows the object to be built in a different directory
# than where source resides.
SOURCE=\sample
# Memory Model: either "SMALL" or "LARGE", no spaces please.
MEMMODEL=SMALL
# Unlinked objects.
OBJ1=ansi.obj basic.obj buffer.obj display.obj error.obj file.obj fileio.obj
OBJ2=ibmbios.obj ibmpc.obj line.obj main.obj putline.obj random.obj region.obj
OBJ3=search.obj spawn.obj tcap.obj termio.obj vt52.obj window.obj word.obj
# Files "putline.m" and "ibmbios.m" use IBM PC ROM BIOS calls for screen and
# keyboard control; they may not work on some IBM-compatible (non-IBM) systems.
# Remove the definition of IBM below and recompile if necessary.
# Note: the definition of IBM is required in addition to the definition
# of TANDY to make a Tandy 2000 version, via the command "make tandy".
CFLAGS=-VQUIET -DIBM -V$(MEMMODEL)
LDFLAGS=-w -o me -V$(MEMMODEL)
# Primary targets.
ibm: me.exe
@
tandy: tandyspecific me.exe
@
tandyspecific:
$(CC) $(CFLAGS) -DTANDY -c $(SOURCE)\putline.m
# Uses "tempfile" to avoid MSDOS command line length restrictions.
me.exe: $(OBJ1) $(OBJ2) $(OBJ3)
echo $(OBJ1) >tempfile
echo $(OBJ2) >>tempfile
echo $(OBJ3) >>tempfile
cc $(LDFLAGS) @tempfile
del tempfile
# Secondary targets.
ansi.obj: $(SOURCE)\ansi.c $(SOURCE)\ed.h
basic.obj: $(SOURCE)\basic.c $(SOURCE)\ed.h
buffer.obj: $(SOURCE)\buffer.c $(SOURCE)\ed.h
display.obj: $(SOURCE)\display.c $(SOURCE)\ed.h
error.obj: $(SOURCE)\error.c $(SOURCE)\ed.h
file.obj: $(SOURCE)\file.c $(SOURCE)\ed.h
fileio.obj: $(SOURCE)\fileio.c $(SOURCE)\ed.h
ibmbios.obj: $(SOURCE)\ibmbios.m
ibmpc.obj: $(SOURCE)\ibmpc.c $(SOURCE)\ed.h
line.obj: $(SOURCE)\line.c $(SOURCE)\ed.h
main.obj: $(SOURCE)\main.c $(SOURCE)\ed.h
putline.obj: $(SOURCE)\putline.m
random.obj: $(SOURCE)\random.c $(SOURCE)\ed.h
region.obj: $(SOURCE)\region.c $(SOURCE)\ed.h
search.obj: $(SOURCE)\search.c $(SOURCE)\ed.h
spawn.obj: $(SOURCE)\spawn.c $(SOURCE)\ed.h
tcap.obj: $(SOURCE)\tcap.c $(SOURCE)\ed.h
termio.obj: $(SOURCE)\termio.c $(SOURCE)\ed.h
vt52.obj: $(SOURCE)\vt52.c $(SOURCE)\ed.h
window.obj: $(SOURCE)\window.c $(SOURCE)\ed.h
word.obj: $(SOURCE)\word.c $(SOURCE)\ed.h

View File

@ -0,0 +1,118 @@
//////////
/ putline.m
/ IBM PC Fast Video Support for MicroEMACS.
/ The routines in this file use IBM PC ROM BIOS Interrupt 0x10
/ to control the screen. They may not work on some IBM-compatible
/ (non-IBM) systems.
//////////
#if IBM
#include <larges.h>
RBVIDEO = 0x10 / IBM PC ROM BIOS video driver interrupt
MONOSEG = 0xB000 / Segment of Screen for Monochrome Adapter
COLSEG = 0xB800 / Segment of Screen for Yes Color Graphics Card
IBMATTRIB = 0x07 / Character Attribute for IBM PC
TANDYATTRIB = 0x0A / Character Attribute for Tandy PC
SETVID = 0 / Function Number to Set the Video State
COL8025 = 3 / Argument for 80x25 Color Character Mode
SETPAGE = 5 / Function Number to Select Displayed Page
PAGE0 = 0 / Argument to Select Page 0
GETVID = 15 / Function Number to get Current Video State
MONOSTATE = 7 / Return Value for Monochrome Adapter
/ NB: COL8025 is Return Value from GETVID, too
//////////
/ Local Data
//////////
.shrd
Vidseg_: .word MONOSEG / Segment of Screen
.shri
//////////
/ vidnit()
/ Checks to see if there is a Color Graphics Card Installed, and
/ sets the Local Variable Vidseg_ appropriately. Initializes the
/ screen to 80x25 Character Mode, if necessary, and then selects
/ displayed page 0.
/ Conditionalized for TANDY, to locate base page for Video Segment.
//////////
Enter( vidnit_ )
#if TANDY
mov dx, es
sub ax, ax
mov es, ax
mov bx, es:0x474 / Location of Video Base Page
mov Vidseg_, bx
mov es, dx
#else
movb ah, $GETVID
int RBVIDEO
cmpb al, $MONOSTATE
je 0f
Map( ds, cx, $@Vidseg_)
mov Vidseg_, $COLSEG
cmpb al, $COL8025
je 0f
movb ah, $SETVID
movb al, $COL8025
int RBVIDEO
0: movb ah, $SETPAGE
movb al, $PAGE0
int RBVIDEO
#endif
Leave
//////////
/ putline(row, col, buf) int row, col; char *buf;
/ Display buffer at designated location, by direct screen update.
//////////
row = LEFTARG
col = row+2
buf = col+2
Enter( putline_ )
Map( ds, ax, $@Vidseg_)
movb ah, row(bp)
decb ah
movb al, $10
mulb ah
add ax, Vidseg_
mov es, ax
#if TANDY
movb ah, $TANDYATTRIB
#else
movb ah, $IBMATTRIB
#endif
mov di, col(bp)
dec di
mov cx, $80
sub cx, di
jna 2f
Lds si, buf(bp)
1: lodsb
stos
loop 1b
2: mov ax, ds
mov es, ax
Leave
#endif
/ end of putline.m

View File

@ -0,0 +1,470 @@
/* @(microEMACS)random.c
*
* 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) */
/*
* Return current column. Stop at first non-blank given TRUE argument.
*/
getccol(bflg)
int bflg;
{
register int c, i;
register int col = 0;
register WINDOW *lcurwp;
lcurwp = curwp;
for (i=0; i<lcurwp->w_doto; ++i) {
c = lgetc(lcurwp->w_dotp, i);
if (c!=' ' && c!='\t' && bflg)
break;
if (c == '\t')
col |= 0x07;
else if (c<0x20 || c==0x7F)
++col;
++col;
}
return(col);
}
/*
* Set fill column to n, if given, otherwise use the current cursor
* column. Either way, tell user where the fill column really is.
*/
setfillcol(f, n)
register int n;
{
if (n < 2)
fillcol = getccol(FALSE);
else
fillcol = n - 1;
mlwrite("[Wrap at column %d]", fillcol+1);
return(TRUE);
}
/*
* Display the current position of the cursor,
* in origin 1 X-Y coordinates, the character that is
* under the cursor (in hex), 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 = 0L;
register long nbc;
register int cbo = 0;
register int linecnt = 1;
register int cac;
register int curline = 0;
int ratio;
int col;
clp = lforw(curbp->b_linep); /* Grovel the data. */
for (;;) {
if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
curline = linecnt;
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);
++linecnt;
cbo = 0;
} else
++cbo;
++nch;
}
col = getccol(FALSE)+1; /* 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) line %d of %d",
col, currow+1, cac, nbc, ratio, nch, curline, linecnt);
return (TRUE);
}
/*
* 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 WINDOW *lcurwp;
register LINE *dotp;
register int doto;
register int cl;
register int cr;
lcurwp = curwp;
dotp = lcurwp->w_dotp;
doto = lcurwp->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 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)
register int 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 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)
register int n;
{
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;
register WINDOW *lcurwp;
lcurwp = curwp;
lp1 = lcurwp->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);
lcurwp->w_dotp = lforw(lp1);
lcurwp->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);
}
#if GEM
/*
* singlecase the characters in the given buffer. this is used for
* buffer and file names on the ST, since everything becomes uppercase
* (whether you like it or not) from the desktop and in file names.
* this routine assumes ASCII.
*/
fixname(bp)
register unsigned char *bp;
{
register unsigned c;
while (c = *bp)
#if UPPERNM
if (c >= 'a' && c <= 'z')
*bp++ = c & ~0x20; /* Lower to upper case */
#else
if (c >= 'A' && c <= 'Z')
*bp++ = c | 0x20; /* Upper to lower case */
#endif
else
++bp;
}
#endif
setfold(f,n)
{
if (f)
ffold = n;
else
ffold = !ffold;
if (ffold)
mlwrite("[Case fold on search]");
else
mlwrite("[Match case on search]");
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,799 @@
/*
* The functions in this file implement the
* search commands (both plain and incremental searches
* are supported) and the query-replace command.
*
* The plain old search code is part of the original
* MicroEMACS "distribution". The incremental search code,
* and the query-replace code, is by Rich Ellison, with
* some stylistic modifications.
*/
#include <stdio.h>
#include "ed.h"
/*
* Should these macros be fixed up to
* correctly handle the upper case and lower case
* diacritical vowels in the multinational
* character set?
*/
#define ISUPPER(x) (('A'<=(x) && (x)<='Z'))
#define ISLOWER(x) (('a'<=(x) && (x)<='z'))
#define TOUPPER(x) ((x)+'A'-'a')
#define TOLOWER(x) ((x)-'A'+'a')
#define CCHR(x) ((x)-'@')
#define SRCH_BEGIN (0) /* Search sub-codes. */
#define SRCH_FORW (-1)
#define SRCH_BACK (-2)
#define SRCH_PREV (-3)
#define SRCH_NEXT (-4)
#define SRCH_NOPR (-5)
#define SRCH_ACCM (-6)
typedef struct {
int s_code;
LINE *s_dotp;
int s_doto;
} SRCHCOM;
extern int ctrlg();
static SRCHCOM cmds[NSRCH];
static int cip;
int srch_lastdir = SRCH_NOPR; /* Last search flags. */
/*
* Search forward.
* Get a search string from the user, and search for it,
* starting at ".". If found, "." gets moved to just after the
* matched characters, and display does all the hard stuff.
* If not found, it just prints a message. This is
* normally bound to "M-S".
*/
forwsearch(f, n)
{
register int s;
if ((s=readpattern("Search")) != TRUE)
return (s);
if (forwsrch() == FALSE) {
mlwrite("Not found");
return (FALSE);
}
srch_lastdir = SRCH_FORW;
return (TRUE);
}
/*
* 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 int s;
if ((s=readpattern("Reverse search")) != TRUE)
return (s);
if (backsrch() == FALSE) {
mlwrite("Not found");
return (FALSE);
}
srch_lastdir = SRCH_BACK;
return (TRUE);
}
/*
* Search again, using the same search string
* and direction as the last search command. The direction
* has been saved in "srch_lastdir", so you know which way
* to go. This is only bound to the "find" key on the
* LK201; it should have a normal binding.
*/
searchagain(f, n)
{
if (srch_lastdir == SRCH_FORW) {
if (forwsrch() == FALSE) {
mlwrite("Not found");
return (FALSE);
}
return (TRUE);
}
if (srch_lastdir == SRCH_BACK) {
if (backsrch() == FALSE) {
mlwrite("Not found");
return (FALSE);
}
return (TRUE);
}
mlwrite("No last search");
return (FALSE);
}
/*
* Use incremental searching, initially in the forward direction.
* isearch ignores any explicit arguments.
*/
forwisearch(f, n)
{
return (isearch(SRCH_FORW));
}
/*
* Use incremental searching, initially in the reverse direction.
* isearch ignores any explicit arguments.
*/
backisearch(f, n)
{
return (isearch(SRCH_BACK));
}
/*
* Incremental Search.
* dir is used as the initial direction to search.
* ^N find next occurance (if first thing typed reuse old string).
* ^P find prev occurance (if first thing typed reuse old string).
* ^S switch direction to forward, find next
* ^R switch direction to reverse, find prev
* ^Q quote next character (allows searching for ^N etc.)
* <ESC> exit from Isearch.
* <DEL>
* ^H undoes last character typed. (tricky job to do this correctly).
* else accumulate into search string
*/
isearch(dir)
{
int c;
int pptr;
LINE *clp;
int cbo;
int success;
for (cip=0; cip<NSRCH; cip++)
cmds[cip].s_code = SRCH_NOPR;
cip = 0;
pptr = -1;
clp = curwp->w_dotp;
cbo = curwp->w_doto;
isrch_lpush();
isrch_cpush(SRCH_BEGIN);
success = TRUE;
isrch_prompt(dir,TRUE,success);
for (;;) {
update();
switch (c = ttgetc()) {
case CCHR('M'):
case METACH:
#if LK201
case AGRAVE:
#endif
srch_lastdir = dir;
mlwrite("[Done]");
return (TRUE);
case CCHR('G'):
curwp->w_dotp = clp;
curwp->w_doto = cbo;
curwp->w_flag |= WFMOVE;
srch_lastdir = dir;
mlwrite("Aborting");
return (FALSE);
case CCHR('S'):
case CCHR('F'):
if (dir == SRCH_BACK) {
dir = SRCH_FORW;
isrch_lpush();
isrch_cpush(SRCH_FORW);
success = TRUE;
}
/* drop through to find next */
case CCHR('N'):
if ((success == FALSE)&&(dir == SRCH_FORW))
break;
isrch_lpush();
forwchar(FALSE, 1);
if (isrch_find(SRCH_NEXT) != FALSE) {
isrch_cpush(SRCH_NEXT);
pptr = strlen(pat);
} else {
backchar(FALSE, 1);
(*term.t_beep)();
success = FALSE;
}
isrch_prompt(dir,FALSE,success);
break;
case CCHR('R'):
case CCHR('B'):
if (dir == SRCH_FORW) {
dir = SRCH_BACK;
isrch_lpush();
isrch_cpush(SRCH_BACK);
success = TRUE;
}
/* drop through to find prev */
case CCHR('P'):
if ((success == FALSE)&&(dir == SRCH_BACK))
break;
isrch_lpush();
backchar(FALSE, 1);
if (isrch_find(SRCH_PREV) != FALSE) {
isrch_cpush(SRCH_PREV);
pptr = strlen(pat);
} else {
forwchar(FALSE, 1);
(*term.t_beep)();
success = FALSE;
}
isrch_prompt(dir,FALSE,success);
break;
case CCHR('H'):
case 0x7F:
if (isrch_undo(&pptr,&dir) != TRUE) {
return (ABORT);
}
if (isrch_peek() != SRCH_ACCM)
success = TRUE;
isrch_prompt(dir,FALSE,success);
break;
case CCHR('^'):
case CCHR('Q'):
c = ttgetc();
case CCHR('U'):
case CCHR('X'):
case CCHR('I'):
case CCHR('J'):
goto addchar;
default:
if (c < ' ') { /* uninterpreted ctrl */
c += '@';
c |= CTRL;
success = execute(c, FALSE, 1);
curwp->w_flag |= WFMOVE;
return (success);
}
addchar:
if (pptr == -1)
pptr=0;
if (pptr == 0)
success = TRUE;
pat[pptr++] = c;
if (pptr==NPAT) {
mlwrite("Pattern too long");
ctrlg(FALSE, 0);
return (ABORT);
}
pat[pptr] = '\0';
isrch_lpush();
if (success != FALSE) {
if (isrch_find(dir) != FALSE) {
isrch_cpush(c);
} else {
success = FALSE;
(*term.t_beep)();
isrch_cpush(SRCH_ACCM);
}
} else {
isrch_cpush(SRCH_ACCM);
}
isrch_prompt(dir, FALSE, success);
}
}
}
isrch_cpush(cmd)
register int cmd;
{
if (++cip >= NSRCH)
cip = 0;
cmds[cip].s_code = cmd;
}
isrch_lpush()
{
register int ctp;
ctp = cip+1;
if (ctp >= NSRCH)
ctp = 0;
cmds[ctp].s_code = SRCH_NOPR;
cmds[ctp].s_doto = curwp->w_doto;
cmds[ctp].s_dotp = curwp->w_dotp;
}
isrch_pop()
{
if (cmds[cip].s_code != SRCH_NOPR) {
curwp->w_doto = cmds[cip].s_doto;
curwp->w_dotp = cmds[cip].s_dotp;
curwp->w_flag |= WFMOVE; /* Was "=", wrong. */
cmds[cip].s_code = SRCH_NOPR;
}
if (--cip <= 0) /* Is "=" right? */
cip = NSRCH-1;
}
int
isrch_peek()
{
if (cip == 0)
return (cmds[NSRCH-1].s_code);
else
return (cmds[cip-1].s_code);
}
int
isrch_undo(pptr,dir)
register int *pptr;
register int *dir;
{
switch (cmds[cip].s_code) {
case SRCH_NOPR:
case SRCH_BEGIN:
case SRCH_NEXT:
case SRCH_PREV:
break;
case SRCH_FORW:
if (*dir == SRCH_BACK) {
mlwrite("ISEARCH error, to back when already back");
return (FALSE);
}
*dir = SRCH_BACK;
break;
case SRCH_BACK:
if (*dir == SRCH_FORW) {
mlwrite("ISEARCH error, to forw when already forw");
return (FALSE);
}
*dir = SRCH_FORW;
break;
case SRCH_ACCM:
default:
if (*pptr == 0) {
mlwrite("ISEARCH error, delete without character");
return (FALSE);
}
*pptr -= 1;
if (*pptr < 0)
*pptr = 0;
pat[*pptr] = '\0';
break;
}
isrch_pop();
return (TRUE);
}
isrch_find(dir)
register int dir;
{
register int plen;
plen = strlen(pat);
if (plen != 0) {
if (dir==SRCH_FORW || dir==SRCH_NEXT) {
backchar(FALSE, plen);
if (forwsrch() == FALSE) {
forwchar(FALSE, plen);
return (FALSE);
}
return (TRUE);
}
if (dir==SRCH_BACK || dir==SRCH_PREV) {
forwchar(FALSE, plen);
if (backsrch() == FALSE) {
backchar(FALSE, plen);
return (FALSE);
}
return (TRUE);
}
mlwrite("bad call to isrch_find");
ctrlg(FALSE, 0);
return (FALSE);
}
return (FALSE);
}
/*
* If called with "dir" not one of SRCH_FORW
* or SRCH_BACK, this routine used to print an error
* message. It also used to return TRUE or FALSE,
* depending on if it liked the "dir". However, none
* of the callers looked at the status, so I just
* made the checking vanish.
*/
isrch_prompt(dir, flag, success)
{
if (dir == SRCH_FORW) {
if (success != FALSE)
isrch_dspl("i-search forward", flag);
else
isrch_dspl("failing i-search forward", flag);
} else if (dir == SRCH_BACK) {
if (success != FALSE)
isrch_dspl("i-search backward", flag);
else
isrch_dspl("failing i-search backward", flag);
}
}
/*
* Prompt writing routine for the incremental
* search. The "prompt" is just a string. The "flag" determines
* if a "[ ]" or ":" embelishment is used. The string is packed
* into a big buffer and zapped out with a single call to
* "mlwrite". The "26" in the "tpat" declaration is the length
* of the longest prompt string. This actually isn't long
* enough if the pattern is full of control characters
* and other things that need an "^".
*/
isrch_dspl(prompt, flag)
char *prompt;
{
register char *cp1;
register char *cp2;
register int c;
char tpat[NPAT+26+2+1+1];
cp1 = &tpat[0];
cp2 = prompt;
while ((c = *cp2++) != '\0')
*cp1++ = c;
if (flag != FALSE) {
*cp1++ = ' ';
*cp1++ = '[';
} else {
*cp1++ = ':';
*cp1++ = ' ';
}
cp2 = &pat[0];
while ((c = *cp2++) != 0) {
if (cp1 < &tpat[NPAT+20-4]) { /* "??]\0" */
if (c<0x20 || c==0x7F) {
*cp1++ = '^';
c ^= 0x40;
} else if (c == '%') /* Map "%" to */
*cp1++ = c; /* "%%". */
*cp1++ = c;
}
}
if (flag != FALSE)
*cp1++ = ']';
*cp1= '\0';
mlwrite(tpat);
}
/*
* Query Replace.
* Replace strings selectively. Does a search and replace operation.
* A space or a comma replaces the string, a period replaces and quits,
* an n doesn't replace, a C-G quits.
*/
queryrepl(f, n)
{
register int s;
char news[NPAT];
LINE *clp;
int cbo;
register int flg;
register int flgc;
int rcnt;
if ((s=readpattern("Old string")) != TRUE)
return (s);
if ((s=mlreply("New string: ",news, NPAT)) == ABORT)
return (s);
if (s == FALSE) /* Null string. */
news[0] = '\0';
mlwrite("Query Replace: [%s] -> [%s]", pat, news);
clp = curwp->w_dotp;
cbo = curwp->w_doto;
rcnt = 0;
flg = TRUE;
flgc = FALSE;
while (flg==TRUE && forwsrch()==TRUE) {
if (flgc != TRUE) {
retry:
update();
switch (ttgetc()) {
case ' ':
case ',':
replstring(news, f);
rcnt++;
break;
case '.':
replstring(news, f);
rcnt++;
flg = FALSE;
break;
case '\007':
flg = ABORT;
ctrlg(FALSE, 0);
break;
case '!':
replstring(news, f);
rcnt++;
flgc = TRUE;
break;
case 'n':
break;
default:
mlwrite("<SP>[,] replace, [.] rep-end, [n] dont, [!] repl rest <C-G> quit");
goto retry;
}
} else {
replstring(news, f);
rcnt++;
}
}
curwp->w_dotp = clp;
curwp->w_doto = cbo;
curwp->w_flag = WFHARD;
update();
if (rcnt == 0)
mlwrite("[No replacements done]");
else if (rcnt == 1)
mlwrite("[1 replacement done]");
else
mlwrite("[%d replacements done]", rcnt);
return (TRUE);
}
#define FLOWER 0 /* Found lower case. */
#define FCAPTL 1 /* Found capital. */
#define FUPPER 2 /* Found upper case. */
replstring(st, f)
char st[];
int f;
{
register char *tpt;
register int plen;
register int rtype;
register int c;
plen = strlen(pat);
backchar(TRUE, plen);
c = lgetc(curwp->w_dotp, curwp->w_doto);
rtype = FLOWER;
if (ISUPPER(c) != FALSE) {
rtype = FCAPTL;
if (curwp->w_doto+1 != llength(curwp->w_dotp)) {
c = lgetc(curwp->w_dotp, curwp->w_doto+1);
if (ISUPPER(c) != FALSE)
rtype = FUPPER;
}
}
ldelete(plen, FALSE);
tpt = &st[0];
if (f != FALSE) {
while (*tpt != '\0')
linsert(1, *tpt++);
} else {
switch (rtype) {
case FLOWER:
while (*tpt != '\0') {
linsert(1, *tpt++);
}
break;
case FCAPTL:
if (*tpt != '\0') {
if (ISLOWER(*tpt) != FALSE)
linsert(1, TOUPPER(*tpt++));
else
linsert(1, *tpt++);
}
while (*tpt != '\0')
linsert(1, *tpt++);
break;
case FUPPER:
while (*tpt != '\0') {
if (ISLOWER(*tpt) != FALSE)
linsert(1, TOUPPER(*tpt++));
else
linsert(1, *tpt++);
}
break;
}
}
curwp->w_flag |= WFHARD;
}
/*
* This routine does the real work of a
* forward search. The pattern is sitting in the external
* variable "pat". If found, dot is updated, the window system
* is notified of the change, and TRUE is returned. If the
* string isn't found, FALSE is returned.
*/
forwsrch()
{
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register char *pp;
register int c;
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: ;
}
return (FALSE);
}
/*
* This routine does the real work of a
* backward search. The pattern is sitting in the external
* variable "pat". If found, dot is updated, the window system
* is notified of the change, and TRUE is returned. If the
* string isn't found, FALSE is returned.
*/
backsrch()
{
register LINE *clp;
register int cbo;
register LINE *tlp;
register int tbo;
register int c;
register char *epp;
register char *pp;
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)
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 (ffold){
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,206 @@
/*
* 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 GEM
movecursor(term.t_nrow, 0); /* Seek to last line. */
(*term.t_flush)();
ttclose();
system("msh");
sgarbf = TRUE;
ttopen();
return(TRUE);
#endif
#if MSDOS
movecursor(term.t_nrow, 0); /* Seek to last line. */
(*term.t_flush)();
ttclose();
execall("c:\\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 GEM
if ((s=mlreply("! ", line, NLINE)) != TRUE)
return (s);
(*term.t_putchar)('\n'); /* Already have '\r' */
(*term.t_flush)();
ttclose();
system(line);
ttopen();
mlputs("[End]"); /* Pause. */
(*term.t_flush)();
while ((s = (*term.t_getchar)()) != '\r' && s != ' ')
;
sgarbf = TRUE;
return (TRUE);
#endif
#if MSDOS
if ((s=mlreply("DOS command: ", line, NLINE)) != TRUE)
return (s);
(*term.t_putchar)('\n');
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

Some files were not shown because too many files have changed in this diff Show More