[9] | 1 | /*
|
---|
| 2 | * Part 2 of the mined editor.
|
---|
| 3 | */
|
---|
| 4 |
|
---|
| 5 | /* ======================================================================== *
|
---|
| 6 | * Move Commands *
|
---|
| 7 | * ======================================================================== */
|
---|
| 8 |
|
---|
| 9 | #include "mined.h"
|
---|
| 10 | #include <string.h>
|
---|
| 11 |
|
---|
| 12 | /*
|
---|
| 13 | * Move one line up.
|
---|
| 14 | */
|
---|
| 15 | void UP()
|
---|
| 16 | {
|
---|
| 17 | if (y == 0) { /* Top line of screen. Scroll one line */
|
---|
| 18 | (void) reverse_scroll();
|
---|
| 19 | move_to(x, y);
|
---|
| 20 | }
|
---|
| 21 | else /* Move to previous line */
|
---|
| 22 | move_to(x, y - 1);
|
---|
| 23 | }
|
---|
| 24 |
|
---|
| 25 | /*
|
---|
| 26 | * Move one line down.
|
---|
| 27 | */
|
---|
| 28 | void DN()
|
---|
| 29 | {
|
---|
| 30 | if (y == last_y) { /* Last line of screen. Scroll one line */
|
---|
| 31 | if (bot_line->next == tail && bot_line->text[0] != '\n') {
|
---|
| 32 | dummy_line(); /* Create new empty line */
|
---|
| 33 | DN();
|
---|
| 34 | return;
|
---|
| 35 | }
|
---|
| 36 | else {
|
---|
| 37 | (void) forward_scroll();
|
---|
| 38 | move_to(x, y);
|
---|
| 39 | }
|
---|
| 40 | }
|
---|
| 41 | else /* Move to next line */
|
---|
| 42 | move_to(x, y + 1);
|
---|
| 43 | }
|
---|
| 44 |
|
---|
| 45 | /*
|
---|
| 46 | * Move left one position.
|
---|
| 47 | */
|
---|
| 48 | void LF()
|
---|
| 49 | {
|
---|
| 50 | if (x == 0 && get_shift(cur_line->shift_count) == 0) {/* Begin of line */
|
---|
| 51 | if (cur_line->prev != header) {
|
---|
| 52 | UP(); /* Move one line up */
|
---|
| 53 | move_to(LINE_END, y);
|
---|
| 54 | }
|
---|
| 55 | }
|
---|
| 56 | else
|
---|
| 57 | move_to(x - 1, y);
|
---|
| 58 | }
|
---|
| 59 |
|
---|
| 60 | /*
|
---|
| 61 | * Move right one position.
|
---|
| 62 | */
|
---|
| 63 | void RT()
|
---|
| 64 | {
|
---|
| 65 | if (*cur_text == '\n') {
|
---|
| 66 | if (cur_line->next != tail) { /* Last char of file */
|
---|
| 67 | DN(); /* Move one line down */
|
---|
| 68 | move_to(LINE_START, y);
|
---|
| 69 | }
|
---|
| 70 | }
|
---|
| 71 | else
|
---|
| 72 | move_to(x + 1, y);
|
---|
| 73 | }
|
---|
| 74 |
|
---|
| 75 | /*
|
---|
| 76 | * Move to coordinates [0, 0] on screen.
|
---|
| 77 | */
|
---|
| 78 | void HIGH()
|
---|
| 79 | {
|
---|
| 80 | move_to(0, 0);
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 | /*
|
---|
| 84 | * Move to coordinates [0, YMAX] on screen.
|
---|
| 85 | */
|
---|
| 86 | void LOW()
|
---|
| 87 | {
|
---|
| 88 | move_to(0, last_y);
|
---|
| 89 | }
|
---|
| 90 |
|
---|
| 91 | /*
|
---|
| 92 | * Move to begin of line.
|
---|
| 93 | */
|
---|
| 94 | void BL()
|
---|
| 95 | {
|
---|
| 96 | move_to(LINE_START, y);
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | /*
|
---|
| 100 | * Move to end of line.
|
---|
| 101 | */
|
---|
| 102 | void EL()
|
---|
| 103 | {
|
---|
| 104 | move_to(LINE_END, y);
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | /*
|
---|
| 108 | * GOTO() prompts for a linenumber and moves to that line.
|
---|
| 109 | */
|
---|
| 110 | void GOTO()
|
---|
| 111 | {
|
---|
| 112 | int number;
|
---|
| 113 | LINE *line;
|
---|
| 114 |
|
---|
| 115 | if (get_number("Please enter line number.", &number) == ERRORS)
|
---|
| 116 | return;
|
---|
| 117 |
|
---|
| 118 | if (number <= 0 || (line = proceed(header->next, number - 1)) == tail)
|
---|
| 119 | error("Illegal line number: ", num_out((long) number));
|
---|
| 120 | else
|
---|
| 121 | move_to(x, find_y(line));
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | /*
|
---|
| 125 | * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes
|
---|
| 126 | * top_line of display.) Try to leave the cursor on the same line. If this is
|
---|
| 127 | * not possible, leave cursor on the line halfway the page.
|
---|
| 128 | */
|
---|
| 129 | void PD()
|
---|
| 130 | {
|
---|
| 131 | register int i;
|
---|
| 132 |
|
---|
| 133 | for (i = 0; i < screenmax; i++)
|
---|
| 134 | if (forward_scroll() == ERRORS)
|
---|
| 135 | break; /* EOF reached */
|
---|
| 136 | if (y - i < 0) /* Line no longer on screen */
|
---|
| 137 | move_to(0, screenmax >> 1);
|
---|
| 138 | else
|
---|
| 139 | move_to(0, y - i);
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 |
|
---|
| 143 | /*
|
---|
| 144 | * Scroll backwards one page or to top of file, whatever comes first. (Top_line
|
---|
| 145 | * becomes bot_line of display). The very bottom line (YMAX) is always blank.
|
---|
| 146 | * Try to leave the cursor on the same line. If this is not possible, leave
|
---|
| 147 | * cursor on the line halfway the page.
|
---|
| 148 | */
|
---|
| 149 | void PU()
|
---|
| 150 | {
|
---|
| 151 | register int i;
|
---|
| 152 |
|
---|
| 153 | for (i = 0; i < screenmax; i++)
|
---|
| 154 | if (reverse_scroll() == ERRORS)
|
---|
| 155 | break; /* Top of file reached */
|
---|
| 156 | set_cursor(0, ymax); /* Erase very bottom line */
|
---|
| 157 | #ifdef UNIX
|
---|
| 158 | tputs(CE, 0, _putchar);
|
---|
| 159 | #else
|
---|
| 160 | string_print(blank_line);
|
---|
| 161 | #endif /* UNIX */
|
---|
| 162 | if (y + i > screenmax) /* line no longer on screen */
|
---|
| 163 | move_to(0, screenmax >> 1);
|
---|
| 164 | else
|
---|
| 165 | move_to(0, y + i);
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | /*
|
---|
| 169 | * Go to top of file, scrolling if possible, else redrawing screen.
|
---|
| 170 | */
|
---|
| 171 | void HO()
|
---|
| 172 | {
|
---|
| 173 | if (proceed(top_line, -screenmax) == header)
|
---|
| 174 | PU(); /* It fits. Let PU do it */
|
---|
| 175 | else {
|
---|
| 176 | reset(header->next, 0);/* Reset top_line, etc. */
|
---|
| 177 | RD(); /* Display full page */
|
---|
| 178 | }
|
---|
| 179 | move_to(LINE_START, 0);
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | /*
|
---|
| 183 | * Go to last line of file, scrolling if possible, else redrawing screen
|
---|
| 184 | */
|
---|
| 185 | void EF()
|
---|
| 186 | {
|
---|
| 187 | if (tail->prev->text[0] != '\n')
|
---|
| 188 | dummy_line();
|
---|
| 189 | if (proceed(bot_line, screenmax) == tail)
|
---|
| 190 | PD(); /* It fits. Let PD do it */
|
---|
| 191 | else {
|
---|
| 192 | reset(proceed(tail->prev, -screenmax), screenmax);
|
---|
| 193 | RD(); /* Display full page */
|
---|
| 194 | }
|
---|
| 195 | move_to(LINE_START, last_y);
|
---|
| 196 | }
|
---|
| 197 |
|
---|
| 198 | /*
|
---|
| 199 | * Scroll one line up. Leave the cursor on the same line (if possible).
|
---|
| 200 | */
|
---|
| 201 | void SU()
|
---|
| 202 | {
|
---|
| 203 | if (top_line->prev == header) /* Top of file. Can't scroll */
|
---|
| 204 | return;
|
---|
| 205 |
|
---|
| 206 | (void) reverse_scroll();
|
---|
| 207 | set_cursor(0, ymax); /* Erase very bottom line */
|
---|
| 208 | #ifdef UNIX
|
---|
| 209 | tputs(CE, 0, _putchar);
|
---|
| 210 | #else
|
---|
| 211 | string_print(blank_line);
|
---|
| 212 | #endif /* UNIX */
|
---|
| 213 | move_to(x, (y == screenmax) ? screenmax : y + 1);
|
---|
| 214 | }
|
---|
| 215 |
|
---|
| 216 | /*
|
---|
| 217 | * Scroll one line down. Leave the cursor on the same line (if possible).
|
---|
| 218 | */
|
---|
| 219 | void SD()
|
---|
| 220 | {
|
---|
| 221 | if (forward_scroll() != ERRORS)
|
---|
| 222 | move_to(x, (y == 0) ? 0 : y - 1);
|
---|
| 223 | else
|
---|
| 224 | set_cursor(x, y);
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 | /*
|
---|
| 228 | * Perform a forward scroll. It returns ERRORS if we're at the last line of the
|
---|
| 229 | * file.
|
---|
| 230 | */
|
---|
| 231 | int forward_scroll()
|
---|
| 232 | {
|
---|
| 233 | if (bot_line->next == tail) /* Last line of file. No dice */
|
---|
| 234 | return ERRORS;
|
---|
| 235 | top_line = top_line->next;
|
---|
| 236 | bot_line = bot_line->next;
|
---|
| 237 | cur_line = cur_line->next;
|
---|
| 238 | set_cursor(0, ymax);
|
---|
| 239 | line_print(bot_line);
|
---|
| 240 |
|
---|
| 241 | return FINE;
|
---|
| 242 | }
|
---|
| 243 |
|
---|
| 244 | /*
|
---|
| 245 | * Perform a backwards scroll. It returns ERRORS if we're at the first line
|
---|
| 246 | * of the file.
|
---|
| 247 | */
|
---|
| 248 | int reverse_scroll()
|
---|
| 249 | {
|
---|
| 250 | if (top_line->prev == header)
|
---|
| 251 | return ERRORS; /* Top of file. Can't scroll */
|
---|
| 252 |
|
---|
| 253 | if (last_y != screenmax) /* Reset last_y if necessary */
|
---|
| 254 | last_y++;
|
---|
| 255 | else
|
---|
| 256 | bot_line = bot_line->prev; /* Else adjust bot_line */
|
---|
| 257 | top_line = top_line->prev;
|
---|
| 258 | cur_line = cur_line->prev;
|
---|
| 259 |
|
---|
| 260 | /* Perform the scroll */
|
---|
| 261 | set_cursor(0, 0);
|
---|
| 262 | #ifdef UNIX
|
---|
| 263 | tputs(AL, 0, _putchar);
|
---|
| 264 | #else
|
---|
| 265 | string_print(rev_scroll);
|
---|
| 266 | #endif /* UNIX */
|
---|
| 267 | set_cursor(0, 0);
|
---|
| 268 | line_print(top_line);
|
---|
| 269 |
|
---|
| 270 | return FINE;
|
---|
| 271 | }
|
---|
| 272 |
|
---|
| 273 | /*
|
---|
| 274 | * A word is defined as a number of non-blank characters separated by tabs
|
---|
| 275 | * spaces or linefeeds.
|
---|
| 276 | */
|
---|
| 277 |
|
---|
| 278 | /*
|
---|
| 279 | * MP() moves to the start of the previous word. A word is defined as a
|
---|
| 280 | * number of non-blank characters separated by tabs spaces or linefeeds.
|
---|
| 281 | */
|
---|
| 282 | void MP()
|
---|
| 283 | {
|
---|
| 284 | move_previous_word(NO_DELETE);
|
---|
| 285 | }
|
---|
| 286 |
|
---|
| 287 | void move_previous_word(remove)
|
---|
| 288 | FLAG remove;
|
---|
| 289 | {
|
---|
| 290 | register char *begin_line;
|
---|
| 291 | register char *textp;
|
---|
| 292 | char start_char = *cur_text;
|
---|
| 293 | char *start_pos = cur_text;
|
---|
| 294 |
|
---|
| 295 | /* Fist check if we're at the beginning of line. */
|
---|
| 296 | if (cur_text == cur_line->text) {
|
---|
| 297 | if (cur_line->prev == header)
|
---|
| 298 | return;
|
---|
| 299 | start_char = '\0';
|
---|
| 300 | }
|
---|
| 301 |
|
---|
| 302 | LF();
|
---|
| 303 |
|
---|
| 304 | begin_line = cur_line->text;
|
---|
| 305 | textp = cur_text;
|
---|
| 306 |
|
---|
| 307 | /* Check if we're in the middle of a word. */
|
---|
| 308 | if (!alpha(*textp) || !alpha(start_char)) {
|
---|
| 309 | while (textp != begin_line && (white_space(*textp) || *textp == '\n'))
|
---|
| 310 | textp--;
|
---|
| 311 | }
|
---|
| 312 |
|
---|
| 313 | /* Now we're at the end of previous word. Skip non-blanks until a blank comes */
|
---|
| 314 | while (textp != begin_line && alpha(*textp))
|
---|
| 315 | textp--;
|
---|
| 316 |
|
---|
| 317 | /* Go to the next char if we're not at the beginning of the line */
|
---|
| 318 | if (textp != begin_line && *textp != '\n')
|
---|
| 319 | textp++;
|
---|
| 320 |
|
---|
| 321 | /* Find the x-coordinate of this address, and move to it */
|
---|
| 322 | move_address(textp);
|
---|
| 323 | if (remove == DELETE)
|
---|
| 324 | delete(cur_line, textp, cur_line, start_pos);
|
---|
| 325 | }
|
---|
| 326 |
|
---|
| 327 | /*
|
---|
| 328 | * MN() moves to the start of the next word. A word is defined as a number of
|
---|
| 329 | * non-blank characters separated by tabs spaces or linefeeds. Always keep in
|
---|
| 330 | * mind that the pointer shouldn't pass the '\n'.
|
---|
| 331 | */
|
---|
| 332 | void MN()
|
---|
| 333 | {
|
---|
| 334 | move_next_word(NO_DELETE);
|
---|
| 335 | }
|
---|
| 336 |
|
---|
| 337 | void move_next_word(remove)
|
---|
| 338 | FLAG remove;
|
---|
| 339 | {
|
---|
| 340 | register char *textp = cur_text;
|
---|
| 341 |
|
---|
| 342 | /* Move to the end of the current word. */
|
---|
| 343 | while (*textp != '\n' && alpha(*textp))
|
---|
| 344 | textp++;
|
---|
| 345 |
|
---|
| 346 | /* Skip all white spaces */
|
---|
| 347 | while (*textp != '\n' && white_space(*textp))
|
---|
| 348 | textp++;
|
---|
| 349 | /* If we're deleting. delete the text in between */
|
---|
| 350 | if (remove == DELETE) {
|
---|
| 351 | delete(cur_line, cur_text, cur_line, textp);
|
---|
| 352 | return;
|
---|
| 353 | }
|
---|
| 354 |
|
---|
| 355 | /* If we're at end of line. move to the first word on the next line. */
|
---|
| 356 | if (*textp == '\n' && cur_line->next != tail) {
|
---|
| 357 | DN();
|
---|
| 358 | move_to(LINE_START, y);
|
---|
| 359 | textp = cur_text;
|
---|
| 360 | while (*textp != '\n' && white_space(*textp))
|
---|
| 361 | textp++;
|
---|
| 362 | }
|
---|
| 363 | move_address(textp);
|
---|
| 364 | }
|
---|
| 365 |
|
---|
| 366 | /* ======================================================================== *
|
---|
| 367 | * Modify Commands *
|
---|
| 368 | * ======================================================================== */
|
---|
| 369 |
|
---|
| 370 | /*
|
---|
| 371 | * DCC deletes the character under the cursor. If this character is a '\n' the
|
---|
| 372 | * current line is joined with the next one.
|
---|
| 373 | * If this character is the only character of the line, the current line will
|
---|
| 374 | * be deleted.
|
---|
| 375 | */
|
---|
| 376 | void DCC()
|
---|
| 377 | {
|
---|
| 378 | if (*cur_text == '\n')
|
---|
| 379 | delete(cur_line,cur_text, cur_line->next,cur_line->next->text);
|
---|
| 380 | else
|
---|
| 381 | delete(cur_line, cur_text, cur_line, cur_text + 1);
|
---|
| 382 | }
|
---|
| 383 |
|
---|
| 384 | /*
|
---|
| 385 | * DPC deletes the character on the left side of the cursor. If the cursor is
|
---|
| 386 | * at the beginning of the line, the last character if the previous line is
|
---|
| 387 | * deleted.
|
---|
| 388 | */
|
---|
| 389 | void DPC()
|
---|
| 390 | {
|
---|
| 391 | if (x == 0 && cur_line->prev == header)
|
---|
| 392 | return; /* Top of file */
|
---|
| 393 |
|
---|
| 394 | LF(); /* Move one left */
|
---|
| 395 | DCC(); /* Delete character under cursor */
|
---|
| 396 | }
|
---|
| 397 |
|
---|
| 398 | /*
|
---|
| 399 | * DLN deletes all characters until the end of the line. If the current
|
---|
| 400 | * character is a '\n', then delete that char.
|
---|
| 401 | */
|
---|
| 402 | void DLN()
|
---|
| 403 | {
|
---|
| 404 | if (*cur_text == '\n')
|
---|
| 405 | DCC();
|
---|
| 406 | else
|
---|
| 407 | delete(cur_line, cur_text, cur_line, cur_text + length_of(cur_text) -1);
|
---|
| 408 | }
|
---|
| 409 |
|
---|
| 410 | /*
|
---|
| 411 | * DNW() deletes the next word (as described in MN())
|
---|
| 412 | */
|
---|
| 413 | void DNW()
|
---|
| 414 | {
|
---|
| 415 | if (*cur_text == '\n')
|
---|
| 416 | DCC();
|
---|
| 417 | else
|
---|
| 418 | move_next_word(DELETE);
|
---|
| 419 | }
|
---|
| 420 |
|
---|
| 421 | /*
|
---|
| 422 | * DPW() deletes the next word (as described in MP())
|
---|
| 423 | */
|
---|
| 424 | void DPW()
|
---|
| 425 | {
|
---|
| 426 | if (cur_text == cur_line->text)
|
---|
| 427 | DPC();
|
---|
| 428 | else
|
---|
| 429 | move_previous_word(DELETE);
|
---|
| 430 | }
|
---|
| 431 |
|
---|
| 432 | /*
|
---|
| 433 | * Insert character `character' at current location.
|
---|
| 434 | */
|
---|
| 435 | void S(character)
|
---|
| 436 | register char character;
|
---|
| 437 | {
|
---|
| 438 | static char buffer[2];
|
---|
| 439 |
|
---|
| 440 | buffer[0] = character;
|
---|
| 441 | /* Insert the character */
|
---|
| 442 | if (insert(cur_line, cur_text, buffer) == ERRORS)
|
---|
| 443 | return;
|
---|
| 444 |
|
---|
| 445 | /* Fix screen */
|
---|
| 446 | if (character == '\n') {
|
---|
| 447 | set_cursor(0, y);
|
---|
| 448 | if (y == screenmax) { /* Can't use display */
|
---|
| 449 | line_print(cur_line);
|
---|
| 450 | (void) forward_scroll();
|
---|
| 451 | }
|
---|
| 452 | else {
|
---|
| 453 | reset(top_line, y); /* Reset pointers */
|
---|
| 454 | display(0, y, cur_line, last_y - y);
|
---|
| 455 | }
|
---|
| 456 | move_to(0, (y == screenmax) ? y : y + 1);
|
---|
| 457 | }
|
---|
| 458 | else if (x + 1 == XBREAK)/* If line must be shifted, just call move_to*/
|
---|
| 459 | move_to(x + 1, y);
|
---|
| 460 | else { /* else display rest of line */
|
---|
| 461 | put_line(cur_line, x, FALSE);
|
---|
| 462 | move_to(x + 1, y);
|
---|
| 463 | }
|
---|
| 464 | }
|
---|
| 465 |
|
---|
| 466 | /*
|
---|
| 467 | * CTL inserts a control-char at the current location. A message that this
|
---|
| 468 | * function is called is displayed at the status line.
|
---|
| 469 | */
|
---|
| 470 | void CTL()
|
---|
| 471 | {
|
---|
| 472 | register char ctrl;
|
---|
| 473 |
|
---|
| 474 | status_line("Enter control character.", NIL_PTR);
|
---|
| 475 | if ((ctrl = getchar()) >= '\01' && ctrl <= '\037') {
|
---|
| 476 | S(ctrl); /* Insert the char */
|
---|
| 477 | clear_status();
|
---|
| 478 | }
|
---|
| 479 | else
|
---|
| 480 | error ("Unknown control character", NIL_PTR);
|
---|
| 481 | }
|
---|
| 482 |
|
---|
| 483 | /*
|
---|
| 484 | * LIB insert a line at the current position and moves back to the end of
|
---|
| 485 | * the previous line.
|
---|
| 486 | */
|
---|
| 487 | void LIB()
|
---|
| 488 | {
|
---|
| 489 | S('\n'); /* Insert the line */
|
---|
| 490 | UP(); /* Move one line up */
|
---|
| 491 | move_to(LINE_END, y); /* Move to end of this line */
|
---|
| 492 | }
|
---|
| 493 |
|
---|
| 494 | /*
|
---|
| 495 | * Line_insert() inserts a new line with text pointed to by `string'.
|
---|
| 496 | * It returns the address of the new line.
|
---|
| 497 | */
|
---|
| 498 | LINE *line_insert(line, string, len)
|
---|
| 499 | register LINE *line;
|
---|
| 500 | char *string;
|
---|
| 501 | int len;
|
---|
| 502 | {
|
---|
| 503 | register LINE *new_line;
|
---|
| 504 |
|
---|
| 505 | /* Allocate space for LINE structure and text */
|
---|
| 506 | new_line = install_line(string, len);
|
---|
| 507 |
|
---|
| 508 | /* Install the line into the double linked list */
|
---|
| 509 | new_line->prev = line;
|
---|
| 510 | new_line->next = line->next;
|
---|
| 511 | line->next = new_line;
|
---|
| 512 | new_line->next->prev = new_line;
|
---|
| 513 |
|
---|
| 514 | /* Increment nlines */
|
---|
| 515 | nlines++;
|
---|
| 516 |
|
---|
| 517 | return new_line;
|
---|
| 518 | }
|
---|
| 519 |
|
---|
| 520 | /*
|
---|
| 521 | * Insert() insert the string `string' at the given line and location.
|
---|
| 522 | */
|
---|
| 523 | int insert(line, location, string)
|
---|
| 524 | register LINE *line;
|
---|
| 525 | char *location, *string;
|
---|
| 526 | {
|
---|
| 527 | register char *bufp = text_buffer; /* Buffer for building line */
|
---|
| 528 | register char *textp = line->text;
|
---|
| 529 |
|
---|
| 530 | if (length_of(textp) + length_of(string) >= MAX_CHARS) {
|
---|
| 531 | error("Line too long", NIL_PTR);
|
---|
| 532 | return ERRORS;
|
---|
| 533 | }
|
---|
| 534 |
|
---|
| 535 | modified = TRUE; /* File has been modified */
|
---|
| 536 |
|
---|
| 537 | /* Copy part of line until `location' has been reached */
|
---|
| 538 | while (textp != location)
|
---|
| 539 | *bufp++ = *textp++;
|
---|
| 540 |
|
---|
| 541 | /* Insert string at this location */
|
---|
| 542 | while (*string != '\0')
|
---|
| 543 | *bufp++ = *string++;
|
---|
| 544 | *bufp = '\0';
|
---|
| 545 |
|
---|
| 546 | if (*(string - 1) == '\n') /* Insert a new line */
|
---|
| 547 | (void) line_insert(line, location, length_of(location));
|
---|
| 548 | else /* Append last part of line */
|
---|
| 549 | copy_string(bufp, location);
|
---|
| 550 |
|
---|
| 551 | /* Install the new text in this line */
|
---|
| 552 | free_space(line->text);
|
---|
| 553 | line->text = alloc(length_of(text_buffer) + 1);
|
---|
| 554 | copy_string(line->text, text_buffer);
|
---|
| 555 |
|
---|
| 556 | return FINE;
|
---|
| 557 | }
|
---|
| 558 |
|
---|
| 559 | /*
|
---|
| 560 | * Line_delete() deletes the argument line out of the line list. The pointer to
|
---|
| 561 | * the next line is returned.
|
---|
| 562 | */
|
---|
| 563 | LINE *line_delete(line)
|
---|
| 564 | register LINE *line;
|
---|
| 565 | {
|
---|
| 566 | register LINE *next_line = line->next;
|
---|
| 567 |
|
---|
| 568 | /* Delete the line */
|
---|
| 569 | line->prev->next = line->next;
|
---|
| 570 | line->next->prev = line->prev;
|
---|
| 571 |
|
---|
| 572 | /* Free allocated space */
|
---|
| 573 | free_space(line->text);
|
---|
| 574 | free_space((char*)line);
|
---|
| 575 |
|
---|
| 576 | /* Decrement nlines */
|
---|
| 577 | nlines--;
|
---|
| 578 |
|
---|
| 579 | return next_line;
|
---|
| 580 | }
|
---|
| 581 |
|
---|
| 582 | /*
|
---|
| 583 | * Delete() deletes all the characters (including newlines) between the
|
---|
| 584 | * startposition and endposition and fixes the screen accordingly. It
|
---|
| 585 | * returns the number of lines deleted.
|
---|
| 586 | */
|
---|
| 587 | void delete(start_line, start_textp, end_line, end_textp)
|
---|
| 588 | register LINE *start_line;
|
---|
| 589 | LINE *end_line;
|
---|
| 590 | char *start_textp, *end_textp;
|
---|
| 591 | {
|
---|
| 592 | register char *textp = start_line->text;
|
---|
| 593 | register char *bufp = text_buffer; /* Storage for new line->text */
|
---|
| 594 | LINE *line, *stop;
|
---|
| 595 | int line_cnt = 0; /* Nr of lines deleted */
|
---|
| 596 | int count = 0;
|
---|
| 597 | int shift = 0; /* Used in shift calculation */
|
---|
| 598 | int nx = x;
|
---|
| 599 |
|
---|
| 600 | modified = TRUE; /* File has been modified */
|
---|
| 601 |
|
---|
| 602 | /* Set up new line. Copy first part of start line until start_position. */
|
---|
| 603 | while (textp < start_textp) {
|
---|
| 604 | *bufp++ = *textp++;
|
---|
| 605 | count++;
|
---|
| 606 | }
|
---|
| 607 |
|
---|
| 608 | /* Check if line doesn't exceed MAX_CHARS */
|
---|
| 609 | if (count + length_of(end_textp) >= MAX_CHARS) {
|
---|
| 610 | error("Line too long", NIL_PTR);
|
---|
| 611 | return;
|
---|
| 612 | }
|
---|
| 613 |
|
---|
| 614 | /* Copy last part of end_line if end_line is not tail */
|
---|
| 615 | copy_string(bufp, (end_textp != NIL_PTR) ? end_textp : "\n");
|
---|
| 616 |
|
---|
| 617 | /* Delete all lines between start and end_position (including end_line) */
|
---|
| 618 | line = start_line->next;
|
---|
| 619 | stop = end_line->next;
|
---|
| 620 | while (line != stop && line != tail) {
|
---|
| 621 | line = line_delete(line);
|
---|
| 622 | line_cnt++;
|
---|
| 623 | }
|
---|
| 624 |
|
---|
| 625 | /* Check if last line of file should be deleted */
|
---|
| 626 | if (end_textp == NIL_PTR && length_of(start_line->text) == 1 && nlines > 1) {
|
---|
| 627 | start_line = start_line->prev;
|
---|
| 628 | (void) line_delete(start_line->next);
|
---|
| 629 | line_cnt++;
|
---|
| 630 | }
|
---|
| 631 | else { /* Install new text */
|
---|
| 632 | free_space(start_line->text);
|
---|
| 633 | start_line->text = alloc(length_of(text_buffer) + 1);
|
---|
| 634 | copy_string(start_line->text, text_buffer);
|
---|
| 635 | }
|
---|
| 636 |
|
---|
| 637 | /* Fix screen. First check if line is shifted. Perhaps we should shift it back*/
|
---|
| 638 | if (get_shift(start_line->shift_count)) {
|
---|
| 639 | shift = (XBREAK - count_chars(start_line)) / SHIFT_SIZE;
|
---|
| 640 | if (shift > 0) { /* Shift line `shift' back */
|
---|
| 641 | if (shift >= get_shift(start_line->shift_count))
|
---|
| 642 | start_line->shift_count = 0;
|
---|
| 643 | else
|
---|
| 644 | start_line->shift_count -= shift;
|
---|
| 645 | nx += shift * SHIFT_SIZE;/* Reset x value */
|
---|
| 646 | }
|
---|
| 647 | }
|
---|
| 648 |
|
---|
| 649 | if (line_cnt == 0) { /* Check if only one line changed */
|
---|
| 650 | if (shift > 0) { /* Reprint whole line */
|
---|
| 651 | set_cursor(0, y);
|
---|
| 652 | line_print(start_line);
|
---|
| 653 | }
|
---|
| 654 | else { /* Just display last part of line */
|
---|
| 655 | set_cursor(x, y);
|
---|
| 656 | put_line(start_line, x, TRUE);
|
---|
| 657 | }
|
---|
| 658 | move_to(nx, y); /* Reset cur_text */
|
---|
| 659 | return;
|
---|
| 660 | }
|
---|
| 661 |
|
---|
| 662 | shift = last_y; /* Save value */
|
---|
| 663 | reset(top_line, y);
|
---|
| 664 | display(0, y, start_line, shift - y);
|
---|
| 665 | move_to((line_cnt == 1) ? nx : 0, y);
|
---|
| 666 | }
|
---|
| 667 |
|
---|
| 668 | /* ======================================================================== *
|
---|
| 669 | * Yank Commands *
|
---|
| 670 | * ======================================================================== */
|
---|
| 671 |
|
---|
| 672 | LINE *mark_line; /* For marking position. */
|
---|
| 673 | char *mark_text;
|
---|
| 674 | int lines_saved; /* Nr of lines in buffer */
|
---|
| 675 |
|
---|
| 676 | /*
|
---|
| 677 | * PT() inserts the buffer at the current location.
|
---|
| 678 | */
|
---|
| 679 | void PT()
|
---|
| 680 | {
|
---|
| 681 | register int fd; /* File descriptor for buffer */
|
---|
| 682 |
|
---|
| 683 | if ((fd = scratch_file(READ)) == ERRORS)
|
---|
| 684 | error("Buffer is empty.", NIL_PTR);
|
---|
| 685 | else {
|
---|
| 686 | file_insert(fd, FALSE);/* Insert the buffer */
|
---|
| 687 | (void) close(fd);
|
---|
| 688 | }
|
---|
| 689 | }
|
---|
| 690 |
|
---|
| 691 | /*
|
---|
| 692 | * IF() prompt for a filename and inserts the file at the current location
|
---|
| 693 | * in the file.
|
---|
| 694 | */
|
---|
| 695 | void IF()
|
---|
| 696 | {
|
---|
| 697 | register int fd; /* File descriptor of file */
|
---|
| 698 | char name[LINE_LEN]; /* Buffer for file name */
|
---|
| 699 |
|
---|
| 700 | /* Get the file name */
|
---|
| 701 | if (get_file("Get and insert file:", name) != FINE)
|
---|
| 702 | return;
|
---|
| 703 |
|
---|
| 704 | if ((fd = open(name, 0)) < 0)
|
---|
| 705 | error("Cannot open ", name);
|
---|
| 706 | else {
|
---|
| 707 | file_insert(fd, TRUE); /* Insert the file */
|
---|
| 708 | (void) close(fd);
|
---|
| 709 | }
|
---|
| 710 | }
|
---|
| 711 |
|
---|
| 712 | /*
|
---|
| 713 | * File_insert() inserts a an opened file (as given by filedescriptor fd)
|
---|
| 714 | * at the current location.
|
---|
| 715 | */
|
---|
| 716 | void file_insert(fd, old_pos)
|
---|
| 717 | int fd;
|
---|
| 718 | FLAG old_pos;
|
---|
| 719 | {
|
---|
| 720 | char line_buffer[MAX_CHARS]; /* Buffer for next line */
|
---|
| 721 | register LINE *line = cur_line;
|
---|
| 722 | register int line_count = nlines; /* Nr of lines inserted */
|
---|
| 723 | LINE *page = cur_line;
|
---|
| 724 | int ret = ERRORS;
|
---|
| 725 |
|
---|
| 726 | /* Get the first piece of text (might be ended with a '\n') from fd */
|
---|
| 727 | if (get_line(fd, line_buffer) == ERRORS)
|
---|
| 728 | return; /* Empty file */
|
---|
| 729 |
|
---|
| 730 | /* Insert this text at the current location. */
|
---|
| 731 | if (insert(line, cur_text, line_buffer) == ERRORS)
|
---|
| 732 | return;
|
---|
| 733 |
|
---|
| 734 | /* Repeat getting lines (and inserting lines) until EOF is reached */
|
---|
| 735 | while ((ret = get_line(fd, line_buffer)) != ERRORS && ret != NO_LINE)
|
---|
| 736 | line = line_insert(line, line_buffer, ret);
|
---|
| 737 |
|
---|
| 738 | if (ret == NO_LINE) { /* Last line read not ended by a '\n' */
|
---|
| 739 | line = line->next;
|
---|
| 740 | (void) insert(line, line->text, line_buffer);
|
---|
| 741 | }
|
---|
| 742 |
|
---|
| 743 | /* Calculate nr of lines added */
|
---|
| 744 | line_count = nlines - line_count;
|
---|
| 745 |
|
---|
| 746 | /* Fix the screen */
|
---|
| 747 | if (line_count == 0) { /* Only one line changed */
|
---|
| 748 | set_cursor(0, y);
|
---|
| 749 | line_print(line);
|
---|
| 750 | move_to((old_pos == TRUE) ? x : x + length_of(line_buffer), y);
|
---|
| 751 | }
|
---|
| 752 | else { /* Several lines changed */
|
---|
| 753 | reset(top_line, y); /* Reset pointers */
|
---|
| 754 | while (page != line && page != bot_line->next)
|
---|
| 755 | page = page->next;
|
---|
| 756 | if (page != bot_line->next || old_pos == TRUE)
|
---|
| 757 | display(0, y, cur_line, screenmax - y);
|
---|
| 758 | if (old_pos == TRUE)
|
---|
| 759 | move_to(x, y);
|
---|
| 760 | else if (ret == NO_LINE)
|
---|
| 761 | move_to(length_of(line_buffer), find_y(line));
|
---|
| 762 | else
|
---|
| 763 | move_to(0, find_y(line->next));
|
---|
| 764 | }
|
---|
| 765 |
|
---|
| 766 | /* If nr of added line >= REPORT, print the count */
|
---|
| 767 | if (line_count >= REPORT)
|
---|
| 768 | status_line(num_out((long) line_count), " lines added.");
|
---|
| 769 | }
|
---|
| 770 |
|
---|
| 771 | /*
|
---|
| 772 | * WB() writes the buffer (yank_file) into another file, which
|
---|
| 773 | * is prompted for.
|
---|
| 774 | */
|
---|
| 775 | void WB()
|
---|
| 776 | {
|
---|
| 777 | register int new_fd; /* Filedescriptor to copy file */
|
---|
| 778 | int yank_fd; /* Filedescriptor to buffer */
|
---|
| 779 | register int cnt; /* Count check for read/write */
|
---|
| 780 | int ret = 0; /* Error check for write */
|
---|
| 781 | char file[LINE_LEN]; /* Output file */
|
---|
| 782 |
|
---|
| 783 | /* Checkout the buffer */
|
---|
| 784 | if ((yank_fd = scratch_file(READ)) == ERRORS) {
|
---|
| 785 | error("Buffer is empty.", NIL_PTR);
|
---|
| 786 | return;
|
---|
| 787 | }
|
---|
| 788 |
|
---|
| 789 | /* Get file name */
|
---|
| 790 | if (get_file("Write buffer to file:", file) != FINE)
|
---|
| 791 | return;
|
---|
| 792 |
|
---|
| 793 | /* Creat the new file */
|
---|
| 794 | if ((new_fd = creat(file, 0644)) < 0) {
|
---|
| 795 | error("Cannot create ", file);
|
---|
| 796 | return;
|
---|
| 797 | }
|
---|
| 798 |
|
---|
| 799 | status_line("Writing ", file);
|
---|
| 800 |
|
---|
| 801 | /* Copy buffer into file */
|
---|
| 802 | while ((cnt = read(yank_fd, text_buffer, sizeof(text_buffer))) > 0)
|
---|
| 803 | if (write(new_fd, text_buffer, cnt) != cnt) {
|
---|
| 804 | bad_write(new_fd);
|
---|
| 805 | ret = ERRORS;
|
---|
| 806 | break;
|
---|
| 807 | }
|
---|
| 808 |
|
---|
| 809 | /* Clean up open files and status_line */
|
---|
| 810 | (void) close(new_fd);
|
---|
| 811 | (void) close(yank_fd);
|
---|
| 812 |
|
---|
| 813 | if (ret != ERRORS) /* Bad write */
|
---|
| 814 | file_status("Wrote", chars_saved, file, lines_saved, TRUE, FALSE);
|
---|
| 815 | }
|
---|
| 816 |
|
---|
| 817 | /*
|
---|
| 818 | * MA sets mark_line (mark_text) to the current line (text pointer).
|
---|
| 819 | */
|
---|
| 820 | void MA()
|
---|
| 821 | {
|
---|
| 822 | mark_line = cur_line;
|
---|
| 823 | mark_text = cur_text;
|
---|
| 824 | status_line("Mark set", NIL_PTR);
|
---|
| 825 | }
|
---|
| 826 |
|
---|
| 827 | /*
|
---|
| 828 | * YA() puts the text between the marked position and the current
|
---|
| 829 | * in the buffer.
|
---|
| 830 | */
|
---|
| 831 | void YA()
|
---|
| 832 | {
|
---|
| 833 | set_up(NO_DELETE);
|
---|
| 834 | }
|
---|
| 835 |
|
---|
| 836 | /*
|
---|
| 837 | * DT() is essentially the same as YA(), but in DT() the text is deleted.
|
---|
| 838 | */
|
---|
| 839 | void DT()
|
---|
| 840 | {
|
---|
| 841 | set_up(DELETE);
|
---|
| 842 | }
|
---|
| 843 |
|
---|
| 844 | /*
|
---|
| 845 | * Set_up is an interface to the actual yank. It calls checkmark () to check
|
---|
| 846 | * if the marked position is still valid. If it is, yank is called with the
|
---|
| 847 | * arguments in the right order.
|
---|
| 848 | */
|
---|
| 849 | void set_up(remove)
|
---|
| 850 | FLAG remove; /* DELETE if text should be deleted */
|
---|
| 851 | {
|
---|
| 852 | switch (checkmark()) {
|
---|
| 853 | case NOT_VALID :
|
---|
| 854 | error("Mark not set.", NIL_PTR);
|
---|
| 855 | return;
|
---|
| 856 | case SMALLER :
|
---|
| 857 | yank(mark_line, mark_text, cur_line, cur_text, remove);
|
---|
| 858 | break;
|
---|
| 859 | case BIGGER :
|
---|
| 860 | yank(cur_line, cur_text, mark_line, mark_text, remove);
|
---|
| 861 | break;
|
---|
| 862 | case SAME : /* Ignore stupid behaviour */
|
---|
| 863 | yank_status = EMPTY;
|
---|
| 864 | chars_saved = 0L;
|
---|
| 865 | status_line("0 characters saved in buffer.", NIL_PTR);
|
---|
| 866 | break;
|
---|
| 867 | }
|
---|
| 868 | }
|
---|
| 869 |
|
---|
| 870 | /*
|
---|
| 871 | * Check_mark() checks if mark_line and mark_text are still valid pointers. If
|
---|
| 872 | * they are it returns SMALLER if the marked position is before the current,
|
---|
| 873 | * BIGGER if it isn't or SAME if somebody didn't get the point.
|
---|
| 874 | * NOT_VALID is returned when mark_line and/or mark_text are no longer valid.
|
---|
| 875 | * Legal() checks if mark_text is valid on the mark_line.
|
---|
| 876 | */
|
---|
| 877 | FLAG checkmark()
|
---|
| 878 | {
|
---|
| 879 | register LINE *line;
|
---|
| 880 | FLAG cur_seen = FALSE;
|
---|
| 881 |
|
---|
| 882 | /* Special case: check is mark_line and cur_line are the same. */
|
---|
| 883 | if (mark_line == cur_line) {
|
---|
| 884 | if (mark_text == cur_text) /* Even same place */
|
---|
| 885 | return SAME;
|
---|
| 886 | if (legal() == ERRORS) /* mark_text out of range */
|
---|
| 887 | return NOT_VALID;
|
---|
| 888 | return (mark_text < cur_text) ? SMALLER : BIGGER;
|
---|
| 889 | }
|
---|
| 890 |
|
---|
| 891 | /* Start looking for mark_line in the line structure */
|
---|
| 892 | for (line = header->next; line != tail; line = line->next) {
|
---|
| 893 | if (line == cur_line)
|
---|
| 894 | cur_seen = TRUE;
|
---|
| 895 | else if (line == mark_line)
|
---|
| 896 | break;
|
---|
| 897 | }
|
---|
| 898 |
|
---|
| 899 | /* If we found mark_line (line != tail) check for legality of mark_text */
|
---|
| 900 | if (line == tail || legal() == ERRORS)
|
---|
| 901 | return NOT_VALID;
|
---|
| 902 |
|
---|
| 903 | /* cur_seen is TRUE if cur_line is before mark_line */
|
---|
| 904 | return (cur_seen == TRUE) ? BIGGER : SMALLER;
|
---|
| 905 | }
|
---|
| 906 |
|
---|
| 907 | /*
|
---|
| 908 | * Legal() checks if mark_text is still a valid pointer.
|
---|
| 909 | */
|
---|
| 910 | int legal()
|
---|
| 911 | {
|
---|
| 912 | register char *textp = mark_line->text;
|
---|
| 913 |
|
---|
| 914 | /* Locate mark_text on mark_line */
|
---|
| 915 | while (textp != mark_text && *textp++ != '\0')
|
---|
| 916 | ;
|
---|
| 917 | return (*textp == '\0') ? ERRORS : FINE;
|
---|
| 918 | }
|
---|
| 919 |
|
---|
| 920 | /*
|
---|
| 921 | * Yank puts all the text between start_position and end_position into
|
---|
| 922 | * the buffer.
|
---|
| 923 | * The caller must check that the arguments to yank() are valid. (E.g. in
|
---|
| 924 | * the right order)
|
---|
| 925 | */
|
---|
| 926 | void yank(start_line, start_textp, end_line, end_textp, remove)
|
---|
| 927 | LINE *start_line, *end_line;
|
---|
| 928 | char *start_textp, *end_textp;
|
---|
| 929 | FLAG remove; /* DELETE if text should be deleted */
|
---|
| 930 | {
|
---|
| 931 | register LINE *line = start_line;
|
---|
| 932 | register char *textp = start_textp;
|
---|
| 933 | int fd;
|
---|
| 934 |
|
---|
| 935 | /* Creat file to hold buffer */
|
---|
| 936 | if ((fd = scratch_file(WRITE)) == ERRORS)
|
---|
| 937 | return;
|
---|
| 938 |
|
---|
| 939 | chars_saved = 0L;
|
---|
| 940 | lines_saved = 0;
|
---|
| 941 | status_line("Saving text.", NIL_PTR);
|
---|
| 942 |
|
---|
| 943 | /* Keep writing chars until the end_location is reached. */
|
---|
| 944 | while (textp != end_textp) {
|
---|
| 945 | if (write_char(fd, *textp) == ERRORS) {
|
---|
| 946 | (void) close(fd);
|
---|
| 947 | return;
|
---|
| 948 | }
|
---|
| 949 | if (*textp++ == '\n') { /* Move to the next line */
|
---|
| 950 | line = line->next;
|
---|
| 951 | textp = line->text;
|
---|
| 952 | lines_saved++;
|
---|
| 953 | }
|
---|
| 954 | chars_saved++;
|
---|
| 955 | }
|
---|
| 956 |
|
---|
| 957 | /* Flush the I/O buffer and close file */
|
---|
| 958 | if (flush_buffer(fd) == ERRORS) {
|
---|
| 959 | (void) close(fd);
|
---|
| 960 | return;
|
---|
| 961 | }
|
---|
| 962 | (void) close(fd);
|
---|
| 963 | yank_status = VALID;
|
---|
| 964 |
|
---|
| 965 | /*
|
---|
| 966 | * Check if the text should be deleted as well. If it should, the following
|
---|
| 967 | * hack is used to save a lot of code. First move back to the start_position.
|
---|
| 968 | * (This might be the location we're on now!) and them delete the text.
|
---|
| 969 | * It might be a bit confusing the first time somebody uses it.
|
---|
| 970 | * Delete() will fix the screen.
|
---|
| 971 | */
|
---|
| 972 | if (remove == DELETE) {
|
---|
| 973 | move_to(find_x(start_line, start_textp), find_y(start_line));
|
---|
| 974 | delete(start_line, start_textp, end_line, end_textp);
|
---|
| 975 | }
|
---|
| 976 |
|
---|
| 977 | status_line(num_out(chars_saved), " characters saved in buffer.");
|
---|
| 978 | }
|
---|
| 979 |
|
---|
| 980 | /*
|
---|
| 981 | * Scratch_file() creates a uniq file in /usr/tmp. If the file couldn't
|
---|
| 982 | * be created other combinations of files are tried until a maximum
|
---|
| 983 | * of MAXTRAILS times. After MAXTRAILS times, an error message is given
|
---|
| 984 | * and ERRORS is returned.
|
---|
| 985 | */
|
---|
| 986 |
|
---|
| 987 | #define MAXTRAILS 26
|
---|
| 988 |
|
---|
| 989 | int scratch_file(mode)
|
---|
| 990 | FLAG mode; /* Can be READ or WRITE permission */
|
---|
| 991 | {
|
---|
| 992 | static int trials = 0; /* Keep track of trails */
|
---|
| 993 | register char *y_ptr, *n_ptr;
|
---|
| 994 | int fd; /* Filedescriptor to buffer */
|
---|
| 995 |
|
---|
| 996 | /* If yank_status == NOT_VALID, scratch_file is called for the first time */
|
---|
| 997 | if (yank_status == NOT_VALID && mode == WRITE) { /* Create new file */
|
---|
| 998 | /* Generate file name. */
|
---|
| 999 | y_ptr = &yank_file[11];
|
---|
| 1000 | n_ptr = num_out((long) getpid());
|
---|
| 1001 | while ((*y_ptr = *n_ptr++) != '\0')
|
---|
| 1002 | y_ptr++;
|
---|
| 1003 | *y_ptr++ = 'a' + trials;
|
---|
| 1004 | *y_ptr = '\0';
|
---|
| 1005 | /* Check file existence */
|
---|
| 1006 | if (access(yank_file, 0) == 0 || (fd = creat(yank_file, 0644)) < 0) {
|
---|
| 1007 | if (trials++ >= MAXTRAILS) {
|
---|
| 1008 | error("Unable to creat scratchfile.", NIL_PTR);
|
---|
| 1009 | return ERRORS;
|
---|
| 1010 | }
|
---|
| 1011 | else
|
---|
| 1012 | return scratch_file(mode);/* Have another go */
|
---|
| 1013 | }
|
---|
| 1014 | }
|
---|
| 1015 | else if ((mode == READ && (fd = open(yank_file, 0)) < 0) ||
|
---|
| 1016 | (mode == WRITE && (fd = creat(yank_file, 0644)) < 0)) {
|
---|
| 1017 | yank_status = NOT_VALID;
|
---|
| 1018 | return ERRORS;
|
---|
| 1019 | }
|
---|
| 1020 |
|
---|
| 1021 | clear_buffer();
|
---|
| 1022 | return fd;
|
---|
| 1023 | }
|
---|
| 1024 |
|
---|
| 1025 | /* ======================================================================== *
|
---|
| 1026 | * Search Routines *
|
---|
| 1027 | * ======================================================================== */
|
---|
| 1028 |
|
---|
| 1029 | /*
|
---|
| 1030 | * A regular expression consists of a sequence of:
|
---|
| 1031 | * 1. A normal character matching that character.
|
---|
| 1032 | * 2. A . matching any character.
|
---|
| 1033 | * 3. A ^ matching the begin of a line.
|
---|
| 1034 | * 4. A $ (as last character of the pattern) mathing the end of a line.
|
---|
| 1035 | * 5. A \<character> matching <character>.
|
---|
| 1036 | * 6. A number of characters enclosed in [] pairs matching any of these
|
---|
| 1037 | * characters. A list of characters can be indicated by a '-'. So
|
---|
| 1038 | * [a-z] matches any letter of the alphabet. If the first character
|
---|
| 1039 | * after the '[' is a '^' then the set is negated (matching none of
|
---|
| 1040 | * the characters).
|
---|
| 1041 | * A ']', '^' or '-' can be escaped by putting a '\' in front of it.
|
---|
| 1042 | * 7. If one of the expressions as described in 1-6 is followed by a
|
---|
| 1043 | * '*' than that expressions matches a sequence of 0 or more of
|
---|
| 1044 | * that expression.
|
---|
| 1045 | */
|
---|
| 1046 |
|
---|
| 1047 | char typed_expression[LINE_LEN]; /* Holds previous expr. */
|
---|
| 1048 |
|
---|
| 1049 | /*
|
---|
| 1050 | * SF searches forward for an expression.
|
---|
| 1051 | */
|
---|
| 1052 | void SF()
|
---|
| 1053 | {
|
---|
| 1054 | search("Search forward:", FORWARD);
|
---|
| 1055 | }
|
---|
| 1056 |
|
---|
| 1057 | /*
|
---|
| 1058 | * SF searches backwards for an expression.
|
---|
| 1059 | */
|
---|
| 1060 | void SR()
|
---|
| 1061 | {
|
---|
| 1062 | search("Search reverse:", REVERSE);
|
---|
| 1063 | }
|
---|
| 1064 |
|
---|
| 1065 | /*
|
---|
| 1066 | * Get_expression() prompts for an expression. If just a return is typed, the
|
---|
| 1067 | * old expression is used. If the expression changed, compile() is called and
|
---|
| 1068 | * the returning REGEX structure is returned. It returns NIL_REG upon error.
|
---|
| 1069 | * The save flag indicates whether the expression should be appended at the
|
---|
| 1070 | * message pointer.
|
---|
| 1071 | */
|
---|
| 1072 | REGEX *get_expression(message)
|
---|
| 1073 | char *message;
|
---|
| 1074 | {
|
---|
| 1075 | static REGEX program; /* Program of expression */
|
---|
| 1076 | char exp_buf[LINE_LEN]; /* Buffer for new expr. */
|
---|
| 1077 |
|
---|
| 1078 | if (get_string(message, exp_buf, FALSE) == ERRORS)
|
---|
| 1079 | return NIL_REG;
|
---|
| 1080 |
|
---|
| 1081 | if (exp_buf[0] == '\0' && typed_expression[0] == '\0') {
|
---|
| 1082 | error("No previous expression.", NIL_PTR);
|
---|
| 1083 | return NIL_REG;
|
---|
| 1084 | }
|
---|
| 1085 |
|
---|
| 1086 | if (exp_buf[0] != '\0') { /* A new expr. is typed */
|
---|
| 1087 | copy_string(typed_expression, exp_buf);/* Save expr. */
|
---|
| 1088 | compile(exp_buf, &program); /* Compile new expression */
|
---|
| 1089 | }
|
---|
| 1090 |
|
---|
| 1091 | if (program.status == REG_ERROR) { /* Error during compiling */
|
---|
| 1092 | error(program.result.err_mess, NIL_PTR);
|
---|
| 1093 | return NIL_REG;
|
---|
| 1094 | }
|
---|
| 1095 | return &program;
|
---|
| 1096 | }
|
---|
| 1097 |
|
---|
| 1098 | /*
|
---|
| 1099 | * GR() a replaces all matches from the current position until the end
|
---|
| 1100 | * of the file.
|
---|
| 1101 | */
|
---|
| 1102 | void GR()
|
---|
| 1103 | {
|
---|
| 1104 | change("Global replace:", VALID);
|
---|
| 1105 | }
|
---|
| 1106 |
|
---|
| 1107 | /*
|
---|
| 1108 | * LR() replaces all matches on the current line.
|
---|
| 1109 | */
|
---|
| 1110 | void LR()
|
---|
| 1111 | {
|
---|
| 1112 | change("Line replace:", NOT_VALID);
|
---|
| 1113 | }
|
---|
| 1114 |
|
---|
| 1115 | /*
|
---|
| 1116 | * Change() prompts for an expression and a substitution pattern and changes
|
---|
| 1117 | * all matches of the expression into the substitution. change() start looking
|
---|
| 1118 | * for expressions at the current line and continues until the end of the file
|
---|
| 1119 | * if the FLAG file is VALID.
|
---|
| 1120 | */
|
---|
| 1121 | void change(message, file)
|
---|
| 1122 | char *message; /* Message to prompt for expression */
|
---|
| 1123 | FLAG file;
|
---|
| 1124 | {
|
---|
| 1125 | char mess_buf[LINE_LEN]; /* Buffer to hold message */
|
---|
| 1126 | char replacement[LINE_LEN]; /* Buffer to hold subst. pattern */
|
---|
| 1127 | REGEX *program; /* Program resulting from compilation */
|
---|
| 1128 | register LINE *line = cur_line;
|
---|
| 1129 | register char *textp;
|
---|
| 1130 | long lines = 0L; /* Nr of lines on which subs occurred */
|
---|
| 1131 | long subs = 0L; /* Nr of subs made */
|
---|
| 1132 | int page = y; /* Index to check if line is on screen*/
|
---|
| 1133 |
|
---|
| 1134 | /* Save message and get expression */
|
---|
| 1135 | copy_string(mess_buf, message);
|
---|
| 1136 | if ((program = get_expression(mess_buf)) == NIL_REG)
|
---|
| 1137 | return;
|
---|
| 1138 |
|
---|
| 1139 | /* Get substitution pattern */
|
---|
| 1140 | build_string(mess_buf, "%s %s by:", mess_buf, typed_expression);
|
---|
| 1141 | if (get_string(mess_buf, replacement, FALSE) == ERRORS)
|
---|
| 1142 | return;
|
---|
| 1143 |
|
---|
| 1144 | set_cursor(0, ymax);
|
---|
| 1145 | flush();
|
---|
| 1146 | /* Substitute until end of file */
|
---|
| 1147 | do {
|
---|
| 1148 | if (line_check(program, line->text, FORWARD)) {
|
---|
| 1149 | lines++;
|
---|
| 1150 | /* Repeat sub. on this line as long as we find a match*/
|
---|
| 1151 | do {
|
---|
| 1152 | subs++; /* Increment subs */
|
---|
| 1153 | if ((textp = substitute(line, program,replacement))
|
---|
| 1154 | == NIL_PTR)
|
---|
| 1155 | return; /* Line too long */
|
---|
| 1156 | } while ((program->status & BEGIN_LINE) != BEGIN_LINE &&
|
---|
| 1157 | (program->status & END_LINE) != END_LINE &&
|
---|
| 1158 | line_check(program, textp, FORWARD));
|
---|
| 1159 | /* Check to see if we can print the result */
|
---|
| 1160 | if (page <= screenmax) {
|
---|
| 1161 | set_cursor(0, page);
|
---|
| 1162 | line_print(line);
|
---|
| 1163 | }
|
---|
| 1164 | }
|
---|
| 1165 | if (page <= screenmax)
|
---|
| 1166 | page++;
|
---|
| 1167 | line = line->next;
|
---|
| 1168 | } while (line != tail && file == VALID && quit == FALSE);
|
---|
| 1169 |
|
---|
| 1170 | copy_string(mess_buf, (quit == TRUE) ? "(Aborted) " : "");
|
---|
| 1171 | /* Fix the status line */
|
---|
| 1172 | if (subs == 0L && quit == FALSE)
|
---|
| 1173 | error("Pattern not found.", NIL_PTR);
|
---|
| 1174 | else if (lines >= REPORT || quit == TRUE) {
|
---|
| 1175 | build_string(mess_buf, "%s %D substitutions on %D lines.", mess_buf,
|
---|
| 1176 | subs, lines);
|
---|
| 1177 | status_line(mess_buf, NIL_PTR);
|
---|
| 1178 | }
|
---|
| 1179 | else if (file == NOT_VALID && subs >= REPORT)
|
---|
| 1180 | status_line(num_out(subs), " substitutions.");
|
---|
| 1181 | else
|
---|
| 1182 | clear_status();
|
---|
| 1183 | move_to (x, y);
|
---|
| 1184 | }
|
---|
| 1185 |
|
---|
| 1186 | /*
|
---|
| 1187 | * Substitute() replaces the match on this line by the substitute pattern
|
---|
| 1188 | * as indicated by the program. Every '&' in the replacement is replaced by
|
---|
| 1189 | * the original match. A \ in the replacement escapes the next character.
|
---|
| 1190 | */
|
---|
| 1191 | char *substitute(line, program, replacement)
|
---|
| 1192 | LINE *line;
|
---|
| 1193 | REGEX *program;
|
---|
| 1194 | char *replacement; /* Contains replacement pattern */
|
---|
| 1195 | {
|
---|
| 1196 | register char *textp = text_buffer;
|
---|
| 1197 | register char *subp = replacement;
|
---|
| 1198 | char *linep = line->text;
|
---|
| 1199 | char *amp;
|
---|
| 1200 |
|
---|
| 1201 | modified = TRUE;
|
---|
| 1202 |
|
---|
| 1203 | /* Copy part of line until the beginning of the match */
|
---|
| 1204 | while (linep != program->start_ptr)
|
---|
| 1205 | *textp++ = *linep++;
|
---|
| 1206 |
|
---|
| 1207 | /*
|
---|
| 1208 | * Replace the match by the substitution pattern. Each occurrence of '&' is
|
---|
| 1209 | * replaced by the original match. A \ escapes the next character.
|
---|
| 1210 | */
|
---|
| 1211 | while (*subp != '\0' && textp < &text_buffer[MAX_CHARS]) {
|
---|
| 1212 | if (*subp == '&') { /* Replace the original match */
|
---|
| 1213 | amp = program->start_ptr;
|
---|
| 1214 | while (amp < program->end_ptr && textp<&text_buffer[MAX_CHARS])
|
---|
| 1215 | *textp++ = *amp++;
|
---|
| 1216 | subp++;
|
---|
| 1217 | }
|
---|
| 1218 | else {
|
---|
| 1219 | if (*subp == '\\' && *(subp + 1) != '\0')
|
---|
| 1220 | subp++;
|
---|
| 1221 | *textp++ = *subp++;
|
---|
| 1222 | }
|
---|
| 1223 | }
|
---|
| 1224 |
|
---|
| 1225 | /* Check for line length not exceeding MAX_CHARS */
|
---|
| 1226 | if (length_of(text_buffer) + length_of(program->end_ptr) >= MAX_CHARS) {
|
---|
| 1227 | error("Substitution result: line too big", NIL_PTR);
|
---|
| 1228 | return NIL_PTR;
|
---|
| 1229 | }
|
---|
| 1230 |
|
---|
| 1231 | /* Append last part of line to the new build line */
|
---|
| 1232 | copy_string(textp, program->end_ptr);
|
---|
| 1233 |
|
---|
| 1234 | /* Free old line and install new one */
|
---|
| 1235 | free_space(line->text);
|
---|
| 1236 | line->text = alloc(length_of(text_buffer) + 1);
|
---|
| 1237 | copy_string(line->text, text_buffer);
|
---|
| 1238 |
|
---|
| 1239 | return(line->text + (textp - text_buffer));
|
---|
| 1240 | }
|
---|
| 1241 |
|
---|
| 1242 | /*
|
---|
| 1243 | * Search() calls get_expression to fetch the expression. If this went well,
|
---|
| 1244 | * the function match() is called which returns the line with the next match.
|
---|
| 1245 | * If this line is the NIL_LINE, it means that a match could not be found.
|
---|
| 1246 | * Find_x() and find_y() display the right page on the screen, and return
|
---|
| 1247 | * the right coordinates for x and y. These coordinates are passed to move_to()
|
---|
| 1248 | */
|
---|
| 1249 | void search(message, method)
|
---|
| 1250 | char *message;
|
---|
| 1251 | FLAG method;
|
---|
| 1252 | {
|
---|
| 1253 | register REGEX *program;
|
---|
| 1254 | register LINE *match_line;
|
---|
| 1255 |
|
---|
| 1256 | /* Get the expression */
|
---|
| 1257 | if ((program = get_expression(message)) == NIL_REG)
|
---|
| 1258 | return;
|
---|
| 1259 |
|
---|
| 1260 | set_cursor(0, ymax);
|
---|
| 1261 | flush();
|
---|
| 1262 | /* Find the match */
|
---|
| 1263 | if ((match_line = match(program, cur_text, method)) == NIL_LINE) {
|
---|
| 1264 | if (quit == TRUE)
|
---|
| 1265 | status_line("Aborted", NIL_PTR);
|
---|
| 1266 | else
|
---|
| 1267 | status_line("Pattern not found.", NIL_PTR);
|
---|
| 1268 | return;
|
---|
| 1269 | }
|
---|
| 1270 |
|
---|
| 1271 | move(0, program->start_ptr, find_y(match_line));
|
---|
| 1272 | clear_status();
|
---|
| 1273 | }
|
---|
| 1274 |
|
---|
| 1275 | /*
|
---|
| 1276 | * find_y() checks if the matched line is on the current page. If it is, it
|
---|
| 1277 | * returns the new y coordinate, else it displays the correct page with the
|
---|
| 1278 | * matched line in the middle and returns the new y value;
|
---|
| 1279 | */
|
---|
| 1280 | int find_y(match_line)
|
---|
| 1281 | LINE *match_line;
|
---|
| 1282 | {
|
---|
| 1283 | register LINE *line;
|
---|
| 1284 | register int count = 0;
|
---|
| 1285 |
|
---|
| 1286 | /* Check if match_line is on the same page as currently displayed. */
|
---|
| 1287 | for (line = top_line; line != match_line && line != bot_line->next;
|
---|
| 1288 | line = line->next)
|
---|
| 1289 | count++;
|
---|
| 1290 | if (line != bot_line->next)
|
---|
| 1291 | return count;
|
---|
| 1292 |
|
---|
| 1293 | /* Display new page, with match_line in center. */
|
---|
| 1294 | if ((line = proceed(match_line, -(screenmax >> 1))) == header) {
|
---|
| 1295 | /* Can't display in the middle. Make first line of file top_line */
|
---|
| 1296 | count = 0;
|
---|
| 1297 | for (line = header->next; line != match_line; line = line->next)
|
---|
| 1298 | count++;
|
---|
| 1299 | line = header->next;
|
---|
| 1300 | }
|
---|
| 1301 | else /* New page is displayed. Set cursor to middle of page */
|
---|
| 1302 | count = screenmax >> 1;
|
---|
| 1303 |
|
---|
| 1304 | /* Reset pointers and redraw the screen */
|
---|
| 1305 | reset(line, 0);
|
---|
| 1306 | RD();
|
---|
| 1307 |
|
---|
| 1308 | return count;
|
---|
| 1309 | }
|
---|
| 1310 |
|
---|
| 1311 | /* Opcodes for characters */
|
---|
| 1312 | #define NORMAL 0x0200
|
---|
| 1313 | #define DOT 0x0400
|
---|
| 1314 | #define EOLN 0x0800
|
---|
| 1315 | #define STAR 0x1000
|
---|
| 1316 | #define BRACKET 0x2000
|
---|
| 1317 | #define NEGATE 0x0100
|
---|
| 1318 | #define DONE 0x4000
|
---|
| 1319 |
|
---|
| 1320 | /* Mask for opcodes and characters */
|
---|
| 1321 | #define LOW_BYTE 0x00FF
|
---|
| 1322 | #define HIGH_BYTE 0xFF00
|
---|
| 1323 |
|
---|
| 1324 | /* Previous is the contents of the previous address (ptr) points to */
|
---|
| 1325 | #define previous(ptr) (*((ptr) - 1))
|
---|
| 1326 |
|
---|
| 1327 | /* Buffer to store outcome of compilation */
|
---|
| 1328 | int exp_buffer[BLOCK_SIZE];
|
---|
| 1329 |
|
---|
| 1330 | /* Errors often used */
|
---|
| 1331 | char *too_long = "Regular expression too long";
|
---|
| 1332 |
|
---|
| 1333 | /*
|
---|
| 1334 | * Reg_error() is called by compile() is something went wrong. It set the
|
---|
| 1335 | * status of the structure to error, and assigns the error field of the union.
|
---|
| 1336 | */
|
---|
| 1337 | #define reg_error(str) program->status = REG_ERROR, \
|
---|
| 1338 | program->result.err_mess = (str)
|
---|
| 1339 | /*
|
---|
| 1340 | * Finished() is called when everything went right during compilation. It
|
---|
| 1341 | * allocates space for the expression, and copies the expression buffer into
|
---|
| 1342 | * this field.
|
---|
| 1343 | */
|
---|
| 1344 | void finished(program, last_exp)
|
---|
| 1345 | register REGEX *program;
|
---|
| 1346 | int *last_exp;
|
---|
| 1347 | {
|
---|
| 1348 | register int length = (last_exp - exp_buffer) * sizeof(int);
|
---|
| 1349 |
|
---|
| 1350 | /* Allocate space */
|
---|
| 1351 | program->result.expression = (int *) alloc(length);
|
---|
| 1352 | /* Copy expression. (expression consists of ints!) */
|
---|
| 1353 | bcopy(exp_buffer, program->result.expression, length);
|
---|
| 1354 | }
|
---|
| 1355 |
|
---|
| 1356 | /*
|
---|
| 1357 | * Compile compiles the pattern into a more comprehensible form and returns a
|
---|
| 1358 | * REGEX structure. If something went wrong, the status field of the structure
|
---|
| 1359 | * is set to REG_ERROR and an error message is set into the err_mess field of
|
---|
| 1360 | * the union. If all went well the expression is saved and the expression
|
---|
| 1361 | * pointer is set to the saved (and compiled) expression.
|
---|
| 1362 | */
|
---|
| 1363 | void compile(pattern, program)
|
---|
| 1364 | register char *pattern; /* Pointer to pattern */
|
---|
| 1365 | REGEX *program;
|
---|
| 1366 | {
|
---|
| 1367 | register int *expression = exp_buffer;
|
---|
| 1368 | int *prev_char; /* Pointer to previous compiled atom */
|
---|
| 1369 | int *acct_field; /* Pointer to last BRACKET start */
|
---|
| 1370 | FLAG negate; /* Negate flag for BRACKET */
|
---|
| 1371 | char low_char; /* Index for chars in BRACKET */
|
---|
| 1372 | char c;
|
---|
| 1373 |
|
---|
| 1374 | /* Check for begin of line */
|
---|
| 1375 | if (*pattern == '^') {
|
---|
| 1376 | program->status = BEGIN_LINE;
|
---|
| 1377 | pattern++;
|
---|
| 1378 | }
|
---|
| 1379 | else {
|
---|
| 1380 | program->status = 0;
|
---|
| 1381 | /* If the first character is a '*' we have to assign it here. */
|
---|
| 1382 | if (*pattern == '*') {
|
---|
| 1383 | *expression++ = '*' + NORMAL;
|
---|
| 1384 | pattern++;
|
---|
| 1385 | }
|
---|
| 1386 | }
|
---|
| 1387 |
|
---|
| 1388 | for (; ;) {
|
---|
| 1389 | switch (c = *pattern++) {
|
---|
| 1390 | case '.' :
|
---|
| 1391 | *expression++ = DOT;
|
---|
| 1392 | break;
|
---|
| 1393 | case '$' :
|
---|
| 1394 | /*
|
---|
| 1395 | * Only means EOLN if it is the last char of the pattern
|
---|
| 1396 | */
|
---|
| 1397 | if (*pattern == '\0') {
|
---|
| 1398 | *expression++ = EOLN | DONE;
|
---|
| 1399 | program->status |= END_LINE;
|
---|
| 1400 | finished(program, expression);
|
---|
| 1401 | return;
|
---|
| 1402 | }
|
---|
| 1403 | else
|
---|
| 1404 | *expression++ = NORMAL + '$';
|
---|
| 1405 | break;
|
---|
| 1406 | case '\0' :
|
---|
| 1407 | *expression++ = DONE;
|
---|
| 1408 | finished(program, expression);
|
---|
| 1409 | return;
|
---|
| 1410 | case '\\' :
|
---|
| 1411 | /* If last char, it must! mean a normal '\' */
|
---|
| 1412 | if (*pattern == '\0')
|
---|
| 1413 | *expression++ = NORMAL + '\\';
|
---|
| 1414 | else
|
---|
| 1415 | *expression++ = NORMAL + *pattern++;
|
---|
| 1416 | break;
|
---|
| 1417 | case '*' :
|
---|
| 1418 | /*
|
---|
| 1419 | * If the previous expression was a [] find out the
|
---|
| 1420 | * begin of the list, and adjust the opcode.
|
---|
| 1421 | */
|
---|
| 1422 | prev_char = expression - 1;
|
---|
| 1423 | if (*prev_char & BRACKET)
|
---|
| 1424 | *(expression - (*acct_field & LOW_BYTE))|= STAR;
|
---|
| 1425 | else
|
---|
| 1426 | *prev_char |= STAR;
|
---|
| 1427 | break;
|
---|
| 1428 | case '[' :
|
---|
| 1429 | /*
|
---|
| 1430 | * First field in expression gives information about
|
---|
| 1431 | * the list.
|
---|
| 1432 | * The opcode consists of BRACKET and if necessary
|
---|
| 1433 | * NEGATE to indicate that the list should be negated
|
---|
| 1434 | * and/or STAR to indicate a number of sequence of this
|
---|
| 1435 | * list.
|
---|
| 1436 | * The lower byte contains the length of the list.
|
---|
| 1437 | */
|
---|
| 1438 | acct_field = expression++;
|
---|
| 1439 | if (*pattern == '^') { /* List must be negated */
|
---|
| 1440 | pattern++;
|
---|
| 1441 | negate = TRUE;
|
---|
| 1442 | }
|
---|
| 1443 | else
|
---|
| 1444 | negate = FALSE;
|
---|
| 1445 | while (*pattern != ']') {
|
---|
| 1446 | if (*pattern == '\0') {
|
---|
| 1447 | reg_error("Missing ]");
|
---|
| 1448 | return;
|
---|
| 1449 | }
|
---|
| 1450 | if (*pattern == '\\')
|
---|
| 1451 | pattern++;
|
---|
| 1452 | *expression++ = *pattern++;
|
---|
| 1453 | if (*pattern == '-') {
|
---|
| 1454 | /* Make list of chars */
|
---|
| 1455 | low_char = previous(pattern);
|
---|
| 1456 | pattern++; /* Skip '-' */
|
---|
| 1457 | if (low_char++ > *pattern) {
|
---|
| 1458 | reg_error("Bad range in [a-z]");
|
---|
| 1459 | return;
|
---|
| 1460 | }
|
---|
| 1461 | /* Build list */
|
---|
| 1462 | while (low_char <= *pattern)
|
---|
| 1463 | *expression++ = low_char++;
|
---|
| 1464 | pattern++;
|
---|
| 1465 | }
|
---|
| 1466 | if (expression >= &exp_buffer[BLOCK_SIZE]) {
|
---|
| 1467 | reg_error(too_long);
|
---|
| 1468 | return;
|
---|
| 1469 | }
|
---|
| 1470 | }
|
---|
| 1471 | pattern++; /* Skip ']' */
|
---|
| 1472 | /* Assign length of list in acct field */
|
---|
| 1473 | if ((*acct_field = (expression - acct_field)) == 1) {
|
---|
| 1474 | reg_error("Empty []");
|
---|
| 1475 | return;
|
---|
| 1476 | }
|
---|
| 1477 | /* Assign negate and bracket field */
|
---|
| 1478 | *acct_field |= BRACKET;
|
---|
| 1479 | if (negate == TRUE)
|
---|
| 1480 | *acct_field |= NEGATE;
|
---|
| 1481 | /*
|
---|
| 1482 | * Add BRACKET to opcode of last char in field because
|
---|
| 1483 | * a '*' may be following the list.
|
---|
| 1484 | */
|
---|
| 1485 | previous(expression) |= BRACKET;
|
---|
| 1486 | break;
|
---|
| 1487 | default :
|
---|
| 1488 | *expression++ = c + NORMAL;
|
---|
| 1489 | }
|
---|
| 1490 | if (expression == &exp_buffer[BLOCK_SIZE]) {
|
---|
| 1491 | reg_error(too_long);
|
---|
| 1492 | return;
|
---|
| 1493 | }
|
---|
| 1494 | }
|
---|
| 1495 | /* NOTREACHED */
|
---|
| 1496 | }
|
---|
| 1497 |
|
---|
| 1498 | /*
|
---|
| 1499 | * Match gets as argument the program, pointer to place in current line to
|
---|
| 1500 | * start from and the method to search for (either FORWARD or REVERSE).
|
---|
| 1501 | * Match() will look through the whole file until a match is found.
|
---|
| 1502 | * NIL_LINE is returned if no match could be found.
|
---|
| 1503 | */
|
---|
| 1504 | LINE *match(program, string, method)
|
---|
| 1505 | REGEX *program;
|
---|
| 1506 | char *string;
|
---|
| 1507 | register FLAG method;
|
---|
| 1508 | {
|
---|
| 1509 | register LINE *line = cur_line;
|
---|
| 1510 | char old_char; /* For saving chars */
|
---|
| 1511 |
|
---|
| 1512 | /* Corrupted program */
|
---|
| 1513 | if (program->status == REG_ERROR)
|
---|
| 1514 | return NIL_LINE;
|
---|
| 1515 |
|
---|
| 1516 | /* Check part of text first */
|
---|
| 1517 | if (!(program->status & BEGIN_LINE)) {
|
---|
| 1518 | if (method == FORWARD) {
|
---|
| 1519 | if (line_check(program, string + 1, method) == MATCH)
|
---|
| 1520 | return cur_line; /* Match found */
|
---|
| 1521 | }
|
---|
| 1522 | else if (!(program->status & END_LINE)) {
|
---|
| 1523 | old_char = *string; /* Save char and */
|
---|
| 1524 | *string = '\n'; /* Assign '\n' for line_check */
|
---|
| 1525 | if (line_check(program, line->text, method) == MATCH) {
|
---|
| 1526 | *string = old_char; /* Restore char */
|
---|
| 1527 | return cur_line; /* Found match */
|
---|
| 1528 | }
|
---|
| 1529 | *string = old_char; /* No match, but restore char */
|
---|
| 1530 | }
|
---|
| 1531 | }
|
---|
| 1532 |
|
---|
| 1533 | /* No match in last (or first) part of line. Check out rest of file */
|
---|
| 1534 | do {
|
---|
| 1535 | line = (method == FORWARD) ? line->next : line->prev;
|
---|
| 1536 | if (line->text == NIL_PTR) /* Header/tail */
|
---|
| 1537 | continue;
|
---|
| 1538 | if (line_check(program, line->text, method) == MATCH)
|
---|
| 1539 | return line;
|
---|
| 1540 | } while (line != cur_line && quit == FALSE);
|
---|
| 1541 |
|
---|
| 1542 | /* No match found. */
|
---|
| 1543 | return NIL_LINE;
|
---|
| 1544 | }
|
---|
| 1545 |
|
---|
| 1546 | /*
|
---|
| 1547 | * Line_check() checks the line (or rather string) for a match. Method
|
---|
| 1548 | * indicates FORWARD or REVERSE search. It scans through the whole string
|
---|
| 1549 | * until a match is found, or the end of the string is reached.
|
---|
| 1550 | */
|
---|
| 1551 | int line_check(program, string, method)
|
---|
| 1552 | register REGEX *program;
|
---|
| 1553 | char *string;
|
---|
| 1554 | FLAG method;
|
---|
| 1555 | {
|
---|
| 1556 | register char *textp = string;
|
---|
| 1557 |
|
---|
| 1558 | /* Assign start_ptr field. We might find a match right away! */
|
---|
| 1559 | program->start_ptr = textp;
|
---|
| 1560 |
|
---|
| 1561 | /* If the match must be anchored, just check the string. */
|
---|
| 1562 | if (program->status & BEGIN_LINE)
|
---|
| 1563 | return check_string(program, string, NIL_INT);
|
---|
| 1564 |
|
---|
| 1565 | if (method == REVERSE) {
|
---|
| 1566 | /* First move to the end of the string */
|
---|
| 1567 | for (textp = string; *textp != '\n'; textp++)
|
---|
| 1568 | ;
|
---|
| 1569 | /* Start checking string until the begin of the string is met */
|
---|
| 1570 | while (textp >= string) {
|
---|
| 1571 | program->start_ptr = textp;
|
---|
| 1572 | if (check_string(program, textp--, NIL_INT))
|
---|
| 1573 | return MATCH;
|
---|
| 1574 | }
|
---|
| 1575 | }
|
---|
| 1576 | else {
|
---|
| 1577 | /* Move through the string until the end of is found */
|
---|
| 1578 | while (quit == FALSE && *textp != '\0') {
|
---|
| 1579 | program->start_ptr = textp;
|
---|
| 1580 | if (check_string(program, textp, NIL_INT))
|
---|
| 1581 | return MATCH;
|
---|
| 1582 | if (*textp == '\n')
|
---|
| 1583 | break;
|
---|
| 1584 | textp++;
|
---|
| 1585 | }
|
---|
| 1586 | }
|
---|
| 1587 |
|
---|
| 1588 | return NO_MATCH;
|
---|
| 1589 | }
|
---|
| 1590 |
|
---|
| 1591 | /*
|
---|
| 1592 | * Check() checks of a match can be found in the given string. Whenever a STAR
|
---|
| 1593 | * is found during matching, then the begin position of the string is marked
|
---|
| 1594 | * and the maximum number of matches is performed. Then the function star()
|
---|
| 1595 | * is called which starts to finish the match from this position of the string
|
---|
| 1596 | * (and expression). Check() return MATCH for a match, NO_MATCH is the string
|
---|
| 1597 | * couldn't be matched or REG_ERROR for an illegal opcode in expression.
|
---|
| 1598 | */
|
---|
| 1599 | int check_string(program, string, expression)
|
---|
| 1600 | REGEX *program;
|
---|
| 1601 | register char *string;
|
---|
| 1602 | int *expression;
|
---|
| 1603 | {
|
---|
| 1604 | register int opcode; /* Holds opcode of next expr. atom */
|
---|
| 1605 | char c; /* Char that must be matched */
|
---|
| 1606 | char *mark; /* For marking position */
|
---|
| 1607 | int star_fl; /* A star has been born */
|
---|
| 1608 |
|
---|
| 1609 | if (expression == NIL_INT)
|
---|
| 1610 | expression = program->result.expression;
|
---|
| 1611 |
|
---|
| 1612 | /* Loop until end of string or end of expression */
|
---|
| 1613 | while (quit == FALSE && !(*expression & DONE) &&
|
---|
| 1614 | *string != '\0' && *string != '\n') {
|
---|
| 1615 | c = *expression & LOW_BYTE; /* Extract match char */
|
---|
| 1616 | opcode = *expression & HIGH_BYTE; /* Extract opcode */
|
---|
| 1617 | if (star_fl = (opcode & STAR)) { /* Check star occurrence */
|
---|
| 1618 | opcode &= ~STAR; /* Strip opcode */
|
---|
| 1619 | mark = string; /* Mark current position */
|
---|
| 1620 | }
|
---|
| 1621 | expression++; /* Increment expr. */
|
---|
| 1622 | switch (opcode) {
|
---|
| 1623 | case NORMAL :
|
---|
| 1624 | if (star_fl)
|
---|
| 1625 | while (*string++ == c) /* Skip all matches */
|
---|
| 1626 | ;
|
---|
| 1627 | else if (*string++ != c)
|
---|
| 1628 | return NO_MATCH;
|
---|
| 1629 | break;
|
---|
| 1630 | case DOT :
|
---|
| 1631 | string++;
|
---|
| 1632 | if (star_fl) /* Skip to eoln */
|
---|
| 1633 | while (*string != '\0' && *string++ != '\n')
|
---|
| 1634 | ;
|
---|
| 1635 | break;
|
---|
| 1636 | case NEGATE | BRACKET:
|
---|
| 1637 | case BRACKET :
|
---|
| 1638 | if (star_fl)
|
---|
| 1639 | while (in_list(expression, *string++, c, opcode)
|
---|
| 1640 | == MATCH)
|
---|
| 1641 | ;
|
---|
| 1642 | else if (in_list(expression, *string++, c, opcode) == NO_MATCH)
|
---|
| 1643 | return NO_MATCH;
|
---|
| 1644 | expression += c - 1; /* Add length of list */
|
---|
| 1645 | break;
|
---|
| 1646 | default :
|
---|
| 1647 | panic("Corrupted program in check_string()");
|
---|
| 1648 | }
|
---|
| 1649 | if (star_fl)
|
---|
| 1650 | return star(program, mark, string, expression);
|
---|
| 1651 | }
|
---|
| 1652 | if (*expression & DONE) {
|
---|
| 1653 | program->end_ptr = string; /* Match ends here */
|
---|
| 1654 | /*
|
---|
| 1655 | * We might have found a match. The last thing to do is check
|
---|
| 1656 | * whether a '$' was given at the end of the expression, or
|
---|
| 1657 | * the match was found on a null string. (E.g. [a-z]* always
|
---|
| 1658 | * matches) unless a ^ or $ was included in the pattern.
|
---|
| 1659 | */
|
---|
| 1660 | if ((*expression & EOLN) && *string != '\n' && *string != '\0')
|
---|
| 1661 | return NO_MATCH;
|
---|
| 1662 | if (string == program->start_ptr && !(program->status & BEGIN_LINE)
|
---|
| 1663 | && !(*expression & EOLN))
|
---|
| 1664 | return NO_MATCH;
|
---|
| 1665 | return MATCH;
|
---|
| 1666 | }
|
---|
| 1667 | return NO_MATCH;
|
---|
| 1668 | }
|
---|
| 1669 |
|
---|
| 1670 | /*
|
---|
| 1671 | * Star() calls check_string() to find out the longest match possible.
|
---|
| 1672 | * It searches backwards until the (in check_string()) marked position
|
---|
| 1673 | * is reached, or a match is found.
|
---|
| 1674 | */
|
---|
| 1675 | int star(program, end_position, string, expression)
|
---|
| 1676 | REGEX *program;
|
---|
| 1677 | register char *end_position;
|
---|
| 1678 | register char *string;
|
---|
| 1679 | int *expression;
|
---|
| 1680 | {
|
---|
| 1681 | do {
|
---|
| 1682 | string--;
|
---|
| 1683 | if (check_string(program, string, expression))
|
---|
| 1684 | return MATCH;
|
---|
| 1685 | } while (string != end_position);
|
---|
| 1686 |
|
---|
| 1687 | return NO_MATCH;
|
---|
| 1688 | }
|
---|
| 1689 |
|
---|
| 1690 | /*
|
---|
| 1691 | * In_list() checks if the given character is in the list of []. If it is
|
---|
| 1692 | * it returns MATCH. if it isn't it returns NO_MATCH. These returns values
|
---|
| 1693 | * are reversed when the NEGATE field in the opcode is present.
|
---|
| 1694 | */
|
---|
| 1695 | int in_list(list, c, list_length, opcode)
|
---|
| 1696 | register int *list;
|
---|
| 1697 | char c;
|
---|
| 1698 | register int list_length;
|
---|
| 1699 | int opcode;
|
---|
| 1700 | {
|
---|
| 1701 | if (c == '\0' || c == '\n') /* End of string, never matches */
|
---|
| 1702 | return NO_MATCH;
|
---|
| 1703 | while (list_length-- > 1) { /* > 1, don't check acct_field */
|
---|
| 1704 | if ((*list & LOW_BYTE) == c)
|
---|
| 1705 | return (opcode & NEGATE) ? NO_MATCH : MATCH;
|
---|
| 1706 | list++;
|
---|
| 1707 | }
|
---|
| 1708 | return (opcode & NEGATE) ? MATCH : NO_MATCH;
|
---|
| 1709 | }
|
---|
| 1710 |
|
---|
| 1711 | /*
|
---|
| 1712 | * Dummy_line() adds an empty line at the end of the file. This is sometimes
|
---|
| 1713 | * useful in combination with the EF and DN command in combination with the
|
---|
| 1714 | * Yank command set.
|
---|
| 1715 | */
|
---|
| 1716 | void dummy_line()
|
---|
| 1717 | {
|
---|
| 1718 | (void) line_insert(tail->prev, "\n", 1);
|
---|
| 1719 | tail->prev->shift_count = DUMMY;
|
---|
| 1720 | if (last_y != screenmax) {
|
---|
| 1721 | last_y++;
|
---|
| 1722 | bot_line = bot_line->next;
|
---|
| 1723 | }
|
---|
| 1724 | }
|
---|