/* ----------------------------------------------------------------------- * * * Copyright 1996-2020 The NASM Authors - All Rights Reserved * See the file AUTHORS included with the NASM distribution for * the specific copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following * conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ----------------------------------------------------------------------- */ /* * preproc.c macro preprocessor for the Netwide Assembler */ /* Typical flow of text through preproc * * pp_getline gets tokenized lines, either * * from a macro expansion * * or * { * read_line gets raw text from stdmacpos, or predef, or current input file * tokenize converts to tokens * } * * expand_mmac_params is used to expand %1 etc., unless a macro is being * defined or a false conditional is being processed * (%0, %1, %+1, %-1, %%foo * * do_directive checks for directives * * expand_smacro is used to expand single line macros * * expand_mmacro is used to expand multi-line macros * * detoken is used to convert the line back to text */ #include "compiler.h" #include "nctype.h" #include "nasm.h" #include "nasmlib.h" #include "error.h" #include "preproc.h" #include "hashtbl.h" #include "quote.h" #include "stdscan.h" #include "eval.h" #include "tokens.h" #include "tables.h" #include "listing.h" /* * Preprocessor execution options that can be controlled by %pragma or * other directives. This structure is initialized to zero on each * pass; this *must* reflect the default initial state. */ static struct pp_opts { … } ppopt; SMacro; MMacro; MMacroInvocation; Context; Token; Line; Include; Cond; /* * This is the internal form which we break input lines up into. * Typically stored in linked lists. * * Note that `type' serves a double meaning: TOK_SMAC_START_PARAMS is * not necessarily used as-is, but is also used to encode the number * and expansion type of substituted parameter. So in the definition * * %define a(x,=y) ( (x) & ~(y) ) * * the token representing `x' will have its type changed to * tok_smac_param(0) but the one representing `y' will be * tok_smac_param(1); see the accessor functions below. * * TOK_INTERNAL_STRING is a string which has been unquoted, but should * be treated as if it was a quoted string. The code is free to change * one into the other at will. TOK_NAKED_STRING is a text token which * should be treated as a string, but which MUST NOT be turned into a * quoted string. TOK_INTERNAL_STRINGs can contain any character, * including NUL, but TOK_NAKED_STRING must be a valid C string. */ enum pp_token_type { … }; static inline enum pp_token_type tok_smac_param(int param) { … } static int smac_nparam(enum pp_token_type toktype) { … } static bool is_smac_param(enum pp_token_type toktype) { … } #define PP_CONCAT_MASK(x) … struct tokseq_match { … }; /* * This is tuned so struct Token should be 64 bytes on 64-bit * systems and 32 bytes on 32-bit systems. It enables them * to be nicely cache aligned, and the text to still be kept * inline for nearly all tokens. * * We prohibit tokens of length > MAX_TEXT even though * length here is an unsigned int; this avoids problems * if the length is passed through an interface with type "int", * and is absurdly large anyway. * * For the text mode, in pointer mode the pointer is stored at the end * of the union and the pad field is cleared. This allows short tokens * to be unconditionally tested for by only looking at the first text * bytes and not examining the type or len fields. */ #define INLINE_TEXT … #define MAX_TEXT … struct Token { … }; /* * Note on the storage of both SMacro and MMacros: the hash table * indexes them case-insensitively, and we then have to go through a * linked list of potential case aliases (and, for MMacros, parameter * ranges); this is to preserve the matching semantics of the earlier * code. If the number of case aliases for a specific macro is a * performance issue, you may want to reconsider your coding style. */ /* * Function call tp obtain the expansion of an smacro */ ExpandSMacro; /* * Store the definition of a single-line macro. */ enum sparmflags { … }; struct smac_param { … }; struct SMacro { … }; /* * "No listing" flags. Inside a loop (%rep..%endrep) we may have * macro listing suppressed with .nolist, but we still need to * update line numbers for error messages and debug information... * unless we are nested inside an actual .nolist macro. */ enum nolist_flags { … }; /* * Store the definition of a multi-line macro. This is also used to * store the interiors of `%rep...%endrep' blocks, which are * effectively self-re-invoking multi-line macros which simply * don't have a name or bother to appear in the hash tables. %rep * blocks are signified by having a NULL `name' field. * * In a MMacro describing a `%rep' block, the `in_progress' field * isn't merely boolean, but gives the number of repeats left to * run. * * The `next' field is used for storing MMacros in hash tables; the * `next_active' field is for stacking them on istk entries. * * When a MMacro is being expanded, `params', `iline', `nparam', * `paramlen', `rotate' and `unique' are local to the invocation. */ /* * Expansion stack. Note that .mmac can point back to the macro itself, * whereas .mstk cannot. */ struct mstk { … }; struct MMacro { … }; /* Store the definition of a multi-line macro, as defined in a * previous recursive macro expansion. */ #if 0 struct MMacroInvocation { MMacroInvocation *prev; /* previous invocation */ Token **params; /* actual parameters */ Token *iline; /* invocation line */ unsigned int nparam, rotate; int *paramlen; uint64_t unique; uint64_t condcnt; }; #endif /* * The context stack is composed of a linked list of these. */ struct Context { … }; static inline const char *tok_text(const struct Token *t) { … } /* * Returns a mutable pointer to the text buffer. The text can be changed, * but the length MUST NOT CHANGE, in either direction; nor is it permitted * to pad with null characters to create an artificially shorter string. */ static inline char *tok_text_buf(struct Token *t) { … } static inline unsigned int tok_check_len(size_t len) { … } static inline bool tok_text_match(const struct Token *a, const struct Token *b) { … } static inline unused_func bool tok_match(const struct Token *a, const struct Token *b) { … } /* strlen() variant useful for set_text() and its variants */ static size_t tok_strlen(const char *str) { … } /* * Set the text field to a copy of the given string; the length if * not given should be obtained with tok_strlen(). */ static Token *set_text(struct Token *t, const char *text, size_t len) { … } /* * Set the text field to the existing pre-allocated string, either * taking over or freeing the allocation in the process. */ static Token *set_text_free(struct Token *t, char *text, unsigned int len) { … } /* * Allocate a new buffer containing a copy of the text field * of the token. */ static char *dup_text(const struct Token *t) { … } /* * Multi-line macro definitions are stored as a linked list of * these, which is essentially a container to allow several linked * lists of Tokens. * * Note that in this module, linked lists are treated as stacks * wherever possible. For this reason, Lines are _pushed_ on to the * `expansion' field in MMacro structures, so that the linked list, * if walked, would give the macro lines in reverse order; this * means that we can walk the list when expanding a macro, and thus * push the lines on to the `expansion' field in _istk_ in reverse * order (so that when popped back off they are in the right * order). It may seem cockeyed, and it relies on my design having * an even number of steps in, but it works... * * Some of these structures, rather than being actual lines, are * markers delimiting the end of the expansion of a given macro. * This is for use in the cycle-tracking and %rep-handling code. * Such structures have `finishes' non-NULL, and `first' NULL. All * others have `finishes' NULL, but `first' may still be NULL if * the line is blank. */ struct Line { … }; /* * To handle an arbitrary level of file inclusion, we maintain a * stack (ie linked list) of these things. * * Note: when we issue a message for a continuation line, we want to * issue it for the actual *start* of the continuation line. This means * we need to remember how many lines to skip over for the next one. */ struct Include { … }; /* * File real name hash, so we don't have to re-search the include * path for every pass (and potentially more than that if a file * is used more than once.) */ struct hash_table FileHash; /* * Counters to trap on insane macro recursion or processing. * Note: for smacros these count *down*, for mmacros they count *up*. */ struct deadman { … }; static struct deadman smacro_deadman, mmacro_deadman; /* * Conditional assembly: we maintain a separate stack of these for * each level of file inclusion. (The only reason we keep the * stacks separate is to ensure that a stray `%endif' in a file * included from within the true branch of a `%if' won't terminate * it and cause confusion: instead, rightly, it'll cause an error.) */ enum cond_state { … }; struct Cond { … }; #define emitting(x) … /* * These defines are used as the possible return values for do_directive */ #define NO_DIRECTIVE_FOUND … #define DIRECTIVE_FOUND … /* * Condition codes. Note that we use c_ prefix not C_ because C_ is * used in nasm.h for the "real" condition codes. At _this_ level, * we treat CXZ and ECXZ as condition codes, albeit non-invertible * ones, so we need a different enum... */ static const char * const conditions[] = …; enum pp_conds { … }; static const enum pp_conds inverse_ccs[] = …; /* * Directive names. */ /* If this is a an IF, ELIF, ELSE or ENDIF keyword */ static int is_condition(enum preproc_token arg) { … } /* For TASM compatibility we need to be able to recognise TASM compatible * conditional compilation directives. Using the NASM pre-processor does * not work, so we look for them specifically from the following list and * then jam in the equivalent NASM directive into the input stream. */ enum { … }; static const char * const tasm_directives[] = …; static int StackSize = …; static const char *StackPointer = …; static int ArgOffset = …; static int LocalOffset = …; static Context *cstk; static Include *istk; static const struct strlist *ipath_list; static struct strlist *deplist; static uint64_t unique; /* unique identifier numbers */ static Line *predef = …; static bool do_predef; static enum preproc_mode pp_mode; /* * The current set of multi-line macros we have defined. */ static struct hash_table mmacros; /* * The current set of single-line macros we have defined. */ static struct hash_table smacros; /* * The multi-line macro we are currently defining, or the %rep * block we are currently reading, if any. */ static MMacro *defining; static uint64_t nested_mac_count; static uint64_t nested_rep_count; /* * The number of macro parameters to allocate space for at a time. */ #define PARAM_DELTA … /* * The standard macro set: defined in macros.c in a set of arrays. * This gives our position in any macro set, while we are processing it. * The stdmacset is an array of such macro sets. */ static macros_t *stdmacpos; static macros_t **stdmacnext; static macros_t *stdmacros[8]; static macros_t *extrastdmac; /* * Map of which %use packages have been loaded */ static bool *use_loaded; /* * Forward declarations. */ static void pp_add_stdmac(macros_t *macros); static Token *expand_mmac_params(Token * tline); static Token *expand_smacro(Token * tline); static Token *expand_id(Token * tline); static Context *get_ctx(const char *name, const char **namep); static Token *make_tok_num(Token *next, int64_t val); static Token *make_tok_qstr(Token *next, const char *str); static Token *make_tok_qstr_len(Token *next, const char *str, size_t len); static Token *make_tok_char(Token *next, char op); static Token *new_Token(Token * next, enum pp_token_type type, const char *text, size_t txtlen); static Token *new_Token_free(Token * next, enum pp_token_type type, char *text, size_t txtlen); static Token *dup_Token(Token *next, const Token *src); static Token *new_White(Token *next); static Token *delete_Token(Token *t); static Token *steal_Token(Token *dst, Token *src); static const struct use_package * get_use_pkg(Token *t, const char *dname, const char **name); static void mark_smac_params(Token *tline, const SMacro *tmpl, enum pp_token_type type); /* Safe test for token type, false on x == NULL */ static inline bool tok_type(const Token *x, enum pp_token_type t) { … } /* Whitespace token? */ static inline bool tok_white(const Token *x) { … } /* Skip past any whitespace */ static inline Token *skip_white(Token *x) { … } /* Delete any whitespace */ static Token *zap_white(Token *x) { … } /* * Single special character tests. The use of & rather than && is intentional; it * tells the compiler that it is safe to access text.a[1] unconditionally; hopefully * a smart compiler should turn it into a 16-bit memory reference. */ static inline bool tok_is(const Token *x, char c) { … } /* True if any other kind of token that "c", but not NULL */ static inline bool tok_isnt(const Token *x, char c) { … } /* * Unquote a token if it is a string, and set its type to * TOK_INTERNAL_STRING. */ static const char *unquote_token(Token *t) { … } /* * Same as unquote_token(), but error out if the resulting string * contains unacceptable control characters. */ static const char *unquote_token_cstr(Token *t) { … } /* * Convert a TOK_INTERNAL_STRING token to a quoted * TOK_STRING tokens. */ static Token *quote_any_token(Token *t); static inline unused_func Token *quote_token(Token *t) { … } /* * Convert *any* kind of token to a quoted * TOK_STRING token. */ static Token *quote_any_token(Token *t) { … } /* * In-place reverse a list of tokens. */ static Token *reverse_tokens(Token *t) { … } /* * getenv() variant operating on an input token */ static const char *pp_getenv(const Token *t, bool warn) { … } /* * Handle TASM specific directives, which do not contain a % in * front of them. We do it here because I could not find any other * place to do it for the moment, and it is a hack (ideally it would * be nice to be able to use the NASM pre-processor to do it). */ static char *check_tasm_directive(char *line) { … } /* * The pre-preprocessing stage... This function translates line * number indications as they emerge from GNU cpp (`# lineno "file" * flags') into NASM preprocessor line number indications (`%line * lineno file'). */ static inline char *prepreproc(char *line) { … } /* * Free a linked list of tokens. */ static void free_tlist(Token * list) { … } /* * Free a linked list of lines. */ static void free_llist(Line * list) { … } /* * Free an array of linked lists of tokens */ static void free_tlist_array(Token **array, size_t nlists) { … } /* * Duplicate a linked list of tokens. */ static Token *dup_tlist(const Token *list, Token ***tailp) { … } /* * Duplicate a linked list of tokens with a maximum count */ static Token *dup_tlistn(const Token *list, size_t cnt, Token ***tailp) { … } /* * Duplicate a linked list of tokens in reverse order */ static Token *dup_tlist_reverse(const Token *list, Token *tail) { … } /* * Free an MMacro */ static void free_mmacro(MMacro * m) { … } /* * Clear or free an SMacro */ static void free_smacro_members(SMacro *s) { … } static void clear_smacro(SMacro *s) { … } /* * Free an SMacro */ static void free_smacro(SMacro *s) { … } /* * Free all currently defined macros, and free the hash tables if empty */ enum clear_what { … }; static void clear_smacro_table(struct hash_table *smt, enum clear_what what) { … } static void free_smacro_table(struct hash_table *smt) { … } static void free_mmacro_table(struct hash_table *mmt) { … } static void free_macros(void) { … } /* * Initialize the hash tables */ static void init_macros(void) { … } /* * Pop the context stack. */ static void ctx_pop(void) { … } /* * Search for a key in the hash index; adding it if necessary * (in which case we initialize the data pointer to NULL.) */ static void ** hash_findi_add(struct hash_table *hash, const char *str) { … } /* * Like hash_findi, but returns the data element rather than a pointer * to it. Used only when not adding a new element, hence no third * argument. */ static void * hash_findix(struct hash_table *hash, const char *str) { … } /* * read line from standart macros set, * if there no more left -- return NULL */ static char *line_from_stdmac(void) { … } /* * Read a line from a file. Return NULL on end of file. */ static char *line_from_file(FILE *f) { … } /* * Common read routine regardless of source */ static char *read_line(void) { … } /* * Tokenize a line of text. This is a very simple process since we * don't need to parse the value out of e.g. numeric tokens: we * simply split one string into many. */ static Token *tokenize(const char *line) { … } /* * Tokens are allocated in blocks to improve speed. Set the blocksize * to 0 to use regular nasm_malloc(); this is useful for debugging. * * alloc_Token() returns a zero-initialized token structure. */ #define TOKEN_BLOCKSIZE … #if TOKEN_BLOCKSIZE static Token *freeTokens = …; static Token *tokenblocks = …; static Token *alloc_Token(void) { … } static Token *delete_Token(Token *t) { … } static void delete_Blocks(void) { … } #else static inline Token *alloc_Token(void) { Token *t; nasm_new(*t); return t; } static Token *delete_Token(Token *t) { Token *next = t->next; nasm_free(t); return next; } static inline void delete_Blocks(void) { /* Nothing to do */ } #endif /* * this function creates a new Token and passes a pointer to it * back to the caller. It sets the type, text, and next pointer elements. */ static Token *new_Token(Token * next, enum pp_token_type type, const char *text, size_t txtlen) { … } /* * Same as new_Token(), but text belongs to the new token and is * either taken over or freed. This function MUST be called * with valid txt and txtlen, unlike new_Token(). */ static Token *new_Token_free(Token * next, enum pp_token_type type, char *text, size_t txtlen) { … } static Token *dup_Token(Token *next, const Token *src) { … } static Token *new_White(Token *next) { … } /* * This *transfers* the content from one token to another, leaving the * next pointer of the latter intact. Unlike dup_Token(), the old * token is destroyed, except for its next pointer, and the text * pointer allocation, if any, is simply transferred. */ static Token *steal_Token(Token *dst, Token *src) { … } /* * Convert a line of tokens back into text. This modifies the list * by expanding environment variables. * * If expand_locals is not zero, identifiers of the form "%$*xxx" * are also transformed into [email protected] */ static char *detoken(Token * tlist, bool expand_locals) { … } /* * A scanner, suitable for use by the expression evaluator, which * operates on a line of Tokens. Expects a pointer to a pointer to * the first token in the line to be passed in as its private_data * field. * * FIX: This really needs to be unified with stdscan. */ struct ppscan { … }; static int ppscan(void *private_data, struct tokenval *tokval) { … } /* * 1. An expression (true if nonzero 0) * 2. The keywords true, on, yes for true * 3. The keywords false, off, no for false * 4. An empty line, for true * * On error, return defval (usually the previous value) */ static bool pp_get_boolean_option(Token *tline, bool defval) { … } /* * Compare a string to the name of an existing macro; this is a * simple wrapper which calls either strcmp or nasm_stricmp * depending on the value of the `casesense' parameter. */ static int mstrcmp(const char *p, const char *q, bool casesense) { … } /* * Compare a string to the name of an existing macro; this is a * simple wrapper which calls either strcmp or nasm_stricmp * depending on the value of the `casesense' parameter. */ static int mmemcmp(const char *p, const char *q, size_t l, bool casesense) { … } /* * Return the Context structure associated with a %$ token. Return * NULL, having _already_ reported an error condition, if the * context stack isn't deep enough for the supplied number of $ * signs. * * If "namep" is non-NULL, set it to the pointer to the macro name * tail, i.e. the part beyond %$... */ static Context *get_ctx(const char *name, const char **namep) { … } /* * Open an include file. This routine must always return a valid * file pointer if it returns - it's responsible for throwing an * ERR_FATAL and bombing out completely if not. It should also try * the include path one by one until it finds the file or reaches * the end of the path. * * Note: for INC_PROBE the function returns NULL at all times; * instead look for the */ enum incopen_mode { … }; /* This is conducts a full pathname search */ static FILE *inc_fopen_search(const char *file, char **slpath, enum incopen_mode omode, enum file_flags fmode) { … } /* * Open a file, or test for the presence of one (depending on omode), * considering the include path. */ static FILE *inc_fopen(const char *file, struct strlist *dhead, const char **found_path, enum incopen_mode omode, enum file_flags fmode) { … } /* * Opens an include or input file. Public version, for use by modules * that get a file:lineno pair and need to look at the file again * (e.g. the CodeView debug backend). Returns NULL on failure. */ FILE *pp_input_fopen(const char *filename, enum file_flags mode) { … } /* * Determine if we should warn on defining a single-line macro of * name `name', with `nparam' parameters. If nparam is 0 or -1, will * return true if _any_ single-line macro of that name is defined. * Otherwise, will return true if a single-line macro with either * `nparam' or no parameters is defined. * * If a macro with precisely the right number of parameters is * defined, or nparam is -1, the address of the definition structure * will be returned in `defn'; otherwise NULL will be returned. If `defn' * is NULL, no action will be taken regarding its contents, and no * error will occur. * * Note that this is also called with nparam zero to resolve * `ifdef'. */ static bool smacro_defined(Context *ctx, const char *name, int nparam, SMacro **defn, bool nocase, bool find_alias) { … } /* param should be a natural number [0; INT_MAX] */ static int read_param_count(const char *str) { … } /* * Count and mark off the parameters in a multi-line macro call. * This is called both from within the multi-line macro expansion * code, and also to mark off the default parameters when provided * in a %macro definition line. * * Note that we need space in the params array for parameter 0 being * a possible captured label as well as the final NULL. * * Returns a pointer to the pointer to a terminal comma if present; * used to drop an empty terminal argument for legacy reasons. */ static Token **count_mmac_params(Token *tline, int *nparamp, Token ***paramsp) { … } /* * Determine whether one of the various `if' conditions is true or * not. * * We must free the tline we get passed. */ static enum cond_state if_condition(Token * tline, enum preproc_token ct) { … } /* * Default smacro expansion routine: just returns a copy of the * expansion list. */ static Token * smacro_expand_default(const SMacro *s, Token **params, int nparams) { … } /* * Emit a macro defintion or undef to the listing file, if * desired. This is similar to detoken(), but it handles the reverse * expansion list, does not expand %! or local variable tokens, and * does some special handling for macro parameters. */ static void list_smacro_def(enum preproc_token op, const Context *ctx, const SMacro *m) { … } /* * Parse smacro arguments, return argument count. If the tmpl argument * is set, set the nparam, greedy and params field in the template. * *tpp is updated to point to the pointer to the first token after the * prototype. * * The text values from any argument tokens are "stolen" and the * corresponding text fields set to NULL. */ static int parse_smacro_template(Token ***tpp, SMacro *tmpl) { … } /* * Common code for defining an smacro. The tmpl argument, if not NULL, * contains any macro parameters that aren't explicit arguments; * those are the more uncommon macro variants. */ static SMacro *define_smacro(const char *mname, bool casesense, Token *expansion, SMacro *tmpl) { … } /* * Undefine an smacro */ static void undef_smacro(const char *mname, bool undefalias) { … } /* * Parse a mmacro specification. */ static bool parse_mmacro_spec(Token *tline, MMacro *def, const char *directive) { … } /* * Decode a size directive */ static int parse_size(const char *str) { … } /* * Process a preprocessor %pragma directive. Currently there are none. * Gets passed the token list starting with the "preproc" token from * "%pragma preproc". */ static void do_pragma_preproc(Token *tline) { … } static bool is_macro_id(const Token *t) { … } static const char *get_id(Token **tp, const char *dname) { … } /* Parse a %use package name and find the package. Set *err on syntax error. */ static const struct use_package * get_use_pkg(Token *t, const char *dname, const char **name) { … } /* * Mark parameter tokens in an smacro definition. If the type argument * is 0, create smac param tokens, otherwise use the type specified; * normally this is used for TOK_XDEF_PARAM, which is used to protect * parameter tokens during expansion during %xdefine. * * tmpl may not be NULL here. */ static void mark_smac_params(Token *tline, const SMacro *tmpl, enum pp_token_type type) { … } /** * %clear selected macro sets either globally or in contexts */ static void do_clear(enum clear_what what, bool context) { … } /** * find and process preprocessor directive in passed line * Find out if a line contains a preprocessor directive, and deal * with it if so. * * If a directive _is_ found, it is the responsibility of this routine * (and not the caller) to free_tlist() the line. * * @param tline a pointer to the current tokeninzed line linked list * @param output if this directive generated output * @return DIRECTIVE_FOUND or NO_DIRECTIVE_FOUND * */ static int do_directive(Token *tline, Token **output) { … } /* * Ensure that a macro parameter contains a condition code and * nothing else. Return the condition code index if so, or -1 * otherwise. */ static int find_cc(Token * t) { … } static inline bool pp_concat_match(const Token *t, unsigned int mask) { … } /* * This routines walks over tokens strem and handles tokens * pasting, if @handle_explicit passed then explicit pasting * term is handled, otherwise -- implicit pastings only. * The @m array can contain a series of token types which are * executed as separate passes. */ static bool paste_tokens(Token **head, const struct tokseq_match *m, size_t mnum, bool handle_explicit) { … } /* * Computes the proper rotation of mmacro parameters */ static int mmac_rotate(const MMacro *mac, unsigned int n) { … } /* * expands to a list of tokens from %{x:y} */ static void expand_mmac_params_range(MMacro *mac, Token *tline, Token ***tail) { … } /* * Expand MMacro-local things: parameter references (%0, %n, %+n, * %-n) and MMacro-local identifiers (%%foo) as well as * macro indirection (%[...]) and range (%{..:..}). */ static Token *expand_mmac_params(Token * tline) { … } static Token *expand_smacro_noreset(Token * tline); /* * Expand *one* single-line macro instance. If the first token is not * a macro at all, it is simply copied to the output and the pointer * advanced. tpp should be a pointer to a pointer (usually the next * pointer of the previous token) to the first token. **tpp is updated * to point to the first token of the expansion, and *tpp updated to * point to the next pointer of the last token of the expansion. * * If the expansion is empty, *tpp will be unchanged but **tpp will * be advanced past the macro call. * * Return the macro expanded, or NULL if no expansion took place. */ static SMacro *expand_one_smacro(Token ***tpp) { … } /* * Expand all single-line macro calls made in the given line. * Return the expanded version of the line. The original is deemed * to be destroyed in the process. (In reality we'll just move * Tokens from input to output a lot of the time, rather than * actually bothering to destroy and replicate.) */ static Token *expand_smacro(Token *tline) { … } static Token *expand_smacro_noreset(Token *org_tline) { … } /* * Similar to expand_smacro but used exclusively with macro identifiers * right before they are fetched in. The reason is that there can be * identifiers consisting of several subparts. We consider that if there * are more than one element forming the name, user wants a expansion, * otherwise it will be left as-is. Example: * * %define %$abc cde * * the identifier %$abc will be left as-is so that the handler for %define * will suck it and define the corresponding value. Other case: * * %define _%$abc cde * * In this case user wants name to be expanded *before* %define starts * working, so we'll expand %$abc into something (if it has a value; * otherwise it will be left as-is) then concatenate all successive * PP_IDs into one. */ static Token *expand_id(Token * tline) { … } /* * This is called from find_mmacro_in_list() after finding a suitable macro. */ static MMacro *use_mmacro(MMacro *m, int *nparamp, Token ***paramsp) { … } /* * Search a macro list and try to find a match. If matching, call * use_mmacro() to set up the macro call. m points to the list of * search, which is_mmacro() sets to the first *possible* match. */ static MMacro * find_mmacro_in_list(MMacro *m, const char *finding, int *nparamp, Token ***paramsp) { … } /* * Determine whether the given line constitutes a multi-line macro * call, and return the MMacro structure called if so. Doesn't have * to check for an initial label - that's taken care of in * expand_mmacro - but must check numbers of parameters. Guaranteed * to be called with tline->type == TOK_ID, so the putative macro * name is easy to find. */ static MMacro *is_mmacro(Token * tline, int *nparamp, Token ***paramsp) { … } #if 0 /* * Save MMacro invocation specific fields in * preparation for a recursive macro expansion */ static void push_mmacro(MMacro *m) { MMacroInvocation *i; i = nasm_malloc(sizeof(MMacroInvocation)); i->prev = m->prev; i->params = m->params; i->iline = m->iline; i->nparam = m->nparam; i->rotate = m->rotate; i->paramlen = m->paramlen; i->unique = m->unique; i->condcnt = m->condcnt; m->prev = i; } /* * Restore MMacro invocation specific fields that were * saved during a previous recursive macro expansion */ static void pop_mmacro(MMacro *m) { MMacroInvocation *i; if (m->prev) { i = m->prev; m->prev = i->prev; m->params = i->params; m->iline = i->iline; m->nparam = i->nparam; m->rotate = i->rotate; m->paramlen = i->paramlen; m->unique = i->unique; m->condcnt = i->condcnt; nasm_free(i); } } #endif /* * List an mmacro call with arguments (-Lm option) */ static void list_mmacro_call(const MMacro *m) { … } /* * Expand the multi-line macro call made by the given line, if * there is one to be expanded. If there is, push the expansion on * istk->expansion and return 1. Otherwise return 0. */ static int expand_mmacro(Token * tline) { … } /* * This function decides if an error message should be suppressed. * It will never be called with a severity level of ERR_FATAL or * higher. */ static bool pp_suppress_error(errflags severity) { … } static Token * stdmac_file(const SMacro *s, Token **params, int nparams) { … } static Token * stdmac_line(const SMacro *s, Token **params, int nparams) { … } static Token * stdmac_bits(const SMacro *s, Token **params, int nparams) { … } static Token * stdmac_ptr(const SMacro *s, Token **params, int nparams) { … } /* Add magic standard macros */ struct magic_macros { … }; static const struct magic_macros magic_macros[] = …; static void pp_add_magic_stdmac(void) { … } static void pp_reset(const char *file, enum preproc_mode mode, struct strlist *dep_list) { … } static void pp_init(void) { … } /* * Get a line of tokens. If we popped the macro expansion/include stack, * we return a pointer to the dummy token tok_pop; at that point if * istk is NULL then we have reached end of input; */ static Token tok_pop; /* Dummy token placeholder */ static Token *pp_tokline(void) { … } static char *pp_getline(void) { … } static void pp_cleanup_pass(void) { … } static void pp_cleanup_session(void) { … } static void pp_include_path(struct strlist *list) { … } static void pp_pre_include(char *fname) { … } static void pp_pre_define(char *definition) { … } static void pp_pre_undefine(char *definition) { … } /* Insert an early preprocessor command that doesn't need special handling */ static void pp_pre_command(const char *what, char *string) { … } static void pp_add_stdmac(macros_t *macros) { … } static void pp_extra_stdmac(macros_t *macros) { … } /* Create a numeric token */ static Token *make_tok_num(Token *next, int64_t val) { … } /* Create a quoted string token */ static Token *make_tok_qstr_len(Token *next, const char *str, size_t len) { … } static Token *make_tok_qstr(Token *next, const char *str) { … } /* Create a single-character operator token */ static Token *make_tok_char(Token *next, char op) { … } /* * Descent the macro hierarchy and display the expansion after * encountering an error message. */ static void pp_error_list_macros(errflags severity) { … } const struct preproc_ops nasmpp = …;