mined2.c

Go to the documentation of this file.
00001 /*
00002  * Part 2 of the mined editor.
00003  */
00004 
00005 /*  ========================================================================  *
00006  *                              Move Commands                                 * 
00007  *  ========================================================================  */
00008 
00009 #include "mined.h"
00010 #include <string.h>
00011 
00012 /*
00013  * Move one line up.
00014  */
00015 void UP()
00016 {
00017   if (y == 0) {         /* Top line of screen. Scroll one line */
00018         (void) reverse_scroll();
00019         move_to(x, y);
00020   }
00021   else                  /* Move to previous line */
00022         move_to(x, y - 1);
00023 }
00024 
00025 /*
00026  * Move one line down.
00027  */
00028 void DN()
00029 {
00030   if (y == last_y) {    /* Last line of screen. Scroll one line */
00031         if (bot_line->next == tail && bot_line->text[0] != '\n') {
00032                 dummy_line();           /* Create new empty line */
00033                 DN();
00034                 return;
00035         }
00036         else {
00037                 (void) forward_scroll();
00038                 move_to(x, y);
00039         }
00040   }
00041   else                  /* Move to next line */
00042         move_to(x, y + 1);
00043 }
00044 
00045 /*
00046  * Move left one position.
00047  */
00048 void LF()
00049 {
00050   if (x == 0 && get_shift(cur_line->shift_count) == 0) {/* Begin of line */
00051         if (cur_line->prev != header) {
00052                 UP();                                   /* Move one line up */
00053                 move_to(LINE_END, y);
00054         }
00055   }
00056   else
00057         move_to(x - 1, y);
00058 }
00059 
00060 /*
00061  * Move right one position.
00062  */
00063 void RT()
00064 {
00065   if (*cur_text == '\n') {
00066         if (cur_line->next != tail) {           /* Last char of file */
00067                 DN();                           /* Move one line down */
00068                 move_to(LINE_START, y);
00069         }
00070   }
00071   else
00072         move_to(x + 1, y);
00073 }
00074 
00075 /*
00076  * Move to coordinates [0, 0] on screen.
00077  */
00078 void HIGH()
00079 {
00080   move_to(0, 0);
00081 }
00082 
00083 /*
00084  * Move to coordinates [0, YMAX] on screen.
00085  */
00086 void LOW()
00087 {
00088   move_to(0, last_y);
00089 }
00090 
00091 /*
00092  * Move to begin of line.
00093  */
00094 void BL()
00095 {
00096   move_to(LINE_START, y);
00097 }
00098 
00099 /*
00100  * Move to end of line.
00101  */
00102 void EL()
00103 {
00104   move_to(LINE_END, y);
00105 }
00106 
00107 /*
00108  * GOTO() prompts for a linenumber and moves to that line.
00109  */
00110 void GOTO()
00111 {
00112   int number;
00113   LINE *line;
00114 
00115   if (get_number("Please enter line number.", &number) == ERRORS)
00116         return;
00117 
00118   if (number <= 0 || (line = proceed(header->next, number - 1)) == tail)
00119         error("Illegal line number: ", num_out((long) number));
00120   else
00121         move_to(x, find_y(line));
00122 }
00123 
00124 /*
00125  * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes 
00126  * top_line of display.) Try to leave the cursor on the same line. If this is
00127  * not possible, leave cursor on the line halfway the page.
00128  */
00129 void PD()
00130 {
00131   register int i;
00132 
00133   for (i = 0; i < screenmax; i++)
00134         if (forward_scroll() == ERRORS)
00135                 break;                  /* EOF reached */
00136   if (y - i < 0)                                /* Line no longer on screen */
00137         move_to(0, screenmax >> 1);
00138   else
00139         move_to(0, y - i);
00140 }
00141 
00142 
00143 /*
00144  * Scroll backwards one page or to top of file, whatever comes first. (Top_line
00145  * becomes bot_line of display).  The very bottom line (YMAX) is always blank.
00146  * Try to leave the cursor on the same line. If this is not possible, leave
00147  * cursor on the line halfway the page.
00148  */
00149 void PU()
00150 {
00151   register int i;
00152 
00153   for (i = 0; i < screenmax; i++)
00154         if (reverse_scroll() == ERRORS)
00155                 break;                  /* Top of file reached */
00156   set_cursor(0, ymax);                  /* Erase very bottom line */
00157 #ifdef UNIX
00158   tputs(CE, 0, _putchar);
00159 #else
00160   string_print(blank_line);
00161 #endif /* UNIX */
00162   if (y + i > screenmax)                        /* line no longer on screen */
00163         move_to(0, screenmax >> 1);
00164   else
00165         move_to(0, y + i);
00166 }
00167 
00168 /*
00169  * Go to top of file, scrolling if possible, else redrawing screen.
00170  */
00171 void HO()
00172 {
00173   if (proceed(top_line, -screenmax) == header)
00174         PU();                   /* It fits. Let PU do it */
00175   else {
00176         reset(header->next, 0);/* Reset top_line, etc. */
00177         RD();                   /* Display full page */
00178   }
00179   move_to(LINE_START, 0);
00180 }
00181 
00182 /*
00183  * Go to last line of file, scrolling if possible, else redrawing screen
00184  */
00185 void EF()
00186 {
00187   if (tail->prev->text[0] != '\n')
00188         dummy_line();
00189   if (proceed(bot_line, screenmax) == tail)
00190         PD();                   /* It fits. Let PD do it */
00191   else {
00192         reset(proceed(tail->prev, -screenmax), screenmax);
00193         RD();                   /* Display full page */
00194   }
00195   move_to(LINE_START, last_y);
00196 }
00197 
00198 /*
00199  * Scroll one line up. Leave the cursor on the same line (if possible).
00200  */
00201 void SU()
00202 {
00203   if (top_line->prev == header) /* Top of file. Can't scroll */
00204         return;
00205 
00206   (void) reverse_scroll();
00207   set_cursor(0, ymax);          /* Erase very bottom line */
00208 #ifdef UNIX
00209   tputs(CE, 0, _putchar);
00210 #else
00211   string_print(blank_line);
00212 #endif /* UNIX */
00213   move_to(x, (y == screenmax) ? screenmax : y + 1);
00214 }
00215 
00216 /*
00217  * Scroll one line down. Leave the cursor on the same line (if possible).
00218  */
00219 void SD()
00220 {
00221   if (forward_scroll() != ERRORS) 
00222         move_to(x, (y == 0) ? 0 : y - 1);
00223   else
00224         set_cursor(x, y);
00225 }
00226 
00227 /*
00228  * Perform a forward scroll. It returns ERRORS if we're at the last line of the
00229  * file.
00230  */
00231 int forward_scroll()
00232 {
00233   if (bot_line->next == tail)           /* Last line of file. No dice */
00234         return ERRORS;
00235   top_line = top_line->next;
00236   bot_line = bot_line->next;
00237   cur_line = cur_line->next;
00238   set_cursor(0, ymax);
00239   line_print(bot_line);
00240 
00241   return FINE;
00242 }
00243 
00244 /*
00245  * Perform a backwards scroll. It returns ERRORS if we're at the first line
00246  * of the file.
00247  */
00248 int reverse_scroll()
00249 {
00250   if (top_line->prev == header)
00251         return ERRORS;          /* Top of file. Can't scroll */
00252 
00253   if (last_y != screenmax)      /* Reset last_y if necessary */
00254         last_y++;
00255   else
00256         bot_line = bot_line->prev;      /* Else adjust bot_line */
00257   top_line = top_line->prev;
00258   cur_line = cur_line->prev;
00259 
00260 /* Perform the scroll */
00261   set_cursor(0, 0);
00262 #ifdef UNIX
00263   tputs(AL, 0, _putchar);
00264 #else
00265   string_print(rev_scroll);
00266 #endif /* UNIX */
00267   set_cursor(0, 0);
00268   line_print(top_line);
00269 
00270   return FINE;
00271 }
00272 
00273 /*
00274  * A word is defined as a number of non-blank characters separated by tabs
00275  * spaces or linefeeds.
00276  */
00277 
00278 /*
00279  * MP() moves to the start of the previous word. A word is defined as a
00280  * number of non-blank characters separated by tabs spaces or linefeeds.
00281  */
00282 void MP()
00283 {
00284   move_previous_word(NO_DELETE);
00285 }
00286 
00287 void move_previous_word(remove)
00288 FLAG remove;
00289 {
00290   register char *begin_line;
00291   register char *textp;
00292   char start_char = *cur_text;
00293   char *start_pos = cur_text;
00294 
00295 /* Fist check if we're at the beginning of line. */
00296   if (cur_text == cur_line->text) {
00297         if (cur_line->prev == header)
00298                 return;
00299         start_char = '\0';
00300   }
00301 
00302   LF();
00303 
00304   begin_line = cur_line->text;
00305   textp = cur_text;
00306 
00307 /* Check if we're in the middle of a word. */
00308   if (!alpha(*textp) || !alpha(start_char)) {
00309         while (textp != begin_line && (white_space(*textp) || *textp == '\n'))
00310                 textp--;
00311   }
00312 
00313 /* Now we're at the end of previous word. Skip non-blanks until a blank comes */
00314   while (textp != begin_line && alpha(*textp))
00315         textp--;
00316 
00317 /* Go to the next char if we're not at the beginning of the line */
00318   if (textp != begin_line && *textp != '\n')
00319         textp++;
00320 
00321 /* Find the x-coordinate of this address, and move to it */
00322   move_address(textp);
00323   if (remove == DELETE)
00324         delete(cur_line, textp, cur_line, start_pos);
00325 }
00326 
00327 /*
00328  * MN() moves to the start of the next word. A word is defined as a number of
00329  * non-blank characters separated by tabs spaces or linefeeds. Always keep in
00330  * mind that the pointer shouldn't pass the '\n'.
00331  */
00332 void MN()
00333 {
00334   move_next_word(NO_DELETE);
00335 }
00336 
00337 void move_next_word(remove)
00338 FLAG remove;
00339 {
00340   register char *textp = cur_text;
00341 
00342 /* Move to the end of the current word. */
00343   while (*textp != '\n' && alpha(*textp))
00344         textp++;
00345 
00346 /* Skip all white spaces */
00347   while (*textp != '\n' && white_space(*textp))
00348         textp++;
00349 /* If we're deleting. delete the text in between */
00350   if (remove == DELETE) {
00351         delete(cur_line, cur_text, cur_line, textp);
00352         return;
00353   }
00354 
00355 /* If we're at end of line. move to the first word on the next line. */
00356   if (*textp == '\n' && cur_line->next != tail) {
00357         DN();
00358         move_to(LINE_START, y);
00359         textp = cur_text;
00360         while (*textp != '\n' && white_space(*textp))
00361                 textp++;
00362   }
00363   move_address(textp);
00364 }
00365 
00366 /*  ========================================================================  *
00367  *                              Modify Commands                               *
00368  *  ========================================================================  */
00369 
00370 /*
00371  * DCC deletes the character under the cursor.  If this character is a '\n' the
00372  * current line is joined with the next one.
00373  * If this character is the only character of the line, the current line will
00374  * be deleted.
00375  */
00376 void DCC()
00377 {
00378   if (*cur_text == '\n')
00379         delete(cur_line,cur_text, cur_line->next,cur_line->next->text);
00380   else
00381         delete(cur_line, cur_text, cur_line, cur_text + 1);
00382 }
00383 
00384 /*
00385  * DPC deletes the character on the left side of the cursor.  If the cursor is
00386  * at the beginning of the line, the last character if the previous line is
00387  * deleted. 
00388  */
00389 void DPC()
00390 {
00391   if (x == 0 && cur_line->prev == header)
00392         return;                 /* Top of file */
00393   
00394   LF();                         /* Move one left */
00395   DCC();                                /* Delete character under cursor */
00396 }
00397 
00398 /*
00399  * DLN deletes all characters until the end of the line. If the current
00400  * character is a '\n', then delete that char.
00401  */
00402 void DLN()
00403 {
00404   if (*cur_text == '\n')
00405         DCC();
00406   else
00407         delete(cur_line, cur_text, cur_line, cur_text + length_of(cur_text) -1);
00408 }
00409 
00410 /*
00411  * DNW() deletes the next word (as described in MN())
00412  */
00413 void DNW()
00414 {
00415   if (*cur_text == '\n')
00416         DCC();
00417   else
00418         move_next_word(DELETE);
00419 }
00420 
00421 /*
00422  * DPW() deletes the next word (as described in MP())
00423  */
00424 void DPW()
00425 {
00426   if (cur_text == cur_line->text)
00427         DPC();
00428   else
00429         move_previous_word(DELETE);
00430 }
00431 
00432 /*
00433  * Insert character `character' at current location.
00434  */
00435 void S(character)
00436 register char character;
00437 {
00438   static char buffer[2];
00439 
00440   buffer[0] = character;
00441 /* Insert the character */
00442   if (insert(cur_line, cur_text, buffer) == ERRORS)
00443         return;
00444 
00445 /* Fix screen */
00446   if (character == '\n') {
00447         set_cursor(0, y);
00448         if (y == screenmax) {           /* Can't use display */
00449                 line_print(cur_line);
00450                 (void) forward_scroll();
00451         }
00452         else {
00453                 reset(top_line, y);     /* Reset pointers */
00454                 display(0, y, cur_line, last_y - y);
00455         }
00456         move_to(0, (y == screenmax) ? y : y + 1);
00457   }
00458   else if (x + 1 == XBREAK)/* If line must be shifted, just call move_to*/
00459         move_to(x + 1, y);
00460   else {                         /* else display rest of line */
00461         put_line(cur_line, x, FALSE);
00462         move_to(x + 1, y);
00463   }
00464 }
00465 
00466 /*
00467  * CTL inserts a control-char at the current location. A message that this
00468  * function is called is displayed at the status line.
00469  */
00470 void CTL()
00471 {
00472   register char ctrl;
00473 
00474   status_line("Enter control character.", NIL_PTR);
00475   if ((ctrl = getchar()) >= '\01' && ctrl <= '\037') {
00476         S(ctrl);                /* Insert the char */
00477         clear_status();
00478   }
00479   else
00480         error ("Unknown control character", NIL_PTR);
00481 }
00482 
00483 /*
00484  * LIB insert a line at the current position and moves back to the end of
00485  * the previous line.
00486  */
00487 void LIB()
00488 {
00489   S('\n');                      /* Insert the line */
00490   UP();                         /* Move one line up */
00491   move_to(LINE_END, y);         /* Move to end of this line */
00492 }
00493 
00494 /*
00495  * Line_insert() inserts a new line with text pointed to by `string'.
00496  * It returns the address of the new line.
00497  */
00498 LINE *line_insert(line, string, len)
00499 register LINE *line;
00500 char *string;
00501 int len;
00502 {
00503   register LINE *new_line;
00504 
00505 /* Allocate space for LINE structure and text */
00506   new_line = install_line(string, len);
00507 
00508 /* Install the line into the double linked list */
00509   new_line->prev = line;
00510   new_line->next = line->next;
00511   line->next = new_line;
00512   new_line->next->prev = new_line;
00513 
00514 /* Increment nlines */
00515   nlines++;
00516 
00517   return new_line;
00518 }
00519 
00520 /*
00521  * Insert() insert the string `string' at the given line and location.
00522  */
00523 int insert(line, location, string)
00524 register LINE *line;
00525 char *location, *string;
00526 {
00527   register char *bufp = text_buffer;    /* Buffer for building line */
00528   register char *textp = line->text;
00529 
00530   if (length_of(textp) + length_of(string) >= MAX_CHARS) {
00531         error("Line too long", NIL_PTR);
00532         return ERRORS;
00533   }
00534 
00535   modified = TRUE;                      /* File has been modified */
00536 
00537 /* Copy part of line until `location' has been reached */
00538   while (textp != location)
00539         *bufp++ = *textp++;
00540   
00541 /* Insert string at this location */
00542   while (*string != '\0')
00543         *bufp++ = *string++;
00544   *bufp = '\0';
00545   
00546   if (*(string - 1) == '\n')            /* Insert a new line */
00547         (void) line_insert(line, location, length_of(location));
00548   else                                  /* Append last part of line */
00549         copy_string(bufp, location);
00550 
00551 /* Install the new text in this line */
00552   free_space(line->text);
00553   line->text = alloc(length_of(text_buffer) + 1);
00554   copy_string(line->text, text_buffer);
00555 
00556   return FINE;
00557 }
00558 
00559 /*
00560  * Line_delete() deletes the argument line out of the line list. The pointer to
00561  * the next line is returned.
00562  */
00563 LINE *line_delete(line)
00564 register LINE *line;
00565 {
00566   register LINE *next_line = line->next;
00567 
00568 /* Delete the line */
00569   line->prev->next = line->next;
00570   line->next->prev = line->prev;
00571 
00572 /* Free allocated space */
00573   free_space(line->text);
00574   free_space((char*)line);
00575 
00576 /* Decrement nlines */
00577   nlines--;
00578 
00579   return next_line;
00580 }
00581 
00582 /*
00583  * Delete() deletes all the characters (including newlines) between the
00584  * startposition and endposition and fixes the screen accordingly. It
00585  * returns the number of lines deleted.
00586  */
00587 void delete(start_line, start_textp, end_line, end_textp)
00588 register LINE *start_line;
00589 LINE *end_line;
00590 char *start_textp, *end_textp;
00591 {
00592   register char *textp = start_line->text;
00593   register char *bufp = text_buffer;    /* Storage for new line->text */
00594   LINE *line, *stop;
00595   int line_cnt = 0;                     /* Nr of lines deleted */
00596   int count = 0;
00597   int shift = 0;                                /* Used in shift calculation */
00598   int nx = x;
00599 
00600   modified = TRUE;                      /* File has been modified */
00601 
00602 /* Set up new line. Copy first part of start line until start_position. */
00603   while (textp < start_textp) {
00604         *bufp++ = *textp++;
00605         count++;
00606   }
00607 
00608 /* Check if line doesn't exceed MAX_CHARS */
00609   if (count + length_of(end_textp) >= MAX_CHARS) {
00610         error("Line too long", NIL_PTR);
00611         return;
00612   }
00613 
00614 /* Copy last part of end_line if end_line is not tail */
00615   copy_string(bufp, (end_textp != NIL_PTR) ? end_textp : "\n");
00616 
00617 /* Delete all lines between start and end_position (including end_line) */
00618   line = start_line->next;
00619   stop = end_line->next;
00620   while (line != stop && line != tail) {
00621         line = line_delete(line);
00622         line_cnt++;
00623   }
00624 
00625 /* Check if last line of file should be deleted */
00626   if (end_textp == NIL_PTR && length_of(start_line->text) == 1 && nlines > 1) {
00627         start_line = start_line->prev;
00628         (void) line_delete(start_line->next);
00629         line_cnt++;
00630   }
00631   else {        /* Install new text */
00632         free_space(start_line->text);
00633         start_line->text = alloc(length_of(text_buffer) + 1);
00634         copy_string(start_line->text, text_buffer);
00635   }
00636 
00637 /* Fix screen. First check if line is shifted. Perhaps we should shift it back*/
00638   if (get_shift(start_line->shift_count)) {
00639         shift = (XBREAK - count_chars(start_line)) / SHIFT_SIZE;
00640         if (shift > 0) {                /* Shift line `shift' back */
00641                 if (shift >= get_shift(start_line->shift_count))
00642                         start_line->shift_count = 0;
00643                 else
00644                         start_line->shift_count -= shift;
00645                 nx += shift * SHIFT_SIZE;/* Reset x value */
00646         }
00647   }
00648 
00649   if (line_cnt == 0) {              /* Check if only one line changed */
00650         if (shift > 0) {            /* Reprint whole line */
00651                 set_cursor(0, y);
00652                 line_print(start_line);
00653         }
00654         else {                      /* Just display last part of line */
00655                 set_cursor(x, y);
00656                 put_line(start_line, x, TRUE);
00657         }
00658         move_to(nx, y);    /* Reset cur_text */
00659         return;
00660   }
00661 
00662   shift = last_y;          /* Save value */
00663   reset(top_line, y);
00664   display(0, y, start_line, shift - y);
00665   move_to((line_cnt == 1) ? nx : 0, y);
00666 }
00667 
00668 /*  ========================================================================  *
00669  *                              Yank Commands                                 * 
00670  *  ========================================================================  */
00671 
00672 LINE *mark_line;                        /* For marking position. */
00673 char *mark_text;
00674 int lines_saved;                        /* Nr of lines in buffer */
00675 
00676 /*
00677  * PT() inserts the buffer at the current location.
00678  */
00679 void PT()
00680 {
00681   register int fd;              /* File descriptor for buffer */
00682 
00683   if ((fd = scratch_file(READ)) == ERRORS)
00684         error("Buffer is empty.", NIL_PTR);
00685   else {
00686         file_insert(fd, FALSE);/* Insert the buffer */
00687         (void) close(fd);
00688   }
00689 }
00690 
00691 /*
00692  * IF() prompt for a filename and inserts the file at the current location 
00693  * in the file.
00694  */
00695 void IF()
00696 {
00697   register int fd;              /* File descriptor of file */
00698   char name[LINE_LEN];          /* Buffer for file name */
00699 
00700 /* Get the file name */
00701   if (get_file("Get and insert file:", name) != FINE)
00702         return;
00703   
00704   if ((fd = open(name, 0)) < 0)
00705         error("Cannot open ", name);
00706   else {
00707         file_insert(fd, TRUE);  /* Insert the file */
00708         (void) close(fd);
00709   }
00710 }
00711 
00712 /*
00713  * File_insert() inserts a an opened file (as given by filedescriptor fd)
00714  * at the current location.
00715  */
00716 void file_insert(fd, old_pos)
00717 int fd;
00718 FLAG old_pos;
00719 {
00720   char line_buffer[MAX_CHARS];          /* Buffer for next line */
00721   register LINE *line = cur_line;
00722   register int line_count = nlines;     /* Nr of lines inserted */
00723   LINE *page = cur_line;
00724   int ret = ERRORS;
00725   
00726 /* Get the first piece of text (might be ended with a '\n') from fd */
00727   if (get_line(fd, line_buffer) == ERRORS)
00728         return;                         /* Empty file */
00729 
00730 /* Insert this text at the current location. */
00731   if (insert(line, cur_text, line_buffer) == ERRORS)
00732         return;
00733 
00734 /* Repeat getting lines (and inserting lines) until EOF is reached */
00735   while ((ret = get_line(fd, line_buffer)) != ERRORS && ret != NO_LINE)
00736         line = line_insert(line, line_buffer, ret);
00737   
00738   if (ret == NO_LINE) {         /* Last line read not ended by a '\n' */
00739         line = line->next;
00740         (void) insert(line, line->text, line_buffer);
00741   }
00742 
00743 /* Calculate nr of lines added */
00744   line_count = nlines - line_count;
00745 
00746 /* Fix the screen */
00747   if (line_count == 0) {                /* Only one line changed */
00748         set_cursor(0, y);
00749         line_print(line);
00750         move_to((old_pos == TRUE) ? x : x + length_of(line_buffer), y);
00751   }
00752   else {                                /* Several lines changed */
00753         reset(top_line, y);     /* Reset pointers */
00754         while (page != line && page != bot_line->next)
00755                 page = page->next;
00756         if (page != bot_line->next || old_pos == TRUE)
00757                 display(0, y, cur_line, screenmax - y);
00758         if (old_pos == TRUE)
00759                 move_to(x, y);
00760         else if (ret == NO_LINE)
00761                 move_to(length_of(line_buffer), find_y(line));
00762         else 
00763                 move_to(0, find_y(line->next));
00764   }
00765 
00766 /* If nr of added line >= REPORT, print the count */
00767   if (line_count >= REPORT)
00768         status_line(num_out((long) line_count), " lines added.");
00769 }
00770 
00771 /*
00772  * WB() writes the buffer (yank_file) into another file, which
00773  * is prompted for.
00774  */
00775 void WB()
00776 {
00777   register int new_fd;          /* Filedescriptor to copy file */
00778   int yank_fd;                  /* Filedescriptor to buffer */
00779   register int cnt;             /* Count check for read/write */
00780   int ret = 0;                  /* Error check for write */
00781   char file[LINE_LEN];          /* Output file */
00782   
00783 /* Checkout the buffer */
00784   if ((yank_fd = scratch_file(READ)) == ERRORS) {
00785         error("Buffer is empty.", NIL_PTR);
00786         return;
00787   }
00788 
00789 /* Get file name */
00790   if (get_file("Write buffer to file:", file) != FINE)
00791         return;
00792   
00793 /* Creat the new file */
00794   if ((new_fd = creat(file, 0644)) < 0) {
00795         error("Cannot create ", file);
00796         return;
00797   }
00798 
00799   status_line("Writing ", file);
00800   
00801 /* Copy buffer into file */
00802   while ((cnt = read(yank_fd, text_buffer, sizeof(text_buffer))) > 0)
00803         if (write(new_fd, text_buffer, cnt) != cnt) {
00804                 bad_write(new_fd);
00805                 ret = ERRORS;
00806                 break;
00807         }
00808 
00809 /* Clean up open files and status_line */
00810   (void) close(new_fd);
00811   (void) close(yank_fd);
00812 
00813   if (ret != ERRORS)                    /* Bad write */
00814         file_status("Wrote", chars_saved, file, lines_saved, TRUE, FALSE);
00815 }
00816 
00817 /*
00818  * MA sets mark_line (mark_text) to the current line (text pointer). 
00819  */
00820 void MA()
00821 {
00822   mark_line = cur_line;
00823   mark_text = cur_text;
00824   status_line("Mark set", NIL_PTR);
00825 }
00826 
00827 /*
00828  * YA() puts the text between the marked position and the current
00829  * in the buffer.
00830  */
00831 void YA()
00832 {
00833   set_up(NO_DELETE);
00834 }
00835 
00836 /*
00837  * DT() is essentially the same as YA(), but in DT() the text is deleted.
00838  */
00839 void DT()
00840 {
00841   set_up(DELETE);
00842 }
00843 
00844 /*
00845  * Set_up is an interface to the actual yank. It calls checkmark () to check
00846  * if the marked position is still valid. If it is, yank is called with the
00847  * arguments in the right order.
00848  */
00849 void set_up(remove)
00850 FLAG remove;                            /* DELETE if text should be deleted */
00851 {
00852   switch (checkmark()) {
00853         case NOT_VALID :
00854                 error("Mark not set.", NIL_PTR);
00855                 return;
00856         case SMALLER :
00857                 yank(mark_line, mark_text, cur_line, cur_text, remove);
00858                 break;
00859         case BIGGER :
00860                 yank(cur_line, cur_text, mark_line, mark_text, remove);
00861                 break;
00862         case SAME :             /* Ignore stupid behaviour */
00863                 yank_status = EMPTY;
00864                 chars_saved = 0L;
00865                 status_line("0 characters saved in buffer.", NIL_PTR);
00866                 break;
00867   }
00868 }
00869 
00870 /*
00871  * Check_mark() checks if mark_line and mark_text are still valid pointers. If
00872  * they are it returns SMALLER if the marked position is before the current,
00873  * BIGGER if it isn't or SAME if somebody didn't get the point.
00874  * NOT_VALID is returned when mark_line and/or mark_text are no longer valid.
00875  * Legal() checks if mark_text is valid on the mark_line.
00876  */
00877 FLAG checkmark()
00878 {
00879   register LINE *line;
00880   FLAG cur_seen = FALSE;
00881 
00882 /* Special case: check is mark_line and cur_line are the same. */
00883   if (mark_line == cur_line) {
00884         if (mark_text == cur_text)      /* Even same place */
00885                 return SAME;
00886         if (legal() == ERRORS)          /* mark_text out of range */
00887                 return NOT_VALID;
00888         return (mark_text < cur_text) ? SMALLER : BIGGER;
00889   }
00890 
00891 /* Start looking for mark_line in the line structure */
00892   for (line = header->next; line != tail; line = line->next) {
00893         if (line == cur_line)
00894                 cur_seen = TRUE;
00895         else if (line == mark_line)
00896                 break;
00897   }
00898 
00899 /* If we found mark_line (line != tail) check for legality of mark_text */
00900   if (line == tail || legal() == ERRORS)
00901         return NOT_VALID;
00902 
00903 /* cur_seen is TRUE if cur_line is before mark_line */
00904   return (cur_seen == TRUE) ? BIGGER : SMALLER;
00905 }
00906 
00907 /*
00908  * Legal() checks if mark_text is still a valid pointer.
00909  */
00910 int legal()
00911 {
00912   register char *textp = mark_line->text;
00913 
00914 /* Locate mark_text on mark_line */
00915   while (textp != mark_text && *textp++ != '\0')
00916         ;
00917   return (*textp == '\0') ? ERRORS : FINE;
00918 }
00919 
00920 /*
00921  * Yank puts all the text between start_position and end_position into
00922  * the buffer.
00923  * The caller must check that the arguments to yank() are valid. (E.g. in
00924  * the right order)
00925  */
00926 void yank(start_line, start_textp, end_line, end_textp, remove)
00927 LINE *start_line, *end_line;
00928 char *start_textp, *end_textp;
00929 FLAG remove;                            /* DELETE if text should be deleted */
00930 {
00931   register LINE *line = start_line;
00932   register char *textp = start_textp;
00933   int fd;
00934 
00935 /* Creat file to hold buffer */
00936   if ((fd = scratch_file(WRITE)) == ERRORS)
00937         return;
00938   
00939   chars_saved = 0L;
00940   lines_saved = 0;
00941   status_line("Saving text.", NIL_PTR);
00942 
00943 /* Keep writing chars until the end_location is reached. */
00944   while (textp != end_textp) {
00945         if (write_char(fd, *textp) == ERRORS) {
00946                 (void) close(fd);
00947                 return;
00948         }
00949         if (*textp++ == '\n') { /* Move to the next line */
00950                 line = line->next;
00951                 textp = line->text;
00952                 lines_saved++;
00953         }
00954         chars_saved++;
00955   }
00956 
00957 /* Flush the I/O buffer and close file */
00958   if (flush_buffer(fd) == ERRORS) {
00959         (void) close(fd);
00960         return;
00961   }
00962   (void) close(fd);
00963   yank_status = VALID;
00964 
00965 /*
00966  * Check if the text should be deleted as well. If it should, the following
00967  * hack is used to save a lot of code. First move back to the start_position.
00968  * (This might be the location we're on now!) and them delete the text.
00969  * It might be a bit confusing the first time somebody uses it.
00970  * Delete() will fix the screen.
00971  */
00972   if (remove == DELETE) {
00973         move_to(find_x(start_line, start_textp), find_y(start_line));
00974         delete(start_line, start_textp, end_line, end_textp);
00975   }
00976 
00977   status_line(num_out(chars_saved), " characters saved in buffer.");
00978 }
00979 
00980 /*
00981  * Scratch_file() creates a uniq file in /usr/tmp. If the file couldn't
00982  * be created other combinations of files are tried until a maximum
00983  * of MAXTRAILS times. After MAXTRAILS times, an error message is given
00984  * and ERRORS is returned.
00985  */
00986 
00987 #define MAXTRAILS       26
00988 
00989 int scratch_file(mode)
00990 FLAG mode;                              /* Can be READ or WRITE permission */
00991 {
00992   static int trials = 0;                /* Keep track of trails */
00993   register char *y_ptr, *n_ptr;
00994   int fd;                               /* Filedescriptor to buffer */
00995 
00996 /* If yank_status == NOT_VALID, scratch_file is called for the first time */
00997   if (yank_status == NOT_VALID && mode == WRITE) { /* Create new file */
00998         /* Generate file name. */
00999         y_ptr = &yank_file[11];
01000         n_ptr = num_out((long) getpid());
01001         while ((*y_ptr = *n_ptr++) != '\0')
01002                 y_ptr++;
01003         *y_ptr++ = 'a' + trials;
01004         *y_ptr = '\0';
01005         /* Check file existence */
01006         if (access(yank_file, 0) == 0 || (fd = creat(yank_file, 0644)) < 0) {
01007                 if (trials++ >= MAXTRAILS) {
01008                         error("Unable to creat scratchfile.", NIL_PTR);
01009                         return ERRORS;
01010                 }
01011                 else
01012                         return scratch_file(mode);/* Have another go */
01013         }
01014   }
01015   else if ((mode == READ && (fd = open(yank_file, 0)) < 0) ||
01016                         (mode == WRITE && (fd = creat(yank_file, 0644)) < 0)) {
01017         yank_status = NOT_VALID;
01018         return ERRORS;
01019   }
01020 
01021   clear_buffer();
01022   return fd;
01023 }
01024 
01025 /*  ========================================================================  *
01026  *                              Search Routines                               * 
01027  *  ========================================================================  */
01028 
01029 /*
01030  * A regular expression consists of a sequence of:
01031  *      1. A normal character matching that character.
01032  *      2. A . matching any character.
01033  *      3. A ^ matching the begin of a line.
01034  *      4. A $ (as last character of the pattern) mathing the end of a line.
01035  *      5. A <character> matching <character>.
01036  *      6. A number of characters enclosed in [] pairs matching any of these
01037  *         characters. A list of characters can be indicated by a '-'. So
01038  *         [a-z] matches any letter of the alphabet. If the first character
01039  *         after the '[' is a '^' then the set is negated (matching none of
01040  *         the characters). 
01041  *         A ']', '^' or '-' can be escaped by putting a '\' in front of it.
01042  *      7. If one of the expressions as described in 1-6 is followed by a
01043  *         '*' than that expressions matches a sequence of 0 or more of
01044  *         that expression.
01045  */
01046 
01047 char typed_expression[LINE_LEN];        /* Holds previous expr. */
01048 
01049 /*
01050  * SF searches forward for an expression.
01051  */
01052 void SF()
01053 {
01054   search("Search forward:", FORWARD);
01055 }
01056 
01057 /*
01058  * SF searches backwards for an expression.
01059  */
01060 void SR()
01061 {
01062   search("Search reverse:", REVERSE);
01063 }
01064 
01065 /*
01066  * Get_expression() prompts for an expression. If just a return is typed, the
01067  * old expression is used. If the expression changed, compile() is called and
01068  * the returning REGEX structure is returned. It returns NIL_REG upon error.
01069  * The save flag indicates whether the expression should be appended at the
01070  * message pointer.
01071  */
01072 REGEX *get_expression(message)
01073 char *message;
01074 {
01075   static REGEX program;                 /* Program of expression */
01076   char exp_buf[LINE_LEN];                       /* Buffer for new expr. */
01077 
01078   if (get_string(message, exp_buf, FALSE) == ERRORS)
01079         return NIL_REG;
01080   
01081   if (exp_buf[0] == '\0' && typed_expression[0] == '\0') {
01082         error("No previous expression.", NIL_PTR);
01083         return NIL_REG;
01084   }
01085 
01086   if (exp_buf[0] != '\0') {             /* A new expr. is typed */
01087         copy_string(typed_expression, exp_buf);/* Save expr. */
01088         compile(exp_buf, &program);     /* Compile new expression */
01089   }
01090 
01091   if (program.status == REG_ERROR) {    /* Error during compiling */
01092         error(program.result.err_mess, NIL_PTR);
01093         return NIL_REG;
01094   }
01095   return &program;
01096 }
01097 
01098 /*
01099  * GR() a replaces all matches from the current position until the end
01100  * of the file.
01101  */
01102 void GR()
01103 {
01104   change("Global replace:", VALID);
01105 }
01106 
01107 /*
01108  * LR() replaces all matches on the current line.
01109  */
01110 void LR()
01111 {
01112   change("Line replace:", NOT_VALID);
01113 }
01114 
01115 /*
01116  * Change() prompts for an expression and a substitution pattern and changes
01117  * all matches of the expression into the substitution. change() start looking
01118  * for expressions at the current line and continues until the end of the file
01119  * if the FLAG file is VALID.
01120  */
01121 void change(message, file)
01122 char *message;                          /* Message to prompt for expression */
01123 FLAG file;
01124 {
01125   char mess_buf[LINE_LEN];      /* Buffer to hold message */
01126   char replacement[LINE_LEN];   /* Buffer to hold subst. pattern */
01127   REGEX *program;                       /* Program resulting from compilation */
01128   register LINE *line = cur_line;
01129   register char *textp;
01130   long lines = 0L;              /* Nr of lines on which subs occurred */
01131   long subs = 0L;                       /* Nr of subs made */
01132   int page = y;                 /* Index to check if line is on screen*/
01133 
01134 /* Save message and get expression */
01135   copy_string(mess_buf, message);
01136   if ((program = get_expression(mess_buf)) == NIL_REG)
01137         return;
01138   
01139 /* Get substitution pattern */
01140   build_string(mess_buf, "%s %s by:", mess_buf, typed_expression);
01141   if (get_string(mess_buf, replacement, FALSE) == ERRORS)
01142         return;
01143   
01144   set_cursor(0, ymax);
01145   flush();
01146 /* Substitute until end of file */
01147   do {
01148         if (line_check(program, line->text, FORWARD)) {
01149                 lines++;
01150                 /* Repeat sub. on this line as long as we find a match*/
01151                 do {
01152                         subs++; /* Increment subs */
01153                         if ((textp = substitute(line, program,replacement))
01154                                                                      == NIL_PTR)
01155                                 return; /* Line too long */
01156                 } while ((program->status & BEGIN_LINE) != BEGIN_LINE &&
01157                          (program->status & END_LINE) != END_LINE &&
01158                                           line_check(program, textp, FORWARD));
01159                 /* Check to see if we can print the result */
01160                 if (page <= screenmax) {
01161                         set_cursor(0, page);
01162                         line_print(line);
01163                 }
01164         }
01165         if (page <= screenmax)
01166                 page++;
01167         line = line->next;
01168   } while (line != tail && file == VALID && quit == FALSE);
01169 
01170   copy_string(mess_buf, (quit == TRUE) ? "(Aborted) " : "");
01171 /* Fix the status line */
01172   if (subs == 0L && quit == FALSE)
01173         error("Pattern not found.", NIL_PTR);
01174   else if (lines >= REPORT || quit == TRUE) {
01175         build_string(mess_buf, "%s %D substitutions on %D lines.", mess_buf,
01176                                                                    subs, lines);
01177         status_line(mess_buf, NIL_PTR);
01178   }
01179   else if (file == NOT_VALID && subs >= REPORT)
01180         status_line(num_out(subs), " substitutions.");
01181   else
01182         clear_status();
01183   move_to (x, y);
01184 }
01185 
01186 /*
01187  * Substitute() replaces the match on this line by the substitute pattern
01188  * as indicated by the program. Every '&' in the replacement is replaced by 
01189  * the original match. A \ in the replacement escapes the next character.
01190  */
01191 char *substitute(line, program, replacement)
01192 LINE *line;
01193 REGEX *program;
01194 char *replacement;              /* Contains replacement pattern */
01195 {
01196   register char *textp = text_buffer;
01197   register char *subp = replacement;
01198   char *linep = line->text;
01199   char *amp;
01200 
01201   modified = TRUE;
01202 
01203 /* Copy part of line until the beginning of the match */
01204   while (linep != program->start_ptr)
01205         *textp++ = *linep++;
01206   
01207 /*
01208  * Replace the match by the substitution pattern. Each occurrence of '&' is
01209  * replaced by the original match. A \ escapes the next character.
01210  */
01211   while (*subp != '\0' && textp < &text_buffer[MAX_CHARS]) {
01212         if (*subp == '&') {             /* Replace the original match */
01213                 amp = program->start_ptr;
01214                 while (amp < program->end_ptr && textp<&text_buffer[MAX_CHARS])
01215                         *textp++ = *amp++;
01216                 subp++;
01217         }
01218         else {
01219                 if (*subp == '\\' && *(subp + 1) != '\0')
01220                         subp++;
01221                 *textp++ = *subp++;
01222         }
01223   }
01224 
01225 /* Check for line length not exceeding MAX_CHARS */
01226   if (length_of(text_buffer) + length_of(program->end_ptr) >= MAX_CHARS) {
01227         error("Substitution result: line too big", NIL_PTR);
01228         return NIL_PTR;
01229   }
01230 
01231 /* Append last part of line to the new build line */
01232   copy_string(textp, program->end_ptr);
01233 
01234 /* Free old line and install new one */
01235   free_space(line->text);
01236   line->text = alloc(length_of(text_buffer) + 1);
01237   copy_string(line->text, text_buffer);
01238 
01239   return(line->text + (textp - text_buffer));
01240 }
01241 
01242 /*
01243  * Search() calls get_expression to fetch the expression. If this went well,
01244  * the function match() is called which returns the line with the next match.
01245  * If this line is the NIL_LINE, it means that a match could not be found.
01246  * Find_x() and find_y() display the right page on the screen, and return
01247  * the right coordinates for x and y. These coordinates are passed to move_to()
01248  */
01249 void search(message, method)
01250 char *message;
01251 FLAG method;
01252 {
01253   register REGEX *program;
01254   register LINE *match_line;
01255 
01256 /* Get the expression */
01257   if ((program = get_expression(message)) == NIL_REG)
01258         return;
01259 
01260   set_cursor(0, ymax);
01261   flush();
01262 /* Find the match */
01263   if ((match_line = match(program, cur_text, method)) == NIL_LINE) {
01264         if (quit == TRUE)
01265                 status_line("Aborted", NIL_PTR);
01266         else
01267                 status_line("Pattern not found.", NIL_PTR);
01268         return;
01269   }
01270 
01271   move(0, program->start_ptr, find_y(match_line));
01272   clear_status();
01273 }
01274 
01275 /*
01276  * find_y() checks if the matched line is on the current page.  If it is, it
01277  * returns the new y coordinate, else it displays the correct page with the
01278  * matched line in the middle and returns the new y value;
01279  */
01280 int find_y(match_line)
01281 LINE *match_line;
01282 {
01283   register LINE *line;
01284   register int count = 0;
01285 
01286 /* Check if match_line is on the same page as currently displayed. */
01287   for (line = top_line; line != match_line && line != bot_line->next;
01288                                                       line = line->next)
01289         count++;
01290   if (line != bot_line->next)
01291         return count;
01292 
01293 /* Display new page, with match_line in center. */
01294   if ((line = proceed(match_line, -(screenmax >> 1))) == header) {
01295   /* Can't display in the middle. Make first line of file top_line */
01296         count = 0;
01297         for (line = header->next; line != match_line; line = line->next)
01298                 count++;
01299         line = header->next;
01300   }
01301   else  /* New page is displayed. Set cursor to middle of page */
01302         count = screenmax >> 1;
01303 
01304 /* Reset pointers and redraw the screen */
01305   reset(line, 0);
01306   RD();
01307 
01308   return count;
01309 }
01310 
01311 /* Opcodes for characters */
01312 #define NORMAL          0x0200
01313 #define DOT             0x0400
01314 #define EOLN            0x0800
01315 #define STAR            0x1000
01316 #define BRACKET         0x2000
01317 #define NEGATE          0x0100
01318 #define DONE            0x4000
01319 
01320 /* Mask for opcodes and characters */
01321 #define LOW_BYTE        0x00FF
01322 #define HIGH_BYTE       0xFF00
01323 
01324 /* Previous is the contents of the previous address (ptr) points to */
01325 #define previous(ptr)           (*((ptr) - 1))
01326 
01327 /* Buffer to store outcome of compilation */
01328 int exp_buffer[BLOCK_SIZE];
01329 
01330 /* Errors often used */
01331 char *too_long = "Regular expression too long";
01332 
01333 /*
01334  * Reg_error() is called by compile() is something went wrong. It set the
01335  * status of the structure to error, and assigns the error field of the union.
01336  */
01337 #define reg_error(str)  program->status = REG_ERROR, \
01338                                         program->result.err_mess = (str)
01339 /*
01340  * Finished() is called when everything went right during compilation. It
01341  * allocates space for the expression, and copies the expression buffer into
01342  * this field.
01343  */
01344 void finished(program, last_exp)
01345 register REGEX *program;
01346 int *last_exp;
01347 {
01348   register int length = (last_exp - exp_buffer) * sizeof(int);
01349 
01350 /* Allocate space */
01351   program->result.expression = (int *) alloc(length);
01352 /* Copy expression. (expression consists of ints!) */
01353   bcopy(exp_buffer, program->result.expression, length);
01354 }
01355 
01356 /*
01357  * Compile compiles the pattern into a more comprehensible form and returns a 
01358  * REGEX structure. If something went wrong, the status field of the structure
01359  * is set to REG_ERROR and an error message is set into the err_mess field of
01360  * the union. If all went well the expression is saved and the expression
01361  * pointer is set to the saved (and compiled) expression.
01362  */
01363 void compile(pattern, program)
01364 register char *pattern;                 /* Pointer to pattern */
01365 REGEX *program;
01366 {
01367   register int *expression = exp_buffer;
01368   int *prev_char;                       /* Pointer to previous compiled atom */
01369   int *acct_field;              /* Pointer to last BRACKET start */
01370   FLAG negate;                  /* Negate flag for BRACKET */
01371   char low_char;                        /* Index for chars in BRACKET */
01372   char c;
01373 
01374 /* Check for begin of line */
01375   if (*pattern == '^') {
01376         program->status = BEGIN_LINE;
01377         pattern++;
01378   }
01379   else {
01380         program->status = 0;
01381 /* If the first character is a '*' we have to assign it here. */
01382         if (*pattern == '*') {
01383                 *expression++ = '*' + NORMAL;
01384                 pattern++;
01385         }
01386   }
01387 
01388   for (; ;) {
01389         switch (c = *pattern++) {
01390         case '.' :
01391                 *expression++ = DOT;
01392                 break;
01393         case '$' :
01394                 /*
01395                  * Only means EOLN if it is the last char of the pattern
01396                  */
01397                 if (*pattern == '\0') {
01398                         *expression++ = EOLN | DONE;
01399                         program->status |= END_LINE;
01400                         finished(program, expression);
01401                         return;
01402                 }
01403                 else
01404                         *expression++ = NORMAL + '$';
01405                 break;
01406         case '\0' :
01407                 *expression++ = DONE;
01408                 finished(program, expression);
01409                 return;
01410         case '\\' :
01411                 /* If last char, it must! mean a normal '\' */
01412                 if (*pattern == '\0')
01413                         *expression++ = NORMAL + '\\';
01414                 else
01415                         *expression++ = NORMAL + *pattern++;
01416                 break;
01417         case '*' :
01418                 /*
01419                  * If the previous expression was a [] find out the
01420                  * begin of the list, and adjust the opcode.
01421                  */
01422                 prev_char = expression - 1;
01423                 if (*prev_char & BRACKET)
01424                         *(expression - (*acct_field & LOW_BYTE))|= STAR;
01425                 else
01426                         *prev_char |= STAR;
01427                 break;
01428         case '[' :
01429                 /*
01430                  * First field in expression gives information about
01431                  * the list.
01432                  * The opcode consists of BRACKET and if necessary
01433                  * NEGATE to indicate that the list should be negated
01434                  * and/or STAR to indicate a number of sequence of this 
01435                  * list.
01436                  * The lower byte contains the length of the list.
01437                  */
01438                 acct_field = expression++;
01439                 if (*pattern == '^') {  /* List must be negated */
01440                         pattern++;
01441                         negate = TRUE;
01442                 }
01443                 else
01444                         negate = FALSE;
01445                 while (*pattern != ']') {
01446                         if (*pattern == '\0') {
01447                                 reg_error("Missing ]");
01448                                 return;
01449                         }
01450                         if (*pattern == '\\')
01451                                 pattern++;
01452                         *expression++ = *pattern++;
01453                         if (*pattern == '-') {
01454                                                 /* Make list of chars */
01455                                 low_char = previous(pattern);
01456                                 pattern++;      /* Skip '-' */
01457                                 if (low_char++ > *pattern) {
01458                                         reg_error("Bad range in [a-z]");
01459                                         return;
01460                                 }
01461                                 /* Build list */
01462                                 while (low_char <= *pattern)
01463                                         *expression++ = low_char++;
01464                                 pattern++;
01465                         }
01466                         if (expression >= &exp_buffer[BLOCK_SIZE]) {
01467                                 reg_error(too_long);
01468                                 return;
01469                         }
01470                 }
01471                 pattern++;                      /* Skip ']' */
01472                 /* Assign length of list in acct field */
01473                 if ((*acct_field = (expression - acct_field)) == 1) {
01474                         reg_error("Empty []");
01475                         return;
01476                 }
01477                 /* Assign negate and bracket field */
01478                 *acct_field |= BRACKET;
01479                 if (negate == TRUE)
01480                         *acct_field |= NEGATE;
01481                 /*
01482                  * Add BRACKET to opcode of last char in field because
01483                  * a '*' may be following the list.
01484                  */
01485                 previous(expression) |= BRACKET;
01486                 break;
01487         default :
01488                 *expression++ = c + NORMAL;
01489         }
01490         if (expression == &exp_buffer[BLOCK_SIZE]) {
01491                 reg_error(too_long);
01492                 return;
01493         }
01494   }
01495   /* NOTREACHED */
01496 }
01497 
01498 /*
01499  * Match gets as argument the program, pointer to place in current line to 
01500  * start from and the method to search for (either FORWARD or REVERSE).
01501  * Match() will look through the whole file until a match is found.
01502  * NIL_LINE is returned if no match could be found.
01503  */
01504 LINE *match(program, string, method)
01505 REGEX *program;
01506 char *string;
01507 register FLAG method;
01508 {
01509   register LINE *line = cur_line;
01510   char old_char;                                /* For saving chars */
01511 
01512 /* Corrupted program */
01513   if (program->status == REG_ERROR)
01514         return NIL_LINE;
01515 
01516 /* Check part of text first */
01517   if (!(program->status & BEGIN_LINE)) {
01518         if (method == FORWARD) {
01519                 if (line_check(program, string + 1, method) == MATCH)
01520                         return cur_line;        /* Match found */
01521         }
01522         else if (!(program->status & END_LINE)) {
01523                 old_char = *string;     /* Save char and */
01524                 *string = '\n';         /* Assign '\n' for line_check */
01525                 if (line_check(program, line->text, method) == MATCH) {
01526                         *string = old_char; /* Restore char */
01527                         return cur_line;    /* Found match */
01528                 }
01529                 *string = old_char;     /* No match, but restore char */
01530         }
01531   }
01532 
01533 /* No match in last (or first) part of line. Check out rest of file */
01534   do {
01535         line = (method == FORWARD) ? line->next : line->prev;
01536         if (line->text == NIL_PTR)      /* Header/tail */
01537                 continue;
01538         if (line_check(program, line->text, method) == MATCH)
01539                 return line;
01540   } while (line != cur_line && quit == FALSE);
01541 
01542 /* No match found. */
01543   return NIL_LINE;
01544 }
01545 
01546 /*
01547  * Line_check() checks the line (or rather string) for a match. Method
01548  * indicates FORWARD or REVERSE search. It scans through the whole string
01549  * until a match is found, or the end of the string is reached.
01550  */
01551 int line_check(program, string, method)
01552 register REGEX *program;
01553 char *string;
01554 FLAG method;
01555 {
01556   register char *textp = string;
01557 
01558 /* Assign start_ptr field. We might find a match right away! */
01559   program->start_ptr = textp;
01560 
01561 /* If the match must be anchored, just check the string. */
01562   if (program->status & BEGIN_LINE)
01563         return check_string(program, string, NIL_INT);
01564   
01565   if (method == REVERSE) {
01566         /* First move to the end of the string */
01567         for (textp = string; *textp != '\n'; textp++)
01568                 ;
01569         /* Start checking string until the begin of the string is met */
01570         while (textp >= string) {
01571                 program->start_ptr = textp;
01572                 if (check_string(program, textp--, NIL_INT))
01573                         return MATCH;
01574         }
01575   }
01576   else {
01577         /* Move through the string until the end of is found */
01578         while (quit == FALSE && *textp != '\0') {
01579                 program->start_ptr = textp;
01580                 if (check_string(program, textp, NIL_INT))
01581                         return MATCH;
01582                 if (*textp == '\n')
01583                         break;
01584                 textp++;
01585         }
01586   }
01587 
01588   return NO_MATCH;
01589 }
01590 
01591 /*
01592  * Check() checks of a match can be found in the given string. Whenever a STAR
01593  * is found during matching, then the begin position of the string is marked
01594  * and the maximum number of matches is performed. Then the function star()
01595  * is called which starts to finish the match from this position of the string
01596  * (and expression). Check() return MATCH for a match, NO_MATCH is the string 
01597  * couldn't be matched or REG_ERROR for an illegal opcode in expression.
01598  */
01599 int check_string(program, string, expression)
01600 REGEX *program;
01601 register char *string;
01602 int *expression;
01603 {
01604   register int opcode;          /* Holds opcode of next expr. atom */
01605   char c;                               /* Char that must be matched */
01606   char *mark;                   /* For marking position */
01607   int star_fl;                  /* A star has been born */
01608 
01609   if (expression == NIL_INT)
01610         expression = program->result.expression;
01611 
01612 /* Loop until end of string or end of expression */
01613   while (quit == FALSE && !(*expression & DONE) &&
01614                                            *string != '\0' && *string != '\n') {
01615         c = *expression & LOW_BYTE;       /* Extract match char */
01616         opcode = *expression & HIGH_BYTE; /* Extract opcode */
01617         if (star_fl = (opcode & STAR)) {  /* Check star occurrence */
01618                 opcode &= ~STAR;          /* Strip opcode */
01619                 mark = string;            /* Mark current position */
01620         }
01621         expression++;           /* Increment expr. */
01622         switch (opcode) {
01623         case NORMAL :
01624                 if (star_fl)
01625                         while (*string++ == c)  /* Skip all matches */
01626                                 ;
01627                 else if (*string++ != c)
01628                         return NO_MATCH;
01629                 break;
01630         case DOT :
01631                 string++;
01632                 if (star_fl)                    /* Skip to eoln */
01633                         while (*string != '\0' && *string++ != '\n')
01634                                 ;
01635                 break;
01636         case NEGATE | BRACKET:
01637         case BRACKET :
01638                 if (star_fl)
01639                         while (in_list(expression, *string++, c, opcode)
01640                                                                        == MATCH)
01641                                 ;
01642                 else if (in_list(expression, *string++, c, opcode) == NO_MATCH)
01643                         return NO_MATCH;
01644                 expression += c - 1;    /* Add length of list */
01645                 break;
01646         default :
01647                 panic("Corrupted program in check_string()");
01648         }
01649         if (star_fl) 
01650                 return star(program, mark, string, expression);
01651   }
01652   if (*expression & DONE) {
01653         program->end_ptr = string;      /* Match ends here */
01654         /*
01655          * We might have found a match. The last thing to do is check
01656          * whether a '$' was given at the end of the expression, or
01657          * the match was found on a null string. (E.g. [a-z]* always
01658          * matches) unless a ^ or $ was included in the pattern.
01659          */
01660         if ((*expression & EOLN) && *string != '\n' && *string != '\0')
01661                 return NO_MATCH;
01662         if (string == program->start_ptr && !(program->status & BEGIN_LINE)
01663                                          && !(*expression & EOLN))
01664                 return NO_MATCH;
01665         return MATCH;
01666   }
01667   return NO_MATCH;
01668 }
01669 
01670 /*
01671  * Star() calls check_string() to find out the longest match possible.
01672  * It searches backwards until the (in check_string()) marked position
01673  * is reached, or a match is found.
01674  */
01675 int star(program, end_position, string, expression)
01676 REGEX *program;
01677 register char *end_position;
01678 register char *string;
01679 int *expression;
01680 {
01681   do {
01682         string--;
01683         if (check_string(program, string, expression))
01684                 return MATCH;
01685   } while (string != end_position);
01686 
01687   return NO_MATCH;
01688 }
01689 
01690 /*
01691  * In_list() checks if the given character is in the list of []. If it is
01692  * it returns MATCH. if it isn't it returns NO_MATCH. These returns values
01693  * are reversed when the NEGATE field in the opcode is present.
01694  */
01695 int in_list(list, c, list_length, opcode)
01696 register int *list;
01697 char c;
01698 register int list_length;
01699 int opcode;
01700 {
01701   if (c == '\0' || c == '\n')   /* End of string, never matches */
01702         return NO_MATCH;
01703   while (list_length-- > 1) {   /* > 1, don't check acct_field */
01704         if ((*list & LOW_BYTE) == c)
01705                 return (opcode & NEGATE) ? NO_MATCH : MATCH;
01706         list++;
01707   }
01708   return (opcode & NEGATE) ? MATCH : NO_MATCH;
01709 }
01710 
01711 /*
01712  * Dummy_line() adds an empty line at the end of the file. This is sometimes
01713  * useful in combination with the EF and DN command in combination with the
01714  * Yank command set.
01715  */
01716 void dummy_line()
01717 {
01718         (void) line_insert(tail->prev, "\n", 1);
01719         tail->prev->shift_count = DUMMY;
01720         if (last_y != screenmax) {
01721                 last_y++;
01722                 bot_line = bot_line->next;
01723         }
01724 }

Generated on Fri Apr 14 22:57:01 2006 for minix by  doxygen 1.4.6