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(®))
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, ®);
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 ®)))
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 ®);
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(®))
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, ®))
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