/* APPFILE()/EXCELFILE() spreadsheet search.
 * XLSX/XLSM files are ZIP/XML documents; this implementation never launches
 * Excel, executes VBA, recalculates formulas, or follows external links. */
typedef struct B2026AppSheet {
    char *name;
    char *path;
    int index;
    int hidden;
} B2026AppSheet;

typedef struct B2026AppRange {
    char *sheet;
    int sr, sc, er, ec;
} B2026AppRange;

typedef struct B2026AppSearchSettings {
    int case_sensitive;
    int width_sensitive;
    int kana_sensitive;
    int trim_space;
    int collapse_space;
    int search_hidden;
    int max_results;
    int start_position;
    const char *application;
    const char *sheet;
    const char *sheet_regex;
    const char *range;
} B2026AppSearchSettings;

static void b2026_app_pair_num(const char *target,const char *jp,const char *en,double value){
    if(target==NULL)return;hash_set_number(target,jp,value);hash_set_number(target,en,value);
}
static void b2026_app_pair_text(const char *target,const char *jp,const char *en,const char *value){
    if(target==NULL)return;hash_set_string(target,jp,value!=NULL?value:"");hash_set_string(target,en,value!=NULL?value:"");
}
static void b2026_app_clear(const char *target){
    if(target!=NULL&&*target!='\0'){HashVariable *h=ensure_local_hash(get_hash_frame(target),target);clear_hash_entries(h);}
}
static HashEntry *b2026_app_option_entry(HashVariable *h,const char *jp,const char *en){
    HashEntry *e;if(h==NULL)return NULL;e=find_hash_entry(h,jp);if(e==NULL&&en!=NULL)e=find_hash_entry(h,en);return e;
}
static int b2026_app_option_bool(HashVariable *h,const char *jp,const char *en,int dflt,int *out){
    HashEntry *e=b2026_app_option_entry(h,jp,en);if(e==NULL){*out=dflt;return 1;}
    if(e->value.type!=TYPE_NUMBER||!isfinite(e->value.val.number))return 0;*out=e->value.val.number!=0.0;return 1;
}
static int b2026_app_option_int(HashVariable *h,const char *jp,const char *en,int dflt,int *out){
    HashEntry *e=b2026_app_option_entry(h,jp,en);double v;if(e==NULL){*out=dflt;return 1;}
    if(e->value.type!=TYPE_NUMBER||!isfinite(e->value.val.number))return 0;v=e->value.val.number;
    if(v!=(double)(int)v)return 0;*out=(int)v;return 1;
}
static int b2026_app_option_text(HashVariable *h,const char *jp,const char *en,const char **out){
    HashEntry *e=b2026_app_option_entry(h,jp,en);if(e==NULL){*out=NULL;return 1;}
    if(e->value.type==TYPE_NUMBER)return 0;*out=hash_value_as_text(&e->value);return 1;
}
static int b2026_app_settings(const char *options_name,B2026AppSearchSettings *s,int *max_was_clamped){
    HashVariable *h=NULL;memset(s,0,sizeof(*s));*max_was_clamped=0;
    s->case_sensitive=0;s->width_sensitive=0;s->kana_sensitive=1;s->search_hidden=1;s->max_results=65536;s->start_position=1;
    if(options_name!=NULL){h=find_local_hash(get_hash_frame(options_name),options_name);if(h==NULL)return 0;}
    if(!b2026_app_option_bool(h,"大文字小文字を区別","caseSensitive",0,&s->case_sensitive)||
       !b2026_app_option_bool(h,"半角全角を区別","widthSensitive",0,&s->width_sensitive)||
       !b2026_app_option_bool(h,"ひらがなカタカナを区別","kanaSensitive",1,&s->kana_sensitive)||
       !b2026_app_option_bool(h,"前後空白を無視","trimSpace",0,&s->trim_space)||
       !b2026_app_option_bool(h,"連続空白をまとめる","collapseSpace",0,&s->collapse_space)||
       !b2026_app_option_bool(h,"非表示を検索","searchHidden",1,&s->search_hidden)||
       !b2026_app_option_int(h,"最大件数","maxResults",65536,&s->max_results)||
       !b2026_app_option_int(h,"開始位置","startPosition",1,&s->start_position)||
       !b2026_app_option_text(h,"アプリケーション","application",&s->application)||
       !b2026_app_option_text(h,"シート","sheet",&s->sheet)||
       !b2026_app_option_text(h,"シート正規表現","sheetRegex",&s->sheet_regex)||
       !b2026_app_option_text(h,"検索範囲","range",&s->range))return 0;
    if(s->max_results<=0||s->start_position<=0)return 0;
    if(s->max_results>65536){s->max_results=65536;*max_was_clamped=1;}
    if(s->sheet!=NULL&&*s->sheet=='\0')s->sheet=NULL;
    if(s->sheet_regex!=NULL&&*s->sheet_regex=='\0')s->sheet_regex=NULL;
    if(s->range!=NULL&&*s->range=='\0')s->range=NULL;
    if(s->sheet!=NULL&&s->sheet_regex!=NULL)return 0;
    return 1;
}

static double b2026_app_fail(const char *target,int code,const char *path,const char *detail){
    char message[4096];b2026_app_pair_num(target,"成功","ok",0);b2026_app_pair_num(target,"完全","complete",0);
    b2026_app_pair_text(target,"状態","status","FAILED");b2026_app_pair_num(target,"該当セル数","matchedCellCount",-1);
    snprintf(message,sizeof(message),"APPFILE.SEARCH failed: path=%s: %s / 【アプリケーションファイル検索エラー】%s",
             path!=NULL?path:"",detail!=NULL?detail:"unknown error",detail!=NULL?detail:"処理できません");
    basic_set_error_logged(code,message,"APPFILE.SEARCH",current_source_path,current_line,"APPFILE");
    b2026_log_message_grouped("__APPFILE_ERROR.log",message);return -1.0;
}
static void b2026_app_warn(const char *path,const char *detail){
    char message[4096];snprintf(message,sizeof(message),"APPFILE.SEARCH warning: path=%s: %s / 【アプリケーションファイル検索注意】%s",
             path!=NULL?path:"",detail!=NULL?detail:"warning",detail!=NULL?detail:"注意事項があります");
    b2026_log_message_grouped("__APPFILE_ERROR.log",message);
}
static int b2026_app_ascii_eq(const char *a,const char *b){
    unsigned char ca,cb;if(a==NULL||b==NULL)return 0;while(*a&&*b){ca=(unsigned char)*a++;cb=(unsigned char)*b++;
        if(ca>='a'&&ca<='z')ca=(unsigned char)(ca-32);if(cb>='a'&&cb<='z')cb=(unsigned char)(cb-32);if(ca!=cb)return 0;}return *a=='\0'&&*b=='\0';
}
static int b2026_app_excel_application(const char *text){
    return text==NULL||*text=='\0'||b2026_app_ascii_eq(text,"EXCEL")||b2026_app_ascii_eq(text,"MICROSOFT EXCEL")||strcmp(text,"エクセル")==0;
}
static int b2026_app_extension_ok(const char *path){
    const char *dot=path!=NULL?strrchr(path,'.'):NULL;return dot!=NULL&&(b2026_app_ascii_eq(dot,".XLSX")||b2026_app_ascii_eq(dot,".XLSM"));
}

static void b2026_app_sheets_free(B2026AppSheet *s,int n){int i;for(i=0;i<n;++i){free(s[i].name);free(s[i].path);}free(s);}
static int b2026_app_load_sheets(const char *path,B2026AppSheet **out,int *count){
    unsigned char *wb=NULL,*rels=NULL;const char *p;B2026AppSheet *items=NULL;int n=0,index=0;
    *out=NULL;*count=0;wb=excel_style_extract_xlsx_entry(path,"xl/workbook.xml",NULL);rels=excel_style_extract_xlsx_entry(path,"xl/_rels/workbook.xml.rels",NULL);
    if(wb==NULL||rels==NULL){free(wb);free(rels);return 0;}p=(const char*)wb;
    while((p=strstr(p,"<sheet"))!=NULL){char raw[512],rid[256],target[1024],state[64];char *decoded;B2026AppSheet *next;
        if(!excel_style_is_name_delimiter(p[6])){p+=6;continue;}++index;raw[0]=rid[0]=target[0]=state[0]='\0';
        if(!excel_style_extract_attr_value(p,"name",raw,sizeof(raw))){p+=6;continue;}
        if(!excel_style_extract_attr_value(p,"r:id",rid,sizeof(rid)))excel_style_extract_attr_value(p,"id",rid,sizeof(rid));
        excel_style_extract_attr_value(p,"state",state,sizeof(state));
        if(rid[0]=='\0'||!excel_style_find_relationship_target((const char*)rels,rid,target,sizeof(target))){p+=6;continue;}
        decoded=excel_data_xml_unescape_range(raw,strlen(raw));if(decoded==NULL)decoded=strdup(raw);
        next=(B2026AppSheet*)realloc(items,sizeof(B2026AppSheet)*(size_t)(n+1));if(next==NULL){free(decoded);b2026_app_sheets_free(items,n);free(wb);free(rels);return -1;}
        items=next;memset(&items[n],0,sizeof(items[n]));items[n].name=decoded;items[n].path=strdup(target);items[n].index=index;
        items[n].hidden=(b2026_app_ascii_eq(state,"HIDDEN")||b2026_app_ascii_eq(state,"VERYHIDDEN"));if(items[n].path==NULL){b2026_app_sheets_free(items,n+1);free(wb);free(rels);return -1;}++n;p+=6;
    }
    free(wb);free(rels);*out=items;*count=n;return n>0?1:0;
}

static char *b2026_app_trim_copy(const char *start,size_t len){
    char *v;while(len>0&&(*start==' '||*start=='\t'||*start=='\r'||*start=='\n')){++start;--len;}
    while(len>0&&(start[len-1]==' '||start[len-1]=='\t'||start[len-1]=='\r'||start[len-1]=='\n'))--len;
    v=(char*)malloc(len+1);if(v==NULL)return NULL;memcpy(v,start,len);v[len]='\0';return v;
}
static const char *b2026_app_find_unquoted(const char *text,char needle){
    int quoted=0;const char *p=text;while(p!=NULL&&*p){if(*p=='\''){if(quoted&&p[1]=='\''){p+=2;continue;}quoted=!quoted;}else if(!quoted&&*p==needle)return p;++p;}return NULL;
}
static char *b2026_app_unquote_sheet(const char *text,size_t len){
    HtmlBuffer out;size_t i=0;htmlbuf_init(&out);while(len>0&&(text[0]==' '||text[0]=='\t')){++text;--len;}while(len>0&&(text[len-1]==' '||text[len-1]=='\t'))--len;
    if(len>=2&&text[0]=='\''&&text[len-1]=='\''){i=1;--len;}for(;i<len;++i){if(text[i]=='\''&&i+1<len&&text[i+1]=='\''){++i;}htmlbuf_append_char(&out,text[i]);}return htmlbuf_take(&out);
}
static void b2026_app_ranges_free(B2026AppRange *r,int n){int i;for(i=0;i<n;++i)free(r[i].sheet);free(r);}
static int b2026_app_parse_ref(const char *text,int *sc,int *sr,int *ec,int *er){
    char clean[160];size_t i=0,j=0;while(text[i]&&j+1<sizeof(clean)){if(text[i]!='$'&&text[i]!=' '&&text[i]!='\t')clean[j++]=text[i];++i;}clean[j]='\0';
    if(strchr(clean,':')!=NULL){if(!excel_style_parse_range_ref_runtime(clean,sc,sr,ec,er))return 0;}else{if(!excel_style_parse_single_cell_ref_runtime(clean,sc,sr))return 0;*ec=*sc;*er=*sr;}
    return *sr>=1&&*er>=*sr&&*sc>=1&&*ec>=*sc&&*er<=1048576&&*ec<=16384;
}
static int b2026_app_parse_ranges(const char *spec,B2026AppRange **out,int *count,int *qualified_mode){
    const char *p=spec,*start=spec;int quoted=0,n=0,saw_q=0,saw_u=0;B2026AppRange *items=NULL;*out=NULL;*count=0;*qualified_mode=0;
    if(spec==NULL||*spec=='\0')return 1;
    for(;;++p){int end=(*p=='\0');if(!end&&*p=='\''){if(quoted&&p[1]=='\''){++p;continue;}quoted=!quoted;continue;}
        if(end||(!quoted&&*p==',')){char *part=b2026_app_trim_copy(start,(size_t)(p-start));const char *bang;char *ref=NULL,*sheet=NULL;B2026AppRange *next;int sc,sr,ec,er;
            if(part==NULL)goto fail;if(*part=='\0'){free(part);goto fail;}bang=b2026_app_find_unquoted(part,'!');
            if(bang!=NULL){sheet=b2026_app_unquote_sheet(part,(size_t)(bang-part));ref=strdup(bang+1);saw_q=1;}else{ref=strdup(part);saw_u=1;}
            free(part);if(ref==NULL||(bang!=NULL&&(sheet==NULL||*sheet=='\0'))||!b2026_app_parse_ref(ref,&sc,&sr,&ec,&er)){free(ref);free(sheet);goto fail;}free(ref);
            next=(B2026AppRange*)realloc(items,sizeof(B2026AppRange)*(size_t)(n+1));if(next==NULL){free(sheet);goto fail;}items=next;items[n].sheet=sheet;items[n].sr=sr;items[n].sc=sc;items[n].er=er;items[n].ec=ec;++n;
            if(end)break;start=p+1;
        }
    }
    if(quoted||saw_q+saw_u>1)goto fail;*out=items;*count=n;*qualified_mode=saw_q;return 1;
fail:b2026_app_ranges_free(items,n);return 0;
}

static unsigned int b2026_app_half_kana(unsigned int cp){
    static const unsigned short map[]={0x3002,0x300C,0x300D,0x3001,0x30FB,0x30F2,0x30A1,0x30A3,0x30A5,0x30A7,0x30A9,0x30E3,0x30E5,0x30E7,0x30C3,0x30FC,0x30A2,0x30A4,0x30A6,0x30A8,0x30AA,0x30AB,0x30AD,0x30AF,0x30B1,0x30B3,0x30B5,0x30B7,0x30B9,0x30BB,0x30BD,0x30BF,0x30C1,0x30C4,0x30C6,0x30C8,0x30CA,0x30CB,0x30CC,0x30CD,0x30CE,0x30CF,0x30D2,0x30D5,0x30D8,0x30DB,0x30DE,0x30DF,0x30E0,0x30E1,0x30E2,0x30E4,0x30E6,0x30E8,0x30E9,0x30EA,0x30EB,0x30EC,0x30ED,0x30EF,0x30F3,0x3099,0x309A};
    return cp>=0xFF61U&&cp<=0xFF9FU?(unsigned int)map[cp-0xFF61U]:cp;
}
static unsigned int b2026_app_kana_compose(unsigned int base,unsigned int mark){
    if(mark==0x3099U){if(base==0x30A6U)return 0x30F4U;if(base>=0x30ABU&&base<=0x30B3U&&(base&1U))return base+1U;if(base>=0x30B5U&&base<=0x30C2U&&((base-0x30B5U)%2U)==0U)return base+1U;if(base>=0x30C4U&&base<=0x30C9U&&((base-0x30C4U)%2U)==0U)return base+1U;if(base>=0x30CFU&&base<=0x30DBU&&((base-0x30CFU)%3U)==0U)return base+1U;}else if(mark==0x309AU&&base>=0x30CFU&&base<=0x30DBU&&((base-0x30CFU)%3U)==0U)return base+2U;return 0U;
}
static unsigned int b2026_app_fold_width(unsigned int cp){if(cp>=0xFF01U&&cp<=0xFF5EU)return cp-0xFEE0U;if(cp==0x3000U)return 0x20U;if(cp>=0xFF61U&&cp<=0xFF9FU)return b2026_app_half_kana(cp);return cp;}
static unsigned int b2026_app_fold_case(unsigned int cp){if(cp>='A'&&cp<='Z')return cp+32U;if(cp>=0x00C0U&&cp<=0x00D6U)return cp+32U;if(cp>=0x00D8U&&cp<=0x00DEU)return cp+32U;if(cp==0x0178U)return 0x00FFU;if(cp>=0x0391U&&cp<=0x03A1U)return cp+32U;if(cp>=0x03A3U&&cp<=0x03ABU)return cp+32U;if(cp>=0x0410U&&cp<=0x042FU)return cp+32U;return cp;}
static int b2026_app_space(unsigned int cp){return cp==0x20U||cp==0x09U||cp==0x0AU||cp==0x0DU||cp==0x3000U;}
static int b2026_app_append_cp(HtmlBuffer *out,unsigned int cp){char b[4];size_t n;if(cp<=0x7FU){b[0]=(char)cp;n=1;}else if(cp<=0x7FFU){b[0]=(char)(0xC0U|(cp>>6));b[1]=(char)(0x80U|(cp&63U));n=2;}else if(cp<=0xFFFFU){b[0]=(char)(0xE0U|(cp>>12));b[1]=(char)(0x80U|((cp>>6)&63U));b[2]=(char)(0x80U|(cp&63U));n=3;}else{b[0]=(char)(0xF0U|(cp>>18));b[1]=(char)(0x80U|((cp>>12)&63U));b[2]=(char)(0x80U|((cp>>6)&63U));b[3]=(char)(0x80U|(cp&63U));n=4;}htmlbuf_append_bytes(out,b,n);return 1;}
static char *b2026_app_normalize(const char *text,const B2026AppSearchSettings *s){
    const unsigned char *p=(const unsigned char*)(text!=NULL?text:"");HtmlBuffer out;int pending_space=0;htmlbuf_init(&out);
    while(*p){int span=utf8_char_span(p),ns;unsigned int cp,ncp,combined;if(span<1)span=1;cp=utf8_decode_codepoint(p,span);p+=(size_t)span;
        if(!s->width_sensitive){cp=b2026_app_fold_width(cp);if(*p){ns=utf8_char_span(p);if(ns<1)ns=1;ncp=b2026_app_fold_width(utf8_decode_codepoint(p,ns));combined=b2026_app_kana_compose(cp,ncp);if(combined){cp=combined;p+=(size_t)ns;}}}
        if(!s->kana_sensitive&&cp>=0x30A1U&&cp<=0x30F6U)cp-=0x60U;if(!s->case_sensitive)cp=b2026_app_fold_case(cp);if(s->trim_space&&out.len==0U&&b2026_app_space(cp))continue;
        if(s->collapse_space&&b2026_app_space(cp)){pending_space=1;continue;}if(pending_space){if(!(s->trim_space&&out.len==0U))htmlbuf_append_char(&out,' ');pending_space=0;}b2026_app_append_cp(&out,cp);
    }
    if(!s->trim_space&&pending_space)htmlbuf_append_char(&out,' ');if(s->trim_space){while(out.len>0U){unsigned char last=(unsigned char)out.data[out.len-1];if(last==' '||last=='\t'||last=='\r'||last=='\n'){out.data[--out.len]='\0';continue;}if(out.len>=3U&&(unsigned char)out.data[out.len-3]==0xE3U&&(unsigned char)out.data[out.len-2]==0x80U&&last==0x80U){out.len-=3U;out.data[out.len]='\0';continue;}break;}}
    return htmlbuf_take(&out);
}
static int b2026_app_occurrences(const char *hay,const char *needle){int n=0;size_t len=strlen(needle);const char *p=hay;if(len==0)return 0;while((p=strstr(p,needle))!=NULL){++n;p+=len;}return n;}

static int b2026_app_sheet_selected(const B2026AppSheet *s,const B2026AppSearchSettings *opt,regex_t *regex,int regex_ready){
    if(!opt->search_hidden&&s->hidden)return 0;if(opt->sheet!=NULL&&strcmp(opt->sheet,"すべて")!=0&&strcmp(opt->sheet,"全て")!=0&&!b2026_app_ascii_eq(opt->sheet,"ALL")&&strcmp(s->name,opt->sheet)!=0)return 0;
    if(opt->sheet_regex!=NULL&&(!regex_ready||regexec(regex,s->name,0,NULL,0)!=0))return 0;return 1;
}
static int b2026_app_hidden_row(const char *xml,int row){const char *p=xml;if(xml==NULL||strstr(xml,"hidden")==NULL)return 0;while((p=strstr(p,"<row"))!=NULL){char r[32],h[32];if(!excel_style_is_name_delimiter(p[4])){p+=4;continue;}r[0]=h[0]='\0';excel_style_extract_attr_value(p,"r",r,sizeof(r));excel_style_extract_attr_value(p,"hidden",h,sizeof(h));if(atoi(r)==row)return atoi(h)!=0||b2026_app_ascii_eq(h,"TRUE");p+=4;}return 0;}
static int b2026_app_hidden_col(const char *xml,int col){const char *p=xml;if(xml==NULL||strstr(xml,"hidden")==NULL)return 0;while((p=strstr(p,"<col"))!=NULL){char mn[32],mx[32],h[32];if(!excel_style_is_name_delimiter(p[4])){p+=4;continue;}mn[0]=mx[0]=h[0]='\0';excel_style_extract_attr_value(p,"min",mn,sizeof(mn));excel_style_extract_attr_value(p,"max",mx,sizeof(mx));excel_style_extract_attr_value(p,"hidden",h,sizeof(h));if(col>=atoi(mn)&&col<=atoi(mx))return atoi(h)!=0||b2026_app_ascii_eq(h,"TRUE");p+=4;}return 0;}
static int b2026_app_merge(const char *xml,int row,int col,int *sr,int *sc,int *er,int *ec,char *ref,size_t ref_size){
    const char *p=xml;if(xml==NULL||strstr(xml,"<mergeCell")==NULL){*sr=*er=row;*sc=*ec=col;if(ref_size>0)ref[0]='\0';return 0;}while((p=strstr(p,"<mergeCell"))!=NULL){char r[128];if(!excel_style_is_name_delimiter(p[10])){p+=10;continue;}r[0]='\0';if(excel_style_extract_attr_value(p,"ref",r,sizeof(r))&&excel_style_parse_range_ref_runtime(r,sc,sr,ec,er)&&row>=*sr&&row<=*er&&col>=*sc&&col<=*ec){snprintf(ref,ref_size,"%s",r);return 1;}p+=10;}*sr=*er=row;*sc=*ec=col;if(ref_size>0)ref[0]='\0';return 0;
}
static int b2026_app_range_selected(const B2026AppRange *ranges,int count,const char *sheet,int msr,int msc,int mer,int mec){
    int i;if(count==0)return 1;for(i=0;i<count;++i){const B2026AppRange *r=&ranges[i];if(r->sheet!=NULL&&strcmp(r->sheet,sheet)!=0)continue;if(!(mec<r->sc||msc>r->ec||mer<r->sr||msr>r->er))return 1;}return 0;
}
static void b2026_app_detail(const char *target,int index,const B2026AppSheet *sheet,const char *cell,int row,int col,const char *value,int occurrences,int sheet_hidden,int row_hidden,int col_hidden,const char *merge){
    char key[160];if(target==NULL)return;
    snprintf(key,sizeof(key),"シート名,%d",index);hash_set_string(target,key,sheet->name);snprintf(key,sizeof(key),"sheetName,%d",index);hash_set_string(target,key,sheet->name);
    snprintf(key,sizeof(key),"シート番号,%d",index);hash_set_number(target,key,(double)sheet->index);snprintf(key,sizeof(key),"sheetIndex,%d",index);hash_set_number(target,key,(double)sheet->index);
    snprintf(key,sizeof(key),"セル,%d",index);hash_set_string(target,key,cell);snprintf(key,sizeof(key),"cell,%d",index);hash_set_string(target,key,cell);
    snprintf(key,sizeof(key),"行,%d",index);hash_set_number(target,key,(double)row);snprintf(key,sizeof(key),"row,%d",index);hash_set_number(target,key,(double)row);
    snprintf(key,sizeof(key),"列,%d",index);hash_set_number(target,key,(double)col);snprintf(key,sizeof(key),"column,%d",index);hash_set_number(target,key,(double)col);
    snprintf(key,sizeof(key),"表示値,%d",index);hash_set_string(target,key,value);snprintf(key,sizeof(key),"displayValue,%d",index);hash_set_string(target,key,value);
    snprintf(key,sizeof(key),"出現数,%d",index);hash_set_number(target,key,(double)occurrences);snprintf(key,sizeof(key),"occurrenceCount,%d",index);hash_set_number(target,key,(double)occurrences);
    snprintf(key,sizeof(key),"非表示シート,%d",index);hash_set_number(target,key,(double)sheet_hidden);snprintf(key,sizeof(key),"hiddenSheet,%d",index);hash_set_number(target,key,(double)sheet_hidden);
    snprintf(key,sizeof(key),"非表示行,%d",index);hash_set_number(target,key,(double)row_hidden);snprintf(key,sizeof(key),"hiddenRow,%d",index);hash_set_number(target,key,(double)row_hidden);
    snprintf(key,sizeof(key),"非表示列,%d",index);hash_set_number(target,key,(double)col_hidden);snprintf(key,sizeof(key),"hiddenColumn,%d",index);hash_set_number(target,key,(double)col_hidden);
    snprintf(key,sizeof(key),"結合範囲,%d",index);hash_set_string(target,key,merge);snprintf(key,sizeof(key),"mergedRange,%d",index);hash_set_string(target,key,merge);
}

static double b2026_appfile_search(const char *target,int forced_excel,int search_mode,const char *path,const char *query,const char *options_name){
    B2026AppSearchSettings opt;B2026AppSheet *sheets=NULL;B2026AppRange *ranges=NULL;int sheet_count=0,range_count=0,qualified=0,max_clamped=0;
    char *needle=NULL,*abs_path=NULL;int load_rc,i,total=0,total_occ=0,returned=0,matched_sheets=0,target_sheets=0,any_selected=0;
    regex_t sheet_re,cell_re;int regex_ready=0,cell_regex_ready=0;char target_names[32768]="",matched_names[32768]="";double result=-1.0;
    b2026_app_clear(target);basic_clear_error();
    if(path==NULL||*path=='\0'||query==NULL)return b2026_app_fail(target,4600,path,"path$ and search-text$ are required / パスと検索文字列が必要です");
    if(*query=='\0')return b2026_app_fail(target,4601,path,"search text must not be empty / 検索文字列は空にできません");
    if(!b2026_app_settings(options_name,&opt,&max_clamped))return b2026_app_fail(target,4602,path,"invalid or conflicting options / 設定値が不正、または競合しています");
    if((forced_excel&&opt.application!=NULL&&!b2026_app_excel_application(opt.application))||!b2026_app_excel_application(opt.application))return b2026_app_fail(target,4603,path,"application is not Excel / 指定されたアプリケーションはExcelではありません");
    if(b2026_access(path,F_OK)!=0)return b2026_app_fail(target,4604,path,"file does not exist / ファイルが存在しません");
    excel_data_cache_keep_current_appfile_path(path);
    load_rc=b2026_app_load_sheets(path,&sheets,&sheet_count);if(load_rc<=0)return b2026_app_fail(target,4606,path,load_rc<0?"out of memory / メモリ不足":"file is damaged or is not a supported Excel workbook / 破損しているか対応Excelブックではありません");
    if(!b2026_app_parse_ranges(opt.range,&ranges,&range_count,&qualified)){b2026_app_sheets_free(sheets,sheet_count);return b2026_app_fail(target,4607,path,"invalid search range / 検索範囲の指定が不正です");}
    if(qualified&&(opt.sheet!=NULL||opt.sheet_regex!=NULL)){b2026_app_ranges_free(ranges,range_count);b2026_app_sheets_free(sheets,sheet_count);return b2026_app_fail(target,4608,path,"qualified ranges cannot be combined with sheet selectors / シート修飾範囲とシート指定は併用できません");}
    if(opt.sheet_regex!=NULL){int rc=basic_regex_compile(&sheet_re,opt.sheet_regex,REG_EXTENDED);if(rc!=0){b2026_app_ranges_free(ranges,range_count);b2026_app_sheets_free(sheets,sheet_count);return b2026_app_fail(target,4609,path,"invalid sheet regular expression / シート正規表現が不正です");}regex_ready=1;}
    needle=b2026_app_normalize(query,&opt);if(needle==NULL||*needle=='\0'){free(needle);if(regex_ready)regfree(&sheet_re);b2026_app_ranges_free(ranges,range_count);b2026_app_sheets_free(sheets,sheet_count);return b2026_app_fail(target,4610,path,"normalized search text is empty / 正規化後の検索文字列が空です");}
    if(search_mode==2){int rc;if(strlen(needle)>65536U){free(needle);if(regex_ready)regfree(&sheet_re);b2026_app_ranges_free(ranges,range_count);b2026_app_sheets_free(sheets,sheet_count);return b2026_app_fail(target,4612,path,"regular expression exceeds 64 KiB / 正規表現が64KiBを超えています");}rc=basic_regex_compile(&cell_re,needle,REG_EXTENDED);if(rc!=0){free(needle);if(regex_ready)regfree(&sheet_re);b2026_app_ranges_free(ranges,range_count);b2026_app_sheets_free(sheets,sheet_count);return b2026_app_fail(target,4613,path,"invalid regular expression / 正規表現が正しくありません");}cell_regex_ready=1;}
    abs_path=fileinfo_make_absolute_path(path,1);if(abs_path==NULL)abs_path=strdup(path);if(max_clamped)b2026_app_warn(path,"maximum result count was adjusted to 65536 / 最大件数を65536に調整しました");
    for(i=0;i<sheet_count;++i){B2026AppSheet *sheet=&sheets[i];ExcelDataCacheEntry *entry;int has_state=0;long long fs=0,mt=0;unsigned char *sheet_xml_bytes;const char *sheet_xml;int j,sheet_had_match=0;char idx[32];
        if(qualified){int q,has=0;for(q=0;q<range_count;++q)if(ranges[q].sheet!=NULL&&strcmp(ranges[q].sheet,sheet->name)==0){has=1;break;}if(!has||(!opt.search_hidden&&sheet->hidden))continue;}
        else if(!b2026_app_sheet_selected(sheet,&opt,&sheet_re,regex_ready))continue;
        any_selected=1;++target_sheets;if(target_names[0]!='\0')strncat(target_names,",",sizeof(target_names)-strlen(target_names)-1);strncat(target_names,sheet->name,sizeof(target_names)-strlen(target_names)-1);
        snprintf(idx,sizeof(idx),"%d",sheet->index);excel_data_get_file_state(path,&has_state,&fs,&mt);excel_data_cache_invalidate_stale_path(path,has_state,fs,mt);entry=excel_data_cache_find(path,"index",idx,5,0);
        if(entry==NULL)entry=excel_data_build_cache_entry_from_path(path,"index",idx,5,0,has_state,fs,mt);
        if(entry==NULL)continue;sheet_xml_bytes=excel_style_extract_xlsx_entry(path,sheet->path,NULL);sheet_xml=sheet_xml_bytes!=NULL?(const char*)sheet_xml_bytes:"";
        for(j=0;j<entry->item_count;++j){ExcelDataCacheItem *item=&entry->items[j];int row=0,col=0,msr,msc,mer,mec,rh,ch,occ;char merge[128],display[128];const char *text;char *norm;
            if(!excel_style_parse_single_cell_ref_runtime(item->key,&col,&row))continue;
            if(range_count>0)b2026_app_merge(sheet_xml,row,col,&msr,&msc,&mer,&mec,merge,sizeof(merge));else{msr=mer=row;msc=mec=col;merge[0]='\0';}
            if(!b2026_app_range_selected(ranges,range_count,sheet->name,msr,msc,mer,mec))continue;
            if(!opt.search_hidden){rh=b2026_app_hidden_row(sheet_xml,row);ch=b2026_app_hidden_col(sheet_xml,col);if(rh||ch)continue;}else{rh=0;ch=0;}
            if(item->is_number){format_number_string(display,sizeof(display),item->number_value);text=display;}else text=item->string_value!=NULL?item->string_value:"";
            norm=b2026_app_normalize(text,&opt);if(norm==NULL)continue;occ=search_mode==2?(regexec(&cell_re,norm,0,NULL,0)==0?1:0):search_mode==1?(strcmp(norm,needle)==0?1:0):b2026_app_occurrences(norm,needle);free(norm);if(occ<=0)continue;
            if(opt.search_hidden){rh=b2026_app_hidden_row(sheet_xml,row);ch=b2026_app_hidden_col(sheet_xml,col);}if(range_count==0)b2026_app_merge(sheet_xml,row,col,&msr,&msc,&mer,&mec,merge,sizeof(merge));
            ++total;total_occ+=occ;if(!sheet_had_match){sheet_had_match=1;++matched_sheets;if(matched_names[0]!='\0')strncat(matched_names,",",sizeof(matched_names)-strlen(matched_names)-1);strncat(matched_names,sheet->name,sizeof(matched_names)-strlen(matched_names)-1);}
            if(total>=opt.start_position&&returned<opt.max_results){++returned;b2026_app_detail(target,returned,sheet,item->key,row,col,text,occ,sheet->hidden,rh,ch,merge);}
        }
        free(sheet_xml_bytes);
    }
    if(regex_ready)regfree(&sheet_re);
    if(cell_regex_ready)regfree(&cell_re);
    if(!any_selected){free(needle);free(abs_path);b2026_app_ranges_free(ranges,range_count);b2026_app_sheets_free(sheets,sheet_count);return b2026_app_fail(target,4611,path,"no sheet matched the selector / 対象シートが見つかりません");}
    b2026_app_pair_num(target,"成功","ok",1);b2026_app_pair_num(target,"完全","complete",(opt.start_position-1+returned)>=total?1:0);b2026_app_pair_text(target,"状態","status","OK");
    b2026_app_pair_text(target,"アプリケーション","application","Excel");b2026_app_pair_text(target,"形式","format",b2026_app_extension_ok(path)&&b2026_app_ascii_eq(strrchr(path,'.'),".XLSM")?"XLSM":"XLSX");
    b2026_app_pair_text(target,"ファイル","file",abs_path);b2026_app_pair_text(target,"検索文字列","query",query);b2026_app_pair_text(target,"検索方式","searchMode",search_mode==2?"REGEX":search_mode==1?"EXACT":"PARTIAL");b2026_app_pair_text(target,"対象シート","targetSheets",target_names);b2026_app_pair_text(target,"該当シート","matchedSheets",matched_names);
    b2026_app_pair_num(target,"対象シート数","targetSheetCount",target_sheets);b2026_app_pair_num(target,"該当シート数","matchedSheetCount",matched_sheets);b2026_app_pair_num(target,"該当セル数","matchedCellCount",total);b2026_app_pair_num(target,"総出現数","totalOccurrenceCount",total_occ);
    b2026_app_pair_num(target,"返却件数","returnedCount",returned);b2026_app_pair_num(target,"開始位置","startPosition",opt.start_position);b2026_app_pair_num(target,"最大件数","maxResults",opt.max_results);
    b2026_app_pair_num(target,"続きあり","hasMore",(opt.start_position-1+returned)<total?1:0);b2026_app_pair_num(target,"次の開始位置","nextStartPosition",(opt.start_position-1+returned)<total?opt.start_position+returned:0);
    result=(double)total;free(needle);free(abs_path);b2026_app_ranges_free(ranges,range_count);b2026_app_sheets_free(sheets,sheet_count);return result;
}
