TEACL is a fork of TECOC created for the purpose of describing diffs in a document being collaboratively edited
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2130 lines
61 KiB

/*****************************************************************************
zosx.c
System dependent code for Mac OS X
This is based on zunix.c, with changes (see comments "TAA")
for enhanced functionality.
*****************************************************************************/
/*
* Define standard functions.
*/
#include <sys/types.h> /* needed before sys/param.h is included */
#include <errno.h> /* define errno */
#include <signal.h> /* to catch ^C and ^Z signals */
#include <stdio.h> /* define stdin */
#include <string.h> /* strncpy(), strlen(), etc. */
#include <sys/time.h> /* define tm struct */
#include <dirent.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pwd.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <glob.h> /* Rewritten 05/04 by TAA to use glob function */
#if CURSES
#include <curses.h> /* has to come before zport.h */
#endif
#include "zport.h" /* define portability identifiers */
#include "tecoc.h" /* define general identifiers */
#include "chmacs.h" /* define character processing macros */
#include "clpars.h" /* command-line parsing macro */
#include "dchars.h" /* define identifiers for characters */
#include "deferr.h" /* define identifiers for error messages */
#include "defext.h" /* define external global variables */
#include "dscren.h" /* define identifiers for screen i/o */
#if CURSES
/*
* The ACS_xxx symbols are defined in the SunOS version of curses, but not
* in either the BSD or X/OPEN Ultrix versions. Who knows where else they
* are defined? Isn't portability wonderful?
*
* Note that this code implicitly assumes a VT100-compatible terminal.
* Tough. (Jerry Leichter, June 1991)
*/
#ifndef ACS_PLMINUS
#define ACS_PLMINUS (A_ALTCHARSET | 0x67)
#define ACS_LLCORNER (A_ALTCHARSET | 0x60)
#define ACS_LRCORNER (A_ALTCHARSET | 0x6A)
#define ACS_LARROW (A_ALTCHARSET | 0x7C) /* Really not-equal */
#define ACS_HLINE (A_ALTCHARSET | 0x71)
#define ACS_BTEE (A_ALTCHARSET | 0x76)
#define ACS_DIAMOND (A_ALTCHARSET | 0x60)
#define ACS_RTEE (A_ALTCHARSET | 0x75)
#endif
WINDOW *curwin;
int ScopeFlg = 0; /* for ZDspCh and ZDspBuf */
int olddot;
#define DOT (GapBeg - EBfBeg)
#define ISSCW (ScopeFlg)
unsigned char *p_scope_start;
int scope_start;
int winline;
int cursoratbottomline;
int cursorattopline;
static int colsave;
int dotx,doty; /* position of cursor in scope */
int dotx1, doty1; /* pos. after printing character in scope */
int skiprefresh = 0;
int cmdx,cmdy; /* position of cursor in command buffer */
int scope_start, scope_end;
unsigned char *pscope_start;
#define CT ((EzFlag & EZ_VT100GRAPHICS) ? ACS_PLMINUS : '^')
#define LB ((EzFlag & EZ_VT100GRAPHICS) ? ACS_LLCORNER : '[')
#define RB ((EzFlag & EZ_VT100GRAPHICS) ? ACS_LRCORNER : ']')
#else /* else (not CURSES) ----------------------*/
#ifdef LINUX
static struct termios out, cur; /* terminal characteristics buffers */
#else
#include <sgtty.h> /* define CBREAK, ECHO, TIOCGETP, etc. */
static struct sgttyb out, cur; /* terminal characteristics buffers */
#endif
static char tbuf[1024]; /* store TERMCAP entry here */
static char tarea[1024]; /* store decoded TERMCAP stuff here */
static char *ce; /* TERMCAP sequence: clear to end-of-line */
static char *up; /* TERMCAP sequence: cursor up */
static char *so; /* TERMCAP sequence: reverse video on */
static char *se; /* TERMCAP sequence: reverse video off */
int tputs(); /* send termcap string to a given function */
int tgetent(); /* load a terminal capability buffer */
char *tgetstr(); /* get str value of a terminal capability */
#endif
static BOOLEAN tty_set = FALSE; /* Has the terminal been set? */
static struct termios out, cur; /* terminal characteristics buffers */
static char tbuf[1024]; /* store TERMCAP entry here */
// static char tarea[1024]; /* store decoded TERMCAP stuff here */
// static char *ce; /* TERMCAP sequence: clear to end-of-line */
// static char *up; /* TERMCAP sequence: cursor up */
// static char *so; /* TERMCAP sequence: reverse video on */
// static char *se; /* TERMCAP sequence: reverse video off */
int tputs(); /* send termcap string to a given function */
int tgetent(); /* load a terminal capability buffer */
char *tgetstr(); /* get str value of a terminal capability */
static int vernum(); /* see bottom of this file */
//extern int sys_nerr; /* number of system error messages */
static int SupGotCtC = 0;
static glob_t pglob;
static int globindex = 0;
/*****************************************************************************
IFiles holds the file data blocks for input files. There are three
static input streams: the primary input stream, the secondary input stream,
and the input stream used by the EQq command. To access these three files,
identifiers defined in file tecoc.h are used to index into this array.
Other elements of this array are used to access input files for the EI
command.
*****************************************************************************/
FILE *IFiles[NIFDBS];
/* TAA Mod 5/04 -- keep track of last character being CR so we will
* have CRLF -> CRLF instead of CRLFLF in case input file is DOS
* format. */
static int IFisCR[NIFDBS] = {0};
/*****************************************************************************
OFiles holds the file data blocks for the output files. There are
three output streams: the primary output stream, the secondary output
stream and the output stream used by the E%q command. The array is indexed
using identifiers defined in file tecoc.h.
*****************************************************************************/
static struct {
char OFNam[FILENAME_MAX]; /* output file name */
char OTNam[FILENAME_MAX]; /* temporary output file name */
FILE *OStrem; /* stream */
BOOLEAN forBackup; /* TAA Mod */
} OFiles[NOFDBS];
/*****************************************************************************
ZErMsg()
This function displays error message from the operating system on
the terminal screen. The error message text is retrieved from the operating
system and imbedded in a TECO-style message with the SYS mnemonic.
*****************************************************************************/
static VVOID ZErMsg()
{
if (errno < sys_nerr)
ErrStr(ERR_SYS, sys_errlist[errno]);
}
/*****************************************************************************
ZAlloc()
This function allocates memory. The single argument is the number of
bytes to allocate. TECO-C uses the ZFree and ZRaloc functions to de-allocate
and re-allocate, respectively, the memory allocated by this function.
*****************************************************************************/
voidptr ZAlloc(MemSize) /* allocate memory */
SIZE_T MemSize;
{
return (voidptr)malloc(MemSize);
}
/*****************************************************************************
ZBell()
Thus function rings the terminal bell. For most platforms, this
means just writing a bell character (control-G) to the terminal. Under
MS-DOS, ringing the bell this way produces a yucky sound, so for MS-DOS
this function controls the signal generator directly.
*****************************************************************************/
VVOID ZBell(VVOID)
{
#if CURSES
if (EzFlag & EZ_AUDIO_BEEP)
beep(); /* audio beep */
else
flash(); /* visible flash */
#else
ZDspCh('\7');
#endif
}
/*****************************************************************************
ZChIn()
This function inputs a single character from the terminal.
1. the character is not echoed on the terminal
2. ^C calls an interrupt routine. Note that this must be
implemented so that a ^C will cancel a current output via
ZDspBf. The ^C must be a true interrupt.
3. type-ahead is always nice
4. The character must be returned immediately: no fooling
around waiting for a carriage-return before returning.
5. If the NoWait argument is TRUE, don't wait.
6. When the user hits the RETURN key, TECO is supposed to see
a carriage return and then a line feed. The function must
deal with this by returning a carriage return to the caller
and then "remembering" to send a line feed on the next call.
7. handle ET_BKSP_IS_DEL flag
*****************************************************************************/
DEFAULT ZChIn(NoWait) /* input a character from terminal */
BOOLEAN NoWait; /* return immediately? */
{
char Charac;
static BOOLEAN LastLF = FALSE;
if (LastLF) {
LastLF = FALSE;
return (DEFAULT)LINEFD;
}
#if CURSES
if ((Charac = getch()) == ERR) {
#else
if (read(fileno(stdin), &Charac, 1) != 1) {
#endif
if (GotCtC || SupGotCtC)
return (DEFAULT)CTRL_C;
if (!GotCtC) {
ZErMsg();
ErrMsg(ERR_URC);
ZClnUp();
exit(EXIT_FAILURE);
}
}
GotCtC = FALSE;
if (Charac == CRETRN) {
LastLF = TRUE;
return (DEFAULT)CRETRN;
}
if (EtFlag & ET_BKSP_IS_DEL) {
if (Charac == DELETE) {
Charac = BAKSPC;
} else if (Charac == BAKSPC) {
Charac = DELETE;
}
}
return (DEFAULT)Charac;
}
/*****************************************************************************
ZClnEG()
This function executes a :EG command. The :EG commands are used to
get access to operating system functions. The minimum set of functions is
:EGINI$ gets, sets or clears the initialization file name
:EGMEM$ gets, sets or clears the file name memory
:EGLIB$ gets, sets or clears the macro library directory
:EGVTE$ gets, sets or clears the video macro file name
although more functions may be defined.
The :EG command was designed to access logical names, which are supported
by DEC's VAX/VMS and RSX operating systems. Logical names are a useful way
to specify, for example, a directory that a program is to find a set of files
in. A user can define logical names to set up a program's environment.
Programs can read, create or delete logical names.
Logical names are stored separately from program memory, so if a program sets
a logical name and then exits, the logical name still exists. TECO on a VAX
uses a logical name to store the name of the file being edited. If the user
starts TECO without specifying a file name, TECO looks for the logical name
and, if it exists, uses the value of the logical name as a filename. This
allows users to edit a file several times in a session without having to
type the file name each time they start TECO (except the first time).
Unix doesn't have logical names. The closest thing is environment variables,
which are passed to a program when it is started. A user can define
environment variables, and a program can get the values with a getenv call.
A program can even add to it's private list of environment variables, but
the list disappears when the program exits. So environment variables don't
fill the needs of the :EG command.
Environment variables are, however, natural for some of what :EG is really
used for. Users rarely need the :EG command, even in macros. The main use
of :EG is in the command-line-parsing macro (in CLPARS.TES, CLPARS.TEC and
CLPARS.H). That macro can handle a partially-implemented :EG command (it
tests the success/failure flag returned by :EG).
So I partially implemented :EG for Unix. :EG can read the "INI", "LIB" and
"VTE" values, but can't set or clear them. The "MEM" value is supported
using a file (ugh) to save the name of the last-file-edited. The file is
stored in /tmp so it gets deleted when the system boots.
*****************************************************************************/
LONG ZClnEG( /* execute special :EG command */
DEFAULT EGWhat, /* what to get/set/clear: MEM, LIB, etc. */
DEFAULT EGOper, /* operation: get, set or clear */
charptr TxtPtr) /* if setting, value to set */
{
char *cp=NULL; /* environment variable name */
// char buf[100]; /* enough for envname + 80 char filename */
LONG retval; /* -1L on success, 0L on failure */
DBGFEN(2,"ZClnEG",NULL);
DBGFEX(2,DbgFNm,"0");
switch (EGWhat) {
case EG_INI: cp = "TEC_INIT"; break;
case EG_LIB: cp = "TEC_LIBRARY"; break;
case EG_MEM: cp = "TEC_MEMORY"; break;
#if VIDEO
case EG_VTE: cp = "TEC_VTEDIT"; break;
#endif
default: return 0L;
}
if (EGOper == GET_VAL) {
if ((cp = getenv(cp)) == NULL) {
retval = 0L; /* return failure */
} else {
retval = -1L; /* success, copy to FBf */
strcpy((char*)FBfBeg, cp);
FBfPtr = FBfBeg + strlen(cp);
}
} else {
/* Cannot set value */
// strcpy(buf, cp); /* build NAME= */
// strcat(buf, "=");
// if (EGOper == SET_VAL) { /* concatenate new value */
// strcat(buf, (char *)TxtPtr);
// }
// retval = (putenv(buf) != 0) /* if putenv() failed */
// ? 0L /* then return failure */
// : -1L; /* else return success */
retval = 0L;
}
return retval;
}
/*****************************************************************************
See the definition of MEMMOVE in ZPORT.H for a description of this
function.
*****************************************************************************/
VVOID ZCpyBl(Destin, Source, Length)
charptr Destin;
charptr Source;
SIZE_T Length;
{
if (Source < Destin) {
Source += Length;
Destin += Length;
while (Length-- > 0) {
*--Destin = *--Source;
}
} else {
while (Length-- > 0) {
*Destin++ = *Source++;
}
}
}
#if DEBUGGING
ULONG Zcp2ul(cp) /* convert charptr to ULONG */
voidptr cp;
{
return (ULONG)(cp);
}
#endif
/*****************************************************************************
ZClnUp()
This function cleans up in preparation for terminating TECO-C.
*****************************************************************************/
VVOID ZClnUp(VVOID) /* cleanup for TECO-C abort */
{
DBGFEN(3,"ZClnUp","closing terminal channels and exiting");
#if !CURSES
if (tty_set == TRUE)
#ifdef LINUX
tcsetattr(0, TCSANOW, &out);
#else
ioctl(0, TIOCSETP, &out);
#endif
#endif
}
/*****************************************************************************
ZDoCmd()
This function terminates TECO and feeds a command line to the
command line interpreter. The command to be executed is passed to this
function.
*****************************************************************************/
VVOID ZDoCmd(charptr GBfBeg, charptr GBfPtr) /* die and pass command to OS */
{
char buf[128+1];
char *space_p;
DBGFEN(1,"ZDoCmd",NULL);
/*
* 1. Terminate buf[] and command line in GBf
* 2. make local copy since GBf will be free'd in ZClnUp()
* 3. separate program name from arguments, if any
* 4. Call ZClnUp to free up everything
* 5. Execute the command line, with optional arguments. If we know where
* the command processor is, use it so we can execute .BAT batch files
* 6. we shouldn't be here, exit
*/
buf[128] = *GBfPtr = '\0';
(void)strncpy(buf, (char *)GBfBeg, 128);
if ((space_p = strchr(buf,' ')) != NULL) {
*space_p++ = '\0';
}
ZClnUp();
execlp ("/bin/sh", "sh", "-c", buf, (space_p) ? space_p : NULL, NULL);
/* we should never reach this statement */
(void)perror ("");
ZExit (EXIT_SUCCESS);
}
#if CURSES
static void
zaddch(c,wecho)
char c;
int wecho; /* if set - also do refresh */
{
static int retflag = 0;
int needrefresh = 1;
int y,x;
if (c=='\n') {
if (!retflag) /* previous char was not \r */
waddch(curwin,c);
else { /* this LF is part of a CRLF sequence */
waddch(curwin,'\n'); /* this may force a scroll */
waddch(curwin,'\r');
}
} else if (c=='\b') { /* backspace */
getyx(stdscr,y,x);
if (x==0 && y>0)
move(y-1,HtSize - 1);
else if (x>0)
move(y,x-1);
else /* x==0 and y==0 */
;
} else { /* c is neither a newline nor a backspace */
if (retflag)
waddch(curwin,'\r');
if (c!='\r') {
if (ISSCW) {
getyx(stdscr,y,x);
if (x < HtSize -1)
waddch(curwin,c);
} else
if (wecho) {
#ifdef ULTRIX
waddch(curwin,c);
#else
wechochar(curwin,c);
needrefresh = 0;
#endif
} else
waddch(curwin,c);
}
}
retflag = (c=='\r');
if (wecho && needrefresh)
refresh();
}
static void
specon()
{
if (SpcMrk)
wattron(stdscr,0400000L * (long) SpcMrk);
}
static void
specoff()
{
if (SpcMrk)
wattroff(stdscr,0400000L * (long) SpcMrk);
}
static void
gr_on()
{
wattron(stdscr,A_ALTCHARSET);
}
static void
gr_off()
{
wattroff(stdscr,A_ALTCHARSET);
}
static int
intabs(t)
int t;
{
return (t<0) ? -t : t;
}
static void
zaddch2(c)
char c;
{
static int retflag = 0;
if (SeeAll) {
if (c=='\n') {
if (EzFlag & EZ_VT100GRAPHICS) {
gr_on();
waddch(curwin, 'e');
gr_off();
} else {
waddch(curwin,CT);
waddch(curwin,'J');
}
if (!retflag) { /* previous character was not \r */
waddch(curwin,c);
} else { /* this LF is in a CRLF sequence */
waddch(curwin,'\n');
waddch(curwin,'\r');
}
} else { /* c is not a newline */
if (c=='\r') {
if (EzFlag & EZ_VT100GRAPHICS) {
gr_on();
waddch(curwin,'d'); gr_off();
} else {
waddch(curwin,CT);
waddch(curwin,'M');
}
} else if (c=='\t') {
if (EzFlag & EZ_VT100GRAPHICS) {
int y,x,y1,x1,cntr;
getyx(curwin,y,x);
waddch(curwin,'\t');
getyx(curwin,y1,x1);
move(y,x);
gr_on();
cntr = 0;
waddch(curwin,'b');
getyx(curwin,y,x);
while ((cntr < 8) && (y!=y1 || x!=x1) &&
(y<VtSize - winline - ScroLn)) {
waddch(curwin,'~');
cntr++;
getyx(curwin,y,x);
}
gr_off();
} else {
waddch(curwin,CT); waddch(curwin,'I');
}
} else if (c=='\f') {
if (EzFlag & EZ_VT100GRAPHICS) {
gr_on();
waddch(curwin,'c');
gr_off();
} else {
waddch(curwin,CT);
waddch(curwin,'L');
}
waddch(curwin,'\n');
waddch(curwin,'\r');
} else if (c==27) {
if (EzFlag & EZ_VT100GRAPHICS) {
gr_on();
waddch(curwin,'{');
gr_off();
} else {
waddch(curwin,CT);
waddch(curwin,'[');
}
} else if (c==VRTTAB) { /* Vertical tab */
if (EzFlag & EZ_VT100GRAPHICS) {
gr_on();
waddch(curwin,'i');
gr_off();
} else {
waddch(curwin,CT);
waddch(curwin,'K');
}
waddch(curwin,'\n');
waddch(curwin,'\r');
} else if (c=='\b') {
waddch(curwin,CT);
waddch(curwin,'H');
} else if (c & 0200) {
int i;
char a,b;
waddch(curwin,LB);
c = c & 0177;
i = c/16;
if (i==0)
a='8';
else if (i==1)
a='9';
else
a = i - 2 + 'A';
i = (c % 16);
b = (i > 9) ? (i - 10 + 'A') : (i + '0');
waddch(curwin,a);
waddch(curwin,b);
waddch(curwin,RB);
} else if (c < 32) {
waddch(curwin, CT);
waddch(curwin, c | 64);
} else
waddch(curwin,c);
}
retflag = (c==CRETRN);
} else { /* not in SEEALL mode */
c = c & 0177; /* dump 8th bit */
if (c=='\n') {
waddch(curwin,'\n');
waddch(curwin,'\r');
} else if (c=='\b') {
waddch(curwin,'^');
waddch(curwin,'H');
} else { /* c is neither a newline nor a backspace */
switch (c) {
case CRETRN:
if (EzFlag & EZ_INVCR) {
waddch(curwin, ' ');
} else if (EzFlag & EZ_VT100GRAPHICS) {
gr_on();
waddch(curwin, 'd');
gr_off();
} else if (EzFlag & EZ_ARROW) {
specon();
waddch(curwin, ACS_LARROW);
specoff();
} else {
specon();
waddch(curwin,'`');
specoff();
}
break;
case ESCAPE:
specon();
waddch(curwin,'$');
specoff();
break;
case VRTTAB:
case FORMFD:
if (EzFlag & EZ_VT100GRAPHICS) {
gr_on();
waddch(curwin,c);
gr_off();
} else {
waddch(curwin, c);
}
waddch(curwin, '\n');
waddch(curwin, '\r');
default:
waddch(curwin,c);
}
}
retflag = (c=='\r');
}
}
#endif
/*****************************************************************************
ZDspBf()
This function displays a buffer of a given length on the terminal
screen. On the VAX (and maybe other systems) doing any kind of output
involves a fair amount of overhead, regardless of the size of the buffer
being output. It is therefore better to make a single call to the operating
system's output function than to call the function for each and every
character. If such improvements do not apply to the system this program
is running on, then this function can simply call ZDspCh for every character
in the buffer.
*****************************************************************************/
VVOID ZDspBf(buffer, length) /* output a buffer to terminal */
charptr buffer;
SIZE_T length;
{
#if CURSES
int i;
int y,x;
for (i=0;i<length;i++) {
if (ISSCW) {
getyx(stdscr,y,x);
if (x>=(HtSize-1))
break;
}
if (!GotCtC)
zaddch(*(buffer+i),0);
else
break;
}
wrefresh(curwin);
#else
if (write(fileno(stdout), buffer, length) == -1) {
puts("Unable to write to terminal in function ZDspBf");
TAbort(EXIT_FAILURE);
}
#endif
}
/*****************************************************************************
ZDspCh()
This function outputs a single character to the terminal.
*****************************************************************************/
VVOID ZDspCh(Charac) /* output a character to terminal */
char Charac;
{
#if CURSES
zaddch(Charac,1);
#else
if (write(fileno(stdout), &Charac, 1) == -1) {
puts("Unable to write to terminal in function ZDspCh");
TAbort(EXIT_FAILURE);
}
#endif
}
/*****************************************************************************
ZExCtB()
This function implements the TECO ^B command, which returns the
current date encoded in the following way:
((year-1900)*16+month)*32+day
*****************************************************************************/
DEFAULT ZExCtB() /* return current date */
{
time_t clockt;
struct tm *time_of_day;
int tecodate;
DBGFEN(1,"ZExCtB","");
clockt=time(NULL);
time_of_day=localtime(&clockt);
tecodate = ((time_of_day->tm_year)*16+time_of_day->tm_mon+1)*32
+ time_of_day->tm_mday ;
DBGFEX(1,DbgFNm,"PushEx()");
return PushEx(tecodate, OPERAND);
}
/*****************************************************************************
ZExCtH()
This function implements the TECO ^H command, which returns the
current time encoded in the following way:
(seconds since midnight) / 2
*****************************************************************************/
DEFAULT ZExCtH() /* return current time */
{
time_t clockt;
struct tm *time_of_day;
int tecotime;
DBGFEN(1,"ZExCtH","");
clockt=time(NULL);
time_of_day=localtime(&clockt);
tecotime = time_of_day->tm_hour * 60 /* hours * 60 */;
tecotime += time_of_day->tm_min; /* minutes */
tecotime *= 30;
tecotime += time_of_day->tm_sec >> 1; /* seconds / 2 */
DBGFEX(1,DbgFNm,"PushEx()");
return PushEx(tecotime, OPERAND);
}
/*****************************************************************************
ZExeEJ()
This function executes an EJ command, which returns environment
characteristics. It returns:
-1EJ 1024 under VAX/VMS (4*256 = VAX, 0 = VMS in native mode)
1025 under Ultrix (4*256 = VAX, 1 = Ultrix)
25600 under Sun/SunOS (100*256 = Sun, 0 = SunOS)
25856 under MS-DOS (101*256 = IBM-PC, 0 = MS-DOS)
25857 under OS/2 (101*256 = IBM-PC, 1 = OS/2)
25858 under Linux (101*256 = IBM-PC, 2 = Linux)
0EJ process id on VAXen or Unix/Linux, 0 on anything else
1EJ 0 on all systems
2EJ UIC, in longword format (unlike TECO-11) on VAX/VMS,
0 on all other systems.
*****************************************************************************/
DEFAULT ZExeEJ() /* execute an EJ command */
{
DBGFEN(1,"ZExeEJ",NULL);
if (EStTop == EStBot) { /* if no numeric argument */
NArgmt = 0; /* default is 0EJ */
} else {
UMinus(); /* if -EJ, make it -1EJ */
if (GetNmA() == FAILURE) { /* get numeric argument */
DBGFEX(1,DbgFNm,"FAILURE");
return FAILURE;
}
}
if (NArgmt == -1) {
DBGFEX(1,DbgFNm,"PushEx(25858)");
return PushEx((LONG)25858, OPERAND); /* means "PC Linux" */
}
if (NArgmt == 0) {
DBGFEX(1,DbgFNm,"PushEx(getppid())");
return PushEx((LONG)getppid(), OPERAND);
}
DBGFEX(1,DbgFNm,"ExeNYI()");
return ExeNYI();
}
/*****************************************************************************
ZExit()
This function terminates TECO-C with a status value.
*****************************************************************************/
VVOID ZExit(estat) /* terminate TECO-C */
DEFAULT estat;
{
#if CURSES
nodelay(stdscr,FALSE); /* to avoid a bug in some System V.2 */
/* releases of curses */
endwin();
#endif
ZClnUp();
exit(estat);
}
/*****************************************************************************
ZFree()
This function frees memory previously allocated by the ZAlloc
function.
*****************************************************************************/
VVOID ZFree(pointer) /* free memory allocated by ZAlloc */
voidptr pointer;
{
free(pointer);
}
/*****************************************************************************
ZHelp()
This function accepts a help string and displays the corresponding
help text.
it should be control-C interrupt-able.
*****************************************************************************/
VVOID ZHelp(HlpBeg, HlpEnd, SysLib, Prompt)
charptr HlpBeg; /* first char of help request */
charptr HlpEnd; /* last character of help request */
BOOLEAN SysLib; /* use default HELP library? */
BOOLEAN Prompt; /* enter interactive help mode? */
{
(void)ExeNYI();
}
/*****************************************************************************
ZIClos()
This function closes the current input file. It must
1. if current input stream is not open, simply return
2. close the input file
3. set open indicator to FALSE
*****************************************************************************/
VVOID ZIClos(IfIndx) /* close input file */
DEFAULT IfIndx; /* index into IFiles array */
{
DBGFEN(2,"ZIClos",NULL);
if (IsOpnI[IfIndx]) { /* if it's open */
if (fclose(IFiles[IfIndx]) == EOF) {
ZErMsg();
ErrMsg(ERR_UCI);
ZExit(EXIT_FAILURE);
}
IsOpnI[IfIndx] = FALSE;
}
DBGFEX(2,DbgFNm,NULL);
}
/*****************************************************************************
ZOClDe()
This function closes and deletes the current output stream. It must
1. if no current output stream is defined, simply return
2. close the output stream
3. delete the file just closed
*****************************************************************************/
VVOID ZOClDe(OfIndx) /* close and delete output file */
DEFAULT OfIndx; /* index into OFiles array */
{
DBGFEN(2,"ZOClDe",NULL);
if (IsOpnO[OfIndx]) { /* if output stream is open */
if (fclose(OFiles[OfIndx].OStrem) == EOF) {
ZErMsg();
ErrStr(ERR_UCO, OFiles[OfIndx].OFNam);
ZExit(EXIT_FAILURE);
}
if (remove(OFiles[OfIndx].OFNam) != 0) {
ZErMsg();
ErrStr(ERR_UCD, OFiles[OfIndx].OFNam);
ZExit(EXIT_FAILURE);
}
IsOpnO[OfIndx] = FALSE;
}
DBGFEX(2,DbgFNm,NULL);
}
/*****************************************************************************
ZOClos()
This function closes the current output stream. It is only called
when an output stream is defined. It must
1. flush output to the stream, if neccessary
2. close the stream
3. set OFile to -1
*****************************************************************************/
VVOID ZOClos(OfIndx) /* close output file */
DEFAULT OfIndx; /* index into OFiles array */
{
int ver;
char TmpFsp[FILENAME_MAX];
char move_err[1024];
DBGFEN(2,"ZOClos",NULL);
if (!IsOpnO[OfIndx]) { /* if it's not open */
DBGFEX(2,DbgFNm,NULL);
return; /* we're done */
}
if (fclose(OFiles[OfIndx].OStrem) == EOF) { /* close it */
ZErMsg();
ErrMsg(ERR_UCO); /* unable to close */
ZExit(EXIT_FAILURE);
}
if (OFiles[OfIndx].OTNam[0] != '\0') { /* if temporary output file */
if (OFiles[OfIndx].forBackup) {
(void)strcpy(TmpFsp, OFiles[OfIndx].OTNam);/* copy to TmpFsp */
if ((EzFlag&EZ_NO_STRIP)==0) {
char *DotPtr = strchr(TmpFsp, '.'); /* find the "." */
if (DotPtr != NULL) { /* if "." exists */
*DotPtr = '\0'; /* make it null */
}
}
if (EzFlag&EZ_NO_VER) {
(void)strcat(TmpFsp, ".bak"); /* append ".bak" */
if (access(TmpFsp, 0) == 0) { /* old "x.bak"? */
#if DEBUGGING
sprintf(DbgSBf,
"deleting old version of %s\r\n",
TmpFsp);
DbgFMs(2,DbgFNm,DbgSBf);
#endif
if (remove(TmpFsp) != 0) { /* delete it */
ZErMsg();
ErrMsg(ERR_UCO);
ZClnUp();
exit(EXIT_FAILURE);
}
}
} else {
ver = vernum(TmpFsp);
if (ver==(-3)) {
puts("\nWARNING: Versioning disabled\n");
(void)strcat(TmpFsp, ".bak"); /* append ".bak" */
if (access(TmpFsp, 0) == 0) { /* old "x.bak"? */
#if DEBUGGING
sprintf(DbgSBf,
"deleting old version of %s\r\n",
TmpFsp);
DbgFMs(2,DbgFNm,DbgSBf);
#endif
if (remove(TmpFsp) != 0) { /* delete it */
ZErMsg();
ErrMsg(ERR_UCO);
ZClnUp();
exit(EXIT_FAILURE);
}
}
} else if (ver==(-2) || ver==0) {
(void)strcat(TmpFsp, ";1");
} else if (ver==(-1)) { /* can't read dir */
ZErMsg();
ErrMsg(ERR_UCO); /* una to close o */
ZClnUp();
exit(EXIT_FAILURE);
} else { /* ver > 0 */
int ln = strlen(TmpFsp);
ver++;
strcat(TmpFsp, ";");
MakDBf((LONG)ver, 10);
strncat(TmpFsp, (char *)DBfBeg, DBfPtr-DBfBeg);
*(TmpFsp+ln+(1+DBfPtr-DBfBeg)+1) = '\0';
}
}
#if DEBUGGING
sprintf(DbgSBf,"renaming %s to %s\r\n",
OFiles[OfIndx].OTNam, TmpFsp);
DbgFMs(2,DbgFNm,DbgSBf);
#endif
if (rename(OFiles[OfIndx].OTNam, TmpFsp)) { /* TAA changed to use rename */
ZErMsg();
ZDspBf((unsigned char *)"Edit saved in ", 14);
ZDspBf(OFiles[OfIndx].OFNam,
strlen(OFiles[OfIndx].OFNam));
ErrMsg(ERR_UCO);
ZClnUp();
exit(EXIT_FAILURE);
}
} else { /* Delete original if not backing up */
if (remove(OFiles[OfIndx].OTNam) != 0) {
ZErMsg();
ErrMsg(ERR_UCO);
DBGFEX(2,DbgFNm,"remove() failed");
exit(EXIT_FAILURE);
}
}
#if DEBUGGING
sprintf(DbgSBf,"renaming %s to %s\r\n",
OFiles[OfIndx].OFNam, OFiles[OfIndx].OTNam);
DbgFMs(2,DbgFNm,DbgSBf);
#endif
if (rename(OFiles[OfIndx].OFNam,
OFiles[OfIndx].OTNam)) {
ZErMsg();
ErrMsg(ERR_UCO);
ZClnUp();
exit(EXIT_FAILURE);
}
}
IsOpnO[OfIndx] = FALSE;
DBGFEX(2,DbgFNm,NULL);
}
/*****************************************************************************
ZOpInp()
This function opens an input file. The name of the file is pointed
to by FBfBeg. FBfPtr points to the character following the last character of
the file name.
This function is used to open all files, including macro files
needed by the "EI" command. The "EIFlag" argument tells this function if
it's an "EI" file. If it is, some extra file searching is done to make
things convenient for the user. The extra processing is modelled after what
happens under VMS (or really, what SHOULD happen under VMS). The basic idea
is to find the macro file whether the user has specificed the ".tec" or not,
and whether it's in the current directory or the macro library directory.
The basic Unix logic is like this:
if (the file exists)
open it and return SUCCESS
if (EIfile) {
if (there's no dot and appending ".tec" works)
open it and return SUCCESS
if (prepending default library directory works)
open it and return SUCCESS
if (prepending library and appending ".tec" works)
open it and return SUCCESS
}
file not found, so return with error
Under VAX/VMS, it's a little different. VMS tries to open the file only
twice, each time with the RMS "default type" field set to ".TEC", so VMS
will insert ".TEC" if the user doesn't. There's no straightforward way to
avoid putting ".TEC" on the end of your TECO macro file namess under VMS,
which some would argue is a good thing, as long as you don't have to type
the ".TEC" when you use them.
Under MS-DOS, the above PDL works, except that when the logic talks about
appending ".tec", it doesn't happen if there's alreay a dot in the file
name, as you can only have one dot in MS-DOS file names.
*****************************************************************************/
DEFAULT ZOpInp(IfIndx, EIFile, RepFNF)
DEFAULT IfIndx; /* index into file data block array IFiles */
BOOLEAN EIFile; /* is it a macro file (hunt for it) */
BOOLEAN RepFNF; /* report "file not found" error? */
{
#if DEBUGGING
static char *DbgFNm = "ZOpInp";
sprintf(DbgSBf,", FBf = \"%.*s\"", (int)(FBfPtr-FBfBeg), FBfBeg);
DbgFEn(2,DbgFNm,DbgSBf);
#endif
*FBfPtr = '\0'; /* terminate the file name */
if ((IFiles[IfIndx] = fopen((char *)FBfBeg, "r")) != NULL) {
DBGFEX(1,DbgFNm,"SUCCESS");
IFisCR[IfIndx] = 0;
return SUCCESS;
}
if (EIFile) {
charptr dummyp = NULL;
char TmpBfr[FILENAME_MAX];
ptrdiff_t TmpLen = FBfPtr-FBfBeg;
BOOLEAN noDot;
if (noDot = strchr((char *)FBfBeg,'.') == NULL) { /* if no dot */
(void)strcat(FBfBeg,".tec"); /* append .tec */
FBfPtr += 4;
if ((IFiles[IfIndx] = fopen(FBfBeg, "r")) != NULL) {
DBGFEX(1,DbgFNm,"SUCCESS");
IFisCR[IfIndx] = 0;
return SUCCESS;
}
}
MEMMOVE(TmpBfr, FBfBeg, TmpLen); /* save file name */
if (ZClnEG(EG_LIB, GET_VAL, dummyp) != -1) { /* get dir spec */
goto open_failed;
}
MEMMOVE(FBfPtr, TmpBfr, TmpLen); /* append name to dir spec */
FBfPtr += TmpLen;
*FBfPtr = '\0'; /* terminate file name */
if ((IFiles[IfIndx] = fopen(FBfBeg, "r")) != NULL) {
DBGFEX(1,DbgFNm,"SUCCESS");
IFisCR[IfIndx] = 0;
return SUCCESS;
}
if (noDot) { /* if no dot */
(void)strcat(FBfBeg,".tec"); /* append .tec */