+static const char *gnu_attribute_names[GNU_AK_LAST] = {
+ [GNU_AK_CONST] = "const",
+ [GNU_AK_VOLATILE] = "volatile",
+ [GNU_AK_CDECL] = "cdecl",
+ [GNU_AK_STDCALL] = "stdcall",
+ [GNU_AK_FASTCALL] = "fastcall",
+ [GNU_AK_DEPRECATED] = "deprecated",
+ [GNU_AK_NOINLINE] = "noinline",
+ [GNU_AK_NORETURN] = "noreturn",
+ [GNU_AK_NAKED] = "naked",
+ [GNU_AK_PURE] = "pure",
+ [GNU_AK_ALWAYS_INLINE] = "always_inline",
+ [GNU_AK_MALLOC] = "malloc",
+ [GNU_AK_WEAK] = "weak",
+ [GNU_AK_CONSTRUCTOR] = "constructor",
+ [GNU_AK_DESTRUCTOR] = "destructor",
+ [GNU_AK_NOTHROW] = "nothrow",
+ [GNU_AK_TRANSPARENT_UNION] = "transparent_union",
+ [GNU_AK_COMMON] = "coommon",
+ [GNU_AK_NOCOMMON] = "nocommon",
+ [GNU_AK_PACKED] = "packed",
+ [GNU_AK_SHARED] = "shared",
+ [GNU_AK_NOTSHARED] = "notshared",
+ [GNU_AK_USED] = "used",
+ [GNU_AK_UNUSED] = "unused",
+ [GNU_AK_NO_INSTRUMENT_FUNCTION] = "no_instrument_function",
+ [GNU_AK_WARN_UNUSED_RESULT] = "warn_unused_result",
+ [GNU_AK_LONGCALL] = "longcall",
+ [GNU_AK_SHORTCALL] = "shortcall",
+ [GNU_AK_LONG_CALL] = "long_call",
+ [GNU_AK_SHORT_CALL] = "short_call",
+ [GNU_AK_FUNCTION_VECTOR] = "function_vector",
+ [GNU_AK_INTERRUPT] = "interrupt",
+ [GNU_AK_INTERRUPT_HANDLER] = "interrupt_handler",
+ [GNU_AK_NMI_HANDLER] = "nmi_handler",
+ [GNU_AK_NESTING] = "nesting",
+ [GNU_AK_NEAR] = "near",
+ [GNU_AK_FAR] = "far",
+ [GNU_AK_SIGNAL] = "signal",
+ [GNU_AK_EIGTHBIT_DATA] = "eightbit_data",
+ [GNU_AK_TINY_DATA] = "tiny_data",
+ [GNU_AK_SAVEALL] = "saveall",
+ [GNU_AK_FLATTEN] = "flatten",
+ [GNU_AK_SSEREGPARM] = "sseregparm",
+ [GNU_AK_EXTERNALLY_VISIBLE] = "externally_visible",
+ [GNU_AK_RETURN_TWICE] = "return_twice",
+ [GNU_AK_MAY_ALIAS] = "may_alias",
+ [GNU_AK_MS_STRUCT] = "ms_struct",
+ [GNU_AK_GCC_STRUCT] = "gcc_struct",
+ [GNU_AK_DLLIMPORT] = "dllimport",
+ [GNU_AK_DLLEXPORT] = "dllexport",
+ [GNU_AK_ALIGNED] = "aligned",
+ [GNU_AK_ALIAS] = "alias",
+ [GNU_AK_SECTION] = "section",
+ [GNU_AK_FORMAT] = "format",
+ [GNU_AK_FORMAT_ARG] = "format_arg",
+ [GNU_AK_WEAKREF] = "weakref",
+ [GNU_AK_NONNULL] = "nonnull",
+ [GNU_AK_TLS_MODEL] = "tls_model",
+ [GNU_AK_VISIBILITY] = "visibility",
+ [GNU_AK_REGPARM] = "regparm",
+ [GNU_AK_MODE] = "mode",
+ [GNU_AK_MODEL] = "model",
+ [GNU_AK_TRAP_EXIT] = "trap_exit",
+ [GNU_AK_SP_SWITCH] = "sp_switch",
+ [GNU_AK_SENTINEL] = "sentinel"
+};
+
+/**
+ * compare two string, ignoring double underscores on the second.
+ */
+static int strcmp_underscore(const char *s1, const char *s2) {
+ if(s2[0] == '_' && s2[1] == '_') {
+ s2 += 2;
+ size_t l1 = strlen(s1);
+ if(l1 + 2 != strlen(s2)) {
+ /* not equal */
+ return 1;
+ }
+ return strncmp(s1, s2, l1);
+ }
+ return strcmp(s1, s2);
+}
+
+/**
+ * Allocate a new gnu temporal attribute.
+ */
+static gnu_attribute_t *allocate_gnu_attribute(gnu_attribute_kind_t kind) {
+ gnu_attribute_t *attribute = obstack_alloc(&temp_obst, sizeof(*attribute));
+ attribute->kind = kind;
+ attribute->next = NULL;
+ attribute->invalid = false;
+ attribute->have_arguments = false;
+
+ return attribute;
+ return attribute;
+}
+
+/**
+ * parse one constant expression argument.
+ */
+static void parse_gnu_attribute_const_arg(gnu_attribute_t *attribute) {
+ expression_t *expression;
+ add_anchor_token(')');
+ expression = parse_constant_expression();
+ rem_anchor_token(')');
+ expect(')');
+ (void)expression;
+ return;
+end_error:
+ attribute->invalid = true;
+}
+
+/**
+ * parse a list of constant expressions arguments.
+ */
+static void parse_gnu_attribute_const_arg_list(gnu_attribute_t *attribute) {
+ expression_t *expression;
+ add_anchor_token(')');
+ add_anchor_token(',');
+ while(true){
+ expression = parse_constant_expression();
+ if(token.type != ',')
+ break;
+ next_token();
+ }
+ rem_anchor_token(',');
+ rem_anchor_token(')');
+ expect(')');
+ (void)expression;
+ return;
+end_error:
+ attribute->invalid = true;
+}
+
+/**
+ * parse one string literal argument.
+ */
+static void parse_gnu_attribute_string_arg(gnu_attribute_t *attribute,
+ string_t *string)