1 /* @(#)ved.c	1.94 21/08/20 Copyright 1984, 85, 86, 88, 89, 97, 2000-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)ved.c	1.94 21/08/20 Copyright 1984, 85, 86, 88, 89, 97, 2000-2021 J. Schilling";
6 #endif
7 /*
8  *	VED Visual EDitor
9  *
10  *	Copyright (c) 1984, 85, 86, 88, 89, 97, 2000-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 /*
27  * This is the the command line parser and main loop ved.
28  *
29  * The following include files are available:
30  *
31  * buffer.h	- for users of the paged virtual memory package
32  * func.h	- definitions for exported functions (included by ved.h)
33  * map.h 	- for users ot the mapper package
34  * movedot.h	- definitions for functions used in movedot.c
35  * terminal.h	- external interface to the TERMCAP package
36  * ttys.h	- internal interface to the TERMCAP package
37  * ved.h	- main ved include file (includes func.h)
38  */
39 #define	GT_COMERR		/* #define comerr gtcomerr */
40 #define	GT_ERROR		/* #define error gterror   */
41 #include "ved.h"
42 #include "buffer.h"
43 #include "movedot.h"
44 #include "terminal.h"
45 #include "version.h"
46 #include <schily/errno.h>
47 #include <schily/signal.h>
48 #include <schily/sigblk.h>
49 
50 EXPORT	char	ved_version[] = VERSION;
51 EXPORT	int	mflag;		/* if > 0 : take characters from macro	    */
52 EXPORT	int	ReadOnly;	/* if > 0 : do not allow to write back mods */
53 
54 EXPORT	int	nobak = 0;	/* Es wird kein ___.bak angelegt	    */
55 EXPORT	int	nolock = 0;	/* Es wird kein record locking versucht	    */
56 EXPORT	int	dotags = 0;	/* "Filename" ist ctag			    */
57 EXPORT	Llong	startline = -1;	/* Cursorposition Bei Start		    */
58 EXPORT	int	noedtmp = 0;	/* Kein .vedtmp erzeugen		    */
59 EXPORT	int	recover = 0;	/* altes File reparieren		    */
60 EXPORT	BOOL	autodos = TRUE;	/* wp->dosmode aus isdos() bestimmen	    */
61 
62 EXPORT	int	nfiles;		/* Anzahl der zu editierenden Files	    */
63 EXPORT	int	fileidx = 0;	/* Index des editierten Files		    */
64 EXPORT	Uchar	**files;	/* Array der zu editierenden Files	    */
65 EXPORT	Uchar	curfname[FNAMESIZE]; /* global file name storage space	    */
66 
67 EXPORT	int	pid;		/* process id used for unique tmp file names */
68 
69 #ifndef	BFSIZE
70 LOCAL	Uchar bufferout[BUFSIZ]; /* To inhibit line buffering on stdout	    */
71 #endif
72 
73 EXPORT	ewin_t	rootwin;
74 
75 LOCAL	void	usage		__PR((int exitcode));
76 EXPORT	int	main		__PR((int ac, char **av));
77 LOCAL	int	handlesignal	__PR((void));
78 LOCAL	void	hupintr		__PR((int sig));
79 LOCAL	void	exintr		__PR((int sig));
80 EXPORT	void	settmodes	__PR((ewin_t *wp));
81 EXPORT	void	rsttmodes	__PR((ewin_t *wp));
82 LOCAL	Uchar	*gethelpfile	__PR((void));
83 LOCAL	Uchar	*_gethelpfile	__PR((char *name));
84 
85 LOCAL void
usage(exitcode)86 usage(exitcode)
87 	int	exitcode;
88 {
89 	error("Usage:	ved [options]  [filename_1...filename_n]\n");
90 	error("Options:\n");
91 	error("	-version	Print version information and exit.\n");
92 	error("	-vhelp		Gives on-line Help.\n");
93 	error("	-readonly	Take readonly Mode.\n");
94 	error("	buffers=#	# of %d KB Incore Buffers (default is %d).\n",
95 						BUFFERSIZE/1024, MAXBUFFERS);
96 	error("	-nobak		Don't create filename.bak.\n");
97 	error("	-nolock		Don't lock the edited file for writing.\n");
98 	error("	-edtmp		Don't write to .vedtmp.\n");
99 	error("	-raw8		Don't expand 8bit chars.\n");
100 	error("	-dos		Map '\\n' to '\\r\\n', supress '\\r' in '\\r\\n'.\n");
101 	error("	-nodos		Do not map '\\n' to '\\r\\n'.\n");
102 	error("	-tag		filename_1 is a ctag.\n");
103 	error("	wrapmargin=#	set default wrapmargin to #.\n");
104 	error("	maxlinelen=#	set max line length for autowrap to #.\n");
105 	error("	+#		Start editing at line #.\n");
106 	error("	s=searchstr	Start editing with search to 'searchstr'.\n");
107 	error("	-Recover	Recover Session from filename.\n\n");
108 	error("	Options may be abbreviated by their first letter.\n");
109 	error("	If no file name is specified, the last edited file is used.\n");
110 	error("	Typing the command '^X ^H' gives on line help.\n");
111 	error("	Call 'ved -vhelp' to get the on-line help from command line.\n");
112 	exit(exitcode);
113 
114 }
115 
116 LOCAL	char	opts[] =
117 "help,version,readonly+,r+,vhelp,vedhelp,v,buffers#,b#,nobak,n,nolock,raw8,dos,d,nodos,tag,t,wrapmargin#,w#,maxlinelen#,maxll#,+#LL,s*,edtmp,e,Recover,R";
118 
119 EXPORT int
main(ac,av)120 main(ac, av)
121 	int	ac;
122 	char	*av[];
123 {
124 	epos_t		pos = (epos_t)0;
125 	int		cac;
126 	char * const	*cav;
127 	BOOL		help = FALSE;
128 	BOOL		prvers = FALSE;
129 	BOOL		Vhelp = FALSE;
130 	int		buffers = -1;
131 	Uchar		*errstr;
132 	SIGBLK		sigblk;
133 	SIGBLK		sigfirst;
134 	int		err;
135 	char		*searchstr = NULL;
136 	ewin_t		*wp = &rootwin;
137 	BOOL		no_dos = FALSE;
138 
139 	save_args(ac, av);
140 
141 	(void) setlocale(LC_ALL, "");
142 
143 #ifdef  USE_NLS
144 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
145 #define	TEXT_DOMAIN "ved"	/* Use this only if it weren't */
146 #endif
147 	{ char	*dir;
148 	dir = searchfileinpath("share/locale", F_OK,
149 					SIP_ANY_FILE|SIP_NO_PATH, NULL);
150 	if (dir)
151 		(void) bindtextdomain(TEXT_DOMAIN, dir);
152 	else
153 #if defined(PROTOTYPES) && defined(INS_BASE)
154 	(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
155 #else
156 	(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
157 #endif
158 	(void) textdomain(TEXT_DOMAIN);
159 	}
160 #endif 	/* USE_NLS */
161 
162 	cav = av, cac = ac;
163 	++cav, --cac;
164 
165 	if (getallargs(&cac, &cav, opts,
166 			&help, &prvers,
167 			&ReadOnly, &ReadOnly,
168 			&Vhelp, &Vhelp, &Vhelp,
169 			&buffers, &buffers,
170 			&nobak, &nobak,
171 			&nolock,
172 			&wp->raw8,
173 			&wp->dosmode, &wp->dosmode, &no_dos,
174 			&dotags, &dotags,
175 			&wp->wrapmargin, &wp->wrapmargin,
176 			&wp->maxlinelen, &wp->maxlinelen,
177 			&startline, &searchstr,
178 			&noedtmp, &noedtmp,
179 			&recover, &recover) < 0) {
180 		errmsgno(EX_BAD, "Bad flag '%s'.\n", cav[0]);
181 		usage(EX_BAD);
182 	}
183 	if (help)
184 		usage(0);
185 	if (prvers) {
186 		gtprintf("ved %s %s (%s-%s-%s)\n\n",
187 			ved_version, VERSION_DATE,
188 			HOST_CPU, HOST_VENDOR, HOST_OS);
189 		gtprintf("Copyright (C) 1984, 85, 86, 88, 89, 97, 2000-2021 %s\n", _("J�rg Schilling"));
190 		gtprintf("This is free software; see the source for copying conditions.  There is NO\n");
191 		gtprintf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
192 		exit(0);
193 	}
194 	if (Vhelp) {
195 		ReadOnly = 2;		/* Do not even allow QUIT !	*/
196 		noedtmp = 1;
197 	}
198 	if (searchstr) {
199 		extern Uchar sbuf[];
200 		extern int sflg;
201 		strcpy(C sbuf, searchstr);
202 		sflg = strlen(C sbuf);
203 	}
204 	if (no_dos)
205 		wp->dosmode = FALSE;
206 	if (no_dos || wp->dosmode)
207 		autodos = FALSE;
208 	/*
209 	 * Look for being called as ved-e or ved-w
210 	 */
211 	if ((errstr = (Uchar *)strrchr(av[0], '/')) == NULL)
212 		errstr = (Uchar *)av[0];
213 	if ((errstr = (Uchar *)strchr(C errstr, '-')) != NULL) {
214 		errstr++;
215 		if (strchr((char *)errstr, 'e'))
216 			noedtmp = 1;
217 		if (strchr((char *)errstr, 'w') && wp->maxlinelen == 0)
218 			wp->maxlinelen = 78;	/* Try to follow RFC-2822 */
219 	}
220 	cav = av, cac = ac;
221 	++cav, --cac;
222 
223 	file_raise((FILE *)0, FALSE);
224 	signal(SIGINT, exintr);
225 	get_modes(wp);			/* Get old ttymodes from tty driver  */
226 #ifdef	SIGHUP
227 	signal(SIGHUP, hupintr);
228 #endif
229 	signal(SIGTERM, hupintr);
230 	starthandlecond(&sigfirst);
231 	handlecond("any_other", &sigblk, (handlefunc_t)handlesignal, 0L);
232 
233 	/*
234 	 * Initialize the TERMCAP package
235 	 */
236 	if ((errstr = t_start(wp)) != NULL)
237 		exitcomerr(wp, "%s\r\n", errstr);
238 	if (wp->llen <= 0 || wp->psize <= 0)	/* Paranoia: check screen size */
239 		excomerrno(wp, EX_BAD,
240 		"Bad line length or page size in terminal descriptor.\r\n");
241 	{ extern char	HC;
242 	if (HC)
243 		excomerrno(wp, EX_BAD,
244 		"Cannot work on a hardcopy terminal - yet.\r\n");
245 	}
246 
247 	wp->markwrap = TRUE;
248 	wp->magic = TRUE;
249 	wp->eflags = COLUPDATE|SAVEDEL;
250 	wp->number = 1;			/* curnum is copied from this mult # master */
251 	wp->curnum = 1;			/* mult # fot next edit command	    */
252 	wp->tabstop = 8;		/* Breite eines 'tabs'		    */
253 	wp->optline = wp->psize/2;	/* set standard screen adjustment   */
254 	wp->curfd = -1;			/* Kein File writelock vorhanden    */
255 	set_modes();			/* set "ved" ttymodes		    */
256 	t_begin();			/* init terminal state for editing  */
257 	pid = getpid();			/* set pid for unique temp file names */
258 	initnum();			/* init number commands		    */
259 
260 	if (Vhelp) {
261 		/*
262 		 * Edit help file.
263 		 */
264 		wp->curfile = gethelpfile();
265 
266 	} else if (recover) {
267 		/*
268 		 * Run Recover session.
269 		 */
270 		if (getfiles(&cac, &cav, opts) > 0) {
271 			openrecoverfile(wp, cav[0]);
272 			wp->curfile = getrecoverfile(&pos, &wp->column);
273 			errmsgno(EX_BAD, "Recoverfile: %s.\r\n", wp->curfile);
274 		} else {
275 			excomerrno(wp, EX_BAD,
276 			"Must have name of File from which to recover.\n");
277 		}
278 
279 	} else if (getfiles(&cac, &cav, opts) > 0) {
280 		/*
281 		 * We found filenames on the command line.
282 		 * Save them in the 'files' array.
283 		 * Save the current filename in .vedtmp.
284 		 */
285 		wp->curfile = UC cav[0];
286 		files = UCP cav;
287 		nfiles = cac;
288 
289 		if (get_vedtmp(wp, &pos, &wp->column)) {
290 			if (startline >= 0 || searchstr) { /* XXX Pfusch */
291 				pos = (epos_t)0;
292 				wp->column = 0;
293 			}
294 		}
295 		/* XXX opensyserr()/openerrmsg() Probleme */
296 		if (!noedtmp && !dotags)
297 			put_vedtmp(wp, FALSE);
298 
299 	} else {
300 		/*
301 		 * There were no filenames on the command line
302 		 * and -vhelp was not set.
303 		 * Try to get the filename from .vedtmp.
304 		 */
305 		if (get_vedtmp(wp, &pos, &wp->column)) {
306 			if (startline >= 0 || searchstr) { /* XXX Pfusch */
307 				pos = (epos_t)0;
308 				wp->column = 0;
309 			}
310 		}
311 		wp->curfile = curfname;
312 	}
313 	if (streql(getenv("SLASH"), "off") && strchr(C curfname, '/')) {
314 		if (wp->curfile != 0)
315 			*wp->curfile = '\0';
316 	}
317 
318 #ifndef	NO_SCRATCHFILE
319 	if (wp->curfile == curfname && curfname[0] == '\0') {
320 		strcpy(C curfname, "vedXXXXXX");
321 		mktemp(C curfname);
322 		ReadOnly = 2;		/* Do not even allow QUIT !	*/
323 		noedtmp = 1;
324 	}
325 #endif
326 
327 	/*
328 	 * We had problems to get a proper filename setup.
329 	 * XXX There might be problems with macros if we introduce a "scratch"
330 	 * XXX file editing here. Check if this is possible.
331 	 */
332 	if (wp->curfile == 0 || *wp->curfile == '\0') {
333 		excomerrno(wp, EX_BAD,
334 			"No name in .vedtmp, need filename to edit.\n");
335 	}
336 
337 	if (isatty(fdown(stdin)))	/* don't buffer stdin if it is a tty */
338 		setbuf(stdin, NULL);	/* so we get no pre read !!!	    */
339 #ifdef	BFSIZE
340 	setbuf(stdout, NULL);		/* Buffering on stdout is done in _bb */
341 #else
342 	setbuf(stdout, bufferout);	/* Inhibit line buffering on stdout */
343 #endif
344 
345 	init_charset(wp);		/* Initialisierung der char Laengen */
346 					/* und Strings			    */
347 
348 	init_binding();			/* Initialisierung der Bindung von  */
349 					/* Funktioinen an Zeichen	    */
350 
351 	tmpsetup();			/* Init several tmp file names	    */
352 
353 	/*
354 	 * Initialize the paged virtual memory and the status line.
355 	 */
356 	initbuffers(wp, buffers);
357 	bufdebug(wp);
358 	initmessage(wp);
359 #ifdef	DO_VERSION_INFO
360 	defaultinfo(wp, UC "ved-1.8");
361 #endif
362 
363 	if (nfiles > 1)
364 		writemsg(wp, "%d files to edit.", nfiles);
365 
366 	if (dotags) {
367 		switch (gettag(&wp->curfile)) {
368 
369 		case -1:
370 			termbuffers(wp);
371 			excomerrno(wp, EX_BAD, "No such Tag.\n");
372 			/* NOTREACHED */
373 
374 		case  0:
375 			err = geterrno();
376 			termbuffers(wp);
377 			excomerrno(wp, err, "Cannot open 'tags'.\n");
378 			/* NOTREACHED */
379 
380 		case  1:
381 			strncpy(C curfname, C wp->curfile, sizeof (curfname));
382 			curfname[sizeof (curfname)-1] = '\0';
383 			wp->curfile = curfname;
384 		}
385 	}
386 	/*
387 	 * Load the file into the paged virtual memory.
388 	 */
389 	wp->curftime = gftime(C wp->curfile);
390 	if (!loadfile(wp, wp->curfile, TRUE)) {
391 		if ((nfiles > 0 || Vhelp) &&
392 		    (geterrno() != ENOENT || ReadOnly > 0)) {
393 			err = geterrno();
394 			termbuffers(wp);
395 			excomerrno(wp, err, "Cannot open '%s'.\n", wp->curfile);
396 		}
397 	}
398 	if (pos < 0 || pos > wp->eof) {
399 		if (recover) {
400 			termbuffers(wp);
401 			excomerrno(wp, EX_BAD,
402 				"Bad File to recover (file too small).\n");
403 		} else {
404 			pos = (epos_t)0;
405 			wp->column = 0;
406 			writeerr(wp, "BAD POS IN .vedtmp");
407 			sleep(1);	/* Overwritten by "READ ONLY ..." */
408 		}
409 	}
410 	if (autodos)
411 		wp->dosmode = isdos(wp);
412 
413 	if (dotags && (pos = searchtag(wp, (epos_t)0)) > wp->eof)
414 		pos = (epos_t)0;
415 
416 	namemsg(wp->curfile);
417 	CLEAR_SCREEN(wp);
418 	refreshmsg(wp);
419 
420 	tmpopen(wp);			/* Open several temporary files	    */
421 
422 	settakename(wp, UC "default");
423 	if (wp->dosmode)
424 		writemsg(wp, "DOS MODE");
425 	(void) wrtcheck(wp, FALSE);
426 	writelockmsg(wp);
427 	writenum(wp, wp->number);
428 	macro_init(wp);
429 	map_init();
430 
431 	/*
432 	 * Go to the right place in the file and update the screen.
433 	 */
434 	wp->dot = pos;
435 	if (startline > 1 && wp->dot == 0)
436 		wp->dot = forwline(wp, wp->dot, (ecnt_t)startline - 1);
437 	if (searchstr)
438 		vagainsrch(wp);
439 	newwindow(wp);
440 	flush();
441 	wp->modflg = 0;
442 	newprot(wp);
443 	edit(wp);
444 
445 	unhandlecond(&sigfirst);
446 	return (0);	/* Keep lint happy */
447 }
448 
449 /*
450  * Handle a software signal, do a clean exit.
451  */
452 LOCAL int
handlesignal()453 handlesignal()
454 {
455 	rsttmodes(&rootwin);	/* XXX -> (ewin_t *)0 ??? */
456 	return (0);
457 }
458 
459 /*
460  * Signal handler for signals that kill us.
461  * Used by SIGHUP & SIGTERM.
462  */
463 /* ARGSUSED */
464 LOCAL void
hupintr(sig)465 hupintr(sig)
466 	int	sig;
467 {
468 	set_oldmodes();
469 	tmpcleanup(&rootwin, FALSE);	/* XXX -> (ewin_t *)0 ??? */
470 
471 	exit(sig);
472 }
473 
474 /*
475  * Catch signals while ved is in startup.
476  * Let us silently die when a signal is received
477  * before the file is completely loaded.
478  */
479 /* ARGSUSED */
480 LOCAL void
exintr(sig)481 exintr(sig)
482 	int	sig;
483 {
484 	eexit(&rootwin);	/* XXX -> (ewin_t *)0 ??? */
485 	exit(EX_BAD);
486 }
487 
488 /*
489  * Get and save current tty modes, set editing tty modes
490  * and set editing terminal state.
491  */
492 EXPORT void
settmodes(wp)493 settmodes(wp)
494 	ewin_t	*wp;
495 {
496 	get_modes(wp);
497 	set_modes();
498 	t_begin();			/* init terminal state for editing   */
499 }
500 
501 /*
502  * Put cursor to the end of the screen, set back terminal state
503  * and reset tty modes to saved values.
504  */
505 EXPORT void
rsttmodes(wp)506 rsttmodes(wp)
507 	ewin_t	*wp;
508 {
509 	if (wp == NULL)
510 		wp = &rootwin;
511 
512 	if (f_move_cursor) {
513 		MOVE_CURSOR(wp, wp->psize, 0);
514 		CLEAR_TO_EOF_LINE(wp);
515 	} else {
516 		output((Uchar *) "\n");
517 	}
518 	t_done();			/* put terminal to non-editing state */
519 	flush();
520 	set_oldmodes();
521 }
522 
523 /*
524  * Search for the ved.help file in the PATH of the user.
525  * Assume that the file is ... bin/../man/help/ved.hlp
526  */
527 LOCAL Uchar *
gethelpfile()528 gethelpfile()
529 {
530 	Uchar	*hfile = HELPFILE;
531 	Uchar	*name;
532 
533 	name = _gethelpfile("share/man/help/ved.help");
534 	if (name == NULL)
535 		name = _gethelpfile("man/help/ved.help");
536 
537 	if (name == NULL)
538 		return (hfile);
539 
540 	return (name);
541 }
542 
543 LOCAL Uchar *
_gethelpfile(name)544 _gethelpfile(name)
545 	char	*name;
546 {
547 	char	*path = getenv("PATH");
548 	Uchar	*nbuf = curfname;	/* Construct path in curfname space */
549 	Uchar	*np;
550 
551 	if (path == NULL)
552 		return (NULL);
553 
554 	for (;;) {
555 		np = nbuf;
556 		while (*path != ':' && *path != '\0' &&
557 		    np < &nbuf[sizeof (curfname)-sizeof (name)-1])
558 				*np++ = *path++;
559 		*np = '\0';
560 		while (np > nbuf && np[-1] == '/')
561 			*--np = '\0';
562 		if (np >= &nbuf[4] && streql(C &np[-4], "/bin"))
563 			np = &np[-4];
564 		*np++ = '/';
565 		*np   = '\0';
566 		strcpy(C np, name);
567 
568 		if (readable(nbuf))
569 			return (nbuf);
570 
571 		if (*path == '\0')
572 			break;
573 		path++;
574 	}
575 	return (NULL);
576 }
577