1: /***************************************************************************
   2:  * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
   3:  * is provided to you without charge, and with no warranty.  You may give  *
   4:  * away copies of JOVE, including sources, provided that this notice is    *
   5:  * included in all the files.                                              *
   6:  ***************************************************************************/
   7: 
   8: #include "jove.h"
   9: #include "io.h"
  10: #include "re.h"
  11: #include "ctype.h"
  12: 
  13: #ifdef MAC
  14: #	include "mac.h"
  15: #else
  16: #	include <sys/stat.h>
  17: #endif
  18: 
  19: #ifdef MAC
  20: #	undef private
  21: #	define private
  22: #endif
  23: 
  24: #ifdef  LINT_ARGS
  25: private Bufpos * doisearch(int, int, int);
  26: 
  27: private void
  28:     IncSearch(int),
  29:     replace(int, int);
  30: private int
  31:     isearch(int, Bufpos *),
  32:     lookup(char *, char *, char *, char *),
  33:     substitute(int, Line *, int, Line *, int);
  34: #else
  35: private Bufpos * doisearch();
  36: 
  37: private void
  38:     IncSearch(),
  39:     replace();
  40: private int
  41:     isearch(),
  42:     lookup(),
  43:     substitute();
  44: #endif	/* LINT_ARGS */
  45: 
  46: #ifdef MAC
  47: #	undef private
  48: #	define private static
  49: #endif
  50: 
  51: private int
  52: substitute(query, l1, char1, l2, char2)
  53: Line    *l1,
  54:     *l2;
  55: {
  56:     Line    *lp;
  57:     int numdone = 0,
  58:         offset = curchar,
  59:         stop = NO;
  60:     disk_line   UNDO_da = 0;
  61:     Line        *UNDO_lp = 0;
  62: 
  63:     lsave();
  64:     REdirection = FORWARD;
  65: 
  66:     lp = l1;
  67:     for (lp = l1; (lp != l2->l_next) && !stop; lp = lp->l_next) {
  68:         offset = (lp == l1) ? char1 : 0;
  69:         while (!stop && re_lindex(lp, offset, compbuf, alternates, 0)) {
  70:             if (lp == l2 && REeom > char2)  /* nope, leave this alone */
  71:                 break;
  72:             DotTo(lp, REeom);
  73:             offset = curchar;
  74:             if (query) {
  75:                 message("Replace (Type '?' for help)? ");
  76: reswitch:           redisplay();
  77:                 switch (CharUpcase(getchar())) {
  78:                 case '.':
  79:                     stop = YES;
  80:                     /* Fall into ... */
  81: 
  82:                 case ' ':
  83:                 case 'Y':
  84:                     break;
  85: 
  86:                 case BS:
  87:                 case RUBOUT:
  88:                 case 'N':
  89:                     if (linebuf[offset++] == '\0')
  90:                         goto nxtline;
  91:                     continue;
  92: 
  93:                 case CTL('W'):
  94:                     re_dosub(linebuf, YES);
  95:                     numdone += 1;
  96:                     offset = curchar = REbom;
  97:                     makedirty(curline);
  98:                     /* Fall into ... */
  99: 
 100:                 case CTL('R'):
 101:                 case 'R':
 102:                     RErecur();
 103:                     offset = curchar;
 104:                     lp = curline;
 105:                     continue;
 106: 
 107:                 case CTL('U'):
 108:                 case 'U':
 109:                     if (UNDO_lp == 0)
 110:                         continue;
 111:                     lp = UNDO_lp;
 112:                     lp->l_dline = UNDO_da | DIRTY;
 113:                     offset = 0;
 114:                     numdone -= 1;
 115:                     continue;
 116: 
 117:                 case 'P':
 118:                 case '!':
 119:                     query = 0;
 120:                     break;
 121: 
 122:                 case CR:
 123:                 case LF:
 124:                 case 'Q':
 125:                     goto done;
 126: 
 127:                 case CTL('L'):
 128:                     RedrawDisplay();
 129:                     goto reswitch;
 130: 
 131:                 default:
 132:                     rbell();
 133: message("Space or Y, Period, Rubout or N, C-R or R, C-W, C-U or U, P or !, Return.");
 134:                     goto reswitch;
 135:                 }
 136:             }
 137:             re_dosub(linebuf, NO);
 138:             numdone += 1;
 139:             modify();
 140:             offset = curchar = REeom;
 141:             makedirty(curline);
 142:             if (query) {
 143:                 message(mesgbuf);   /* no blinking */
 144:                 redisplay();        /* show the change */
 145:             }
 146:             UNDO_da = curline->l_dline;
 147:             UNDO_lp = curline;
 148:             if (linebuf[offset] == 0)
 149: nxtline:            break;
 150:         }
 151:     }
 152: done:   return numdone;
 153: }
 154: 
 155: /* prompt for search and replacement strings and do the substitution */
 156: private void
 157: replace(query, inreg)
 158: {
 159:     Mark    *m;
 160:     char    *rep_ptr;
 161:     Line    *l1 = curline,
 162:         *l2 = curbuf->b_last;
 163:     int char1 = curchar,
 164:         char2 = length(curbuf->b_last),
 165:         numdone;
 166: 
 167:     if (inreg) {
 168:         m = CurMark();
 169:         l2 = m->m_line;
 170:         char2 = m->m_char;
 171:         (void) fixorder(&l1, &char1, &l2, &char2);
 172:     }
 173: 
 174:     /* get search string */
 175:     strcpy(rep_search, ask(rep_search[0] ? rep_search : (char *) 0, ProcFmt));
 176:     REcompile(rep_search, UseRE, compbuf, alternates);
 177:     /* Now the replacement string.  Do_ask() so the user can play with
 178: 	   the default (previous) replacement string by typing C-R in ask(),
 179: 	   OR, he can just hit Return to replace with nothing. */
 180:     rep_ptr = do_ask("\r\n", (int (*)()) 0, rep_str, ": %f %s with ", rep_search);
 181:     if (rep_ptr == 0)
 182:         rep_ptr = NullStr;
 183:     strcpy(rep_str, rep_ptr);
 184: 
 185:     if (((numdone = substitute(query, l1, char1, l2, char2)) != 0) &&
 186:         (inreg == NO)) {
 187:         do_set_mark(l1, char1);
 188:         add_mess(" ");      /* just making things pretty */
 189:     } else
 190:         message("");
 191:     add_mess("(%d substitution%n)", numdone, numdone);
 192: }
 193: 
 194: void
 195: RegReplace()
 196: {
 197:     replace(0, YES);
 198: }
 199: 
 200: void
 201: QRepSearch()
 202: {
 203:     replace(1, NO);
 204: }
 205: 
 206: void
 207: RepSearch()
 208: {
 209:     replace(0, NO);
 210: }
 211: 
 212: /* Lookup a tag in tag file FILE.  FILE is assumed to be sorted
 213:    alphabetically.  The FASTTAGS code, which is implemented with
 214:    a binary search, depends on this assumption.  If it's not true
 215:    it is possible to comment out the fast tag code (which is clearly
 216:    labeled) and everything else will just work. */
 217: 
 218: private int
 219: lookup(searchbuf, filebuf, tag, file)
 220: char    *searchbuf,
 221:     *filebuf,
 222:     *tag,
 223:     *file;
 224: {
 225:     register int    taglen = strlen(tag);
 226:     char    line[BUFSIZ],
 227:         pattern[128];
 228:     register File   *fp;
 229:     struct stat stbuf;
 230:     int fast = YES,
 231:         success = NO;
 232:     register off_t  lower, upper;
 233: 
 234:     sprintf(pattern, "^%s[^\t]*\t*\\([^\t]*\\)\t*[?/]\\([^?/]*\\)[?/]", tag);
 235:     fp = open_file(file, iobuff, F_READ, !COMPLAIN, QUIET);
 236:     if (fp == NIL)
 237:         return 0;
 238: 
 239:     /* ********BEGIN FAST TAG CODE******** */
 240: 
 241:     if (stat(file, &stbuf) < 0)
 242:         fast = NO;
 243:     else {
 244:         lower = 0;
 245:         upper = stbuf.st_size;
 246:         if (upper - lower < BUFSIZ)
 247:             fast = NO;
 248:     }
 249:     if (fast == YES) for (;;) {
 250:         off_t   mid;
 251:         int whichway,
 252:             chars_eq;
 253: 
 254:         if (upper - lower < BUFSIZ) {
 255:             f_seek(fp, lower);
 256:             break;          /* stop this nonsense */
 257:         }
 258:         mid = (lower + upper) / 2;
 259:         f_seek(fp, mid);
 260:         f_toNL(fp);
 261:         if (f_gets(fp, line, sizeof line) == EOF)
 262:             break;
 263:         chars_eq = numcomp(line, tag);
 264:         if (chars_eq == taglen && iswhite(line[chars_eq]))
 265:             goto found;
 266:         whichway = line[chars_eq] - tag[chars_eq];
 267:         if (whichway < 0) {     /* line is BEFORE tag */
 268:             lower = mid;
 269:             continue;
 270:         } else if (whichway > 0) {  /* line is AFTER tag */
 271:             upper = mid;
 272:             continue;
 273:         }
 274:     }
 275:     f_toNL(fp);
 276:     /* END FAST TAG CODE */
 277: 
 278:     while (f_gets(fp, line, sizeof line) != EOF) {
 279:         int cmp;
 280: 
 281:         if (line[0] > *tag)
 282:             break;
 283:         else if ((cmp = strncmp(line, tag, taglen)) > 0)
 284:             break;
 285:         else if (cmp < 0)
 286:             continue;
 287:         /* if we get here, we've found the match */
 288: found:      if (!LookingAt(pattern, line, 0)) {
 289:             complain("I thought I saw it!");
 290:             break;
 291:         } else {
 292:             putmatch(1, filebuf, FILESIZE);
 293:             putmatch(2, searchbuf, 100);
 294:             success = YES;
 295:             break;
 296:         }
 297:     }
 298:     close_file(fp);
 299: 
 300:     if (success == NO)
 301:         s_mess("Can't find tag \"%s\".", tag);
 302:     return success;
 303: }
 304: 
 305: #ifndef MSDOS
 306: char    TagFile[FILESIZE] = "./tags";
 307: #else /* MSDOS */
 308: char    TagFile[FILESIZE] = "tags";
 309: #endif /* MSDOS */
 310: 
 311: void
 312: find_tag(tag, localp)
 313: char    *tag;
 314: {
 315:     char    filebuf[FILESIZE],
 316:         sstr[100],
 317:         tfbuf[FILESIZE];
 318:     register Bufpos *bp;
 319:     register Buffer *b;
 320:     char    *tagfname;
 321: 
 322:     if (!localp)
 323:         tagfname = ask_file("With tag file: ", TagFile, tfbuf);
 324:     else
 325:         tagfname = TagFile;
 326:     if (lookup(sstr, filebuf, tag, tagfname) == 0)
 327:         return;
 328:     set_mark();
 329:     b = do_find(curwind, filebuf, 0);
 330:     if (curbuf != b)
 331:         SetABuf(curbuf);
 332:     SetBuf(b);
 333:     if ((bp = dosearch(sstr, BACKWARD, 0)) == 0 &&
 334:         ((bp = dosearch(sstr, FORWARD, 0)) == 0))
 335:         message("Well, I found the file, but the tag is missing.");
 336:     else
 337:         SetDot(bp);
 338: }
 339: 
 340: void
 341: FindTag()
 342: {
 343:     int localp = !is_an_arg();
 344:     char    tag[128];
 345: 
 346:     strcpy(tag, ask((char *) 0, ProcFmt));
 347:     find_tag(tag, localp);
 348: }
 349: 
 350: /* Find Tag at Dot. */
 351: 
 352: void
 353: FDotTag()
 354: {
 355:     int c1 = curchar,
 356:         c2 = c1;
 357:     char    tagname[50];
 358: 
 359:     if (!ismword(linebuf[curchar]))
 360:         complain("Not a tag!");
 361:     while (c1 > 0 && ismword(linebuf[c1 - 1]))
 362:         c1 -= 1;
 363:     while (ismword(linebuf[c2]))
 364:         c2 += 1;
 365: 
 366:     null_ncpy(tagname, linebuf + c1, c2 - c1);
 367:     find_tag(tagname, !is_an_arg());
 368: }
 369: 
 370: /* I-search returns a code saying what to do:
 371:    STOP:	We found the match, so unwind the stack and leave
 372: 		where it is.
 373:    DELETE:	Rubout the last command.
 374:    BACKUP:	Back up to where the isearch was last NOT failing.
 375: 
 376:    When a character is typed it is appended to the search string, and
 377:    then, isearch is called recursively.  When C-S or C-R is typed, isearch
 378:    is again called recursively. */
 379: 
 380: #define STOP    1
 381: #define DELETE  2
 382: #define BACKUP  3
 383: #define TOSTART 4
 384: 
 385: static char ISbuf[128],
 386:         *incp = 0;
 387: int SExitChar = CR;
 388: 
 389: #define cmp_char(a, b)  ((a) == (b) || (CaseIgnore && (CharUpcase(a) == CharUpcase(b))))
 390: 
 391: static Bufpos *
 392: doisearch(dir, c, failing)
 393: register int    c,
 394:         dir,
 395:         failing;
 396: {
 397:     static Bufpos   buf;
 398:     Bufpos  *bp;
 399:     extern int  okay_wrap;
 400: 
 401:     if (c == CTL('S') || c == CTL('R'))
 402:         goto dosrch;
 403: 
 404:     if (failing)
 405:         return 0;
 406:     DOTsave(&buf);
 407:     if (dir == FORWARD) {
 408:         if (cmp_char(linebuf[curchar], c)) {
 409:             buf.p_char = curchar + 1;
 410:             return &buf;
 411:         }
 412:     } else {
 413:         if (look_at(ISbuf))
 414:             return &buf;
 415:     }
 416: dosrch: okay_wrap = YES;
 417:     if ((bp = dosearch(ISbuf, dir, 0)) == 0)
 418:         rbell();    /* ring the first time there's no match */
 419:     okay_wrap = NO;
 420:     return bp;
 421: }
 422: 
 423: void
 424: IncFSearch()
 425: {
 426:     IncSearch(FORWARD);
 427: }
 428: 
 429: void
 430: IncRSearch()
 431: {
 432:     IncSearch(BACKWARD);
 433: }
 434: 
 435: private void
 436: IncSearch(dir)
 437: {
 438:     Bufpos  save_env;
 439: 
 440:     DOTsave(&save_env);
 441:     ISbuf[0] = 0;
 442:     incp = ISbuf;
 443:     if (isearch(dir, &save_env) == TOSTART)
 444:         SetDot(&save_env);
 445:     else {
 446:         if (LineDist(curline, save_env.p_line) >= MarkThresh)
 447:             do_set_mark(save_env.p_line, save_env.p_char);
 448:     }
 449:     setsearch(ISbuf);
 450: }
 451: 
 452: /* Nicely recursive. */
 453: 
 454: private int
 455: isearch(dir, bp)
 456: Bufpos  *bp;
 457: {
 458:     Bufpos  pushbp;
 459:     int c,
 460:         ndir,
 461:         failing;
 462:     char    *orig_incp;
 463: 
 464:     if (bp != 0) {      /* Move to the new position. */
 465:         pushbp.p_line = bp->p_line;
 466:         pushbp.p_char = bp->p_char;
 467:         SetDot(bp);
 468:         failing = 0;
 469:     } else {
 470:         DOTsave(&pushbp);
 471:         failing = 1;
 472:     }
 473:     orig_incp = incp;
 474:     ndir = dir;     /* Same direction as when we got here, unless
 475: 				   we change it with C-S or C-R. */
 476:     for (;;) {
 477:         SetDot(&pushbp);
 478:         message(NullStr);
 479:         if (failing)
 480:             add_mess("Failing ");
 481:         if (dir == BACKWARD)
 482:             add_mess("reverse-");
 483:         add_mess("I-search: %s", ISbuf);
 484:         DrawMesg(NO);
 485:         add_mess(NullStr);  /* tell me this is disgusting ... */
 486:         c = getch();
 487:         if (c == SExitChar)
 488:             return STOP;
 489:         if (c == AbortChar) {
 490:             /* If we're failing, we backup until we're no longer
 491: 			   failing or we've reached the beginning; else, we
 492: 			   just about the search and go back to the start. */
 493:             if (failing)
 494:                 return BACKUP;
 495:             return TOSTART;
 496:         }
 497:         switch (c) {
 498:         case RUBOUT:
 499:         case BS:
 500:             return DELETE;
 501: 
 502:         case CTL('\\'):
 503:             c = CTL('S');
 504: 
 505:         case CTL('S'):
 506:         case CTL('R'):
 507:             /* If this is the first time through and we have a
 508: 			   search string left over from last time, use that
 509: 			   one now. */
 510:             if (incp == ISbuf) {
 511:                 strcpy(ISbuf, getsearch());
 512:                 incp = &ISbuf[strlen(ISbuf)];
 513:             }
 514:             ndir = (c == CTL('S')) ? FORWARD : BACKWARD;
 515:             /* If we're failing and we're not changing our
 516: 			   direction, don't recur since there's no way
 517: 			   the search can work. */
 518:             if (failing && ndir == dir) {
 519:                 rbell();
 520:                 continue;
 521:             }
 522:             break;
 523: 
 524:         case '\\':
 525:             if (incp > &ISbuf[(sizeof ISbuf) - 1]) {
 526:                 rbell();
 527:                 continue;
 528:             }
 529:             *incp++ = '\\';
 530:             add_mess("\\");
 531:             /* Fall into ... */
 532: 
 533:         case CTL('Q'):
 534:         case CTL('^'):
 535:             add_mess("");
 536:             c = getch() | 0400;
 537:             /* Fall into ... */
 538: 
 539:         default:
 540:             if (c & 0400)
 541:                 c &= CHARMASK;
 542:             else {
 543: #ifdef IBMPC
 544:                 if (c == RUBOUT || c == 0xff || (c < ' ' && c != '\t')) {
 545: #else
 546:                 if (c > RUBOUT || (c < ' ' && c != '\t')) {
 547: #endif
 548:                     Ungetc(c);
 549:                     return STOP;
 550:                 }
 551:             }
 552:             if (incp > &ISbuf[(sizeof ISbuf) - 1]) {
 553:                 rbell();
 554:                 continue;
 555:             }
 556:             *incp++ = c;
 557:             *incp = 0;
 558:             break;
 559:         }
 560:         add_mess("%s", orig_incp);
 561:         add_mess(" ...");   /* so we know what's going on */
 562:         DrawMesg(NO);       /* do it now */
 563:         switch (isearch(ndir, doisearch(ndir, c, failing))) {
 564:         case TOSTART:
 565:             return TOSTART;
 566: 
 567:         case STOP:
 568:             return STOP;
 569: 
 570:         case BACKUP:
 571:             /* If we're not failing, we just continue to to the
 572: 			   for loop; otherwise we keep returning to the
 573: 			   previous levels until we find one that isn't
 574: 			   failing OR we reach the beginning. */
 575:             if (failing)
 576:                 return BACKUP;
 577:             /* Fall into ... */
 578: 
 579:         case DELETE:
 580:             incp = orig_incp;
 581:             *incp = 0;
 582:             continue;
 583:         }
 584:     }
 585: }

Defined functions

FDotTag defined in line 352; used 4 times
IncFSearch defined in line 423; used 4 times
IncRSearch defined in line 429; used 4 times
IncSearch defined in line 435; used 4 times
QRepSearch defined in line 200; used 4 times
RegReplace defined in line 194; used 4 times
RepSearch defined in line 206; used 4 times
doisearch defined in line 391; used 3 times
find_tag defined in line 311; used 6 times
isearch defined in line 454; used 4 times
lookup defined in line 218; used 3 times
replace defined in line 156; used 5 times
substitute defined in line 51; used 3 times

Defined variables

ISbuf defined in line 385; used 14 times
SExitChar defined in line 387; used 3 times
TagFile defined in line 308; used 5 times
incp defined in line 386; used 11 times
private defined in line 454; never used

Defined macros

BACKUP defined in line 382; used 2 times
DELETE defined in line 381; used 1 times
STOP defined in line 380; used 3 times
TOSTART defined in line 383; used 3 times
cmp_char defined in line 389; used 1 times
private defined in line 48; used 10 times
Last modified: 1988-08-11
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3782
Valid CSS Valid XHTML 1.0 Strict