Blame regex.c

Packit Bot a3ac83
/* regex.c: regular expression interface routines for the ed line editor. */
Packit Bot a3ac83
/*  GNU ed - The GNU line editor.
Packit Bot a3ac83
    Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
Packit Bot a3ac83
    Copyright (C) 2006-2017 Antonio Diaz Diaz.
Packit Bot a3ac83
Packit Bot a3ac83
    This program is free software: you can redistribute it and/or modify
Packit Bot a3ac83
    it under the terms of the GNU General Public License as published by
Packit Bot a3ac83
    the Free Software Foundation, either version 2 of the License, or
Packit Bot a3ac83
    (at your option) any later version.
Packit Bot a3ac83
Packit Bot a3ac83
    This program is distributed in the hope that it will be useful,
Packit Bot a3ac83
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Bot a3ac83
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Bot a3ac83
    GNU General Public License for more details.
Packit Bot a3ac83
Packit Bot a3ac83
    You should have received a copy of the GNU General Public License
Packit Bot a3ac83
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Bot a3ac83
*/
Packit Bot a3ac83
Packit Bot a3ac83
#include <stddef.h>
Packit Bot a3ac83
#include <errno.h>
Packit Bot a3ac83
#include <sys/types.h>
Packit Bot a3ac83
#include <regex.h>
Packit Bot a3ac83
#include <stdio.h>
Packit Bot a3ac83
#include <stdlib.h>
Packit Bot a3ac83
#include <string.h>
Packit Bot a3ac83
Packit Bot a3ac83
#include "ed.h"
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static regex_t * subst_regex_ = 0;	/* regex of previous substitution */
Packit Bot a3ac83
Packit Bot a3ac83
static char * rbuf = 0;		/* replacement buffer */
Packit Bot a3ac83
static int rbufsz = 0;		/* replacement buffer size */
Packit Bot a3ac83
static int rlen = 0;		/* replacement length */
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
bool subst_regex( void ) { return subst_regex_ != 0; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* translate characters in a string */
Packit Bot a3ac83
static void translit_text( char * p, int len, const char from, const char to )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  while( --len >= 0 )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( *p == from ) *p = to;
Packit Bot a3ac83
    ++p;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* overwrite newlines with ASCII NULs */
Packit Bot a3ac83
static void newline_to_nul( char * const s, const int len )
Packit Bot a3ac83
  { translit_text( s, len, '\n', '\0' ); }
Packit Bot a3ac83
Packit Bot a3ac83
/* overwrite ASCII NULs with newlines */
Packit Bot a3ac83
static void nul_to_newline( char * const s, const int len )
Packit Bot a3ac83
  { translit_text( s, len, '\0', '\n' ); }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* expand a POSIX character class */
Packit Bot a3ac83
static const char * parse_char_class( const char * p )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  char c, d;
Packit Bot a3ac83
Packit Bot a3ac83
  if( *p == '^' ) ++p;
Packit Bot a3ac83
  if( *p == ']' ) ++p;
Packit Bot a3ac83
  for( ; *p != ']' && *p != '\n'; ++p )
Packit Bot a3ac83
    if( *p == '[' && ( ( d = p[1] ) == '.' || d == ':' || d == '=' ) )
Packit Bot a3ac83
      for( ++p, c = *++p; *p != ']' || c != d; ++p )
Packit Bot a3ac83
        if( ( c = *p ) == '\n' )
Packit Bot a3ac83
          return 0;
Packit Bot a3ac83
  return ( ( *p == ']' ) ? p : 0 );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* copy a pattern string from the command buffer; return pointer to the copy */
Packit Bot a3ac83
static char * extract_pattern( const char ** const ibufpp, const char delimiter )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  const char * nd = *ibufpp;
Packit Bot a3ac83
  int len;
Packit Bot a3ac83
Packit Bot a3ac83
  while( *nd != delimiter && *nd != '\n' )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( *nd == '[' )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      nd = parse_char_class( ++nd );
Packit Bot a3ac83
      if( !nd ) { set_error_msg( "Unbalanced brackets ([])" ); return 0; }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else if( *nd == '\\' && *++nd == '\n' )
Packit Bot a3ac83
      { set_error_msg( "Trailing backslash (\\)" ); return 0; }
Packit Bot a3ac83
    ++nd;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  len = nd - *ibufpp;
Packit Bot a3ac83
  if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
Packit Bot a3ac83
  memcpy( buf, *ibufpp, len );
Packit Bot a3ac83
  buf[len] = 0;
Packit Bot a3ac83
  *ibufpp = nd;
Packit Bot a3ac83
  if( isbinary() ) nul_to_newline( buf, len );
Packit Bot a3ac83
  return buf;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return pointer to compiled regex from command buffer, or to previous
Packit Bot a3ac83
   compiled regex if empty RE. return 0 if error */
Packit Bot a3ac83
static regex_t * get_compiled_regex( const char ** const ibufpp,
Packit Bot a3ac83
                                     const bool test_delimiter )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static regex_t store[2];		/* space for two compiled regexes */
Packit Bot a3ac83
  static regex_t * exp = 0;
Packit Bot a3ac83
  const char * pat;
Packit Bot a3ac83
  const char delimiter = **ibufpp;
Packit Bot a3ac83
  int n;
Packit Bot a3ac83
Packit Bot a3ac83
  if( delimiter == ' ' )
Packit Bot a3ac83
    { set_error_msg( "Invalid pattern delimiter" ); return 0; }
Packit Bot a3ac83
  if( delimiter == '\n' || *++*ibufpp == delimiter ||
Packit Bot a3ac83
      ( **ibufpp == '\n' && !test_delimiter ) )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( !exp ) set_error_msg( "No previous pattern" );
Packit Bot a3ac83
    return exp;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  pat = extract_pattern( ibufpp, delimiter );
Packit Bot a3ac83
  if( !pat ) return 0;
Packit Bot a3ac83
  if( test_delimiter && delimiter != **ibufpp )
Packit Bot a3ac83
    { set_error_msg( "Missing pattern delimiter" ); return 0; }
Packit Bot a3ac83
  /* exp compiled && not copied */
Packit Bot a3ac83
  if( exp && exp != subst_regex_ ) regfree( exp );
Packit Bot a3ac83
  else exp = ( &store[0] != subst_regex_ ) ? &store[0] : &store[1];
Packit Bot a3ac83
  n = regcomp( exp, pat, 0 );
Packit Bot a3ac83
  if( n )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    char buf[80];
Packit Bot a3ac83
    regerror( n, exp, buf, sizeof buf );
Packit Bot a3ac83
    set_error_msg( buf );
Packit Bot a3ac83
    exp = 0;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return exp;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
bool set_subst_regex( const char ** const ibufpp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  regex_t * exp;
Packit Bot a3ac83
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  exp = get_compiled_regex( ibufpp, true );
Packit Bot a3ac83
  if( exp && exp != subst_regex_ )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( subst_regex_ ) regfree( subst_regex_ );
Packit Bot a3ac83
    subst_regex_ = exp;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  return ( exp ? true : false );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* add line matching a regular expression to the global-active list */
Packit Bot a3ac83
bool build_active_list( const char ** const ibufpp, const int first_addr,
Packit Bot a3ac83
                        const int second_addr, const bool match )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const regex_t * exp;
Packit Bot a3ac83
  const line_t * lp;
Packit Bot a3ac83
  int addr;
Packit Bot a3ac83
  const char delimiter = **ibufpp;
Packit Bot a3ac83
Packit Bot a3ac83
  if( delimiter == ' ' || delimiter == '\n' )
Packit Bot a3ac83
    { set_error_msg( "Invalid pattern delimiter" ); return false; }
Packit Bot a3ac83
  exp = get_compiled_regex( ibufpp, false );
Packit Bot a3ac83
  if( !exp ) return false;
Packit Bot a3ac83
  if( **ibufpp == delimiter ) ++*ibufpp;
Packit Bot a3ac83
  clear_active_list();
Packit Bot a3ac83
  lp = search_line_node( first_addr );
Packit Bot a3ac83
  for( addr = first_addr; addr <= second_addr; ++addr, lp = lp->q_forw )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    char * const s = get_sbuf_line( lp );
Packit Bot a3ac83
    if( !s ) return false;
Packit Bot a3ac83
    if( isbinary() ) nul_to_newline( s, lp->len );
Packit Bot a3ac83
    if( match == !regexec( exp, s, 0, 0, 0 ) && !set_active_node( lp ) )
Packit Bot a3ac83
      return false;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return the address of the next line matching a regular expression in a
Packit Bot a3ac83
   given direction. wrap around begin/end of editor buffer if necessary */
Packit Bot a3ac83
int next_matching_node_addr( const char ** const ibufpp, const bool forward )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const regex_t * const exp = get_compiled_regex( ibufpp, false );
Packit Bot a3ac83
  int addr = current_addr();
Packit Bot a3ac83
Packit Bot a3ac83
  if( !exp ) return -1;
Packit Bot a3ac83
  do {
Packit Bot a3ac83
    addr = ( forward ? inc_addr( addr ) : dec_addr( addr ) );
Packit Bot a3ac83
    if( addr )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      const line_t * const lp = search_line_node( addr );
Packit Bot a3ac83
      char * const s = get_sbuf_line( lp );
Packit Bot a3ac83
      if( !s ) return -1;
Packit Bot a3ac83
      if( isbinary() ) nul_to_newline( s, lp->len );
Packit Bot a3ac83
      if( !regexec( exp, s, 0, 0, 0 ) ) return addr;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  while( addr != current_addr() );
Packit Bot a3ac83
  set_error_msg( "No match" );
Packit Bot a3ac83
  return -1;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* extract substitution replacement from the command buffer */
Packit Bot a3ac83
bool extract_replacement( const char ** const ibufpp, const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;		/* temporary buffer */
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  int i = 0;
Packit Bot a3ac83
  const char delimiter = **ibufpp;
Packit Bot a3ac83
Packit Bot a3ac83
  if( delimiter == '\n' )
Packit Bot a3ac83
    { set_error_msg( "Missing pattern delimiter" ); return false; }
Packit Bot a3ac83
  ++*ibufpp;
Packit Bot a3ac83
  if( **ibufpp == '%' && ( (*ibufpp)[1] == delimiter || (*ibufpp)[1] == '\n' ) )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    ++*ibufpp;
Packit Bot a3ac83
    if( !rbuf ) { set_error_msg( "No previous substitution" ); return false; }
Packit Bot a3ac83
    return true;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  while( **ibufpp != delimiter )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( **ibufpp == '\n' )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( isglobal && (*ibufpp)[1] != 0 )
Packit Bot a3ac83
        { set_error_msg( "Invalid newline substitution" ); return false; }
Packit Bot a3ac83
      break;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return false;
Packit Bot a3ac83
    if( ( buf[i++] = *(*ibufpp)++ ) == '\\' &&
Packit Bot a3ac83
        ( buf[i++] = *(*ibufpp)++ ) == '\n' && !isglobal )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      /* not reached if isglobal; in command-list, newlines are unescaped */
Packit Bot a3ac83
      int size = 0;
Packit Bot a3ac83
      *ibufpp = get_stdin_line( &size );
Packit Bot a3ac83
      if( !*ibufpp ) return false;			/* error */
Packit Bot a3ac83
      if( size <= 0 ) return false;			/* EOF */
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  /* make sure that buf gets allocated if empty replacement */
Packit Bot a3ac83
  if( !resize_buffer( &buf, &bufsz, i + 1 ) ) return false;
Packit Bot a3ac83
  buf[i] = 0;
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  { char * p = buf; buf = rbuf; rbuf = p;		/* swap buffers */
Packit Bot a3ac83
    rlen = i; i = bufsz; bufsz = rbufsz; rbufsz = i; }
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* Produce replacement text from matched text and replacement template.
Packit Bot a3ac83
   Return new offset to end of replacement text, or -1 if error. */
Packit Bot a3ac83
static int replace_matched_text( char ** txtbufp, int * const txtbufszp,
Packit Bot a3ac83
                                 const char * const txt,
Packit Bot a3ac83
                                 const regmatch_t * const rm, int offset,
Packit Bot a3ac83
                                 const int re_nsub )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const char * sub = rbuf;
Packit Bot a3ac83
Packit Bot a3ac83
  for( ; sub - rbuf < rlen; ++sub )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    int n;
Packit Bot a3ac83
    if( *sub == '&' )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      int j = rm[0].rm_so; int k = rm[0].rm_eo;
Packit Bot a3ac83
      if( !resize_buffer( txtbufp, txtbufszp, offset + k - j ) ) return -1;
Packit Bot a3ac83
      while( j < k ) (*txtbufp)[offset++] = txt[j++];
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else if( *sub == '\\' && *++sub >= '1' && *sub <= '9' &&
Packit Bot a3ac83
             ( n = *sub - '0' ) <= re_nsub )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      int j = rm[n].rm_so; int k = rm[n].rm_eo;
Packit Bot a3ac83
      if( !resize_buffer( txtbufp, txtbufszp, offset + k - j ) ) return -1;
Packit Bot a3ac83
      while( j < k ) (*txtbufp)[offset++] = txt[j++];
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( !resize_buffer( txtbufp, txtbufszp, offset + 1 ) ) return -1;
Packit Bot a3ac83
      (*txtbufp)[offset++] = *sub;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( !resize_buffer( txtbufp, txtbufszp, offset + 1 ) ) return -1;
Packit Bot a3ac83
  (*txtbufp)[offset] = 0;
Packit Bot a3ac83
  return offset;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* Produce new text with one or all matches replaced in a line.
Packit Bot a3ac83
   Return size of the new line text, 0 if no change, -1 if error */
Packit Bot a3ac83
static int line_replace( char ** txtbufp, int * const txtbufszp,
Packit Bot a3ac83
                         const line_t * const lp, const int snum )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  enum { se_max = 30 };	/* max subexpressions in a regular expression */
Packit Bot a3ac83
  regmatch_t rm[se_max];
Packit Bot a3ac83
  char * txt = get_sbuf_line( lp );
Packit Bot a3ac83
  const char * eot;
Packit Bot a3ac83
  int i = 0, offset = 0;
Packit Bot a3ac83
  const bool global = ( snum <= 0 );
Packit Bot a3ac83
  bool changed = false;
Packit Bot a3ac83
Packit Bot a3ac83
  if( !txt ) return -1;
Packit Bot a3ac83
  if( isbinary() ) nul_to_newline( txt, lp->len );
Packit Bot a3ac83
  eot = txt + lp->len;
Packit Bot a3ac83
  if( !regexec( subst_regex_, txt, se_max, rm, 0 ) )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    int matchno = 0;
Packit Bot a3ac83
    do {
Packit Bot a3ac83
      if( global || snum == ++matchno )
Packit Bot a3ac83
        {
Packit Bot a3ac83
        changed = true; i = rm[0].rm_so;
Packit Bot a3ac83
        if( !resize_buffer( txtbufp, txtbufszp, offset + i ) ) return -1;
Packit Bot a3ac83
        if( isbinary() ) newline_to_nul( txt, rm[0].rm_eo );
Packit Bot a3ac83
        memcpy( *txtbufp + offset, txt, i ); offset += i;
Packit Bot a3ac83
        offset = replace_matched_text( txtbufp, txtbufszp, txt, rm, offset,
Packit Bot a3ac83
                                       subst_regex_->re_nsub );
Packit Bot a3ac83
        if( offset < 0 ) return -1;
Packit Bot a3ac83
        }
Packit Bot a3ac83
      else
Packit Bot a3ac83
        {
Packit Bot a3ac83
        i = rm[0].rm_eo;
Packit Bot a3ac83
        if( !resize_buffer( txtbufp, txtbufszp, offset + i ) ) return -1;
Packit Bot a3ac83
        if( isbinary() ) newline_to_nul( txt, i );
Packit Bot a3ac83
        memcpy( *txtbufp + offset, txt, i ); offset += i;
Packit Bot a3ac83
        }
Packit Bot a3ac83
      txt += rm[0].rm_eo;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    while( *txt && ( !changed || ( global && rm[0].rm_eo ) ) &&
Packit Bot a3ac83
           !regexec( subst_regex_, txt, se_max, rm, REG_NOTBOL ) );
Packit Bot a3ac83
    i = eot - txt;
Packit Bot a3ac83
    if( !resize_buffer( txtbufp, txtbufszp, offset + i + 2 ) ) return -1;
Packit Bot a3ac83
    if( global && i > 0 && !rm[0].rm_eo )
Packit Bot a3ac83
      { set_error_msg( "Infinite substitution loop" ); return -1; }
Packit Bot a3ac83
    if( isbinary() ) newline_to_nul( txt, i );
Packit Bot a3ac83
    memcpy( *txtbufp + offset, txt, i );		/* tail copy */
Packit Bot a3ac83
    memcpy( *txtbufp + offset + i, "\n", 2 );
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return ( changed ? offset + i + 1 : 0 );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* for each line in a range, change text matching a regular expression
Packit Bot a3ac83
   according to a substitution template (replacement); return false if error */
Packit Bot a3ac83
bool search_and_replace( const int first_addr, const int second_addr,
Packit Bot a3ac83
                         const int snum, const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * txtbuf = 0;		/* new text of line buffer */
Packit Bot a3ac83
  static int txtbufsz = 0;		/* new text of line buffer size */
Packit Bot a3ac83
  int addr = first_addr;
Packit Bot a3ac83
  int lc;
Packit Bot a3ac83
  bool match_found = false;
Packit Bot a3ac83
Packit Bot a3ac83
  for( lc = 0; lc <= second_addr - first_addr; ++lc, ++addr )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const line_t * const lp = search_line_node( addr );
Packit Bot a3ac83
    const int size = line_replace( &txtbuf, &txtbufsz, lp, snum );
Packit Bot a3ac83
    if( size < 0 ) return false;
Packit Bot a3ac83
    if( size )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      const char * txt = txtbuf;
Packit Bot a3ac83
      const char * const eot = txtbuf + size;
Packit Bot a3ac83
      undo_t * up = 0;
Packit Bot a3ac83
      disable_interrupts();
Packit Bot a3ac83
      if( !delete_lines( addr, addr, isglobal ) )
Packit Bot a3ac83
        { enable_interrupts(); return false; }
Packit Bot a3ac83
      set_current_addr( addr - 1 );
Packit Bot a3ac83
      do {
Packit Bot a3ac83
        txt = put_sbuf_line( txt, eot - txt );
Packit Bot a3ac83
        if( !txt ) { enable_interrupts(); return false; }
Packit Bot a3ac83
        if( up ) up->tail = search_line_node( current_addr() );
Packit Bot a3ac83
        else
Packit Bot a3ac83
          {
Packit Bot a3ac83
          up = push_undo_atom( UADD, current_addr(), current_addr() );
Packit Bot a3ac83
          if( !up ) { enable_interrupts(); return false; }
Packit Bot a3ac83
          }
Packit Bot a3ac83
        }
Packit Bot a3ac83
      while( txt != eot );
Packit Bot a3ac83
      enable_interrupts();
Packit Bot a3ac83
      addr = current_addr();
Packit Bot a3ac83
      match_found = true;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( !match_found && !isglobal )
Packit Bot a3ac83
    { set_error_msg( "No match" ); return false; }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }