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