aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpaulcantrell <paulcantrell>2007-12-10 21:59:20 +0000
committerpaulcantrell <paulcantrell>2007-12-10 21:59:20 +0000
commit2ee4787d1df9784d40a7eda91e61691da3d56f74 (patch)
tree0947afb6c37ffeb4ae193401a187255cf27b0ed3
downloadvideoteco-fork-2ee4787d1df9784d40a7eda91e61691da3d56f74.tar.gz
Initial revision
-rw-r--r--config.h225
-rw-r--r--poll.h19
-rw-r--r--syspoll.h153
-rw-r--r--tecbuf.c2710
-rw-r--r--teccmd.c2668
-rw-r--r--tecdebug.c401
-rw-r--r--tecdisp.c2867
-rw-r--r--tecexec.c2801
-rw-r--r--tecmem.c456
-rw-r--r--teco.c1888
-rw-r--r--teco.h657
-rw-r--r--tecparse.c2178
-rw-r--r--tecparse.h327
-rw-r--r--tecstate.c2374
-rw-r--r--tecterm.c1579
-rw-r--r--tecundo.c504
-rw-r--r--tecvms.h51
17 files changed, 21858 insertions, 0 deletions
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..708cd2d
--- /dev/null
+++ b/config.h
@@ -0,0 +1,225 @@
+/* config.h. Generated by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if the `closedir' function returns void instead of `int'. */
+/* #undef CLOSEDIR_VOID */
+
+/* Define to 1 if `TIOCGWINSZ' requires <sys/ioctl.h>. */
+/* #undef GWINSZ_IN_SYS_IOCTL */
+
+/* Define to 1 if you have the `alarm' function. */
+#define HAVE_ALARM 1
+
+/* Define to 1 if you have the `bzero' function. */
+#define HAVE_BZERO 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the declaration of `cfgetospeed', and to 0 if you
+ don't. */
+#define HAVE_DECL_CFGETOSPEED 1
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `ncurses' library (-lncurses). */
+#define HAVE_LIBNCURSES 1
+
+/* Define to 1 if you have the `termcap' library (-ltermcap). */
+#define HAVE_LIBTERMCAP 1
+
+/* Define to 1 if you have the `terminfo' library (-lterminfo). */
+/* #undef HAVE_LIBTERMINFO */
+
+/* Define to 1 if you support file names longer than 14 characters. */
+#define HAVE_LONG_FILE_NAMES 1
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+ to 0 otherwise. */
+#define HAVE_MALLOC 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mkdir' function. */
+#define HAVE_MKDIR 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the `sbrk' function. */
+#define HAVE_SBRK 1
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the <sgtty.h> header file. */
+#define HAVE_SGTTY_H 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+/* #undef HAVE_STAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if `c_cflag' is member of `struct termios'. */
+#define HAVE_STRUCT_TERMIOS_C_CFLAG 1
+
+/* Define to 1 if `c_ospeed' is member of `struct termios'. */
+#define HAVE_STRUCT_TERMIOS_C_OSPEED 1
+
+/* Define to 1 if `sg_ospeed' is member of `struct termios'. */
+/* #undef HAVE_STRUCT_TERMIOS_SG_OSPEED */
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/filio.h> header file. */
+#define HAVE_SYS_FILIO_H 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the `tcgetattr' function. */
+#define HAVE_TCGETATTR 1
+
+/* Define to 1 if you have the `tcsetattr' function. */
+#define HAVE_TCSETATTR 1
+
+/* Define to 1 if you have the <termcap.h> header file. */
+#define HAVE_TERMCAP_H 1
+
+/* Define to 1 if you have the <termios.h> header file. */
+#define HAVE_TERMIOS_H 1
+
+/* Define to 1 if you have the <termio.h> header file. */
+/* #undef HAVE_TERMIO_H */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if `fork' works. */
+/* #undef HAVE_WORKING_FORK */
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+ slash. */
+/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "paul@copters.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "VTECO"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "VTECO 6.4"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "vteco"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "6.4"
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* Define to the type of arg 1 for `select'. */
+#define SELECT_TYPE_ARG1 int
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#define SELECT_TYPE_ARG234 (fd_set *)
+
+/* Define to the type of arg 5 for `select'. */
+#define SELECT_TYPE_ARG5 (struct timeval *)
+
+/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+/* #undef STAT_MACROS_BROKEN */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to rpl_malloc if the replacement function should be used. */
+/* #undef malloc */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
diff --git a/poll.h b/poll.h
new file mode 100644
index 0000000..ade728d
--- /dev/null
+++ b/poll.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
+/* The copyright notice above does not evidence any */
+/* actual or intended publication of such source code. */
+
+#ifndef _POLL_H
+#define _POLL_H
+
+#pragma ident "@(#)poll.h 1.7 92/07/14 SMI" /* SVr4.0 1.2 */
+
+/*
+ * Poll system call interface definitions.
+ */
+
+#include "syspoll.h"
+
+#endif /* _POLL_H */
diff --git a/syspoll.h b/syspoll.h
new file mode 100644
index 0000000..68e4884
--- /dev/null
+++ b/syspoll.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
+/* The copyright notice above does not evidence any */
+/* actual or intended publication of such source code. */
+
+/*
+ * Copyright (c) 1995 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SYS_POLL_H
+#define _SYS_POLL_H
+
+#pragma ident "@(#)poll.h 1.24 97/04/18 SMI" /* SVr4.0 11.9 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Structure of file descriptor/event pairs supplied in
+ * the poll arrays.
+ */
+typedef struct pollfd {
+ int fd; /* file desc to poll */
+ short events; /* events of interest on fd */
+ short revents; /* events that occurred on fd */
+} pollfd_t;
+
+typedef unsigned long nfds_t;
+
+/*
+ * Testable select events
+ */
+#define POLLIN 0x0001 /* fd is readable */
+#define POLLPRI 0x0002 /* high priority info at fd */
+#define POLLOUT 0x0004 /* fd is writeable (won't block) */
+#define POLLRDNORM 0x0040 /* normal data is readable */
+#define POLLWRNORM POLLOUT
+#define POLLRDBAND 0x0080 /* out-of-band data is readable */
+#define POLLWRBAND 0x0100 /* out-of-band data is writeable */
+
+#define POLLNORM POLLRDNORM
+
+/*
+ * Non-testable poll events (may not be specified in events field,
+ * but may be returned in revents field).
+ */
+#define POLLERR 0x0008 /* fd has error condition */
+#define POLLHUP 0x0010 /* fd has been hung up on */
+#define POLLNVAL 0x0020 /* invalid pollfd entry */
+
+#ifdef _KERNEL
+
+/*
+ * Additional private poll flags supported only by strpoll().
+ * Must be bit-wise distinct from the above POLL flags.
+ */
+#define POLLRDDATA 0x200 /* Wait for M_DATA; ignore M_PROTO only msgs */
+#define POLLNOERR 0x400 /* Ignore POLLERR conditions */
+
+#endif /* _KERNEL */
+
+#if defined(_KERNEL) || defined(_KMEMUSER)
+
+#include <sys/t_lock.h>
+#include <sys/thread.h>
+
+/*
+ * Poll list head structure. A pointer to this is passed to
+ * pollwakeup() from the caller indicating an event has occurred.
+ * Only the ph_list field is used, but for DDI compliance, we can't
+ * change the size of the structure.
+ */
+typedef struct pollhead {
+ struct polldat *ph_list; /* list of pollers */
+ struct polldat *ph_dummy; /* unused -- see above */
+ short ph_events; /* unused -- see above */
+} pollhead_t;
+
+/*
+ * Data necessary to notify process sleeping in poll(2)
+ * when an event has occurred.
+ */
+typedef struct polldat {
+ kthread_t *pd_thread; /* thread doing poll */
+ int pd_events; /* events being polled */
+ struct polldat *pd_next; /* next in poll list */
+ struct polldat *pd_prev; /* previous in poll list */
+ struct pollhead *pd_headp; /* backpointer to head of list */
+ struct pollhead *pd_sphp; /* stored pollhead struct pointer */
+} polldat_t;
+
+/*
+ * State information kept by each polling thread
+ */
+typedef struct pollstate {
+ int ps_nfds;
+ int ps_flag;
+ pollfd_t *ps_pollfd;
+ polldat_t *ps_polldat;
+ kmutex_t ps_lock; /* mutex for the polling thread */
+ kmutex_t ps_no_exit; /* protects ps_busy*, can't be nested */
+ int ps_busy; /* can only exit when its 0 */
+ kcondvar_t ps_busy_cv; /* cv to wait on if ps_busy != 0 */
+ kcondvar_t ps_cv; /* cv to wait on if needed */
+} pollstate_t;
+
+/* ps_flag */
+#define T_POLLTIME 0x01 /* poll timeout pending */
+#define T_POLLWAKE 0x02 /* pollwakeup() occurred */
+
+#if defined(_KERNEL)
+
+/*
+ * Routine called to notify a process of the occurrence
+ * of an event.
+ */
+extern void pollwakeup(pollhead_t *, short);
+/*
+ * pollwakeup_safe will replace pollwakeup when the support
+ * for unsafe drivers is removed.
+ */
+extern void pollwakeup_safe(pollhead_t *, short);
+
+/*
+ * Internal routines.
+ */
+extern void polltime(kthread_t *);
+extern void pollrun(kthread_t *);
+extern void polldel(pollstate_t *);
+extern void polllock(pollhead_t *, kmutex_t *);
+extern int pollunlock(pollhead_t *);
+extern void pollrelock(pollhead_t *, int);
+extern void pollcleanup(kthread_t *);
+
+extern void ppwaitemptylist(pollhead_t *);
+
+#endif /* defined(_KERNEL) */
+
+#endif /* defined(_KERNEL) || defined(_KMEMUSER) */
+
+#if defined(__STDC__) && !defined(_KERNEL)
+int poll(struct pollfd *, nfds_t, int);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_POLL_H */
diff --git a/tecbuf.c b/tecbuf.c
new file mode 100644
index 0000000..1dd93e9
--- /dev/null
+++ b/tecbuf.c
@@ -0,0 +1,2710 @@
+char *tecbuf_c_version = "tecbuf.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecbuf.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecbuf.c
+ * Subroutines to handle the edit buffers
+ *
+ * COPYRIGHT (c) 1985-2004 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+char rcs_date[] = AUTO_DATE;
+
+/*
+ * Global Storage is defined here for lack of a better place.
+ * First off are all the flags.
+ */
+
+/*
+ * Global Variables
+ */
+ struct buff_header *buffer_headers;
+ struct buff_header *curbuf;
+ struct buff_header *qregister_push_down_list;
+
+/*
+ * Global structures
+ */
+ struct buff_line *line_buffer_lookaside_list = NULL;
+/*
+ * Forward References
+ */
+ struct buff_line *allocate_line_buffer(int);
+ void movc3(char *,char *,int);
+ void buff_buffer_map( void );
+ unsigned int stringHash( char *str );
+
+ extern FILE *teco_debug_log;
+ extern int term_columns;
+ extern struct window *curwin;
+ extern char checkpoint_modified;
+
+/* BUFF_FIND - Find the named buffer
+ *
+ * Function:
+ *
+ * This routine is called with the name of the buffer that we want to
+ * find. It searches all the buffer headers until it finds the name.
+ * It will either return the address of the buffer header, or null if
+ * it could not find one with the proper name.
+ */
+struct buff_header *
+buff_find(name)
+char *name;
+{
+register struct buff_header *bp;
+register char *cp1,*cp2;
+unsigned int hash = stringHash( name );
+
+ PREAMBLE();
+ for(bp = buffer_headers; bp != NULL; bp = bp->next_header){
+ if( hash != bp->buf_hash )
+ {
+// printf("skipping because hash 0x%08x != 0x%08x %s %s\n",
+// hash, bp->buf_hash, name, bp->name);
+ continue;
+ }
+
+ printf("hash match! %s %s\n",name,bp->name);
+/*
+ * This loop implements the equivalent of strcmp, except that in the
+ * case of VMS, it is case insensitive.
+ */
+ for(cp1 = name, cp2 = bp->name;;cp1++,cp2++){
+#ifdef VMS
+ if(UPCASE(*cp1) != UPCASE(*cp2)) break;
+#else
+ if(*cp1 != *cp2) break;
+#endif
+ if(*cp1 == '\0') return(bp);
+ }/* End FOR */
+
+ }/* End FOR */
+
+ return(NULL);
+
+}/* End Routine */
+
+
+
+/* BUFF_QFIND - Find the named Q register
+ *
+ * Function:
+ *
+ * This routine is called with the name of the Q register that we
+ * want to find. It constructs the internal name of the Q register
+ * and then calls buff_find to find the buffer. If the buffer is not
+ * found and the create flag is set, we call buff_create to cause
+ * the Q register to be created.
+ */
+struct buff_header *
+buff_qfind(name,create_flag)
+char name;
+char create_flag;
+{
+register struct buff_header *hbp;
+register int i;
+char tmp_buffer[LINE_BUFFER_SIZE],tmp_message[LINE_BUFFER_SIZE];
+struct buff_header *buff_create();
+
+ PREAMBLE();
+ (void) strcpy(tmp_buffer,TECO_INTERNAL_BUFFER_NAME);
+ i = strlen(tmp_buffer);
+ name = UPCASE((int)name);
+ tmp_buffer[i] = name; tmp_buffer[i+1] = '\0';
+ hbp = buff_find(tmp_buffer);
+ if(hbp == NULL){
+ if(create_flag){
+ hbp = buff_create(tmp_buffer,1);
+ if(hbp == (struct buff_header *)NULL){
+ sprintf(tmp_message,"?Cannot create Q-register %c",name);
+ error_message(tmp_message);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+ }/* End IF */
+ }/* End IF */
+
+ return(hbp);
+
+}/* End Routine */
+
+
+
+/* BUFF_CREATE - Create a new buffer
+ *
+ * Function:
+ *
+ * This routine is called when we need to create a new buffer. The caller
+ * should have already verified that this doesn't already exist. The
+ * routine returns the address of the new buffer, or NULL if there is a
+ * problem of some sort.
+ */
+struct buff_header *
+buff_create(name,internal_flag)
+char *name;
+char internal_flag;
+{
+register struct buff_header *bp;
+register struct buff_header *obp;
+register struct buff_line *lbp;
+register int i = 0;
+
+ PREAMBLE();
+
+ if(buff_find(name)) return(NULL);
+
+/*
+ * Create the buffer header itself.
+ */
+ bp = (struct buff_header *)tec_alloc(
+ TYPE_C_BHDR,
+ sizeof(struct buff_header)
+ );
+
+ if(bp == NULL) return(NULL);
+ bp->buf_magic = MAGIC_BUFFER;
+ bp->buf_hash = stringHash( name );
+
+ bp->name = tec_alloc(TYPE_C_CBUFF,strlen(name)+1);
+ if(bp->name == NULL){
+ bp->buf_magic = 0;
+ tec_release(TYPE_C_BHDR,(char *)bp);
+ return(NULL);
+ }/* End IF */
+ (void) strcpy(bp->name,name);
+
+ obp = buffer_headers;
+ if(obp) i = obp->buffer_number;
+ while(obp){
+ if(internal_flag){
+ if(obp->buffer_number < i) i = obp->buffer_number;
+ }
+ else {
+ if(obp->buffer_number > i) i = obp->buffer_number;
+ }
+ obp = obp->next_header;
+ }/* End While */
+
+ if(internal_flag) bp->buffer_number = i - 1;
+ else bp->buffer_number = i + 1;
+
+ bp->ismodified = NO;
+ bp->isreadonly = NO;
+ bp->isbackedup = NO;
+ bp->dot = 0;
+ bp->zee = 0;
+
+/*
+ * Create the first line buffer structure
+ */
+ lbp = allocate_line_buffer(1);
+ if(lbp == NULL){
+ bp->buf_magic = 0;
+ tec_release(TYPE_C_CBUFF,bp->name);
+ tec_release(TYPE_C_BHDR,(char *)bp);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+ bp->first_line = lbp;
+/*
+ * Initialize the rest of the special locations
+ */
+ bp->pos_cache.lbp = NULL;
+ bp->pos_cache.base = 0;
+
+/*
+ * Link it into the list of buffer headers, in order by buffer number
+ */
+ obp = buffer_headers;
+ while(obp){
+ if(obp->next_header == NULL) break;
+ if(obp->next_header->buffer_number > bp->buffer_number) break;
+ obp = obp->next_header;
+ }/* End While */
+
+ if(obp == NULL || buffer_headers->buffer_number > bp->buffer_number){
+ bp->next_header = buffer_headers;
+ buffer_headers = bp;
+ }/* End IF */
+
+ else {
+ bp->next_header = obp->next_header;
+ obp->next_header = bp;
+ }/* End Else */
+
+ return(bp);
+
+}/* End Routine */
+
+
+
+/* BUFF_DUPLICATE - Make a duplicate of a buffer
+ *
+ * Function:
+ *
+ * This routine is called to duplicate the specified buffer and return
+ * a pointer to the duplicate. The current requirement for this is for
+ * the push Q register command ('[').
+ */
+struct buff_header *
+buff_duplicate(sbp)
+register struct buff_header *sbp;
+{
+register struct buff_header *dbp;
+register struct buff_line *slp;
+register struct buff_line *dlp;
+
+ PREAMBLE();
+/*
+ * Create the buffer header itself.
+ */
+ dbp = (struct buff_header *)
+ tec_alloc(TYPE_C_BHDR,sizeof(struct buff_header));
+ if(dbp == NULL){
+ return((struct buff_header *)NULL);
+ }/* End IF */
+
+ dbp->name = tec_alloc(TYPE_C_CBUFF,strlen(sbp->name)+1);
+ if(dbp->name == NULL){
+ tec_release(TYPE_C_BHDR,(char *)dbp);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+
+ (void) strcpy(dbp->name,sbp->name);
+
+ dbp->buf_magic = MAGIC_BUFFER;
+ dbp->ismodified = sbp->ismodified;
+ dbp->isreadonly = sbp->isreadonly;
+ dbp->isbackedup = sbp->isbackedup;
+ dbp->dot = sbp->dot;
+ dbp->zee = sbp->zee;
+ dbp->ivalue = sbp->ivalue;
+ dbp->pos_cache.lbp = NULL;
+ dbp->pos_cache.base = 0;
+ dbp->first_line = NULL;
+
+/*
+ * Copy the line buffer structures. If an allocation error occurs, we
+ * have to return all the allocated memory, and return an error.
+ */
+ slp = sbp->first_line;
+
+ if(slp){
+ dbp->first_line = dlp = allocate_line_buffer(slp->byte_count);
+ if(dlp == NULL){
+ buff_destroy(dbp);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+ movc3(slp->buffer,dlp->buffer,slp->byte_count);
+ dlp->byte_count = slp->byte_count;
+
+ while((slp = slp->next_line)){
+ dlp->next_line = allocate_line_buffer(slp->byte_count);
+ if(dlp->next_line == NULL){
+ buff_destroy(dbp);
+ return((struct buff_header *)NULL);
+ }/* End IF */
+ dlp->next_line->prev_line = dlp;
+ dlp = dlp->next_line;
+ movc3(slp->buffer,dlp->buffer,slp->byte_count);
+ dlp->byte_count = slp->byte_count;
+ }/* End While */
+
+ }/* End IF */
+
+ return(dbp);
+
+}/* End Routine */
+
+
+
+/* MOVC3 - Copy the specified number of bytes
+ *
+ * Function:
+ *
+ * This routine copies 'n' bytes from the source to the
+ * destination.
+ */
+void
+movc3(source,dest,count)
+register char *source;
+register char *dest;
+register int count;
+{
+
+ PREAMBLE();
+ while(count-- > 0) *dest++ = *source++;
+
+}/* End Routine */
+
+
+
+/* BUFF_DESTROY - Delete an existing buffer
+ *
+ * Function:
+ *
+ * This routine is called to delete an existing buffer. The routine is
+ * supplied the address of the buffer structure. We have to clean up
+ * all the lines in the buffer, all the display lines, etc.
+ */
+void
+buff_destroy(hbp)
+register struct buff_header *hbp;
+{
+register struct buff_header *bp;
+register struct buff_line *lbp;
+
+ PREAMBLE();
+/*
+ * There are certain buffers we don't allow to be destroyed
+ */
+ if(strcmp(hbp->name,"TECO_INTERNAL-Main") == 0){
+ return;
+ }/* End IF */
+/*
+ * Clean up all the data in the buffer first
+ */
+ lbp = hbp->first_line;
+ hbp->pos_cache.lbp = NULL;
+ hbp->first_line = NULL;
+ buff_free_line_buffer_list(lbp);
+
+/*
+ * Now unlink this buffer header from the header list.
+ * Check to see if it is at the head of the list.
+ */
+ bp = buffer_headers;
+ if(bp == hbp){
+ buffer_headers = bp->next_header;
+ }/* End IF */
+
+/*
+ * If not at the head of the list, we have to search the list till
+ * we find it's father. Then we make it's father's child be it's
+ * child.
+ */
+ else {
+ while(bp){
+ if(bp->next_header == hbp){
+ bp->next_header = hbp->next_header;
+ break;
+ }/* End IF */
+ bp = bp->next_header;
+ }/* End While */
+
+ }/* End Else */
+
+/*
+ * Now give back the storage for the name, and for the structure itself.
+ */
+ if(hbp->name) tec_release(TYPE_C_CBUFF,(char *)hbp->name);
+ tec_release(TYPE_C_BHDR,(char *)hbp);
+
+ return;
+
+}/* End Routine */
+
+
+
+/* BUFF_FIND_LINE - Find the line structure that <position> is on
+ *
+ * Function:
+ *
+ * This routine is called to find the line_buffer structure that
+ * the buffer position resides on.
+ */
+struct buff_line *
+buff_find_line(hbp,position)
+register struct buff_header *hbp;
+int position;
+{
+register struct buff_line *lbp;
+register int i;
+
+ PREAMBLE();
+
+ if(hbp == NULL) return(NULL);
+
+/*
+ * First step is to see if the current position cache in the buffer header will
+ * help us out. Our position must be on this line if it is to be of help to us.
+ */
+ lbp = hbp->pos_cache.lbp;
+ if(lbp){
+
+ i = hbp->pos_cache.base + lbp->byte_count;
+/*
+ * Test to see if it is a currently existing character position that is mapped
+ * by our position cache.
+ */
+ if(position >= hbp->pos_cache.base && position < i){
+ return(lbp);
+ }/* End IF */
+/*
+ * If we are inserting at the end of the buffer, the position doesn't really
+ * exist, but we can still resolve it if we happen to have the final line
+ * cached. The only case where we don't want to do this is when the line
+ * already has a trailing newline. Then we want to fall down into the code
+ * below which will recognize this and construct a new line buffer.
+ */
+ if(position == hbp->zee && position == i){
+ if(lbp->byte_count == 0) return(lbp);
+ if(lbp->buffer[lbp->byte_count - 1] != '\n') return(lbp);
+ }/* End IF */
+
+ }/* End IF */
+
+/*
+ * We get here if the current cached location does not help us out. In this
+ * case we need to hunt till we find the correct place in the buffer. We also
+ * update our cache to remember this.
+ */
+ i = position;
+ lbp = hbp->first_line;
+
+ if(lbp == NULL){
+ return(NULL);
+ }/* End IF */
+
+/*
+ * Now, even though the cache did not give us the correct line, it can still
+ * help us out in the search if it maps a position before the one we are
+ * looking for, since we can begin our search at it's base.
+ */
+ if(hbp->pos_cache.lbp && hbp->pos_cache.base < position){
+ i -= hbp->pos_cache.base;
+ lbp = hbp->pos_cache.lbp;
+ }/* End IF */
+
+ while(1){
+ if(lbp->byte_count >= i) break;
+ i -= lbp->byte_count;
+ if(lbp->next_line == NULL){
+ char panic_string[LINE_BUFFER_SIZE];
+ sprintf(panic_string,
+ "buff_find_line: position %d specified in buffer %s, z=%d",
+ position,hbp->name,hbp->zee);
+ tec_panic(panic_string);
+ printf("NULL ptr %d bytes still to go\n",i);
+ }/* End IF */
+ lbp = lbp->next_line;
+ }/* End While */
+
+ if(i == lbp->byte_count && i && lbp->buffer[i-1] == '\n'){
+ i = 0;
+ if(lbp->next_line == NULL){
+ lbp->next_line = allocate_line_buffer(1);
+ if(lbp->next_line == NULL) return((struct buff_line *)NULL);
+ lbp->next_line->prev_line = lbp;
+ }/* End IF */
+ lbp = lbp->next_line;
+ }/* End IF */
+
+ hbp->pos_cache.lbp = lbp;
+ hbp->pos_cache.base = position - i;
+
+ return(lbp);
+
+}/* End Routine */
+
+
+
+/* BUFF_FIND_OFFSET - Find offset onto line structure that <position> is on
+ *
+ * Function:
+ *
+ * This routine is called to find the offset into the line_buffer of
+ * the specified position.
+ */
+int
+buff_find_offset(hbp,lbp,position)
+register struct buff_header *hbp;
+register struct buff_line *lbp;
+int position;
+{
+
+ PREAMBLE();
+ if(hbp == NULL) return(-1);
+/*
+ * First step is to see if the current position cache in the buffer header will
+ * help us out. Our position must be on this line if it is to be of help to us.
+ */
+ if( lbp == NULL || hbp->pos_cache.lbp != lbp){
+ lbp = buff_find_line(hbp,position);
+ if(lbp == NULL) return(-1);
+ }/* End IF */
+
+/*
+ * Now we simply calculate the position's offset from the line structure base.
+ */
+ return( position - hbp->pos_cache.base );
+
+}/* End Routine */
+
+
+
+/* BUFF_CONTENTS - Return the character at the specified position
+ *
+ * Function:
+ *
+ * This routine is called to fetch the single character which is at
+ * the specified buffer position. This routine is generally called
+ * by routines which are low frequence or only handle a small number
+ * of characters at any one time.
+ */
+int
+buff_contents(hbp,position)
+register struct buff_header *hbp;
+register int position;
+{
+register struct buff_line *lbp;
+register int i;
+
+ PREAMBLE();
+/*
+ * Insure that the specified position is legal
+ */
+ if(position > hbp->zee || position < 0){
+ char panic_string[LINE_BUFFER_SIZE];
+ sprintf(panic_string,
+ "buff_contents: illegal position %d specified in buffer %s",
+ position,hbp->name);
+ tec_panic(panic_string);
+ }/* End IF */
+
+/*
+ * Call buff_find_line to find the line structure that the specified
+ * position resides on.
+ */
+ lbp = buff_find_line(hbp,position);
+ i = buff_find_offset(hbp,lbp,position);
+ if(i == -1) return(-1);
+
+ return(lbp->buffer[i] & 0xFF);
+
+}/* End Routine */
+
+/* BUFF_CACHED_CONTENTS - Return the character and positional information
+ *
+ * Function:
+ *
+ * This routine is called to fetch the single character which is at
+ * the specified buffer position. This routine is generally called
+ * by routines which are low frequence or only handle a small number
+ * of characters at any one time.
+ */
+int
+buff_cached_contents(hbp,position,cache)
+register struct buff_header *hbp;
+int position;
+struct position_cache *cache;
+{
+register struct buff_line *lbp;
+register int i;
+
+ PREAMBLE();
+/*
+ * Insure that the specified position is legal
+ */
+ if(position > hbp->zee || position < 0){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"buff_contents: illegal position %d %s Z = %d\n",
+ position,hbp->name,hbp->zee);
+ tec_panic(tmp_message);
+ }/* End IF */
+
+/*
+ * Call buff_find_line to find the line structure that the specified
+ * position resides on.
+ */
+ lbp = buff_find_line(hbp,position);
+ if(lbp == NULL) return(-1);
+ i = buff_find_offset(hbp,lbp,position);
+ if(i == -1) return(-1);
+/*
+ * If he has specified a local position cache, copy that information back to
+ * his local cache. It's up to him to invalidate it if the buffer gets changed.
+ */
+ if(cache){
+ cache->lbp = lbp;
+ cache->base = i;
+ }/* End IF */
+
+ return(lbp->buffer[i] & 0xFF);
+
+}/* End Routine */
+
+
+
+/* BUFF_INIT - Routine to initialize the buffer routines
+ *
+ * Function:
+ *
+ * This routine is called before the first buffer operations to
+ * initialize the buffer routines.
+ */
+int
+buff_init()
+{
+ register struct buff_header *hbp;
+ char tmp_buffer[LINE_BUFFER_SIZE];
+
+ PREAMBLE();
+/*
+ * Create the buffer map buffer
+ */
+ (void) strcpy(tmp_buffer,TECO_INTERNAL_BUFFER_NAME);
+ (void) strcat(tmp_buffer,"BUFFER-MAP");
+ hbp = buff_create(tmp_buffer,1);
+ if(hbp == NULL) return(FAIL);
+ hbp->buffer_number = 0;
+
+/*
+ * Create a default edit buffer
+ */
+ (void) strcpy(tmp_buffer,TECO_INTERNAL_BUFFER_NAME);
+ (void) strcat(tmp_buffer,"Main");
+ curbuf = hbp = buff_create(tmp_buffer,1);
+ if(hbp == NULL) return(FAIL);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_OPENBUFFER - Routine to read a file into a TECO edit buffer
+ *
+ * Function:
+ *
+ * This routine is called to load a file into the named buffer. If the
+ * buffer already exists, we simply switch to it and don't modify it.
+ */
+int
+buff_openbuffer(name,buffer_number,readonly_flag)
+char *name;
+int buffer_number;
+int readonly_flag;
+{
+ register struct buff_header *hbp = NULL;
+
+ PREAMBLE();
+/*
+ * If no name is supplied then the buffer_number argument is significant,
+ * otherwise we ignore it.
+ */
+ if(name == NULL){
+ hbp = buffer_headers;
+ while(hbp){
+ if(hbp->buffer_number == buffer_number){
+ name = hbp->name;
+ break;
+ }/* End IF */
+ hbp = hbp->next_header;
+ }/* End While */
+ }/* End IF */
+/*
+ * If no name is supplied, the buffer_number argument must already exist
+ * or an error is declared.
+ */
+ if(name == NULL){
+ error_message("?No such buffer number exists");
+ return(FAIL);
+ }/* End IF */
+/*
+ * First determine if a buffer of that name already exists
+ */
+ if(hbp == NULL){
+ hbp = buff_find(name);
+ }/* End IF */
+
+ if(hbp){
+ buff_switch(hbp,1);
+ return(SUCCESS);
+ }/* End IF */
+
+/*
+ * If there is not such buffer in existence, we need to create one
+ */
+ hbp = buff_create(name,0);
+ if(hbp == NULL) return(FAIL);
+ if(readonly_flag == YES) hbp->isreadonly = YES;
+
+/*
+ * Ignore possible open error, since he may be creating a new file
+ */
+ buff_read(hbp,name);
+
+ hbp->dot = 0;
+ hbp->ismodified = NO;
+
+ buff_switch(hbp,1);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_OPENBUFFNUM - Routine to open buffer number 'n'
+ *
+ * Function:
+ *
+ * This routine is called with the number of a buffer which is to be
+ * made the 'current' edit buffer.
+ */
+int
+buff_openbuffnum(buffer_number,map_flag)
+int buffer_number;
+int map_flag;
+{
+ register struct buff_header *hbp;
+
+ PREAMBLE();
+/*
+ * Search the list of buffers for the specified buffer number
+ */
+ hbp = buffer_headers;
+ while(hbp){
+ if(hbp->buffer_number == buffer_number){
+ buff_switch(hbp,map_flag);
+ return(SUCCESS);
+ }/* End IF */
+ hbp = hbp->next_header;
+ }/* End While */
+
+ return(FAIL);
+
+}/* End Routine */
+
+
+
+/* BUFF_REOPENBUFF - Called by undo to re-create an edit buffer
+ *
+ * Function:
+ *
+ * This routine is called to place a buffer back onto the buffer
+ * list.
+ */
+void
+buff_reopenbuff(bp)
+register struct buff_header *bp;
+{
+register struct buff_header *obp;
+
+ PREAMBLE();
+/*
+ * Make sure there isn't another buffer of this name already here. The
+ * way this can happen is automatically created Q-registers like the
+ * search string and rubout string Q-registers might be created. Since
+ * they don't get undone, they would stick around and then we might
+ * try to undo the removal of an earlier copy of one of them. This way,
+ * we make sure that any earlier copy replaces any more recent copy.
+ */
+ obp = buffer_headers;
+ while(obp){
+ if(strcmp(obp->name,bp->name) == 0){
+ buff_destroy(obp);
+ break;
+ }/* End IF */
+ obp = obp->next_header;
+ }/* End While */
+/*
+ * Insert this buffer in the proper place on the buffer list.
+ */
+ obp = buffer_headers;
+ while(obp){
+ if(obp->next_header == NULL) break;
+ if(obp->next_header->buffer_number > bp->buffer_number) break;
+ obp = obp->next_header;
+ }/* End While */
+
+ if(obp == NULL || buffer_headers->buffer_number > bp->buffer_number){
+ bp->next_header = buffer_headers;
+ buffer_headers = bp;
+ }/* End IF */
+
+ else {
+ bp->next_header = obp->next_header;
+ obp->next_header = bp;
+ }/* End Else */
+
+}/* End Routine */
+
+
+
+/* BUFF_READ - Routine to read a file into an existing TECO edit buffer
+ *
+ * Function:
+ *
+ * This routine is called to read a file into the specified edit buffer.
+ * The edit buffer must already exist.
+ */
+int
+buff_read(hbp,name)
+struct buff_header *hbp;
+char *name;
+{
+ int iochan;
+ char tmp_message[LINE_BUFFER_SIZE];
+ int status;
+
+ PREAMBLE();
+/*
+ * Attempt to open the file
+ */
+ iochan = open(name,O_RDONLY);
+ if(iochan < 0){
+ sprintf(tmp_message,"?Cannot open <%s>: %s",name,error_text(errno));
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ status = buff_readfd(hbp,name,iochan);
+ close(iochan);
+ return(status);
+
+}/* End Routine */
+
+
+
+/* BUFF_READFD - Reads the file descriptor into the specified buffer
+ *
+ * Function:
+ *
+ * This routine is called with a buffer and a file descriptor. The
+ * entire contents of the file descriptor are read into the specified
+ * buffer.
+ */
+int
+buff_readfd(hbp,name,iochan)
+struct buff_header *hbp;
+char *name;
+int iochan;
+{
+ char iobuf[IO_BUFFER_SIZE];
+ char linebuf[IO_BUFFER_SIZE];
+ int linebuf_cnt;
+ char tmp_message[LINE_BUFFER_SIZE];
+ register int bcount;
+ register char *cp,*iop = 0;
+ register int i;
+ register struct buff_line *lbp;
+ struct buff_line *olbp;
+ int original_zee = hbp->zee;
+ int error_status = SUCCESS;
+
+ PREAMBLE();
+/*
+ * Read the file and insert the characters into the buffer
+ */
+ cp = linebuf;
+ linebuf_cnt = bcount = 0;
+
+ while(1){
+ if(bcount <= 0){
+ bcount = read(iochan,iobuf,sizeof(iobuf));
+ iop = iobuf;
+ if(bcount == 0) break;
+ if(bcount < 0){
+ sprintf(tmp_message,"?Error reading <%s>: %s",
+ name,error_text(errno));
+ error_message(tmp_message);
+ error_status = FAIL;
+ break;
+ }/* End IF */
+ }/* End IF */
+
+ linebuf_cnt += 1;
+ bcount -= 1;
+ if((*cp++ = *iop++) == '\n' || linebuf_cnt >= sizeof(linebuf)){
+ lbp = buff_find_line(hbp,hbp->dot);
+ if(lbp == NULL){
+ error_status = FAIL;
+ break;
+ }/* End IF */
+ i = buff_find_offset(hbp,lbp,hbp->dot);
+ if(i == -1){
+ error_status = FAIL;
+ break;
+ }/* End IF */
+ if(i == 0){
+ olbp = lbp;
+ lbp = allocate_line_buffer(linebuf_cnt);
+ if(lbp == NULL){
+ error_status = FAIL;
+ break;
+ }/* End IF */
+
+ lbp->next_line = olbp;
+ lbp->prev_line = olbp->prev_line;
+ olbp->prev_line = lbp;
+ if(lbp->prev_line) lbp->prev_line->next_line = lbp;
+ if(hbp->first_line == olbp) hbp->first_line = lbp;
+ movc3(linebuf,lbp->buffer,linebuf_cnt);
+ lbp->byte_count = linebuf_cnt;
+ hbp->pos_cache.lbp = lbp;
+ hbp->pos_cache.base = hbp->dot;
+ hbp->dot += linebuf_cnt;
+ hbp->zee += linebuf_cnt;
+ }/* End IF */
+
+ else {
+ if(buff_insert(hbp,hbp->dot,linebuf,linebuf_cnt) == FAIL){
+ error_status = FAIL;
+ break;
+ }/* End IF */
+ }/* End Else */
+
+ cp = linebuf;
+ linebuf_cnt = 0;
+ continue;
+ }/* End IF */
+
+ }/* End While */
+
+ if(error_status == SUCCESS && linebuf_cnt != 0){
+ if(buff_insert(hbp,hbp->dot,linebuf,linebuf_cnt) == FAIL){
+ error_status = FAIL;
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * If the buffer just became modified for the first time, we need to change the
+ * label line to reflect this.
+ */
+ if(hbp->ismodified == NO && hbp->zee != original_zee){
+ if(hbp == curbuf){
+ if(screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED) == FAIL){
+ error_status = FAIL;
+ }/* End IF */
+ }/* End IF */
+ hbp->ismodified = YES;
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+ return(error_status);
+
+}/* End Routine */
+
+
+
+/* BUFF_WRITE - Write the specified buffer out to a file
+ *
+ * Function:
+ *
+ * This routine is generally called on an EW command when the user
+ * wishes to write out the contents of the buffer.
+ */
+int
+buff_write(hbp,chan,start,end)
+struct buff_header *hbp;
+int chan;
+int start;
+int end;
+{
+register int bcount;
+register struct buff_line *lbp;
+register char *cp1,*cp2;
+register int line_bcount;
+char iobuf[IO_BUFFER_SIZE];
+int status;
+
+ PREAMBLE();
+
+ bcount = 0;
+ cp2 = iobuf;
+
+ lbp = buff_find_line(hbp,start);
+ line_bcount = buff_find_offset(hbp,lbp,start);
+ cp1 = lbp->buffer + line_bcount;
+ line_bcount = lbp->byte_count - line_bcount;
+
+ while(start < end){
+ if(bcount == sizeof(iobuf)){
+ status = write(chan,iobuf,sizeof(iobuf));
+ if(status != sizeof(iobuf)){
+ return(FAIL);
+ }/* End IF */
+ bcount = 0;
+ cp2 = iobuf;
+ }/* End IF */
+
+ if(line_bcount == 0){
+ lbp = lbp->next_line;
+ cp1 = lbp->buffer;
+ line_bcount = lbp->byte_count;
+ }/* End IF */
+
+ *cp2++ = *cp1++;
+ bcount += 1;
+ line_bcount -= 1;
+ start += 1;
+
+ }/* End While */
+
+ if(bcount > 0){
+ status = write(chan,iobuf,(unsigned)bcount);
+ if(status != bcount){
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_SWITCH - Switch the current edit buffer
+ *
+ * Function:
+ *
+ * This routine is called when we wish to switch the current buffer
+ * to some other edit buffer.
+ */
+int
+buff_switch(hbp,map_flag)
+struct buff_header *hbp;
+int map_flag;
+{
+char tmp_message[LINE_BUFFER_SIZE];
+char *cp;
+
+ PREAMBLE();
+
+ curbuf = curwin->win_buffer = hbp;
+
+ sprintf(tmp_message,"<Buffer %d> %s",hbp->buffer_number,hbp->name);
+ screen_label_line(hbp,tmp_message,LABEL_C_FILENAME);
+
+ cp = "";
+ if(hbp->isreadonly) cp = "(READONLY)";
+ screen_label_line(hbp,cp,LABEL_C_READONLY);
+
+ cp = "";
+ if(hbp->ismodified) cp = "(modified)";
+ screen_label_line(hbp,cp,LABEL_C_MODIFIED);
+
+ if(hbp->buffer_number == 0 && map_flag){
+ buff_buffer_map();
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_BUFFER_MAP - Build a map of the existing buffers.
+ *
+ * Function:
+ *
+ * This routine is called when the buffer map buffer is entered. It is
+ * used to fill this routine with the appropriate text.
+ */
+void
+buff_buffer_map()
+{
+register struct buff_header *hbp;
+char tmp_buffer[LINE_BUFFER_SIZE],padd_buffer[LINE_BUFFER_SIZE];
+register int i;
+int count;
+register char *cp;
+int max_length;
+
+ PREAMBLE();
+
+ buff_delete(curbuf,0,curbuf->zee);
+
+ for(i = 0; i < sizeof(padd_buffer); i++){
+ padd_buffer[i] = ' ';
+ }/* End FOR */
+ padd_buffer[sizeof(padd_buffer)-1] = '\0';
+
+ max_length = 0;
+ for(hbp = buffer_headers; hbp != NULL ; hbp = hbp->next_header){
+ if(hbp->buffer_number == 0) continue;
+ i = strlen(hbp->name);
+ if(i > max_length) max_length = i;
+ }/* End FOR */
+
+ for(hbp = buffer_headers; hbp != NULL ; hbp = hbp->next_header){
+ if(hbp->buffer_number <= 0) continue;
+ i = max_length - strlen(hbp->name);
+ sprintf(tmp_buffer,"<Buffer %-4d> %s%s %s %6d bytes\n",
+ hbp->buffer_number,hbp->name,&padd_buffer[sizeof(padd_buffer)-1-i],
+ hbp->ismodified ? "(modified)" : " ",hbp->zee);
+ i = strlen(tmp_buffer);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,i);
+ }/* End FOR */
+
+ buff_insert(curbuf,curbuf->dot,"\n",1);
+
+ for(hbp = buffer_headers; hbp != NULL ; hbp = hbp->next_header){
+ if(hbp->buffer_number >= 0) continue;
+ i = max_length - strlen(hbp->name);
+ sprintf(tmp_buffer,"<Buffer %-4d> %s%s %s %6d bytes\n",
+ hbp->buffer_number,hbp->name,&padd_buffer[sizeof(padd_buffer)-1-i],
+ hbp->ismodified ? "(modified)" : " ",hbp->zee);
+ i = strlen(tmp_buffer);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,i);
+ }/* End FOR */
+
+/*
+ * Here to tell him if any q registers are currently on the push down
+ * list.
+ */
+ i = count = 0;
+ for(hbp = qregister_push_down_list; hbp != NULL ; hbp = hbp->next_header){
+ i += 1;
+ count += hbp->zee;
+ }/* End FOR */
+
+ if(i > 0){
+ sprintf(tmp_buffer,
+ "\nQ register push down list contains %d register%s, %d bytes\n",
+ i,(i > 1)?"s":"",count);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+ }/* End IF */
+
+/*
+ * Insert our TITLE
+ */
+ max_length = term_columns <= 80 ? term_columns : 80;
+
+ buff_insert(curbuf,curbuf->dot,"\n\n",2);
+ (void) strcpy(tmp_buffer,"Video TECO");
+ i = (max_length - strlen(tmp_buffer))/2;
+ if(i < 0) i = 0;
+ (void) strcat(tmp_buffer,"\n");
+ buff_insert(curbuf,curbuf->dot,padd_buffer,i);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+ (void) strcpy(tmp_buffer," by Paul Cantrell & Joyce Nishinaga");
+ i = (max_length - strlen(tmp_buffer))/2;
+ if(i < 0) i = 0;
+ (void) strcat(tmp_buffer,"\n");
+ buff_insert(curbuf,curbuf->dot,padd_buffer,i);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+#if 0
+ (void) strcpy(tmp_buffer,id_string);
+ if(tmp_buffer[0] == '%'){
+ tmp_buffer[0] = '\0';
+ cp = rcs_id;
+ i = 0;
+ if(*cp == '$'){
+ cp++;
+ while(cp[i] == "Revision: "[i]) i += 1;
+ cp += i;
+ }/* End IF */
+ i = 0;
+ while(*cp != '$' && *cp != '\0'){
+ tmp_buffer[i++] = *cp++;
+ }/* End While */
+ tmp_buffer[i] = '\0';
+ if(i <= 2) sprintf(tmp_buffer,"*%d.%d* ",VMAJOR,VMINOR);
+
+ cp = rcs_date;
+ i = 0;
+ if(*cp == '$'){
+ cp++;
+ while(cp[i] == "Date: "[i]) i += 1;
+ cp += i;
+ }/* End IF */
+ i = strlen(tmp_buffer);
+ while(*cp != '$' && *cp != '\0'){
+ tmp_buffer[i++] = *cp++;
+ }/* End While */
+ tmp_buffer[i] = '\0';
+
+ }/* End IF */
+#endif
+ sprintf(tmp_buffer,"*%d.%d* ",VMAJOR,VMINOR);
+ cp = rcs_date;
+ i = 0;
+ if(*cp == '$'){
+ cp++;
+ while(cp[i] == "Date: "[i]) i += 1;
+ cp += i;
+ }/* End IF */
+ i = strlen(tmp_buffer);
+ while(*cp != '$' && *cp != '\0'){
+ tmp_buffer[i++] = *cp++;
+ }/* End While */
+ tmp_buffer[i] = '\0';
+
+ i = (max_length - strlen(tmp_buffer))/2;
+ if(i < 0) i = 0;
+ (void) strcat(tmp_buffer,"\n");
+ buff_insert(curbuf,curbuf->dot,padd_buffer,i);
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+ tecmem_stats();
+
+ curbuf->dot = 0;
+ return;
+
+}/* End Routine */
+
+
+
+/* BUFF_INSERT - Insert a string at current position
+ *
+ * Function:
+ *
+ * This routine is called to insert a string into the specified buffer
+ * at the buffer's current location.
+ */
+int
+buff_insert(hbp,position,buffer,length)
+struct buff_header *hbp;
+register int position;
+register char *buffer;
+register int length;
+{
+struct buff_header fake_header;
+struct buff_line *fake_line;
+register char *cp;
+
+ PREAMBLE();
+
+ fake_line =
+ (struct buff_line *)tec_alloc(TYPE_C_LINE,sizeof(struct buff_line));
+
+ if(fake_line == NULL) return(FAIL);
+
+ fake_header.dot = 0;
+ fake_header.zee = 0;
+ fake_header.pos_cache.lbp = fake_line;
+ fake_header.pos_cache.base = 0;
+ fake_header.first_line = fake_line;
+
+ fake_line->buffer_size = LINE_BUFFER_SIZE;
+ fake_line->buffer = buffer;
+ fake_line->next_line = NULL;
+ fake_line->prev_line = NULL;
+ fake_line->format_line = NULL;
+
+ while(length){
+ cp = fake_line->buffer;
+ fake_line->byte_count = 0;
+ while(1){
+ fake_line->byte_count += 1;
+ length -= 1;
+ if(*cp == '\n' || length == 0){
+ buff_insert_from_buffer_with_undo(
+ NULL,
+ hbp,
+ position,
+ &fake_header,
+ 0,
+ fake_line->byte_count
+ );
+/*
+ * The following is a terrible kludge. The buff_find_line code ends up
+ * inserting a null line buffer after our fake line, in order to
+ * represent the location following the last character. If we don't
+ * free it, it will get lost and consume memory...
+ */
+ if(fake_line->next_line){
+ buff_free_line_buffer(fake_line->next_line);
+ fake_line->next_line = NULL;
+ }/* End IF */
+ fake_line->buffer += fake_line->byte_count;
+ position += fake_line->byte_count;
+ break;
+ }/* End IF */
+ cp++;
+ }/* End While */
+ }/* End While */
+
+ tec_release(TYPE_C_LINE,(char *)fake_line);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_INSERT_FROM_BUFFER_WITH_UNDO - Copy bytes from another buffer
+ *
+ * Function:
+ *
+ * This routine copies bytes from one buffer to another, arranging
+ * undo capability so that we can reverse the process. Because it
+ * knows the lengths of each line, this should be a very efficient
+ * method of inserting data, and should certainly have preference
+ * over using the byte-at-a-time routines.
+ */
+int
+buff_insert_from_buffer_with_undo(ct,dbp,dest_position,sbp,src_position,length)
+struct cmd_token *ct;
+struct buff_header *dbp;
+int dest_position;
+struct buff_header *sbp;
+int src_position;
+int length;
+{
+register int i;
+struct undo_token *ut = 0;
+struct buff_line *dlbp,*sb_lbp,*se_lbp,*nlbp;
+int doff,sb_off,se_off;
+int newline_included_in_src_data;
+int bytes_to_copy_from_source;
+int bytes_required_for_line,bytes_to_allocate;
+char *new_buffer;
+register char *dcp,*scp;
+int bytes_inserted_so_far = 0;
+
+#ifdef DEBUG
+char outbuf[1024];
+char inbuf[10];
+extern int tty_input_chan;
+ term_goto(0,0);
+ term_clrtobot();
+ term_flush();
+#endif /* DEBUG */
+
+ PREAMBLE();
+/*
+ * Insure that the specified position is legal
+ */
+ if(src_position < 0 || src_position > sbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(
+ tmp_message,
+ "buff_insert_from_buffer_with_undo: bad src pos %d %s Z = %d\n",
+ src_position,
+ sbp->name,sbp->zee
+ );
+ tec_panic(tmp_message);
+ }/* End IF */
+ if(dest_position < 0 || dest_position > dbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(
+ tmp_message,
+ "buff_insert_from_buffer_with_undo: bad dest pos %d %s Z = %d\n",
+ dest_position,
+ dbp->name,dbp->zee
+ );
+ tec_panic(tmp_message);
+ }/* End IF */
+
+ if(ct){
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ }/* End IF */
+
+/*
+ * Determine which line the current insert point is on by calling the
+ * find routines.
+ */
+ dlbp = buff_find_line(dbp,dest_position);
+ doff = buff_find_offset(dbp,dlbp,dest_position);
+
+ sb_lbp = buff_find_line(sbp,src_position);
+ sb_off = buff_find_offset(sbp,sb_lbp,src_position);
+
+ se_lbp = buff_find_line(sbp,src_position+length);
+ se_off = buff_find_offset(sbp,se_lbp,src_position+length);
+
+#ifdef DEBUG
+ movc3(dlbp->buffer,outbuf,dlbp->byte_count);
+ outbuf[dlbp->byte_count] = '\0';
+ if(outbuf[dlbp->byte_count-1] == '\n') outbuf[dlbp->byte_count-1] = '\0';
+ fprintf(stderr,"'%s' dest begin doff %d\n",outbuf,doff);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[doff+1] = '^'; outbuf[doff+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+
+ movc3(sb_lbp->buffer,outbuf,sb_lbp->byte_count);
+ outbuf[sb_lbp->byte_count] = '\0';
+ if(outbuf[sb_lbp->byte_count-1] == '\n'){
+ outbuf[sb_lbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' source begin sb_off %d\n",outbuf,sb_off);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[sb_off+1] = '^'; outbuf[sb_off+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+
+ movc3(se_lbp->buffer,outbuf,se_lbp->byte_count);
+ outbuf[se_lbp->byte_count] = '\0';
+ if(outbuf[se_lbp->byte_count-1] == '\n'){
+ outbuf[se_lbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' source end se_off %d\n",outbuf,se_off);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[se_off+1] = '^'; outbuf[se_off+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+#endif /* DEBUG */
+
+/*
+ * If the buffer just became modified for the first time, we need to change the
+ * label line to reflect this.
+ */
+ if(dbp->ismodified == NO){
+ if(dbp == curbuf){
+ screen_label_line(dbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ dbp->ismodified = YES;
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * Since we are modifying the destination line buffer structure, we
+ * want to delete any format structures so that the screen display
+ * code will know to reformat it.
+ */
+ if(dlbp->format_line){
+ screen_free_format_lines(dlbp->format_line);
+ dlbp->format_line = NULL;
+ }/* End IF */
+/*
+ * The cases we have to handle are much more complex if there are newlines
+ * being inserted, so we determine now whether this is the case or not.
+ */
+ newline_included_in_src_data = 1;
+ if(sb_lbp->buffer[sb_lbp->byte_count-1] != '\n'){
+ newline_included_in_src_data = 0;
+ }/* End IF */
+
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "newline_included_in_src_data = %d\n",
+ newline_included_in_src_data
+ );
+#endif /* DEBUG */
+
+ bytes_to_copy_from_source = sb_lbp->byte_count - sb_off;
+ if(bytes_to_copy_from_source > length){
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "bytes_to_copy_from_source %d > length %d\n",
+ bytes_to_copy_from_source,
+ length
+ );
+#endif /* DEBUG */
+ bytes_to_copy_from_source = length;
+ newline_included_in_src_data = 0;
+ }/* End IF */
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "bytes_to_copy_from_source is now %d\n",
+ bytes_to_copy_from_source
+ );
+#endif /* DEBUG */
+
+/*
+ * Test for a simple case - one in which there are no newlines in the source
+ * data. In this case, we know that we simply have to insert it into the
+ * current line buffer. If the buffer area is not big enough, we may have
+ * to allocate a new buffer.
+ */
+ if(newline_included_in_src_data == 0){
+/*
+ * The total line length will be the current line length, plus the amount
+ * of data being inserted.
+ */
+ bytes_required_for_line = dlbp->byte_count + bytes_to_copy_from_source;
+
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "no newline, bytes_required_for_line %d \n",
+ bytes_required_for_line
+ );
+#endif /* DEBUG */
+
+/*
+ * If the current buffer is too small, we allocate a new buffer and copy
+ * all the data into it, and then free the old buffer.
+ */
+ if(bytes_required_for_line > dlbp->buffer_size){
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "new allocation required\n"
+ );
+#endif /* DEBUG */
+ bytes_to_allocate =
+ bytes_required_for_line + INCREMENTAL_LINE_BUFFER_SIZE - 1;
+ bytes_to_allocate -=
+ bytes_to_allocate % INCREMENTAL_LINE_BUFFER_SIZE;
+ if(dlbp->buffer_size > LARGEST_LOOKASIDE_BLOCK){
+ bytes_to_allocate = dlbp->buffer_size * 2;
+ }/* End IF */
+
+ if(bytes_to_allocate < bytes_required_for_line){
+ char tmp_message[1024];
+ sprintf(
+ tmp_message,
+ "buff_insert_from_buffer_with_undo need %d alloced %d\n",
+ bytes_required_for_line,
+ bytes_to_allocate
+ );
+ tec_panic(tmp_message);
+ }/* End IF */
+
+ new_buffer =
+ tec_alloc(
+ TYPE_C_LINEBUF,
+ bytes_to_allocate
+ );
+
+ if(new_buffer == NULL){
+ return(FAIL);
+ }/* End IF */
+
+ dcp = new_buffer;
+/*
+ * Copy up to the current insertion point in the new buffer, then the
+ * bytes coming from the source line, then if the source line didn't
+ * contribute a newline, the rest of the bytes coming from the original
+ * destination line.
+ */
+ movc3(dlbp->buffer,dcp,doff);
+ dcp += doff;
+ movc3(sb_lbp->buffer+sb_off,dcp,bytes_to_copy_from_source);
+ dcp += bytes_to_copy_from_source;
+ movc3(&dlbp->buffer[doff],dcp,dlbp->byte_count-doff);
+ bytes_inserted_so_far += bytes_to_copy_from_source;
+
+ tec_release(TYPE_C_LINEBUF,dlbp->buffer);
+ dlbp->buffer = new_buffer;
+ dlbp->buffer_size = bytes_to_allocate;
+ dlbp->byte_count = bytes_required_for_line;
+ }/* End IF */
+/*
+ * If the current buffer is large enough, make a hole to insert the new
+ * data into, and copy in the new data.
+ */
+ else {
+#ifdef DEBUG
+ fprintf(
+ stderr,
+ "it will fit into the current size of %d\n",
+ dlbp->buffer_size
+ );
+#endif /* DEBUG */
+ dcp = &dlbp->buffer[bytes_required_for_line-1];
+ scp = &dlbp->buffer[dlbp->byte_count-1];
+ for(i = 0; i < dlbp->byte_count - doff; i++){
+ *dcp-- = *scp--;
+ }/* End FOR */
+ movc3(
+ &sb_lbp->buffer[sb_off],
+ &dlbp->buffer[doff],
+ bytes_to_copy_from_source
+ );
+ dlbp->byte_count = bytes_required_for_line;
+ bytes_inserted_so_far += bytes_to_copy_from_source;
+ }/* End Else */
+
+#ifdef DEBUG
+ doff += bytes_to_copy_from_source;
+ movc3(dlbp->buffer,outbuf,dlbp->byte_count);
+ outbuf[dlbp->byte_count] = '\0';
+ if(outbuf[dlbp->byte_count-1] == '\n'){
+ outbuf[dlbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' new dest doff %d\n",outbuf,doff);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[doff+1] = '^'; outbuf[doff+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+#endif /* DEBUG */
+ }/* End IF */
+
+/*
+ * Else, if there is a newline involved, things get more complicated...
+ * The first thing we do is construct a line buffer which holds any
+ * trailing part of the source data (i.e. not terminated by a newline)
+ * along with the trailing part of the line we are inserting into.
+ */
+ else {
+ bytes_required_for_line = dlbp->byte_count - doff + se_off;
+ if(bytes_required_for_line > 0){
+ nlbp = allocate_line_buffer(bytes_required_for_line);
+ if(nlbp == NULL) return(FAIL);
+
+ nlbp->next_line = dlbp->next_line;
+ dlbp->next_line = nlbp;
+ if(nlbp->next_line) nlbp->next_line->prev_line = nlbp;
+ nlbp->prev_line = dlbp;
+ movc3(se_lbp->buffer,nlbp->buffer,se_off);
+ movc3(
+ &dlbp->buffer[doff],
+ &nlbp->buffer[se_off],
+ dlbp->byte_count-doff
+ );
+ nlbp->byte_count = bytes_required_for_line;
+ dlbp->byte_count -= ( dlbp->byte_count - doff );
+ bytes_inserted_so_far += se_off;
+ }/* End IF */
+/*
+ * Now we want to merge the data from the begining of the original
+ * destinatio line, and the data up to the first newline from the
+ * source data.
+ */
+ bytes_required_for_line =
+ dlbp->byte_count + sb_lbp->byte_count - sb_off;
+ if(bytes_required_for_line > dlbp->buffer_size){
+ bytes_to_allocate =
+ bytes_required_for_line + INCREMENTAL_LINE_BUFFER_SIZE - 1;
+ bytes_to_allocate -=
+ bytes_to_allocate % INCREMENTAL_LINE_BUFFER_SIZE;
+ if(dlbp->buffer_size > LARGEST_LOOKASIDE_BLOCK){
+ bytes_to_allocate = dlbp->buffer_size * 2;
+ }/* End IF */
+
+ if(bytes_to_allocate < bytes_required_for_line){
+ char tmp_message[1024];
+ sprintf(
+ tmp_message,
+ "buff_insert_from_buffer_with_undo needs %d alloced %d\n",
+ bytes_required_for_line,
+ bytes_to_allocate
+ );
+ tec_panic(tmp_message);
+ }/* End IF */
+
+ new_buffer =
+ tec_alloc(
+ TYPE_C_LINEBUF,
+ bytes_to_allocate
+ );
+ if(new_buffer != NULL){
+ dcp = new_buffer;
+ movc3(dlbp->buffer,dcp,dlbp->byte_count);
+ dcp += dlbp->byte_count;
+ movc3(&sb_lbp->buffer[sb_off],dcp,sb_lbp->byte_count - sb_off);
+ tec_release(TYPE_C_LINEBUF,dlbp->buffer);
+ dlbp->buffer = new_buffer;
+ dlbp->buffer_size = bytes_to_allocate;
+ dlbp->byte_count = bytes_required_for_line;
+ bytes_inserted_so_far += (sb_lbp->byte_count - sb_off);
+ }/* End IF */
+ }/* End IF */
+
+ else {
+ movc3(
+ &sb_lbp->buffer[sb_off],
+ &dlbp->buffer[doff],
+ sb_lbp->byte_count - sb_off
+ );
+ dlbp->byte_count = bytes_required_for_line;
+ bytes_inserted_so_far += (sb_lbp->byte_count - sb_off);
+ }/* End Else */
+
+/*
+ * Now we want to copy all the full-length lines in between the starting
+ * and ending line buffers.
+ */
+ sb_lbp = sb_lbp->next_line;
+ while(sb_lbp){
+ if(sb_lbp == se_lbp) break;
+ bytes_required_for_line = sb_lbp->byte_count;
+ nlbp = allocate_line_buffer(bytes_required_for_line);
+ if(nlbp == NULL) break;
+ nlbp->next_line = dlbp->next_line;
+ dlbp->next_line = nlbp;
+ if(nlbp->next_line) nlbp->next_line->prev_line = nlbp;
+ nlbp->prev_line = dlbp;
+ movc3(sb_lbp->buffer,nlbp->buffer,bytes_required_for_line);
+ nlbp->byte_count = bytes_required_for_line;
+ bytes_inserted_so_far += bytes_required_for_line;
+ dlbp = nlbp;
+ sb_lbp = sb_lbp->next_line;
+ }/* End While */
+
+#ifdef DEBUG
+ movc3(dlbp->buffer,outbuf,dlbp->byte_count);
+ outbuf[dlbp->byte_count] = '\0';
+ if(outbuf[dlbp->byte_count-1] == '\n'){
+ outbuf[dlbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' new dest doff %d\n",outbuf,doff);
+ for(i = 0; i < sizeof(outbuf); i++) outbuf[i] = ' ';
+ outbuf[doff+1] = '^'; outbuf[doff+2] = '\0';
+ fprintf(stderr,"%s\n",outbuf);
+
+ movc3(nlbp->buffer,outbuf,nlbp->byte_count);
+ outbuf[nlbp->byte_count] = '\0';
+ if(outbuf[nlbp->byte_count-1] == '\n'){
+ outbuf[nlbp->byte_count-1] = '\0';
+ }
+ fprintf(stderr,"'%s' new line\n",outbuf);
+#endif /* DEBUG */
+ }/* End Else */
+
+
+ if(ct){
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)dbp;
+ ut->iarg1 = dest_position;
+ ut->iarg2 = bytes_inserted_so_far;
+ }/* End IF */
+
+/*
+ * Depending on whether or not the insert position precedes dot or not, we
+ * may have to increment dot.
+ */
+ if(dest_position <= dbp->dot) dbp->dot += bytes_inserted_so_far;
+ dbp->zee += bytes_inserted_so_far;
+/*
+ * If we are inserting characters before the position cache, it will
+ * shift our cached position down by the length, so we adjust the cache.
+ */
+ if(dest_position < dbp->pos_cache.base){
+ dbp->pos_cache.base += bytes_inserted_so_far;
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+/* BUFF_INSERT_WITH_UNDO - Insert a string and arrange for undo capability
+ *
+ * Function:
+ *
+ * This routine is called when characters need to be inserted, and
+ * also need to be un-inserted if the command is undone.
+ */
+int
+buff_insert_with_undo(ct,hbp,position,buffer,length)
+struct cmd_token *ct;
+struct buff_header *hbp;
+register int position;
+register char *buffer;
+register int length;
+{
+struct undo_token *ut;
+
+ PREAMBLE();
+
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)hbp;
+ ut->iarg1 = position;
+ ut->iarg2 = length;
+
+ if(buff_insert(hbp,position,buffer,length) == FAIL){
+ ut->iarg2 = 0;
+ return(FAIL);
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_INSERT_CHAR - Insert the specified character into the current position
+ *
+ * Function:
+ *
+ * This routine is called to insert the single character into the buffer
+ * at the current position (dot). It has to worry about such things as
+ * noting that the buffer has been modified, the screen display may be
+ * invalid, etc.
+ */
+int
+buff_insert_char(hbp,position,data)
+register struct buff_header *hbp;
+int position;
+char data;
+{
+register struct buff_line *lbp;
+struct buff_line *nlbp;
+register char *cp,*ocp;
+register int i,j;
+
+ PREAMBLE();
+
+/*
+ * Insure that the specified position is legal
+ */
+ if(position > hbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"buff_insert_char: bad position %d %s Z = %d\n",
+ position,hbp->name,hbp->zee);
+ tec_panic(tmp_message);
+ }/* End IF */
+/*
+ * Determine which line the current insert point is on by calling the
+ * find routines.
+ */
+ lbp = buff_find_line(hbp,position);
+ i = buff_find_offset(hbp,lbp,position);
+/*
+ * Depending on whether or not the specified position precedes dot or not, we
+ * may have to decrement dot.
+ */
+ if(position <= hbp->dot) hbp->dot += 1;
+ hbp->zee += 1;
+/*
+ * If the buffer just became modified for the first time, we need to change the
+ * label line to reflect this.
+ */
+ if(hbp->ismodified == NO){
+ if(hbp == curbuf){
+ screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ hbp->ismodified = YES;
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * If we are inserting a character before the position cache, it will
+ * shift our cached position down by one, so we adjust the cache.
+ */
+ if(position < hbp->pos_cache.base){
+ hbp->pos_cache.base += 1;
+ }/* End IF */
+/*
+ * If there is a format_line structure associated with this line, we make it
+ * go away so that the screen display routine will rebuild the display contents
+ * of this line.
+ */
+ if(lbp->format_line){
+ screen_free_format_lines(lbp->format_line);
+ lbp->format_line = NULL;
+ }/* End IF */
+
+/*
+ * If this is a newline, we do things a little differently because this will
+ * actually cause the line to be broken up. In this case we actually have to
+ * create a new line buffer structure.
+ */
+ if(data == '\n'){
+/*
+ * Calculate how many bytes are to move to the new line
+ */
+ j = lbp->byte_count - i;
+ if(j > 0){
+ nlbp = allocate_line_buffer(j);
+ if(nlbp == NULL) return(FAIL);
+ nlbp->next_line = lbp->next_line;
+ lbp->next_line = nlbp;
+ if(nlbp->next_line) nlbp->next_line->prev_line = nlbp;
+ nlbp->prev_line = lbp;
+
+/*
+ * Now copy the data into the new line buffer
+ */
+ cp = lbp->buffer + i;
+ ocp = nlbp->buffer;
+
+ while(nlbp->byte_count < j){
+ *ocp++ = *cp++;
+ nlbp->byte_count++;
+ lbp->byte_count--;
+ }/* End While */
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * If this line buffer does not have room for the data, we need to expand it
+ */
+ if(lbp->byte_count == lbp->buffer_size){
+ ocp =
+ tec_alloc(
+ TYPE_C_LINEBUF,
+ lbp->buffer_size + INCREMENTAL_LINE_BUFFER_SIZE
+ );
+ if(ocp == NULL) return(FAIL);
+ movc3(lbp->buffer,ocp,lbp->buffer_size);
+ tec_release(TYPE_C_LINEBUF,lbp->buffer);
+ lbp->buffer = ocp;
+ lbp->buffer_size += INCREMENTAL_LINE_BUFFER_SIZE;
+ }/* End IF */
+
+
+/*
+ * Make a hole in which to place the byte.
+ */
+ if(lbp->byte_count){
+ ocp = lbp->buffer + lbp->byte_count;
+ cp = ocp - 1;
+ j = lbp->byte_count - i;
+ while(j--){
+ *ocp-- = *cp--;
+ }/* End While */
+ }/* End IF */
+
+ cp = lbp->buffer + i;
+/*
+ * Ok, now insert the data into the hole
+ */
+ *cp = data;
+ lbp->byte_count += 1;
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+/* BUFF_INSERT_CHAR_WITH_UNDO - Insert a single char with undo capability
+ *
+ * Function:
+ *
+ * This is an envelope routine which guarentees that the character
+ * to be input can be undone if necessary.
+ */
+int
+buff_insert_char_with_undo(ct,hbp,position,data)
+struct cmd_token *ct;
+register struct buff_header *hbp;
+int position;
+char data;
+{
+struct undo_token *ut;
+
+ PREAMBLE();
+
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)hbp;
+ ut->iarg1 = position;
+ ut->iarg2 = 1;
+
+ if(buff_insert_char(hbp,position,data) == FAIL){
+ ut->iarg2 = 0;
+ return(FAIL);
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_DELETE - Delete specified number of characters
+ *
+ * Function:
+ *
+ * This routine is called to delete the specified number of characters
+ * at the specified location in the buffer.
+ */
+void
+buff_delete(hbp,position,count)
+struct buff_header *hbp;
+int position;
+register int count;
+{
+
+ PREAMBLE();
+/*
+ * Insure that there are that many character in the buffer after the specified
+ * position.
+ */
+ if((position + count) > hbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"buff_delete: illegal range %d-%d %s Z = %d\n",
+ position,position+count,hbp->name,hbp->zee);
+ tec_panic(tmp_message);
+ }/* End IF */
+/*
+ * For now we are cheap and just repeatedly call the single character delete
+ * routine, because this is simple. It is also very expensive and will have to
+ * be fixed eventually.
+ */
+ while(count){
+ buff_delete_char(hbp,position);
+ count -= 1;
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* BUFF_DELETE_CHAR - Delete the character at the current position
+ *
+ * Function:
+ *
+ * This routine is called to delete the single character which is at
+ * the current buffer position.
+ */
+int
+buff_delete_char(hbp,position)
+register struct buff_header *hbp;
+int position;
+{
+register struct buff_line *lbp;
+struct buff_line *nlbp;
+register char *cp,*ocp;
+register int i,j;
+
+ PREAMBLE();
+/*
+ * Insure that the specified position is legal
+ */
+ if(position >= hbp->zee){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"buff_delete_char: bad position %d %s Z = %d\n",
+ position,hbp->name,hbp->zee);
+ tec_panic(tmp_message);
+ }/* End IF */
+/*
+ * Call buff_find_line to find the line structure that the specified
+ * position resides on.
+ */
+ lbp = buff_find_line(hbp,position);
+ i = buff_find_offset(hbp,lbp,position);
+/*
+ * If there is a format_line structure associated with this line, we make it
+ * go away so that the screen display routine will rebuild the display contents
+ * of this line.
+ */
+ if(lbp->format_line){
+ screen_free_format_lines(lbp->format_line);
+ lbp->format_line = NULL;
+ }/* End IF */
+/*
+ * Get a pointer to the character that is about to be deleted
+ */
+ cp = lbp->buffer + i;
+/*
+ * Depending on whether or not the specified position precedes dot or not, we
+ * may have to decrement dot.
+ */
+ if(position < hbp->dot) hbp->dot -= 1;
+ hbp->zee -= 1;
+/*
+ * If this has modified the buffer for the first time, it needs to be displayed
+ * in the label line.
+ */
+ if(hbp->ismodified == NO){
+ hbp->ismodified = YES;
+ if(hbp == curbuf){
+ screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * If we are deleting a character before the position cache, it will
+ * shift our cached position up by one, so we adjust the cache.
+ */
+ if(position < hbp->pos_cache.base){
+ hbp->pos_cache.base -= 1;
+ }/* End IF */
+/*
+ * If this is not a newline, then things are pretty simple. Just
+ * make the character go away...
+ */
+ if(*cp != '\n'){
+ movc3(cp+1,cp,lbp->byte_count-i-1);
+ lbp->byte_count -= 1;
+ return(SUCCESS);
+ }/* End IF */
+
+/*
+ * If this is a newline, we do things a little differently because this
+ * actually causes the two lines to become concatenated (unless this is the
+ * final line in the buffer). If the buffer area is not big enough to hold
+ * both lines, we have to create a bigger area.
+ */
+ nlbp = lbp->next_line;
+ if(nlbp == NULL){
+ lbp->byte_count -= 1;
+ return(SUCCESS);
+ }/* End IF */
+
+/*
+ * If there is a format_line structure associated with the second line, we make
+ * it go away since this line is about to be trashed.
+ */
+ if(nlbp->format_line){
+ screen_free_format_lines(nlbp->format_line);
+ nlbp->format_line = NULL;
+ }/* End IF */
+
+/*
+ * Test whether or not the buffer is big enough to hold the data from both
+ * lines.
+ */
+ j = lbp->byte_count + nlbp->byte_count - 1;
+ if(lbp->buffer_size < j){
+ i = j + INCREMENTAL_LINE_BUFFER_SIZE - 1;
+ i -= i % INCREMENTAL_LINE_BUFFER_SIZE;
+ cp = tec_alloc(TYPE_C_LINEBUF,i);
+ if(cp == NULL) return(FAIL);
+ movc3(lbp->buffer,cp,lbp->buffer_size);
+ tec_release(TYPE_C_LINEBUF,lbp->buffer);
+ lbp->buffer = cp;
+ lbp->buffer_size = i;
+ }/* End IF */
+
+/*
+ * Ok, now concatenate the two line buffers by copying the second line
+ * into the first. Because we copy on top of the newline, it effectively
+ * goes away, so we have to decrement the line count by one.
+ */
+ lbp->byte_count -= 1;
+ cp = lbp->buffer + lbp->byte_count;
+ ocp = nlbp->buffer;
+ i = nlbp->byte_count;
+ while(i--){
+ *cp++ = *ocp++;
+ lbp->byte_count++;
+ }/* End While */
+
+/*
+ * Fixup the next and previous pointers so that they point around the
+ * line that we are about to remove.
+ */
+ lbp->next_line = nlbp->next_line;
+ if(nlbp->next_line) nlbp->next_line->prev_line = lbp;
+
+ if(hbp->pos_cache.lbp == nlbp) hbp->pos_cache.lbp = NULL;
+ buff_free_line_buffer(nlbp);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_DELETE_WITH_UNDO - Clobber buffer positions and build undo list
+ *
+ * Function:
+ *
+ * This routine is called when we want to delete a range of bytes in the
+ * buffer, and build an undo list so that we could replace them if we need
+ * to. This happens to be pretty efficient for a bulk delete since we can
+ * just move the line buffers over to the undo list.
+ */
+int
+buff_delete_with_undo(ct,hbp,position,count)
+struct cmd_token *ct;
+struct buff_header *hbp;
+int position;
+int count;
+{
+register char *cp;
+struct undo_token *ut;
+register struct buff_line *lbp;
+struct buff_line *top_lbp;
+int top_offset,local_count,bulk_position;
+int bytes_deleted_so_far = 0;
+
+ PREAMBLE();
+
+/* ???
+ * I think I may need this to insure that dot doesn't get changed. For
+ * instance, if we had <dot>abc and deleted 'a', we would have <dot>bc.
+ * Then if we undo, it's an insert of 'a' at position zero which causes
+ * dot to shift so we would now have a<dot>bc.
+ */
+ if(hbp->dot == (position+count)){
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+ }/* End IF */
+
+/*
+ * Because some of the undo functions only work in the 'current' buffer, if
+ * the user is deleting from a buffer other than the 'current' one, we have
+ * to set up some undo tokens to switch the current buffer back and forth
+ * during the undo process, should it occur.
+ */
+ if(hbp != curbuf){
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = curbuf->buffer_number;
+ }/* End IF */
+
+ top_lbp = buff_find_line(hbp,position);
+ top_offset = buff_find_offset(hbp,top_lbp,position);
+
+/*
+ * First step will be to delete the (hopefully) large block of line buffer
+ * structures in the middle. This will leave a partial line at the top and
+ * also at the bottom. To start, if the begining is in the middle of a line,
+ * we need to skip over the bytes on that line.
+ */
+ local_count = count;
+ lbp = top_lbp;
+ bulk_position = position;
+ if(top_offset){
+ local_count -= lbp->byte_count - top_offset;
+ bulk_position = position + lbp->byte_count - top_offset;
+ lbp = lbp->next_line;
+ }/* End IF */
+ if(lbp) top_lbp = lbp->prev_line;
+
+/*
+ * Ok, now we want to chain over all the line buffer structures which can be
+ * removed in bulk. lbp gets left pointing at the final line which can be bulk
+ * removed.
+ */
+ if(lbp && lbp->byte_count <= local_count){
+/*
+ * We will get to bulk delete at least one line, so allocate an undo buffer.
+ * Point the undo token at the head of the list of line buffers. Remember
+ * the buffer position of the first byte of bulk data in iarg1, and keep a
+ * running count of the number of bytes of bulk data in iarg2.
+ */
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_BULK_INSERT;
+ ut->carg1 = (char *)lbp;
+ ut->iarg1 = bulk_position;
+ ut->iarg2 = 0;
+/*
+ * Now scan down until we reach the bottom of the bulk region. Keep track of
+ * how many characters this all represents, and clean up any associated format
+ * line structures.
+ */
+ while(1){
+ ut->iarg2 += lbp->byte_count;
+ local_count -= lbp->byte_count;
+ if(lbp->format_line){
+ screen_free_format_lines(lbp->format_line);
+ lbp->format_line = NULL;
+ }/* End IF */
+ if(lbp->next_line == NULL) break;
+ if(lbp->next_line->byte_count > local_count) break;
+ lbp = lbp->next_line;
+ }/* End While */
+/*
+ * Unchain the bulk area from the rest of the buffer. First, make the parent
+ * structure's forward pointer point to the next line buffer after the final
+ * one in the bulk deletion region.
+ */
+ if(top_lbp) top_lbp->next_line = lbp->next_line;
+ else hbp->first_line = lbp->next_line;
+ if(hbp->first_line == NULL) hbp->first_line = allocate_line_buffer(1);
+/*
+ * Now we have to fixup the backwards pointer of the trailing line buffer
+ * structure, if it exists.
+ */
+ if(lbp->next_line){
+ lbp->next_line->prev_line = top_lbp;
+ }/* End IF */
+/*
+ * Finally, clobber the 'next' pointer in lbp which is the end of the bulk
+ * area.
+ */
+ lbp->next_line = NULL;
+/*
+ * Update the remaining count of bytes to be deleted.
+ */
+ bytes_deleted_so_far += ut->iarg2;
+/*
+ * Depending on whether or not the bulk position precedes dot or not, we
+ * may have to decrement dot. In any case, we have to update zee.
+ */
+ if(bulk_position < hbp->dot){
+ if(hbp->dot > bulk_position + ut->iarg2) hbp->dot -= ut->iarg2;
+ else hbp->dot = bulk_position;
+ }/* End IF */
+ hbp->zee -= ut->iarg2;
+/*
+ * If this has modified the buffer for the first time, it needs to be displayed
+ * in the label line.
+ */
+ if(hbp->ismodified == NO){
+ hbp->ismodified = YES;
+ if(hbp == curbuf){
+ screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * If we are deleting from before the position cache, it will invalidate the
+ * information there, so we have to set it invalid so it does not get used.
+ */
+ if(hbp->pos_cache.base >= bulk_position){
+ hbp->pos_cache.lbp = NULL;
+ }/* End IF */
+
+ }/* End IF */
+
+/*
+ * Now, just brute force clobber the remaining few bytes
+ */
+ if(bytes_deleted_so_far < count){
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_MEMFREE;
+ ut->carg1 = cp = tec_alloc(TYPE_C_CBUFF,count-bytes_deleted_so_far);
+ if(cp != NULL){
+/*
+ * Set up the undo token to put the text back into the q-register, and copy
+ * it into our save buffer as we delete it from the q-register.
+ */
+ ut = allocate_undo_token(ct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_INSERT;
+ ut->iarg1 = position;
+ ut->iarg2 = 0;
+ ut->carg1 = cp;
+ while(count > bytes_deleted_so_far){
+ *cp++ = buff_contents(hbp,position);
+ if(buff_delete_char(hbp,position) == FAIL) return(FAIL);
+ bytes_deleted_so_far += 1;
+ ut->iarg2 += 1;
+ }/* End While */
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * Insure that the undo commands are working on the buffer the user specified
+ */
+ if(hbp != curbuf){
+ ut = allocate_undo_token(ct);
+ if(ut == FAIL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = hbp->buffer_number;
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* BUFF_BULK_INSERT - Insert a list of line buffer structures
+ *
+ * Function:
+ *
+ * This routine is used to insert a list of line buffer structures at
+ * the specified position in the buffer.
+ */
+void
+buff_bulk_insert(hbp,position,count,lbp)
+struct buff_header *hbp;
+int position;
+int count;
+register struct buff_line *lbp;
+{
+register struct buff_line *olbp;
+int offset;
+
+ PREAMBLE();
+/*
+ * If this will modify the buffer for the first time, it needs to be displayed
+ * in the label line.
+ */
+ if(hbp->ismodified == NO){
+ hbp->ismodified = YES;
+ if(hbp == curbuf){
+ screen_label_line(hbp,"(modified)",LABEL_C_MODIFIED);
+ }/* End IF */
+ }/* End IF */
+
+ checkpoint_modified = YES;
+
+/*
+ * We special case position zero because this is an easy case, and is common
+ * when HK is being used a lot
+ */
+ if(position == 0){
+ if((olbp = hbp->first_line) != NULL){
+ if(olbp->byte_count == 0){
+ olbp = olbp->next_line;
+ buff_free_line_buffer(hbp->first_line);
+ }/* End IF */
+ }/* End IF */
+ hbp->first_line = lbp;
+ while(lbp->next_line) lbp = lbp->next_line;
+ lbp->next_line = olbp;
+ if(olbp) olbp->prev_line = lbp;
+ hbp->zee += count;
+ hbp->dot += count;
+ hbp->pos_cache.lbp = NULL;
+ return;
+ }/* End IF */
+
+/*
+ * Also special check for position zee since this is also an easy case
+ */
+ if(position == hbp->zee){
+ olbp = hbp->first_line;
+ if(hbp->pos_cache.lbp) olbp = hbp->pos_cache.lbp;
+ while(olbp->next_line) olbp = olbp->next_line;
+ if(olbp->byte_count == 0){
+ olbp = olbp->prev_line;
+ buff_free_line_buffer(olbp->next_line);
+ }/* End IF */
+ olbp->next_line = lbp;
+ lbp->prev_line = olbp;
+ if(hbp->dot == hbp->zee) hbp->dot += count;
+ hbp->zee += count;
+ hbp->pos_cache.lbp = NULL;
+ return;
+ }/* End IF */
+
+/*
+ * Ok, if the insertion is not at the begining or the end of the buffer, then
+ * we just have to do it the hard way...
+ */
+ olbp = buff_find_line(hbp,position);
+ offset = buff_find_offset(hbp,olbp,position);
+
+/*
+ * Bulk inserts should always be guarenteed aligned.
+ */
+ if(offset){
+ error_message("BULK_INSERT: internal error! non-aligned bulk insert");
+ return;
+ }/* End IF */
+
+ if(olbp->prev_line) olbp->prev_line->next_line = lbp;
+ lbp->prev_line = olbp->prev_line;
+
+ while(lbp->next_line) lbp = lbp->next_line;
+ lbp->next_line = olbp;
+ olbp->prev_line = lbp;
+
+ if(hbp->dot >= position) hbp->dot += count;
+ if(hbp->pos_cache.base >= position) hbp->pos_cache.base += count;
+ hbp->zee += count;
+
+ return;
+
+}/* End Routine */
+
+
+
+/* ALLOCATE_LINE_BUFFER - Routine to allocate a line buffer structure
+ *
+ * Function:
+ *
+ * This routine is used to allocate a line buffer structure with
+ * a data buffer large enough to accommodate the specified number
+ * of bytes.
+ */
+struct buff_line *
+allocate_line_buffer(size)
+register int size;
+{
+register struct buff_line *lbp;
+
+ PREAMBLE();
+/*
+ * If he is asking for a size which will result in the minimum allocation, try
+ * to satisfy the request off of the lookaside list.
+ */
+ if(size <= INITIAL_LINE_BUFFER_SIZE){
+ if((lbp = line_buffer_lookaside_list) != NULL){
+ line_buffer_lookaside_list = lbp->next_line;
+ lbp->next_line = NULL;
+ lbp->prev_line = NULL;
+ lbp->format_line = NULL;
+ lbp->byte_count = 0;
+ lbp->lin_magic = MAGIC_LINE;
+ return(lbp);
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * Lookaside list is empty, or he is asking for a non-standard size
+ */
+ lbp = (struct buff_line *)tec_alloc(TYPE_C_LINE,sizeof(struct buff_line));
+ if(lbp == NULL) return(NULL);
+
+ if(size <= INITIAL_LINE_BUFFER_SIZE){
+ lbp->buffer_size = INITIAL_LINE_BUFFER_SIZE;
+ }/* End IF */
+ else if(size < INITIAL_LINE_BUFFER_SIZE + INCREMENTAL_LINE_BUFFER_SIZE){
+ lbp->buffer_size = INITIAL_LINE_BUFFER_SIZE +
+ INCREMENTAL_LINE_BUFFER_SIZE;
+ }
+ else {
+ lbp->buffer_size = size + INCREMENTAL_LINE_BUFFER_SIZE - 1;
+ lbp->buffer_size -= lbp->buffer_size % INCREMENTAL_LINE_BUFFER_SIZE;
+ }/* End IF */
+
+ lbp->buffer = tec_alloc(TYPE_C_LINEBUF,lbp->buffer_size);
+ if(lbp->buffer == NULL){
+ tec_release(TYPE_C_LINE,(char *)lbp);
+ return(NULL);
+ }/* End IF */
+
+ lbp->byte_count = 0;
+ lbp->next_line = NULL;
+ lbp->prev_line = NULL;
+ lbp->format_line = NULL;
+ lbp->lin_magic = MAGIC_LINE;
+ return(lbp);
+
+}/* End Routine */
+
+
+/* BUFF_FREE_LINE_BUFFER - Free up the associated storage
+ *
+ * Function:
+ *
+ * This routine is called when a line buffer is deleted. It cleans up any
+ * associated storage as well as the buffer itself.
+ */
+void
+buff_free_line_buffer(lbp)
+register struct buff_line *lbp;
+{
+
+ PREAMBLE();
+
+ if(lbp->format_line){
+ screen_free_format_lines(lbp->format_line);
+ lbp->format_line = NULL;
+ }/* End IF */
+
+/*
+ * If this is a standard size line buffer, place it back on the lookaside list
+ * rather than giving it back to tec_alloc.
+ */
+ lbp->lin_magic = 0;
+
+ if(lbp->buffer_size == INITIAL_LINE_BUFFER_SIZE){
+ lbp->lin_magic = MAGIC_LINE_LOOKASIDE;
+ lbp->next_line = line_buffer_lookaside_list;
+ line_buffer_lookaside_list = lbp;
+ return;
+ }/* End IF */
+
+ tec_release(TYPE_C_LINEBUF,lbp->buffer);
+ tec_release(TYPE_C_LINE,(char *)lbp);
+
+}/* End Routine */
+
+/* BUFF_FREE_LINE_BUFFER_LIST - Release a whole list of line buffers
+ *
+ * Function:
+ *
+ * This routine will delete an entire list of line buffers
+ */
+void
+buff_free_line_buffer_list(lbp)
+register struct buff_line *lbp;
+{
+register struct buff_line *olbp;
+
+ PREAMBLE();
+
+ while((olbp = lbp)){
+ lbp = lbp->next_line;
+ buff_free_line_buffer(olbp);
+ }/* End While */
+
+}/* End Routine */
+
+/* BUFF_DEALLOCATE_LINE_BUFFER_LOOKASIDE_LIST - Free up lookaside list
+ *
+ * Function:
+ *
+ * This routine frees up any format_lines we've cached on our local
+ * lookaside list.
+ */
+void
+buff_deallocate_line_buffer_lookaside_list()
+{
+register struct buff_line *lbp;
+
+ PREAMBLE();
+
+ while((lbp = line_buffer_lookaside_list) != NULL){
+ line_buffer_lookaside_list = lbp->next_line;
+ lbp->lin_magic = 0;
+ tec_release(TYPE_C_LINEBUF,lbp->buffer);
+ tec_release(TYPE_C_LINE,(char *)lbp);
+ }/* End While */
+
+}/* End Routine */
+
+
+
+#ifdef DEBUG_DUMPING
+buff_dump_buffer(who_string,hbp,channel)
+char *who_string;
+register struct buff_header *hbp;
+FILE *channel;
+{
+register struct buff_line *lbp;
+register int i,j;
+
+ PREAMBLE();
+
+ fprintf(channel,"%s requested buffer dump:\n");
+
+ for(i = 0, lbp = hbp->first_line; lbp != NULL; i++,lbp = lbp->next_line){
+ fprintf(channel,"line %4d (count %4d)'",i,lbp->byte_count);
+ buff_dump_line(lbp,channel);
+ fprintf(channel,"'\n");
+ }/* End FOR */
+
+ fflush(debug_chan);
+
+}/* End Routine */
+
+buff_dump_line(lbp,channel)
+register struct buff_line *lbp;
+FILE *channel;
+{
+register struct buff_header *hbp = curbuf;
+register int i,j;
+
+ PREAMBLE();
+
+ for(j = 0; j < lbp->byte_count; j++){
+ if(lbp->buffer[j] == '\n'){
+ fprintf(channel,"<newline>");
+ }/* End IF */
+ else fprintf(channel,"%c",lbp->buffer[j]);
+ }/* End FOR */
+
+}/* End Routine */
+
+#endif
+
+unsigned int
+stringHash( char *str )
+{
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++) & 0xFF )
+ {
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+ }
+
+ return hash;
+}
diff --git a/teccmd.c b/teccmd.c
new file mode 100644
index 0000000..f55af95
--- /dev/null
+++ b/teccmd.c
@@ -0,0 +1,2668 @@
+char *teccmd_c_version = "teccmd.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/teccmd.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* teccmd.c
+ * Subroutines which implement (usually large) TECO commands
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+/*
+ * Define constants for search table code
+ */
+#define SEARCH_M_MATCHREPEAT 0x80
+#define SEARCH_M_NOT 0x40
+
+#define SEARCH_M_TYPE 0x0F
+#define SEARCH_C_BYMASK 1
+#define SEARCH_C_ATLEASTONE 2
+#define SEARCH_C_QREGNUM 3
+#define SEARCH_C_QREGCHARS 4
+
+/*
+ * Globals
+ */
+ int last_search_pos1;
+ int last_search_pos2;
+ int last_search_status;
+ struct tags *current_tags;
+
+#ifdef CHECKPOINT
+ static char *checkpoint_filename = NULL;
+#endif /* CHECKPOINT */
+
+/*
+ * External Routines, etc.
+ */
+ extern int forced_height,forced_width;
+ extern char eight_bit_output_flag,hide_cr_flag;
+
+#ifdef CHECKPOINT
+ void cmd_checkpoint(void);
+#endif /* CHECKPOINT */
+
+ struct tags *tag_load_file();
+
+ void srch_setbits(int *bit_table,char *string);
+ void srch_setbit(int *bit_table,unsigned char value);
+ void srch_setallbits(int *bit_table);
+ void srch_invert_sense(int *bit_table);
+ int cmd_writebak(int,char *,char *,char *,int);
+
+ extern struct buff_header *curbuf;
+ extern struct buff_header *buffer_headers;
+ extern char waiting_for_input_flag;
+ extern char alternate_escape_character;
+ extern char alternate_delete_character;
+ extern char intr_flag;
+ extern int term_lines;
+ extern char checkpoint_flag;
+ extern char checkpoint_enabled;
+ extern int checkpoint_interval;
+ extern char checkpoint_modified;
+ extern char resize_flag;
+ extern unsigned int IntBits[BITS_PER_INT];
+ extern struct search_buff search_string;
+ extern char suspend_is_okay_flag;
+
+/* CMD_SEARCH - Execute a search command
+ *
+ * Function:
+ *
+ * This is the runtime execution of the search command. ARG1 and ARG2 may
+ * specify a buffer range a,bS<mumble> in which case the search is
+ * constrained within the range. In this case, the direction of the search
+ * is determined by whether ARG1 is greater than or less than ARG2.
+ * If ARG2 == -1, then it is an ignored argument and ARG1 specifies the
+ * direction of search and the number of times to search.
+ */
+int
+cmd_search(arg1,arg2,search_tbl)
+int arg1;
+int arg2;
+struct search_buff *search_tbl;
+{
+int count;
+char forwards;
+int status;
+int pos1;
+int pos2;
+int original_dot;
+
+ PREAMBLE();
+
+/*
+ * Compile the search Q-register into a search table
+ */
+ if(compile_search_string(search_tbl) == FAIL) return(FAIL);
+
+/*
+ * Before looking at the arguments, set up the defaults
+ */
+ original_dot = curbuf->dot;
+ count = 1;
+ forwards = 1;
+ pos1 = 0;
+ pos2 = curbuf->zee;
+
+/*
+ * If ARG2 is -1, then he did not specify a range, but rather a count
+ */
+ if(arg2 == -1){
+ if(arg1 < 0){
+ forwards = 0;
+ count = -arg1;
+ }/* End IF */
+
+ else count = arg1;
+ }/* End IF */
+/*
+ * Else, if ARG2 != -1, then he specified a range for the search
+ */
+ else {
+ if(arg1 < arg2){
+ curbuf->dot = pos1 = arg1;
+ pos2 = arg2;
+ }/* End IF */
+
+ else {
+ forwards = 0;
+ pos1 = arg2;
+ curbuf->dot = pos2 = arg1;
+ }/* End Else */
+ }/* End Else */
+
+/*
+ * Now implement the search
+ */
+ while(count--){
+ if(forwards){
+ status = cmd_forward_search(pos1,pos2,search_tbl);
+ }/* End IF */
+
+ else {
+ status = cmd_reverse_search(pos1,pos2,search_tbl);
+ }/* End IF */
+
+ if(status == FAIL){
+ curbuf->dot = original_dot;
+ last_search_pos1 = last_search_pos2 = -1;
+ last_search_status = 0;
+ return(FAIL);
+ }/* End IF */
+ }/* End While */
+
+ last_search_status = -1;
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* CMD_FORWARD_SEARCH - Search in a forward direction
+ *
+ * Function:
+ *
+ * This routine is used when the search progresses forward
+ */
+int
+cmd_forward_search(pos1,pos2,search_tbl)
+int pos1;
+int pos2;
+struct search_buff *search_tbl;
+{
+register struct search_element *ep;
+register unsigned char buffer_char;
+register int table_position;
+int dotpos;
+char token_match;
+char tmp_message[LINE_BUFFER_SIZE];
+struct position_cache base_position;
+struct position_cache running_position;
+
+ PREAMBLE();
+
+ dotpos = curbuf->dot;
+/*
+ * Insure the search string is non-null
+ */
+ if(search_tbl->length <= 0){
+ error_message("?Null Search String");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End IF */
+
+ if(dotpos < pos1) return(FAIL);
+
+ table_position = 0;
+ ep = &search_tbl->data[table_position];
+ buffer_char = buff_cached_contents(curbuf,dotpos,&base_position);
+ running_position = base_position;
+
+ while(dotpos < pos2){
+
+ if(table_position == 0){
+ last_search_pos1 = dotpos;
+ base_position = running_position;
+ }/* End IF */
+
+ buffer_char = BUFF_CACHE_CONTENTS(&running_position);
+ BUFF_INCREMENT_CACHE_POSITION(&running_position);
+ dotpos++;
+ token_match = 1;
+
+ switch(ep->type & SEARCH_M_TYPE){
+ case SEARCH_C_BYMASK:
+ if((ep->bitmask.intarray[buffer_char / BITS_PER_INT] &
+ IntBits[buffer_char % BITS_PER_INT]) == 0){
+ token_match = 0;
+ break;
+ }/* End IF */
+ if(ep->type & SEARCH_M_MATCHREPEAT){
+ while(dotpos < pos2){
+ buffer_char = BUFF_CACHE_CONTENTS(&running_position);
+ if((ep->bitmask.intarray[buffer_char / BITS_PER_INT] &
+ IntBits[buffer_char % BITS_PER_INT]) == 0) break;
+ BUFF_INCREMENT_CACHE_POSITION(&running_position);
+ dotpos++;
+ }/* End While */
+ }/* End IF */
+ break;
+ case SEARCH_C_QREGCHARS:
+ { struct buff_header *qbp;
+ register int i;
+ qbp = buff_qfind(ep->value,0);
+ token_match = 0;
+ if(qbp == NULL) break;
+ for(i = 0; i < qbp->zee; i++){
+ if(buffer_char == buff_contents(qbp,i)) token_match = 1;
+ }/* End FOR */
+ if(ep->type & SEARCH_M_NOT){
+ token_match = token_match ? 0 : 1;
+ }
+ }/* End Block */
+ break;
+ case SEARCH_C_ATLEASTONE:
+ error_message("?^EM not implemented...");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ case SEARCH_C_QREGNUM:
+ { struct buff_header *qbp;
+ qbp = buff_qfind(ep->value,0);
+ token_match = 0;
+ if(qbp == NULL) break;
+ if(buffer_char == qbp->ivalue) token_match = 1;
+ if(ep->type & SEARCH_M_NOT){
+ token_match = token_match ? 0 : 1;
+ }/* End IF */
+ }/* End Block */
+ break;
+ default:
+ sprintf(tmp_message,
+ "SEARCH: Illegal token type %d - internal error",
+ ep->type);
+ tec_panic(tmp_message);
+ return(FAIL);
+ }/* End Switch */
+
+ if(token_match){
+ table_position += 1;
+ ep++;
+ if(table_position == search_tbl->length){
+ curbuf->dot = last_search_pos2 = dotpos;
+ {/* Local Block */
+ register struct buff_header *hbp;
+ hbp = buff_qfind('_',1);
+ if(hbp == NULL) return(FAIL);
+ hbp->ivalue = last_search_pos1;
+ }/* End Block */
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+
+ else {
+ if(intr_flag){
+ error_message("?Search aborted");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End IF */
+ table_position = 0;
+ ep = &search_tbl->data[table_position];
+ dotpos = last_search_pos1 + 1;
+ running_position = base_position;
+ BUFF_INCREMENT_CACHE_POSITION(&running_position);
+ }/* End IF */
+
+ }/* End While */
+
+ return(FAIL);
+
+}/* End Routine */
+
+
+
+/* CMD_REVERSE_SEARCH - Search in a reverse direction
+ *
+ * Function:
+ *
+ * This routine is used when the search progresses backwards
+ */
+int
+cmd_reverse_search(pos1,pos2,search_tbl)
+int pos1;
+int pos2;
+struct search_buff *search_tbl;
+{
+register struct search_element *ep;
+register unsigned char buffer_char;
+register int table_position;
+int dotpos;
+char token_match;
+char tmp_message[LINE_BUFFER_SIZE];
+struct position_cache base_position;
+struct position_cache running_position;
+
+ PREAMBLE();
+
+ dotpos = curbuf->dot;
+/*
+ * Insure the search string is non-null
+ */
+ if(search_tbl->length <= 0){
+ error_message("?Null Search String");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End IF */
+
+ if(dotpos > pos2) return(FAIL);
+
+ table_position = search_tbl->length - 1;
+ ep = &search_tbl->data[table_position];
+ buffer_char = buff_cached_contents(curbuf,dotpos,&base_position);
+ running_position = base_position;
+
+ while(dotpos > pos1){
+
+ if(table_position == (search_tbl->length - 1)){
+ last_search_pos2 = dotpos;
+ base_position = running_position;
+ }/* End IF */
+
+ dotpos--;
+ BUFF_DECREMENT_CACHE_POSITION(&running_position);
+ buffer_char = BUFF_CACHE_CONTENTS(&running_position);
+ token_match = 1;
+
+ switch(ep->type & SEARCH_M_TYPE){
+ case SEARCH_C_BYMASK:
+ if((ep->bitmask.intarray[buffer_char / BITS_PER_INT] &
+ IntBits[buffer_char % BITS_PER_INT]) == 0){
+ token_match = 0;
+ break;
+ }/* End IF */
+ if(ep->type & SEARCH_M_MATCHREPEAT){
+ while(dotpos > pos1){
+ BUFF_DECREMENT_CACHE_POSITION(&running_position);
+ dotpos--;
+ buffer_char = BUFF_CACHE_CONTENTS(&running_position);
+ if(ep->bitmask.intarray[buffer_char / BITS_PER_INT] &
+ IntBits[buffer_char % BITS_PER_INT]) continue;
+ dotpos++;
+ BUFF_INCREMENT_CACHE_POSITION(&running_position);
+ break;
+ }/* End While */
+ }/* End IF */
+ break;
+ case SEARCH_C_QREGCHARS:
+ { struct buff_header *qbp;
+ register int i;
+ qbp = buff_qfind(ep->value,0);
+ token_match = 0;
+ if(qbp == NULL) break;
+ for(i = 0; i < qbp->zee; i++){
+ if(buffer_char == buff_contents(qbp,i)) token_match = 1;
+ }/* End FOR */
+ }/* End Block */
+ break;
+ case SEARCH_C_ATLEASTONE:
+ error_message("?^EM not implemented...");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ case SEARCH_C_QREGNUM:
+ { struct buff_header *qbp;
+ qbp = buff_qfind(ep->value,0);
+ token_match = 0;
+ if(qbp == NULL) break;
+ if(buffer_char == qbp->ivalue) token_match = 1;
+ }/* End Block */
+ break;
+ default:
+ sprintf(tmp_message,
+ "SEARCH: Illegal token type %d - internal error",
+ ep->type);
+ tec_panic(tmp_message);
+ return(FAIL);
+ }/* End Switch */
+
+ if(token_match){
+ table_position -= 1;
+ ep--;
+ if(table_position < 0){
+ curbuf->dot = last_search_pos1 = dotpos;
+ {/* Local Block */
+ register struct buff_header *hbp;
+ hbp = buff_qfind('_',1);
+ if(hbp == NULL) return(FAIL);
+ hbp->ivalue = last_search_pos2;
+ }/* End Block */
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+
+ else {
+ if(intr_flag){
+ error_message("?Search aborted\n");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End IF */
+ table_position = search_tbl->length - 1;
+ ep = &search_tbl->data[table_position];
+ dotpos = last_search_pos2 - 1;
+ running_position = base_position;
+ BUFF_DECREMENT_CACHE_POSITION(&running_position);
+ }/* End IF */
+
+ }/* End While */
+
+ return(FAIL);
+
+}/* End Routine */
+
+
+
+/* SET_SEARCH_STRING_WITH_UNDO - Sets Q-register '_' to the new search string
+ *
+ * Function:
+ *
+ * This routine is called from the exec functions when a search string
+ * is to be set. It takes care of loading Q-register '_' with the new
+ * search string, as well as setting up all the undo tokens to insure
+ * that this is all reversable.
+ */
+int
+set_search_string_with_undo(string,uct)
+char *string;
+struct cmd_token *uct;
+{
+register struct buff_header *qbp;
+register struct undo_token *ut;
+register int i,c;
+register char *cp;
+int new_length;
+
+ PREAMBLE();
+
+/*
+ * Get a pointer to the special Q-register which holds the search string. If
+ * it does not exist, have it be created automatically.
+ */
+ qbp = buff_qfind('_',1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+
+/*
+ * This code allows us to load the numeric half of the search string
+ * Q-register with the length of the string we just found. In order
+ * to be able to undo it, we have to protect the previous value, and
+ * it just happens that by doing it here we probably catch all the
+ * places where the search could have taken place. Then again, maybe
+ * not (search really should not be calling this routine repeatedly
+ * in an iteration or macro, for instance). If this gets cleaned up,
+ * then this code might not be appropriate... XXX
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_UQREGISTER;
+ ut->iarg1 = '_';
+ ut->iarg2 = qbp->ivalue;
+
+ if(string == NULL) return(SUCCESS);
+ if((new_length = strlen(string)) == 0) return(SUCCESS);
+
+/*
+ * Set up undo tokens for the search_state and search_position information
+ * which typically gets used to pass search information between halves of
+ * a search / replace type command.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_SET_SEARCH_GLOBALS;
+ ut->iarg1 = last_search_pos1;
+ ut->iarg2 = last_search_pos2;
+ ut->carg1 = (char *)last_search_status;
+
+/*
+ * If the new length is the same as the old length, there is a chance we are
+ * just setting the same search string over again. To find out, we actually
+ * have to loop through comparing bytes in the Q-register to bytes in the
+ * supplied string. If none of them are different, we can just return without
+ * changing the search string. This happens often in iterations...
+ */
+ if(new_length == qbp->zee){
+ cp = string;
+ for(i = 0; i < new_length; i++){
+ c = buff_contents(qbp,i);
+ if(*cp++ != c) break;
+ }/* End FOR */
+
+ if(i == new_length) return(SUCCESS);
+
+ }/* End IF */
+/*
+ * At this point we set an undo token so that if the following stuff gets
+ * undone, we will re-compile the search string.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_SET_SEARCH_STRING;
+/*
+ * Delete the current contents of the search string Q-register to make room
+ * for the new contents.
+ */
+ if(qbp->zee > 0) buff_delete_with_undo(uct,qbp,0,qbp->zee);
+/*
+ * Set up the undo token to delete the text we are going to insert into the
+ * search string Q-register.
+ */
+ buff_insert_with_undo(uct,qbp,0,string,new_length);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* COMPILE_SEARCH_STRING - Creates the search table for a given input string
+ *
+ * Function:
+ *
+ * This routine parses the input search string and creates the search
+ * table which will describe to the search routines how to match the
+ * buffer characters. Note that the search table must be constructed
+ * in such a way that it works equally well backward or forward.
+ */
+int
+compile_search_string(search_tbl)
+struct search_buff *search_tbl;
+{
+register int position;
+register char c;
+register char *cp;
+register struct buff_header *qbp;
+register struct search_element *ep;
+int radix;
+int bracket_bits[256/BITS_PER_INT];
+char tmp_message[LINE_BUFFER_SIZE];
+char either_case_flag = YES;
+char bracket_wildcard = NO;
+char bracket_not_flag = NO;
+char not_flag = NO;
+char matchrepeat_flag = NO;
+
+ PREAMBLE();
+
+#define QBUF_CHAR() \
+ (position < qbp->zee ? *cp++ = buff_contents(qbp,position++) : 0)
+
+ search_tbl->error_message_given = NO;
+
+/*
+ * Get a pointer to the special Q-register which holds the search string. If
+ * it does not exist, have it be created automatically.
+ */
+ qbp = buff_qfind('_',1);
+ if(qbp == NULL){
+ error_message("Internal error - qfind returned null");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End IF */
+
+ if(qbp->zee >= SEARCH_STRING_MAX){
+ error_message("Search Q-register too long");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End IF */
+
+ search_string.length = 0;
+ position = 0;
+ cp = search_string.input;
+ ep = search_string.data;
+ bzero(&ep->bitmask,sizeof(ep->bitmask));
+
+ while((c = QBUF_CHAR())){
+/*
+ * Check for CNTRL_E which is the standard lead-in for wildcard search
+ * strings.
+ */
+ if(c == CNTRL_E){
+ c = QBUF_CHAR();
+ switch(c){
+/*
+ * [ specifies a list of possible matches, ANY of which satisfy the
+ * position.
+ */
+ case '[':
+ bzero(bracket_bits,sizeof(bracket_bits));
+ bracket_wildcard = YES;
+ if(not_flag){
+ not_flag = NO;
+ bracket_not_flag = YES;
+ }/* End IF */
+ continue;
+/*
+ * A specifies any ALPHABETIC character as a match. Thus any upper or lower
+ * case character in the range a-z will match.
+ */
+ case 'A': case 'a':
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbits((int *)&ep->bitmask,
+ "abcdefghijklmnopqrstuvwxyz");
+ srch_setbits((int *)&ep->bitmask,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ break;
+/*
+ * B specifies any separator character, when separator is defined as any
+ * non-alphanumeric character.
+ */
+ case 'B': case 'b':
+ {
+ register int i;
+ ep->type = SEARCH_C_BYMASK;
+ for(i = 0; i < 256; i++){
+ if(!isalnum(i)) srch_setbit((int *)&ep->bitmask,i);
+ }/* End FOR */
+ break;
+ }
+/*
+ * C specifies any symbol constituent. This is any alpha-numeric character,
+ * or dot (.), dollar sign ($), or underscore (_).
+ */
+ case 'C': case 'c':
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbits((int *)&ep->bitmask,
+ "abcdefghijklmnopqrstuvwxyz");
+ srch_setbits((int *)&ep->bitmask,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ srch_setbits((int *)&ep->bitmask,"0123456789.$_");
+ break;
+/*
+ * D specifies that any digit (0 to 9) is acceptable.
+ */
+ case 'D': case 'd':
+ ep->type = SEARCH_C_BYMASK | SEARCH_M_MATCHREPEAT;
+ srch_setbits((int *)&ep->bitmask,"0123456789");
+ break;
+/*
+ * E or ^E is a Video TECO only definition which means that whether searches
+ * are case sensitive or not should be toggled.
+ */
+ case 'E': case 'e': case CNTRL_E:
+ either_case_flag ^= 1;
+ continue;
+/*
+ * G followed by a Q register name matches any character which is in the
+ * text portion of the Q register.
+ */
+ case 'G': case 'g':
+ if(bracket_wildcard){
+ error_message("^EG inside ^E[,,,] illegal");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End IF */
+ ep->type = SEARCH_C_QREGCHARS;
+ ep->value = QBUF_CHAR();
+ break;
+/*
+ * L matches any line terminator character.
+ */
+ case 'L': case 'l':
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbits((int *)&ep->bitmask,"\n\r\f");
+ break;
+/*
+ * M is a flag that says any number of the following character (but at least
+ * one) are acceptable. Thus ^EMA would match A or AA or AAA, etc.
+ */
+ case 'M': case 'm':
+ matchrepeat_flag = YES;
+ continue;
+/*
+ * R matches any alphanumeric
+ */
+ case 'R': case 'r':
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbits((int *)&ep->bitmask,
+ "abcdefghijklmnopqrstuvwxyz");
+ srch_setbits((int *)&ep->bitmask,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ srch_setbits((int *)&ep->bitmask,"0123456789");
+ break;
+/*
+ * S matches any number of tabs and spaces, in any mixture (but there must
+ * be at least one).
+ */
+ case 'S': case 's':
+ ep->type = SEARCH_C_BYMASK | SEARCH_M_MATCHREPEAT;
+ srch_setbits((int *)&ep->bitmask," \t");
+ break;
+/*
+ * U matches against the ASCII code which is contained in the Q register's
+ * numeric storage.
+ */
+ case 'U': case 'u':
+ if(bracket_wildcard){
+ error_message("^EU inside ^E[,,,] illegal");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End IF */
+ ep->type = SEARCH_C_QREGNUM;
+ ep->value = QBUF_CHAR();
+ break;
+/*
+ * V matches any lowercase letter.
+ */
+ case 'V': case 'v':
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbits((int *)&ep->bitmask,
+ "abcdefghijklmnopqrstuvwxyz");
+ break;
+/*
+ * W matches any uppercase letter
+ */
+ case 'W': case 'w':
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbits((int *)&ep->bitmask,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ break;
+/*
+ * X is similar to ^X in that it matches any character
+ */
+ case 'X': case 'x':
+ ep->type = SEARCH_C_BYMASK;
+ srch_setallbits((int *)&ep->bitmask);
+ break;
+/*
+ * The < character opens the sequence for ^E<nnn> where nnn is a numeric
+ * (ASCII) code to search for. Classic TECO says that this code is octal,
+ * but Video TECO defaults to decimal, octal if the first digit is zero,
+ * or hex if the first two digits are 0x.
+ */
+ case '<':
+ ep->value = 0;
+ radix = 10;
+ c = QBUF_CHAR();
+ if(c == '0'){
+ radix = 8;
+ c = QBUF_CHAR();
+ if(c == 'x' || c == 'X'){
+ radix = 16;
+ c = QBUF_CHAR();
+ }/* End IF */
+ }/* End IF */
+ while(c && c != '>'){
+ if(c >= '0' && c <= '7'){
+ ep->value *= radix;
+ ep->value += c - '0';
+ c = QBUF_CHAR();
+ continue;
+ }/* End IF */
+ if(c >= '8' && c <= '9' && radix > 8){
+ ep->value *= radix;
+ ep->value += c - '0';
+ c = QBUF_CHAR();
+ continue;
+ }/* End IF */
+ if(c >= 'a' && c <= 'f' && radix == 16){
+ c = TOUPPER(c);
+ }/* End IF */
+ if(c >= 'A' && c <= 'F' && radix == 16){
+ ep->value *= radix;
+ ep->value += c - 'A' + 10;
+ c = QBUF_CHAR();
+ continue;
+ }/* End IF */
+ sprintf(tmp_message,
+ "?Illegal ^E code '%c' in search string",c);
+ search_tbl->error_message_given = YES;
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End While */
+ if(c == '>') c = QBUF_CHAR();
+ else {
+ error_message("?Missing '>' in ^E<nnn> search string");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End Else */
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbit((int *)&ep->bitmask,ep->value);
+ break;
+/*
+ * This case is really just to catch the user typing ^E followed by the
+ * end of the search string. We just make it search for ^E in this case.
+ */
+ case '\0':
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbit((int *)&ep->bitmask,CNTRL_E);
+ break;
+/*
+ * Any unrecognized ^E code just searches for the exact character which
+ * follows the ^E. This is probably not very good, we should issue an
+ * error message instead.
+ */
+ default:
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbit((int *)&ep->bitmask,c);
+ break;
+ }/* End Switch */
+ }/* End IF */
+/*
+ * The ^X code embedded in a search string means match any character at
+ * this position.
+ */
+ else if(c == CNTRL_X){
+ ep->type = SEARCH_C_BYMASK;
+ srch_setallbits((int *)&ep->bitmask);
+ }/* End Else */
+/*
+ * The ^N code embedded in a search string means match any character EXCEPT
+ * the one that follows.
+ */
+ else if(c == CNTRL_N){
+ not_flag = YES;
+ continue;
+ }/* End Else */
+/*
+ * Else, here if it is just a normal character which should be searched for
+ */
+ else {
+ ep->type = SEARCH_C_BYMASK;
+ srch_setbit((int *)&ep->bitmask,c);
+ if(either_case_flag){
+ if(islower((int)c)) srch_setbit((int *)&ep->bitmask,TOUPPER((int)c));
+ if(isupper((int)c)) srch_setbit((int *)&ep->bitmask,TOLOWER((int)c));
+ }/* End IF */
+ }/* End Else */
+
+/*
+ * If sense has been inverted with ^N, invert the bits
+ */
+ if(not_flag){
+ if(ep->type & SEARCH_C_BYMASK){
+ srch_invert_sense((int *)&ep->bitmask);
+ }/* End IF */
+ ep->type &= ~SEARCH_M_MATCHREPEAT;
+ ep->type |= SEARCH_M_NOT;
+ not_flag = NO;
+ }/* End IF */
+/*
+ * If already inside a ^E[a,b,c] type wildcard, check for comma or close
+ * bracket.
+ */
+ if(bracket_wildcard){
+ c = QBUF_CHAR();
+ switch(c){
+ case ',':
+ {
+ register int i;
+ for(i = 0; i < 256 / BITS_PER_INT; i++){
+ bracket_bits[i] |= ep->bitmask.intarray[i];
+ }/* End FOR */
+ bzero(&ep->bitmask,sizeof(ep->bitmask));
+ continue;
+ }
+ case ']':
+ {
+ register int i;
+ for(i = 0; i < 256 / BITS_PER_INT; i++){
+ ep->bitmask.intarray[i] |= bracket_bits[i];
+ }/* End FOR */
+ if(bracket_not_flag){
+ srch_invert_sense((int *)&ep->bitmask);
+ ep->type &= ~SEARCH_M_MATCHREPEAT;
+ bracket_not_flag = NO;
+ }/* End IF */
+ bracket_wildcard = NO;
+ break;
+ }
+ default:
+ error_message("Illegal syntax in [,,,] construct");
+ search_tbl->error_message_given = YES;
+ return(FAIL);
+ }/* End Switch */
+ }/* End IF */
+/*
+ * Test for match repeating characters flag
+ */
+ if(matchrepeat_flag){
+ ep->type |= SEARCH_M_MATCHREPEAT;
+ matchrepeat_flag = NO;
+ }/* End IF */
+/*
+ * Point to the next entry in the search string table
+ */
+ ep++;
+ bzero(&ep->bitmask,sizeof(ep->bitmask));
+ search_string.length += 1;
+
+ }/* End While */
+
+ *cp = '\0';
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SRCH_SETALLBITS - Set ALL the bits on for a search table entry
+ *
+ * Function:
+ *
+ * This routine makes ANY character match the search string table
+ * entry.
+ */
+void
+srch_setallbits(bit_table)
+register int *bit_table;
+{
+register int i;
+
+ PREAMBLE();
+
+ for(i = 0; i < 256 / BITS_PER_INT; i++){
+ bit_table[i] = ~0;
+ }/* End FOR */
+
+}/* End Routine */
+
+/* SRCH_INVERT_SENSE - Invert the sense of a search table entry
+ *
+ * Function:
+ *
+ * This routine is called when a 'NOT' modifier has been used,
+ * meaning that just the opposite matching should occur. We
+ * simply invert the setting of the bits in the search table entry.
+ */
+void
+srch_invert_sense(bit_table)
+register int *bit_table;
+{
+register int i;
+
+ PREAMBLE();
+
+ for(i = 0; i < 256 / BITS_PER_INT; i++){
+ bit_table[i] ^= ~0;
+ }/* End FOR */
+
+}/* End Routine */
+
+/* SRCH_SETBITS - Set the corresponding search bits to 'on'
+ *
+ * Function:
+ *
+ * This routine is called with a zero terminated string of characters.
+ * It sets each corresponding bit in the search table to a 1.
+ */
+void
+srch_setbits(bit_table,string)
+register int *bit_table;
+register char *string;
+{
+register int c;
+
+ PREAMBLE();
+
+ while((c = *string++)){
+ bit_table[c/BITS_PER_INT] |= IntBits[c%BITS_PER_INT];
+ }/* End While */
+
+}/* End Routine */
+
+/* SRCH_SETBIT - Set the corresponding search bit to 'on'
+ *
+ * Function:
+ *
+ * This routine is called with a char whose corresponding bit should
+ * be set in the search table.
+ */
+void
+srch_setbit(bit_table,value)
+register int *bit_table;
+register unsigned char value;
+{
+
+ PREAMBLE();
+
+ bit_table[value/BITS_PER_INT] |= IntBits[value%BITS_PER_INT];
+
+}/* End Routine */
+
+
+
+/* CMD_WRITE - Implements the EW command by writing out a buffer
+ *
+ * Function:
+ *
+ * This routine will write out the contents of the buffer after creating
+ * the appropriate backup files. If there is not already a file with the
+ * a .OLD extension, this is created. Otherwise, a .BAK file is created.
+ * If the name is so long that we can't append a of suffix, we have to
+ * hack it up a bit. This is not guaranteed to work, but...
+ */
+int
+cmd_write(hbp,filename)
+struct buff_header *hbp;
+char *filename;
+{
+char base_filename[TECO_FILENAME_COMPONENT_LENGTH + 1];
+char path_name[TECO_FILENAME_TOTAL_LENGTH + 1];
+char tmp_filename[TECO_FILENAME_TOTAL_LENGTH + 1];
+char tmp_message[LINE_BUFFER_SIZE];
+register char *cp1,*cp2;
+register int i;
+int path_len;
+int file_len;
+int combined_len;
+int fi;
+int status;
+
+ PREAMBLE();
+
+#ifdef UNIX
+/*
+ * First step is to separate the path elements from the filename itself.
+ * To do this, we search for the directory path separator.
+ */
+ cp1 = cp2 = filename;
+ while(*cp1){
+ if(*cp1++ == '/') cp2 = cp1;
+ }/* End While */
+
+ combined_len = strlen(filename);
+ file_len = strlen(cp2);
+ path_len = combined_len - file_len;
+
+/*
+ * The filename really should not be longer than this.
+ */
+ if(file_len > TECO_FILENAME_COMPONENT_LENGTH){
+ sprintf(tmp_message,"?Filename is too long <%s>",cp2);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+/*
+ * Make a copy of the filename portion for safe keeping.
+ */
+ (void) strcpy(base_filename,cp2);
+/*
+ * And do the same for the path portion.
+ */
+ cp1 = path_name;
+ cp2 = filename;
+ for(i = 0; i < path_len; i++){
+ *cp1++ = *cp2++;
+ }/* End FOR */
+ *cp1 = '\0';
+
+/*
+ * Now the first thing to do is see if the file exists, because this
+ * will impact us on whether we want to attempt .BAK files
+ */
+ if(hbp->isbackedup == NO){
+
+ fi = open(filename,O_RDONLY);
+ if(fi >= 0){
+
+#ifdef CREATE_OLD_FILES
+#ifdef HAVE_LONG_FILE_NAMES
+ (void) strcpy(tmp_filename,base_filename);
+ (void) strcat(tmp_filename,".OLD");
+ status = cmd_writebak(fi,filename,path_name,tmp_filename,O_EXCL);
+#else
+ (void) strcpy(tmp_filename,path_name);
+ (void) strcat(tmp_filename,".TECOLD");
+ status = cmd_writebak(fi,filename,tmp_filename,base_filename,O_EXCL);
+#endif
+ if(status == SUCCESS) hbp->isbackedup = YES;
+
+ if(status == FAIL && errno != EEXIST){
+ sprintf(tmp_message,"?Error opening <%s>: %s",
+ filename,error_text(errno));
+ error_message(tmp_message);
+ close(fi);
+ return(FAIL);
+ }/* End IF */
+#endif
+
+ if(hbp->isbackedup == NO){
+#ifdef HAVE_LONG_FILE_NAMES
+ (void) strcpy(tmp_filename,base_filename);
+ (void) strcat(tmp_filename,".BAK");
+ status = cmd_writebak(fi,filename,path_name,tmp_filename,0);
+#else
+ (void) strcpy(tmp_filename,path_name);
+ (void) strcat(tmp_filename,".TECBAK");
+ status = cmd_writebak(fi,filename,tmp_filename,base_filename,0);
+#endif
+ if(status == SUCCESS) hbp->isbackedup = YES;
+ if(status == FAIL){
+ sprintf(tmp_message,"?Error opening <%s>: %s",
+ filename,error_text(errno));
+ error_message(tmp_message);
+ close(fi);
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+ }/* End IF */
+
+ close(fi);
+
+ }/* End IF */
+
+/* END OF UNIX CONDITIONAL CODE */
+
+#endif
+
+#ifdef VMS
+ fi = creat(filename,0,"mrs = 0");
+#else
+ fi = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
+#endif
+
+ if(fi < 0){
+ sprintf(tmp_message,"?Error opening <%s>: %s",
+ filename,error_text(errno));
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ status = buff_write(hbp,fi,0,hbp->zee);
+
+ close(fi);
+
+ if(status == FAIL){
+ sprintf(tmp_message,"?Error writing <%s>: %s",
+ filename,error_text(errno));
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ hbp->ismodified = NO;
+ if(hbp == curbuf){
+ screen_label_line(hbp,"",LABEL_C_MODIFIED);
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* CMD_WRITEBAK - Routine to open the BAK or OLD file
+ *
+ * Function:
+ *
+ * This routine opens a channel to the backup file and creates any
+ * subdirectories that may be necessary.
+ */
+int
+cmd_writebak(fi,input_filename,pathname,output_filename,open_flags)
+int fi;
+char *pathname;
+char *input_filename;
+char *output_filename;
+int open_flags;
+{
+char tmp_filename[TECO_FILENAME_TOTAL_LENGTH + 1];
+char tmp_message[LINE_BUFFER_SIZE];
+char iobuf[IO_BUFFER_SIZE];
+int fo;
+register int i;
+register int status;
+
+ PREAMBLE();
+
+#ifndef HAVE_LONG_FILE_NAMES
+ if(mkdir(pathname,0777)){
+ if(errno != EEXIST){
+ sprintf(tmp_message,"?Error creating subdirectory <%s>: %s",
+ pathname,error_text(errno));
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+#endif
+
+ (void) strcpy(tmp_filename,pathname);
+ if(strlen(tmp_filename)){
+ (void) strcat(tmp_filename,"/");
+ }/* End IF */
+
+ (void) strcat(tmp_filename,output_filename);
+ chmod(tmp_filename,0666);
+ fo = open(tmp_filename,O_WRONLY|O_CREAT|O_TRUNC|open_flags,0666);
+ if(fo < 0){
+ if(errno == EEXIST){
+ chmod(tmp_filename,0444);
+ return(FAIL);
+ }/* End IF */
+ sprintf(tmp_message,"?Error opening <%s>: %s",
+ tmp_filename,error_text(errno));
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ while(1){
+ i = read(fi,iobuf,IO_BUFFER_SIZE);
+ if(i < 0){
+ sprintf(tmp_message,"?Error reading <%s>: %s",
+ input_filename,error_text(errno));
+ error_message(tmp_message);
+ close(fo);
+ return(FAIL);
+ }/* End IF */
+
+ status = write(fo,iobuf,(unsigned)i);
+ if(status < 0){
+ sprintf(tmp_message,"?Error writing <%s>: %s",
+ tmp_filename,error_text(errno));
+ error_message(tmp_message);
+ close(fo);
+ return(FAIL);
+ }/* End IF */
+
+ if(i < IO_BUFFER_SIZE) break;
+
+ }/* End While */
+
+ close(fo);
+ chmod(tmp_filename,0444);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* CMD_WORDMOVE - Here to move within the edit buffer by words
+ *
+ * Function:
+ *
+ * Here in response to one of the 'word' commands. We move the
+ * current edit position forward or backward by the specified
+ * number of words.
+ */
+int
+cmd_wordmove(count)
+register int count;
+{
+ register int c;
+
+ PREAMBLE();
+
+ while(count > 0){
+ while(1){
+ if(curbuf->dot == curbuf->zee) break;
+ c = buff_contents(curbuf,curbuf->dot);
+ if(isspace(c)) break;
+ curbuf->dot++;
+ }/* End While */
+
+ while(1){
+ if(curbuf->dot == curbuf->zee) break;
+ c = buff_contents(curbuf,curbuf->dot);
+ if(!isspace(c)) break;
+ curbuf->dot++;
+ }/* End While */
+
+ count -= 1;
+
+ }/* End While */
+
+ while(count < 0){
+ while(1){
+ if(curbuf->dot == 0) break;
+ c = buff_contents(curbuf,curbuf->dot-1);
+ if(isspace(c)) break;
+ curbuf->dot--;
+ }/* End While */
+
+ while(1){
+ if(curbuf->dot == 0) break;
+ c = buff_contents(curbuf,curbuf->dot-1);
+ if(!isspace(c)) break;
+ curbuf->dot--;
+ }/* End While */
+
+ count += 1;
+
+ }/* End While */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* CMD_SUSPEND - We get here on a suspend signal from the tty
+ *
+ * Function:
+ *
+ * Here when the user has typed ^Z. We want to suspend back to the
+ * original shell.
+ */
+void
+cmd_suspend()
+{
+#if defined(UNIX) || defined(VMS)
+extern char susp_flag;
+#endif
+
+ PREAMBLE();
+
+#ifdef UNIX
+ if(waiting_for_input_flag == YES){
+ pause_while_in_input_wait();
+ return;
+ }/* End IF */
+
+#ifdef JOB_CONTROL
+ if(susp_flag++ >= 4){
+ signal(SIGTSTP,(void (*)())SIG_DFL);
+ }/* End IF */
+#endif
+
+#endif
+
+#ifdef VMS
+ if(suspend_is_okay_flag == YES){
+ susp_flag++;
+ }/* End IF */
+#endif
+
+}/* End Routine */
+
+/* CMD_PAUSE - Here when we are ready to suspend back to the shell
+ *
+ * Function:
+ *
+ * Here when the parser has noticed that the suspend flag is set, and
+ * is ready for us to suspend the process.
+ */
+void
+cmd_pause()
+{
+extern char susp_flag;
+
+#ifdef VMS
+long owner_pid;
+long status;
+
+struct itmlst {
+ short buffer_length;
+ short item_code;
+ char *buffer_address;
+ long *length_address;
+ long terminating_zero;
+}list;
+#endif
+
+ PREAMBLE();
+
+ restore_tty();
+
+#if defined(JOB_CONTROL) || defined(VMS)
+
+#ifdef CHECKPOINT
+ alarm(0);
+#endif /* CHECKPOINT */
+
+#ifdef UNIX
+ if(suspend_is_okay_flag == YES){
+ kill(getpid(),SIGSTOP);
+ signal(SIGTSTP,(void (*)())cmd_suspend);
+ }
+#endif /* UNIX */
+
+#ifdef VMS
+ owner_pid = 0;
+ list.buffer_length = sizeof(owner_pid);
+ list.item_code = JPI$_OWNER;
+ list.buffer_address = (char *)&owner_pid;
+ list.length_address = 0;
+ list.terminating_zero = 0;
+ sys$getjpiw(0,0,0,&list,0,0,0);
+
+ if(owner_pid){
+ status = lib$attach(&owner_pid);
+ if(status != SS$_NORMAL){
+ error_message("?LIB$ATTACH failed");
+ susp_flag = 0;
+ return;
+ }/* End IF */
+ }/* End IF */
+
+ else {
+ error_message("?Cannot suspend. No parent process for LIB$ATTACH");
+ susp_flag = 0;
+ return;
+ }/* End IF */
+#endif
+
+#ifdef CHECKPOINT
+ alarm(checkpoint_interval);
+#endif /* CHECKPOINT */
+
+#endif /* JOB_CONTROL */
+
+ initialize_tty();
+ screen_redraw();
+ susp_flag = 0;
+
+}/* End Routine */
+
+
+
+/* CMD_WINCH - We get here when the OS says the window changed size
+ *
+ * Function:
+ *
+ * Here when the OS says our window changed size. We just wanna
+ * call the screen package's resize entry point so it can build
+ * a new screen for us.
+ */
+void
+cmd_winch()
+{
+ PREAMBLE();
+
+#ifdef ATT_UNIX
+ signal(SIGWINCH,(void (*)())cmd_winch);
+#endif
+
+#ifdef SEQUOIA
+ screen_message("WINCH!\g\g");
+ screen_refresh();
+ return;
+#endif /* SEQUOIA */
+
+ resize_flag = YES;
+
+ if(waiting_for_input_flag){
+ screen_resize();
+ screen_refresh();
+ }/* End IF */
+
+}/* End Routine */
+
+
+
+/* CMD_INTERRUPT - We get here on an interrupt signal from the user
+ *
+ * Function:
+ *
+ * Here when the user has typed ^C. We want to cause any current commands
+ * to abort. This gives the user a way to break out of infinite iterations
+ * and macros.
+ */
+void
+cmd_interrupt()
+{
+ PREAMBLE();
+
+#ifdef ATT_UNIX
+ signal(SIGINT,(void (*)())cmd_interrupt);
+#endif
+
+ if(intr_flag++ >= 4){
+ if(waiting_for_input_flag == NO){
+ restore_tty();
+ fprintf(stderr,"TECO aborted...\n");
+ signal(SIGINT,(void (*)())SIG_DFL);
+ kill(getpid(),SIGINT);
+ }/* End IF */
+ }/* End IF */
+
+ screen_message("interrupt!");
+
+}/* End Routine */
+
+
+
+#ifdef CHECKPOINT
+
+/* CMD_ALARM - Here to when the interval timer expires
+ *
+ * Function:
+ *
+ * This function is called periodically when the interval timer says
+ * it is time for us to checkpoint all our buffers.
+ */
+cmd_alarm()
+{
+
+ PREAMBLE();
+
+#if defined(ATT_UNIX) || defined(VMS)
+ signal(SIGALRM,(void (*)())cmd_alarm);
+#endif
+
+ if(waiting_for_input_flag == 0){
+ checkpoint_flag = YES;
+ }/* End IF */
+
+ else {
+ if(checkpoint_enabled == YES){
+ cmd_checkpoint();
+ }
+ checkpoint_flag = NO;
+ }/* End Else */
+
+#ifdef VMS
+ alarm(0);
+#endif /* VMS */
+ alarm(checkpoint_interval);
+
+}/* End Routine */
+
+#endif /* CHECKPOINT */
+
+
+
+#ifdef CHECKPOINT
+
+/* CMD_CHECKPOINT - Here to write all our buffers to a checkpoint file
+ *
+ * Function:
+ *
+ * Here we check out all the buffers and write out any which have
+ * ever been modified.
+ */
+void
+cmd_checkpoint()
+{
+register struct buff_header *bp;
+int fd = 0;
+char temp_buffer[TECO_FILENAME_TOTAL_LENGTH+20];
+char message_save_buff[SCREEN_NOMINAL_LINE_WIDTH];
+char filename_buffer[64];
+register char *cp;
+int i,status;
+
+ PREAMBLE();
+
+ if(checkpoint_modified == NO) return;
+ checkpoint_modified = NO;
+
+ if(checkpoint_filename == NULL){
+ temp_buffer[0] = '\0';
+ if(cp = (char *)getenv("HOME")){
+ (void) strcpy(temp_buffer,cp);
+#ifndef VMS
+ (void) strcat(temp_buffer,"/");
+#endif /* VMS */
+ }/* End IF */
+
+#ifndef VMS
+ strcpy(filename_buffer,".tecXXXXXX");
+#else
+ strcpy(filename_buffer,"tecXXXXXX.CKP");
+#endif /* VMS */
+ cp = mktemp(filename_buffer);
+
+ strcat(temp_buffer,cp);
+ checkpoint_filename = tec_alloc(TYPE_C_CPERM,strlen(temp_buffer)+1);
+ if(checkpoint_filename == NULL) return;
+ strcpy(checkpoint_filename,temp_buffer);
+ }/* End IF */
+
+ status = 0;
+ for(bp = buffer_headers; bp != NULL; bp = bp->next_header){
+ if(bp->ismodified){
+ status = 1;
+ }/* End IF */
+ }/* End FOR */
+
+ if(status == 0) return;
+
+ screen_save_current_message(message_save_buff,sizeof(message_save_buff));
+ screen_message("checkpointing...");
+ screen_refresh();
+
+ fd = open(checkpoint_filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
+ if(fd == 0 || fd == -1){
+ sprintf(temp_buffer,"?Checkpoint file %s open failed %s",
+ checkpoint_filename,error_text(errno));
+ error_message(temp_buffer);
+ screen_refresh();
+ sleep(2);
+ return;
+ }/* End IF */
+
+
+ for(bp = buffer_headers; bp != NULL; bp = bp->next_header){
+ if(bp->ismodified == NO && bp->buffer_number <= 0) continue;
+ if(bp->buffer_number == 0)continue;
+ i = (78 - strlen(bp->name) - 4) / 2;
+ cp = temp_buffer;
+ *cp++ = '\n';
+ while(i-- > 0)*cp++ = '*';
+ *cp++ = '(';
+ strcpy(cp,bp->name);
+ while(*cp)cp++;
+ *cp++ = ')';
+ if(bp->ismodified) *cp++ = '+';
+ while(cp < &temp_buffer[78]) *cp++ = '*';
+ *cp++ = '\n';
+ *cp++ = '\0';
+ i = strlen(temp_buffer);
+
+ if(write(fd,temp_buffer,i) != i){
+ sprintf(temp_buffer,"?checkpoint failed %s",error_text(errno));
+ error_message(temp_buffer);
+ screen_refresh();
+ sleep(2);
+ close(fd);
+ return;
+ }/* End IF */
+
+
+ if(bp->ismodified){
+ status = buff_write(bp,fd,0,bp->zee);
+ }/* End IF */
+
+ }/* End FOR */
+
+ close(fd);
+
+ screen_message("checkpointing... done");
+ screen_refresh();
+ screen_message(message_save_buff);
+
+}/* End Routine */
+
+#endif /* CHECKPOINT */
+
+
+
+#ifdef CHECKPOINT
+
+/* REMOVE_CHECKPOINT_FILE - On exit we clean up the checkpoint file
+ *
+ * Function:
+ *
+ * This routine is called when a clean exit is assured. We remove
+ * the checkpoint file so they don't clutter up the disk.
+ */
+void
+remove_checkpoint_file()
+{
+
+ PREAMBLE();
+
+ if(checkpoint_filename == NULL) return;
+#ifndef VMS
+ unlink(checkpoint_filename);
+#endif
+
+}/* End Routine */
+
+#endif /* CHECKPOINT */
+
+
+
+#if defined(VMS) || defined(STARDENT) || defined(STARDENT_860) || defined(SEQUOIA) || defined(LOCUS_SYSV) || defined(SCO_386)
+/* BZERO - Zero an array of bytes
+ *
+ * Function:
+ *
+ * This routine zeros an array of bytes. For some reason the VMS
+ * library doesn't seem to know about it.
+ */
+bzero(array,length)
+register char *array;
+register int length;
+{
+ PREAMBLE();
+
+ while(length-- > 0) *array++ = '\0';
+
+}/* End Routine */
+#endif /* VMS || STARDENT et al */
+
+
+
+#ifdef UNIX
+/* CMD_OSCMD - Issue a command to the operating system
+ *
+ * Function:
+ *
+ * This routine is called in response to the EC command which allows the
+ * user to execute operating system commands from within the editor.
+ */
+int
+cmd_oscmd(ct)
+register struct cmd_token *ct;
+{
+register char *cp;
+int pid,w,status;
+int pipe_desc[2];
+int last_intr_flag;
+int line_cnt;
+char tmpbuf[LINE_BUFFER_SIZE];
+char pipebuff[IO_BUFFER_SIZE];
+extern char susp_flag;
+int pipe_buf_flag = 0;
+int buf_pipe[2];
+
+ PREAMBLE();
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ pipe_buf_flag = 1;
+ status = pipe(buf_pipe);
+ if (status != 0) {
+ error_message("?Error creating buffer pipe");
+ return(FAIL);
+ }
+ }/* End IF */
+
+ cp = ct->ctx.carg;
+/*
+ * Create a PIPE so that we can capture the output of the child process
+ */
+ status = pipe(pipe_desc);
+ if(status != 0){
+ sprintf(tmpbuf,"?Error creating PIPE: %s",error_text(errno));
+ error_message(tmpbuf);
+ return(FAIL);
+ }/* End IF */
+/*
+ * Fork to get a process to run the shell
+ */
+#ifdef LINUX
+ if((pid = fork()) == 0){
+ close(1); dup(pipe_desc[1]);
+ close(2); dup(pipe_desc[1]);
+ close(pipe_desc[0]);
+ close(pipe_desc[1]);
+ execl("/bin/bash","bash","-c",cp,0);
+ _exit(127);
+ }/* End IF */
+#else
+ if((pid = fork()) == 0){
+ if (pipe_buf_flag) {
+ close(0); dup(buf_pipe[0]);
+ close(buf_pipe[1]);
+ } else {
+ close(pipe_desc[0]);
+ }
+ close(1); dup(pipe_desc[1]);
+ close(2); dup(pipe_desc[1]);
+ close(pipe_desc[0]);
+ close(pipe_desc[1]);
+ execl("/bin/csh","csh","-cf",cp,0);
+ _exit(127);
+ }/* End IF */
+#endif
+ if(pid == -1){
+ close(pipe_desc[0]);
+ close(pipe_desc[1]);
+ sprintf(tmpbuf,"?Error performing FORK: %s",error_text(errno));
+ return(FAIL);
+ }/* End IF */
+
+ if (pipe_buf_flag) {
+ struct buff_line *tmp_line = curbuf->first_line;
+
+ close(buf_pipe[0]);
+ while (tmp_line) {
+ int n = tmp_line->byte_count;
+ char *lb = tmp_line->buffer;
+
+ w = write(buf_pipe[1], lb, n);
+ while (w > 0) {
+ n -= w;
+ lb += w;
+ if (n == 0)
+ break;
+ w = write(pipe_desc[1], lb, n);
+ }
+ tmp_line = tmp_line->next_line;
+ }
+ close(buf_pipe[1]);
+ }
+ close(pipe_desc[1]);
+
+/*
+ * Loop reading stuff coming back from the pipe until we get an
+ * EOF which means the process has finished. Update the screen
+ * on newlines so the user can see what is going on.
+ */
+ last_intr_flag = intr_flag;
+ line_cnt = 0;
+
+ while((w = read(pipe_desc[0],pipebuff,sizeof(pipebuff))) > 0){
+
+ buff_insert(curbuf,curbuf->dot,pipebuff,w);
+
+ if(intr_flag != last_intr_flag){
+ kill(pid,SIGINT);
+ last_intr_flag = intr_flag;
+ }/* End IF */
+
+ if(susp_flag){
+ cmd_pause();
+ }/* End IF */
+
+ cp = pipebuff;
+ while(w--){
+ if(intr_flag != last_intr_flag) break;
+ if(*cp++ == '\n' && line_cnt++ > (term_lines / 4)){
+ line_cnt = 0;
+ if(tty_input_pending()) break;
+ screen_format_windows();
+ screen_refresh();
+ }/* End IF */
+ }/* End While */
+
+ }/* End While */
+
+ close(pipe_desc[0]);
+
+/*
+ * The wait here is required so that the process we forked doesn't
+ * stay around as a zombie.
+ */
+ status = 0;
+ while((w = wait(&status)) != pid && w != -1);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+/* END OF UNIX CONDITIONAL CODE */
+
+#endif
+
+
+
+#ifdef VMS
+
+/* CMD_OSCMD - Issue a command to the operating system
+ *
+ * Function:
+ *
+ * This routine is called in response to the EC command which allows the
+ * user to execute operating system commands from within the editor.
+ */
+cmd_oscmd(ct)
+register struct cmd_token *ct;
+{
+ PREAMBLE();
+
+ error_message("?VMS Does not currently support EC");
+ return(FAIL);
+
+}/* End Routine */
+
+#endif
+
+
+
+/* LOAD_QNAME_REGISTER - Loads buffer name into q-register *
+ *
+ * Function:
+ *
+ * This routine is called by a command operating on the text
+ * portion of a q-register if the specified q-register is *.
+ * In this case, we load the name of the current edit buffer
+ * into it so the user can easilly access it.
+ */
+void
+load_qname_register()
+{
+register struct buff_header *hbp;
+register struct buff_header *qbp;
+
+ PREAMBLE();
+
+ hbp = curbuf;
+ qbp = buff_qfind('*',1);
+
+ if(qbp->zee > 0){
+ buff_delete(qbp,0,qbp->zee);
+ }/* End IF */
+
+ buff_insert(qbp,qbp->dot,hbp->name,strlen(hbp->name));
+
+}/* End Routine */
+
+/* RENAME_EDIT_BUFFER - Change the name of the specified edit buffer
+ *
+ * Function:
+ *
+ * This routine is called by parser exec routines which have
+ * loaded q-register *. Since the text portion of this q-register
+ * is the buffer name, it has the effect of changing the name
+ * of the buffer.
+ */
+int
+rename_edit_buffer(hbp,new_name,uct)
+register struct buff_header *hbp;
+register char *new_name;
+register struct cmd_token *uct;
+{
+register int i;
+register struct undo_token *ut;
+int length;
+
+ PREAMBLE();
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_RENAME_BUFFER;
+ ut->carg1 = hbp->name;
+
+ length = strlen(new_name);
+ hbp->name = tec_alloc(TYPE_C_CBUFF,length+1);
+ if(hbp->name == NULL) return(FAIL);
+ for(i = 0; i < length; i++){
+ hbp->name[i] = new_name[i];
+ }/* End FOR */
+ hbp->name[length] = '\0';
+
+ buff_switch(hbp,0);
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* CMD_SETOPTIONS - Set runtime options via the EJ command
+ *
+ * Function:
+ *
+ * This routine is called when the user sets runtime variables using
+ * the EJ command. Although the command is a classic TECO command, the
+ * actual values are specific to Video TECO
+ */
+int
+cmd_setoptions(arg1,arg2,uct)
+int arg1;
+int arg2;
+struct undo_token *uct;
+{
+struct undo_token fake_token;
+extern int tab_width;
+
+ PREAMBLE();
+
+ if(arg1 < SETOPTION_MIN_OPTION || arg1 > SETOPTION_MAX_OPTION){
+ error_message("EJ option selector out of range");
+ return(FAIL);
+ }/* End IF */
+
+ if(!uct) uct = &fake_token;
+
+ switch(arg1){
+ case SETOPTION_ALTERNATE_ESCAPE_CHAR:
+ uct->iarg1 = arg1;
+ uct->iarg2 = alternate_escape_character;
+ alternate_escape_character = arg2;
+ break;
+ case SETOPTION_SCREEN_WIDTH:
+ uct->iarg1 = arg1;
+ uct->iarg2 = forced_width;
+ forced_width = arg2;
+ screen_resize();
+ break;
+ case SETOPTION_SCREEN_HEIGHT:
+ if(arg2 != 0 && arg2 < 3){
+ error_message("TECO requires a larger window");
+ return(FAIL);
+ }/* End IF */
+ uct->iarg1 = arg1;
+ uct->iarg2 = forced_height;
+ forced_height = arg2;
+ screen_resize();
+ break;
+ case SETOPTION_ALTERNATE_DELETE_CHAR:
+ uct->iarg1 = arg1;
+ uct->iarg2 = alternate_delete_character;
+ alternate_delete_character = arg2;
+ break;
+ case SETOPTION_FORCE_8_BIT_CHARS:
+ uct->iarg1 = arg1;
+ uct->iarg2 = eight_bit_output_flag;
+ eight_bit_output_flag = arg2 ? YES : NO;
+ screen_reformat_windows();
+ break;
+ case SETOPTION_TAB_WIDTH:
+ uct->iarg1 = arg1;
+ uct->iarg2 = tab_width;
+ tab_width = arg2 >= 1 && arg2 <= 16 ? arg2 : NORMAL_TAB_WIDTH;
+ screen_reformat_windows();
+ break;
+ case SETOPTION_HIDE_CR_CHARS:
+ uct->iarg1 = arg1;
+ uct->iarg2 = hide_cr_flag;
+ hide_cr_flag = arg2 ? YES : NO;
+ screen_reformat_windows();
+ break;
+
+ }/* End Switch */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* CMD_FTAGS - Perform UNIX tags function
+ *
+ * Function:
+ *
+ * This routine performs various unix tags functions. The following
+ * functions are supported:
+ *
+ * ARG1: Function:
+ *
+ * none Load tags file indicated in <string>
+ * 0 (same as <none>)
+ * 1 Find tag entry which corresponds with <string>. ARG2
+ * is a flags word which says what to do with the tag
+ */
+int
+cmd_tags(uct,arg_count,arg1,arg2,string)
+struct cmd_token *uct;
+int arg_count;
+int arg1;
+int arg2;
+char *string;
+{
+register struct undo_token *ut;
+register struct tags *old_tags;
+register struct tags *new_tags;
+register struct tagent *tep = NULL;
+int hashval,skip_cnt;
+
+ PREAMBLE();
+
+ if(arg_count == 0) arg1 = 0;
+ if(arg_count < 2) arg2 = 0;
+
+ if(arg1 < TAGS_MIN_OPTION || arg1 > TAGS_MAX_OPTION){
+ error_message("FT option selector out of range");
+ return(FAIL);
+ }/* End IF */
+
+ switch(arg1){
+ case TAGS_LOAD_TAGS_FILE:
+ old_tags = current_tags;
+ new_tags = tag_load_file(string);
+ if(new_tags == NULL) return(FAIL);
+ current_tags = new_tags;
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_LOAD_TAGS;
+ ut->carg1 = (char *)old_tags;
+ break;
+ case TAGS_SEARCH_TAGS_FILE:
+ if(current_tags == NULL) return(FAIL);
+ hashval = tag_calc_hash(string);
+ tep = current_tags->tagents[hashval];
+ skip_cnt = arg2;
+ while(tep){
+ if(strcmp(string,tep->te_symbol) == 0){
+ if(skip_cnt-- <= 0){
+ if(current_tags->current_entry != tep){
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_SELECT_TAGS;
+ ut->carg1 = (char *)current_tags->current_entry;
+ }/* End IF */
+ current_tags->current_entry = tep;
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+ tep = tep->te_next;
+ }/* End While */
+ return(FAIL);
+ case TAGS_TEST_FOR_LOADED_TAGS:
+ if(current_tags){
+ return(SUCCESS);
+ }/* End IF */
+ return(FAIL);
+ case TAGS_INSERT_TARGET_FILE:
+ if(current_tags->current_entry){
+ if(current_tags->current_entry->te_filename){
+ buff_insert_with_undo(
+ uct,
+ curbuf,
+ curbuf->dot,
+ current_tags->current_entry->te_filename,
+ strlen(current_tags->current_entry->te_filename)
+ );
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+ return(FAIL);
+ case TAGS_INSERT_SEARCH_STRING:
+ if(current_tags->current_entry){
+ if(current_tags->current_entry->te_search_string){
+ buff_insert_with_undo(
+ uct,
+ curbuf,
+ curbuf->dot,
+ current_tags->current_entry->te_search_string,
+ strlen(current_tags->current_entry->te_search_string)
+ );
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+ return(FAIL);
+ case TAGS_INSERT_LINENO:
+ if(current_tags->current_entry){
+ if(current_tags->current_entry->te_lineno){
+ buff_insert_with_undo(
+ uct,
+ curbuf,
+ curbuf->dot,
+ current_tags->current_entry->te_lineno,
+ strlen(current_tags->current_entry->te_lineno)
+ );
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+ return(FAIL);
+ case TAGS_INSERT_ALL:
+ if(current_tags){
+ tag_dump_database(current_tags,uct);
+ return(SUCCESS);
+ }/* End IF */
+ return(FAIL);
+ }/* End Switch */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+/* TAG_LOAD_FILE - Read in a tags file
+ *
+ * Function:
+ *
+ * This is a huge monolithic nasty routine which reads either VI or
+ * EMACS tags files, and builds an internal representation.
+ */
+struct tags *
+tag_load_file(string)
+register char *string;
+{
+FILE *fd = NULL;
+register struct tags *tp = NULL;
+register struct tagent *tep = NULL;
+register char *cp;
+char *outcp = NULL;
+char *filename_cp = NULL;
+int state = 0;
+int hashval;
+int c;
+char emacs_flag = NO;
+char comma_seen = 0;
+char tmp_buffer[LINE_BUFFER_SIZE];
+char tmp_search_string[LINE_BUFFER_SIZE];
+char tmp_filename[LINE_BUFFER_SIZE];
+char tmp_symbol[LINE_BUFFER_SIZE];
+char tmp_number[LINE_BUFFER_SIZE];
+char tmp_message[LINE_BUFFER_SIZE];
+int line = 0;
+
+ fd = fopen(string,"r");
+ if(fd == NULL){
+ sprintf(
+ tmp_message,
+ "Could not open TAGS file <%s>: %s",
+ string,
+ error_text(errno)
+ );
+ error_message(tmp_message);
+ return(NULL);
+ }/* End IF */
+
+ tp = (struct tags *)tec_alloc(TYPE_C_TAGS,sizeof(struct tags));
+ if(tp == NULL) return(NULL);
+ bzero((char *)tp,sizeof(struct tags));
+
+/*
+ * Loop reading tag entries
+ */
+ while(1){
+ cp = tmp_buffer;
+ bzero(cp,sizeof(tmp_buffer));
+ if(fgets(cp,sizeof(tmp_buffer),fd) == NULL) break;
+ line++;
+/*
+ * Ok, we have the start of an entry. Check for continuation.
+ */
+ while(cp[0]){
+ if(cp[0] == '\n'){
+ cp[1] = '\0';
+ break;
+ }/* End IF */
+ if(cp[0] == '\\' && cp[1] == '\n'){
+ if(fgets(cp,sizeof(tmp_buffer)-(cp-tmp_buffer),fd) == NULL){
+ cp[0] = '\n'; cp[1] = '\0';
+ }/* End IF */
+ line++;
+ }/* End IF */
+ cp++;
+ }/* End While */
+
+/*
+ * First step is to get the name of the symbol so that we can obtain the
+ * hash value
+ */
+ cp = tmp_buffer;
+
+ if(emacs_flag == NO) state = 0;
+
+ while(*cp){
+ if(state < 0) break;
+ switch(state){
+/*
+ * Skip over symbol name until we hit whitespace. Then calculate the hash
+ * value for that symbol, allocate the tag entry structure, and copy the
+ * symbol name into it.
+ */
+ case 0:
+ if(cp[0] == '\f' && cp[1] == '\n'){
+ emacs_flag = YES;
+ state = 100;
+ continue;
+ }/* End IF */
+
+ if(isspace((int)*cp)){
+ c = *cp;
+ *cp = '\0';
+ hashval = tag_calc_hash(tmp_buffer);
+ tep = (struct tagent *)tec_alloc(
+ TYPE_C_TAGENT,
+ sizeof(struct tagent)
+ );
+ if(tep == NULL) goto err;
+ bzero((char *)tep,sizeof(*tep));
+
+ tep->te_next = tp->tagents[hashval];
+ tp->tagents[hashval] = tep;
+
+ tep->te_symbol = (char *)tec_alloc(
+ TYPE_C_TAGSTR,
+ strlen(tmp_buffer) + 1
+ );
+ if(tep->te_symbol == NULL) goto err;
+ strcpy(tep->te_symbol,tmp_buffer);
+
+ *cp = c;
+ state++;
+ }/* End IF */
+ cp++;
+ continue;
+/*
+ * Skip any amount of whitespace which seperates the symbol from the
+ * filename.
+ */
+ case 1:
+ if(!isspace((int)*cp)){
+ filename_cp = cp;
+ state++;
+ continue;
+ }/* End IF */
+ cp++;
+ continue;
+/*
+ * Find the length of the filename portion and copy it to the tagent
+ */
+ case 2:
+ if(isspace((int)*cp)){
+ c = *cp;
+ *cp = '\0';
+
+ tep->te_filename = (char *)tec_alloc(
+ TYPE_C_TAGSTR,
+ strlen(filename_cp) + 1
+ );
+ if(tep->te_filename == NULL) goto err;
+ strcpy(tep->te_filename,filename_cp);
+
+ *cp = c;
+ state++;
+ }/* End IF */
+ cp++;
+ continue;
+/*
+ * Skip any amount of whitespace which seperates the filename from the
+ * search string
+ */
+ case 3:
+ if(!isspace((int)*cp)){
+ state++;
+ continue;
+ }/* End IF */
+ cp++;
+ continue;
+
+/*
+ * The next character should be a slash to begin the search string
+ */
+ case 4:
+ if(*cp == '/'){
+ outcp = tmp_search_string;
+ *outcp = '\0';
+ state++;
+ }
+ cp++;
+ continue;
+/*
+ * Find the length of the search string portion and copy it to the tagent
+ */
+ case 5:
+ if(*cp == '/'){
+ *outcp = '\0';
+
+ tep->te_search_string = (char *)tec_alloc(
+ TYPE_C_TAGSTR,
+ strlen(tmp_search_string) + 1
+ );
+ if(tep->te_search_string == NULL) goto err;
+ strcpy(tep->te_search_string,tmp_search_string);
+
+ state = -1;
+ continue;
+ }/* End IF */
+ if(*cp == '\\'){
+ state = state + 1;
+ cp++;
+ continue;
+ }/* End IF */
+ *outcp++ = *cp++;
+ continue;
+
+/*
+ * Here on a backquote character. Just skip it and pass through the quoted
+ * character without testing it for termination of the search string.
+ */
+ case 6:
+ *outcp++ = *cp++;
+ state = state - 1;
+ continue;
+
+/*
+ * Here if we encounter a form-feed. This is a giveaway that we have an
+ * emacs style tags file, so we have to parse it differently.
+ */
+ case 100:
+ if(cp[0] != '\f' || cp[1] != '\n'){
+ cp++;
+ continue;
+ }/* End IF */
+ cp += 2;
+ state = 101;
+ outcp = tmp_filename;
+ comma_seen = 0;
+ continue;
+/*
+ * Here we get the name of the source file
+ */
+ case 101:
+ if(*cp == '\n'){
+ if(comma_seen == 0){
+ state = 100;
+ continue;
+ }/* End IF */
+ outcp--;
+ while(*outcp != ',') outcp--;
+ *outcp = '\0';
+ cp++;
+ state = 102;
+ outcp = tmp_symbol;
+ *outcp++ = 127;
+ continue;
+ }/* End IF */
+
+ if(*cp == ',') comma_seen = 1;
+ *outcp++ = *cp++;
+ continue;
+/*
+ * Now we get symbol names
+ */
+ case 102:
+ if(*cp == '\f'){
+ state = 100;
+ continue;
+ }/* End IF */
+
+ if(*cp != 127){
+ *outcp++ = *cp++;
+ continue;
+ }/* End IF */
+/*
+ * Here on a 127 code which terminates the symbol name. First eat back to
+ * the first symbol character.
+ */
+ while((c = outcp[-1])){
+ if(c == 127) break;
+ if(
+ isalnum(c) ||
+ isdigit(c) ||
+ c == '_' ||
+ c == '$' ||
+ c == '.'
+ ){
+ *outcp = '\0';
+ break;
+ }/* End IF */
+ outcp--;
+ }/* End While */
+/*
+ * Now go back until we find the first non-symbol character.
+ */
+ while((c = outcp[-1])){
+ if(c == 127) break;
+ if(
+ isalnum(c) ||
+ isdigit(c) ||
+ c == '_' ||
+ c == '$' ||
+ c == '.'
+ ){
+ outcp--;
+ continue;
+ }/* End IF */
+ break;
+ }/* End While */
+
+ hashval = tag_calc_hash(outcp);
+ tep = (struct tagent *)tec_alloc(
+ TYPE_C_TAGENT,
+ sizeof(struct tagent)
+ );
+ if(tep == NULL) goto err;
+ bzero((char *)tep,sizeof(*tep));
+
+ tep->te_next = tp->tagents[hashval];
+ tp->tagents[hashval] = tep;
+
+ tep->te_symbol = (char *)tec_alloc(
+ TYPE_C_TAGSTR,
+ strlen(outcp) + 1
+ );
+ if(tep->te_symbol == NULL) goto err;
+ strcpy(tep->te_symbol,outcp);
+ tep->te_filename = (char *)tec_alloc(
+ TYPE_C_TAGSTR,
+ strlen(tmp_filename) + 1
+ );
+ if(tep->te_filename == NULL) goto err;
+ strcpy(tep->te_filename,tmp_filename);
+
+ state = 103;
+ continue;
+
+/*
+ * Eat until we reach a number, which is the line number
+ */
+ case 103:
+ if(isdigit((int)*cp)){
+ state = 104;
+ outcp = tmp_number;
+ continue;
+ }/* End IF */
+
+ if(*cp == '\f'){
+ state = 100;
+ continue;
+ }/* End IF */
+
+ cp++;
+ continue;
+/*
+ * Read in the line number
+ */
+ case 104:
+ if(isdigit((int)*cp)){
+ *outcp++ = *cp++;
+ continue;
+ }/* End IF */
+
+ *outcp++ = '\0';
+ tep->te_lineno = (char *)tec_alloc(
+ TYPE_C_TAGSTR,
+ strlen(tmp_number) + 1
+ );
+ if(tep->te_lineno == NULL) goto err;
+ strcpy(tep->te_lineno,tmp_number);
+
+ state = 105;
+ continue;
+/*
+ * Eat until end of line
+ */
+ case 105:
+ if(*cp == '\n'){
+ cp++;
+ state = 102;
+ outcp = tmp_symbol;
+ continue;
+ }/* End IF */
+ if(*cp == '\f'){
+ state = 100;
+ continue;
+ }/* End IF */
+ cp++;
+ continue;
+ }/* End Switch */
+ }/* End While */
+
+ }/* End While */
+
+ fclose(fd);
+ return(tp);
+
+err:
+
+ tag_free_struct(tp);
+ if(fd) fclose(fd);
+ return(NULL);
+
+}/* End Routine */
+
+void
+tag_free_struct(tp)
+register struct tags *tp;
+{
+register struct tagent **tepp;
+register struct tagent *tep;
+register int i;
+
+ if(tp == NULL) return;
+
+ for(i = 0, tepp = &tp->tagents[0]; i < ELEMENTS(tp->tagents); i++,tepp++){
+ while((tep = *tepp)){
+ *tepp = tep->te_next;
+
+ if(tep->te_symbol){
+ tec_release(TYPE_C_TAGSTR,tep->te_symbol);
+ }/* End IF */
+
+ if(tep->te_filename){
+ tec_release(TYPE_C_TAGSTR,tep->te_filename);
+ }/* End IF */
+
+ if(tep->te_search_string){
+ tec_release(TYPE_C_TAGSTR,tep->te_search_string);
+ }/* End IF */
+
+ tec_release(TYPE_C_TAGENT,(char *)tep);
+
+ }/* End While */
+
+ }/* End FOR */
+
+ tec_release(TYPE_C_TAGS,(char *)tp);
+
+}/* End Routine */
+
+int
+tag_calc_hash(string)
+register char *string;
+{
+register int hash = 0;
+register int c;
+register int shift = 0;
+
+ while((c = *string++)){
+ shift = shift == 8 ? 0 : shift + 1;
+ hash += c << shift;
+ }/* End While */
+
+ return(hash % TAG_HASH_ENTRIES);
+
+}/* End Routine */
+
+void
+tag_dump_database(tp,uct)
+register struct tags *tp;
+struct cmd_token *uct;
+{
+register struct tagent **tepp;
+register struct tagent *tep;
+register int i;
+char tmp_output[LINE_BUFFER_SIZE];
+
+ if(tp == NULL) return;
+
+ for(i = 0, tepp = &tp->tagents[0]; i < ELEMENTS(tp->tagents); i++,tepp++){
+ tep = *tepp;
+ while(tep){
+
+ sprintf(
+ tmp_output,
+ "sym <%s> hash %d file <%s> line <%s> search <%s>\n",
+ tep->te_symbol ? tep->te_symbol : "",
+ tep->te_symbol ? tag_calc_hash(tep->te_symbol) : 0,
+ tep->te_filename ? tep->te_filename : "",
+ tep->te_lineno ? tep->te_lineno : "",
+ tep->te_search_string ? tep->te_search_string : ""
+ );
+
+ buff_insert_with_undo(
+ uct,
+ curbuf,
+ curbuf->dot,
+ tmp_output,
+ strlen(tmp_output)
+ );
+
+ tep = tep->te_next;
+
+ }/* End While */
+ }/* End FOR */
+
+ return;
+
+}/* End Routine */
diff --git a/tecdebug.c b/tecdebug.c
new file mode 100644
index 0000000..1d06808
--- /dev/null
+++ b/tecdebug.c
@@ -0,0 +1,401 @@
+char *tecdebug_c_version = "tecdebug.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecdebug.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecdebug.c
+ * Debugging routines for Teco
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+ extern struct screen_line *saved_screen;
+ extern char saved_screen_valid;
+ extern int term_lines;
+
+ extern struct buff_header *buffer_headers;
+ extern struct buff_header *curbuf;
+ extern struct buff_header *qregister_push_down_list;
+
+ extern struct format_line *format_line_free_list;
+ extern struct format_line *format_line_alloc_list;
+
+ extern char format_lines_invalid;
+
+void
+do_preamble_checks()
+{
+ tecdebug_check_screen_magic();
+ tecdebug_check_buffer_magic();
+ tecdebug_check_line_magic();
+ tecdebug_check_format_magic();
+ tecdebug_check_companion_pointers();
+ tecdebug_check_window_pointers();
+}
+
+void
+do_return_checks()
+{
+ do_preamble_checks();
+}
+
+/* TECDEBUG_CHECK_SCREEN_MAGIC - Check for matching magic numbers
+ *
+ * Function:
+ *
+ * This routine checks that each saved screen array element has
+ * the correct magic number.
+ */
+void
+tecdebug_check_screen_magic()
+{
+register int i;
+register struct screen_line *sbp;
+/*
+ * Basic checks first - check that each saved screen structure has the proper
+ * magic number in it.
+ */
+ if(saved_screen_valid){
+ for(sbp = saved_screen,i = 0; i < term_lines && sbp != NULL;i++,sbp++){
+ if(sbp->scr_magic != MAGIC_SCREEN){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?saved_screen[%d] bad magic#, 0x%08x should be 0x%08x\n",
+ i,
+ sbp->scr_magic,
+ (int)MAGIC_SCREEN
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ }/* End FOR */
+ }/* End IF */
+}/* End Routine */
+
+
+
+/* TECDEBUG_CHECK_BUFFER_MAGIC - Check for matching magic numbers
+ *
+ * Function:
+ *
+ * This routine checks that each buffer header to check for
+ * the correct magic number.
+ */
+void
+tecdebug_check_buffer_magic()
+{
+register struct buff_header *bp;
+register char saw_curbuf;
+register int count;
+
+ bp = buffer_headers;
+ count = 0;
+ saw_curbuf = 0;
+ while(bp){
+ if(bp == curbuf) saw_curbuf = 1;
+ if(bp->buf_magic != MAGIC_BUFFER){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?buff_headers[%d] bad magic#, 0x%08x should be 0x%08x\n",
+ count,
+ bp->buf_magic,
+ (int)MAGIC_BUFFER
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ count += 1;
+ bp = bp->next_header;
+ }/* End While */
+
+ if(curbuf != NULL && saw_curbuf == 0){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?curbuf 0x%x not on buff_headers list\n",
+ (unsigned int)curbuf
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+
+ bp = qregister_push_down_list;
+ count = 0;
+ while(bp){
+ if(bp->buf_magic != MAGIC_BUFFER){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?qpushdown[%d] bad magic#, 0x%08x should be 0x%08x\n",
+ count,
+ bp->buf_magic,
+ (int)MAGIC_BUFFER
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ count += 1;
+ bp = bp->next_header;
+ }/* End While */
+}/* End Routine */
+
+
+
+/* TECDEBUG_CHECK_LINE_MAGIC - Check for matching magic numbers
+ *
+ * Function:
+ *
+ * This routine checks that each line buffer data structure for
+ * the correct magic number.
+ */
+void
+tecdebug_check_line_magic()
+{
+register struct buff_header *bp;
+register struct buff_line *lbp;
+register int count;
+
+ bp = buffer_headers;
+ while(bp){
+ lbp = bp->first_line;
+ count = 0;
+ while(lbp){
+ if(lbp->lin_magic != MAGIC_LINE){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?buf[%s] line %d bad magic#, 0x%08x should be 0x%08x\n",
+ bp->name,
+ count,
+ lbp->lin_magic,
+ (int)MAGIC_LINE
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ count += 1;
+ lbp = lbp->next_line;
+ }/* End While */
+ bp = bp->next_header;
+ }/* End While */
+
+ bp = qregister_push_down_list;
+ while(bp){
+ lbp = bp->first_line;
+ count = 0;
+ while(lbp){
+ if(lbp->lin_magic != MAGIC_LINE){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?qreg pdl line %d bad magic#, 0x%08x should be 0x%08x\n",
+ count,
+ lbp->lin_magic,
+ (int)MAGIC_LINE
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ count += 1;
+ lbp = lbp->next_line;
+ }/* End While */
+ bp = bp->next_header;
+ }/* End While */
+}/* End Routine */
+
+
+
+/* TECDEBUG_CHECK_FORMAT_MAGIC - Check for matching magic numbers
+ *
+ * Function:
+ *
+ * This routine checks each screen format buffer data structure for
+ * the correct magic number.
+ */
+void
+tecdebug_check_format_magic()
+{
+register struct format_line *fp;
+register int count;
+
+ fp = format_line_alloc_list;
+ while(fp){
+ if(fp->fmt_magic != MAGIC_FORMAT){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?format line bad magic#, 0x%08x should be 0x%08x\n",
+ fp->fmt_magic,
+ (int)MAGIC_FORMAT
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ fp = fp->fmt_next_alloc;
+ }/* End While */
+
+ fp = format_line_free_list;
+ count = 0;
+ while(fp){
+ if(fp->fmt_magic != MAGIC_FORMAT_LOOKASIDE){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?format lookaside[%d] bad magic#, 0x%08x should be 0x%08x\n",
+ count,
+ fp->fmt_magic,
+ (int)MAGIC_FORMAT_LOOKASIDE
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ fp = fp->fmt_next_line;
+ count += 1;
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* TECDEBUG_CHECK_COMPANION_POINTERS - Check for pointer consistency
+ *
+ * Function:
+ *
+ * This routine checks that each saved_screen entry points to
+ * a single format line which also points back. It's an error
+ * if a structure points to another structure which doesn't
+ * point back.
+ */
+void
+tecdebug_check_companion_pointers()
+{
+register int i;
+register struct screen_line *sbp;
+register struct format_line *fbp;
+
+/*
+ * First check from the saved screen side that the pointers seem to be
+ * consistent - i.e. that every structure points to one and only one
+ * structure which points back.
+ */
+ if(!saved_screen_valid) return;
+ for(sbp = saved_screen,i = 0; i < term_lines && sbp != NULL;i++,sbp++){
+ if(sbp->companion == NULL) continue;
+ if(sbp->companion->fmt_saved_line != sbp){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?saved_screen[%d] companion 0x%08x ->fmt_saved_line 0x%08x\n",
+ i,
+ (unsigned int)sbp->companion,
+ (unsigned int)sbp->companion->fmt_saved_line
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ }/* End FOR */
+
+/*
+ * Now check from the allocated format structure side of things, to make sure
+ * that each one of them which points to a screen structure is pointed back
+ * at by that screen structure.
+ */
+ fbp = format_line_alloc_list;
+ while(fbp){
+ if(fbp->fmt_saved_line && fbp->fmt_saved_line->companion != fbp){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?fbp 0x%08x fbp->fmt_saved 0x%08x ->companion 0x%08x\n",
+ (unsigned int)fbp,
+ (unsigned int)fbp->fmt_saved_line,
+ (unsigned int)fbp->fmt_saved_line->companion
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ fbp = fbp->fmt_next_alloc;
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* TECDEBUG_CHECK_WINDOW_POINTERS - Check for window pointer consistency
+ *
+ * Function:
+ *
+ * This routine checks that every format line is correctly chained with respect
+ * to which window it is connected to. An error occurs if the line_buffer does
+ * not chain down to it. Also, for every "next_window" pointer, all format
+ * structures chained off of it must belong to the same window.
+ */
+void
+tecdebug_check_window_pointers()
+{
+register struct format_line *fbp,*fl_win_head,*fl_temp;
+register struct window *wptr;
+register struct buff_line *blp;
+char saw_our_format_line;
+
+ if(format_lines_invalid) return;
+
+/*
+ * Check each format line on the allocated list
+ */
+ for(fbp = format_line_alloc_list; fbp != NULL; fbp = fbp->fmt_next_alloc){
+ saw_our_format_line = 0;
+ blp = fbp->fmt_buffer_line;
+ fl_win_head = blp->format_line;
+ for(; fl_win_head != NULL; fl_win_head = fl_win_head->fmt_next_window){
+ wptr = fl_win_head->fmt_window_ptr;
+ for(fl_temp = fl_win_head; fl_temp != NULL; fl_temp = fl_temp->fmt_next_line){
+ if(fl_temp == fbp) saw_our_format_line = 1;
+ if(fl_temp->fmt_window_ptr != wptr){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?wrong window in fmt chain format_line 0x%08x format_line->wptr 0x%08x wptr 0x%08x\n",
+ (unsigned int)fl_temp,
+ (unsigned int)fl_temp->fmt_window_ptr,
+ (unsigned int)wptr
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ if(fl_temp->fmt_buffer_line != blp){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?wrong buffer_line in fmt chain fmt 0x%08x fmt->fmt_buffer_line 0x%08x lbp 0x%08x\n",
+ (unsigned int)fl_temp,
+ (unsigned int)fl_temp->fmt_buffer_line,
+ (unsigned int)blp
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ }/* End FOR */
+ }/* End FOR */
+ if(saw_our_format_line == 0){
+ restore_tty();
+ fprintf(
+ stderr,
+ "?never encountered our format line 0x%08x in format list\n",
+ (unsigned int)fbp
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ }/* End FOR */
+
+}/* End Routine */
diff --git a/tecdisp.c b/tecdisp.c
new file mode 100644
index 0000000..a1d2bb6
--- /dev/null
+++ b/tecdisp.c
@@ -0,0 +1,2867 @@
+char *tecdisp_c_version = "tecdisp.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecdisp.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecdisp.c
+ * Terminal Screen Display Subroutines
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+/*
+ * Global Storage is defined here for lack of a better place.
+ */
+/*
+ * External Variables
+ */
+ extern char teco_startup;
+ extern char screen_startup;
+ extern int term_columns;
+ extern int term_lines;
+ extern int forced_height,forced_width;
+ extern int tty_input_chan;
+ extern int tty_output_chan;
+ extern char insert_delete_line_capability;
+ extern char eight_bit_output_flag;
+ extern char hide_cr_flag;
+ extern char tab_expand[];
+ extern int tab_width;
+
+#ifdef TERMCAP
+ extern int termcap_sg;
+#endif
+
+#ifdef TERMINFO
+ extern int terminfo_magic_cookie_glitch;
+#endif
+
+/*
+ * Global Variables
+ */
+ int curx;
+ int cury;
+ short cur_mode;
+ int cursor_x;
+ int cursor_y;
+ int screen_sequence;
+ int echo_cnt;
+ char ring_audible_bell;
+ int buffer_li;
+ int buffer_half_li;
+ char format_lines_invalid;
+
+ struct format_line *
+ allocate_format_buffer(struct window *,struct buff_line *);
+ struct format_line *
+ screen_find_window_format_line(struct window *,struct buff_line *);
+ struct window *create_window(int,int);
+ void screen_optimize_lines(void);
+ void screen_optimize_by_pattern_matching(void);
+ void screen_gc_format_lines(void);
+ void screen_free_window_format_lines(struct window *,struct buff_line *);
+ void screen_account_for_delete_line(int y, int count);
+ void screen_account_for_insert_line(int y, int count);
+ int screen_rebuild_from_scratch(struct window *wptr);
+ int screen_format_buff_line(struct window *wptr,struct buff_line *lbp);
+ int screen_find_dot(struct window *wptr);
+
+/*
+ * Global structures
+ */
+ struct screen_line *saved_screen;
+ char saved_screen_valid;
+
+ int next_window_number = 1;
+ struct window *curwin;
+ struct window *window_list;
+ struct format_line message_line;
+ char message_flag = NO;
+ struct format_line echo_line;
+
+ struct format_line *format_line_free_list = NULL;
+ struct format_line *format_line_alloc_list = NULL;
+
+/*
+ * External Routines, etc.
+ */
+ extern struct buff_header *curbuf;
+ extern struct buff_header *buffer_headers;
+
+ extern int term_speed;
+ extern char input_pending_flag;
+ extern char resize_flag;
+
+/* SCREEN_INIT - Initialize the TECO screen package
+ *
+ * Function:
+ *
+ * This entry point gives the screen package a chance to initialize
+ * itself.
+ */
+int
+screen_init()
+{
+register int i,j;
+register struct window *wptr;
+register struct screen_line *lp;
+register short *sp;
+
+ PREAMBLE();
+
+/*
+ * Call the terminal handling routines, and give them a chance to set
+ * the terminal up.
+ */
+ term_init();
+
+/*
+ * Set the cursor to the upper left of the display and perform a clear to end
+ * of display command. This has the effect of clearing the entire display.
+ */
+ term_goto(0,0);
+ term_clrtobot();
+
+/*
+ * If this is a restart (from ^Z most likely), then that is all we have to do.
+ * The rest of this is only to initialize data structures the first time
+ * through
+ */
+ if(screen_startup == NO) return(SUCCESS);
+ screen_startup = NO;
+
+/*
+ * Calculate how many lines are available for display of the edit buffer.
+ */
+ buffer_li = term_lines - SCREEN_RESERVED_LINES;
+ buffer_half_li = ((buffer_li + 1) & ~1) / 2;
+
+/*
+ * Allocate memory for the saved screen structure. This is used to remember
+ * what is currently showing on the terminal. Since we just cleared the screen,
+ * we set the current contents of this to be all spaces (i.e., blank).
+ */
+ i = sizeof(struct screen_line) * term_lines;
+ saved_screen = (struct screen_line *)tec_alloc(TYPE_C_SCR,i);
+ if(saved_screen == NULL) return(FAIL);
+
+ bzero(saved_screen,i);
+ screen_sequence = 1;
+
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ lp->scr_magic = MAGIC_SCREEN;
+ lp->companion = NULL;
+ lp->sequence = 0;
+ lp->buffer = (short *)
+ tec_alloc(TYPE_C_SCRBUF,(int)(sizeof(short) * term_columns));
+ sp = lp->buffer;
+ for(j = 0; j < term_columns; j++,sp++) *sp = ' ';
+ }/* End FOR */
+
+ saved_screen_valid = 1;
+
+/*
+ * Allocate the initial window structure
+ */
+ window_list = wptr =
+ create_window(term_columns,term_lines - SCREEN_RESERVED_LINES);
+ curwin = wptr;
+ wptr->win_buffer = curbuf;
+
+ lp = saved_screen;
+ lp += term_lines - SCREEN_RESERVED_LINES - 1;
+ wptr->win_label_line.fmt_saved_line = lp;
+ lp->companion = &wptr->win_label_line;
+/*
+ * Now we set up the structures for our special lines which form the bottom
+ * of the display screen.
+ */
+ echo_line.fmt_buffer_line = message_line.fmt_buffer_line = NULL;
+ echo_line.fmt_buffer_size = message_line.fmt_buffer_size = term_columns;
+
+ echo_line.fmt_buffer = (short *)
+ tec_alloc(
+ TYPE_C_SCRBUF,
+ (int)(sizeof(short) * echo_line.fmt_buffer_size)
+ );
+ message_line.fmt_buffer = (short *)
+ tec_alloc(
+ TYPE_C_SCRBUF,
+ (int)(sizeof(short)*message_line.fmt_buffer_size)
+ );
+
+ for(i = 0; i < term_columns; i++){
+ message_line.fmt_buffer[i] = ' ';
+ echo_line.fmt_buffer[i] = ' ';
+ }/* End FOR */
+
+ echo_line.fmt_buffer[0] = '*';
+ echo_line.fmt_sequence = message_line.fmt_sequence = 1;
+ echo_line.fmt_next_line = message_line.fmt_next_line = NULL;
+
+ message_line.fmt_visible_line_position =
+ echo_line.fmt_visible_line_position = -1;
+
+ lp = saved_screen;
+ lp += term_lines - SCREEN_RESERVED_LINES;
+
+ message_line.fmt_permanent = 1;
+ message_line.fmt_saved_line = lp;
+ message_line.fmt_magic = MAGIC_FORMAT_LOOKASIDE;
+ lp->companion = &message_line;
+
+ lp += 1;
+ echo_line.fmt_permanent = 1;
+ echo_line.fmt_saved_line = lp;
+ echo_line.fmt_magic = MAGIC_FORMAT_LOOKASIDE;
+ lp->companion = &echo_line;
+
+ screen_label_line(curbuf," TECO",LABEL_C_TECONAME);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_RESIZE - Cause the screen to be refreshed at a new size
+ *
+ * Function:
+ *
+ * This function is called when the size of the terminal screen has
+ * changed. This is common on a windowing terminal. This routine has
+ * to clean up the existing screen database and re-initialize it.
+ */
+void
+screen_resize()
+{
+register int i;
+register struct window *wptr;
+register struct screen_line *lp;
+
+#ifdef SUN_STYLE_WINDOW_SIZING
+ struct winsize os_window;
+#endif
+
+ PREAMBLE();
+
+/*
+ * The screen_gc_format_line is going to scavenge all the lines because
+ * we're making them look not-used here. However, we still go ahead and
+ * clobber the line_buffer's pointer, because we don't like the idea of
+ * having hanging pointers pointed at our screen lines.
+ */
+ if(saved_screen_valid){
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ if(lp->companion){
+ if(lp->companion->fmt_saved_line != lp){
+ fprintf(
+ stderr,
+ "lp->companion 0x%x != ->fmt_saved_line 0x%x\n",
+ (unsigned int)lp->companion,
+ (unsigned int)lp->companion->fmt_saved_line
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ lp->companion->fmt_saved_line = NULL;
+ lp->companion = NULL;
+ }/* End IF */
+ }/* End FOR */
+ }/* End IF */
+
+ screen_gc_format_lines();
+
+/*
+ * Return the memory used for the saved screen structure. This structure
+ * is the one used to remember what is currently showing on the terminal.
+ */
+ if(saved_screen_valid){
+ saved_screen_valid = 0;
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ if(lp->buffer) tec_release(TYPE_C_SCRBUF,(char *)lp->buffer);
+ lp->buffer = NULL;
+ }/* End FOR */
+
+ if(saved_screen) tec_release(TYPE_C_SCR,(char *)saved_screen);
+ saved_screen = NULL;
+
+ }/* End IF */
+
+/*
+ * Reclaim all the window structures
+ */
+ while(1){
+ wptr = window_list;
+ if(wptr == NULL) break;
+ window_list = wptr->win_next_window;
+/*
+ * Release any memory used to hold the strings in the label line
+ */
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ if(wptr->win_label_field_contents[i]){
+ tec_release(
+ TYPE_C_LABELFIELD,
+ wptr->win_label_field_contents[i]
+ );
+ wptr->win_label_field_contents[i] = NULL;
+ }/* End IF */
+ }/* End FOR */
+/*
+ * Release the buffer used to hold formatted data
+ */
+ tec_release(TYPE_C_SCRBUF,(char *)wptr->win_label_line.fmt_buffer);
+ tec_release(TYPE_C_WINDOW,(char *)wptr);
+
+ }/* End While */
+
+ window_list = NULL;
+
+ if(echo_line.fmt_buffer){
+ tec_release(TYPE_C_SCRBUF,(char *)echo_line.fmt_buffer);
+ }/* End IF */
+
+ if(message_line.fmt_buffer){
+ tec_release(TYPE_C_SCRBUF,(char *)message_line.fmt_buffer);
+ }/* End IF */
+ echo_cnt = 0;
+
+ echo_line.fmt_buffer_line = message_line.fmt_buffer_line = NULL;
+ echo_line.fmt_buffer_size = message_line.fmt_buffer_size = 0;
+
+/*
+ * If the OS supports window size ioctls, we try for that since it
+ * will tend to be more correct for a window environment.
+ */
+#ifdef SUN_STYLE_WINDOW_SIZING
+ if(ioctl(tty_output_chan,TIOCGWINSZ,&os_window) >= 0){
+ term_lines = os_window.ws_row;
+ term_columns = os_window.ws_col;
+ }/* End IF */
+#endif
+ if(forced_height > 0) term_lines = forced_height;
+ if(forced_width > 0) term_columns = forced_width;
+
+ if(term_lines == 0) term_lines = 24;
+ if(term_columns == 0) term_columns = 80;
+
+ resize_flag = NO;
+ screen_startup = YES;
+ screen_init();
+ buff_switch(curbuf,0);
+ parser_reset_echo();
+ screen_echo('\0');
+ screen_format_windows();
+
+}/* End Routine */
+
+
+
+/* SCREEN_FINISH - Here when we are exiting the editor
+ *
+ * Function:
+ *
+ * This routine is called when TECO is being exited to give us a chance
+ * to cleanly leave the screen package. We put the cursor at the bottom
+ * of the screen, send any termination escape sequences that are required
+ * and flush it all out.
+ */
+void
+screen_finish()
+{
+
+ term_finish();
+
+}/* End Routine */
+
+
+
+/* SCREEN_REFRESH - Redraw the screen based on new database
+ *
+ * Function:
+ *
+ * This routine gets called after the new companion entries have been set
+ * up for the saved_screen database. Therefore, we can run through the
+ * lines and update only those which have changed.
+ */
+void
+screen_refresh()
+{
+register int x,y;
+register struct screen_line *lp;
+register struct format_line *his_lp;
+register short *sp,*osp;
+char pos_flag;
+
+ PREAMBLE();
+
+ input_pending_flag = tty_input_pending();
+ if(input_pending_flag == YES) return;
+
+/*
+ * First try to optimize output by performing insert/delete line operations
+ */
+ if(insert_delete_line_capability){
+ screen_optimize_lines();
+ }/* End IF */
+/*
+ * Go through each line and insure that the screen appearence is changed to
+ * match those described by the line buffer structures.
+ */
+ for(y = 0; y < term_lines; y++){
+ if(term_speed <= 2400){
+ term_flush();
+ }/* End IF */
+
+ input_pending_flag = tty_input_pending();
+ if(input_pending_flag == YES) break;
+
+/*
+ * Get pointers to the saved screen entry which tells us what the screen
+ * currently looks like, and pointers to the line buffer structure which
+ * tells us what the screen ought to look like when we are done.
+ */
+ lp = saved_screen + y;
+ osp = lp->buffer;
+ his_lp = lp->companion;
+/*
+ * If there is no line buffer structure, the line ought to appear blank on
+ * the screen. If it already is blank, no sweat, otherwise we want to do
+ * a clear to end of line function. If the saved_screen sequence number
+ * is zero, this is a flag that the line is already clear.
+ */
+ if(his_lp == NULL){
+ if(lp->sequence == 0) continue;
+ for(x = 0; x < term_columns; x++,osp++){
+ if((*osp & SCREEN_M_DATA) != ' '){
+ if(cury != y || curx > x) term_goto(x,y);
+ if(cur_mode & SCREEN_M_REVERSE){
+ term_standend();
+ cur_mode &= ~SCREEN_M_REVERSE;
+ }/* End IF */
+ term_clrtoeol();
+ while(x < term_columns){
+ *osp++ = ' ';
+ x += 1;
+ }/* End While */
+ }/* End IF */
+ }/* End FOR */
+ lp->sequence = 0;
+ continue;
+ }/* End IF */
+/*
+ * If the line buffer sequence number is less than that of the saved screen
+ * sequence number, we know that this line has not been changed, and thus we
+ * don't even have to check to see whether they match.
+ */
+ if(his_lp->fmt_sequence <= lp->sequence) continue;
+ lp->sequence = screen_sequence;
+ sp = his_lp->fmt_buffer;
+ his_lp->fmt_visible_line_position = y;
+/*
+ * Well, changes have been made, so we have to go through the line on a byte
+ * by byte basis comparing character positions.
+ */
+ for(x = 0; x < term_columns; x++,sp++,osp++){
+/*
+ * If the characters are the same we don't have to take any special action
+ * unless it is a newline in which case sometimes we have special things to do
+ */
+ if((*sp & SCREEN_M_DATA) != '\n' && *osp == *sp){
+ continue;
+ }/* End If */
+/*
+ * If it IS a newline, we want to make sure that the screen shows blanks
+ * for the rest of this line. If it does not, we have to do a clear to
+ * end of line operation and update the saved screen database to reflect
+ * this.
+ */
+ if((*sp & SCREEN_M_DATA) == '\n'){
+ for(; x < term_columns; x++,osp++){
+ if(*osp == ' ') continue;
+ if(curx != x || cury != y) term_goto(x,y);
+ if(cur_mode & SCREEN_M_REVERSE){
+ term_standend();
+ cur_mode &= ~SCREEN_M_REVERSE;
+ }/* End IF */
+ term_clrtoeol();
+ for(; x < term_columns; x++){
+ *osp++ = ' ';
+ }/* End FOR */
+ }/* End FOR */
+ break;
+ }/* End IF */
+/*
+ * Copy the byte into our database to reflect the new appearance of the screen
+ */
+ *osp = *sp;
+ pos_flag = 1;
+/*
+ * If the current position of the cursor is not the position of the character
+ * we want to update, we have to position to that place. If we are on the
+ * wrong line, or if we are already to the right of the desired position,
+ * we have to do a full position escape sequence.
+ */
+ if(cury != y || curx > x){
+ term_goto(x,y);
+ pos_flag = 0;
+ }/* End IF */
+/*
+ * If we are not positioned yet, check to see if we are only a few positions
+ * to the left of where we want to be. If this is the case, it is faster to
+ * output the actual characters than to generate a full cursor motion escape
+ * sequence.
+ */
+ if(pos_flag && x - curx < 5){
+ while(curx < x){
+ if((his_lp->fmt_buffer[curx] & SCREEN_M_REVERSE) !=
+ (cur_mode & SCREEN_M_REVERSE)) break;
+ term_putc(his_lp->fmt_buffer[curx] & SCREEN_M_DATA);
+ curx++;
+ }/* End While */
+ pos_flag = 0;
+ }/* End IF */
+/*
+ * If we get to here and the position is not correct, no shortcuts helped so
+ * we have to output the full escape sequence to position the cursor
+ */
+ if(curx != x || cury != y) term_goto(x,y);
+/*
+ * Check to see that we are in the correct mode (standout or not). If not,
+ * we have to change it before we output the data byte
+ */
+ if((*sp & SCREEN_M_REVERSE) != (cur_mode & SCREEN_M_REVERSE)){
+ if(*sp & SCREEN_M_REVERSE){
+ term_standout();
+ cur_mode |= SCREEN_M_REVERSE;
+ }/* End IF */
+ else {
+ term_standend();
+ cur_mode &= ~SCREEN_M_REVERSE;
+ }/* End Else */
+ }/* End IF */
+/*
+ * Output the data byte and record that that changes the current x position
+ */
+ term_putc(*sp & SCREEN_M_DATA);
+ curx++;
+
+ }/* End FOR */
+ }/* End FOR */
+
+/*
+ * We don't want to leave the terminal in reverse video between screen updates,
+ * so if we are in reverse video mode, put it back to non-reverse.
+ */
+ if(cur_mode & SCREEN_M_REVERSE){
+ term_standend();
+ cur_mode &= ~SCREEN_M_REVERSE;
+ }/* End IF */
+
+/*
+ * Make sure that the cursor gets left in the position that indicates the
+ * "dot" position in the edit buffer.
+ */
+ if(curx != cursor_x || cury != cursor_y){
+ term_goto(cursor_x,cursor_y);
+ }/* End IF */
+
+/*
+ * Force this all to be written to the terminal, and increment the sequence
+ * number
+ */
+ term_flush();
+ screen_gc_format_lines();
+ screen_sequence += 1;
+
+}/* End Routine */
+
+
+
+/* SCREEN_OPTIMIZE_LINES - Optimize output with insert/delete line operations
+ *
+ * Function:
+ *
+ * This routine looks for left over mappings between our saved screen
+ * array and the formatted format_line structures. Old mappings can
+ * tell us how lines that are still going to be visible have moved.
+ */
+void
+screen_optimize_lines()
+{
+register int y;
+int vlp;
+register struct screen_line *lp;
+int blk_delete_count;
+int blk_insert_count;
+int companion_count;
+int offset;
+
+ PREAMBLE();
+
+ companion_count = 0;
+
+/*
+ * Search for a line which is known to be out of place
+ */
+ for(y = 0; y < buffer_li; y++){
+ lp = &saved_screen[y];
+ if(lp->companion == NULL) continue;
+ if((vlp = lp->companion->fmt_visible_line_position) == -1) continue;
+ if(vlp != y) break;
+ companion_count += 1;
+ break;
+ }/* End FOR */
+
+ if(y == buffer_li){
+ if(companion_count == 0) screen_optimize_by_pattern_matching();
+ return;
+ }/* End IF */
+
+/*
+ * Search for delete line opportunities
+ */
+ offset = 0;
+ for(y = 0; y < term_lines; y++){
+ lp = &saved_screen[y];
+ if(lp->companion == NULL) continue;
+ if((vlp = lp->companion->fmt_visible_line_position) == -1) continue;
+ vlp += offset;
+ if(vlp == y) continue;
+
+ if(vlp > y){
+ blk_delete_count = vlp - y;
+ term_delete_line(y-offset,blk_delete_count);
+ screen_account_for_delete_line(y-offset,blk_delete_count);
+ continue;
+ }/* End IF */
+
+ if(vlp < y){
+ offset += y - vlp;
+ }/* End IF */
+
+ }/* End FOR */
+/*
+ * Search for insert line opportunities
+ */
+ for(y = 0; y < term_lines; y++){
+ lp = &saved_screen[y];
+ if(lp->companion == NULL) continue;
+ if((vlp = lp->companion->fmt_visible_line_position) == -1) continue;
+ if(vlp == y) continue;
+
+ if(vlp > y){
+ void screen_message();
+ screen_message("insert_line confused");
+ }/* End IF */
+
+ if(vlp < y){
+ blk_insert_count = y - vlp;
+ term_insert_line(vlp,blk_insert_count);
+ screen_account_for_insert_line(vlp,blk_insert_count);
+ }/* End IF */
+
+ }/* End FOR */
+
+}/* End Routine */
+
+
+
+/* SCREEN_ACCOUNT_FOR_DELETE_LINE - Update screen database
+ *
+ * Function:
+ *
+ * This routine is called when a delete line sequence is sent to
+ * the terminal. It adjusts the screen database to reflect what
+ * has happened to the terminal.
+ */
+void
+screen_account_for_delete_line(y,count)
+int y;
+int count;
+{
+register int i,x;
+register struct screen_line *top_lp,*bottom_lp;
+register struct format_line *slp;
+register short *sp;
+
+ PREAMBLE();
+
+/*
+ * The result of a delete line operation will be that starting at the
+ * location it was issued, lines will be replaced by the contents of
+ * lines lower down on the screen.
+ */
+ top_lp = &saved_screen[y];
+ bottom_lp = top_lp + count;
+
+ for(i = y + count; i < term_lines; i++,top_lp++,bottom_lp++){
+ sp = top_lp->buffer;
+ top_lp->buffer = bottom_lp->buffer;
+ bottom_lp->buffer = sp;
+
+ top_lp->sequence = bottom_lp->sequence;
+ }/* End FOR */
+
+ top_lp = saved_screen;
+ for(i = 0; i < term_lines; i++,top_lp++){
+ if((slp = top_lp->companion) == NULL) continue;
+ if(slp->fmt_visible_line_position >= y){
+ slp->fmt_visible_line_position -= count;
+ }/* End IF */
+ if(slp->fmt_visible_line_position < 0){
+ slp->fmt_visible_line_position = -1;
+ }/* End IF */
+ }/* End FOR */
+
+ top_lp = &saved_screen[term_lines - count];
+ for(i = term_lines - count; i < term_lines; i++,top_lp++){
+
+ top_lp->sequence = 0;
+
+ sp = top_lp->buffer;
+ for(x = 0; x < term_columns; x++){
+ *sp++ = ' ';
+ }/* End FOR */
+
+ }/* End FOR */
+
+}/* End Routine */
+
+/* SCREEN_ACCOUNT_FOR_INSERT_LINE - Update screen database
+ *
+ * Function:
+ *
+ * This routine is called when an insert line sequence is sent to
+ * the terminal. It adjusts the screen database to reflect what
+ * has happened to the terminal appearence.
+ */
+void
+screen_account_for_insert_line(y,count)
+int y;
+int count;
+{
+register int i,x;
+register struct screen_line *top_lp,*bottom_lp;
+register struct format_line *slp;
+register short *sp;
+
+ PREAMBLE();
+
+ top_lp = &saved_screen[term_lines - count - 1];
+ bottom_lp = &saved_screen[term_lines - 1];
+
+ for(i = term_lines - count - 1; i >= y; i--,top_lp--,bottom_lp--){
+ sp = bottom_lp->buffer;
+ bottom_lp->buffer = top_lp->buffer;
+ top_lp->buffer = sp;
+
+ bottom_lp->sequence = top_lp->sequence;
+
+ }/* End FOR */
+
+ top_lp = saved_screen;
+ for(i = 0; i < term_lines; i++,top_lp++){
+ if((slp = top_lp->companion) == NULL) continue;
+ if(slp->fmt_visible_line_position >= y){
+ slp->fmt_visible_line_position += count;
+ }/* End IF */
+ if(slp->fmt_visible_line_position >= term_lines){
+ slp->fmt_visible_line_position = -1;
+ }/* End IF */
+ }/* End FOR */
+
+ top_lp = &saved_screen[y];
+ for(i = 0; i < count; i++,top_lp++){
+
+ top_lp->sequence = 0;
+
+ sp = top_lp->buffer;
+ for(x = 0; x < term_columns; x++){
+ *sp++ = ' ';
+ }/* End FOR */
+ }/* End FOR */
+
+}/* End Routine */
+
+
+
+/* SCREEN_OPTIMIZE_BY_PATTERN_MATCHING - More insert/delete line optimizations
+ *
+ * Function:
+ *
+ * This routine is called previous to the brute force screen repaint,
+ * but after the first crack at insert/delete line optimization. This
+ * routine looks for patterns of lines which indicate insert/delete
+ * line can save us some output.
+ */
+void
+screen_optimize_by_pattern_matching()
+{
+register int x,y,i,c;
+register struct screen_line *lp;
+register struct format_line *his_lp;
+register short *sp,*osp;
+char stuff_flag;
+int old_check[SCREEN_MAX_LINES],new_check[SCREEN_MAX_LINES];
+int first_change,last_change;
+
+ PREAMBLE();
+
+ stuff_flag = 0;
+ for(y = 0; y < buffer_li; y++){
+
+/*
+ * Get pointers to the saved screen entry which tells us what the screen
+ * currently looks like, and pointers to the line buffer structure which
+ * tells us what the screen ought to look like when we are done.
+ */
+ lp = saved_screen + y;
+ osp = lp->buffer;
+ his_lp = lp->companion;
+/*
+ * If there is no line buffer structure, the line ought to appear blank on
+ * the screen. If it already is blank, no sweat, otherwise we want to do
+ * a clear to end of line function.
+ */
+ new_check[y] = old_check[y] = 0;
+ if(his_lp && his_lp->fmt_sequence <= lp->sequence) continue;
+ if(his_lp == NULL && lp->sequence == 0) continue;
+/*
+ * Calculate the checksum for the data currently showing on the screen
+ */
+ for(x = 0; x < term_columns; x++,osp++){
+ if((c = *osp & SCREEN_M_DATA) != ' '){
+ old_check[y] = (old_check[y] << 1) + c;
+ stuff_flag = 1;
+ }/* End IF */
+ }/* End FOR */
+
+ if(his_lp == NULL) continue;
+ sp = his_lp->fmt_buffer;
+/*
+ * Calculate the checksum for the data which we want to be showing on the
+ * screen.
+ */
+ for(x = 0; x < term_columns; x++,sp++){
+ c = *sp & SCREEN_M_DATA;
+ if(c == ' ') continue;
+ if(c == '\n') break;
+ new_check[y] = (new_check[y] << 1) + c;
+ stuff_flag = 1;
+ }/* End FOR */
+
+ }/* End FOR */
+
+/*
+ * If no changes have been made, just return
+ */
+ if(!stuff_flag) return;
+
+/*
+ * Determine which region of the screen contains lines which have changed
+ */
+ first_change = last_change = -1;
+ for(y = 0; y < buffer_li; y++){
+ if(old_check[y] == new_check[y]) continue;
+ first_change = y;
+ break;
+ }/* End FOR */
+
+/*
+ * If checksum arrays seem to be identical or only one line changed, then
+ * just return and don't attempt further optimization.
+ */
+ if(first_change == -1) return;
+
+ for(y = buffer_li - 1; y >= first_change; y--){
+ if(old_check[y] == new_check[y]) continue;
+ last_change = y;
+ break;
+ }/* End FOR */
+
+ if(first_change == last_change) return;
+
+/*
+ * Here is the code which tries for a delete-line optimization
+ */
+ for(i = 1; i < last_change - first_change - 3; i++){
+ if(old_check[first_change + i] == new_check[first_change]){
+ for(y = first_change; y < last_change - i; y++){
+ if(old_check[y + i] != new_check[y]) break;
+ }/* End FOR */
+ if(y == (last_change - i)){
+
+ term_delete_line(first_change,i);
+ screen_account_for_delete_line(first_change,i);
+ term_insert_line(last_change+1-i,i);
+ screen_account_for_insert_line(last_change+1-i,i);
+
+ return;
+
+ }/* End IF */
+ }/* End IF */
+ }/* End FOR */
+
+/*
+ * Here is the code which tries for a insert-line optimization
+ */
+ for(i = 1; i < last_change - first_change - 3; i++){
+ if(old_check[first_change] == new_check[first_change + i]){
+ for(y = first_change; y < last_change - i; y++){
+ if(old_check[y] != new_check[y + i]) break;
+ }/* End FOR */
+
+ if(y == (last_change - i)){
+ term_delete_line(last_change+1-i,i);
+ screen_account_for_delete_line(last_change+1-i,i);
+ term_insert_line(first_change,i);
+ screen_account_for_insert_line(first_change,i);
+ break;
+ }/* End IF */
+
+ }/* End IF */
+ }/* End FOR */
+
+}/* End Routine */
+
+
+
+/* SCREEN_ECHO - Echo the character
+ *
+ * Function:
+ *
+ * This routine allows the parser to hand us input characters which need
+ * to be echoed. The reason this is done here instead of by the normal
+ * screen formatting routines is that some characters are echoed in a
+ * different way than they are normally displayed. The most notable
+ * example is newline which just indicates end of line in the normal
+ * buffer output, but which gets echoed as '<CR>' when it is an echo
+ * operation.
+ */
+void
+screen_echo(data)
+char data;
+{
+register int i;
+
+ PREAMBLE();
+
+/*
+ * If we are getting close to the right edge of the screen, we remove the
+ * first 3/4 of the echo line and just redisplay the last 1/4 to make room
+ * for future echo characters.
+ */
+ if(echo_cnt > (term_columns - 4)){
+ for(i = 0; i < (term_columns / 4); i++){
+ echo_line.fmt_buffer[ 1 + i ] =
+ echo_line.fmt_buffer[ 1 + echo_cnt - (term_columns / 4) + i ];
+ }/* End FOR */
+ echo_cnt = i;
+ for(; i < term_columns-2; i++){
+ echo_line.fmt_buffer[ 1 + i ] = ' ';
+ }/* End FOR */
+ }/* End IF */
+
+/*
+ * Dispatch the byte to determine how it should be formatted. NULL is a
+ * special case which is used to flag the fake cursor position on the
+ * echo line. This is shown by outputing a reverse-video space to the
+ * position.
+ */
+ switch(data){
+ case '\0':
+
+/*
+ * If we are on a high-speed line, do the reverse-video space trick to
+ * show a non-blinking cursor.
+ */
+ if(term_speed > 1200){
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = ' ' | SCREEN_M_REVERSE;
+ echo_line.fmt_buffer[ 2 + echo_cnt ] = ' ';
+ echo_line.fmt_buffer[ 3 + echo_cnt ] = '\n';
+ }/* End IF */
+/*
+ * Otherwise, if we are on a low speed line, just insert a carriage return
+ * so that the rest of the line is shown as blanks.
+ */
+ else {
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = '\n';
+ }/* End IF */
+
+ echo_line.fmt_sequence = screen_sequence;
+ return;
+
+ case ESCAPE:
+ data = '$';
+ break;
+ case '\n':
+ for(i = 0; i < strlen("<CR>"); i++){
+ screen_echo("<CR>"[i]);
+ }/* End FOR */
+ return;
+ case RUBOUT:
+ return;
+/*
+ * Tabs are always echoed as a fixed length string of spaces regardless of
+ * the screen position since normal tab algorithms look weird on the echo
+ * line.
+ */
+ case '\t':
+ for(i = 0; i < strlen(" "); i++){
+ screen_echo(" "[i]);
+ }/* End FOR */
+ return;
+ default:
+ break;
+ }/* End Switch */
+
+/*
+ * If this is a control character, echo as ^byte
+ */
+ if((unsigned char)data < ' '){
+ screen_echo('^');
+ screen_echo(data + 'A' - 1);
+ return;
+ }/* End IF */
+
+/*
+ * Here if it is a normal printable character. We can just echo the character
+ * as is with no special stuff going on.
+ */
+ echo_line.fmt_buffer[ 1 + echo_cnt++ ] = data;
+ echo_line.fmt_sequence = screen_sequence;
+
+}/* End Routine */
+
+/* SCREEN_RESET_ECHO - Reset the echo line
+ *
+ * Function:
+ *
+ * This routine is called to reset the echo line according to the current
+ * list of command tokens. This is used after difficult things like rubout
+ * which would be very difficult to back out of.
+ */
+void
+screen_reset_echo(ct)
+register struct cmd_token *ct;
+{
+
+ PREAMBLE();
+
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = ' ';
+
+ while(echo_cnt){
+ echo_cnt -= 1;
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = ' ';
+ }/* End While */
+
+ while(ct){
+ if(ct->opcode == TOK_C_INPUTCHAR){
+ screen_echo(ct->input_byte);
+ }/* End IF */
+ ct = ct->next_token;
+ }/* End While */
+
+ echo_line.fmt_buffer[ 1 + echo_cnt ] = '\n';
+
+ echo_line.fmt_sequence = screen_sequence;
+
+}/* End Routine */
+
+
+
+/* SCREEN_MESSAGE - Place a message in the message line
+ *
+ * Function:
+ *
+ * This routine allows the parser to hand us messages which need to be
+ * displayed. These can be error messages generated by the parse, or
+ * they can be output messages from a user generated with the ^A command.
+ */
+void
+screen_message(string)
+register char *string;
+{
+register short *sp;
+register int i;
+register int c = 0;
+register char *mep;
+char multi_echo_buffer[32];
+
+ PREAMBLE();
+
+ message_flag = YES;
+ sp = message_line.fmt_buffer;
+ i = term_columns;
+ mep = "";
+
+ while(*mep || *string){
+ while(*mep){
+ *sp++ = c = *mep++;
+ if(i-- <= 0) goto too_wide;
+ continue;
+ }/* End While */
+
+ c = *string++;
+
+ switch(c){
+// case NULL:
+ case 0:
+ goto too_wide;
+ case ESCAPE:
+ c = '$';
+ break;
+ case '\n':
+ mep = "<CR>";
+ continue;
+ case RUBOUT:
+ mep = "<RUBOUT>";
+ continue;
+ case '\t':
+ mep = " ";
+ continue;
+ }/* End Switch */
+
+ if((unsigned char)c < ' '){
+ multi_echo_buffer[0] = '^';
+ multi_echo_buffer[1] = c + 'A' - 1;
+ multi_echo_buffer[2] = '\0';
+ mep = multi_echo_buffer;
+ continue;
+ }/* End IF */
+
+ *sp++ = c;
+ if(i-- <= 0) goto too_wide;
+ }/* End While */
+
+too_wide:
+ if(c != '\n' && i > 0) *sp++ = '\n';
+ message_line.fmt_sequence = screen_sequence;
+
+}/* End Routine */
+
+/* ERROR_MESSAGE - Place an error message in the message line
+ *
+ * Function:
+ *
+ * This routine allows the parser to hand us error messages
+ * which need to be displayed. In addition, the BELL character
+ * is output to get the humans attention.
+ */
+void
+error_message(string)
+char *string;
+{
+ PREAMBLE();
+
+ screen_message(string);
+ ring_audible_bell = 1;
+
+}/* End Routine */
+
+/* SCREEN_RESET_MESSAGE - Reset the message line
+ *
+ * Function:
+ *
+ * This routine is called to reset the message line to null
+ */
+void
+screen_reset_message()
+{
+register int i;
+register short *sp;
+
+ PREAMBLE();
+
+ if(message_flag == NO) return;
+ message_flag = NO;
+ sp = message_line.fmt_buffer;
+
+ for(i = 0; i < term_columns; i++){
+ *sp++ = ' ';
+ }/* End FOR */
+
+ message_line.fmt_sequence = screen_sequence;
+
+}/* End Routine */
+
+/* SCREEN_SAVE_CURRENT_MESSAGE - Copy out the current message
+ *
+ * Function:
+ *
+ * This routine copies out the current message up to a certain length.
+ */
+void
+screen_save_current_message(message_save_buff,message_save_max_length)
+char *message_save_buff;
+int message_save_max_length;
+{
+int i;
+short *sp;
+
+ PREAMBLE();
+
+ sp = message_line.fmt_buffer;
+ i = term_columns;
+ if(i >= message_save_max_length) i = message_save_max_length - 1;
+ while(i-- > 0){
+ if((*sp & SCREEN_M_DATA) == '\n') break;
+ *message_save_buff++ = *sp++ & SCREEN_M_DATA;
+ }
+ *message_save_buff = '\0';
+
+}/* End Routine */
+
+
+
+/* SCREEN_LABEL_LINE - Routine to change the contents of the label line
+ *
+ * Function:
+ *
+ * This routine is called to change the label line
+ */
+int
+screen_label_line(buffer,string,field)
+struct buff_header *buffer;
+char *string;
+int field;
+{
+register short *sp;
+register char *cp;
+register int i;
+register int column;
+register struct window *wptr;
+
+ PREAMBLE();
+
+/*
+ * If the field specified is out of range, punt
+ */
+ if(field < 0 || field >= SCREEN_MAX_LABEL_FIELDS){
+ screen_message("Illegal field specified to label line\n");
+ return(FAIL);
+ }/* End IF */
+
+/*
+ * We want to modify the label in any window which is displaying this buffer.
+ * There could be several windows with the same buffer displayed...
+ */
+ for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){
+ if(wptr->win_buffer != buffer) continue;
+/*
+ * If no space has been allocated for this field yet, we need to call the
+ * allocator. Notice the overkill in that each field gets more than enough
+ * memory.
+ */
+ if(wptr->win_label_field_contents[field] == NULL){
+ wptr->win_label_field_contents[field] =
+ tec_alloc(TYPE_C_LABELFIELD,SCREEN_NOMINAL_LINE_WIDTH);
+ if(wptr->win_label_field_contents[field] == NULL){
+ return(FAIL);
+ }/* End IF */
+ *(wptr->win_label_field_contents[field]) = '\0';
+ }/* End IF */
+/*
+ * If the current contents of the field are already what we want, we don't
+ * have to change a thing.
+ */
+ if(strcmp(string,wptr->win_label_field_contents[field]) == 0) continue;
+ (void) strcpy(wptr->win_label_field_contents[field],string);
+/*
+ * Figure out which column the field begins on. This changes as fields change,
+ * so it must be computed each time a change happens.
+ */
+ for(i = 0 , column = 0; i < field; i++){
+ if(wptr->win_label_field_contents[i] == NULL) continue;
+ column += strlen(wptr->win_label_field_contents[i]) + 1;
+ }/* End FOR */
+/*
+ * Now we have to update the label line from this column onward until we
+ * reach the end of the final field.
+ */
+ sp = &wptr->win_label_line.fmt_buffer[column];
+ for(i = field; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ cp = wptr->win_label_field_contents[i];
+ if(cp == NULL) continue;
+ while(*cp){
+ if(column >= term_columns) break;
+ *sp++ = *cp++ | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End While */
+ if(column >= term_columns) break;
+ *sp++ = ' ' | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End FOR */
+/*
+ * Blank fill to the end of the screen incase we shrank the label line.
+ */
+ while(column < term_columns){
+#ifdef TERMCAP
+ *sp++ = termcap_sg ? '-' : ' ' | SCREEN_M_REVERSE;
+#endif
+#ifdef TERMINFO
+ *sp++ = (terminfo_magic_cookie_glitch > 0) ?
+ '-' : ' ' | SCREEN_M_REVERSE;
+#endif
+ column += 1;
+ }/* End While */
+/*
+ * Insure that the next screen refresh knows this line has changed
+ */
+ wptr->win_label_line.fmt_sequence = screen_sequence;
+
+ }/* End FOR */
+
+ return( SUCCESS );
+
+}/* End Routine */
+
+
+
+/* SCREEN_LABEL_WINDOW - Routine to set the window number field
+ *
+ * Function:
+ *
+ * This routine is called to modify the TECONAME field of the
+ * label lines to reflect the window number.
+ */
+int
+screen_label_window()
+{
+register short *sp;
+register char *cp;
+register int i;
+register int column;
+register struct window *wptr;
+char tmp_buffer[SCREEN_NOMINAL_LINE_WIDTH];
+int field = LABEL_C_TECONAME;
+
+ PREAMBLE();
+
+/*
+ * We want to check the label in every window
+ */
+ for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){
+/*
+ * If no space has been allocated for this field yet, we need to call the
+ * allocator. Notice the overkill in that each field gets more than enough
+ * memory.
+ */
+ if(wptr->win_label_field_contents[field] == NULL){
+ wptr->win_label_field_contents[field] =
+ tec_alloc(TYPE_C_LABELFIELD,SCREEN_NOMINAL_LINE_WIDTH);
+ if(wptr->win_label_field_contents[field] == NULL){
+ return(FAIL);
+ }/* End IF */
+ *(wptr->win_label_field_contents[field]) = '\0';
+ }/* End IF */
+/*
+ * If the current contents of the field are already what we want, we don't
+ * have to change a thing.
+ */
+ sprintf(tmp_buffer," TECO-%d",wptr->win_window_number);
+ if(strcmp(tmp_buffer,wptr->win_label_field_contents[field]) == 0){
+ continue;
+ }/* End IF */
+
+ (void) strcpy(wptr->win_label_field_contents[field],tmp_buffer);
+
+/*
+ * Now we have to update the label line from this column onward until we
+ * reach the end of the final field.
+ */
+ column = 0;
+ sp = &wptr->win_label_line.fmt_buffer[column];
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ cp = wptr->win_label_field_contents[i];
+ if(cp == NULL) continue;
+ while(*cp){
+ if(column >= term_columns) break;
+ *sp++ = *cp++ | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End While */
+ if(column >= term_columns) break;
+ *sp++ = ' ' | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End FOR */
+/*
+ * Blank fill to the end of the screen incase we shrank the label line.
+ */
+ while(column < term_columns){
+ *sp++ = ' ' | SCREEN_M_REVERSE;
+ column += 1;
+ }/* End While */
+/*
+ * Insure that the next screen refresh knows this line has changed
+ */
+ wptr->win_label_line.fmt_sequence = screen_sequence;
+
+ }/* End FOR */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_FORMAT_WINDOWS - Generate display contents for all windows
+ *
+ * Function:
+ *
+ * This is the main entry point to the screen formatting function.
+ * It causes screen format_lines to be generated for all the buffers
+ * that are currently visible on the screen.
+ */
+void
+screen_format_windows()
+{
+register struct window *wptr;
+
+ PREAMBLE();
+
+ for(wptr = window_list; wptr != NULL; wptr = wptr->win_next_window){
+ screen_display_window(wptr);
+ }/* End FOR */
+
+}/* End Routine */
+
+/* SCREEN_REFORMAT_WINDOWS - Reformat windows (because tab size changed)
+ *
+ * Function:
+ *
+ * This entry point is called when the format buffers of all the
+ * displayed windows need to be regenerated. The current case in
+ * mind is when the user changes the tab stops - this causes all
+ * the format lines to be obsolete.
+ */
+void
+screen_reformat_windows()
+{
+register int i;
+register struct screen_line *lp;
+
+ PREAMBLE();
+
+/*
+ * The screen_gc_format_line is going to scavenge all the lines because
+ * we're making them look not-used here. However, we still go ahead and
+ * clobber the line_buffer's pointer, because we don't like the idea of
+ * having hanging pointers pointed at our screen lines.
+ */
+ if(saved_screen_valid){
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ if(lp->companion){
+ if(lp->companion->fmt_saved_line != lp){
+ fprintf(
+ stderr,
+ "lp->companion 0x%x != ->fmt_saved_line 0x%x\n",
+ (unsigned int)lp->companion,
+ (unsigned int)lp->companion->fmt_saved_line
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ if( lp->companion->fmt_permanent ) continue;
+ lp->companion->fmt_saved_line = NULL;
+ lp->companion = NULL;
+ }/* End IF */
+ }/* End FOR */
+ }/* End IF */
+
+ screen_gc_format_lines();
+
+}/* End Routine */
+
+/* SCREEN_DISPLAY_WINDOW - Generate display for a window
+ *
+ * Function:
+ *
+ * This routine is called to build up the format line descriptor
+ * structures which represent the edit buffer displayed in the
+ * specified window.
+ */
+int
+screen_display_window(wptr)
+register struct window *wptr;
+{
+struct buff_header *hbp;
+struct buff_line *tlbp,*blbp;
+struct format_line *tsbp,*bsbp;
+struct screen_line *sp;
+register int i;
+int lines;
+int anchor_line;
+char saw_dot;
+
+ PREAMBLE();
+
+/*
+ * Find a line in our saved screen array that still maps to a buffer location
+ */
+ sp = saved_screen + wptr->win_y_base;
+ anchor_line = -1;
+ for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){
+ if(sp->companion == NULL) continue;
+ if(sp->companion->fmt_owning_buffer != wptr->win_buffer) continue;
+ anchor_line = i;
+ break;
+ }/* End FOR */
+
+ if(anchor_line < 0){
+ return(screen_rebuild_from_scratch(wptr));
+ }/* End IF */
+
+/*
+ * Now walk the top back
+ */
+ tsbp = sp->companion;
+ tlbp = tsbp->fmt_buffer_line;
+ while(i > wptr->win_y_base){
+
+/*
+ * If there is a format line in front of the one we are on, just back
+ * up to it.
+ */
+ if(tsbp->fmt_prev_line){
+ tsbp = tsbp->fmt_prev_line;
+ i -= 1;
+ continue;
+ }/* End IF */
+/*
+ * Else if there is a buffer line in front of us, get to the final
+ * format line associated with it.
+ */
+ if(tlbp->prev_line != NULL){
+ tlbp = tlbp->prev_line;
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ if(tsbp == NULL){
+ if(screen_format_buff_line(wptr,tlbp) == FAIL) return(FAIL);
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ }/* End IF */
+ while(tsbp->fmt_next_line) tsbp = tsbp->fmt_next_line;
+ i -= 1;
+ continue;
+ }/* End IF */
+/*
+ * If we got here, that means that before we could work back up to the old
+ * top line, we hit the begining of the buffer. Give up.
+ */
+ return(screen_rebuild_from_scratch(wptr));
+
+ }/* End While */
+
+/*
+ * If we make it to here, we have worked back to the top of the screen, now
+ * we want to work down to the bottom of the screen.
+ */
+ lines = 1;
+ blbp = tlbp;
+ bsbp = tsbp;
+
+/*
+ * Determine the format_line that dot lies on so that as we work forward we can
+ * keep an eye out for it.
+ */
+ hbp = wptr->win_buffer;
+ buff_find_line(hbp,hbp->dot);
+ screen_find_dot(wptr);
+ saw_dot = 0;
+
+ while(lines <= wptr->win_y_size){
+
+/*
+ * If we see the line that dot is on, remember it. The one case when we don't
+ * do this is if it is the top line of the screen. The reason for this is to
+ * avoid over-optimizing such that the user makes changes but never sees them
+ * because the changes are above the top line of the screen.
+ */
+ if(bsbp == wptr->win_dot_format_line && lines != 1) saw_dot = 1;
+
+/*
+ * If there is a format line below our current bottom one, simply go
+ * forward to it.
+ */
+ if(bsbp->fmt_next_line){
+ bsbp = bsbp->fmt_next_line;
+ lines += 1;
+ continue;
+ }/* End IF */
+/*
+ * If we have to go on to the next buffer line, but there is not one there,
+ * that means that we hit the end of the buffer.
+ */
+ if(blbp->next_line == NULL){
+ return(screen_rebuild_from_scratch(wptr));
+ }/* End IF */
+/*
+ * Else, if there is another buffer line after this, go to the first
+ * format line associated with it.
+ */
+ if(blbp->next_line != NULL){
+ blbp = blbp->next_line;
+ bsbp = screen_find_window_format_line(wptr,blbp);
+ if(bsbp == NULL){
+ if(screen_format_buff_line(wptr,blbp) == FAIL) return(FAIL);
+ bsbp = screen_find_window_format_line(wptr,blbp);
+ }/* End IF */
+ lines += 1;
+ }/* End IF */
+
+ }/* End While */
+
+/*
+ * Test to see whether we ever saw dot. If not, we need to do the scratch
+ * screen rebuild.
+ */
+ if(curwin == wptr && saw_dot == 0){
+ return(screen_rebuild_from_scratch(wptr));
+ }/* End IF */
+
+/*
+ * Now we can start setting up the screen appearance
+ */
+ sp = saved_screen + wptr->win_y_base;
+
+ for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){
+ if(tlbp == NULL){
+ if(sp->companion) sp->companion->fmt_saved_line = NULL;
+ sp->companion = NULL;
+ continue;
+ }/* End IF */
+
+ if(sp->companion != tsbp){
+ if(sp->companion) sp->companion->fmt_saved_line = NULL;
+ sp->companion = NULL;
+ if(tsbp->fmt_saved_line) tsbp->fmt_saved_line->companion = NULL;
+ tsbp->fmt_saved_line = NULL;
+ sp->companion = tsbp;
+ tsbp->fmt_saved_line = sp;
+ tsbp->fmt_sequence = screen_sequence;
+ }/* End IF */
+
+ if(curwin == wptr && tsbp == wptr->win_dot_format_line){
+ cursor_y = i;
+ cursor_x = wptr->win_dot_screen_offset;
+ }/* End IF */
+
+ tsbp = tsbp->fmt_next_line;
+ if(tsbp == NULL){
+ tlbp = tlbp->next_line;
+ if(tlbp){
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ }/* End IF */
+ }/* End IF */
+ }/* End FOR */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_REBUILD_FROM_SCRATCH - Things have changed a lot, just rebuild it
+ *
+ * Function:
+ *
+ * This routine is called to build up the format line descriptor
+ * structures which represent the current edit buffer.
+ */
+int
+screen_rebuild_from_scratch(wptr)
+register struct window *wptr;
+{
+struct buff_header *hbp;
+struct buff_line *tlbp,*blbp;
+struct format_line *tsbp,*bsbp;
+struct screen_line *scrbp;
+register int i;
+int lines,olines;
+
+ PREAMBLE();
+
+/*
+ * Find the buffer line that dot is on, and then the format_line that
+ * it is on.
+ */
+ hbp = wptr->win_buffer;
+ tlbp = blbp = buff_find_line(hbp,hbp->dot);
+ if(screen_find_dot(wptr) == FAIL) return(FAIL);
+ tsbp = bsbp = wptr->win_dot_format_line;
+
+ lines = 1;
+
+/*
+ * Now walk the top back and the bottom forward
+ */
+ while(lines < wptr->win_y_size){
+ olines = lines;
+
+/*
+ * If there is a format_line in front of the one we are on, just back
+ * up to it.
+ */
+ if(tsbp->fmt_prev_line){
+ tsbp = tsbp->fmt_prev_line;
+ lines += 1;
+ }/* End IF */
+
+/*
+ * Else if there is a buffer line in front of us, get to the final
+ * format_line associated with it.
+ */
+ else if(tlbp->prev_line != NULL){
+ tlbp = tlbp->prev_line;
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ if(tsbp == NULL){
+ if(screen_format_buff_line(wptr,tlbp) == FAIL) return(FAIL);
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ }/* End IF */
+ while(tsbp && tsbp->fmt_next_line) tsbp = tsbp->fmt_next_line;
+ lines += 1;
+ }/* End IF */
+
+/*
+ * If there is a format_line below our current bottom one, simply go
+ * forward to it.
+ */
+ if(bsbp->fmt_next_line){
+ bsbp = bsbp->fmt_next_line;
+ lines += 1;
+ }/* End IF */
+
+/*
+ * Else, if there is another buffer line after this, go to the first
+ * format_line associated with it.
+ */
+ else if(blbp->next_line != NULL){
+ blbp = blbp->next_line;
+ bsbp = screen_find_window_format_line(wptr,blbp);
+ if(bsbp == NULL){
+ if(screen_format_buff_line(wptr,blbp) == FAIL) return(FAIL);
+ bsbp = screen_find_window_format_line(wptr,blbp);
+ }/* End IF */
+ lines += 1;
+ }/* End IF */
+
+/*
+ * If we havn't made any progress, then the buffer is smaller than the
+ * screen, so just display it.
+ */
+ if(lines == olines) break;
+
+ }/* End While */
+
+/*
+ * Now we can start setting up the screen appearance
+ */
+ scrbp = saved_screen + wptr->win_y_base;
+
+ for(i = wptr->win_y_base; i < wptr->win_y_end; i++,scrbp++){
+ if(tlbp == NULL){
+ if(scrbp->companion) scrbp->companion->fmt_saved_line = NULL;
+ scrbp->companion = NULL;
+ continue;
+ }/* End IF */
+
+ if(scrbp->companion != tsbp){
+ if(scrbp->companion) scrbp->companion->fmt_saved_line = NULL;
+ scrbp->companion = NULL;
+ if(tsbp->fmt_saved_line){
+ tsbp->fmt_saved_line->companion = NULL;
+ tsbp->fmt_saved_line = NULL;
+ }/* End IF */
+ scrbp->companion = tsbp;
+ tsbp->fmt_saved_line = scrbp;
+ tsbp->fmt_sequence = screen_sequence;
+ }/* End IF */
+
+ if(curwin == wptr && tsbp == wptr->win_dot_format_line){
+ cursor_y = i;
+ cursor_x = wptr->win_dot_screen_offset;
+ }/* End IF */
+
+ tsbp = tsbp->fmt_next_line;
+ if(tsbp == NULL){
+ tlbp = tlbp->next_line;
+ if(tlbp){
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ if(tsbp == NULL){
+ if(screen_format_buff_line(wptr,tlbp) == FAIL){
+ return(FAIL);
+ }/* End IF */
+ tsbp = screen_find_window_format_line(wptr,tlbp);
+ }/* End IF */
+ }/* End IF */
+ }/* End IF */
+ }/* End FOR */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_SCROLL - Move the current screen up or down specified # lines
+ *
+ * Function:
+ *
+ * Here in response to the ES command. The user can scroll the screen
+ * up and down using this command. The current edit position does not
+ * move, so it is pointless to move it such that 'dot' moves off of the
+ * screen.
+ */
+void
+screen_scroll(count)
+int count;
+{
+struct window *wptr = curwin;
+struct screen_line *sp;
+register int i;
+
+ PREAMBLE();
+
+ i = count;
+ if(i < 0) i = -i;
+ if(i > wptr->win_y_end) return;
+
+/*
+ * If the scroll is forward, move the saved lines up in the array by count
+ */
+ if(count > 0){
+ sp = &saved_screen[wptr->win_y_base];
+ for(i = wptr->win_y_base; i < wptr->win_y_end - count; i++,sp++){
+ if(sp->companion) sp->companion->fmt_saved_line = NULL;
+ sp->companion = (sp+count)->companion;
+ (sp+count)->companion = NULL;
+ if(sp->companion == NULL) continue;
+ sp->companion->fmt_saved_line = sp;
+ sp->companion->fmt_sequence = screen_sequence;
+ }/* End FOR */
+ sp = &saved_screen[wptr->win_y_end - count];
+ for(i = wptr->win_y_end - count; i < wptr->win_y_end; i++,sp++){
+ if(sp->companion == NULL) continue;
+ sp->companion->fmt_saved_line = NULL;
+ sp->companion = NULL;
+ }/* End FOR */
+ }/* End IF */
+/*
+ * Else, if the scroll is backward, move the saved lines down in the array
+ */
+ else if(count < 0){
+ sp = &saved_screen[wptr->win_y_end - 1];
+ for(i = wptr->win_y_end - 1; i >= wptr->win_y_base - count; i--,sp--){
+ if(sp->companion) sp->companion->fmt_saved_line = NULL;
+ sp->companion = (sp+count)->companion;
+ (sp+count)->companion = NULL;
+ if(sp->companion == NULL) continue;
+ sp->companion->fmt_saved_line = sp;
+ sp->companion->fmt_sequence = screen_sequence;
+ }/* End FOR */
+ sp = &saved_screen[wptr->win_y_base];
+ for(i = wptr->win_y_base; i < wptr->win_y_base - count; i++,sp++){
+ if(sp->companion == NULL) continue;
+ sp->companion->fmt_saved_line = NULL;
+ sp->companion = NULL;
+ }/* End FOR */
+ }/* End IF */
+
+}/* End Routine */
+
+
+
+/* SCREEN_GC_FORMAT_LINES - Garbage collect unused format line structures
+ *
+ * Function:
+ *
+ * This routine determines which format structures are in use and
+ * deallocates the rest of them so that we don't use an excessive
+ * number of them.
+ */
+void
+screen_gc_format_lines()
+{
+register struct format_line *sbp,*tsbp;
+register struct screen_line *sp;
+register struct buff_line *lbp;
+register int i;
+struct window *wptr;
+
+ PREAMBLE();
+
+/*
+ * First mark all the format_line structures as unused
+ */
+ sbp = format_line_alloc_list;
+ while(sbp){
+ sbp->fmt_in_use = 0;
+ sbp = sbp->fmt_next_alloc;
+ }/* End While */
+
+/*
+ * Now for each displayed line, find the format structure associated with it.
+ * Then find the line_buffer structure which owns that format structure and
+ * mark all of the format structures under it as in use.
+ */
+ for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){
+ sp = saved_screen + wptr->win_y_base;
+ for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){
+ sbp = sp->companion;
+ if(sbp == NULL) continue;
+ if(sbp->fmt_permanent) continue;
+ if(sbp->fmt_buffer_line == NULL) continue;
+ sbp =
+ screen_find_window_format_line(
+ wptr,
+ sbp->fmt_buffer_line
+ );
+ if(sbp == NULL) continue;
+ while(sbp){
+ sbp->fmt_in_use = 1;
+ sbp = sbp->fmt_next_line;
+ }/* End While */
+ }/* End FOR */
+ }/* End FOR */
+/*
+ * Now go back and deallocate any format_line structures which are not in use
+ */
+ sbp = format_line_alloc_list;
+ tsbp = NULL;
+ while(sbp){
+ if(sbp->fmt_in_use){
+ tsbp = sbp;
+ sbp = sbp->fmt_next_alloc;
+ continue;
+ }/* End IF */
+
+ lbp = sbp->fmt_buffer_line;
+
+ screen_free_window_format_lines(sbp->fmt_window_ptr,lbp);
+
+ sbp = tsbp ? tsbp : format_line_alloc_list;
+
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* SCREEN_FREE_WINDOW_FORMAT_LINES - Deallocate a window's format lines
+ *
+ * Function:
+ *
+ * This routine is called to deallocate the list of format lines
+ * linked off of a line buffer, if they are associated with the
+ * specified window.
+ */
+void
+screen_free_window_format_lines(wptr,lbp)
+struct window *wptr;
+register struct buff_line *lbp;
+{
+register struct format_line *sbp,*tsbp;
+
+ PREAMBLE();
+
+/*
+ * There are a couple cases to check for: The first situation is when
+ * the very first format_line (chained off the buff_line structure) is
+ * the one we want to clobber.
+ */
+ sbp = lbp->format_line;
+ if(sbp == NULL) return;
+
+ if(sbp->fmt_window_ptr == wptr){
+ format_lines_invalid = 1;
+ tsbp = sbp->fmt_next_window;
+ lbp->format_line = tsbp;
+ if(tsbp) tsbp->fmt_prev_window = NULL;
+ sbp->fmt_next_window = NULL;
+ screen_free_format_lines(sbp);
+ format_lines_invalid = 0;
+ return;
+ }/* End IF */
+
+/*
+ * Here if it's not the head of the list. We want to keep the forward
+ * and backward pointers consistent, so we need to treat this case a
+ * little differently.
+ */
+ while((tsbp = sbp->fmt_next_window)){
+ if(tsbp->fmt_window_ptr == wptr){
+ format_lines_invalid = 1;
+ sbp->fmt_next_window = tsbp->fmt_next_window;
+ if(tsbp->fmt_next_window){
+ tsbp->fmt_next_window->fmt_prev_window = sbp;
+ }/* End IF */
+
+ tsbp->fmt_next_window = tsbp->fmt_prev_window = NULL;
+ screen_free_format_lines(tsbp);
+ format_lines_invalid = 0;
+ return;
+ }/* End IF */
+
+ sbp = tsbp;
+
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* SCREEN_FREE_FORMAT_LINES - Deallocate a list of format lines
+ *
+ * Function:
+ *
+ * This routine will clean up all the storage associated with a list of
+ * format lines. It is called with the address of the head of the list,
+ * and it will continue on down until the entire list is cleaned up.
+ * It will also take care of cleaning up any companion pointers from the
+ * screen array.
+ */
+void
+screen_free_format_lines(sbp)
+register struct format_line *sbp;
+{
+register struct format_line *osbp;
+register struct format_line *next_win;
+ PREAMBLE();
+
+/*
+ * Loop on down the list till we hit the tail
+ */
+ while(sbp){
+
+/*
+ * If this structure is tied to one of the current screen lines, break the
+ * connection.
+ */
+ if(sbp->fmt_saved_line){
+ if(sbp->fmt_saved_line->companion != sbp){
+ fprintf(
+ stderr,
+ "sbp->fmt_saved_line 0x%x != sbp->...companion 0x%x\n",
+ (unsigned int)sbp->fmt_saved_line,
+ (unsigned int)sbp->fmt_saved_line->companion
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ sbp->fmt_saved_line->companion = NULL;
+ sbp->fmt_saved_line = NULL;
+ }/* End IF */
+
+ next_win = sbp->fmt_next_window;
+ if(next_win){
+ sbp->fmt_next_window = NULL;
+ sbp->fmt_prev_window = NULL;
+ if(sbp->fmt_next_line){
+ sbp->fmt_next_line->fmt_next_window = next_win;
+ }/* End IF */
+ }/* End IF */
+/*
+ * Take it off the head of the list and place it on the free list.
+ */
+ sbp->fmt_magic = MAGIC_FORMAT_LOOKASIDE;
+ osbp = sbp;
+ sbp = sbp->fmt_next_line;
+ if(sbp == NULL) sbp = next_win;
+ osbp->fmt_next_line = format_line_free_list;
+ format_line_free_list = osbp;
+
+/*
+ * Remove it from the allocated list. This list is used to find all
+ * the format_line structures which are outstanding at any given time.
+ */
+ if(osbp->fmt_prev_alloc){
+ osbp->fmt_prev_alloc->fmt_next_alloc = osbp->fmt_next_alloc;
+ }/* End IF */
+
+ else if(format_line_alloc_list == osbp){
+ format_line_alloc_list = osbp->fmt_next_alloc;
+ }/* End Else */
+
+ if(osbp->fmt_next_alloc){
+ osbp->fmt_next_alloc->fmt_prev_alloc = osbp->fmt_prev_alloc;
+ }/* End IF */
+
+ osbp->fmt_next_alloc = osbp->fmt_prev_alloc = NULL;
+
+ }/* End While */
+
+}/* End Routine */
+
+void
+screen_check_format_lines(sbp,who)
+register struct format_line *sbp;
+int who;
+{
+
+ PREAMBLE();
+
+/*
+ * Loop on down the list till we hit the tail
+ */
+ while(sbp){
+
+/*
+ * If this structure is tied to one of the current screen lines, break the
+ * connection.
+ */
+ if(sbp->fmt_saved_line){
+ if(sbp->fmt_saved_line->companion != sbp){
+ fprintf(
+ stderr,
+ "w%d sbp->fmt_saved_line 0x%x != sbp->...companion 0x%x\n",
+ who,
+ (unsigned int)sbp->fmt_saved_line,
+ (unsigned int)sbp->fmt_saved_line->companion
+ );
+ CAUSE_BUS_ERROR();
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * Take it off the head of the list and place it on the free list.
+ */
+ sbp = sbp->fmt_next_line;
+
+ }/* End While */
+
+}
+
+
+/* SCREEN_DEALLOCATE_FORMAT_LOOKASIDE_LIST
+ *
+ * Function:
+ *
+ * This routine frees up any format_lines we've cached on our local
+ * lookaside list.
+ */
+void
+screen_deallocate_format_lookaside_list()
+{
+register struct format_line *sbp;
+
+ PREAMBLE();
+
+ while((sbp = format_line_free_list) != NULL){
+ format_line_free_list = sbp->fmt_next_line;
+ sbp->fmt_magic = 0;
+ tec_release(TYPE_C_SCREENBUF,(char *)sbp->fmt_buffer);
+ tec_release(TYPE_C_SCREEN,(char *)sbp);
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* SCREEN_FIND_WINDOW_FORMAT_LINE - Finds format lines for a particular window
+ *
+ * Function:
+ *
+ * Each buffer line has chained off of it the format lines which hold
+ * the data as it appears on the screen. In order to handle multiple
+ * windows, we need the ability to have a format line per visible
+ * window. Thus, from the buff_line structure, we chain off a list
+ * of format lines. Since a buffer line may need many format lines to
+ * represent it (because it may wrap) we need a list. In addition,
+ * we want to have one of these lists on a per-window basis, so we
+ * have a pointer to the next list of format lines for another window.
+ */
+struct format_line *
+screen_find_window_format_line(wptr,lbp)
+struct window *wptr;
+struct buff_line *lbp;
+{
+struct format_line *sbp;
+
+ PREAMBLE();
+
+ sbp = lbp->format_line;
+ while(sbp){
+ if(sbp->fmt_window_ptr == wptr) return(sbp);
+ sbp = sbp->fmt_next_window;
+ }/* End While */
+
+ return(NULL);
+
+}/* End Routine */
+
+
+
+/* SCREEN_FORMAT_BUFF_LINE - Create the format_line structures for a buff line
+ *
+ * Function:
+ *
+ * This routine is called with the address of a buff_line structure. It
+ * creates the format_line structures which represent the printed version
+ * of the data, i.e., ready to be written to the terminal.
+ */
+int
+screen_format_buff_line(wptr,lbp)
+struct window *wptr;
+struct buff_line *lbp;
+{
+register struct format_line *sbp;
+register char *cp;
+register short *sp;
+register unsigned int c;
+register int i;
+int byte_count;
+int current_column;
+char *multi_byte_echo;
+char expand_buffer[MAXOF(32,MAX_TAB_WIDTH+1)];
+
+ PREAMBLE();
+
+/*
+ * If we're called with a null lbp, we've run out of memory or something,
+ * and we just punt
+ */
+ if(lbp == NULL) return(FAIL);
+
+/*
+ * If there is an old format structure hanging around (doubtful), make it
+ * go away. Note that this has been modified to try to deal with the new
+ * scheme where we have a different set of format structures for each
+ * window.
+ */
+ if(lbp->format_line){
+ screen_free_window_format_lines(wptr,lbp);
+ }/* End IF */
+
+ sbp = allocate_format_buffer(wptr,lbp);
+ if(sbp == NULL) return(FAIL);
+
+ sbp->fmt_next_window = lbp->format_line;
+ if(sbp->fmt_next_window){
+ sbp->fmt_next_window->fmt_prev_window = sbp;
+ }/* End IF */
+ lbp->format_line = sbp;
+
+ sp = sbp->fmt_buffer;
+ cp = lbp->buffer;
+ byte_count = lbp->byte_count;
+ c = ' ';
+ current_column = 0;
+ multi_byte_echo = NULL;
+
+ while(byte_count > 0){
+
+ if(current_column >= term_columns){
+ sbp->fmt_next_line = allocate_format_buffer(wptr,lbp);
+ if(sbp->fmt_next_line == NULL) return(FAIL);
+ sbp->fmt_next_line->fmt_prev_line = sbp;
+ sbp = sbp->fmt_next_line;
+ sp = sbp->fmt_buffer;
+ current_column = 0;
+ }/* End IF */
+
+ if(multi_byte_echo){
+ c = *multi_byte_echo++;
+ if(*multi_byte_echo == '\0') multi_byte_echo = NULL;
+ }/* End IF */
+
+ else {
+ c = *cp++;
+ byte_count -= 1;
+ }/* End Else */
+
+ c &= 0xFF;
+
+ switch(c){
+ case '\t':
+ i = tab_width - ( current_column % tab_width );
+ multi_byte_echo = tab_expand + MAX_TAB_WIDTH - i;
+ break;
+ case ESCAPE:
+ multi_byte_echo = "$";
+ break;
+
+ case '\n':
+ *sp++ = c;
+ current_column = 0;
+ break;
+ default:
+ if(c < ' '){
+ if( hide_cr_flag ){
+ expand_buffer[0] = ' ';
+ expand_buffer[1] = '\0';
+ } else {
+ expand_buffer[0] = '^';
+ expand_buffer[1] = c + 'A' - 1;
+ expand_buffer[2] = '\0';
+ }
+ multi_byte_echo = expand_buffer;
+ continue;
+ }/* End IF */
+/*
+ * If the high bit is on, display it as the code with that bit stripped off,
+ * or as '.' if it is a non-printable character. We display it reverse video
+ * if the terminal is not a brain damaged 'magic cookie' terminal.
+ */
+ if(eight_bit_output_flag == NO && c >= 128){
+ c -= 128;
+ if(isprint(c)) c |= SCREEN_M_REVERSE;
+ else c = '.' | SCREEN_M_REVERSE;
+#ifdef TERMCAP
+ if(termcap_sg){
+ c &= ~SCREEN_M_REVERSE;
+ }/* End IF */
+#endif
+ }/* End IF */
+ *sp++ = c;
+ current_column += 1;
+ break;
+ }/* End Switch */
+
+ }/* End While */
+
+ if(c != '\n'){
+ if(current_column < (term_columns - 1)){
+ *sp++ = '\n';
+ }/* End IF */
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* SCREEN_FIND_DOT - Find position of dot in a format_line
+ *
+ * Function:
+ *
+ * There are several times that we need to find the format buffer that is
+ * associated with dot. This routine determines which format_line dot is
+ * on, and the offset onto that line (in bytes).
+ */
+int
+screen_find_dot(wptr)
+struct window *wptr;
+{
+register struct buff_line *lbp;
+register struct format_line *sbp;
+register char *cp;
+register unsigned char c;
+int i;
+int byte_count;
+int current_column;
+char *multi_byte_echo;
+char expand_buffer[32];
+struct buff_header *hbp = wptr->win_buffer;
+
+ PREAMBLE();
+
+/*
+ * Find the line buffer and format buffer structures
+ */
+ lbp = buff_find_line(hbp,hbp->dot);
+
+ sbp = screen_find_window_format_line(wptr,lbp);
+ if(sbp == NULL){
+ if(screen_format_buff_line(wptr,lbp) == FAIL) return(FAIL);
+ sbp = screen_find_window_format_line(wptr,lbp);
+ }/* End IF */
+
+ cp = lbp->buffer;
+ byte_count = buff_find_offset(hbp,lbp,hbp->dot);
+ current_column = 0;
+
+ multi_byte_echo = NULL;
+
+ while(byte_count > 0 || multi_byte_echo){
+
+ if(current_column >= term_columns){
+ sbp = sbp->fmt_next_line;
+ current_column = 0;
+ }/* End IF */
+
+ if(multi_byte_echo){
+ c = *multi_byte_echo++;
+ if(*multi_byte_echo == '\0') multi_byte_echo = NULL;
+ }/* End IF */
+
+ else {
+ c = *cp++;
+ byte_count -= 1;
+ }/* End Else */
+
+ switch(c){
+ case '\t':
+ i = tab_width - ( current_column % tab_width );
+ multi_byte_echo = tab_expand + MAX_TAB_WIDTH - i;
+ break;
+ case ESCAPE:
+ multi_byte_echo = "$";
+ break;
+ case '\n':
+ current_column = 0;
+ break;
+ default:
+ if(c < ' '){
+ expand_buffer[0] = '^';
+ expand_buffer[1] = c + 'A' - 1;
+ expand_buffer[2] = '\0';
+ multi_byte_echo = expand_buffer;
+ continue;
+ }/* End IF */
+ current_column += 1;
+ break;
+ }/* End Switch */
+
+ }/* End While */
+
+ wptr->win_dot_format_line = sbp;
+ wptr->win_dot_screen_offset = current_column;
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* ALLOCATE_FORMAT_BUFFER - Routine to return a format_line structure
+ *
+ * Function:
+ *
+ * This routine will return a format_line structure to the caller. It
+ * first looks on the free list, and if there is not one there, it will
+ * create one the hard way.
+ */
+struct format_line *
+allocate_format_buffer(wptr,lbp)
+struct window *wptr;
+struct buff_line *lbp;
+{
+register struct format_line *sbp;
+struct buff_header *hbp = wptr->win_buffer;
+
+ PREAMBLE();
+
+ sbp = format_line_free_list;
+ if(sbp != NULL){
+ if(sbp->fmt_buffer_size == term_columns){
+ format_line_free_list = sbp->fmt_next_line;
+ }
+ else {
+ screen_deallocate_format_lookaside_list();
+ sbp = NULL;
+ }
+ }/* End IF */
+
+ if(sbp == NULL){
+ sbp = (struct format_line *)
+ tec_alloc(TYPE_C_SCREEN,sizeof(struct format_line));
+ if(sbp == NULL) return(NULL);
+
+ sbp->fmt_buffer_size = term_columns;
+ sbp->fmt_buffer = (short *)
+ tec_alloc(
+ TYPE_C_SCREENBUF,
+ (int)(sizeof(short) * sbp->fmt_buffer_size)
+ );
+ if(sbp->fmt_buffer == NULL){
+ tec_release(TYPE_C_SCREEN,(char *)sbp);
+ return(NULL);
+ }/* End IF */
+ }/* End Else */
+
+ sbp->fmt_magic = MAGIC_FORMAT;
+ sbp->fmt_owning_buffer = hbp;
+ sbp->fmt_buffer_line = lbp;
+ sbp->fmt_sequence = screen_sequence;
+ sbp->fmt_next_line = sbp->fmt_prev_line = NULL;
+ sbp->fmt_next_window = sbp->fmt_prev_window = NULL;
+ sbp->fmt_window_ptr = wptr;
+ sbp->fmt_saved_line = NULL;
+ sbp->fmt_visible_line_position = -1;
+ sbp->fmt_permanent = 0;
+
+/*
+ * Place this at the head of the allocated queue
+ */
+ sbp->fmt_next_alloc = format_line_alloc_list;
+ sbp->fmt_prev_alloc = NULL;
+ if(sbp->fmt_next_alloc) sbp->fmt_next_alloc->fmt_prev_alloc = sbp;
+ format_line_alloc_list = sbp;
+
+ return(sbp);
+
+}/* End Routine */
+
+
+
+/* SCREEN_REDRAW - Cause a redraw of the screen
+ *
+ * Function:
+ *
+ * This routine is called when the screen may have become corrupted
+ * and needs to be refreshed.
+ */
+void
+screen_redraw()
+{
+register struct screen_line *lp;
+register short *sp;
+register int i,j;
+
+ PREAMBLE();
+
+ term_goto(0,0);
+ term_clrtobot();
+
+ for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){
+ sp = lp->buffer;
+ for(j = 0; j < term_columns; j++,sp++) *sp = ' ';
+ lp->sequence = 0;
+ }/* End FOR */
+
+ screen_format_windows();
+ screen_refresh();
+
+}/* End Routine */
+
+
+
+/* CREATE_WINDOW - Create a window data structure
+ *
+ * Function:
+ *
+ * This function is called to create a window data structure which
+ * then occupies some portion of the screen. The initial structure
+ * starts out owning all but the reserved lines, but then lines are
+ * stolen from it as it is split into other windows.
+ */
+struct window *
+create_window(x_size,y_size)
+int x_size;
+int y_size;
+{
+register struct window *wptr;
+register int i;
+
+ PREAMBLE();
+
+ wptr = (struct window *)tec_alloc(TYPE_C_WINDOW,sizeof(struct window));
+ if(wptr == NULL) return(NULL);
+
+ wptr->win_label_line.fmt_buffer = (short *)
+ tec_alloc(TYPE_C_SCRBUF,(int)(sizeof(short) * x_size));
+ if(wptr->win_label_line.fmt_buffer == NULL){
+ tec_release(TYPE_C_WINDOW,(char *)wptr);
+ return(NULL);
+ }/* End IF */
+
+ wptr->win_window_number = next_window_number++;
+ wptr->win_next_window = NULL;
+ wptr->win_buffer = NULL;
+/*
+ * Initialize the xy size of the window. The y gets set to size-1 to leave
+ * room for the label line.
+ */
+ wptr->win_x_size = x_size;
+ wptr->win_y_size = y_size - 1;
+ wptr->win_y_base = 0;
+ wptr->win_y_end = wptr->win_y_base + wptr->win_y_size;
+/*
+ * Initialize the label line structure. Each window has one of these in
+ * reverse video showing which buffer is being displayed.
+ */
+ wptr->win_label_line.fmt_next_line =
+ wptr->win_label_line.fmt_prev_line =
+ wptr->win_label_line.fmt_next_alloc =
+ wptr->win_label_line.fmt_prev_alloc = NULL;
+
+ wptr->win_label_line.fmt_permanent = 1;
+ wptr->win_label_line.fmt_sequence = 0;
+ wptr->win_label_line.fmt_owning_buffer = NULL;
+ wptr->win_label_line.fmt_visible_line_position = -1;
+
+ wptr->win_label_line.fmt_window_ptr = wptr; /* FIX */
+ wptr->win_label_line.fmt_buffer_line = NULL;
+ wptr->win_label_line.fmt_buffer_size = x_size;
+
+ for(i = 0; i < x_size; i++){
+ wptr->win_label_line.fmt_buffer[i] = ' ' | SCREEN_M_REVERSE;
+ }/* End FOR */
+
+ wptr->win_label_line.fmt_sequence = screen_sequence;
+
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ wptr->win_label_field_contents[i] = NULL;
+ }/* End FOR */
+
+ return(wptr);
+
+}/* End Routine */
+
+
+
+/* SCREEN_SPLIT_WINDOW - Make two where there is only one
+ *
+ * Function:
+ *
+ * This routine is called when the user wants to split the current window
+ * into two. This allows him to display several buffers simultaneously.
+ */
+struct window *
+screen_split_window(old_wptr,lines,buffer_number)
+register struct window *old_wptr;
+int lines;
+int buffer_number;
+{
+register int i;
+register struct window *new_wptr;
+register struct screen_line *lp;
+
+ PREAMBLE();
+
+ if(old_wptr->win_y_size < SCREEN_MINIMUM_WINDOW_HEIGHT){
+ error_message("Window is already as small as possible");
+ return(NULL);
+ }/* End IF */
+
+ if(lines < SCREEN_MINIMUM_WINDOW_HEIGHT){
+ lines = SCREEN_MINIMUM_WINDOW_HEIGHT;
+ }/* End If */
+
+ if((old_wptr->win_y_size - (lines + 1)) < SCREEN_MINIMUM_WINDOW_HEIGHT){
+ error_message("Resulting windows would be too small");
+ return(NULL);
+ }/* End IF */
+
+ new_wptr = create_window(old_wptr->win_x_size,old_wptr->win_y_size-lines);
+ new_wptr->win_next_window = window_list;
+ window_list = new_wptr;
+ new_wptr->win_buffer = old_wptr->win_buffer;
+/*
+ * Take the lines of the new window away from the old window
+ */
+ old_wptr->win_y_size -= new_wptr->win_y_size + 1;
+/*
+ * Have the new window start above the old one
+ */
+ new_wptr->win_y_base = old_wptr->win_y_base;
+ old_wptr->win_y_base += new_wptr->win_y_size + 1;
+/*
+ * Recalculate the ends of each window
+ */
+ old_wptr->win_y_end = old_wptr->win_y_base + old_wptr->win_y_size;
+ new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size;
+/*
+ * Initialize the label line structure. Each window has one of these in
+ * reverse video showing which buffer is being displayed.
+ */
+ for(i = 0; i < new_wptr->win_x_size; i++){
+ new_wptr->win_label_line.fmt_buffer[i] =
+ old_wptr->win_label_line.fmt_buffer[i];
+ }/* End FOR */
+
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ if(old_wptr->win_label_field_contents[i]){
+ screen_label_line(old_wptr->win_buffer,
+ old_wptr->win_label_field_contents[i],i);
+ }/* End IF */
+ }/* End FOR */
+
+ screen_label_window();
+
+ lp = saved_screen + new_wptr->win_y_end;
+ if(new_wptr->win_label_line.fmt_saved_line){
+ new_wptr->win_label_line.fmt_saved_line->companion = NULL;
+ }/* End IF */
+ new_wptr->win_label_line.fmt_saved_line = lp;
+ if(lp->companion) lp->companion->fmt_saved_line = NULL;
+ lp->companion = &new_wptr->win_label_line;
+ new_wptr->win_label_line.fmt_sequence = screen_sequence;
+
+ if(buffer_number){
+ buff_openbuffnum(buffer_number,0);
+ }/* End IF */
+
+ return(new_wptr);
+
+}/* End Routine */
+
+
+
+/* SCREEN_DELETE_WINDOW - Here to delete a window
+ *
+ * Function:
+ *
+ * This function is called when a user wants to delete a window.
+ * The space gets given back to the window next to it.
+ */
+void
+screen_delete_window(old_wptr)
+register struct window *old_wptr;
+{
+
+register int i;
+register struct window *new_wptr;
+register struct screen_line *lp;
+
+ PREAMBLE();
+
+ if((old_wptr->win_y_size + 1) == (term_lines - SCREEN_RESERVED_LINES)){
+ error_message("Illegal to delete the final window");
+ return;
+ }/* End IF */
+/*
+ * Find the old_wptr on the list of windows, and remove it
+ */
+ if(window_list == old_wptr) window_list = old_wptr->win_next_window;
+ else {
+ new_wptr = window_list;
+ while(new_wptr->win_next_window != old_wptr){
+ new_wptr = new_wptr->win_next_window;
+ }/* End While */
+ new_wptr->win_next_window = old_wptr->win_next_window;
+ }/* End Else */
+/*
+ * Remove the label line from the screen display
+ */
+ lp = old_wptr->win_label_line.fmt_saved_line;
+ lp->companion = NULL;
+ old_wptr->win_label_line.fmt_saved_line = NULL;
+/*
+ * If the window is at the bottom of the screen, we will combine it with
+ * the window above it.
+ */
+ if((old_wptr->win_y_end + 1) == (term_lines - SCREEN_RESERVED_LINES)){
+ new_wptr = window_list;
+ while(new_wptr){
+ if((new_wptr->win_y_end + 1) == old_wptr->win_y_base) break;
+ new_wptr = new_wptr->win_next_window;
+ }/* End While */
+ if(!new_wptr){
+ error_message("Failure to find window above");
+ return;
+ }/* End IF */
+ lp = new_wptr->win_label_line.fmt_saved_line;
+ lp->companion = NULL;
+ new_wptr->win_y_size += old_wptr->win_y_size + 1;
+ new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size;
+ lp = saved_screen + new_wptr->win_y_end;
+ lp->companion = &new_wptr->win_label_line;
+ new_wptr->win_label_line.fmt_saved_line = lp;
+ }/* End IF */
+/*
+ * Else, we combine it with the window below it
+ */
+ else {
+ new_wptr = window_list;
+ while(new_wptr){
+ if(new_wptr->win_y_base == (old_wptr->win_y_end + 1)) break;
+ new_wptr = new_wptr->win_next_window;
+ }/* End While */
+ if(!new_wptr){
+ error_message("Failure to find window below");
+ return;
+ }/* End IF */
+ lp = new_wptr->win_label_line.fmt_saved_line;
+ lp->companion = NULL;
+ new_wptr->win_y_base -= old_wptr->win_y_size + 1;
+ new_wptr->win_y_size += old_wptr->win_y_size + 1;
+ new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size;
+ lp = saved_screen + new_wptr->win_y_end;
+ lp->companion = &new_wptr->win_label_line;
+ new_wptr->win_label_line.fmt_saved_line = lp;
+ }/* End IF */
+/*
+ * Release any memory used to hold the strings in the label line
+ */
+ for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){
+ if(old_wptr->win_label_field_contents[i]){
+ tec_release(
+ TYPE_C_LABELFIELD,
+ old_wptr->win_label_field_contents[i]
+ );
+ old_wptr->win_label_field_contents[i] = NULL;
+ }/* End IF */
+ }/* End FOR */
+/*
+ * Release the buffer used to hold formatted data
+ */
+ tec_release(TYPE_C_SCRBUF,(char *)old_wptr->win_label_line.fmt_buffer);
+ tec_release(TYPE_C_WINDOW,(char *)old_wptr);
+
+ curwin = new_wptr;
+ curbuf = new_wptr->win_buffer;
+
+}/* End Routine */
+
+
+
+/* WINDOW_SWITCH - Switch to a new window
+ *
+ * Function:
+ *
+ * This function is called to switch the current window to a different
+ * window.
+ */
+int
+window_switch(window_number)
+int window_number;
+{
+register struct window *wptr;
+
+ PREAMBLE();
+
+/*
+ * If the window number is specified as zero, just select the next window
+ * on the list.
+ */
+ if(window_number == 0){
+ if((wptr = curwin->win_next_window) == NULL) wptr = window_list;
+ window_number = wptr->win_window_number;
+ }/* End IF */
+
+ wptr = window_list;
+/*
+ * Search the list until we find the correct window.
+ */
+ while(wptr && wptr->win_window_number != window_number){
+ wptr = wptr->win_next_window;
+ }/* End While */
+/*
+ * If the search failed, give up.
+ */
+ if(wptr == NULL){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(tmp_message,"can't switch to window %d",window_number);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+/*
+ * Check to see that we actually found the right window.
+ */
+ if(wptr->win_window_number != window_number) return(FAIL);
+/*
+ * Switch the current window and edit buffer to the newly selected window.
+ */
+ curwin = wptr;
+ return(SUCCESS);
+
+}/* End Routine */
diff --git a/tecexec.c b/tecexec.c
new file mode 100644
index 0000000..6d94fd5
--- /dev/null
+++ b/tecexec.c
@@ -0,0 +1,2801 @@
+char *tecexec_c_version = "tecexec.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecexec.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecexec.c
+ * The SWITCH/CASE statements which implement execution stage of the parser
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+ extern struct cmd_token *jump_to_token;
+ extern struct cmd_token *last_token_executed;
+ extern struct cmd_token *last_cmd_list;
+ extern struct search_buff search_string;
+ extern char user_message[PARSER_STRING_MAX];
+
+ extern char exit_flag;
+ extern struct window *window_list;
+ extern struct buff_header *curbuf,*buffer_headers;
+ extern struct buff_header *qregister_push_down_list;
+ extern int last_search_pos1,last_search_pos2;
+ extern int last_search_status;
+ extern int remembered_dot;
+ extern char intr_flag;
+ extern char immediate_execute_flag;
+ extern struct window *curwin;
+
+ int find_conditional_else(struct cmd_token *);
+ int find_conditional_end(struct cmd_token *);
+ int compare_label(struct cmd_token *,struct cmd_token *);
+ void extract_label(struct cmd_token *,char *);
+
+
+
+/* EXECUTE_A_STATE - Do the runtime execution part of a command
+ *
+ * Function:
+ *
+ * This routine is called to execute the runtime part of a command token.
+ * This could be during immediate execution mode, directly after a token
+ * gets parsed, or it could be during final stages when we are either
+ * running commands which cannot be undone (like ex), or simply complex
+ * things like an iteration. The execute_state which we dispatch on was
+ * set by the parse state during syntax analysis.
+ */
+int
+execute_a_state(ct,uct)
+register struct cmd_token *ct;
+struct cmd_token *uct;
+{
+register struct undo_token *ut;
+char tmp_buffer[LINE_BUFFER_SIZE],tmp_message[LINE_BUFFER_SIZE];
+
+ PREAMBLE();
+
+ last_token_executed = ct;
+
+ switch(ct->execute_state){
+/*
+ * Here to return the numeric value of a q-register. This has to be done
+ * at run time because the value of the q-register might be modified during
+ * the course of the command. Note that we return an error if the q-register
+ * has never been loaded. Otherwise, we return the value in tmpval so that
+ * it can be the input to further arithmetic expressions.
+ */
+ case EXEC_C_QVALUE:
+ {/* Local Block */
+ register struct buff_header *hbp;
+
+ if(ct->q_register == '*'){
+ ct->ctx.tmpval = curbuf->buffer_number;
+ return(SUCCESS);
+ }/* End IF */
+
+ hbp = buff_qfind(ct->q_register,0);
+ if(hbp == NULL){
+ sprintf(tmp_message,"?Q-register %c empty",ct->q_register);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+ ct->ctx.tmpval = hbp->ivalue;
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to return the numeric value of a q-register and also increment it's
+ * value. Other than the incrementing, this is exactly like the QVALUE state.
+ */
+ case EXEC_C_PERCENT_VALUE:
+ {/* Local Block */
+ register struct buff_header *hbp;
+
+ hbp = buff_qfind(ct->q_register,0);
+ if(hbp == NULL){
+ sprintf(tmp_message,"?Q-register %c empty",ct->q_register);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+/*
+ * Set up an undo block so that we can put the previous value back into
+ * the q-register if this command gets undone.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+
+ ut->opcode = UNDO_C_UQREGISTER;
+ ut->iarg1 = ct->q_register;
+ ut->iarg2 = hbp->ivalue;
+
+ hbp->ivalue += ct->ctx.tmpval;
+ ct->ctx.tmpval = hbp->ivalue;
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to read a file into a q-register. We also set up undo tokens to
+ * restore the previous contents of the q-register if he undoes this.
+ */
+ case EXEC_C_EQQREGISTER:
+ {/* Local Block */
+ register struct buff_header *qbp;
+ struct wildcard_expansion *name_list,*np;
+ struct wildcard_expansion *expand_filename();
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = curbuf->buffer_number;
+
+ qbp = buff_qfind(ct->ctx.tmpval,1);
+
+ if(ct->ctx.tmpval == '*'){
+ if(ct->ctx.carg){
+ rename_edit_buffer(curbuf,ct->ctx.carg,uct);
+ }/* End IF */
+ else {
+ load_qname_register();
+ buff_switch(qbp,1);
+ }/* End Else */
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ }/* End IF */
+ return(SUCCESS);
+ }/* End IF */
+
+ if(ct->ctx.carg == NULL || ct->ctx.carg[0] == '\0'){
+ buff_switch(qbp,1);
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ }/* End IF */
+ return(SUCCESS);
+ }/* End IF */
+/*
+ * Delete the previous contents of the q-register, but allow it to be
+ * reconstructed if this gets undone. Note that we don't have to do
+ * this if there was nothing there. Also, if the colon modifier was
+ * specified, then we append to the buffer instead of replacing it.
+ */
+ if((ct->ctx.flags & CTOK_M_COLON_SEEN) == 0 && qbp->zee){
+ buff_delete_with_undo(uct,qbp,0,qbp->zee);
+ }/* End IF */
+
+/*
+ * Set up an undo argument to delete any characters which get inserted as
+ * part of the buff_read we do to load the q-register.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)qbp;
+ ut->iarg1 = qbp->dot;
+ ut->iarg2 = qbp->zee;
+
+ np = expand_filename(ct->ctx.carg);
+ if(np == NULL){
+ sprintf(tmp_message,"EQ No such file <%s>",ct->ctx.carg);
+ error_message(tmp_message);
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ return(FAIL);
+ }/* End IF */
+
+ while(np){
+ buff_read(qbp,np->we_name);
+ name_list = np;
+ np = np->we_next;
+ tec_release(TYPE_C_WILD,(char *)name_list);
+ }/* End While */
+
+ ut->iarg2 = qbp->zee - ut->iarg2;
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ }/* End IF */
+ return(SUCCESS);
+ }/* End Local Block */
+
+/*
+ * Here to load a q-register with a numeric value. Note that not only do we
+ * have to put the new number into it, we also have to set up the undo token
+ * so that if this command is backed out of we can restore the previous value
+ * to the q-register.
+ */
+ case EXEC_C_UQREGISTER:
+ {/* Local Block */
+ register struct buff_header *hbp;
+/*
+ * Find the q-register. If it does not exist yet, create it.
+ */
+ hbp = buff_qfind(ct->q_register,1);
+ if(hbp == NULL){
+ return(FAIL);
+ }/* End IF */
+/*
+ * Insure that we have an argument to store
+ */
+ if(ct->ctx.iarg1_flag == NO){
+ error_message("?Argument required for U command");
+ return(FAIL);
+ }/* End IF */
+/*
+ * Set up an undo block so that we can put the previous value back into
+ * the q-register if this command gets undone.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_UQREGISTER;
+ ut->iarg1 = ct->q_register;
+ ut->iarg2 = hbp->ivalue;
+
+ hbp->ivalue = ct->ctx.iarg1;
+ return(SUCCESS);
+ }/* End Local Block */
+
+/*
+ * Here to load the text contents of the specified q-register into the
+ * edit buffer at the current location.
+ */
+ case EXEC_C_GQREGISTER:
+ {/* Local Block */
+ register struct buff_header *hbp;
+ register struct buff_header *qbp;
+
+ hbp = curbuf;
+/*
+ * If this is the buffer name q-register, we have to do some special
+ * stuff.
+ */
+ if(ct->q_register == '*') load_qname_register();
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, declare an error.
+ */
+ qbp = buff_qfind(ct->q_register,0);
+ if(qbp == NULL){
+ sprintf(tmp_message,"?Q-register <%c> does not exist",
+ ct->q_register);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+/*
+ * Test if the q-register is empty, and declare an error if it is
+ */
+ if(qbp->zee == 0){
+ sprintf(tmp_message,"?Q-register <%c> is empty",
+ ct->q_register);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+/*
+ * Make sure the Q register and the edit buffer are not the same buffer
+ */
+ if(qbp == hbp){
+ strcpy(tmp_message,
+ "?Cannot use G to copy from a Q-register to itself");
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+/*
+ * Now copy the text out of the q-register and insert it into the edit buffer
+ */
+ buff_insert_from_buffer_with_undo(uct,hbp,hbp->dot,qbp,0,qbp->zee);
+
+ return(SUCCESS);
+ }/* End Local Block */
+
+/*
+ * Here to execute the macro in the specified Q register
+ */
+ case EXEC_C_MQREGISTER:
+ {/* Local Block */
+ register struct buff_header *qbp;
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, declare an error.
+ */
+ qbp = buff_qfind(ct->q_register,0);
+ if(qbp == NULL){
+ sprintf(tmp_message,"?Q-register <%c> does not exist",
+ ct->q_register);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+/*
+ * It's an error if the q-register is empty
+ */
+ if(qbp->zee == 0){
+ sprintf(tmp_message,"?Q-register <%c> is empty",
+ ct->q_register);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+/*
+ * Set up the undo block, and pass its address to the tecmacro routine so
+ * that the tecmacro can chain the command list off of it.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_MACRO;
+ ut->carg1 = NULL;
+
+ return(tecmacro(qbp,ct,(struct cmd_token **)&ut->carg1));
+
+ }/* End Local Block */
+
+/*
+ * Here to load text into a q-register in response to the X command.
+ * This clears any existing text from the q-register, and then copies
+ * the specified text into it.
+ */
+ case EXEC_C_XQREGISTER:
+ {/* Local Block */
+ register struct buff_header *hbp;
+ register struct buff_header *qbp;
+ register struct buff_line *lbp;
+ register int i,j;
+ int pos1,pos2;
+
+ hbp = curbuf;
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, create it.
+ */
+ qbp = buff_qfind(ct->q_register,1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+/*
+ * Make sure the Q register and the edit buffer are not the same buffer
+ */
+ if(qbp == hbp){
+ strcpy(tmp_message,
+ "?Cannot use X to copy from a Q-register to itself");
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+/*
+ * If two arguments were specified, then he has specified an a,b range to
+ * be copied.
+ */
+ if(ct->ctx.iarg1_flag == YES && ct->ctx.iarg2_flag == YES){
+ pos1 = ct->ctx.iarg1;
+ pos2 = ct->ctx.iarg2;
+ if(parse_illegal_buffer_position(pos1,pos2,"X")){
+ return(FAIL);
+ }/* End IF */
+ if(pos2 < pos1){
+ pos2 = ct->ctx.iarg1;
+ pos1 = ct->ctx.iarg2;
+ }/* End IF */
+ }/* End IF */
+/*
+ * Else, if it was a single command, it is a relative copy which affects a
+ * portion of the buffer in a similar way as if it was an L command. Our job
+ * now is to turn it into a a,b range.
+ */
+ else {
+ lbp = buff_find_line(hbp,hbp->dot);
+ if(lbp == NULL) return(FAIL);
+/*
+ * Start by pretending it was a 0L command, and get to the begining of
+ * the line. That allows us to not worry about offset onto current line.
+ */
+ j = 0 - buff_find_offset(hbp,lbp,hbp->dot);
+ i = ct->ctx.iarg1;
+ if(ct->ctx.iarg1_flag == NO) i = 1;
+/*
+ * If argument is positive, movement is forward
+ */
+ if(i > 0){
+ while(i-- && lbp){
+ j += lbp->byte_count;
+ lbp = lbp->next_line;
+ }/* End WHILE */
+ if(lbp == NULL){
+ error_message("?Attempt to Move Pointer Off Page with X");
+ return(FAIL);
+ }/* End IF */
+ pos1 = hbp->dot;
+ pos2 = hbp->dot + j;
+ }/* End IF */
+/*
+ * Else, it is backwards
+ */
+ else {
+
+ while(i++ && lbp){
+ lbp = lbp->prev_line;
+ if(lbp == NULL){
+ error_message("?Attempt to Move Pointer Off Page with X");
+ return(FAIL);
+ }/* End IF */
+ j -= lbp->byte_count;
+ }/* End WHILE */
+ pos1 = hbp->dot + j;
+ pos2 = hbp->dot;
+ }/* End Else */
+ }/* End Else */
+/*
+ * Now that we have calculated the buffer positions to be used by the command,
+ * we can actually do the work.
+ */
+ j = pos2 - pos1;
+/*
+ * Delete the previous contents of the q-register, but allow it to be
+ * reconstructed if this gets undone. Note that we don't have to do
+ * this if there was nothing there. Also, if the colon modifier was
+ * specified, then we append to the buffer instead of replacing it.
+ */
+ if((ct->ctx.flags & CTOK_M_COLON_SEEN) == 0 && qbp->zee){
+ buff_delete_with_undo(uct,qbp,0,qbp->zee);
+ }/* End IF */
+/*
+ * Now copy the new text from the edit buffer into the q-register
+ */
+ buff_insert_from_buffer_with_undo(uct,qbp,qbp->zee,hbp,pos1,j);
+/*
+ * If the q-register specified is '*', then the actual result is to rename
+ * the current buffer.
+ */
+ if(ct->q_register == '*'){
+ rename_edit_buffer(hbp,qbp->name,uct);
+ }/* End IF */
+
+ return(SUCCESS);
+ }/* End Local Block */
+
+/*
+ * Here to load text into a q-register in response to the * command.
+ * This clears any existing text from the q-register, and then copies
+ * the previous command string into it.
+ */
+ case EXEC_C_SAVECOMMAND:
+ {/* Local Block */
+ register struct buff_header *qbp;
+ register struct cmd_token *oct;
+ register int i;
+
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, create it.
+ */
+ qbp = buff_qfind(ct->q_register,1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+/*
+ * Delete the previous contents of the q-register, but allow it to be
+ * reconstructed if this gets undone. Note that we don't have to do
+ * this if there was nothing there.
+ */
+ if(qbp->zee) buff_delete_with_undo(uct,qbp,0,qbp->zee);
+/*
+ * Set up an undo token to delete the contents of the q-register that we
+ * are about to load.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)qbp;
+ ut->iarg1 = 0;
+ ut->iarg2 = 0;
+
+/*
+ * Now we go through the last command list and anytime we see an input
+ * byte, we copy it into the q-register.
+ */
+ oct = last_cmd_list;
+ i = 0;
+ while(oct){
+ if(oct->flags & TOK_M_EAT_TOKEN){
+ buff_insert_char(qbp,i++,oct->input_byte);
+ ut->iarg2 += 1;
+ }/* End IF */
+ oct = oct->next_token;
+ }/* End While */
+
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to push the specified q-register onto the q-register stack
+ */
+ case EXEC_C_PUSH_QREGISTER:
+ if(ct->q_register == '*') load_qname_register();
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ if(push_qregister(ct->q_register) != SUCCESS) return(FAIL);
+ ut->opcode = UNDO_C_POP;
+ return(SUCCESS);
+/*
+ * Here to pop a Q register off of the stack and replace a Q register
+ * with it. Since this destroys the Q register we pop onto, we first
+ * must save the entire Q register.
+ */
+ case EXEC_C_POP_QREGISTER:
+ {/* Local Block */
+ register struct buff_header *pbp;
+ register struct buff_header *qbp;
+/*
+ * Get a pointer to the pushed buffer structure. If there are none on
+ * the stack, then he has tried to pop too many, and this is an error.
+ */
+ pbp = qregister_push_down_list;
+
+ if(pbp == NULL){
+ error_message("?Q-register push down list is empty");
+ return(FAIL);
+ }/* End IF */
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, create it.
+ */
+ qbp = buff_qfind(ct->q_register,1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+/*
+ * Arrange to restore the numeric contents of the Q register if this
+ * should be undone.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_UQREGISTER;
+ ut->iarg1 = ct->q_register;
+ ut->iarg2 = qbp->ivalue;
+
+ qbp->ivalue = pbp->ivalue;
+/*
+ * Delete the previous contents of the q-register, but allow it to be
+ * reconstructed if this gets undone. Note that we don't have to do
+ * this if there was nothing there.
+ */
+ if(qbp->zee){
+ if(buff_delete_with_undo(uct,qbp,0,qbp->zee) == FAIL){
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+/*
+ * Arrange for it to be deleted if this gets undone.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)qbp;
+ ut->iarg1 = 0;
+ ut->iarg2 = pbp->zee;
+/*
+ * Transfer the contents of the pushed register into the actual Q register
+ */
+ buff_bulk_insert(qbp,0,pbp->zee,pbp->first_line);
+ pbp->first_line = NULL;
+ qbp->pos_cache.lbp = NULL;
+ qbp->pos_cache.base = 0;
+
+/*
+ * Arrange that the push be re-done if the pop is undone.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_PUSH;
+ ut->iarg1 = ct->q_register;
+/*
+ * Place it on the main buffer list so that buff_destroy can find it
+ */
+ qregister_push_down_list = pbp->next_header;
+ pbp->next_header = buffer_headers;
+ buffer_headers = pbp;
+ buff_destroy(pbp);
+/*
+ * If the q-register specified is '*', then the actual result is to rename
+ * the current buffer.
+ */
+ if(ct->q_register == '*'){
+ rename_edit_buffer(curbuf,qbp->name,uct);
+ }/* End IF */
+
+ return(SUCCESS);
+ }/* End Local Block */
+
+/*
+ * Here to return the current position of 'dot'
+ */
+ case EXEC_C_DOTVALUE:
+ ct->ctx.tmpval = curbuf->dot;
+ return(SUCCESS);
+/*
+ * Here to return the number of bytes in the edit buffer
+ */
+ case EXEC_C_ZEEVALUE:
+ ct->ctx.tmpval = curbuf->zee;
+ return(SUCCESS);
+/*
+ * Here to return the value 0,Z which H is shorthand for
+ */
+ case EXEC_C_HVALUE:
+ ct->ctx.iarg1 = 0;
+ ct->ctx.iarg2 = curbuf->zee;
+ return(SUCCESS);
+/*
+ * Print out the numeric value of an expression
+ */
+ case EXEC_C_EQUALS:
+ if(ct->ctx.iarg1_flag == NO){
+ error_message("?Argument required for = command");
+ return(FAIL);
+ }/* End IF */
+
+ sprintf(tmp_buffer,"value = %d",ct->ctx.iarg1);
+ screen_message(tmp_buffer);
+ return(SUCCESS);
+
+ case EXEC_C_TWO_EQUALS:
+ sprintf(tmp_buffer,"value = 0%o",ct->ctx.iarg1);
+ screen_message(tmp_buffer);
+ return(SUCCESS);
+
+ case EXEC_C_THREE_EQUALS:
+ sprintf(tmp_buffer,"value = 0x%X",ct->ctx.iarg1);
+ screen_message(tmp_buffer);
+ return(SUCCESS);
+/*
+ * Here to insert the value of the expression into the edit buffer as a
+ * decimal representation.
+ */
+ case EXEC_C_BACKSLASH:
+ {/* Local Block */
+ register int i,j;
+ char *cp;
+ int length = 0;
+
+ if(ct->ctx.iarg2 < 2 || ct->ctx.iarg2 > 36) ct->ctx.iarg2 = 10;
+ j = ct->ctx.iarg1;
+ cp = tmp_buffer + sizeof(tmp_buffer);
+ do {
+ i = j % ct->ctx.iarg2;
+ j = (j - i) / ct->ctx.iarg2;
+ if(i > 9) i += ('A' - 10);
+ else i += '0';
+ *(--cp) = i;
+ length += 1;
+ } while(j);
+
+ buff_insert_with_undo(uct,curbuf,curbuf->dot,cp,length);
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to interpret the ascii digits following 'dot' in the edit buffer
+ * as a decimal number, and return this number as the value of this expression,
+ * leaving 'dot' positioned after the digits.
+ */
+ case EXEC_C_BACKSLASHARG:
+ {/* Local Block */
+ register int i;
+ register int radix;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+ radix = ct->ctx.tmpval;
+ ct->ctx.tmpval = 0;
+
+ for(;;){
+ i = buff_contents(curbuf,curbuf->dot);
+
+ if(i >= '0' && i <= '9') i -= '0';
+ else if(i >= 'a' && i <= 'z') i -= ('a' - 10);
+ else if(i >= 'A' && i <= 'Z') i -= ('A' - 10);
+ else break;
+
+ if(i < 0 || i >= radix) break;
+
+ ct->ctx.tmpval = ct->ctx.tmpval * radix + i;
+ if(curbuf->dot == curbuf->zee) break;
+ curbuf->dot++;
+ }/* End FOR */
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * The poor bastard wants to leave...
+ */
+ case EXEC_C_EXITCOMMAND:
+ {/* Local Block */
+ register struct buff_header *hbp;
+
+ if(ct->ctx.iarg1_flag == NO || ct->ctx.iarg1 != -1){
+ for(hbp = buffer_headers; hbp != NULL; hbp = hbp->next_header){
+ if(hbp->buffer_number <= 0) continue;
+ if(hbp->ismodified != 0 && hbp->isreadonly == 0){
+ error_message("?Modified buffers exist");
+ return(FAIL);
+ }/* End IF */
+ }/* End FOR */
+ }/* End IF */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_SET_EXIT_FLAG;
+
+ exit_flag = YES;
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to jump to an absolute position in the buffer
+ */
+ case EXEC_C_JUMP:
+ if(ct->ctx.iarg1_flag == YES && ct->ctx.iarg1 < 0){
+ error_message("?Negative Argument to J");
+ return(FAIL);
+ }/* End IF */
+
+ if(ct->ctx.iarg1_flag == NO) ct->ctx.iarg1 = 0;
+
+ if(parse_illegal_buffer_position(ct->ctx.iarg1,0,"J")){
+ return(FAIL);
+ }/* End IF */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+ if(ct->ctx.iarg1_flag == NO) curbuf->dot = 0;
+ else curbuf->dot = ct->ctx.iarg1;
+ return(SUCCESS);
+/*
+ * Here to move dot by a relative number of buffer positions
+ */
+ case EXEC_C_CHAR:
+ {/* Local Block */
+ register int i;
+
+ i = curbuf->dot + 1;
+
+ if(ct->ctx.iarg1_flag == YES){
+ i = curbuf->dot + ct->ctx.iarg1;
+ }/* End IF */
+
+ if(parse_illegal_buffer_position(i,0,"C")) return(FAIL);
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+ curbuf->dot = i;
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to move dot by a relative number of buffer positions, just like
+ * the "C" command, except that the direction is by default backwards.
+ */
+ case EXEC_C_RCHAR:
+ {/* Local Block */
+ register int i;
+
+ i = curbuf->dot - 1;
+ if(ct->ctx.iarg1_flag == YES){
+ i = curbuf->dot - ct->ctx.iarg1;
+ }/* End IF */
+
+ if(parse_illegal_buffer_position(i,0,"R")) return(FAIL);
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+ curbuf->dot = i;
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to delete the specified number of characters following 'dot'.
+ * A negative value indicates we should delete characters BEFORE 'dot'.
+ */
+ case EXEC_C_DELETE:
+ {/* Local Block */
+ register int i;
+
+ i = 1;
+ if(ct->ctx.iarg1_flag == YES) i = ct->ctx.iarg1;
+
+ if(parse_illegal_buffer_position(curbuf->dot+i,0,"D")){
+ return(FAIL);
+ }/* End IF */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+ if(i < 0){
+ i = 0 - i;
+ curbuf->dot -= i;
+ }/* End IF */
+
+/*
+ * Call the standard routine which deletes the specified buffer positions
+ * and constructs the undo list so that it can be rebuilt if necessary.
+ */
+ if(i) buff_delete_with_undo(uct,curbuf,curbuf->dot,i);
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to move 'dot' by the specified number of words. Note that words
+ * can have a pretty nebulous definition, depending on what you are doing.
+ * This should probably have a q-register which contains all of the delimeters.
+ */
+ case EXEC_C_WORD:
+ {/* Local Block */
+ register int count;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+ count = ct->ctx.iarg1;
+ if(ct->ctx.iarg1_flag == NO) count = 1;
+
+ cmd_wordmove(count);
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to delete the specified number of words. Note that words
+ * can have a pretty nebulous definition, depending on what you are doing.
+ * This should probably have a q-register which contains all of the delimeters.
+ */
+ case EXEC_C_DELWORD:
+ {/* Local Block */
+ register int count;
+ int original_position;
+ int pos;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = original_position = curbuf->dot;
+
+ count = ct->ctx.iarg1;
+ if(ct->ctx.iarg1_flag == NO) count = 1;
+
+ cmd_wordmove(count);
+
+ pos = original_position;
+ count = curbuf->dot - original_position;
+ if(count < 0){
+ count = 0 - count;
+ pos = curbuf->dot;
+ }/* End IF */
+
+ if(count) buff_delete_with_undo(uct,curbuf,pos,count);
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to delete backward the specified number of words. Note that
+ * words can have a pretty nebulous definition, depending on what you are
+ * doing. This should probably have a q-register which contains all of the
+ * delimeters.
+ */
+ case EXEC_C_RDELWORD:
+ {/* Local Block */
+ register int count;
+ int original_position;
+ int pos;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = original_position = curbuf->dot;
+
+ count = 0 - ct->ctx.iarg1;
+ if(ct->ctx.iarg1_flag == NO) count = -1;
+
+ cmd_wordmove(count);
+
+ pos = original_position;
+ count = curbuf->dot - original_position;
+ if(count < 0){
+ count = 0 - count;
+ pos = curbuf->dot;
+ }/* End IF */
+
+ if(count) buff_delete_with_undo(uct,curbuf,pos,count);
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to move the 'dot' by the specified number of lines.
+ */
+ case EXEC_C_LINE:
+ {/* Local Block */
+ register struct buff_header *hbp;
+ register struct buff_line *lbp;
+ register int i,j;
+
+ hbp = curbuf;
+ lbp = buff_find_line(hbp,hbp->dot);
+/*
+ * Start by pretending it was a 0L command, and get to the begining of
+ * the line. That allows us to not worry about offset onto current line.
+ */
+ j = 0 - buff_find_offset(hbp,lbp,hbp->dot);
+ i = ct->ctx.iarg1;
+ if(ct->ctx.iarg1_flag == NO) i = 1;
+/*
+ * If argument is positive, movement is forward
+ */
+ if(i > 0){
+ while(i-- && lbp){
+ j += lbp->byte_count;
+ lbp = lbp->next_line;
+ }/* End WHILE */
+ if(lbp == NULL){
+ error_message("?Attempt to Move Pointer Off Page with L");
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+
+ else {
+ while(i++ && lbp){
+ lbp = lbp->prev_line;
+ if(lbp == NULL){
+ error_message("?Attempt to Move Pointer Off Page with L");
+ return(FAIL);
+ }/* End IF */
+ j -= lbp->byte_count;
+ }/* End WHILE */
+ }/* End Else */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = hbp->dot;
+
+ hbp->dot += j;
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to move the 'dot' backward by the specified number of lines.
+ */
+ case EXEC_C_RLINE:
+ {/* Local Block */
+ register struct buff_header *hbp;
+ register struct buff_line *lbp;
+ register int i,j;
+
+ hbp = curbuf;
+ lbp = buff_find_line(hbp,hbp->dot);
+/*
+ * Start by pretending it was a 0L command, and get to the begining of
+ * the line. That allows us to not worry about offset onto current line.
+ */
+ j = 0 - buff_find_offset(hbp,lbp,hbp->dot);
+ i = 0 - ct->ctx.iarg1;
+ if(ct->ctx.iarg1_flag == NO) i = -1;
+/*
+ * If argument is positive, movement is forward
+ */
+ if(i > 0){
+ while(i-- && lbp){
+ j += lbp->byte_count;
+ lbp = lbp->next_line;
+ }/* End WHILE */
+ if(lbp == NULL){
+ error_message("?Attempt to Move Pointer Off Page with B");
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+
+ else {
+ while(i++ && lbp){
+ lbp = lbp->prev_line;
+ if(lbp == NULL){
+ error_message("?Attempt to Move Pointer Off Page with B");
+ return(FAIL);
+ }/* End IF */
+ j -= lbp->byte_count;
+ }/* End WHILE */
+ }/* End Else */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = hbp->dot;
+
+ hbp->dot += j;
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to kill the specified number of lines.
+ */
+ case EXEC_C_KILL:
+ {/* Local Block */
+ register struct buff_header *hbp;
+ register struct buff_line *lbp;
+ register int i,j;
+ int pos1,pos2;
+
+ hbp = curbuf;
+/*
+ * If two arguments were specified, then he has specified an a,b range to
+ * be killed.
+ */
+ if(ct->ctx.iarg1_flag == YES && ct->ctx.iarg2_flag == YES){
+ pos1 = ct->ctx.iarg1;
+ pos2 = ct->ctx.iarg2;
+ if(parse_illegal_buffer_position(pos1,pos2,"K")){
+ return(FAIL);
+ }/* End IF */
+ if(pos2 < pos1){
+ pos2 = ct->ctx.iarg1;
+ pos1 = ct->ctx.iarg2;
+ }/* End IF */
+ }/* End IF */
+/*
+ * Else, if it was a single command, it is a relative kill which affects a
+ * portion of the buffer in a similar way as if it was an L command. Our job
+ * now is to turn it into a a,b range.
+ */
+ else {
+ lbp = buff_find_line(hbp,hbp->dot);
+/*
+ * Start by pretending it was a 0L command, and get to the begining of
+ * the line. That allows us to not worry about offset onto current line.
+ */
+ j = 0 - buff_find_offset(hbp,lbp,hbp->dot);
+ i = ct->ctx.iarg1;
+ if(ct->ctx.iarg1_flag == NO) i = 1;
+/*
+ * If argument is positive, movement is forward
+ */
+ if(i > 0){
+ while(i-- && lbp){
+ j += lbp->byte_count;
+ lbp = lbp->next_line;
+ }/* End WHILE */
+ if(lbp == NULL){
+ error_message("?Attempt to Move Pointer Off Page with K");
+ return(FAIL);
+ }/* End IF */
+ pos1 = hbp->dot;
+ pos2 = hbp->dot + j;
+ }/* End IF */
+
+ else {
+
+ while(i++ && lbp){
+ lbp = lbp->prev_line;
+ if(lbp == NULL){
+ error_message("?Attempt to Move Pointer Off Page with K");
+ return(FAIL);
+ }/* End IF */
+ j -= lbp->byte_count;
+ }/* End WHILE */
+ pos1 = hbp->dot + j;
+ pos2 = hbp->dot;
+ }/* End Else */
+ }/* End Else */
+
+ j = pos2 - pos1;
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = hbp->dot;
+
+/*
+ * Call the standard routine which deletes the specified buffer positions
+ * and constructs the undo list so that it can be rebuilt if necessary.
+ */
+ if(j) buff_delete_with_undo(uct,hbp,pos1,j);
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to insert a character as part of an insert command
+ */
+ case EXEC_C_INSERT:
+ buff_insert_char_with_undo(
+ uct,
+ curbuf,
+ curbuf->dot,
+ ct->ctx.iarg1_flag ? ct->ctx.iarg1 : ct->input_byte
+ );
+ return(SUCCESS);
+/*
+ * This state is used when a subroutine state is trying to return a
+ * value to the calling state. It gets specially noticed by the parser,
+ * so it really doesn't do much except act as a flag.
+ */
+ case EXEC_C_STOREVAL:
+ return(SUCCESS);
+/*
+ * Store the current expression value as the first argument to a command
+ */
+ case EXEC_C_STORE1:
+ ct->ctx.iarg1 = ct->ctx.tmpval;
+ return(SUCCESS);
+/*
+ * Store the current expression value as the second argument to a command
+ */
+ case EXEC_C_STORE2:
+ ct->ctx.iarg2 = ct->ctx.tmpval;
+ return(SUCCESS);
+/*
+ * Here when we have a leading minus sign in an expression such as -3+4
+ */
+ case EXEC_C_UMINUS:
+ ct->ctx.tmpval = 0 - ct->ctx.tmpval;
+ return(SUCCESS);
+/*
+ * Here to add the two parts of the expression to produce a new temporary
+ * value.
+ */
+ case EXEC_C_PLUS:
+ ct->ctx.tmpval = ct->ctx.iarg2 + ct->ctx.tmpval;
+ return(SUCCESS);
+/*
+ * Here to subtract the two parts of the expression to produce a new temporary
+ * value.
+ */
+ case EXEC_C_MINUS:
+ ct->ctx.tmpval = ct->ctx.iarg2 - ct->ctx.tmpval;
+ return(SUCCESS);
+/*
+ * Here to multiply the two parts of the expression to produce a new temporary
+ * value.
+ */
+ case EXEC_C_TIMES:
+ ct->ctx.tmpval = ct->ctx.iarg1 * ct->ctx.tmpval;
+ return(SUCCESS);
+/*
+ * Here to divide the two parts of the expression to produce a new temporary
+ * value.
+ */
+ case EXEC_C_DIVIDE:
+ if(ct->ctx.tmpval == 0){
+ error_message("?Attempt to Divide by Zero");
+ return(FAIL);
+ }/* End IF */
+ ct->ctx.tmpval = ct->ctx.iarg1 / ct->ctx.tmpval;
+ return(SUCCESS);
+/*
+ * Here in response to the "A" command which returns as a value the ASCII
+ * value of the character at 'dot' + offset.
+ */
+ case EXEC_C_ACOMMAND:
+ {/* Local Block */
+ register int i;
+
+ i = curbuf->dot + ct->ctx.tmpval;
+
+ if(parse_illegal_buffer_position(i,i+1,"A")){
+ return(FAIL);
+ }/* End IF */
+
+ ct->ctx.tmpval = buff_contents(curbuf,i);
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to cause a repaint of the screen incase it got messed up by
+ * something
+ */
+ case EXEC_C_REDRAW_SCREEN:
+ screen_redraw();
+ return(SUCCESS);
+/*
+ * Here to search for a given string. This gets used not only in the
+ * "S" command, but as a substate in any command which requires searching
+ * for a string (such as FD, FK, FS, FR, etc.)
+ */
+ case EXEC_C_SEARCH:
+ {/* Local Block */
+ int arg1,arg2;
+#if 0
+ struct buff_header *qbp;
+#endif
+
+ if(set_search_string_with_undo(ct->ctx.carg,uct) == FAIL){
+ return(FAIL);
+ }/* End IF */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+ arg1 = 1;
+ arg2 = -1;
+ if(ct->ctx.iarg1_flag) arg1 = ct->ctx.iarg1;
+ if(ct->ctx.iarg2_flag) arg2 = ct->ctx.iarg2;
+
+/*
+ * If there are two arguments, then it is a constrained search and we must
+ * insure that the two positions specified are valid buffer positions.
+ */
+ if(ct->ctx.iarg1_flag && ct->ctx.iarg2_flag){
+ if(arg1 < 0 || arg1 > curbuf->zee){
+ sprintf(tmp_message,
+ "?Illegal buffer position %d in search command",arg1);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+ if(arg2 < 0 || arg2 > curbuf->zee){
+ sprintf(tmp_message,
+ "?Illegal buffer position %d in search command",arg2);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * That done, we call the search command to perform the search
+ */
+ if(cmd_search(arg1,arg2,&search_string) == FAIL){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+
+ if(!ct->ctx.inest && search_string.error_message_given == NO){
+ sprintf(tmp_message,
+ "?Cannot find '%.40s'",search_string.input);
+ error_message(tmp_message);
+ }/* End IF */
+
+ return(SUCCESS);
+ }/* End IF */
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = -1;
+ }/* End IF */
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to implement the FR command. The FR command is similar to the FS
+ * command (Find-Substitute), except that if the second string argument is
+ * null, rather than simply deleting the first string argument, it is replaced
+ * by the default replace string.
+ */
+ case EXEC_C_FRREPLACE:
+ {/* Local Block */
+ register struct buff_header *qbp;
+ register int j;
+
+ if(last_search_status == 0){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ if(ct->ctx.inest != 0) return(SUCCESS);
+ return(FAIL);
+ }/* End IF */
+
+ j = last_search_pos2 - last_search_pos1;
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+/*
+ * Call the standard routine which deletes the specified buffer positions
+ * and constructs the undo list so that it can be rebuilt if necessary.
+ */
+ if(j) buff_delete_with_undo(uct,curbuf,last_search_pos1,j);
+
+ qbp = buff_qfind('-',1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+
+ buff_insert_from_buffer_with_undo(
+ uct,
+ curbuf, /* destination is current edit buf */
+ last_search_pos1, /* at the search position */
+ qbp, /* source is replacement q-register*/
+ 0, /* from position 0 to the end */
+ qbp->zee
+ );
+
+ if(ct->ctx.iarg1_flag == YES){
+ if( ct->ctx.iarg1 < 0){
+ curbuf->dot = last_search_pos1;
+ }/* End IF */
+ else if(ct->ctx.iarg2_flag == YES){
+ if(ct->ctx.iarg2 < ct->ctx.iarg1){
+ curbuf->dot = last_search_pos1;
+ }/* End IF */
+ }/* End Else */
+ }/* End IF */
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = -1;
+ }/* End IF */
+
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to implement the FS (Find-Substitute) command which searches for
+ * the first string argument and replaces it with the second.
+ */
+ case EXEC_C_FSREPLACE1:
+ {/* Local Block */
+ register int j;
+ register struct buff_header *qbp;
+
+ ct->ctx.tmpval = 0;
+
+ if(last_search_status == 0){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ if(ct->ctx.inest != 0) return(SUCCESS);
+ return(FAIL);
+ }/* End IF */
+
+#ifdef INTERACTIVE_FS
+ j = last_search_pos2 - last_search_pos1;
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+ if(j) buff_delete_with_undo(uct,curbuf,last_search_pos1,j);
+#endif /* INTERACTIVE_FS */
+
+ return(SUCCESS);
+
+ case EXEC_C_FSREPLACE2:
+
+ if(last_search_status == 0){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ if(ct->ctx.inest != 0) return(SUCCESS);
+ return(FAIL);
+ }/* End IF */
+
+ qbp = buff_qfind('-',1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+
+#ifdef INTERACTIVE_FS
+
+ buff_insert_char_with_undo(uct,curbuf,curbuf->dot,ct->input_byte);
+
+#endif /* INTERACTIVE_FS */
+
+/*
+ * What this does is hope that the replacement string is the same as the
+ * previous one. Only if we get a miscompare do we delete the contents of
+ * the replacement q-register, and insert the new string.
+ */
+ if(qbp->zee > ct->ctx.tmpval){
+ if(ct->input_byte == buff_contents(qbp,ct->ctx.tmpval)){
+ ct->ctx.tmpval += 1;
+ return(SUCCESS);
+ }
+
+ buff_delete_with_undo(uct,qbp,ct->ctx.tmpval,
+ qbp->zee-ct->ctx.tmpval);
+
+ }
+
+ buff_insert_char_with_undo(uct,qbp,ct->ctx.tmpval,ct->input_byte);
+
+ ct->ctx.tmpval += 1;
+ return(SUCCESS);
+
+ case EXEC_C_FSREPLACE3:
+
+ qbp = buff_qfind('-',1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+
+ if(qbp->zee > ct->ctx.tmpval){
+ buff_delete_with_undo(uct,qbp,ct->ctx.tmpval,
+ qbp->zee-ct->ctx.tmpval);
+ }
+
+#ifndef INTERACTIVE_FS
+ j = last_search_pos2 - last_search_pos1;
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+ if(j) buff_delete_with_undo(uct,curbuf,last_search_pos1,j);
+
+ buff_insert_from_buffer_with_undo(uct,
+ curbuf,
+ curbuf->dot,
+ qbp,
+ 0,
+ qbp->zee
+ );
+
+#endif /* !INTERACTIVE_FS */
+
+ if(ct->ctx.iarg1_flag == YES){
+ if( ct->ctx.iarg1 < 0){
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+ curbuf->dot = last_search_pos1;
+ }/* End IF */
+ else if(ct->ctx.iarg2_flag == YES){
+ if(ct->ctx.iarg2 < ct->ctx.iarg1){
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+ curbuf->dot = last_search_pos1;
+ }/* End IF */
+ }/* End Else */
+ }/* End IF */
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = last_search_status;
+ }/* End IF */
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to execute the FT (tags) command. Since this is a fairly complicated
+ * command, we call a subroutine to perform the actions.
+ */
+ case EXEC_C_FTAGS:
+ {/* Local Block */
+ register int arg_count = 0;
+ register int status;
+
+ if(ct->ctx.iarg1_flag == YES) arg_count = 1;
+ if(ct->ctx.iarg2_flag == YES) arg_count = 2;
+
+ status = cmd_tags(
+ uct,
+ arg_count,
+ ct->ctx.iarg1,
+ ct->ctx.iarg2,
+ ct->ctx.carg
+ );
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = status;
+ return(SUCCESS);
+ }/* End IF */
+
+ return(status);
+
+ }/* End Local Block */
+
+/*
+ * Here to execute the FD command which searches for a string and deletes
+ * it. It is like FS but saves you from typing the second escape.
+ */
+ case EXEC_C_FDCOMMAND:
+ {/* Local Block */
+ register int j;
+
+ if(last_search_status == 0){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ if(ct->ctx.inest != 0) return(SUCCESS);
+ return(FAIL);
+ }/* End IF */
+
+ j = last_search_pos2 - last_search_pos1;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+/*
+ * Call the standard routine which deletes the specified buffer positions
+ * and constructs the undo list so that it can be rebuilt if necessary.
+ */
+ if(j) buff_delete_with_undo(uct,curbuf,last_search_pos1,j);
+
+ curbuf->dot = last_search_pos1;
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = -1;
+ }/* End IF */
+
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to implement the FK command. The FK command remembers the current
+ * position in the buffer and searches for the specified string. It then
+ * deletes the characters from the old current position up to but not
+ * including the search string.
+ */
+ case EXEC_C_FKCOMMAND:
+ {/* Local Block */
+ register int j;
+
+ if(last_search_status == 0){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ if(ct->ctx.inest != 0) return(SUCCESS);
+ return(FAIL);
+ }/* End IF */
+
+ if(remembered_dot <= last_search_pos1){
+ last_search_pos2 = last_search_pos1;
+ last_search_pos1 = remembered_dot;
+ }/* End IF */
+
+ else {
+ last_search_pos1 = last_search_pos2;
+ last_search_pos2 = remembered_dot;
+ curbuf->dot = last_search_pos1;
+ }/* End IF */
+
+ j = last_search_pos2 - last_search_pos1;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = curbuf->dot;
+
+/*
+ * Call the standard routine which deletes the specified buffer positions
+ * and constructs the undo list so that it can be rebuilt if necessary.
+ */
+ if(j) buff_delete_with_undo(uct,curbuf,last_search_pos1,j);
+
+ curbuf->dot = last_search_pos1;
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = -1;
+ }/* End IF */
+
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * This state is used by the FK command to remember the current dot
+ * position before it is changed by the search command.
+ */
+ case EXEC_C_REMEMBER_DOT:
+ remembered_dot = curbuf->dot;
+ return(SUCCESS);
+/*
+ * Here to implement the N search which searches across edit buffers for
+ * the given string.
+ */
+ case EXEC_C_NSEARCH:
+ {/* Local Block */
+ int count;
+ struct buff_header *original_buffer;
+ int original_dot;
+ int saved_dot,saved_top,saved_bottom;
+ int first_buffer;
+ int last_buffer = 0;
+ char back_in_original_buffer;
+/*
+ * We set the search string, just like all the other search commands
+ */
+ if(set_search_string_with_undo(ct->ctx.carg,uct) == FAIL){
+ return(FAIL);
+ }/* End IF */
+/*
+ * Remember the buffer that we started in, and be ready to change back to
+ * it if we undo.
+ */
+ original_buffer = curbuf;
+ original_dot = curbuf->dot;
+
+ count = 1;
+ if(ct->ctx.iarg1_flag) count = ct->ctx.iarg1;
+
+/*
+ * If he says 1,5n<string>$ then we search for the string in buffers 1
+ * through 5. We check that both buffers exist before we begin the search.
+ */
+ if(ct->ctx.iarg2_flag){
+ count = 1;
+ first_buffer = ct->ctx.iarg1;
+ last_buffer = ct->ctx.iarg2;
+
+ if(first_buffer != curbuf->buffer_number){
+ if(buff_openbuffer(NULL,last_buffer,0) != SUCCESS){
+ return(FAIL);
+ }/* End IF */
+ if(buff_openbuffer(NULL,first_buffer,0) != SUCCESS){
+ return(FAIL);
+ }/* End IF */
+ curbuf->dot = 0;
+ }/* End IF */
+ }/* End IF */
+
+/*
+ * In the starting buffer, we want to search from 'dot' to the end. Then
+ * we would search all the other buffers, and then the original one again,
+ * but from the top to 'dot'. The saved_dot stuff is necessary because
+ * the cmd_search routine has the side effect of changing 'dot' in the
+ * buffer being searched. However, we only want to affect 'dot' in the
+ * final buffer that the final occurance of the search string is found in.
+ */
+ saved_dot = curbuf->dot;
+ saved_top = curbuf->dot;
+ saved_bottom = curbuf->zee;
+ back_in_original_buffer = 0;
+
+ while(count > 0){
+
+ if(cmd_search(saved_top,saved_bottom,&search_string) != FAIL){
+ saved_top = curbuf->dot;
+ count -= 1;
+ continue;
+ }/* End IF */
+/*
+ * Here if the search failed. We want to switch over to the next buffer and
+ * try again, until we exhaust all the buffers. If we reach the final buffer
+ * without finding the string, we return an error to the user, leaving him
+ * back in his original buffer.
+ */
+ if(ct->ctx.iarg2_flag && curbuf->buffer_number == last_buffer){
+ if(buff_openbuffer(NULL,original_buffer->buffer_number,0) != SUCCESS){
+ error_message("Boom! lost original buffer");
+ return(FAIL);
+ }/* End IF */
+ back_in_original_buffer = 1;
+ }/* End IF */
+
+ if(back_in_original_buffer){
+ curbuf->dot = saved_dot;
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ sprintf(tmp_message,
+ "?Cannot find '%.40s'",search_string.input);
+ if(!ct->ctx.inest && !intr_flag &&
+ search_string.error_message_given == NO){
+ error_message(tmp_message);
+ }/* End IF */
+ return(SUCCESS);
+ }/* End IF */
+/*
+ * We havn't tried all the buffers yet, switch to the next one.
+ */
+ curbuf->dot = saved_dot;
+
+ while(1){
+ curbuf = curbuf->next_header;
+ if(curbuf == NULL) curbuf = buffer_headers;
+ if(curbuf->buffer_number == -1) break;
+ if(curbuf->buffer_number > 0) break;
+ if(ct->ctx.iarg2_flag) continue;
+ if(curbuf == original_buffer) break;
+ }/* End While */
+
+ saved_dot = curbuf->dot;
+ saved_top = 0;
+ saved_bottom = curbuf->zee;
+
+ if(ct->ctx.iarg2_flag == NO){
+ if(curbuf->buffer_number == original_buffer->buffer_number){
+ back_in_original_buffer = 1;
+ saved_bottom = original_dot;
+ }/* End IF */
+ }/* End IF */
+
+ }/* End While */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = original_buffer->buffer_number;
+
+ buff_switch(curbuf,1);
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEDOT;
+ ut->iarg1 = saved_dot;
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = -1;
+ }/* End IF */
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to begin an iteration < ... >
+ */
+ case EXEC_C_ITERATION_BEGIN:
+ {/* Local Block */
+ register struct cmd_token *oct;
+ if(ct->ctx.iarg1_flag == YES){
+ if(ct->ctx.iarg1 <= 0){
+ oct = ct;
+ while((oct = oct->next_token)){
+ if(oct->opcode != TOK_C_ITERATION_END) continue;
+ if(oct->ctx.inest != (ct->ctx.inest-1)) continue;
+ jump_to_token = oct;
+ return(SUCCESS);
+ }/* End While */
+ ct->ctx.go_flag = NO;
+ return(SUCCESS);
+ }/* End IF */
+ ct->ctx.iarg1 -= 1;
+ }/* End IF */
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to provide the execution code for the end of an iteration.
+ * This must search backwards in the parse tokens to find the begining
+ * of the iteration, and set up to jump to that token
+ */
+ case EXEC_C_ITERATION_END:
+ {/* Local Block */
+ register struct cmd_token *oct;
+/*
+ * The parser left us a pointer to the begining of the iteration so we can
+ * find it without having to search.
+ */
+ oct = ct->ctx.caller_token;
+
+ if(oct->ctx.iarg1_flag == YES){
+ if(oct->ctx.iarg1 <= 0){
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+/*
+ * The following line grabs the old iteration value so that when the
+ * begining of iteration code grabs the "previous" iarg1, this will
+ * be it. Think of it this way: the code has to do something different
+ * the first time into the iteration (it has to load the results of the
+ * STORE1, then the subsequent times it has to decrement what's already
+ * there. That's why this seems a little kludgy.
+ */
+ ct->ctx.iarg1 = oct->ctx.iarg1;
+ jump_to_token = oct;
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to implement the semicolon command which is used to jump out
+ * of an iteration if the argument is >= 0.
+ */
+ case EXEC_C_SEMICOLON:
+ {/* Local Block */
+ register struct cmd_token *oct;
+ int value;
+
+ value = last_search_status;
+ if(ct->ctx.iarg1_flag == YES) value = ct->ctx.iarg1;
+
+ if(value < 0) return(SUCCESS);
+
+ oct = ct;
+ while((oct = oct->next_token)){
+ if(oct->opcode != TOK_C_ITERATION_END) continue;
+ if(oct->ctx.inest != (ct->ctx.inest-1)) continue;
+ if(oct->next_token){
+ jump_to_token = oct->next_token;
+ return(SUCCESS);
+ }/* End IF */
+ }/* End While */
+
+ ct->ctx.go_flag = NO;
+ return(BLOCKED);
+
+ }/* End Local Block */
+/*
+ * Here to implement the various conditional operators
+ */
+ case EXEC_C_COND_GT:
+ if(ct->ctx.iarg1 <= 0){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_COND_LT:
+ if(ct->ctx.iarg1 >= 0){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_COND_EQ:
+ if(ct->ctx.iarg1 != 0){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_COND_NE:
+ if(ct->ctx.iarg1 == 0){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_COND_DIGIT:
+ if(!isdigit(ct->ctx.iarg1)){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_COND_ALPHA:
+ if(!isalpha(ct->ctx.iarg1)){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_COND_LOWER:
+ if(!islower(ct->ctx.iarg1)){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_COND_UPPER:
+ if(!isupper(ct->ctx.iarg1)){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_COND_SYMBOL:
+ if(!isalnum(ct->ctx.iarg1) && ct->ctx.iarg1 != '_'){
+ return(find_conditional_else(ct));
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_SKIP_ELSE:
+ return(find_conditional_end(ct));
+/*
+ * Here to implement the GOTO command OLABEL$
+ */
+ case EXEC_C_GOTO:
+ {/* Local Block */
+ struct cmd_token *goto_ptr;
+ struct cmd_token *test_ptr;
+/*
+ * First step is to find the begining of this goto string
+ */
+ goto_ptr = ct;
+ while((goto_ptr = goto_ptr->prev_token)){
+ if(goto_ptr->opcode != TOK_C_GOTO_BEGIN) continue;
+ break;
+ }/* End While */
+/*
+ * Now try to find the label. First search backwards for it
+ */
+ test_ptr = goto_ptr;
+ while((test_ptr = test_ptr->prev_token)){
+ if(test_ptr->opcode != TOK_C_LABEL_BEGIN) continue;
+ if(compare_label(goto_ptr,test_ptr) == YES){
+ ct->ctx.go_flag = NO;
+ jump_to_token = test_ptr;
+ return(SUCCESS);
+ }/* End IF */
+ }/* End While */
+/*
+ * The label is not in front of us, look for it afterwards
+ */
+ test_ptr = goto_ptr;
+ while((test_ptr = test_ptr->next_token)){
+ if(test_ptr->opcode == TOK_C_FINALTOKEN){
+ char string1[PARSER_STRING_MAX];
+
+ extract_label(goto_ptr,string1);
+ (void) sprintf(tmp_message,"?Can't find label <%s>",
+ string1);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ if(test_ptr->opcode != TOK_C_LABEL_BEGIN) continue;
+
+ if(compare_label(goto_ptr,test_ptr) == YES){
+ jump_to_token = test_ptr;
+ return(SUCCESS);
+ }/* End IF */
+ }/* End While */
+
+ ct->ctx.go_flag = NO;
+ return(BLOCKED);
+
+ }/* End Local Block */
+/*
+ * Here to write out the buffer. If the filename is specified, write it
+ * to that name, otherwise write it to the default name of the buffer.
+ */
+ case EXEC_C_WRITEFILE:
+ {/* Local Block */
+ int status;
+ struct wildcard_expansion *name_list,*np;
+ struct wildcard_expansion *expand_filename();
+ char *filename;
+
+ if(curbuf->isreadonly){
+ error_message("?File is Readonly");
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ return(FAIL);
+ }/* End IF */
+
+ if(ct->ctx.carg) filename = ct->ctx.carg;
+ else filename = curbuf->name;
+
+ np = expand_filename(filename);
+ if(np == NULL){
+ sprintf(tmp_message,"EW No such file <%s>",filename);
+ error_message(tmp_message);
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ return(FAIL);
+ }/* End IF */
+
+ if(np->we_next){
+ sprintf(tmp_message,"EW: Non-unique filename <%s>",filename);
+ error_message(tmp_message);
+ while(np){
+ name_list = np;
+ np = np->we_next;
+ tec_release(TYPE_C_WILD,(char *)name_list);
+ }/* End While */
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ return(FAIL);
+ }/* End IF */
+
+ status = cmd_write(curbuf,np->we_name);
+ tec_release(TYPE_C_WILD,(char *)np);
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = status;
+ return(SUCCESS);
+ }/* End IF */
+ return(status);
+
+ }/* End Local Block */
+/*
+ * Read in the named file to the current position in the edit buffer
+ */
+ case EXEC_C_READFILE:
+ {/* Local Block */
+ struct wildcard_expansion *name_list,*np;
+ struct wildcard_expansion *expand_filename();
+
+ if(ct->ctx.carg == NULL || ct->ctx.carg[0] == '\0'){
+ error_message("?ER Requires a filename");
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ return(FAIL);
+ }/* End IF */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)curbuf;
+ ut->iarg1 = curbuf->dot;
+ ut->iarg2 = curbuf->zee;
+
+ np = expand_filename(ct->ctx.carg);
+ if(np == NULL){
+ sprintf(tmp_message,"ER No such file <%s>",ct->ctx.carg);
+ error_message(tmp_message);
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ return(FAIL);
+ }/* End IF */
+
+ while(np){
+ buff_read(curbuf,np->we_name);
+ name_list = np;
+ np = np->we_next;
+ tec_release(TYPE_C_WILD,(char *)name_list);
+ }/* End While */
+
+
+ ut->iarg2 = curbuf->zee - ut->iarg2;
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ return(SUCCESS);
+ }/* End IF */
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to change the current edit buffer. If a numeric argument is
+ * supplied, change to that buffer, otherwise a string should have been
+ * specified, and this is the name of the file/buffer to be loaded.
+ */
+ case EXEC_C_EDITBUF:
+ {/* Local Block */
+ struct buff_header *hbp,*old_buffer,*first_buffer;
+ struct wildcard_expansion *name_list,*np;
+ struct wildcard_expansion *expand_filename();
+
+/*
+ * Here if there is no string argument. It better be a numeric buffer
+ * number argument. If so, switch to that buffer, and set the undo to
+ * switch back to the old one if this gets undone.
+ */
+ if(ct->ctx.carg == NULL || ct->ctx.carg[0] == '\0'){
+ if(ct->ctx.iarg1_flag == NO){
+ error_message("?EB Requires a filename or argument");
+ return(FAIL);
+ }/* End IF */
+
+ old_buffer = curbuf;
+ if(buff_openbuffer(0,ct->ctx.iarg1,0) == FAIL){
+ return(FAIL);
+ }/* End IF */
+
+ if(curbuf->buffer_number != ct->ctx.iarg1){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }
+ return(FAIL);
+ }/* End IF */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = old_buffer->buffer_number;
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ }
+ return(SUCCESS);
+
+ }/* End IF */
+
+ old_buffer = curbuf;
+ first_buffer = NULL;
+
+ np = expand_filename(ct->ctx.carg);
+ if(np == NULL){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }
+ sprintf(tmp_message,"EB No such file <%s>",ct->ctx.carg);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ while(np){
+/*
+ * The following hack detects whether a new buffer was created so that
+ * we know whether or not to set up an undo to un-create it.
+ */
+ hbp = buff_find(np->we_name);
+ if(buff_openbuffer(np->we_name,0,0) == SUCCESS){
+ if(hbp == NULL){
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CLOSEBUFF;
+ ut->carg1 = (char *)curbuf;
+ }/* End IF */
+ if(first_buffer == NULL) first_buffer = curbuf;
+ }/* End IF */
+
+ name_list = np;
+ np = np->we_next;
+ tec_release(TYPE_C_WILD,(char *)name_list);
+ }/* End While */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = old_buffer->buffer_number;
+
+ buff_openbuffer(0,first_buffer->buffer_number,0);
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ return(SUCCESS);
+ }
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to implement the EF command which removes an edit buffer.
+ */
+ case EXEC_C_CLOSEBUF:
+ {/* Local Block */
+ register struct buff_header *hbp,*bp;
+ register int i;
+
+ hbp = curbuf;
+ if(curbuf->buffer_number == 0){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ error_message("?EF Not allowed on special buffers");
+ return(FAIL);
+ }/* End IF */
+
+ if(hbp->ismodified && !hbp->isreadonly){
+ if(ct->ctx.iarg1_flag == NO ||
+ ct->ctx.iarg1 != -1){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }/* End IF */
+ sprintf(tmp_message,"?EF Buffer %s is modified",
+ curbuf->name);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+ }/* End IF */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = hbp->buffer_number;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_REOPENBUFF;
+ ut->iarg1 = hbp->buffer_number;
+ ut->carg1 = (char *)hbp;
+
+/*
+ * Unlink this buffer header from the header list. Check to see if it is
+ * at the head of the list. If not at the head of the list, we have to
+ * search the list till we find it's father. Then we make it's father's
+ * child be it's child.
+ */
+ i = 0;
+ bp = buffer_headers;
+ if(bp == hbp) buffer_headers = bp->next_header;
+ else {
+ while(bp){
+ if(bp->next_header == hbp) break;
+ bp = bp->next_header;
+ }/* End While */
+ bp->next_header = hbp->next_header;
+ curbuf = NULL;
+ if(bp->buffer_number > 0 && hbp->buffer_number > 0){
+ i = bp->buffer_number;
+ }/* End IF */
+ else if(bp->buffer_number < 0 && hbp->buffer_number < 0){
+ i = bp->buffer_number;
+ }/* End Else */
+ else if(hbp->buffer_number > 0 && hbp->next_header){
+ i = hbp->next_header->buffer_number;
+ }/* End Else */
+ else i = -1;
+ }/* End Else */
+/*
+ * Pick a different buffer to be the current buffer
+ */
+ if(i){
+ if(buff_openbuffer(NULL,i,0) == FAIL) i = 0;
+ }/* End IF */
+ if(!i){
+ if(buff_openbuffer(NULL,-1,0) == FAIL){
+ buff_openbuffer(NULL,0,0);
+ }/* End IF */
+ }/* End IF */
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ }/* End IF */
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to implement the EV command which is identical to the EB command
+ * except that if the buffer does not exist, when it is created it is created
+ * as a read only buffer.
+ */
+ case EXEC_C_VIEWBUF:
+ {/* Local Block */
+ struct buff_header *hbp,*old_buffer,*first_buffer;
+ struct wildcard_expansion *name_list,*np;
+ struct wildcard_expansion *expand_filename();
+
+/*
+ * Here if there is no string argument. It better be a numeric buffer
+ * number argument. If so, switch to that buffer, and set the undo to
+ * switch back to the old one if this gets undone.
+ */
+ if(ct->ctx.carg == NULL || ct->ctx.carg[0] == '\0'){
+ if(ct->ctx.iarg1_flag == NO){
+ error_message("?EV Requires a filename or argument");
+ return(FAIL);
+ }/* End IF */
+
+ old_buffer = curbuf;
+ buff_openbuffer(0,ct->ctx.iarg1,1);
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = old_buffer->buffer_number;
+
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ }
+ return(SUCCESS);
+
+ }/* End IF */
+
+ old_buffer = curbuf;
+ first_buffer = NULL;
+
+ np = expand_filename(ct->ctx.carg);
+ if(np == NULL){
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = FAIL;
+ return(SUCCESS);
+ }
+ sprintf(tmp_message,"EV No such file <%s>",ct->ctx.carg);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ while(np){
+/*
+ * The following hack detects whether a new buffer was created so that
+ * we know whether or not to set up an undo to un-create it.
+ */
+ hbp = buff_find(np->we_name);
+ if(buff_openbuffer(np->we_name,0,1) == SUCCESS){
+ if(hbp == NULL){
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CLOSEBUFF;
+ ut->carg1 = (char *)curbuf;
+ }/* End IF */
+ if(first_buffer == NULL) first_buffer = curbuf;
+ }/* End IF */
+
+ name_list = np;
+ np = np->we_next;
+ tec_release(TYPE_C_WILD,(char *)name_list);
+ }/* End While */
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = old_buffer->buffer_number;
+
+ buff_openbuffer(0,first_buffer->buffer_number,1);
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.tmpval = SUCCESS;
+ return(SUCCESS);
+ }
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to scroll the screen the specified number of lines
+ */
+ case EXEC_C_SCROLL:
+ if(ct->ctx.iarg1_flag == NO) ct->ctx.iarg1 = 1;
+ screen_scroll(ct->ctx.iarg1);
+ return(SUCCESS);
+/*
+ * Here to cause the screen to update
+ */
+ case EXEC_C_UPDATE_SCREEN:
+ screen_format_windows();
+ screen_refresh();
+ return(SUCCESS);
+/*
+ * Here to either split or combine windows
+ */
+ case EXEC_C_WINDOW_CONTROL:
+ {/* Local Block */
+ struct window *old_wptr;
+ struct window *new_wptr;
+
+ if(ct->ctx.iarg1_flag == NO) screen_delete_window(curwin);
+
+ if(ct->ctx.iarg1_flag == YES && ct->ctx.iarg2_flag == YES){
+ old_wptr = curwin;
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+
+ new_wptr =
+ screen_split_window(
+ curwin,
+ ct->ctx.iarg1,
+ ct->ctx.iarg2
+ );
+
+ if(new_wptr == NULL) return(FAIL);
+
+ ut->opcode = UNDO_C_WINDOW_SPLIT;
+ ut->iarg1 = old_wptr->win_window_number;
+ ut->carg1 = (char *)new_wptr;
+ }/* End Else */
+
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * Here to select the next window as active
+ */
+ case EXEC_C_NEXT_WINDOW:
+ {/* Local Block */
+ int status;
+ int new_window;
+
+ new_window = ct->ctx.iarg1_flag == YES ? ct->ctx.iarg1 : 0;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_WINDOW_SWITCH;
+ ut->iarg1 = curwin->win_window_number;
+
+ status = window_switch(new_window);
+
+ return(status);
+
+ }/* End Local Block */
+/*
+ * Here to reset a ^A message to NULL
+ */
+ case EXEC_C_RESET_MESSAGE:
+ user_message[0] = '\0';
+ return(SUCCESS);
+/*
+ * Here to add a byte to the length of the current user message string
+ */
+ case EXEC_C_MESSAGE:
+ {/* Local Block */
+ register int i;
+
+ if(strlen(user_message) > (sizeof(user_message) - 1)){
+ error_message("?^A message length exceeded");
+ return(FAIL);
+ }/* End IF */
+
+ i = strlen(user_message);
+ user_message[i] = ct->input_byte;
+ user_message[i+1] = '\0';
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_SHORTEN_MESSAGE;
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+/*
+ * Here to print the current user message to the status line of the screen
+ */
+ case EXEC_C_OUTPUT_MESSAGE:
+ screen_message(user_message);
+ screen_refresh();
+ return(SUCCESS);
+/*
+ * Here to execute the specified operating system command, and insert the
+ * generated output into the edit buffer.
+ */
+ case EXEC_C_ECCOMMAND:
+ {/* Local Block */
+ int status;
+
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)curbuf;
+ ut->iarg1 = curbuf->dot;
+ ut->iarg2 = 0;
+
+ status = cmd_oscmd(ct);
+ ut->iarg2 = curbuf->dot - ut->iarg1;
+ return(status);
+
+ }/* End Local Block */
+
+ case EXEC_C_SET_IMMEDIATE_MODE:
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_SET_IMMEDIATE_MODE;
+ ut->iarg1 = immediate_execute_flag;
+
+ immediate_execute_flag = YES;
+ if(ct->ctx.iarg1 == 0){
+ ct->ctx.go_flag = NO;
+ immediate_execute_flag = NO;
+ }/* End IF */
+ return(SUCCESS);
+
+ case EXEC_C_OPENBRACE:
+ {/* Local Block */
+ register struct buff_header *qbp;
+
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, create it.
+ */
+ qbp = buff_qfind('$',1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+/*
+ * Set up the undo token to remember the current edit buffer, and then switch
+ * to the Q-register.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_CHANGEBUFF;
+ ut->iarg1 = curbuf->buffer_number;
+
+ buff_switch(qbp,1);
+/*
+ * Delete the previous contents of the q-register, but allow it to be
+ * reconstructed if this gets undone. Note that we don't have to do
+ * this if there was nothing there.
+ */
+ if(qbp->zee) buff_delete_with_undo(uct,qbp,0,qbp->zee);
+/*
+ * Arrange for it to be deleted if this gets undone.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+/*
+ * Copy the parser command line into the Q-register
+ */
+ parser_dump_command_line(qbp);
+
+ ut->opcode = UNDO_C_DELETE;
+ ut->carg1 = (char *)qbp;
+ ut->iarg1 = 0;
+ ut->iarg2 = qbp->zee;
+
+ return(SUCCESS);
+ }/* End Local Block */
+
+ case EXEC_C_CLOSEBRACE:
+ {/* Local Block */
+ register struct buff_header *qbp;
+
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, that is an error
+ */
+ qbp = buff_qfind('$',0);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+/*
+ * Replace the command line with the contents of the Q-register
+ */
+ parser_replace_command_line(qbp);
+
+ return(INVALIDATE);
+
+ }/* End Local Block */
+
+/*
+ * Here to skip a label (this occurs when we run into a label)
+ */
+ case EXEC_C_SKIPLABEL:
+ while(ct){
+ if(ct->opcode == TOK_C_LABEL_END){
+ jump_to_token = ct;
+ return(SUCCESS);
+ }/* End IF */
+ ct = ct->next_token;
+ }/* End While */
+ return(SUCCESS);
+
+/*
+ * Here if he wants to set some options up
+ */
+ case EXEC_C_SETOPTIONS:
+ ut = allocate_undo_token(uct);
+ if(ut == NULL) return(FAIL);
+ ut->opcode = UNDO_C_SETOPTIONS;
+
+ return(cmd_setoptions(ct->ctx.iarg1,ct->ctx.iarg2,ut));
+
+ default:
+ sprintf(tmp_message,"?TECEXEC: unknown state %d dispatched",
+ ct->execute_state);
+ error_message(tmp_message);
+ return(FAIL);
+
+ }/* End Switch */
+
+}/* End Routine */
+
+
+
+/* PUSH_QREGISTER - Push a Q register onto the stack
+ *
+ * Function:
+ *
+ * This routine is called to make a copy of a Q register and place it
+ * on the Q register stack.
+ */
+int
+push_qregister(letter)
+char letter;
+{
+register struct buff_header *hbp;
+register struct buff_header *qbp;
+
+ PREAMBLE();
+
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, create it.
+ */
+ qbp = buff_qfind(letter,1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+
+ hbp = buff_duplicate(qbp);
+
+ hbp->next_header = qregister_push_down_list;
+ qregister_push_down_list = hbp;
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+/* EXTRACT_LABEL - Build a string with the contents of the label in it
+ *
+ * Function:
+ *
+ * This routine is called to build a string with the label name
+ * in it. The current use is for error messages.
+ */
+void
+extract_label(label_ptr,string1)
+register struct cmd_token *label_ptr;
+char *string1;
+{
+register char *cp1;
+
+ cp1 = string1;
+ *cp1 = '\0';
+ while(label_ptr){
+ if(cp1 >= &string1[PARSER_STRING_MAX-1]) break;
+ if(label_ptr->opcode == TOK_C_INPUTCHAR){
+ if(label_ptr->input_byte == '!') break;
+ *cp1++ = label_ptr->input_byte;
+ *cp1 = '\0';
+ }/* End IF */
+ if(label_ptr->opcode == TOK_C_LABEL_END) break;
+ label_ptr = label_ptr->next_token;
+ }/* End While */
+
+}/* End Routine */
+
+/* COMPARE_LABEL - Test if this is our label
+ *
+ * Function:
+ *
+ * This routine is called by the GOTO function to compare labels
+ * to see if they match.
+ */
+int
+compare_label(goto_ptr,label_ptr)
+register struct cmd_token *goto_ptr;
+register struct cmd_token *label_ptr;
+{
+char string1[PARSER_STRING_MAX],string2[PARSER_STRING_MAX];
+char *cp1;
+
+ PREAMBLE();
+
+ cp1 = string1;
+
+ *cp1 = '\0';
+ while(goto_ptr){
+ if(cp1 >= &string1[PARSER_STRING_MAX-1]) break;
+ if(goto_ptr->opcode == TOK_C_INPUTCHAR){
+ if(goto_ptr->input_byte == ESCAPE) break;
+ *cp1++ = goto_ptr->input_byte;
+ *cp1 = '\0';
+ }/* End IF */
+ if(goto_ptr->opcode == TOK_C_GOTO_END) break;
+ goto_ptr = goto_ptr->next_token;
+ }/* End While */
+
+ cp1 = string2;
+
+ *cp1 = '\0';
+ while(label_ptr){
+ if(cp1 >= &string2[PARSER_STRING_MAX-1]) break;
+ if(label_ptr->opcode == TOK_C_INPUTCHAR){
+ if(label_ptr->input_byte == '!') break;
+ *cp1++ = label_ptr->input_byte;
+ *cp1 = '\0';
+ }/* End IF */
+ if(label_ptr->opcode == TOK_C_LABEL_END) break;
+ label_ptr = label_ptr->next_token;
+ }/* End While */
+
+ if(strcmp(string1,string2) == 0) return(YES);
+
+ return(NO);
+
+}/* End Routine */
+
+
+
+/* FIND_CONDITIONAL_ELSE - Routine to find the else of a conditional
+ *
+ * Function:
+ *
+ * This routine is called by the conditional expressions when the
+ * specified condition has not been met and we want to execute the
+ * else clause of the conditional. The routine searches until it
+ * finds either the else, or the end of the conditional.
+ */
+int
+find_conditional_else(ct)
+register struct cmd_token *ct;
+{
+ register struct cmd_token *oct;
+
+ PREAMBLE();
+
+ oct = ct;
+ while((oct = oct->next_token)){
+ if(oct->opcode == TOK_C_CONDITIONAL_ELSE){
+ if(oct->ctx.cnest == ct->ctx.cnest){
+ jump_to_token = oct;
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+
+ if(oct->opcode == TOK_C_CONDITIONAL_END){
+ if(oct->ctx.cnest == (ct->ctx.cnest-1)){
+ jump_to_token = oct;
+ return(SUCCESS);
+ }/* End IF */
+ }/* End IF */
+
+ }/* End While */
+ ct->ctx.go_flag = NO;
+ return(BLOCKED);
+
+}/* End Routine */
+
+/* FIND_CONDITIONAL_END - Routine to find the close of a conditional
+ *
+ * Function:
+ *
+ * This routine is called to skip over the else clause and find the
+ * end of the conditional.
+ */
+int
+find_conditional_end(ct)
+register struct cmd_token *ct;
+{
+ register struct cmd_token *oct;
+
+ PREAMBLE();
+
+ oct = ct;
+ while((oct = oct->next_token)){
+ if(oct->opcode != TOK_C_CONDITIONAL_END) continue;
+ if(oct->ctx.cnest != (ct->ctx.cnest-1)) continue;
+ jump_to_token = oct;
+ return(SUCCESS);
+ }/* End While */
+ ct->ctx.go_flag = NO;
+ return(BLOCKED);
+
+}/* End Routine */
+
+
+
+/* EXEC_DOQ0 - Execute Q register zero as a macro
+ *
+ * Function:
+ *
+ * This routine is called to execute Q register zero as a macro on
+ * startup. This allows the user to initialize things from his teco.ini
+ * before teco reaches command level.
+ */
+int
+exec_doq0()
+{
+ register struct buff_header *qbp;
+ register struct undo_token *ut;
+ struct cmd_token *ct;
+
+ PREAMBLE();
+
+/*
+ * Get a pointer to the q-register buffer structure. If it doesn't
+ * exist, declare an error.
+ */
+ qbp = buff_qfind('0',0);
+ if(qbp == NULL){
+ error_message("?Internal Error! Q register 0 disappeared during init");
+ return(FAIL);
+ }/* End IF */
+/*
+ * If the Q register is empty, just return with no error.
+ */
+ if(qbp->zee == 0){
+ return(SUCCESS);
+ }/* End IF */
+/*
+ * Set up a fake command token and undo token structure for the parser to
+ * chain undo blocks off of. We don't ever undo this stuff, but the routine
+ * expects it to be there.
+ */
+ ct = allocate_cmd_token((struct cmd_token *)NULL);
+ if(ct == NULL) return(FAIL);
+
+ ut = allocate_undo_token(ct);
+ if(ut == NULL){
+ free_cmd_token(ct);
+ return(FAIL);
+ }/* End IF */
+
+ ut->opcode = UNDO_C_MACRO;
+ ut->carg1 = NULL;
+
+ tecmacro(qbp,ct,(struct cmd_token **)&ut->carg1);
+ parser_cleanup_ctlist(ct);
+ return(SUCCESS);
+
+}/* End Routine */
diff --git a/tecmem.c b/tecmem.c
new file mode 100644
index 0000000..739265b
--- /dev/null
+++ b/tecmem.c
@@ -0,0 +1,456 @@
+char *tecmem_c_version = "tecmem.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecmem.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecmem.c
+ * Subroutines to manage memory allocation and deallocation
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+
+#if HAVE_SBRK
+#ifndef HAVE_UNISTD_H
+ char *sbrk();
+#endif
+#endif
+
+ char *starting_break;
+
+#ifndef HAVE_UNISTD_H
+ char *malloc();
+ void free();
+ void exit();
+#endif
+
+ extern struct buff_header *curbuf;
+ extern FILE *teco_debug_log;
+
+struct memblock {
+ unsigned char type;
+ int size;
+};
+
+struct memlist {
+ unsigned char type;
+ struct memlist *next_block;
+};
+
+char *malloc_leftover;
+int malloc_leftover_size;
+struct memlist *lookaside_lists[LOOKASIDE_COUNT];
+int lookaside_stat_allocated[LOOKASIDE_COUNT];
+int lookaside_stat_available[LOOKASIDE_COUNT];
+int malloc_stat_call_count,malloc_stat_inuse;
+
+int memstat_by_type[TYPE_C_MAXTYPE-TYPE_C_MINTYPE+1];
+
+void tecmem_verify(unsigned char,char *,char *);
+
+/* TEC_ALLOC - Routine to allocate some memory
+ *
+ * Function:
+ *
+ * This routine is called to request memory
+ */
+char *
+tec_alloc(type,size)
+int type;
+int size;
+{
+int actual_size;
+register int i,j;
+register char *cp;
+register struct memblock *mp;
+register struct memlist *lp;
+extern char outof_memory;
+
+ PREAMBLE();
+
+/*
+ * First we check that the caller is requesting a known memory block
+ * type. This is just a consitency check.
+ */
+ if(type < TYPE_C_MINTYPE || type > TYPE_C_MAXTYPE){
+ printf("\nTECO: UNKNOWN MEMORY BLOCK TYPE %d in TEC_ALLOC\n",type);
+#ifdef UNIX
+ kill(getpid(),SIGQUIT);
+#endif
+ exit(ENOMEM);
+ }/* End IF */
+
+
+#ifdef INSERT_RANDOM_MALLOC_ERRORS
+ {
+ int chance;
+ chance = rand() % 100;
+ if(chance == 0){
+ return(NULL);
+ }/* End IF */
+ }
+#endif /* INSERT_RANDOM_MALLOC_ERRORS */
+
+/*
+ * If the size required is larger than the largest lookaside block we support,
+ * we just call malloc with it.
+ */
+ actual_size = size + sizeof(struct memblock);
+ if(actual_size > LARGEST_LOOKASIDE_BLOCK){
+ mp = (struct memblock *)malloc((unsigned)actual_size);
+
+ if(mp == NULL){
+ outof_memory = YES;
+ return(NULL);
+ }/* End IF */
+
+ malloc_stat_call_count += 1;
+ malloc_stat_inuse += actual_size;
+ mp->type = type;
+ mp->size = actual_size;
+ cp = (char *)mp + sizeof(struct memblock);
+ return(cp);
+ }/* End IF */
+
+/*
+ * Round up to the next larger lookaside block size
+ */
+ if(actual_size & (MINIMUM_ALLOCATION_BLOCK - 1)){
+ actual_size += MINIMUM_ALLOCATION_BLOCK;
+ actual_size &= ~(MINIMUM_ALLOCATION_BLOCK - 1);
+ }/* End IF */
+
+/*
+ * Check whether we have a lookaside entry of that size. If not, we allocate
+ * some number of entries of that size from our large memory hunk.
+ */
+ i = (actual_size / MINIMUM_ALLOCATION_BLOCK) - 1;
+
+ if(lookaside_lists[i] == NULL){
+ tec_gc_lists();
+ }/* End IF */
+
+ if(lookaside_lists[i] == NULL){
+ if(malloc_leftover != NULL){
+ if(malloc_leftover_size < actual_size){
+ i = (malloc_leftover_size / MINIMUM_ALLOCATION_BLOCK) - 1;
+ lp = (struct memlist *)malloc_leftover;
+ lp->next_block = lookaside_lists[i];
+ lookaside_lists[i] = lp;
+ lookaside_stat_available[i] += 1;
+ i = (actual_size / MINIMUM_ALLOCATION_BLOCK) - 1;
+ malloc_leftover = NULL;
+ malloc_leftover_size= 0;
+ }/* End IF */
+ else {
+ lp = (struct memlist *)malloc_leftover;
+ malloc_leftover += actual_size;
+ malloc_leftover_size -= actual_size;
+ if(malloc_leftover_size == 0) malloc_leftover = NULL;
+ lp->next_block = NULL;
+ lookaside_lists[i] = lp;
+ lookaside_stat_available[i] += 1;
+ }/* End Else */
+ }/* End IF */
+ }/* End IF */
+
+ if(lookaside_lists[i] == NULL){
+ j = BIG_MALLOC_HUNK_SIZE / actual_size;
+ cp = malloc((unsigned)(BIG_MALLOC_HUNK_SIZE));
+
+ if(cp == NULL){
+ printf("\nTECO: malloc failed!\n");
+ outof_memory = YES;
+ return(NULL);
+ }/* End IF */
+
+/*
+ * Place the one block on the lookaside list, and the rest as a malloc
+ * leftover.
+ */
+ lp = (struct memlist *)cp;
+ lookaside_stat_available[i] += 1;
+ lp->next_block = NULL;
+ lookaside_lists[i] = lp;
+ malloc_leftover = cp + actual_size;
+ malloc_leftover_size = BIG_MALLOC_HUNK_SIZE - actual_size;
+
+ }/* End IF */
+
+ lp = lookaside_lists[i];
+ lookaside_lists[i] = lp->next_block;
+ lookaside_stat_allocated[i] += 1;
+ lookaside_stat_available[i] -= 1;
+ memstat_by_type[type-TYPE_C_MINTYPE] += 1;
+
+ mp = (struct memblock *)lp;
+ mp->type = type;
+ mp->size = actual_size;
+ cp = (char *)mp + sizeof(struct memblock);
+ bzero(cp,size);
+ return(cp);
+
+}/* End Routine */
+
+/* TEC_RELEASE - Envelope routine for free()
+ *
+ * Function:
+ *
+ * This routine is called to release memory which was previously allocated
+ * by calling the tec_alloc routine.
+ */
+void
+tec_release(type,addr)
+unsigned char type;
+register char *addr;
+{
+register struct memblock *mp;
+register struct memlist *lp;
+register int i;
+
+ PREAMBLE();
+
+ mp = (struct memblock *)( addr - sizeof(struct memblock) );
+ if(mp->type != type){
+ printf("\nTEC_RELEASE: TYPE Mismatch: Supplied %d Stored %d addr 0x%x\n",
+ type,mp->type,(unsigned int)addr);
+#ifdef UNIX
+ kill(getpid(),SIGQUIT);
+#endif
+ exit(ENOMEM);
+ }/* End IF */
+
+ if(mp->size > LARGEST_LOOKASIDE_BLOCK){
+ malloc_stat_inuse -= mp->size;
+ free(mp);
+ return;
+ }/* End IF */
+
+ mp->type += 1000;
+ i = mp->size / MINIMUM_ALLOCATION_BLOCK - 1;
+ lp = (struct memlist *)mp;
+ lp->next_block = lookaside_lists[i];
+ lookaside_lists[i] = lp;
+
+ lookaside_stat_available[i] += 1;
+ lookaside_stat_allocated[i] -= 1;
+ memstat_by_type[type-TYPE_C_MINTYPE] -= 1;
+
+ return;
+
+}/* End Routine */
+
+
+
+/* TECMEM_VERIFY - Verify that structure type hasn't been corrupted
+ *
+ * Function:
+ *
+ * This debug routine checks to see whether the memory block has been
+ * overwritten by checking the type code.
+ */
+void
+tecmem_verify(type,addr,message)
+unsigned char type;
+register char *addr;
+char *message;
+{
+register struct memblock *mp;
+#if 0
+register struct memlist *lp;
+#endif
+#if 0
+register int i;
+#endif
+
+ PREAMBLE();
+
+ mp = (struct memblock *)( addr - sizeof(struct memblock) );
+ if(mp->type != type){
+ printf(
+ "\nTYPE Mismatch: Supplied %d Stored %d addr %x '%s'\n",
+ type,mp->type,(unsigned int)addr,message);
+ exit(1);
+ }/* End IF */
+
+ return;
+
+}/* End Routine */
+
+
+
+/* TEC_GC_LISTS - Garbage collect any local lookaside lists
+ *
+ * Function:
+ *
+ * This routine is called before we call malloc for more memory in
+ * an attempt to find the memory on some local lookaside lists.
+ */
+void
+tec_gc_lists()
+{
+
+ PREAMBLE();
+
+ screen_deallocate_format_lookaside_list();
+ buff_deallocate_line_buffer_lookaside_list();
+
+}/* End Routine */
+
+
+void
+initialize_memory_stats()
+{
+ starting_break = 0;
+
+#if HAVE_SBRK
+ if(starting_break == NULL){
+ starting_break = sbrk(0);
+ }/* End IF */
+#endif
+
+}
+
+
+/* TECMEM_STATS - Insert memory statistics into the current buffer
+ *
+ * Function:
+ *
+ * This routine is called by the buffer map routine to allow us to
+ * insert some memory statistics into the map.
+ */
+void
+tecmem_stats()
+{
+char tmp_buffer[LINE_BUFFER_SIZE];
+register int i;
+register int size;
+int total_memory_in_use;
+char *current_break;
+int bss_in_use;
+
+ PREAMBLE();
+
+ total_memory_in_use = malloc_stat_inuse;
+
+ sprintf(
+ tmp_buffer,
+ "\n\n%d non-lookaside allocations, %d bytes outstanding\n\n",
+ malloc_stat_call_count,
+ malloc_stat_inuse
+ );
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+ for(i = 0; i < LOOKASIDE_COUNT; i++){
+ if(lookaside_stat_allocated[i] == 0 &&
+ lookaside_stat_available[i] == 0) continue;
+ size = ( i + 1 ) * MINIMUM_ALLOCATION_BLOCK;
+ total_memory_in_use += lookaside_stat_allocated[i] * size;
+
+ sprintf(
+ tmp_buffer,
+ "LA%2d, size %4d, bytes in use %6d, bytes available %6d\n",
+ i,
+ size,
+ lookaside_stat_allocated[i] * size,
+ lookaside_stat_available[i] * size
+ );
+
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+ }/* End FOR */
+
+ sprintf(
+ tmp_buffer,
+ "Malloc Leftover %d bytes\n",
+ malloc_leftover_size
+ );
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+ buff_insert(curbuf,curbuf->dot,"\n",1);
+
+ for(i = 0; i < TYPE_C_MAXTYPE-TYPE_C_MINTYPE; i++){
+ static char *type_name_list[] = {
+ "CBUFF",
+ "CPERM",
+ "CMD",
+ "UNDO",
+ "SCR",
+ "SCRBUF",
+ "SCREEN",
+ "SCREENBUF",
+ "LINE",
+ "LINEBUF",
+ "BHDR",
+ "WINDOW",
+ "LABELFIELD",
+ "WILD",
+ "TAGS",
+ "TAGENT",
+ "TAGSTR",
+ "MAXTYPE"
+ };
+
+ if(i >= ELEMENTS(type_name_list)){
+ sprintf(
+ tmp_buffer,
+ "Unknown memory type index %d(%d)\n",
+ i,
+ i+TYPE_C_MINTYPE
+ );
+ }/* End IF */
+
+ else {
+ sprintf(
+ tmp_buffer,
+ "Type %10s, blocks in use %d\n",
+ type_name_list[i],
+ memstat_by_type[i]
+ );
+ }/* End Else */
+
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+ }/* End FOR */
+
+#if HAVE_SBRK
+ current_break = sbrk(0);
+ if(current_break != sbrk(0)){
+ tec_panic("sbrk(0) seems to be allocating space!\n");
+ }/* End IF */
+ bss_in_use = current_break - starting_break;
+#else
+ bss_in_use = 0;
+#endif /* UNIX */
+
+ if( bss_in_use ){
+ sprintf(
+ tmp_buffer,
+ "\nTotal memory in use %d, Total allocated bss %d\n",
+ total_memory_in_use,
+ bss_in_use
+ );
+ } else {
+ sprintf(tmp_buffer,"\nTotal memory in use %d\n",total_memory_in_use);
+ }
+ buff_insert(curbuf,curbuf->dot,tmp_buffer,strlen(tmp_buffer));
+
+}/* End Routine */
diff --git a/teco.c b/teco.c
new file mode 100644
index 0000000..b2a9443
--- /dev/null
+++ b/teco.c
@@ -0,0 +1,1888 @@
+char *teco_c_version = "teco.c: $Revision: 1.1 $";
+char *copyright = "Copyright (c) 1985-2003 Paul Cantrell & Joyce Nishinaga";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/teco.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* teco.c
+ * Main TECO entry point with most of the initialization code
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+/*
+ * Global Storage is defined here for lack of a better place.
+ * First off are all the flags.
+ */
+ char readonly_flag = NO;
+ char piped_input_flag = NO;
+ char piped_output_flag = NO;
+ char tty_modes_saved = NO;
+ char exit_flag = NO;
+ char intr_flag = 0;
+ char susp_flag = 0;
+ char input_pending_flag = NO;
+ int tab_width = NORMAL_TAB_WIDTH;
+ char alternate_escape_character = ESCAPE;
+ char main_delete_character = RUBOUT;
+ char alternate_delete_character = RUBOUT;
+ char eight_bit_output_flag = NO;
+ char hide_cr_flag = NO;
+ char waiting_for_input_flag = NO;
+ char resize_flag = NO;
+ char teco_startup = YES;
+ char screen_startup = YES;
+ char checkpoint_flag = NO;
+ char checkpoint_enabled = YES;
+ char checkpoint_modified = NO;
+ char outof_memory = NO;
+ char suspend_is_okay_flag = YES;
+
+/*
+ * Global Variables
+ */
+ int tty_input_chan;
+ int tty_output_chan;
+ int term_speed;
+ FILE *teco_debug_log;
+
+ int term_columns;
+ int term_lines;
+ int forced_width,forced_height;
+
+ int checkpoint_interval = DEFAULT_CHECKPOINT;
+
+ char *output_tty_name;
+/*
+ * Table of Bit Positions
+ */
+ unsigned int IntBits[BITS_PER_INT];
+
+/*
+ * Table of Spaces for tab expansion
+ */
+ char tab_expand[MAX_TAB_WIDTH+1];
+/*
+ * Global structures
+ */
+#if HAVE_TERMIOS_H
+ static struct termios tty_input_modes;
+ static struct termios tty_output_modes;
+#else
+#ifdef ATT_UNIX
+ static struct termio tty_input_modes;
+ static struct termio tty_output_modes;
+#endif
+#ifdef BSD_UNIX
+ static struct sgttyb tty_input_modes;
+ static struct sgttyb tty_output_modes;
+#endif
+#endif
+
+/*
+ * External Variables
+ */
+
+/*
+ * Prototypes
+ */
+ void init_signals(void);
+ void teco_ini(void);
+ void process_directory(char *,char*,int);
+ int match_name(char *,char *);
+ int map_baud(int);
+
+
+
+/* TECO - Text Edit and COrrector
+ *
+ * Function:
+ *
+ * This is the entry point for TECO. It initializes the terminal, reads
+ * in the specified files, and generally gets things going.
+ */
+int
+main(argc,argv)
+int argc;
+char **argv;
+{
+register int i;
+
+ initialize_memory_stats();
+
+ for(i = 0; i < BITS_PER_INT; i++){
+ IntBits[i] = 1 << i;
+ }/* End FOR */
+ for(i = 0; i < MAX_TAB_WIDTH; i++){
+ tab_expand[i] = ' ';
+ }/* End FOR */
+ tab_expand[MAX_TAB_WIDTH] = '\0';
+
+ if(buff_init() == FAIL){
+ tec_error(ENOMEM,"Unable to initialize buffers");
+ }/* End IF */
+
+ handle_command_line(0,argc,argv);
+
+ init_signals();
+ initialize_tty();
+
+ if(strcmp(argv[0],TECO_READONLY_NAME) == 0){
+ readonly_flag = YES;
+ }/* End IF */
+
+ handle_command_line(1,argc,argv);
+
+ teco_ini();
+
+ teco_startup = NO;
+
+ while(exit_flag == NO){
+ tecparse();
+ intr_flag = 0;
+ }/* End While */
+
+ if(piped_output_flag){
+ extern struct buff_header *curbuf;
+ buff_openbuffnum(-1,0);
+ buff_write(curbuf,fileno(stdout),0,curbuf->zee);
+ }/* End IF */
+
+ restore_tty();
+/*
+ * Output a final newline to get the terminal onto a fresh
+ * line.
+ */
+ term_putc('\n');
+ term_flush();
+
+#ifdef CHECKPOINT
+ remove_checkpoint_file();
+#endif /* CHECKPOINT */
+
+#ifdef UNIX
+ return(0);
+#endif
+
+#ifdef VMS
+ return(STS$M_SUCCESS);
+#endif
+
+}/* End Routine */
+
+
+
+#ifdef UNIX
+
+/* INITIALIZE_TTY - Set the tty up the way we want it
+ *
+ * Function:
+ *
+ * This routine is called at startup time to set up the terminal the way
+ * that we require, i.e., it sets the tty modes. It also gives the screen
+ * package a chance to initialize itself.
+ */
+void
+initialize_tty()
+{
+#if HAVE_TERMIOS_H
+ struct termios tmp_tty_modes;
+#else
+#ifdef ATT_UNIX
+#define GET_IOCTL TCGETA
+#define SET_IOCTL TCSETA
+ struct termio tmp_tty_modes;
+#endif
+#ifdef BSD_UNIX
+#define GET_IOCTL TIOCGETP
+#define SET_IOCTL TIOCSETN
+#ifdef INTEGRATED_SOLUTIONS
+ int mask;
+#endif /* INTEGRATED_SOLUTIONS */
+
+ struct sgttyb tmp_tty_modes;
+#endif /* BSD_UNIX */
+#endif /* HAVE_TERMIOS_H */
+
+#ifdef SUN_STYLE_WINDOW_SIZING
+ struct winsize os_window;
+#endif
+
+/*
+ * We start by assuming that standard input is a tty device. If it turns out
+ * that it is not, however, then we want to use /dev/tty as the keyboard.
+ */
+ if(!isatty(tty_input_chan = fileno(stdin))){
+ tty_input_chan = open("/dev/tty",O_RDONLY);
+ piped_input_flag = YES;
+ if(tty_input_chan < 0){
+ perror("cannot open /dev/tty");
+ punt(errno);
+ }/* End IF */
+ }/* End IF */
+/*
+ * Standard output MUST by a tty at this time.
+ */
+ if(output_tty_name){
+ tty_output_chan = open(output_tty_name,O_RDWR);
+ }/* End IF */
+
+ else {
+ tty_output_chan = fileno(stdout);
+ }/* End Else */
+
+ if(!isatty(tty_output_chan)){
+ tty_output_chan = open("/dev/tty",O_RDWR);
+ piped_output_flag = YES;
+ if(tty_output_chan < 0){
+ perror("cannot open /dev/tty");
+ punt(errno);
+ }/* End IF */
+ }/* End IF */
+/*
+ * Get the current modes of the tty so it can be restored later.
+ */
+#if HAVE_TERMIOS_H
+ if(tcgetattr(tty_input_chan,&tty_input_modes) < 0){
+ perror("could not get input tty modes");
+ punt(errno);
+ }/* End IF */
+
+ if(tcgetattr(tty_output_chan,&tty_output_modes) < 0){
+ perror("could not get output tty modes");
+ punt(errno);
+ }/* End IF */
+
+#else
+ if(ioctl(tty_input_chan,GET_IOCTL,&tty_input_modes) < 0){
+ perror("could not get input tty modes");
+ punt(errno);
+ }/* End IF */
+
+ if(ioctl(tty_output_chan,GET_IOCTL,&tty_output_modes) < 0){
+ perror("could not get output tty modes");
+ punt(errno);
+ }/* End IF */
+
+#endif
+
+#if 0 /* Turned off because xterm is getting clobbered by this */
+#if defined(ATT_UNIX) || defined(_POSIX_SOURCE)
+ if((tty_output_modes.c_cflag & CSIZE) == CS8){
+ eight_bit_output_flag = YES;
+ }/* End IF */
+#endif
+#endif
+
+#if defined(HAVE_DECL_CFGETOSPEED) && HAVE_DECL_CFGETOSPEED
+ term_speed = map_baud( cfgetospeed(&tty_output_modes) );
+#else
+# if defined(HAVE_STRUCT_TERMIOS_C_CFLAG) && defined(CBAUD)
+ term_speed = map_baud(tty_output_modes.c_cflag & CBAUD);
+# else
+# if HAVE_STRUCT_TERMIOS_C_OSPEED
+ term_speed = map_baud(tty_output_modes.c_ospeed);
+#else
+# if HAVE_STRUCT_TERMIOS_SG_OSPEED
+ term_speed = map_baud(tty_output_modes.sg_ospeed);
+# endif
+# endif
+# endif
+#endif
+
+#if HAVE_TERMIOS_H
+ main_delete_character = tty_input_modes.c_cc[VERASE];
+#else
+#if defined(BSD_UNIX)
+ main_delete_character = tty_input_modes.sg_erase;
+#endif
+#endif
+
+ tty_modes_saved = YES;
+/*
+ * Check out the termcap description for this tty now. It makes
+ * no sense to go changing the terminal modes all around until
+ * we decide whether we can run on this terminal or not.
+ */
+ init_term_description();
+/*
+ * If the OS supports window size ioctls, we try for that since it
+ * will tend to be more correct for a window environment.
+ */
+#ifdef SUN_STYLE_WINDOW_SIZING
+ if(ioctl(tty_output_chan,TIOCGWINSZ,&os_window) >= 0){
+
+ if((unsigned)os_window.ws_row >= SCREEN_MAX_LINES){
+ fprintf(stderr,"os_window.ws_row %d os_window.ws_col %d\n",
+ (int)os_window.ws_row,(int)os_window.ws_col);
+ fprintf(stderr,"term_lines %d term_columns %d\n",
+ term_lines,term_columns);
+ fprintf(stderr,"forced_height %d forced_width %d\n",
+ forced_height,forced_width);
+ os_window.ws_row = term_lines;
+ os_window.ws_col = term_columns;
+ }/* End IF */
+
+ if(forced_height > 0) os_window.ws_row = forced_height;
+ if(forced_width > 0) os_window.ws_col = forced_width;
+ if(teco_startup == YES){
+ term_lines = os_window.ws_row;
+ term_columns = os_window.ws_col;
+ }/* End IF */
+ if(term_lines == 0) term_lines = 24;
+ if(term_columns == 0) term_columns = 80;
+ if(term_lines != os_window.ws_row || term_columns != os_window.ws_col){
+ screen_resize();
+ }/* End IF */
+ }/* End IF */
+#else
+ if(forced_height > 0) term_lines = forced_height;
+ if(forced_width > 0) term_columns = forced_width;
+ if(term_lines == 0) term_lines = 24;
+ if(term_columns == 0) term_columns = 80;
+#endif
+
+ if(term_lines >= SCREEN_MAX_LINES){
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(
+ tmp_message,
+ "terminal line count of %d exceeds maximum of %d",
+ term_lines,
+ SCREEN_MAX_LINES
+ );
+ tec_error(E2BIG,tmp_message);
+ }/* End IF */
+
+/*
+ * Now set the modes that we want
+ */
+ tmp_tty_modes = tty_input_modes;
+
+#if HAVE_TERMIOS_H
+ tmp_tty_modes.c_lflag &= ~(ICANON | ECHO);
+ tmp_tty_modes.c_cc[VMIN] = 1;
+ tmp_tty_modes.c_cc[VTIME] = 1;
+#else
+#ifdef SYS_III_UNIX
+ tmp_tty_modes.c_lflag &= ~(ICANON | ECHO | ISTATUS);
+ tmp_tty_modes.c_cc[VMIN] = 1;
+ tmp_tty_modes.c_cc[VTIME] = 1;
+#endif
+
+#ifdef BSD_UNIX
+ tmp_tty_modes.sg_flags |= CBREAK;
+ tmp_tty_modes.sg_flags &= ~(ECHO);
+#endif
+#endif /* SYSV or POSIX */
+
+#if HAVE_TERMIOS_H
+ if(tcsetattr(tty_input_chan,TCSANOW,&tmp_tty_modes) < 0){
+ perror("could not set input tty modes");
+ punt(errno);
+ }/* End IF */
+#else
+ if(ioctl(tty_input_chan,SET_IOCTL,&tmp_tty_modes) < 0){
+ perror("could not set input tty modes");
+ punt(errno);
+ }/* End IF */
+#endif
+
+#ifdef INTEGRATED_SOLUTIONS
+ mask = LLITOUT;
+ ioctl(tty_output_chan,TIOCLBIS,&mask);
+#endif
+
+ screen_init();
+
+}/* End Routine */
+
+#endif /* UNIX */
+
+/* END OF UNIX CONDITIONAL CODE */
+
+
+
+#if !HAVE_TCGETATTR
+
+/* TCGETATTR - Mimic the POSIX terminal get/set functions
+ *
+ * Function:
+ *
+ * For a system being compiled with POSIX headers, but which doesn't
+ * actually have all the POSIX functions in libc, we have to fake up
+ * this call.
+ */
+int
+tcgetattr(fd,termios_p)
+int fd;
+struct termios *termios_p;
+{
+struct termio tmp_modes;
+register int i;
+int status;
+
+ bzero(&tmp_modes,sizeof(tmp_modes));
+ if(status = ioctl(fd,TCGETA,&tmp_modes) < 0){
+ return(-1);
+ }/* End IF */
+
+ termios_p->c_iflag = tmp_modes.c_iflag;
+ termios_p->c_oflag = tmp_modes.c_oflag;
+ termios_p->c_cflag = tmp_modes.c_cflag;
+ termios_p->c_lflag = tmp_modes.c_lflag;
+ termios_p->c_line = tmp_modes.c_line;
+ for(i = 0; i < (NCCS < NCC ? NCCS : NCC); i++){
+ termios_p->c_cc[i] = tmp_modes.c_cc[i];
+ }/* End FOR */
+
+ return(status);
+
+}/* End Routine */
+
+#endif /* !HAVE_TCGETATTR */
+
+#if !HAVE_TCSETATTR
+
+/* TCSETATTR - Mimic the POSIX terminal get/set functions
+ *
+ * Function:
+ *
+ * For a system being compiled with POSIX headers, but which doesn't
+ * actually have all the POSIX functions in libc, we have to fake up
+ * this call.
+ */
+tcsetattr(fd,flags,termios_p)
+int fd;
+int flags;
+const struct termios *termios_p;
+{
+struct termio tmp_modes;
+register int i;
+int status;
+
+ bzero(&tmp_modes,sizeof(tmp_modes));
+ tmp_modes.c_iflag = termios_p->c_iflag;
+ tmp_modes.c_oflag = termios_p->c_oflag;
+ tmp_modes.c_cflag = termios_p->c_cflag;
+ tmp_modes.c_lflag = termios_p->c_lflag;
+ tmp_modes.c_line = termios_p->c_line;
+ for(i = 0; i < (NCCS < NCC ? NCCS : NCC); i++){
+ tmp_modes.c_cc[i] = termios_p->c_cc[i];
+ }/* End FOR */
+ if(status = ioctl(fd,TCSETA,&tmp_modes) < 0){
+ return(-1);
+ }/* End IF */
+
+ return(status);
+
+}/* End Routine */
+
+#endif /* !HAVE_TCSETATTR */
+
+#ifdef VMS
+
+
+/* INITIALIZE_TTY - Set the tty up the way we want it
+ *
+ * Function:
+ *
+ * This routine is called at startup time to set up the terminal the way
+ * that we require, i.e., it sets the tty modes. It also gives the screen
+ * package a chance to initialize itself.
+ */
+void
+initialize_tty()
+{
+register int status;
+$DESCRIPTOR(input_name,"SYS$INPUT");
+$DESCRIPTOR(output_name,"SYS$OUTPUT");
+
+struct sense_mode_buffer {
+ unsigned char class;
+ unsigned char type;
+ unsigned short buffer_size;
+ unsigned char characteristics[3];
+ unsigned char page_length;
+} sense_buffer;
+
+struct sense_iosb {
+ unsigned short status;
+ unsigned char transmit_speed;
+ unsigned char receive_speed;
+ unsigned char cr_fill_count;
+ unsigned char lf_fill_count;
+ unsigned char parity_flags;
+ unsigned char unused;
+} sense_iosb;
+
+/*
+ * Assign channels to the terminal
+ */
+ status = sys$assign(&input_name,&tty_input_chan,0,0);
+ if(!(status & STS$M_SUCCESS)){
+ tec_error(status,"VTECO: ASSIGN of SYS$INPUT Failed");
+ }/* End IF */
+
+/*
+ * Standard output MUST by a tty at this time.
+ */
+ status = sys$assign(&output_name,&tty_output_chan,0,0);
+ if(!(status & STS$M_SUCCESS)){
+ tec_error(status,"VTECO: ASSIGN of SYS$OUTPUT Failed");
+ }/* End IF */
+
+/*
+ * Get the baud rate of the terminal
+ */
+ status = sys$qiow(0,tty_output_chan,IO$_SENSEMODE,
+ &sense_iosb,0,0,&sense_buffer,sizeof(sense_buffer),0,0,0,0);
+ if(!(status & STS$M_SUCCESS)){
+ tec_error(status,"VTECO: Error on sense mode %d (%x)",status,status);
+ }/* End IF */
+
+ term_lines = sense_buffer.page_length;
+ term_columns = sense_buffer.buffer_size;
+
+ term_speed = map_baud(sense_iosb.transmit_speed);
+ tty_modes_saved = YES;
+
+/*
+ * Check out the termcap description for this tty now. It makes
+ * no sense to go changing the terminal modes all around until
+ * we decide whether we can run on this terminal or not.
+ */
+ init_term_description();
+/*
+ * Now set the modes that we want
+ */
+ screen_init();
+
+}/* End Routine */
+
+/* END OF VMS CONDITIONAL CODE */
+
+#endif
+
+
+
+/* RESTORE_TTY - Set the tty back to the way that it was
+ *
+ * Function:
+ *
+ * This routine is called at exit time to set the terminal modes back
+ * how they were when we started.
+ */
+void
+restore_tty()
+{
+
+/*
+ * Give the screen package a chance to cleanup
+ */
+ screen_finish();
+
+/*
+ * Can't restore the terminal modes if they haven't been saved yet
+ */
+ if(tty_modes_saved != YES) return;
+
+/*
+ * Restore the modes that we saved away previously
+ */
+#if HAVE_TERMIOS_H
+ if(tcsetattr(tty_input_chan,TCSADRAIN,&tty_input_modes) < 0){
+ perror("could not set input tty modes");
+ punt(errno);
+ }/* End IF */
+ if(tcsetattr(tty_output_chan,TCSADRAIN,&tty_output_modes) < 0){
+ perror("could not set output tty modes");
+ punt(errno);
+ }/* End IF */
+#else
+#ifdef UNIX
+ if(ioctl(tty_input_chan,SET_IOCTL,&tty_input_modes) < 0){
+ perror("could not restore input tty modes");
+ }/* End IF */
+
+ if(ioctl(tty_output_chan,SET_IOCTL,&tty_output_modes) < 0){
+ perror("could not restore output tty modes");
+ }/* End IF */
+#endif
+#endif
+
+}/* End Routine */
+
+
+
+/* TTY_INPUT_PENDING - Check if there is input waiting on the cmd channel
+ *
+ * Function:
+ *
+ * This routine returns a true false indication of whether or not there
+ * is input waiting on the command channel. This generally gets used to
+ * set the input_pending_flag variable. Now it is set to only work if
+ * the baud rate is quite low, since some of the current side effects are
+ * undesirable.
+ */
+int
+tty_input_pending()
+{
+#ifdef HAS_FIONREAD
+long temp;
+#endif
+
+#ifdef VMS
+struct sense_typeahead_buffer {
+ unsigned short count;
+ unsigned char first_char;
+ unsigned char reserved1;
+ unsigned long reserved2;
+} sense_typeahead_buffer;
+#endif
+
+#ifdef SEQUOIA
+struct sequoia_weirdo_get_param_buffer {
+ unsigned short fc_open_mode;
+ unsigned long fc_num_bytes_remaining;
+ char fc_no_delay;
+ char fc_modify_no_delay;
+ char fc_append;
+ char fc_modify_append;
+ char fc_close_across_exec;
+ char fc_modify_close_across_exec;
+ char fc_no_further_opens;
+ char fc_modify_no_further_opens;
+}term_params;
+
+typedef enum
+{
+ FC_SET_PARAMETERS,
+ FC_GET_PARAMETERS,
+ FC_SET_BUFFER_REPLACEMENT_PARAMETERS,
+ FC_GET_BUFFER_REPLACEMENT_PARAMETERS,
+} FILE_CONTROL_REQUEST;
+#endif
+
+#ifdef SEQUOIA
+int err;
+ err = seqksr_file_control(&term_params,tty_input_chan,FC_GET_PARAMETERS);
+ if(err == 0 && term_params.fc_num_bytes_remaining != 0) return(YES);
+#endif
+
+#ifdef HAS_POLL
+ int err;
+ struct pollfd pollStructs[1];
+#endif
+
+#ifdef HAS_SELECT
+ int err;
+ fd_set theFds;
+ struct timeval zeroTime;
+#endif
+
+ PREAMBLE();
+
+#ifdef HAS_SELECT
+ FD_ZERO( &theFds );
+ FD_SET( tty_input_chan, &theFds );
+ zeroTime.tv_sec = 0;
+ zeroTime.tv_usec = 0;
+ err = select( tty_input_chan+1, &theFds, 0, 0, &zeroTime );
+ if(err > 0)
+ {
+ return(YES);
+ }
+#endif
+
+#ifdef HAS_POLL
+ pollStructs[0].fd = tty_input_chan;
+ pollStructs[0].events = POLLIN;
+ pollStructs[0].revents = 0;
+ err = poll( pollStructs, ELEMENTS(pollStructs), 0 );
+ if( err >= 0 && pollStructs[0].revents & POLLIN) return(YES);
+#endif
+
+#ifdef HAS_FIONREAD
+ temp = 0;
+ if(ioctl(tty_input_chan,FIONREAD,&temp) < 0) return(NO);
+ if(temp > 0) return(YES);
+#endif
+
+#ifdef VMS
+ temp = sys$qiow(0,tty_input_chan,IO$_SENSEMODE|IO$M_TYPEAHDCNT,
+ 0,0,0,&sense_typeahead_buffer,sizeof(sense_typeahead_buffer),0,0,0,0);
+ if(!(temp & STS$M_SUCCESS)){
+ return(NO);
+ }/* End IF */
+
+ if(sense_typeahead_buffer.count > 0) return(YES);
+
+#endif
+
+ return(NO);
+
+}/* End Routine */
+
+
+
+/* INIT_SIGNALS - Initialize the signal interface for our use
+ *
+ * Function:
+ *
+ * This routine sets up the way that we want signals to behave
+ */
+void
+init_signals()
+{
+
+#if 0
+int cmd_suspend();
+#endif
+int cmd_interrupt();
+int cmd_alarm();
+int cmd_winch();
+
+ PREAMBLE();
+
+#ifdef CHECKPOINT
+ signal(SIGALRM,(void (*)())cmd_alarm);
+ alarm(checkpoint_interval);
+#endif /* CHECKPOINT */
+
+ signal(SIGINT,(void (*)())cmd_interrupt);
+
+#ifdef JOB_CONTROL
+ if(suspend_is_okay_flag == YES){
+ signal(SIGTSTP,(void (*)())cmd_suspend);
+ }
+#endif
+
+#ifdef SUN_STYLE_WINDOW_SIZING
+ signal(SIGWINCH,(void (*)())cmd_winch);
+#endif
+
+}/* End Routine */
+
+
+
+/* TECO_INI - Routine to read the TECO_INI file
+ *
+ * Function:
+ *
+ * This routine reads the user's TECO_INI file and sets up the default
+ * Q-Register contents he has specified. It also executes Q-Register 0.
+ */
+void
+teco_ini()
+{
+register char *cp;
+register int c,dc;
+register int i;
+char filename[LINE_BUFFER_SIZE];
+char tmp_message[LINE_BUFFER_SIZE];
+FILE *fp;
+struct buff_header *hbp;
+char zero_flag = NO;
+char comment_flag = NO;
+
+ PREAMBLE();
+
+#ifdef UNIX
+
+ if((cp = getenv("HOME"))){
+ (void) strcpy(filename,cp);
+ }/* End IF */
+
+ else return;
+
+ (void) strcat(filename,"/.teco_ini");
+
+#endif
+
+#ifdef VMS
+
+ strcpy(filename,"SYS$LOGIN:TECO.INI");
+
+#endif
+
+ fp = fopen(filename,"r");
+
+ if(fp == NULL) return;
+
+/*
+ * Outer loop reads the name of the Q-Register to be loaded and the delimiter,
+ * inner loop reads contents of a Q-Register in.
+ */
+ while(1){
+ while(1){
+ if((c = getc(fp)) == EOF){
+ fclose(fp);
+ if(zero_flag) exec_doq0();
+ return;
+ }/* End IF */
+
+ if(comment_flag){
+ if(c == '!') comment_flag = NO;
+ continue;
+ }/* End IF */
+
+ if(c == '!'){
+ comment_flag = YES;
+ continue;
+ }/* End IF */
+
+ if(isspace(c)) continue;
+ break;
+
+ }/* End While */
+
+ if(c == '0') zero_flag = YES;
+
+ hbp = buff_qfind(c,1);
+ if(hbp == NULL){
+ fclose(fp);
+ return;
+ }/* End IF */
+
+ if((dc = getc(fp)) == EOF){
+ sprintf(tmp_message,"?EOF while loading Q-REGISTER <%c>",c);
+ error_message(tmp_message);
+ fclose(fp);
+ return;
+ }/* End IF */
+
+ while(1){
+ if((i = getc(fp)) == EOF){
+ sprintf(tmp_message,"?EOF while loading Q-REGISTER <%c>",c);
+ error_message(tmp_message);
+ fclose(fp);
+ return;
+ }/* End IF */
+
+ if(i == dc) break;
+
+ buff_insert_char(hbp,hbp->zee,i);
+
+ }/* End While */
+
+ hbp->ismodified = NO;
+
+ }/* End While */
+
+}/* End Routine */
+
+
+
+void
+check_for_forced_screen_size(argc,argv)
+int argc;
+char **argv;
+{
+register int i;
+char *cp, c;
+
+ PREAMBLE();
+
+ for(i = 1; i < argc; i++){
+ if(argv[i][0] == '-'){
+ cp = &argv[i][1];
+ while((c = *cp++)){
+ switch(c){
+ case 'X': case 'x':
+ case 'W': case 'w':
+ if(isdigit((int)*cp)){
+ forced_width = 0;
+ while(isdigit((int)*cp)){
+ forced_width = forced_width * 10 + *cp++ - '0';
+ }/* End While */
+ }/* End IF */
+ else if( ((i+1) < argc) && isdigit((int)argv[i+1][0])){
+ forced_width = atoi(argv[i+1]);
+ i++;
+ }
+ else forced_width = 80;
+ break;
+ case 'Y': case 'y':
+ case 'H': case 'h':
+ if(isdigit((int)*cp)){
+ forced_height = 0;
+ while(isdigit((int)*cp)){
+ forced_height = forced_height * 10 +
+ *cp++ - '0';
+ }/* End While */
+ }/* End IF */
+ else if( ((i+1) < argc) && isdigit((int)argv[i+1][0])){
+ forced_height = atoi(argv[i+1]);
+ i++;
+ }
+ else forced_height = 24;
+ break;
+ case 'O': case 'o':
+ output_tty_name = argv[i+1];
+ i++;
+ break;
+ }/* End Switch */
+ }/* End While */
+ continue;
+ }/* End IF */
+
+ }/* End FOR */
+
+}/* End Routine */
+
+
+
+/* HANDLE_COMMAND_LINE - OS Dependent Code to handle command line arguments
+ *
+ * Function:
+ *
+ * This code handles the command line arguments to VTECO. This requires
+ * different processing for different operating systems.
+ */
+#ifdef UNIX
+int
+handle_command_line(which_time,argc,argv)
+int which_time;
+int argc;
+char **argv;
+{
+register int i;
+extern struct buff_header *curbuf;
+int first_file_argument;
+char *cp, c;
+int arg_skip;
+
+ PREAMBLE();
+
+ first_file_argument = 0;
+ for(i = 1; i < argc; i += arg_skip){
+ arg_skip = 1;
+ if(argv[i][0] == '-'){
+ cp = &argv[i][1];
+ while((c = *cp++)){
+ switch(c){
+ case 'C': case 'c':
+ checkpoint_enabled = NO;
+ break;
+ case 'X': case 'x':
+ case 'W': case 'w':
+ if(isdigit((int)*cp)){
+ forced_width = 0;
+ while(isdigit((int)*cp)){
+ forced_width = forced_width * 10 + *cp++ - '0';
+ }/* End While */
+ }/* End IF */
+ else if( ((i+1) < argc) && isdigit((int)argv[i+1][0])){
+ forced_width = atoi(argv[i+1]);
+ arg_skip++;
+ }
+ else forced_width = 80;
+ break;
+ case 'Y': case 'y':
+ case 'H': case 'h':
+ if(isdigit((int)*cp)){
+ forced_height = 0;
+ while(isdigit((int)*cp)){
+ forced_height = forced_height * 10 +
+ *cp++ - '0';
+ }/* End While */
+ }/* End IF */
+ else if( ((i+1) < argc) && isdigit((int)argv[i+1][0])){
+ forced_height = atoi(argv[i+1]);
+ i++;
+ }
+ else forced_height = 24;
+ break;
+ case 'O': case 'o':
+ output_tty_name = argv[i+1];
+ arg_skip++;
+ break;
+ default:
+ fprintf(stderr,"teco: unknown switch '%c'\n",c);
+ cp = "";
+ break;
+ }/* End Switch */
+ }/* End While */
+ continue;
+ }/* End IF */
+
+ if(which_time == 0) return( SUCCESS );
+
+ if(!first_file_argument) first_file_argument = i;
+ if(!outof_memory){
+ buff_openbuffer(argv[i],0,readonly_flag);
+ }/* End IF */
+
+ }/* End FOR */
+
+ if(first_file_argument){
+ buff_openbuffer(argv[first_file_argument],0,readonly_flag);
+ }/* End IF */
+
+ if(piped_input_flag){
+ buff_openbuffnum(-1,0);
+ buff_readfd(curbuf,"Standard Input",fileno(stdin));
+ curbuf->dot = 0;
+ }/* End IF */
+
+ return( SUCCESS );
+
+}/* End Routine */
+#endif
+
+#ifdef VMS
+
+int
+handle_command_line(which_time,argc,argv)
+int which_time;
+int argc;
+char **argv;
+{
+register int i,j;
+int count;
+register char *cp,*dp;
+struct wildcard_expansion *name_list,*np;
+struct wildcard_expansion *expand_filename();
+int number_of_buffers_opened = 0;
+char switch_buffer[TECO_FILENAME_TOTAL_LENGTH];
+char *command_line;
+struct dsc$descriptor command_desc;
+struct dsc$descriptor symbol_value;
+$DESCRIPTOR (symbol_name,"teco_spawn_execute");
+long table = LIB$K_CLI_LOCAL_SYM;
+long flags = 0;
+long status;
+long completion_status;
+long sub_pid;
+
+ PREAMBLE();
+
+ for(i = 1; i < argc; i++){
+ cp = argv[i];
+ if(*cp == '/'){
+ cp++;
+ dp = switch_buffer;
+ while(isalnum(*cp)) *dp++ = *cp++;
+ *dp = '\0';
+ if(strcmp(switch_buffer,"subjb") == 0){
+ continue;
+ }/* End IF */
+
+ else if(strcmp(switch_buffer,"nosuspend") == 0){
+ suspend_is_okay_flag = NO;
+ }/* End IF */
+
+ else if(strcmp(switch_buffer,"nocheckpoint") == 0){
+ checkpoint_enabled = NO;
+ }/* End IF */
+
+ else if(strcmp(switch_buffer,"spawn") == 0){
+ if(which_time == 1){
+ cp -= 5;
+ dp = "subjb";
+ while(*dp) *cp++ = *dp++;
+ strcpy(switch_buffer,"$ ");
+ strcat(switch_buffer,argv[0]);
+ symbol_value.dsc$w_length = strlen(switch_buffer);
+ symbol_value.dsc$b_dtype = 0;
+ symbol_value.dsc$b_class = DSC$K_CLASS_S;
+ symbol_value.dsc$a_pointer = switch_buffer;
+ status = lib$set_symbol(&symbol_name,&symbol_value,&table);
+ if(status != SS$_NORMAL){
+ perror("could not create symbol 'teco_spawn_execute'");
+ punt(status);
+ }/* End IF */
+ count = strlen(symbol_name.dsc$a_pointer);
+ for(j = 1; j < argc; j++){
+ count += strlen(argv[j]) + 1;
+ }/* End FOR */
+ command_line = tec_alloc(TYPE_C_CBUFF,count+8);
+ if(command_line == NULL) return(FAIL);
+ dp = command_line;
+ cp = symbol_name.dsc$a_pointer;
+ while(*dp++ = *cp++); dp--;
+ for(j = 1; j < argc; j++){
+ *dp++ = ' ';
+ cp = argv[j];
+ while(*dp++ = *cp++);
+ dp--;
+ }/* End FOR */
+ command_desc.dsc$w_length = strlen(command_line);
+ command_desc.dsc$b_dtype = 0;
+ command_desc.dsc$b_class = DSC$K_CLASS_S;
+ command_desc.dsc$a_pointer = command_line;
+
+ printf("VTECO: Spawning subjob for edit\n");
+
+ status = lib$spawn(&command_desc,0,0,&flags,0,
+ &sub_pid,&completion_status,0,0,0,0,0);
+ if(status != SS$_NORMAL){
+ perror("VTECO: LIB$SPAWN failed");
+ punt(status);
+ }/* End IF */
+
+ exit(SS$_NORMAL);
+
+ }/* End IF */
+ }/* End IF */
+
+ else {
+ char tmp_message[LINE_BUFFER_SIZE];
+ sprintf(
+ tmp_message,
+ "?Unknown VTECO switch '%s'",
+ switch_buffer
+ );
+ tec_error(EINVAL,tmp_message);
+ }/* End Else */
+
+ }/* End IF */
+
+ if(which_time == 0) return(SUCCESS);
+
+ if(argv[i][0] != '/'){
+ np = expand_filename(argv[i]);
+ while(np){
+ number_of_buffers_opened++;
+ buff_openbuffer(np->we_name,0,readonly_flag);
+ name_list = np;
+ np = np->we_next;
+ tec_release(TYPE_C_WILD,name_list);
+ }/* End While */
+ }/* End IF */
+
+ }/* End FOR */
+
+ if(number_of_buffers_opened > 0) buff_openbuffer(0,1,readonly_flag);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* EXPAND_FILENAME - Expand VMS wildcards to a list of files
+ *
+ * Function:
+ *
+ * This routine returns a linked list of expanded filenames.
+ */
+struct wildcard_expansion *
+expand_filename(wildcard_string)
+char *wildcard_string;
+{
+struct wildcard_expansion *name_list,*np;
+struct dsc$descriptor argument;
+struct dsc$descriptor result;
+struct {
+ unsigned short curlen;
+ char body[TECO_FILENAME_TOTAL_LENGTH];
+}result_buffer;
+int context;
+int fnd_flags;
+int status;
+char flags = 0;
+char *cp,*sp;
+
+#define NODE_SEEN (1<<0)
+#define DEV_SEEN (1<<1)
+#define DIR_SEEN (1<<2)
+#define VER_SEEN (1<<3)
+
+ PREAMBLE();
+
+/*
+ * Determine which fields are present in the supplied string so that we
+ * may trim the resulting filespecs some.
+ */
+ for(cp = wildcard_string; *cp != '\0'; cp++){
+ switch(*cp){
+ case ':':
+ if(cp[1] == ':'){
+ flags |= NODE_SEEN;
+ cp++;
+ }/* End IF */
+ else flags |= DEV_SEEN | DIR_SEEN;
+ break;
+ case '[':
+ flags |= DIR_SEEN;
+ break;
+ case ';':
+ if(cp[1] != '\0') flags |= VER_SEEN;
+ break;
+ }/* End Switch */
+ }/* End FOR */
+
+ argument.dsc$w_length = strlen(wildcard_string);
+ argument.dsc$b_dtype = 0;
+ argument.dsc$b_class = DSC$K_CLASS_S;
+ argument.dsc$a_pointer = wildcard_string;
+
+ result.dsc$w_length = sizeof(result_buffer.body);
+ result.dsc$b_dtype = DSC$K_DTYPE_VT;
+ result.dsc$b_class = DSC$K_CLASS_VS;
+ result.dsc$a_pointer = &result_buffer;
+
+ context = 0;
+ fnd_flags = (1<<1);
+
+ name_list = NULL;
+
+ while(1){
+ status = lib$find_file(&argument,&result,&context,0,0,0,&fnd_flags);
+ if((status & 0xFFFF) != SS$_NORMAL) break;
+ np = tec_alloc(TYPE_C_WILD, sizeof(struct wildcard_expansion));
+ if(np == NULL) return(NULL);
+
+/*
+ * Trim the resulting filespec as much as possible
+ */
+ sp = result_buffer.body;
+ sp[result_buffer.curlen] = '\0';
+
+ if((flags & DEV_SEEN) == 0){
+ cp = sp;
+ while(*cp && *cp != ':') cp++;
+ if(*cp == ':') strcpy(sp,cp + 1);
+ }/* End IF */
+
+ if((flags & DIR_SEEN) == 0){
+ cp = sp;
+ while(*cp && *cp != '[') cp++;
+ if(*cp == '['){
+ sp = cp + 1;
+ while(*sp && *sp != ']') sp++;
+ if(*sp == ']'){
+ strcpy(cp,sp+1);
+ }/* End IF */
+ sp = result_buffer.body;
+ }/* End IF */
+ }/* End IF */
+
+ if((flags & VER_SEEN) == 0){
+ cp = sp;
+ while(*cp && *cp != ';') cp++;
+ *cp = '\0';
+ }/* End IF */
+
+ if(strlen(sp) > (sizeof(np->we_name)-1)){
+ error_message("Filename too long");
+ continue;
+ }/* End IF */
+
+ strcpy(np->we_name,sp);
+ np->we_next = name_list;
+ name_list = np;
+ }/* End While */
+
+ lib$find_file_end(&context);
+
+/*
+ * If no expansion names at all, perhaps he specified a non-wild file which
+ * he wants to create. If so, we will just use the wild specification as is.
+ */
+ if(name_list == NULL){
+ np = tec_alloc(TYPE_C_WILD, sizeof(struct wildcard_expansion));
+ if(np == NULL) return(NULL);
+ if(strlen(wildcard_string) > (sizeof(np->we_name)-1)){
+ error_message("Filename too long");
+ return(NULL);
+ }/* End IF */
+
+ strcpy(np->we_name,wildcard_string);
+ np->we_next = name_list;
+ name_list = np;
+
+ }/* End IF */
+
+ return(name_list);
+
+}/* End Routine */
+
+/* END OF VMS CONDITIONAL CODE */
+
+#endif
+
+
+
+/*
+ * EXPAND_FILENAME - Expand a wildcard specification to a list of files
+ *
+ * Function:
+ *
+ * This routine returns a linked list of expanded filenames.
+ *
+ */
+#ifdef UNIX
+struct wildcard_expansion *name_list;
+struct wildcard_expansion *name_list_end;
+
+struct wildcard_expansion *
+expand_filename(wildcard_string)
+char *wildcard_string;
+{
+char temp_name[TECO_FILENAME_TOTAL_LENGTH];
+register char *cp,*sp;
+struct passwd *pw;
+struct passwd *getpwnam();
+
+ PREAMBLE();
+
+ name_list = NULL;
+ name_list_end = NULL;
+
+/*
+ * Else, start branching down into the directory list he specified
+ */
+ if(wildcard_string[0] == '/'){
+ process_directory(&wildcard_string[0],"/",2);
+ }
+ else if (wildcard_string[0] == '~'){
+ cp = temp_name;
+ sp = &wildcard_string[1];
+ while(*sp && *sp != '/') *cp++ = *sp++;
+ *cp++ = '\0';
+ if(temp_name[0] == '\0'){
+ if((cp = getenv("HOME"))) strcpy(temp_name,cp);
+ }
+ else {
+ pw = getpwnam(temp_name);
+ if(pw) strcpy(temp_name,pw->pw_dir);
+ }
+ strcat(temp_name,sp);
+ process_directory(temp_name,"/",2);
+ }
+ else {
+ strcpy(temp_name,"./");
+ strcat(temp_name,wildcard_string);
+ process_directory(temp_name,".",1);
+ }
+
+ return(name_list);
+
+}/* End Routine */
+
+void
+process_directory(wildstr,path,flags)
+char *wildstr;
+char *path;
+int flags;
+{
+char directory_path[TECO_FILENAME_TOTAL_LENGTH];
+struct wildcard_expansion *np;
+DIR *dirp;
+
+struct dirent *dp;
+
+struct stat statbuf;
+int c;
+char *cp;
+
+ PREAMBLE();
+
+/*
+ * Find the next slash, and if there isn't one, then we have reached
+ * the end of the file specification, and this should be a file rather
+ * than a directory.
+ */
+ wildstr = (char *)strchr(wildstr,'/');
+
+ directory_path[0] = '\0';
+
+ if(wildstr == NULL){
+ if(flags < 0){
+ if(stat(path,&statbuf) < 0) return;
+#ifndef USE_S_MACS
+ if((statbuf.st_mode & S_IFMT) != S_IFREG) return;
+#else
+ if(!S_ISREG(statbuf.st_mode)) return;
+#endif
+ }/* End IF */
+ if(strlen(path) > (sizeof(np->we_name)-1)){
+ error_message("Filename too long");
+ return;
+ }/* End IF */
+ np = (struct wildcard_expansion *)
+ tec_alloc(TYPE_C_WILD, sizeof(struct wildcard_expansion));
+ if(np == NULL) return;
+ np->we_next = NULL;
+ strcpy(np->we_name,path);
+ if(name_list == NULL) name_list = np;
+ if(name_list_end)name_list_end->we_next = np;
+ name_list_end = np;
+ return;
+ }/* End IF */
+
+#ifndef USE_S_MACS
+ if(stat(path,&statbuf) < 0 || ((statbuf.st_mode & S_IFMT) != S_IFDIR)){
+ return;
+ }/* End IF */
+#else
+ if (stat(path,&statbuf) < 0 || !S_ISDIR(statbuf.st_mode)) {
+ return;
+ }
+#endif
+
+/*
+ * This loop tests whether there are any wildcard characters in the
+ * next directory. If not, don't go through the overhead of reading
+ * directories, just simply hop down to that subdir.
+ */
+ cp = wildstr+1;
+ while((c = *cp++)){
+ switch(c){
+ case '*':
+ case '?':
+ case '[':
+ case '{':
+ while(*cp != '\0' && *cp != '/') cp++;
+ goto read_directory;
+ case '\0':
+ strcpy(directory_path,path);
+ strcat(directory_path,"/");
+ strcat(directory_path,wildstr+1);
+ process_directory(NULL,directory_path,-1);
+ return;
+ case '/':
+ cp--;
+ *cp = '\0';
+ switch(flags){
+ case 2:
+ strcpy(directory_path,"/");
+ strcat(directory_path,wildstr+1);
+ flags = 0;
+ break;
+ case 1:
+ strcpy(directory_path,wildstr+1);
+ flags = 0;
+ break;
+ case 0:
+ case -1:
+ strcpy(directory_path,path);
+ strcat(directory_path,"/");
+ strcat(directory_path,wildstr+1);
+ break;
+ }/* End Switch */
+
+ *cp = '/';
+ process_directory(wildstr+1,directory_path,flags);
+ return;
+ }/* End Switch */
+ }/* End While */
+
+ switch(flags){
+ case 2:
+ strcat(directory_path,wildstr);
+ flags = 0;
+ break;
+ case 1:
+ strcpy(directory_path,wildstr+1);
+ flags = 0;
+ break;
+ case 0:
+ case -1:
+ strcpy(directory_path,path);
+ strcat(directory_path,wildstr);
+ break;
+ }/* End Switch */
+ process_directory("",directory_path,flags);
+ return;
+
+read_directory:
+
+ dirp = opendir(path);
+
+ while((dp = readdir(dirp)) != NULL) {
+#ifndef _POSIX_SOURCE
+ if(dp->d_ino == 0) continue;
+#endif /* _POSIX_SOURCE */
+ if(match_name(dp->d_name,wildstr+1)){
+ switch(flags){
+ case 2:
+ strcpy(directory_path,"/");
+ strcat(directory_path,dp->d_name);
+ break;
+ case 1:
+ strcpy(directory_path,dp->d_name);
+ break;
+ case -1:
+ case 0:
+ strcpy(directory_path,path);
+ strcat(directory_path,"/");
+ strcat(directory_path,dp->d_name);
+ }/* End Switch */
+ process_directory(cp,directory_path,-1);
+ }/* End IF */
+ }/* End While */
+
+ closedir(dirp);
+
+}/* End Routine */
+
+/*
+ * MATCH_NAME - Check whether a name satisfies a wildcard specification
+ *
+ * Function:
+ *
+ * This routine attempts to do csh style wildcard matching so that
+ * internal teco routines may support this behavior.
+ */
+int
+match_name(name,pattern)
+char *name;
+char *pattern;
+{
+char temp_buff[TECO_FILENAME_TOTAL_LENGTH];
+register int c;
+register char *cp,*sp;
+int match;
+int previous_char;
+int pattern_char;
+
+ PREAMBLE();
+
+ while(1){
+ switch(pattern_char = *pattern++){
+ case '/':
+ case '\0':
+ return(*name == '\0' ? 1 : 0);
+ case '?':
+ return(*name == '\0' || *name == '/' ? 0 : 1);
+/*
+ * Open-Bracket allows any of a list of characters to match, such as [abc],
+ * or a range such as [a-c], or a combination such as [abg-mz].
+ */
+ case '[':
+ match = 0;
+ c = *name;
+ previous_char = 'A';
+ while((pattern_char = *pattern++)){
+ if(pattern_char == c) match = 1;
+ if(pattern_char == ']'){
+ if(match) break;
+ return(0);
+ }/* End IF */
+ if(pattern_char == '-'){
+ pattern_char = *pattern++;
+ if(pattern_char == ']'){
+ pattern--;
+ pattern_char = 'z';
+ }/* End IF */
+ if(c >= previous_char && c <= pattern_char){
+ match = 1;
+ }/* End IF */
+ }/* End IF */
+ previous_char = pattern_char;
+ }/* End While */
+ break;
+/*
+ * Brace allows a list of strings, any one of which may match, and any
+ * one of which may contain further wildcard specifications.
+ */
+ case '{':
+ cp = temp_buff;
+ while((pattern_char = *pattern++)){
+ if(pattern_char == ',' || pattern_char == '}'){
+ sp = pattern;
+ if(pattern_char == ','){
+ while((c = *sp++)){
+ if(c == '}') break;
+ }/* End While */
+ }/* End IF */
+ while((*cp++ = *sp++));
+ if(match_name(name,temp_buff)) return(1);
+ if(pattern_char == '}') return(0);
+ cp = temp_buff;
+ }/* End IF */
+ else *cp++ = pattern_char;
+ }/* End While */
+ break;
+/*
+ * Asterisk matches any string
+ */
+ case '*':
+ if(*pattern == '\0') return(1);
+ if(*pattern == '/') return(1);
+ for(c = 0; name[c] != '\0'; c++){
+ if(match_name(&name[c],pattern)) return(1);
+ }/* End FOR */
+ return(0);
+ default:
+ if(pattern_char != *name) return(0);
+ break;
+ }/* End Switch */
+ name++;
+ }/* End While */
+}/* End Routine */
+
+#endif
+
+
+
+/* PUNT - Exit with a specified error code
+ *
+ * Function:
+ *
+ * PUNT is called with an errno code with which we want to exit
+ *
+ */
+void
+punt(exit_code)
+int exit_code;
+{
+ PREAMBLE();
+
+ exit(exit_code);
+
+}/* End Routine */
+
+
+/* TEC_PANIC - Print an error string
+ *
+ * Function:
+ *
+ * This routine is called previous to punt to print an error string
+ */
+void
+tec_panic(string)
+char *string;
+{
+ PREAMBLE();
+
+ fputs(string,stdout);
+ fputs("\n",stdout);
+
+#ifndef UNIX
+ exit(1);
+#else
+ kill(getpid(),SIGQUIT);
+#endif
+
+}/* End Routine */
+
+/* TEC_ERROR - Exit with specified error code, printing an error string
+ *
+ * Function:
+ *
+ * This routine is called to exit with the specified error code
+ * after printing the supplied error string.
+ */
+void
+tec_error(code,string)
+int code;
+char *string;
+{
+ PREAMBLE();
+
+ fprintf(stderr,"%s\n",string);
+
+#ifdef VMS
+ exit(1);
+#endif
+
+ exit(code);
+
+}/* End Routine */
+
+#if DEBUG_UNUSED
+/* OPEN_DEBUG_LOG_FILE - Open a file to write debug output to
+ *
+ * Function:
+ *
+ * This routine is called so that a log file is open for writing of
+ * debugging messages.
+ */
+void
+open_debug_log_file()
+{
+ PREAMBLE();
+
+ teco_debug_log = fopen("teco.dbg","w");
+ if(teco_debug_log == NULL){
+ teco_debug_log = stderr;
+ }/* End IF */
+
+}/* End Routine */
+#endif
+
+
+
+/* MAP_BAUD - Map system dependent baud fields to a standard integer
+ *
+ * Function:
+ *
+ * This routine takes an operating system specific baud rate
+ * representation, and maps it into a simple integer value.
+ */
+int
+map_baud(input_baud_rate)
+int input_baud_rate;
+{
+register int i;
+int return_baud;
+
+#ifdef UNIX
+
+#ifndef B19200
+#define B19200 B9600
+#endif
+#ifndef B38400
+#define B38400 B9600
+#endif
+
+static unsigned int encoded_bauds[] = {
+ B0,B50,B75,B110,B134,B150,B200,B300,B600,B1200,
+ B1800,B2400,B4800,B9600,B19200,B38400,
+ EXTA,EXTB };
+static int equivalent_baudrates[] = {
+ 0,50,75,110,134,150,200,300,600,1200,
+ 1800,2400,4800,9600,19200,38400,
+ 9600,9600 };
+
+/* END OF UNIX CONDITIONAL CODE */
+
+#endif
+
+#ifdef VMS
+static unsigned char encoded_bauds[] = {
+ TT$C_BAUD_50,TT$C_BAUD_75,TT$C_BAUD_110,TT$C_BAUD_134,
+ TT$C_BAUD_150,TT$C_BAUD_300,TT$C_BAUD_600,TT$C_BAUD_1200,
+ TT$C_BAUD_1800,TT$C_BAUD_2000,TT$C_BAUD_2400,TT$C_BAUD_3600,
+ TT$C_BAUD_4800,TT$C_BAUD_7200,TT$C_BAUD_9600,TT$C_BAUD_19200 };
+static int equivalent_baudrates[] = {
+ 50,75,110,134,
+ 150,300,600,1200,
+ 1800,2000,2400,3600,
+ 4800,7200,9600,19200 };
+
+/* END OF VMS CONDITIONAL CODE */
+
+#endif
+
+ PREAMBLE();
+
+/*
+ * Default is a common baud rate incase we miss somehow
+ */
+ return_baud = 9600;
+
+/*
+ * Determine how many entries there are in the baud table
+ */
+ i = sizeof(encoded_bauds) / sizeof(encoded_bauds[0]);
+
+/*
+ * Now scan the table for our input baud rate
+ */
+ while(--i >= 0){
+ if(input_baud_rate != encoded_bauds[i]) continue;
+ return_baud = equivalent_baudrates[i];
+ break;
+ }/* End While */
+
+ return(return_baud);
+
+}/* End Routine */
+
+
+
+/* ERROR_TEXT - Return 'errno' style error string
+ *
+ * Function:
+ *
+ * This routine is called to return the error string associated with an
+ * errno error code.
+ */
+char *
+error_text(err_num)
+register int err_num;
+{
+
+#ifdef VMS
+
+ int sys_nerr;
+
+static char *tec_errlist[] = {
+ "success", /* ESUCCESS */
+ "not owner", /* EPERM */
+ "no such file or directory", /* ENOENT */
+ "no such process", /* ESRCH */
+ "interrupted system call", /* EINTR */
+ "i/o error", /* EIO */
+ "no such device or address", /* ENXIO */
+ "arg list too long", /* E2BIG */
+ "exec format error", /* ENOEXEC */
+ "bad file number", /* EBADF */
+ "no children", /* ECHILD */
+ "no more processes", /* EAGAIN */
+ "not enough core", /* ENOMEM */
+ "permission denied", /* EACCES */
+ "bad address", /* EFAULT */
+ "block device required", /* ENOTBLK */
+ "mount device busy", /* EBUSY */
+ "file exists", /* EEXIST */
+ "cross-device link", /* EXDEV */
+ "no such device", /* ENODEV */
+ "not a directory", /* ENOTDIR */
+ "is a directory", /* EISDIR */
+ "invalid argument", /* EINVAL */
+ "file table overflow", /* ENFILE */
+ "too many open files", /* EMFILE */
+ "not a typewriter", /* ENOTTY */
+ "text file busy", /* ETXTBSY */
+ "file too large", /* EFBIG */
+ "no space left on device", /* ENOSPC */
+ "illegal seek", /* ESPIPE */
+ "read-only file system", /* EROFS */
+ "too many links", /* EMLINK */
+ "broken pipe", /* EPIPE */
+ "math argument", /* EDOM */
+ "result too large" /* ERANGE */
+};
+
+ PREAMBLE();
+
+ sys_nerr = sizeof(tec_errlist) / sizeof(tec_errlist[0]) - 1;
+
+/* END OF VMS CONDITIONAL CODE */
+
+#endif
+
+#if 0
+ if(err_num < 0 || err_num > sys_nerr){
+ return("unknown error");
+ }/* End IF */
+#endif
+
+#ifdef UNIX
+#if 0
+ return((char *)sys_errlist[err_num]);
+#endif
+ return( strerror( err_num ));
+#endif
+
+#ifdef VMS
+ return(tec_errlist[err_num]);
+#endif
+
+}/* End Routine */
diff --git a/teco.h b/teco.h
new file mode 100644
index 0000000..e1e11b8
--- /dev/null
+++ b/teco.h
@@ -0,0 +1,657 @@
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/teco.h,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* teco.h
+ * Global TECO Definitions
+ * %W% (PC) %G%
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#define YES 1
+#define NO 0
+#define FAIL 0
+#define SUCCESS 1
+#define NULL 0
+
+#define CREATE_OLD_FILES
+
+#define BLOCKED 2
+#define INVALIDATE 3
+
+#define VMAJOR 6
+#define VMINOR 7
+#define AUTO_DATE "$Date: 2007/12/10 21:59:20 $"
+
+#define TECO_FILENAME_TOTAL_LENGTH 1024
+#define TECO_FILENAME_COMPONENT_LENGTH 256
+#define TECO_READONLY_NAME "visit"
+#define TECO_INTERNAL_BUFFER_NAME "TECO-"
+#define TERMCAP_BUFFER_SIZE 2048
+#define SCREEN_OUTPUT_BUFFER_SIZE 4096
+#define SCREEN_RESERVED_LINES 2
+#define SCREEN_MAX_LABEL_FIELDS 4
+#define SCREEN_NOMINAL_LINE_WIDTH 132
+#define SCREEN_MAX_LINES 1024
+#define SCREEN_MINIMUM_WINDOW_HEIGHT 4
+#define IO_BUFFER_SIZE 512
+#define LINE_BUFFER_SIZE 1024
+#define DEFAULT_CHECKPOINT (5*60)
+#define TAG_HASH_ENTRIES 1024
+#define PARSER_STRING_MAX 1024
+#define SEARCH_STRING_MAX PARSER_STRING_MAX
+#define NORMAL_TAB_WIDTH 8
+#define MAX_TAB_WIDTH 16
+#define MAXOF( a,b) (a) > (b) ? (a) : (b)
+
+#define INITIAL_LINE_BUFFER_SIZE 32
+#define INCREMENTAL_LINE_BUFFER_SIZE 32
+#define MINIMUM_ALLOCATION_BLOCK 32
+#define LOOKASIDE_COUNT 32
+#define LARGEST_LOOKASIDE_BLOCK (LOOKASIDE_COUNT * MINIMUM_ALLOCATION_BLOCK)
+#define BIG_MALLOC_HUNK_SIZE (LARGEST_LOOKASIDE_BLOCK * 64)
+#define NORMAL_TAB_WIDTH 8
+
+/*
+ * The following is where the different machines are described so that
+ * conditional compilation based on different machines quirks can be done.
+ */
+
+/*
+ * Unless defined otherwise, we assume 32 bits to an integer and 8 bits / char
+ */
+#ifndef BITS_PER_INT
+#define BITS_PER_INT 32
+#endif
+#ifndef BITS_PER_CHAR
+#define BITS_PER_CHAR 8
+#endif
+
+#define ESCAPE 27
+#define RUBOUT 127
+
+#define CNTRL_A 1
+#define CNTRL_D 4
+#define CNTRL_E 5
+#define CNTRL_G 7
+#define CNTRL_N 14
+#define CNTRL_R 18
+#define CNTRL_U 21
+#define CNTRL_V 22
+#define CNTRL_W 23
+#define CNTRL_X 24
+#define CNTRL_Z 26
+#define BELL 7
+
+/*
+ * Define memory allocation block types
+ */
+#define TYPE_C_MINTYPE 33
+#define TYPE_C_CBUFF 33
+#define TYPE_C_CPERM 34
+#define TYPE_C_CMD 35
+#define TYPE_C_UNDO 36
+#define TYPE_C_SCR 37
+#define TYPE_C_SCRBUF 38
+#define TYPE_C_SCREEN 39
+#define TYPE_C_SCREENBUF 40
+#define TYPE_C_LINE 41
+#define TYPE_C_LINEBUF 42
+#define TYPE_C_BHDR 43
+#define TYPE_C_WINDOW 44
+#define TYPE_C_LABELFIELD 45
+#define TYPE_C_WILD 46
+#define TYPE_C_TAGS 47
+#define TYPE_C_TAGENT 48
+#define TYPE_C_TAGSTR 49
+#define TYPE_C_MAXTYPE 49
+
+#define MAGIC_SCREEN 0x01010101
+#define MAGIC_BUFFER 0x02020202
+#define MAGIC_LINE 0x03030303
+#define MAGIC_LINE_LOOKASIDE 0x04040404
+#define MAGIC_FORMAT 0x05050505
+#define MAGIC_FORMAT_LOOKASIDE 0x06060606
+
+#ifdef DEBUG1
+#define PREAMBLE() \
+ do_preamble_checks();
+
+#define RETURN \
+ do_return_checks(); \
+ return;
+
+#define RETURN_VAL(val) \
+ do_return_checks(); \
+ return(val);
+#else
+#define PREAMBLE()
+#define RETURN
+#define RETURN_VAL()
+#endif /* DEBUG1 */
+
+
+#define ELEMENTS(xyzzy) (sizeof(xyzzy)/sizeof(xyzzy[0]))
+
+/*
+ * Define structures used throughout TECO
+ */
+ struct position_cache {
+ struct buff_line *lbp;
+ int base;
+ };
+
+#define BUFF_CACHE_CONTENTS(pos_cache) \
+ (pos_cache)->lbp->buffer[(pos_cache)->base]
+#define BUFF_INCREMENT_CACHE_POSITION(pos_cache) \
+ if(++((pos_cache)->base) >= (pos_cache)->lbp->byte_count){ \
+ (pos_cache)->lbp = (pos_cache)->lbp->next_line; (pos_cache)->base = 0; \
+ }
+#define BUFF_DECREMENT_CACHE_POSITION(pos_cache) \
+ { \
+ if((pos_cache)->base == 0){ \
+ (pos_cache)->lbp = (pos_cache)->lbp->prev_line; \
+ (pos_cache)->base = (pos_cache)->lbp->byte_count; \
+ } \
+ (pos_cache)->base -= 1; \
+ }
+
+ struct buff_header {
+ int buf_magic;
+ unsigned int buf_hash;
+ char *name;
+ int buffer_number;
+ struct buff_header *next_header;
+ char ismodified;
+ char isreadonly;
+ char isbackedup;
+ int dot;
+ int zee;
+ int ivalue;
+ struct position_cache pos_cache;
+
+ struct buff_line *first_line;
+ };
+
+ struct buff_line {
+ int lin_magic;
+ int buffer_size;
+ int byte_count;
+ char *buffer;
+ struct buff_line *next_line;
+ struct buff_line *prev_line;
+ struct format_line *format_line;
+ };
+
+ struct format_line {
+ int fmt_magic;
+ struct buff_header *fmt_owning_buffer;
+ struct buff_line *fmt_buffer_line;
+ int fmt_buffer_size;
+ short *fmt_buffer;
+ int fmt_sequence;
+ struct format_line *fmt_next_line;
+ struct format_line *fmt_prev_line;
+ struct format_line *fmt_next_alloc;
+ struct format_line *fmt_prev_alloc;
+ struct format_line *fmt_next_window;
+ struct format_line *fmt_prev_window;
+ char fmt_in_use;
+ char fmt_permanent;
+ struct screen_line *fmt_saved_line;
+ short fmt_visible_line_position;
+ struct window *fmt_window_ptr;
+ };
+
+ struct screen_line {
+ int scr_magic;
+ short *buffer;
+ struct format_line *companion;
+ int sequence;
+ };
+
+ struct window {
+ struct window *win_next_window;
+ int win_window_number;
+ int win_x_size;
+ int win_y_size;
+ int win_y_base;
+ int win_y_end;
+ struct buff_header *win_buffer;
+ struct format_line win_label_line;
+ struct format_line *win_dot_format_line;
+ int win_dot_screen_offset;
+ char *win_label_field_contents[SCREEN_MAX_LABEL_FIELDS];
+ };
+
+ struct search_element {
+ unsigned char type;
+ unsigned char value;
+ union {
+ int intarray[256/BITS_PER_INT];
+ char bytearray[256/BITS_PER_CHAR];
+ }bitmask;
+ union {
+ int intarray[256/BITS_PER_INT];
+ char bytearray[256/BITS_PER_CHAR];
+ }repmask;
+ };
+
+ struct search_buff {
+ int length;
+ char input[PARSER_STRING_MAX];
+ struct search_element data[PARSER_STRING_MAX];
+ char error_message_given;
+ };
+
+ struct wildcard_expansion {
+ struct wildcard_expansion *we_next;
+ char we_name[TECO_FILENAME_TOTAL_LENGTH-4];
+ };
+
+ struct tagent {
+ char *te_symbol;
+ char *te_filename;
+ char *te_lineno;
+ char *te_search_string;
+ struct tagent *te_next;
+ };
+
+ struct tags {
+ struct tagent *current_entry;
+ struct tagent *tagents[TAG_HASH_ENTRIES];
+ };
+
+/*
+ * Define the various flags in a screen display short int
+ */
+#define SCREEN_M_DATA 0x00FF
+#define SCREEN_M_FLAGS 0xFF00
+#define SCREEN_M_REVERSE 0x0100
+
+/*
+ * Define constants for the label line fields
+ */
+#define LABEL_C_TECONAME 0
+#define LABEL_C_FILENAME 1
+#define LABEL_C_READONLY 2
+#define LABEL_C_MODIFIED 3
+
+/*
+ * Define constants for EJ command
+ */
+#define SETOPTION_MIN_OPTION 1
+
+#define SETOPTION_ALTERNATE_ESCAPE_CHAR 1
+#define SETOPTION_SCREEN_HEIGHT 2
+#define SETOPTION_SCREEN_WIDTH 3
+#define SETOPTION_ALTERNATE_DELETE_CHAR 4
+#define SETOPTION_FORCE_8_BIT_CHARS 5
+#define SETOPTION_TAB_WIDTH 6
+#define SETOPTION_HIDE_CR_CHARS 7
+
+#define SETOPTION_MAX_OPTION 7
+
+/*
+ * Define constants for FT command
+ */
+#define TAGS_MIN_OPTION 0
+
+#define TAGS_LOAD_TAGS_FILE 0
+#define TAGS_SEARCH_TAGS_FILE 1
+#define TAGS_TEST_FOR_LOADED_TAGS 2
+#define TAGS_INSERT_TARGET_FILE 3
+#define TAGS_INSERT_SEARCH_STRING 4
+#define TAGS_INSERT_LINENO 5
+#define TAGS_INSERT_ALL 6
+
+#define TAGS_MAX_OPTION 6
+
+/*
+ * Define useful macros
+ */
+#define UPCASE(char) (islower(char) ? TOUPPER(char) : char)
+#ifdef SYS_III_UNIX
+#define TOUPPER(char) _toupper(char)
+#define TOLOWER(char) _tolower(char)
+#else
+#define TOUPPER(char) toupper(char)
+#define TOLOWER(char) tolower(char)
+#endif
+
+typedef unsigned long teco_ptrint_t;
+
+/*
+ * We define unix except for the really different operating systems, like
+ * vms. It lets us write our own version of functions which simply do not
+ * exist outside of unix.
+ */
+#ifndef VMS
+#define UNIX
+#define JOB_CONTROL
+#endif
+
+/*
+ * Include Files From GNU Autoconf/Autoheader
+ */
+#include "config.h"
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+/*
+ * sys/filio.h has the FIONREAD definition
+ */
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#ifdef FIONREAD
+#define HAS_FIONREAD
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#else
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#endif
+
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+# if !HAVE_STRCHR
+# if !defined(strchr)
+# define strchr index
+# endif
+# if !defined(strrchr)
+# define strrchr rindex
+# endif
+# endif
+#endif
+
+/* on SGI this gets us BZERO */
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#if HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#if HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#if GWINSZ_IN_SYS_IOCTL
+# include <sys/ioctl.h>
+#define SUN_STYLE_WINDOW_SIZING 1
+#else
+#if HAVE_TERMIOS_H
+# include <termios.h>
+#define SUN_STYLE_WINDOW_SIZING 1
+#endif
+#endif
+
+#if HAVE_TERMIO_H
+# include <termio.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if !defined(HAVE_TERMIO_H) && !defined(HAVE_TERMIOS_H)
+#if HAVE_SGTTY_H
+#include <sgtty.h>
+#endif
+#endif
+
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#if HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+
+/* current end of autoconf stuff*/
+
+#if HAVE_LIBTERMCAP
+#define TERMCAP
+#else
+#if defined(HAVE_LIBTERMINFO)
+#define TERMINFO
+#endif
+#endif
+
+#if !defined(TERMCAP) && !defined(TERMINFO)
+#if defined(HAVE_LIBNCURSES)
+#define TERMINFO
+#endif
+#endif
+
+
+/* we prefer select over poll */
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#define HAS_SELECT
+#else
+#if HAVE_POLL_H
+#include <poll.h>
+#define HAS_POLL
+#endif
+#endif
+
+/*
+ * Digital Equpment VMS Operating System
+ */
+#ifdef VMS
+#include "tecvms.h"
+#endif
+
+#ifndef CAUSE_BUS_ERROR
+#define CAUSE_BUS_ERROR() \
+ { \
+ int fake_destination; \
+ fake_destination = *((int *)(0x0FFFFFFF)); \
+ }
+#endif
+
+/* method declarations */
+void error_message(char * string);
+void tec_release(unsigned char type, char *addr);
+void tec_panic( char *string);
+int buff_switch(struct buff_header *hbp,int map_flag);
+int buff_read(struct buff_header *hbp,char *name);
+int buff_readfd(struct buff_header *hbp,char *name,int iochan);
+int buff_insert(struct buff_header *hbp,int position,char *buffer,int length);
+int screen_label_line(struct buff_header *buffer,char *string,int field);
+int buff_write(struct buff_header *hbp,int chan,int start,int end);
+void tecmem_stats();
+void screen_free_format_lines(struct format_line *sbp);
+int buff_delete_char(struct buff_header *hbp,int position);
+int compile_search_string(struct search_buff *search_tbl);
+int cmd_forward_search(int pos1,int pos2,struct search_buff *search_tbl);
+int cmd_reverse_search(int pos1,int pos2,struct search_buff *search_tbl);
+int buff_cached_contents(struct buff_header *,int,struct position_cache *);
+int buff_contents(struct buff_header *hbp,int position);
+void pause_while_in_input_wait(void);
+void restore_tty(void);
+void initialize_tty(void);
+void screen_redraw(void);
+void screen_resize(void);
+void screen_refresh(void);
+void screen_message(char *string);
+int tty_input_pending(void);
+void screen_format_windows(void);
+void load_qname_register(void);
+void buff_delete(struct buff_header *,int,int);
+void screen_reformat_windows(void);
+int tag_calc_hash(char *string);
+void tag_free_struct(struct tags *tp);
+void tecdebug_check_screen_magic(void);
+void tecdebug_check_buffer_magic(void);
+void tecdebug_check_line_magic(void);
+void tecdebug_check_format_magic(void);
+void tecdebug_check_companion_pointers(void);
+void tecdebug_check_window_pointers(void);
+void term_init(void);
+int term_goto( int dest_x,int dest_y);
+void term_clrtobot(void);
+void parser_reset_echo(void);
+void term_finish(void);
+void term_standend(void);
+void term_clrtoeol(void);
+void term_standout(void);
+void screen_save_current_message(char *,int);
+int window_switch(int window_number);
+void screen_delete_window( struct window *old_wptr);
+void buff_reopenbuff(struct buff_header *bp);
+void buff_free_line_buffer_list(struct buff_line *);
+int screen_display_window(struct window *wptr);
+int buff_find_offset(struct buff_header *,struct buff_line *,int);
+int buff_openbuffnum(int,int);
+int parse_illegal_buffer_position(int,int,char *);
+int buff_insert_char(struct buff_header *,int,char);
+int push_qregister(char);
+void buff_bulk_insert(struct buff_header *,int,int,struct buff_line *);
+void buff_destroy(struct buff_header *);
+int cmd_wordmove(int count);
+int cmd_search(int,int,struct search_buff *);
+int buff_openbuffer(char *,int,int);
+int cmd_write(struct buff_header *,char *);
+void screen_scroll(int);
+void parser_dump_command_line(struct buff_header *);
+int parser_replace_command_line(struct buff_header *);
+void tec_gc_lists(void);
+void screen_deallocate_format_lookaside_list();
+void buff_deallocate_line_buffer_lookaside_list();
+int buff_init();
+void tec_error(int, char *);
+int handle_command_line(int,int,char **);
+void tecparse();
+//void term_putc(char);
+int term_putc(int);
+void term_flush();
+int screen_init();
+void screen_finish();
+int exec_doq0();
+void punt(int);
+void screen_reset_message();
+int tecparse_syntax(int);
+void screen_echo(char);
+void cmd_pause();
+void cmd_suspend();
+int term_putnum(char *,int,int);
+int term_scroll_region(int,int);
+int init_term_description();
+//int tgetent(char *,char *);
+//int tgetnum(char *);
+char *tec_alloc(int,int);
+void initialize_memory_stats( void );
+struct buff_header *buff_qfind(char,char);
+char *error_text(int);
+struct buff_line *buff_find_line(struct buff_header *hbp,int);
+void term_insert_line(int,int);
+void term_delete_line(int,int);
+struct buff_header *buff_find(char *);
+struct buff_header *buff_duplicate(struct buff_header *);
+struct window *screen_split_window(struct window *,int,int);
diff --git a/tecparse.c b/tecparse.c
new file mode 100644
index 0000000..9682bf0
--- /dev/null
+++ b/tecparse.c
@@ -0,0 +1,2178 @@
+char *tecparse_c_version = "tecparse.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecparse.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecparse.c
+ * Subroutines to implement the finite state parser
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+ struct cmd_token *cmd_list = NULL;
+ struct cmd_token *last_cmd_list = NULL;
+ struct cmd_token *jump_to_token;
+ struct cmd_token *resume_execute_ct;
+ struct cmd_token *last_token_executed;
+ struct search_buff search_string;
+ char user_message[PARSER_STRING_MAX];
+ int remembered_dot;
+ char immediate_execute_flag = YES;
+ char trace_mode_flag = NO;
+
+ extern int tty_input_chan;
+ extern int errno;
+ extern char exit_flag;
+ extern char intr_flag;
+ extern char susp_flag;
+ extern char input_pending_flag;
+ extern char alternate_escape_character;
+ extern char main_delete_character;
+ extern char alternate_delete_character;
+ extern char checkpoint_flag;
+ extern char checkpoint_enabled;
+ extern char waiting_for_input_flag;
+ extern char resize_flag;
+ extern char ring_audible_bell;
+ extern char suspend_is_okay_flag;
+ extern struct buff_header *curbuf,*buffer_headers;
+ extern int term_speed;
+
+/*
+ * Forward declarations
+ */
+ struct cmd_token *parse_rubout_character(struct cmd_token *,int);
+ struct cmd_token *parse_rubout_cmd_token(struct cmd_token *);
+ void preserve_rubout_char(char);
+ void parser_clean_preserve_list(void);
+ int parser_getc(void);
+ int unpreserve_rubout_char(struct cmd_token *);
+
+
+
+/* TECPARSE - Main entry point of the parser
+ *
+ * Function:
+ *
+ * This routine reads input characters and builds the token
+ * list. It also calls the execute states as long as the go
+ * flag is set. When it hits the final parse state, it then
+ * executes any states which were not immediately executed.
+ */
+void
+tecparse()
+{
+register struct cmd_token *ct;
+int status;
+int c;
+static char no_mem = 0;
+
+ PREAMBLE();
+
+/*
+ * We start our command token list with a special token that simply means that
+ * this is the begining of the list. We also set the current state to be the
+ * initial state of the parser.
+ */
+ resume_execute_ct = NULL;
+ ct = allocate_cmd_token((struct cmd_token *)NULL);
+ if(ct != NULL){
+ no_mem = 0;
+ ct->opcode = TOK_C_FIRSTTOKEN;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->ctx.go_flag = YES;
+ if(immediate_execute_flag == NO) ct->ctx.go_flag = NO;
+ cmd_list = ct;
+ screen_reset_echo(cmd_list);
+/*
+ * Now we loop until we reach a final state, or something horrible happens.
+ */
+ while(1){
+ c = parser_getc();
+ status = tecparse_syntax(c);
+ if(status == FAIL) break;
+ }/* End While */
+
+ screen_format_windows();
+
+ }/* End IF */
+
+ else {
+ if(no_mem){
+ tec_panic("no memory available");
+ }/* End IF */
+ no_mem = 1;
+ error_message("<no memory>");
+ }/* End Else */
+
+ screen_refresh();
+
+/*
+ * Here to clean up old parse lists. Note that we always clean up the list
+ * on last_cmd_list, and then move the current command list over there. This
+ * is so that we always remember one command string back incase the user uses
+ * the '*' command to save it all to a q-register. Besides unchaining command
+ * blocks, the cleanup routine follows down undo lists and macro lists to
+ * reclaim all the memory they are using.
+ */
+ parser_cleanup_ctlist(last_cmd_list);
+ last_cmd_list = cmd_list;
+ cmd_list = NULL;
+
+/*
+ * Also clean up the rubout Q-register.
+ */
+ parser_clean_preserve_list();
+
+/*
+ * Also, clean up any lookaside lists
+ */
+ tec_gc_lists();
+
+}/* End Routine */
+
+
+
+/* TECPARSE_SYNTAX - Here to manipulate the tree according to new input
+ *
+ * Function:
+ *
+ * This routine is called with an input byte to manipulate the
+ * parser tree and execute any associated code.
+ */
+int
+tecparse_syntax(c)
+int c;
+{
+register struct cmd_token *ct;
+struct cmd_token trace_ct;
+register struct cmd_token *final_token,*last_token;
+int status;
+char token_used,token_marked;
+char old_modified_state;
+struct buff_header *old_curbuf;
+struct undo_token *ut;
+
+ PREAMBLE();
+
+/*
+ * Find the end of the current command list.
+ */
+ ct = cmd_list;
+ while(ct->next_token){
+ ct = ct->next_token;
+ }/* End While */
+/*
+ * parse_special_character tests whether this character is an immediate
+ * typing control character which should not be sent to the state machine but
+ * handled locally. This is how we handle things like rubout, rubout word,
+ * and Control-U.
+ */
+ if(parse_special_character(ct,c)){
+ return(1);
+ }/* End IF */
+/*
+ * We only echo the character if is is not a special character
+ */
+ screen_echo(c);
+ if(input_pending_flag == NO){
+ screen_format_windows();
+ screen_refresh();
+ }/* End IF */
+/*
+ * We set the token_used flag to indicate that we have not used the input
+ * byte yet.
+ */
+ token_used = token_marked = NO;
+/*
+ * Now we loop until we use the input byte, or reach a terminating state
+ */
+ while(1){
+/*
+ * If the last state transition used the input byte, we return to our
+ * caller.
+ */
+ if(token_used == YES){
+ return(1);
+ }/* End IF */
+/*
+ * Allocate a command token to hold the next state transition in. The routine
+ * will automatically link it onto the end of the list.
+ */
+ ct = allocate_cmd_token(ct);
+ if(ct == NULL){
+ error_message("<out of memory>");
+ return(1);
+ }/* End IF */
+/*
+ * Here we check for the special RETURN state. Although all this could be done
+ * in the state machine itself, this was a good place to consolidate things.
+ * We pop the state machine stack a level so as to return to the calling state.
+ * Note that iarg1 and iarg2 are copied from the calling state, so that they
+ * are preserved. This means that the substate must return it's value through
+ * tmpval which is not preserved.
+ */
+ if(ct->ctx.state == STATE_C_RETURN){
+ ct->ctx.state = ct->ctx.return_state;
+ ct->ctx.return_state = ct->ctx.caller_token->ctx.return_state;
+ ct->ctx.iarg1_flag = ct->ctx.caller_token->ctx.iarg1_flag;
+ ct->ctx.iarg2_flag = ct->ctx.caller_token->ctx.iarg2_flag;
+ ct->ctx.iarg1 = ct->ctx.caller_token->ctx.iarg1;
+ ct->ctx.iarg2 = ct->ctx.caller_token->ctx.iarg2;
+ ct->ctx.caller_token = ct->ctx.caller_token->ctx.caller_token;
+ }/* End IF */
+/*
+ * Set up the initial state of the command token. The COPYTOKEN opcode doesn't
+ * really mean anything except that the opcode hasn't really been set, so it's
+ * more for debugging purposes than anything else. Notice that since we are
+ * actually placing an input byte in input_byte, we set the default that the
+ * state will indeed eat the byte. If this is not the case, it is up to the
+ * state to clear the flag.
+ */
+ ct->opcode = TOK_C_COPYTOKEN;
+ ct->flags |= TOK_M_EAT_TOKEN;
+ ct->input_byte = c;
+/*
+ * If this is the first time through here for this input character,
+ * mark this is the input character token so that rubout works correctly.
+ */
+ if(token_marked == NO){
+ ct->opcode = TOK_C_INPUTCHAR;
+ token_marked = YES;
+ }/* End IF */
+/*
+ * Call the state machine to process this character. Although more command
+ * tokens may be added onto the list by the state machine, we leave ct where
+ * it is for the moment so that we can work forward through all the generated
+ * states.
+ */
+ parse_input_character(ct,ct);
+/*
+ * If the state machine indicates that the input character was eaten, remember
+ * this fact.
+ */
+ if(ct->flags & TOK_M_EAT_TOKEN) token_used = YES;
+/*
+ * If this character would cause us to transition into an error state, we
+ * simply don't allow it. We generate a rubout sequence to remove the offending
+ * character and allow the user to make a decision about what to do next.
+ */
+ if(ct->ctx.state == STATE_C_ERRORSTATE){
+ token_used = YES;
+ while(ct->next_token) ct = ct->next_token;
+ ct = parse_rubout_character(ct,0);
+ screen_reset_echo(cmd_list);
+ return(1);
+ }/* End IF*/
+
+/*
+ * If ? debug mode is on, record new syntax tokens in the ? buffer
+ */
+ if(trace_mode_flag == YES){
+ last_token = ct;
+ while(last_token){
+ trace_mode(TRACE_C_PARSE,last_token,0);
+ last_token = last_token->next_token;
+ }/* End While */
+ }/* End IF */
+
+/*
+ * Ok, the state transition seems to have gone smoothly, if the go flag is set
+ * we are in immediate execute mode and can perform the execution phase of the
+ * state transition.
+ */
+ if(resume_execute_ct != NULL){
+ ct = resume_execute_ct;
+ ct->ctx.go_flag = YES;
+ resume_execute_ct = NULL;
+ }
+
+/*
+ * We need a place to chain off undo tokens. If we use the current token,
+ * loops will distribute undo tokens all over the parse tree, with no way
+ * to tell which order they were created in. So we find the last token, and
+ * chain everything off of that. That way, if a rubout is hit, all the undo
+ * tokens will be undone in order back to the last keystroke token. As the
+ * user types more keystrokes, we keep finding the new end of the command
+ * chain, and chaining undo tokens off of that. Thus order will be preserved.
+ */
+ final_token = ct;
+ while (final_token->next_token) final_token = final_token->next_token;
+ last_token = ct->prev_token;
+
+ while(ct){
+/*
+ * We still want to copy tmpvals forward from state to state unless the
+ * parse stage is attempting a store.
+ */
+ if(last_token){
+/*
+ * Carry the argument values forward to this state. If the last state was a
+ * RETURN state, then only copy out the tmpval, and fetch the callers iargs.
+ */
+ if((ct->flags & TOK_M_PARSELOAD_IARG1) == 0){
+ ct->ctx.iarg1 = last_token->ctx.iarg1;
+ }/* End IF */
+ if((ct->flags & TOK_M_PARSELOAD_IARG2) == 0){
+ ct->ctx.iarg2 = last_token->ctx.iarg2;
+ }/* End IF */
+
+ if(ct->ctx.state == STATE_C_RETURN){
+ if((ct->flags & TOK_M_PARSELOAD_IARG1) == 0){
+ ct->ctx.iarg1 = ct->ctx.caller_token->ctx.iarg1;
+ }/* End IF */
+ if((ct->flags & TOK_M_PARSELOAD_IARG2) == 0){
+ ct->ctx.iarg2 = ct->ctx.caller_token->ctx.iarg2;
+ }/* End IF */
+ }/* End IF */
+/*
+ * If the next state is STOREVAL, the syntax stage is setting storeval. The
+ * typical example of this is when the syntax stage sees a constant like '1'.
+ * It uses this state to set the temporary value to 1.
+ */
+ if(
+ ct->execute_state != EXEC_C_STOREVAL &&
+ ((ct->flags & TOK_M_PARSELOAD_TMPVAL) == 0)
+ ){
+ ct->ctx.tmpval = last_token->ctx.tmpval;
+ }/* End IF */
+
+ }/* End IF */
+
+ if(ct->execute_state){
+/*
+ * This is sort of a kludge, but it's an easy place to see if this
+ * state transition causes the buffer to become modified. If so, we
+ * link on an undo token to that if this gets undone, the modified
+ * status gets put back. If we didn't do it here, we would have to
+ * either do it in every command, and that would get tedious.
+ */
+ old_modified_state = curbuf->ismodified;
+ old_curbuf = curbuf;
+
+ if(trace_mode_flag == YES){
+ trace_ct = *ct;
+ }/* End IF */
+
+ status = execute_a_state(ct,final_token);
+ if(status == BLOCKED){
+ resume_execute_ct = ct;
+ }
+/*
+ * An INVALIDATE status occurs when the execute engine changes the
+ * command token list in such a way that we may no longer be pointing
+ * at something valid. So we back out, and re-enter..
+ */
+ if(status == INVALIDATE){
+ return(SUCCESS);
+ }
+
+ if(trace_mode_flag == YES){
+ trace_mode(TRACE_C_EXEC,&trace_ct,ct);
+ }/* End IF */
+/*
+ * If the modified state went from unmodified to modified, link on an
+ * undo token incase this gets undone - then the modified status will
+ * get put back to its original state.
+ */
+ if(old_modified_state == NO && old_curbuf->ismodified == YES){
+ if(ct->undo_list){
+ ut = ct->undo_list;
+ while(ut->next_token) ut = ut->next_token;
+ ut->next_token = allocate_undo_token(NULL);
+ ut = ut->next_token;
+ }/* End IF */
+
+ else ct->undo_list = ut = allocate_undo_token(NULL);
+ if(ut != NULL){
+ ut->opcode = UNDO_C_MODIFIED;
+ ut->carg1 = (char *)old_curbuf;
+ }/* End IF */
+ }/* End IF */
+
+ if(susp_flag) cmd_pause();
+/*
+ * Since we are in immediate execute mode, if we get an error during the
+ * execution phase, we know it was generated by the most recently input
+ * character. Therefore, we can handle it the same way we do during the syntax
+ * parsing, by pretending a rubout character was input.
+ */
+ if(status == BLOCKED) break;
+ if(intr_flag) break;
+
+ if(status != SUCCESS){
+ token_used = YES;
+ while(ct->next_token) ct = ct->next_token;
+ ct = parse_rubout_character(ct,0);
+ screen_reset_echo(cmd_list);
+ break;
+ }/* End IF */
+ }/* End IF */
+
+ last_token = ct;
+
+ if(jump_to_token){
+ ct = jump_to_token;
+ jump_to_token = NULL;
+ continue;
+ }
+
+ if(ct->next_token) ct = ct->next_token;
+ else break;
+ }/* End While */
+/*
+ * Now regardless of whether go_flag is set, chain ct forward to the end of
+ * the token list.
+ */
+ while(ct->next_token) ct = ct->next_token;
+/*
+ * If we hit FINALSTATE, this is the way the state machine has of telling us
+ * to reset to the begining parser state. We remember the final token address
+ * so that all additional undo tokens can be chained off of it.
+ */
+ if(ct->ctx.state == STATE_C_FINALSTATE){
+ return(0);
+ }/* End IF */
+
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* TECMACRO - Here to execute a Q register as a macro
+ *
+ * Function:
+ *
+ * This routine executes the specified q register as a macro. It returns
+ * SUCCESS or FAILURE depending on what happens within the macro. Note
+ * that it is very similar to the main parse routine. There are two major
+ * differences: First, the characters are gotton from the q-register
+ * rather than the input stream, and second, the entire parse is done
+ * to completion before any execution takes place. This means that the
+ * macro can modify the contents of the q-register without changing the
+ * way the macro will execute.
+ */
+int
+tecmacro(qbp,input_ct,macro_cmd_list)
+register struct buff_header *qbp;
+struct cmd_token *input_ct;
+struct cmd_token **macro_cmd_list;
+{
+register struct cmd_token *ct;
+struct cmd_token trace_ct;
+register struct cmd_token *last_token;
+int c = 0;
+int status;
+char token_used;
+int i;
+char old_modified_state;
+struct buff_header *old_curbuf;
+struct undo_token *ut;
+
+ PREAMBLE();
+
+/*
+ * We start our command token list with a special token that simply means that
+ * this is the begining of the list. We also set the current state to be the
+ * initial state of the parser.
+ */
+ ct = allocate_cmd_token((struct cmd_token *)NULL);
+ if(ct == NULL) return(FAIL);
+
+ ct->opcode = TOK_C_FIRSTTOKEN;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->ctx.go_flag = NO;
+/*
+ * By setting STATUS_PASSED, we insure that the first command in the macro will
+ * get any arguments we have been passed.
+ */
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ ct->ctx.iarg1_flag = input_ct->ctx.iarg1_flag;
+ ct->ctx.iarg2_flag = input_ct->ctx.iarg2_flag;
+ ct->ctx.iarg1 = input_ct->ctx.iarg1;
+ ct->ctx.iarg2 = input_ct->ctx.iarg2;
+ *macro_cmd_list = ct;
+/*
+ * We set the token_used flag so that we will call the getc routine. This flag
+ * in general lets us remember whether or not a state transistion ate an input
+ * character or not.
+ */
+ token_used = YES;
+/*
+ * Now we loop until we reach a final state, or something horrible happens.
+ */
+ i = 0;
+
+ while(i <= qbp->zee){
+/*
+ * Allocate a command token to hold the next state transition in. The routine
+ * will automatically link it onto the end of the list.
+ */
+ ct = allocate_cmd_token(ct);
+ if(ct == NULL) return(FAIL);
+/*
+ * Here we check for the special RETURN state. Although all this could be done
+ * in the state machine itself, this was a good place to consolidate things.
+ * We pop the state machine stack a level so as to return to the calling state.
+ * Note that iarg1 and iarg2 are copied from the calling state, so that they
+ * are preserved. This means that the substate must return it's value through
+ * tmpval which is not preserved.
+ */
+ if(ct->ctx.state == STATE_C_RETURN){
+ ct->ctx.state = ct->ctx.return_state;
+ ct->ctx.return_state = ct->ctx.caller_token->ctx.return_state;
+ ct->ctx.iarg1_flag = ct->ctx.caller_token->ctx.iarg1_flag;
+ ct->ctx.iarg2_flag = ct->ctx.caller_token->ctx.iarg2_flag;
+ ct->ctx.iarg1 = ct->ctx.caller_token->ctx.iarg1;
+ ct->ctx.iarg2 = ct->ctx.caller_token->ctx.iarg2;
+ ct->ctx.caller_token = ct->ctx.caller_token->ctx.caller_token;
+ }/* End IF */
+/*
+ * Set up the comand token a little
+ */
+ ct->opcode = TOK_C_COPYTOKEN;
+ ct->flags |= TOK_M_EAT_TOKEN;
+ ct->input_byte = c;
+/*
+ * If the last state ate the input character, we need to fetch another from the
+ * command source.
+ */
+ if(token_used == YES){
+ if(i == qbp->zee) c = ESCAPE;
+ else c = buff_contents(qbp,i++);
+#ifdef NEEDED_WHICH_I_DONT_THINK_IT_IS
+ if(parse_special_character(ct,c)){
+ ct = *macro_cmd_list;
+ while(ct->next_token) ct = ct->next_token;
+ continue;
+ }/* End IF */
+#endif
+ ct->opcode = TOK_C_INPUTCHAR;
+ ct->input_byte = c;
+ }/* End IF */
+
+ token_used = NO;
+/*
+ * Call the state machine to process this character. Although more command
+ * tokens may be added onto the list by the state machine, we leave ct where
+ * it is for the moment so that we can work forward through all the generated
+ * states.
+ */
+ parse_input_character(ct,*macro_cmd_list);
+/*
+ * If the state machine indicates that the input character was eaten, remember
+ * this fact.
+ */
+ if(ct->flags & TOK_M_EAT_TOKEN) token_used = YES;
+/*
+ * If this character would cause us to transition into an error state, we
+ * terminate the macro and indicate where the error was.
+ */
+ if(ct->ctx.state == STATE_C_ERRORSTATE){
+ token_used = YES;
+ while(ct->next_token) ct = ct->next_token;
+ while(ct->opcode != TOK_C_FIRSTTOKEN){
+ ct = parse_rubout_character(ct,0);
+ }/* End While */
+ return(FAIL);
+ }/* End IF*/
+/*
+ * Always chain ct forward over any generated tokens
+ */
+ while(ct->next_token) ct = ct->next_token;
+/*
+ * If we hit FINALSTATE, this is the way the state machine has of telling us
+ * to reset to the begining parser state.
+ */
+ if(ct->ctx.state == STATE_C_FINALSTATE) break;
+
+ }/* End While */
+
+/*
+ * Now that the syntax parsing has been completed, we start execution of the
+ * macro.
+ */
+ ct = last_token = *macro_cmd_list;
+
+ while(ct){
+
+/*
+ * If we hit the final state, we don't need to go any further.
+ */
+ if(ct->ctx.state == STATE_C_FINALSTATE) break;
+
+/*
+ * Carry the argument values forward to this state. If the last state was a
+ * RETURN state, then only copy out the tmpval, and fetch the callers iargs.
+ */
+ if((ct->flags & TOK_M_PARSELOAD_IARG1) == 0){
+ ct->ctx.iarg1 = last_token->ctx.iarg1;
+ }/* End IF */
+ if((ct->flags & TOK_M_PARSELOAD_IARG2) == 0){
+ ct->ctx.iarg2 = last_token->ctx.iarg2;
+ }/* End IF */
+
+ if(ct->ctx.state == STATE_C_RETURN){
+ if((ct->flags & TOK_M_PARSELOAD_IARG1) == 0){
+ ct->ctx.iarg1 = ct->ctx.caller_token->ctx.iarg1;
+ }/* End IF */
+ if((ct->flags & TOK_M_PARSELOAD_IARG2) == 0){
+ ct->ctx.iarg2 = ct->ctx.caller_token->ctx.iarg2;
+ }/* End IF */
+ }/* End IF */
+/*
+ * If the next state is STOREVAL, the syntax stage is setting storeval. The
+ * typical example of this is when the syntax stage sees a constant like '1'.
+ * It uses this state to set the temporary value to 1.
+ */
+ if(
+ ct->execute_state != EXEC_C_STOREVAL &&
+ ((ct->flags & TOK_M_PARSELOAD_TMPVAL) == 0)
+ ){
+ ct->ctx.tmpval = last_token->ctx.tmpval;
+ }/* End IF */
+
+/*
+ * If this token has an execute state, call the execute phase to implement it.
+ */
+ if(trace_mode_flag == YES){
+ trace_mode(TRACE_C_MACRO,ct,0);
+ }/* End IF */
+
+ if(ct->execute_state){
+
+ old_modified_state = curbuf->ismodified;
+ old_curbuf = curbuf;
+
+ if(trace_mode_flag == YES){
+ trace_ct = *ct;
+ }/* End IF */
+
+ status = execute_a_state(ct,*macro_cmd_list);
+
+ if(trace_mode_flag == YES){
+ trace_mode(TRACE_C_EXEC,&trace_ct,ct);
+ }/* End IF */
+
+ if(old_modified_state == NO && old_curbuf->ismodified == YES){
+ if(ct->undo_list){
+ ut = ct->undo_list;
+ while(ut->next_token) ut = ut->next_token;
+ ut->next_token = allocate_undo_token(NULL);
+ ut = ut->next_token;
+ }/* End IF */
+
+ else ct->undo_list = ut = allocate_undo_token(NULL);
+ if(ut != NULL){
+ ut->opcode = UNDO_C_MODIFIED;
+ ut->carg1 = (char *)old_curbuf;
+ }/* End IF */
+ }/* End IF */
+
+ if(status != SUCCESS) goto cleanup;
+ if(exit_flag == YES) goto cleanup;
+ if(intr_flag) goto cleanup;
+ if(susp_flag) cmd_pause();
+ }/* End IF */
+/*
+ * Ok, now chain to the next token, or quit if done
+ */
+ last_token = ct;
+ ct = ct->next_token;
+ if(jump_to_token){
+ ct = jump_to_token;
+ last_token = ct;
+ jump_to_token = NULL;
+ }/* End IF */
+ }/* End While */
+
+cleanup:
+
+ if(input_ct->ctx.flags & CTOK_M_STATUS_PASSED){
+ input_ct->ctx.iarg1_flag = last_token->ctx.iarg1_flag;
+ input_ct->ctx.iarg1 = last_token->ctx.iarg1;
+ }/* End IF */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* PARSE_SPECIAL_CHARACTER - Test for and handle special characters
+ *
+ * Function:
+ *
+ * This routine is called on each input character to determine whether
+ * it requires special handling. It catches characters such as rubout,
+ * ^W, ^U.
+ */
+int
+parse_special_character(ct,c)
+register struct cmd_token *ct;
+register int c;
+{
+char state_seen;
+int tmp;
+
+ PREAMBLE();
+
+ switch(c){
+ case RUBOUT:
+ ct = parse_rubout_character(ct,1);
+ break;
+ case CNTRL_U:
+/*
+ * Get rid of any <CR> right at the deletion point
+ */
+ if(ct->opcode == TOK_C_INPUTCHAR){
+ ct = parse_rubout_character(ct,1);
+ }/* End IF */
+
+ while(ct != cmd_list){
+ if(ct->opcode == TOK_C_INPUTCHAR && ct->input_byte == '\n'){
+ break;
+ }/* End IF */
+ ct = parse_rubout_character(ct,1);
+ }/* End While */
+ break;
+ case CNTRL_W:
+ state_seen = 0;
+ tmp = 0;
+
+ while(ct != cmd_list){
+ if(ct->flags & TOK_M_WORDBOUNDARY){
+ state_seen = 1;
+ break;
+ }/* End IF */
+
+ if(ct->opcode == TOK_C_INPUTCHAR){
+ tmp += 1;
+ if(tmp == 1 && ct->input_byte == '\n'){
+ preserve_rubout_char(ct->input_byte);
+ ct = parse_rubout_cmd_token(ct);
+ break;
+ }/* End IF */
+
+ if(ct->input_byte != ' ' && ct->input_byte != '\t') break;
+ preserve_rubout_char(ct->input_byte);
+
+ }/* End IF */
+
+ ct = parse_rubout_cmd_token(ct);
+
+ }/* End While */
+
+ while(ct != cmd_list){
+ if(ct->flags & TOK_M_WORDBOUNDARY) state_seen = 1;
+
+ if(ct->opcode == TOK_C_INPUTCHAR){
+ if(state_seen){
+ preserve_rubout_char(ct->input_byte);
+ ct = parse_rubout_cmd_token(ct);
+ break;
+ }/* End IF */
+
+ if(isspace((int)ct->input_byte)) break;
+ preserve_rubout_char(ct->input_byte);
+
+ }/* End IF */
+
+ ct = parse_rubout_cmd_token(ct);
+
+ }/* End While */
+
+ break;
+
+ case CNTRL_R:
+ if(!unpreserve_rubout_char(ct)){
+ error_message("^R no input tokens available");
+ }/* End IF */
+ return(1);
+
+ default:
+ return(0);
+ }/* End Switch */
+
+ screen_reset_echo(cmd_list);
+ return(1);
+
+}/* End Routine */
+
+
+
+/* PARSE_RUBOUT_CHARACTER - Rubout the most recent character
+ *
+ * Function:
+ *
+ * This routine is called when a rubout character is typed. We have to
+ * back the parser up to the state it was in before the original char
+ * was typed, as well as performing any undo functions to make sure
+ * that the edit buffer also gets backed up.
+ */
+struct cmd_token *
+parse_rubout_character(ct,preserve_flag)
+register struct cmd_token *ct;
+int preserve_flag;
+{
+register struct cmd_token *oct;
+struct undo_token *ut;
+char saved_opcode;
+
+ PREAMBLE();
+
+ while(1){
+
+/*
+ * If the preserve flag is on, we want to save any deleted bytes in the
+ * q-register that is used for this purpose. That way, the user can
+ * correct for mistakenly typing the wrong rubout code.
+ */
+ if(preserve_flag && ct->opcode == TOK_C_INPUTCHAR){
+ preserve_rubout_char(ct->input_byte);
+ }/* End IF */
+
+/*
+ * For each of the command tokens, we need to undo all the chained undo
+ * tokens.
+ */
+ while( (ut = ct->undo_list) != NULL ){
+/*
+ * Take the top undo token off of the list and change the listhead to point
+ * to it's child.
+ */
+ ct->undo_list = ut->next_token;
+ ut->next_token = NULL;
+/*
+ * Now call the undo routine to back out the changes to the edit buffer that
+ * this token calls for. Then place the token back on the free list.
+ */
+ parser_undo(ut);
+ free_undo_token(ut);
+
+ }/* End While */
+/*
+ * If this is TOK_C_FIRSTTOKEN, it means he is trying to rubout the head of the
+ * list. This is probably not such a hot idea...
+ */
+ if(ct->opcode == TOK_C_FIRSTTOKEN) break;
+/*
+ * We save the opcode so that after cleaning up the token, we will know whether
+ * it was an input character or not.
+ */
+ saved_opcode = ct->opcode;
+
+ oct = ct->prev_token;
+ if(oct == NULL) break;
+ oct->next_token = NULL;
+ ct->prev_token = NULL;
+ free_cmd_token(ct);
+ ct = oct;
+/*
+ * If this was an input character, then we have backed up enough.
+ */
+ if(saved_opcode == TOK_C_INPUTCHAR) break;
+
+ }/* End While */
+
+ return(ct);
+
+}/* End Routine */
+
+
+
+/* PARSE_RUBOUT_CMD_TOKEN - Rubout the most recent command token
+ *
+ * Function:
+ *
+ * This routine is called to remove the last token on the command
+ * list. It gets used in rubout / ^U / ^W processing.
+ */
+struct cmd_token *
+parse_rubout_cmd_token(ct)
+register struct cmd_token *ct;
+{
+register struct cmd_token *oct;
+struct undo_token *ut;
+
+ PREAMBLE();
+
+/*
+ * We need to undo all the undo tokens chained off of this command token.
+ */
+ while( (ut = ct->undo_list) != NULL ){
+/*
+ * Take the top undo token off of the list and change the listhead to point
+ * to it's child.
+ */
+ ct->undo_list = ut->next_token;
+ ut->next_token = NULL;
+/*
+ * Now call the undo routine to back out the changes to the edit buffer that
+ * this token calls for. Then place the token back on the free list.
+ */
+ parser_undo(ut);
+ free_undo_token(ut);
+
+ }/* End While */
+
+ oct = ct->prev_token;
+ if(oct) oct->next_token = NULL;
+ ct->prev_token = NULL;
+ free_cmd_token(ct);
+ return(oct);
+
+}/* End Routine */
+
+
+
+/* PARSER_GETC - Return next input character
+ *
+ * Function:
+ *
+ * This routine is called by the parser when another input byte is needed.
+ */
+int
+parser_getc()
+{
+register int i;
+char inbuf[4];
+
+#ifdef VMS
+ short qio_iosb[4];
+#endif /* VMS */
+
+ PREAMBLE();
+
+/*
+ * If there are no unclaimed bytes in the buffer, we need to read one
+ * from the command channel.
+ */
+ input_pending_flag = tty_input_pending();
+
+ if(input_pending_flag == NO){
+ if(ring_audible_bell){
+ term_putc(BELL);
+ ring_audible_bell = 0;
+ }/* End IF */
+ screen_echo('\0');
+ screen_format_windows();
+ screen_refresh();
+ }/* End IF */
+
+#ifdef CHECKPOINT
+ if(checkpoint_enabled == YES && checkpoint_flag == YES){
+ cmd_checkpoint();
+ checkpoint_flag = NO;
+ }/* End IF */
+#endif /* CHECKPOINT */
+
+ while(1){
+
+#ifdef UNIX
+
+ if(susp_flag){
+ pause_while_in_input_wait();
+ continue;
+ }/* End IF */
+
+ if(resize_flag){
+ screen_resize();
+ }/* End IF */
+
+ waiting_for_input_flag = YES;
+ i = read(tty_input_chan,inbuf,1);
+ waiting_for_input_flag = NO;
+
+ intr_flag = 0;
+
+ if(i >= 0) break;
+
+ if(errno == EINTR) continue;
+#endif
+#ifdef VMS
+ waiting_for_input_flag = YES;
+ i = sys$qiow(0,tty_input_chan,IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
+ qio_iosb,0,0,inbuf,1,0,0,0,0);
+ waiting_for_input_flag = NO;
+ if(!(i & STS$M_SUCCESS)) exit(i);
+if(qio_iosb[0] != 1)
+printf("!!! iosb[0] is %d\n",qio_iosb[0]);
+ if(inbuf[0] == '\r') inbuf[0] = '\n';
+ else if(inbuf[0] == '\n') inbuf[0] = '\r';
+ if(inbuf[0] == '`') inbuf[0] = ESCAPE;
+ if(inbuf[0] == CNTRL_Z && suspend_is_okay_flag == YES){
+ pause_while_in_input_wait();
+ continue;
+ }/* End IF */
+
+ break;
+#endif
+ perror("error reading command input");
+ punt(errno);
+ }/* End While */
+
+ screen_reset_message();
+
+ input_pending_flag = tty_input_pending();
+
+ if(inbuf[0] == alternate_escape_character){
+ return(ESCAPE);
+ }/* End IF */
+
+ if(inbuf[0] == main_delete_character){
+ return(RUBOUT);
+ }/* End IF */
+
+ if(inbuf[0] == alternate_delete_character){
+ return(RUBOUT);
+ }/* End IF */
+
+ return(inbuf[0]);
+
+}/* End Routine */
+
+
+
+/* PRESERVE_RUBOUT_CHARCTER - Save rubbed out characters
+ *
+ * Function:
+ *
+ * This routine is called in response to the user rubbing out input
+ * characters with rubout, ^U, or ^W. Sometimes he does this by
+ * mistake, and can delete large amounts of typing unintentially.
+ * This routine saves these characters in a special Q-register so
+ * that he can get them back if he wants.
+ */
+void
+preserve_rubout_char(the_byte)
+char the_byte;
+{
+register struct buff_header *qbp;
+
+ PREAMBLE();
+
+/*
+ * Get a pointer to the special Q-register which holds the characters which
+ * have been rubbed out.
+ */
+ qbp = buff_qfind('@',1);
+ if(qbp == NULL){
+ return;
+ }/* End IF */
+
+/*
+ * Put the deleted byte into the Q-register where it can be retrieved by
+ * the user if so desired.
+ */
+ buff_insert_char(qbp,0,the_byte);
+
+}/* End Routine */
+
+
+
+/* UNPRESERVE_RUBOUT_CHAR - Poke a rubbed out char back into the parse
+ *
+ * Function:
+ *
+ * This routine is called on behalf of a ^R command which causes the
+ * most recent rubbed out character to be restored to the parse tree.
+ * The return code determines whether this was done okay or not.
+ */
+int
+unpreserve_rubout_char(ct)
+struct cmd_token *ct;
+{
+register struct buff_header *qbp;
+int c;
+
+ PREAMBLE();
+
+/*
+ * Get a pointer to the special Q-register which holds the characters which
+ * have been rubbed out.
+ */
+ qbp = buff_qfind('@',1);
+ if(qbp == NULL){
+ return(FAIL);
+ }/* End IF */
+
+/*
+ * Make sure thre are some bytes in it
+ */
+ if(qbp->zee <= 0) return(FAIL);
+
+/*
+ * Get the first byte saved in the Q-register
+ */
+ c = buff_contents(qbp,0);
+ buff_delete(qbp,0,1);
+
+ tecparse_syntax(c);
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* PARSER_CLEAN_PRESERVE_LIST - Zero the rubout preserve Q-register
+ *
+ * Function:
+ *
+ * This routine is called at double-escape time to clean up the
+ * special Q-register so that it doesn't grow without bounds.
+ */
+void
+parser_clean_preserve_list()
+{
+register struct buff_header *qbp;
+
+ PREAMBLE();
+
+/*
+ * Get a pointer to the special Q-register which holds the characters which
+ * have been rubbed out.
+ */
+ qbp = buff_qfind('@',1);
+ if(qbp == NULL){
+ return;
+ }/* End IF */
+
+/*
+ * Delete all the bytes in the Q-register
+ */
+ buff_delete(qbp,0,qbp->zee);
+
+}/* End Routine */
+
+
+
+/* PARSER_DUMP_COMMAND_LINE - Copy the current parse tree into a Q-register
+ *
+ * Function:
+ *
+ * This routine copies the input bytes from the current command string
+ * into the specified Q-register.
+ */
+void
+parser_dump_command_line(qbp)
+register struct buff_header *qbp;
+{
+register struct cmd_token *ct;
+
+ PREAMBLE();
+
+ ct = cmd_list;
+
+ while(ct){
+ if(ct->opcode == TOK_C_INPUTCHAR){
+ buff_insert_char(qbp,qbp->zee,ct->input_byte);
+ }/* End IF */
+ ct = ct->next_token;
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* PARSER_REPLACE_COMMAND_LINE - Replace current parse tree
+ *
+ * Function:
+ *
+ * This routine replaces the current command string with the contents
+ * the specified Q-register.
+ */
+int
+parser_replace_command_line(qbp)
+register struct buff_header *qbp;
+{
+register struct cmd_token *ct;
+register struct cmd_token *first_different_ct;
+register int c;
+char temp;
+register char *command_buffer;
+register int cb_zee;
+
+ PREAMBLE();
+
+ cb_zee = 0;
+ command_buffer = tec_alloc(TYPE_C_CBUFF,qbp->zee);
+ if(command_buffer == NULL) return(FAIL);
+/*
+ * Walk the parse list to find the first place there is a difference
+ * between the current parse list, and the one to be installed. When
+ * we find the first difference, remember it, and then start copying
+ * the changed bytes into our temporary Q-register.
+ */
+ first_different_ct = NULL;
+ ct = cmd_list;
+ for(c = 0; c < qbp->zee - 1; c++){
+ temp = buff_contents(qbp,c);
+ if(first_different_ct == NULL){
+ while(ct && ct->opcode != TOK_C_INPUTCHAR){
+ ct = ct->next_token;
+ }/* End While */
+ if(ct && ct->input_byte != temp) first_different_ct = ct;
+ if(ct && ct->next_token) ct = ct->next_token;
+ }/* End IF */
+ if(first_different_ct){
+ command_buffer[cb_zee++] = temp;
+ }/* End IF */
+ }/* End FOR */
+
+ if(ct != NULL && first_different_ct == NULL){
+ while(ct->next_token){
+ if(ct->opcode == TOK_C_INPUTCHAR) break;
+ ct = ct->next_token;
+ }
+ first_different_ct = ct;
+ }
+/*
+ * We only have to do the following if there were any changed bytes at
+ * all. If he changed his mind and didn't modify the command list at all,
+ * nothing really has to happen at all.
+ */
+ if(first_different_ct){
+ while(first_different_ct->next_token != NULL &&
+ first_different_ct->next_token->opcode != TOK_C_INPUTCHAR){
+ first_different_ct = first_different_ct->next_token;
+ }
+ ct = cmd_list;
+ while(ct->next_token) ct = ct->next_token;
+/*
+ * Remove from the tail of the parse list all the bytes up to and including
+ * the first different character. This leaves us with a parse list which is
+ * just the part that has not changed.
+ */
+ temp = 1;
+ while(ct != cmd_list && temp != 0){
+ if(ct == first_different_ct) temp = 0;
+ ct = parse_rubout_character(ct,0);
+ }/* End While */
+
+/*
+ * Now we walk through the Q-register, adding these bytes to the end of the
+ * parse list. These are the bytes which are different from the original
+ * parse list.
+ */
+ qbp->pos_cache.lbp = NULL;
+ input_pending_flag = YES;
+ for(c = 0; c < cb_zee; c++){
+ temp = command_buffer[c];
+ tecparse_syntax(temp);
+ }/* End While */
+ }/* End IF */
+ tec_release(TYPE_C_CBUFF,command_buffer);
+ parser_reset_echo();
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* ALLOCATE_CMD_TOKEN - Allocate a command token structure
+ *
+ * Function:
+ *
+ * This routine is called to allocate a command token structure. If there
+ * is one on the free list, it is used, otherwise we allocate one. If
+ * an old_token was specified, the context area is copied into the new
+ * token, which has the effect of rippling the context forward through
+ * the parse.
+ */
+struct cmd_token *
+allocate_cmd_token(old_token)
+register struct cmd_token *old_token;
+{
+register struct cmd_token *ct;
+
+ PREAMBLE();
+
+/*
+ * Call the memory allocator for a command token block
+ */
+ ct = (struct cmd_token *)tec_alloc(TYPE_C_CMD,sizeof(struct cmd_token));
+ if(ct == NULL) return(NULL);
+
+/*
+ * We set up the initial state to be that of an empty token, with fields
+ * which are not set up yet set to their proper defaults.
+ */
+ ct->opcode = TOK_C_UNUSED;
+ ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = NO;
+ ct->ctx.iarg1 = ct->ctx.iarg2 = 0;
+ ct->ctx.carg = NULL;
+ ct->next_token = NULL;
+ ct->undo_list = NULL;
+ ct->execute_state = 0;
+ ct->flags = 0;
+
+ ct->prev_token = NULL;
+ ct->ctx.state = 0;
+ ct->ctx.go_flag = NO;
+ ct->ctx.return_state = 0;
+ ct->ctx.caller_token = NULL;
+ ct->ctx.pnest = ct->ctx.inest = ct->ctx.cnest = 0;
+/*
+ * If there was an old token specified, two things must happen. First, we
+ * have to link the new token onto the old token list. Second, we have to
+ * copy the context area from the old token into the new one.
+ */
+ if(old_token){
+ old_token->next_token = ct;
+ ct->prev_token = old_token;
+ ct->ctx = old_token->ctx;
+ }/* End IF */
+
+ return(ct);
+
+}/* End Routine */
+
+
+
+/* FREE_CMD_TOKEN - Routine to place a cmd token on the free list
+ *
+ * Function:
+ *
+ * This routine is called with the address of the command token
+ * to be placed on the free list.
+ */
+void
+free_cmd_token(ct)
+register struct cmd_token *ct;
+{
+
+ PREAMBLE();
+
+ tec_release(TYPE_C_CMD,(char *)ct);
+
+}/* End Routine */
+
+/* PAUSE_WHILE_IN_INPUT_WAIT - Pauses with the terminal in a good state
+ *
+ * Function:
+ *
+ * This routine is called to pause the editor while we have been in
+ * an input wait state. The big deal here is that we want to remove
+ * the reverse video box that may be on the echo line before we pause
+ * back to the system command processor.
+ */
+void
+pause_while_in_input_wait()
+{
+
+ PREAMBLE();
+
+ screen_reset_echo(cmd_list);
+ screen_refresh();
+ cmd_pause();
+ screen_echo('\0');
+ screen_refresh();
+
+}/* End Routine */
+
+
+
+/* PARSER_CLEANUP_CTLIST - Routine to deallocate all the blocks on a ct list
+ *
+ * Function:
+ *
+ * This function deallocates all the blocks on a command token list,
+ * including cmd_tokens and undo_tokens.
+ */
+void
+parser_cleanup_ctlist(ct)
+register struct cmd_token *ct;
+{
+register struct cmd_token *oct;
+
+ PREAMBLE();
+
+/*
+ * For each command token, if it has a list of undo tokens connected we
+ * call the routine which knows how to clean them up.
+ */
+ while(ct){
+ if(ct->undo_list){
+ tecundo_cleanup(ct->undo_list);
+ }/* End IF */
+
+ ct->undo_list = NULL;
+/*
+ * Now we remember what the next token in the list is, and then call the
+ * routine to free the current one.
+ */
+ oct = ct;
+ ct = ct->next_token;
+ free_cmd_token(oct);
+
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* PARSE_ANY_ARGUMENTS - Called by routines that don't want any arguments
+ *
+ * Function:
+ *
+ * This function is called by states which don't want to accept any
+ * arguments. If there are any present, it will generate an error message
+ * and set the error state.
+ */
+int
+parse_any_arguments(ct,cmd_name)
+register struct cmd_token *ct;
+char *cmd_name;
+{
+char tmp_message[LINE_BUFFER_SIZE];
+
+ PREAMBLE();
+
+/*
+ * If either of the iarg flags is set, that means we received an argument.
+ */
+ if(ct->ctx.iarg1_flag == YES || ct->ctx.iarg2_flag == YES){
+ (void) strcpy(tmp_message,"?The ");
+ (void) strcat(tmp_message,cmd_name);
+ (void) strcat(tmp_message," command accepts no arguments");
+ error_message(tmp_message);
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return(1);
+ }/* End IF */
+
+ return(0);
+
+}/* End Routine */
+
+/* PARSE_MORE_THAN_ONE_ARG - Called by routines that only want one arg
+ *
+ * Function:
+ *
+ * This function is called by states which only want to accept one
+ * argument. If there are two present, it will generate an error message
+ * and set the error state.
+ */
+int
+parse_more_than_one_arg(ct,cmd_name)
+register struct cmd_token *ct;
+char *cmd_name;
+{
+char tmp_message[LINE_BUFFER_SIZE];
+
+ PREAMBLE();
+
+/*
+ * If the iarg2 flag is set, that means that we received two arguments when
+ * we really only want one.
+ */
+ if(ct->ctx.iarg2_flag == YES){
+ (void) strcpy(tmp_message,"?Two Arguments to ");
+ (void) strcat(tmp_message,cmd_name);
+ (void) strcat(tmp_message," not allowed");
+ error_message(tmp_message);
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return(1);
+ }/* End IF */
+
+ return(0);
+
+}/* End Routine */
+
+
+
+/* PARSE_ILLEGAL_BUFFER_POSITION - Check for illegal buffer positions
+ *
+ * Function:
+ *
+ * This routine is called to verify that buffer positions specified are
+ * legal. If they are not, it generates an error message.
+ */
+int
+parse_illegal_buffer_position(pos1,pos2,cmd_name)
+register int pos1;
+register int pos2;
+char *cmd_name;
+{
+char illegal_position;
+char tmp_message[LINE_BUFFER_SIZE];
+
+ PREAMBLE();
+
+ illegal_position = 0;
+ if(pos1 < 0) illegal_position = 1;
+ if(pos2 < 0) illegal_position = 1;
+ if(pos1 > curbuf->zee) illegal_position = 1;
+ if(pos2 > curbuf->zee) illegal_position = 1;
+
+ if(illegal_position == 0) return(0);
+
+ (void) strcpy(tmp_message,"?Attempt to Move Pointer Off Page with ");
+ (void) strcat(tmp_message,cmd_name);
+ error_message(tmp_message);
+ return(1);
+
+}/* End Routine */
+
+
+
+/* PARSER_RESET_ECHO - Re-echo the input line
+ *
+ * Function:
+ *
+ * This is just an entry point to restore the echo line from a module
+ * that doesn't want to know about the cmd_list structure.
+ */
+void
+parser_reset_echo()
+{
+ PREAMBLE();
+
+ screen_reset_echo(cmd_list);
+
+}/* End Routine */
+
+
+
+/* TRACE_MODE - Trace execution of commands
+ *
+ * Function:
+ *
+ * This routine is called at parse and execute time if tracing has been
+ * enabled by the ? command. It causes q-register ? to be filled with
+ * execution trace information.
+ */
+void
+trace_mode(phase,ct0,ct1)
+register int phase;
+struct cmd_token *ct0;
+struct cmd_token *ct1;
+{
+register struct buff_header *qbp;
+char tmp_message[LINE_BUFFER_SIZE];
+register char *cp;
+register char *state_name;
+char *trace_convert_state_to_name();
+char *trace_convert_opcode_to_name();
+char *trace_convert_exec_state_to_name();
+
+ PREAMBLE();
+
+ qbp = buff_qfind('?',1);
+ if(qbp == NULL) return;
+
+ switch(phase){
+ case TRACE_C_PARSE:
+ state_name = trace_convert_opcode_to_name(ct0->opcode);
+ if(state_name == NULL) state_name = "UNKNOWN";
+ sprintf(tmp_message,"PARSE: 0x%08X Opcode %s %c",
+ (unsigned int)ct0,
+ state_name,
+/* ct0->opcode == TOK_C_INPUTCHAR ? ct0->input_byte : ' '); */
+ ct0->flags & TOK_M_EAT_TOKEN ? ct0->input_byte : ' ');
+ if(ct0->flags & TOK_M_EAT_TOKEN){
+ strcat(tmp_message," TOK_M_EAT_TOKEN");
+ }/* End IF */
+ if(ct0->flags & TOK_M_WORDBOUNDARY){
+ strcat(tmp_message," TOK_M_WORDBOUNDARY");
+ }/* End IF */
+ if(ct0->flags & TOK_M_PARSELOAD_IARG1){
+ strcat(tmp_message," TOK_M_PARSELOAD_IARG1");
+ }/* End IF */
+ if(ct0->flags & TOK_M_PARSELOAD_IARG2){
+ strcat(tmp_message," TOK_M_PARSELOAD_IARG2");
+ }/* End IF */
+ if(ct0->flags & TOK_M_PARSELOAD_TMPVAL){
+ strcat(tmp_message," TOK_M_PARSELOAD_TMPVAL");
+ }/* End IF */
+ strcat(tmp_message,"\n");
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+/* int execute_state; */
+ state_name = trace_convert_state_to_name(ct0->ctx.state);
+ if(state_name == NULL) state_name = "UNKNOWN";
+ sprintf(tmp_message," Parser State: %s '%c'",
+ state_name,isprint((int)ct0->q_register) ?
+ ct0->q_register : ' ');
+ if(ct0->ctx.flags & CTOK_M_COLON_SEEN){
+ strcat(tmp_message," CTOK_M_COLON_SEEN");
+ }/* End IF */
+ if(ct0->ctx.flags & CTOK_M_ATSIGN_SEEN){
+ strcat(tmp_message," CTOK_M_ATSIGN_SEEN");
+ }/* End IF */
+ if(ct0->ctx.flags & CTOK_M_STATUS_PASSED){
+ strcat(tmp_message," CTOK_M_STATUS_PASSED");
+ }/* End IF */
+ strcat(tmp_message,"\n");
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ if(ct0->ctx.go_flag == 0){
+ sprintf(tmp_message," Go flag clear\n");
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+ }/* End IF */
+
+ if(ct0->ctx.pnest || ct0->ctx.inest || ct0->ctx.cnest){
+ sprintf(tmp_message," PNEST: %d INEST %d CNEST %d\n",
+ ct0->ctx.pnest,ct0->ctx.inest,ct0->ctx.cnest);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+ }/* End IF */
+
+ sprintf(tmp_message," iarg1_flag %d iarg1 %d (0x%x)\n",
+ ct0->ctx.iarg1_flag,ct0->ctx.iarg1,ct0->ctx.iarg1);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," iarg2_flag %d iarg2 %d (0x%x)\n",
+ ct0->ctx.iarg2_flag,ct0->ctx.iarg2,ct0->ctx.iarg2);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," carg 0x%x\n",(unsigned int)ct0->ctx.carg);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," tmpval %d (0x%x)\n",
+ ct0->ctx.tmpval,ct0->ctx.tmpval);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ if(ct0->ctx.return_state || ct0->ctx.caller_token){
+ state_name = "";
+ if(ct0->ctx.return_state){
+ state_name =
+ trace_convert_state_to_name(ct0->ctx.return_state);
+ if(state_name == NULL) state_name = "UNKNOWN";
+ }/* End IF */
+ sprintf(tmp_message," return_state %s caller_token 0x%x\n",
+ state_name,(unsigned int)ct0->ctx.caller_token);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+ }/* End IF */
+ break;
+
+ case TRACE_C_EXEC:
+ state_name = trace_convert_exec_state_to_name(ct0->execute_state);
+ if(state_name == NULL) state_name = "UNKNOWN";
+ sprintf(tmp_message,"EXEC: 0x%08X Exec State: %s\n",
+ (unsigned int)ct1,state_name);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," iarg1_flag %d iarg1 %d (0x%x)",
+ ct0->ctx.iarg1_flag,ct0->ctx.iarg1,ct0->ctx.iarg1);
+ while(strlen(tmp_message) < 39) strcat(tmp_message," ");
+ strcat(tmp_message," ");
+ cp = &tmp_message[strlen(tmp_message)];
+ sprintf(cp," iarg1_flag %d iarg1 %d (0x%x)\n",
+ ct1->ctx.iarg1_flag,ct1->ctx.iarg1,ct1->ctx.iarg1);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," iarg2_flag %d iarg2 %d (0x%x)",
+ ct0->ctx.iarg2_flag,ct0->ctx.iarg2,ct0->ctx.iarg2);
+ while(strlen(tmp_message) < 39) strcat(tmp_message," ");
+ strcat(tmp_message," ");
+ cp = &tmp_message[strlen(tmp_message)];
+ sprintf(cp," iarg2_flag %d iarg2 %d (0x%x)\n",
+ ct1->ctx.iarg2_flag,ct1->ctx.iarg2,ct1->ctx.iarg2);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," carg 0x%x",(unsigned int)ct0->ctx.carg);
+ while(strlen(tmp_message) < 39) strcat(tmp_message," ");
+ strcat(tmp_message," ");
+ cp = &tmp_message[strlen(tmp_message)];
+ sprintf(cp," carg 0x%x\n",(unsigned int)ct1->ctx.carg);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," tmpval %d (0x%x)",
+ ct0->ctx.tmpval,ct0->ctx.tmpval);
+ while(strlen(tmp_message) < 39) strcat(tmp_message," ");
+ strcat(tmp_message," ");
+ cp = &tmp_message[strlen(tmp_message)];
+ sprintf(cp," tmpval %d (0x%x)\n",
+ ct1->ctx.tmpval,ct1->ctx.tmpval);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+ break;
+
+ case TRACE_C_MACRO:
+ state_name = trace_convert_opcode_to_name(ct0->opcode);
+ if(state_name == NULL) state_name = "UNKNOWN";
+ sprintf(tmp_message,"MACRO PLACEHOLDER: 0x%08X Opcode %s %c",
+ (unsigned int)ct0,
+ state_name,
+/* ct0->opcode == TOK_C_INPUTCHAR ? ct0->input_byte : ' '); */
+ ct0->flags & TOK_M_EAT_TOKEN ? ct0->input_byte : ' ');
+ if(ct0->flags & TOK_M_EAT_TOKEN){
+ strcat(tmp_message," TOK_M_EAT_TOKEN");
+ }/* End IF */
+ if(ct0->flags & TOK_M_WORDBOUNDARY){
+ strcat(tmp_message," TOK_M_WORDBOUNDARY");
+ }/* End IF */
+ if(ct0->flags & TOK_M_PARSELOAD_IARG1){
+ strcat(tmp_message," TOK_M_PARSELOAD_IARG1");
+ }/* End IF */
+ if(ct0->flags & TOK_M_PARSELOAD_IARG2){
+ strcat(tmp_message," TOK_M_PARSELOAD_IARG2");
+ }/* End IF */
+ if(ct0->flags & TOK_M_PARSELOAD_TMPVAL){
+ strcat(tmp_message," TOK_M_PARSELOAD_TMPVAL");
+ }/* End IF */
+ strcat(tmp_message,"\n");
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+/* int execute_state; */
+ state_name = trace_convert_state_to_name(ct0->ctx.state);
+ if(state_name == NULL) state_name = "UNKNOWN";
+ sprintf(tmp_message," Parser State: %s '%c'",
+ state_name,
+ isprint((int)ct0->q_register) ? ct0->q_register : ' ');
+ if(ct0->ctx.flags & CTOK_M_COLON_SEEN){
+ strcat(tmp_message," CTOK_M_COLON_SEEN");
+ }/* End IF */
+ if(ct0->ctx.flags & CTOK_M_ATSIGN_SEEN){
+ strcat(tmp_message," CTOK_M_ATSIGN_SEEN");
+ }/* End IF */
+ if(ct0->ctx.flags & CTOK_M_STATUS_PASSED){
+ strcat(tmp_message," CTOK_M_STATUS_PASSED");
+ }/* End IF */
+ strcat(tmp_message,"\n");
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ if(ct0->ctx.go_flag == 0){
+ sprintf(tmp_message," Go flag clear\n");
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+ }/* End IF */
+
+ if(ct0->ctx.pnest || ct0->ctx.inest || ct0->ctx.cnest){
+ sprintf(tmp_message," PNEST: %d INEST %d CNEST %d\n",
+ ct0->ctx.pnest,ct0->ctx.inest,ct0->ctx.cnest);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+ }/* End IF */
+
+ sprintf(tmp_message," iarg1_flag %d iarg1 %d (0x%x)\n",
+ ct0->ctx.iarg1_flag,ct0->ctx.iarg1,ct0->ctx.iarg1);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," iarg2_flag %d iarg2 %d (0x%x)\n",
+ ct0->ctx.iarg2_flag,ct0->ctx.iarg2,ct0->ctx.iarg2);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," carg 0x%x\n",(unsigned int)ct0->ctx.carg);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ sprintf(tmp_message," tmpval %d (0x%x)\n",
+ ct0->ctx.tmpval,ct0->ctx.tmpval);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+
+ if(ct0->ctx.return_state || ct0->ctx.caller_token){
+ state_name = "";
+ if(ct0->ctx.return_state){
+ state_name =
+ trace_convert_state_to_name(ct0->ctx.return_state);
+ if(state_name == NULL) state_name = "UNKNOWN";
+ }/* End IF */
+ sprintf(tmp_message," return_state %s caller_token 0x%x\n",
+ state_name,(unsigned int)ct0->ctx.caller_token);
+ buff_insert(qbp,qbp->zee,tmp_message,strlen(tmp_message));
+ }/* End IF */
+ break;
+
+ }/* End Switch */
+
+}
+
+char *
+trace_convert_opcode_to_name(opcode)
+int opcode;
+{
+
+ PREAMBLE();
+
+ switch(opcode){
+ case TOK_C_UNUSED:
+ return("TOK_C_UNUSED");
+ case TOK_C_FIRSTTOKEN:
+ return("TOK_C_FIRSTTOKEN");
+ case TOK_C_INPUTCHAR:
+ return("TOK_C_INPUTCHAR");
+ case TOK_C_COPYTOKEN:
+ return("TOK_C_COPYTOKEN");
+ case TOK_C_ITERATION_BEGIN:
+ return("TOK_C_ITERATION_BEGIN");
+ case TOK_C_ITERATION_END:
+ return("TOK_C_ITERATION_END");
+ case TOK_C_CONDITIONAL_END:
+ return("TOK_C_CONDITIONAL_END");
+ case TOK_C_LABEL_BEGIN:
+ return("TOK_C_LABEL_BEGIN");
+ case TOK_C_LABEL_END:
+ return("TOK_C_LABEL_END");
+ case TOK_C_GOTO_BEGIN:
+ return("TOK_C_GOTO_BEGIN");
+ case TOK_C_GOTO_END:
+ return("TOK_C_GOTO_END");
+ case TOK_C_FINALTOKEN:
+ return("TOK_C_FINALTOKEN");
+ case TOK_C_INITIALSTATE:
+ return("TOK_C_INITIALSTATE");
+ case TOK_C_CONDITIONAL_ELSE:
+ return("TOK_C_CONDITIONAL_ELSE");
+ default:
+ return(NULL);
+ }/* End Switch */
+
+}/* End Routine */
+
+char *
+trace_convert_state_to_name(state)
+int state;
+{
+
+ PREAMBLE();
+
+ switch(state){
+ case STATE_C_INITIALSTATE:
+ return("STATE_C_INITIALSTATE");
+ case STATE_C_MAINCOMMANDS:
+ return("STATE_C_MAINCOMMANDS");
+ case STATE_C_ESCAPESEEN:
+ return("STATE_C_ESCAPESEEN");
+ case STATE_C_ECOMMAND:
+ return("STATE_C_ECOMMAND");
+ case STATE_C_ARG1:
+ return("STATE_C_ARG1");
+ case STATE_C_ARG2:
+ return("STATE_C_ARG2");
+ case STATE_C_EXPRESSION:
+ return("STATE_C_EXPRESSION");
+ case STATE_C_OPERATOR:
+ return("STATE_C_OPERATOR");
+ case STATE_C_PLUS:
+ return("STATE_C_PLUS");
+ case STATE_C_MINUS:
+ return("STATE_C_MINUS");
+ case STATE_C_TIMES:
+ return("STATE_C_TIMES");
+ case STATE_C_DIVIDE:
+ return("STATE_C_DIVIDE");
+ case STATE_C_MINUSSEEN:
+ return("STATE_C_MINUSSEEN");
+ case STATE_C_SUBEXPRESSION:
+ return("STATE_C_SUBEXPRESSION");
+ case STATE_C_OPERAND:
+ return("STATE_C_OPERAND");
+ case STATE_C_NUMBER_SUBSTATE:
+ return("STATE_C_NUMBER_SUBSTATE");
+ case STATE_C_QOPERAND:
+ return("STATE_C_QOPERAND");
+ case STATE_C_INSERT:
+ return("STATE_C_INSERT");
+ case STATE_C_QUOTED_INSERT:
+ return("STATE_C_QUOTED_INSERT");
+ case STATE_C_UMINUS:
+ return("STATE_C_UMINUS");
+ case STATE_C_LABEL:
+ return("STATE_C_LABEL");
+ case STATE_C_UQREGISTER:
+ return("STATE_C_UQREGISTER");
+ case STATE_C_WRITEFILE:
+ return("STATE_C_WRITEFILE");
+ case STATE_C_STRING:
+ return("STATE_C_STRING");
+ case STATE_C_SEARCH:
+ return("STATE_C_SEARCH");
+ case STATE_C_FCOMMAND:
+ return("STATE_C_FCOMMAND");
+ case STATE_C_FSPART1:
+ return("STATE_C_FSPART1");
+ case STATE_C_FSPART2:
+ return("STATE_C_FSPART2");
+ case STATE_C_EDITBUF:
+ return("STATE_C_EDITBUF");
+ case STATE_C_READFILE:
+ return("STATE_C_READFILE");
+ case STATE_C_XQREGISTER:
+ return("STATE_C_XQREGISTER");
+ case STATE_C_GQREGISTER:
+ return("STATE_C_GQREGISTER");
+ case STATE_C_MQREGISTER:
+ return("STATE_C_MQREGISTER");
+ case STATE_C_VIEWBUF:
+ return("STATE_C_VIEWBUF");
+ case STATE_C_FDCOMMAND:
+ return("STATE_C_FDCOMMAND");
+ case STATE_C_CONDITIONALS:
+ return("STATE_C_CONDITIONALS");
+ case STATE_C_GOTO:
+ return("STATE_C_GOTO");
+ case STATE_C_FRPART1:
+ return("STATE_C_FRPART1");
+ case STATE_C_FRPART2:
+ return("STATE_C_FRPART2");
+ case STATE_C_MESSAGE:
+ return("STATE_C_MESSAGE");
+ case STATE_C_FKCOMMAND:
+ return("STATE_C_FKCOMMAND");
+ case STATE_C_ECCOMMAND:
+ return("STATE_C_ECCOMMAND");
+ case STATE_C_SAVECOMMAND:
+ return("STATE_C_SAVECOMMAND");
+ case STATE_C_PERCENT_OPERAND:
+ return("STATE_C_PERCENT_OPERAND");
+ case STATE_C_ATINSERT:
+ return("STATE_C_ATINSERT");
+ case STATE_C_ATINSERT_PART2:
+ return("STATE_C_ATINSERT_PART2");
+ case STATE_C_ONE_EQUALS:
+ return("STATE_C_ONE_EQUALS");
+ case STATE_C_TWO_EQUALS:
+ return("STATE_C_TWO_EQUALS");
+ case STATE_C_SKIP_ELSE:
+ return("STATE_C_SKIP_ELSE");
+ case STATE_C_PUSH_QREGISTER:
+ return("STATE_C_PUSH_QREGISTER");
+ case STATE_C_POP_QREGISTER:
+ return("STATE_C_POP_QREGISTER");
+ case STATE_C_NSEARCH:
+ return("STATE_C_NSEARCH");
+ case STATE_C_ACCEPT_ARGS:
+ return("STATE_C_ACCEPT_ARGS");
+ case STATE_C_EQQREGISTER1:
+ return("STATE_C_EQQREGISTER1");
+ case STATE_C_EQQREGISTER2:
+ return("STATE_C_EQQREGISTER2");
+ case STATE_C_RADIX:
+ return("STATE_C_RADIX");
+ case STATE_C_HEX_NUMBER:
+ return("STATE_C_HEX_NUMBER");
+ case STATE_C_OCTAL_NUMBER:
+ return("STATE_C_OCTAL_NUMBER");
+ case STATE_C_HEX_NUMBER_SUBSTATE:
+ return("STATE_C_HEX_NUMBER_SUBSTATE");
+ case STATE_C_OCTAL_NUMBER_SUBSTATE:
+ return("STATE_C_OCTAL_NUMBER_SUBSTATE");
+ case STATE_C_BACKSLASH:
+ return("STATE_C_BACKSLASH");
+ case STATE_C_DELAYED_MINUS:
+ return("STATE_C_DELAYED_MINUS");
+ case STATE_C_DELAYED_PLUS:
+ return("STATE_C_DELAYED_PLUS");
+ case STATE_C_FSPART3:
+ return("STATE_C_FSPART3");
+
+
+ case STATE_C_RETURN:
+ return("STATE_C_RETURN");
+ case STATE_C_FINALSTATE:
+ return("STATE_C_FINALSTATE");
+ case STATE_C_ERRORSTATE:
+ return("STATE_C_ERRORSTATE");
+ default:
+ return(NULL);
+ }/* End Switch */
+
+}/* End Routine */
+
+
+
+char *
+trace_convert_exec_state_to_name(state)
+int state;
+{
+
+ PREAMBLE();
+
+ switch(state){
+ case EXEC_C_NULLSTATE:
+ return("EXEC_C_NULLSTATE");
+ case EXEC_C_DOTARG1:
+ return("EXEC_C_DOTARG1");
+ case EXEC_C_ZEEARG1:
+ return("EXEC_C_ZEEARG1");
+ case EXEC_C_HARGUMENT:
+ return("EXEC_C_HARGUMENT");
+ case EXEC_C_EXITCOMMAND:
+ return("EXEC_C_EXITCOMMAND");
+ case EXEC_C_UQREGISTER:
+ return("EXEC_C_UQREGISTER");
+ case EXEC_C_JUMP:
+ return("EXEC_C_JUMP");
+ case EXEC_C_INSERT:
+ return("EXEC_C_INSERT");
+ case EXEC_C_LINE:
+ return("EXEC_C_LINE");
+ case EXEC_C_CHAR:
+ return("EXEC_C_CHAR");
+ case EXEC_C_RCHAR:
+ return("EXEC_C_RCHAR");
+ case EXEC_C_DELETE:
+ return("EXEC_C_DELETE");
+ case EXEC_C_HVALUE:
+ return("EXEC_C_HVALUE");
+ case EXEC_C_DOTVALUE:
+ return("EXEC_C_DOTVALUE");
+ case EXEC_C_ZEEVALUE:
+ return("EXEC_C_ZEEVALUE");
+ case EXEC_C_QVALUE:
+ return("EXEC_C_QVALUE");
+ case EXEC_C_EQUALS:
+ return("EXEC_C_EQUALS");
+ case EXEC_C_REDRAW_SCREEN:
+ return("EXEC_C_REDRAW_SCREEN");
+ case EXEC_C_STOREVAL:
+ return("EXEC_C_STOREVAL");
+ case EXEC_C_STORE1:
+ return("EXEC_C_STORE1");
+ case EXEC_C_STORE2:
+ return("EXEC_C_STORE2");
+ case EXEC_C_UMINUS:
+ return("EXEC_C_UMINUS");
+ case EXEC_C_PLUS:
+ return("EXEC_C_PLUS");
+ case EXEC_C_MINUS:
+ return("EXEC_C_MINUS");
+ case EXEC_C_TIMES:
+ return("EXEC_C_TIMES");
+ case EXEC_C_DIVIDE:
+ return("EXEC_C_DIVIDE");
+ case EXEC_C_KILL:
+ return("EXEC_C_KILL");
+ case EXEC_C_WRITEFILE:
+ return("EXEC_C_WRITEFILE");
+ case EXEC_C_SEARCH:
+ return("EXEC_C_SEARCH");
+ case EXEC_C_SETSEARCH:
+ return("EXEC_C_SETSEARCH");
+ case EXEC_C_FSREPLACE1:
+ return("EXEC_C_FSREPLACE1");
+ case EXEC_C_ITERATION_BEGIN:
+ return("EXEC_C_ITERATION_BEGIN");
+ case EXEC_C_ITERATION_END:
+ return("EXEC_C_ITERATION_END");
+ case EXEC_C_READFILE:
+ return("EXEC_C_READFILE");
+ case EXEC_C_EDITBUF:
+ return("EXEC_C_EDITBUF");
+ case EXEC_C_XQREGISTER:
+ return("EXEC_C_XQREGISTER");
+ case EXEC_C_GQREGISTER:
+ return("EXEC_C_GQREGISTER");
+ case EXEC_C_SEMICOLON:
+ return("EXEC_C_SEMICOLON");
+ case EXEC_C_MQREGISTER:
+ return("EXEC_C_MQREGISTER");
+ case EXEC_C_CLOSEBUF:
+ return("EXEC_C_CLOSEBUF");
+ case EXEC_C_VIEWBUF:
+ return("EXEC_C_VIEWBUF");
+ case EXEC_C_FDCOMMAND:
+ return("EXEC_C_FDCOMMAND");
+ case EXEC_C_ACOMMAND:
+ return("EXEC_C_ACOMMAND");
+ case EXEC_C_BACKSLASH:
+ return("EXEC_C_BACKSLASH");
+ case EXEC_C_BACKSLASHARG:
+ return("EXEC_C_BACKSLASHARG");
+ case EXEC_C_COND_GT:
+ return("EXEC_C_COND_GT");
+ case EXEC_C_COND_LT:
+ return("EXEC_C_COND_LT");
+ case EXEC_C_COND_EQ:
+ return("EXEC_C_COND_EQ");
+ case EXEC_C_COND_NE:
+ return("EXEC_C_COND_NE");
+ case EXEC_C_COND_DIGIT:
+ return("EXEC_C_COND_DIGIT");
+ case EXEC_C_COND_ALPHA:
+ return("EXEC_C_COND_ALPHA");
+ case EXEC_C_COND_LOWER:
+ return("EXEC_C_COND_LOWER");
+ case EXEC_C_COND_UPPER:
+ return("EXEC_C_COND_UPPER");
+ case EXEC_C_COND_SYMBOL:
+ return("EXEC_C_COND_SYMBOL");
+ case EXEC_C_GOTO:
+ return("EXEC_C_GOTO");
+ case EXEC_C_FRREPLACE:
+ return("EXEC_C_FRREPLACE");
+ case EXEC_C_MESSAGE:
+ return("EXEC_C_MESSAGE");
+ case EXEC_C_RESET_MESSAGE:
+ return("EXEC_C_RESET_MESSAGE");
+ case EXEC_C_OUTPUT_MESSAGE:
+ return("EXEC_C_OUTPUT_MESSAGE");
+ case EXEC_C_FKCOMMAND:
+ return("EXEC_C_FKCOMMAND");
+ case EXEC_C_REMEMBER_DOT:
+ return("EXEC_C_REMEMBER_DOT");
+ case EXEC_C_ECCOMMAND:
+ return("EXEC_C_ECCOMMAND");
+ case EXEC_C_SAVECOMMAND:
+ return("EXEC_C_SAVECOMMAND");
+ case EXEC_C_SCROLL:
+ return("EXEC_C_SCROLL");
+ case EXEC_C_UPDATE_SCREEN:
+ return("EXEC_C_UPDATE_SCREEN");
+ case EXEC_C_SET_IMMEDIATE_MODE:
+ return("EXEC_C_SET_IMMEDIATE_MODE");
+ case EXEC_C_PERCENT_VALUE:
+ return("EXEC_C_PERCENT_VALUE");
+ case EXEC_C_WORD:
+ return("EXEC_C_WORD");
+ case EXEC_C_TWO_EQUALS:
+ return("EXEC_C_TWO_EQUALS");
+ case EXEC_C_THREE_EQUALS:
+ return("EXEC_C_THREE_EQUALS");
+ case EXEC_C_SKIP_ELSE:
+ return("EXEC_C_SKIP_ELSE");
+ case EXEC_C_PUSH_QREGISTER:
+ return("EXEC_C_PUSH_QREGISTER");
+ case EXEC_C_POP_QREGISTER:
+ return("EXEC_C_POP_QREGISTER");
+ case EXEC_C_NSEARCH:
+ return("EXEC_C_NSEARCH");
+ case EXEC_C_EQQREGISTER:
+ return("EXEC_C_EQQREGISTER");
+ case EXEC_C_WINDOW_CONTROL:
+ return("EXEC_C_WINDOW_CONTROL");
+ case EXEC_C_NEXT_WINDOW:
+ return("EXEC_C_NEXT_WINDOW");
+ case EXEC_C_RLINE:
+ return("EXEC_C_RLINE");
+ case EXEC_C_DELWORD:
+ return("EXEC_C_DELWORD");
+ case EXEC_C_RDELWORD:
+ return("EXEC_C_RDELWORD");
+ case EXEC_C_OPENBRACE:
+ return("EXEC_C_OPENBRACE");
+ case EXEC_C_CLOSEBRACE:
+ return("EXEC_C_CLOSEBRACE");
+ case EXEC_C_SKIPLABEL:
+ return("EXEC_C_SKIPLABEL");
+ case EXEC_C_SETOPTIONS:
+ return("EXEC_C_SETOPTIONS");
+ case EXEC_C_FSREPLACE2:
+ return("EXEC_C_FSREPLACE2");
+ case EXEC_C_FSREPLACE3:
+ return("EXEC_C_FSREPLACE3");
+ default:
+ return(NULL);
+ }/* End Switch */
+
+}/* End Routine */
diff --git a/tecparse.h b/tecparse.h
new file mode 100644
index 0000000..3ae0268
--- /dev/null
+++ b/tecparse.h
@@ -0,0 +1,327 @@
+/*
+ * $Date: 2007/12/10 21:59:20 $
+ * $Source: /cvsroot/videoteco/videoteco/tecparse.h,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecparse.h
+ * Definitions for TECO parser
+ * %W% (PC) %G%
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+/*
+ * This structure holds the context which must be passed forward as the
+ * parse progresses. We split it out of the cmd_token structure so that
+ * it can be easily duplicated.
+ */
+struct cmd_context {
+ char state;
+ char flags;
+ char go_flag;
+ char pnest;
+ char inest;
+ char cnest;
+ char iarg1_flag;
+ char iarg2_flag;
+ char delimeter;
+ int iarg1;
+ int iarg2;
+ char *carg;
+ int tmpval;
+ int return_state;
+ struct cmd_token *caller_token;
+};
+
+/*
+ * Define the structure which we use to describe commands
+ */
+struct cmd_token {
+ char opcode;
+ char input_byte;
+ char q_register;
+ char flags;
+ int execute_state;
+ struct cmd_token *next_token;
+ struct cmd_token *prev_token;
+ struct undo_token *undo_list;
+ struct cmd_context ctx;
+};
+
+struct undo_token {
+ char opcode;
+ int iarg1;
+ int iarg2;
+ char *carg1;
+ struct undo_token *next_token;
+};
+
+#define TRACE_C_PARSE 1
+#define TRACE_C_EXEC 2
+#define TRACE_C_MACRO 3
+
+#define TOK_M_EAT_TOKEN (1 << 0)
+#define TOK_M_WORDBOUNDARY (1 << 1)
+#define TOK_M_PARSELOAD_IARG1 (1 << 2)
+#define TOK_M_PARSELOAD_IARG2 (1 << 3)
+#define TOK_M_PARSELOAD_TMPVAL (1 << 4)
+
+#define CTOK_M_COLON_SEEN (1 << 0)
+#define CTOK_M_ATSIGN_SEEN (1 << 1)
+#define CTOK_M_STATUS_PASSED (1 << 2)
+
+#define TOK_C_UNUSED 0 /* Value before it gets set */
+#define TOK_C_FIRSTTOKEN 1 /* The begining of it all */
+#define TOK_C_INPUTCHAR 2 /* An input character was recieved here */
+#define TOK_C_COPYTOKEN 3 /* A copy of a previous command token */
+#define TOK_C_ITERATION_BEGIN 4 /* Marks the begining of an iteration */
+#define TOK_C_ITERATION_END 5 /* Marks the end of an iteration */
+#define TOK_C_CONDITIONAL_END 6 /* Marks end of a conditional cmd */
+#define TOK_C_LABEL_BEGIN 7 /* Begining of a label tag !like this! */
+#define TOK_C_LABEL_END 8 /* End of a label tag */
+#define TOK_C_GOTO_BEGIN 9 /* Begining of an Omumble$ string */
+#define TOK_C_GOTO_END 10 /* End of an Omumble$ string */
+#define TOK_C_FINALTOKEN 11 /* Like FINALSTATE */
+#define TOK_C_INITIALSTATE 12 /* Used by ^W to find begining of cmds */
+#define TOK_C_CONDITIONAL_ELSE 13/* Marks begining of an ELSE clause */
+
+#define STATE_C_INITIALSTATE 0
+#define STATE_C_MAINCOMMANDS 1
+#define STATE_C_ESCAPESEEN 2
+#define STATE_C_ECOMMAND 3
+#define STATE_C_ARG1 4
+#define STATE_C_ARG2 5
+#define STATE_C_EXPRESSION 6
+#define STATE_C_OPERATOR 7
+#define STATE_C_PLUS 8
+#define STATE_C_MINUS 9
+#define STATE_C_TIMES 10
+#define STATE_C_DIVIDE 11
+#define STATE_C_MINUSSEEN 14
+#define STATE_C_SUBEXPRESSION 15
+#define STATE_C_OPERAND 16
+#define STATE_C_NUMBER_SUBSTATE 17
+#define STATE_C_QOPERAND 18
+#define STATE_C_INSERT 19
+#define STATE_C_QUOTED_INSERT 20
+#define STATE_C_UMINUS 21
+#define STATE_C_LABEL 22
+#define STATE_C_UQREGISTER 23
+#define STATE_C_WRITEFILE 24
+#define STATE_C_STRING 25
+#define STATE_C_STRING1 26
+#define STATE_C_SEARCH 27
+#define STATE_C_FCOMMAND 28
+#define STATE_C_FSPART1 29
+#define STATE_C_FSPART2 30
+#define STATE_C_EDITBUF 31
+#define STATE_C_READFILE 32
+#define STATE_C_XQREGISTER 33
+#define STATE_C_GQREGISTER 34
+#define STATE_C_MQREGISTER 35
+#define STATE_C_VIEWBUF 36
+#define STATE_C_FDCOMMAND 37
+#define STATE_C_CONDITIONALS 38
+#define STATE_C_GOTO 39
+#define STATE_C_FRPART1 40
+#define STATE_C_FRPART2 41
+#define STATE_C_MESSAGE 42
+#define STATE_C_FKCOMMAND 43
+#define STATE_C_ECCOMMAND 44
+#define STATE_C_SAVECOMMAND 45
+#define STATE_C_PERCENT_OPERAND 46
+#define STATE_C_ATINSERT 47
+#define STATE_C_ATINSERT_PART2 48
+#define STATE_C_ONE_EQUALS 49
+#define STATE_C_TWO_EQUALS 50
+#define STATE_C_SKIP_ELSE 51
+#define STATE_C_PUSH_QREGISTER 52
+#define STATE_C_POP_QREGISTER 53
+#define STATE_C_NSEARCH 54
+#define STATE_C_ACCEPT_ARGS 55
+#define STATE_C_EQQREGISTER1 56
+#define STATE_C_EQQREGISTER2 57
+#define STATE_C_RADIX 58
+#define STATE_C_HEX_NUMBER 59
+#define STATE_C_OCTAL_NUMBER 60
+#define STATE_C_HEX_NUMBER_SUBSTATE 61
+#define STATE_C_OCTAL_NUMBER_SUBSTATE 62
+#define STATE_C_BACKSLASH 63
+#define STATE_C_DELAYED_MINUS 64
+#define STATE_C_DELAYED_PLUS 65
+#define STATE_C_FSPART3 66
+#define STATE_C_FTAGS 67
+
+
+#define STATE_C_RETURN 97
+#define STATE_C_FINALSTATE 98
+#define STATE_C_ERRORSTATE 99
+
+/*
+ * Here we define the execution time states
+ */
+#define EXEC_C_NULLSTATE 0
+#define EXEC_C_DOTARG1 1
+#define EXEC_C_ZEEARG1 2
+#define EXEC_C_HARGUMENT 3
+#define EXEC_C_EXITCOMMAND 4
+#define EXEC_C_UQREGISTER 5
+#define EXEC_C_JUMP 8
+#define EXEC_C_INSERT 9
+#define EXEC_C_LINE 10
+#define EXEC_C_CHAR 11
+#define EXEC_C_RCHAR 12
+#define EXEC_C_DELETE 13
+#define EXEC_C_HVALUE 14
+#define EXEC_C_DOTVALUE 15
+#define EXEC_C_ZEEVALUE 16
+#define EXEC_C_QVALUE 17
+#define EXEC_C_EQUALS 18
+#define EXEC_C_REDRAW_SCREEN 19
+#define EXEC_C_STOREVAL 20
+#define EXEC_C_STORE1 21
+#define EXEC_C_STORE2 22
+#define EXEC_C_UMINUS 23
+#define EXEC_C_PLUS 24
+#define EXEC_C_MINUS 25
+#define EXEC_C_TIMES 26
+#define EXEC_C_DIVIDE 27
+#define EXEC_C_KILL 28
+#define EXEC_C_WRITEFILE 29
+#define EXEC_C_SEARCH 30
+#define EXEC_C_SETSEARCH 31
+#define EXEC_C_FSREPLACE1 32
+#define EXEC_C_ITERATION_BEGIN 33
+#define EXEC_C_ITERATION_END 34
+#define EXEC_C_READFILE 35
+#define EXEC_C_EDITBUF 36
+#define EXEC_C_XQREGISTER 37
+#define EXEC_C_GQREGISTER 38
+#define EXEC_C_SEMICOLON 39
+#define EXEC_C_MQREGISTER 40
+#define EXEC_C_CLOSEBUF 41
+#define EXEC_C_VIEWBUF 42
+#define EXEC_C_FDCOMMAND 43
+#define EXEC_C_ACOMMAND 44
+#define EXEC_C_BACKSLASH 45
+#define EXEC_C_BACKSLASHARG 46
+#define EXEC_C_COND_GT 47
+#define EXEC_C_COND_LT 48
+#define EXEC_C_COND_EQ 49
+#define EXEC_C_COND_NE 50
+#define EXEC_C_COND_DIGIT 51
+#define EXEC_C_COND_ALPHA 52
+#define EXEC_C_COND_LOWER 53
+#define EXEC_C_COND_UPPER 54
+#define EXEC_C_COND_SYMBOL 55
+#define EXEC_C_GOTO 56
+#define EXEC_C_FRREPLACE 57
+#define EXEC_C_MESSAGE 58
+#define EXEC_C_RESET_MESSAGE 59
+#define EXEC_C_OUTPUT_MESSAGE 60
+#define EXEC_C_FKCOMMAND 61
+#define EXEC_C_REMEMBER_DOT 62
+#define EXEC_C_ECCOMMAND 63
+#define EXEC_C_SAVECOMMAND 64
+#define EXEC_C_SCROLL 65
+#define EXEC_C_UPDATE_SCREEN 66
+#define EXEC_C_SET_IMMEDIATE_MODE 67
+#define EXEC_C_PERCENT_VALUE 68
+#define EXEC_C_WORD 69
+#define EXEC_C_TWO_EQUALS 70
+#define EXEC_C_THREE_EQUALS 71
+#define EXEC_C_SKIP_ELSE 72
+#define EXEC_C_PUSH_QREGISTER 73
+#define EXEC_C_POP_QREGISTER 74
+#define EXEC_C_NSEARCH 75
+#define EXEC_C_EQQREGISTER 76
+#define EXEC_C_WINDOW_CONTROL 77
+#define EXEC_C_NEXT_WINDOW 78
+#define EXEC_C_RLINE 79
+#define EXEC_C_DELWORD 80
+#define EXEC_C_RDELWORD 81
+#define EXEC_C_OPENBRACE 82
+#define EXEC_C_CLOSEBRACE 83
+#define EXEC_C_SKIPLABEL 84
+#define EXEC_C_SETOPTIONS 85
+#define EXEC_C_FSREPLACE2 86
+#define EXEC_C_FSREPLACE3 87
+#define EXEC_C_FTAGS 88
+
+/*
+ * Define the UNDO symbols
+ */
+#define UNDO_C_UNUSED 0
+#define UNDO_C_CHANGEDOT 1
+#define UNDO_C_DELETE 2
+#define UNDO_C_INSERT 3
+#define UNDO_C_UQREGISTER 4
+#define UNDO_C_MEMFREE 5
+#define UNDO_C_SHORTEN_STRING 6
+#define UNDO_C_SET_SEARCH_STRING 7
+#define UNDO_C_CHANGEBUFF 8
+#define UNDO_C_MACRO 9
+#define UNDO_C_SHORTEN_MESSAGE 10
+#define UNDO_C_BULK_INSERT 11
+#define UNDO_C_SET_IMMEDIATE_MODE 12
+#define UNDO_C_PUSH 13
+#define UNDO_C_POP 14
+#define UNDO_C_MODIFIED 15
+#define UNDO_C_CLOSEBUFF 16
+#define UNDO_C_WINDOW_SWITCH 17
+#define UNDO_C_WINDOW_SPLIT 18
+#define UNDO_C_REOPENBUFF 19
+#define UNDO_C_RENAME_BUFFER 20
+#define UNDO_C_PRESERVEARGS 21
+#define UNDO_C_SETOPTIONS 22
+#define UNDO_C_SET_SEARCH_GLOBALS 23
+#define UNDO_C_LOAD_TAGS 24
+#define UNDO_C_SELECT_TAGS 25
+#define UNDO_C_SET_EXIT_FLAG 26
+
+int cmd_oscmd(struct cmd_token *ct);
+int buff_insert_from_buffer_with_undo( struct cmd_token *,
+ struct buff_header *,int,struct buff_header *,int,int);
+int buff_delete_with_undo( struct cmd_token *,struct buff_header *,int,int);
+int buff_insert_with_undo( struct cmd_token *,
+ struct buff_header *,int,char *,int);
+int rename_edit_buffer(struct buff_header *,char *,struct cmd_token *);
+int cmd_setoptions(int,int,struct undo_token *);
+void tag_dump_database(struct tags *tp,struct cmd_token *uct);
+int buff_insert_char_with_undo(struct cmd_token *,
+ struct buff_header *,int,char);
+int set_search_string_with_undo(char *,struct cmd_token *uct);
+int cmd_tags(struct cmd_token *uct,int,int,int,char *);
+int tecmacro(struct buff_header *,struct cmd_token *,struct cmd_token **);
+void parser_cleanup_ctlist(struct cmd_token *);
+void free_cmd_token(struct cmd_token *);
+int parser_undo(struct undo_token *);
+void free_undo_token(struct undo_token *);
+void screen_reset_echo(struct cmd_token *);
+int parse_special_character(struct cmd_token *,int);
+void parse_input_character(struct cmd_token *,struct cmd_token *);
+void trace_mode(int, struct cmd_token *, struct cmd_token *);
+int execute_a_state(struct cmd_token *, struct cmd_token *);
+void tecundo_cleanup(struct undo_token *);
+int parse_any_arguments(struct cmd_token *,char *);
+int parse_more_than_one_arg(struct cmd_token *,char *);
+int parse_check_qname(struct cmd_token *,char);
+struct cmd_token *allocate_cmd_token(struct cmd_token *old_token);
+struct undo_token *allocate_undo_token(struct cmd_token *ct);
+void buff_free_line_buffer(struct buff_line *);
+
diff --git a/tecstate.c b/tecstate.c
new file mode 100644
index 0000000..d908e74
--- /dev/null
+++ b/tecstate.c
@@ -0,0 +1,2374 @@
+char *tecstate_c_version = "tecstate.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:21 $
+ * $Source: /cvsroot/videoteco/videoteco/tecstate.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecstate.c
+ * Main SWITCH/CASE statements to implement the parser syntax stage
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+ extern char immediate_execute_flag;
+ extern char trace_mode_flag;
+ extern char suspend_is_okay_flag;
+
+
+
+/* PARSE_INPUT_CHARACTER - Continue the parse with the supplied character
+ *
+ * Function:
+ *
+ * We re-enter the parser with a new character at this point. We jump back
+ * to the state we left before.
+ */
+void
+parse_input_character(ct,uct)
+register struct cmd_token *ct;
+struct cmd_token *uct;
+{
+char tmp_message[LINE_BUFFER_SIZE];
+register struct cmd_token *oct = NULL;
+
+ PREAMBLE();
+
+ switch(ct->ctx.state){
+/*
+ * Here on initial command state. This is the begining of a command, so we are
+ * looking for arguments that might go with a command. If there are no args,
+ * we just transfer into the main command loop.
+ */
+ case STATE_C_INITIALSTATE:
+ ct->ctx.flags &= ~(CTOK_M_COLON_SEEN | CTOK_M_ATSIGN_SEEN);
+ ct->flags |= TOK_M_WORDBOUNDARY;
+ case STATE_C_ACCEPT_ARGS:
+ ct->ctx.carg = NULL;
+ switch(ct->input_byte){
+/*
+ * If it looks like an argument, then transfer into a subexpression parser to
+ * get the value of the expression. ARG1 will stuff the result into iarg1 and
+ * also check for a comma (',') incase he is specifing a twin argument command
+ */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'Q': case 'q': case '%':
+#ifdef CLASSIC_B_BEHAVIOR
+ case 'B': case 'b':
+#endif
+ case 'Z': case 'z':
+ case '\\':
+ case '.':
+ case '-':
+ case '(':
+ case '^':
+ ct->ctx.flags &= ~CTOK_M_STATUS_PASSED;
+ ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = NO;
+ ct->ctx.state = STATE_C_EXPRESSION;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_ARG1;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+/*
+ * H is a special case, since the single argument actually implies two args.
+ * (H actually is the same as 0,z<cmd>)
+ */
+ case 'H': case 'h':
+ ct->ctx.flags &= ~CTOK_M_STATUS_PASSED;
+ ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = YES;
+ ct->ctx.state = STATE_C_MAINCOMMANDS;
+ ct->execute_state = EXEC_C_HVALUE;
+ return;
+/*
+ * Here on the @ command. This says to use user specified delimeters for
+ * strings, rather than just terminating with escape.
+ */
+ case '@':
+ ct->ctx.flags |= CTOK_M_ATSIGN_SEEN;
+ ct->ctx.state = STATE_C_ACCEPT_ARGS;
+ return;
+/*
+ * Here on the : command. This is just a flag to many commands to tell them
+ * to work in a slightly different way. The most common usage is to mean that
+ * the command should return a value which says whether it worked or not.
+ */
+ case ':':
+ ct->ctx.flags |= CTOK_M_COLON_SEEN;
+ ct->ctx.state = STATE_C_ACCEPT_ARGS;
+ return;
+/*
+ * Well, it doesn't look like he is going to be specifying any arguments, so we
+ * just go and decode the command.
+ */
+ default:
+ if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
+ ct->ctx.state = STATE_C_MAINCOMMANDS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.flags &= ~CTOK_M_STATUS_PASSED;
+ return;
+ }/* End IF */
+ ct->ctx.state = STATE_C_MAINCOMMANDS;
+ ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = NO;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+
+ }/* End Switch */
+/*
+ * Here to parse the major commands (i.e., ones that have no lead-in)
+ */
+ case STATE_C_MAINCOMMANDS:
+ switch(ct->input_byte){
+/*
+ * Space is a nop so that it can be used to make macros more readable
+ */
+ case ' ':
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+/*
+ * Carriage Return is a nop so that it can be used in a macro to avoid
+ * extremely long lines wrapping.
+ */
+ case '\n':
+ ct->ctx.state = STATE_C_MAINCOMMANDS;
+ return;
+/*
+ * Here on an escape. First of all, we have to remember that we have seen an
+ * escape since two in a row will terminate the command. Also, escape has the
+ * effect of blocking arguments from commands i.e. 2L will move two lines, but
+ * 2$L will only move one since the escape eats the argument.
+ */
+ case ESCAPE:
+ ct->ctx.iarg1_flag = ct->ctx.iarg2_flag = NO;
+ ct->ctx.state = STATE_C_ESCAPESEEN;
+ return;
+/*
+ * Here on an Asterisk command. This causes the last double-escaped command
+ * sequence to be saved in the named q-register.
+ */
+ case '*':
+ if(parse_any_arguments(ct,"*")) return;
+ ct->ctx.state = STATE_C_SAVECOMMAND;
+ return;
+/*
+ * Here on the @ command. This says to use user specified delimeters for
+ * strings, rather than just terminating with escape.
+ */
+ case '@':
+ ct->ctx.flags |= CTOK_M_ATSIGN_SEEN;
+ return;
+/*
+ * Here on the : command. This is just a flag to many commands to tell them
+ * to work in a slightly different way. The most common usage is to mean that
+ * the command should return a value which says whether it worked or not.
+ */
+ case ':':
+ ct->ctx.flags |= CTOK_M_COLON_SEEN;
+ return;
+/*
+ * Here on the [ command. This causes the specified Q register to be pushed
+ * onto the Q register pushdown stack.
+ */
+ case '[':
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ ct->ctx.state = STATE_C_PUSH_QREGISTER;
+ return;
+
+/*
+ * Here on the ] command. This causes a Q register to be popped off of the
+ * Q register pushdown stack, and replaces the specified Q register.
+ */
+ case ']':
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ ct->ctx.state = STATE_C_POP_QREGISTER;
+ return;
+/*
+ * The B command moves backwards by lines. It is strictly a Video TECO
+ * enhancement. In normal TECO, the B command returns the address of the
+ * begining of the buffer, i.e. 0.
+ */
+ case 'B': case 'b':
+ if(parse_more_than_one_arg(ct,"B")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_RLINE;
+ return;
+/*
+ * Here on the C command. The C command is a relative move command, i.e., the
+ * argument says how many spaces from the current position to move.
+ */
+ case 'C': case 'c':
+ if(parse_more_than_one_arg(ct,"C")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_CHAR;
+ return;
+/*
+ * The D command deletes the specified number of characters in the indicated
+ * direction.
+ */
+ case 'D': case 'd':
+ if(parse_more_than_one_arg(ct,"D")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_DELETE;
+ return;
+/*
+ * Here on an E command. This is simply the lead-in to any one of the many
+ * E commands.
+ */
+ case 'E': case 'e':
+ ct->ctx.state = STATE_C_ECOMMAND;
+ return;
+/*
+ * Here on an F command. This is a lead-in to one of the F? commands.
+ */
+ case 'F': case 'f':
+ ct->ctx.state = STATE_C_FCOMMAND;
+ return;
+/*
+ * The G command copies the contents of the specified q-register into the
+ * edit buffer at the current position.
+ */
+ case 'G': case 'g':
+ if(parse_any_arguments(ct,"G")) return;
+ ct->ctx.state = STATE_C_GQREGISTER;
+ return;
+/*
+ * The I command inserts characters until an escape is input. If this is
+ * entered with the tab command, not only do we enter insert mode, we also
+ * insert the tab itself.
+ */
+ case '\t':
+ if(ct->ctx.iarg1_flag == NO) ct->flags &= ~TOK_M_EAT_TOKEN;
+
+
+ case 'I': case 'i':
+ if(parse_more_than_one_arg(ct,"I")) return;
+
+ if(ct->ctx.iarg1_flag == YES){
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_INSERT;
+ return;
+ }/* End IF */
+
+ if(ct->ctx.flags & CTOK_M_ATSIGN_SEEN){
+ ct->ctx.state = STATE_C_ATINSERT;
+ return;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_INSERT;
+ return;
+/*
+ * Here on the J command. The J command jumps to an absolute position in the
+ * buffer.
+ */
+ case 'J': case 'j':
+ if(parse_more_than_one_arg(ct,"J")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_JUMP;
+ return;
+/*
+ * The K command acts just like the L command except that it deletes instead
+ * of moving over. Also, it is legal to specify an a,b range to K
+ */
+ case 'K': case 'k':
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_KILL;
+ return;
+/*
+ * The L command moves over the specified number of new-line characters. An
+ * argument of zero means get to the begining of the current line.
+ */
+ case 'L': case 'l':
+ if(parse_more_than_one_arg(ct,"L")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_LINE;
+ return;
+/*
+ * The M command executes the contents of the specified Q register as a macro.
+ */
+ case 'M': case 'm':
+ ct->ctx.state = STATE_C_MQREGISTER;
+ return;
+/*
+ * The N command will search for the specified string across multiple edit
+ * buffers.
+ */
+ case 'N': case 'n':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_NSEARCH;
+ return;
+/*
+ * The O command goes to the specified label
+ */
+ case 'O': case 'o':
+ if(parse_any_arguments(ct,"O")) return;
+ ct->ctx.state = STATE_C_GOTO;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_GOTO_BEGIN;
+ return;
+/*
+ * The P command selects the next window
+ */
+ case 'P': case 'p':
+ if(parse_more_than_one_arg(ct,"P")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_NEXT_WINDOW;
+ return;
+/*
+ * The R command is exactly like the C command, except that the direction
+ * is reversed.
+ */
+ case 'R': case 'r':
+ if(parse_more_than_one_arg(ct,"R")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_RCHAR;
+ return;
+/*
+ * The S command will search for the specified string
+ */
+ case 'S': case 's':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_SEARCH;
+ return;
+/*
+ * The ^L command is temporarily used to redraw the screen
+ */
+ case '\f':
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_REDRAW_SCREEN;
+ return;
+/*
+ * The U command loads the argument into the specified Q register
+ */
+ case 'U': case 'u':
+ if(parse_more_than_one_arg(ct,"U")) return;
+ ct->ctx.state = STATE_C_UQREGISTER;
+ return;
+/*
+ * The V command deletes words. This is a Vido TECO enhancement. In normal
+ * TECO, the V command was equivalent to 0TT
+ */
+ case 'V': case 'v':
+ if(parse_more_than_one_arg(ct,"V")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_DELWORD;
+ return;
+/*
+ * The W command moves by words
+ */
+ case 'W': case 'w':
+ if(parse_more_than_one_arg(ct,"W")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_WORD;
+ return;
+/*
+ * The X command moves a block of characters from the edit buffer into
+ * the specified q register.
+ */
+ case 'X': case 'x':
+ ct->ctx.state = STATE_C_XQREGISTER;
+ return;
+/*
+ * The Y command deletes words in reverse direction. This is a Video TECO
+ * enhancement. In classic TECO, the Y command 'yanked' input data into the
+ * edit buffer.
+ */
+ case 'Y': case 'y':
+ if(parse_more_than_one_arg(ct,"Y")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_RDELWORD;
+ return;
+/*
+ * The ^A command allows you to print a message to the message line
+ */
+ case CNTRL_A:
+ if(parse_any_arguments(ct,"^A")) return;
+ ct->ctx.state = STATE_C_MESSAGE;
+ ct->execute_state = EXEC_C_RESET_MESSAGE;
+ return;
+/*
+ * The < command opens an iteration
+ */
+ case '<':
+ if(parse_more_than_one_arg(ct,"<")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_ITERATION_BEGIN;
+ ct->ctx.inest += 1;
+ ct->execute_state = EXEC_C_ITERATION_BEGIN;
+ return;
+/*
+ * The > command closes an iteration
+ */
+ case '>':
+ if(parse_more_than_one_arg(ct,">")) return;
+ if(ct->ctx.inest == 0){
+ error_message("?No Iteration Present");
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_ITERATION_END;
+ while((oct = oct->prev_token)){
+ if(oct->opcode != TOK_C_ITERATION_BEGIN) continue;
+ if(oct->ctx.inest != ct->ctx.inest) continue;
+ ct->ctx.caller_token = oct;
+ break;
+ }/* End While */
+/*
+ * Preserve the arguments so that if the loop gets undone, it will get redone
+ * the correct number of iterations when the > is typed again.
+ */
+ {
+ register struct undo_token *ut;
+ ut = allocate_undo_token(ct);
+ if(ut == NULL){
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ut->opcode = UNDO_C_PRESERVEARGS;
+ ut->iarg1 = oct->ctx.iarg1;
+ ut->iarg2 = oct->ctx.iarg2;
+ ut->carg1 = (char *)oct;
+
+ ct->ctx.inest -= 1;
+ ct->execute_state = EXEC_C_ITERATION_END;
+ ct = allocate_cmd_token(ct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_COPYTOKEN;
+ return;
+ }
+/*
+ * The ; command terminates an iteration if the argument is >= 0.
+ * If there is no argument supplied, it uses the state of the last
+ * search operation performed as a value.
+ */
+ case ';':
+ if(parse_more_than_one_arg(ct,";")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_SEMICOLON;
+ return;
+/*
+ * The = command prints out the value of the supplied expression.
+ * A single = prints out in decimal, == prints in octal and === in hex.
+ */
+ case '=':
+ if(parse_more_than_one_arg(ct,"=")) return;
+ ct->ctx.state = STATE_C_ONE_EQUALS;
+ ct->execute_state = EXEC_C_EQUALS;
+ return;
+/*
+ * The " command is the conditional 'IF' operator. The following commands only
+ * get executed if the condition is satisfied.
+ */
+ case '"':
+ if(parse_more_than_one_arg(ct,"\"")) return;
+ ct->ctx.state = STATE_C_CONDITIONALS;
+ ct->ctx.cnest += 1;
+ return;
+/*
+ * The | command provides an else clause to a conditional expression
+ */
+ case '|':
+ if(parse_any_arguments(ct,"|")) return;
+ if(ct->ctx.cnest <= 0){
+ error_message("?Not in a conditional");
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_SKIP_ELSE;
+ ct->execute_state = EXEC_C_SKIP_ELSE;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+/*
+ * The ' command ends a conditional expression.
+ */
+ case '\'':
+ if(parse_any_arguments(ct,"'")) return;
+ if(ct->ctx.cnest <= 0){
+ error_message("?Not in a conditional");
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_CONDITIONAL_END;
+ ct->ctx.cnest -= 1;
+ return;
+/*
+ * The Label command allows you to set a tag which can be jumped to with
+ * the 'O' command. It also provides a way to comment macros.
+ */
+ case '!':
+ if(parse_any_arguments(ct,"!")) return;
+ ct->ctx.state = STATE_C_LABEL;
+ ct->execute_state = EXEC_C_SKIPLABEL;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_LABEL_BEGIN;
+ return;
+/*
+ * The backslash command with an argument inserts the decimal
+ * representation of the argument into the buffer at the current position.
+ */
+ case '\\':
+ if(ct->ctx.iarg2_flag == NO){
+ ct->ctx.iarg2 = 10;
+ ct->flags |= TOK_M_PARSELOAD_IARG2;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_BACKSLASH;
+ return;
+/*
+ * The open-brace command copies the current command string into a special
+ * Q-register and places the user there so he can edit it.
+ */
+ case '{':
+ if(parse_any_arguments(ct,"{")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_OPENBRACE;
+ return;
+
+/*
+ * The close-brace command replaces the command string with the string in
+ * the special Q-register.
+ */
+ case '}':
+ if(parse_any_arguments(ct,"}")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_CLOSEBRACE;
+ return;
+
+/*
+ * Here on a system which doesn't really do suspend correctly
+ */
+ case CNTRL_Z:
+ if(suspend_is_okay_flag == YES){
+ cmd_suspend();
+ }/* End IF */
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+
+/*
+ * Here to toggle trace mode. This inserts information into q-register ?
+ * to help us track execution.
+ */
+ case '?':
+ trace_mode_flag = !trace_mode_flag;
+ screen_message(trace_mode_flag ? "Trace Mode ON" :
+ "Trace Mode OFF");
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+
+/*
+ * We only get here on an error, since we should never dispatch a command that
+ * does not have a corresponding case statement.
+ */
+ default:
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,
+ "?MAINCOMMANDS: unknown command <%c>",
+ UPCASE((int)ct->input_byte));
+ error_message(tmp_message);
+ return;
+ }/* End Switch */
+/*
+ * Here when we have seen the begining of a conditional '"'. Now we have to
+ * look at the next character to determine what the actual condition test is.
+ */
+ case STATE_C_CONDITIONALS:
+ switch(ct->input_byte){
+ case 'G': case 'g':
+ case '>':
+ ct->execute_state = EXEC_C_COND_GT;
+ break;
+ case 'L': case 'l':
+ case 'T': case 't':
+ case 'S': case 's':
+ case '<':
+ ct->execute_state = EXEC_C_COND_LT;
+ break;
+ case 'E': case 'e':
+ case 'F': case 'f':
+ case 'U': case 'u':
+ case '=':
+ ct->execute_state = EXEC_C_COND_EQ;
+ break;
+ case 'N': case 'n':
+ case '!':
+ ct->execute_state = EXEC_C_COND_NE;
+ break;
+ case 'C': case 'c':
+ ct->execute_state = EXEC_C_COND_SYMBOL;
+ break;
+ case 'D': case 'd':
+ ct->execute_state = EXEC_C_COND_DIGIT;
+ break;
+ case 'A': case 'a':
+ ct->execute_state = EXEC_C_COND_ALPHA;
+ break;
+ case 'V': case 'v':
+ ct->execute_state = EXEC_C_COND_LOWER;
+ break;
+ case 'W': case 'w':
+ ct->execute_state = EXEC_C_COND_UPPER;
+ break;
+ default:
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,
+ "?Unknown conditional command '%c'",
+ UPCASE((int)ct->input_byte));
+ error_message(tmp_message);
+ return;
+ }/* End Switch */
+
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+/*
+ * Here to watch for the end of a label
+ */
+ case STATE_C_LABEL:
+ switch(ct->input_byte){
+ case '!':
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_LABEL_END;
+ return;
+ default:
+ ct->ctx.state = STATE_C_LABEL;
+ return;
+ }/* End Switch */
+/*
+ * Here to eat the characters in a GOTO command
+ */
+ case STATE_C_GOTO:
+ switch(ct->input_byte){
+ case ESCAPE:
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_GOTO_END;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->execute_state = EXEC_C_GOTO;
+ return;
+ default:
+ ct->ctx.state = STATE_C_GOTO;
+ return;
+ }/* End Switch */
+/*
+ * Here if we have seen an escape. If we see another, then he wants us to
+ * complete the parse and execute any remaining commands.
+ */
+ case STATE_C_ESCAPESEEN:
+ switch(ct->input_byte){
+ case ESCAPE:
+ if(ct->ctx.inest){
+ error_message("?Unterminated Iteration");
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ if(ct->ctx.cnest){
+ error_message("?Unterminated Conditional");
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_FINALSTATE;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->opcode = TOK_C_FINALTOKEN;
+ return;
+
+ default:
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+ }/* End Switch */
+/*
+ * Here if we have seen an E as a lead-in to one of the E commands.
+ */
+ case STATE_C_ECOMMAND:
+ switch(ct->input_byte){
+/*
+ * The EB command creates a new edit buffer and reads the specified file in.
+ * If the command is given an argument, then this is a shorthand switch to
+ * the buffer that has that number.
+ */
+ case 'B': case 'b':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ if(ct->ctx.iarg1_flag == YES){
+ ct->execute_state = EXEC_C_EDITBUF;
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_EDITBUF;
+ return;
+/*
+ * The EC command allows you to execute a command, and the output is placed
+ * into the edit buffer.
+ */
+ case 'C': case 'c':
+ if(ct->ctx.iarg1_flag == YES){
+ ct->execute_state = EXEC_C_ECCOMMAND;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_ECCOMMAND;
+ return;
+/*
+ * The EF command closes the current edit buffer. If the current buffer
+ * is modified, the command will fail unless the user specifies an argument
+ * to the command.
+ */
+ case 'F': case 'f':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->execute_state = EXEC_C_CLOSEBUF;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The EI command allows you to alter immediate execution mode. With no
+ * argument, execute mode is turned off for the remainder of the current
+ * command. An argument of 0 turns it off until further notice and an
+ * argument of 1 turns it back on.
+ */
+ case 'I': case 'i':
+ if(parse_more_than_one_arg(ct,"EI")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+
+ if(ct->ctx.iarg1_flag == NO){
+ ct->ctx.go_flag = NO;
+ return;
+ }/* End IF */
+
+ ct->execute_state = EXEC_C_SET_IMMEDIATE_MODE;
+ return;
+/*
+ * The EJ command allows you to load runtime options into TECO.
+ */
+ case 'J': case 'j':
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_SETOPTIONS;
+ return;
+
+/*
+ * The EP command splits the current window in half. If an argument is
+ * supplied, the command deletes the current window.
+ */
+ case 'P': case 'p':
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_WINDOW_CONTROL;
+ return;
+/*
+ * The EV command works exactly like the EB command except that the buffer
+ * which is created is 'readonly'. Thus this command stands for 'VIEW' file.
+ */
+ case 'V': case 'v':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ if(ct->ctx.iarg1_flag == YES){
+ ct->execute_state = EXEC_C_VIEWBUF;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_VIEWBUF;
+ return;
+/*
+ * The EQ command reads a file into the specified Q-register.
+ */
+ case 'Q': case 'q':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ if(parse_any_arguments(ct,"EQ")) return;
+ ct->ctx.state = STATE_C_EQQREGISTER1;
+ return;
+/*
+ * The ER command reads the specified file into the current buffer location.
+ */
+ case 'R': case 'r':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_READFILE;
+ return;
+/*
+ * The ES command causes the screen to scroll
+ */
+ case 'S': case 's':
+ if(parse_more_than_one_arg(ct,"ES")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_SCROLL;
+ return;
+/*
+ * The ET command causes the screen to update
+ */
+ case 'T': case 't':
+ if(parse_any_arguments(ct,"ET")) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_UPDATE_SCREEN;
+ return;
+/*
+ * The EW command writes out the current buffer
+ */
+ case 'W': case 'w':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_WRITEFILE;
+ return;
+/*
+ * The EX command causes the editor to exit
+ */
+ case 'X': case 'x':
+ ct->execute_state = EXEC_C_EXITCOMMAND;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->ctx.go_flag = NO;
+ return;
+
+ default:
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,
+ "?Unknown command E%c",UPCASE((int)ct->input_byte));
+ error_message(tmp_message);
+ return;
+ }/* End Switch */
+/*
+ * Here if we have seen an F as a lead-in to one of the F commands.
+ */
+ case STATE_C_FCOMMAND:
+ switch(ct->input_byte){
+/*
+ * The FD command finds the string and deletes it.
+ */
+ case 'D': case 'd':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_FDCOMMAND;
+ return;
+/*
+ * The FK command clears the text between the current position and the position
+ * of the searched for text. The searched for text is left unmodified.
+ */
+ case 'K': case 'k':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_FKCOMMAND;
+ return;
+/*
+ * The FS command finds the first string and replaces it with the second
+ */
+ case 'S': case 's':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_FSPART1;
+ return;
+/*
+ * The FT command is a Video TECO extension to read & use a unix style
+ * tags file.
+ */
+ case 'T': case 't':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->ctx.state = STATE_C_STRING;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_FTAGS;
+ return;
+
+/*
+ * The FR command acts like the FS command except that if the second argument
+ * is NULL, the string is replaced with the last replace value.
+ */
+ case 'R': case 'r':
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ ct->ctx.flags |= CTOK_M_STATUS_PASSED;
+ }/* End IF */
+ ct->execute_state = EXEC_C_SEARCH;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->execute_state = EXEC_C_FRREPLACE;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->ctx.flags & CTOK_M_STATUS_PASSED){ /* mch */
+ ct = allocate_cmd_token(ct);
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+
+ return;
+/*
+ * Here when we have an 'F' command with an unknown second character. This
+ * makes it an illegal command, and we don't waste any time letting the user
+ * know about it.
+ */
+ default:
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,
+ "?Unknown command F%c",UPCASE((int)ct->input_byte));
+ error_message(tmp_message);
+ return;
+ }/* End Switch */
+/*
+ * The following state is the return-state from STRING when the first part
+ * of an FS command has been parsed.
+ */
+ case STATE_C_FSPART1:
+ ct->ctx.state = STATE_C_FSPART2;
+ ct->execute_state = EXEC_C_SEARCH;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->execute_state = EXEC_C_FSREPLACE1;
+ return;
+
+ case STATE_C_FSPART2:
+ ct->flags |= TOK_M_WORDBOUNDARY;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_FSPART3;
+ return;
+
+ case STATE_C_FSPART3:
+ if(ct->ctx.flags & CTOK_M_ATSIGN_SEEN){
+ if(ct->input_byte == ct->ctx.delimeter){
+ ct->ctx.state = STATE_C_ESCAPESEEN;
+ ct->execute_state = EXEC_C_FSREPLACE3;
+ return;
+ }
+
+ else {
+ ct->ctx.state = STATE_C_FSPART3;
+ ct->execute_state = EXEC_C_FSREPLACE2;
+ return;
+
+ }
+ }
+
+ if(ct->input_byte == ESCAPE){
+ ct->ctx.state = STATE_C_ESCAPESEEN;
+ ct->execute_state = EXEC_C_FSREPLACE3;
+ if(ct->ctx.flags & CTOK_M_STATUS_PASSED){ /* mch */
+ ct = allocate_cmd_token(ct);
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+ }
+
+ else {
+ ct->ctx.state = STATE_C_FSPART3;
+ ct->execute_state = EXEC_C_FSREPLACE2;
+ return;
+
+ }
+
+#ifdef WIMPY_CODER_WHO_HAS_NOT_HANDLED_THIS_YET
+ case CNTRL_V:
+ ct->ctx.state = STATE_C_QUOTED_INSERT;
+ return;
+#endif /* WIMPY_CODER_WHO_HAS_NOT_HANDLED_THIS_YET */
+
+/*
+ * This state is the return state from STRING and the TAGS command is
+ * ready to execute.
+ */
+ case STATE_C_FTAGS:
+ ct->execute_state = EXEC_C_FTAGS;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+
+/*
+ * The following state is the return-state from STRING when a search string
+ * has been completely specified.
+ */
+ case STATE_C_SEARCH:
+ ct->execute_state = EXEC_C_SEARCH;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The following state is the return-state from STRING when a search string
+ * has been completely specified for the 'N' search command.
+ */
+ case STATE_C_NSEARCH:
+ ct->execute_state = EXEC_C_NSEARCH;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ if(ct->ctx.flags & CTOK_M_STATUS_PASSED){
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The following state is the return-state from STRING when the search part
+ * of an FD command has been parsed.
+ */
+ case STATE_C_FDCOMMAND:
+ ct->execute_state = EXEC_C_SEARCH;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.carg = NULL;
+ ct->execute_state = EXEC_C_FDCOMMAND;
+ if(ct->ctx.flags & CTOK_M_STATUS_PASSED){ /* mch */
+ ct = allocate_cmd_token(ct);
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The following state is the return-state from STRING when the search part
+ * of an FK command has been parsed.
+ */
+ case STATE_C_FKCOMMAND:
+ ct->execute_state = EXEC_C_REMEMBER_DOT;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->execute_state = EXEC_C_SEARCH;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.carg = NULL;
+ ct->execute_state = EXEC_C_FKCOMMAND;
+ if(ct->ctx.flags & CTOK_M_STATUS_PASSED){ /* mch */
+ ct = allocate_cmd_token(ct);
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+
+ return;
+/*
+ * The following state is a return-state from STRING when we have seen an
+ * escape terminated string which in this case is the name of the file to
+ * write.
+ */
+ case STATE_C_WRITEFILE:
+ ct->execute_state = EXEC_C_WRITEFILE;
+ ct->ctx.go_flag = NO;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The following state is a return-state from STRING when we have seen an
+ * escape terminated string which in this case is the name of the file to
+ * read into the current buffer location.
+ */
+ case STATE_C_READFILE:
+ ct->execute_state = EXEC_C_READFILE;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The following state is a return-state from STRING when we have seen an
+ * escape terminated string which in this case is the name of the file which
+ * we want to create a buffer for and load.
+ */
+ case STATE_C_EDITBUF:
+ ct->execute_state = EXEC_C_EDITBUF;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ oct = ct;
+ ct = allocate_cmd_token(ct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The following state is a return-state from STRING when we have seen an
+ * escape terminated string which in this case is the name of the file which
+ * we want to create a readonly buffer for and load.
+ */
+ case STATE_C_VIEWBUF:
+ ct->execute_state = EXEC_C_VIEWBUF;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ oct = ct;
+ ct = allocate_cmd_token(ct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The following state is a return-state from STRING when we have seen an
+ * escape terminated string which in this case is the command the user wants
+ * to execute.
+ */
+ case STATE_C_ECCOMMAND:
+ ct->execute_state = EXEC_C_ECCOMMAND;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ if(ct->input_byte == ESCAPE) ct->ctx.state = STATE_C_ESCAPESEEN;
+ return;
+/*
+ * The following state gets entered when the user has typed ^A
+ * to print out a message.
+ */
+ case STATE_C_MESSAGE:
+ switch(ct->input_byte){
+ case CNTRL_A:
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_OUTPUT_MESSAGE;
+ return;
+ default:
+ ct->execute_state = EXEC_C_MESSAGE;
+ ct->ctx.state = STATE_C_MESSAGE;
+ return;
+ }/* End Switch */
+
+/*
+ * The following state is a return-state from EXPRESSION when we have seen
+ * what appears to be a first argument. It stashes the argument and tests if
+ * there is a second argument available (indicated by a comma).
+ */
+ case STATE_C_ARG1:
+ ct->ctx.iarg1_flag = YES;
+ ct->execute_state = EXEC_C_STORE1;
+ switch(ct->input_byte){
+ case ',':
+ ct->ctx.state = STATE_C_EXPRESSION;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_ARG2;
+ return;
+
+ default:
+ ct->ctx.state = STATE_C_MAINCOMMANDS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+ }/* End Switch */
+/*
+ * Here if the first argument was followed by a comma. This indicates that a
+ * second argument is being specified. Only certain commands accept a double
+ * argument.
+ */
+ case STATE_C_ARG2:
+ ct->ctx.iarg2_flag = YES;
+ ct->execute_state = EXEC_C_STORE2;
+ switch(ct->input_byte){
+ case ',':
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ error_message("?Maximum of two arguments exceeded");
+ return;
+
+ default:
+ ct->ctx.state = STATE_C_MAINCOMMANDS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+ }/* End Switch */
+/*
+ * The next state is the expression substate. States outside of the expression
+ * parser call through here so that pnest (the current nesting level of
+ * parentheses) gets set to zero. Then it calls the expression parser's own
+ * internal sub-expression state.
+ */
+ case STATE_C_EXPRESSION:
+ ct->ctx.pnest = 0;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_SUBEXPRESSION;
+ return;
+/*
+ * This is the sub-expression state. It gets called from within the expression
+ * parser when we want to recursively parse an expression. This lets us handle
+ * precedence and parenthesis correctly.
+ */
+ case STATE_C_SUBEXPRESSION:
+ switch(ct->input_byte){
+/*
+ * When we see an open parenthesis, we want to recursively call the
+ * subexpression state to parse the contents of the parenthesis. Since the open
+ * parenthesis always occurs in place of an operand, we set that to be the next
+ * state to call.
+ */
+ case '(':
+ ct->ctx.pnest += 1;
+ ct->ctx.state = STATE_C_OPERAND;
+
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_OPERATOR;
+
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_OPERATOR;
+ return;
+/*
+ * If we see a minus sign here, it is a unary minus. The difference between
+ * the unary minus and the normal minus is one of precedence. The unary minus
+ * is very high precedence.
+ */
+ case '-':
+ ct->ctx.state = STATE_C_MINUSSEEN;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_UMINUS;
+ return;
+/*
+ * Here on a leading % command. This just means he is going to default to
+ * incrementing the queue register by one.
+ */
+ case '%':
+ ct->ctx.tmpval = 1;
+ ct->flags |= TOK_M_PARSELOAD_TMPVAL;
+/* ;;; the following line shouldn't be required, but is. A bug. */
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.state = STATE_C_PERCENT_OPERAND;
+ return;
+/*
+ * Well, not much exciting going on here, so we just call the normal operand
+ * state to parse the current token.
+ */
+ default:
+ ct->ctx.state = STATE_C_OPERAND;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_OPERATOR;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+ }/* End Switch */
+/*
+ * Here if we saw a unary minus. The reason for calling this state at all is to
+ * catch constructs like -L which really implies -1L. In a case like this,
+ * the OPERAND state will not see anything it understands, and will return.
+ * So as to make the defaulting work correctly, we catch that case here and
+ * default so that we get our -1.
+ */
+ case STATE_C_MINUSSEEN:
+ switch(ct->input_byte){
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'Q': case 'q': case '%':
+ case '.':
+#ifdef CLASSIC_B_BEHAVIOR
+ case 'B': case 'b':
+#endif
+ case 'Z': case 'z':
+ case '(':
+ case '^':
+ ct->ctx.state = STATE_C_OPERAND;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+
+ default:
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = 1;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_RETURN;
+ return;
+ }/* End Switch */
+/*
+ * Here on the return from the OPERAND state. Whatever it parsed, we want to
+ * make it minus.
+ */
+ case STATE_C_UMINUS:
+ ct->execute_state = EXEC_C_UMINUS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_OPERATOR;
+ return;
+/*
+ * Here when we are expecting an operator like + = * /. Note that ')' also gets
+ * handled here since it always appears in the place an operator would be
+ * expected to appear.
+ */
+ case STATE_C_OPERATOR:
+ switch(ct->input_byte){
+/*
+ * Here on an a-b case. Note that since minus is a low precedence operator,
+ * we stash the temporary value, and call subexpression to parse the rest
+ * of the expression. In that way, if the next operator was * or /, it will
+ * get processed first.
+ */
+ case '-':
+ ct->execute_state = EXEC_C_STORE2;
+ ct->ctx.state = STATE_C_OPERAND;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_MINUS;
+ return;
+/*
+ * Here on an a+b case. This is similar to the case above in that we want to
+ * just stash the first value, and then recursively call the parser to handle
+ * the rest of the expression first. Note that this causes a+b+c to actually
+ * get handled as a+(b+c) which is a little weird, but works out ok.
+ */
+ case '+':
+ ct->execute_state = EXEC_C_STORE2;
+ ct->ctx.state = STATE_C_OPERAND;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_PLUS;
+ return;
+/*
+ * Here on the a*b case. Unlike the above two cases, we want to immediately
+ * handle this case since it is the highest precedence of the four operators.
+ */
+ case '*':
+ ct->execute_state = EXEC_C_STORE1;
+ ct->ctx.state = STATE_C_OPERAND;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_TIMES;
+ return;
+/*
+ * Here on the a/b case. This is just like the multiply state
+ */
+ case '/':
+ ct->execute_state = EXEC_C_STORE1;
+ ct->ctx.state = STATE_C_OPERAND;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_DIVIDE;
+ return;
+
+/*
+ * Here on the n%q case. This just collapses to the value of the Q-register
+ * after it has been incremented by 'n'
+ */
+ case '%':
+ ct->ctx.state = STATE_C_PERCENT_OPERAND;
+ return;
+/*
+ * Here on the 'A' command which returns a value
+ */
+ case 'A': case 'a':
+ ct->execute_state = EXEC_C_ACOMMAND;
+ ct->ctx.state = STATE_C_OPERATOR;
+ return;
+/*
+ * Here on a close parenthesis. This will force the end of a subexpression
+ * parse even though it would not normally have terminated yet. Note the check
+ * for the guy typing too many of these.
+ */
+ case ')':
+ if(ct->ctx.pnest == 0){
+ error_message("?No Matching Open Parenthesis");
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.pnest -= 1;
+ ct->ctx.state = STATE_C_RETURN;
+ return;
+/*
+ * If this is not an operator character, it must be the end of the expression.
+ * In this case, we must be back at the zero level for parenthesis nesting.
+ */
+ default:
+ if(ct->ctx.pnest > 0){
+ sprintf(tmp_message,"?Missing %d Close Parenthesis",
+ ct->ctx.pnest);
+ error_message(tmp_message);
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_RETURN;
+ return;
+ }/* End Switch */
+/*
+ * Here when we have the 'b' part of an a+b expression. This happens either
+ * when the end of the expression has been completed, or a parenthesis has
+ * forced a temporary end as in: (a+b)
+ */
+ case STATE_C_PLUS:
+ switch(ct->input_byte){
+ case '-':
+ case '+':
+ case ')':
+ ct->execute_state = EXEC_C_PLUS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_OPERATOR;
+ return;
+ default:
+ ct->ctx.state = STATE_C_OPERATOR;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_DELAYED_PLUS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+ }/* End Switch */
+
+ case STATE_C_DELAYED_PLUS:
+ ct->execute_state = EXEC_C_PLUS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_OPERATOR;
+ return;
+
+/*
+ * Here when we have the 'b' part of an a-b expression. This happens either
+ * when the end of the expression has been completed, or a parenthesis has
+ * forced a temporary end as in: (a-b)
+ */
+ case STATE_C_MINUS:
+ switch(ct->input_byte){
+ case '-':
+ case '+':
+ case ')':
+ ct->execute_state = EXEC_C_MINUS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_OPERATOR;
+ return;
+ default:
+ ct->ctx.state = STATE_C_OPERATOR;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_DELAYED_MINUS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+ }/* End Switch */
+
+ case STATE_C_DELAYED_MINUS:
+ ct->execute_state = EXEC_C_MINUS;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_OPERATOR;
+ return;
+
+/*
+ * Here when we have both arguments to a multiply. Because multiply is a higher
+ * precedence operator than +-, it happens immediately unless parenthesis get
+ * involved.
+ */
+ case STATE_C_TIMES:
+ ct->execute_state = EXEC_C_TIMES;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_OPERATOR;
+ return;
+/*
+ * Here when we have both arguments to a divide. Because division is a higher
+ * precedence operator than -+, it generally happens immediately. Note that we
+ * check to be sure that the divisor is non-zero.
+ */
+ case STATE_C_DIVIDE:
+ ct->execute_state = EXEC_C_DIVIDE;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_OPERATOR;
+ return;
+/*
+ * Here to parse any legal TECO operand. These would include numbers and
+ * commands that return values. Since open parenthesis occur in the same place
+ * as operands, we also catch them here.
+ */
+ case STATE_C_OPERAND:
+ switch(ct->input_byte){
+/*
+ * If we see a numeric digit, call a substate which will continue eating bytes
+ * until a non-digit is seen.
+ */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ ct->ctx.state = STATE_C_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = ct->input_byte - '0';
+ return;
+/*
+ * Here if he wants to input in a different radix
+ */
+ case '^':
+ ct->ctx.state = STATE_C_RADIX;
+ ct->ctx.tmpval = 0;
+ return;
+/*
+ * Here if he is specifying the numeric value of a q-register. We have to go
+ * to another state to determine which q-register he wants to access.
+ */
+ case 'Q': case 'q':
+ ct->ctx.state = STATE_C_QOPERAND;
+ return;
+/*
+ * Here when he has specified <dot>. This will return as a value the current
+ * position in the edit buffer.
+ */
+ case '.':
+ ct->ctx.state = STATE_C_RETURN;
+ ct->execute_state = EXEC_C_DOTVALUE;
+ return;
+#ifdef CLASSIC_B_BEHAVIOR
+/*
+ * B is a special operand which returns the address of the first character
+ * in the buffer. This is currently always 0, but may change if some really
+ * obscure features get put in one of these days.
+ */
+ case 'B': case 'b':
+ ct->ctx.state = STATE_C_RETURN;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = 0;
+ return;
+#endif
+/*
+ * Z is a special operand which is the number of characters currently in the
+ * edit buffer.
+ */
+ case 'Z': case 'z':
+ ct->ctx.state = STATE_C_RETURN;
+ ct->execute_state = EXEC_C_ZEEVALUE;
+ return;
+/*
+ * BACKSLASH with no argument actually looks in the buffer at the current
+ * position and eats numeric digits until it hits a non-digit. It then
+ * returns the number as a value.
+ */
+ case '\\':
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_BACKSLASH;
+ ct->ctx.tmpval = 10;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct = allocate_cmd_token(ct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->execute_state = EXEC_C_STORE1;
+ return;
+/*
+ * If we see a parenthesis in place of an operand, we want to parse it as
+ * a complete expression of its own until a matching close parenthesis.
+ */
+ case '(':
+ ct->ctx.state = STATE_C_SUBEXPRESSION;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+/*
+ * We really should not get here if the guy is typing good syntax, so we
+ * tell him it is junk and we don't allow it.
+ */
+ default:
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,
+ "?Unexpected character <%c> in operand",
+ ct->input_byte);
+ error_message(tmp_message);
+ return;
+ }/* End Switch */
+/*
+ * The following is a substate to parse a number. It will continue until
+ * it sees a non-digit, and then return to the calling state.
+ */
+ case STATE_C_NUMBER_SUBSTATE:
+ switch(ct->input_byte){
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ ct->ctx.state = STATE_C_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = ct->ctx.tmpval * 10 + ct->input_byte - '0';
+ return;
+ default:
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_RETURN;
+ return;
+ }/* End Switch*/
+/*
+ * The following is a substate to parse a hex number. It will continue until
+ * it sees a non hex digit, and then return to the calling state.
+ */
+ case STATE_C_HEX_NUMBER_SUBSTATE:
+ switch(ct->input_byte){
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval *= 16;
+ ct->ctx.tmpval += ct->input_byte - '0';
+ return;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval *= 16;
+ ct->ctx.tmpval += ct->input_byte - 'a' + 10;
+ return;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval *= 16;
+ ct->ctx.tmpval += ct->input_byte - 'A' + 10;
+ return;
+ default:
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_RETURN;
+ return;
+ }/* End Switch*/
+/*
+ * The following is a substate to parse a octal number. It will continue until
+ * it sees a non octal digit, and then return to the calling state.
+ */
+ case STATE_C_OCTAL_NUMBER_SUBSTATE:
+ switch(ct->input_byte){
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ ct->ctx.state = STATE_C_OCTAL_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval *= 8;
+ ct->ctx.tmpval += ct->input_byte - '0';
+ return;
+ case '8': case '9':
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,"?Illegal octal digit <%c> in operand",
+ ct->input_byte);
+ error_message(tmp_message);
+ return;
+ default:
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_RETURN;
+ return;
+ }/* End Switch*/
+/*
+ * The following state gets the value of the specified q-register
+ */
+ case STATE_C_QOPERAND:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_RETURN;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_QVALUE;
+ return;
+/*
+ * This is just like QOPERAND except the execute state is different
+ */
+ case STATE_C_PERCENT_OPERAND:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_OPERATOR;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_PERCENT_VALUE;
+ return;
+/*
+ * Here on a input radix character
+ */
+ case STATE_C_RADIX:
+ switch(ct->input_byte){
+ case 'X': case 'x':
+ ct->ctx.state = STATE_C_HEX_NUMBER;
+ ct->execute_state = EXEC_C_STOREVAL;
+ return;
+ case 'O': case 'o':
+ ct->ctx.state = STATE_C_OCTAL_NUMBER;
+ ct->execute_state = EXEC_C_STOREVAL;
+ return;
+ default:
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,"?Unknown radix ^<%c> in operand",
+ ct->input_byte);
+ error_message(tmp_message);
+ return;
+ }/* End Switch */
+
+ case STATE_C_HEX_NUMBER:
+ switch(ct->input_byte){
+ case '\\':
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_BACKSLASH;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = 16;
+ return;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = ct->input_byte - '0';
+ return;
+ case 'a': case 'b': case 'c': case 'd': case 'e':
+ case 'f':
+ ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = ct->input_byte - 'a' + 10;
+ return;
+ case 'A': case 'B': case 'C': case 'D': case 'E':
+ case 'F':
+ ct->ctx.state = STATE_C_HEX_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = ct->input_byte - 'A' + 10;
+ return;
+ default:
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,"?Illegal hex digit <%c> in operand",
+ ct->input_byte);
+ error_message(tmp_message);
+ return;
+ }/* End Switch */
+
+ case STATE_C_OCTAL_NUMBER:
+ switch(ct->input_byte){
+ case '\\':
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_BACKSLASH;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = 8;
+ return;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ ct->ctx.state = STATE_C_OCTAL_NUMBER_SUBSTATE;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = ct->input_byte - '0';
+ return;
+ default:
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ sprintf(tmp_message,"?Illegal octal digit <%c> in operand",
+ ct->input_byte);
+ error_message(tmp_message);
+ return;
+ }/* End Switch*/
+
+/*
+ * The next state is the string substate. Outside states call in through here
+ * which does the initial tec_alloc of the string storage. Strings are limited
+ * to PARSER_STRING_MAX bytes at this time.
+ */
+ case STATE_C_STRING:
+ if((ct->ctx.flags & CTOK_M_ATSIGN_SEEN) == 0){
+ ct->ctx.delimeter = ESCAPE;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ }
+ else {
+ ct->ctx.delimeter = ct->input_byte;
+ }
+ ct->ctx.state = STATE_C_STRING1;
+ return;
+
+ case STATE_C_STRING1:
+/*
+ * First, check if this is the termination character. This would normally be
+ * an escape, but could be another character if the user used the @ form.
+ */
+ if( ( (ct->input_byte == ESCAPE) &&
+ (!(ct->ctx.flags & CTOK_M_ATSIGN_SEEN))
+ ) ||
+ ( (ct->ctx.flags & CTOK_M_ATSIGN_SEEN) &&
+ (ct->input_byte == ct->ctx.delimeter) &&
+ (ct->ctx.carg != NULL)
+ )
+ ){
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_RETURN;
+ return;
+ }/* End IF */
+
+/*
+ * Here when we see a normal character inside of the string. Note that we
+ * have to do the undo stuff to manage the string since it gets stored in
+ * a regular string array rather than being spread through the command
+ * tokens.
+ */
+ {/* Local Block */
+ register char *cp;
+ register struct undo_token *ut;
+/*
+ * Get the address of the string
+ */
+ cp = ct->ctx.carg;
+/*
+ * If there is no string allocated yet, then we have to allocate one.
+ * Also, if an @ was seen, then record the delimeter character.
+ */
+ if(cp == NULL){
+ ct->ctx.carg = cp = tec_alloc(TYPE_C_CBUFF,PARSER_STRING_MAX);
+ if(cp == NULL){
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ *cp = '\0';
+ ut = allocate_undo_token(uct);
+ if(ut == NULL){
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ut->opcode = UNDO_C_MEMFREE;
+ ut->carg1 = cp;
+ }/* End IF */
+/*
+ * Each time we append a character to the string, we also have to allocate
+ * an undo token which will shorten the string if the input character gets
+ * rubbed out.
+ */
+ ut = allocate_undo_token(uct);
+ if(ut == NULL){
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ut->opcode = UNDO_C_SHORTEN_STRING;
+ ut->carg1 = cp;
+ ut->iarg1 = 0;
+ while(*cp){
+ ut->iarg1 += 1;
+ cp++;
+ }/* End While */
+ if(ut->iarg1 >= (PARSER_STRING_MAX-1)){
+ sprintf(tmp_message,"?Exceeded maximum string length of %d",
+ PARSER_STRING_MAX-1);
+ error_message(tmp_message);
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+/*
+ * Finally, we append the input byte to the string
+ */
+ *cp++ = ct->input_byte;
+ *cp++ = '\0';
+ ct->ctx.state = STATE_C_STRING1;
+ return;
+ }/* End Local Block */
+/*
+ * The following state gets the Q register that will be used with the
+ * G command.
+ */
+ case STATE_C_GQREGISTER:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_GQREGISTER;
+ return;
+/*
+ * The following state gets the Q register that will be used with the
+ * M command.
+ */
+ case STATE_C_MQREGISTER:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_MQREGISTER;
+ return;
+/*
+ * The following two states implement the EQ command which reads a file into
+ * the specified Q-register.
+ */
+ case STATE_C_EQQREGISTER1:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.tmpval = ct->input_byte;
+ ct->ctx.state = STATE_C_STRING;
+
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.caller_token = oct;
+ ct->ctx.return_state = STATE_C_EQQREGISTER2;
+ return;
+ case STATE_C_EQQREGISTER2:
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_EQQREGISTER;
+ if(ct->ctx.flags & CTOK_M_COLON_SEEN){
+ oct = ct;
+ ct = allocate_cmd_token(oct);
+ if(ct == NULL){
+ oct->ctx.state = STATE_C_ERRORSTATE;
+ return;
+ }/* End IF */
+ ct->ctx.iarg1_flag = YES; ct->ctx.iarg2_flag = NO;
+ ct->execute_state = EXEC_C_STORE1;
+ }/* End IF */
+ return;
+/*
+ * The following state gets the Q register that will be loaded with the
+ * U command.
+ */
+ case STATE_C_UQREGISTER:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_UQREGISTER;
+ return;
+/*
+ * The following state gets the Q register that will be loaded with the
+ * X command.
+ */
+ case STATE_C_XQREGISTER:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_XQREGISTER;
+ return;
+/*
+ * The following state gets the Q register that will be loaded with the
+ * previous command line in response to the '*' command.
+ */
+ case STATE_C_SAVECOMMAND:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_SAVECOMMAND;
+ return;
+/*
+ * The following state gets the Q register which will be pushed onto the
+ * Q register stack.
+ */
+ case STATE_C_PUSH_QREGISTER:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_PUSH_QREGISTER;
+ return;
+/*
+ * The following state gets the Q register which will be popped from the
+ * Q register stack.
+ */
+ case STATE_C_POP_QREGISTER:
+ if(!parse_check_qname(ct,ct->input_byte)) return;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->q_register = ct->input_byte;
+ ct->execute_state = EXEC_C_POP_QREGISTER;
+ return;
+
+/*
+ * Here to insert characters. The insert state continues until an escape
+ * is seen. Escapes can also be quoted if they are to be inserted into the
+ * buffer. If there are many escapes to be inserted, it may be easier to use
+ * the @ insert command...
+ */
+ case STATE_C_INSERT:
+ switch(ct->input_byte){
+/*
+ * Here when we see a non-quoted escape. This terminates the insert.
+ */
+ case ESCAPE:
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+/*
+ * Here to enter a quote character. This lets you insert characters that would
+ * normally be handled otherwise. ESCAPE is a good example of this.
+ */
+ case CNTRL_V:
+ ct->ctx.state = STATE_C_QUOTED_INSERT;
+ return;
+/*
+ * Here to handle a normal character. It will simply be inserted into the
+ * edit buffer.
+ */
+ default:
+ ct->ctx.state = STATE_C_INSERT;
+ ct->execute_state = EXEC_C_INSERT;
+ return;
+ }/* End Switch */
+/*
+ * Here to insert a character directly following the quote character. If
+ * this is a special character such as an escape, it will be inserted and
+ * will not terminate the insert command.
+ */
+ case STATE_C_QUOTED_INSERT:
+ ct->ctx.state = STATE_C_INSERT;
+ ct->execute_state = EXEC_C_INSERT;
+ return;
+
+/*
+ * Here for the alternate form of insert, @I/text/
+ */
+ case STATE_C_ATINSERT:
+ ct->ctx.tmpval = ct->input_byte;
+ ct->execute_state = EXEC_C_STOREVAL;
+ ct->ctx.state = STATE_C_ATINSERT_PART2;
+ return;
+
+ case STATE_C_ATINSERT_PART2:
+ if(ct->input_byte == ct->ctx.tmpval){
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+ }/* End IF */
+
+ ct->ctx.state = STATE_C_ATINSERT_PART2;
+ ct->execute_state = EXEC_C_INSERT;
+ return;
+/*
+ * Here after seeing an equals command. This lets us test for two or three
+ * in a row which output in octal and hex respectively.
+ */
+ case STATE_C_ONE_EQUALS:
+ if(ct->input_byte != '='){
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+ }/* End IF */
+ ct->ctx.state = STATE_C_TWO_EQUALS;
+ ct->execute_state = EXEC_C_TWO_EQUALS;
+ return;
+ case STATE_C_TWO_EQUALS:
+ if(ct->input_byte != '='){
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->flags &= ~TOK_M_EAT_TOKEN;
+ return;
+ }/* End IF */
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ ct->execute_state = EXEC_C_THREE_EQUALS;
+ return;
+
+/*
+ * Here when we have seen a vertical bar. We have to put a mark here so
+ * that if the condition is not met, the else clause can be found.
+ */
+ case STATE_C_SKIP_ELSE:
+ ct->opcode = TOK_C_CONDITIONAL_ELSE;
+ ct->ctx.state = STATE_C_INITIALSTATE;
+ return;
+
+ case STATE_C_BACKSLASH:
+ ct->ctx.state = STATE_C_RETURN;
+ ct->execute_state = EXEC_C_BACKSLASHARG;
+ return;
+
+ default:
+ sprintf(tmp_message,"?Dispatched unknown state %d",ct->ctx.state);
+ error_message(tmp_message);
+ return;
+ }/* End Switch */
+
+}/* End Routine */
+
+
+
+/* PARSE_CHECK_QNAME - Check that the specified Q-register name is ok
+ *
+ * Function:
+ *
+ * This routine is called when a parse state wants to verify that the
+ * specified Q-register name is syntactically correct. It does not
+ * verify that the Q-register exists, because the execute phase may just
+ * not have got around to that yet.
+ */
+int
+parse_check_qname(ct,name)
+register struct cmd_token *ct;
+register char name;
+{
+char tmp_message[LINE_BUFFER_SIZE];
+
+ PREAMBLE();
+
+ switch(name){
+ case '*':
+ case '_':
+ case '-':
+ case '@':
+ case '?':
+ return(SUCCESS);
+ }/* End Switch */
+
+ if(isalnum((int)name)) return(SUCCESS);
+
+ sprintf(tmp_message,"?Illegal Q-register Name <%c>",name);
+ error_message(tmp_message);
+ ct->ctx.state = STATE_C_ERRORSTATE;
+ return(FAIL);
+
+}/* End Routine */
diff --git a/tecterm.c b/tecterm.c
new file mode 100644
index 0000000..23b506f
--- /dev/null
+++ b/tecterm.c
@@ -0,0 +1,1579 @@
+char *tecterm_c_version = "tecterm.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:21 $
+ * $Source: /cvsroot/videoteco/videoteco/tecterm.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecterm.c
+ * Terminal Escape Sequence Subroutines (termcap/terminfo et all)
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+#ifdef TERMINFO
+#include <curses.h>
+#include <term.h>
+#endif
+
+/*
+ * Global Storage is defined here for lack of a better place.
+ */
+
+/*
+ * Global Variables
+ */
+ extern int curx;
+ extern int cury;
+ extern int term_columns;
+ extern int term_lines;
+ char scr_outbuf[SCREEN_OUTPUT_BUFFER_SIZE];
+ char *scr_outbuf_ptr;
+ int scr_outbuf_left;
+ char insert_delete_line_capability;
+
+ void term_insert_line();
+ void term_delete_line();
+ void term_puts();
+ int term_putc(int);
+ void term_flush();
+
+#ifdef TERMCAP
+ char *tgetstr();
+#endif
+
+#ifdef TERMCAP
+ int termcap_co;
+ int termcap_li;
+ int termcap_sg;
+ char *termcap_cm;
+ char *termcap_ti;
+ char *termcap_te;
+ char *termcap_pc;
+ char *termcap_so;
+ char *termcap_se;
+ char *termcap_cd;
+ char *termcap_ce;
+ char *termcap_cs;
+ char *termcap_sf;
+ char *termcap_sr;
+ char *termcap_al;
+ char *termcap_dl;
+ char *termcap_AL_arg;
+ char *termcap_DL_arg;
+#endif
+
+#ifdef TERMINFO
+ int terminfo_co;
+ int terminfo_li;
+ int terminfo_magic_cookie_glitch;
+#endif
+
+/*
+ * External Routines, etc.
+ */
+
+ extern int term_speed;
+ extern char teco_startup;
+ extern int tty_input_chan;
+ extern int tty_output_chan;
+
+/*
+ * The following array gives the number of tens of milliseconds per
+ * character for each speed as returned by gtty. Thus since 300
+ * baud returns a 7, there are 33.3 milliseconds per char at 300 baud.
+ */
+#ifdef TERMCAP
+static
+short tmspc10[] = {
+ 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10
+};
+#endif
+
+/* TERM_INIT - Initialize the terminal for cursor operations
+ *
+ * Function:
+ *
+ * This entry point gives the terminal package a chance to initialize
+ * the terminal for cursor type operations.
+ */
+void
+term_init()
+{
+/*
+ * Initialize our output buffer.
+ */
+ scr_outbuf_ptr = scr_outbuf;
+ scr_outbuf_left = SCREEN_OUTPUT_BUFFER_SIZE;
+
+#ifdef TERMCAP
+/*
+ * "TI - String to begin programs that use cm"
+ */
+ term_puts(termcap_ti,1);
+
+/*
+ * Set a fast flag which says whether or not terminal is capable of insert and
+ * delete line functions.
+ */
+ insert_delete_line_capability = 1;
+
+ if(termcap_dl == NULL && termcap_DL_arg == NULL){
+ insert_delete_line_capability = 0;
+ }/* End IF */
+
+ if(termcap_al == NULL && termcap_AL_arg == NULL){
+ insert_delete_line_capability = 0;
+ }/* End IF */
+
+ if(termcap_cs && termcap_sf && termcap_sr){
+ insert_delete_line_capability = 1;
+ }/* End IF */
+#endif
+
+#ifdef TERMINFO
+/*
+ * Initialize terminfo package
+ */
+ setupterm((char *)0, 1, (int*)0);
+
+ insert_delete_line_capability = 1;
+
+ if(parm_insert_line == NULL && insert_line == NULL){
+ insert_delete_line_capability = 0;
+ }/* End IF */
+
+ if(parm_delete_line == NULL && delete_line == NULL){
+ insert_delete_line_capability = 0;
+ }/* End IF */
+
+ if(change_scroll_region && scroll_forward && scroll_reverse){
+ insert_delete_line_capability = 1;
+ }/* End IF */
+
+ terminfo_magic_cookie_glitch = magic_cookie_glitch > 0 ?
+ magic_cookie_glitch : 0;
+
+#endif
+
+}/* End Routine */
+
+
+
+/* TERM_FINISH - Here when we are exiting the editor
+ *
+ * Function:
+ *
+ * This routine is called when TECO is being exited to give us a chance
+ * to cleanly leave the screen package. We put the cursor at the bottom
+ * of the screen, send any termination escape sequences that are required
+ * and flush it all out.
+ */
+void
+term_finish()
+{
+
+/*
+ * We want to leave the cursor at the bottom of the screen
+ */
+ term_goto(0,term_lines-1);
+
+#ifdef TERMCAP
+
+/*
+ * Output any termination escape sequences
+ */
+ term_puts(termcap_te,1);
+
+#endif
+
+/*
+ * Now flush that all out
+ */
+ term_flush();
+
+}/* End Routine */
+
+
+
+/* TERM_STANDOUT - Set standout mode on the terminal
+ *
+ * Function:
+ *
+ * This routine is called to set the highlighting standout
+ * mode. All characters output from now on will typically
+ * be reverse video, or whatever attribute the terminal uses.
+ */
+void
+term_standout()
+{
+
+#ifdef TERMCAP
+
+ if(!termcap_sg) term_puts(termcap_so,1);
+
+#endif
+
+#ifdef TERMINFO
+
+ if(terminfo_magic_cookie_glitch <= 0){
+ tputs(enter_standout_mode,1,term_putc);
+ }
+
+#endif
+
+}/* End Routine */
+
+/* TERM_STANDEND - Clear standout mode on the terminal
+ *
+ * Function:
+ *
+ * This routine is called to reset the highlighting standout
+ * mode. All characters output from now on will be in the
+ * normal rendition mode.
+ */
+void
+term_standend()
+{
+
+#ifdef TERMCAP
+
+ if(!termcap_sg) term_puts(termcap_se,1);
+
+#endif
+
+#ifdef TERMINFO
+
+ if(terminfo_magic_cookie_glitch <= 0){
+ tputs(exit_standout_mode,1,term_putc);
+ }
+
+#endif
+
+}/* End Routine */
+
+/* TERM_CLRTOEOL - Clear to End Of Line
+ *
+ * Function:
+ *
+ * This routine sends the ESCape sequence to clear all characters
+ * from the present position until the end of the line.
+ */
+void
+term_clrtoeol()
+{
+#ifdef TERMCAP
+
+ term_puts(termcap_ce,1);
+
+#endif
+
+#ifdef TERMINFO
+
+ tputs(clr_eol,1,term_putc);
+
+#endif
+
+}/* End Routine */
+
+/* TERM_CLRTOBOT - Clear to End Of Screen
+ *
+ * Function:
+ *
+ * This routine sends the ESCape sequence to clear all characters
+ * from the present position until the end of the terminal screen.
+ */
+void
+term_clrtobot()
+{
+
+#ifdef TERMCAP
+
+ term_puts(termcap_cd,1);
+
+
+#endif
+
+#ifdef TERMINFO
+
+ tputs(clr_eos,1,term_putc);
+
+#endif
+
+}/* End Routine */
+
+
+
+/* TERM_INSERT_LINE - Perform an insert-line terminal function
+ *
+ * Function:
+ *
+ * This routine is called to output the insert-line escape sequence.
+ * It checks to see what insert-line capabilities the terminal has,
+ * and tries to use the optimum one.
+ */
+void
+term_insert_line(position,count)
+int position;
+register int count;
+{
+/*
+ * Test whether the terminal supports an insert line escape sequence which
+ * takes an argument
+ */
+#ifdef TERMCAP
+ if(count > 1 && termcap_AL_arg){
+ term_goto(0,position);
+ term_putnum(termcap_AL_arg,count,term_lines-position);
+ return;
+ }/* End IF */
+#endif
+
+#ifdef TERMINFO
+ if(count > 1 && parm_insert_line){
+ term_goto(0,position);
+ tputs(
+ tparm(parm_insert_line,count),
+ term_lines-position,
+ term_putc
+ );
+ return;
+ }/* End IF */
+#endif
+
+/*
+ * Ok, here if either the terminal does not support insert line with an
+ * argument, or if count happens to be 1
+ */
+#ifdef TERMCAP
+ if(termcap_al){
+ term_goto(0,position);
+ while(count--){
+ term_puts(termcap_al,term_lines-position);
+ }/* End While */
+ return;
+ }/* End IF */
+#endif
+
+#ifdef TERMINFO
+ if(insert_line){
+ term_goto(0,position);
+ while(count--){
+ tputs(insert_line,term_lines-position,term_putc);
+ }/* End While */
+ return;
+ }/* End IF */
+#endif
+
+
+/*
+ * Hmm, termcap_al must be undefined. That must mean that cs is defined and
+ * that we have to do this by using scroll regions.
+ */
+ if(position) term_scroll_region(position,term_lines-1);
+ if(curx != 0 || cury != position) term_goto(0,position);
+ while(count--){
+
+#ifdef TERMCAP
+ term_puts(termcap_sr,term_lines-position);
+#endif
+
+#ifdef TERMINFO
+ tputs(scroll_reverse,term_lines-position,term_putc);
+#endif
+
+ }/* End While */
+ if(position) term_scroll_region(0,term_lines-1);
+
+#ifdef TERMCAP
+ if(curx != 0 || cury != position) term_goto(0,term_lines-1);
+#endif
+#ifdef TERMINFO
+ curx = cury = -1; /* Unsure of present position... */
+#endif
+
+}/* End Routine */
+
+
+
+/* TERM_DELETE_LINE - Perform an delete-line terminal function
+ *
+ * Function:
+ *
+ * This routine is called to output the delete-line escape sequence.
+ * It checks to see what delete-line capabilities the terminal has,
+ * and tries to use the optimum one.
+ */
+void
+term_delete_line(position,count)
+int position;
+register int count;
+{
+ term_goto(0,position);
+
+/*
+ * Test whether the terminal supports an delete line escape sequence which
+ * takes an argument
+ */
+#ifdef TERMCAP
+ if(count > 1 && termcap_DL_arg){
+ term_putnum(termcap_DL_arg,count,term_lines-position);
+ return;
+ }/* End IF */
+#endif
+
+#ifdef TERMINFO
+ if(count > 1 && parm_delete_line){
+ term_goto(0,position);
+ tputs(
+ tparm(parm_delete_line,count),
+ term_lines-position,
+ term_putc
+ );
+ return;
+ }/* End IF */
+#endif
+
+/*
+ * Ok, here if either the terminal does not support delete line with an
+ * argument, or if count happens to be 1
+ */
+#ifdef TERMCAP
+ if(termcap_dl){
+ while(count--){
+ term_puts(termcap_dl,term_lines-position);
+ }/* End While */
+ return;
+ }/* End IF */
+#endif
+
+#ifdef TERMINFO
+ if(delete_line){
+ term_goto(0,position);
+ while(count--){
+ tputs(delete_line,term_lines-position,term_putc);
+ }/* End While */
+ return;
+ }/* End IF */
+#endif
+
+/*
+ * Hmm, termcap_dl must be undefined. That must mean that cs is defined and
+ * that we have to do this by using scroll regions.
+ */
+ if(position) term_scroll_region(position,term_lines-1);
+ if(curx != 0 || cury != (term_lines-1)) term_goto(0,term_lines-1);
+ while(count--){
+
+#ifdef TERMCAP
+ term_puts(termcap_sf,term_lines-position);
+#endif
+
+#ifdef TERMINFO
+ tputs(scroll_forward,term_lines-position,term_putc);
+#endif
+
+ }/* End While */
+ if(position) term_scroll_region(0,term_lines-1);
+
+#ifdef TERMCAP
+ if(curx != 0 || cury != (term_lines-1)) term_goto(0,term_lines-1);
+#endif
+#ifdef TERMINFO
+ curx = cury = -1; /* Unsure of present position... */
+#endif
+
+
+}/* End Routine */
+
+
+
+#ifdef TERMCAP
+
+/* TERM_PUTNUM - Output a termcap string which takes a single argument
+ *
+ * Function:
+ *
+ * This routine is called to output a termcap string which has an
+ * imbedded %d to be replaced with the single argument.
+ */
+int
+term_putnum(termcap_string,argument,affected)
+char *termcap_string;
+int argument;
+int affected;
+{
+register char *cp;
+register char *dp;
+register int c;
+static char result[40];
+
+ cp = termcap_string;
+ if(cp == NULL) return(FAIL);
+ dp = result;
+
+/*
+ * Loop until we hit the end of the string
+ */
+ while((c = *cp++)){
+ if(c != '%'){
+ *dp++ = c;
+ continue;
+ }/* End IF */
+
+ switch(c = *cp++){
+ case 'd':
+ if(argument < 10) goto one;
+ if(argument < 100) goto two;
+ /* fall through */
+ case '3':
+ *dp++ = (argument / 100) + '0';
+ argument %= 100;
+ /* fall through */
+ case '2':
+two:
+ *dp++ = argument / 10 | '0';
+one:
+ *dp++ = argument % 10 | '0';
+ continue;
+ case '%':
+ *dp++ = c;
+ continue;
+
+ case 'B':
+ argument = (argument/10 << 4) + argument%10;
+ continue;
+
+ case 'D':
+ argument = argument - 2 * (argument%16);
+ continue;
+ case 'p':
+ cp++; /* eat p1, p2 type stuff */
+ continue;
+
+ default:
+ return(FAIL);
+ }/* End Switch */
+ }/* End While */
+
+ *dp++ = '\0';
+ term_puts(result,affected);
+ return(SUCCESS);
+
+}/* End Routine */
+
+#endif
+
+
+
+#ifdef TERMCAP
+
+/* TERM_PUTS - Output a termcap string with padding
+ *
+ * Function:
+ *
+ * This routine is called to output a termcap string with padding added.
+ */
+void
+term_puts(termcap_string,lines_affected)
+register char *termcap_string;
+int lines_affected;
+{
+register int delay;
+register int mspc10;
+
+/*
+ * If this termcap string is undefined, we can't do much for him.
+ */
+ if(termcap_string == NULL) return;
+
+/*
+ * Convert the number representing the delay
+ */
+ delay = 0;
+ while(isdigit((int)*termcap_string)){
+ delay = delay * 10 + *termcap_string++ - '0';
+ }/* End While */
+
+/*
+ * If there is a decimal place, read up to one decimal and then
+ * eat all remaining decimal places.
+ */
+ delay *= 10;
+ if(*termcap_string == '.'){
+ termcap_string++;
+ if(isdigit((int)*termcap_string)) delay += *termcap_string - '0';
+ while(isdigit((int)*termcap_string)) termcap_string++;
+ }/* End IF */
+
+/*
+ * If the delay is followed by a `*', then multiply by the affected
+ * lines count.
+ */
+ if(*termcap_string == '*'){
+ termcap_string++;
+ delay *= lines_affected;
+ }/* End IF */
+
+/*
+ * Output all the rest of the string
+ */
+ while(*termcap_string){
+ *scr_outbuf_ptr++ = *termcap_string++;
+ scr_outbuf_left -= 1;
+ if(scr_outbuf_left <= 0){
+ term_flush();
+ }/* End IF */
+ }/* End While */
+
+/*
+ * If no delay is needed, or we can't figure out what the output
+ * speed is, then we just give up on the delay.
+ */
+ if(delay == 0) return;
+ if(term_speed <= 0) return;
+ if(term_speed >= (sizeof(tmspc10)/sizeof(tmspc10[0]))) return;
+
+/*
+ * Round up by half a character frame, and then do the delay.
+ */
+ mspc10 = tmspc10[term_speed];
+ delay += mspc10 / 2;
+ delay /= mspc10;
+ while(delay > 0){
+ delay -= 1;
+ *scr_outbuf_ptr++ = *termcap_pc;
+ scr_outbuf_left -= 1;
+ if(scr_outbuf_left <= 0){
+ term_flush();
+ }/* End IF */
+ }/* End While */
+
+}/* End Routine */
+
+#endif
+
+
+
+/* TERM_GOTO - Send sequence to position the terminal cursor
+ *
+ * Function:
+ *
+ * This routine provides direct cursoring capability by using the
+ * CM termcap/terminfo string. This string looks like a printf which
+ * must be interpreted by this code. This allows pretty much
+ * arbitrary terminal escape sequences to be supported. The meaning
+ * of the special characters in the string are:
+ *
+ * %d - Same as in printf
+ * %2 - like %2d
+ * %3 - like %3d
+ * %. - gives %c hacking special case characters
+ * %+x - like %c but adding x first
+ *
+ * The following codes affect the state but don't use up a value
+ *
+ * %>xy if value > x add y
+ * %r reverses row column
+ * %i increments row column (for one origin indexing)
+ * %% gives %
+ * %B BCD (2 decimal digits encoded in one byte)
+ * %D Delta Data (backwards bcd)
+ *
+ * all other characters simply get output
+ */
+int
+term_goto(dest_x,dest_y)
+int dest_x;
+int dest_y;
+{
+#ifdef TERMCAP
+register char *cp;
+register char *dp;
+register int c;
+register int which;
+static char result[40];
+static char added[10];
+char *UP = NULL;
+char *BC = NULL;
+#endif /* TERMCAP */
+
+#ifdef TERMCAP
+int oncol = 0;
+#endif /* TERMCAP */
+
+#ifdef TERMINFO
+ if(cursor_address == NULL) return(FAIL);
+ tputs(tparm(cursor_address,dest_y,dest_x),1,term_putc);
+ curx = dest_x;
+ cury = dest_y;
+ return(SUCCESS);
+#endif
+
+#ifdef TERMCAP
+ cp = termcap_cm;
+
+ if(cp == NULL) return(FAIL);
+ dp = result;
+ which = dest_y;
+
+ added[0] = '\0';
+
+ curx = dest_x;
+ cury = dest_y;
+
+/*
+ * Loop until we hit the end of the string
+ */
+ while((c = *cp++)){
+ if(c != '%'){
+ *dp++ = c;
+ continue;
+ }/* End IF */
+
+ switch(c = *cp++){
+ case 'p':
+ cp++; /* for p1, p2, or p<n>, eat the digit, because I don't understand what it means yet */
+ continue;
+ case 'n':
+ dest_x ^= 0140;
+ dest_y ^= 0140;
+ goto setwhich;
+ case 'd':
+ if(which < 10) goto one;
+ if(which < 100) goto two;
+ /* fall through */
+ case '3':
+ *dp++ = (which / 100) + '0';
+ which %= 100;
+ /* fall through */
+ case '2':
+ two:
+ *dp++ = which / 10 | '0';
+ one:
+ *dp++ = which % 10 | '0';
+ swap:
+ oncol = 1 - oncol;
+ setwhich:
+ which = oncol ? dest_x : dest_y;
+ continue;
+
+ case '>':
+ if (which > *cp++) which += *cp++;
+ else cp++;
+ continue;
+
+ case '+':
+ which += *cp++;
+ /* fall into... */
+
+ case '.':
+ if(
+ which == 0 ||
+ which == CNTRL_D ||
+ which == '\t' ||
+ which == '\n'
+ ){
+ if (oncol || UP){
+ do {
+ (void) strcat(
+ added,
+ oncol ? (BC ? BC : "\b") : UP
+ );
+ which++;
+ } while (which == '\n');
+ }/* End IF */
+ }/* End IF */
+
+ *dp++ = which;
+ goto swap;
+
+ case 'r':
+ oncol = 1;
+ goto setwhich;
+
+ case 'i':
+ dest_x++;
+ dest_y++;
+ which++;
+ continue;
+
+ case '%':
+ *dp++ = c;
+ continue;
+
+ case 'B':
+ which = (which/10 << 4) + which%10;
+ continue;
+
+ case 'D':
+ which = which - 2 * (which%16);
+ continue;
+
+ default:
+fprintf(stderr,"\n\r\n\rBad char in TGOTO string: %c\n",c);
+sleep(5);
+ return(FAIL);
+ }/* End Switch */
+ }/* End While */
+
+ (void) strcpy(dp,added);
+ term_puts(result,1);
+ return(SUCCESS);
+
+#else
+ return(SUCCESS);
+#endif
+
+
+}/* End Routine */
+
+
+
+#ifdef TERMCAP
+
+/* TERM_SCROLL_REGION - Send sequence to set up a scroll region ala VT100
+ *
+ * Function:
+ *
+ * This routine is identical to term_goto in most respects, except that
+ * it is sending the 'set scroll region' escape sequence instead of the
+ * cursor position sequence. The following escapes are defined for
+ * substituting row and column:
+ *
+ * %d - Same as in printf
+ * %2 - like %2d
+ * %3 - like %3d
+ * %. - gives %c hacking special case characters
+ * %+x - like %c but adding x first
+ *
+ * The following codes affect the state but don't use up a value
+ *
+ * %>xy if value > x add y
+ * %r reverses row column
+ * %i increments row column (for one origin indexing)
+ * %% gives %
+ * %B BCD (2 decimal digits encoded in one byte)
+ * %D Delta Data (backwards bcd)
+ *
+ * all other characters simply get output
+ */
+int
+term_scroll_region(top,bottom)
+int top;
+int bottom;
+{
+register char *cp;
+register char *dp;
+register int c;
+register int which;
+int onbottom = 0;
+static char result[40];
+static char added[10];
+
+char *UP = NULL;
+char *BC = NULL;
+
+ cp = termcap_cs;
+ if(cp == NULL) return(FAIL);
+ dp = result;
+ which = top;
+
+ added[0] = '\0';
+
+ curx = -1;
+ cury = -1;
+
+/*
+ * Loop until we hit the end of the string
+ */
+ while((c = *cp++)){
+ if(c != '%'){
+ *dp++ = c;
+ continue;
+ }/* End IF */
+
+ switch(c = *cp++){
+ case 'n':
+ top ^= 0140;
+ bottom ^= 0140;
+ goto setwhich;
+ case 'd':
+ if(which < 10) goto one;
+ if(which < 100) goto two;
+ /* fall through */
+ case '3':
+ *dp++ = (which / 100) + '0';
+ which %= 100;
+ /* fall through */
+ case '2':
+two:
+ *dp++ = which / 10 | '0';
+one:
+ *dp++ = which % 10 | '0';
+swap:
+ onbottom = 1 - onbottom;
+setwhich:
+ which = onbottom ? bottom : top;
+ continue;
+
+ case '>':
+ if (which > *cp++) which += *cp++;
+ else cp++;
+ continue;
+
+ case '+':
+ which += *cp++;
+ /* fall into... */
+
+ case '.':
+ if(
+ which == 0 ||
+ which == CNTRL_D ||
+ which == '\t' ||
+ which == '\n'
+ ){
+ if (onbottom || UP){
+ do {
+ (void) strcat(
+ added,
+ onbottom ? (BC ? BC : "\b") : UP
+ );
+ which++;
+ } while (which == '\n');
+ }/* End IF */
+ }/* End IF */
+
+ *dp++ = which;
+ goto swap;
+
+ case 'r':
+ onbottom = 1;
+ goto setwhich;
+
+ case 'i':
+ top++;
+ bottom++;
+ which++;
+ continue;
+
+ case '%':
+ *dp++ = c;
+ continue;
+
+ case 'B':
+ which = (which/10 << 4) + which%10;
+ continue;
+
+ case 'D':
+ which = which - 2 * (which%16);
+ continue;
+
+ default:
+ return(FAIL);
+ }/* End Switch */
+ }/* End While */
+
+ (void) strcpy(dp,added);
+ term_puts(result,1);
+ return(SUCCESS);
+
+}/* End Routine */
+
+#endif
+
+#ifdef TERMINFO
+
+/* TERM_SCROLL_REGION - Set up a scroll region
+ *
+ * Function:
+ *
+ * This routine is called to set up a scrolling region on the
+ * terminal. This is typically being used to emulate insert/delete
+ * line.
+ */
+int
+term_scroll_region(top,bottom)
+int top;
+int bottom;
+{
+ if(change_scroll_region == NULL) return(FAIL);
+
+ tputs(tparm(change_scroll_region,top,bottom),1,term_putc);
+ return(SUCCESS);
+
+}/* End Routine */
+
+#endif
+
+
+
+/* TERM_PUTC - Output a single character
+ *
+ * Function:
+ *
+ * This routine is called to output a single character to the screen
+ * output buffer. If this fills the output buffer, flush the buffer.
+ */
+int
+term_putc(data)
+//char data;
+int data;
+{
+ *scr_outbuf_ptr++ = data;
+ scr_outbuf_left -= 1;
+
+ if(scr_outbuf_left <= 0){
+ term_flush();
+ }/* End IF */
+
+ return( data );
+
+}/* End Routine */
+
+/* TERM_FLUSH - Flush any data in the output buffer
+ *
+ * Function:
+ *
+ * This routine flushes any bytes sitting in the screen output
+ * buffer to the terminal.
+ */
+void
+term_flush()
+{
+ register int i;
+
+ i = SCREEN_OUTPUT_BUFFER_SIZE - scr_outbuf_left;
+
+#ifdef UNIX
+ if(i) write(tty_output_chan,scr_outbuf,(unsigned)i);
+#endif
+
+#ifdef VMS
+ if(i){
+ i = sys$qiow(0,tty_output_chan,IO$_WRITEVBLK|IO$M_NOFORMAT,0,0,0,
+ scr_outbuf,i,0,0,0,0);
+ if(!(i & STS$M_SUCCESS)) exit(i);
+ }/* End IF*/
+#endif
+
+ scr_outbuf_left = SCREEN_OUTPUT_BUFFER_SIZE;
+ scr_outbuf_ptr = scr_outbuf;
+
+}/* End Routine */
+
+
+
+/* INIT_TERMCAP - Read in the termcap description of the tty
+ *
+ * Function:
+ *
+ * This routine looks up the terminal description in the termcap file, and
+ * sets up the stuff for the screen package. It also checks that the tty
+ * has certain basic capabilities that we require.
+ */
+int
+init_term_description()
+{
+ char termcap_description_buffer[TERMCAP_BUFFER_SIZE];
+ char *terminal_name;
+#ifdef TERMCAP
+ char *cp;
+#endif
+ char temp_string[256];
+ register int status;
+
+ if(teco_startup == NO) return( SUCCESS );
+
+ if((terminal_name = getenv("TECO_TERM")) == NULL){
+ if((terminal_name = getenv("TERM")) == NULL){
+ tec_error(ENOENT,"Environment variable TERM not found");
+ }/* End IF */
+ }/* End IF */
+
+ status = tgetent(termcap_description_buffer,terminal_name);
+ if(status == -1) tec_error(EIO,"Cannot read termcap file");
+ if(status == 0){
+ sprintf(temp_string,"No termcap entry for terminal type %s",
+ terminal_name);
+ tec_error(ESRCH,temp_string);
+ }/* End IF */
+
+/*
+ * Determine how many rows and columns this terminal has. If either number
+ * is already non-zero, then it has been set up by some previous code that
+ * really knows the size of the terminal window, and in this case, we will
+ * leave it alone.
+ */
+#ifdef TERMCAP
+ if(!term_columns) term_columns = tgetnum("co");
+ if(!term_lines) term_lines = tgetnum("li");
+#endif
+
+#ifdef TERMINFO
+ if(!term_columns) term_columns = tgetnum("columns");
+ if(!term_lines) term_lines = tgetnum("lines");
+#endif
+
+#ifdef TERMCAP
+/*
+ * Now we read the escape sequence description strings for termcap
+ */
+
+/*
+ * CM is the basic cursor movement string. This allows you to position the
+ * cursor to any line,column on the terminal.
+ */
+ cp = &temp_string[0];
+ termcap_cm = NULL;
+ if(tgetstr("cm",&cp)){
+ termcap_cm = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_cm == NULL) return(FAIL);
+ (void) strcpy(termcap_cm,temp_string);
+ }/* End IF */
+ else {
+ tec_error(ENOTTY,"?TECO: Terminal does not support cursor addressing");
+ }/* End Else */
+
+/*
+ * TI is a string which needs to be output at init time by programs
+ * which are going to use CM
+ */
+ cp = &temp_string[0];
+ termcap_ti = NULL;
+ if(tgetstr("ti",&cp)){
+ termcap_ti = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_ti == NULL) return(FAIL);
+ (void) strcpy(termcap_ti,temp_string);
+ }/* End IF */
+
+/*
+ * TE is a string which needs to be output before exit time by programs
+ * which have been using CM
+ */
+ cp = &temp_string[0];
+ termcap_te = NULL;
+ if(tgetstr("te",&cp)){
+ termcap_te = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_te == NULL) return(FAIL);
+ (void) strcpy(termcap_te,temp_string);
+ }/* End IF */
+
+/*
+ * PC is the padd character which is used to fill for terminals which
+ * require some time after certain sequences.
+ */
+ cp = &temp_string[0];
+ termcap_pc = NULL;
+ if(tgetstr("pc",&cp)){
+ termcap_pc = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_pc == NULL) return(FAIL);
+ (void) strcpy(termcap_pc,temp_string);
+ }/* End IF */
+ else termcap_pc = "";
+
+/*
+ * SG is the number of screen locations it takes to set or clear
+ * standout mode. It alerts us to brain damaged magic cookie ttys
+ */
+ termcap_sg = tgetnum("sg");
+ if(termcap_sg < 0) termcap_sg = 0;
+
+/*
+ * SO is the string which will cause the terminal to enter reverse
+ * video (stand-out) mode.
+ */
+ cp = &temp_string[0];
+ termcap_so = NULL;
+ if(tgetstr("so",&cp)){
+ termcap_so = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_so == NULL) return(FAIL);
+ (void) strcpy(termcap_so,temp_string);
+ }/* End IF */
+
+/*
+ * SE is the string which will cause the terminal to leave reverse
+ * video (stand-out) mode and re-enter normal drawing mode.
+ */
+ cp = &temp_string[0];
+ termcap_se = NULL;
+ if(tgetstr("se",&cp)){
+ termcap_se = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_se == NULL) return(FAIL);
+ (void) strcpy(termcap_se,temp_string);
+ }/* End IF */
+
+/*
+ * CD is the string which will cause the terminal to clear from
+ * the present cursor location to the end of the screen
+ */
+ cp = &temp_string[0];
+ termcap_cd = NULL;
+ if(tgetstr("cd",&cp)){
+ termcap_cd = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_cd == NULL) return(FAIL);
+ (void) strcpy(termcap_cd,temp_string);
+ }/* End IF */
+
+/*
+ * CE is the string which will cause the terminal to clear from
+ * the present cursor location to the end of the current line
+ */
+ cp = &temp_string[0];
+ termcap_ce = NULL;
+ if(tgetstr("ce",&cp)){
+ termcap_ce = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_ce == NULL) return(FAIL);
+ (void) strcpy(termcap_ce,temp_string);
+ }/* End IF */
+
+/*
+ * cs is the string which will cause the terminal to change the scroll region
+ * This is mostly useful in emulating insert / delete line on vt100 terminals
+ */
+ cp = &temp_string[0];
+ termcap_cs = NULL;
+ if(tgetstr("cs",&cp)){
+ termcap_cs = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_cs == NULL) return(FAIL);
+ (void) strcpy(termcap_cs,temp_string);
+ }/* End IF */
+
+/*
+ * sf is the string which will cause the terminal to scroll forward. This is
+ * used in conjunction with scroll regions for delete line.
+ */
+ cp = &temp_string[0];
+ termcap_sf = NULL;
+ if(tgetstr("sf",&cp)){
+ termcap_sf = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_sf == NULL) return(FAIL);
+ (void) strcpy(termcap_sf,temp_string);
+ }/* End IF */
+
+/*
+ * sr is the string which will cause the terminal to scroll in reverse. This
+ * is used in conjunction with scroll regions for insert line.
+ */
+ cp = &temp_string[0];
+ termcap_sr = NULL;
+ if(tgetstr("sr",&cp)){
+ termcap_sr = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_sr == NULL) return(FAIL);
+ (void) strcpy(termcap_sr,temp_string);
+ }/* End IF */
+
+/*
+ * al is the string which will cause the terminal to insert a blank line at the
+ * present cursor location.
+ */
+ cp = &temp_string[0];
+ termcap_al = NULL;
+ if(tgetstr("al",&cp)){
+ termcap_al = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_al == NULL) return(FAIL);
+ (void) strcpy(termcap_al,temp_string);
+ }/* End IF */
+
+/*
+ * dl is the string which will cause the terminal to delete the current line.
+ * The following lines will all scroll up.
+ */
+ cp = &temp_string[0];
+ termcap_dl = NULL;
+ if(tgetstr("dl",&cp)){
+ termcap_dl = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_dl == NULL) return(FAIL);
+ (void) strcpy(termcap_dl,temp_string);
+ }/* End IF */
+
+/*
+ * AL is the string which will cause the terminal to insert a blank line at the
+ * present cursor location.
+ */
+ cp = &temp_string[0];
+ termcap_AL_arg = NULL;
+ if(tgetstr("AL",&cp)){
+ termcap_AL_arg = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_AL_arg == NULL) return(FAIL);
+ (void) strcpy(termcap_AL_arg,temp_string);
+ }/* End IF */
+
+/*
+ * DL is the string which will cause the terminal to delete the current line.
+ * The following lines will all scroll up.
+ */
+ cp = &temp_string[0];
+ termcap_DL_arg = NULL;
+ if(tgetstr("DL",&cp)){
+ termcap_DL_arg = tec_alloc(TYPE_C_CPERM,strlen(temp_string)+1);
+ if(termcap_DL_arg == NULL) return(FAIL);
+ (void) strcpy(termcap_DL_arg,temp_string);
+ }/* End IF */
+
+#endif
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+#ifdef VMS
+char *current_termcap_description;
+char *termcap =
+"d1|vt100-80|vt-100|dec vt100:\
+:co#80:li#24:cl=50\\E[;H\\E[2J:bs:am:cm=5\\E[%i%2;%2H:nd=2\\E[C:up=2\\E[A:\
+:ce=3\\E[K:cd=50\\E[J:so=2\\E[7m:se=2\\E[m:us=2\\E[4m:ue=2\\E[m:\
+:is=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:\
+:ku=\\E[A:kd=\\E[B:kr=\\E[C:kl=\\E[D:\
+:cs=\\E[%i%d;%dr:sf=5\\ED:\
+:kh=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:pt:sr=5\\EM:xn:xv:";
+
+
+/* TGETENT - Version of TGETENT for Non-UNIX systems
+ *
+ * Function:
+ *
+ * This routine is used on non-UNIX systems to return a termcap
+ * description into the specified buffer.
+ */
+int
+tgetent(buffer,term_name)
+char *buffer;
+char *term_name;
+{
+register char *cp = NULL;
+register char *dp;
+char *temp_buffer = NULL;
+char *termcap_filename;
+FILE *fd;
+
+/*
+ * If current_termcap_description isn't set up yet, check for a logical
+ * name which points to a termcap file, and see if we can find the entry
+ * in there.
+ */
+ if(current_termcap_description == NULL){
+ termcap_filename = getenv("TERMCAP");
+ if(termcap_filename != NULL){
+ fd = fopen(termcap_filename,"r");
+ if(fd != NULL){
+ temp_buffer = malloc(TERMCAP_BUFFER_SIZE);
+ if(temp_buffer == NULL) return(FAIL);
+ if(scan_termcap(fd,temp_buffer,term_name)){
+ cp = temp_buffer;
+ }/* End IF */
+ fclose(fd);
+ }/* End IF */
+ }/* End IF */
+ }/* End IF */
+
+ if(cp == NULL) cp = termcap;
+ dp = current_termcap_description = buffer;
+
+ while(*cp != NULL){
+ if(cp[0] == '\\'){
+ if(cp[1] == 'E' || cp[1] == 'e'){
+ *dp++ = ESCAPE;
+ cp += 2;
+ continue;
+ }/* End IF */
+ }/* End IF */
+
+ *dp++ = *cp++;
+
+ }/* End While */
+
+ if(temp_buffer){
+ free(temp_buffer);
+ temp_buffer = NULL;
+ }/* End IF */
+
+ return(1);
+
+}/* End Routine */
+
+/* SCAN_TERMCAP - Attempt to find a terminal description in the termcap file
+ *
+ * Function:
+ *
+ * This routine scans the termcap file for an entry which matches our
+ * terminal, and returns non-zero if it finds it.
+ */
+scan_termcap(fd,temp_buffer,term_name)
+FILE *fd;
+char *temp_buffer;
+char *term_name;
+{
+register char *cp;
+char *temp_name;
+char status;
+
+/*
+ * Loop reading terminal descriptions into the buffer
+ */
+ while(1){
+ cp = temp_buffer;
+ bzero(cp,TERMCAP_BUFFER_SIZE);
+ if(fgets(cp,TERMCAP_BUFFER_SIZE,fd) == NULL) return(0);
+ if(*cp == '#') continue;
+/*
+ * Ok, we have the start of an entry. Check for continuation.
+ */
+ while(cp[0]){
+ if(cp[0] == '\n'){
+ cp[1] = '\0';
+ break;
+ }/* End IF */
+ if(cp[0] == '\\' && cp[1] == '\n'){
+ if(fgets(cp,TERMCAP_BUFFER_SIZE-(cp-temp_buffer),fd) == NULL){
+ cp[0] = '\n'; cp[1] = '\0';
+ }/* End IF */
+ }/* End IF */
+ cp++;
+ }/* End While */
+
+ cp = temp_name = temp_buffer;
+
+ while(*cp){
+ if(*cp == '|'){
+ *cp = '\0';
+ status = strcmp(temp_name,term_name);
+ *cp = '|';
+ if(status == 0) return(1);
+ cp++;
+ temp_name = cp;
+ continue;
+ }/* End IF */
+ if(*cp == ':'){
+ *cp = '\0';
+ status = strcmp(temp_name,term_name);
+ *cp = ':';
+ if(status == 0) return(1);
+ break;
+ }/* End IF */
+ cp++;
+ }/* End While */
+
+ }/* End While */
+
+ return(0);
+
+}/* End Routine */
+
+
+
+/* TGETNUM - Version of TGETNUM for Non-UNIX systems
+ *
+ * Function:
+ *
+ * This routine is used on non-UNIX systems to return a specific
+ * termcap number from the termcap description of our terminal.
+ */
+int
+tgetnum(num_name)
+char *num_name;
+{
+register char *cp;
+int temp;
+char minus_seen = 0;
+
+ cp = current_termcap_description;
+ if(strlen(num_name) != 2){
+ fprintf(stderr,"TGETNUM: capability name %s should be 2 bytes long\n",
+ num_name);
+ exit(1);
+ }/* End IF */
+
+ for(cp = current_termcap_description; *cp != NULL; cp++){
+ if(cp[0] != ':') continue;
+ if(cp[1] != num_name[0]) continue;
+ if(cp[2] != num_name[1]) continue;
+
+ cp += 3;
+ if(*cp == '#') cp++;
+ temp = 0;
+ if(*cp == '-'){
+ minus_seen = 1;
+ cp++;
+ }/* End IF */
+
+ while(isdigit(*cp)){
+ temp = temp * 10 + *cp++ - '0';
+ }/* End While */
+
+ if(minus_seen) temp = 0 - temp;
+ return(temp);
+
+ }/* End FOR */
+
+ return(0);
+
+}/* End Routine */
+
+/* TGETSTR - Version of TGETSTR for Non-UNIX systems
+ *
+ * Function:
+ *
+ * This routine is used on non-UNIX systems to return a specific
+ * termcap string from the termcap description of our terminal.
+ */
+char *
+tgetstr(str_name,buffer_ptr)
+char *str_name;
+char **buffer_ptr;
+{
+register char *cp;
+register char *dp;
+char *buffer;
+
+ buffer = *buffer_ptr;
+ cp = current_termcap_description;
+ dp = buffer;
+ if(strlen(str_name) != 2){
+ fprintf(stderr,"TGETSTR: capability name %s should be 2 bytes long\n",
+ str_name);
+ exit(1);
+ }/* End IF */
+
+ for(cp = current_termcap_description; *cp != NULL; cp++){
+ if(cp[0] != ':') continue;
+ if(cp[1] != str_name[0]) continue;
+ if(cp[2] != str_name[1]) continue;
+
+ cp += 3;
+ if(*cp == '=') cp += 1;
+ while(*cp != ':' && *cp != NULL){
+ *dp++ = *cp++;
+ }/* End While */
+ *dp++ = '\0';
+
+ return(buffer);
+
+ }/* End FOR */
+
+ return(NULL);
+}
+
+/* END OF VMS CONDITIONAL CODE */
+
+#endif
diff --git a/tecundo.c b/tecundo.c
new file mode 100644
index 0000000..e0f9422
--- /dev/null
+++ b/tecundo.c
@@ -0,0 +1,504 @@
+char *tecundo_c_version = "tecundo.c: $Revision: 1.1 $";
+
+/*
+ * $Date: 2007/12/10 21:59:21 $
+ * $Source: /cvsroot/videoteco/videoteco/tecundo.c,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecundo.c
+ * Subroutines to implement the undo capabilities of the parser
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#include "teco.h"
+#include "tecparse.h"
+
+ extern struct search_buff search_string;
+ extern char user_message[];
+ extern char exit_flag;
+ extern char immediate_execute_flag;
+ extern int last_search_pos1,last_search_pos2;
+ extern int last_search_status;
+
+ extern struct buff_header *curbuf,*buffer_headers;
+ extern struct buff_header *qregister_push_down_list;
+ extern struct tags *current_tags;
+ void tecundo_list(struct undo_token *);
+
+
+
+/* PARSER_UNDO - Execute an UNDO command
+ *
+ * Function:
+ *
+ * This routine is called with an undo token to reverse some changes
+ * to the program state.
+ */
+int
+parser_undo(ut)
+register struct undo_token *ut;
+{
+char tmp_message[LINE_BUFFER_SIZE];
+register struct cmd_token *ct;
+
+ PREAMBLE();
+
+ switch(ut->opcode){
+/*
+ * CHANGEDOT is used to undo a movement of dot. iarg1 contains the buffer
+ * position we want to go back to.
+ */
+ case UNDO_C_CHANGEDOT:
+ curbuf->dot = ut->iarg1;
+ break;
+/*
+ * DELETE is used to undo the addition of characters to the buffer. iarg1
+ * contains the buffer position to begin deleting at, iarg2 contains the
+ * number of characters that should be deleted.
+ */
+ case UNDO_C_DELETE:
+ if(ut->iarg2 > 0){
+ buff_delete((struct buff_header *)ut->carg1,ut->iarg1,
+ ut->iarg2);
+ }/* End IF */
+ break;
+/*
+ * INSERT is used to undo the deletion of characters from the buffer.
+ * iarg1 contains the position in the buffer of the deletion. iarg2
+ * contains a count of the number of bytes which were deleted. carg1
+ * is a pointer to a tec_alloc'ed buffer which holds the deleted bytes.
+ */
+ case UNDO_C_INSERT:
+ buff_insert(curbuf,ut->iarg1,ut->carg1,ut->iarg2);
+ break;
+/*
+ * UQREGISTER is used to undo the loading of an integer value into a
+ * Q register. iarg1 is the Q register name, iarg2 is the old value.
+ */
+ case UNDO_C_UQREGISTER:
+ {/* Local Block */
+ register struct buff_header *hbp;
+
+ hbp = buff_qfind(ut->iarg1,0);
+ if(hbp == NULL){
+ sprintf(tmp_message,"?Cannot find Q-register <%c>",
+ ut->iarg1);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End IF */
+
+ hbp->ivalue = ut->iarg2;
+ break;
+ }/* End Local Block */
+/*
+ * MEMFREE allows us to free up memory allocated to a command token or an
+ * undo token.
+ */
+ case UNDO_C_MEMFREE:
+ tec_release(TYPE_C_CBUFF,ut->carg1);
+ ut->carg1 = NULL;
+ return(SUCCESS);
+/*
+ * SHORTEN_STRING allows us to handle rubouts etc while a string was being
+ * built.
+ */
+ case UNDO_C_SHORTEN_STRING:
+ {/* Local Block */
+ register int i;
+ register char *cp;
+/*
+ * IARG1 holds the length we want to shorten the string to. If the string is
+ * already shorter than that, we leave it alone of course. Note that we use
+ * IARG2 to hold the address of the string.
+ */
+ i = ut->iarg1;
+ cp = ut->carg1;
+ while(i--){
+ if(*cp) cp++;
+ }/* End While */
+ *cp = '\0';
+ return(SUCCESS);
+ }/* End Local Block */
+/*
+ * SET_SEARCH_STRING sets the default search string
+ */
+ case UNDO_C_SET_SEARCH_STRING:
+ return(SUCCESS);
+
+/*
+ * CHANGEBUFF changes the current buffer. We pass a flag to buff_openbuffnum
+ * so that if we return to buffer zero, it doesn't build a new map.
+ */
+ case UNDO_C_CHANGEBUFF:
+
+ buff_openbuffnum(ut->iarg1,0);
+ return(SUCCESS);
+
+/*
+ * MACRO undoes all the changes a macro made
+ */
+ case UNDO_C_MACRO:
+ ct = (struct cmd_token *)ut->carg1;
+ while(ct->next_token) ct = ct->next_token;
+ while(ct){
+/*
+ * If this command token has an undo list associated with it, call the
+ * routine which will undo the entire undo list under this token.
+ */
+ if(ct->undo_list){
+ tecundo_list(ct->undo_list);
+ ct->undo_list = NULL;
+ }/* End IF */
+/*
+ * If the command token is not at the head of the list, back up one so
+ * as to keep it's address while we free up this command token.
+ */
+ if(ct->prev_token){
+ ct = ct->prev_token;
+ free_cmd_token(ct->next_token);
+ ct->next_token = NULL;
+ }/* End IF */
+/*
+ * Else, it IS the head of the list and we can simply free it up and break
+ * out of the loop.
+ */
+ else {
+ free_cmd_token(ct);
+ ut->carg1 = NULL;
+ break;
+ }/* End Else */
+ }/* End While */
+
+ return(SUCCESS);
+
+/*
+ * SHORTEN_MESSAGE handles rubouts in ^A messages
+ */
+ case UNDO_C_SHORTEN_MESSAGE:
+ {/* Local Block */
+ register int i;
+ i = strlen(user_message);
+ if(i > 0) user_message[i-1] = '\0';
+ return(SUCCESS);
+ }/* End Local Block */
+
+/*
+ * BULK_INSERT is used to undo a deleted region
+ */
+ case UNDO_C_BULK_INSERT:
+ buff_bulk_insert(curbuf,
+ ut->iarg1,ut->iarg2,(struct buff_line *)ut->carg1);
+ return(SUCCESS);
+/*
+ * SET_IMMEDIATE_MODE resets the immediate execute state
+ */
+ case UNDO_C_SET_IMMEDIATE_MODE:
+ immediate_execute_flag = ut->iarg1;
+ return(SUCCESS);
+
+/*
+ * PUSH is used to re-push a Q register onto the Q register push down
+ * list (stack).
+ */
+ case UNDO_C_PUSH:
+ push_qregister(ut->iarg1);
+ return(SUCCESS);
+/*
+ * POP is used to undo a Q register push. It just takes an entry off of
+ * the Q register pushdown list and destroys it.
+ */
+ case UNDO_C_POP:
+ {/* Local Block */
+ register struct buff_header *hbp;
+
+ hbp = qregister_push_down_list;
+ if(hbp){
+ qregister_push_down_list = hbp->next_header;
+/*
+ * Place it on the main buffer list so that buff_destroy can find it
+ */
+ hbp->next_header = buffer_headers;
+ buffer_headers = hbp;
+ buff_destroy(hbp);
+ }/* End IF */
+
+ return(SUCCESS);
+
+ }/* End Local Block */
+
+/*
+ * MODIFIED is used to flag a change in the state of the current edit buffers
+ * modified status. We undo it by clearing the modified flag and clearing the
+ * indication in the buffer status line display.
+ */
+ case UNDO_C_MODIFIED:
+ ((struct buff_header *)(ut->carg1))->ismodified = NO;
+ if(screen_label_line((struct buff_header *)ut->carg1,
+ "",LABEL_C_MODIFIED) == FAIL){
+ return(FAIL);
+ }/* End IF */
+ return(SUCCESS);
+/*
+ * This function undoes the creation of a buffer by the EB command
+ */
+ case UNDO_C_CLOSEBUFF:
+ buff_destroy((struct buff_header *)ut->carg1);
+ break;
+/*
+ * This function undoes a window switch
+ */
+ case UNDO_C_WINDOW_SWITCH:
+ window_switch(ut->iarg1);
+ break;
+/*
+ * This function undoes a window split
+ */
+ case UNDO_C_WINDOW_SPLIT:
+ window_switch(ut->iarg1);
+ screen_delete_window((struct window *)ut->carg1);
+ break;
+/*
+ * This function reopens a buffer which was closed with EF
+ */
+ case UNDO_C_REOPENBUFF:
+ buff_reopenbuff((struct buff_header *)ut->carg1);
+ break;
+/*
+ * This function replaces the buffer name which was changed by a rename
+ * operation.
+ */
+ case UNDO_C_RENAME_BUFFER:
+ tec_release(TYPE_C_CBUFF,curbuf->name);
+ curbuf->name = ut->carg1;
+ break;
+
+/*
+ * This function replaces arguments which may have been overwritten during
+ * a loop.
+ */
+ case UNDO_C_PRESERVEARGS:
+ ct = (struct cmd_token *)ut->carg1;
+ ct->ctx.iarg1 = ut->iarg1;
+ ct->ctx.iarg2 = ut->iarg2;
+ break;
+
+/*
+ * This function replaces runtime options changed by the EJ command
+ */
+ case UNDO_C_SETOPTIONS:
+ cmd_setoptions(ut->iarg1,ut->iarg2,NULL);
+ break;
+/*
+ * This function restores some globals used by the searching system
+ */
+ case UNDO_C_SET_SEARCH_GLOBALS:
+ last_search_pos1 = ut->iarg1;
+ last_search_pos2 = ut->iarg2;
+ last_search_status = (int)ut->carg1;
+ break;
+/*
+ * This function puts the previous tags file back
+ */
+ case UNDO_C_LOAD_TAGS:
+ tag_free_struct(current_tags);
+ current_tags = (struct tags *)ut->carg1;
+ break;
+
+/*
+ * This function puts the previous tag entry selection back
+ */
+ case UNDO_C_SELECT_TAGS:
+ current_tags->current_entry = (struct tagent *)ut->carg1;
+ break;
+
+ case UNDO_C_SET_EXIT_FLAG:
+ exit_flag = 0;
+ break;
+
+/*
+ * Here when we get an undo code we don't know about. This is an internal
+ * error to teco!
+ */
+ default:
+ sprintf(tmp_message,"?Unknown undo opcode %d",ut->opcode);
+ error_message(tmp_message);
+ return(FAIL);
+ }/* End Switch */
+
+ return(SUCCESS);
+
+}/* End Routine */
+
+
+
+/* ALLOCATE_UNDO_TOKEN - Allocate an undo token structure
+ *
+ * Function:
+ *
+ * This routine is called to allocate an undo token structure. If there
+ * is one on the free list, it is used, otherwise we allocate one. UNDO
+ * tokens are used so that we can back out of operations which change
+ * some state other than that of the parser. The normal case is that we
+ * make some change to the edit buffer, and want to be able to back out
+ * of it if we want.
+ */
+struct undo_token *
+allocate_undo_token(ct)
+register struct cmd_token *ct;
+{
+register struct undo_token *ut;
+
+ PREAMBLE();
+
+ ut = (struct undo_token *)tec_alloc(TYPE_C_UNDO,sizeof(struct undo_token));
+ if(ut == NULL) return(NULL);
+
+ ut->opcode = UNDO_C_UNUSED;
+ ut->iarg1 = 0;
+ ut->iarg2 = 0;
+ ut->carg1 = NULL;
+ ut->next_token = NULL;
+
+ if(ct != NULL){
+ ut->next_token = ct->undo_list;
+ ct->undo_list = ut;
+ }/* End IF */
+
+ return(ut);
+
+}/* End Routine */
+
+
+
+/* FREE_UNDO_TOKEN - Routine to place an undo token on the free list
+ *
+ * Function:
+ *
+ * This routine is called with the address of the undo token
+ * to be placed on the free list.
+ */
+void
+free_undo_token(ut)
+register struct undo_token *ut;
+{
+
+ PREAMBLE();
+
+ tec_release(TYPE_C_UNDO,(char *)ut);
+
+}/* End Routine */
+
+
+
+/* TECUNDO_LIST - Cause a list of undo structures to be backed out
+ *
+ * Function:
+ *
+ * This routine is called with the head of a list of undo tokens that
+ * need to be undone. They are placed on the list such that the head
+ * of the list is the first that needs to be undone.
+ */
+void
+tecundo_list(nut)
+register struct undo_token *nut;
+{
+register struct undo_token *ut;
+
+ PREAMBLE();
+
+ while((ut = nut)){
+
+/*
+ * Remember what the next token address is and unchain it from the current
+ * undo token.
+ */
+ nut = ut->next_token;
+ ut->next_token = NULL;
+/*
+ * Now call the undo routine to back out the changes to the edit buffer that
+ * this token calls for. Then place the token back on the free list.
+ */
+ parser_undo(ut);
+ free_undo_token(ut);
+
+ }/* End While */
+
+}/* End Routine */
+
+
+
+/* TECUNDO_CLEANUP - Place the list of undo tokens back on the free list
+ *
+ * Function:
+ *
+ * This routine is called with the head of a list of undo tokens. It
+ * places them back on the undo free list while releasing any resources
+ * they may have allocated. For example, the MEMFREE token has to free
+ * the memory the block points to.
+ */
+void
+tecundo_cleanup(tut)
+register struct undo_token *tut;
+{
+register struct undo_token *ut;
+
+ PREAMBLE();
+
+ while((ut = tut)){
+ tut = tut->next_token;
+ switch(ut->opcode){
+ case UNDO_C_MEMFREE:
+ if(ut->carg1) tec_release(TYPE_C_CBUFF,ut->carg1);
+ ut->carg1 = NULL;
+ free_undo_token(ut);
+ break;
+ case UNDO_C_MACRO:
+ if(ut->carg1){
+ parser_cleanup_ctlist((struct cmd_token *)ut->carg1);
+ }/* End IF */
+ ut->carg1 = NULL;
+ free_undo_token(ut);
+ break;
+ case UNDO_C_BULK_INSERT:
+ buff_free_line_buffer_list((struct buff_line *)ut->carg1);
+ ut->carg1 = NULL;
+ free_undo_token(ut);
+ break;
+ case UNDO_C_REOPENBUFF:
+ {/* Local Block */
+ register struct buff_header *hbp;
+ hbp = (struct buff_header *)ut->carg1;
+ hbp->next_header = buffer_headers;
+ buffer_headers = hbp;
+ buff_destroy(hbp);
+ }/* End Local Block */
+ free_undo_token(ut);
+ break;
+ case UNDO_C_RENAME_BUFFER:
+ tec_release(TYPE_C_CBUFF,ut->carg1);
+ free_undo_token(ut);
+ break;
+ case UNDO_C_LOAD_TAGS:
+ tag_free_struct((struct tags *)ut->carg1);
+ break;
+ default:
+ free_undo_token(ut);
+ }/* End Switch */
+ }/* End While */
+}/* End Routine */
diff --git a/tecvms.h b/tecvms.h
new file mode 100644
index 0000000..fb1cb6c
--- /dev/null
+++ b/tecvms.h
@@ -0,0 +1,51 @@
+/*
+ * $Date: 2007/12/10 21:59:21 $
+ * $Source: /cvsroot/videoteco/videoteco/tecvms.h,v $
+ * $Revision: 1.1 $
+ * $Locker: $
+ */
+
+/* tecvms.h
+ * Include file for VMS build
+ * %W% (PC) %G%
+ *
+ * COPYRIGHT (c) 1985-2003 BY
+ * PAUL CANTRELL & J. M. NISHINAGA
+ * SUDBURY, MA 01776
+ * ALL RIGHTS RESERVED
+ *
+ * This software is furnished in it's current state free of charge.
+ * The authors reserve all rights to the software. Further
+ * distribution of the software is not authorized. Modifications to
+ * the software may be made locally, but shall not be distributed
+ * without the consent of the authors. This software or any other
+ * copies thereof, may not be provided or otherwise made available
+ * to anyone without express permission of the authors. Title to and
+ * ownership of this software remains with the authors.
+ *
+ */
+
+#define VMS
+
+#include file
+#include errno
+#include stdio
+#include ctype
+#include perror
+#include signal
+#include jpidef
+#include ssdef
+#include iodef
+#include stsdef
+#include descrip
+#include ttdef
+
+#ifndef LIB$K_CLI_LOCAL_SYM
+#define LIB$K_CLI_LOCAL_SYM 1
+#endif
+#ifndef CLI$M_NOWAIT
+#define CLI$M_NOWAIT (1<<0)
+#endif
+#ifndef CLI$M_NOTIFY
+#define CLI$M_NOTIFY (1<<4)
+#endif