|
|
1.1 ! root 1: /* ex.c */ ! 2: ! 3: /* Author: ! 4: * Steve Kirkendall ! 5: * 16820 SW Tallac Way ! 6: * Beaverton, OR 97006 ! 7: * [email protected], or ...uunet!tektronix!psueea!jove!kirkenda ! 8: */ ! 9: ! 10: ! 11: /* This file contains the code for reading ex commands. */ ! 12: ! 13: #include "config.h" ! 14: #include <ctype.h> ! 15: #include "vi.h" ! 16: ! 17: #ifndef isascii ! 18: # define isascii(c) !((c)&~0x7f) ! 19: #endif ! 20: ! 21: /* This data type is used to describe the possible argument combinations */ ! 22: typedef short ARGT; ! 23: #define FROM 1 /* allow a linespec */ ! 24: #define TO 2 /* allow a second linespec */ ! 25: #define BANG 4 /* allow a ! after the command name */ ! 26: #define EXTRA 8 /* allow extra args after command name */ ! 27: #define XFILE 16 /* expand wildcards in extra part */ ! 28: #define NOSPC 32 /* no spaces allowed in the extra part */ ! 29: #define DFLALL 64 /* default file range is 1,$ */ ! 30: #define DFLNONE 128 /* no default file range */ ! 31: #define NODFL 256 /* do not default to the current file name */ ! 32: #define EXRCOK 512 /* can be in a .exrc file */ ! 33: #define NL 1024 /* if mode!=MODE_EX, then write a newline first */ ! 34: #define PLUS 2048 /* allow a line number, as in ":e +32 foo" */ ! 35: #define ZERO 4096 /* allow 0 to be given as a line number */ ! 36: #define FILES (XFILE + EXTRA) /* multiple extra files allowed */ ! 37: #define WORD1 (EXTRA + NOSPC) /* one extra word allowed */ ! 38: #define FILE1 (FILES + NOSPC) /* 1 file allowed, defaults to current file */ ! 39: #define NAMEDF (FILE1 + NODFL) /* 1 file allowed, defaults to "" */ ! 40: #define NAMEDFS (FILES + NODFL) /* multiple files allowed, default is "" */ ! 41: #define RANGE (FROM + TO) /* range of linespecs allowed */ ! 42: #define NONE 0 /* no args allowed at all */ ! 43: ! 44: /* This array maps ex command names to command codes. The order in which ! 45: * command names are listed below is significant -- ambiguous abbreviations ! 46: * are always resolved to be the first possible match. (e.g. "r" is taken ! 47: * to mean "read", not "rewind", because "read" comes before "rewind") ! 48: */ ! 49: static struct ! 50: { ! 51: char *name; /* name of the command */ ! 52: CMD code; /* enum code of the command */ ! 53: void (*fn)();/* function which executes the command */ ! 54: ARGT argt; /* command line arguments permitted/needed/used */ ! 55: } ! 56: cmdnames[] = ! 57: { /* cmd name cmd code function arguments */ ! 58: {"append", CMD_APPEND, cmd_append, FROM+ZERO }, ! 59: #ifdef DEBUG ! 60: {"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, ! 61: #endif ! 62: {"change", CMD_CHANGE, cmd_append, RANGE }, ! 63: {"delete", CMD_DELETE, cmd_delete, RANGE+WORD1 }, ! 64: {"edit", CMD_EDIT, cmd_edit, BANG+FILE1+PLUS }, ! 65: {"file", CMD_FILE, cmd_file, NAMEDF }, ! 66: {"global", CMD_GLOBAL, cmd_global, RANGE+BANG+EXTRA+DFLALL}, ! 67: {"insert", CMD_INSERT, cmd_append, FROM }, ! 68: {"join", CMD_INSERT, cmd_join, RANGE }, ! 69: {"k", CMD_MARK, cmd_mark, FROM+WORD1 }, ! 70: {"list", CMD_LIST, cmd_print, RANGE+NL }, ! 71: {"move", CMD_MOVE, cmd_move, RANGE+EXTRA }, ! 72: {"next", CMD_NEXT, cmd_next, BANG+NAMEDFS }, ! 73: {"Next", CMD_PREVIOUS, cmd_next, BANG }, ! 74: {"print", CMD_PRINT, cmd_print, RANGE+NL }, ! 75: {"quit", CMD_QUIT, cmd_xit, BANG }, ! 76: {"read", CMD_READ, cmd_read, FROM+ZERO+NAMEDF}, ! 77: {"substitute", CMD_SUBSTITUTE, cmd_substitute, RANGE+EXTRA }, ! 78: {"to", CMD_COPY, cmd_move, RANGE+EXTRA }, ! 79: {"undo", CMD_UNDO, cmd_undo, NONE }, ! 80: {"vglobal", CMD_VGLOBAL, cmd_global, RANGE+EXTRA+DFLALL}, ! 81: {"write", CMD_WRITE, cmd_write, RANGE+BANG+FILE1+DFLALL}, ! 82: {"xit", CMD_XIT, cmd_xit, BANG+NL }, ! 83: {"yank", CMD_YANK, cmd_delete, RANGE+WORD1 }, ! 84: ! 85: {"!", CMD_BANG, cmd_shell, EXRCOK+RANGE+NAMEDFS+DFLNONE+NL}, ! 86: {"<", CMD_SHIFTL, cmd_shift, RANGE }, ! 87: {">", CMD_SHIFTR, cmd_shift, RANGE }, ! 88: {"=", CMD_EQUAL, cmd_file, RANGE }, ! 89: {"&", CMD_SUBAGAIN, cmd_substitute, RANGE }, ! 90: #ifndef NO_AT ! 91: {"@", CMD_AT, cmd_at, EXTRA }, ! 92: #endif ! 93: ! 94: #ifndef NO_ABBR ! 95: {"abbreviate", CMD_ABBR, cmd_abbr, EXRCOK+EXTRA }, ! 96: #endif ! 97: {"args", CMD_ARGS, cmd_args, EXRCOK+NAMEDFS }, ! 98: #ifndef NO_ERRLIST ! 99: {"cc", CMD_CC, cmd_make, BANG+FILES }, ! 100: #endif ! 101: {"cd", CMD_CD, cmd_cd, EXRCOK+NAMEDF }, ! 102: {"copy", CMD_COPY, cmd_move, RANGE+EXTRA }, ! 103: #ifndef NO_DIGRAPH ! 104: {"digraph", CMD_DIGRAPH, cmd_digraph, EXRCOK+BANG+EXTRA}, ! 105: #endif ! 106: #ifndef NO_ERRLIST ! 107: {"errlist", CMD_ERRLIST, cmd_errlist, BANG+NAMEDF }, ! 108: #endif ! 109: {"ex", CMD_EDIT, cmd_edit, BANG+FILE1 }, ! 110: {"map", CMD_MAP, cmd_map, EXRCOK+BANG+EXTRA}, ! 111: #ifndef NO_MKEXRC ! 112: {"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF }, ! 113: #endif ! 114: {"number", CMD_NUMBER, cmd_print, RANGE+NL }, ! 115: {"put", CMD_PUT, cmd_put, FROM+ZERO+WORD1 }, ! 116: {"set", CMD_SET, cmd_set, EXRCOK+EXTRA }, ! 117: {"shell", CMD_SHELL, cmd_shell, NL }, ! 118: {"source", CMD_SOURCE, cmd_source, EXRCOK+NAMEDF }, ! 119: {"tag", CMD_TAG, cmd_tag, BANG+WORD1 }, ! 120: {"version", CMD_VERSION, cmd_version, EXRCOK+NONE }, ! 121: {"visual", CMD_VISUAL, cmd_visual, NONE }, ! 122: {"wq", CMD_WQUIT, cmd_xit, NL }, ! 123: ! 124: #ifdef DEBUG ! 125: {"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, ! 126: {"validate", CMD_VALIDATE, cmd_validate, BANG+NL }, ! 127: #endif ! 128: {"chdir", CMD_CD, cmd_cd, EXRCOK+NAMEDF }, ! 129: #ifndef NO_ERRLIST ! 130: {"make", CMD_MAKE, cmd_make, BANG+NAMEDFS }, ! 131: #endif ! 132: {"mark", CMD_MARK, cmd_mark, FROM+WORD1 }, ! 133: {"previous", CMD_PREVIOUS, cmd_next, BANG }, ! 134: {"rewind", CMD_REWIND, cmd_next, BANG }, ! 135: {"unmap", CMD_UNMAP, cmd_map, EXRCOK+BANG+EXTRA}, ! 136: #ifndef NO_ABBR ! 137: {"unabbreviate",CMD_UNABBR, cmd_abbr, EXRCOK+WORD1 }, ! 138: #endif ! 139: ! 140: {(char *)0} ! 141: }; ! 142: ! 143: ! 144: /* This function parses a search pattern - given a pointer to a / or ?, ! 145: * it replaces the ending / or ? with a \0, and returns a pointer to the ! 146: * stuff that came after the pattern. ! 147: */ ! 148: char *parseptrn(ptrn) ! 149: REG char *ptrn; ! 150: { ! 151: REG char *scan; ! 152: ! 153: for (scan = ptrn + 1; ! 154: *scan && *scan != *ptrn; ! 155: scan++) ! 156: { ! 157: /* allow backslashed versions of / and ? in the pattern */ ! 158: if (*scan == '\\' && scan[1] != '\0') ! 159: { ! 160: scan++; ! 161: } ! 162: } ! 163: if (*scan) ! 164: { ! 165: *scan++ = '\0'; ! 166: } ! 167: ! 168: return scan; ! 169: } ! 170: ! 171: ! 172: /* This function parses a line specifier for ex commands */ ! 173: char *linespec(s, markptr) ! 174: REG char *s; /* start of the line specifier */ ! 175: MARK *markptr; /* where to store the mark's value */ ! 176: { ! 177: long num; ! 178: REG char *t; ! 179: ! 180: /* parse each ;-delimited clause of this linespec */ ! 181: do ! 182: { ! 183: /* skip an initial ';', if any */ ! 184: if (*s == ';') ! 185: { ! 186: s++; ! 187: } ! 188: ! 189: /* skip leading spaces */ ! 190: while (isascii(*s) && isspace(*s)) ! 191: { ! 192: s++; ! 193: } ! 194: ! 195: /* dot means current position */ ! 196: if (*s == '.') ! 197: { ! 198: s++; ! 199: *markptr = cursor; ! 200: } ! 201: /* '$' means the last line */ ! 202: else if (*s == '$') ! 203: { ! 204: s++; ! 205: *markptr = MARK_LAST; ! 206: } ! 207: /* digit means an absolute line number */ ! 208: else if (isascii(*s) && isdigit(*s)) ! 209: { ! 210: for (num = 0; isascii(*s) && isdigit(*s); s++) ! 211: { ! 212: num = num * 10 + *s - '0'; ! 213: } ! 214: *markptr = MARK_AT_LINE(num); ! 215: } ! 216: /* appostrophe means go to a set mark */ ! 217: else if (*s == '\'') ! 218: { ! 219: s++; ! 220: *markptr = m_tomark(cursor, 1L, (int)*s); ! 221: s++; ! 222: } ! 223: /* slash means do a search */ ! 224: else if (*s == '/' || *s == '?') ! 225: { ! 226: /* put a '\0' at the end of the search pattern */ ! 227: t = parseptrn(s); ! 228: ! 229: /* search for the pattern */ ! 230: *markptr &= ~(BLKSIZE - 1); ! 231: if (*s == '/') ! 232: { ! 233: pfetch(markline(*markptr)); ! 234: if (plen > 0) ! 235: *markptr += plen - 1; ! 236: *markptr = m_fsrch(*markptr, s); ! 237: } ! 238: else ! 239: { ! 240: *markptr = m_bsrch(*markptr, s); ! 241: } ! 242: ! 243: /* adjust command string pointer */ ! 244: s = t; ! 245: } ! 246: ! 247: /* if linespec was faulty, quit now */ ! 248: if (!*markptr) ! 249: { ! 250: return s; ! 251: } ! 252: ! 253: /* maybe add an offset */ ! 254: t = s; ! 255: if (*t == '-' || *t == '+') ! 256: { ! 257: s++; ! 258: for (num = 0; *s >= '0' && *s <= '9'; s++) ! 259: { ! 260: num = num * 10 + *s - '0'; ! 261: } ! 262: if (num == 0) ! 263: { ! 264: num = 1; ! 265: } ! 266: *markptr = m_updnto(*markptr, num, *t); ! 267: } ! 268: } while (*s == ';' || *s == '+' || *s == '-'); ! 269: ! 270: return s; ! 271: } ! 272: ! 273: ! 274: ! 275: /* This function reads an ex command and executes it. */ ! 276: void ex() ! 277: { ! 278: char cmdbuf[80]; ! 279: REG int cmdlen; ! 280: static long oldline; ! 281: ! 282: significant = FALSE; ! 283: oldline = markline(cursor); ! 284: ! 285: while (mode == MODE_EX) ! 286: { ! 287: /* read a line */ ! 288: cmdlen = vgets(':', cmdbuf, sizeof cmdbuf); ! 289: if (cmdlen < 0) ! 290: { ! 291: return; ! 292: } ! 293: ! 294: /* if empty line, assume ".+1" */ ! 295: if (cmdlen == 0) ! 296: { ! 297: strcpy(cmdbuf, ".+1"); ! 298: qaddch('\r'); ! 299: clrtoeol(); ! 300: } ! 301: else ! 302: { ! 303: addch('\n'); ! 304: } ! 305: refresh(); ! 306: ! 307: /* parse & execute the command */ ! 308: doexcmd(cmdbuf); ! 309: ! 310: /* handle autoprint */ ! 311: if (significant || markline(cursor) != oldline) ! 312: { ! 313: significant = FALSE; ! 314: oldline = markline(cursor); ! 315: if (*o_autoprint && mode == MODE_EX) ! 316: { ! 317: cmd_print(cursor, cursor, CMD_PRINT, FALSE, ""); ! 318: } ! 319: } ! 320: } ! 321: } ! 322: ! 323: void doexcmd(cmdbuf) ! 324: char *cmdbuf; /* string containing an ex command */ ! 325: { ! 326: REG char *scan; /* used to scan thru cmdbuf */ ! 327: MARK frommark; /* first linespec */ ! 328: MARK tomark; /* second linespec */ ! 329: REG int cmdlen; /* length of the command name given */ ! 330: CMD cmd; /* what command is this? */ ! 331: ARGT argt; /* argument types for this command */ ! 332: short forceit; /* bang version of a command? */ ! 333: REG int cmdidx; /* index of command */ ! 334: REG char *build; /* used while copying filenames */ ! 335: int iswild; /* boolean: filenames use wildcards? */ ! 336: int isdfl; /* using default line ranges? */ ! 337: int didsub; /* did we substitute file names for % or # */ ! 338: ! 339: ! 340: /* ex commands can't be undone via the shift-U command */ ! 341: U_line = 0L; ! 342: ! 343: /* ignore command lines that start with a double-quote */ ! 344: if (*cmdbuf == '"') ! 345: { ! 346: return; ! 347: } ! 348: ! 349: /* permit extra colons at the start of the line */ ! 350: while (*cmdbuf == ':') ! 351: { ! 352: cmdbuf++; ! 353: } ! 354: ! 355: /* parse the line specifier */ ! 356: scan = cmdbuf; ! 357: if (nlines < 1) ! 358: { ! 359: /* no file, so don't allow addresses */ ! 360: } ! 361: else if (*scan == '%') ! 362: { ! 363: /* '%' means all lines */ ! 364: frommark = MARK_FIRST; ! 365: tomark = MARK_LAST; ! 366: scan++; ! 367: } ! 368: else if (*scan == '0') ! 369: { ! 370: frommark = tomark = MARK_UNSET; ! 371: scan++; ! 372: } ! 373: else ! 374: { ! 375: frommark = cursor; ! 376: scan = linespec(scan, &frommark); ! 377: tomark = frommark; ! 378: if (frommark && *scan == ',') ! 379: { ! 380: scan++; ! 381: scan = linespec(scan, &tomark); ! 382: } ! 383: if (!tomark) ! 384: { ! 385: /* faulty line spec -- fault already described */ ! 386: return; ! 387: } ! 388: if (frommark > tomark) ! 389: { ! 390: msg("first address exceeds the second"); ! 391: return; ! 392: } ! 393: } ! 394: isdfl = (scan == cmdbuf); ! 395: ! 396: /* skip whitespace */ ! 397: while (isascii(*scan) && isspace(*scan)) ! 398: { ! 399: scan++; ! 400: } ! 401: ! 402: /* if no command, then just move the cursor to the mark */ ! 403: if (!*scan) ! 404: { ! 405: cursor = tomark; ! 406: return; ! 407: } ! 408: ! 409: /* figure out how long the command name is */ ! 410: if (isascii(*scan) && !isalpha(*scan)) ! 411: { ! 412: cmdlen = 1; ! 413: } ! 414: else ! 415: { ! 416: for (cmdlen = 1; ! 417: !isascii(scan[cmdlen]) || isalpha(scan[cmdlen]); ! 418: cmdlen++) ! 419: { ! 420: } ! 421: } ! 422: ! 423: /* lookup the command code */ ! 424: for (cmdidx = 0; ! 425: cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen); ! 426: cmdidx++) ! 427: { ! 428: } ! 429: argt = cmdnames[cmdidx].argt; ! 430: cmd = cmdnames[cmdidx].code; ! 431: if (cmd == CMD_NULL) ! 432: { ! 433: #if OSK ! 434: msg("Unknown command \"%s\"", scan); ! 435: #else ! 436: msg("Unknown command \"%.*s\"", cmdlen, scan); ! 437: #endif ! 438: return; ! 439: } ! 440: ! 441: /* if the command ended with a bang, set the forceit flag */ ! 442: scan += cmdlen; ! 443: if ((argt & BANG) && *scan == '!') ! 444: { ! 445: scan++; ! 446: forceit = 1; ! 447: } ! 448: else ! 449: { ! 450: forceit = 0; ! 451: } ! 452: ! 453: /* skip any more whitespace, to leave scan pointing to arguments */ ! 454: while (isascii(*scan) && isspace(*scan)) ! 455: { ! 456: scan++; ! 457: } ! 458: ! 459: /* a couple of special cases for filenames */ ! 460: if (argt & XFILE) ! 461: { ! 462: /* if names were given, process them */ ! 463: if (*scan) ! 464: { ! 465: for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++) ! 466: { ! 467: switch (*scan) ! 468: { ! 469: case '%': ! 470: if (!*origname) ! 471: { ! 472: msg("No filename to substitute for %%"); ! 473: return; ! 474: } ! 475: strcpy(build, origname); ! 476: while (*build) ! 477: { ! 478: build++; ! 479: } ! 480: didsub = TRUE; ! 481: break; ! 482: ! 483: case '#': ! 484: if (!*prevorig) ! 485: { ! 486: msg("No filename to substitute for #"); ! 487: return; ! 488: } ! 489: strcpy(build, prevorig); ! 490: while (*build) ! 491: { ! 492: build++; ! 493: } ! 494: didsub = TRUE; ! 495: break; ! 496: ! 497: case '*': ! 498: case '?': ! 499: #if !(MSDOS || TOS) ! 500: case '[': ! 501: case '`': ! 502: case '{': /* } */ ! 503: case '$': ! 504: case '~': ! 505: #endif ! 506: *build++ = *scan; ! 507: iswild = TRUE; ! 508: break; ! 509: ! 510: default: ! 511: *build++ = *scan; ! 512: } ! 513: } ! 514: *build = '\0'; ! 515: ! 516: if (cmd == CMD_BANG ! 517: || cmd == CMD_READ && tmpblk.c[0] == '!' ! 518: || cmd == CMD_WRITE && tmpblk.c[0] == '!') ! 519: { ! 520: if (didsub) ! 521: { ! 522: if (mode != MODE_EX) ! 523: { ! 524: addch('\n'); ! 525: } ! 526: addstr(tmpblk.c); ! 527: addch('\n'); ! 528: exrefresh(); ! 529: } ! 530: } ! 531: else ! 532: { ! 533: if (iswild && tmpblk.c[0] != '>') ! 534: { ! 535: scan = wildcard(tmpblk.c); ! 536: } ! 537: } ! 538: } ! 539: else /* no names given, maybe assume origname */ ! 540: { ! 541: if (!(argt & NODFL)) ! 542: { ! 543: strcpy(tmpblk.c, origname); ! 544: } ! 545: else ! 546: { ! 547: *tmpblk.c = '\0'; ! 548: } ! 549: } ! 550: ! 551: scan = tmpblk.c; ! 552: } ! 553: ! 554: /* bad arguments? */ ! 555: if (!(argt & EXRCOK) && nlines < 1L) ! 556: { ! 557: msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC); ! 558: return; ! 559: } ! 560: if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET) ! 561: { ! 562: msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name); ! 563: return; ! 564: } ! 565: if (!(argt & FROM) && frommark != cursor && nlines >= 1L) ! 566: { ! 567: msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name); ! 568: return; ! 569: } ! 570: if (!(argt & TO) && tomark != frommark && nlines >= 1L) ! 571: { ! 572: msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name); ! 573: return; ! 574: } ! 575: if (!(argt & EXTRA) && *scan) ! 576: { ! 577: msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name); ! 578: return; ! 579: } ! 580: if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!'))) ! 581: { ! 582: build = scan; ! 583: #ifndef CRUNCH ! 584: if ((argt & PLUS) && *build == '+') ! 585: { ! 586: while (*build && !(isascii(*build) && isspace(*build))) ! 587: { ! 588: build++; ! 589: } ! 590: while (*build && isascii(*build) && isspace(*build)) ! 591: { ! 592: build++; ! 593: } ! 594: } ! 595: #endif /* not CRUNCH */ ! 596: for (; *build; build++) ! 597: { ! 598: if (isspace(*build)) ! 599: { ! 600: msg("Too many %s to \"%s\" command.", ! 601: (argt & XFILE) ? "filenames" : "arguments", ! 602: cmdnames[cmdidx].name); ! 603: return; ! 604: } ! 605: } ! 606: } ! 607: ! 608: /* some commands have special default ranges */ ! 609: if (isdfl && (argt & DFLALL)) ! 610: { ! 611: frommark = MARK_FIRST; ! 612: tomark = MARK_LAST; ! 613: } ! 614: else if (isdfl && (argt & DFLNONE)) ! 615: { ! 616: frommark = tomark = 0L; ! 617: } ! 618: ! 619: /* write a newline if called from visual mode */ ! 620: if ((argt & NL) && mode != MODE_EX && !exwrote) ! 621: { ! 622: addch('\n'); ! 623: exrefresh(); ! 624: } ! 625: ! 626: /* act on the command */ ! 627: (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan); ! 628: } ! 629: ! 630: ! 631: /* This function executes EX commands from a file. It returns 1 normally, or ! 632: * 0 if the file could not be opened for reading. ! 633: */ ! 634: int doexrc(filename) ! 635: char *filename; /* name of a ".exrc" file */ ! 636: { ! 637: int fd; /* file descriptor */ ! 638: int len; /* length of the ".exrc" file */ ! 639: char buf[MAXRCLEN]; /* buffer, holds the entire .exrc file */ ! 640: ! 641: /* open the file, read it, and close */ ! 642: fd = open(filename, O_RDONLY); ! 643: if (fd < 0) ! 644: { ! 645: return 0; ! 646: } ! 647: len = tread(fd, buf, MAXRCLEN); ! 648: close(fd); ! 649: ! 650: /* execute the string */ ! 651: exstring(buf, len); ! 652: ! 653: return 1; ! 654: } ! 655: ! 656: void exstring(buf, len) ! 657: char *buf; /* the commands to execute */ ! 658: int len; /* the length of the string */ ! 659: { ! 660: char *cmd; /* start of a command */ ! 661: char *end; /* used to search for the end of cmd */ ! 662: ! 663: /* find & do each command */ ! 664: for (cmd = buf; cmd < &buf[len]; cmd = end + 1) ! 665: { ! 666: /* find the end of the command */ ! 667: for (end = cmd; *end != '\n' && *end != '|'; end++) ! 668: { ! 669: } ! 670: *end = '\0'; ! 671: ! 672: /* do it */ ! 673: doexcmd(cmd); ! 674: } ! 675: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.