/**
 * @file keywords.cpp
 * Manages the table of keywords.
 *
 * @author  Ben Gardner
 * @author  Guy Maurel since version 0.62 for uncrustify4Qt
 *          October 2015, 2023
 * @license GPL v2+
 */

#include "keywords.h"

#include "args.h"
#include "prototypes.h"
#include "uncrustify.h"
#include "uncrustify_limits.h"
//# define DEBUG_LANGUAGE
// see also the call to dump_keyword_for_lang
#ifdef DEBUG_LANGUAGE
#include "unc_tools.h"
#endif

#include <cerrno>
#include <map>


using namespace std;

// Dynamic keyword map
typedef map<string, E_Token> dkwmap;
static dkwmap dkwm;


/**
 * Compares two chunk_tag_t entries using strcmp on the strings
 *
 * @param the 'left' entry
 * @param the 'right' entry
 *
 * @return == 0  if both keywords are equal
 * @return  < 0  p1 is smaller than p2
 * @return  > 0  p2 is smaller than p1
 */
static int kw_compare(const void *p1, const void *p2);


/**
 * search in static keywords for first occurrence of a given tag
 *
 * @param tag/keyword to search for
 */
static const chunk_tag_t *kw_static_first(const chunk_tag_t *tag);


static const chunk_tag_t *kw_static_match(bool orig_list, const chunk_tag_t *tag, int lang_flags);

/**
 * selected keywords for the chosen language.
 */

static chunk_tag_t keyword_for_lang[uncrustify::limits::MAX_KEYWORDS];
static size_t      language_count;

/**
 * interesting static keywords - keep sorted.
 * Table includes the Name, Type, and Language flags.
 */
static chunk_tag_t keywords[] =
{
   // TODO: it might be useful if users could add their custom keywords to this list
   { "@autoreleasepool",                CT_AUTORELEASEPOOL,  LANG_OC                                                                     },
   { "@available",                      CT_OC_AVAILABLE,     LANG_OC                                                                     },
   { "@catch",                          CT_CATCH,            LANG_OC                                                                     },
   { "@dynamic",                        CT_OC_DYNAMIC,       LANG_OC                                                                     },
   { "@end",                            CT_OC_END,           LANG_OC                                                                     },
   { "@finally",                        CT_FINALLY,          LANG_OC                                                                     },
   { "@implementation",                 CT_OC_IMPL,          LANG_OC                                                                     },
   { "@interface",                      CT_OC_INTF,          LANG_OC                                                                     },
   { "@interface",                      CT_CLASS,            LANG_JAVA                                                                   },
   { "@private",                        CT_ACCESS,           LANG_OC                                                                     },
   { "@property",                       CT_OC_PROPERTY,      LANG_OC                                                                     },
   { "@protected",                      CT_ACCESS,           LANG_OC                                                                     },
   { "@protocol",                       CT_OC_PROTOCOL,      LANG_OC                                                                     },
   { "@public",                         CT_ACCESS,           LANG_OC                                                                     },
   { "@selector",                       CT_OC_SEL,           LANG_OC                                                                     },
   { "@synchronized",                   CT_SYNCHRONIZED,     LANG_OC                                                                     },
   { "@synthesize",                     CT_OC_DYNAMIC,       LANG_OC                                                                     },
   { "@throw",                          CT_THROW,            LANG_OC                                                                     },
   { "@try",                            CT_TRY,              LANG_OC                                                                     },
   { "API_AVAILABLE",                   CT_ATTRIBUTE,        LANG_OC                                                                     },
   { "API_DEPRECATED",                  CT_ATTRIBUTE,        LANG_OC                                                                     },
   { "API_DEPRECATED_WITH_REPLACEMENT", CT_ATTRIBUTE,        LANG_OC                                                                     },
   { "API_UNAVAILABLE",                 CT_ATTRIBUTE,        LANG_OC                                                                     },
   { "BOOL",                            CT_TYPE,             LANG_OC                                                                     },
   { "INT16_C",                         CT_TYPE,             LANG_CPP                                                                    },
   { "INT32_C",                         CT_TYPE,             LANG_CPP                                                                    },
   { "INT64_C",                         CT_TYPE,             LANG_CPP                                                                    },
   { "INT8_C",                          CT_TYPE,             LANG_CPP                                                                    },
   { "INTMAX_C",                        CT_TYPE,             LANG_CPP                                                                    },
   { "NS_ENUM",                         CT_ENUM,             LANG_OC                                                                     },
   { "NS_OPTIONS",                      CT_ENUM,             LANG_OC                                                                     },
   { "Q_EMIT",                          CT_Q_EMIT,           LANG_CPP                                                                    },
   { "Q_FOREACH",                       CT_FOR,              LANG_CPP                                                                    },
   { "Q_FOREVER",                       CT_Q_FOREVER,        LANG_CPP                                                                    },
   { "Q_GADGET",                        CT_Q_GADGET,         LANG_CPP                                                                    },
   { "Q_OBJECT",                        CT_COMMENT_EMBED,    LANG_CPP                                                                    },
   { "Q_SIGNALS",                       CT_ACCESS,           LANG_CPP                                                                    },
   { "UINT16_C",                        CT_TYPE,             LANG_CPP                                                                    },
   { "UINT32_C",                        CT_TYPE,             LANG_CPP                                                                    },
   { "UINT64_C",                        CT_TYPE,             LANG_CPP                                                                    },
   { "UINT8_C",                         CT_TYPE,             LANG_CPP                                                                    },
   { "UINTMAX_C",                       CT_TYPE,             LANG_CPP                                                                    },
   { "_Bool",                           CT_TYPE,             LANG_C | LANG_CPP                                                           },
   { "_Complex",                        CT_TYPE,             LANG_C | LANG_CPP                                                           },
   { "_Imaginary",                      CT_TYPE,             LANG_C | LANG_CPP                                                           },
   { "_Nonnull",                        CT_QUALIFIER,        LANG_OC                                                                     },
   { "_Null_unspecified",               CT_QUALIFIER,        LANG_OC                                                                     },
   { "_Nullable",                       CT_QUALIFIER,        LANG_OC                                                                     },
   { "_Pragma",                         CT_PP_PRAGMA,        LANG_ALL | FLAG_PP                                                          },
   { "__DI__",                          CT_DI,               LANG_C | LANG_CPP                                                           },
   { "__HI__",                          CT_HI,               LANG_C | LANG_CPP                                                           },
   { "__QI__",                          CT_QI,               LANG_C | LANG_CPP                                                           },
   { "__SI__",                          CT_SI,               LANG_C | LANG_CPP                                                           },
   { "__asm__",                         CT_ASM,              LANG_C | LANG_CPP                                                           },
   { "__attribute__",                   CT_ATTRIBUTE,        LANG_C | LANG_CPP | LANG_OC                                                 },
   { "__autoreleasing",                 CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__block",                         CT_QUALIFIER,        LANG_C | LANG_CPP | LANG_OC                                                 },
   { "__bridge",                        CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__bridge_retained",               CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__bridge_transfer",               CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__const__",                       CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__declspec",                      CT_DECLSPEC,         LANG_C | LANG_CPP                                                           },
   { "__except",                        CT_CATCH,            LANG_C | LANG_CPP                                                           },
   { "__finally",                       CT_FINALLY,          LANG_C | LANG_CPP                                                           },
   { "__has_include",                   CT_CNG_HASINC,       LANG_C | LANG_CPP | LANG_OC | FLAG_PP                                       },
   { "__has_include_next",              CT_CNG_HASINCN,      LANG_C | LANG_CPP | FLAG_PP                                                 },
   { "__inline__",                      CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__nonnull",                       CT_QUALIFIER,        LANG_OC                                                                     },
   { "__nothrow__",                     CT_NOTHROW,          LANG_C | LANG_CPP                                                           },
   { "__null_unspecified",              CT_QUALIFIER,        LANG_OC                                                                     },
   { "__nullable",                      CT_QUALIFIER,        LANG_OC                                                                     },
   { "__pragma",                        CT_PP_PRAGMA,        LANG_ALL | FLAG_PP                                                          },
   { "__restrict",                      CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__signed__",                      CT_TYPE,             LANG_C | LANG_CPP                                                           },
   { "__strong",                        CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__thread",                        CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__traits",                        CT_QUALIFIER,        LANG_D                                                                      },
   { "__try",                           CT_TRY,              LANG_C | LANG_CPP                                                           },
   { "__typeof",                        CT_DECLTYPE,         LANG_C | LANG_CPP | LANG_OC                                                 },
   { "__typeof__",                      CT_DECLTYPE,         LANG_C | LANG_CPP                                                           },
   { "__unsafe_unretained",             CT_QUALIFIER,        LANG_OC                                                                     },
   { "__unused",                        CT_ATTRIBUTE,        LANG_C | LANG_CPP                                                           },
   { "__volatile__",                    CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__weak",                          CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "__word__",                        CT_WORD_,            LANG_C | LANG_CPP                                                           },
   { "abstract",                        CT_QUALIFIER,        LANG_CS | LANG_D | LANG_JAVA | LANG_VALA | LANG_ECMA                        },
   { "add",                             CT_GETSET,           LANG_CS                                                                     },
   { "alias",                           CT_USING,            LANG_D                                                                      },
   { "align",                           CT_ALIGN,            LANG_D                                                                      },
   { "alignof",                         CT_SIZEOF,           LANG_CPP                                                                    },
   { "and",                             CT_SBOOL,            LANG_CPP                                                                    },
   { "and_eq",                          CT_SASSIGN,          LANG_CPP                                                                    },
   { "as",                              CT_AS,               LANG_CS | LANG_VALA                                                         },
   { "asm",                             CT_ASM,              LANG_C | LANG_CPP | LANG_D                                                  },
   { "asm",                             CT_PP_ASM,           LANG_ALL | FLAG_PP                                                          },
   { "assert",                          CT_ASSERT,           LANG_JAVA                                                                   },
   { "assert",                          CT_FUNCTION,         LANG_D | LANG_PAWN                                                          },
   { "assert",                          CT_PP_ASSERT,        LANG_PAWN | FLAG_PP                                                         },
   { "async",                           CT_QUALIFIER,        LANG_VALA                                                                   },
   { "auto",                            CT_TYPE,             LANG_C | LANG_CPP | LANG_D                                                  },
   { "base",                            CT_BASE,             LANG_CS | LANG_VALA                                                         },
   { "bit",                             CT_TYPE,             LANG_D                                                                      },
   { "bitand",                          CT_ARITH,            LANG_C | LANG_CPP                                                           },
   { "bitor",                           CT_ARITH,            LANG_C | LANG_CPP                                                           },
   { "body",                            CT_BODY,             LANG_D                                                                      },
   { "bool",                            CT_TYPE,             LANG_C | LANG_CPP | LANG_CS | LANG_VALA                                     },
   { "boolean",                         CT_TYPE,             LANG_JAVA | LANG_ECMA                                                       },
   { "break",                           CT_BREAK,            LANG_ALL                                                                    },
   { "byte",                            CT_TYPE,             LANG_CS | LANG_D | LANG_JAVA | LANG_ECMA                                    },
   { "case",                            CT_CASE,             LANG_ALL                                                                    },
   { "cast",                            CT_D_CAST,           LANG_D                                                                      },
   { "catch",                           CT_CATCH,            LANG_CPP | LANG_CS | LANG_VALA | LANG_D | LANG_JAVA | LANG_ECMA             },
   { "cdouble",                         CT_TYPE,             LANG_D                                                                      },
   { "cent",                            CT_TYPE,             LANG_D                                                                      },
   { "cfloat",                          CT_TYPE,             LANG_D                                                                      },
   { "char",                            CT_CHAR,             LANG_PAWN                                                                   },
   { "char",                            CT_TYPE,             LANG_ALLC                                                                   },
   { "checked",                         CT_QUALIFIER,        LANG_CS                                                                     },
   { "class",                           CT_CLASS,            LANG_CPP | LANG_CS | LANG_D | LANG_JAVA | LANG_VALA | LANG_ECMA             },
   { "compl",                           CT_ARITH,            LANG_CPP                                                                    },
   { "const",                           CT_QUALIFIER,        LANG_ALL                                                                    },
   { "const_cast",                      CT_TYPE_CAST,        LANG_CPP                                                                    },
   { "constexpr",                       CT_QUALIFIER,        LANG_CPP                                                                    },
   { "construct",                       CT_CONSTRUCT,        LANG_VALA                                                                   },
   { "continue",                        CT_CONTINUE,         LANG_ALL                                                                    },
   { "creal",                           CT_TYPE,             LANG_D                                                                      },
   { "dchar",                           CT_TYPE,             LANG_D                                                                      },
   { "debug",                           CT_DEBUG,            LANG_D                                                                      },
   { "debugger",                        CT_DEBUGGER,         LANG_ECMA                                                                   },
   { "decltype",                        CT_DECLTYPE,         LANG_CPP                                                                    },
   { "default",                         CT_DEFAULT,          LANG_ALL                                                                    },
   { "define",                          CT_PP_DEFINE,        LANG_ALL | FLAG_PP                                                          },
   { "defined",                         CT_DEFINED,          LANG_PAWN                                                                   },
   { "defined",                         CT_PP_DEFINED,       LANG_ALLC | FLAG_PP                                                         },
   { "delegate",                        CT_DELEGATE,         LANG_CS | LANG_VALA | LANG_D                                                },
   { "delete",                          CT_DELETE,           LANG_CPP | LANG_D | LANG_ECMA | LANG_VALA                                   },
   { "deprecated",                      CT_QUALIFIER,        LANG_D                                                                      },
   { "do",                              CT_DO,               LANG_ALL                                                                    },
   { "double",                          CT_TYPE,             LANG_ALLC                                                                   },
   { "dynamic",                         CT_QUALIFIER,        LANG_VALA                                                                   },
   { "dynamic_cast",                    CT_TYPE_CAST,        LANG_CPP                                                                    },
   { "elif",                            CT_PP_ELSE,          LANG_ALLC | FLAG_PP                                                         },
   { "else",                            CT_ELSE,             LANG_ALL                                                                    },
   { "else",                            CT_PP_ELSE,          LANG_ALL | FLAG_PP                                                          },
   { "elseif",                          CT_PP_ELSE,          LANG_PAWN | FLAG_PP                                                         },
   { "emit",                            CT_PP_EMIT,          LANG_PAWN | FLAG_PP                                                         },
   { "endif",                           CT_PP_ENDIF,         LANG_ALL | FLAG_PP                                                          },
   { "endinput",                        CT_PP_ENDINPUT,      LANG_PAWN | FLAG_PP                                                         },
   { "endregion",                       CT_PP_ENDREGION,     LANG_ALL | FLAG_PP                                                          },
   { "endscript",                       CT_PP_ENDINPUT,      LANG_PAWN | FLAG_PP                                                         },
   { "enum",                            CT_ENUM,             LANG_ALL                                                                    },
   { "error",                           CT_PP_ERROR,         LANG_PAWN | FLAG_PP                                                         },
   { "errordomain",                     CT_ENUM,             LANG_VALA                                                                   },
   { "event",                           CT_TYPE,             LANG_CS                                                                     },
   { "exit",                            CT_FUNCTION,         LANG_PAWN                                                                   },
   { "explicit",                        CT_QUALIFIER,        LANG_CPP | LANG_CS                                                          },
   { "export",                          CT_EXPORT,           LANG_CPP | LANG_D | LANG_ECMA                                               },
   { "extends",                         CT_QUALIFIER,        LANG_JAVA | LANG_ECMA                                                       },
   { "extern",                          CT_EXTERN,           LANG_C | LANG_CPP | LANG_OC | LANG_CS | LANG_D | LANG_VALA                  },
   { "false",                           CT_WORD,             LANG_ALL                                                                    },
   { "file",                            CT_PP_FILE,          LANG_PAWN | FLAG_PP                                                         },
   { "final",                           CT_QUALIFIER,        LANG_CPP | LANG_D | LANG_ECMA                                               },
   { "finally",                         CT_FINALLY,          LANG_D | LANG_CS | LANG_VALA | LANG_ECMA | LANG_JAVA                        },
   { "fixed",                           CT_FIXED,            LANG_CS                                                                     },
   { "flags",                           CT_TYPE,             LANG_VALA                                                                   },
   { "float",                           CT_TYPE,             LANG_ALLC                                                                   },
   { "for",                             CT_FOR,              LANG_ALL                                                                    },
   { "foreach",                         CT_FOR,              LANG_CS | LANG_D | LANG_VALA                                                },
   { "foreach_reverse",                 CT_FOR,              LANG_D                                                                      },
   { "forward",                         CT_FORWARD,          LANG_PAWN                                                                   },
   { "friend",                          CT_FRIEND,           LANG_CPP                                                                    },
   { "function",                        CT_FUNCTION,         LANG_D | LANG_ECMA                                                          },
   { "get",                             CT_GETSET,           LANG_CS | LANG_VALA                                                         },
   { "goto",                            CT_GOTO,             LANG_ALL                                                                    },
   { "idouble",                         CT_TYPE,             LANG_D                                                                      },
   { "if",                              CT_IF,               LANG_ALL                                                                    },
   { "if",                              CT_PP_IF,            LANG_ALL | FLAG_PP                                                          },
   { "ifdef",                           CT_PP_IF,            LANG_ALLC | FLAG_PP                                                         },
   { "ifloat",                          CT_TYPE,             LANG_D                                                                      },
   { "ifndef",                          CT_PP_IF,            LANG_ALLC | FLAG_PP                                                         },
   { "implements",                      CT_QUALIFIER,        LANG_JAVA | LANG_ECMA                                                       },
   { "implicit",                        CT_QUALIFIER,        LANG_CS                                                                     },
   { "import",                          CT_IMPORT,           LANG_D | LANG_JAVA | LANG_ECMA                                              },
   { "import",                          CT_PP_INCLUDE,       LANG_OC | FLAG_PP                                                           },
   { "in",                              CT_IN,               LANG_D | LANG_CS | LANG_VALA | LANG_ECMA | LANG_OC                          },
   { "include",                         CT_PP_INCLUDE,       LANG_C | LANG_CPP | LANG_OC | LANG_PAWN | FLAG_PP                           },
   { "inline",                          CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "inout",                           CT_QUALIFIER,        LANG_D                                                                      },
   { "instanceof",                      CT_SIZEOF,           LANG_JAVA | LANG_ECMA                                                       },
   { "int",                             CT_TYPE,             LANG_ALLC                                                                   },
   { "interface",                       CT_CLASS,            LANG_CPP | LANG_CS | LANG_D | LANG_JAVA | LANG_VALA | LANG_ECMA             },
   { "internal",                        CT_QUALIFIER,        LANG_CS | LANG_VALA                                                         },
   { "invariant",                       CT_INVARIANT,        LANG_D                                                                      },
   { "ireal",                           CT_TYPE,             LANG_D                                                                      },
   { "is",                              CT_SCOMPARE,         LANG_D | LANG_CS | LANG_VALA                                                },
   { "lazy",                            CT_LAZY,             LANG_D                                                                      },
   { "line",                            CT_PP_LINE,          LANG_PAWN | FLAG_PP                                                         },
   { "lock",                            CT_LOCK,             LANG_CS | LANG_VALA                                                         },
   { "long",                            CT_TYPE,             LANG_ALLC                                                                   },
   { "macro",                           CT_D_MACRO,          LANG_D                                                                      },
   { "mixin",                           CT_CLASS,            LANG_D                                                                      }, // may need special handling
   { "module",                          CT_D_MODULE,         LANG_D                                                                      },
   { "mutable",                         CT_QUALIFIER,        LANG_CPP                                                                    },
   { "namespace",                       CT_NAMESPACE,        LANG_CPP | LANG_CS | LANG_VALA                                              },
   { "native",                          CT_NATIVE,           LANG_PAWN                                                                   },
   { "native",                          CT_QUALIFIER,        LANG_JAVA | LANG_ECMA                                                       },
   { "new",                             CT_NEW,              LANG_CPP | LANG_CS | LANG_D | LANG_JAVA | LANG_PAWN | LANG_VALA | LANG_ECMA },
   { "noexcept",                        CT_NOEXCEPT,         LANG_CPP                                                                    },
   { "nonnull",                         CT_TYPE,             LANG_OC                                                                     },
   { "not",                             CT_SARITH,           LANG_CPP                                                                    },
   { "not_eq",                          CT_SCOMPARE,         LANG_CPP                                                                    },
// { "null",                            CT_TYPE,             LANG_CS | LANG_D | LANG_JAVA | LANG_VALA                                    },
   { "null_resettable",                 CT_OC_PROPERTY_ATTR, LANG_OC                                                                     },
   { "null_unspecified",                CT_TYPE,             LANG_OC                                                                     },
   { "nullable",                        CT_TYPE,             LANG_OC                                                                     },
   { "object",                          CT_TYPE,             LANG_CS                                                                     },
   { "operator",                        CT_OPERATOR,         LANG_CPP | LANG_CS | LANG_PAWN                                              },
   { "or",                              CT_SBOOL,            LANG_CPP                                                                    },
   { "or_eq",                           CT_SASSIGN,          LANG_CPP                                                                    },
   { "out",                             CT_QUALIFIER,        LANG_CS | LANG_D | LANG_VALA                                                },
   { "override",                        CT_QUALIFIER,        LANG_CPP | LANG_CS | LANG_D | LANG_VALA                                     },
   { "owned",                           CT_QUALIFIER,        LANG_VALA                                                                   },
   { "package",                         CT_ACCESS,           LANG_D                                                                      },
   { "package",                         CT_PACKAGE,          LANG_ECMA | LANG_JAVA                                                       },
   { "params",                          CT_TYPE,             LANG_CS | LANG_VALA                                                         },
   { "pragma",                          CT_PP_PRAGMA,        LANG_ALL | FLAG_PP                                                          },
   { "private",                         CT_ACCESS,           LANG_ALLC_NOT_C                                                             }, // Issue # 4044
   { "property",                        CT_PP_PROPERTY,      LANG_CS | FLAG_PP                                                           },
   { "protected",                       CT_ACCESS,           LANG_ALLC_NOT_C                                                             }, // Issue # 4044
   { "public",                          CT_ACCESS,           LANG_ALLC_NOT_C                                                             }, // Issue # 4044
   { "readonly",                        CT_QUALIFIER,        LANG_CS                                                                     },
   { "real",                            CT_TYPE,             LANG_D                                                                      },
   { "ref",                             CT_QUALIFIER,        LANG_CS | LANG_VALA                                                         },
   { "region",                          CT_PP_REGION,        LANG_ALL | FLAG_PP                                                          },
   { "register",                        CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "reinterpret_cast",                CT_TYPE_CAST,        LANG_CPP                                                                    },
   { "remove",                          CT_GETSET,           LANG_CS                                                                     },
   { "restrict",                        CT_QUALIFIER,        LANG_C | LANG_CPP                                                           },
   { "return",                          CT_RETURN,           LANG_ALL                                                                    },
   { "sbyte",                           CT_TYPE,             LANG_CS                                                                     },
   { "scope",                           CT_D_SCOPE,          LANG_D                                                                      },
   { "sealed",                          CT_QUALIFIER,        LANG_CS                                                                     },
   { "section",                         CT_PP_SECTION,       LANG_PAWN | FLAG_PP                                                         },
   { "self",                            CT_THIS,             LANG_OC                                                                     },
   { "set",                             CT_GETSET,           LANG_CS | LANG_VALA                                                         },
   { "short",                           CT_TYPE,             LANG_ALLC                                                                   },
   { "signal",                          CT_ACCESS,           LANG_VALA                                                                   },
   { "signals",                         CT_ACCESS,           LANG_CPP                                                                    },
   { "signed",                          CT_TYPE,             LANG_C | LANG_CPP                                                           },
   { "size_t",                          CT_TYPE,             LANG_ALLC                                                                   },
   { "sizeof",                          CT_SIZEOF,           LANG_C | LANG_CPP | LANG_CS | LANG_VALA | LANG_PAWN                         },
   { "sleep",                           CT_SIZEOF,           LANG_PAWN                                                                   },
   { "stackalloc",                      CT_NEW,              LANG_CS                                                                     },
   { "state",                           CT_STATE,            LANG_PAWN                                                                   },
   { "static",                          CT_QUALIFIER,        LANG_ALL                                                                    },
   { "static_cast",                     CT_TYPE_CAST,        LANG_CPP                                                                    },
   { "stock",                           CT_STOCK,            LANG_PAWN                                                                   },
   { "strictfp",                        CT_QUALIFIER,        LANG_JAVA                                                                   },
   { "string",                          CT_TYPE,             LANG_CS | LANG_VALA                                                         },
   { "struct",                          CT_STRUCT,           LANG_C | LANG_CPP | LANG_OC | LANG_CS | LANG_D | LANG_VALA                  },
   { "super",                           CT_SUPER,            LANG_D | LANG_JAVA | LANG_ECMA                                              },
   { "switch",                          CT_SWITCH,           LANG_ALL                                                                    },
   { "synchronized",                    CT_QUALIFIER,        LANG_D | LANG_ECMA                                                          },
   { "synchronized",                    CT_SYNCHRONIZED,     LANG_JAVA                                                                   },
   { "tagof",                           CT_TAGOF,            LANG_PAWN                                                                   },
   { "template",                        CT_TEMPLATE,         LANG_CPP | LANG_D                                                           },
   { "this",                            CT_THIS,             LANG_CPP | LANG_CS | LANG_D | LANG_JAVA | LANG_VALA | LANG_ECMA             },
   { "throw",                           CT_THROW,            LANG_CPP | LANG_CS | LANG_VALA | LANG_D | LANG_JAVA | LANG_ECMA             },
   { "throws",                          CT_QUALIFIER,        LANG_JAVA | LANG_ECMA | LANG_VALA                                           },
   { "transient",                       CT_QUALIFIER,        LANG_JAVA | LANG_ECMA                                                       },
   { "true",                            CT_WORD,             LANG_ALL                                                                    },
   { "try",                             CT_TRY,              LANG_CPP | LANG_CS | LANG_D | LANG_JAVA | LANG_ECMA | LANG_VALA             },
   { "tryinclude",                      CT_PP_INCLUDE,       LANG_PAWN | FLAG_PP                                                         },
   { "typedef",                         CT_TYPEDEF,          LANG_C | LANG_CPP | LANG_OC | LANG_D                                        },
   { "typeid",                          CT_SIZEOF,           LANG_CPP | LANG_D                                                           },
   { "typename",                        CT_TYPENAME,         LANG_CPP                                                                    },
   { "typeof",                          CT_DECLTYPE,         LANG_C | LANG_CPP                                                           },
   { "typeof",                          CT_SIZEOF,           LANG_CS | LANG_D | LANG_VALA | LANG_ECMA                                    },
   { "ubyte",                           CT_TYPE,             LANG_D                                                                      },
   { "ucent",                           CT_TYPE,             LANG_D                                                                      },
   { "uint",                            CT_TYPE,             LANG_CS | LANG_VALA | LANG_D                                                },
   { "ulong",                           CT_TYPE,             LANG_CS | LANG_VALA | LANG_D                                                },
   { "unchecked",                       CT_QUALIFIER,        LANG_CS                                                                     },
   { "undef",                           CT_PP_UNDEF,         LANG_ALL | FLAG_PP                                                          },
   { "union",                           CT_UNION,            LANG_C | LANG_CPP | LANG_D                                                  },
   { "unittest",                        CT_UNITTEST,         LANG_D                                                                      },
   { "unowned",                         CT_QUALIFIER,        LANG_VALA                                                                   },
   { "unsafe",                          CT_UNSAFE,           LANG_CS                                                                     },
   { "unsafe_unretained",               CT_QUALIFIER,        LANG_OC                                                                     },
   { "unsigned",                        CT_TYPE,             LANG_C | LANG_CPP                                                           },
   { "ushort",                          CT_TYPE,             LANG_CS | LANG_VALA | LANG_D                                                },
   { "using",                           CT_USING,            LANG_CPP | LANG_CS | LANG_VALA                                              },
   { "var",                             CT_TYPE,             LANG_CS | LANG_VALA | LANG_ECMA                                             },
   { "version",                         CT_D_VERSION,        LANG_D                                                                      },
   { "virtual",                         CT_QUALIFIER,        LANG_CPP | LANG_CS | LANG_VALA                                              },
   { "void",                            CT_TYPE,             LANG_ALLC                                                                   },
   { "volatile",                        CT_QUALIFIER,        LANG_C | LANG_CPP | LANG_CS | LANG_JAVA | LANG_ECMA                         },
   { "volatile",                        CT_VOLATILE,         LANG_D                                                                      },
   { "wchar",                           CT_TYPE,             LANG_D                                                                      },
   { "wchar_t",                         CT_TYPE,             LANG_C | LANG_CPP                                                           },
   { "weak",                            CT_QUALIFIER,        LANG_VALA                                                                   },
   { "when",                            CT_WHEN,             LANG_CS                                                                     },
   { "where",                           CT_WHERE,            LANG_CS                                                                     },
   { "while",                           CT_WHILE,            LANG_ALL                                                                    },
   { "with",                            CT_D_WITH,           LANG_D | LANG_ECMA                                                          },
   { "xor",                             CT_SARITH,           LANG_CPP                                                                    },
   { "xor_eq",                          CT_SASSIGN,          LANG_CPP                                                                    },
};


// Issue #3353
void init_keywords_for_language()
{
   unsigned int local_flags    = cpd.lang_flags;
   size_t       keywords_count = ARRAY_SIZE(keywords);

   language_count = 0;

   for (size_t idx = 0; idx < keywords_count; idx++)
   {
      chunk_tag_t *tag = &keywords[idx];

      if ((tag->lang_flags & local_flags) != 0)
      {
         // for debugging only
         // fprintf(stderr, "%s(%d): %zu Keyword: '%s', type is '%s'\n",
         //         __func__, __LINE__, idx, tag->tag, get_token_name(tag->type));
         keyword_for_lang[language_count].tag        = tag->tag;
         keyword_for_lang[language_count].type       = tag->type;
         keyword_for_lang[language_count].lang_flags = tag->lang_flags;
         language_count++;
      }
   }

#ifdef DEBUG_LANGUAGE
   dump_keyword_for_lang(language_count, keyword_for_lang);
#endif

   LOG_FMT(LDYNKW, "%s(%d): Number of Keywords for language %s: '%zu'\n",
           __func__, __LINE__, language_name_from_flags(local_flags), language_count);
} // init_keywords_for_language


static int kw_compare(const void *p1, const void *p2)
{
   const chunk_tag_t *t1 = static_cast<const chunk_tag_t *>(p1);
   const chunk_tag_t *t2 = static_cast<const chunk_tag_t *>(p2);

   return(strcmp(t1->tag, t2->tag));
} // kw_compare


bool keywords_are_sorted()
{
   size_t keywords_count = ARRAY_SIZE(keywords);

   for (size_t idx = 1; idx < keywords_count; idx++)
   {
      if (kw_compare(&keywords[idx - 1], &keywords[idx]) > 0)
      {
         fprintf(stderr, "%s: bad sort order at idx %d, words '%s' and '%s'\n",
                 __func__, (int)idx - 1, keywords[idx - 1].tag, keywords[idx].tag);
         // coveralls will always complain.
         // these lines are only needed for the developer.
         log_flush(true);
         exit(EX_SOFTWARE);
      }
   }

   return(true);
} // keywords_are_sorted


void add_keyword(const std::string &tag, E_Token type)
{
   // See if the keyword has already been added
   dkwmap::iterator it = dkwm.find(tag);

   if (it != dkwm.end())
   {
      LOG_FMT(LDYNKW, "%s(%d): changed '%s' to '%s'\n",
              __func__, __LINE__, tag.c_str(), get_token_name(type));
      (*it).second = type;
      return;
   }
   // Insert the keyword
   dkwm.insert(dkwmap::value_type(tag, type));
   LOG_FMT(LDYNKW, "%s(%d): added '%s' as '%s'\n",
           __func__, __LINE__, tag.c_str(), get_token_name(type));
} // add_keyword


static const chunk_tag_t *kw_static_first(const chunk_tag_t *tag)
{
   const chunk_tag_t *prev = tag - 1;

   // TODO: avoid pointer arithmetic
   // loop over static keyword array
   while (  prev >= &keyword_for_lang[0]        // not at beginning of keyword array
         && strcmp(prev->tag, tag->tag) == 0)   // tags match
   {
      tag = prev;
      prev--;
   }
   return(tag);
} // kw_static_first


static const chunk_tag_t *kw_static_match(bool orig_list, const chunk_tag_t *tag, int lang_flags)
{
   bool in_pp = (  cpd.in_preproc != CT_NONE
                && cpd.in_preproc != CT_PP_DEFINE);

   for (const chunk_tag_t *iter = kw_static_first(tag);
        (orig_list) ? (iter < &keywords[ARRAY_SIZE(keywords)]) : (iter < &keyword_for_lang[language_count]);
        iter++)
   {
      bool pp_iter = (iter->lang_flags & FLAG_PP) != 0; // forcing value to bool

      if (  (strcmp(iter->tag, tag->tag) == 0)
         && language_is_set(iter->lang_flags)
         && (lang_flags & iter->lang_flags)
         && in_pp == pp_iter)
      {
         return(iter);
      }
   }

   return(nullptr);
} // kw_static_match


E_Token find_keyword_type(const char *word, size_t len)
{
   if (len <= 0)
   {
      return(CT_NONE);
   }
   // check the dynamic word list first
   string           ss(word, len);
   dkwmap::iterator it = dkwm.find(ss);

   if (it != dkwm.end())
   {
      return((*it).second);
   }
   chunk_tag_t key;

   key.tag = ss.c_str();

   // check the static word list
   const chunk_tag_t *p_ret = static_cast<const chunk_tag_t *>(
      bsearch(&key, keyword_for_lang, language_count, sizeof(keyword_for_lang[0]), kw_compare));

   if (p_ret != nullptr)
   {
      if (  strcmp(p_ret->tag, "__pragma") == 0
         || strcmp(p_ret->tag, "_Pragma") == 0)
      {
         cpd.in_preproc = CT_PREPROC;
      }
      p_ret = kw_static_match(false, p_ret, cpd.lang_flags);
   }
   return((p_ret != nullptr) ? p_ret->type : CT_WORD);
} // find_keyword_type


int load_keyword_file(const char *filename)
{
   FILE *pf = fopen(filename, "r");

   if (pf == nullptr)
   {
      LOG_FMT(LERR, "%s: fopen(%s) failed: %s (%d)\n",
              __func__, filename, strerror(errno), errno);
      exit(EX_IOERR);
   }
   const int max_line_length = 256;
   const int max_arg_count   = 2;

   // maximal length of a line in the file
   char   buf[max_line_length];
   char   *args[max_arg_count];
   size_t line_no = 0;

   // read file line by line
   while (fgets(buf, max_line_length, pf) != nullptr)
   {
      line_no++;

      // remove comments after '#' sign
      char *ptr;

      if ((ptr = strchr(buf, '#')) != nullptr)
      {
         *ptr = 0; // set string end where comment begins
      }
      size_t argc = Args::SplitLine(buf, args, max_arg_count);

      if (argc > 0)
      {
         if (  argc == 1
            && CharTable::IsKw1(*args[0]))
         {
            add_keyword(args[0], CT_TYPE);
         }
         else
         {
            LOG_FMT(LWARN, "%s:%zu Invalid line (starts with '%s')\n",
                    filename, line_no, args[0]);
            exit(EX_SOFTWARE);
         }
      }
      else
      {
         continue; // the line is empty
      }
   }
   fclose(pf);
   return(EX_OK);
} // load_keyword_file


void print_custom_keywords(FILE *pfile)
{
   for (const auto &keyword_pair : dkwm)
   {
      E_Token tt = keyword_pair.second;

      if (tt == CT_TYPE)
      {
         fprintf(pfile, "custom type %*.s%s\n",
                 uncrustify::limits::MAX_OPTION_NAME_LEN - 10, " ",
                 keyword_pair.first.c_str());
      }
      else if (tt == CT_MACRO_OPEN)
      {
         fprintf(pfile, "macro-open %*.s%s\n",
                 uncrustify::limits::MAX_OPTION_NAME_LEN - 11, " ",
                 keyword_pair.first.c_str());
      }
      else if (tt == CT_MACRO_CLOSE)
      {
         fprintf(pfile, "macro-close %*.s%s\n",
                 uncrustify::limits::MAX_OPTION_NAME_LEN - 12, " ",
                 keyword_pair.first.c_str());
      }
      else if (tt == CT_MACRO_ELSE)
      {
         fprintf(pfile, "macro-else %*.s%s\n",
                 uncrustify::limits::MAX_OPTION_NAME_LEN - 11, " ",
                 keyword_pair.first.c_str());
      }
      else
      {
         const char *tn = get_token_name(tt);

         fprintf(pfile, "set %s %*.s%s\n",
                 tn,
                 uncrustify::limits::MAX_OPTION_NAME_LEN - (4 + static_cast<int>(strlen(tn))),
                 " ", keyword_pair.first.c_str());
      }
   }
} // print_custom_keywords


void clear_keyword_file()
{
   dkwm.clear();
} // clear_keyword_file


pattern_class_e get_token_pattern_class(E_Token tok)
{
   // TODO: instead of this switch better assign the pattern class to each statement
   switch (tok)
   {
   case CT_IF:
   case CT_ELSEIF:
   case CT_SWITCH:
   case CT_FOR:
   case CT_WHILE:
   case CT_SYNCHRONIZED:
   case CT_USING_STMT:
   case CT_LOCK:
   case CT_D_WITH:
   case CT_D_VERSION_IF:
   case CT_D_SCOPE_IF:
      return(pattern_class_e::PBRACED);

   case CT_ELSE:
      return(pattern_class_e::ELSE);

   case CT_DO:
   case CT_TRY:
   case CT_FINALLY:
   case CT_BODY:
   case CT_UNITTEST:
   case CT_UNSAFE:
   case CT_VOLATILE:
   case CT_GETSET:
      return(pattern_class_e::BRACED);

   case CT_CATCH:
   case CT_D_VERSION:
   case CT_DEBUG:
      return(pattern_class_e::OPBRACED);

   case CT_NAMESPACE:
      return(pattern_class_e::VBRACED);

   case CT_WHILE_OF_DO:
      return(pattern_class_e::PAREN);

   case CT_INVARIANT:
      return(pattern_class_e::OPPAREN);

   default:
      return(pattern_class_e::NONE);
   } // switch
}    // get_token_pattern_class
