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