1 /*-
2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1993, 1994, 1995, 1996
5 * Keith Bostic. All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10 #include "config.h"
11
12 #ifndef lint
13 static const char sccsid[] = "@(#)cl_main.c 10.36 (Berkeley) 10/14/96";
14 #endif /* not lint */
15
16 #include <sys/types.h>
17 #include <sys/queue.h>
18
19 #include <bitstring.h>
20 #include <curses.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <termios.h>
28 #include <unistd.h>
29
30 #include "../common/common.h"
31 #ifdef RUNNING_IP
32 #include "../ip/ip.h"
33 #endif
34 #include "cl.h"
35 #include "pathnames.h"
36
37 GS *__global_list; /* GLOBAL: List of screens. */
38 sigset_t __sigblockset; /* GLOBAL: Blocked signals. */
39
40 static void cl_func_std __P((GS *));
41 static CL_PRIVATE *cl_init __P((GS *));
42 static GS *gs_init __P((char *));
43 static void perr __P((char *, char *));
44 static int setsig __P((int, struct sigaction *, void (*)(int)));
45 static void sig_end __P((GS *));
46 static void term_init __P((char *, char *));
47
48 /*
49 * main --
50 * This is the main loop for the standalone curses editor.
51 */
52 int
main(argc,argv)53 main(argc, argv)
54 int argc;
55 char *argv[];
56 {
57 static int reenter;
58 CL_PRIVATE *clp;
59 GS *gp;
60 size_t rows, cols;
61 int rval;
62 char *ip_arg, **p_av, **t_av, *ttype;
63
64 /* If loaded at 0 and jumping through a NULL pointer, stop. */
65 if (reenter++)
66 abort();
67
68 /* Create and initialize the global structure. */
69 __global_list = gp = gs_init(argv[0]);
70
71 /*
72 * Strip out any arguments that vi isn't going to understand. There's
73 * no way to portably call getopt twice, so arguments parsed here must
74 * be removed from the argument list.
75 */
76 #ifdef RUNNING_IP
77 ip_arg = NULL;
78 for (p_av = t_av = argv;;) {
79 if (*t_av == NULL) {
80 *p_av = NULL;
81 break;
82 }
83 if (!strcmp(*t_av, "--")) {
84 while ((*p_av++ = *t_av++) != NULL);
85 break;
86 }
87 if (!memcmp(*t_av, "-I", sizeof("-I") - 1)) {
88 if (t_av[0][2] != '\0') {
89 ip_arg = t_av[0] + 2;
90 ++t_av;
91 --argc;
92 continue;
93 }
94 if (t_av[1] != NULL) {
95 ip_arg = t_av[1];
96 t_av += 2;
97 argc -= 2;
98 continue;
99 }
100 }
101 *p_av++ = *t_av++;
102 }
103
104 /*
105 * If we're being called as an editor library, we're done here, we
106 * get loaded with the curses screen, we don't share much code.
107 */
108 if (ip_arg != NULL)
109 exit (ip_main(argc, argv, gp, ip_arg));
110 #else
111 ip_arg = argv[0];
112 #endif
113
114 /* Create and initialize the CL_PRIVATE structure. */
115 clp = cl_init(gp);
116
117 /*
118 * Initialize the terminal information.
119 *
120 * We have to know what terminal it is from the start, since we may
121 * have to use termcap/terminfo to find out how big the screen is.
122 */
123 if ((ttype = getenv("TERM")) == NULL)
124 ttype = "unknown";
125 term_init(gp->progname, ttype);
126
127 /* Add the terminal type to the global structure. */
128 if ((OG_D_STR(gp, GO_TERM) =
129 OG_STR(gp, GO_TERM) = strdup(ttype)) == NULL)
130 perr(gp->progname, NULL);
131
132 /* Figure out how big the screen is. */
133 if (cl_ssize(NULL, 0, &rows, &cols, NULL))
134 exit (1);
135
136 /* Add the rows and columns to the global structure. */
137 OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = rows;
138 OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = cols;
139
140 /* Ex wants stdout to be buffered. */
141 (void)setvbuf(stdout, NULL, _IOFBF, 0);
142
143 /* Start catching signals. */
144 if (sig_init(gp, NULL))
145 exit (1);
146
147 /* Run ex/vi. */
148 rval = editor(gp, argc, argv);
149
150 /* Clean up signals. */
151 sig_end(gp);
152
153 /* Clean up the terminal. */
154 (void)cl_quit(gp);
155
156 /*
157 * XXX
158 * Reset the O_MESG option.
159 */
160 if (clp->tgw != TGW_UNKNOWN)
161 (void)cl_omesg(NULL, clp, clp->tgw == TGW_SET);
162
163 /*
164 * XXX
165 * Reset the X11 xterm icon/window name.
166 */
167 if (F_ISSET(clp, CL_RENAME)) {
168 (void)printf(XTERM_RENAME, ttype);
169 (void)fflush(stdout);
170 }
171
172 /* If a killer signal arrived, pretend we just got it. */
173 if (clp->killersig) {
174 (void)signal(clp->killersig, SIG_DFL);
175 (void)kill(getpid(), clp->killersig);
176 /* NOTREACHED */
177 }
178
179 /* Free the global and CL private areas. */
180 #if defined(DEBUG) || defined(PURIFY) || defined(LIBRARY)
181 free(clp);
182 free(gp);
183 #endif
184
185 exit (rval);
186 }
187
188 /*
189 * gs_init --
190 * Create and partially initialize the GS structure.
191 */
192 static GS *
gs_init(name)193 gs_init(name)
194 char *name;
195 {
196 CL_PRIVATE *clp;
197 GS *gp;
198 char *p;
199
200 /* Figure out what our name is. */
201 if ((p = strrchr(name, '/')) != NULL)
202 name = p + 1;
203
204 /* Allocate the global structure. */
205 CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
206 if (gp == NULL)
207 perr(name, NULL);
208
209
210 gp->progname = name;
211 return (gp);
212 }
213
214 /*
215 * cl_init --
216 * Create and partially initialize the CL structure.
217 */
218 static CL_PRIVATE *
cl_init(gp)219 cl_init(gp)
220 GS *gp;
221 {
222 CL_PRIVATE *clp;
223 int fd;
224
225 /* Allocate the CL private structure. */
226 CALLOC_NOMSG(NULL, clp, CL_PRIVATE *, 1, sizeof(CL_PRIVATE));
227 if (clp == NULL)
228 perr(gp->progname, NULL);
229 gp->cl_private = clp;
230
231 /*
232 * Set the CL_STDIN_TTY flag. It's purpose is to avoid setting
233 * and resetting the tty if the input isn't from there. We also
234 * use the same test to determine if we're running a script or
235 * not.
236 */
237 if (isatty(STDIN_FILENO))
238 F_SET(clp, CL_STDIN_TTY);
239 else
240 F_SET(gp, G_SCRIPTED);
241
242 /*
243 * We expect that if we've lost our controlling terminal that the
244 * open() (but not the tcgetattr()) will fail.
245 */
246 if (F_ISSET(clp, CL_STDIN_TTY)) {
247 if (tcgetattr(STDIN_FILENO, &clp->orig) == -1)
248 goto tcfail;
249 } else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
250 if (tcgetattr(fd, &clp->orig) == -1) {
251 tcfail: perr(gp->progname, "tcgetattr");
252 exit (1);
253 }
254 (void)close(fd);
255 }
256
257 /* Initialize the list of curses functions. */
258 cl_func_std(gp);
259
260 return (clp);
261 }
262
263 /*
264 * term_init --
265 * Initialize terminal information.
266 */
267 static void
term_init(name,ttype)268 term_init(name, ttype)
269 char *name, *ttype;
270 {
271 int err;
272
273 /* Set up the terminal database information. */
274 setupterm(ttype, STDOUT_FILENO, &err);
275 switch (err) {
276 case -1:
277 (void)fprintf(stderr,
278 "%s: No terminal database found\n", name);
279 exit (1);
280 case 0:
281 (void)fprintf(stderr,
282 "%s: %s: unknown terminal type\n", name, ttype);
283 exit (1);
284 }
285 }
286
287 #define GLOBAL_CLP \
288 CL_PRIVATE *clp = GCLP(__global_list);
289 static void
h_hup(signo)290 h_hup(signo)
291 int signo;
292 {
293 GLOBAL_CLP;
294
295 F_SET(clp, CL_SIGHUP);
296 clp->killersig = SIGHUP;
297 }
298
299 static void
h_int(signo)300 h_int(signo)
301 int signo;
302 {
303 GLOBAL_CLP;
304
305 F_SET(clp, CL_SIGINT);
306 }
307
308 static void
h_term(signo)309 h_term(signo)
310 int signo;
311 {
312 GLOBAL_CLP;
313
314 F_SET(clp, CL_SIGTERM);
315 clp->killersig = SIGTERM;
316 }
317
318 static void
h_winch(signo)319 h_winch(signo)
320 int signo;
321 {
322 GLOBAL_CLP;
323
324 F_SET(clp, CL_SIGWINCH);
325 }
326 #undef GLOBAL_CLP
327
328 /*
329 * sig_init --
330 * Initialize signals.
331 *
332 * PUBLIC: int sig_init __P((GS *, SCR *));
333 */
334 int
sig_init(gp,sp)335 sig_init(gp, sp)
336 GS *gp;
337 SCR *sp;
338 {
339 CL_PRIVATE *clp;
340
341 clp = GCLP(gp);
342
343 if (sp == NULL) {
344 (void)sigemptyset(&__sigblockset);
345 if (sigaddset(&__sigblockset, SIGHUP) ||
346 setsig(SIGHUP, &clp->oact[INDX_HUP], h_hup) ||
347 sigaddset(&__sigblockset, SIGINT) ||
348 setsig(SIGINT, &clp->oact[INDX_INT], h_int) ||
349 sigaddset(&__sigblockset, SIGTERM) ||
350 setsig(SIGTERM, &clp->oact[INDX_TERM], h_term)
351 #ifdef SIGWINCH
352 ||
353 sigaddset(&__sigblockset, SIGWINCH) ||
354 setsig(SIGWINCH, &clp->oact[INDX_WINCH], h_winch)
355 #endif
356 ) {
357 perr(gp->progname, NULL);
358 return (1);
359 }
360 } else
361 if (setsig(SIGHUP, NULL, h_hup) ||
362 setsig(SIGINT, NULL, h_int) ||
363 setsig(SIGTERM, NULL, h_term)
364 #ifdef SIGWINCH
365 ||
366 setsig(SIGWINCH, NULL, h_winch)
367 #endif
368 ) {
369 msgq(sp, M_SYSERR, "signal-reset");
370 }
371 return (0);
372 }
373
374 /*
375 * setsig --
376 * Set a signal handler.
377 */
378 static int
setsig(signo,oactp,handler)379 setsig(signo, oactp, handler)
380 int signo;
381 struct sigaction *oactp;
382 void (*handler) __P((int));
383 {
384 struct sigaction act;
385
386 /*
387 * Use sigaction(2), not signal(3), since we don't always want to
388 * restart system calls. The example is when waiting for a command
389 * mode keystroke and SIGWINCH arrives. Besides, you can't portably
390 * restart system calls (thanks, POSIX!). On the other hand, you
391 * can't portably NOT restart system calls (thanks, Sun!). SunOS
392 * used SA_INTERRUPT as their extension to NOT restart read calls.
393 * We sure hope nobody else used it for anything else. Mom told me
394 * there'd be days like this. She just never told me that there'd
395 * be so many.
396 */
397 act.sa_handler = handler;
398 sigemptyset(&act.sa_mask);
399
400 #ifdef SA_INTERRUPT
401 act.sa_flags = SA_INTERRUPT;
402 #else
403 act.sa_flags = 0;
404 #endif
405 return (sigaction(signo, &act, oactp));
406 }
407
408 /*
409 * sig_end --
410 * End signal setup.
411 */
412 static void
sig_end(gp)413 sig_end(gp)
414 GS *gp;
415 {
416 CL_PRIVATE *clp;
417
418 clp = GCLP(gp);
419 (void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]);
420 (void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]);
421 (void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]);
422 #ifdef SIGWINCH
423 (void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]);
424 #endif
425 }
426
427 /*
428 * cl_func_std --
429 * Initialize the standard curses functions.
430 */
431 static void
cl_func_std(gp)432 cl_func_std(gp)
433 GS *gp;
434 {
435 gp->scr_addstr = cl_addstr;
436 gp->scr_attr = cl_attr;
437 gp->scr_baud = cl_baud;
438 gp->scr_bell = cl_bell;
439 gp->scr_busy = NULL;
440 gp->scr_clrtoeol = cl_clrtoeol;
441 gp->scr_cursor = cl_cursor;
442 gp->scr_deleteln = cl_deleteln;
443 gp->scr_event = cl_event;
444 gp->scr_ex_adjust = cl_ex_adjust;
445 gp->scr_fmap = cl_fmap;
446 gp->scr_insertln = cl_insertln;
447 gp->scr_keyval = cl_keyval;
448 gp->scr_move = cl_move;
449 gp->scr_msg = NULL;
450 gp->scr_optchange = cl_optchange;
451 gp->scr_refresh = cl_refresh;
452 gp->scr_rename = cl_rename;
453 gp->scr_screen = cl_screen;
454 gp->scr_suspend = cl_suspend;
455 gp->scr_usage = cl_usage;
456 }
457
458 /*
459 * perr --
460 * Print system error.
461 */
462 static void
perr(name,msg)463 perr(name, msg)
464 char *name, *msg;
465 {
466 (void)fprintf(stderr, "%s:", name);
467 if (msg != NULL)
468 (void)fprintf(stderr, "%s:", msg);
469 (void)fprintf(stderr, "%s\n", strerror(errno));
470 exit(1);
471 }
472