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 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 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 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 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 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 337 ctrlg(int f, int n) 338 { 339 return (ABORT); 340 } 341