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