1 /*****************************************************************************
2 	ZLinux.c
3 	System dependent code for Linux
4     This is based on zunix.c, with changes (see comments "TAA")
5     for enhanced functionality.
6 *****************************************************************************/
7 /*
8  * Define standard functions.
9  */
10 
11 #include <sys/types.h>		/* needed before sys/param.h is included */
12 #include <errno.h>		/* define errno */
13 #include <signal.h>		/* to catch ^C and ^Z signals */
14 #include <stdio.h>		/* define stdin */
15 #include <string.h>		/* strncpy(), strlen(), etc. */
16 #include <sys/time.h>		/* define tm struct */
17 #include <dirent.h>
18 #include <sys/file.h>
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 #include <sys/wait.h>
22 #include <pwd.h>
23 #include <time.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <termios.h>
27 #include <glob.h>		/* Rewritten 05/04 by TAA to use glob function */
28 
29 #include "zport.h"		/* define portability identifiers */
30 #include "tecoc.h"		/* define general identifiers */
31 #include "chmacs.h"		/* define character processing macros */
32 #include "clpars.h"		/* command-line parsing macro */
33 #include "dchars.h"		/* define identifiers for characters */
34 #include "deferr.h"		/* define identifiers for error messages */
35 #include "defext.h"		/* define external global variables */
36 #include "dscren.h"		/* define identifiers for screen i/o */
37 
38 static BOOLEAN tty_set = FALSE;	/* Has the terminal been set? */
39 static struct termios  out, cur;	/* terminal characteristics buffers */
40 static char tbuf[1024];		/* store TERMCAP entry here */
41 // static char tarea[1024];	/* store decoded TERMCAP stuff here */
42 // static char *ce;		/* TERMCAP sequence: clear to end-of-line */
43 // static char *up;		/* TERMCAP sequence: cursor up */
44 // static char *so;		/* TERMCAP sequence: reverse video on */
45 // static char *se;		/* TERMCAP sequence: reverse video off */
46 int	tputs();		/* send termcap string to a given function */
47 int	tgetent();		/* load a terminal capability buffer */
48 char 	*tgetstr();		/* get str value of a terminal capability */
49 
50 static int vernum();		/* see bottom of this file */
51 
52 static	int SupGotCtC = 0;
53 
54 static glob_t pglob;
55 static int globindex = 0;
56 
57 /*****************************************************************************
58 	IFiles holds the file data blocks for input files.  There are three
59 static input streams:   the primary input stream,  the secondary input stream,
60 and the input stream used by the EQq command.  To access these three files,
61 identifiers defined in file tecoc.h are used to index into this array.
62 Other elements of this array are used to access input files for the EI
63 command.
64 *****************************************************************************/
65 
66 FILE *IFiles[NIFDBS];
67 /* TAA Mod 5/04 -- keep track of last character being CR so we will
68  * have CRLF -> CRLF instead of CRLFLF in case input file is DOS
69  * format. */
70 static int IFisCR[NIFDBS] = {0};
71 
72 /*****************************************************************************
73 	OFiles holds the file data blocks for the output files.  There are
74 three output streams:   the primary output stream,  the secondary output
75 stream and the output stream used by the E%q command.  The array is indexed
76 using identifiers defined in file tecoc.h.
77 *****************************************************************************/
78 
79 static struct {
80 	char	OFNam[FILENAME_MAX];	/* output file name */
81 	char	OTNam[FILENAME_MAX];	/* temporary output file name */
82 	FILE	*OStrem;		/* stream */
83 	BOOLEAN forBackup;          /* TAA Mod */
84 } OFiles[NOFDBS];
85 
86 
87 /*****************************************************************************
88 
89 	ZErMsg()
90 
91 	This function displays error message from the operating system on
92 the terminal screen.  The error message text is retrieved from the operating
93 system and imbedded in a TECO-style message with the SYS mnemonic.
94 
95 *****************************************************************************/
96 
ZErMsg()97 static VVOID ZErMsg()
98 {
99 	if (errno < sys_nerr)
100 		ErrStr(ERR_SYS, sys_errlist[errno]);
101 }
102 
103 /*****************************************************************************
104 
105 	ZAlloc()
106 
107 	This function allocates memory.  The single argument is the number of
108 bytes to allocate.  TECO-C uses the ZFree and ZRaloc functions to de-allocate
109 and re-allocate, respectively,  the memory allocated by this function.
110 
111 *****************************************************************************/
112 
ZAlloc(MemSize)113 voidptr ZAlloc(MemSize)		/* allocate memory */
114 SIZE_T MemSize;
115 {
116 	return (voidptr)malloc(MemSize);
117 }
118 
119 /*****************************************************************************
120 
121 	ZBell()
122 
123 	Thus function rings the terminal bell.  For most platforms,  this
124 means just writing a bell character (control-G) to the terminal.  Under
125 MS-DOS, ringing the bell this way produces a yucky sound,  so for MS-DOS
126 this function controls the signal generator directly.
127 
128 *****************************************************************************/
129 
ZBell(VVOID)130 VVOID ZBell(VVOID)
131 {
132 	ZDspCh('\7');
133 }
134 
135 /*****************************************************************************
136 
137 	ZChIn()
138 
139 	This function inputs a single character from the terminal.
140 
141 	1.  the character is not echoed on the terminal
142 	2.  ^C calls an interrupt routine.  Note that this must be
143 	    implemented so that a ^C will cancel a current output via
144 	    ZDspBf.  The ^C must be a true interrupt.
145 	3.  type-ahead is always nice
146 	4.  The character must be returned immediately:  no fooling
147 	    around waiting for a carriage-return before returning.
148 	5.  If the NoWait argument is TRUE, don't wait.
149 	6.  When the user hits the RETURN key,  TECO is supposed to see
150 	    a carriage return and then a line feed.  The function must
151 	    deal with this by returning a carriage return to the caller
152 	    and then "remembering" to send a line feed on the next call.
153 	7.  handle ET_BKSP_IS_DEL flag
154 
155 *****************************************************************************/
156 
ZChIn(NoWait)157 DEFAULT ZChIn(NoWait)			/* input a character from terminal */
158 BOOLEAN NoWait;				/* return immediately? */
159 {
160 	char Charac;
161 	static	BOOLEAN	LastLF = FALSE;
162 
163 	if (LastLF) {
164 		LastLF = FALSE;
165 		return (DEFAULT)LINEFD;
166 	}
167 	if (read(fileno(stdin), &Charac, 1) != 1) {
168 		if (GotCtC || SupGotCtC)
169 			return (DEFAULT)CTRL_C;
170 		if (!GotCtC) {
171 			ZErMsg();
172 			ErrMsg(ERR_URC);
173 			ZClnUp();
174 			exit(EXIT_FAILURE);
175 		}
176 	}
177 	GotCtC = FALSE;
178 	if (Charac == CRETRN) {
179 		LastLF = TRUE;
180 		return (DEFAULT)CRETRN;
181 	}
182 	if (EtFlag & ET_BKSP_IS_DEL) {
183 		if (Charac == DELETE) {
184 			Charac = BAKSPC;
185 		} else if (Charac == BAKSPC) {
186 			Charac = DELETE;
187 		}
188 	}
189 	return (DEFAULT)Charac;
190 }
191 
192 /*****************************************************************************
193 
194 	ZClnEG()
195 
196 	This function executes a :EG command.  The :EG commands are used to
197 get access to operating system functions.  The minimum set of functions is
198 
199 	:EGINI$		gets, sets or clears the initialization file name
200 	:EGMEM$		gets, sets or clears the file name memory
201 	:EGLIB$		gets, sets or clears the macro library directory
202 	:EGVTE$		gets, sets or clears the video macro file name
203 
204 although more functions may be defined.
205 
206 The :EG command was designed to access logical names,  which are supported
207 by DEC's VAX/VMS and RSX operating systems.  Logical names are a useful way
208 to specify, for example, a directory that a program is to find a set of files
209 in.  A user can define logical names to set up a program's environment.
210 Programs can read, create or delete logical names.
211 
212 Logical names are stored separately from program memory, so if a program sets
213 a logical name and then exits, the logical name still exists.  TECO on a VAX
214 uses a logical name to store the name of the file being edited.  If the user
215 starts TECO without specifying a file name, TECO looks for the logical name
216 and, if it exists, uses the value of the logical name as a filename.  This
217 allows users to edit a file several times in a session without having to
218 type the file name each time they start TECO (except the first time).
219 
220 Unix doesn't have logical names.  The closest thing is environment variables,
221 which are passed to a program when it is started.  A user can define
222 environment variables,  and a program can get the values with a getenv call.
223 A program can even add to it's private list of environment variables,  but
224 the list disappears when the program exits.  So environment variables don't
225 fill the needs of the :EG command.
226 
227 Environment variables are, however, natural for some of what :EG is really
228 used for.  Users rarely need the :EG command,  even in macros.  The main use
229 of :EG is in the command-line-parsing macro (in CLPARS.TES, CLPARS.TEC and
230 CLPARS.H).  That macro can handle a partially-implemented :EG command (it
231 tests the success/failure flag returned by :EG).
232 
233 So I partially implemented :EG for Unix.  :EG can read the "INI", "LIB" and
234 "VTE" values,  but can't set or clear them.  The "MEM" value is supported
235 using a file (ugh) to save the name of the last-file-edited.  The file is
236 stored in /tmp so it gets deleted when the system boots.
237 *****************************************************************************/
ZClnEG(DEFAULT EGWhat,DEFAULT EGOper,charptr TxtPtr)238 	LONG ZClnEG(                    /* execute special :EG command */
239 				DEFAULT EGWhat,         /* what to get/set/clear: MEM, LIB, etc. */
240 				DEFAULT EGOper,         /* operation: get, set or clear */
241 				charptr TxtPtr)         /* if setting,  value to set */
242 	{
243 		char    *cp=NULL;       /* environment variable name */
244 //		char    buf[100];       /* enough for envname + 80 char filename */
245 		LONG    retval;         /* -1L on success, 0L on failure */
246 
247 		DBGFEN(2,"ZClnEG",NULL);
248 		DBGFEX(2,DbgFNm,"0");
249 
250 		switch (EGWhat) {
251 			case EG_INI: cp = "TEC_INIT";    break;
252 			case EG_LIB: cp = "TEC_LIBRARY"; break;
253 			case EG_MEM: cp = "TEC_MEMORY";  break;
254 #if VIDEO
255 			case EG_VTE: cp = "TEC_VTEDIT";  break;
256 #endif
257 			default: return 0L;
258 		}
259 
260 		if (EGOper == GET_VAL) {
261 			if ((cp = getenv(cp)) == NULL) {
262 				retval = 0L;            /* return failure */
263 			} else {
264 				retval = -1L;           /* success, copy to FBf */
265 				strcpy((char*)FBfBeg, cp);
266 				FBfPtr = FBfBeg + strlen(cp);
267 			}
268 		} else {
269 			/* Cannot set value */
270 // 			strcpy(buf, cp);                /* build NAME= */
271 // 			strcat(buf, "=");
272 // 			if (EGOper == SET_VAL) {        /* concatenate new value */
273 // 				strcat(buf, (char *)TxtPtr);
274 // 			}
275 // 			retval = (putenv(buf) != 0)     /* if putenv() failed */
276 // 					 ? 0L            /*   then return failure */
277 // 					 : -1L;          /*   else return success */
278 			retval = 0L;
279 		}
280 		return retval;
281 	}
282 
283 
284 /*****************************************************************************
285   See the definition of MEMMOVE in ZPORT.H for a description of this
286   function.
287 *****************************************************************************/
288 
ZCpyBl(Destin,Source,Length)289 VVOID ZCpyBl(Destin, Source, Length)
290 charptr Destin;
291 charptr Source;
292 SIZE_T Length;
293 {
294 	if (Source < Destin) {
295 		Source += Length;
296 		Destin += Length;
297 		while (Length-- > 0) {
298 			*--Destin = *--Source;
299 		}
300 	} else {
301 		while (Length-- > 0) {
302 			*Destin++ = *Source++;
303 		}
304 	}
305 }
306 
307 #if DEBUGGING
Zcp2ul(cp)308 ULONG Zcp2ul(cp)		/* convert charptr to ULONG */
309 voidptr cp;
310 {
311 	return (ULONG)(cp);
312 }
313 #endif
314 
315 /*****************************************************************************
316 
317 	ZClnUp()
318 
319 	This function cleans up in preparation for terminating TECO-C.
320 
321 *****************************************************************************/
322 
ZClnUp(VVOID)323 VVOID ZClnUp(VVOID)			/* cleanup for TECO-C abort */
324 {
325 	DBGFEN(3,"ZClnUp","closing terminal channels and exiting");
326 	if (tty_set == TRUE)
327 		tcsetattr(0, TCSANOW, &out);
328 }
329 
330 /*****************************************************************************
331 
332 	ZDoCmd()
333 
334 	This function terminates TECO and feeds a command line to the
335 command line interpreter.  The command to be executed is passed to this
336 function.
337 
338 
339 *****************************************************************************/
340 
ZDoCmd(charptr GBfBeg,charptr GBfPtr)341 VVOID ZDoCmd(charptr GBfBeg, charptr GBfPtr)	/* die and pass command to OS */
342 {
343 	char	buf[128+1];
344 	char	*space_p;
345 
346 	DBGFEN(1,"ZDoCmd",NULL);
347 
348 /*
349  * 1. Terminate buf[] and command line in GBf
350  * 2. make local copy since GBf will be free'd in ZClnUp()
351  * 3. separate program name from arguments, if any
352  * 4. Call ZClnUp to free up everything
353  * 5. Execute the command line, with optional arguments.  If we know where
354  *    the command processor is, use it so we can execute .BAT batch files
355  * 6. we shouldn't be here, exit
356  */
357 
358 	buf[128] = *GBfPtr = '\0';
359 	(void)strncpy(buf, GBfBeg, 128);
360 	if ((space_p = strchr(buf,' ')) != NULL) {
361 		*space_p++ = '\0';
362 	}
363 	ZClnUp();
364 
365 	execlp ("/bin/sh", "sh", "-c", buf, (space_p) ? space_p : NULL, NULL);
366 
367 	/* we should never reach this statement */
368 	(void)perror ("");
369 	ZExit (EXIT_SUCCESS);
370 }
371 
372 /*****************************************************************************
373 
374 	ZDspBf()
375 
376 	This function displays a buffer of a given length on the terminal
377 screen.  On the VAX (and maybe other systems) doing any kind of output
378 involves a fair amount of overhead,  regardless of the size of the buffer
379 being output.  It is therefore better to make a single call to the operating
380 system's output function than to call the function for each and every
381 character.  If such improvements do not apply to the system this program
382 is running on,  then this function can simply call ZDspCh for every character
383 in the buffer.
384 
385 *****************************************************************************/
386 
ZDspBf(buffer,length)387 VVOID ZDspBf(buffer, length)		/* output a buffer to terminal */
388 charptr buffer;
389 SIZE_T length;
390 {
391 	if (write(fileno(stdout), buffer, length) == -1) {
392 		puts("Unable to write to terminal in function ZDspBf");
393 		TAbort(EXIT_FAILURE);
394 	}
395 }
396 
397 /*****************************************************************************
398 
399 	ZDspCh()
400 
401 	This function outputs a single character to the terminal.
402 
403 *****************************************************************************/
404 
ZDspCh(Charac)405 VVOID ZDspCh(Charac)		/* output a character to terminal */
406 char Charac;
407 {
408 	if (write(fileno(stdout), &Charac, 1) == -1) {
409 		puts("Unable to write to terminal in function ZDspCh");
410 		TAbort(EXIT_FAILURE);
411 	}
412 }
413 
414 /*****************************************************************************
415 
416 	ZExCtB()
417 
418 	This function implements the TECO ^B command,  which returns the
419 current date encoded in the following way:
420 
421 		((year-1900)*16+month)*32+day
422 
423 *****************************************************************************/
424 
ZExCtB()425 DEFAULT ZExCtB()			/* return current date */
426 {
427 	time_t clockt;
428 	struct tm *time_of_day;
429 	int tecodate;
430 
431 	DBGFEN(1,"ZExCtB","");
432 	clockt=time(NULL);
433 	time_of_day=localtime(&clockt);
434 
435 	tecodate = ((time_of_day->tm_year)*16+time_of_day->tm_mon+1)*32
436 		+ time_of_day->tm_mday ;
437 
438 	DBGFEX(1,DbgFNm,"PushEx()");
439 	return PushEx(tecodate, OPERAND);
440 }
441 
442 /*****************************************************************************
443 
444 	ZExCtH()
445 
446 	This function implements the TECO ^H command,  which returns the
447 current time encoded in the following way:
448 
449 		(seconds since midnight) / 2
450 
451 *****************************************************************************/
452 
ZExCtH()453 DEFAULT ZExCtH()			/* return current time */
454 {
455 	time_t clockt;
456 	struct tm *time_of_day;
457 	int tecotime;
458 
459 	DBGFEN(1,"ZExCtH","");
460 	clockt=time(NULL);
461 	time_of_day=localtime(&clockt);
462 
463 	tecotime = time_of_day->tm_hour * 60	/* hours * 60 */;
464 	tecotime += time_of_day->tm_min;	/* minutes */
465 	tecotime *= 30;
466 	tecotime += time_of_day->tm_sec >> 1;	/* seconds / 2 */
467 
468 	DBGFEX(1,DbgFNm,"PushEx()");
469 	return PushEx(tecotime, OPERAND);
470 }
471 
472 /*****************************************************************************
473 
474 	ZExeEJ()
475 
476 	This function executes an EJ command,  which returns environment
477 characteristics.  It returns:
478 
479 	-1EJ	1024 under VAX/VMS	(4*256 = VAX, 0 = VMS in native mode)
480         1025 under Ultrix       (4*256 = VAX, 1 = Ultrix)
481         25600 under Sun/SunOS   (100*256 = Sun, 0 = SunOS)
482 		25856 under MS-DOS	(101*256 = IBM-PC, 0 = MS-DOS)
483 		25857 under OS/2    (101*256 = IBM-PC, 1 = OS/2)
484         25858 under Linux   (101*256 = IBM-PC, 2 = Linux)
485 
486 	0EJ	process id on VAXen or Unix/Linux, 0 on anything else
487 
488 	1EJ	0 on all systems
489 
490 	2EJ	UIC, in longword format (unlike TECO-11) on VAX/VMS,
491 		0 on all other systems.
492 
493 *****************************************************************************/
494 
ZExeEJ()495 DEFAULT ZExeEJ()			/* execute an EJ command */
496 {
497 	DBGFEN(1,"ZExeEJ",NULL);
498 	if (EStTop == EStBot) {			/* if no numeric argument */
499 		NArgmt = 0;			/* default is 0EJ */
500 	} else {
501 		UMinus();			/* if -EJ, make it -1EJ */
502 		if (GetNmA() == FAILURE) {	/* get numeric argument */
503 			DBGFEX(1,DbgFNm,"FAILURE");
504 			return FAILURE;
505 		}
506 	}
507 
508 	if (NArgmt == -1) {
509 		DBGFEX(1,DbgFNm,"PushEx(25858)");
510 		return PushEx((LONG)25858, OPERAND);	/* means "PC Linux" */
511 	}
512 
513 	if (NArgmt == 0) {
514 		DBGFEX(1,DbgFNm,"PushEx(getppid())");
515 		return PushEx((LONG)getppid(), OPERAND);
516 	}
517 
518 	DBGFEX(1,DbgFNm,"ExeNYI()");
519 	return ExeNYI();
520 }
521 
522 /*****************************************************************************
523 
524 	ZExit()
525 
526 	This function terminates TECO-C with a status value.
527 
528 *****************************************************************************/
529 
ZExit(estat)530 VVOID ZExit(estat)		/* terminate TECO-C */
531 DEFAULT estat;
532 {
533 	ZClnUp();
534 	exit(estat);
535 }
536 
537 /*****************************************************************************
538 
539 	ZFree()
540 
541 	This function frees memory previously allocated by the ZAlloc
542 function.
543 
544 *****************************************************************************/
545 
ZFree(pointer)546 VVOID ZFree(pointer)		/* free memory allocated by ZAlloc */
547 voidptr pointer;
548 {
549 	free(pointer);
550 }
551 
552 /*****************************************************************************
553 
554 	ZHelp()
555 
556 	This function accepts a help string and displays the corresponding
557 help text.
558 
559 	it should be control-C interrupt-able.
560 
561 *****************************************************************************/
562 
ZHelp(HlpBeg,HlpEnd,SysLib,Prompt)563 VVOID ZHelp(HlpBeg, HlpEnd, SysLib, Prompt)
564 charptr HlpBeg;		/* first char of help request */
565 charptr HlpEnd;		/* last character of help request */
566 BOOLEAN SysLib;		/* use default HELP library? */
567 BOOLEAN	Prompt;		/* enter interactive help mode? */
568 {
569 	(void)ExeNYI();
570 }
571 
572 /*****************************************************************************
573 
574 	ZIClos()
575 
576 	This function closes the current input file.  It must
577 
578 	1.  if current input stream is not open,  simply return
579 	2.  close the input file
580 	3.  set open indicator to FALSE
581 
582 *****************************************************************************/
583 
ZIClos(IfIndx)584 VVOID ZIClos(IfIndx)			/* close input file */
585 DEFAULT	IfIndx;				/* index into IFiles array */
586 {
587 	DBGFEN(2,"ZIClos",NULL);
588 
589 	if (IsOpnI[IfIndx]) {			/* if it's open */
590 		if (fclose(IFiles[IfIndx]) == EOF) {
591 			ZErMsg();
592 			ErrMsg(ERR_UCI);
593 			ZExit(EXIT_FAILURE);
594 		}
595 		IsOpnI[IfIndx] = FALSE;
596 	}
597 	DBGFEX(2,DbgFNm,NULL);
598 }
599 
600 /*****************************************************************************
601 
602 	ZOClDe()
603 
604 	This function closes and deletes the current output stream.  It must
605 
606 	1.  if no current output stream is defined,  simply return
607 	2.  close the output stream
608 	3.  delete the file just closed
609 
610 *****************************************************************************/
611 
ZOClDe(OfIndx)612 VVOID ZOClDe(OfIndx)			/* close and delete output file */
613 DEFAULT	OfIndx;				/* index into OFiles array */
614 {
615 	DBGFEN(2,"ZOClDe",NULL);
616 
617 	if (IsOpnO[OfIndx]) {		/* if output stream is open */
618 		if (fclose(OFiles[OfIndx].OStrem) == EOF) {
619 			ZErMsg();
620 			ErrStr(ERR_UCO, OFiles[OfIndx].OFNam);
621 			ZExit(EXIT_FAILURE);
622 		}
623 		if (remove(OFiles[OfIndx].OFNam) != 0) {
624 			ZErMsg();
625 			ErrStr(ERR_UCD, OFiles[OfIndx].OFNam);
626 			ZExit(EXIT_FAILURE);
627 		}
628 		IsOpnO[OfIndx] = FALSE;
629 	}
630 	DBGFEX(2,DbgFNm,NULL);
631 }
632 
633 /*****************************************************************************
634 
635 	ZOClos()
636 
637 	This function closes the current output stream.  It is only called
638 when an output stream is defined.  It must
639 
640 	1.  flush output to the stream,  if neccessary
641 	2.  close the stream
642 	3.  set OFile to -1
643 
644 *****************************************************************************/
645 
ZOClos(OfIndx)646 VVOID ZOClos(OfIndx)		/* close output file */
647 DEFAULT	OfIndx;			/* index into OFiles array */
648 {
649 	int ver;
650 	char TmpFsp[FILENAME_MAX];
651 	char move_err[1024];
652 
653 	DBGFEN(2,"ZOClos",NULL);
654 
655 	if (!IsOpnO[OfIndx]) {				/* if it's not open */
656 		DBGFEX(2,DbgFNm,NULL);
657 		return;					/* we're done */
658 	}
659 
660 	if (fclose(OFiles[OfIndx].OStrem) == EOF) {	/* close it */
661 		ZErMsg();
662 		ErrMsg(ERR_UCO);			/* unable to close */
663 		ZExit(EXIT_FAILURE);
664 	}
665 
666 	if (OFiles[OfIndx].OTNam[0] != '\0') {	/* if temporary output file */
667         if (OFiles[OfIndx].forBackup) {
668 		(void)strcpy(TmpFsp, OFiles[OfIndx].OTNam);/* copy to TmpFsp */
669 		if ((EzFlag&EZ_NO_STRIP)==0) {
670 			char *DotPtr = strchr(TmpFsp, '.');	/* find the "." */
671 			if (DotPtr != NULL) {		/* if "." exists */
672 				*DotPtr = '\0';			/* make it null */
673 			}
674 		}
675 		if (EzFlag&EZ_NO_VER) {
676 			(void)strcat(TmpFsp, ".bak");		/* append ".bak" */
677 			if (access(TmpFsp, 0) == 0) {		/* old "x.bak"? */
678 #if DEBUGGING
679 				sprintf(DbgSBf,
680 						"deleting old version of %s\r\n",
681 						TmpFsp);
682 				DbgFMs(2,DbgFNm,DbgSBf);
683 #endif
684 				if (remove(TmpFsp) != 0) {	/* delete it */
685 					ZErMsg();
686 					ErrMsg(ERR_UCO);
687 					ZClnUp();
688 					exit(EXIT_FAILURE);
689 				}
690 			}
691 		} else {
692 			ver = vernum(TmpFsp);
693 			if (ver==(-3)) {
694 				puts("\nWARNING: Versioning disabled\n");
695 				(void)strcat(TmpFsp, ".bak");	/* append ".bak" */
696 				if (access(TmpFsp, 0) == 0) {	/* old "x.bak"? */
697 #if DEBUGGING
698 					sprintf(DbgSBf,
699 							"deleting old version of %s\r\n",
700 							TmpFsp);
701 					DbgFMs(2,DbgFNm,DbgSBf);
702 #endif
703 					if (remove(TmpFsp) != 0) {  /* delete it */
704 						ZErMsg();
705 						ErrMsg(ERR_UCO);
706 						ZClnUp();
707 						exit(EXIT_FAILURE);
708 					}
709 				}
710 			} else if (ver==(-2) || ver==0) {
711 				(void)strcat(TmpFsp, ";1");
712 			} else if (ver==(-1)) {			/* can't read dir */
713 				ZErMsg();
714 				ErrMsg(ERR_UCO);		/* una to close o */
715 				ZClnUp();
716 				exit(EXIT_FAILURE);
717 			} else {				/* ver > 0 */
718 				int ln = strlen(TmpFsp);
719 				ver++;
720 				strcat(TmpFsp, ";");
721 				MakDBf((LONG)ver, 10);
722 				strncat(TmpFsp, DBfBeg, DBfPtr-DBfBeg);
723 				*(TmpFsp+ln+(1+DBfPtr-DBfBeg)+1) = '\0';
724 			}
725 		}
726 #if DEBUGGING
727 		sprintf(DbgSBf,"renaming %s to %s\r\n",
728 				OFiles[OfIndx].OTNam, TmpFsp);
729 		DbgFMs(2,DbgFNm,DbgSBf);
730 #endif
731 		if (rename(OFiles[OfIndx].OTNam, TmpFsp)) { /* TAA changed to use rename */
732 			ZErMsg();
733 			ZDspBf("Edit saved in ", 14);
734 			ZDspBf(OFiles[OfIndx].OFNam,
735 				   strlen(OFiles[OfIndx].OFNam));
736 			ErrMsg(ERR_UCO);
737 			ZClnUp();
738 			exit(EXIT_FAILURE);
739 		}
740 		} else { /* Delete original if not backing up */
741 			if (remove(OFiles[OfIndx].OTNam) != 0) {
742 				ZErMsg();
743 				ErrMsg(ERR_UCO);
744 				DBGFEX(2,DbgFNm,"remove() failed");
745 				exit(EXIT_FAILURE);
746 			}
747 		}
748 
749 #if DEBUGGING
750 		sprintf(DbgSBf,"renaming %s to %s\r\n",
751 				OFiles[OfIndx].OFNam, OFiles[OfIndx].OTNam);
752 		DbgFMs(2,DbgFNm,DbgSBf);
753 #endif
754 		if (rename(OFiles[OfIndx].OFNam,
755 					 OFiles[OfIndx].OTNam)) {
756 			ZErMsg();
757 			ErrMsg(ERR_UCO);
758 			ZClnUp();
759 			exit(EXIT_FAILURE);
760 		}
761 	}
762 
763 	IsOpnO[OfIndx] = FALSE;
764 	DBGFEX(2,DbgFNm,NULL);
765 }
766 
767 /*****************************************************************************
768 
769 	ZOpInp()
770 
771 	This function opens an input file.  The name of the file is pointed
772 to by FBfBeg.  FBfPtr points to the character following the last character of
773 the file name.
774 	This function is used to open all files,  including macro files
775 needed by the "EI" command.  The "EIFlag" argument tells this function if
776 it's an "EI" file.  If it is,  some extra file searching is done to make
777 things convenient for the user.  The extra processing is modelled after what
778 happens under VMS (or really,  what SHOULD happen under VMS).  The basic idea
779 is to find the macro file whether the user has specificed the ".tec" or not,
780 and whether it's in the current directory or the macro library directory.
781 The basic Unix logic is like this:
782 
783 	if (the file exists)
784 	    open it and return SUCCESS
785 	if (EIfile) {
786 	    if (there's no dot and appending ".tec" works)
787 		open it and return SUCCESS
788 	    if (prepending default library directory works)
789 		open it and return SUCCESS
790 	    if (prepending library and appending ".tec" works)
791 		open it and return SUCCESS
792 	}
793 	file not found, so return with error
794 
795 Under VAX/VMS,  it's a little different.  VMS tries to open the file only
796 twice,  each time with the RMS "default type" field set to ".TEC",  so VMS
797 will insert ".TEC" if the user doesn't.  There's no straightforward way to
798 avoid putting ".TEC" on the end of your TECO macro file namess under VMS,
799 which some would argue is a good thing,  as long as you don't have to type
800 the ".TEC" when you use them.
801 
802 Under MS-DOS,  the above PDL works,  except that when the logic talks about
803 appending ".tec",  it doesn't happen if there's alreay a dot in the file
804 name,  as you can only have one dot in MS-DOS file names.
805 
806 *****************************************************************************/
807 
ZOpInp(IfIndx,EIFile,RepFNF)808 DEFAULT ZOpInp(IfIndx, EIFile, RepFNF)
809 DEFAULT IfIndx;			/* index into file data block array IFiles */
810 BOOLEAN	EIFile;			/* is it a macro file (hunt for it) */
811 BOOLEAN RepFNF;			/* report "file not found" error? */
812 {
813 #if DEBUGGING
814     static char *DbgFNm = "ZOpInp";
815     sprintf(DbgSBf,", FBf = \"%.*s\"", (int)(FBfPtr-FBfBeg), FBfBeg);
816     DbgFEn(2,DbgFNm,DbgSBf);
817 #endif
818 
819     *FBfPtr = '\0';			/* terminate the file name */
820 
821     if ((IFiles[IfIndx] = fopen(FBfBeg, "r")) != NULL) {
822 	DBGFEX(1,DbgFNm,"SUCCESS");
823 	IFisCR[IfIndx] = 0;
824 	return SUCCESS;
825     }
826 
827     if (EIFile) {
828 	charptr dummyp = NULL;
829 	char TmpBfr[FILENAME_MAX];
830 	ptrdiff_t TmpLen = FBfPtr-FBfBeg;
831 
832 	if (strchr(FBfBeg,'.') == NULL) {		/* if no dot */
833 	    (void)strcat(FBfBeg,".tec");		/* append .tec */
834 	    FBfPtr += 4;
835 	    if ((IFiles[IfIndx] = fopen(FBfBeg, "r")) != NULL) {
836 		DBGFEX(1,DbgFNm,"SUCCESS");
837 		IFisCR[IfIndx] = 0;
838 		return SUCCESS;
839 	      }
840 	}
841 
842 	MEMMOVE(TmpBfr, FBfBeg, TmpLen);		/* save file name */
843 	if (ZClnEG(EG_LIB, GET_VAL, dummyp) != -1) {	/* get dir spec */
844 	  goto open_failed;
845 	}
846 	MEMMOVE(FBfPtr, TmpBfr, TmpLen);	/* append name to dir spec */
847 	FBfPtr += TmpLen;
848 	*FBfPtr = '\0';				/* terminate file name */
849 	if ((IFiles[IfIndx] = fopen(FBfBeg, "r")) != NULL) {
850 	    DBGFEX(1,DbgFNm,"SUCCESS");
851 	    IFisCR[IfIndx] = 0;
852 	    return SUCCESS;
853 	}
854 
855 	if (strchr(FBfBeg,'.') == NULL) {		/* if no dot */
856 	    (void)strcat(FBfBeg,".tec");		/* append .tec */
857 	    FBfPtr += 4;
858 	    if ((IFiles[IfIndx] = fopen(FBfBeg, "r")) != NULL) {
859 		IFisCR[IfIndx] = 0;
860 		DBGFEX(1,DbgFNm,"SUCCESS");
861 		return SUCCESS;
862 	      }
863 	}
864 
865     }
866 
867 /*
868  * The fopen failed,  so return with error.
869  */
870 open_failed:
871     if (!RepFNF && ((errno == ENODEV) || (errno == ENOENT))) {
872 	DBGFEX(2,DbgFNm,"FILENF");
873 	return FILENF;
874     }
875     ZErMsg();
876     DBGFEX(2,DbgFNm,"FAILURE");
877     return FAILURE;
878 }
879 
880 /*****************************************************************************
881 
882 	ZOpOut()
883 
884 	This function creates (and opens) an output file.  The name of
885 the file to be created is pointed to by FBfBeg.  FBfPtr points to the
886 character following the last character of the file name.
887 
888 *****************************************************************************/
889 
890 /*
891  * Unix file names do not have version numbers,  so we have to deal with
892  * creating ".bak" versions of files.  For output files,  this means that
893  * when the output file is opened,  we check if a file with the same name
894  * already exists.  If a file already exists,  then we open a temporary
895  * output file and,  when the file is closed,  the close routine will deal
896  * with renaming files to make them come out right.  If no file with the
897  * same name already exists,  then the output file can simply be opened.
898  * The close routine will only rename files if a temporary file was created
899  * by this routine.
900  */
901 
ZOpOut(OfIndx,RepErr,Backup)902 DEFAULT ZOpOut(OfIndx, RepErr, Backup)		/* open output file */
903 DEFAULT	OfIndx;				/* output file indicator */
904 BOOLEAN RepErr;				/* report errors? */
905 BOOLEAN Backup;				/* TAA Added */
906 {
907 	char *tfname;
908 	struct stat *bufstat = NULL;
909 	int tmpflag = 0;
910 
911 #if DEBUGGING
912 	static char *DbgFNm = "ZOpOut";
913 	sprintf(DbgSBf,", FBf = \"%.*s\"",(int)(FBfPtr-FBfBeg),FBfBeg);
914 	DbgFEn(2,DbgFNm,DbgSBf);
915 #endif
916 
917 /*
918  * If the output file already exists,  make a temporary file.
919  */
920 	*FBfPtr = '\0';
921 	if (access(FBfBeg, 0) == 0) {		/* if file already exists */
922 		char *dirname,*s;
923 	 	int n;
924 		bufstat = (struct stat *)malloc(sizeof(struct stat));
925 		stat(FBfBeg, bufstat);
926 		tmpflag = 1;
927 		n = FBfPtr - FBfBeg;		/* strlen(FBfBeg) */
928 		dirname = (char *)malloc(n+4);
929 		strcpy(dirname, FBfBeg);
930 		s=dirname + n;
931 		while (*s != '/' && s > dirname)
932 			s--;
933 		if (*s=='/' && s==dirname) {	/* must be root directory */
934 			*(s+1)='\0';
935 		} else if (*s=='/')  {		/* we have string/name */
936 			*s='\0';
937 		} else {			/* must have s==dirname and
938 						   *s != '/', so current
939 						   directory */
940 			*dirname='.'; *(dirname+1)='\0';
941 		}
942 		tfname = tempnam(dirname,"tecoc");
943 		(void)strcpy(OFiles[OfIndx].OFNam, tfname);
944 		free(tfname);
945 		free(dirname);
946 		(void)strcpy(OFiles[OfIndx].OTNam, FBfBeg);
947 #if DEBUGGING
948 		sprintf(DbgSBf,	"file %s already exists\r\n", FBfBeg);
949 		DbgFMs(2,DbgFNm,DbgSBf);
950 #endif
951 	} else {
952 		(void)strcpy(OFiles[OfIndx].OFNam, FBfBeg);
953 		OFiles[OfIndx].OTNam[0] = '\0';
954 	}
955 
956 #if DEBUGGING
957 	sprintf(DbgSBf,	"creating file %s\r\n",	OFiles[OfIndx].OFNam);
958 	DbgFMs(2,DbgFNm,DbgSBf);
959 #endif
960 	OFiles[OfIndx].OStrem = fopen(OFiles[OfIndx].OFNam, "w");
961 	if (OFiles[OfIndx].OStrem == NULL) {
962 		if (RepErr)
963 			ZErMsg();
964 		DBGFEX(2,DbgFNm,"FAILURE");
965 		return FAILURE;
966 	}
967 	if (tmpflag) {
968 		fchmod(fileno(OFiles[OfIndx].OStrem),
969 		       bufstat->st_mode | S_IRUSR | S_IWUSR);
970 		free(bufstat);
971 	}
972 	DBGFEX(2,DbgFNm,"SUCCESS");
973 
974 	OFiles[OfIndx].forBackup = Backup;
975 
976 	if (OFiles[OfIndx].OTNam[0] != '\0' && RepErr && !Backup) {
977 		ZDspBf("% Superseding existing file\r\n", 29);
978 	}
979 	return SUCCESS;
980 }
981 
982 /*****************************************************************************
983 
984 	ZPrsCL()
985 
986 	Parse the command line using a TECO macro.
987 
988 	load q-register Z with the command line
989 	if USE_ANSI_CLPARS
990 		directly execute command-line parsing macro in clpars[]
991 	else
992 		load q-register Y with a command-line parsing macro
993 		do an MY$$
994 
995 *****************************************************************************/
996 
ZPrsCL(argc,argv)997 VVOID ZPrsCL(argc, argv)		/* parse a TECOC command line */
998 int argc;
999 char *argv[];
1000 {
1001 	int i;
1002 	char TmpBuf[256];
1003 	SIZE_T	line_len;
1004 	char *cmd;
1005 
1006 	DBGFEN(2,"ZPrsCL",NULL);
1007 
1008 /*
1009  * If the command line contains arguments,  construct a replica of the
1010  * command line in Q-register Z.  It's a "replica" because spacing might
1011  * be wrong.
1012  */
1013 	TmpBuf[0] = '\0';
1014 	cmd = strrchr(argv[0], '/');
1015 	if (cmd) {
1016 		cmd++;
1017 	} else {
1018 		cmd = argv[0];
1019 	}
1020 	if (strcmp(cmd, "teco") == 0 ||
1021 		  strcmp(cmd, "mung") == 0) {
1022 		strcat(TmpBuf, cmd);
1023 		strcat(TmpBuf, " ");
1024 	} else if (strcmp(cmd, "inspect") == 0) {
1025 		strcat(TmpBuf, "teco -inspect ");
1026 	} else if (strcmp(cmd, "Make") == 0) {
1027 		strcat(TmpBuf, "make ");
1028 	}
1029 	if (argc > 1 || TmpBuf[0] != '\0') {
1030 		for (i=1; i<argc; i++) {
1031 			if ( strchr(*++argv, ' ') != NULL ) {
1032 						// There is embedded space, turn into quoted string
1033 				strcat(TmpBuf, "\"");
1034 				strcat(TmpBuf, *argv);
1035 				strcat(TmpBuf, "\" ");
1036 			}
1037 			else {
1038 				strcat(TmpBuf, *argv);
1039 				strcat(TmpBuf, " ");
1040 			}
1041 		}
1042 		line_len = strlen(TmpBuf)-1;	/* remove trailing space */
1043 		QR = &QRgstr[35];		/* 35 = q-register Z */
1044 		if (MakRom(line_len) == FAILURE) {
1045 			DBGFEX(2,DbgFNm,"exiting with EXIT_FAILURE");
1046 			ZExit(EXIT_FAILURE);
1047 		}
1048 		MEMMOVE(QR->Start, TmpBuf, line_len);
1049 		QR->End_P1 += line_len;		/* length of q-reg text */
1050 	}
1051 #if USE_ANSI_CLPARS
1052 
1053 /*
1054  * execute imbedded command line-parsing macro directly from clpars[]
1055  */
1056 
1057 	CStBeg = (charptr)clpars;		/* command string start */
1058 	CBfPtr = (charptr)clpars;		/* command string start */
1059 	CStEnd = (charptr)((unsigned int)clpars + CLPARS_LEN);		/* command string end */
1060 	EStTop = EStBot;			/* clear expression stack */
1061 	ExeCSt();				/* execute command string */
1062 
1063 #else
1064 
1065 /*
1066  * Load imbedded command-line parsing macro into Q-register Y
1067  */
1068 
1069 	QR = &QRgstr[34];			/* 34 = q-register Y */
1070 	if (MakRom((SIZE_T)CLPARS_LEN) == FAILURE) {
1071 		DBGFEX(2,DbgFNm,"MakRom(CLPARS_LEN) failed, calling exit()");
1072 		ZExit(EXIT_FAILURE);
1073 	}
1074 	for (i = 0; i < CLPARS_LINES; i++) {
1075 		line_len = strlen(clpars[i]);
1076 		MEMMOVE(QR->End_P1, clpars[i], line_len);
1077 		QR->End_P1 += line_len;		/* length of q-reg text */
1078 	}
1079 
1080 /*
1081  * Execute an MY$$ command.
1082  */
1083 
1084 	CBfPtr = (charptr)"my\33\33";		/* command string start */
1085 	CStEnd = CBfPtr + 3;			/* command string end */
1086 	EStTop = EStBot;			/* clear expression stack */
1087 	ExeCSt();				/* execute command string */
1088 
1089 /*
1090  * Clear the command-line parsing macro from Q-register Y
1091  */
1092 
1093 	QR = &QRgstr[34];			/* 34 = q-register Y */
1094 	ZFree (QR->Start);
1095 	QR->Start = QR->End_P1 = NULL;
1096 
1097 #endif
1098 
1099 	DBGFEX(2,DbgFNm,NULL);
1100 }
1101 
1102 /*****************************************************************************
1103 
1104 	ZPWild()
1105 
1106 	This function presets the wildcard lookup filename.  It is called
1107 when the user executes an ENfilename$ command.  Later executions of the EN$
1108 command will cause the ZSWild function to be called to return successive
1109 wildcard matches.
1110 
1111 *****************************************************************************/
1112 /* Rewritten 05/04 by TAA */
1113 
ZPWild()1114 DEFAULT ZPWild()		/* preset the wildcard lookup filename */
1115 {
1116 	int result,i;
1117 /*
1118  * if we didn't process all names from a previous EN` call then discard them
1119  */
1120 	globfree(&pglob);
1121 	globindex = 0;
1122 	*FBfPtr='\0';				/* terminate string */
1123 	result = glob(FBfBeg, GLOB_ERR|GLOB_MARK, NULL, &pglob);
1124 	if (result != 0) {
1125 		return FAILURE;
1126 	}
1127 	result = 0; /* Count files matched */
1128 	for (i = 0; i < pglob.gl_pathc; i++) {
1129 		if (pglob.gl_pathv[i][strlen(pglob.gl_pathv[i])-1] != '/') {
1130 			result++;
1131 		}
1132 	}
1133 	if (result > 0) {
1134 		return SUCCESS;
1135 	}
1136 	return FAILURE;
1137 }
1138 
1139 /*****************************************************************************
1140 
1141 	ZRaloc()
1142 
1143 	This function performs the standard C library function realloc.
1144 
1145 *****************************************************************************/
1146 
ZRaloc(OldBlk,NewSiz)1147 voidptr ZRaloc(OldBlk, NewSiz)		/* re-allocate memory */
1148 voidptr OldBlk;
1149 SIZE_T NewSiz;
1150 {
1151 	return (voidptr)realloc(OldBlk, NewSiz);
1152 }
1153 
1154 /*****************************************************************************
1155 
1156 	ZRdLin()
1157 
1158 	This function reads a line from a file.  It is passed a buffer, the
1159 size of the buffer, and a file pointer.  It returns the length of the line,
1160 or sets IsEofI[] to TRUE if the end of file is encountered.
1161 
1162 *****************************************************************************/
1163 
ZRdLin(ibuf,ibuflen,IfIndx,retlen)1164 DEFAULT ZRdLin(ibuf, ibuflen, IfIndx, retlen)
1165 charptr ibuf;			/* where to put string */
1166 ptrdiff_t ibuflen;		/* max length of ibuf */
1167 int IfIndx;			/* index into IFiles[] */
1168 DEFAULT *retlen;		/* returned length of string */
1169 {
1170 	int character;		/* the last character read */
1171 	DEFAULT shortBuf;	/* max size to read, 32767 or ibuflen */
1172 	DEFAULT charsLeft;	/* number of characters left */
1173 	FILE *fp;		/* input stream pointer to read from */
1174 	char *iBuf;		/* non-huge pointer into IBf for speed */
1175 
1176 #if DEBUGGING
1177 	static char *DbgFNm = "ZRdLin";
1178 	sprintf(DbgSBf,"ibuf = %ld, ibuflen = %ld, IfIndx = %d",
1179 		Zcp2ul(ibuf),(LONG)ibuflen,IfIndx);
1180 	DbgFEn(3,DbgFNm,DbgSBf);
1181 #endif
1182 
1183 	shortBuf = (ibuflen > 32767) ? 32767 : (DEFAULT) ibuflen;
1184 	charsLeft = shortBuf;
1185 	fp = IFiles[IfIndx];
1186 	iBuf = (char *) ibuf;
1187 
1188 	while ((character = getc(fp)) >= 0) {	 /* we got one */
1189 		*iBuf++ = character;
1190 		if (character == LINEFD) {
1191 			*retlen = shortBuf - charsLeft + 1;
1192 			if ((EzFlag & EZ_UNIXNL) == 0 && IFisCR[IfIndx] == 0) {
1193 				ibuf[(*retlen) -1] = CRETRN;
1194 				ibuf[(*retlen)] = LINEFD;
1195 				(*retlen)++;
1196 			}
1197 			IFisCR[IfIndx] = 0;
1198 #if DEBUGGING
1199 			sprintf(DbgSBf,"SUCCESS, retlen = %d", *retlen);
1200 			DbgFEx(3,DbgFNm,DbgSBf);
1201 #endif
1202 			return SUCCESS;
1203 		}
1204 		IFisCR[IfIndx] = character == '\r'; /* last character was a CR -- DOS file? */
1205 		if (character == FORMFD && !(EzFlag & ED_FF)) {
1206 			/* toss form feed, then finished */
1207 			*retlen = shortBuf - charsLeft;
1208 			FFPage = -1;
1209 #if DEBUGGING
1210 			sprintf(DbgSBf,"SUCCESS, retlen = %d", *retlen);
1211 			DbgFEx(3,DbgFNm,DbgSBf);
1212 #endif
1213 			return SUCCESS;
1214 		}
1215 		/* TAA Mod 5/04 -- must allow for extra character if
1216 		 * UNIXNL since LF becomes CRLF */
1217 		if (--charsLeft == ((EzFlag&EZ_UNIXNL) ? 0 : 1)) {
1218 			/* no more room, so return */
1219 			*retlen = shortBuf;
1220 #if DEBUGGING
1221 			sprintf(DbgSBf,"SUCCESS, retlen = %d", *retlen);
1222 			DbgFEx(3,DbgFNm,DbgSBf);
1223 #endif
1224 			return SUCCESS;
1225 		}
1226 	}
1227 
1228 /*
1229  * If we made it to here, the read has failed --- EOF or Error.
1230  */
1231 	if (ferror(fp)) {			/* if we got an error */
1232 		*retlen = 0;			/* say didn't read anything */
1233 		ZErMsg();
1234 #if DEBUGGING
1235 		sprintf(DbgSBf,"ferror() FAILURE");
1236 		DbgFEx(3,DbgFNm,DbgSBf);
1237 #endif
1238 		return FAILURE;
1239 	}
1240 
1241 /*
1242  * If we made it to here, the read has failed because of EOF.
1243  */
1244 	if ((*retlen = shortBuf-charsLeft) == 0) {
1245 		IsEofI[IfIndx] = TRUE;		/* say we reached EOF */
1246 	}
1247 #if DEBUGGING
1248 	sprintf(DbgSBf,"SUCCESS, retlen = %d", *retlen);
1249 	DbgFEx(3,DbgFNm,DbgSBf);
1250 #endif
1251 	return SUCCESS;
1252 }
1253 
1254 /*****************************************************************************
1255 
1256 	ZScrOp()
1257 
1258 	This function is called to perform special screen functions.
1259 
1260 *****************************************************************************/
1261 
ZScrOp(OpCode)1262 VVOID ZScrOp(OpCode)		/* do a screen operation */
1263 int OpCode;			/* code for operation */
1264 
1265 {
1266 // 	if (CrType == UNTERM || tbuf[0] == 0) {/* if unknown terminal type */
1267 // 		return;			/* can't do screen operations */
1268 // 	}
1269 //
1270 // 	switch (OpCode) {
1271 // 		case SCR_CUP:	tputs(up, 1, ZDspCh);
1272 // 				break;
1273 // 		case SCR_EEL:	tputs(ce, 1, ZDspCh);
1274 // 				break;
1275 // 		case SCR_ROF:	tputs(se, 1, ZDspCh);
1276 // 				break;
1277 // 		case SCR_RON:	tputs(so, 1, ZDspCh);
1278 // 	}
1279 }
1280 
1281 /*****************************************************************************
1282 
1283 	ZSetTT()
1284 
1285 	This function sets or clears terminal parameters.  The only terminal
1286 parameters that TECO can set are
1287 
1288 	1. whether the terminal can display 8-bit characters
1289 	2. the number of rows
1290 	3. the number of columns
1291 
1292 *****************************************************************************/
1293 
ZSetTT(TTWhat,TTVal)1294 DEFAULT ZSetTT(TTWhat, TTVal)	/* tell operating system we set the term. */
1295 DEFAULT TTWhat;		/* what terminal parameter to set */
1296 DEFAULT TTVal;		/* what to set it to */
1297 {
1298 	return ExeNYI();
1299 }
1300 
1301 /*****************************************************************************
1302 
1303 	ZSWild()
1304 
1305 	This function searches for the next wildcard filename.  It
1306 is called when the user executes an "EN$" or ":EN$" command.  If the user
1307 executes an "ENfilename$" command,  the ZPWild function is called,  not this
1308 function.
1309 
1310 	This function returns
1311 
1312 		1. SUCCESS if the filename buffer has a new file name
1313 		2. FAILURE if the search failed somehow other than FILENF
1314 		3. FILENF if no more occurrences of the wildcard exist
1315 
1316 *****************************************************************************/
1317 /* Rewritten 05/04 by TAA */
ZSWild()1318 DEFAULT ZSWild()		/* search for next wildcard filename */
1319 {
1320 	int filename_length;
1321 
1322 	if (pglob.gl_pathc == 0)
1323 		return FAILURE;
1324 
1325 	do {
1326 		if (globindex >= pglob.gl_pathc) return FILENF;
1327 		if (pglob.gl_pathv[globindex][strlen(pglob.gl_pathv[globindex]-1)] != '/') break;
1328 		globindex++;
1329 	} while (1);
1330 
1331 /*
1332  * we really have a file name now
1333  */
1334 	filename_length = strlen(pglob.gl_pathv[globindex]);
1335 	MEMMOVE(FBfBeg, pglob.gl_pathv[globindex], filename_length);
1336 	FBfPtr = FBfBeg + filename_length;
1337 	globindex++;
1338 	return SUCCESS;
1339 }
1340 
1341 /*****************************************************************************
1342 
1343 	ZTrmnl()
1344 
1345 	This function sets up the input/output of commands.  Usually, that
1346 means the input/output channels to the terminal,  but TECOC might be run
1347 from a command procedure (under VMS) or a script file (under __UNIX__),  and
1348 that possibility must be handled.  In addition,  the handling of interrupts
1349 is found here.
1350 	In general,  this function must:
1351 
1352 		1. Set TIChan so it can be used to read commands
1353 		2. Set TOChan so it can be used for output
1354 		3. handle interrupts
1355 		4. initialize CrType (what kind of terminal it is)
1356 		5. initialize EtFlag (terminal capability bits)
1357 		6. initialize HtSize (number columns terminal has)
1358 		7. initialize VtSize (number rows terminal has)
1359 
1360 	When TECO is started, the terminal will probably be set up for
1361 buffered I/O,  so characters won't be received until a RETURN is hit,  and
1362 they will be automatically echoed.  Set the terminal up for raw I/O,  so each
1363 character is received when it is struck, and no echoing is performed.  Save
1364 the terminal characteristics so when we exit we can reset them (in ZClnUp)
1365 to what they were before we changed them.
1366 
1367 *****************************************************************************/
1368 
CntrlC()1369 static VVOID CntrlC()
1370 {
1371     signal(SIGINT, SIG_IGN);
1372     SupGotCtC = 0;
1373     if (EtFlag & ET_TRAP_CTRL_C) {		/* if user wants it */
1374 	EtFlag &= ~ET_TRAP_CTRL_C;		/* turn off bit */
1375 	SupGotCtC = 1;
1376     } else {					/* user doesn't want it */
1377 	if (EtFlag & ET_MUNG_MODE) {		/* if in MUNG mode */
1378 	    TAbort(EXIT_SUCCESS);
1379 	}
1380 	GotCtC = TRUE;				/* set "stop soon" flag */
1381     }
1382     signal(SIGINT, CntrlC);
1383 }
1384 
1385 
1386 /*
1387  * sighup - what we do if we get a hang up?
1388  */
sighup()1389 static void sighup()
1390 {
1391 	TAbort(EXIT_FAILURE);
1392 }
1393 
1394 
1395 /*
1396  * sigstop - what to do if we get a ^Z
1397  */
sigstop()1398 static void sigstop()
1399 {
1400 	tcsetattr(0, TCSANOW, &out);
1401 	puts("[Suspending...]\r\n");
1402 	kill(getpid(), SIGSTOP);
1403 	puts("[Resuming...]\r\n");
1404 	tcsetattr(0, TCSANOW, &cur);
1405 }
1406 
1407 /*
1408  * ZTrmnl - set up terminal modes
1409  */
ZTrmnl()1410 VVOID ZTrmnl()			/* set up I/O to the terminal */
1411 {
1412 	EtFlag = ET_READ_LOWER |	/* guess: term has lowercase and */
1413 		 ET_EIGHTBIT |          /* terminal uses 8-bit characters */
1414      		 ET_SCOPE;		/* it's a scope, not hardcopy */
1415 
1416 	EzFlag = EZ_NO_VER |		/* don't do VMS-style file versions */
1417 	         EZ_INVCR;		/* don't show little c/r characters */
1418 
1419 /*
1420  * get terminal characteristics and set some signals
1421  */
1422 	if (tcgetattr(0,  &out) != -1)
1423 		tty_set = TRUE;		/* tell ZClnUp to clean up */
1424 	tcgetattr(0, &cur);
1425 #ifdef SIGTSTP
1426 	signal(SIGTSTP, sigstop);	/* call sigstop on stop (control-Z) */
1427 #endif
1428 
1429 /*
1430  * set CBREAK/noECHO/noCRMOD
1431  */
1432 
1433 	cur.c_lflag &= ~ICANON;
1434 	cur.c_lflag &= ~ECHO;
1435 	cur.c_oflag &= ~ONLCR;
1436 	cur.c_iflag &= ~(ICRNL | INLCR);
1437 	tcsetattr(0, TCSANOW, &cur);
1438 
1439 	signal(SIGINT, CntrlC);		/* call CntrlC on interrupt */
1440 	signal(SIGHUP, sighup);		/* call sighup on hang up */
1441 	siginterrupt(SIGINT, 1);
1442 
1443 /*
1444  * set up termcap stuff
1445  */
1446 	tbuf[0] = 0;
1447 // 	if ((ta = getenv("TERM")) == NULL) {	/* get terminal type */
1448 // 		ta = "dumb";
1449 // 	}
1450 // 	tgetent(tbuf, ta);		/* tbuf gets terminal description */
1451 // 	ta = tarea;
1452 // 	ce = xtgetstr("ce",&ta);	/* clear to end of line */
1453 // 	se = xtgetstr("se",&ta);	/* end standout mode (rev. video) */
1454 // 	so = xtgetstr("so",&ta);	/* begin standout mode */
1455 // 	up = xtgetstr("up",&ta);	/* cursor up */
1456 
1457 	CrType = VT102;			/* Let's pretend we are a VT102
1458 					   even though we are really using
1459 					   termcap! */
1460 }
1461 
1462 
1463 /*****************************************************************************
1464 
1465 	ZVrbos()
1466 
1467 	This function displays the verbose form of an error message.
1468 
1469 *****************************************************************************/
1470 
ZVrbos(ErrNum,ErMnem)1471 VVOID ZVrbos(ErrNum, ErMnem)
1472 WORD ErrNum;
1473 char *ErMnem;
1474 {
1475 	char **TmpPtr;
1476 #include "vrbmsg.h"
1477 
1478 	ZDspBf("\r\n",2);
1479 	for (TmpPtr = &ParaTx[StartP[LstErr]]; *TmpPtr; ++TmpPtr) {
1480 		ZDspBf((charptr)*TmpPtr, strlen(*TmpPtr));
1481 		ZDspBf("\r\n",2);
1482 	}
1483 }
1484 
1485 
1486 /*****************************************************************************
1487 
1488 	ZWrBfr()
1489 
1490 	This function writes a buffer to a file,  one line at a time.  It is
1491 passed an output file index and pointers to the beginning and end of the
1492 buffer to be output.
1493 
1494 *****************************************************************************/
1495 
ZWrBfr(OfIndx,BfrBeg,BfrEnd)1496 DEFAULT ZWrBfr(OfIndx, BfrBeg, BfrEnd)
1497 DEFAULT OfIndx;			/* index into OFiles array */
1498 charptr BfrBeg;			/* address of output buffer beginning */
1499 charptr BfrEnd;			/* address of output buffer end */
1500 {
1501 
1502 #if DEBUGGING
1503 	static char *DbgFNm = "ZWrBfr";
1504 	sprintf(DbgSBf,"OfIndx = %d, BfrBeg = %ld, BfrEnd = %ld",
1505 		OfIndx, Zcp2ul(BfrBeg), Zcp2ul(BfrEnd));
1506 	DbgFEn(2,DbgFNm,DbgSBf);
1507 #endif
1508 
1509 /*
1510  * If we're doing Unix-style editing,  where lines are terminated with a
1511  * line feed (newline) instead of a carriage-return/line-feed pair,  then
1512  * we can just fwrite the buffer.  Otherwise,  we have to scan the buffer
1513  * and convert CR/LF pairs to just LF before output.
1514  */
1515 	if (EzFlag & EZ_UNIXNL) {
1516 	    ptrdiff_t bufsiz = BfrEnd-BfrBeg+1;
1517 	    if (fwrite(BfrBeg, sizeof(char), bufsiz,
1518 		       OFiles[OfIndx].OStrem) != bufsiz) {
1519 		ZErMsg();
1520 		ErrMsg(ERR_UWL);
1521 		DBGFEX(2,DbgFNm,"Zfwrite() failed");
1522 		return FAILURE;
1523 	    }
1524 	} else {
1525 	    charptr BfrPtr = BfrBeg;		/* output buffer pointer */
1526 	    while (BfrPtr <= BfrEnd) {
1527 		if (*BfrPtr == CRETRN) {
1528 		    BfrPtr++;
1529 		    if ((BfrPtr > BfrEnd) || (*BfrPtr != LINEFD)) {
1530 			BfrPtr--;
1531 		    }
1532 		}
1533 		if (fputc(*BfrPtr, OFiles[OfIndx].OStrem) == EOF) {
1534 		    ZErMsg();
1535 		    ErrMsg(ERR_UWL);
1536 		    DBGFEX(2,DbgFNm,"FAILURE");
1537 		    return FAILURE;
1538 		}
1539 		++BfrPtr;
1540 	    }
1541 	}
1542 
1543 	DBGFEX(2,DbgFNm,"SUCCESS");
1544 	return SUCCESS;
1545 }
1546 
1547 
1548 /****************************************************************************
1549 
1550 	vernum()
1551 
1552 	From Mark Henderson,  was in a separate file named vernum.c.
1553 
1554 *****************************************************************************/
1555 
1556 
vernum(target)1557 static int vernum(target)
1558 /*
1559 maximum version number of target (similar to VMS mechanism)
1560 return
1561 -3	error - problems other than file not found and can't open directory
1562 -2	error - did not find file
1563 -1	error - cannot open directory containing target
1564  0	file found - no version numbers found
1565  n > 0	version number of highest name;n
1566 */
1567 char *target;
1568 {
1569 	DIR            *dirp;
1570 	int found = 0;	/* file found flag */
1571 	char *ftarget;
1572 	int maxver = 0;
1573 	char *dirname;
1574 	struct dirent  *dp;
1575 	int n;
1576 	char *s;
1577 	n = strlen(target);
1578 	dirname=(char *)malloc(strlen(target)+4);
1579 	ftarget=(char *)malloc(strlen(target)+4);
1580 	strcpy(dirname, target);
1581 	s=dirname + n;
1582 	while (*s != '/' && s > dirname)
1583 		s--;
1584 	if (*s=='/' && s==dirname) { /* must be root directory */
1585 		strcpy(ftarget,s+1);
1586 		*(s+1)='\0';
1587 	} else if (*s=='/')  { /* we have string/name */
1588 		strcpy(ftarget,s+1);
1589 		*s='\0';
1590 	} else { /* must have s==dirname and *s!='/', so current directory */
1591 		strcpy(ftarget,target);
1592 		*dirname='.'; *(dirname+1)='\0';
1593 	}
1594 	dirp = opendir(dirname);
1595 	if (dirp == NULL) {
1596 		ZDspBf("\nerror openning directory ", 26);
1597 		ZDspBf(dirname, strlen(dirname));
1598 		ZDspCh('\n');
1599 		free(dirname);
1600 		free(ftarget);
1601 		return(-1);
1602 	}
1603 	n=strlen(ftarget);
1604 	for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
1605 		if (strncmp(dp->d_name,ftarget,n) == 0) {
1606 			char *suffix = dp->d_name + n;
1607 			if (*suffix == '\0') {
1608 				found = 1;
1609 				if (maxver <= 0)
1610 					maxver = 0;
1611 			}
1612 			if (*suffix == ';') {
1613 				int k;
1614 				found = 1;
1615 				k=atoi(suffix+1);
1616 				if (k<=0) {
1617 					free(dirname);
1618 					free(ftarget);
1619 					closedir(dirp);
1620 					return(-3);
1621 				}
1622 				if (k>=maxver)
1623 					maxver = k;
1624 			}
1625 		}
1626 	}
1627 	free(dirname);
1628 	free(ftarget);
1629 	closedir(dirp);
1630 	return (found) ? maxver : -2;
1631 }
1632