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