1 /* -*- mode: C; mode: fold; -*- */
2 /* Copyright (c) 1992, 1998 John E. Davis
3  * This file is part of JED editor library source.
4  *
5  * You may distribute this file under the terms the GNU General Public
6  * License.  See the file COPYING for more information.
7  */
8 #include "config.h"
9 #include "jed-feat.h"
10 
11 /*{{{ Include Files */
12 
13 #include <stdio.h>
14 #include <slang.h>
15 
16 #include "jdmacros.h"
17 
18 #include <string.h>
19 #include "buffer.h"
20 #include "vfile.h"
21 #include "search.h"
22 #include "misc.h"
23 #include "ins.h"
24 #include "paste.h"
25 #include "ledit.h"
26 #include "kanji.h"
27 
28 /*}}}*/
29 
30 int Case_Sensitive = 0;
31 static SLsearch_Type Search_Struct;
32 
search(char * str,int dir,int n)33 int search(char *str, int dir, int n) /*{{{*/
34 {
35    unsigned char *beg, *end, *p;
36    Line *line;
37    int key_len;
38    unsigned int num = 0;
39 
40    if ((key_len = SLsearch_init (str, dir, Case_Sensitive, &Search_Struct)) <= 0)
41      return 0;
42 
43    line = CLine;
44 
45    if (dir == 1)
46      {
47 	beg = line->data + Point;
48 	end = line->data + line->len;
49 	do
50 	  {
51 	     if (NULL != (p = SLsearch (beg, end, &Search_Struct)))
52 	       {
53 		  CLine = line;
54 		  LineNum += num;
55 		  Point = (int) (p - line->data);
56 		  return key_len;
57 	       }
58 	     line = line->next; num++;
59 	     if (line == NULL) return 0;
60 	     beg = line->data;
61 	     end = line->data + line->len;
62 	  }
63 	while (--n);
64      }
65    else if (dir == -1)
66      {
67 	end = beg = line->data;
68 	if (Point)
69 	  {
70 	     int tmp = Point - 1 + key_len;
71 	     if (tmp > line->len) tmp = line->len;
72 	     end += tmp;
73 	  }
74 
75 	do
76 	  {
77 	     if (NULL != (p = SLsearch (beg, end, &Search_Struct)))
78 	       {
79 		  CLine = line;
80 		  LineNum -= num;
81 
82 		  Point = (int) (p - line->data);
83 		  return (key_len);
84 	       }
85 	     line = line->prev;
86 	     num++;
87 	     if (line == NULL) return(0);
88 	     beg = line->data;
89 	     end = line->data + line->len;
90 	  }
91 	while (--n);
92      }
93    return(0);
94 }
95 
96 /*}}}*/
97 
search_forward(char * s)98 int search_forward(char *s) /*{{{*/
99 {
100    return search(s, 1, 0);
101 }
102 
103 /*}}}*/
104 
search_backward(char * s)105 int search_backward(char *s) /*{{{*/
106 {
107    return search(s, -1, 0);
108 }
109 
110 /*}}}*/
111 
forward_search_line(char * s)112 int forward_search_line(char *s) /*{{{*/
113 {
114    return search(s, 1, 1);
115 }
116 
117 /*}}}*/
118 
backward_search_line(char * s)119 int backward_search_line(char *s) /*{{{*/
120 {
121    return( search(s, -1, 1));
122 }
123 
124 /*}}}*/
125 
bol_fsearch(char * str)126 int bol_fsearch (char *str) /*{{{*/
127 {
128    Line *tthis;
129    int max, cs = Case_Sensitive;
130    unsigned int num;
131    unsigned char ch;
132 
133    if (Point)
134      {
135 	tthis = CLine->next;
136 	if (tthis == NULL)
137 	  return 0;
138 	num = 1;
139      }
140    else
141      {
142 	tthis = CLine;
143 	num = 0;
144      }
145 
146    max = strlen (str);
147    if (max == 0)
148      goto return_success;
149 
150    ch = str[0];
151 
152    if (cs != 0)
153      {
154 	while (tthis != NULL)
155 	  {
156 	     if ((tthis->len >= max)
157 		 && (*tthis->data == ch))
158 	       {
159 		  int n;
160 		  unsigned char *s;
161 
162 		  s = tthis->data;
163 		  n = 1;
164 		  while (1)
165 		    {
166 		       if (n == max)
167 			 goto return_success;
168 
169 		       if (s[n] != str[n])
170 			 break;
171 		       n++;
172 		    }
173 	       }
174 	     num++;
175 	     tthis = tthis->next;
176 	  }
177 	return 0;
178      }
179 
180    /* Here we have the case-insensitive match */
181    while (tthis != NULL)
182      {
183 	if (tthis->len >= max)
184 	  {
185 	     unsigned char *s = tthis->data;
186 	     int n = 0;
187 
188 	     while (1)
189 	       {
190 		  if (iskanji(str[n]))
191 		    {
192 			if(s[n] != (unsigned char)str[n] || s[n+1] != (unsigned char)str[n+1]) break;
193 			n++;
194 		    }
195 		  else if (UPPER_CASE(s[n]) != UPPER_CASE(str[n])) break;
196 		  n++;
197 		  if (n == max)
198 		    goto return_success;
199 	       }
200 	  }
201 	num++;
202 	tthis = tthis->next;
203      }
204    return 0;
205 
206    return_success:
207    Point = 0;
208    LineNum += num;
209    CLine = tthis;
210    return max;
211 }
212 
213 /*}}}*/
214 
bol_bsearch(char * str)215 int bol_bsearch (char *str) /*{{{*/
216 {
217    Line *tthis;
218    int max, n, cs = Case_Sensitive;
219    unsigned int num;
220    unsigned char ch;
221 
222    if (Point == 0)
223      {
224 	if (NULL == (tthis = CLine->prev))
225 	  return 0;
226 	num = 1;
227      }
228    else
229      {
230 	tthis = CLine;
231 	num = 0;
232      }
233 
234    max = strlen (str);
235    if (max == 0)
236      {
237 	Point = 0;
238 	LineNum -= num;
239 	CLine = tthis;
240 	return 0;
241      }
242 
243    ch = str[0];
244    if (cs == 0) ch = UPPER_CASE(ch);
245 
246    while (tthis != NULL)
247      {
248 	unsigned char *s;
249 
250 	if (tthis->len < max)
251 	  {
252 	     num++;
253 	     tthis = tthis->prev;
254 	     continue;
255 	  }
256 	s = tthis->data;
257 
258 	if ((*s != ch)
259 	    && ((cs != 0)
260 		|| (ch != UPPER_CASE(*s))))
261 	  {
262 	     num++;
263 	     tthis = tthis->prev;
264 	     continue;
265 	  }
266 
267 	if (cs)
268 	  {
269 	     for (n = 1; n < max; n++)
270 	       {
271 		  if (s[n] != str[n])
272 		    break;
273 	       }
274 	  }
275 	else
276 	  {
277 	     for (n = 1; n < max; n++)
278 	       {
279 		  if (UPPER_CASE(s[n]) != UPPER_CASE(str[n]))
280 		    break;
281 	       }
282 	  }
283 
284 	if (n == max)
285 	  {
286 	     Point = 0;
287 	     CLine = tthis;
288 	     LineNum -= num;
289 	     return max;
290 	  }
291 
292 	num++;
293 	tthis = tthis->prev;
294      }
295    return 0;
296 }
297 
298 /*}}}*/
299 
300 static SLRegexp_Type reg;
301 
re_search_dir(unsigned char * pat,int dir)302 static int re_search_dir(unsigned char *pat, int dir) /*{{{*/
303 {
304    int psave, skip, n, epos;
305    unsigned char rbuf[512], *match;
306    Line *l;
307 
308    reg.case_sensitive = Case_Sensitive;
309    reg.buf = rbuf;
310    reg.pat = pat;
311    reg.buf_len = 512;
312 
313    if (SLang_regexp_compile(&reg))
314      {
315 	msg_error("Unable to compile pattern.");
316 	return(0);
317      }
318 
319    if (reg.osearch)
320      {
321 	if (reg.must_match_bol)
322 	  {
323 	     if (dir > 0)
324 	       {
325 		  if (0 == bol_fsearch ((char *) pat))
326 		    return 0;
327 	       }
328 	     else if (0 == bol_bsearch ((char *) pat))
329 	       return 0;
330 	  }
331 	else if (!search((char *) pat, dir, 0)) return (0);
332 
333 	reg.beg_matches[0] = Point;
334 	n = strlen((char *) pat);
335 	reg.end_matches[0] = n;
336 	return n + 1;
337      }
338 
339    if (reg.must_match_bol)
340      {
341 	if (dir < 0)
342 	  {
343 	     if (Point == 0)
344 	       {
345 		  if (!backwchars(&Number_One)) return 0;
346 	       }
347 	  }
348 	else if (Point)
349 	  {
350 	     if (CLine->next == NULL) return (0);
351 	     CLine = CLine->next; LineNum++; Point = 0;
352 	  }
353      }
354 
355 
356    if (reg.must_match && (0 != reg.must_match_str[1])) skip = 0; else skip = 1;
357    while (1)
358      {
359 	psave = Point;
360 	if (!skip)
361 	  {
362 	     l = CLine;
363 	     if (!search((char *) reg.must_match_str, dir, 0)) return (0);
364 
365 	     if (l != CLine)
366 	       {
367 		  if (dir < 0) eol(); else Point = 0;
368 		  /* if ((dir == -1) && (!reg.must_match_bol)) eol(); else Point = 0; */
369 		  psave = Point;
370 	       }
371 	  }
372 
373 	Point = psave;
374 	if (dir == 1)
375 	  {
376 	     match = SLang_regexp_match(CLine->data + Point, CLine->len - Point, &reg);
377 	     if (match != NULL)
378 	       {
379 		  /* adjust offsets */
380 		  reg.offset = Point;
381 	       }
382 	  }
383 
384 	else if (NULL != (match = SLang_regexp_match(CLine->data,
385 						     Point, /* was CLine->len */
386 						     &reg)))
387 	  {
388 	     if (Point && (reg.beg_matches[0] >= Point)) match = NULL;
389 	     else if (reg.must_match_bol == 0)
390 	       {
391 		  epos = Point - 1;
392 		  /* found a match on line now find one closest to current point */
393 		  while (epos >= 0)
394 		    {
395 		       match = SLang_regexp_match(CLine->data + epos,
396 						  Point - epos, /* was: CLine->len - epos, */
397 						  &reg);
398 		       if (match != NULL)
399 			 {
400 			    reg.offset = epos;
401 			    break;
402 			 }
403 		       epos--;
404 		    }
405 	       }
406 	  }
407 	if (match != NULL)
408 	  {
409 	     Point = (int) (match - CLine->data);
410 	     n = reg.end_matches[0];
411 	     return (n + 1);
412 	  }
413 	if (dir > 0)
414 	  {
415 	     if (CLine->next == NULL) break;
416 	     CLine = CLine->next; LineNum++; Point = 0;
417 	  }
418 	else
419 	  {
420 	     if (CLine->prev == NULL) break;
421 	     CLine = CLine->prev; LineNum--;
422 	     eol ();
423 	  }
424      }
425    return (0);
426 }
427 
428 /*}}}*/
429 
re_search_forward(char * pat)430 int re_search_forward(char *pat) /*{{{*/
431 {
432    int n, p, len;
433    Line *l;
434 
435    p = Point; n = LineNum; l = CLine;
436    if (0 != (len = re_search_dir((unsigned char *) pat, 1))) return (len);
437    Point = p; LineNum = n; CLine = l;
438    return (0);
439 }
440 
441 /*}}}*/
442 
re_search_backward(char * pat)443 int re_search_backward(char *pat) /*{{{*/
444 {
445    int n, p, len;
446    Line *l;
447 
448    p = Point; n = LineNum; l = CLine;
449    if (0 != (len = re_search_dir((unsigned char *) pat, -1))) return (len);
450    Point = p; LineNum = n; CLine = l;
451    return (0);
452 }
453 
454 /*}}}*/
455 
replace_match(char * s,int * literal)456 int replace_match(char *s, int *literal) /*{{{*/
457 {
458    int n, nmax;
459    char ch;
460    unsigned char *p;
461 
462    if ((reg.pat == NULL) || (reg.beg_matches[0] == -1)
463        || (reg.beg_matches[0] + reg.offset + reg.end_matches[0] >= (unsigned int) CLine->len))
464      return 0;
465 
466    if (*literal)
467      {
468 	Point = reg.beg_matches[0] + reg.offset;
469 	n = reg.end_matches[0];
470 	generic_deln(&n);
471 	insert_string(s);
472 	return (1);
473      }
474    /* This is painful --- \& means whole expression, \x x = 1..9 means a
475       sub expression */
476 
477    /* must work with relative numbers since ins/del may do a realloc */
478    Point = reg.end_matches[0] + reg.offset + reg.beg_matches[0];
479    while ((ch = *s++) != 0)
480      {
481 	if ((ch != '\\') || ((ch = *s++) == '\\'))
482 	  {
483 	     if (ch != '\n') ins(ch);
484 	     /* Note that I should do a 'newline' call.  However, as soon as
485 	      * I do this, I lose the nth_match strings.  Clearly, I need to
486 	      * re-think this!  */
487 	     continue;
488 	  }
489 	if (ch == 0) break;
490 	if (ch == '&') ch = '0';
491 
492 	if ((ch >= '0') && (ch <= '9'))
493 	  {
494 	     nmax = ch - '0';
495 	     if ((n = reg.beg_matches[nmax]) == -1) continue;
496 	     nmax = reg.end_matches[nmax] + reg.beg_matches[nmax];
497 	  }
498 	else continue;
499 
500 	while (n < nmax)
501 	  {
502 	     p = CLine->data + reg.offset;
503 	     ins((char) *(p + n));
504 	     n++;
505 	  }
506      }
507    push_spot();
508    Point = reg.beg_matches[0] + reg.offset;
509    n = reg.end_matches[0];
510    generic_deln(&n);
511    pop_spot();
512    return (1);
513 }
514 
515 /*}}}*/
516 
push_string(char * b,int n)517 static int push_string(char *b, int n) /*{{{*/
518 {
519    char *s;
520 #if 1
521    /* Right now, slang cannot handle strings containing ASCII 0.  Thus,
522     * use a simple string interface.
523     */
524    s = SLang_create_nslstring (b, n);
525    if (s != NULL)
526      {
527 	SLang_push_string (s);
528 	SLang_free_slstring (s);
529      }
530 #else
531    s = SLmalloc (n + 1);
532    if (s == NULL)
533      return 0;
534 
535    if (n) SLMEMCPY(s, b, n);
536    *(s + n) = 0;
537 
538    SLang_push_malloced_string (s);
539 #endif
540    return (SLang_Error == 0);
541 }
542 
543 /*}}}*/
544 
regexp_nth_match(int * np)545 void regexp_nth_match (int *np) /*{{{*/
546 {
547    int b = 0, n = *np;
548 
549    if ((reg.pat == NULL) || (reg.beg_matches[0] == -1)
550        || (reg.beg_matches[0] + reg.offset + reg.end_matches[0] > (unsigned int) CLine->len))
551      {
552 	SLang_Error = SL_UNKNOWN_ERROR;
553 	return;
554      }
555 
556    if ((n <= 0) || (n > 9)) n = 0;
557    else
558      {
559 	if ((b = reg.beg_matches[n]) == -1) n = 0;
560 	else
561 	  {
562 	     n = reg.end_matches[n];
563 	  }
564      }
565    b += reg.offset;
566    push_string((char *) CLine->data + b, n);
567 }
568 
569 /*}}}*/
570 
search_file(char * file,char * pat,int * np)571 int search_file(char *file, char *pat, int *np) /*{{{*/
572 {
573    unsigned char rbuf[512], *buf;
574    unsigned int n;
575    VFILE *vp;
576    int n_matches = 0, n_max = *np, key_len = 1;
577    int code;
578 
579    reg.case_sensitive = Case_Sensitive;
580    reg.buf = rbuf;
581    reg.pat = (unsigned char *) pat;
582    reg.buf_len = 512;
583 
584    if (SLang_regexp_compile(&reg))
585      {
586 	msg_error("Unable to compile pattern.");
587 	return(0);
588      }
589 
590    if (reg.osearch)
591      {
592         key_len = SLsearch_init ((char *) reg.pat, 1, reg.case_sensitive, &Search_Struct);
593      }
594    else if (reg.must_match)
595      {
596 	key_len = SLsearch_init ((char *) reg.must_match_str, 1, reg.case_sensitive, &Search_Struct);
597      }
598 
599    if (key_len <= 0) return 0;
600    if (NULL == (vp = vopen(file, 0, VFILE_TEXT))) return(0);
601    code = is_kanji_filecode(file);
602    while (NULL != (buf = (unsigned char *) vgets(vp, &n)))
603      {
604 	unsigned char *p;
605 	p = KanjiCodeConv(buf, &n, code, kSLcode, SKanaToDKana);
606 	if (reg.must_match)
607 	  {
608 	     if ((unsigned int) key_len > n)
609 	       {
610 		  if(p != buf)	free(p);
611 		  continue;
612 	       }
613 	     if (NULL == SLsearch (p, p + n, &Search_Struct))
614 	       {
615 		  if(p != buf)	free(p);
616 		  continue;
617 	       }
618 	     if (reg.osearch)
619 	       {
620 		  goto match_found;
621 	       }
622 	  }
623 
624 	if (!SLang_regexp_match(p, (int) n, &reg))
625 	  {
626 	     if(p != buf)	free(p);
627 	     continue;
628 	  }
629 
630 	match_found:
631 	n_matches++;
632 
633 	n_max--;
634 	if (!push_string((char *) p, n) || (n_max == 0))
635 	  {
636 	     if(p != buf) free(p);
637 	     break;
638 	  }
639 	if(p != buf)	free(p);
640      }
641    vclose(vp);
642    return(n_matches);
643 }
644 
645 /*}}}*/
646 
insert_file_region(char * file,char * rbeg,char * rend)647 int insert_file_region (char *file, char *rbeg, char *rend) /*{{{*/
648 {
649    VFILE *vp;
650    unsigned int n;
651    unsigned int len = (unsigned int) strlen (rbeg);
652    int num = 0;
653    unsigned char *buf;
654    unsigned char *p;
655    int code;
656 
657    if (NULL == (vp = vopen(file, 0, VFILE_TEXT))) return (-1);
658    code = is_kanji_filecode(file);
659 
660    while (NULL != (buf = (unsigned char *) vgets(vp, &n)))
661      {
662 	p = KanjiCodeConv(buf, &n, code, kSLcode, SKanaToDKana);
663 	if ((len == 0) ||
664 	    ((n >= len) && !strncmp ((char *) p, rbeg, len)))
665 	  {
666 	     Suspend_Screen_Update = 1;
667 	     quick_insert(p, (int) n);
668 	     num++;
669 	     if(p != buf) free(p);
670 
671 	     len = (unsigned int) strlen (rend);
672 
673 	     while (NULL != (buf = (unsigned char *) vgets(vp, &n)))
674 	       {
675 		  p = KanjiCodeConv(buf, &n, code, kSLcode, SKanaToDKana);
676 		  if (len &&
677 		      ((n >= len) && !strncmp ((char *) p, rend, len)))
678 		    {
679 		       if(p != buf) free(p);
680 		       break;
681 		    }
682 
683 		  quick_insert(p, (int) n);
684 	  	  if(p != buf) free(p);
685 		  if (SLang_Error) break;
686 		  num++;
687 	       }
688 	     break;
689 	  }
690 	  else if(p != buf)	free(p);
691      }
692    vclose (vp);
693    return num;
694 }
695 
696 /*}}}*/
697 
698