+ eat(T_extern);
+ assert(token.type == T_STRING_LITERAL);
+
+ const char *linkage = parse_string_literals().begin;
+
+ linkage_kind_t old_linkage = current_linkage;
+ linkage_kind_t new_linkage;
+ if (strcmp(linkage, "C") == 0) {
+ new_linkage = LINKAGE_C;
+ } else if (strcmp(linkage, "C++") == 0) {
+ new_linkage = LINKAGE_CXX;
+ } else {
+ errorf(HERE, "linkage string \"%s\" not recognized", linkage);
+ new_linkage = LINKAGE_INVALID;
+ }
+ current_linkage = new_linkage;
+
+ if (token.type == '{') {
+ next_token();
+ parse_externals();
+ expect('}');
+ } else {
+ parse_external();
+ }
+
+end_error:
+ assert(current_linkage == new_linkage);
+ current_linkage = old_linkage;
+}
+
+static void parse_external(void)
+{
+ switch (token.type) {
+ DECLARATION_START_NO_EXTERN
+ case T_IDENTIFIER:
+ case T___extension__:
+ case '(': /* for function declarations with implicit return type and
+ * parenthesized declarator, i.e. (f)(void); */
+ parse_external_declaration();
+ return;
+
+ case T_extern:
+ if (look_ahead(1)->type == T_STRING_LITERAL) {
+ parse_linkage_specification();
+ } else {
+ parse_external_declaration();
+ }
+ return;
+
+ case T_asm:
+ parse_global_asm();
+ return;
+
+ case T_namespace:
+ parse_namespace_definition();
+ return;
+
+ case ';':
+ if (!strict_mode) {
+ if (warning.other)
+ warningf(HERE, "stray ';' outside of function");
+ next_token();
+ return;
+ }
+ /* FALLTHROUGH */
+
+ default:
+ errorf(HERE, "stray %K outside of function", &token);
+ if (token.type == '(' || token.type == '{' || token.type == '[')
+ eat_until_matching_token(token.type);
+ next_token();
+ return;
+ }
+}
+
+static void parse_externals(void)
+{
+ add_anchor_token('}');