YourSensitiveString expand_lcl(const char * local, const char * value) { MACRO_EVAL_CONTEXT ctx = def_ctx; ctx.localname = local; gstr.set(expand_macro(value, TestingMacroSet, ctx)); ystr = gstr.ptr(); return ystr; }
struct token *expand(struct token *original) { const struct token *list; struct token *res; /* Do nothing if there is nothing to expand. */ if (!needs_expansion(original)) return original; list = original; res = calloc(1, sizeof(*res)); res[0] = token_end; while (list->token != END) { const struct macro *def = definition(*list); struct token **args; /* Only expand function-like macros if they appear as function * invocations, beginning with an open paranthesis. */ if (def && !is_macro_expanded(def) && (def->type != FUNCTION_LIKE || peek_next(list + 1) == '(')) { args = read_args(list + 1, &list, def); res = concat(res, expand_macro(def, args)); } else { res = append(res, *list++); } } free(original); return res; }
cell proc_macroexpand(const cell &arglist) { cell macro; if (!arglist.car || (macro = proc_eval(*arglist.car)).type != v_macro) throw(exception("Error: expected macro as first argument to macroexpand.")); return expand_macro(macro, arglist.cdr? *arglist.cdr : cell(v_list)); }
YourSensitiveString expand_as(const char * prefix, const char * value) { MACRO_EVAL_CONTEXT ctx = def_ctx; ctx.subsys = prefix; gstr.set(expand_macro(value, TestingMacroSet, ctx)); ystr = gstr.ptr(); return ystr; }
extern li_object *li_eval(li_object *exp, li_object *env) { li_object *seq, *proc, *args; int done; done = 0; while (!li_is_self_evaluating(exp) && !done) { li_stack_trace_push(exp); if (li_is_symbol(exp)) { exp = li_environment_lookup(env, exp); done = 1; } else if (li_is_quoted(exp)) { check_special_form(li_cdr(exp) && !li_cddr(exp), exp); exp = li_cadr(exp); done = 1; } else if (li_is_quasiquoted(exp)) { check_special_form(li_cdr(exp) && !li_cddr(exp), exp); exp = eval_quasiquote(li_cadr(exp), env); done = 1; } else if (li_is_application(exp)) { proc = li_eval(li_car(exp), env); args = li_cdr(exp); if (li_is_procedure(proc)) args = list_of_values(args, env); if (li_is_lambda(proc)) { env = extend_environment(li_to_lambda(proc).vars, args, li_to_lambda(proc).env); for (seq = li_to_lambda(proc).body; seq && li_cdr(seq); seq = li_cdr(seq)) li_eval(li_car(seq), env); exp = li_car(seq); } else if (li_is_macro(proc)) { exp = expand_macro(proc, args); } else if (li_is_primitive_procedure(proc)) { exp = li_to_primitive_procedure(proc)(args); done = 1; } else if (li_is_special_form(proc)) { exp = li_to_special_form(proc)(args, env); } else { li_error("not applicable", proc); } } else { li_error("unknown expression type", exp); } li_stack_trace_pop(); } return exp; }
/* * eval - eval all macros and builtins calls * argc - number of elements in argv. * argv - element vector : * argv[0] = definition of a user * macro or NULL if built-in. * argv[1] = name of the macro or * built-in. * argv[2] = parameters to user-defined * . macro or built-in. * . * * A call in the form of macro-or-builtin() will result in: * argv[0] = nullstr * argv[1] = macro-or-builtin * argv[2] = nullstr * * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin */ void eval(const char *argv[], int argc, int td, int is_traced) { size_t mark = SIZE_MAX; expansion_id++; if (td & RECDEF) m4errx(1, "expanding recursive definition for %s.", argv[1]); if (is_traced) mark = trace(argv, argc, infile+ilevel); if (td == MACRTYPE) expand_macro(argv, argc); else expand_builtin(argv, argc, td); if (mark != SIZE_MAX) finish_trace(mark); }
static void expand_token (struct obstack *obs, token_type t, token_data *td, int line) { symbol *sym; switch (t) { /* TOKSW */ case TOKEN_EOF: case TOKEN_MACDEF: break; case TOKEN_OPEN: case TOKEN_COMMA: case TOKEN_CLOSE: case TOKEN_SIMPLE: case TOKEN_STRING: shipout_text (obs, TOKEN_DATA_TEXT (td), strlen (TOKEN_DATA_TEXT (td)), line); break; case TOKEN_WORD: sym = lookup_symbol (TOKEN_DATA_TEXT (td), SYMBOL_LOOKUP); if (sym == NULL || SYMBOL_TYPE (sym) == TOKEN_VOID || (SYMBOL_TYPE (sym) == TOKEN_FUNC && SYMBOL_BLIND_NO_ARGS (sym) && peek_token () != TOKEN_OPEN)) { #ifdef ENABLE_CHANGEWORD shipout_text (obs, TOKEN_DATA_ORIG_TEXT (td), strlen (TOKEN_DATA_ORIG_TEXT (td)), line); #else shipout_text (obs, TOKEN_DATA_TEXT (td), strlen (TOKEN_DATA_TEXT (td)), line); #endif } else expand_macro (sym); break; default: M4ERROR ((warning_status, 0, "INTERNAL ERROR: bad token type in expand_token ()")); abort (); } }
/* * eval - eval all macros and builtins calls * argc - number of elements in argv. * argv - element vector : * argv[0] = definition of a user * macro or nil if built-in. * argv[1] = name of the macro or * built-in. * argv[2] = parameters to user-defined * . macro or built-in. * . * * A call in the form of macro-or-builtin() will result in: * argv[0] = nullstr * argv[1] = macro-or-builtin * argv[2] = nullstr * * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin */ void eval(const char *argv[], int argc, int td) { ssize_t mark = -1; expansion_id++; if (td & RECDEF) errx(1, "%s at line %lu: expanding recursive definition for %s", CURRENT_NAME, CURRENT_LINE, argv[1]); if (traced_macros && is_traced(argv[1])) mark = trace(argv, argc, infile+ilevel); if (td == MACRTYPE) expand_macro(argv, argc); else expand_builtin(argv, argc, td); if (mark != -1) finish_trace(mark); }
/************************************************************************ ** get_actuals : Paren must already be found. If all the actuals can ** be read, the macro is pushed and expansion begins. Otherwise, ** this function is quickly exited and lets the tiny lexer take ** care of rescanning. ************************************************************************/ void get_actuals(pdefn_t pdef, int n_formals) { /* ** The only concern with this is that a rescan could finish while ** this is trying to collect actuals. When a rescan finishes, it ** may reset Act_ptr and Exp_ptr. Unless these are saved before the ** end of rescan is handled, the part of the actual collected so far ** would be lost. */ REG ptext_t start; UCHAR c; ptext_t actuals_start; int paste; int level; *Exp_ptr++ = PREVCH(); /* must be oparen */ level = 0; actuals_start = Act_ptr; while( level >= 0) { if(Exp_ptr >= ELIMIT) { fatal_in_macro(10056); } more_white: if( ! can_get_non_white()) { return; } if(CHARMAP(CHECKCH()) == LX_SLASH) { SKIPCH(); if(skip_comment()) { goto more_white; } else { start = Exp_ptr; *Exp_ptr++ = '/'; } } else { start = Exp_ptr; } paste = FALSE; for(;;) { switch(CHARMAP(c = GETCH())) { case LX_CPAREN: if(--level < 0) { goto leave_loop; } break; case LX_COMMA: /* ** if the comma is not at level == 0, it is part of ** a parenthesized list and not a delimiter */ if(level == 0) { goto leave_loop; } break; case LX_SLASH: if( ! skip_comment()) { break; } if(*(Exp_ptr - 1) == ' ') { continue; } c = ' '; break; case LX_CR: case LX_NL: case LX_WHITE: UNGETCH(); /* This char is valid white space */ if( ! can_get_non_white()) { return; } continue; break; case LX_OPAREN: ++level; break; case LX_DQUOTE: case LX_SQUOTE: Exp_ptr = gather_chars(Exp_ptr, c); continue; break; case LX_ID: *Exp_ptr++ = c; while(LXC_IS_IDENT(c = GETCH())) { if(Exp_ptr >= ELIMIT) { fatal_in_macro(10056); } *Exp_ptr++ = c; } if(CHARMAP(c) != LX_MACFORMAL) { UNGETCH(); continue; } paste = TRUE; /* ** FALLTHROUGH */ case LX_MACFORMAL: move_to_exp(do_macformal(&paste)); continue; break; case LX_STRFORMAL: move_to_exp_esc('"', do_strformal()); continue; break; case LX_CHARFORMAL: move_to_exp_esc('\'', do_strformal()); continue; break; case LX_EOS: /* ** Will saving this pointers create dead space in the ** buffers? Yes, but only temporarily. ** ** handle_eos() may reset Act_ptr and Exp_ptr to the ** beginning of the buffers if a rescan is finishing ** and Macro_depth is going to be 0. ANSI allows ** actuals to start within a macro defintion and be ** completed (further actuals and closing paren) later ** in the text. ** ** These buffer pointers will eventually be reset to ** the beginnings of their respective buffers when the ** macro for the actuals being collected right now ** finish rescan ** ** This is special handling for folks who use ** unbalanced parens in macro definitions */ { ptext_t Exp_save; ptext_t Act_save; int eos_res; Exp_save = Exp_ptr; Act_save = Act_ptr; if((eos_res = handle_eos()) & (ACTUAL_EOS | RESCAN_EOS)) { return; } Act_ptr = Act_save; Exp_ptr = Exp_save; if(eos_res == BACKSLASH_EOS) { /* ??? DFP QUESTION */ *Exp_ptr++ = c; /* save the \ */ c = get_non_eof(); /* get char following \ */ break; } } continue; break; } *Exp_ptr++ = c; } leave_loop: /* ** if the last character was whitespace, hose it */ if(CHARMAP(*(Exp_ptr - 1)) == LX_WHITE) { Exp_ptr--; } /* ** if Exp_ptr <= start, foo() was read, don't incr N_actuals */ if(Exp_ptr > start) { N_actuals++; move_to_actual(start, Exp_ptr); } *Exp_ptr++ = c; } P_actuals = actuals_start; if(n_formals < N_actuals) { Msg_Temp = GET_MSG (4002); SET_MSG (Msg_Text, Msg_Temp, Reuse_1); warning(4002); } else if(n_formals > N_actuals) { Msg_Temp = GET_MSG (4003); SET_MSG (Msg_Text, Msg_Temp, Reuse_1); warning(4003); } if(DEFN_TEXT(pdef)) { push_macro(pdef); expand_macro(); } else { /* ** the macro expands to nothing (no definition) ** This essentially means delete the macro and its actuals ** from the expanded text */ Act_ptr = P_actuals; /* reset pointer to get rid of actuals */ Exp_ptr = Save_Exp_ptr; /* delete macro & actuals from exp text */ } }
/************************************************************************ ** handle_eos : handle the end of a string. ************************************************************************/ int handle_eos(void) { if(PREVCH() == '\\') { if(checknl()) { return(FILE_EOS); } else { return(BACKSLASH_EOS); } } if(Macro_depth == 0) { /* found end of file buffer or backslash */ if(io_eob()) { /* end of buffer in here is bad */ Msg_Temp = GET_MSG(1004); SET_MSG (Msg_Text, Msg_Temp); fatal (1004); } return(FILE_EOS); } again: switch(GETCH()) { case EOS_PAD: goto again; case EOS_ACTUAL: /* ** Just finished expanding actual. Check to see if there are ** any more actuals to be expanded. If there are, set up to ** expand them and return. Otherwise, set up to expand defn */ /* move expanded text of this actual to act_buffer */ move_to_actual(CURRENT_TEXT, Exp_ptr); /* reset Exp_ptr for more expansions at this macro depth */ Exp_ptr = CURRENT_TEXT; /* expand next actual if there, otherwise expand definition */ expand_macro(); return(ACTUAL_EOS); break; case EOS_DEFINITION: if(rescan_expansion()) { return(RESCAN_EOS); } else { return(DEFINITION_EOS); } break; case EOS_RESCAN: /* ** Reset Current_char, Exp_ptr and Act_ptr, pop the macro */ /* get input from the previous stream */ Current_char = CURRENT_STRING; /* mark this macro as not expanding */ DEFN_EXPANDING(CURRENT_MACRO)--; /* ** if looking for the actuals of a macro, these pointers ** should really not be reset, however, it is cleaner to ** save them before calling handle_eos, and restore them ** upon returning, than check a static variable here. */ if(Macro_depth == 1) { Act_ptr = ACT_BUFFER; Exp_ptr = EXP_BUFFER; } --Macro_depth; return(DEFINITION_EOS); break; /* the following conditional compile is so brackets match */ } }
static void mcpp_main( void) /* * Main process for mcpp -- copies tokens from the current input stream * (main file or included file) to the output file. */ { int c; /* Current character */ char * wp; /* Temporary pointer */ DEFBUF * defp; /* Macro definition */ int line_top; /* Is in the line top, possibly spaces */ LINE_COL line_col; /* Location of macro call in source */ keep_comments = option_flags.c && !no_output; keep_spaces = option_flags.k; /* Will be turned off if !compiling */ line_col.col = line_col.line = 0L; /* * This loop is started "from the top" at the beginning of each line. * 'wrong_line' is set TRUE in many places if it is necessary to write * a #line record. (But we don't write them when expanding macros.) * * 'newlines' variable counts the number of blank lines that have been * skipped over. These are then either output via #line records or * by outputting explicit blank lines. * 'newlines' will be cleared on end of an included file by get_ch(). */ while (1) { /* For the whole input */ newlines = 0; /* Count empty lines */ while (1) { /* For each line, ... */ out_ptr = output; /* Top of the line buf */ c = get_ch(); if (src_col) break; /* There is a residual tokens on the line */ while (char_type[ c] & HSP) { /* ' ' or '\t' */ if (c != COM_SEP) *out_ptr++ = c; /* Retain line top white spaces */ /* Else skip 0-length comment */ c = get_ch(); } if (c == '#') { /* Is 1st non-space '#' */ directive(); /* Do a #directive */ } else if (mcpp_mode == STD && option_flags.dig && c == '%') { /* In POST_STD digraphs are already converted */ if (get_ch() == ':') { /* '%:' i.e. '#' */ directive(); /* Do a #directive */ } else { unget_ch(); if (! compiling) { skip_nl(); newlines++; } else { break; } } } else if (c == CHAR_EOF) { /* End of input */ break; } else if (! compiling) { /* #ifdef false? */ skip_nl(); /* Skip to newline */ newlines++; /* Count it, too. */ } else if (in_asm && ! no_output) { /* In #asm block */ put_asm(); /* Put out as it is */ } else if (c == '\n') { /* Blank line */ if (keep_comments) mcpp_fputc( '\n', OUT); /* May flush comments */ else newlines++; /* Wait for a token */ } else { break; /* Actual token */ } } if (c == CHAR_EOF) /* Exit process at */ break; /* end of input */ /* * If the loop didn't terminate because of end of file, we * know there is a token to compile. First, clean up after * absorbing newlines. newlines has the number we skipped. */ if (no_output) { wrong_line = FALSE; } else { if (wrong_line || newlines > 10) { sharp( NULL, 0); /* Output # line number */ if (keep_spaces && src_col) { while (src_col--) /* Adjust columns */ mcpp_fputc( ' ', OUT); src_col = 0; } } else { /* If just a few, stuff */ while (newlines-- > 0) /* them out ourselves */ mcpp_fputc('\n', OUT); } } /* * Process each token on this line. */ line_top = TRUE; while (c != '\n' && c != CHAR_EOF) { /* For the whole line */ /* * has_pragma is set to TRUE so as to execute _Pragma() operator * when the psuedo macro _Pragma() is found. */ int has_pragma; if ((mcpp_debug & MACRO_CALL) && ! in_directive) { line_col.line = src_line; /* Location in source */ line_col.col = infile->bptr - infile->buffer - 1; } if (scan_token( c, (wp = out_ptr, &wp), out_wend) == NAM && (defp = is_macro( &wp)) != NULL) { /* A macro */ wp = expand_macro( defp, out_ptr, out_wend, line_col , & has_pragma); /* Expand it completely */ if (line_top) { /* The first token is a macro */ char * tp = out_ptr; while (char_type[ *tp & UCHARMAX] & HSP) tp++; /* Remove excessive spaces */ memmove( out_ptr, tp, strlen( tp) + 1); wp -= (tp - out_ptr); } if (has_pragma) { /* Found _Pramga() */ do_pragma_op(); /* Do _Pragma() operator*/ out_ptr = output; /* Do the rest of line */ wrong_line = TRUE; /* Line-num out of sync */ } else { out_ptr = wp; } if (keep_spaces && wrong_line && infile && *(infile->bptr) != '\n' && *(infile->bptr) != EOS) { src_col = infile->bptr - infile->buffer; /* Remember the current colums */ break; /* Do sharp() now */ } } else { /* Not a macro call */ out_ptr = wp; /* Advance the place */ if (wrong_line) /* is_macro() swallowed */ break; /* the newline */ } while (char_type[ c = get_ch()] & HSP) { /* Horizontal space */ if (c != COM_SEP) /* Skip 0-length comment*/ *out_ptr++ = c; } line_top = FALSE; /* Read over some token */ } /* Loop for line */ putout( output); /* Output the line */ } /* Continue until EOF */ }
static int parse_fields (char *msg) { char stemp[MAX_MSG_LEN+1]; char *e; char *save; strcpy (stemp, msg); e = strtok_r (stemp, "*#", &save); while (e != NULL) { switch (*e) { case 'A': switch (e[1]) { case 'A': parse_object_name (e); break; case 'B': parse_symbol (e); break; case 'C': /* * New in 1.2: test for 10 digit callsign. */ if (tt_call10_to_text(e+2,1,stemp) == 0) { strcpy(m_callsign, stemp); } break; default: parse_callsign (e); break; } break; case 'B': parse_location (e); break; case 'C': parse_comment (e); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': expand_macro (e); break; case '\0': /* Empty field. Just ignore it. */ /* This would happen if someone uses a leading *. */ break; default: text_color_set(DW_COLOR_ERROR); dw_printf ("Entry does not start with A, B, C, or digit: \"%s\"\n", msg); return (TT_ERROR_D_MSG); } e = strtok_r (NULL, "*#", &save); } return (0); } /* end parse_fields */
cell proc_eval(const cell &x) { bool listvars = false; if (listvars) proc_listvars(cell()); if (x.type == v_string || x.type == v_number || x.type == v_function) return x; else if (x.type == v_symbol) { //std::cout << "fetching var " << x.str << ": " << toString(env->get(x.str)) << "\n"; return env->get(x.str); } else if (x.type == v_list) { if (!x.car) return nil; cell head = proc_eval(*x.car); if (head.type == v_proc) return head.proc(x.cdr? *x.cdr : nil); if (head.type == v_function) { std::shared_ptr<environment> oldenv = env; std::shared_ptr<environment> newenv(new environment(head.env)); const cell *name_iter = head.car; const cell *arg_iter = x.cdr; while (arg_iter && arg_iter->car && name_iter && name_iter->car) { if (name_iter->car->str == "&REST") { if (!(name_iter->cdr && name_iter->cdr->car && name_iter->cdr->car->type == v_symbol)) throw(exception("Error: expected name for &rest parameter")); cell head(v_list); cell *tail = &head; while (arg_iter && arg_iter->car) { tail->car = new cell(); *tail->car = proc_eval(*arg_iter->car); tail->cdr = new cell(v_list); tail = tail->cdr; arg_iter = arg_iter->cdr; } newenv->vars[name_iter->cdr->car->str] = head; name_iter = name_iter->cdr->cdr; break; //skip the outer loop so we don't dereference the null car pointer. } newenv->vars[name_iter->car->str] = proc_eval(*arg_iter->car); arg_iter = arg_iter->cdr; name_iter = name_iter->cdr; } if (arg_iter && arg_iter->car) throw(exception("Error: too many arguments to function")); if (name_iter && name_iter->car) throw(exception("Error: too few arguments to function")); env = newenv; cell result; cell *body_iter = head.cdr; while (body_iter && body_iter->car) { result = proc_eval(*body_iter->car); body_iter = body_iter->cdr; } env = oldenv; return result; } if (head.type == v_macro) return proc_eval(expand_macro(head, x.cdr? *x.cdr : cell(v_list))); throw(exception("Error: attempt to call non-proc")); } else { throw(exception("Unrecognised cell type! (eval)")); } }
void expand_macro(source_t *src, string_t *destination, wchar_t *current_string, boolean_t cmd) { string_t str; wchar_t buf[STRING_BUFFER_LENGTH]; wchar_t closer = L'\0'; unsigned int closer_level = 0; wchar_t *block_start; boolean_t quote_seen; /* * First, we copy the (macro-expanded) macro name into string. */ INIT_STRING_FROM_STACK(str, buf); recheck_first_char: /* * Check the first char of the macro name to figure out what to do. */ switch (source_popchar(src)) { case L'\0': src = get_next_block_fn(src); if (src == NULL) { fatal_reader_mksh(catgets(libmksdmsi18n_catd, 1, 114, "'$' at end of string `%ls'"), current_string); } if (src->src_error_converting == B_TRUE) { fatal_reader_mksh(NOCATGETS("Internal error: Invalid" " byte sequence in expand_macro()")); } goto recheck_first_char; case L'(': /* * Multi-character name. */ closer = L')'; break; case L'{': /* * Multi-character name. */ closer = L'}'; break; case L'\n': fatal_reader_mksh(catgets(libmksdmsi18n_catd, 1, 115, "'$' at end of line")); break; default: /* * Single-character macro name. Just suck it up. */ append_char(*src->src_str.str_p, &str); goto next_step; } /* * Handle multi-character macro names: */ block_start = src->src_str.str_p; quote_seen = B_FALSE; for (;; (void) source_popchar(src)) { wchar_t c; switch (c = source_peekchar(src)) { case L'\0': append_string_wide(block_start, &str, src->src_str.str_p - block_start); src = get_next_block_fn(src); if (src == NULL) { if (current_string != NULL) { fatal_reader_mksh(catgets( libmksdmsi18n_catd, 1, 116, "Unmatched `%lc' in string `%ls'"), closer == L'}' ? L'{' : L'(', current_string); } else { fatal_reader_mksh(catgets( libmksdmsi18n_catd, 1, 117, "Premature EOF")); } } if (src->src_error_converting == B_TRUE) { fatal_reader_mksh(NOCATGETS("Internal error: " "Invalid byte sequence in expand_macro()")); } block_start = src->src_str.str_p; src->src_str.str_p--; /* because we're about to pop */ continue; case L'\n': fatal_reader_mksh(catgets(libmksdmsi18n_catd, 1, 118, "Unmatched `%lc' on line"), closer == L'}' ? L'{' : L'('); break; case L'\\': /* * Quote '$' in macro value: */ if (cmd == B_FALSE) { quote_seen = quote_seen == B_TRUE ? B_FALSE : B_TRUE; } continue; case L'$': /* * Macro names may reference macros. * This expands the value of such macros into the * macro name string. */ if (quote_seen == B_TRUE) { append_string_wide(block_start, &str, src->src_str.str_p - block_start - 1); block_start = src->src_str.str_p; break; } append_string_wide(block_start, &str, src->src_str.str_p - block_start); /* * Move past the $ for this macro, and expand * the reference: */ (void) source_popchar(src); expand_macro(src, &str, current_string, cmd); block_start = src->src_str.str_p; src->src_str.str_p--; /* because about to pop */ break; case L'(': /* * Allow nested pairs of () in the macro name. */ if (closer == L')') closer_level++; break; case L'{': /* * Allow nested pairs of {} in the macro name. */ if (closer == L'}') closer_level++; break; case L')': case L'}': /* * End of the name. Save the string in the macro * name string. */ if (c == closer && --closer_level == 0) { append_string_wide(block_start, &str, src->src_str.str_p - block_start); /* * Discard the closer character, and get out: */ (void) source_popchar(src); goto next_step; } break; } quote_seen = B_FALSE; } next_step: /* * We have the macro name. Inspect it to see if it specifies * any translations of the value. */ _process_macro_name(&str, destination, cmd); if (str.str_free_after_use == B_TRUE) free(str.str_buf_start); }
/* * expand_value(value, destination, cmd) * * Recursively expands all macros in the string value. * destination is where the expanded value should be appended. * * Parameters: * value The value we are expanding * destination Where to deposit the expansion * cmd If we are evaluating a command line we * turn \ quoting off * * Global variables used: */ void expand_value(name_t *value, string_t *destination, boolean_t cmd) { source_t *src; wchar_t *block_start; boolean_t quote_seen = B_FALSE; if (value == NULL) { /* * Make sure to get a string allocated even if it * will be empty. * XXX this seems like it might be a misunderstanding * of the interface? Surely we can guarantee that * every string_t is allocated correctly... */ append_string_wide(L"", destination, FIND_LENGTH); destination->str_end = destination->str_p; return; } if (value->n_dollar == B_FALSE) { /* * If the value we are expanding does not contain * any $, we don't have to parse it. */ append_string_name(value, destination); destination->str_end = destination->str_p; return; } if (value->n_being_expanded == B_TRUE) { fatal_reader_mksh(catgets(libmksdmsi18n_catd, 1, 113, "Loop detected when expanding macro value `%ls'"), value->n_key); } value->n_being_expanded = B_TRUE; src = source_from_wchar(value->n_key); /* * We parse the string in segments. * We read characters until we find a $, then we append what we have * read so far (since last $ processing) to the destination. When * we find a '$', we call expand_macro() and let it expand that * particular $ reference into destination. */ quote_seen = B_FALSE; block_start = src->src_str.str_p; for (;; (void) source_popchar(src)) { switch (source_peekchar(src)) { case L'\\': /* Quote $ in macro value */ if (cmd == B_FALSE) { quote_seen = quote_seen == B_TRUE ? B_FALSE : B_TRUE; } continue; case L'$': /* Save the plain string we found since */ /* start of string or previous $ */ if (quote_seen == B_TRUE) { append_string_wide(block_start, destination, src->src_str.str_p - block_start - 1); block_start = src->src_str.str_p; break; } append_string_wide(block_start, destination, src->src_str.str_p - block_start); /* * Move past the $ for this macro, and expand * the reference: */ (void) source_popchar(src); expand_macro(src, destination, src->src_str.str_buf_start, cmd); block_start = src->src_str.str_p + 1; break; case L'\0': /* The string ran out. Get some more */ append_string_wide(block_start, destination, src->src_str.str_p - block_start); src = get_next_block_fn(src); if (src == NULL) { /* * We're done. */ destination->str_end = destination->str_p; value->n_being_expanded = B_FALSE; return; } if (src->src_error_converting == B_TRUE) { fatal_reader_mksh(NOCATGETS("Internal error: " "Invalid byte sequence in expand_value()")); } block_start = src->src_str.str_p; src->src_str.str_p--; /* because we're about to pop */ continue; } quote_seen = B_FALSE; } #if 0 out: if (src->src_string.str_free_after_use == B_TRUE) free(sourceb.src_string.str_buf_start); #endif }
// The expand helpers return a ystr, and also store the malloc'ed return value // in an auto_free_ptr so that the will be automatically freed by the next REQUIRE // YourSensitiveString expand(const char * value) { gstr.set(expand_macro(value, TestingMacroSet, def_ctx)); ystr = gstr.ptr(); return ystr; }