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