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