source: trunk/minix/commands/mined/mined2.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 43.9 KB
Line 
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 */
15void 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 */
28void 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 */
48void 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 */
63void 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 */
78void HIGH()
79{
80 move_to(0, 0);
81}
82
83/*
84 * Move to coordinates [0, YMAX] on screen.
85 */
86void LOW()
87{
88 move_to(0, last_y);
89}
90
91/*
92 * Move to begin of line.
93 */
94void BL()
95{
96 move_to(LINE_START, y);
97}
98
99/*
100 * Move to end of line.
101 */
102void EL()
103{
104 move_to(LINE_END, y);
105}
106
107/*
108 * GOTO() prompts for a linenumber and moves to that line.
109 */
110void 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 */
129void 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 */
149void 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 */
171void 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 */
185void 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 */
201void 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 */
219void 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 */
231int 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 */
248int 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 */
282void MP()
283{
284 move_previous_word(NO_DELETE);
285}
286
287void move_previous_word(remove)
288FLAG 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 */
332void MN()
333{
334 move_next_word(NO_DELETE);
335}
336
337void move_next_word(remove)
338FLAG 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 */
376void 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 */
389void 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 */
402void 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 */
413void 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 */
424void 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 */
435void S(character)
436register 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 */
470void 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 */
487void 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 */
498LINE *line_insert(line, string, len)
499register LINE *line;
500char *string;
501int 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 */
523int insert(line, location, string)
524register LINE *line;
525char *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 */
563LINE *line_delete(line)
564register 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 */
587void delete(start_line, start_textp, end_line, end_textp)
588register LINE *start_line;
589LINE *end_line;
590char *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
672LINE *mark_line; /* For marking position. */
673char *mark_text;
674int lines_saved; /* Nr of lines in buffer */
675
676/*
677 * PT() inserts the buffer at the current location.
678 */
679void 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 */
695void 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 */
716void file_insert(fd, old_pos)
717int fd;
718FLAG 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 */
775void 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 */
820void 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 */
831void 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 */
839void 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 */
849void set_up(remove)
850FLAG 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 */
877FLAG 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 */
910int 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 */
926void yank(start_line, start_textp, end_line, end_textp, remove)
927LINE *start_line, *end_line;
928char *start_textp, *end_textp;
929FLAG 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
989int scratch_file(mode)
990FLAG 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
1047char typed_expression[LINE_LEN]; /* Holds previous expr. */
1048
1049/*
1050 * SF searches forward for an expression.
1051 */
1052void SF()
1053{
1054 search("Search forward:", FORWARD);
1055}
1056
1057/*
1058 * SF searches backwards for an expression.
1059 */
1060void 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 */
1072REGEX *get_expression(message)
1073char *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 */
1102void GR()
1103{
1104 change("Global replace:", VALID);
1105}
1106
1107/*
1108 * LR() replaces all matches on the current line.
1109 */
1110void 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 */
1121void change(message, file)
1122char *message; /* Message to prompt for expression */
1123FLAG 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 */
1191char *substitute(line, program, replacement)
1192LINE *line;
1193REGEX *program;
1194char *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 */
1249void search(message, method)
1250char *message;
1251FLAG 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 */
1280int find_y(match_line)
1281LINE *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 */
1328int exp_buffer[BLOCK_SIZE];
1329
1330/* Errors often used */
1331char *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 */
1344void finished(program, last_exp)
1345register REGEX *program;
1346int *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 */
1363void compile(pattern, program)
1364register char *pattern; /* Pointer to pattern */
1365REGEX *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 */
1504LINE *match(program, string, method)
1505REGEX *program;
1506char *string;
1507register 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 */
1551int line_check(program, string, method)
1552register REGEX *program;
1553char *string;
1554FLAG 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 */
1599int check_string(program, string, expression)
1600REGEX *program;
1601register char *string;
1602int *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 */
1675int star(program, end_position, string, expression)
1676REGEX *program;
1677register char *end_position;
1678register char *string;
1679int *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 */
1695int in_list(list, c, list_length, opcode)
1696register int *list;
1697char c;
1698register int list_length;
1699int 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 */
1716void 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}
Note: See TracBrowser for help on using the repository browser.