1 /* $NetBSD: main.c,v 1.5 2013/12/01 02:34:54 christos Exp $ */ 2 /*- 3 * Copyright (c) 1992, 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 1992, 1993, 1994, 1995, 1996 6 * Keith Bostic. All rights reserved. 7 * 8 * See the LICENSE file for redistribution information. 9 */ 10 11 #include "config.h" 12 13 #ifndef lint 14 static const char copyright[] = 15 "%Z% Copyright (c) 1992, 1993, 1994\n\ 16 The Regents of the University of California. All rights reserved.\n\ 17 %Z% Copyright (c) 1992, 1993, 1994, 1995, 1996\n\ 18 Keith Bostic. All rights reserved.\n"; 19 #endif /* not lint */ 20 21 #ifndef lint 22 static const char sccsid[] = "Id: main.c,v 10.63 2001/11/01 15:24:43 skimo Exp (Berkeley) Date: 2001/11/01 15:24:43 "; 23 #endif /* not lint */ 24 25 #include <sys/types.h> 26 #include <sys/queue.h> 27 #include <sys/stat.h> 28 #include <sys/time.h> 29 30 #include <bitstring.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <limits.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "common.h" 40 #include "../vi/vi.h" 41 #include "pathnames.h" 42 43 static void v_estr __P((const char *, int, const char *)); 44 static int v_obsolete __P((char *, char *[])); 45 46 /* 47 * editor -- 48 * Main editor routine. 49 * 50 * PUBLIC: int editor __P((WIN *, int, char *[])); 51 */ 52 int 53 editor(WIN *wp, int argc, char **argv) 54 { 55 extern int optind; 56 extern char *optarg; 57 const char *p; 58 EVENT ev; 59 FREF *frp; 60 SCR *sp; 61 GS *gp; 62 size_t len; 63 u_int flags; 64 int ch, flagchk, lflag, secure, startup, readonly, rval, silent; 65 #ifdef GTAGS 66 int gtags = 0; 67 #endif 68 char *tag_f, *wsizearg, path[256]; 69 const CHAR_T *w; 70 size_t wlen; 71 72 gp = wp->gp; 73 74 /* Initialize the busy routine, if not defined by the screen. */ 75 if (gp->scr_busy == NULL) 76 gp->scr_busy = vs_busy; 77 /* Initialize the message routine, if not defined by the screen. */ 78 if (wp->scr_msg == NULL) 79 wp->scr_msg = vs_msg; 80 81 /* Set initial screen type and mode based on the program name. */ 82 readonly = 0; 83 if (!strcmp(gp->progname, "ex") || !strcmp(gp->progname, "nex")) 84 LF_INIT(SC_EX); 85 else { 86 /* Nview, view are readonly. */ 87 if (!strcmp(gp->progname, "nview") || 88 !strcmp(gp->progname, "view")) 89 readonly = 1; 90 91 /* Vi is the default. */ 92 LF_INIT(SC_VI); 93 } 94 95 /* Convert old-style arguments into new-style ones. */ 96 if (v_obsolete(gp->progname, argv)) 97 return (1); 98 99 /* Parse the arguments. */ 100 flagchk = '\0'; 101 tag_f = wsizearg = NULL; 102 lflag = secure = silent = 0; 103 startup = 1; 104 105 /* Set the file snapshot flag. */ 106 F_SET(gp, G_SNAPSHOT); 107 108 while ((ch = getopt(argc, argv, "c:" 109 #ifdef DEBUG 110 "D:" 111 #endif 112 "eF" 113 #ifdef GTAGS 114 "G" 115 #endif 116 "lRrSsT:t:vw:")) != EOF) 117 switch (ch) { 118 case 'c': /* Run the command. */ 119 /* 120 * XXX 121 * We should support multiple -c options. 122 */ 123 if (gp->c_option != NULL) { 124 v_estr(gp->progname, 0, 125 "only one -c command may be specified."); 126 return (1); 127 } 128 gp->c_option = optarg; 129 break; 130 #ifdef DEBUG 131 case 'D': 132 switch (optarg[0]) { 133 case 's': 134 startup = 0; 135 break; 136 case 'w': 137 attach(gp); 138 break; 139 default: 140 v_estr(gp->progname, 0, 141 "usage: -D requires s or w argument."); 142 return (1); 143 } 144 break; 145 #endif 146 case 'e': /* Ex mode. */ 147 LF_CLR(SC_VI); 148 LF_SET(SC_EX); 149 break; 150 case 'F': /* No snapshot. */ 151 v_estr(gp->progname, 0, 152 "-F option no longer supported."); 153 break; 154 case 'l': /* Set lisp, showmatch options. */ 155 lflag = 1; 156 break; 157 #ifdef GTAGS 158 case 'G': /* gtags mode. */ 159 gtags = 1; 160 break; 161 #endif 162 case 'R': /* Readonly. */ 163 readonly = 1; 164 break; 165 case 'r': /* Recover. */ 166 if (flagchk == 't') { 167 v_estr(gp->progname, 0, 168 "only one of -r and -t may be specified."); 169 return (1); 170 } 171 flagchk = 'r'; 172 break; 173 case 'S': 174 secure = 1; 175 break; 176 case 's': 177 silent = 1; 178 break; 179 #ifdef TRACE 180 case 'T': /* Trace. */ 181 (void)vtrace_init(optarg); 182 break; 183 #endif 184 case 't': /* Tag. */ 185 if (flagchk == 'r') { 186 v_estr(gp->progname, 0, 187 "only one of -r and -t may be specified."); 188 return (1); 189 } 190 if (flagchk == 't') { 191 v_estr(gp->progname, 0, 192 "only one tag file may be specified."); 193 return (1); 194 } 195 flagchk = 't'; 196 tag_f = optarg; 197 break; 198 case 'v': /* Vi mode. */ 199 LF_CLR(SC_EX); 200 LF_SET(SC_VI); 201 break; 202 case 'w': 203 wsizearg = optarg; 204 break; 205 case '?': 206 default: 207 (void)gp->scr_usage(); 208 return (1); 209 } 210 argc -= optind; 211 argv += optind; 212 213 /* 214 * -s option is only meaningful to ex. 215 * 216 * If not reading from a terminal, it's like -s was specified. 217 */ 218 if (silent && !LF_ISSET(SC_EX)) { 219 v_estr(gp->progname, 0, "-s option is only applicable to ex."); 220 goto err; 221 } 222 if (LF_ISSET(SC_EX) && F_ISSET(gp, G_SCRIPTED)) 223 silent = 1; 224 225 /* 226 * Build and initialize the first/current screen. This is a bit 227 * tricky. If an error is returned, we may or may not have a 228 * screen structure. If we have a screen structure, put it on a 229 * display queue so that the error messages get displayed. 230 * 231 * !!! 232 * Everything we do until we go interactive is done in ex mode. 233 */ 234 if (screen_init(gp, NULL, &sp)) { 235 if (sp != NULL) { 236 TAILQ_INSERT_HEAD(&wp->scrq, sp, q); 237 sp->wp = wp; 238 } 239 goto err; 240 } 241 F_SET(sp, SC_EX); 242 TAILQ_INSERT_HEAD(&wp->scrq, sp, q); 243 sp->wp = wp; 244 245 if (v_key_init(sp)) /* Special key initialization. */ 246 goto err; 247 248 { int oargs[5], *oargp = oargs; 249 if (lflag) { /* Command-line options. */ 250 *oargp++ = O_LISP; 251 *oargp++ = O_SHOWMATCH; 252 } 253 if (readonly) 254 *oargp++ = O_READONLY; 255 #ifdef GTAGS 256 if (gtags) 257 *oargp++ = O_GTAGSMODE; 258 #endif 259 if (secure) 260 *oargp++ = O_SECURE; 261 *oargp = -1; /* Options initialization. */ 262 if (opts_init(sp, oargs)) 263 goto err; 264 } 265 if (wsizearg != NULL) { 266 ARGS *av[2], a, b; 267 (void)snprintf(path, sizeof(path), "window=%s", wsizearg); 268 a.bp = (CHAR_T *)path; 269 a.len = strlen(path); 270 b.bp = NULL; 271 b.len = 0; 272 av[0] = &a; 273 av[1] = &b; 274 (void)opts_set(sp, av, NULL); 275 } 276 if (silent) { /* Ex batch mode option values. */ 277 O_CLR(sp, O_AUTOPRINT); 278 O_CLR(sp, O_PROMPT); 279 O_CLR(sp, O_VERBOSE); 280 O_CLR(sp, O_WARN); 281 F_SET(sp, SC_EX_SILENT); 282 } 283 284 sp->rows = O_VAL(sp, O_LINES); /* Make ex formatting work. */ 285 sp->cols = O_VAL(sp, O_COLUMNS); 286 287 if (!silent && startup) { /* Read EXINIT, exrc files. */ 288 if (ex_exrc(sp)) 289 goto err; 290 if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) { 291 if (screen_end(sp)) 292 goto err; 293 goto done; 294 } 295 } 296 297 /* 298 * List recovery files if -r specified without file arguments. 299 * Note, options must be initialized and startup information 300 * read before doing this. 301 */ 302 if (flagchk == 'r' && argv[0] == NULL) { 303 if (rcv_list(sp)) 304 goto err; 305 if (screen_end(sp)) 306 goto err; 307 goto done; 308 } 309 310 /* 311 * !!! 312 * Initialize the default ^D, ^U scrolling value here, after the 313 * user has had every opportunity to set the window option. 314 * 315 * It's historic practice that changing the value of the window 316 * option did not alter the default scrolling value, only giving 317 * a count to ^D/^U did that. 318 */ 319 sp->defscroll = (O_VAL(sp, O_WINDOW) + 1) / 2; 320 321 /* 322 * If we don't have a command-line option, switch into the right 323 * editor now, so that we position default files correctly, and 324 * so that any tags file file-already-locked messages are in the 325 * vi screen, not the ex screen. 326 * 327 * XXX 328 * If we have a command-line option, the error message can end 329 * up in the wrong place, but I think that the combination is 330 * unlikely. 331 */ 332 if (gp->c_option == NULL) { 333 F_CLR(sp, SC_EX | SC_VI); 334 F_SET(sp, LF_ISSET(SC_EX | SC_VI)); 335 } 336 337 /* Open a tag file if specified. */ 338 if (tag_f != NULL) { 339 CHAR2INT(sp, tag_f, strlen(tag_f) + 1, w, wlen); 340 if (ex_tag_first(sp, w)) 341 goto err; 342 } 343 344 /* 345 * Append any remaining arguments as file names. Files are recovery 346 * files if -r specified. If the tag option or ex startup commands 347 * loaded a file, then any file arguments are going to come after it. 348 */ 349 if (*argv != NULL) { 350 if (sp->frp != NULL) { 351 /* Cheat -- we know we have an extra argv slot. */ 352 MALLOC_NOMSG(sp, 353 *--argv, char *, strlen(sp->frp->name) + 1); 354 if (*argv == NULL) { 355 v_estr(gp->progname, errno, NULL); 356 goto err; 357 } 358 (void)strcpy(*argv, sp->frp->name); 359 } 360 sp->argv = sp->cargv = argv; 361 F_SET(sp, SC_ARGNOFREE); 362 if (flagchk == 'r') 363 F_SET(sp, SC_ARGRECOVER); 364 } 365 366 /* 367 * If the ex startup commands and or/the tag option haven't already 368 * created a file, create one. If no command-line files were given, 369 * use a temporary file. 370 */ 371 if (sp->frp == NULL) { 372 if (sp->argv == NULL) { 373 if ((frp = file_add(sp, NULL)) == NULL) 374 goto err; 375 } else { 376 if ((frp = file_add(sp, sp->argv[0])) == NULL) 377 goto err; 378 if (F_ISSET(sp, SC_ARGRECOVER)) 379 F_SET(frp, FR_RECOVER); 380 } 381 382 if (file_init(sp, frp, NULL, 0)) 383 goto err; 384 if (EXCMD_RUNNING(wp)) { 385 (void)ex_cmd(sp); 386 if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) { 387 if (screen_end(sp)) 388 goto err; 389 goto done; 390 } 391 } 392 } 393 394 /* 395 * Check to see if we need to wait for ex. If SC_SCR_EX is set, ex 396 * was forced to initialize the screen during startup. We'd like to 397 * wait for a single character from the user, but we can't because 398 * we're not in raw mode. We can't switch to raw mode because the 399 * vi initialization will switch to xterm's alternate screen, causing 400 * us to lose the messages we're pausing to make sure the user read. 401 * So, wait for a complete line. 402 */ 403 if (F_ISSET(sp, SC_SCR_EX)) { 404 p = msg_cmsg(sp, CMSG_CONT_R, &len); 405 (void)write(STDOUT_FILENO, p, len); 406 for (;;) { 407 if (v_event_get(sp, &ev, 0, 0)) 408 goto err; 409 if (ev.e_event == E_INTERRUPT || 410 (ev.e_event == E_CHARACTER && 411 (ev.e_value == K_CR || ev.e_value == K_NL))) 412 break; 413 (void)gp->scr_bell(sp); 414 } 415 } 416 417 /* Switch into the right editor, regardless. */ 418 F_CLR(sp, SC_EX | SC_VI); 419 F_SET(sp, LF_ISSET(SC_EX | SC_VI) | SC_STATUS_CNT); 420 421 /* 422 * Main edit loop. Vi handles split screens itself, we only return 423 * here when switching editor modes or restarting the screen. 424 */ 425 while (sp != NULL) 426 if (F_ISSET(sp, SC_EX) ? ex(&sp) : vi(&sp)) 427 goto err; 428 429 done: rval = 0; 430 if (0) 431 err: rval = 1; 432 433 return (rval); 434 } 435 436 /* 437 * v_obsolete -- 438 * Convert historic arguments into something getopt(3) will like. 439 */ 440 static int 441 v_obsolete(char *name, char **argv) 442 { 443 size_t len; 444 char *p; 445 446 /* 447 * Translate old style arguments into something getopt will like. 448 * Make sure it's not text space memory, because ex modifies the 449 * strings. 450 * Change "+" into "-c$". 451 * Change "+<anything else>" into "-c<anything else>". 452 * Change "-" into "-s" 453 * The c, T, t and w options take arguments so they can't be 454 * special arguments. 455 * 456 * Stop if we find "--" as an argument, the user may want to edit 457 * a file named "+foo". 458 */ 459 while (*++argv && strcmp(argv[0], "--")) 460 if (argv[0][0] == '+') { 461 if (argv[0][1] == '\0') { 462 MALLOC_NOMSG(NULL, argv[0], char *, 4); 463 if (argv[0] == NULL) 464 goto nomem; 465 (void)strcpy(argv[0], "-c$"); 466 } else { 467 p = argv[0]; 468 len = strlen(argv[0]); 469 MALLOC_NOMSG(NULL, argv[0], char *, len + 2); 470 if (argv[0] == NULL) 471 goto nomem; 472 argv[0][0] = '-'; 473 argv[0][1] = 'c'; 474 (void)strcpy(argv[0] + 2, p + 1); 475 } 476 } else if (argv[0][0] == '-') { 477 if (argv[0][1] == '\0') { 478 MALLOC_NOMSG(NULL, argv[0], char *, 3); 479 if (argv[0] == NULL) { 480 nomem: v_estr(name, errno, NULL); 481 return (1); 482 } 483 (void)strcpy(argv[0], "-s"); 484 } else 485 if ((argv[0][1] == 'c' || argv[0][1] == 'T' || 486 argv[0][1] == 't' || argv[0][1] == 'w') && 487 argv[0][2] == '\0') 488 ++argv; 489 } 490 return (0); 491 } 492 493 #ifdef DEBUG 494 static void 495 attach(GS *gp) 496 { 497 int fd; 498 char ch; 499 500 if ((fd = open(_PATH_TTY, O_RDONLY, 0)) < 0) { 501 v_estr(gp->progname, errno, _PATH_TTY); 502 return; 503 } 504 505 (void)printf("process %lu waiting, enter <CR> to continue: ", 506 (u_long)getpid()); 507 (void)fflush(stdout); 508 509 do { 510 if (read(fd, &ch, 1) != 1) { 511 (void)close(fd); 512 return; 513 } 514 } while (ch != '\n' && ch != '\r'); 515 (void)close(fd); 516 } 517 #endif 518 519 static void 520 v_estr(const char *name, int eno, const char *msg) 521 { 522 (void)fprintf(stderr, "%s", name); 523 if (msg != NULL) 524 (void)fprintf(stderr, ": %s", msg); 525 if (eno) 526 (void)fprintf(stderr, ": %s", strerror(errno)); 527 (void)fprintf(stderr, "\n"); 528 } 529