/*=gfunc find_file * * what: locate a file in the search path * exparg: file-name, name of file with text * exparg: @suffix @ file suffix to try, too @ opt @ * * doc: * * AutoGen has a search path that it uses to locate template and definition * files. This function will search the same list for @file{file-name}, both * with and without the @file{.suffix}, if provided. =*/ SCM ag_scm_find_file(SCM file, SCM suffix) { SCM res = SCM_UNDEFINED; if (! AG_SCM_STRING_P(file)) scm_wrong_type_arg(FIND_FILE_NAME, 1, file); { char z[ AG_PATH_MAX+1 ]; char const * pz = ag_scm2zchars(file, "file-name"); /* * The suffix is optional. If provided, it will be a string. */ if (AG_SCM_STRING_P(suffix)) { char* apz[2]; apz[0] = (char *)ag_scm2zchars(suffix, "file suffix"); apz[1] = NULL; if (SUCCESSFUL(findFile(pz, z, (char const **)apz, NULL))) res = AG_SCM_STR02SCM(z); } else if (SUCCESSFUL(findFile(pz, z, NULL, NULL))) res = AG_SCM_STR02SCM(z); } return res; }
/*=gfunc extract * * what: extract text from another file * general_use: * exparg: file-name, name of file with text * exparg: marker-fmt, format for marker text * exparg: caveat, warn about changing marker, opt * exparg: default, default initial text, opt * * doc: * * This function is used to help construct output files that may contain * text that is carried from one version of the output to the next. * * The first two arguments are required, the second are optional: * * @itemize @bullet * @item * The @code{file-name} argument is used to name the file that * contains the demarcated text. * @item * The @code{marker-fmt} is a formatting string that is used to construct * the starting and ending demarcation strings. The sprintf function is * given the @code{marker-fmt} with two arguments. The first is either * "START" or "END". The second is either "DO NOT CHANGE THIS COMMENT" * or the optional @code{caveat} argument. * @item * @code{caveat} is presumed to be absent if it is the empty string * (@code{""}). If absent, ``DO NOT CHANGE THIS COMMENT'' is used * as the second string argument to the @code{marker-fmt}. * @item * When a @code{default} argument is supplied and no pre-existing text * is found, then this text will be inserted between the START and END * markers. * @end itemize * * @noindent * The resulting strings are presumed to be unique within * the subject file. As a simplified example: * * @example * [+ (extract "fname" "// %s - SOMETHING - %s" "" * "example default") +] * @end example * @noindent * will result in the following text being inserted into the output: * * @example * // START - SOMETHING - DO NOT CHANGE THIS COMMENT * example default * // END - SOMETHING - DO NOT CHANGE THIS COMMENT * @end example * * @noindent * The ``@code{example default}'' string can then be carried forward to * the next generation of the output, @strong{@i{provided}} the output * is not named "@code{fname}" @i{and} the old output is renamed to * "@code{fname}" before AutoGen-eration begins. * * @table @strong * @item NB: * You can set aside previously generated source files inside the pseudo * macro with a Guile/scheme function, extract the text you want to keep * with this extract function. Just remember you should delete it at the * end, too. Here is an example from my Finite State Machine generator: * * @example * [+ AutoGen5 Template -*- Mode: text -*- * h=%s-fsm.h c=%s-fsm.c * (shellf * "test -f %1$s-fsm.h && mv -f %1$s-fsm.h .fsm.head * test -f %1$s-fsm.c && mv -f %1$s-fsm.c .fsm.code" (base-name)) * +] * @end example * * This code will move the two previously produced output files to files * named ".fsm.head" and ".fsm.code". At the end of the 'c' output * processing, I delete them. * * @item also NB: * This function presumes that the output file ought to be editable so * that the code between the @code{START} and @code{END} marks can be edited * by the template user. Consequently, when the @code{(extract ...)} function * is invoked, if the @code{writable} option has not been specified, then * it will be set at that point. If this is not the desired behavior, the * @code{--not-writable} command line option will override this. * Also, you may use the guile function @code{(chmod "file" mode-value)} * to override whatever AutoGen is using for the result mode. * @end table =*/ SCM ag_scm_extract(SCM file, SCM marker, SCM caveat, SCM def) { char const * pzStart; char const * pzEnd; char const * pzText; if (! AG_SCM_STRING_P(file) || ! AG_SCM_STRING_P(marker)) return SCM_UNDEFINED; pzText = load_extract_file(ag_scm2zchars(file, "extr file")); { char const * pzMarker = ag_scm2zchars(marker, "marker"); char const * pzCaveat = EXTRACT_CAVEAT; if (AG_SCM_STRING_P(caveat) && (AG_SCM_STRLEN(caveat) > 0)) pzCaveat = ag_scm2zchars(caveat, "caveat"); pzStart = aprf(pzMarker, EXTRACT_START, pzCaveat); pzEnd = aprf(pzMarker, EXTRACT_END, pzCaveat); } { SCM res; if (pzText == NULL) res = mk_empty_text(pzStart, pzEnd, def); else res = get_text(pzText, pzStart, pzEnd, def); AGFREE((void*)pzStart); AGFREE((void*)pzEnd); return res; } }
SCM ag_scm_string_contains_p(SCM text, SCM substr) { char* pzText = ag_scm2zchars(text, "text to match"); char* pzSubstr = ag_scm2zchars(substr, "match expr"); return (strstr(pzText, pzSubstr) == NULL) ? SCM_BOOL_F : SCM_BOOL_T; }
SCM ag_scm_string_ends_with_p(SCM text, SCM substr) { char* pzText = ag_scm2zchars(text, "text to match"); char* pzSubstr = ag_scm2zchars(substr, "match expr"); return (SUCCESSFUL(Select_Compare_End(pzText, pzSubstr))) ? SCM_BOOL_T : SCM_BOOL_F; }
SCM ag_scm_string_equals_p(SCM text, SCM substr) { char* pzText = ag_scm2zchars(text, "text to match"); char* pzSubstr = ag_scm2zchars(substr, "match expr"); return (strcmp(pzText, pzSubstr) == 0) ? SCM_BOOL_T : SCM_BOOL_F; }
SCM ag_scm_string_starts_eqv_p(SCM text, SCM substr) { char* pzText = ag_scm2zchars(text, "text to match"); char* pzSubstr = ag_scm2zchars(substr, "match expr"); return (SUCCESSFUL(Select_Equivalent_Start(pzText, pzSubstr))) ? SCM_BOOL_T : SCM_BOOL_F; }
/*=gfunc prefix * * what: prefix lines with a string * general_use: * * exparg: prefix, string to insert at start of each line * exparg: text, multi-line block of text * * doc: * Prefix every line in the second string with the first string. * * For example, if the first string is "# " and the second contains: * @example * two * lines * @end example * @noindent * The result string will contain: * @example * # two * # lines * @end example =*/ SCM ag_scm_prefix(SCM prefix, SCM text) { char* pzPfx; char* pzText; char* pzDta; size_t out_size, rem_size; size_t pfx_size; char* pzRes; pzPfx = ag_scm2zchars(prefix, "prefix"); pzDta = \ pzText = ag_scm2zchars(text, "text"); pfx_size = strlen(pzPfx); out_size = pfx_size; for (;;) { switch (*(pzText++)) { case NUL: goto exit_count; case NL: out_size += pfx_size; /* FALLTHROUGH */ default: out_size++; } } exit_count:; pzText = pzDta; pzRes = pzDta = ag_scribble(rem_size = out_size); strcpy(pzDta, pzPfx); pzDta += pfx_size; rem_size -= pfx_size; pfx_size++; for (;;) { char ch = *(pzText++); switch (ch) { case NUL: if (rem_size != 0) AG_ABEND(PREFIX_FAIL); return AG_SCM_STR2SCM(pzRes, out_size); case NL: *pzDta = ch; strcpy(pzDta+1, pzPfx); pzDta += pfx_size; rem_size -= pfx_size; break; default: rem_size--; *(pzDta++) = ch; break; } } }
/*=gfunc prefix * * what: prefix lines with a string * general_use: * * exparg: prefix, string to insert at start of each line * exparg: text, multi-line block of text * * doc: * Prefix every line in the second string with the first string. * This includes empty lines, though trailing white space will * be removed if the line consists only of the "prefix". * Also, if the last character is a newline, then *two* prefixes will * be inserted into the result text. * * For example, if the first string is "# " and the second contains: * @example * "two\nlines\n" * @end example * @noindent * The result string will contain: * @example * # two * # lines * # * @end example * * The last line will be incomplete: no newline and no space after the * hash character, either. =*/ SCM ag_scm_prefix(SCM prefx, SCM txt) { char * prefix = ag_scm2zchars(prefx, "pfx"); char * text = ag_scm2zchars(txt, "txt"); char * scan = text; size_t pfx_size = strlen(prefix); char * r_str; /* result string */ { size_t out_size = pfx_size + 1; // NUL or NL byte adjustment for (;;) { switch (*(scan++)) { case NUL: out_size += scan - text; goto exit_count; case NL: out_size += pfx_size; } } exit_count:; r_str = scan = scribble_get((ssize_t)out_size); } memcpy(scan, prefix, pfx_size); scan += pfx_size; pfx_size++; for (;;) { char ch = *(text++); switch (ch) { case NUL: /* * Trim trailing white space on the final line. */ scan = SPN_HORIZ_WHITE_BACK(r_str, scan); return AG_SCM_STR2SCM(r_str, scan - r_str); case NL: /* * Trim trailing white space on previous line first. */ scan = SPN_HORIZ_WHITE_BACK(r_str, scan); *scan = NL; memcpy(scan+1, prefix, pfx_size - 1); scan += pfx_size; // prefix length plus 1 for new line break; default: *(scan++) = ch; break; } } }
/** * Convert version number strings into a binary representation and compare. */ SCM ag_scm_version_compare(SCM op, SCM v1, SCM v2) { static char const zVer[] = "version str"; ver_type_t val1 = str2int_ver(ag_scm2zchars(v1, zVer)); ver_type_t val2 = str2int_ver(ag_scm2zchars(v2, zVer)); v1 = SCM_FROM(val1); v2 = SCM_FROM(val2); return scm_apply(op, v1, scm_cons(v2, AG_SCM_LISTOFNULL())); }
/*=gfunc string_tr_x * * what: convert characters * general_use: * * exparg: source, string to transform * exparg: match, characters to be converted * exparg: translation, conversion list * * doc: This is the same as the @code{tr(1)} program, except the * string to transform is the first argument. The second and * third arguments are used to construct mapping arrays for the * transformation of the first argument. * * It is too bad this little program has so many different * and incompatible implementations! =*/ SCM ag_scm_string_tr_x(SCM str, SCM from_xform, SCM to_xform) { unsigned char ch_map[ 1 << 8 /* bits-per-byte */ ]; int i = sizeof(ch_map) - 1; char * from = ag_scm2zchars(from_xform, "str"); char * to = ag_scm2zchars(to_xform, "str"); do { ch_map[i] = (unsigned char)i; } while (--i > 0); for (; i <= (int)sizeof(ch_map) - 1; i++) { unsigned char fch = (unsigned char)*(from++); unsigned char tch = (unsigned char)*(to++); if (tch == NUL) { to--; tch = (unsigned char)to[-1]; } switch (fch) { case NUL: goto map_done; case '-': if ((i > 0) && (tch == '-')) { unsigned char fs = (unsigned char)from[-2]; unsigned char fe = (unsigned char)from[0]; unsigned char ts = (unsigned char)to[-2]; unsigned char te = (unsigned char)to[0]; if (te != NUL) { while (fs < fe) { ch_map[ fs++ ] = ts; if (ts < te) ts++; } break; } } default: ch_map[ fch ] = tch; } } map_done:; to = C(char *, scm_i_string_chars(str)); i = (int)AG_SCM_STRLEN(str); while (i-- > 0) { *to = (char)ch_map[ (int)*to ]; to++; } return str; }
/*=gfunc string_tr_x * * what: convert characters * general_use: * * exparg: source, string to transform * exparg: match, characters to be converted * exparg: translation, conversion list * * doc: This is the same as the @code{tr(1)} program, except the * string to transform is the first argument. The second and * third arguments are used to construct mapping arrays for the * transformation of the first argument. * * It is too bad this little program has so many different * and incompatible implementations! =*/ SCM ag_scm_string_tr_x(SCM str, SCM from_xform, SCM to_xform) { unsigned char ch_map[ 1 << 8 /* bits-per-byte */ ]; int i = sizeof(ch_map) - 1; char* pzFrom = ag_scm2zchars(from_xform, "str"); char* pzTo = ag_scm2zchars(to_xform, "str"); do { ch_map[i] = i; } while (--i > 0); for (;i <= sizeof(ch_map) - 1;i++) { unsigned char fch = (unsigned char)*(pzFrom++); unsigned char tch = (unsigned char)*(pzTo++); if (tch == NUL) { pzTo--; tch = pzTo[-1]; } switch (fch) { case NUL: goto mapDone; case '-': if ((i > 0) && (tch == '-')) { unsigned char fs = (unsigned char)pzFrom[-2]; unsigned char fe = (unsigned char)pzFrom[0]; unsigned char ts = (unsigned char)pzTo[-2]; unsigned char te = (unsigned char)pzTo[0]; if (te != NUL) { while (fs < fe) { ch_map[ fs++ ] = ts; if (ts < te) ts++; } break; } } default: ch_map[ fch ] = tch; } } mapDone:; pzTo = (char*)(void*)AG_SCM_CHARS(str); i = AG_SCM_STRLEN(str); while (i-- > 0) { *pzTo = ch_map[ (int)*pzTo ]; pzTo++; } return str; }
SCM ag_scm_string_has_match_p(SCM text, SCM substr) { SCM res; regex_t re; compile_re(&re, ag_scm2zchars( substr, "match expr" ), REG_EXTENDED); if (regexec(&re, ag_scm2zchars(text, "text to match"), (size_t)0, NULL, 0) == 0) res = SCM_BOOL_T; else res = SCM_BOOL_F; regfree(&re); return res; }
/*=gfunc stack * * what: make list of AutoGen values * * exparg: ag-name, AutoGen value name * * doc: Create a scheme list of all the strings that are associated * with a name. They must all be text values or we choke. =*/ SCM ag_scm_stack(SCM obj) { SCM res; SCM * pos = &res; def_ent_t ** ppDE; def_ent_t * pDE; SCM str; res = SCM_EOL; ppDE = find_def_ent_list(ag_scm2zchars(obj, "AG Object")); if (ppDE == NULL) return SCM_EOL; for (;;) { pDE = *(ppDE++); if (pDE == NULL) break; if (pDE->de_type != VALTYP_TEXT) return SCM_UNDEFINED; str = AG_SCM_STR02SCM(pDE->de_val.dvu_text); *pos = scm_cons(str, SCM_EOL); pos = SCM_CDRLOC(*pos); } return res; }
static SCM shell_stringify(SCM obj, uint_t qt) { char * pzNew; size_t dtaSize = 3; char * pzDta = ag_scm2zchars(obj, "AG Object"); char * pz = pzDta; for (;;) { char c = *(pz++); switch (c) { case NUL: goto loopDone1; case '"': case '`': case '\\': dtaSize += 2; break; default: dtaSize++; } } loopDone1:; pzNew = AGALOC(dtaSize, "shell string"); dtaSize = stringify_for_sh(pzNew, qt, pzDta); { SCM res = AG_SCM_STR2SCM(pzNew, dtaSize); AGFREE(pzNew); return res; } }
/*=gfunc count * * what: definition count * * exparg: ag-name, name of AutoGen value * * doc: Count the number of entries for a definition. * The input argument must be a string containing the name * of the AutoGen values to be counted. If there is no * value associated with the name, the result is an SCM * immediate integer value of zero. =*/ SCM ag_scm_count(SCM obj) { int ent_len = count_entries(ag_scm2zchars(obj, "ag object")); return AG_SCM_INT2SCM(ent_len); }
/*=gfunc len * * what: get count of values * * exparg: ag-name, name of AutoGen value * * doc: If the named object is a group definition, then "len" is * the same as "count". Otherwise, if it is one or more text * definitions, then it is the sum of their string lengths. * If it is a single text definition, then it is equivalent to * @code{(string-length (get "ag-name"))}. =*/ SCM ag_scm_len(SCM obj) { int len = entry_length(ag_scm2zchars(obj, "ag value")); return AG_SCM_INT2SCM(len); }
/*=gfunc stack * * what: make list of AutoGen values * * exparg: ag-name, AutoGen value name * * doc: Create a scheme list of all the strings that are associated * with a name. They must all be text values or we choke. =*/ SCM ag_scm_stack(SCM obj) { SCM res; SCM * pos = &res; tDefEntry** ppDE; tDefEntry* pDE; SCM str; res = SCM_EOL; ppDE = findEntryList(ag_scm2zchars(obj, "AG Object")); if (ppDE == NULL) return SCM_EOL; for (;;) { pDE = *(ppDE++); if (pDE == NULL) break; if (pDE->valType != VALTYP_TEXT) return SCM_UNDEFINED; str = AG_SCM_STR02SCM(pDE->val.pzText); *pos = scm_cons(str, SCM_EOL); pos = SCM_CDRLOC(*pos); } return res; }
SCM ag_scm_string_contains_eqv_p(SCM text, SCM substr) { static char const zSrch[] = "search string"; char* pzSubstr; SCM res; AGDUPSTR(pzSubstr, ag_scm2zchars( substr, zSrch ), "substring"); upString(pzSubstr); if (SUCCESSFUL(Select_Equivalent(ag_scm2zchars(text, "sample text"), pzSubstr))) res = SCM_BOOL_T; else res = SCM_BOOL_F; AGFREE((void*)pzSubstr); return res; }
/** * Replace marker text. * * Replace all occurrances of the marker text with the substitution text. * The result is stored in an automatically freed temporary buffer. * * @param src_str The source string * @param str_len The length of the string * @param match the SCM-ized marker string * @param repl the SCM-ized replacement string * @param ppz_res pointer to the result pointer * @param res_len pointer to result length */ static void do_substitution( char const * src_str, ssize_t str_len, SCM match, SCM repl, char ** ppz_res, ssize_t * res_len) { char * pzMatch = ag_scm2zchars(match, "match text"); char * rep_str = ag_scm2zchars(repl, "repl text"); int mark_len = (int)AG_SCM_STRLEN(match); int repl_len = (int)AG_SCM_STRLEN(repl); { int ct = sub_count(src_str, pzMatch); if (ct == 0) return; /* No substitutions -- no work. */ str_len += (repl_len - mark_len) * ct; } { char * dest = scribble_get(str_len + 1); *ppz_res = dest; *res_len = str_len; for (;;) { char const * next = strstr(src_str, pzMatch); size_t len; if (next == NULL) break; len = (size_t)(next - src_str); if (len != 0) { memcpy(dest, src_str, len); dest += len; } memcpy(dest, rep_str, (size_t)repl_len); dest += repl_len; src_str = next + mark_len; } strcpy(dest, src_str); } }
SCM ag_scm_string_has_eqv_match_p(SCM text, SCM substr) { char* pzText = ag_scm2zchars(text, "text to match"); char* pzSubstr = ag_scm2zchars(substr, "match expr"); SCM res; regex_t re; compile_re(&re, pzSubstr, REG_EXTENDED | REG_ICASE); if (regexec(&re, pzText, (size_t)0, NULL, 0) == 0) res = SCM_BOOL_T; else res = SCM_BOOL_F; regfree(&re); return res; }
/*=gfunc mk_gettextable * * what: print a string in a gettext-able format * exparg: string, a multi-paragraph string * * doc: Returns SCM_UNDEFINED. The input text string is printed * to the current output as one puts() call per paragraph. =*/ SCM ag_scm_mk_gettextable(SCM txt) { if (AG_SCM_STRING_P(txt)) { char const * pz = ag_scm2zchars(txt, "txt"); optionPrintParagraphs(pz, false, cur_fpstack->stk_fp); } return SCM_UNDEFINED; }
SCM ag_scm_string_eqv_p(SCM text, SCM substr) { char* pzText; char* pzSubstr; /* * We are overloading the "=" operator. Our arguments may be * numbers... */ if (! AG_SCM_STRING_P(text) || ! AG_SCM_STRING_P(substr)) return scm_num_eq_p(text, substr); pzText = ag_scm2zchars(text, "text to match"); pzSubstr = ag_scm2zchars(substr, "match expr"); return (streqvcmp(pzText, pzSubstr) == 0) ? SCM_BOOL_T : SCM_BOOL_F; }
/*=gfunc fprintf * * what: format to a file * general_use: * * exparg: port, Guile-scheme output port * exparg: format, formatting string * exparg: format-arg, list of arguments to formatting string, opt, list * * doc: Format a string using arguments from the alist. * Write to a specified port. The result will NOT appear in your * output. Use this to print information messages to a template user. =*/ SCM ag_scm_fprintf(SCM port, SCM fmt, SCM alist) { int list_len = scm_ilength(alist); char* pzFmt = ag_scm2zchars(fmt, zFormat); SCM res = run_printf(pzFmt, list_len, alist); return scm_display(res, port); }
/*=gfunc printf * * what: format to stdout * general_use: * * exparg: format, formatting string * exparg: format-arg, list of arguments to formatting string, opt, list * * doc: Format a string using arguments from the alist. * Write to the standard out port. The result will NOT appear in your * output. Use this to print information messages to a template user. * Use ``(sprintf ...)'' to add text to your document. =*/ SCM ag_scm_printf(SCM fmt, SCM alist) { int list_len = scm_ilength(alist); char* pzFmt = ag_scm2zchars(fmt, zFormat); AG_SCM_DISPLAY(run_printf(pzFmt, list_len, alist)); return SCM_UNDEFINED; }
/*=gfunc tpl_file_next_line * * what: get the template file plus next line number * * exparg: msg-fmt, formatting for line message, optional * * doc: * This is almost the same as @xref{SCM tpl-file-line}, except that * the line referenced is the next line, per C compiler conventions, and * consequently defaults to the format: # <line-no+1> "<file-name>" =*/ SCM ag_scm_tpl_file_next_line(SCM fmt) { char const * pzFmt = "# %2$d \"%1$s\""; if (AG_SCM_STRING_P(fmt)) pzFmt = ag_scm2zchars(fmt, "file/line format"); return do_tpl_file_line(1, pzFmt); }
/*=gfunc c_string * * what: emit string for ANSI C * general_use: * * exparg: string, string to reformat * * doc: * Reform a string so that, when printed, the C compiler will be able to * compile the data and construct a string that contains exactly what the * current string contains. Many non-printing characters are replaced with * escape sequences. Newlines are replaced with a backslash, an @code{n}, a * closing quote, a newline, seven spaces and another re-opening quote. The * compiler will implicitly concatenate them. The reader will see line * breaks. * * A K&R compiler will choke. Use @code{kr-string} for that compiler. * =*/ SCM ag_scm_c_string(SCM str) { char const * pz = ag_scm2zchars(str, "cstr"); SCM res; pz = optionQuoteString(pz, C_STRING_NEWLINE); res = AG_SCM_STR02SCM(pz); AGFREE(pz); return res; }
/*=gfunc tpl_file_line * * what: get the template file+line number * * exparg: msg-fmt, formatting for line message, optional * * doc: * Returns the file and line number of the current template macro using * either the default format, "from %s line %d", or else the format you * supply. For example, if you want to insert a "C" language file-line * directive, you would supply the format "# %2$d \"%1$s\"", but that * is also already supplied with the scheme variable * @xref{SCM c-file-line-fmt}. You may use it thus: * @example * (tpl-file-line c-file-line-fmt) * @end example * * It is also safe to use the formatting string, "%2$d". AutoGen uses * an argument vector version of printf: @xref{snprintfv}, * and it does not need to know the types of each argument in order to * skip forward to the second argument. =*/ SCM ag_scm_tpl_file_line(SCM fmt) { char const * pzFmt = "from %s line %d"; if (AG_SCM_STRING_P(fmt)) pzFmt = ag_scm2zchars(fmt, "file/line format"); return do_tpl_file_line(0, pzFmt); }
/*=gfunc sprintf * * what: format a string * general_use: * * exparg: format, formatting string * exparg: format-arg, list of arguments to formatting string, opt, list * * doc: Format a string using arguments from the alist. =*/ SCM ag_scm_sprintf(SCM fmt, SCM alist) { int list_len = scm_ilength(alist); char* pzFmt = ag_scm2zchars(fmt, zFormat); if (list_len <= 0) return fmt; return run_printf(pzFmt, list_len, alist); }
/*=gfunc ag_function_p * * what: test for function * * exparg: ag-name, name of AutoGen macro * * doc: return SCM_BOOL_T if a specified name is a user-defined AutoGen * macro, otherwise return SCM_BOOL_F. =*/ SCM ag_scm_ag_function_p(SCM obj) { SCM res; if (findTemplate(ag_scm2zchars(obj, "ag user macro")) == NULL) res = SCM_BOOL_F; else res = SCM_BOOL_T; return res; }
SCM ag_scm_string_start_eqv_match_p(SCM text, SCM substr) { char* pzText = ag_scm2zchars(text, "text to match"); char* pzSubstr = ag_scm2zchars(substr, "match expr"); SCM res; regex_t re; regmatch_t m[2]; compile_re(&re, pzSubstr, REG_EXTENDED | REG_ICASE); if (regexec(&re, pzText, (size_t)2, m, 0) != 0) res = SCM_BOOL_F; else if (m[0].rm_so != 0) res = SCM_BOOL_F; else res = SCM_BOOL_T; regfree(&re); return res; }