DBA Data[Home] [Help]

PACKAGE BODY: OWAPUB.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);
410       addpat(max_val, pat, lastj);
407       addpat(lastcl,  pat, lastj);
408       addpat(0,       pat, lastj);
409       addpat(min_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);
567             end if;
564                end if;
565             else
566                raise_application_error(-20000,arg||': nested *?+ in regular expression');
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
683             in_b := FALSE;
680          if (line_i != NEWLINE)
681          then
682             bump := 1;
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
789          then
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)
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, pat(openbr+LOC_IND),
919                             pat(lastbr+LOC_IND) - pat(openbr+LOC_IND));
920          end if;
921 
922          lastbr := pat(lastbr+PREVBR_IND);
923       end loop;
924 
925       return offset;
926    end;
927 
928    function amatch(line     in     varchar2,
929                    from_loc in     integer,
930                    pat      in     varchar2,
931                    backrefs    out owa_text.vc_arr,
932                    flags    in     varchar2 DEFAULT NULL) return integer is
933       p pattern;
934    begin
935       getpat(pat, p);
936       return amatch(line, from_loc, p, backrefs, flags);
937    end;
938 
939    function amatch(line     in     varchar2,
940                    from_loc in     integer,
941                    pat      in out pattern,
942                    flags    in     varchar2 DEFAULT NULL) return integer is
943       backrefs owa_text.vc_arr;
944    begin
945       return amatch(line, from_loc, pat, backrefs, flags);
946    end;
947 
948    function amatch(line     in varchar2,
952       p pattern;
949                    from_loc in integer,
950                    pat      in varchar2,
951                    flags    in varchar2 DEFAULT NULL) return integer is
953    begin
954       getpat(pat, p);
955       return amatch(line, from_loc, p, flags);
956    end;
957 
958    function match(line     in     varchar2,
959                   pat      in out pattern,
960                   backrefs    out owa_text.vc_arr,
961                   flags    in     varchar2 DEFAULT NULL) return boolean is
962    begin
963       for i in 1..line_len(line)
964       loop
965          if (amatch(line, i, pat, backrefs, flags) > 0)
966          then return TRUE;
967          end if;
968       end loop;
969 
970       return FALSE;
971    end;
972 
973    function match(line     in     varchar2,
974                   pat      in     varchar2,
975                   backrefs    out owa_text.vc_arr,
976                   flags    in     varchar2 DEFAULT NULL) return boolean is
977       p pattern;
978    begin
979       getpat(pat, p);
980       return match(line, p, backrefs, flags);
981    end;
982 
983    function match(line  in     varchar2,
984                   pat   in out pattern,
985                   flags in     varchar2 DEFAULT NULL) return boolean is
986       backrefs owa_text.vc_arr;
987    begin
988       return match(line, pat, backrefs, flags);
989    end;
990 
991    function match(line  in varchar2,
992                   pat   in varchar2,
993                   flags in varchar2 DEFAULT NULL) return boolean is
994       p pattern;
995    begin
996       getpat(pat, p);
997       return match(line, p, flags);
998    end;
999 
1000    function match(mline  in     owa_text.multi_line,
1001                   pat    in out pattern,
1002                   rlist     out owa_text.row_list,
1003                   flags  in     varchar2 DEFAULT NULL) return boolean is
1004       temp_rlist owa_text.row_list;
1005    begin
1006       temp_rlist := owa_text.new_row_list;
1007 
1008       for i in 1..mline.num_rows
1009       loop
1010          if match(mline.rows(i), pat, flags)
1011          then
1012             temp_rlist.num_rows := temp_rlist.num_rows + 1;
1013             temp_rlist.rows(temp_rlist.num_rows) := i;
1014          end if;
1015       end loop;
1016 
1017       rlist := temp_rlist;
1018 
1019       return temp_rlist.num_rows > 0;
1020    end;
1021 
1022    function match(mline in     owa_text.multi_line,
1023                   pat   in     varchar2,
1024                   rlist    out owa_text.row_list,
1025                   flags in     varchar2 DEFAULT NULL) return boolean is
1026       p pattern;
1027    begin
1028       getpat(pat,p);
1029       return match(mline, p, rlist, flags);
1030    end;
1031 
1032 
1033    procedure catsub(line     in     varchar2,
1034                     from_loc in     integer,
1035                     to_loc   in     integer,
1036                     sub      in     substitution,
1037                     backrefs in     owa_text.vc_arr,
1038                     new      in out varchar2) is
1039       i integer;
1040       j integer;
1041    begin
1042       i := 1;
1043       while (sub(i) != EOP)
1044       loop
1045          if (sub(i) = DITTO)
1046          then
1047             new := new||substr(line,from_loc,to_loc - from_loc);
1048          else if (sub(i) = BREF)
1049          then
1050             i := i + 1;
1051             new := new||backrefs(sub(i));
1052          else
1053             new := new||sub(i);
1054          end if;
1055          end if;
1056 
1057          i := i + 1;
1058       end loop;
1059    end;
1060 
1061    procedure escsub(arg in     varchar2,
1062                     i   in out integer,
1063                     sub in out substitution,
1064                     j   in out integer) is
1065    begin
1066       if (ind(arg,i) != ESCAPE)
1067       then
1068          addpat(ind(arg,i), sub, j);
1069       else if (instr(DIGITS, ind(arg,i+1)) != 0)
1070       then
1071          addpat(BREF, sub, j);
1072          i := i + 1;
1073          addpat(get_int(arg, i), sub, j);
1074          i := i - 1; -- get_int puts i up past the last digit.
1075       else
1076          addpat(esc(arg,i), sub, j);
1077       end if;
1078       end if;
1079    end;
1080 
1081    procedure getsub(arg in varchar2, sub out substitution)  is
1082       i integer;
1083       j integer;
1084       s substitution;
1085    begin
1086       j := 1;
1087       i := 1;
1088       while (i <= length(arg))
1089       loop
1090          if ind(arg,i) = AMP
1091          then
1092             addpat(DITTO, s, j);
1093          else
1094             escsub(arg, i, s, j);
1095          end if;
1096 
1097          i := i + 1;
1098       end loop;
1099 
1100       addpat(EOP, s, j);
1101       sub := s;
1102    end;
1103 
1104    function change(line     in out varchar2,
1105                    from_str in     varchar2,
1106                    to_str   in     varchar2,
1107                    flags    in     varchar2 DEFAULT NULL) return integer is
1108       p     pattern;
1109       s     substitution;
1110       i     integer;
1111       m     integer;
1112       lastm integer;
1113       new   varchar2(32767); -- MAX_VC_LEN
1114 
1115       backrefs owa_text.vc_arr;
1116 
1117       num_matches integer;
1118    begin
1119       getpat(from_str,p);
1120       getsub(to_str,s);
1121 
1122       num_matches := 0;
1123       lastm := 0;
1127          m := amatch(line, i , p, backrefs, flags);
1124       i := 1;
1125       while (i <= line_len(line))
1126       loop
1128          if (m > 0) AND (lastm != m)
1129          then
1130             num_matches := num_matches + 1;
1131             catsub(line, i, m, s, backrefs, new);
1135             if (flags IS NULL) OR (instr(flags,'g') = 0)
1132 
1133             /* New code make the default behavior to be change 1st match. */
1134             /* Enhancement to Kernighan's code.                           */
1136             then
1137                new := new||substr(line,m);
1138                exit;
1139             end if;
1140             /* End enhancement code */
1141 
1142             lastm := m;
1143          end if;
1144 
1145          if (m in (0,i))
1146          then
1147             new := new||ind(line,i);
1148             i := i + 1;
1149          else
1150             i := m;
1151          end if;
1152       end loop;
1153 
1154       line := new;
1155       return num_matches;
1156    end;
1157 
1158    procedure change(line     in out varchar2,
1159                     from_str in     varchar2,
1160                     to_str   in     varchar2,
1161                     flags    in     varchar2 DEFAULT NULL) is
1162       ignore integer;
1163    begin
1164       ignore := change(line, from_str, to_str, flags);
1165    end;
1166 
1167    function change(mline    in out owa_text.multi_line,
1168                    from_str in     varchar2,
1169                    to_str   in     varchar2,
1170                    flags    in     varchar2 DEFAULT NULL) return integer is
1171       num_matches integer;
1172    begin
1173       num_matches := 0;
1174 
1175       for i in 1..mline.num_rows
1176       loop
1177          num_matches := num_matches +
1178                           change(mline.rows(i), from_str, to_str, flags);
1179       end loop;
1180 
1181       return num_matches;
1182    end;
1183 
1184    procedure change(mline     in out owa_text.multi_line,
1185                     from_str in     varchar2,
1186                     to_str   in     varchar2,
1187                     flags    in     varchar2 DEFAULT NULL) is
1188       ignore integer;
1189    begin
1190       ignore := change(mline, from_str, to_str, flags);
1191    end;
1192 
1193 end;