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