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