1992-02-04 19:51:11 +00:00
|
|
|
|
/* Subroutines shared by all languages that are variants of C.
|
|
|
|
|
Copyright (C) 1992 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
This file is part of GNU CC.
|
|
|
|
|
|
|
|
|
|
GNU CC is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
|
|
GNU CC is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with GNU CC; see the file COPYING. If not, write to
|
|
|
|
|
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "tree.h"
|
|
|
|
|
#include "c-lex.h"
|
|
|
|
|
#include "c-tree.h"
|
|
|
|
|
#include "flags.h"
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
#undef NULL
|
|
|
|
|
#define NULL 0
|
|
|
|
|
|
|
|
|
|
/* Given a chain of STRING_CST nodes,
|
|
|
|
|
concatenate them into one STRING_CST
|
|
|
|
|
and give it a suitable array-of-chars data type. */
|
|
|
|
|
|
|
|
|
|
tree
|
|
|
|
|
combine_strings (strings)
|
|
|
|
|
tree strings;
|
|
|
|
|
{
|
|
|
|
|
register tree value, t;
|
|
|
|
|
register int length = 1;
|
|
|
|
|
int wide_length = 0;
|
|
|
|
|
int wide_flag = 0;
|
|
|
|
|
int wchar_bytes = TYPE_PRECISION (wchar_type_node) / BITS_PER_UNIT;
|
|
|
|
|
int nchars;
|
|
|
|
|
|
|
|
|
|
if (TREE_CHAIN (strings))
|
|
|
|
|
{
|
|
|
|
|
/* More than one in the chain, so concatenate. */
|
|
|
|
|
register char *p, *q;
|
|
|
|
|
|
|
|
|
|
/* Don't include the \0 at the end of each substring,
|
|
|
|
|
except for the last one.
|
|
|
|
|
Count wide strings and ordinary strings separately. */
|
|
|
|
|
for (t = strings; t; t = TREE_CHAIN (t))
|
|
|
|
|
{
|
|
|
|
|
if (TREE_TYPE (t) == wchar_array_type_node)
|
|
|
|
|
{
|
|
|
|
|
wide_length += (TREE_STRING_LENGTH (t) - wchar_bytes);
|
|
|
|
|
wide_flag = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
length += (TREE_STRING_LENGTH (t) - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If anything is wide, the non-wides will be converted,
|
|
|
|
|
which makes them take more space. */
|
|
|
|
|
if (wide_flag)
|
|
|
|
|
length = length * wchar_bytes + wide_length;
|
|
|
|
|
|
|
|
|
|
p = savealloc (length);
|
|
|
|
|
|
|
|
|
|
/* Copy the individual strings into the new combined string.
|
|
|
|
|
If the combined string is wide, convert the chars to ints
|
|
|
|
|
for any individual strings that are not wide. */
|
|
|
|
|
|
|
|
|
|
q = p;
|
|
|
|
|
for (t = strings; t; t = TREE_CHAIN (t))
|
|
|
|
|
{
|
|
|
|
|
int len = (TREE_STRING_LENGTH (t)
|
|
|
|
|
- ((TREE_TYPE (t) == wchar_array_type_node)
|
|
|
|
|
? wchar_bytes : 1));
|
|
|
|
|
if ((TREE_TYPE (t) == wchar_array_type_node) == wide_flag)
|
|
|
|
|
{
|
|
|
|
|
bcopy (TREE_STRING_POINTER (t), q, len);
|
|
|
|
|
q += len;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
|
((int *) q)[i] = TREE_STRING_POINTER (t)[i];
|
|
|
|
|
q += len * wchar_bytes;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (wide_flag)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < wchar_bytes; i++)
|
|
|
|
|
*q++ = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
*q = 0;
|
|
|
|
|
|
|
|
|
|
value = make_node (STRING_CST);
|
|
|
|
|
TREE_STRING_POINTER (value) = p;
|
|
|
|
|
TREE_STRING_LENGTH (value) = length;
|
|
|
|
|
TREE_CONSTANT (value) = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = strings;
|
|
|
|
|
length = TREE_STRING_LENGTH (value);
|
|
|
|
|
if (TREE_TYPE (value) == wchar_array_type_node)
|
|
|
|
|
wide_flag = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compute the number of elements, for the array type. */
|
|
|
|
|
nchars = wide_flag ? length / wchar_bytes : length;
|
|
|
|
|
|
|
|
|
|
/* Create the array type for the string constant.
|
|
|
|
|
-Wwrite-strings says make the string constant an array of const char
|
|
|
|
|
so that copying it to a non-const pointer will get a warning. */
|
|
|
|
|
if (warn_write_strings
|
|
|
|
|
&& (! flag_traditional && ! flag_writable_strings))
|
|
|
|
|
{
|
|
|
|
|
tree elements
|
|
|
|
|
= build_type_variant (wide_flag ? wchar_type_node : char_type_node,
|
|
|
|
|
1, 0);
|
|
|
|
|
TREE_TYPE (value)
|
|
|
|
|
= build_array_type (elements,
|
|
|
|
|
build_index_type (build_int_2 (nchars - 1, 0)));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TREE_TYPE (value)
|
|
|
|
|
= build_array_type (wide_flag ? wchar_type_node : char_type_node,
|
|
|
|
|
build_index_type (build_int_2 (nchars - 1, 0)));
|
|
|
|
|
TREE_CONSTANT (value) = 1;
|
|
|
|
|
TREE_STATIC (value) = 1;
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Process the attributes listed in ATTRIBUTES
|
|
|
|
|
and install them in DECL. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
decl_attributes (decl, attributes)
|
|
|
|
|
tree decl, attributes;
|
|
|
|
|
{
|
|
|
|
|
tree a;
|
|
|
|
|
for (a = attributes; a; a = TREE_CHAIN (a))
|
1992-03-30 03:20:11 +00:00
|
|
|
|
if (TREE_VALUE (a) == get_identifier ("packed"))
|
|
|
|
|
{
|
|
|
|
|
if (TREE_CODE (decl) == FIELD_DECL)
|
|
|
|
|
DECL_PACKED (decl) = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (TREE_VALUE (a) != 0
|
1992-02-04 19:51:11 +00:00
|
|
|
|
&& TREE_CODE (TREE_VALUE (a)) == TREE_LIST
|
1992-03-30 03:20:11 +00:00
|
|
|
|
&& TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("mode"))
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *specified_name
|
|
|
|
|
= IDENTIFIER_POINTER (TREE_VALUE (TREE_VALUE (a)));
|
|
|
|
|
|
|
|
|
|
/* Give this decl a type with the specified mode. */
|
|
|
|
|
for (i = 0; i < NUM_MACHINE_MODES; i++)
|
|
|
|
|
if (!strcmp (specified_name, GET_MODE_NAME (i)))
|
|
|
|
|
{
|
|
|
|
|
tree type
|
1992-04-25 01:51:43 +00:00
|
|
|
|
= type_for_mode (i, TREE_UNSIGNED (TREE_TYPE (decl)));
|
1992-03-30 03:20:11 +00:00
|
|
|
|
if (type != 0)
|
|
|
|
|
{
|
|
|
|
|
TREE_TYPE (decl) = type;
|
|
|
|
|
DECL_SIZE (decl) = 0;
|
|
|
|
|
layout_decl (decl);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
error ("no data type for mode `%s'", specified_name);
|
1992-04-25 01:51:43 +00:00
|
|
|
|
break;
|
1992-03-30 03:20:11 +00:00
|
|
|
|
}
|
|
|
|
|
if (i == NUM_MACHINE_MODES)
|
|
|
|
|
error ("unknown machine mode `%s'", specified_name);
|
|
|
|
|
}
|
|
|
|
|
else if (TREE_VALUE (a) != 0
|
|
|
|
|
&& TREE_CODE (TREE_VALUE (a)) == TREE_LIST
|
|
|
|
|
&& TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("aligned"))
|
1992-02-04 19:51:11 +00:00
|
|
|
|
{
|
|
|
|
|
int align = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (a)))
|
|
|
|
|
* BITS_PER_UNIT;
|
|
|
|
|
|
|
|
|
|
if (exact_log2 (align) == -1)
|
1992-04-25 01:51:43 +00:00
|
|
|
|
error_with_decl (decl,
|
|
|
|
|
"requested alignment of `%s' is not a power of 2");
|
1992-02-04 19:51:11 +00:00
|
|
|
|
else if (TREE_CODE (decl) != VAR_DECL
|
|
|
|
|
&& TREE_CODE (decl) != FIELD_DECL)
|
1992-04-25 01:51:43 +00:00
|
|
|
|
error_with_decl (decl,
|
|
|
|
|
"alignment specified for `%s', not a variable");
|
1992-02-04 19:51:11 +00:00
|
|
|
|
else
|
|
|
|
|
DECL_ALIGN (decl) = align;
|
|
|
|
|
}
|
|
|
|
|
else if (TREE_VALUE (a) != 0
|
|
|
|
|
&& TREE_CODE (TREE_VALUE (a)) == TREE_LIST
|
1992-03-30 03:20:11 +00:00
|
|
|
|
&& TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("format"))
|
1992-02-04 19:51:11 +00:00
|
|
|
|
{
|
|
|
|
|
tree list = TREE_VALUE (TREE_VALUE (a));
|
|
|
|
|
tree format_type = TREE_PURPOSE (list);
|
|
|
|
|
int format_num = TREE_INT_CST_LOW (TREE_PURPOSE (TREE_VALUE (list)));
|
|
|
|
|
int first_arg_num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
|
|
|
|
|
int is_scan;
|
|
|
|
|
|
|
|
|
|
if (TREE_CODE (decl) != FUNCTION_DECL)
|
|
|
|
|
{
|
1992-04-25 01:51:43 +00:00
|
|
|
|
error_with_decl (decl,
|
|
|
|
|
"argument format specified for non-function `%s'");
|
1992-02-04 19:51:11 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (format_type == get_identifier ("printf"))
|
|
|
|
|
is_scan = 0;
|
|
|
|
|
else if (format_type == get_identifier ("scanf"))
|
|
|
|
|
is_scan = 1;
|
|
|
|
|
else
|
|
|
|
|
{
|
1992-04-25 01:51:43 +00:00
|
|
|
|
error_with_decl (decl, "unrecognized format specifier for `%s'");
|
1992-02-04 19:51:11 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (first_arg_num != 0 && first_arg_num <= format_num)
|
|
|
|
|
{
|
1992-04-25 01:51:43 +00:00
|
|
|
|
error_with_decl (decl,
|
1992-02-04 19:51:11 +00:00
|
|
|
|
"format string arg follows the args to be formatted, for `%s'");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record_format_info (DECL_NAME (decl), is_scan, format_num,
|
|
|
|
|
first_arg_num);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
c_expand_expr_stmt (expr)
|
|
|
|
|
tree expr;
|
|
|
|
|
{
|
|
|
|
|
/* Do default conversion if safe and possibly important,
|
|
|
|
|
in case within ({...}). */
|
|
|
|
|
if ((TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE && lvalue_p (expr))
|
|
|
|
|
|| TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE)
|
|
|
|
|
expr = default_conversion (expr);
|
|
|
|
|
|
|
|
|
|
if (TREE_TYPE (expr) != error_mark_node
|
|
|
|
|
&& TYPE_SIZE (TREE_TYPE (expr)) == 0
|
|
|
|
|
&& TREE_CODE (TREE_TYPE (expr)) != ARRAY_TYPE)
|
|
|
|
|
error ("expression statement has incomplete type");
|
|
|
|
|
|
|
|
|
|
expand_expr_stmt (expr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Validate the expression after `case' and apply default promotions. */
|
|
|
|
|
|
|
|
|
|
tree
|
|
|
|
|
check_case_value (value)
|
|
|
|
|
tree value;
|
|
|
|
|
{
|
|
|
|
|
if (value == NULL_TREE)
|
|
|
|
|
return value;
|
|
|
|
|
|
|
|
|
|
/* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */
|
|
|
|
|
if (TREE_CODE (value) == NON_LVALUE_EXPR)
|
|
|
|
|
value = TREE_OPERAND (value, 0);
|
|
|
|
|
|
|
|
|
|
if (TREE_CODE (value) != INTEGER_CST
|
|
|
|
|
&& value != error_mark_node)
|
|
|
|
|
{
|
|
|
|
|
error ("case label does not reduce to an integer constant");
|
|
|
|
|
value = error_mark_node;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Promote char or short to int. */
|
|
|
|
|
value = default_conversion (value);
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return an integer type with BITS bits of precision,
|
|
|
|
|
that is unsigned if UNSIGNEDP is nonzero, otherwise signed. */
|
|
|
|
|
|
|
|
|
|
tree
|
|
|
|
|
type_for_size (bits, unsignedp)
|
|
|
|
|
unsigned bits;
|
|
|
|
|
int unsignedp;
|
|
|
|
|
{
|
|
|
|
|
if (bits <= TYPE_PRECISION (signed_char_type_node))
|
|
|
|
|
return unsignedp ? unsigned_char_type_node : signed_char_type_node;
|
|
|
|
|
|
|
|
|
|
if (bits <= TYPE_PRECISION (short_integer_type_node))
|
|
|
|
|
return unsignedp ? short_unsigned_type_node : short_integer_type_node;
|
|
|
|
|
|
|
|
|
|
if (bits <= TYPE_PRECISION (integer_type_node))
|
|
|
|
|
return unsignedp ? unsigned_type_node : integer_type_node;
|
|
|
|
|
|
|
|
|
|
if (bits <= TYPE_PRECISION (long_integer_type_node))
|
|
|
|
|
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
|
|
|
|
|
|
|
|
|
|
if (bits <= TYPE_PRECISION (long_long_integer_type_node))
|
|
|
|
|
return (unsignedp ? long_long_unsigned_type_node
|
|
|
|
|
: long_long_integer_type_node);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a data type that has machine mode MODE.
|
|
|
|
|
If the mode is an integer,
|
|
|
|
|
then UNSIGNEDP selects between signed and unsigned types. */
|
|
|
|
|
|
|
|
|
|
tree
|
|
|
|
|
type_for_mode (mode, unsignedp)
|
|
|
|
|
enum machine_mode mode;
|
|
|
|
|
int unsignedp;
|
|
|
|
|
{
|
|
|
|
|
if (mode == TYPE_MODE (signed_char_type_node))
|
|
|
|
|
return unsignedp ? unsigned_char_type_node : signed_char_type_node;
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (short_integer_type_node))
|
|
|
|
|
return unsignedp ? short_unsigned_type_node : short_integer_type_node;
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (integer_type_node))
|
|
|
|
|
return unsignedp ? unsigned_type_node : integer_type_node;
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (long_integer_type_node))
|
|
|
|
|
return unsignedp ? long_unsigned_type_node : long_integer_type_node;
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (long_long_integer_type_node))
|
|
|
|
|
return unsignedp ? long_long_unsigned_type_node : long_long_integer_type_node;
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (float_type_node))
|
|
|
|
|
return float_type_node;
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (double_type_node))
|
|
|
|
|
return double_type_node;
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (long_double_type_node))
|
|
|
|
|
return long_double_type_node;
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (build_pointer_type (char_type_node)))
|
|
|
|
|
return build_pointer_type (char_type_node);
|
|
|
|
|
|
|
|
|
|
if (mode == TYPE_MODE (build_pointer_type (integer_type_node)))
|
|
|
|
|
return build_pointer_type (integer_type_node);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print an error message for invalid operands to arith operation CODE.
|
|
|
|
|
NOP_EXPR is used as a special case (see truthvalue_conversion). */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
binary_op_error (code)
|
|
|
|
|
enum tree_code code;
|
|
|
|
|
{
|
|
|
|
|
register char *opname;
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case NOP_EXPR:
|
|
|
|
|
error ("invalid truth-value expression");
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case PLUS_EXPR:
|
|
|
|
|
opname = "+"; break;
|
|
|
|
|
case MINUS_EXPR:
|
|
|
|
|
opname = "-"; break;
|
|
|
|
|
case MULT_EXPR:
|
|
|
|
|
opname = "*"; break;
|
|
|
|
|
case MAX_EXPR:
|
|
|
|
|
opname = "max"; break;
|
|
|
|
|
case MIN_EXPR:
|
|
|
|
|
opname = "min"; break;
|
|
|
|
|
case EQ_EXPR:
|
|
|
|
|
opname = "=="; break;
|
|
|
|
|
case NE_EXPR:
|
|
|
|
|
opname = "!="; break;
|
|
|
|
|
case LE_EXPR:
|
|
|
|
|
opname = "<="; break;
|
|
|
|
|
case GE_EXPR:
|
|
|
|
|
opname = ">="; break;
|
|
|
|
|
case LT_EXPR:
|
|
|
|
|
opname = "<"; break;
|
|
|
|
|
case GT_EXPR:
|
|
|
|
|
opname = ">"; break;
|
|
|
|
|
case LSHIFT_EXPR:
|
|
|
|
|
opname = "<<"; break;
|
|
|
|
|
case RSHIFT_EXPR:
|
|
|
|
|
opname = ">>"; break;
|
|
|
|
|
case TRUNC_MOD_EXPR:
|
1992-04-04 19:11:28 +00:00
|
|
|
|
case FLOOR_MOD_EXPR:
|
1992-02-04 19:51:11 +00:00
|
|
|
|
opname = "%"; break;
|
|
|
|
|
case TRUNC_DIV_EXPR:
|
1992-04-04 19:11:28 +00:00
|
|
|
|
case FLOOR_DIV_EXPR:
|
1992-02-04 19:51:11 +00:00
|
|
|
|
opname = "/"; break;
|
|
|
|
|
case BIT_AND_EXPR:
|
|
|
|
|
opname = "&"; break;
|
|
|
|
|
case BIT_IOR_EXPR:
|
|
|
|
|
opname = "|"; break;
|
|
|
|
|
case TRUTH_ANDIF_EXPR:
|
|
|
|
|
opname = "&&"; break;
|
|
|
|
|
case TRUTH_ORIF_EXPR:
|
|
|
|
|
opname = "||"; break;
|
|
|
|
|
case BIT_XOR_EXPR:
|
|
|
|
|
opname = "^"; break;
|
1992-04-04 19:11:28 +00:00
|
|
|
|
case LROTATE_EXPR:
|
|
|
|
|
case RROTATE_EXPR:
|
|
|
|
|
opname = "rotate"; break;
|
1992-02-04 19:51:11 +00:00
|
|
|
|
}
|
|
|
|
|
error ("invalid operands to binary %s", opname);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Subroutine of build_binary_op, used for comparison operations.
|
|
|
|
|
See if the operands have both been converted from subword integer types
|
|
|
|
|
and, if so, perhaps change them both back to their original type.
|
|
|
|
|
|
|
|
|
|
The arguments of this function are all pointers to local variables
|
|
|
|
|
of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1,
|
|
|
|
|
RESTYPE_PTR is &RESULT_TYPE and RESCODE_PTR is &RESULTCODE.
|
|
|
|
|
|
|
|
|
|
If this function returns nonzero, it means that the comparison has
|
|
|
|
|
a constant value. What this function returns is an expression for
|
|
|
|
|
that value. */
|
|
|
|
|
|
|
|
|
|
tree
|
|
|
|
|
shorten_compare (op0_ptr, op1_ptr, restype_ptr, rescode_ptr)
|
|
|
|
|
tree *op0_ptr, *op1_ptr;
|
|
|
|
|
tree *restype_ptr;
|
|
|
|
|
enum tree_code *rescode_ptr;
|
|
|
|
|
{
|
|
|
|
|
register tree type;
|
|
|
|
|
tree op0 = *op0_ptr;
|
|
|
|
|
tree op1 = *op1_ptr;
|
|
|
|
|
int unsignedp0, unsignedp1;
|
|
|
|
|
int real1, real2;
|
|
|
|
|
tree primop0, primop1;
|
|
|
|
|
enum tree_code code = *rescode_ptr;
|
|
|
|
|
|
|
|
|
|
/* Throw away any conversions to wider types
|
|
|
|
|
already present in the operands. */
|
|
|
|
|
|
|
|
|
|
primop0 = get_narrower (op0, &unsignedp0);
|
|
|
|
|
primop1 = get_narrower (op1, &unsignedp1);
|
|
|
|
|
|
|
|
|
|
/* Handle the case that OP0 does not *contain* a conversion
|
|
|
|
|
but it *requires* conversion to FINAL_TYPE. */
|
|
|
|
|
|
|
|
|
|
if (op0 == primop0 && TREE_TYPE (op0) != *restype_ptr)
|
|
|
|
|
unsignedp0 = TREE_UNSIGNED (TREE_TYPE (op0));
|
|
|
|
|
if (op1 == primop1 && TREE_TYPE (op1) != *restype_ptr)
|
|
|
|
|
unsignedp1 = TREE_UNSIGNED (TREE_TYPE (op1));
|
|
|
|
|
|
|
|
|
|
/* If one of the operands must be floated, we cannot optimize. */
|
|
|
|
|
real1 = TREE_CODE (TREE_TYPE (primop0)) == REAL_TYPE;
|
|
|
|
|
real2 = TREE_CODE (TREE_TYPE (primop1)) == REAL_TYPE;
|
|
|
|
|
|
|
|
|
|
/* If first arg is constant, swap the args (changing operation
|
|
|
|
|
so value is preserved), for canonicalization. */
|
|
|
|
|
|
|
|
|
|
if (TREE_CONSTANT (primop0))
|
|
|
|
|
{
|
|
|
|
|
register tree tem = primop0;
|
|
|
|
|
register int temi = unsignedp0;
|
|
|
|
|
primop0 = primop1;
|
|
|
|
|
primop1 = tem;
|
|
|
|
|
tem = op0;
|
|
|
|
|
op0 = op1;
|
|
|
|
|
op1 = tem;
|
|
|
|
|
*op0_ptr = op0;
|
|
|
|
|
*op1_ptr = op1;
|
|
|
|
|
unsignedp0 = unsignedp1;
|
|
|
|
|
unsignedp1 = temi;
|
|
|
|
|
temi = real1;
|
|
|
|
|
real1 = real2;
|
|
|
|
|
real2 = temi;
|
|
|
|
|
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case LT_EXPR:
|
|
|
|
|
code = GT_EXPR;
|
|
|
|
|
break;
|
|
|
|
|
case GT_EXPR:
|
|
|
|
|
code = LT_EXPR;
|
|
|
|
|
break;
|
|
|
|
|
case LE_EXPR:
|
|
|
|
|
code = GE_EXPR;
|
|
|
|
|
break;
|
|
|
|
|
case GE_EXPR:
|
|
|
|
|
code = LE_EXPR;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
*rescode_ptr = code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If comparing an integer against a constant more bits wide,
|
|
|
|
|
maybe we can deduce a value of 1 or 0 independent of the data.
|
|
|
|
|
Or else truncate the constant now
|
|
|
|
|
rather than extend the variable at run time.
|
|
|
|
|
|
|
|
|
|
This is only interesting if the constant is the wider arg.
|
|
|
|
|
Also, it is not safe if the constant is unsigned and the
|
|
|
|
|
variable arg is signed, since in this case the variable
|
|
|
|
|
would be sign-extended and then regarded as unsigned.
|
|
|
|
|
Our technique fails in this case because the lowest/highest
|
|
|
|
|
possible unsigned results don't follow naturally from the
|
|
|
|
|
lowest/highest possible values of the variable operand.
|
|
|
|
|
For just EQ_EXPR and NE_EXPR there is another technique that
|
|
|
|
|
could be used: see if the constant can be faithfully represented
|
|
|
|
|
in the other operand's type, by truncating it and reextending it
|
|
|
|
|
and see if that preserves the constant's value. */
|
|
|
|
|
|
|
|
|
|
if (!real1 && !real2
|
|
|
|
|
&& TREE_CODE (primop1) == INTEGER_CST
|
|
|
|
|
&& TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr))
|
|
|
|
|
{
|
|
|
|
|
int min_gt, max_gt, min_lt, max_lt;
|
|
|
|
|
tree maxval, minval;
|
|
|
|
|
/* 1 if comparison is nominally unsigned. */
|
|
|
|
|
int unsignedp = TREE_UNSIGNED (*restype_ptr);
|
|
|
|
|
tree val;
|
|
|
|
|
|
|
|
|
|
type = signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0));
|
|
|
|
|
|
|
|
|
|
maxval = TYPE_MAX_VALUE (type);
|
|
|
|
|
minval = TYPE_MIN_VALUE (type);
|
|
|
|
|
|
|
|
|
|
if (unsignedp && !unsignedp0)
|
|
|
|
|
*restype_ptr = signed_type (*restype_ptr);
|
|
|
|
|
|
|
|
|
|
if (TREE_TYPE (primop1) != *restype_ptr)
|
|
|
|
|
primop1 = convert (*restype_ptr, primop1);
|
|
|
|
|
if (type != *restype_ptr)
|
|
|
|
|
{
|
|
|
|
|
minval = convert (*restype_ptr, minval);
|
|
|
|
|
maxval = convert (*restype_ptr, maxval);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unsignedp && unsignedp0)
|
|
|
|
|
{
|
|
|
|
|
min_gt = INT_CST_LT_UNSIGNED (primop1, minval);
|
|
|
|
|
max_gt = INT_CST_LT_UNSIGNED (primop1, maxval);
|
|
|
|
|
min_lt = INT_CST_LT_UNSIGNED (minval, primop1);
|
|
|
|
|
max_lt = INT_CST_LT_UNSIGNED (maxval, primop1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
min_gt = INT_CST_LT (primop1, minval);
|
|
|
|
|
max_gt = INT_CST_LT (primop1, maxval);
|
|
|
|
|
min_lt = INT_CST_LT (minval, primop1);
|
|
|
|
|
max_lt = INT_CST_LT (maxval, primop1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val = 0;
|
|
|
|
|
/* This used to be a switch, but Genix compiler can't handle that. */
|
|
|
|
|
if (code == NE_EXPR)
|
|
|
|
|
{
|
|
|
|
|
if (max_lt || min_gt)
|
|
|
|
|
val = integer_one_node;
|
|
|
|
|
}
|
|
|
|
|
else if (code == EQ_EXPR)
|
|
|
|
|
{
|
|
|
|
|
if (max_lt || min_gt)
|
|
|
|
|
val = integer_zero_node;
|
|
|
|
|
}
|
|
|
|
|
else if (code == LT_EXPR)
|
|
|
|
|
{
|
|
|
|
|
if (max_lt)
|
|
|
|
|
val = integer_one_node;
|
|
|
|
|
if (!min_lt)
|
|
|
|
|
val = integer_zero_node;
|
|
|
|
|
}
|
|
|
|
|
else if (code == GT_EXPR)
|
|
|
|
|
{
|
|
|
|
|
if (min_gt)
|
|
|
|
|
val = integer_one_node;
|
|
|
|
|
if (!max_gt)
|
|
|
|
|
val = integer_zero_node;
|
|
|
|
|
}
|
|
|
|
|
else if (code == LE_EXPR)
|
|
|
|
|
{
|
|
|
|
|
if (!max_gt)
|
|
|
|
|
val = integer_one_node;
|
|
|
|
|
if (min_gt)
|
|
|
|
|
val = integer_zero_node;
|
|
|
|
|
}
|
|
|
|
|
else if (code == GE_EXPR)
|
|
|
|
|
{
|
|
|
|
|
if (!min_lt)
|
|
|
|
|
val = integer_one_node;
|
|
|
|
|
if (max_lt)
|
|
|
|
|
val = integer_zero_node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If primop0 was sign-extended and unsigned comparison specd,
|
|
|
|
|
we did a signed comparison above using the signed type bounds.
|
|
|
|
|
But the comparison we output must be unsigned.
|
|
|
|
|
|
|
|
|
|
Also, for inequalities, VAL is no good; but if the signed
|
|
|
|
|
comparison had *any* fixed result, it follows that the
|
|
|
|
|
unsigned comparison just tests the sign in reverse
|
|
|
|
|
(positive values are LE, negative ones GE).
|
|
|
|
|
So we can generate an unsigned comparison
|
|
|
|
|
against an extreme value of the signed type. */
|
|
|
|
|
|
|
|
|
|
if (unsignedp && !unsignedp0)
|
|
|
|
|
{
|
|
|
|
|
if (val != 0)
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case LT_EXPR:
|
|
|
|
|
case GE_EXPR:
|
|
|
|
|
primop1 = TYPE_MIN_VALUE (type);
|
|
|
|
|
val = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case LE_EXPR:
|
|
|
|
|
case GT_EXPR:
|
|
|
|
|
primop1 = TYPE_MAX_VALUE (type);
|
|
|
|
|
val = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
type = unsigned_type (type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (max_lt && !unsignedp0)
|
|
|
|
|
{
|
|
|
|
|
/* This is the case of (char)x >?< 0x80, which people used to use
|
|
|
|
|
expecting old C compilers to change the 0x80 into -0x80. */
|
|
|
|
|
if (val == integer_zero_node)
|
|
|
|
|
warning ("comparison is always 0 due to limited range of data type");
|
|
|
|
|
if (val == integer_one_node)
|
|
|
|
|
warning ("comparison is always 1 due to limited range of data type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (min_gt && unsignedp0)
|
|
|
|
|
{
|
|
|
|
|
/* This is the case of (unsigned char)x >?< -1. */
|
|
|
|
|
if (val == integer_zero_node)
|
|
|
|
|
warning ("comparison is always 0 due to limited range of data type");
|
|
|
|
|
if (val == integer_one_node)
|
|
|
|
|
warning ("comparison is always 1 due to limited range of data type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (val != 0)
|
|
|
|
|
{
|
|
|
|
|
/* Don't forget to evaluate PRIMOP0 if it has side effects. */
|
|
|
|
|
if (TREE_SIDE_EFFECTS (primop0))
|
|
|
|
|
return build (COMPOUND_EXPR, TREE_TYPE (val), primop0, val);
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Value is not predetermined, but do the comparison
|
|
|
|
|
in the type of the operand that is not constant.
|
|
|
|
|
TYPE is already properly set. */
|
|
|
|
|
}
|
|
|
|
|
else if (real1 && real2
|
|
|
|
|
&& TYPE_PRECISION (TREE_TYPE (primop0)) == TYPE_PRECISION (TREE_TYPE (primop1)))
|
|
|
|
|
type = TREE_TYPE (primop0);
|
|
|
|
|
|
|
|
|
|
/* If args' natural types are both narrower than nominal type
|
|
|
|
|
and both extend in the same manner, compare them
|
|
|
|
|
in the type of the wider arg.
|
|
|
|
|
Otherwise must actually extend both to the nominal
|
|
|
|
|
common type lest different ways of extending
|
|
|
|
|
alter the result.
|
|
|
|
|
(eg, (short)-1 == (unsigned short)-1 should be 0.) */
|
|
|
|
|
|
|
|
|
|
else if (unsignedp0 == unsignedp1 && real1 == real2
|
|
|
|
|
&& TYPE_PRECISION (TREE_TYPE (primop0)) < TYPE_PRECISION (*restype_ptr)
|
|
|
|
|
&& TYPE_PRECISION (TREE_TYPE (primop1)) < TYPE_PRECISION (*restype_ptr))
|
|
|
|
|
{
|
|
|
|
|
type = common_type (TREE_TYPE (primop0), TREE_TYPE (primop1));
|
|
|
|
|
type = signed_or_unsigned_type (unsignedp0
|
|
|
|
|
|| TREE_UNSIGNED (*restype_ptr),
|
|
|
|
|
type);
|
|
|
|
|
/* Make sure shorter operand is extended the right way
|
|
|
|
|
to match the longer operand. */
|
|
|
|
|
primop0 = convert (signed_or_unsigned_type (unsignedp0, TREE_TYPE (primop0)),
|
|
|
|
|
primop0);
|
|
|
|
|
primop1 = convert (signed_or_unsigned_type (unsignedp1, TREE_TYPE (primop1)),
|
|
|
|
|
primop1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Here we must do the comparison on the nominal type
|
|
|
|
|
using the args exactly as we received them. */
|
|
|
|
|
type = *restype_ptr;
|
|
|
|
|
primop0 = op0;
|
|
|
|
|
primop1 = op1;
|
|
|
|
|
|
|
|
|
|
if (!real1 && !real2 && integer_zerop (primop1)
|
|
|
|
|
&& TREE_UNSIGNED (TREE_TYPE (primop0)))
|
|
|
|
|
{
|
|
|
|
|
tree value = 0;
|
|
|
|
|
switch (code)
|
|
|
|
|
{
|
|
|
|
|
case GE_EXPR:
|
|
|
|
|
if (extra_warnings)
|
|
|
|
|
warning ("unsigned value >= 0 is always 1");
|
|
|
|
|
value = integer_one_node;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case LT_EXPR:
|
|
|
|
|
if (extra_warnings)
|
|
|
|
|
warning ("unsigned value < 0 is always 0");
|
|
|
|
|
value = integer_zero_node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (value != 0)
|
|
|
|
|
{
|
|
|
|
|
/* Don't forget to evaluate PRIMOP0 if it has side effects. */
|
|
|
|
|
if (TREE_SIDE_EFFECTS (primop0))
|
|
|
|
|
return build (COMPOUND_EXPR, TREE_TYPE (value),
|
|
|
|
|
primop0, value);
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*op0_ptr = convert (type, primop0);
|
|
|
|
|
*op1_ptr = convert (type, primop1);
|
|
|
|
|
|
|
|
|
|
*restype_ptr = integer_type_node;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Prepare expr to be an argument of a TRUTH_NOT_EXPR,
|
|
|
|
|
or validate its data type for an `if' or `while' statement or ?..: exp.
|
|
|
|
|
|
|
|
|
|
This preparation consists of taking the ordinary
|
|
|
|
|
representation of an expression expr and producing a valid tree
|
|
|
|
|
boolean expression describing whether expr is nonzero. We could
|
|
|
|
|
simply always do build_binary_op (NE_EXPR, expr, integer_zero_node, 1),
|
|
|
|
|
but we optimize comparisons, &&, ||, and !.
|
|
|
|
|
|
|
|
|
|
The resulting type should always be `integer_type_node'. */
|
|
|
|
|
|
|
|
|
|
tree
|
|
|
|
|
truthvalue_conversion (expr)
|
|
|
|
|
tree expr;
|
|
|
|
|
{
|
|
|
|
|
register enum tree_code code;
|
|
|
|
|
|
|
|
|
|
switch (TREE_CODE (expr))
|
|
|
|
|
{
|
|
|
|
|
/* It is simpler and generates better code to have only TRUTH_*_EXPR
|
|
|
|
|
or comparison expressions as truth values at this level. */
|
|
|
|
|
#if 0
|
|
|
|
|
case COMPONENT_REF:
|
|
|
|
|
/* A one-bit unsigned bit-field is already acceptable. */
|
|
|
|
|
if (1 == TREE_INT_CST_LOW (DECL_SIZE (TREE_OPERAND (expr, 1)))
|
|
|
|
|
&& TREE_UNSIGNED (TREE_OPERAND (expr, 1)))
|
|
|
|
|
return expr;
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
case EQ_EXPR:
|
|
|
|
|
/* It is simpler and generates better code to have only TRUTH_*_EXPR
|
|
|
|
|
or comparison expressions as truth values at this level. */
|
|
|
|
|
#if 0
|
|
|
|
|
if (integer_zerop (TREE_OPERAND (expr, 1)))
|
|
|
|
|
return build_unary_op (TRUTH_NOT_EXPR, TREE_OPERAND (expr, 0), 0);
|
|
|
|
|
#endif
|
|
|
|
|
case NE_EXPR: case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR:
|
|
|
|
|
case TRUTH_ANDIF_EXPR:
|
|
|
|
|
case TRUTH_ORIF_EXPR:
|
|
|
|
|
case TRUTH_AND_EXPR:
|
|
|
|
|
case TRUTH_OR_EXPR:
|
|
|
|
|
case ERROR_MARK:
|
|
|
|
|
return expr;
|
|
|
|
|
|
|
|
|
|
case INTEGER_CST:
|
|
|
|
|
return integer_zerop (expr) ? integer_zero_node : integer_one_node;
|
|
|
|
|
|
|
|
|
|
case REAL_CST:
|
|
|
|
|
return real_zerop (expr) ? integer_zero_node : integer_one_node;
|
|
|
|
|
|
|
|
|
|
case ADDR_EXPR:
|
|
|
|
|
if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 0)))
|
|
|
|
|
return build (COMPOUND_EXPR, integer_type_node,
|
|
|
|
|
TREE_OPERAND (expr, 0), integer_one_node);
|
|
|
|
|
else
|
|
|
|
|
return integer_one_node;
|
|
|
|
|
|
|
|
|
|
case NEGATE_EXPR:
|
|
|
|
|
case ABS_EXPR:
|
|
|
|
|
case FLOAT_EXPR:
|
|
|
|
|
case FFS_EXPR:
|
|
|
|
|
/* These don't change whether an object is non-zero or zero. */
|
|
|
|
|
return truthvalue_conversion (TREE_OPERAND (expr, 0));
|
|
|
|
|
|
|
|
|
|
case LROTATE_EXPR:
|
|
|
|
|
case RROTATE_EXPR:
|
|
|
|
|
/* These don't change whether an object is zero or non-zero, but
|
|
|
|
|
we can't ignore them if their second arg has side-effects. */
|
|
|
|
|
if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)))
|
|
|
|
|
return build (COMPOUND_EXPR, integer_type_node, TREE_OPERAND (expr, 1),
|
|
|
|
|
truthvalue_conversion (TREE_OPERAND (expr, 0)));
|
|
|
|
|
else
|
|
|
|
|
return truthvalue_conversion (TREE_OPERAND (expr, 0));
|
|
|
|
|
|
|
|
|
|
case COND_EXPR:
|
|
|
|
|
/* Distribute the conversion into the arms of a COND_EXPR. */
|
|
|
|
|
return fold (build (COND_EXPR, integer_type_node, TREE_OPERAND (expr, 0),
|
|
|
|
|
truthvalue_conversion (TREE_OPERAND (expr, 1)),
|
|
|
|
|
truthvalue_conversion (TREE_OPERAND (expr, 2))));
|
|
|
|
|
|
|
|
|
|
case CONVERT_EXPR:
|
|
|
|
|
/* Don't cancel the effect of a CONVERT_EXPR from a REFERENCE_TYPE,
|
|
|
|
|
since that affects how `default_conversion' will behave. */
|
|
|
|
|
if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE
|
|
|
|
|
|| TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
|
|
|
|
|
break;
|
|
|
|
|
/* fall through... */
|
|
|
|
|
case NOP_EXPR:
|
|
|
|
|
/* If this is widening the argument, we can ignore it. */
|
|
|
|
|
if (TYPE_PRECISION (TREE_TYPE (expr))
|
|
|
|
|
>= TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (expr, 0))))
|
|
|
|
|
return truthvalue_conversion (TREE_OPERAND (expr, 0));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIT_XOR_EXPR:
|
|
|
|
|
case MINUS_EXPR:
|
|
|
|
|
/* These can be changed into a comparison of the two objects. */
|
|
|
|
|
if (TREE_TYPE (TREE_OPERAND (expr, 0))
|
|
|
|
|
== TREE_TYPE (TREE_OPERAND (expr, 1)))
|
|
|
|
|
return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0),
|
|
|
|
|
TREE_OPERAND (expr, 1), 1);
|
|
|
|
|
return build_binary_op (NE_EXPR, TREE_OPERAND (expr, 0),
|
|
|
|
|
fold (build1 (NOP_EXPR,
|
|
|
|
|
TREE_TYPE (TREE_OPERAND (expr, 0)),
|
|
|
|
|
TREE_OPERAND (expr, 1))), 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return build_binary_op (NE_EXPR, expr, integer_zero_node, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read the rest of a #-directive from input stream FINPUT.
|
|
|
|
|
In normal use, the directive name and the white space after it
|
|
|
|
|
have already been read, so they won't be included in the result.
|
|
|
|
|
We allow for the fact that the directive line may contain
|
|
|
|
|
a newline embedded within a character or string literal which forms
|
|
|
|
|
a part of the directive.
|
|
|
|
|
|
|
|
|
|
The value is a string in a reusable buffer. It remains valid
|
|
|
|
|
only until the next time this function is called. */
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
get_directive_line (finput)
|
|
|
|
|
register FILE *finput;
|
|
|
|
|
{
|
|
|
|
|
static char *directive_buffer = NULL;
|
|
|
|
|
static unsigned buffer_length = 0;
|
|
|
|
|
register char *p;
|
|
|
|
|
register char *buffer_limit;
|
|
|
|
|
register int looking_for = 0;
|
|
|
|
|
register int char_escaped = 0;
|
|
|
|
|
|
|
|
|
|
if (buffer_length == 0)
|
|
|
|
|
{
|
|
|
|
|
directive_buffer = (char *)xmalloc (128);
|
|
|
|
|
buffer_length = 128;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer_limit = &directive_buffer[buffer_length];
|
|
|
|
|
|
|
|
|
|
for (p = directive_buffer; ; )
|
|
|
|
|
{
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
/* Make buffer bigger if it is full. */
|
|
|
|
|
if (p >= buffer_limit)
|
|
|
|
|
{
|
|
|
|
|
register unsigned bytes_used = (p - directive_buffer);
|
|
|
|
|
|
|
|
|
|
buffer_length *= 2;
|
|
|
|
|
directive_buffer
|
|
|
|
|
= (char *)xrealloc (directive_buffer, buffer_length);
|
|
|
|
|
p = &directive_buffer[bytes_used];
|
|
|
|
|
buffer_limit = &directive_buffer[buffer_length];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c = getc (finput);
|
|
|
|
|
|
|
|
|
|
/* Discard initial whitespace. */
|
|
|
|
|
if ((c == ' ' || c == '\t') && p == directive_buffer)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Detect the end of the directive. */
|
|
|
|
|
if (c == '\n' && looking_for == 0)
|
|
|
|
|
{
|
|
|
|
|
ungetc (c, finput);
|
|
|
|
|
c = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*p++ = c;
|
|
|
|
|
|
|
|
|
|
if (c == 0)
|
|
|
|
|
return directive_buffer;
|
|
|
|
|
|
|
|
|
|
/* Handle string and character constant syntax. */
|
|
|
|
|
if (looking_for)
|
|
|
|
|
{
|
|
|
|
|
if (looking_for == c && !char_escaped)
|
|
|
|
|
looking_for = 0; /* Found terminator... stop looking. */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if (c == '\'' || c == '"')
|
|
|
|
|
looking_for = c; /* Don't stop buffering until we see another
|
|
|
|
|
another one of these (or an EOF). */
|
|
|
|
|
|
|
|
|
|
/* Handle backslash. */
|
|
|
|
|
char_escaped = (c == '\\' && ! char_escaped);
|
|
|
|
|
}
|
|
|
|
|
}
|