1 /* $OpenBSD: cl_main.c,v 1.36 2021/10/24 21:24:17 deraadt Exp $ */
2
3 /*-
4 * Copyright (c) 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1993, 1994, 1995, 1996
7 * Keith Bostic. All rights reserved.
8 *
9 * See the LICENSE file for redistribution information.
10 */
11
12 #include "config.h"
13
14 #include <sys/types.h>
15 #include <sys/queue.h>
16
17 #include <bitstring.h>
18 #include <curses.h>
19 #include <err.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <paths.h>
23 #include <signal.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <term.h>
28 #include <termios.h>
29 #include <unistd.h>
30
31 #include "../common/common.h"
32 #include "cl.h"
33
34 GS *__global_list; /* GLOBAL: List of screens. */
35
36 volatile sig_atomic_t cl_sigint;
37 volatile sig_atomic_t cl_sigterm;
38 volatile sig_atomic_t cl_sigwinch;
39
40 static void cl_func_std(GS *);
41 static CL_PRIVATE *cl_init(GS *);
42 static GS *gs_init(void);
43 static int setsig(int, struct sigaction *, void (*)(int));
44 static void sig_end(GS *);
45 static void term_init(char *);
46
47 /*
48 * main --
49 * This is the main loop for the standalone curses editor.
50 */
51 int
main(int argc,char * argv[])52 main(int argc, char *argv[])
53 {
54 CL_PRIVATE *clp;
55 GS *gp;
56 size_t rows, cols;
57 int rval;
58 char *ttype;
59
60 /* Create and initialize the global structure. */
61 __global_list = gp = gs_init();
62
63 /* Create and initialize the CL_PRIVATE structure. */
64 clp = cl_init(gp);
65
66 /*
67 * Initialize the terminal information.
68 *
69 * We have to know what terminal it is from the start, since we may
70 * have to use termcap/terminfo to find out how big the screen is.
71 */
72 if ((ttype = getenv("TERM")) == NULL)
73 ttype = "unknown";
74 term_init(ttype);
75
76 /* Add the terminal type to the global structure. */
77 if ((OG_D_STR(gp, GO_TERM) =
78 OG_STR(gp, GO_TERM) = strdup(ttype)) == NULL)
79 err(1, NULL);
80
81 /* Figure out how big the screen is. */
82 if (cl_ssize(NULL, 0, &rows, &cols, NULL))
83 exit (1);
84
85 /* Add the rows and columns to the global structure. */
86 OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = rows;
87 OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = cols;
88
89 /* Ex wants stdout to be buffered. */
90 (void)setvbuf(stdout, NULL, _IOFBF, 0);
91
92 /* Start catching signals. */
93 if (sig_init(gp, NULL))
94 exit (1);
95
96 /* Run ex/vi. */
97 rval = editor(gp, argc, argv);
98
99 /* Clean up signals. */
100 sig_end(gp);
101
102 /* Clean up the terminal. */
103 (void)cl_quit(gp);
104
105 /*
106 * XXX
107 * Reset the O_MESG option.
108 */
109 if (clp->tgw != TGW_UNKNOWN)
110 (void)cl_omesg(NULL, clp, clp->tgw == TGW_SET);
111
112 /*
113 * XXX
114 * Reset the X11 xterm icon/window name.
115 */
116 if (F_ISSET(clp, CL_RENAME)) {
117 (void)printf(XTERM_RENAME, ttype);
118 (void)fflush(stdout);
119 }
120
121 /* If a killer signal arrived, pretend we just got it. */
122 if (cl_sigterm) {
123 (void)signal(cl_sigterm, SIG_DFL);
124 (void)kill(getpid(), cl_sigterm);
125 /* NOTREACHED */
126 }
127
128 /* Free the global and CL private areas. */
129 #if defined(DEBUG) || defined(PURIFY)
130 free(clp);
131 free(gp);
132 #endif
133
134 exit (rval);
135 }
136
137 /*
138 * gs_init --
139 * Create and partially initialize the GS structure.
140 */
141 static GS *
gs_init(void)142 gs_init(void)
143 {
144 GS *gp;
145
146 /* Allocate the global structure. */
147 if ((gp = calloc(1, sizeof(GS))) == NULL)
148 err(1, NULL);
149
150 return (gp);
151 }
152
153 /*
154 * cl_init --
155 * Create and partially initialize the CL structure.
156 */
157 static CL_PRIVATE *
cl_init(GS * gp)158 cl_init(GS *gp)
159 {
160 CL_PRIVATE *clp;
161 int fd;
162
163 /* Allocate the CL private structure. */
164 if ((clp = calloc(1, sizeof(CL_PRIVATE))) == NULL)
165 err(1, NULL);
166 gp->cl_private = clp;
167
168 /*
169 * Set the CL_STDIN_TTY flag. It's purpose is to avoid setting
170 * and resetting the tty if the input isn't from there. We also
171 * use the same test to determine if we're running a script or
172 * not.
173 */
174 if (isatty(STDIN_FILENO))
175 F_SET(clp, CL_STDIN_TTY);
176 else
177 F_SET(gp, G_SCRIPTED);
178
179 /*
180 * We expect that if we've lost our controlling terminal that the
181 * open() (but not the tcgetattr()) will fail.
182 */
183 if (F_ISSET(clp, CL_STDIN_TTY)) {
184 if (tcgetattr(STDIN_FILENO, &clp->orig) == -1)
185 goto tcfail;
186 } else if ((fd = open(_PATH_TTY, O_RDONLY)) != -1) {
187 if (tcgetattr(fd, &clp->orig) == -1)
188 tcfail: err(1, "tcgetattr");
189 (void)close(fd);
190 }
191
192 /* Initialize the list of curses functions. */
193 cl_func_std(gp);
194
195 return (clp);
196 }
197
198 /*
199 * term_init --
200 * Initialize terminal information.
201 */
202 static void
term_init(char * ttype)203 term_init(char *ttype)
204 {
205 int err;
206
207 /* Set up the terminal database information. */
208 setupterm(ttype, STDOUT_FILENO, &err);
209 switch (err) {
210 case -1:
211 errx(1, "No terminal database found");
212 case 0:
213 errx(1, "%s: unknown terminal type", ttype);
214 }
215 }
216
217 static void
h_int(int signo)218 h_int(int signo)
219 {
220 cl_sigint = 1;
221 }
222
223 static void
h_term(int signo)224 h_term(int signo)
225 {
226 cl_sigterm = signo;
227 }
228
229 static void
h_winch(int signo)230 h_winch(int signo)
231 {
232 cl_sigwinch = 1;
233 }
234
235 /*
236 * sig_init --
237 * Initialize signals.
238 *
239 * PUBLIC: int sig_init(GS *, SCR *);
240 */
241 int
sig_init(GS * gp,SCR * sp)242 sig_init(GS *gp, SCR *sp)
243 {
244 CL_PRIVATE *clp;
245
246 clp = GCLP(gp);
247
248 cl_sigint = 0;
249 cl_sigterm = 0;
250 cl_sigwinch = 0;
251
252 if (sp == NULL) {
253 if (setsig(SIGHUP, &clp->oact[INDX_HUP], h_term) ||
254 setsig(SIGINT, &clp->oact[INDX_INT], h_int) ||
255 setsig(SIGTERM, &clp->oact[INDX_TERM], h_term) ||
256 setsig(SIGWINCH, &clp->oact[INDX_WINCH], h_winch)
257 )
258 err(1, NULL);
259 } else
260 if (setsig(SIGHUP, NULL, h_term) ||
261 setsig(SIGINT, NULL, h_int) ||
262 setsig(SIGTERM, NULL, h_term) ||
263 setsig(SIGWINCH, NULL, h_winch)
264 ) {
265 msgq(sp, M_SYSERR, "signal-reset");
266 }
267 return (0);
268 }
269
270 /*
271 * setsig --
272 * Set a signal handler.
273 */
274 static int
setsig(int signo,struct sigaction * oactp,void (* handler)(int))275 setsig(int signo, struct sigaction *oactp, void (*handler)(int))
276 {
277 struct sigaction act;
278
279 /*
280 * Use sigaction(2), not signal(3), since we don't always want to
281 * restart system calls. The example is when waiting for a command
282 * mode keystroke and SIGWINCH arrives. Besides, you can't portably
283 * restart system calls (thanks, POSIX!).
284 */
285 act.sa_handler = handler;
286 sigemptyset(&act.sa_mask);
287
288 act.sa_flags = 0;
289 return (sigaction(signo, &act, oactp));
290 }
291
292 /*
293 * sig_end --
294 * End signal setup.
295 */
296 static void
sig_end(GS * gp)297 sig_end(GS *gp)
298 {
299 CL_PRIVATE *clp;
300
301 clp = GCLP(gp);
302 (void)sigaction(SIGHUP, NULL, &clp->oact[INDX_HUP]);
303 (void)sigaction(SIGINT, NULL, &clp->oact[INDX_INT]);
304 (void)sigaction(SIGTERM, NULL, &clp->oact[INDX_TERM]);
305 (void)sigaction(SIGWINCH, NULL, &clp->oact[INDX_WINCH]);
306 }
307
308 /*
309 * cl_func_std --
310 * Initialize the standard curses functions.
311 */
312 static void
cl_func_std(GS * gp)313 cl_func_std(GS *gp)
314 {
315 gp->scr_addstr = cl_addstr;
316 gp->scr_attr = cl_attr;
317 gp->scr_baud = cl_baud;
318 gp->scr_bell = cl_bell;
319 gp->scr_busy = NULL;
320 gp->scr_clrtoeol = cl_clrtoeol;
321 gp->scr_cursor = cl_cursor;
322 gp->scr_deleteln = cl_deleteln;
323 gp->scr_event = cl_event;
324 gp->scr_ex_adjust = cl_ex_adjust;
325 gp->scr_fmap = cl_fmap;
326 gp->scr_insertln = cl_insertln;
327 gp->scr_keyval = cl_keyval;
328 gp->scr_move = cl_move;
329 gp->scr_msg = NULL;
330 gp->scr_optchange = cl_optchange;
331 gp->scr_refresh = cl_refresh;
332 gp->scr_rename = cl_rename;
333 gp->scr_screen = cl_screen;
334 gp->scr_suspend = cl_suspend;
335 gp->scr_usage = cl_usage;
336 }
337