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