/* Portable, bounded PDF regular-expression search.
 * The matcher is a Thompson NFA: it has no recursive backtracking and does
 * not invoke an OS regex library or an external command. */
#define B2026_RX_MAX_PATTERN 65536U
#define B2026_RX_MAX_STATES 65536
#define B2026_RX_MAX_DEPTH 256
#define B2026_RX_MAX_REPEAT 1000000

enum { B2026_RX_EMPTY=1,B2026_RX_LITERAL,B2026_RX_ANY,B2026_RX_CLASS,B2026_RX_BOL,B2026_RX_EOL,
       B2026_RX_CONCAT,B2026_RX_ALT,B2026_RX_REPEAT };
enum { B2026_RX_JUMP=1,B2026_RX_S_CHAR,B2026_RX_S_ANY,B2026_RX_S_CLASS,B2026_RX_S_SPLIT,
       B2026_RX_S_MATCH,B2026_RX_S_BOL,B2026_RX_S_EOL };
enum { B2026_RX_TERM_RANGE=1,B2026_RX_TERM_DIGIT,B2026_RX_TERM_WORD,B2026_RX_TERM_SPACE,
       B2026_RX_TERM_HAN,B2026_RX_TERM_HIRAGANA,B2026_RX_TERM_KATAKANA,B2026_RX_TERM_LETTER,
       B2026_RX_TERM_NUMBER };

typedef struct { int type,left,right,min,max,lazy,class_index;unsigned int cp; } B2026RxNode;
typedef struct { int type,negate;unsigned int lo,hi; } B2026RxTerm;
typedef struct { int first,count,negate; } B2026RxClass;
typedef struct {
    const unsigned char *pattern;size_t length,pos,error_offset;int depth,error,limit;
    char detail[256];B2026PdfSearchSettings search;int dot_newline,multiline;
    B2026RxNode *nodes;int node_count,node_capacity;
    B2026RxTerm *terms;int term_count,term_capacity;
    B2026RxClass *classes;int class_count,class_capacity;
} B2026RxParser;
typedef struct { int type,out1,out2,class_index;unsigned int cp; } B2026RxState;
typedef struct { int state,which,next; } B2026RxPatch;
typedef struct { int start,head,tail; } B2026RxFrag;
typedef struct {
    B2026RxParser *parser;B2026RxState *states;int state_count,start;
    B2026RxPatch *patches;int patch_count,failed,limit;
} B2026RxCompiler;
typedef struct { int *items,count;unsigned int *seen,generation; } B2026RxList;

static void b2026_rx_error(B2026RxParser *p,size_t offset,const char *detail){
    if(p->error)return;p->error=1;p->error_offset=offset;snprintf(p->detail,sizeof(p->detail),"%s",detail!=NULL?detail:"invalid regular expression / 正規表現が正しくありません");
}
static int b2026_rx_reserve(void **data,int *capacity,int count,size_t unit){
    int cap=*capacity;void *next;if(count<=cap)return 1;if(cap==0)cap=32;while(cap<count){if(cap>B2026_RX_MAX_STATES/2){cap=B2026_RX_MAX_STATES;break;}cap*=2;}
    if(cap<count||cap>B2026_RX_MAX_STATES)return 0;next=realloc(*data,(size_t)cap*unit);if(next==NULL)return 0;*data=next;*capacity=cap;return 1;
}
static int b2026_rx_node(B2026RxParser *p,int type,int left,int right){
    B2026RxNode *n;if(p->node_count>=B2026_RX_MAX_STATES||!b2026_rx_reserve((void**)&p->nodes,&p->node_capacity,p->node_count+1,sizeof(*p->nodes))){p->limit=1;b2026_rx_error(p,p->pos,"regular expression state limit exceeded / 正規表現の状態数上限を超えました");return -1;}
    n=&p->nodes[p->node_count];memset(n,0,sizeof(*n));n->type=type;n->left=left;n->right=right;n->max=0;return p->node_count++;
}
static int b2026_rx_term(B2026RxParser *p,int type,int negate,unsigned int lo,unsigned int hi){
    B2026RxTerm *t;if(p->term_count>=B2026_RX_MAX_STATES||!b2026_rx_reserve((void**)&p->terms,&p->term_capacity,p->term_count+1,sizeof(*p->terms))){p->limit=1;b2026_rx_error(p,p->pos,"regular expression class limit exceeded / 文字集合の上限を超えました");return 0;}
    t=&p->terms[p->term_count++];t->type=type;t->negate=negate;t->lo=lo;t->hi=hi;return 1;
}
static int b2026_rx_class(B2026RxParser *p,int first,int count,int negate){
    B2026RxClass *c;if(p->class_count>=B2026_RX_MAX_STATES||!b2026_rx_reserve((void**)&p->classes,&p->class_capacity,p->class_count+1,sizeof(*p->classes))){p->limit=1;b2026_rx_error(p,p->pos,"regular expression class limit exceeded / 文字集合の上限を超えました");return -1;}
    c=&p->classes[p->class_count];c->first=first;c->count=count;c->negate=negate;return p->class_count++;
}
static int b2026_rx_hex(unsigned char c){if(c>='0'&&c<='9')return c-'0';if(c>='a'&&c<='f')return c-'a'+10;if(c>='A'&&c<='F')return c-'A'+10;return -1;}
static int b2026_rx_name_equal(const unsigned char *s,size_t n,const char *name){size_t i,m=strlen(name);if(n!=m)return 0;for(i=0;i<n;++i)if(toupper((unsigned char)s[i])!=toupper((unsigned char)name[i]))return 0;return 1;}
static unsigned int b2026_rx_fold_cp(B2026RxParser *p,unsigned int cp){if(p->search.ignore_width)cp=b2026_pdf_search_width_fold(cp);if(!p->search.case_sensitive)cp=b2026_pdf_search_case_fold(cp);return cp;}
static int b2026_rx_read_cp(B2026RxParser *p,unsigned int *cp){int span;if(p->pos>=p->length)return 0;span=utf8_char_span(p->pattern+p->pos);if(span<1||(size_t)span>p->length-p->pos)span=1;*cp=utf8_decode_codepoint(p->pattern+p->pos,span);p->pos+=(size_t)span;return 1;}
static void b2026_rx_skip_space(B2026RxParser *p){
    if(!p->search.ignore_whitespace)return;while(p->pos<p->length){size_t save=p->pos;unsigned int cp;if(!b2026_rx_read_cp(p,&cp))break;cp=p->search.ignore_width?b2026_pdf_search_width_fold(cp):cp;if(!b2026_pdf_search_space(cp)){p->pos=save;break;}}
}
static int b2026_rx_property(const unsigned char *name,size_t n){
    if(b2026_rx_name_equal(name,n,"HAN"))return B2026_RX_TERM_HAN;if(b2026_rx_name_equal(name,n,"HIRAGANA"))return B2026_RX_TERM_HIRAGANA;
    if(b2026_rx_name_equal(name,n,"KATAKANA"))return B2026_RX_TERM_KATAKANA;if(b2026_rx_name_equal(name,n,"L")||b2026_rx_name_equal(name,n,"LETTER"))return B2026_RX_TERM_LETTER;
    if(b2026_rx_name_equal(name,n,"N")||b2026_rx_name_equal(name,n,"NUMBER"))return B2026_RX_TERM_NUMBER;return 0;
}
typedef struct { int kind,negate;unsigned int cp; } B2026RxToken;
static int b2026_rx_escape(B2026RxParser *p,B2026RxToken *tok){
    unsigned char c;size_t start=p->pos;memset(tok,0,sizeof(*tok));if(p->pos>=p->length){b2026_rx_error(p,start,"trailing backslash / 正規表現の末尾がバックスラッシュです");return 0;}c=p->pattern[p->pos++];
    if(c=='d'||c=='D'){tok->kind=B2026_RX_TERM_DIGIT;tok->negate=c=='D';return 1;}if(c=='w'||c=='W'){tok->kind=B2026_RX_TERM_WORD;tok->negate=c=='W';return 1;}if(c=='s'||c=='S'){tok->kind=B2026_RX_TERM_SPACE;tok->negate=c=='S';return 1;}
    if(c=='p'||c=='P'){size_t name_start;int prop;if(p->pos>=p->length||p->pattern[p->pos]!='{'){b2026_rx_error(p,start,"Unicode property requires braces / Unicodeプロパティには{}が必要です");return 0;}++p->pos;name_start=p->pos;while(p->pos<p->length&&p->pattern[p->pos]!='}')++p->pos;if(p->pos>=p->length){b2026_rx_error(p,start,"unterminated Unicode property / Unicodeプロパティが閉じられていません");return 0;}prop=b2026_rx_property(p->pattern+name_start,p->pos-name_start);++p->pos;if(prop==0){b2026_rx_error(p,name_start,"unsupported Unicode property / 未対応のUnicodeプロパティです");return 0;}tok->kind=prop;tok->negate=c=='P';return 1;}
    tok->kind=B2026_RX_TERM_RANGE;if(c=='n')tok->cp='\n';else if(c=='r')tok->cp='\r';else if(c=='t')tok->cp='\t';
    else if(c=='x'||c=='u'){int digits=c=='x'?2:4,i,v;unsigned int cp=0U;for(i=0;i<digits;++i){if(p->pos>=p->length||(v=b2026_rx_hex(p->pattern[p->pos++]))<0){b2026_rx_error(p,start,"invalid hexadecimal escape / 16進エスケープが正しくありません");return 0;}cp=(cp<<4)|(unsigned int)v;}tok->cp=cp;}
    else if(strchr("\\.^$|?*+()[]{}- ",c)!=NULL)tok->cp=(unsigned int)c;
    else{b2026_rx_error(p,start,"unsupported escape sequence / 未対応のエスケープです");return 0;}tok->cp=b2026_rx_fold_cp(p,tok->cp);return 1;
}
static int b2026_rx_balanced(B2026RxParser *p,int type,int *items,int first,int count){int mid,left,right;if(count<=0)return b2026_rx_node(p,B2026_RX_EMPTY,-1,-1);if(count==1)return items[first];mid=count/2;left=b2026_rx_balanced(p,type,items,first,mid);right=b2026_rx_balanced(p,type,items,first+mid,count-mid);if(left<0||right<0)return -1;return b2026_rx_node(p,type,left,right);}
static int b2026_rx_parse_expression(B2026RxParser *p);
static int b2026_rx_parse_class_node(B2026RxParser *p){
    int negate=0,first=p->term_count,count=0,closed=0;size_t class_start=p->pos-1;if(p->pos<p->length&&p->pattern[p->pos]=='^'){negate=1;++p->pos;}
    while(p->pos<p->length){B2026RxToken a,b;size_t item_start;b2026_rx_skip_space(p);if(p->pos>=p->length)break;if(p->pattern[p->pos]==']'&&count>0){++p->pos;closed=1;break;}item_start=p->pos;memset(&a,0,sizeof(a));
        if(p->pattern[p->pos]=='\\'){++p->pos;if(!b2026_rx_escape(p,&a))return -1;}else{a.kind=B2026_RX_TERM_RANGE;if(!b2026_rx_read_cp(p,&a.cp))break;a.cp=b2026_rx_fold_cp(p,a.cp);}
        if(a.kind==B2026_RX_TERM_RANGE&&p->pos<p->length&&p->pattern[p->pos]=='-'&&p->pos+1<p->length&&p->pattern[p->pos+1]!=']'){++p->pos;memset(&b,0,sizeof(b));if(p->pattern[p->pos]=='\\'){++p->pos;if(!b2026_rx_escape(p,&b))return -1;}else{b.kind=B2026_RX_TERM_RANGE;if(!b2026_rx_read_cp(p,&b.cp))return -1;b.cp=b2026_rx_fold_cp(p,b.cp);}if(b.kind!=B2026_RX_TERM_RANGE){b2026_rx_error(p,item_start,"character range endpoint must be a literal / 文字範囲の終点は通常文字で指定してください");return -1;}if(a.cp>b.cp){unsigned int swap=a.cp;a.cp=b.cp;b.cp=swap;}if(!b2026_rx_term(p,B2026_RX_TERM_RANGE,0,a.cp,b.cp))return -1;}
        else{if(!b2026_rx_term(p,a.kind,a.negate,a.cp,a.cp))return -1;}++count;
    }
    if(!closed){b2026_rx_error(p,class_start,"unterminated character class / 文字集合が]で閉じられていません");return -1;}if(count==0){b2026_rx_error(p,class_start,"empty character class / 空の文字集合です");return -1;}
    {int ci=b2026_rx_class(p,first,count,negate),node;if(ci<0)return -1;node=b2026_rx_node(p,B2026_RX_CLASS,-1,-1);if(node>=0)p->nodes[node].class_index=ci;return node;}
}
static int b2026_rx_parse_atom(B2026RxParser *p){
    size_t start;unsigned char c;int node;b2026_rx_skip_space(p);if(p->pos>=p->length||p->pattern[p->pos]==')'||p->pattern[p->pos]=='|')return b2026_rx_node(p,B2026_RX_EMPTY,-1,-1);start=p->pos;c=p->pattern[p->pos++];
    if(c=='('){if(++p->depth>B2026_RX_MAX_DEPTH){p->limit=1;b2026_rx_error(p,start,"regular expression nesting limit exceeded / 正規表現の括弧の深さ上限を超えました");return -1;}if(p->pos+1<p->length&&p->pattern[p->pos]=='?'&&p->pattern[p->pos+1]==':')p->pos+=2;else if(p->pos<p->length&&p->pattern[p->pos]=='?'){b2026_rx_error(p,start,"lookaround and special groups are not supported / 先読み・後読み等の特殊グループは未対応です");return -1;}node=b2026_rx_parse_expression(p);if(p->pos>=p->length||p->pattern[p->pos]!=')'){b2026_rx_error(p,start,"unterminated group / グループが)で閉じられていません");return -1;}++p->pos;--p->depth;return node;}
    if(c=='[')return b2026_rx_parse_class_node(p);if(c=='.')return b2026_rx_node(p,B2026_RX_ANY,-1,-1);if(c=='^')return b2026_rx_node(p,B2026_RX_BOL,-1,-1);if(c=='$')return b2026_rx_node(p,B2026_RX_EOL,-1,-1);
    if(c=='\\'){B2026RxToken tok;if(!b2026_rx_escape(p,&tok))return -1;if(tok.kind==B2026_RX_TERM_RANGE){node=b2026_rx_node(p,B2026_RX_LITERAL,-1,-1);if(node>=0)p->nodes[node].cp=tok.cp;return node;}else{int first=p->term_count,ci;if(!b2026_rx_term(p,tok.kind,tok.negate,0U,0U))return -1;ci=b2026_rx_class(p,first,1,0);node=b2026_rx_node(p,B2026_RX_CLASS,-1,-1);if(node>=0)p->nodes[node].class_index=ci;return node;}}
    if(strchr("*+?{}]",c)!=NULL){b2026_rx_error(p,start,"quantifier or bracket has no preceding expression / 量指定子または括弧の位置が正しくありません");return -1;}
    {unsigned int cp;if(c<0x80U)cp=c;else{p->pos=start;if(!b2026_rx_read_cp(p,&cp))return -1;}cp=b2026_rx_fold_cp(p,cp);if(p->search.ignore_width&&p->pos<p->length){size_t save=p->pos;unsigned int next,combined;if(b2026_rx_read_cp(p,&next)){next=b2026_pdf_search_width_fold(next);combined=b2026_pdf_search_kana_compose(cp,next);if(combined!=0U)cp=combined;else p->pos=save;}}node=b2026_rx_node(p,B2026_RX_LITERAL,-1,-1);if(node>=0)p->nodes[node].cp=cp;return node;}
}
static int b2026_rx_parse_uint(B2026RxParser *p,int *value){unsigned long n=0UL;size_t start=p->pos;if(start>=p->length||!isdigit(p->pattern[start]))return 0;while(p->pos<p->length&&isdigit(p->pattern[p->pos])){n=n*10UL+(unsigned long)(p->pattern[p->pos++]-'0');if(n>B2026_RX_MAX_REPEAT){p->limit=1;b2026_rx_error(p,start,"repeat count exceeds 1000000 / 繰り返し回数が1000000を超えています");return 0;}}*value=(int)n;return 1;}
static int b2026_rx_parse_repeat(B2026RxParser *p){
    int atom=b2026_rx_parse_atom(p),min,max,lazy=0,node;size_t q;if(atom<0)return -1;b2026_rx_skip_space(p);if(p->pos>=p->length)return atom;q=p->pos;
    if(p->pattern[p->pos]=='*'){min=0;max=-1;++p->pos;}else if(p->pattern[p->pos]=='+'){min=1;max=-1;++p->pos;}else if(p->pattern[p->pos]=='?'){min=0;max=1;++p->pos;}else if(p->pattern[p->pos]=='{'){++p->pos;if(!b2026_rx_parse_uint(p,&min)){if(!p->error)b2026_rx_error(p,q,"repeat count is missing / 繰り返し回数がありません");return -1;}if(p->pos<p->length&&p->pattern[p->pos]=='}'){max=min;++p->pos;}else if(p->pos<p->length&&p->pattern[p->pos]==','){++p->pos;if(p->pos<p->length&&p->pattern[p->pos]=='}'){max=-1;++p->pos;}else{if(!b2026_rx_parse_uint(p,&max))return -1;if(p->pos>=p->length||p->pattern[p->pos]!='}'){b2026_rx_error(p,q,"unterminated repeat / 繰り返し指定が}で閉じられていません");return -1;}++p->pos;if(max<min){b2026_rx_error(p,q,"repeat maximum is smaller than minimum / 繰り返し上限が下限より小さいです");return -1;}}}else{b2026_rx_error(p,q,"invalid repeat syntax / 繰り返し指定が正しくありません");return -1;}}else return atom;
    if(p->pos<p->length&&p->pattern[p->pos]=='?'){lazy=1;++p->pos;}if(p->pos<p->length&&strchr("*+?{",p->pattern[p->pos])!=NULL){b2026_rx_error(p,p->pos,"multiple quantifiers are not allowed / 量指定子が重複しています");return -1;}node=b2026_rx_node(p,B2026_RX_REPEAT,atom,-1);if(node>=0){p->nodes[node].min=min;p->nodes[node].max=max;p->nodes[node].lazy=lazy;}return node;
}
static int b2026_rx_parse_concat(B2026RxParser *p){int *items=NULL,count=0,cap=0,result;b2026_rx_skip_space(p);while(p->pos<p->length&&p->pattern[p->pos]!=')'&&p->pattern[p->pos]!='|'){int node=b2026_rx_parse_repeat(p);if(node<0){free(items);return -1;}if(count==cap){int nc=cap?cap*2:16;int *next=(int*)realloc(items,(size_t)nc*sizeof(*items));if(next==NULL){free(items);p->limit=1;b2026_rx_error(p,p->pos,"out of memory / メモリ不足です");return -1;}items=next;cap=nc;}items[count++]=node;b2026_rx_skip_space(p);}result=b2026_rx_balanced(p,B2026_RX_CONCAT,items,0,count);free(items);return result;}
static int b2026_rx_parse_expression(B2026RxParser *p){int *items=NULL,count=0,cap=0,result;for(;;){int node=b2026_rx_parse_concat(p);if(node<0){free(items);return -1;}if(count==cap){int nc=cap?cap*2:8;int *next=(int*)realloc(items,(size_t)nc*sizeof(*items));if(next==NULL){free(items);p->limit=1;b2026_rx_error(p,p->pos,"out of memory / メモリ不足です");return -1;}items=next;cap=nc;}items[count++]=node;if(p->pos>=p->length||p->pattern[p->pos]!='|')break;++p->pos;}result=b2026_rx_balanced(p,B2026_RX_ALT,items,0,count);free(items);return result;}

static int b2026_rx_state(B2026RxCompiler *c,int type,unsigned int cp,int ci,int out1,int out2){B2026RxState *s;if(c->state_count>=B2026_RX_MAX_STATES){c->failed=1;c->limit=1;return -1;}s=&c->states[c->state_count];s->type=type;s->cp=cp;s->class_index=ci;s->out1=out1;s->out2=out2;return c->state_count++;}
static int b2026_rx_patch_new(B2026RxCompiler *c,int state,int which){int i;if(c->patch_count>=B2026_RX_MAX_STATES*2){c->failed=1;c->limit=1;return -1;}i=c->patch_count++;c->patches[i].state=state;c->patches[i].which=which;c->patches[i].next=-1;return i;}
static B2026RxFrag b2026_rx_frag(int start,int head,int tail){B2026RxFrag f;f.start=start;f.head=head;f.tail=tail;return f;}
static B2026RxFrag b2026_rx_badfrag(void){return b2026_rx_frag(-1,-1,-1);}
static void b2026_rx_patch_apply(B2026RxCompiler *c,int head,int target){while(head>=0){B2026RxPatch *p=&c->patches[head];if(p->which==1)c->states[p->state].out1=target;else c->states[p->state].out2=target;head=p->next;}}
static B2026RxFrag b2026_rx_frag_append(B2026RxCompiler *c,B2026RxFrag a,B2026RxFrag b){if(a.start<0||b.start<0)return b2026_rx_badfrag();b2026_rx_patch_apply(c,a.head,b.start);return b2026_rx_frag(a.start,b.head,b.tail);}
static void b2026_rx_out_join(B2026RxCompiler *c,B2026RxFrag *a,B2026RxFrag b){if(a->head<0){a->head=b.head;a->tail=b.tail;}else if(b.head>=0){c->patches[a->tail].next=b.head;a->tail=b.tail;}}
static B2026RxFrag b2026_rx_compile_node(B2026RxCompiler *c,int index,int depth){
    B2026RxNode *n;int s,i;if(c->failed||index<0||index>=c->parser->node_count||depth>B2026_RX_MAX_DEPTH){c->failed=1;if(depth>B2026_RX_MAX_DEPTH)c->limit=1;return b2026_rx_badfrag();}n=&c->parser->nodes[index];
    if(n->type==B2026_RX_EMPTY){int patch;s=b2026_rx_state(c,B2026_RX_JUMP,0U,0,-1,-1);patch=b2026_rx_patch_new(c,s,1);return b2026_rx_frag(s,patch,patch);}
    if(n->type==B2026_RX_LITERAL||n->type==B2026_RX_ANY||n->type==B2026_RX_CLASS||n->type==B2026_RX_BOL||n->type==B2026_RX_EOL){int patch,type=n->type==B2026_RX_LITERAL?B2026_RX_S_CHAR:n->type==B2026_RX_ANY?B2026_RX_S_ANY:n->type==B2026_RX_CLASS?B2026_RX_S_CLASS:n->type==B2026_RX_BOL?B2026_RX_S_BOL:B2026_RX_S_EOL;s=b2026_rx_state(c,type,n->cp,n->class_index,-1,-1);patch=b2026_rx_patch_new(c,s,1);return b2026_rx_frag(s,patch,patch);}
    if(n->type==B2026_RX_CONCAT){B2026RxFrag a=b2026_rx_compile_node(c,n->left,depth+1),b=b2026_rx_compile_node(c,n->right,depth+1);return b2026_rx_frag_append(c,a,b);}
    if(n->type==B2026_RX_ALT){B2026RxFrag a=b2026_rx_compile_node(c,n->left,depth+1),b=b2026_rx_compile_node(c,n->right,depth+1),out;s=b2026_rx_state(c,B2026_RX_S_SPLIT,0U,0,a.start,b.start);out=b2026_rx_frag(s,a.head,a.tail);b2026_rx_out_join(c,&out,b);return out;}
    if(n->type==B2026_RX_REPEAT){B2026RxFrag out=b2026_rx_badfrag();for(i=0;i<n->min&&!c->failed;++i){B2026RxFrag one=b2026_rx_compile_node(c,n->left,depth+1);if(out.start<0)out=one;else out=b2026_rx_frag_append(c,out,one);}if(out.start<0){int patch;s=b2026_rx_state(c,B2026_RX_JUMP,0U,0,-1,-1);patch=b2026_rx_patch_new(c,s,1);out=b2026_rx_frag(s,patch,patch);}if(n->max<0){B2026RxFrag one=b2026_rx_compile_node(c,n->left,depth+1),tail;int patch,split=b2026_rx_state(c,B2026_RX_S_SPLIT,0U,0,n->lazy?-1:one.start,n->lazy?one.start:-1);b2026_rx_patch_apply(c,one.head,split);patch=b2026_rx_patch_new(c,split,n->lazy?1:2);tail=b2026_rx_frag(split,patch,patch);out=b2026_rx_frag_append(c,out,tail);}else for(i=n->min;i<n->max&&!c->failed;++i){B2026RxFrag one=b2026_rx_compile_node(c,n->left,depth+1),optional;int split=b2026_rx_state(c,B2026_RX_S_SPLIT,0U,0,n->lazy?-1:one.start,n->lazy?one.start:-1);optional=b2026_rx_frag(split,one.head,one.tail);{int skip=b2026_rx_patch_new(c,split,n->lazy?1:2);B2026RxFrag skipf=b2026_rx_frag(split,skip,skip);b2026_rx_out_join(c,&optional,skipf);}out=b2026_rx_frag_append(c,out,optional);}return out;}
    c->failed=1;return b2026_rx_badfrag();
}
static int b2026_rx_is_digit(unsigned int cp){return b2026_rx_unicode_in_ranges(cp,b2026_rx_unicode_digit,B2026_RX_UNICODE_DIGIT_COUNT);}
static int b2026_rx_is_number(unsigned int cp){return b2026_rx_unicode_in_ranges(cp,b2026_rx_unicode_number,B2026_RX_UNICODE_NUMBER_COUNT);}
static int b2026_rx_is_han(unsigned int cp){return (cp>=0x3400U&&cp<=0x4DBFU)||(cp>=0x4E00U&&cp<=0x9FFFU)||(cp>=0xF900U&&cp<=0xFAFFU)||(cp>=0x20000U&&cp<=0x323AFU);}
static int b2026_rx_is_hiragana(unsigned int cp){return (cp>=0x3040U&&cp<=0x309FU)||cp==0x1B001U||(cp>=0x1B150U&&cp<=0x1B152U);}
static int b2026_rx_is_katakana(unsigned int cp){return (cp>=0x30A0U&&cp<=0x30FFU)||(cp>=0x31F0U&&cp<=0x31FFU)||(cp>=0x1B000U&&cp<=0x1B0FFU)||(cp>=0x1B164U&&cp<=0x1B167U);}
static int b2026_rx_is_letter(unsigned int cp){return b2026_rx_unicode_in_ranges(cp,b2026_rx_unicode_letter,B2026_RX_UNICODE_LETTER_COUNT);}
static int b2026_rx_term_match(const B2026RxTerm *t,unsigned int cp){int yes=0;if(t->type==B2026_RX_TERM_RANGE)yes=cp>=t->lo&&cp<=t->hi;else if(t->type==B2026_RX_TERM_DIGIT)yes=b2026_rx_is_digit(cp);else if(t->type==B2026_RX_TERM_NUMBER)yes=b2026_rx_is_number(cp);else if(t->type==B2026_RX_TERM_WORD)yes=b2026_rx_is_letter(cp)||b2026_rx_is_number(cp)||cp=='_';else if(t->type==B2026_RX_TERM_SPACE)yes=b2026_pdf_search_space(cp)||cp==0x00A0U||cp==0x1680U||(cp>=0x2000U&&cp<=0x200AU)||cp==0x2028U||cp==0x2029U||cp==0x202FU||cp==0x205FU||cp==0x3000U;else if(t->type==B2026_RX_TERM_HAN)yes=b2026_rx_is_han(cp);else if(t->type==B2026_RX_TERM_HIRAGANA)yes=b2026_rx_is_hiragana(cp);else if(t->type==B2026_RX_TERM_KATAKANA)yes=b2026_rx_is_katakana(cp);else if(t->type==B2026_RX_TERM_LETTER)yes=b2026_rx_is_letter(cp);return t->negate?!yes:yes;}
static int b2026_rx_class_match(B2026RxParser *p,int ci,unsigned int cp){B2026RxClass *cl;int i,yes=0;if(ci<0||ci>=p->class_count)return 0;cl=&p->classes[ci];for(i=0;i<cl->count;++i)if(b2026_rx_term_match(&p->terms[cl->first+i],cp)){yes=1;break;}return cl->negate?!yes:yes;}
static int b2026_rx_anchor_bol(B2026RxParser *p,const unsigned int *text,int pos){return pos==0||(p->multiline&&pos>0&&(text[pos-1]=='\n'||text[pos-1]=='\r'));}
static int b2026_rx_anchor_eol(B2026RxParser *p,const unsigned int *text,int len,int pos){return pos==len||(p->multiline&&pos<len&&(text[pos]=='\n'||text[pos]=='\r'));}
static void b2026_rx_addstate(B2026RxCompiler *c,B2026RxList *list,int start,const unsigned int *text,int len,int pos,int *stack){int top=0;if(start>=0)stack[top++]=start;while(top>0){int si=stack[--top];B2026RxState *s;if(si<0||si>=c->state_count||list->seen[si]==list->generation)continue;list->seen[si]=list->generation;s=&c->states[si];if(s->type==B2026_RX_JUMP){if(s->out1>=0)stack[top++]=s->out1;}else if(s->type==B2026_RX_S_SPLIT){if(s->out2>=0)stack[top++]=s->out2;if(s->out1>=0)stack[top++]=s->out1;}else if(s->type==B2026_RX_S_BOL){if(b2026_rx_anchor_bol(c->parser,text,pos)&&s->out1>=0)stack[top++]=s->out1;}else if(s->type==B2026_RX_S_EOL){if(b2026_rx_anchor_eol(c->parser,text,len,pos)&&s->out1>=0)stack[top++]=s->out1;}else list->items[list->count++]=si;}}
static int b2026_rx_has_match(B2026RxCompiler *c,B2026RxList *list){int i;for(i=0;i<list->count;++i)if(c->states[list->items[i]].type==B2026_RX_S_MATCH)return 1;return 0;}
static unsigned int *b2026_rx_codepoints(const char *text,int *length){const unsigned char *p=(const unsigned char*)(text!=NULL?text:"");unsigned int *out=NULL;int count=0,cap=0;while(*p){int span=utf8_char_span(p);unsigned int cp;if(span<1)span=1;cp=utf8_decode_codepoint(p,span);if(count==cap){int nc=cap?cap*2:256;unsigned int *next=(unsigned int*)realloc(out,(size_t)nc*sizeof(*out));if(next==NULL){free(out);return NULL;}out=next;cap=nc;}out[count++]=cp;p+=(size_t)span;}*length=count;return out;}
static int b2026_rx_execute(B2026RxCompiler *c,const char *text,double started,int *timed_out){
    unsigned int *cp;unsigned int *seen1,*seen2;int *items1,*items2,*stack,len,pos,i;unsigned long steps=0UL;B2026RxList a,b,*cur=&a,*next=&b;*timed_out=0;cp=b2026_rx_codepoints(text,&len);if(cp==NULL&&text!=NULL&&*text!='\0')return -1;
    items1=(int*)malloc((size_t)c->state_count*sizeof(int));items2=(int*)malloc((size_t)c->state_count*sizeof(int));stack=(int*)malloc((size_t)c->state_count*2U*sizeof(int));seen1=(unsigned int*)calloc((size_t)c->state_count,sizeof(unsigned int));seen2=(unsigned int*)calloc((size_t)c->state_count,sizeof(unsigned int));if(items1==NULL||items2==NULL||stack==NULL||seen1==NULL||seen2==NULL){free(cp);free(items1);free(items2);free(stack);free(seen1);free(seen2);return -1;}
    a.items=items1;a.seen=seen1;a.generation=1U;a.count=0;b.items=items2;b.seen=seen2;b.generation=1U;b.count=0;b2026_rx_addstate(c,cur,c->start,cp,len,0,stack);if(b2026_rx_has_match(c,cur))goto found;
    for(pos=0;pos<len;++pos){next->count=0;if((pos&4095)==0&&g_io_timeout_seconds>0&&(b2026_runtime_ms_now()-started)>(double)g_io_timeout_seconds*1000.0){*timed_out=1;goto not_found;}if(++next->generation==0U){memset(next->seen,0,(size_t)c->state_count*sizeof(unsigned int));next->generation=1U;}for(i=0;i<cur->count;++i){B2026RxState *s=&c->states[cur->items[i]];int yes=s->type==B2026_RX_S_CHAR?cp[pos]==s->cp:s->type==B2026_RX_S_ANY?(c->parser->dot_newline||(cp[pos]!='\n'&&cp[pos]!='\r')):s->type==B2026_RX_S_CLASS?b2026_rx_class_match(c->parser,s->class_index,cp[pos]):0;if(yes)b2026_rx_addstate(c,next,s->out1,cp,len,pos+1,stack);if(((++steps)&4095UL)==0UL&&g_io_timeout_seconds>0&&(b2026_runtime_ms_now()-started)>(double)g_io_timeout_seconds*1000.0){*timed_out=1;goto not_found;}}b2026_rx_addstate(c,next,c->start,cp,len,pos+1,stack);{B2026RxList *swap=cur;cur=next;next=swap;}if(b2026_rx_has_match(c,cur))goto found;}
not_found:free(cp);free(items1);free(items2);free(stack);free(seen1);free(seen2);return 0;
found:free(cp);free(items1);free(items2);free(stack);free(seen1);free(seen2);return 1;
}
static void b2026_pdf_regex_result(const char *target,int ok,int complete,int found,const char *status,int page_count,int searched,int text_pages,int matched,const unsigned char *matches,const char *spec,double offset,const char *detail){b2026_pdf_search_result(target,ok,complete,found,status,page_count,searched,text_pages,matched,matches,spec);b2026_pdf_search_pair_number(target,"エラー位置","errorOffset",offset);b2026_pdf_search_pair_text(target,"エラー内容","errorDetail",detail!=NULL?detail:"");}
static double b2026_pdf_regex_fail(const char *target,int code,const char *status,const char *path,double offset,const char *detail){char message[2048];b2026_pdf_regex_result(target,0,0,0,status,0,0,0,0,NULL,"",offset,detail);snprintf(message,sizeof(message),"PDF.REGEXSEARCH failed: path=%s offset=%.0f: %s",path!=NULL?path:"",offset,detail!=NULL?detail:"regular expression search failed / 正規表現検索に失敗しました");basic_set_error_logged(code,message,"PDF.REGEXSEARCH",current_source_path,current_line,"PDF");b2026_try_append_log_line("__PDF_ERROR.log",message);return -1.0;}
static int b2026_pdf_regex_settings(const B2026PdfArg *arg,B2026RxParser *p){HashVariable *h=NULL;if(!b2026_pdf_search_settings(arg,&p->search))return 0;p->dot_newline=0;p->multiline=0;if(arg==NULL)return 1;if(!b2026_pdf_tf_hash(arg,&h))return 0;return b2026_pdf_search_bool(h,"ドットを改行に一致","dotMatchesNewline",0,&p->dot_newline)&&b2026_pdf_search_bool(h,"複数行モード","multiline",0,&p->multiline);}
static double b2026_pdf_regex_search(const char *target,int argc,B2026PdfArg *args){
    B2026RxParser p;B2026RxCompiler c;B2026RxFrag frag;B2026PdfDocument doc;B2026PdfTextSettings extract;B2026PdfArg page_arg;const char *path="",*pattern="";unsigned char *selected=NULL,*matched=NULL;char *page_spec=NULL;int root,match_state,rc,i,partial=0,searched=0,text_pages=0,matched_pages=0;double result=-1.0,started=b2026_runtime_ms_now();const char *status;
    memset(&p,0,sizeof(p));memset(&c,0,sizeof(c));memset(&doc,0,sizeof(doc));b2026_pdf_search_clear(target);basic_clear_error();
    if((argc!=2&&argc!=3)||args[0].kind!=1||args[1].kind!=1||args[0].text==NULL||args[1].text==NULL||(argc==3&&args[2].kind!=3)||!b2026_pdf_regex_settings(argc==3?&args[2]:NULL,&p))return b2026_pdf_regex_fail(target,4360,"FAILED","",-1.0,"path$, regular-expression$ [, settings{}] is required / PDFパス、正規表現、任意の設定が必要です");
    path=args[0].text;pattern=args[1].text;p.pattern=(const unsigned char*)pattern;p.length=strlen(pattern);if(p.length==0U)return b2026_pdf_regex_fail(target,4361,"INVALID_PATTERN",path,0.0,"regular expression must not be empty / 正規表現は空にできません");if(p.length>B2026_RX_MAX_PATTERN)return b2026_pdf_regex_fail(target,4362,"LIMIT_EXCEEDED",path,0.0,"regular expression exceeds 64 KiB / 正規表現が64KiBを超えています");
    root=b2026_rx_parse_expression(&p);if(root>=0&&!p.error&&p.pos==p.length&&p.nodes[root].type==B2026_RX_EMPTY)b2026_rx_error(&p,0U,"regular expression is empty after normalization / 正規表現が正規化後に空になりました");if(root<0||p.error||p.pos!=p.length){if(!p.error)b2026_rx_error(&p,p.pos,"unexpected closing token / 予期しない閉じ記号です");result=b2026_pdf_regex_fail(target,p.limit?4362:4361,p.limit?"LIMIT_EXCEEDED":"INVALID_PATTERN",path,(double)p.error_offset,p.detail);goto cleanup;}
    c.parser=&p;c.states=(B2026RxState*)calloc(B2026_RX_MAX_STATES,sizeof(*c.states));c.patches=(B2026RxPatch*)calloc(B2026_RX_MAX_STATES*2,sizeof(*c.patches));if(c.states==NULL||c.patches==NULL){result=b2026_pdf_regex_fail(target,4364,"FAILED",path,-1.0,"out of memory / メモリ不足です");goto cleanup;}frag=b2026_rx_compile_node(&c,root,0);match_state=b2026_rx_state(&c,B2026_RX_S_MATCH,0U,0,-1,-1);if(c.failed||frag.start<0||match_state<0){result=b2026_pdf_regex_fail(target,4362,c.limit?"LIMIT_EXCEEDED":"FAILED",path,-1.0,c.limit?"compiled state limit exceeded / コンパイル後の状態数上限を超えました":"regular expression compilation failed / 正規表現のコンパイルに失敗しました");goto cleanup;}b2026_rx_patch_apply(&c,frag.head,match_state);c.start=frag.start;
    rc=b2026_pdf_load(&doc,path,0);if(rc<=0){int timed_out=g_last_error.timeout!=0;const char *detail=b2026_pdf_load_error(rc);if(timed_out){char message[2048];b2026_pdf_regex_result(target,0,0,0,"TIMEOUT",0,0,0,0,NULL,"",-1.0,"PDF read timed out / PDF読み込みがタイムアウトしました");snprintf(message,sizeof(message),"PDF.REGEXSEARCH timeout: path=%s",path);b2026_try_append_log_line("__PDF_ERROR.log",message);result=-1.0;goto cleanup;}result=b2026_pdf_regex_fail(target,4365,"FAILED",path,-1.0,detail);goto cleanup;}
    memset(&page_arg,0,sizeof(page_arg));page_arg.kind=1;page_arg.text=p.search.page_spec;if(!b2026_pdf_parse_page_spec(&page_arg,doc.page_count,&selected)){result=b2026_pdf_regex_fail(target,4366,"FAILED",path,-1.0,"invalid target pages / 対象ページの指定が正しくありません");goto cleanup;}matched=(unsigned char*)calloc((size_t)doc.page_count,1U);if(matched==NULL){result=b2026_pdf_regex_fail(target,4364,"FAILED",path,-1.0,"out of memory / メモリ不足です");goto cleanup;}
    extract.coordinate_order=1;extract.space=1;extract.line_break=1;extract.tolerance=-1.0;extract.separator="\f";for(i=0;i<doc.page_count;++i)if(selected[i]){char *text,*normalized;int found,timed_out=0;++searched;text=b2026_pdf_text_page(&doc,i,&extract,path,&partial);if(text==NULL){result=b2026_pdf_regex_fail(target,4364,"FAILED",path,-1.0,"text extraction failed / 文字抽出に失敗しました");goto cleanup;}if(*text!='\0')++text_pages;normalized=b2026_pdf_search_normalize(text,&p.search);free(text);if(normalized==NULL){result=b2026_pdf_regex_fail(target,4364,"FAILED",path,-1.0,"out of memory / メモリ不足です");goto cleanup;}found=b2026_rx_execute(&c,normalized,started,&timed_out);free(normalized);if(timed_out){double elapsed=(b2026_runtime_ms_now()-started)/1000.0;char message[2048];basic_set_io_timeout_error("PDF.REGEXSEARCH",path,"SEARCH","NOT_APPLIED",elapsed);b2026_pdf_regex_result(target,0,0,0,"TIMEOUT",doc.page_count,searched,text_pages,0,NULL,"",-1.0,"regular expression search timed out / 正規表現検索がタイムアウトしました");snprintf(message,sizeof(message),"PDF.REGEXSEARCH timeout: path=%s",path);b2026_try_append_log_line("__PDF_ERROR.log",message);result=-1.0;goto cleanup;}if(found<0){result=b2026_pdf_regex_fail(target,4364,"FAILED",path,-1.0,"out of memory / メモリ不足です");goto cleanup;}if(found){matched[i]=1;++matched_pages;}}
    page_spec=b2026_pdf_search_page_spec(matched,doc.page_count);if(page_spec==NULL){result=b2026_pdf_regex_fail(target,4364,"FAILED",path,-1.0,"out of memory / メモリ不足です");goto cleanup;}status=partial?"PARTIAL":(matched_pages>0?"FOUND":(text_pages>0?"NOT_FOUND":"NO_TEXT"));b2026_pdf_regex_result(target,1,partial?0:1,matched_pages>0,status,doc.page_count,searched,text_pages,matched_pages,matched,page_spec,-1.0,"");result=(double)matched_pages;
cleanup:free(page_spec);free(matched);free(selected);b2026_pdf_document_free(&doc);free(c.states);free(c.patches);free(p.nodes);free(p.terms);free(p.classes);return result;
}
