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