xref: /openbsd/usr.bin/mg/main.c (revision 097a140d)
1 /*	$OpenBSD: main.c,v 1.89 2021/03/20 09:00:49 lum Exp $	*/
2 
3 /* This file is in the public domain. */
4 
5 /*
6  *	Mainline.
7  */
8 
9 #include <sys/queue.h>
10 #include <err.h>
11 #include <limits.h>
12 #include <locale.h>
13 #include <signal.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <termios.h>
18 #include <unistd.h>
19 #include <util.h>
20 
21 #include "def.h"
22 #include "kbd.h"
23 #include "funmap.h"
24 #include "macro.h"
25 
26 #ifdef  MGLOG
27 #include "log.h"
28 #endif
29 
30 int		 thisflag;			/* flags, this command	*/
31 int		 lastflag;			/* flags, last command	*/
32 int		 curgoal;			/* goal column		*/
33 int		 startrow;			/* row to start		*/
34 int		 doaudiblebell;			/* audible bell toggle	*/
35 int		 dovisiblebell;			/* visible bell toggle	*/
36 int		 dblspace;			/* sentence end #spaces	*/
37 int		 allbro;			/* all buffs read-only	*/
38 int		 batch;				/* for regress tests	*/
39 struct buffer	*curbp;				/* current buffer	*/
40 struct buffer	*bheadp;			/* BUFFER list head	*/
41 struct mgwin	*curwp;				/* current window	*/
42 struct mgwin	*wheadp;			/* MGWIN listhead	*/
43 char		 pat[NPAT];			/* pattern		*/
44 
45 static void	 edinit(struct buffer *);
46 static void	 pty_init(void);
47 static __dead void usage(void);
48 
49 extern char	*__progname;
50 extern void     closetags(void);
51 
52 static __dead void
53 usage()
54 {
55 	fprintf(stderr, "usage: %s [-nR] [-b file] [-f mode] [-u file] "
56 	    "[+number] [file ...]\n",
57 	    __progname);
58 	exit(1);
59 }
60 
61 int
62 main(int argc, char **argv)
63 {
64 	char		*cp, *conffile = NULL, *init_fcn_name = NULL;
65 	char		*batchfile = NULL;
66 	PF		 init_fcn = NULL;
67 	int	 	 o, i, nfiles;
68 	int	  	 nobackups = 0;
69 	struct buffer	*bp = NULL;
70 
71 	if (pledge("stdio rpath wpath cpath fattr chown getpw tty proc exec",
72 	    NULL) == -1)
73 		err(1, "pledge");
74 
75 	while ((o = getopt(argc, argv, "nRb:f:u:")) != -1)
76 		switch (o) {
77 		case 'b':
78 			batch = 1;
79 			batchfile = optarg;
80 			break;
81 		case 'R':
82 			allbro = 1;
83 			break;
84 		case 'n':
85 			nobackups = 1;
86 			break;
87 		case 'f':
88 			if (init_fcn_name != NULL)
89 				errx(1, "cannot specify more than one "
90 				    "initial function");
91 			init_fcn_name = optarg;
92 			break;
93 		case 'u':
94 			conffile = optarg;
95 			break;
96 		default:
97 			usage();
98 		}
99 
100 	if (batch && (conffile != NULL)) {
101                 fprintf(stderr, "%s: -b and -u are mutually exclusive.\n",
102                     __progname);
103                 exit(1);
104 	}
105 	if (batch) {
106 		pty_init();
107 		conffile = batchfile;
108 	}
109 	if (conffile != NULL && access(conffile, R_OK) != 0) {
110                 fprintf(stderr, "%s: Problem with file: %s\n", __progname,
111 		    conffile);
112                 exit(1);
113 	}
114 
115 	argc -= optind;
116 	argv += optind;
117 
118 	setlocale(LC_CTYPE, "");
119 
120 	maps_init();		/* Keymaps and modes.		*/
121 	funmap_init();		/* Functions.			*/
122 
123 #ifdef  MGLOG
124 	if (!mgloginit())
125 		errx(1, "Unable to create logging environment.");
126 #endif
127 
128 	/*
129 	 * This is where we initialize standalone extensions that should
130 	 * be loaded dynamically sometime in the future.
131 	 */
132 	{
133 		extern void grep_init(void);
134 		extern void cmode_init(void);
135 		extern void dired_init(void);
136 
137 		dired_init();
138 		grep_init();
139 		cmode_init();
140 	}
141 
142 	if (init_fcn_name &&
143 	    (init_fcn = name_function(init_fcn_name)) == NULL)
144 		errx(1, "Unknown function `%s'", init_fcn_name);
145 
146 	vtinit();		/* Virtual terminal.		*/
147 	dirinit();		/* Get current directory.	*/
148 	edinit(bp);		/* Buffers, windows.		*/
149 	ttykeymapinit();	/* Symbols, bindings.		*/
150 	bellinit();		/* Audible and visible bell.	*/
151 	dblspace = 1;		/* two spaces for sentence end. */
152 
153 	/*
154 	 * doing update() before reading files causes the error messages from
155 	 * the file I/O show up on the screen.	(and also an extra display of
156 	 * the mode line if there are files specified on the command line.)
157 	 */
158 	update(CMODE);
159 
160 	/* user startup file. */
161 	if ((cp = startupfile(NULL, conffile)) != NULL)
162 		(void)load(cp);
163 
164 	if (batch)
165 		return (0);
166 
167 	/*
168 	 * Now ensure any default buffer modes from the startup file are
169 	 * given to any files opened when parsing the startup file.
170 	 * Note *scratch* will also be updated.
171 	 */
172 	for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
173 		bp->b_flag = defb_flag;
174 		for (i = 0; i <= defb_nmodes; i++) {
175                 	bp->b_modes[i] = defb_modes[i];
176         	}
177 	}
178 
179 	/* Force FFOTHARG=1 so that this mode is enabled, not simply toggled */
180 	if (init_fcn)
181 		init_fcn(FFOTHARG, 1);
182 
183 	if (nobackups)
184 		makebkfile(FFARG, 0);
185 
186 	for (nfiles = 0, i = 0; i < argc; i++) {
187 		if (argv[i][0] == '+' && strlen(argv[i]) >= 2) {
188 			long long lval;
189 			const char *errstr;
190 
191 			lval = strtonum(&argv[i][1], INT_MIN, INT_MAX, &errstr);
192 			if (argv[i][1] == '\0' || errstr != NULL)
193 				goto notnum;
194 			startrow = lval;
195 		} else {
196 notnum:
197 			cp = adjustname(argv[i], FALSE);
198 			if (cp != NULL) {
199 				if (nfiles == 1)
200 					splitwind(0, 1);
201 
202 				if (fisdir(cp) == TRUE) {
203 					(void)do_dired(cp);
204 					continue;
205 				}
206 				if ((curbp = findbuffer(cp)) == NULL) {
207 					vttidy();
208 					errx(1, "Can't find current buffer!");
209 				}
210 				(void)showbuffer(curbp, curwp, 0);
211 				if (readin(cp) != TRUE)
212 					killbuffer(curbp);
213 				else {
214 					/* Ensure enabled, not just toggled */
215 					if (init_fcn_name)
216 						init_fcn(FFOTHARG, 1);
217 					nfiles++;
218 				}
219 				if (allbro)
220 					curbp->b_flag |= BFREADONLY;
221 			}
222 		}
223 	}
224 
225 	if (nfiles > 2)
226 		listbuffers(0, 1);
227 
228 	/* fake last flags */
229 	thisflag = 0;
230 	for (;;) {
231 		if (epresf == KCLEAR)
232 			eerase();
233 		if (epresf == TRUE)
234 			epresf = KCLEAR;
235 		if (winch_flag) {
236 			do_redraw(0, 0, TRUE);
237 			winch_flag = 0;
238 		}
239 		update(CMODE);
240 		lastflag = thisflag;
241 		thisflag = 0;
242 
243 		switch (doin()) {
244 		case TRUE:
245 			break;
246 		case ABORT:
247 			ewprintf("Quit");
248 			/* FALLTHRU */
249 		case FALSE:
250 		default:
251 			macrodef = FALSE;
252 		}
253 	}
254 }
255 
256 /*
257  * Initialize default buffer and window. Default buffer is called *scratch*.
258  */
259 static void
260 edinit(struct buffer *bp)
261 {
262 	struct mgwin	*wp;
263 
264 	bheadp = NULL;
265 	bp = bfind("*scratch*", TRUE);		/* Text buffer.          */
266 	if (bp == NULL)
267 		panic("edinit");
268 
269 	wp = new_window(bp);
270 	if (wp == NULL)
271 		panic("edinit: Out of memory");
272 
273 	curbp = bp;				/* Current buffer.	 */
274 	wheadp = wp;
275 	curwp = wp;
276 	wp->w_wndp = NULL;			/* Initialize window.	 */
277 	wp->w_linep = wp->w_dotp = bp->b_headp;
278 	wp->w_ntrows = nrow - 2;		/* 2 = mode, echo.	 */
279 	wp->w_rflag = WFMODE | WFFULL;		/* Full.		 */
280 }
281 
282 /*
283  * Create pty for batch mode.
284  */
285 static void
286 pty_init(void)
287 {
288 	struct winsize	 ws;
289 	int		 master;
290 	int		 slave;
291 
292 	memset(&ws, 0, sizeof(ws));
293 	ws.ws_col = 80,
294 	ws.ws_row = 24;
295 
296 	openpty(&master, &slave, NULL, NULL, &ws);
297 	login_tty(slave);
298 
299 	return;
300 }
301 
302 /*
303  * Quit command.  If an argument, always quit.  Otherwise confirm if a buffer
304  * has been changed and not written out.  Normally bound to "C-x C-c".
305  */
306 /* ARGSUSED */
307 int
308 quit(int f, int n)
309 {
310 	int	 s;
311 
312 	if ((s = anycb(FALSE)) == ABORT)
313 		return (ABORT);
314 	if (s == FIOERR || s == UERROR)
315 		return (FALSE);
316 	if (s == FALSE
317 	    || eyesno("Modified buffers exist; really exit") == TRUE) {
318 		vttidy();
319 		closetags();
320 		exit(0);
321 	}
322 	return (TRUE);
323 }
324 
325 /*
326  * User abort.  Should be called by any input routine that sees a C-g to abort
327  * whatever C-g is aborting these days. Currently does nothing.
328  */
329 /* ARGSUSED */
330 int
331 ctrlg(int f, int n)
332 {
333 	return (ABORT);
334 }
335