This change is from an idea suggested by Arthur David Olson.

* c-common.c (decl_attributes, record_function_format,
	check_format_info, init_function_format_info):
	Add support for strftime format checking.
	(enum format_type): New type.
	(record_function_format): Now static, and takes value of type
	enum format_type instead of int.
	(time_char_table): New constant.
	(struct function_format_info): format_type member renamed from is_scan.
	(check_format_info): Use `warning' rather than sprintf followed by
	`warning', to avoid mishandling `%' in warnings.
	Change `pedwarn' to `warning', since these warnings do not necessarily
	mean the program does not conform to the C Standard, as the code
	need not be executed.
	* c-tree.h (record_function_format): Remove decl; no longer extern.
	* extend.texi: Add documentation for strftime format checking.

From-SVN: r19151
This commit is contained in:
Paul Eggert 1998-04-12 19:52:16 +00:00 committed by Jeff Law
parent 71922625f3
commit bb72a0843b
4 changed files with 247 additions and 162 deletions

View File

@ -1,3 +1,25 @@
1998-04-12 Paul Eggert <eggert@twinsun.com>
This change is from an idea suggested by Arthur David Olson.
* c-common.c (decl_attributes, record_function_format,
check_format_info, init_function_format_info):
Add support for strftime format checking.
(enum format_type): New type.
(record_function_format): Now static, and takes value of type
enum format_type instead of int.
(time_char_table): New constant.
(struct function_format_info): format_type member renamed from is_scan.
(check_format_info): Use `warning' rather than sprintf followed by
`warning', to avoid mishandling `%' in warnings.
Change `pedwarn' to `warning', since these warnings do not necessarily
mean the program does not conform to the C Standard, as the code
need not be executed.
* c-tree.h (record_function_format): Remove decl; no longer extern.
* extend.texi: Add documentation for strftime format checking.
Sun Apr 12 20:23:03 1998 Jeffrey A Law (law@cygnus.com)
* mips/ecoffl.h: Do not include mips.h.

View File

@ -44,10 +44,15 @@ enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS};
enum format_type { printf_format_type, scanf_format_type,
strftime_format_type };
static void declare_hidden_char_array PROTO((char *, char *));
static void add_attribute PROTO((enum attrs, char *,
int, int, int));
static void init_attributes PROTO((void));
static void record_function_format PROTO((tree, tree, enum format_type,
int, int));
static void record_international_format PROTO((tree, tree, int));
/* Keep a stack of if statements. We record the number of compound
@ -649,13 +654,13 @@ decl_attributes (node, attributes, prefix_attributes)
case A_FORMAT:
{
tree format_type = TREE_VALUE (args);
tree format_type_id = TREE_VALUE (args);
tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
tree first_arg_num_expr
= TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
int format_num;
int first_arg_num;
int is_scan;
enum format_type format_type;
tree argument;
int arg_num;
@ -666,26 +671,27 @@ decl_attributes (node, attributes, prefix_attributes)
continue;
}
if (TREE_CODE (format_type) == IDENTIFIER_NODE
&& (!strcmp (IDENTIFIER_POINTER (format_type), "printf")
|| !strcmp (IDENTIFIER_POINTER (format_type),
"__printf__")))
is_scan = 0;
else if (TREE_CODE (format_type) == IDENTIFIER_NODE
&& (!strcmp (IDENTIFIER_POINTER (format_type), "scanf")
|| !strcmp (IDENTIFIER_POINTER (format_type),
"__scanf__")))
is_scan = 1;
else if (TREE_CODE (format_type) == IDENTIFIER_NODE)
if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
{
warning ("`%s' is an unrecognized format function type",
IDENTIFIER_POINTER (format_type));
error ("unrecognized format specifier");
continue;
}
else
{
error ("unrecognized format specifier");
continue;
char *p = IDENTIFIER_POINTER (format_type_id);
if (!strcmp (p, "printf") || !strcmp (p, "__printf__"))
format_type = printf_format_type;
else if (!strcmp (p, "scanf") || !strcmp (p, "__scanf__"))
format_type = scanf_format_type;
else if (!strcmp (p, "strftime")
|| !strcmp (p, "__strftime__"))
format_type = strftime_format_type;
else
{
error ("`%s' is an unrecognized format function type", p);
continue;
}
}
/* Strip any conversions from the string index and first arg number
@ -751,7 +757,7 @@ decl_attributes (node, attributes, prefix_attributes)
record_function_format (DECL_NAME (decl),
DECL_ASSEMBLER_NAME (decl),
is_scan, format_num, first_arg_num);
format_type, format_num, first_arg_num);
break;
}
@ -1011,12 +1017,37 @@ static format_char_info scan_char_table[] = {
{ NULL }
};
/* Handle format characters recognized by glibc's strftime.c.
'2' - MUST do years as only two digits
'3' - MAY do years as only two digits (depending on locale)
'E' - E modifier is acceptable
'O' - O modifier is acceptable to Standard C
'o' - O modifier is acceptable as a GNU extension
'G' - other GNU extensions */
static format_char_info time_char_table[] = {
{ "y", 0, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" },
{ "D", 0, NULL, NULL, NULL, NULL, NULL, NULL, "2" },
{ "g", 0, NULL, NULL, NULL, NULL, NULL, NULL, "2O-_0w" },
{ "cx", 0, NULL, NULL, NULL, NULL, NULL, NULL, "3E" },
{ "%RTXnrt", 0, NULL, NULL, NULL, NULL, NULL, NULL, "" },
{ "P", 0, NULL, NULL, NULL, NULL, NULL, NULL, "G" },
{ "HIMSUWdemw", 0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },
{ "Vju", 0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },
{ "Gklsz", 0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" },
{ "ABZa", 0, NULL, NULL, NULL, NULL, NULL, NULL, "^#" },
{ "p", 0, NULL, NULL, NULL, NULL, NULL, NULL, "#" },
{ "bh", 0, NULL, NULL, NULL, NULL, NULL, NULL, "^" },
{ "CY", 0, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOw" },
{ NULL }
};
typedef struct function_format_info
{
struct function_format_info *next; /* next structure on the list */
tree name; /* identifier such as "printf" */
tree assembler_name; /* optional mangled identifier (for C++) */
int is_scan; /* TRUE if *scanf */
enum format_type format_type; /* type of format (printf, scanf, etc.) */
int format_num; /* number of format argument */
int first_arg_num; /* number of first arg (zero for varargs) */
} function_format_info;
@ -1048,15 +1079,26 @@ static void check_format_info PROTO((function_format_info *, tree));
void
init_function_format_info ()
{
record_function_format (get_identifier ("printf"), NULL_TREE, 0, 1, 2);
record_function_format (get_identifier ("fprintf"), NULL_TREE, 0, 2, 3);
record_function_format (get_identifier ("sprintf"), NULL_TREE, 0, 2, 3);
record_function_format (get_identifier ("scanf"), NULL_TREE, 1, 1, 2);
record_function_format (get_identifier ("fscanf"), NULL_TREE, 1, 2, 3);
record_function_format (get_identifier ("sscanf"), NULL_TREE, 1, 2, 3);
record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);
record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);
record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);
record_function_format (get_identifier ("printf"), NULL_TREE,
printf_format_type, 1, 2);
record_function_format (get_identifier ("fprintf"), NULL_TREE,
printf_format_type, 2, 3);
record_function_format (get_identifier ("sprintf"), NULL_TREE,
printf_format_type, 2, 3);
record_function_format (get_identifier ("scanf"), NULL_TREE,
scanf_format_type, 1, 2);
record_function_format (get_identifier ("fscanf"), NULL_TREE,
scanf_format_type, 2, 3);
record_function_format (get_identifier ("sscanf"), NULL_TREE,
scanf_format_type, 2, 3);
record_function_format (get_identifier ("vprintf"), NULL_TREE,
printf_format_type, 1, 0);
record_function_format (get_identifier ("vfprintf"), NULL_TREE,
printf_format_type, 2, 0);
record_function_format (get_identifier ("vsprintf"), NULL_TREE,
printf_format_type, 2, 0);
record_function_format (get_identifier ("strftime"), NULL_TREE,
strftime_format_type, 3, 0);
record_international_format (get_identifier ("gettext"), NULL_TREE, 1);
record_international_format (get_identifier ("dgettext"), NULL_TREE, 2);
@ -1065,19 +1107,19 @@ init_function_format_info ()
/* Record information for argument format checking. FUNCTION_IDENT is
the identifier node for the name of the function to check (its decl
need not exist yet). IS_SCAN is true for scanf-type format checking;
false indicates printf-style format checking. FORMAT_NUM is the number
need not exist yet).
FORMAT_TYPE specifies the type of format checking. FORMAT_NUM is the number
of the argument which is the format control string (starting from 1).
FIRST_ARG_NUM is the number of the first actual argument to check
against the format string, or zero if no checking is not be done
(e.g. for varargs such as vfprintf). */
void
record_function_format (name, assembler_name, is_scan,
static void
record_function_format (name, assembler_name, format_type,
format_num, first_arg_num)
tree name;
tree assembler_name;
int is_scan;
enum format_type format_type;
int format_num;
int first_arg_num;
{
@ -1100,7 +1142,7 @@ record_function_format (name, assembler_name, is_scan,
info->assembler_name = assembler_name;
}
info->is_scan = is_scan;
info->format_type = format_type;
info->format_num = format_num;
info->first_arg_num = first_arg_num;
}
@ -1195,7 +1237,6 @@ check_format_info (info, params)
tree first_fillin_param;
char *format_chars;
format_char_info *fci;
static char message[132];
char flag_chars[8];
int has_operand_number = 0;
@ -1304,7 +1345,7 @@ check_format_info (info, params)
}
flag_chars[0] = 0;
suppressed = wide = precise = FALSE;
if (info->is_scan)
if (info->format_type == scanf_format_type)
{
suppressed = *format_chars == '*';
if (suppressed)
@ -1312,7 +1353,47 @@ check_format_info (info, params)
while (isdigit (*format_chars))
++format_chars;
}
else
else if (info->format_type == strftime_format_type)
{
while (*format_chars != 0 && index ("_-0^#", *format_chars) != 0)
{
if (pedantic)
warning ("ANSI C does not support the strftime `%c' flag",
*format_chars);
if (index (flag_chars, *format_chars) != 0)
{
warning ("repeated `%c' flag in format",
*format_chars);
++format_chars;
}
else
{
i = strlen (flag_chars);
flag_chars[i++] = *format_chars++;
flag_chars[i] = 0;
}
}
while (isdigit ((unsigned char) *format_chars))
{
wide = TRUE;
++format_chars;
}
if (wide && pedantic)
warning ("ANSI C does not support strftime format width");
if (*format_chars == 'E' || *format_chars == 'O')
{
i = strlen (flag_chars);
flag_chars[i++] = *format_chars++;
flag_chars[i] = 0;
if (*format_chars == 'E' || *format_chars == 'O')
{
warning ("multiple E/O modifiers in format");
while (*format_chars == 'E' || *format_chars == 'O')
++format_chars;
}
}
}
else if (info->format_type == printf_format_type)
{
/* See if we have a number followed by a dollar sign. If we do,
it is an operand number, so set PARAMS to that operand. */
@ -1345,11 +1426,7 @@ check_format_info (info, params)
while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
{
if (index (flag_chars, *format_chars) != 0)
{
sprintf (message, "repeated `%c' flag in format",
*format_chars++);
warning (message);
}
warning ("repeated `%c' flag in format", *format_chars++);
else
{
i = strlen (flag_chars);
@ -1392,12 +1469,7 @@ check_format_info (info, params)
&&
(TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
!= unsigned_type_node))
{
sprintf (message,
"field width is not type int (arg %d)",
arg_num);
warning (message);
}
warning ("field width is not type int (arg %d)", arg_num);
}
}
else
@ -1431,12 +1503,8 @@ check_format_info (info, params)
++arg_num;
if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
!= integer_type_node)
{
sprintf (message,
"field width is not type int (arg %d)",
arg_num);
warning (message);
}
warning ("field width is not type int (arg %d)",
arg_num);
}
}
else
@ -1446,92 +1514,106 @@ check_format_info (info, params)
}
}
}
if (*format_chars == 'h' || *format_chars == 'l')
length_char = *format_chars++;
else if (*format_chars == 'q' || *format_chars == 'L')
{
length_char = *format_chars++;
if (pedantic && length_char == 'q')
pedwarn ("ANSI C does not support the `%c' length modifier",
length_char);
}
else if (*format_chars == 'Z')
{
length_char = *format_chars++;
if (pedantic)
pedwarn ("ANSI C does not support the `Z' length modifier");
}
else
length_char = 0;
if (length_char == 'l' && *format_chars == 'l')
{
length_char = 'q', format_chars++;
if (pedantic)
pedwarn ("ANSI C does not support the `ll' length modifier");
}
aflag = 0;
if (*format_chars == 'a' && info->is_scan)
if (info->format_type != strftime_format_type)
{
if (format_chars[1] == 's' || format_chars[1] == 'S'
|| format_chars[1] == '[')
if (*format_chars == 'h' || *format_chars == 'l')
length_char = *format_chars++;
else if (*format_chars == 'q' || *format_chars == 'L')
{
/* `a' is used as a flag. */
aflag = 1;
format_chars++;
length_char = *format_chars++;
if (pedantic && length_char == 'q')
warning ("ANSI C does not support the `%c' length modifier",
length_char);
}
}
if (suppressed && length_char != 0)
{
sprintf (message,
"use of `*' and `%c' together in format",
length_char);
warning (message);
else if (*format_chars == 'Z')
{
length_char = *format_chars++;
if (pedantic)
warning ("ANSI C does not support the `Z' length modifier");
}
else
length_char = 0;
if (length_char == 'l' && *format_chars == 'l')
{
length_char = 'q', format_chars++;
if (pedantic)
warning ("ANSI C does not support the `ll' length modifier");
}
if (*format_chars == 'a' && info->format_type == scanf_format_type)
{
if (format_chars[1] == 's' || format_chars[1] == 'S'
|| format_chars[1] == '[')
{
/* `a' is used as a flag. */
aflag = 1;
format_chars++;
}
}
if (suppressed && length_char != 0)
warning ("use of `*' and `%c' together in format", length_char);
}
format_char = *format_chars;
if (format_char == 0 || format_char == '%')
if (format_char == 0
|| (info->format_type != strftime_format_type && format_char == '%'))
{
warning ("conversion lacks type at end of format");
continue;
}
format_chars++;
fci = info->is_scan ? scan_char_table : print_char_table;
switch (info->format_type)
{
case printf_format_type:
fci = print_char_table;
break;
case scanf_format_type:
fci = scan_char_table;
break;
case strftime_format_type:
fci = time_char_table;
break;
default:
abort ();
}
while (fci->format_chars != 0
&& index (fci->format_chars, format_char) == 0)
++fci;
if (fci->format_chars == 0)
{
if (format_char >= 040 && format_char < 0177)
sprintf (message,
"unknown conversion type character `%c' in format",
warning ("unknown conversion type character `%c' in format",
format_char);
else
sprintf (message,
"unknown conversion type character 0x%x in format",
warning ("unknown conversion type character 0x%x in format",
format_char);
warning (message);
continue;
}
if (pedantic)
{
if (index (fci->flag_chars, 'G') != 0)
warning ("ANSI C does not support `%%%c'", format_char);
if (index (fci->flag_chars, 'o') != 0
&& index (flag_chars, 'O') != 0)
warning ("ANSI C does not support `%%O%c'", format_char);
}
if (wide && index (fci->flag_chars, 'w') == 0)
{
sprintf (message, "width used with `%c' format",
format_char);
warning (message);
}
warning ("width used with `%c' format", format_char);
if (index (fci->flag_chars, '2') != 0)
warning ("`%%%c' yields only last 2 digits of year", format_char);
else if (index (fci->flag_chars, '3') != 0)
warning ("`%%%c' yields only last 2 digits of year in some locales",
format_char);
if (precise && index (fci->flag_chars, 'p') == 0)
{
sprintf (message, "precision used with `%c' format",
format_char);
warning (message);
}
warning ("precision used with `%c' format", format_char);
if (aflag && index (fci->flag_chars, 'a') == 0)
{
sprintf (message, "`a' flag used with `%c' format",
format_char);
warning (message);
warning ("`a' flag used with `%c' format", format_char);
/* To simplify the following code. */
aflag = 0;
}
if (info->is_scan && format_char == '[')
if (info->format_type == scanf_format_type && format_char == '[')
{
/* Skip over scan set, in case it happens to have '%' in it. */
if (*format_chars == '^')
@ -1543,39 +1625,29 @@ check_format_info (info, params)
while (*format_chars && *format_chars != ']')
++format_chars;
if (*format_chars != ']')
/* The end of the format string was reached. */
warning ("no closing `]' for `%%[' format");
/* The end of the format string was reached. */
warning ("no closing `]' for `%%[' format");
}
if (suppressed)
{
if (index (fci->flag_chars, '*') == 0)
{
sprintf (message,
"suppression of `%c' conversion in format",
format_char);
warning (message);
}
warning ("suppression of `%c' conversion in format", format_char);
continue;
}
for (i = 0; flag_chars[i] != 0; ++i)
{
if (index (fci->flag_chars, flag_chars[i]) == 0)
{
sprintf (message, "flag `%c' used with type `%c'",
flag_chars[i], format_char);
warning (message);
}
warning ("flag `%c' used with type `%c'",
flag_chars[i], format_char);
}
if (info->format_type == strftime_format_type)
continue;
integral_format = (format_char == 'd' || format_char == 'i'
|| format_char == 'o' || format_char == 'u'
|| format_char == 'x' || format_char == 'x');
if (precise && index (flag_chars, '0') != 0 && integral_format)
{
sprintf (message,
"`0' flag ignored with precision specifier and `%c' format",
format_char);
warning (message);
}
warning ("`0' flag ignored with precision specifier and `%c' format",
format_char);
switch (length_char)
{
default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break;
@ -1637,20 +1709,17 @@ check_format_info (info, params)
continue;
}
if (TREE_CODE (cur_type) != ERROR_MARK)
{
sprintf (message,
"format argument is not a %s (arg %d)",
((fci->pointer_count + aflag == 1)
? "pointer" : "pointer to a pointer"),
arg_num);
warning (message);
}
warning ("format argument is not a %s (arg %d)",
((fci->pointer_count + aflag == 1)
? "pointer" : "pointer to a pointer"),
arg_num);
break;
}
/* See if this is an attempt to write into a const type with
scanf. */
if (info->is_scan && i == fci->pointer_count + aflag
if (info->format_type == scanf_format_type
&& i == fci->pointer_count + aflag
&& wanted_type != 0
&& TREE_CODE (cur_type) != ERROR_MARK
&& (TYPE_READONLY (cur_type)
@ -1658,10 +1727,7 @@ check_format_info (info, params)
&& (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'c'
|| (TREE_CODE_CLASS (TREE_CODE (cur_param)) == 'd'
&& TREE_READONLY (cur_param))))))
{
sprintf (message, "writing into constant object (arg %d)", arg_num);
warning (message);
}
warning ("writing into constant object (arg %d)", arg_num);
/* Check the type of the "real" argument, if there's a type we want. */
if (i == fci->pointer_count + aflag && wanted_type != 0
@ -1721,11 +1787,7 @@ check_format_info (info, params)
that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
if (strcmp (this, that) != 0)
{
sprintf (message, "%s format, %s arg (arg %d)",
this, that, arg_num);
warning (message);
}
warning ("%s format, %s arg (arg %d)", this, that, arg_num);
}
}
}
@ -1829,7 +1891,7 @@ convert_and_check (type, expr)
|| TREE_UNSIGNED (type)
|| ! int_fits_type_p (expr, unsigned_type (type)))
&& skip_evaluation == 0)
warning ("overflow in implicit constant conversion");
warning ("overflow in implicit constant conversion");
}
else
unsigned_conversion_warning (t, expr);

View File

@ -162,7 +162,6 @@ extern void gen_aux_info_record PROTO((tree, int, int, int));
extern void declare_function_name PROTO((void));
extern void decl_attributes PROTO((tree, tree, tree));
extern void init_function_format_info PROTO((void));
extern void record_function_format PROTO((tree, tree, int, int, int));
extern void check_function_format PROTO((tree, tree, tree));
/* Print an error message for invalid operands to arith operation CODE.
NOP_EXPR is used as a special case (see truthvalue_conversion). */

View File

@ -1274,7 +1274,7 @@ hack ((union foo) x);
@cindex functions in arbitrary sections
@cindex @code{volatile} applied to function
@cindex @code{const} applied to function
@cindex functions with @code{printf} or @code{scanf} style arguments
@cindex functions with @code{printf}, @code{scanf} or @code{strftime} style arguments
@cindex functions that are passed arguments in registers on the 386
@cindex functions that pop the argument stack on the 386
@cindex functions that do not pop the argument stack on the 386
@ -1378,9 +1378,9 @@ return @code{void}.
@item format (@var{archetype}, @var{string-index}, @var{first-to-check})
@cindex @code{format} function attribute
The @code{format} attribute specifies that a function takes @code{printf}
or @code{scanf} style arguments which should be type-checked against a
format string. For example, the declaration:
The @code{format} attribute specifies that a function takes @code{printf},
@code{scanf}, or @code{strftime} style arguments which should be type-checked
against a format string. For example, the declaration:
@smallexample
extern int
@ -1394,7 +1394,8 @@ for consistency with the @code{printf} style format string argument
@code{my_format}.
The parameter @var{archetype} determines how the format string is
interpreted, and should be either @code{printf} or @code{scanf}. The
interpreted, and should be either @code{printf}, @code{scanf}, or
@code{strftime}. The
parameter @var{string-index} specifies which argument is the format
string argument (starting from 1), while @var{first-to-check} is the
number of the first argument to check against the format string. For
@ -1411,7 +1412,7 @@ The @code{format} attribute allows you to identify your own functions
which take format strings as arguments, so that GNU CC can check the
calls to these functions for errors. The compiler always checks formats
for the ANSI library functions @code{printf}, @code{fprintf},
@code{sprintf}, @code{scanf}, @code{fscanf}, @code{sscanf},
@code{sprintf}, @code{scanf}, @code{fscanf}, @code{sscanf}, @code{strftime},
@code{vprintf}, @code{vfprintf} and @code{vsprintf} whenever such
warnings are requested (using @samp{-Wformat}), so there is no need to
modify the header file @file{stdio.h}.
@ -1431,18 +1432,19 @@ my_dgettext (char *my_domain, const char *my_format)
@noindent
causes the compiler to check the arguments in calls to
@code{my_dgettext} whose result is passed to a @code{printf} or
@code{scanf} type function for consistency with the @code{printf} style
format string argument @code{my_format}.
@code{my_dgettext} whose result is passed to a @code{printf},
@code{scanf}, or @code{strftime} type function for consistency with the
@code{printf} style format string argument @code{my_format}.
The parameter @var{string-index} specifies which argument is the format
string argument (starting from 1).
The @code{format-arg} attribute allows you to identify your own
functions which modify format strings, so that GNU CC can check the
calls to @code{printf} and @code{scanf} function whose operands are a
call to one of your own function. The compiler always treats
@code{gettext}, @code{dgettext}, and @code{dcgettext} in this manner.
calls to @code{printf}, @code{scanf}, or @code{strftime} function whose
operands are a call to one of your own function. The compiler always
treats @code{gettext}, @code{dgettext}, and @code{dcgettext} in this
manner.
@item section ("section-name")
@cindex @code{section} function attribute