DBA Data[Home] [Help]

PACKAGE BODY: SYS.OWA_PATTERN

Source


1 package body OWA_PATTERN is
2 
3    subtype substitution is pattern;
4 
5    -- MAX_VC_LEN constant number := 32767;
6    -- PL/SQL doesn't allow one to use constant values in variable declarations,
7    -- like 'vc varchar2(MAX_VC_LEN), so this is really just here as a reminder
8    -- to always use value(MAX_VC_LEN) for declarations.
9 
10    BOL         constant varchar2(1) := '^';
11    EOL         constant varchar2(1) := '$';
12    CCL         constant varchar2(1) := '[';
13    CCLEND      constant varchar2(1) := ']';
14    QUANT       constant varchar2(1) := '{';
15    QUANTEND    constant varchar2(1) := '}';
16    BR          constant varchar2(1) := '(';
17    BREND       constant varchar2(1) := ')';
18    ANY_CHAR    constant varchar2(1) := '.';
19    ESCAPE      constant varchar2(1) := '\';
20    DASH        constant varchar2(1) := '-';
21    NOT_CHAR    constant varchar2(1) := '^';
22    CLOSURE     constant varchar2(1) := '*';
23    ONE_OR_MORE constant varchar2(1) := '+';
24    ZERO_OR_ONE constant varchar2(1) := '?';
25 
26    AMP       constant varchar2(1) := '&';
27 
28    /* The following line should be broken to represent a NEWLINE */
29    NEWLINE constant varchar2(1) := '
30 ';
31    /* The following character is a true "tab" */
32    TAB     constant varchar2(1) := '	';
33    SPACE   constant varchar2(1) := ' ';
34 
35    NCCL      constant varchar2(4) := 'NCCL';
36    CHARCHAR  constant varchar2(4) := 'CHAR';
37    DITTO     constant varchar2(4) := 'DTTO';
38    BREF      constant varchar2(4) := 'BREF';
39    EOP       constant varchar2(4) := 'EOP';
40 
41    CLOSIZE constant integer := 6;
42    BRSIZE  constant integer := 4;
43 
44    DIGITS constant VARCHAR2(10) := '0123456789';
45    LOWLET constant VARCHAR2(26) := 'abcdefghijklmnopqrstuvwxyz';
46    UPPLET constant VARCHAR2(26) := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
47    UNDERBAR constant VARCHAR2(1) := '_';
48 
49    WORD_CHARS  constant VARCHAR2(63) := DIGITS||LOWLET||UNDERBAR||UPPLET;
50    SPACE_CHARS constant VARCHAR2(3)  := SPACE||TAB||NEWLINE;
51 
52    DIG_ESCAPE_CHAR       constant varchar2(1) := 'd'; -- [0-9]
53    NON_DIG_ESCAPE_CHAR   constant varchar2(1) := 'D'; -- [^0-9]
54    WORD_ESCAPE_CHAR      constant varchar2(1) := 'w'; -- [0-9a-z_A-Z]
55    NON_WORD_ESCAPE_CHAR  constant varchar2(1) := 'W'; -- [^0-9a-z_A-Z]
56    SPACE_ESCAPE_CHAR     constant varchar2(1) := 's'; -- [ \t\n]
57    NON_SPACE_ESCAPE_CHAR constant varchar2(1) := 'S'; -- [^ \t\n]
58    BOUND_ESCAPE_CHAR     constant varchar2(1) := 'b';
59    NON_BOUND_ESCAPE_CHAR constant varchar2(1) := 'B';
60 
61    DIG_ESCAPE       constant varchar2(2) := ESCAPE||DIG_ESCAPE_CHAR;
62    NON_DIG_ESCAPE   constant varchar2(2) := ESCAPE||NON_DIG_ESCAPE_CHAR;
63    WORD_ESCAPE      constant varchar2(2) := ESCAPE||WORD_ESCAPE_CHAR;
64    NON_WORD_ESCAPE  constant varchar2(2) := ESCAPE||NON_WORD_ESCAPE_CHAR;
65    SPACE_ESCAPE     constant varchar2(2) := ESCAPE||SPACE_ESCAPE_CHAR;
66    NON_SPACE_ESCAPE constant varchar2(2) := ESCAPE||NON_SPACE_ESCAPE_CHAR;
67    BOUND_ESCAPE     constant varchar2(2) := ESCAPE||BOUND_ESCAPE_CHAR;
68    NON_BOUND_ESCAPE constant varchar2(2) := ESCAPE||NON_BOUND_ESCAPE_CHAR;
69 
70    COUNT_IND  constant integer := 1;
71    PREVCL_IND constant integer := 2;
72    START_IND  constant integer := 3;
73    MIN_IND    constant integer := 4;
74    MAX_IND    constant integer := 5;
75 
76    PREVBR_IND  constant integer := 1;
77    LOC_IND     constant integer := 2;
78    BRNO_IND    constant integer := 3;
79    OPENBR_IND  constant integer := 3;
80 
81    in_b boolean;
82 
83    function ind(str in varchar2, i in integer) return varchar2
84       is
85    begin
86       return substr(str,i,1);
87    end;
88 
89    function line_len(line in varchar2) return integer
90       is
91    begin
92       if (line is null)
93       then
94          return 1;
95       else
96          return length(line);
97       end if;
98    end;
99 
100    function get_int(arg  in     varchar2,
101                     loc  in out integer,
102                     digs in     varchar2 DEFAULT DIGITS) return integer is
103       start_loc integer;
104    begin
105       start_loc := loc;
106 
107       while (instr(digs, ind(arg,loc)) != 0)
108       loop
109          loc := loc + 1;
110       end loop;
111 
112       return substr(arg, start_loc, loc-start_loc);
113    end;
114 
115    function base_convert(str in varchar2, base in integer) return integer
116      is
117       acc integer;
118       len integer;
119    begin
120       len := length(str);
121       if (len is NULL)
122       then
123          return NULL;
124       end if;
125 
126       acc := 0;
127       for i in 1..len
128       loop
129          if (instr('abcdefABCDEF', ind(str,i)) != 0)
130          then
131             acc := acc*base + ascii(convert(lower(ind(str,i)), 'US7ASCII')) - 87;
132          else
133             acc := acc*base + ind(str,i);
134 	 end if;
135       end loop;
136 
137       return acc;
138    end;
139 
140    procedure addpat(ch in varchar2, pat in out pattern, j in out integer)
141       is
142    begin
143       pat(j) := ch;
144       j := j + 1;
145    end;
146 
147    function patsize(pat in pattern, n in integer) return integer is
148    begin
149       if (pat(n) = CHARCHAR)
150       then return 2;
151       end if;
152       if (pat(n) in (BOL, EOL, ANY_CHAR,
153                      DIG_ESCAPE, NON_DIG_ESCAPE,
154                      WORD_ESCAPE, NON_WORD_ESCAPE,
155                      SPACE_ESCAPE, NON_SPACE_ESCAPE,
156                      BOUND_ESCAPE, NON_BOUND_ESCAPE))
157       then return 1;
158       end if;
159       if (pat(n) in (CCL, NCCL))
160       then return pat(n+1) + 2;
161       end if;
162       if (pat(n) = CLOSURE)
163       then return CLOSIZE;
164       end if;
165       if (pat(n) in (BR, BREND))
166       then return BRSIZE;
167       end if;
168 
169       raise_application_error(-20002,'in patsize: shouldn''t happen');
170    end;
171 
172    procedure copypat(pat in out pattern,
173                      j   in out integer,
174                      loc in     integer) is
175       pat_size integer;
176       k    integer;
177    begin
178       pat_size := patsize(pat,loc);
179       for k in loc..loc+pat_size-1
180       loop
181          addpat(pat(k), pat, j);
182       end loop;
183    end;
184 
185    function esc(arg in varchar2, i in out integer) return varchar2
186       is
187       NL_ESCAPE  constant varchar2(1) := 'n';
188       TAB_ESCAPE constant varchar2(1) := 't';
189 
190       arg_i varchar2(5); -- MAX NO OF BYTES FOR NLS CHARSET
191    begin
192       arg_i := ind(arg,i);
193       if (arg_i != ESCAPE)
194       then
195          return arg_i;
196       end if;
197 
198       if (i = length(arg))
199       then
200          return arg_i;
201       end if;
202 
203       i := i+1;
204       arg_i := ind(arg,i);
205       if (arg_i = NL_ESCAPE)
206       then
207          return NEWLINE;
208       end if;
209 
210       if (arg_i = TAB_ESCAPE)
211       then
212          return TAB;
213       end if;
214 
215       return arg_i;
216    end;
217 
218    procedure dodash(valid_chars in varchar2,
219                     arg         in varchar2,
220                     i           in out integer,
221                     pat         in out pattern,
222                     j           in out integer) is
223       limit integer;
224       k     integer;
225    begin
226       i := i + 1;
227       j := j - 1;
228 
229       limit := instr(valid_chars, esc(arg,i));
230       k := instr(valid_chars, pat(j));
231       while (k <= limit)
232       loop
233          addpat(ind(valid_chars,k), pat, j);
234          k := k+1;
235       end loop;
236    end;
237 
238    procedure filset(delim in     varchar2,
239                     arg   in     varchar2,
240                     i     in out integer,
241                     pat   in out pattern,
242                     j     in out integer) is
243       arglen integer;
244       ch     varchar2(5); -- MAX NO OF BYTES FOR NLS CHARSET
245    begin
246       arglen := length(arg);
247 
248       ch := ind(arg,i);
249       while (ch != delim) AND (i <= arglen)
250       loop
251          if (ch = ESCAPE)
252          then
253             addpat(esc(arg,i), pat,j);
254          else if (ch != DASH)
255          then
256             addpat(ch, pat, j);
257          else if (j <= 1) OR (i+1 >= arglen)
258          then
259             addpat(DASH, pat, j);
260          else if (instr(DIGITS,pat(j-1)) > 0)
261          then
262             dodash(DIGITS, arg, i, pat, j);
263          else if (instr(LOWLET, pat(j-1)) > 0)
264          then
265             dodash(LOWLET, arg, i, pat, j);
266          else if (instr(UPPLET, pat(j-1)) > 0)
267          then
268             dodash(UPPLET, arg, i, pat, j);
269          else
270             addpat(DASH, pat, j);
271          end if;
272          end if;
273          end if;
274          end if;
275          end if;
276          end if;
277 
278          i := i + 1;
279          ch := ind(arg,i);
280       end loop;
281    end;
282 
283    procedure getccl(arg in     varchar2,
284                     i   in out integer,
285                     pat in out pattern,
286                     j   in out integer) is
287       jstart integer;
288    begin
289       i := i + 1; -- Skip over the "["
290 
291       if (ind(arg,i) = NOT_CHAR)
292       then
293          addpat(NCCL, pat, j);
294          i := i + 1;
295       else
296          addpat(CCL, pat, j);
297       end if;
298 
299       jstart := j;
300       addpat(0, pat, j);
301       filset(CCLEND, arg, i, pat, j);
302       pat(jstart) := j - jstart - 1;
303       if (ind(arg,i) != CCLEND)
304       then
305          raise_application_error(-20000, 'Error in getccl');
306       end if;
307    end;
308 
309    -- stmin_max returns FALSE if a string beginning with a '{' is not
310    -- a proper {n,m} quantifier
311    function stmin_max(arg     in     varchar2,
312                       i       in out integer,
313                       min_val    out integer,
314                       max_val    out integer) return boolean
315      is
316       COMMA constant varchar2(1) := ',';
317 
318       arg_i varchar2(5); -- MAX NO OF BYTES FOR NLS CHARSET
319       i1    integer;
320       i2    integer;
321 
322       min_v integer;
323    begin
324       arg_i := ind(arg,i);
325       if (arg_i = CLOSURE)
326       then
327          min_val := 0;
328          max_val := NULL;
329       else if (arg_i = ONE_OR_MORE)
330       then
331          min_val := 1;
332          max_val := NULL;
333       else if (arg_i = ZERO_OR_ONE)
334       then
335          min_val := 0;
336          max_val := 1;
337       else if (arg_i = QUANT)
338       then
339          i1 := i + 1;
340          i2 := i1;
341 
342          while (instr(DIGITS, ind(arg,i2)) != 0)
343          loop
344             i2 := i2 + 1;
345          end loop;
346 
347          min_v := substr(arg, i1, i2-i1);
348          if (min_v is null) then return FALSE; end if;
349 
350          if (ind(arg,i2) = QUANTEND)
351          then
352             max_val := min_v;
353          else if (ind(arg,i2) = COMMA)
354          then
355             i1 := i2 + 1;
356             i2 := i1;
357 
358             while (instr(DIGITS, ind(arg,i2)) != 0)
359             loop
360                i2 := i2 + 1;
361             end loop;
362 
363             if (ind(arg,i2) = QUANTEND)
364             then
365                max_val := substr(arg, i1, i2-i1);
366             else
367                return FALSE;
368             end if;
369          else
370             return FALSE;
371          end if;
372          end if;
373 
374          min_val := min_v;
375          i := i2;
376       else
377          raise_application_error(-20001, 'In stmin_max: illegal pattern');
378       end if;
379       end if;
380       end if;
381       end if;
382 
383       return TRUE;
384    end;
385 
386    function stclos(pat     in out pattern,
387                    j       in out integer,
388                    lastj   in out integer,
389                    lastcl  in     integer,
390                    min_val in     integer,
391                    max_val in     integer) return integer is
392       jp         integer;
393       jt         integer;
394       return_val integer;
395    begin
396       for jp in REVERSE lastj..j-1
397       loop
398          jt := jp + CLOSIZE;
399          addpat(pat(jp), pat, jt);
400       end loop;
401 
402       j := j + CLOSIZE;
403       return_val := lastj;
404 
405       addpat(CLOSURE, pat, lastj);
406       addpat(0,       pat, lastj);
407       addpat(lastcl,  pat, lastj);
408       addpat(0,       pat, lastj);
409       addpat(min_val, pat, lastj);
410       addpat(max_val, pat, lastj);
411 
412       return return_val;
413    end;
414 
415    procedure stbr(brtype  in     varchar2,
416                   pat     in out pattern,
417                   j       in out integer,
418                   lastbr  in out integer,
419                   var     in out integer) is
420       -- "var" will be either "brno" if we are on a "("
421       --   or it will be "openbr" if we are on a ")"
422    begin
423       addpat(brtype, pat, j);
424       addpat(lastbr, pat, j);
425       addpat(0,      pat, j);
426 
427       -- var is "brno" increment it before inserting the value.
428       if (brtype = BR)
429       then
430          var := var + 1;
431       end if;
432 
433       addpat(var, pat, j);
434       lastbr := j - BRSIZE;
435 
436       -- var is "openbr" - find the last unmatched openbr.
437       if (brtype = BREND)
438       then
439          var := pat(var + PREVBR_IND);
440 
441          while (var > 0) AND (pat(var) != BR)
442          loop
443             var := pat(var + OPENBR_IND);
444             var := pat(var + PREVBR_IND);
445          end loop;
446       end if;
447    end;
448 
449    /* ESCPAT is an enhancement to Kernighan's algorithms.  It allows */
450    /* more "short-cut" tags in the pattern, such as using '\d' for   */
451    /* [0-9].  This is to extend the algorithms to support more of    */
452    /* Perl's regular expression patterns.                            */
453    procedure escpat(arg in     varchar2,
454                     i   in out integer,
455                     pat in out pattern,
456                     j   in out integer) is
457       HEXCHAR   constant varchar2(1) := 'x';
458       HEXDIGITS constant varchar2(22) := DIGITS||'abcdefABCDEF';
459       OCTDIGITS constant varchar2(8) := '01234567';
460    begin
461       if (ind(arg,i) != ESCAPE)
462       then
463          addpat(CHARCHAR, pat, j);
464          addpat(ind(arg,i), pat, j);
465       else if (ind(arg,i+1) in (DIG_ESCAPE_CHAR, NON_DIG_ESCAPE_CHAR,
466                                 WORD_ESCAPE_CHAR, NON_WORD_ESCAPE_CHAR,
467                                 SPACE_ESCAPE_CHAR, NON_SPACE_ESCAPE_CHAR,
468                                 BOUND_ESCAPE_CHAR, NON_BOUND_ESCAPE_CHAR))
469       then
470          addpat(ESCAPE||ind(arg,i+1), pat, j);
471          i := i + 1;
472       else if ((ind(arg,i+1) = HEXCHAR) AND
473                (instr(HEXDIGITS, ind(arg,i+2)) != 0) AND
474                (instr(HEXDIGITS, ind(arg,i+3)) != 0)
475               )
476       then
477          addpat(CHARCHAR, pat, j);
478          addpat(chr(base_convert(substr(arg,i+2,2), 16)), pat, j);
479          i := i + 3;
480       else if ((instr(OCTDIGITS, ind(arg,i+1)) != 0) AND
481                (instr(OCTDIGITS, ind(arg,i+2)) != 0)
482               )
483       then
484          if (instr(OCTDIGITS, ind(arg,i+3)) != 0)
485          then
486             addpat(CHARCHAR, pat, j);
487             addpat(chr(base_convert(substr(arg,i+1,3), 8)), pat, j);
488             i := i + 3;
489          else
490             addpat(CHARCHAR, pat, j);
491             addpat(chr(base_convert(substr(arg,i+1,2), 8)), pat, j);
492             i := i + 2;
493          end if;
494       else
495          addpat(CHARCHAR, pat, j);
496          addpat(esc(arg,i), pat, j);
497       end if;
498       end if;
499       end if;
500       end if;
501    end;
502 
503    /* GETPAT is a merge of the "getpat" and "makpat" functions which */
504    /* Kernighan details.  The additional level of abstraction which  */
505    /* makpat provides is unnecessary in this implementation.         */
506    procedure getpat(arg in varchar2, pat in out pattern) is
507       arglen integer;
508 
509       i integer;
510       j integer;
511 
512       lastcl integer;
513       lastj  integer;
514       lj     integer;
515 
516       lastbr integer;
517       openbr integer;
518       brno   integer;
519 
520       arg_i varchar2(5); -- MAX NO OF BYTES FOR NLS CHARSET
521 
522       min_val integer;
523       max_val integer;
524    begin
525       arglen := length(arg);
526 
527       j := 1;
528       lastj  := 1;
529       lastcl := 0;
530       lastbr := 0;
531       openbr := 0;
532       brno   := 0;
533 
534       i := 1;
535       while (i <= arglen)
536       loop
537          lj := j;
538 
539          arg_i := ind(arg,i);
540          if (arg_i = ANY_CHAR)
541          then
542             addpat(ANY_CHAR, pat, j);
543          else if (arg_i = BOL) AND (i = 1)
544          then
545             addpat(BOL, pat, j);
546          else if (arg_i = EOL) AND (i = arglen)
547          then
548             addpat(EOL, pat, j);
549          else if (arg_i = CCL)
550          then
551             getccl(arg, i, pat, j);
552          else if (arg_i in (CLOSURE, ZERO_OR_ONE,
553                             ONE_OR_MORE, QUANT)) AND (i > 1)
554          then
555             lj := lastj;
556             if (pat(lj) NOT in (BOL, EOL, CLOSURE, ZERO_OR_ONE,
557                                           ONE_OR_MORE, QUANT))
558             then
559                if (stmin_max(arg, i, min_val, max_val))
560                then
561                   lastcl := stclos(pat, j, lastj, lastcl, min_val, max_val);
562                else
563                   escpat(arg, i, pat, j);
564                end if;
565             else
566                raise_application_error(-20000,arg||': nested *?+ in regular expression');
567             end if;
568          else if (arg_i = BR)
569          then
570             openbr := j;
571             stbr(BR, pat, j, lastbr, brno);
572          else if (arg_i = BREND)
573          then
574             if (openbr = 0)
575             then
576                raise_application_error(-20000,arg||': unmatched () in regular expression');
577             end if;
578 
579             stbr(BREND, pat, j, lastbr, openbr);
580          else
581             escpat(arg, i, pat, j);
582          end if;
583          end if;
584          end if;
585          end if;
586          end if;
587          end if;
588          end if;
589 
590          lastj := lj;
591          i := i+1;
592       end loop;
593 
594       if (openbr != 0)
595       then
596          raise_application_error(-20000,arg||': unmatched () in regular expression');
597       end if;
598 
599       addpat(EOP, pat, j);
600    end;
601 
602    procedure printpat(pat in pattern) is
603       i integer;
604    begin
605       i := 1;
606       while pat(i) != EOP
607       loop
608          dbms_output.put_line('pat('||i||') = '||pat(i));
609          i := i+1;
610       end loop;
611    end;
612 
613    /* LOCATE - Determine if character 'ch' is in the character class */
614    /*          found at pat(offset).                                 */
615    function locate(ch in varchar2, pat    in pattern,
616                                    offset in integer) return boolean is
617    begin
618       -- Fix 611104
619       --   pat(offset) has #of chars in character class
620       for i in REVERSE (offset + 1)..offset+pat(offset)
621       loop
622          if (ch = pat(i))
623          then
624             return TRUE;
625          end if;
626       end loop;
627 
628       return FALSE;
629    end;
630 
631    function is_word_char(ch in varchar2) return boolean
632      is
633    begin
634       return (instr (DIGITS||LOWLET||'_'||UPPLET, ch) != 0);
635    end;
636 
637    function omatch(line  in     varchar2,
638                    i     in out integer,
639                    pat   in     pattern,
640                    j     in     integer,
641                    flags in     varchar2 DEFAULT NULL) return boolean is
642       bump   integer;
643       line_i varchar2(5); -- MAX NO OF BYTES FOR NLS CHARSET
644       pat_j  varchar2(4);
645 
646       save_i integer;
647    begin
648       bump := -1;
649       line_i := ind(line,i);
650       pat_j  := pat(j);
651 
652       if (pat_j = CHARCHAR)
653       then
654          /* Here is a simple extension to add case-insensitive searches. */
655          /* This is the easiest place to put this, although it may not   */
656          /* be the most efficient location for it.                       */
657          if /* (flags is not null) AND */ (instr(flags,'i') != 0)
658          then
659             if (lower(line_i) = lower(pat(j+1)))
660             then
661                bump := 1;
662                in_b := FALSE;
663             end if;
664          else
665             if (line_i = pat(j+1))
666             then
667                bump := 1;
668                in_b := FALSE;
669             end if;
670          end if;
671       else if (pat_j = BOL)
672       then
673          if ( (i = 1) OR (ind(line,i-1) = NEWLINE) )
674          then
675             bump := 0;
676             in_b := FALSE;
677          end if;
678       else if (pat_j = ANY_CHAR)
679       then
680          if (line_i != NEWLINE)
681          then
682             bump := 1;
683             in_b := FALSE;
684          end if;
685       else if (pat_j = EOL)
686       then
687          if (line_i = NEWLINE) OR (i > length(line))
688          then
689             bump := 0;
690             in_b := FALSE;
691          end if;
692       else if (pat_j = BOUND_ESCAPE)
693       then
694          if ( i = 1 ) OR (i > length(line))
695          then
696             bump := 0;
697             in_b := TRUE;
698          else
699          if ( ( is_word_char(line_i) AND
700                 NOT is_word_char(ind(line,i-1)) )
701             OR
702               ( is_word_char(ind(line,i-1)) AND
703                 NOT is_word_char(line_i) ) )
704          then
705             bump := 0;
706             in_b := TRUE;
707          end if;
708          end if;
709       else if (pat_j = DIG_ESCAPE)
710       then
711          if (instr(DIGITS, line_i) != 0)
712          then
713             bump := 1;
714             in_b := FALSE;
715          end if;
716       else if (pat_j = NON_DIG_ESCAPE)
717       then
718          if (instr(DIGITS, line_i) = 0)
719          then
720             bump := 1;
721             in_b := FALSE;
722          end if;
723       else if (pat_j = WORD_ESCAPE)
724       then
725          if (instr(WORD_CHARS, line_i) != 0)
726          then
727             bump := 1;
728             in_b := FALSE;
729          end if;
730       else if (pat_j = NON_WORD_ESCAPE)
731       then
732          if (instr(WORD_CHARS, line_i) = 0)
733          then
734             bump := 1;
735             in_b := FALSE;
736          end if;
737       else if (pat_j = SPACE_ESCAPE)
738       then
739          if (instr(SPACE_CHARS, line_i) != 0)
740          then
741             bump := 1;
742             in_b := FALSE;
743          end if;
744       else if (pat_j = NON_SPACE_ESCAPE)
745       then
746          if (instr(SPACE_CHARS, line_i) = 0)
747          then
748             bump := 1;
749             in_b := FALSE;
750          end if;
751       else if (pat_j = CCL)
752       then
753          if (locate(line_i, pat, j+1) = TRUE)
754          then
755             bump := 1;
756             in_b := FALSE;
757          end if;
758       else if (pat_j = NCCL)
759       then
760          if (line_i != NEWLINE) AND (locate(line_i, pat, j+1) = FALSE)
761          then
762             bump :=1;
763             in_b := FALSE;
764          end if;
765       else
766          raise_application_error(-20001,'In omatch: illegal pattern found');
767       end if;
768       end if;
769       end if;
770       end if;
771       end if;
772       end if;
773       end if;
774       end if;
775       end if;
776       end if;
777       end if;
778       end if;
779       end if;
780 
781       if (bump >= 0)
782       then
783          i := i + bump;
784          return TRUE;
785       else
786          /* We just validated a word-boundary match the last time through. */
787          /* Here we chew up as much whitespace as is necessary.            */
788          if (in_b)
789          then
790             if (NOT is_word_char(ind(line,i)))
791             then
792                save_i := i;
793                i := i + 1;
794                if (omatch(line, i, pat, j, flags) = TRUE)
795                then
796                   in_b := FALSE;
797                   return TRUE;
798                else
799                   i := save_i;
800                   in_b := FALSE;
801                   return FALSE;
802                end if;
803             end if;
804             in_b := FALSE;
805          end if;
806       end if;
807 
808       return FALSE;
809    end;
810 
811    procedure clo_backoff(pat    in out pattern,
812                          j      in out integer,
813                          stack  in out integer,
814                          offset in out integer) is
815    begin
816       while (stack > 0) AND
817             (pat(stack+COUNT_IND) <= pat(stack+MIN_IND))
818       loop
819          stack := pat(stack + PREVCL_IND);
820       end loop;
821 
822       if (stack > 0)
823       then
824          pat(stack + COUNT_IND) := pat(stack + COUNT_IND) - 1;
825          j := stack + CLOSIZE;
826          offset := pat(stack + START_IND) + pat(stack + COUNT_IND);
827       end if;
828    end;
829 
830    procedure br_backoff(pat    in out pattern,
831                         j      in     integer,
832                         lastbr in out integer) is
833    begin
834       while (lastbr > j)
835       loop
836          pat(lastbr + LOC_IND) := 0;
837          lastbr := pat(lastbr + PREVBR_IND);
838       end loop;
839    end;
840 
841    function amatch(line     in     varchar2,
842                    from_loc in     integer,
843                    pat      in out pattern,
844                    backrefs    out owa_text.vc_arr,
845                    flags    in     varchar2 DEFAULT NULL) return integer is
846       i      integer;
847       j      integer;
848       offset integer;
849       stack  integer;
850 
851       openbr integer;
852       lastbr integer;
853    begin
854       lastbr := 0;
855       stack := 0;
856       offset := from_loc;
857 
858       j := 1;
859       while (pat(j) != EOP)
860       loop
861          if (pat(j) = CLOSURE)
862          then
863             stack := j;
864             j := j + CLOSIZE;
865 
866             i := offset;
867             if (pat(stack + MAX_IND) is NULL)
868             then
869                while (i <= length(line)) AND
870                      (omatch(line, i, pat, j, flags) = TRUE)
871                loop null;
872                end loop;
873             else
874                while (i <= length(line)) AND
875                      (i - offset < pat(stack + MAX_IND)) AND
876                      (omatch(line, i, pat, j, flags) = TRUE)
877                loop null;
878                end loop;
879             end if;
880 
881             -- Check if we matched enough values.  If not, then back off.
882             if ((i - offset) >= pat(stack + MIN_IND))
883             then
884                pat(stack + COUNT_IND) := i - offset;
885                pat(stack + START_IND) := offset;
886                offset := i;
887             else
888                j := stack;
889                stack := pat(stack + PREVCL_IND);
890 
891                clo_backoff(pat, j, stack, offset);
892                br_backoff(pat, j, lastbr);
893                if (stack <= 0) then return 0; end if;
894            end if;
895          else if (pat(j) in (BR, BREND))
896          then
897             pat(j + LOC_IND) := offset;
898             lastbr := j;
899          else if (omatch(line, offset, pat, j, flags) = FALSE)
900          then
901             clo_backoff(pat, j, stack, offset);
902             br_backoff(pat, j, lastbr);
903             if (stack <= 0) then return 0; end if;
904          end if;
905          end if;
906          end if;
907 
908          j := j + patsize(pat,j);
909       end loop;
910 
911       while (lastbr > 0)
912       loop
913          if (pat(lastbr) = BREND)
914          then
915             openbr := pat(lastbr + OPENBR_IND);
916 
917             backrefs(pat(openbr+BRNO_IND)) :=
918                substr(line, to_number(pat(openbr+LOC_IND)),
919                             to_number(pat(lastbr+LOC_IND))
920 	                    - to_number(pat(openbr+LOC_IND)));
921          end if;
922 
923          lastbr := pat(lastbr+PREVBR_IND);
924       end loop;
925 
926       return offset;
927    end;
928 
929    function amatch(line     in     varchar2,
930                    from_loc in     integer,
931                    pat      in     varchar2,
932                    backrefs    out owa_text.vc_arr,
933                    flags    in     varchar2 DEFAULT NULL) return integer is
934       p pattern;
935    begin
936       getpat(pat, p);
937       return amatch(line, from_loc, p, backrefs, flags);
938    end;
939 
940    function amatch(line     in     varchar2,
941                    from_loc in     integer,
942                    pat      in out pattern,
943                    flags    in     varchar2 DEFAULT NULL) return integer is
944       backrefs owa_text.vc_arr;
945    begin
946       return amatch(line, from_loc, pat, backrefs, flags);
947    end;
948 
949    function amatch(line     in varchar2,
950                    from_loc in integer,
951                    pat      in varchar2,
952                    flags    in varchar2 DEFAULT NULL) return integer is
953       p pattern;
954    begin
955       getpat(pat, p);
956       return amatch(line, from_loc, p, flags);
957    end;
958 
959    function match(line     in     varchar2,
960                   pat      in out pattern,
961                   backrefs    out owa_text.vc_arr,
962                   flags    in     varchar2 DEFAULT NULL) return boolean is
963    begin
964       for i in 1..line_len(line)
965       loop
966          if (amatch(line, i, pat, backrefs, flags) > 0)
967          then return TRUE;
968          end if;
969       end loop;
970 
971       return FALSE;
972    end;
973 
974    function match(line     in     varchar2,
975                   pat      in     varchar2,
976                   backrefs    out owa_text.vc_arr,
977                   flags    in     varchar2 DEFAULT NULL) return boolean is
978       p pattern;
979    begin
980       getpat(pat, p);
981       return match(line, p, backrefs, flags);
982    end;
983 
984    function match(line  in     varchar2,
985                   pat   in out pattern,
986                   flags in     varchar2 DEFAULT NULL) return boolean is
987       backrefs owa_text.vc_arr;
988    begin
989       return match(line, pat, backrefs, flags);
990    end;
991 
992    function match(line  in varchar2,
993                   pat   in varchar2,
994                   flags in varchar2 DEFAULT NULL) return boolean is
995       p pattern;
996    begin
997       getpat(pat, p);
998       return match(line, p, flags);
999    end;
1000 
1001    function match(mline  in     owa_text.multi_line,
1002                   pat    in out pattern,
1003                   rlist     out owa_text.row_list,
1004                   flags  in     varchar2 DEFAULT NULL) return boolean is
1005       temp_rlist owa_text.row_list;
1006    begin
1007       temp_rlist := owa_text.new_row_list;
1008 
1009       for i in 1..mline.num_rows
1010       loop
1011          if match(mline.rows(i), pat, flags)
1012          then
1013             temp_rlist.num_rows := temp_rlist.num_rows + 1;
1014             temp_rlist.rows(temp_rlist.num_rows) := i;
1015          end if;
1016       end loop;
1017 
1018       rlist := temp_rlist;
1019 
1020       return temp_rlist.num_rows > 0;
1021    end;
1022 
1023    function match(mline in     owa_text.multi_line,
1024                   pat   in     varchar2,
1025                   rlist    out owa_text.row_list,
1026                   flags in     varchar2 DEFAULT NULL) return boolean is
1027       p pattern;
1028    begin
1029       getpat(pat,p);
1030       return match(mline, p, rlist, flags);
1031    end;
1032 
1033 
1034    procedure catsub(line     in     varchar2,
1035                     from_loc in     integer,
1036                     to_loc   in     integer,
1037                     sub      in     substitution,
1038                     backrefs in     owa_text.vc_arr,
1039                     new      in out varchar2) is
1040       i integer;
1041       j integer;
1042    begin
1043       i := 1;
1044       while (sub(i) != EOP)
1045       loop
1046          if (sub(i) = DITTO)
1047          then
1048             new := new||substr(line,from_loc,to_loc - from_loc);
1049          else if (sub(i) = BREF)
1050          then
1051             i := i + 1;
1052             new := new||backrefs(sub(i));
1053          else
1054             new := new||sub(i);
1055          end if;
1056          end if;
1057 
1058          i := i + 1;
1059       end loop;
1060    end;
1061 
1062    procedure escsub(arg in     varchar2,
1063                     i   in out integer,
1064                     sub in out substitution,
1065                     j   in out integer) is
1066    begin
1067       if (ind(arg,i) != ESCAPE)
1068       then
1069          addpat(ind(arg,i), sub, j);
1070       else if (instr(DIGITS, ind(arg,i+1)) != 0)
1071       then
1072          addpat(BREF, sub, j);
1073          i := i + 1;
1074          addpat(get_int(arg, i), sub, j);
1075          i := i - 1; -- get_int puts i up past the last digit.
1076       else
1077          addpat(esc(arg,i), sub, j);
1078       end if;
1079       end if;
1080    end;
1081 
1082    procedure getsub(arg in varchar2, sub out substitution)  is
1083       i integer;
1084       j integer;
1085       s substitution;
1086    begin
1087       j := 1;
1088       i := 1;
1089       while (i <= length(arg))
1090       loop
1091          if ind(arg,i) = AMP
1092          then
1093             addpat(DITTO, s, j);
1094          else
1095             escsub(arg, i, s, j);
1096          end if;
1097 
1098          i := i + 1;
1099       end loop;
1100 
1101       addpat(EOP, s, j);
1102       sub := s;
1103    end;
1104 
1105    function change(line     in out varchar2,
1106                    from_str in     varchar2,
1107                    to_str   in     varchar2,
1108                    flags    in     varchar2 DEFAULT NULL) return integer is
1109       p     pattern;
1110       s     substitution;
1111       i     integer;
1112       m     integer;
1113       lastm integer;
1114       new   varchar2(32767); -- MAX_VC_LEN
1115 
1116       backrefs owa_text.vc_arr;
1117 
1118       num_matches integer;
1119    begin
1120       getpat(from_str,p);
1121       getsub(to_str,s);
1122 
1123       num_matches := 0;
1124       lastm := 0;
1125       i := 1;
1126       while (i <= line_len(line))
1127       loop
1128          m := amatch(line, i , p, backrefs, flags);
1129          if (m > 0) AND (lastm != m)
1130          then
1131             num_matches := num_matches + 1;
1132             catsub(line, i, m, s, backrefs, new);
1133 
1134             /* New code make the default behavior to be change 1st match. */
1135             /* Enhancement to Kernighan's code.                           */
1136             if (flags IS NULL) OR (instr(flags,'g') = 0)
1137             then
1138                new := new||substr(line,m);
1139                exit;
1140             end if;
1141             /* End enhancement code */
1142 
1143             lastm := m;
1144          end if;
1145 
1146          if (m in (0,i))
1147          then
1148             new := new||ind(line,i);
1149             i := i + 1;
1150          else
1151             i := m;
1152          end if;
1153       end loop;
1154 
1155       line := new;
1156       return num_matches;
1157    end;
1158 
1159    procedure change(line     in out varchar2,
1160                     from_str in     varchar2,
1161                     to_str   in     varchar2,
1162                     flags    in     varchar2 DEFAULT NULL) is
1163       ignore integer;
1164    begin
1165       ignore := change(line, from_str, to_str, flags);
1166    end;
1167 
1168    function change(mline    in out owa_text.multi_line,
1169                    from_str in     varchar2,
1170                    to_str   in     varchar2,
1171                    flags    in     varchar2 DEFAULT NULL) return integer is
1172       num_matches integer;
1173    begin
1174       num_matches := 0;
1175 
1176       for i in 1..mline.num_rows
1177       loop
1178          num_matches := num_matches +
1179                           change(mline.rows(i), from_str, to_str, flags);
1180       end loop;
1181 
1182       return num_matches;
1183    end;
1184 
1185    procedure change(mline     in out owa_text.multi_line,
1186                     from_str in     varchar2,
1187                     to_str   in     varchar2,
1188                     flags    in     varchar2 DEFAULT NULL) is
1189       ignore integer;
1190    begin
1191       ignore := change(mline, from_str, to_str, flags);
1192    end;
1193 
1194 end;