home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!usc!elroy.jpl.nasa.gov!swrinde!mips!darwin.sura.net!Sirius.dfn.de!math.fu-berlin.de!wolff
- From: wolff@inf.fu-berlin.de (Thomas Wolff)
- Newsgroups: comp.editors
- Subject: Editor mined (3/4)
- Message-ID: <52I5VTD@math.fu-berlin.de>
- Date: 28 Jul 92 15:03:50 GMT
- Sender: news@math.fu-berlin.de (Math Department)
- Organization: Free University of Berlin, Germany
- Lines: 2194
-
- #! /bin/sh
- : This is a sharchive -- extract the files by running through sh
-
- echo ---------------------- extracting mined2.c -----------------
- sed 's/^,//' << \EOSED > mined2.c
- ,/* ================================================================== *
- , * Editor mined *
- , * Part 2 *
- , * ================================================================== */
- ,
- ,#include "mined.h"
- ,
- ,/* ================================================================== *
- , * Definitions specific for mined2.c *
- , * ================================================================== */
- ,
- ,#ifdef vms
- ,#define copycommand "copy %s %s"
- ,#endif
- ,#ifdef msdos
- ,#define copycommand "copy %s %s > nul:"
- ,#endif
- ,
- ,/* ================================================================== *
- , * Move Commands *
- , * ================================================================== */
- ,
- ,/*
- , * Move one line up.
- , */
- ,MUP ()
- ,{
- , if (hop_flag > 0) return HIGH ();
- , if (y == 0) { /* Top line of screen. Scroll one line */
- , (void) reverse_scroll ();
- , set_cursor (0, YMAX); /* Erase very bottom line */
- , clear_eol ();
- , move_y (y);
- , }
- , else /* Move to previous line */
- , move_y (y - 1);
- ,}
- ,
- ,/*
- , * Move one line down.
- , */
- ,MDN ()
- ,{
- , if (hop_flag > 0) return LOW ();
- , if (y == last_y) { /* Last line of screen. Scroll one line */
- , if (bot_line->next == tail && bot_line->text [0] != '\n') {
- , /* dummy_line (); don't create new empty line ! */
- , /* MDN (); */
- , return;
- , }
- , else {
- , (void) forward_scroll ();
- , move_y (y);
- , }
- , }
- , else /* Move to next line */
- , move_y (y + 1);
- ,}
- ,
- ,/*
- , * Move left one position.
- , */
- ,MLF ()
- ,{
- , if (hop_flag > 0) return BL ();
- , if (x == 0 && get_shift (cur_line->shift_count) == 0) {/* Begin of line */
- , if (cur_line->prev != header) {
- , MUP (); /* Move one line up */
- , move_to (LINE_END, y);
- , }
- , }
- , else
- , move_to (x - 1, y);
- ,}
- ,
- ,/*
- , * Move right one position.
- , */
- ,MRT ()
- ,{
- , if (hop_flag > 0) return EL ();
- , if (* cur_text == '\n') {
- , if (cur_line->next != tail) { /* Last char of file */
- , MDN (); /* Move one line down */
- , move_to (LINE_START, y);
- , }
- , }
- , else
- , move_to (x + 1, y);
- ,}
- ,
- ,/*
- , * Move to top of screen
- , */
- ,HIGH ()
- ,{
- , move_y (0);
- ,}
- ,
- ,/*
- , * Move to bottom of screen
- , */
- ,LOW ()
- ,{
- , move_y (last_y);
- ,}
- ,
- ,/*
- , * Move to begin of line.
- , */
- ,BL ()
- ,{
- , move_to (LINE_START, y);
- ,}
- ,
- ,/*
- , * Move to end of line.
- , */
- ,EL ()
- ,{
- , move_to (LINE_END, y);
- ,}
- ,
- ,/*
- , * GOTO () prompts for a linenumber and moves to that line.
- , */
- ,GOTO ()
- ,{
- , u_char c;
- , char end;
- , extern int (* key_map []) ();
- , int number;
- ,
- , if (! char_ready_within (500)) status_msg ("HOP ... command (fortified) or line number...");
- , if (quit == TRUE) return;
- , c = readchar ();
- , if (quit == TRUE) return;
- , if (('0' <= c) && (c <= '9')) {
- , end = get_number ("Please continue line number...", c, & number);
- , if (end == '%')
- , goproz (number);
- , else if (end != ERRORS)
- , goline (number);
- , return;
- , }
- , else {
- , clear_status ();
- , hop_flag = 1;
- , (* key_map [c]) (c);
- , return;
- , }
- ,}
- ,
- ,goline (number)
- , int number;
- ,{
- , LINE * line;
- , if (number <= 0 || (line = proceed (header->next, number - 1)) == tail)
- , error ("Illegal line number: ", num_out ((long) number));
- , else {
- , clear_status ();
- , move_y (find_y (line));
- , }
- ,}
- ,
- ,goproz (number)
- , int number;
- ,{
- , goline (number * nlines / 100);
- ,}
- ,
- ,/*
- , * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes
- , * top_line of display.) Try to leave the cursor on the same line. If this is
- , * not possible, leave cursor on the line halfway the page.
- , */
- ,PD ()
- ,{
- , register int i;
- ,
- , if (hop_flag > 0) {hop_flag = 0; return EF ();}
- , for (i = 0; i < SCREENMAX; i ++)
- , if (forward_scroll () == ERRORS)
- , break; /* EOF reached */
- , if (y - i < 0) /* Line no longer on screen */
- , move_y (SCREENMAX >> 1);
- , else
- , move_y (y - i);
- ,}
- ,
- ,/*
- , * Scroll backwards one page or to top of file, whatever comes first.
- , * (Top_line becomes bot_line of display).
- , * The very bottom line (YMAX) is always blank.
- , * Try to leave the cursor on the same line.
- , * If this is not possible, leave cursor on the line halfway the page.
- , */
- ,PU ()
- ,{
- , register int i;
- ,
- , if (hop_flag > 0) {hop_flag = 0; return HOME ();}
- , for (i = 0; i < SCREENMAX; i ++)
- , if (reverse_scroll () == ERRORS)
- , break; /* Top of file reached */
- , set_cursor (0, YMAX); /* Erase very bottom line */
- , clear_eol ();
- , if (y + i > SCREENMAX) /* line no longer on screen */
- , move_y (SCREENMAX >> 1);
- , else
- , move_y (y + i);
- ,}
- ,
- ,/*
- , * Go to top of file, scrolling if possible, else redrawing screen.
- , */
- ,HOME ()
- ,{
- , if (proceed (top_line, - SCREENMAX) == header)
- , PU (); /* It fits. Let PU do it */
- , else {
- , reset (header->next, 0);/* Reset top_line, etc. */
- , RD (); /* Display full page */
- , }
- , move_to (LINE_START, 0);
- ,}
- ,
- ,/*
- , * Go to last line of file, scrolling if possible, else redrawing screen
- , */
- ,EF ()
- ,{
- , if (tail->prev->text [0] != '\n')
- , /* dummy_line () */;
- , if (proceed (bot_line, SCREENMAX) == tail)
- , PD (); /* It fits. Let PD do it */
- , else {
- , reset (proceed (tail->prev, - SCREENMAX), SCREENMAX);
- , RD (); /* Display full page */
- , }
- , move_to (LINE_END /* not START (EF!) */, last_y);
- ,}
- ,
- ,/*
- , * Scroll one line up. Leave the cursor on the same line (if possible).
- , */
- ,SU ()
- ,{
- , register int i;
- ,
- , if (hop_flag > 0) {
- , hop_flag = 0;
- , for (i = 0; i < SCREENMAX >> 1; i ++) SU ();
- , return;
- , }
- ,
- , if (top_line->prev == header) /* Top of file. Can't scroll */
- , return;
- , (void) reverse_scroll ();
- , set_cursor (0, YMAX); /* Erase very bottom line */
- , clear_eol ();
- , move_y ((y == SCREENMAX) ? SCREENMAX : y + 1);
- ,}
- ,
- ,/*
- , * Scroll one line down. Leave the cursor on the same line (if possible).
- , */
- ,SD ()
- ,{
- , register int i;
- ,
- , if (hop_flag > 0) {
- , hop_flag = 0;
- , for (i = 0; i < SCREENMAX >> 1; i ++) SD ();
- , return;
- , }
- ,
- , if (forward_scroll () != ERRORS)
- , move_y ((y == 0) ? 0 : y - 1);
- , else
- , set_cursor (x, y);
- ,}
- ,
- ,/*
- , * Perform a forward scroll. It returns ERRORS if we're at the last line of
- , * the file.
- , */
- ,forward_scroll ()
- ,{
- , if (bot_line->next == tail) /* Last line of file. No dice */
- , return ERRORS;
- , top_line = top_line->next;
- , bot_line = bot_line->next;
- , cur_line = cur_line->next;
- , scroll_forward ();
- , set_cursor (0, SCREENMAX);
- , line_print (bot_line);
- ,
- , return FINE;
- ,}
- ,
- ,/*
- , * Perform a backwards scroll. It returns ERRORS if we're at the first line
- , * of the file.
- , */
- ,reverse_scroll ()
- ,{
- , if (top_line->prev == header)
- , return ERRORS; /* Top of file. Can't scroll */
- ,
- , if (last_y != SCREENMAX) /* Reset last_y if necessary */
- , last_y ++;
- , else
- , bot_line = bot_line->prev; /* Else adjust bot_line */
- , top_line = top_line->prev;
- , cur_line = cur_line->prev;
- ,
- ,/* Perform the scroll */
- , set_cursor (0, 0);
- , scroll_reverse ();
- , set_cursor (0, 0);
- , line_print (top_line);
- ,
- , return FINE;
- ,}
- ,
- ,/*----------------------*
- , * Word moves *
- , *----------------------*/
- ,/*
- , * Word definitions
- , */
- ,#define white_space(c) ((c) == ' ' || (c) == '\t')
- ,#define alpha(c) ((c) != ' ' && (c) != '\t' && (c) != '\n')
- ,/*
- , * A word was previously defined as a number of non-blank characters
- , * separated by tabs, spaces or linefeeds.
- , * By consulting idfchar (), sequences of real letters only or digits
- , * or underlines are recognized as words.
- , */
- ,int
- ,idfchar (ch)
- , u_char ch;
- ,{
- , return ('0' <= ch && ch <= '9') ||
- , ('A' <= ch && ch <= 'Z') ||
- , ('a' <= ch && ch <= 'z') ||
- ,#ifdef msdos
- , ((u_char) '\200' <= ch) || /* might be more specific */
- ,#else
- , ((u_char) '\300' <= ch && ch != (u_char) 'W' && ch != (u_char) 'w') ||
- , (ch == (u_char) '5') ||
- ,#endif
- , (ch == '_');
- ,}
- ,
- ,/*
- , * MPW () moves to the start of the previous word. A word is defined as a
- , * number of non-blank characters separated by tabs spaces or linefeeds.
- , */
- ,MPW ()
- ,{
- , if (hop_flag > 0) return BSEN ();
- , move_previous_word (NO_DELETE);
- ,}
- ,
- ,move_previous_word (remove)
- , FLAG remove;
- ,{
- , register char * begin_line;
- , register char * textp;
- , char start_char = * cur_text;
- , char * start_pos = cur_text;
- , FLAG idfsearch;
- ,
- , if ((remove == DELETE) && (viewonly == TRUE))
- , return viewonlyerr ();
- ,
- ,/* First check if we're at the beginning of line. */
- , if (cur_text == cur_line->text) {
- , if (cur_line->prev == header)
- , return;
- , start_char = '\0';
- , }
- ,
- , MLF ();
- ,
- , begin_line = cur_line->text;
- , textp = cur_text;
- ,
- ,/* Check if we're in the middle of a word. */
- , if (!alpha (* textp) || !alpha (start_char)) {
- , while (textp != begin_line && (white_space (* textp) || * textp == '\n'))
- , textp --;
- , }
- ,
- ,/* Now we're at the end of previous word. Skip non-blanks until a blank comes */
- , if (idfchar (* textp)) {
- , idfsearch = TRUE;
- , while (textp != begin_line && idfchar (* textp))
- , textp --;
- , }
- , else {
- , idfsearch = FALSE;
- , while (textp != begin_line && alpha (* textp) && !idfchar (* textp))
- , textp --;
- , }
- ,
- ,/* Go to the next char if we're not at the beginning of the line */
- ,/* At the beginning of the line, check whether to stay or to go to the word */
- , if (textp != begin_line && * textp != '\n')
- , textp ++;
- , else if (textp == begin_line &&
- , (idfsearch == TRUE) ? !idfchar (* textp) /* : white_space (* t.) */
- , : (!alpha (* textp) || idfchar (* textp))) {
- , textp ++;
- , if (white_space (* textp) || textp == start_pos)
- , /* no word there or not moved, so go back */
- , textp --;
- , }
- ,
- ,/* Find the x-coordinate of this address, and move to it */
- , move_address (textp, y);
- , if (remove == DELETE)
- , delete_text (cur_line, textp, cur_line, start_pos);
- ,}
- ,
- ,/*
- , * MNW () moves to the start of the next word. A word is defined as a number of
- , * non-blank characters separated by tabs spaces or linefeeds. Always keep in
- , * mind that the pointer shouldn't pass the '\n'.
- , */
- ,MNW ()
- ,{
- , if (hop_flag > 0) return ESEN ();
- , move_next_word (NO_DELETE);
- ,}
- ,
- ,move_next_word (remove)
- , FLAG remove;
- ,{
- , register char * textp = cur_text;
- ,
- , if ((remove == DELETE) && (viewonly == TRUE))
- , return viewonlyerr ();
- ,
- ,/* Move to the end of the current word. */
- , if (idfchar (* textp))
- , while (* textp != '\n' && idfchar (* textp))
- , textp ++;
- , else
- , while (alpha (* textp) && !idfchar (* textp))
- , textp ++;
- ,
- ,/* Skip all white spaces */
- , while (* textp != '\n' && white_space (* textp))
- , textp ++;
- ,/* If we're deleting, delete the text in between */
- , if (remove == DELETE) {
- , delete_text (cur_line, cur_text, cur_line, textp);
- , return;
- , }
- ,
- ,/* If we're at end of line, move to the beginning of (first word on) the next line */
- , if (* textp == '\n' && cur_line->next != tail) {
- , MDN ();
- , move_to (LINE_START, y);
- , textp = cur_text;
- ,/* while (* textp != '\n' && white_space (* textp))
- ,/* textp ++; /**/
- , }
- , move_address (textp, y);
- ,}
- ,
- ,/*
- , * BSEN () and ESEN () look for the beginning or end of the current sentence.
- , */
- ,BSEN ()
- ,{
- , search_for ("[;.]", REVERSE);
- ,}
- ,
- ,ESEN ()
- ,{
- , search_for ("[;.]", FORWARD);
- ,}
- ,
- ,/*
- , * find_y () checks if the matched line is on the current page. If it is, it
- , * returns the new y coordinate, else it displays the correct page with the
- , * matched line in the middle and returns the new y value;
- , */
- ,find_y (match_line)
- , LINE * match_line;
- ,{
- , return find_y_RD (match_line, TRUE);
- ,}
- ,
- ,find_y_w_o_RD (match_line)
- , LINE * match_line;
- ,{
- , return find_y_RD (match_line, FALSE);
- ,}
- ,
- ,find_y_RD (match_line, redrawflag)
- , LINE * match_line;
- , FLAG redrawflag;
- ,{
- , register LINE * line;
- , register int count = 0;
- ,
- ,/* Check if match_line is on the same page as currently displayed. */
- , for (line = top_line; line != match_line && line != bot_line->next;
- , line = line->next)
- , count ++;
- , if (line != bot_line->next)
- , return count;
- ,
- ,/* Display new page, with match_line in center. */
- , if ((line = proceed (match_line, - (SCREENMAX >> 1))) == header) {
- , /* Can't display in the middle. Make first line of file top_line */
- , count = 0;
- , for (line = header->next; line != match_line; line = line->next)
- , count ++;
- , line = header->next;
- , }
- , else /* New page is displayed. Set cursor to middle of page */
- , count = SCREENMAX >> 1;
- ,
- ,/* Reset pointers and redraw the screen */
- , reset (line, 0);
- , if (redrawflag == TRUE) RD ();
- ,
- , return count;
- ,}
- ,
- ,/*
- , * Dummy_line () adds an empty line at the end of the file. This is sometimes
- , * useful in combination with the EF and MDN command in combination with the
- , * Yank command set.
- , * !!! I see no use for this and I don't consider such autonomous
- , * !!! modifications of the text (without user request) acceptable. TW.
- , */
- ,#ifdef UNUSED
- ,dummy_line ()
- ,{
- , (void) line_insert (tail->prev, "\n", 1);
- , tail->prev->shift_count = DUMMY;
- , if (last_y != SCREENMAX) {
- , last_y ++;
- , bot_line = bot_line->next;
- , }
- ,}
- ,#endif UNUSED
- ,
- ,/* ================================================================== *
- , * Modify Commands *
- , * ================================================================== */
- ,
- ,/*
- , * viewonlyerr () outputs an error message with a beep
- , */
- ,viewonlyerr ()
- ,{
- , ring_bell ();
- , error ("View only mode", NIL_PTR);
- , return ERRORS;
- ,}
- ,
- ,/*
- , * DCC deletes the character under the cursor. If this character is a '\n' the
- , * current line is joined with the next one.
- , * If this character is the only character of the line, the current line will
- , * be deleted.
- , */
- ,DCC ()
- ,{
- , if (* cur_text == '\n')
- , if (cur_line->next == tail)
- , return;
- , else
- , delete_text (cur_line, cur_text, cur_line->next, cur_line->next->text);
- , else
- , delete_text (cur_line, cur_text, cur_line, cur_text + 1);
- ,}
- ,
- ,/*
- , * DPC deletes the character on the left side of the cursor. If the cursor
- , * is at the beginning of the line, the last character if the previous line
- , * is deleted.
- , */
- ,DPC ()
- ,{
- , hop_flag = 0;
- ,
- , if (x == 0 && cur_line->prev == header)
- , return; /* Top of file */
- ,
- , if (viewonly == TRUE)
- , return viewonlyerr ();
- ,
- , MLF (); /* Move one left */
- , DCC (); /* Delete character under cursor */
- ,}
- ,
- ,/*
- , * DLN deletes all characters until the end of the line. If the current
- , * character is a '\n', then delete that char.
- , */
- ,DLN ()
- ,{
- , if (* cur_text == '\n')
- , DCC ();
- , else
- , delete_text (cur_line, cur_text, cur_line, cur_text + length_of (cur_text) - 1);
- ,}
- ,
- ,/*
- , * DNW () deletes the next word (as defined in MNW ())
- , */
- ,DNW ()
- ,{
- , if (* cur_text == '\n')
- , DCC ();
- , else
- , move_next_word (DELETE);
- ,}
- ,
- ,/*
- , * DPW () deletes the previous word (as defined in MPW ())
- , */
- ,DPW ()
- ,{
- , if (cur_text == cur_line->text)
- , DPC ();
- , else
- , move_previous_word (DELETE);
- ,}
- ,
- ,/*
- , * Insert character `character' at current location.
- , */
- ,SNL ()
- ,{
- , S ('\n');
- ,}
- ,S (character)
- , register char character;
- ,{
- , static char buffer [2];
- ,
- , buffer [0] = character;
- ,/* Insert the character */
- , if (insert (cur_line, cur_text, buffer) == ERRORS)
- , return;
- ,
- ,/* Fix screen */
- , if (character == '\n') {
- , set_cursor (0, y);
- , if (y == SCREENMAX) { /* Can't use display () */
- , line_print (cur_line);
- , (void) forward_scroll ();
- , }
- , else {
- , reset (top_line, y); /* Reset pointers */
- , if (can_add_line == TRUE) {
- , add_line (y + 1);
- , clear_status ();
- , display (0, y, cur_line, 1);
- , }
- , else display (0, y, cur_line, last_y - y);
- , }
- , move_to (0, (y == SCREENMAX) ? y : y + 1);
- , }
- , else if (x + 1 == XBREAK) /* If line must be shifted, just call move_to */
- , move_to (x + 1, y);
- , else { /* else display rest of line */
- , put_line (cur_line, x, FALSE);
- , move_to (x + 1, y);
- , }
- ,}
- ,
- ,/*
- , * CTRl inserts a control-char at the current location. A message that this
- , * function is called is displayed at the status line.
- , */
- ,CTRl ()
- ,{
- , register u_char ctrl;
- ,
- , status_msg ("Enter control character...");
- , ctrl = readchar ();
- , clear_status ();
- , if ((ctrl == '\177') || (ctrl == '?')) return S ('\177');
- , ctrl = ctrl & '\237';
- , if (ctrl == '\0') error ("Can't handle NULL char - not inserted", NIL_PTR);
- , else S (ctrl);
- ,}
- ,
- ,/*
- , * LIB insert a line at the current position and moves back to the end of
- , * the previous line.
- , */
- ,LIB ()
- ,{
- , hop_flag = 0;
- ,
- , if (viewonly == TRUE)
- , return viewonlyerr ();
- ,
- , S ('\n'); /* Insert the line */
- , MUP (); /* Move one line up */
- , move_to (LINE_END, y); /* Move to end of this line */
- ,}
- ,
- ,/*
- , * Install_line installs the buffer into a LINE structure it returns
- , * a pointer to the allocated structure.
- , */
- ,LINE *
- ,install_line (buffer, length)
- , char * buffer;
- , int length;
- ,{
- , register LINE * new_line = (LINE *) alloc (sizeof (LINE));
- ,
- , new_line->text = alloc (length + 1);
- , new_line->shift_count = 0;
- , copy_string (new_line->text, buffer);
- ,
- , return new_line;
- ,}
- ,
- ,/*
- , * Line_insert () inserts a new line with text pointed to by `string'.
- , * It returns the address of the new line.
- , */
- ,LINE *
- ,line_insert (line, string, len)
- , register LINE * line;
- , char * string;
- , int len;
- ,{
- , register LINE * new_line;
- ,
- ,/* Allocate space for LINE structure and text */
- , new_line = install_line (string, len);
- ,
- ,/* Install the line into the double linked list */
- , new_line->prev = line;
- , new_line->next = line->next;
- , line->next = new_line;
- , new_line->next->prev = new_line;
- ,
- ,/* Increment nlines */
- , nlines ++;
- ,
- , return new_line;
- ,}
- ,
- ,/*
- , * Insert () insert the string `string' at the given line and location.
- , */
- ,insert (line, location, string)
- , register LINE * line;
- , char * location, * string;
- ,{
- , register char * bufp = text_buffer; /* Buffer for building line */
- , register char * textp = line->text;
- ,
- , if (viewonly == TRUE)
- , return viewonlyerr ();
- ,
- , if (length_of (textp) + length_of (string) >= MAX_CHARS) {
- , error ("Line too long", NIL_PTR);
- , return ERRORS;
- , }
- ,
- , modified = TRUE; /* File has been modified */
- ,
- ,/* Copy part of line until `location' has been reached */
- , while (textp != location)
- , * bufp ++ = * textp ++;
- ,
- ,/* Insert string at this location */
- , while (* string != '\0')
- , * bufp ++ = * string ++;
- , * bufp = '\0';
- ,
- , if (* (string - 1) == '\n') /* Insert a new line */
- , (void) line_insert (line, location, length_of (location));
- , else /* Append last part of line */
- , copy_string (bufp, location);
- ,
- ,/* Install the new text in this line */
- , free_space (line->text);
- , line->text = alloc (length_of (text_buffer) + 1);
- , copy_string (line->text, text_buffer);
- ,
- , return FINE;
- ,}
- ,
- ,/*
- , * Line_delete () deletes the argument line out of the line list. The pointer
- , * to the next line is returned.
- , */
- ,LINE *
- ,line_delete (line)
- , register LINE * line;
- ,{
- , register LINE * next_line = line->next;
- ,
- ,/* Delete the line */
- , line->prev->next = line->next;
- , line->next->prev = line->prev;
- ,
- ,/* Free allocated space */
- , free_space (line->text);
- , free_space (line);
- ,
- ,/* Decrement nlines */
- , nlines --;
- ,
- , return next_line;
- ,}
- ,
- ,/*
- , * Delete () deletes all the characters (including newlines) between the
- , * startposition and endposition and fixes the screen accordingly. It
- , * returns the number of lines deleted.
- , */
- ,delete_text (start_line, start_textp, end_line, end_textp)
- , register LINE * start_line;
- , LINE * end_line;
- , char * start_textp, * end_textp;
- ,{
- , register char * textp = start_line->text;
- , register char * bufp = text_buffer; /* Storage for new line->text */
- , LINE * line;
- , LINE * after_end = end_line->next;
- , int line_cnt = 0; /* Nr of lines deleted */
- , int count = 0;
- , int shift = 0; /* Used in shift calculation */
- , int nx = x;
- ,
- , if (viewonly == TRUE)
- , return viewonlyerr ();
- ,
- , modified = TRUE; /* File has been modified */
- ,
- ,/* Set up new line. Copy first part of start line until start_position. */
- , while (textp < start_textp) {
- , * bufp ++ = * textp ++;
- , count ++;
- , }
- ,
- ,/* Check if line doesn't exceed MAX_CHARS */
- , if (count + length_of (end_textp) >= MAX_CHARS) {
- , error ("Line too long", NIL_PTR);
- , return;
- , }
- ,
- ,/* Copy last part of end_line if end_line is not tail */
- , copy_string (bufp, (end_textp != NIL_PTR) ? end_textp : "\n");
- ,
- ,/* Delete all lines between start and end_position (including end_line) */
- , line = start_line->next;
- , while (line != after_end && line != tail) {
- , /* Here, the original mined compared with end_line->next which has
- , already been discarded when the comparison should become true.
- , This severe error remained undetected until I ported to MSDOS */
- , line = line_delete (line);
- , line_cnt ++;
- , }
- ,
- ,/* Check if last line of file should be deleted */
- , if (end_textp == NIL_PTR && length_of (start_line->text) == 1 && nlines > 1) {
- , start_line = start_line->prev;
- , (void) line_delete (start_line->next);
- , line_cnt ++;
- , }
- , else { /* Install new text */
- , free_space (start_line->text);
- , start_line->text = alloc (length_of (text_buffer) + 1);
- , copy_string (start_line->text, text_buffer);
- , }
- ,
- ,/* Fix screen. First check if line is shifted. Perhaps we should shift it back */
- ,/* !!! This resulted in a positioning error when a line containing TABs
- ,/* !!! was shifted back. So better leave it.
- ,/* if (get_shift (start_line->shift_count)) {
- ,/* shift = (XBREAK - count_chars (start_line)) / SHIFT_SIZE;
- ,/* if (shift > 0) { /* Shift line `shift' back */
- ,/* if (shift >= get_shift (start_line->shift_count))
- ,/* start_line->shift_count = 0;
- ,/* else
- ,/* start_line->shift_count -= shift;
- ,/* nx += shift * SHIFT_SIZE; /* Reset x value */
- ,/* }
- ,/* } */
- ,
- , if (line_cnt == 0) { /* Check if only one line changed */
- , if (shift > 0) { /* Reprint whole line */
- , set_cursor (0, y);
- , line_print (start_line);
- , }
- , else { /* Just display last part of line */
- , set_cursor (x, y);
- , put_line (start_line, x, TRUE);
- , }
- , move_to (nx, y); /* Reset cur_text */
- , return;
- , }
- ,
- , shift = last_y; /* Save value */
- , reset (top_line, y);
- , if ((line_cnt <= SCREENMAX - y) && (can_delete_line == TRUE)) {
- , clear_status ();
- , display (0, y, start_line, 0);
- , line = proceed (start_line, SCREENMAX - y - line_cnt + 1);
- , while (line_cnt -- > 0) {
- , delete_line (y + 1);
- , if (line != tail) {
- , set_cursor (0, SCREENMAX);
- , line_print (line);
- , line = line->next;
- , }
- , }
- , }
- , else
- , display (0, y, start_line, shift - y);
- ,/* move_to ((line_cnt == 1) ? nx : 0, y); */
- , move_to (nx, y);
- ,}
- ,
- ,/* ================================================================== *
- , * Yank Commands *
- , * ================================================================== */
- ,
- ,LINE * mark_line = NIL_LINE; /* For marking position. */
- ,char * mark_text = NIL_PTR;
- ,int lines_saved; /* Nr of lines in buffer */
- ,
- ,/*
- , * PT () inserts the buffer at the current location.
- , */
- ,PT ()
- ,{
- , register int fd; /* File descriptor for buffer */
- ,
- , if (viewonly == TRUE)
- , return viewonlyerr ();
- ,
- , if (hop_flag > 0) {
- , if ((fd = open (yankie_file, O_RDONLY, 0)) < 0) {
- , error ("No inter window buffer present.", NIL_PTR);
- , return;
- , }
- , }
- , else if ((fd = scratch_file (READ, FALSE)) == ERRORS) {
- , error ("Buffer is empty.", NIL_PTR);
- , return;
- , }
- , /* Insert the buffer */
- ,/* file_insert (fd, FALSE); /* could yield a positioning error */
- , file_insert (fd, TRUE);
- ,}
- ,
- ,/*
- , * INF () prompt for a filename and inserts the file at the current location
- , * in the file.
- , */
- ,INF ()
- ,{
- , register int fd; /* File descriptor of file */
- , char name [maxLINE_LEN]; /* Buffer for file name */
- ,
- , if (viewonly == TRUE)
- , return viewonlyerr ();
- ,
- ,/* Get the file name */
- , if (get_file ("Get and insert file:", name) != FINE)
- , return;
- , clear_status ();
- ,
- , if ((fd = open (name, O_RDONLY, 0)) < 0)
- , error ("Cannot open file: " /*, name */, serror ());
- , else { /* Insert the file */
- , file_insert (fd, TRUE); /* leave cursor at begin of insertion */
- , }
- ,}
- ,
- ,/*
- , * File_insert () inserts the contents of an opened file (as given by
- , * filedescriptor fd) at the current location.
- , * After the insertion, if old_pos is TRUE, the cursor remains at the
- , * start of the inserted text, if old_pos is FALSE, it is placed to
- , * its end. If old_pos is FALSE, this works erroneously if the last line
- , * inserted contains a TAB character!!
- , */
- ,file_insert (fd, old_pos)
- , int fd;
- , FLAG old_pos;
- ,{
- , char line_buffer [MAX_CHARS]; /* Buffer for next line */
- , register LINE * line = cur_line;
- , register int line_count = nlines; /* Nr of lines inserted */
- , LINE * page = cur_line;
- , int ret = ERRORS;
- ,
- , get_line_err1 = NIL_PTR;
- , get_line_err2 = NIL_PTR;
- ,
- ,/* Get the first piece of text (might be ended with a '\n') from fd */
- , if (get_line (fd, line_buffer) == ERRORS)
- , return; /* Empty file */
- ,
- ,/* Insert this text at the current location */
- , if (insert (line, cur_text, line_buffer) == ERRORS)
- , return;
- ,
- ,/* Repeat getting lines (and inserting lines) until EOF is reached */
- , while ((ret = get_line (fd, line_buffer)) != ERRORS && ret != NO_LINE)
- , line = line_insert (line, line_buffer, ret);
- ,
- , if (ret == NO_LINE) { /* Last line read not ended by a '\n' */
- , line = line->next;
- , (void) insert (line, line->text, line_buffer);
- , }
- ,
- , (void) close (fd);
- ,
- ,/* If illegal lines were input, report */
- , if ((get_line_err1 != NIL_PTR) || (get_line_err2 != NIL_PTR)) {
- , ring_bell ();
- , error (get_line_err1, get_line_err2);
- , sleep (1);
- , }
- ,
- ,/* Calculate nr of lines added */
- , line_count = nlines - line_count;
- ,
- ,/* Fix the screen */
- , if (line_count == 0) { /* Only one line changed */
- , set_cursor (0, y);
- , line_print (line);
- , move_to ((old_pos == TRUE) ? x : x + length_of (line_buffer), y);
- , }
- , else { /* Several lines changed */
- , reset (top_line, y); /* Reset pointers */
- , while (page != line && page != bot_line->next)
- , page = page->next;
- , if (page != bot_line->next || old_pos == TRUE)
- , display (0, y, cur_line, SCREENMAX - y);
- , if (old_pos == TRUE)
- , move_to (x, y);
- , else if (ret == NO_LINE)
- , move_to (length_of (line_buffer), find_y (line));
- , else
- , move_to (0, find_y (line->next));
- , }
- ,
- ,/* If nr of added line >= REPORT, print the count */
- , if (line_count >= REPORT)
- , status_line (num_out ((long) line_count), " lines added.");
- ,}
- ,
- ,/*
- , * WB () writes the buffer (yank_file) into another file, which
- , * is prompted for.
- , */
- ,WB ()
- ,{
- , register int new_fd; /* Filedescriptor to copy file */
- , int yank_fd; /* Filedescriptor to buffer */
- , register int cnt; /* Count check for read/write */
- , int ret; /* Error check for write */
- , char file_name [maxLINE_LEN]; /* Output file name */
- , char * msg_doing; char * msg_done;
- ,
- ,/* Checkout the buffer */
- , if ((yank_fd = scratch_file (READ, FALSE)) == ERRORS) {
- , error ("Buffer is empty.", NIL_PTR);
- , return;
- , }
- ,
- ,/* Get file name */
- , if (get_file ((hop_flag > 0) ? "Append buffer to file:"
- , : "Write buffer to file:", file_name) != FINE)
- , return;
- ,
- ,/* Create the new file or open previous file for appending */
- , if (hop_flag > 0) {
- , if ((new_fd = open (file_name, O_WRONLY | O_CREAT | O_APPEND, fprot)) < 0) {
- , error ("Cannot append to file: ", serror ());
- , return;
- , }
- , msg_doing = "Appending "; msg_done = "Appended";
- , }
- , else {
- , if (checkoverwrite (file_name) != TRUE)
- , return ERRORS;
- , else if ((new_fd = creat (file_name, fprot)) < 0) {
- , error ("Cannot create file: ", serror ());
- , return;
- , }
- , msg_doing = "Writing "; msg_done = "Wrote";
- , }
- ,
- , status_line (msg_doing, file_name);
- ,
- ,/* Copy buffer into file */
- , while ((cnt = read (yank_fd, text_buffer, sizeof (text_buffer))) > 0)
- , if (write (new_fd, text_buffer, cnt) != cnt) {
- , bad_write (new_fd);
- , ret = ERRORS;
- , break;
- , }
- ,
- ,/* Clean up open files and status_line */
- , (void) close (new_fd);
- , (void) close (yank_fd);
- ,
- , if (ret != ERRORS) /* Bad write */
- , file_status (msg_done, chars_saved, file_name, lines_saved, TRUE, FALSE, FALSE);
- ,}
- ,
- ,/*
- , * MARK sets mark_line / mark_text to the current line / current text pointer.
- , */
- ,MARK ()
- ,{
- , if (hop_flag > 0) return GOMA ();
- , mark_line = cur_line;
- , mark_text = cur_text;
- , status_msg ("Mark set");
- ,}
- ,
- ,/*
- , * GOMA moves to the marked position
- , */
- ,GOMA ()
- ,{
- , FLAG checkmark ();
- ,
- , if (checkmark () == NOT_VALID)
- , return error ("Mark not set.", NIL_PTR);
- , else
- , move_address (mark_text, find_y (mark_line));
- ,}
- ,
- ,/*
- , * YA () puts the text between the marked position and the current
- , * in the buffer.
- , */
- ,YA ()
- ,{
- , set_up (NO_DELETE, (hop_flag > 0) ? TRUE : FALSE);
- ,}
- ,
- ,/*
- , * DT () is essentially the same as YA (), but in DT () the text is deleted.
- , */
- ,DT ()
- ,{
- , if (viewonly == TRUE)
- , return viewonlyerr ();
- ,
- , set_up (DELETE, (hop_flag > 0) ? TRUE : FALSE);
- ,}
- ,
- ,/*
- , * Set_up is an interface to the actual yank. It calls checkmark () to check
- , * if the marked position is still valid. If it is, yank is called with the
- , * arguments in the right order.
- , */
- ,set_up (remove, append)
- , FLAG remove; /* == DELETE if text should be deleted */
- , FLAG append; /* == TRUE if text should only be appended to yank buffer */
- ,{
- , FLAG checkmark ();
- ,
- , switch (checkmark ()) {
- , case NOT_VALID :
- , error ("Mark not set.", NIL_PTR);
- , return;
- , case SMALLER :
- , yank (mark_line, mark_text, cur_line, cur_text, remove, append);
- , yankie ();
- , break;
- , case BIGGER :
- , yank (cur_line, cur_text, mark_line, mark_text, remove, append);
- , yankie ();
- , break;
- , case SAME : /* Ignore stupid behaviour */
- , /* yank_status = EMPTY; */
- , chars_saved = 0L;
- , status_msg ("Nothing to save");
- , break;
- , }
- ,}
- ,
- ,/*
- , * Yankie () provides a reference to the last saved buffer to be read
- , * by other mined invocations.
- , */
- ,yankie ()
- ,{
- , delete_file (yankie_file);
- ,#ifdef unix
- , link (yank_file, yankie_file);
- ,#else
- , build_string (text_buffer, copycommand, yank_file, yankie_file);
- , system (text_buffer);
- ,#endif
- ,}
- ,
- ,/*
- , * Check_mark () checks if mark_line and mark_text are still valid pointers.
- , * If they are it returns
- , * SMALLER if the marked position is before the current,
- , * BIGGER if it isn't or SAME if somebody didn't get the point.
- , * NOT_VALID is returned when mark_line and/or mark_text are no longer valid.
- , * Legal () checks if mark_text is valid on the mark_line.
- , */
- ,FLAG
- ,checkmark ()
- ,{
- , register LINE * line;
- , FLAG cur_seen = FALSE;
- ,
- ,/* Special case: check is mark_line and cur_line are the same. */
- , if (mark_line == cur_line) {
- , if (mark_text == cur_text) /* Even same place */
- , return SAME;
- , if (legal () == ERRORS) /* mark_text out of range */
- , return NOT_VALID;
- , return (mark_text < cur_text) ? SMALLER : BIGGER;
- , }
- ,
- ,/* Start looking for mark_line in the line structure */
- , for (line = header->next; line != tail; line = line->next) {
- , if (line == cur_line)
- , cur_seen = TRUE;
- , else if (line == mark_line)
- , break;
- , }
- ,
- ,/* If we found mark_line (line != tail) check for legality of mark_text */
- , if (line == tail || legal () == ERRORS)
- , return NOT_VALID;
- ,
- ,/* cur_seen is TRUE if cur_line is before mark_line */
- , return (cur_seen == TRUE) ? BIGGER : SMALLER;
- ,}
- ,
- ,/*
- , * Legal () checks if mark_text is still a valid pointer.
- , */
- ,legal ()
- ,{
- , register char * textp = mark_line->text;
- ,
- ,/* Locate mark_text on mark_line */
- , while (textp != mark_text && * textp ++ != '\0')
- , ;
- , return (* textp == '\0') ? ERRORS : FINE;
- ,}
- ,
- ,/*
- , * Yank puts all the text between start_position and end_position into
- , * the buffer.
- , * The caller must check that the arguments to yank () are valid (e.g. in
- , * the right order).
- , */
- ,yank (start_line, start_textp, end_line, end_textp, remove, append)
- , LINE * start_line, * end_line;
- , char * start_textp, * end_textp;
- , FLAG remove; /* == DELETE if text should be deleted */
- , FLAG append; /* == TRUE if text should only be appended to yank buffer */
- ,{
- , register LINE * line = start_line;
- , register char * textp = start_textp;
- , int fd;
- ,
- ,/* Create file to hold buffer */
- , if ((fd = scratch_file (WRITE, append)) == ERRORS)
- , return;
- ,
- , chars_saved = 0L;
- , lines_saved = 0;
- , if (append == TRUE)
- , status_msg ("Appending text ...");
- , else status_msg ("Saving text ...");
- ,
- ,/* Keep writing chars until the end_location is reached. */
- , while (textp != end_textp) {
- , if (writechar (fd, * textp) == ERRORS) {
- , (void) close (fd);
- , return;
- , }
- , if (* textp ++ == '\n') { /* Move to the next line */
- , line = line->next;
- , textp = line->text;
- , lines_saved ++;
- , }
- , chars_saved ++;
- , }
- ,
- ,/* Flush the I/O buffer and close file */
- , if (flush_buffer (fd) == ERRORS) {
- , (void) close (fd);
- , return;
- , }
- , (void) close (fd);
- , yank_status = VALID;
- ,
- , /*
- , * Check if the text should be deleted as well. In case it should,
- , * the following hack is used to save a lot of code.
- , * First move back to the start_position (this might be the current
- , * location) and then delete the text.
- , * This might look a bit confusing to the user the first time.
- , * Delete () will fix the screen.
- , */
- , if (remove == DELETE) {
- , move_to (find_x (start_line, start_textp), find_y (start_line));
- , delete_text (start_line, start_textp, end_line, end_textp);
- , mark_line = cur_line;
- , mark_text = cur_text;
- , }
- ,
- , if (append == TRUE)
- , status_line (num_out (chars_saved), " characters appended to buffer.");
- , else status_line (num_out (chars_saved), " characters saved in buffer.");
- ,}
- ,
- ,/*
- , * Scratch_file () tries to create a unique file in a temporary directory.
- , * It tries several different filenames until one can be created
- , * or MAXTRIALS attempts have been made.
- , * After MAXTRIALS times, an error message is given and ERRORS is returned.
- , */
- ,
- ,#define MAXTRIALS 99
- ,
- ,scratch_file (mode, append)
- , FLAG mode; /* Can be READ or WRITE permission */
- , FLAG append; /* == TRUE if text should only be appended to yank buffer */
- ,{
- , static int trials = 0; /* Keep track of trials */
- , register char * y_ptr, * n_ptr;
- , int fd = 0; /* Filedescriptor to buffer */
- ,
- ,/* If yank_status == NOT_VALID, scratch_file is called for the first time */
- , if (yank_status == NOT_VALID && mode == WRITE) { /* Create new file */
- , /* Generate file name. */
- ,#ifdef msdos
- , build_string (yank_file, "%s.%d", yankie_file, trials);
- ,#else
- , build_string (yank_file, "%s.%d.%d", yankie_file, getpid (), trials);
- ,#endif
- , /* Check file existence */
- , if (access (yank_file, 0 /* F_OK */) == 0
- , || (fd = creat (yank_file, fprot)) < 0) {
- , if (++ trials >= MAXTRIALS) {
- , build_string (text_buffer, "Unable to create scratchfile %s: ", yank_file);
- , if (fd == 0)
- , error (text_buffer, "File exists");
- , else
- , error (text_buffer, serror ());
- , return ERRORS;
- , }
- , else
- , return scratch_file (mode, append); /* try again */
- , }
- , }
- , else if (yank_status == NOT_VALID && mode == READ) {
- , return ERRORS;
- , }
- , else /* yank_status == VALID */
- , if ( (mode == READ && (fd = open (yank_file, O_RDONLY, 0)) < 0)
- , || (mode == WRITE &&
- , (fd = open (yank_file, O_WRONLY | O_CREAT |
- , ((append == TRUE) ? O_APPEND : O_TRUNC), fprot)) < 0)) {
- , yank_status = NOT_VALID;
- , return ERRORS;
- , }
- ,
- , clear_buffer ();
- , return fd;
- ,}
- ,
- ,/* ================================================================== *
- , * Search Commands *
- , * ================================================================== */
- ,
- ,/*
- , * A regular expression consists of a sequence of:
- , * 1. A normal character matching that character.
- , * 2. A . matching any character.
- , * 3. A ^ matching the begin of a line.
- , * 4. A $ (as last character of the pattern) mathing the end of a line.
- , * 5. A \<character> matching <character>.
- , * 6. A number of characters enclosed in [] pairs matching any of these
- , * characters. A list of characters can be indicated by a '-'. So
- , * [a-z] matches any letter of the alphabet. If the first character
- , * after the '[' is a '^' then the set is negated (matching none of
- , * the characters).
- , * A ']', '^' or '-' can be escaped by putting a '\' in front of it.
- , * 7. If one of the expressions as described in 1-6 is followed by a
- , * '*' than that expressions matches a sequence of 0 or more of
- , * that expression.
- , */
- ,
- ,char typed_expression [maxLINE_LEN]; /* Holds previous search expression */
- ,
- ,/*
- , * SFW searches forward for an expression.
- , */
- ,SFW ()
- ,{
- , search ("Search forward:", FORWARD);
- ,}
- ,
- ,/*
- , * SRV searches backwards for an expression.
- , */
- ,SRV ()
- ,{
- , search ("Search reverse:", REVERSE);
- ,}
- ,
- ,/*
- , * RS searches using the last search direction and expression.
- , */
- ,RS ()
- ,{
- , re_search ();
- ,}
- ,
- ,/*
- , * Get_expression () prompts for an expression. If just a return is typed, the
- , * old expression is used. If the expression changed, compile () is called and
- , * the returning REGEX structure is returned. It returns NIL_REG upon error.
- , * The save flag indicates whether the expression should be appended at the
- , * message pointer.
- , */
- ,REGEX *
- ,get_expression (message)
- , char * message;
- ,{
- , static REGEX program; /* Program of expression */
- , char exp_buf [maxLINE_LEN]; /* Buffer for new expr. */
- ,
- , if (get_string (message, exp_buf, FALSE) == ERRORS)
- , return NIL_REG;
- ,
- , if (exp_buf [0] == '\0' && typed_expression [0] == '\0') {
- , error ("No previous search expression.", NIL_PTR);
- , return NIL_REG;
- , }
- ,
- , if (exp_buf [0] != '\0') { /* A new expr. is typed */
- , copy_string (typed_expression, exp_buf); /* Save expr. */
- , compile (exp_buf, & program); /* Compile new expression */
- , }
- ,
- , if (program.status == REG_ERROR) { /* Error during compiling */
- , error (program.result.err_mess, NIL_PTR);
- , return NIL_REG;
- , }
- , return & program;
- ,}
- ,
- ,REGEX *
- ,make_expression (expr)
- , char * expr;
- ,{
- , static REGEX program; /* Program of expression */
- ,
- , compile (expr, & program); /* Compile new expression */
- ,
- , if (program.status == REG_ERROR) { /* Error during compiling */
- , error (program.result.err_mess, NIL_PTR);
- , return NIL_REG;
- , }
- , return & program;
- ,}
- ,
- ,/*
- , * GR () a replaces all matches from the current position until the end
- , * of the file.
- , */
- ,GR ()
- ,{
- , change ("Global replace:", VALID, FALSE);
- ,}
- ,
- ,/*
- , * Replace is substitute with confirmation dialogue.
- , */
- ,REPL ()
- ,{
- , change ("Global replace (with confirm):", VALID, TRUE);
- ,}
- ,
- ,/*
- , * LR () replaces all matches on the current line.
- , */
- ,LR ()
- ,{
- , change ("Line replace:", NOT_VALID, FALSE);
- ,}
- ,
- ,/*
- , * Change () prompts for an expression and a substitution pattern and changes
- , * all matches of the expression into the substitution.
- , * change () starts looking for expressions at the current line and
- , * continues until the end of the file if the FLAG file is VALID.
- , * For a call graph of search procedures see search ().
- , */
- ,change (message, global, confirm)
- , char * message; /* Message to prompt for expression */
- , FLAG global, confirm;
- ,{
- , char mess_buf [maxLINE_LEN]; /* Buffer to hold message */
- , char replacement [maxLINE_LEN]; /* Buffer to hold subst. pattern */
- , REGEX * program; /* Program resulting from compilation */
- , register LINE * line = cur_line;
- , register char * textp;
- , char * substitute ();
- , long lines = 0L; /* Nr of lines on which subs occurred */
- , long subs = 0L; /* Nr of subs made */
- , int ly = y; /* Index to check if line is on screen */
- , char c;
- ,
- , if (viewonly == TRUE)
- , return viewonlyerr ();
- ,
- ,/* Save message and get expression */
- , copy_string (mess_buf, message);
- , if ((program = get_expression (mess_buf)) == NIL_REG)
- , return;
- ,
- ,/* Get substitution pattern */
- , build_string (mess_buf, "%s %s by:", mess_buf, typed_expression);
- , if (get_string (mess_buf, replacement, FALSE) == ERRORS)
- , return;
- ,
- , set_cursor (0, YMAX);
- , flush ();
- ,/* Substitute until end of file */
- , do {
- , if (line_check (program, line->text, FORWARD)) {
- , lines ++;
- , /* Repeat sub. on this line as long as we find a match*/
- , do {
- , if (confirm == TRUE) {
- , ly = find_y (line);
- , textp = program->start_ptr;
- , move_address (program->start_ptr, ly);
- , status_msg ("Replace ? (y/n)");
- , c = promptyn ();
- , clear_status ();
- , if (c == 'y') {
- , subs ++; /* Increment subs */
- , if ((textp = substitute (line, program, replacement, confirm))
- , == NIL_PTR)
- , return; /* Line too long */
- , set_cursor (0, ly);
- , line_print (line);
- , }
- , else
- , textp ++;
- , }
- , else {
- , subs ++; /* Increment subs */
- , if ((textp = substitute (line, program, replacement, confirm))
- , == NIL_PTR)
- , return; /* Line too long */
- , }
- , } while ( (program->status & BEGIN_LINE) != BEGIN_LINE
- , && (program->status & END_LINE) != END_LINE
- , && line_check (program, textp, FORWARD)
- , && quit == FALSE);
- , /* Check to see if we can print the result */
- , if (confirm == FALSE && ly <= SCREENMAX) {
- , set_cursor (0, ly);
- , line_print (line);
- , }
- , }
- , if (ly <= SCREENMAX)
- , ly ++;
- , line = line->next;
- , } while (line != tail && global == VALID && quit == FALSE);
- ,
- , copy_string (mess_buf, (quit == TRUE) ? "(Aborted) " : "");
- ,/* Fix the status line */
- , if (subs == 0L && quit == FALSE)
- , error ("Pattern not found.", NIL_PTR);
- , else if (lines >= REPORT || quit == TRUE) {
- , build_string (mess_buf, "%s %ld substitutions on %ld lines.",
- , mess_buf, subs, lines);
- , status_msg (mess_buf);
- , }
- , else if (global == NOT_VALID && subs >= REPORT)
- , status_line (num_out (subs), " substitutions.");
- , else
- , clear_status ();
- , move_to (x, y);
- ,/* if (quit == TRUE) swallow_dummy_quit_char (); */
- , quit = FALSE;
- ,}
- ,
- ,/*
- , * Substitute () replaces the match on this line by the substitute pattern
- , * as indicated by the program. Every '&' in the replacement is replaced by
- , * the original match. A \ in the replacement escapes the next character.
- , */
- ,char *
- ,substitute (line, program, replacement, confirm)
- , LINE * line;
- , REGEX * program;
- , char * replacement; /* Contains replacement pattern */
- , FLAG confirm;
- ,{
- , register char * textp = text_buffer;
- , register char * subp = replacement;
- , char * linep = line->text;
- , char * amp;
- ,
- , modified = TRUE;
- ,
- ,/* Copy part of line until the beginning of the match */
- , while (linep != program->start_ptr)
- , * textp ++ = * linep ++;
- ,
- ,/*
- , * Replace the match by the substitution pattern. Each occurrence of '&' is
- , * replaced by the original match. A \ escapes the next character.
- , */
- , while (* subp != '\0' && textp < & text_buffer [MAX_CHARS]) {
- , if (* subp == '&') { /* Replace the original match */
- , amp = program->start_ptr;
- , while (amp < program->end_ptr && textp < & text_buffer [MAX_CHARS])
- , * textp ++ = * amp ++;
- , subp ++;
- , }
- , else {
- , if (* subp == '\\' && * (subp + 1) != '\0')
- , subp ++;
- , * textp ++ = * subp ++;
- , }
- , }
- ,
- ,/* Check for line length not exceeding MAX_CHARS */
- , if (length_of (text_buffer) + length_of (program->end_ptr) >= MAX_CHARS) {
- , error ("Substitution failed: resulted line too long", NIL_PTR);
- , return NIL_PTR;
- , }
- ,
- ,/* Append last part of line to the new build line */
- , copy_string (textp, program->end_ptr);
- ,
- ,/* Free old line and install new one */
- , free_space (line->text);
- , line->text = alloc (length_of (text_buffer) + 1);
- , copy_string (line->text, text_buffer);
- ,
- , return (line->text + (textp - text_buffer));
- ,}
- ,
- ,/*
- , * Search () calls get_expression to fetch the expression. If this went well,
- , * the function match () is called which returns the line with the next match.
- , * If this line is the NIL_LINE, it means that a match could not be found.
- , * Find_x () and find_y () display the right page on the screen, and return
- , * the right coordinates for x and y.
- , * These coordinates are passed to move_to ().
- , * Re_search () searches using the last search program and direction.
- , Call graph of search procedures:
- ,RS ------------\
- ,SFW -\ ; re_search ; match -----------\
- ,SRV --; search + \
- , \ \
- , ; get_expression ; compile ; line_check ; check_string
- ,GR \ / /
- ,REPL ; change +---------------------------------/
- ,LR / \
- , \ substitute
- ,
- , */
- ,REGEX * lastprogram = NIL_REG;
- ,FLAG lastmethod = NOT_VALID; /* FORWARD, REVERSE */
- ,
- ,search (message, method)
- , char * message;
- , FLAG method;
- ,{
- , register REGEX * program;
- ,
- , lastmethod = method;
- ,
- ,/* Get the expression */
- , if ((program = get_expression (message)) == NIL_REG)
- , return;
- , lastprogram = program;
- , re_search ();
- ,}
- ,
- ,search_for (expr, method)
- , char * expr;
- , FLAG method;
- ,{
- , register REGEX * program;
- ,
- ,/* make the expression */
- , if ((program = make_expression (expr)) == NIL_REG)
- , return;
- , do_search (program, method);
- ,}
- ,
- ,re_search ()
- ,{
- , do_search (lastprogram, lastmethod);
- ,}
- ,
- ,do_search (program, method)
- , register REGEX * program;
- , FLAG method;
- ,{
- , register LINE * match_line;
- ,
- , if (method == NOT_VALID) return error ("No previous search.", NIL_PTR);
- , if (program == NIL_REG) return error ("No previous search expression.", NIL_PTR);
- ,
- , set_cursor (0, YMAX);
- , clear_status ();
- , flush ();
- ,/* Find the match */
- , if ((match_line = match (program, cur_text, method)) == NIL_LINE) {
- , if (quit == TRUE) {
- , status_msg ("Aborted");
- ,/* swallow_dummy_quit_char (); */
- , quit = FALSE;
- , }
- , else
- , status_msg ("Pattern not found");
- , return;
- , }
- ,
- ,/* clear_status (); */
- , move_address (program->start_ptr, find_y (match_line));
- ,}
- ,
- ,/* Now the search and replace utilities */
- ,/* ------------------------------------ */
- ,/* Opcodes for characters */
- ,#define NORMAL 0x0200
- ,#define DOT 0x0400
- ,#define EOLN 0x0800
- ,#define STAR 0x1000
- ,#define BRACKET 0x2000
- ,#define NEGATE 0x0100
- ,#define DONE 0x4000
- ,
- ,/* Mask for opcodes and characters */
- ,#define LOW_BYTE 0x00FF
- ,#define HIGH_BYTE 0xFF00
- ,
- ,/* Previous is the contents of the previous address (ptr) points to */
- ,#define previous(ptr) (* ((ptr) - 1))
- ,
- ,/* Buffer to store outcome of compilation */
- ,int exp_buffer [BLOCK_SIZE];
- ,
- ,/* Errors often used */
- ,char * too_long = "Regular expression too long";
- ,
- ,/*
- , * Reg_error () is called by compile () if something went wrong. It sets the
- , * status of the structure to error, and assigns the error field of the union.
- , */
- ,#define reg_error(str) program->status = REG_ERROR, \
- , program->result.err_mess = (str)
- ,/*
- , * Finished () is called when everything went right during compilation. It
- , * allocates space for the expression, and copies the expression buffer into
- , * this field.
- , */
- ,finished (program, last_exp)
- , register REGEX * program;
- , int * last_exp;
- ,{
- , register int length = (last_exp - exp_buffer) * sizeof (int);
- ,/* Allocate space */
- , program->result.expression = (int *) alloc (length);
- ,/* Copy expression. (expression consists of ints!) */
- , bcopy (exp_buffer, program->result.expression, length);
- ,}
- ,
- ,/*
- , * Bcopy copies `bytes' bytes from the `from' address into the `to' address.
- , */
- ,#ifdef vms
- ,#define defbcopy
- ,#endif
- ,#ifdef msdos
- ,#define defbcopy
- ,#endif
- ,#ifdef defbcopy /* otherwise also in standard library */
- ,bcopy (from, to, bytes)
- , register char * from, * to;
- , register int bytes;
- ,{
- , while (bytes --)
- , * to ++ = * from ++;
- ,}
- ,#endif
- ,
- ,/*
- , * Compile compiles the pattern into a more comprehensible form and returns a
- , * REGEX structure. If something went wrong, the status field of the structure
- , * is set to REG_ERROR and an error message is set into the err_mess field of
- , * the union. If all went well the expression is saved and the expression
- , * pointer is set to the saved (and compiled) expression.
- , */
- ,compile (pattern, program)
- , register u_char * pattern; /* Pointer to pattern */
- , REGEX * program;
- ,{
- , register int * expression = exp_buffer;
- , int * prev_char; /* Pointer to previous compiled atom */
- , int * acct_field; /* Pointer to last BRACKET start */
- , FLAG negate; /* Negate flag for BRACKET */
- , u_char low_char; /* Index for chars in BRACKET */
- , u_char c;
- ,
- ,/* Check for begin of line */
- , if (* pattern == '^') {
- , program->status = BEGIN_LINE;
- , pattern ++;
- , }
- , else {
- , program->status = 0;
- ,/* If the first character is a '*' we have to assign it here. */
- , if (* pattern == '*') {
- , * expression ++ = '*' + NORMAL;
- , pattern ++;
- , }
- , }
- ,
- , for (; ;) { c = * pattern ++;
- , switch (c) {
- , case '.' :
- , * expression ++ = DOT;
- , break;
- , case '$' :
- , /*
- , * Only means EOLN if it is the last char of the pattern
- , */
- , if (* pattern == '\0') {
- , * expression ++ = EOLN | DONE;
- , program->status |= END_LINE;
- , finished (program, expression);
- , return;
- , }
- , else
- , * expression ++ = NORMAL + '$';
- , break;
- , case '\0' :
- , * expression ++ = DONE;
- , finished (program, expression);
- , return;
- , case '\\' :
- , /* If last char, it must! mean a normal '\' */
- , if (* pattern == '\0')
- , * expression ++ = NORMAL + '\\';
- , else
- , * expression ++ = NORMAL + * pattern ++;
- , break;
- , case '*' :
- , /*
- , * If the previous expression was a [] find out the
- , * begin of the list, and adjust the opcode.
- , */
- , prev_char = expression - 1;
- , if (* prev_char & BRACKET)
- , * (expression - (* acct_field & LOW_BYTE)) |= STAR;
- , else
- , * prev_char |= STAR;
- , break;
- , case '[' :
- , /*
- , * First field in expression gives information about
- , * the list.
- , * The opcode consists of BRACKET and if necessary
- , * NEGATE to indicate that the list should be negated
- , * and/or STAR to indicate a number of sequence of this
- , * list.
- , * The lower byte contains the length of the list.
- , */
- , acct_field = expression ++;
- , if (* pattern == '^') { /* List must be negated */
- , pattern ++;
- , negate = TRUE;
- , }
- , else
- , negate = FALSE;
- , while (* pattern != ']') {
- , if (* pattern == '\0') {
- , reg_error ("Missing ]");
- , return;
- , }
- , if (* pattern == '\\')
- , pattern ++;
- , * expression ++ = * pattern ++;
- , if (* pattern == '-') {
- , /* Make list of chars */
- , low_char = previous (pattern);
- , pattern ++; /* Skip '-' */
- , if (low_char ++ > * pattern) {
- , reg_error ("Bad range in [a-z]");
- , return;
- , }
- , /* Build list */
- , while (low_char <= * pattern
- , && low_char != '\0')
- , /* avoid wrap-around beyond '\377' (loop!) */
- , * expression ++ = low_char ++;
- , pattern ++;
- , }
- , if (expression >= & exp_buffer [BLOCK_SIZE]) {
- , reg_error (too_long);
- , return;
- , }
- , }
- , pattern ++; /* Skip ']' */
- , /* Assign length of list in acct field */
- , if ((* acct_field = (expression - acct_field)) == 1) {
- , reg_error ("Empty []");
- , return;
- , }
- , /* Assign negate and bracket field */
- , * acct_field |= BRACKET;
- , if (negate == TRUE)
- , * acct_field |= NEGATE;
- , /*
- , * Add BRACKET to opcode of last char in field because
- , * a '*' may be following the list.
- , */
- , previous (expression) |= BRACKET;
- , break;
- , default :
- , * expression ++ = c + NORMAL;
- , }
- , if (expression == & exp_buffer [BLOCK_SIZE]) {
- , reg_error (too_long);
- , return;
- , }
- , }
- , /* NOTREACHED */
- ,}
- ,
- ,/*
- , * Match gets as argument the program, pointer to place in current line to
- , * start from and the method to search for (either FORWARD or REVERSE).
- , * Match () will look through the whole file until a match is found.
- , * NIL_LINE is returned if no match could be found.
- , */
- ,LINE *
- ,match (program, string, method)
- , REGEX * program;
- , u_char * string;
- , register FLAG method;
- ,{
- , register LINE * line = cur_line;
- , u_char old_char; /* For saving chars */
- ,
- ,/* Corrupted program */
- , if (program->status == REG_ERROR)
- , return NIL_LINE;
- ,
- ,/* Check part of text first */
- , if (! (program->status & BEGIN_LINE)) {
- , if (method == FORWARD) {
- , if (line_check (program, string + 1, method) == MATCH)
- , return cur_line; /* Match found */
- , }
- , else if (! (program->status & END_LINE)) {
- , old_char = * string; /* Save char and */
- , * string = '\n'; /* Assign '\n' for line_check */
- , if (line_check (program, line->text, method) == MATCH) {
- , * string = old_char; /* Restore char */
- , return cur_line; /* Found match */
- , }
- , * string = old_char; /* No match, but restore char */
- , }
- , }
- ,
- ,/* No match in last (or first) part of line. Check out rest of file */
- , do {
- , line = (method == FORWARD) ? line->next : line->prev;
- , if (line->text == NIL_PTR) { /* Header/tail */
- , status_msg ("Search wrapped around end of file");
- , continue;
- , }
- , if (line_check (program, line->text, method) == MATCH)
- , return line;
- , } while (line != cur_line && quit == FALSE);
- ,
- ,/* No match found. */
- , return NIL_LINE;
- ,}
- ,
- ,/*
- , * Line_check () checks the line (or rather string) for a match. Method
- , * indicates FORWARD or REVERSE search. It scans through the whole string
- , * until a match is found, or the end of the string is reached.
- , */
- ,line_check (program, string, method)
- , register REGEX * program;
- , char * string;
- , FLAG method;
- ,{
- , register char * textp = string;
- ,
- ,/* Assign start_ptr field. We might find a match right away! */
- , program->start_ptr = textp;
- ,
- ,/* If the match must be anchored, just check the string. */
- , if (program->status & BEGIN_LINE)
- , return check_string (program, string, NIL_INT);
- ,
- , if (method == REVERSE) {
- , /* First move to the end of the string */
- , for (textp = string; * textp != '\n'; textp ++)
- , ;
- , /* Start checking string until the begin of the string is met */
- , while (textp >= string) {
- , program->start_ptr = textp;
- , if (check_string (program, textp --, NIL_INT))
- , return MATCH;
- , }
- , }
- , else {
- , /* Move through the string until the end of is found */
- , while (quit == FALSE && * textp != '\0') {
- , program->start_ptr = textp;
- , if (check_string (program, textp, NIL_INT))
- , return MATCH;
- , if (* textp == '\n')
- , break;
- , textp ++;
- , }
- , }
- ,
- , return NO_MATCH;
- ,}
- ,
- ,/*
- , * Check_string () checks if a match can be found in the given string.
- , * Whenever a STAR is found during matching, then the begin position of
- , * the string is marked and the maximum number of matches is performed.
- , * Then the function star () is called which starts to finish the match
- , * from this position of the string (and expression).
- , * Check () returns MATCH for a match, NO_MATCH if the string
- , * couldn't be matched or REG_ERROR for an illegal opcode in expression.
- , */
- ,check_string (program, string, expression)
- , REGEX * program;
- , register u_char * string;
- , int * expression;
- ,{
- , register int opcode; /* Holds opcode of next expr. atom */
- , u_char c; /* Char that must be matched */
- , u_char * mark; /* For marking position */
- , int star_fl; /* A star has been born */
- ,
- , if (expression == NIL_INT)
- , expression = program->result.expression;
- ,
- ,/* Loop until end of string or end of expression */
- , while (quit == FALSE && ! (* expression & DONE) &&
- , * string != '\0' && * string != '\n') {
- , c = * expression & LOW_BYTE; /* Extract match char */
- , opcode = * expression & HIGH_BYTE; /* Extract opcode */
- , if ((star_fl = (opcode & STAR)) != 0) { /* Check star occurrence */
- , opcode &= ~STAR; /* Strip opcode */
- , mark = string; /* Mark current position */
- , }
- , expression ++; /* Increment expr. */
- , switch (opcode) {
- , case NORMAL :
- , if (star_fl)
- , while (* string ++ == c) /* Skip all matches */
- , ;
- , else if (* string ++ != c)
- , return NO_MATCH;
- , break;
- , case DOT :
- , string ++;
- , if (star_fl) /* Skip to eoln */
- , while (* string != '\0' && * string ++ != '\n')
- , ;
- , break;
- , case NEGATE | BRACKET:
- , case BRACKET :
- , if (star_fl)
- , while (in_list (expression, * string ++, c, opcode) == MATCH)
- , ;
- , else
- , if (in_list (expression, * string ++, c, opcode) == NO_MATCH)
- , return NO_MATCH;
- , expression += c - 1; /* Add length of list */
- , break;
- , default :
- , panic ("Corrupted search program", NIL_PTR);
- , }
- , if (star_fl)
- , return star (program, mark, string, expression);
- , }
- , if (* expression & DONE) {
- , program->end_ptr = (char *) string; /* Match ends here */
- , /*
- , * We might have found a match. The last thing to do is check
- , * whether a '$' was given at the end of the expression, or
- , * the match was found on a null string. (E.g. [a-z]* always
- , * matches) unless a ^ or $ was included in the pattern.
- , */
- , if ((* expression & EOLN) && * string != '\n' && * string != '\0')
- , return NO_MATCH;
- , if (string == (u_char *) program->start_ptr
- , && ! (program->status & BEGIN_LINE)
- , && ! (* expression & EOLN))
- , return NO_MATCH;
- , return MATCH;
- , }
- , return NO_MATCH;
- ,}
- ,
- ,/*
- , * Star () calls check_string () to find out the longest match possible.
- , * It searches backwards until the (in check_string ()) marked position
- , * is reached, or a match is found.
- , */
- ,star (program, end_position, string, expression)
- , REGEX * program;
- , register char * end_position;
- , register char * string;
- , int * expression;
- ,{
- , do {
- , string --;
- , if (check_string (program, string, expression))
- , return MATCH;
- , } while (string != end_position);
- ,
- , return NO_MATCH;
- ,}
- ,
- ,/*
- , * In_list () checks if the given character is in the list of []. If it is
- , * it returns MATCH. if it isn't it returns NO_MATCH. These returns values
- , * are reversed when the NEGATE field in the opcode is present.
- , */
- ,in_list (list, c, list_length, opcode)
- , register int * list;
- , u_char c;
- , register int list_length;
- , int opcode;
- ,{
- , if (c == '\0' || c == '\n') /* End of string, never matches */
- , return NO_MATCH;
- , while (list_length -- > 1) { /* > 1, don't check acct_field */
- , if ((* list & LOW_BYTE) == c)
- , return (opcode & NEGATE) ? NO_MATCH : MATCH;
- , list ++;
- , }
- , return (opcode & NEGATE) ? MATCH : NO_MATCH;
- ,}
- ,
- ,/* ================================================================== *
- , * End *
- , * ================================================================== */
- EOSED
-
- exit 0
-