text: #include <stddef.h>
  #include "jam.h"
  #include "regexp.h"
  #include "hash.h"
  
  #include "object.h"
  #include "lists.h"
  #include "compile.h"
  #include "frames.h"
  #include "builtins.h"
  
  struct regex_entry
  {
      OBJECT* pattern;
      regexp* regex;
  };
  typedef struct regex_entry regex_entry;
  
  static struct hash* regex_hash;
  
  regexp* regex_compile( OBJECT* pattern )
  {
      int found;
      regex_entry * e ;
  
      if ( !regex_hash )
          regex_hash = hashinit(sizeof(regex_entry), "regex");
  
      e = (regex_entry *)hash_insert( regex_hash, pattern, &found );
      if ( !found )
      {
          e->pattern = object_copy( pattern );
          e->regex = regcomp( (char*)pattern );
      }
  
      return e->regex;
  }
  
  LIST * builtin_subst( FRAME * frame, int flags )
  {
    LIST* result = L0;
    LIST* arg1 = lol_get( frame->args, 0 );
    LISTITER iter = list_begin( arg1 ), end = list_end( arg1 );
  
    if ( iter != end && list_next( iter ) != end && list_next( list_next( iter ) ) != end )
    {
  
        const char* source = object_str( list_item( iter ) );
        OBJECT * pattern = list_item( list_next( iter ) );
        regexp* repat = regex_compile( pattern );
  
        if ( regexec( repat, (char*)source) )
        {
            LISTITER subst = list_next( iter );
  
            while ( ( subst = list_next( subst ) ) != end )
            {
  # define BUFLEN 4096
                char buf[BUFLEN + 1];
                const char* in = object_str( list_item( subst ) );
                char* out = buf;
  
                for ( ; *in && out < buf + BUFLEN; ++in )
                {
                    if ( *in == '\\' || *in == '$' )
                    {
                        ++in;
                        if ( *in == 0 )
                        {
                            break;
                        }
                        else if ( *in >= '0' && *in <= '9' )
                        {
                            unsigned n = *in - '0';
                            const size_t srclen = repat->endp[n] - repat->startp[n];
                            const size_t remaining = buf + BUFLEN - out;
                            const size_t len = srclen < remaining ? srclen : remaining;
                            memcpy( out, repat->startp[n], len );
                            out += len;
                            continue;
                        }
                        /* fall through and copy the next character */
                    }
                    *out++ = *in;
                }
                *out = 0;
  
                result = list_push_back( result, object_new( buf ) );
  #undef BUFLEN
            }
        }
    }
  
    return result;
  }
  
  
  static void free_regex( void * xregex, void * data )
  {
      regex_entry * regex = (regex_entry *)xregex;
      object_free( regex->pattern );
      BJAM_FREE( regex->regex );
  }
  
  
  void regex_done()
  {
      if ( regex_hash )
      {
          hashenumerate( regex_hash, free_regex, (void *)0 );
          hashdone( regex_hash );
      }
  }
  