home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Der Mediaplex Sampler - Die 6 von Plex
/
6_v_plex.zip
/
6_v_plex
/
DISK6
/
OS_05
/
DTE.ZIP
/
FINDREP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-24
|
34KB
|
1,133 lines
/*
* Written by Douglas Thomson (1989/1990)
*
* This source code is released into the public domain.
*/
/*
* Name: dte - Doug's Text Editor program - find/replace module
* Purpose: This file contains the functions relating to finding text
* and replacing text.
* It also contains the code for moving the cursor to various
* other positions in the file.
* File: findrep.c
* Author: Douglas Thomson
* System: this file is intended to be system-independent
* Date: October 1, 1989
*/
#ifdef HPXL
#include "commonh"
#include "findreph"
#include "utilsh"
#else
#include "common.h"
#include "findrep.h"
#include "utils.h"
#endif
/*
* prototypes for all functions in this file
*/
int set_flags ARGS((char *flag_str, int *flags, int *count));
int get_flags ARGS((int lines));
int mystrcmp ARGS((char *s1, char *s2));
int mystrcmpi ARGS((char *s1, char *s2));
void on_screen ARGS((windows *window, text_ptr cursor, int last));
void do_replace ARGS((windows *window, text_ptr start));
void do_last ARGS((windows *window));
void find_string ARGS((windows *window));
void replace_string ARGS((windows *window));
void goto_prep ARGS((windows *window));
void goto_complete ARGS((windows *window, text_ptr cursor));
void goto_marker ARGS((windows *window, int n));
void goto_top_file ARGS((windows *window));
void goto_end_file ARGS((windows *window));
text_ptr scan_forward ARGS((text_ptr start, char *opp, char *target));
text_ptr scan_backward ARGS((text_ptr start, char *opp, char *target));
void match_pair ARGS((windows *window, int forward));
void goto_line ARGS((windows *window));
/*
* find and replace flags
*/
#define F_BACKWARD 0x01 /* search backwards through file */
#define F_GLOBAL 0x02 /* search entire file */
#define F_LOCAL 0x04 /* search only marked block */
#define F_AUTO 0x08 /* behave as if user always answered yes */
#define F_IGNORE 0x10 /* ignore case differences */
#define F_WORD 0x20 /* match whole words only */
#define F_MATCH 0x40 /* match the case of the text being replaced */
/*
* Name: set_flags
* Purpose: To set up find and replace flags.
* Date: October 1, 1989
* Passed: flag_str: the flags chosen by the user
* Returns: flags: the equivalent binary flags
* count: the repeat count embedded in the flags
* TRUE if flags were OK, FALSE otherwise
*/
int set_flags(flag_str, flags, count)
char *flag_str;
int *flags;
int *count;
{
char *p; /* used to scan through flag_str for flags */
/*
* start with no flags set, then add those chosen
*/
*flags = 0;
/*
* assume just a single find / replace required
*/
*count = 1;
/*
* scan flag_str to work out which flags were chosen
*/
for (p=flag_str; *p; ++p) {
if (isdigit(*p)) {
/*
* extract an embedded repeat count
*/
*count = atoi(p);
while (isdigit(*++p)) {
;
}
--p;
if (*count <= 0) {
error(WARNING, "bad repeat count: %d", *count);
*count = 1;
return FALSE;
}
}
else if ((*p = toupper(*p)) == 'B') {
*flags |= F_BACKWARD;
}
else if (*p == 'G') {
*flags |= F_GLOBAL;
*count = GLOB_COUNT;
}
else if (*p == 'L') {
*flags |= F_LOCAL;
*count = GLOB_COUNT;
}
else if (*p == 'N') {
*flags |= F_AUTO;
}
else if (*p == 'U') {
*flags |= F_IGNORE;
}
else if (*p == 'W') {
*flags |= F_WORD;
}
else if (*p == 'M') {
*flags |= F_MATCH;
}
else {
error(WARNING, "unknown flag: %c", *p);
return FALSE;
}
}
return TRUE;
}
/*
* Name: get_flags
* Purpose: To input find and replace flags.
* Date: October 1, 1989
* Passed: lines: no. of lines up from bottom of screen
* Returns: [g_status.flags]: binary flags set
* [g_status.flag_str]: flags as character string
* [g_status.search_count]: repeat find count
* OK if flags were entered, ERROR if user wanted to abort
*/
int get_flags(lines)
int lines;
{
char flag_str[MAX_COLS]; /* temporary copy of g_status.flag_str */
int flags; /* temporary copy of g_status.flags */
int count; /* temporary copy of g_status.count */
/*
* use the previous flags as the default
*/
strcpy(flag_str, g_status.flag_str);
/*
* keep on asking for flags until something acceptable is entered
*/
for (;;) {
if (get_name("Options (B,G,L,M,N,n,U,W): ", lines, flag_str) !=
OK) {
return ERROR;
}
if (set_flags(flag_str, &flags, &count)) {
break;
}
}
g_status.flags = flags;
g_status.search_count = count;
strcpy(g_status.flag_str, flag_str);
return OK;
}
/*
* Name: mystrcmp
* Purpose: To compare two strings up to the length of the first.
* Date: October 1, 1989
* Passed: s1: first string to compare
* s2: second string to compare
* Returns: 0 if strings match
* <0 if s1 < s2
* >0 if s1 > s2
*/
int mystrcmp(s1, s2)
char *s1;
char *s2;
{
for(;;) {
if (*s1 == '\0') {
return 0;
}
if (*s1 != *s2) {
return *s1 - *s2;
}
++s1;
++s2;
}
}
/*
* Name: mystrcmpi
* Purpose: To compare two strings up to the length of the first, ignoring
* case.
* Date: October 1, 1989
* Passed: s1: first string to compare
* s2: second string to compare
* Returns: 0 if strings match
* <0 if s1 < s2
* >0 if s1 > s2
*/
int mystrcmpi(s1, s2)
char *s1;
char *s2;
{
for(;;) {
if (*s1 == '\0') {
return 0;
}
if (tolower(*s1) != tolower(*s2)) {
return tolower(*s1) - tolower(*s2);
}
++s1;
++s2;
}
}
/*
* Name: on_screen
* Purpose: To move the cursor to a new position, without redrawing the
* window unless the new position is off the window.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
* cursor: the new target position in the text
* last: the last line considered to be on the screen (this
* only affects the bottom window)
*/
void on_screen(window, cursor, last)
windows *window;
text_ptr cursor;
int last;
{
text_ptr p; /* used to scan from current cursor towards new one */
int line; /* used to count screen line */
/*
* if this is the bottom window displayed, then some lines at the
* bottom may need to be reserved for messages.
* Otherwise, the last line available is simply the bottom line used
* for the window.
*/
last = min(g_display.nlines-last-1, window->bottom_line);
line = window->cline;
if (window->cursor >= cursor) {
/*
* new cursor position is above old one
*/
for (p=window->cursor; ; p--) {
if (*p == '\n') {
--line;
}
if (line < window->top_line) {
/*
* off top of screen, so place in middle of display
*/
window->cline = window->place_line;
/*
* now check that we are not wasting the top part of the
* screen
*/
line = window->cline;
for (p=cursor; ; p--) {
if (*p == '\n') {
--line;
}
if (*p == '\0') {
window->cline -= line - window->top_line;
break;
}
if (line < window->top_line) {
break;
}
}
break;
}
if (p <= cursor) {
/*
* position found on screen
*/
window->cline = line;
break;
}
}
}
else {
/*
* new cursor position is below current one
*/
for (p=window->cursor; ; p++) {
if (*p == '\n') {
++line;
}
if (line > last) {
/*
* off bottom of screen or window
*/
window->cline = window->place_line;
/*
* now check that we are not wasting the bottom part of the
* screen
*/
line = window->cline;
for (p=cursor; ; p++) {
if (*p == '\n') {
++line;
}
if (*p == '\0') {
window->cline += last - line;
break;
}
if (line > last) {
break;
}
}
break;
}
if (p >= cursor) {
window->cline = line;
break;
}
}
}
window->cursor = cursor;
}
/*
* Name: do_replace
* Purpose: To replace text once match has been found.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
* start: location of start of matched text
*/
void do_replace(window, start)
windows *window;
text_ptr start;
{
int old_len; /* length of original text */
int new_len; /* length of replacement text */
text_ptr source; /* source of block move */
text_ptr dest; /* destination of block move */
long number; /* number of characters moved */
char new_text[MAX_COLS]; /* replacement text (case matched) */
old_len = strlen(g_status.pattern);
new_len = strlen(g_status.subst);
/*
* unless case is to be matched, the replacement text is exactly
* what the user entered
*/
strcpy(new_text, g_status.subst);
if (g_status.flags & F_MATCH) {
/*
* change case of new text to match old
*/
for (dest=new_text, source=start; *dest; ++dest) {
if (isupper(*source)) {
*dest = toupper(*dest);
}
else if (islower(*source)) {
*dest = tolower(*dest);
}
/*
* if the replacement is longer than the original text, then
* keep on matching the case of the last character of the
* original text
*/
if (++source >= start + old_len) {
--source;
}
}
}
/*
* move the text to either make room for the extra replacement text
* or to close up the gap left
*/
source = start + old_len;
dest = start + new_len;
number = g_status.end_mem - source;
hw_move(dest, source, number);
/*
* insert the replacement text
*/
for (dest=start, source=new_text; *source; ) {
*dest++ = *source++;
}
/*
* fix up any affected marks
*/
fix_marks(window, start, -(long)old_len);
fix_marks(window, start, (long) new_len);
}
/*
* Name: do_last
* Purpose: To repeat the previous find or replace operation.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
* Notes: This function performs a very simple-minded and inefficient
* text matching algorithm. When I have more time, I might try
* replacing this with something like the Boyer-Moore string
* pattern matching algorithm...
*/
void do_last(window)
windows *window;
{
int len; /* length of current line */
int count; /* number of matches still to be made */
text_ptr start; /* start of area to be searched */
text_ptr end; /* end of area to be searched */
text_ptr orig; /* original cursor location in text */
text_ptr cursor; /* where cursor line would be if stopped now */
text_ptr final_cursor; /* where cursor should be placed at end */
char *pattern; /* pattern to be searched for */
int pat_len; /* length of pattern */
int rep_len; /* length of replacement text */
int result; /* find/replace this one? */
int backwards; /* searching backwards? */
cmp_func cmp; /* string comparison function in use */
un_copy_line(window);
if (strlen(g_status.pattern) == 0) {
error(WARNING, "nothing to search for");
return;
}
/*
* save current cursor position as previous
*/
len = linelen(window->cursor) - 1;
if (window->ccol < len) {
len = window->ccol;
}
orig = window->cursor + len;
window->file_info->marker[PREVIOUS] = orig;
/*
* work out where to start and end searching
*/
backwards = g_status.flags & F_BACKWARD;
pattern = g_status.pattern;
pat_len = strlen(pattern);
rep_len = strlen(g_status.subst);
if (g_status.flags & F_GLOBAL) {
if (backwards) {
end = window->file_info->start_text;
start = window->file_info->end_text-1 - pat_len;
cursor = start - prelinelen(start);
}
else {
cursor = start = window->file_info->start_text;
end = window->file_info->end_text-1 - pat_len;
}
}
else if (g_status.flags & F_LOCAL) {
if (!window->file_info->visible ||
window->file_info->marker[START_BLOCK] >=
window->file_info->marker[END_BLOCK]) {
error(WARNING, "no block to search");
return;
}
if (backwards) {
end = window->file_info->marker[START_BLOCK];
start = window->file_info->marker[END_BLOCK] - pat_len;
cursor = window->file_info->marker[END_BLOCK] -
prelinelen(window->file_info->marker[END_BLOCK]);
}
else {
start = window->file_info->marker[START_BLOCK];
end = window->file_info->marker[END_BLOCK] - pat_len;
cursor = window->file_info->marker[START_BLOCK] -
prelinelen(window->file_info->marker[START_BLOCK]);
}
}
else {
if (backwards) {
end = window->file_info->start_text;
start = orig - pat_len;
}
else {
start = orig + 1;
end = window->file_info->end_text-1 - pat_len;
}
cursor = window->cursor;
}
/*
* work out how to compare
*/
if (g_status.flags & (F_IGNORE | F_MATCH)) {
cmp = mystrcmpi;
}
else {
cmp = mystrcmp;
}
/*
* find the required number of matches
*/
count = g_status.search_count;
while (count > 0) {
/*
* check if finished searching
*/
if (backwards) {
if (start < end) {
break;
}
}
else {
if (start > end) {
break;
}
}
/*
* keep track of where cursor line may start
*/
if (*start == '\n') {
cursor = start+1;
}
/*
* try for match
*/
if ((*cmp)(pattern, (char *)start) == 0) {
if (!(g_status.flags & F_WORD) ||
!(myisalnum(*(start-1)) || myisalnum(*(start+pat_len)))
) {
/*
* we have a valid match
*/
--count;
/*
* position the cursor to show the user
*/
if (backwards) {
/*
* since we have not got to the \n yet, we must
* scan backwards until we find it, so that we
* can display the screen properly.
*/
cursor = start - prelinelen(start);
}
window->ccol = (int) (start - cursor);
if (window->ccol >= g_display.ncols) {
window->ccol = g_display.ncols-1;
}
/*
* since this might be the last match, remember the
* spot so that the cursor can be left there at the
* end
*/
final_cursor = cursor;
if (g_status.flags & F_AUTO) {
if (g_status.replace) {
/*
* replace the string
*/
do_replace(window, start);
if (!backwards) {
/*
* replace may have changed the position of the
* end of the search
*/
end += rep_len - pat_len;
/*
* don't risk recursive replace!
*/
start += rep_len - 1;
/*
* this fixes a problem with a replacement
* containing the pattern combined with
* multiple ^L replaces...
*/
window->ccol = (int) (start - cursor);
if (window->ccol >= g_display.ncols) {
window->ccol = g_display.ncols-1;
}
}
}
}
else {
/*
* see if it is on the current screen, and if so
* adjust cursor line accordingly; otherwise
* place in center of screen
*/
on_screen(window, cursor, 1);
/*
* if necessary, ask the user if this replacement
* should be made, or if this is the desired
* occurrence for find
*/
if (g_status.replace || count > 0) {
/*
* arrange for matched text to be highlighted
*/
g_status.match_start = start;
g_status.match_end = start + pat_len;
/*
* find out what to do
*/
set_prompt("this one? (y/n/a/q): ", 1);
result = display(get_ynaq, 1);
/*
* remove highlighting
*/
g_status.match_start = g_status.match_end;
/*
* do whatever is required
*/
switch (result) {
case A_ABORT:
case A_QUIT:
return;
case A_ALWAYS:
/*
* switch to automatic mode
*/
g_status.flags |= F_AUTO;
if (!g_status.replace) {
break;
}
/*
* if replacing, then fall through to
* replace this one before moving on to
* the rest of them
*/
case A_YES:
if (g_status.replace) {
/*
* replace the string
*/
do_replace(window, start);
if (!backwards) {
/*
* see comments above
*/
end += rep_len - pat_len;
start += rep_len - 1;
window->ccol = (int) (start - cursor);
if (window->ccol >= g_display.ncols) {
window->ccol = g_display.ncols-1;
}
}
}
else {
/*
* found desired occurrence
*/
return;
}
break;
case A_NO:
/*
* keep looking
*/
break;
}
}
}
}
}
/*
* try again starting one character nearer the end
*/
if (backwards) {
--start;
}
else {
++start;
}
}
/*
* report no matches if necessary
*/
if (count == g_status.search_count) {
error(WARNING, "no match");
}
else {
/*
* leave the cursor on the final match
*/
on_screen(window, final_cursor, 1);
}
}
/*
* Name: find_string
* Purpose: To set up and perform a find operation.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
*/
void find_string(window)
windows *window;
{
char pattern[MAX_COLS]; /* text to be found */
/*
* get replacement text, using previous as default
*/
strcpy(pattern, g_status.pattern);
if (get_name("String to find: ", 1, pattern) != OK) {
return;
}
strcpy(g_status.pattern, pattern);
/*
* get find options to use
*/
if (get_flags(2) != OK) {
return;
}
/*
* record that this is a find operation
*/
g_status.replace = FALSE;
/*
* pretend we are repeating the previous find
*/
do_last(window);
}
/*
* Name: replace_string
* Purpose: To set up and perform a replace operation.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
*/
void replace_string(window)
windows *window;
{
char pattern[MAX_COLS]; /* the old and replacement text */
/*
* get the old text, using the previous as the default
*/
strcpy(pattern, g_status.pattern);
if (get_name("String to find: ", 1, pattern) != OK) {
return;
}
strcpy(g_status.pattern, pattern);
/*
* get the replacement text, using the previous as the default
*/
strcpy(pattern, g_status.subst);
if (get_name("Replacement: ", 2, pattern) != OK) {
return;
}
strcpy(g_status.subst, pattern);
/*
* get the replace flags
*/
if (get_flags(3) != OK) {
return;
}
/*
* record that this is a replace operation
*/
g_status.replace = TRUE;
/*
* go away and do the replace
*/
do_last(window);
}
/*
* Name: goto_prep
* Purpose: To get ready to perform a goto operation, mainly by recording
* the current position as previous.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
*/
void goto_prep(window)
windows *window;
{
int len; /* length of cursor line */
/*
* make sure there is no confusion with the line buffer
*/
un_copy_line(window);
/*
* set the previous marker to the cursor position
*/
len = linelen(window->cursor);
if (window->ccol < len) {
len = window->ccol;
}
window->file_info->marker[PREVIOUS] = window->cursor + len;
}
/*
* Name: goto_complete
* Purpose: To clean up after a goto operation, mainly by making sure the
* cursor is nicely positioned on the screen.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
* cursor: final destination in file buffer
*/
void goto_complete(window, cursor)
windows *window;
text_ptr cursor;
{
on_screen(window, cursor - prelinelen(cursor), 0);
window->ccol = prelinelen(cursor);
if (window->ccol >= g_display.ncols) {
window->ccol = g_display.ncols-1;
}
}
/*
* Name: goto_marker
* Purpose: To move the cursor to a particular position marker.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
* n: the position marker to be used
* Notes: n must be in the range 0 .. 9
*/
void goto_marker(window, n)
windows *window;
int n;
{
text_ptr cursor; /* desired cursor position */
un_copy_line(window);
cursor = window->file_info->marker[n];
if (cursor) {
goto_prep(window);
goto_complete(window, cursor);
}
else {
error(WARNING, "no marker set");
}
}
/*
* Name: goto_top_file
* Purpose: To move the cursor to the top of the file.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
*/
void goto_top_file(window)
windows *window;
{
goto_prep(window);
goto_complete(window, window->file_info->start_text);
}
/*
* Name: goto_end_file
* Purpose: To move the cursor to the end of the file.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
*/
void goto_end_file(window)
windows *window;
{
if (window->file_info->end_text > window->file_info->start_text) {
goto_prep(window);
goto_complete(window, window->file_info->end_text-1); /* -1 for \0
at end of text */
}
}
/*
* Name: scan_forward
* Purpose: To find the corresponding occurrence of target, ignoring
* embedded pairs of opp and target, searching forwards.
* Date: October 1, 1989
* Passed: start: position of character to be paired
* opp: the opposite to target, if any
* target: the string to be found
* Returns: the location of the corresponding target in the text buffer
*/
text_ptr scan_forward(start, opp, target)
text_ptr start;
char *opp;
char *target;
{
int count = 0; /* number of unmatched opposites found */
while (*++start) {
if (opp && mystrcmpi(opp, (char *) start) == 0) {
count++;
}
else if (mystrcmpi(target, (char *) start) == 0) {
if (count == 0) {
break;
}
--count;
}
}
return start;
}
/*
* Name: scan_backward
* Purpose: To find the corresponding occurrence of target, ignoring
* embedded pairs of opp and target, searching backwards.
* Date: October 1, 1989
* Passed: start: position of character to be paired
* opp: the opposite to target, if any
* target: the string to be found
* Returns: the location of the corresponding target in the text buffer
*/
text_ptr scan_backward(start, opp, target)
text_ptr start;
char *opp;
char *target;
{
int count = 0; /* number of unmatched opposites found */
while (*--start) {
if (opp && mystrcmpi(opp, (char *) start) == 0) {
count++;
}
else if (mystrcmpi(target, (char *) start) == 0) {
if (count == 0) {
break;
}
--count;
}
}
return start;
}
/*
* Name: match_pair
* Purpose: To find the corresponding pair to the character under the
* cursor.
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
* forward: the user would prefer to move forwards?
* Notes: If the cursor character differs from its pair, then the search
* direction is chosen automatically, regardless of what the
* user requested.
* The direction only affects single and double quotes.
* Searching is very simple-minded, and does not cope with things
* like brackets embedded within quoted strings.
*/
void match_pair(window, forward)
windows *window;
int forward;
{
text_ptr orig; /* cursor location in text */
/*
* make sure the character under the cursor is one that has a
* matched pair
*/
un_copy_line(window);
if (window->ccol >= linelen(window->cursor)) {
return;
}
orig = window->cursor + window->ccol;
if (strchr("[]{}()\"\'/\*bBeE", *orig) == NULL) {
return;
}
if (*orig == '/' && *(orig+1) != '*') {
return;
}
if (*orig == '*' && *(orig+1) != '/') {
return;
}
if (*orig == 'b' || *orig == 'B') {
if (mystrcmpi("begin", (char *) orig) != 0) {
return;
}
}
if (*orig == 'e' || *orig == 'E') {
if (mystrcmpi("end", (char *) orig) != 0) {
return;
}
}
/*
* record the cursor position as previous
*/
goto_prep(window);
/*
* find the matching pair
*/
switch (tolower(*orig)) {
case '[':
orig = scan_forward(orig, "[", "]");
break;
case '(':
orig = scan_forward(orig, "(", ")");
break;
case '{':
orig = scan_forward(orig, "{", "}");
break;
case 'b':
orig = scan_forward(orig, "begin", "end");
break;
case ']':
orig = scan_backward(orig, "]", "[");
break;
case ')':
orig = scan_backward(orig, ")", "(");
break;
case '}':
orig = scan_backward(orig, "}", "{");
break;
case 'e':
orig = scan_backward(orig, "end", "begin");
break;
case '"':
if (forward) {
orig = scan_forward(orig, NULL, "\"");
}
else {
orig = scan_backward(orig, NULL, "\"");
}
break;
case '\'':
if (forward) {
orig = scan_forward(orig, NULL, "\'");
}
else {
orig = scan_backward(orig, NULL, "\'");
}
break;
case '/':
orig = scan_forward(orig, NULL, "*\/");
break;
case '*':
orig = scan_backward(orig, NULL, "/\*");
break;
}
/*
* searching backward may leave us on the leading \0
*/
if (orig < window->file_info->start_text) {
orig = window->file_info->start_text;
}
/*
* now show the user what we have found
*/
goto_complete(window, orig);
}
/*
* Name: goto_line
* Purpose: To move the cursor to a particular line in the file
* Date: October 1, 1989
* Passed: window: information allowing access to current window etc
* Notes: Counting lines from the start of the file buffer is not
* very efficient...
*/
void goto_line(window)
windows *window;
{
int number; /* line number selected */
int i; /* lines passed so far */
char num_str[MAX_COLS]; /* line number as string */
text_ptr p; /* used to scan through file counting lines */
/*
* find out where we are going
*/
strcpy(num_str, "");
if (get_name("Line number: ", 1, num_str) != OK) {
return;
}
number = atoi(num_str);
/*
* start from the start of the file, and count lines until we
* get there.
*/
un_copy_line(window);
p = window->file_info->start_text;
for (i=1; i < number; i++) {
p = find_next(p);
if (p == NULL) {
error(WARNING, "only %d lines in file", i);
return;
}
}
/*
* found the line, now note the previous position and show the user.
*/
goto_prep(window);
goto_complete(window, p);
}