Select Git revision
-
Fredrik Hübinette (Hubbe) authored
Rev: src/docode.c:1.39
Fredrik Hübinette (Hubbe) authoredRev: src/docode.c:1.39
cpp.c 54.15 KiB
/*
|| This file is part of Pike. For copyright information see COPYRIGHT.
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING
|| for more information.
*/
#include "global.h"
#include "stralloc.h"
#include "module_support.h"
#include "interpret.h"
#include "svalue.h"
#include "pike_macros.h"
#include "hashtable.h"
#include "program.h"
#include "object.h"
#include "pike_error.h"
#include "array.h"
#include "mapping.h"
#include "builtin_functions.h"
#include "operators.h"
#include "constants.h"
#include "time.h"
#include "stuff.h"
#include "version.h"
#include "pike_types.h"
#include "cpp.h"
#include "lex.h"
#include <ctype.h>
#define sp Pike_sp
#define CPP_NO_OUTPUT 1 /* Inside false section of #if/#else */
#define CPP_EXPECT_ELSE 2 /* Expect #else/#elif/#elseif. */
#define CPP_EXPECT_ENDIF 4 /* Expect #endif */
#define CPP_REALLY_NO_OUTPUT 8 /* Entire preprocessor is in false section. */
#define CPP_END_AT_NEWLINE 16 /* Halt at end of line. */
#define CPP_DO_IF 32
#define CPP_NO_EXPAND 64
#define OUTP() (!(flags & (CPP_NO_OUTPUT | CPP_REALLY_NO_OUTPUT)))
#define PUTNL() string_builder_putchar(&this->buf, '\n')
#define GOBBLE(X) (data[pos]==(X)?++pos,1:0)
#define PUTC(C) do { \
int c_=(C); if(OUTP() || c_=='\n') string_builder_putchar(&this->buf, c_); }while(0)
#define MAX_ARGS 255
#define DEF_ARG_STRINGIFY 0x100000
#define DEF_ARG_NOPRESPACE 0x200000
#define DEF_ARG_NOPOSTSPACE 0x400000
#define DEF_ARG_NEED_COMMA 0x800000
#define DEF_ARG_MASK 0x0fffff
#if 0
#define CALC_DUMPPOS(X) DUMPPOS(X)
#else /* !0 */
#define CALC_DUMPPOS(X)
#endif /* 0 */
static struct pike_string *efun_str;
static struct pike_string *constant_str;
static struct pike_string *defined_str;
/* Some string builder debug. */
#if 0
#define string_builder_putchar(X, Y) do { \
int Y_Y_ = Y; \
fprintf(stderr, "%s:%d string_builder_putchar(%s, %s, '%c')\n", \
__FILE__,__LINE__, #X, #Y, Y_Y_); \
string_builder_putchar(X, Y_Y_); \
} while(0)
#endif /* 0 */
struct pike_predef_s
{
struct pike_predef_s *next;
char *name;
char *value;
};
static int use_initial_predefs;
static struct pike_predef_s *first_predef = NULL, *last_predef = NULL;
struct define_part
{
int argument;
struct pike_string *postfix;
};
struct define_argument {
PCHARP arg;
ptrdiff_t len;
};
struct cpp;
struct define;
typedef void (*magic_define_fun)(struct cpp *,
struct define *,
struct define_argument *,
struct string_builder *);
struct define
{
struct hash_entry link; /* must be first */
magic_define_fun magic;
int args;
ptrdiff_t num_parts;
short inside; /* 1 - Don't expand. 2 - In use. */
short varargs;
struct pike_string *first;
struct define_part parts[1];
};
#define find_define(N) \
(this->defines?BASEOF(hash_lookup(this->defines, N), define, link):0)
struct cpp
{
struct hash_table *defines;
INT32 current_line;
INT32 compile_errors;
struct pike_string *current_file;
struct string_builder buf;
struct object *handler;
struct object *compat_handler;
int compat_major;
int compat_minor;
struct pike_string *data;
int picky_cpp;
};
struct define *defined_macro =0;
static struct pike_string *binary_findstring1(p_wchar1 *str, ptrdiff_t len);
static struct pike_string *binary_findstring2(p_wchar2 *str, ptrdiff_t len);
static void cpp_error(struct cpp *this, const char *err)
{
this->compile_errors++;
if(this->compile_errors > 10) return;
if((this->handler && this->handler->prog) || get_master())
{
ref_push_string(this->current_file);
push_int(this->current_line);
push_text(err);
low_safe_apply_handler("compile_error", this->handler,
this->compat_handler, 3);
pop_stack();
}else{
(void)fprintf(stderr, "%s:%ld: %s\n",
this->current_file->str,
(long)this->current_line,
err);
fflush(stderr);
}
}
static void cpp_error_vsprintf (struct cpp *this, const char *fmt,
va_list args)
{
struct string_builder s;
struct pike_string *msg;
this->compile_errors++;
if (this->compile_errors > 10) return;
init_string_builder(&s, 0);
string_builder_vsprintf(&s, fmt, args);
msg = finish_string_builder(&s);
if((this->handler && this->handler->prog) || get_master())
{
ref_push_string(this->current_file);
push_int(this->current_line);
push_string(msg);
low_safe_apply_handler("compile_error", this->handler,
this->compat_handler, 3);
pop_stack();
return;
}
if (this->current_file->size_shift) {
fprintf(stderr, "WIDE:%ld: ", (long)this->current_line);
} else {
fprintf(stderr, "%s:%ld: ",
this->current_file->str, (long)this->current_line);
}
if (!msg->size_shift) {
fprintf(stderr, "%s\n", msg->str);
} else {
fprintf(stderr, "WIDE (fmt: %s)\n", fmt);
}
free_string(msg);
fflush(stderr);
}
static void cpp_error_sprintf(struct cpp *this, const char *fmt, ...)
{
va_list args;
va_start(args,fmt);
cpp_error_vsprintf (this, fmt, args);
va_end(args);
}
static void cpp_handle_exception(struct cpp *this,
const char *cpp_error_fmt, ...)
{
struct svalue thrown;
move_svalue (&thrown, &throw_value);
mark_free_svalue (&throw_value);
if (cpp_error_fmt) {
va_list args;
va_start (args, cpp_error_fmt);
cpp_error_vsprintf (this, cpp_error_fmt, args);
va_end (args);
}
push_svalue(&thrown);
low_safe_apply_handler("compile_exception",
this->handler, this->compat_handler, 1);
if (SAFE_IS_ZERO(sp-1)) {
struct pike_string *s = format_exception_for_error_msg (&thrown);
if (s) {
cpp_error_sprintf(this, "%S", s);
free_string (s);
}
}
pop_stack();
free_svalue(&thrown);
}
static void cpp_warning(struct cpp *this, const char *cpp_warn_fmt, ...)
{
struct string_builder sb;
va_list args;
init_string_builder (&sb, 0);
va_start(args, cpp_warn_fmt);
string_builder_vsprintf (&sb, cpp_warn_fmt, args);
va_end(args);
if((this->handler && this->handler->prog) || get_master())
{
ref_push_string(this->current_file);
push_int(this->current_line);
push_string (finish_string_builder (&sb));
low_safe_apply_handler("compile_warning", this->handler,
this->compat_handler, 3);
pop_stack();
}else{
(void)fprintf(stderr, "%s:%ld: %s\n",
this->current_file->str,
(long)this->current_line,
sb.s->str);
fflush(stderr);
free_string_builder (&sb);
}
}
/*! @class MasterObject
*/
/*! @decl inherit CompilationHandler
*!
*! The master object acts as fallback compilation handler for
*! @[compile()] and @[cpp()].
*/
/*! @decl CompilationHandler get_compilation_handler(int major, int minor)
*!
*! Get compilation handler for simulation of Pike v@[major].@[minor].
*!
*! This function is called by @[cpp()] when it encounters
*! @expr{#pike@} directives.
*!
*! @param major
*! Major version.
*!
*! @param minor
*! Minor version.
*!
*! @returns
*! Returns a compilation handler for Pike >= @[major].@[minor].
*/
/*! @decl string decode_charset(string raw, string charset)
*!
*! Convert @[raw] from encoding @[charset] to UNICODE.
*!
*! This function is called by @[cpp()] when it encounters
*! @expr{#charset@} directives.
*!
*! @param raw
*! String to convert.
*!
*! @param charset
*! Name of encoding that @[raw] uses.
*!
*! @returns
*! @[raw] decoded to UNICODE, or @expr{0@} (zero) if the decoding failed.
*!
*! @seealso
*! @[Locale.Charset]
*/
/*! @endclass
*/
/*! @class CompilationHandler
*!
*! Objects used by the compiler to handle references to global symbols,
*! modules, external files, etc.
*!
*! There can be up to three compilation handlers active at the same
*! time during a compilation. They are in order of precedence:
*!
*! @ol
*! @item
*! The error handler
*!
*! This is the object passed to @[compile()] as
*! the second argument (if any). This object is returned by
*! @[get_active_error_handler()] during a compilation.
*!
*! @item
*! The compatibility handler
*!
*! This is the object returned by
*! @[master()->get_compilation_handler()] (if any), which
*! the compiler calls when it sees @tt{#pike@}-directives,
*! or expressions using the version scope
*! (eg @expr{7.4::rusage@}). This object is returned by
*! @[get_active_compilation_handler()] during a compilation.
*!
*! @item
*! The master object.
*!
*! This is returned by @[master()] at any time.
*! @endol
*!
*! Any of the objects may implement a subset of the @[CompilationHandler]
*! functions, and the first object that implements a function will be
*! used. The error handler object can thus be used to block certain
*! functionality (eg to restrict the number of available functions).
*!
*! @seealso
*! @[master()->get_compilation_handler()], @[get_active_error_handler()],
*! @[get_active_compilation_handler()], @[compile()]
*/
/*! @decl void compile_error(string filename, int line, string msg)
*!
*! Called by @[compile()] and @[cpp()] when they encounter
*! errors in the code they compile.
*!
*! @param filename
*! File where the error was detected.
*!
*! @param line
*! Line where the error was detected.
*!
*! @param msg
*! Description of error.
*!
*! @seealso
*! @[compile_warning()].
*/
/*! @decl void compile_exception(mixed exception)
*!
*! Called by @[compile()] and @[cpp()] if they trigger
*! exceptions.
*/
/*! @decl mapping(string:mixed) get_predefines()
*!
*! Called by @[cpp()] to get the set of global symbols.
*!
*! @returns
*! Returns a mapping from symbol name to symbol value.
*! Returns zero on failure.
*!
*! @seealso
*! @[resolv()], @[get_default_module()]
*/
/*! @decl mixed resolv(string symbol, string filename, @
*! CompilationHandler handler)
*!
*! Called by @[compile()] and @[cpp()] to resolv
*! module references.
*!
*! @returns
*! Returns the resolved value, or @[UNDEFINED] on failure.
*!
*! @seealso
*! @[get_predefines()]
*/
/*! @decl mixed handle_import(string path, string filename, @
*! CompilationHandler handler)
*!
*! Called by @[compile()] and @[cpp()] to handle import
*! directives specifying specific paths.
*!
*! @returns
*! Returns the resolved value, or @[UNDEFINED] on failure.
*/
/*! @decl string handle_include(string header_file, string current_file, @
*! int(0..1) is_local_ref)
*!
*! Called by @[cpp()] to resolv @expr{#include@} and @expr{#string@}
*! directives.
*!
*! @param header_file
*! File that was requested for inclusion.
*!
*! @param current_file
*! File where the directive was found.
*!
*! @param is_local_ref
*! Specifies reference method.
*! @int
*! @value 0
*! Directive was @expr{#include <header_file>@}.
*! @value 1
*! Directive was @expr{#include "header_file"@}.
*! @endint
*!
*! @returns
*! Returns the filename to pass to @[read_include()] if found,
*! and @expr{0@} (zero) on failure.
*!
*! @seealso
*! @[read_include()]
*/
/*! @decl string read_include(string filename)
*!
*! Called by @[cpp()] to read included files.
*!
*! @param filename
*! Filename as returned by @[handle_include()].
*!
*! @returns
*! Returns a string with the content of the header file on success,
*! and @expr{0@} (zero) on failure.
*!
*! @seealso
*! @[handle_include()]
*/
/*! @endclass
*/
/* #pike handling. */
void cpp_change_compat(struct cpp *this, int major, int minor)
{
if(this->compat_major == major &&
this->compat_minor == minor) return;
if(this->compat_handler)
{
free_object(this->compat_handler);
this->compat_handler=0;
}
if((major == PIKE_MAJOR_VERSION &&
minor == PIKE_MINOR_VERSION) || major < 0)
{
this->compat_major=PIKE_MAJOR_VERSION;
this->compat_minor=PIKE_MINOR_VERSION;
return; /* Our work here is done */
}
push_int(major);
push_int(minor);
SAFE_APPLY_MASTER("get_compilation_handler",2);
if(TYPEOF(sp[-1]) == T_OBJECT)
{
if (SUBTYPEOF(sp[-1])) {
cpp_error(this,
"#pike: Subtyped compilation handlers are not supported yet.");
}
this->compat_handler=sp[-1].u.object;
dmalloc_touch_svalue(Pike_sp-1);
sp--;
}
this->compat_major=major;
this->compat_minor=minor;
}
/* #if macros and functions. */
static void check_defined(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
struct pike_string *s = NULL;
switch(args[0].arg.shift) {
case 0:
s=binary_findstring((char *)args[0].arg.ptr, args[0].len);
break;
case 1:
s = binary_findstring1((p_wchar1 *)args[0].arg.ptr, args[0].len);
break;
case 2:
s = binary_findstring2((p_wchar2 *)args[0].arg.ptr, args[0].len);
break;
#ifdef PIKE_DEBUG
default:
Pike_fatal("cpp(): Symbol has unsupported shift: %d\n", args[0].arg.shift);
break;
#endif
}
if(s && find_define(s))
{
string_builder_binary_strcat(tmp, " 1 ", 3);
}else{
string_builder_binary_strcat(tmp, " 0 ", 3);
}
}
static int do_safe_index_call(struct cpp *this, struct pike_string *s)
{
int res;
JMP_BUF recovery;
if(!s) return 0;
if (SETJMP_SP(recovery, 1)) {
if (CPP_TEST_COMPAT (this, 7, 4)) {
free_svalue (&throw_value);
mark_free_svalue (&throw_value);
}
else if(this->picky_cpp) {
cpp_warning (this, "Error indexing module with %S.", s);
}
res = 0;
push_undefined();
} else {
ref_push_string(s);
f_index(2);
res=!(UNSAFE_IS_ZERO(sp-1) && SUBTYPEOF(sp[-1]) == NUMBER_UNDEFINED);
}
UNSETJMP(recovery);
return res;
}
static void cpp_low_constant(struct cpp *this, int value)
{
struct svalue *save_stack=sp;
struct array *arr;
INT_TYPE res = 0;
int n;
/* FIXME: Protection against errors. */
/* Remove extra whitespace. */
push_constant_text(" ");
o_subtract();
push_constant_text("\t");
o_subtract();
/* Split on . */
push_constant_text(".");
o_divide();
#ifdef PIKE_DEBUG
if (TYPEOF(Pike_sp[-1]) != T_ARRAY) {
Pike_fatal("Bad result from division in constant(): %s "
"(expected array(string)).\n",
get_name_of_type(TYPEOF(Pike_sp[-1])));
}
#endif /* PIKE_DEBUG */
arr = Pike_sp[-1].u.array;
#ifdef PIKE_DEBUG
if (!arr->size) {
Pike_fatal("Got an empty array from division in constant().\n");
}
if ((arr->type_field & ~BIT_STRING) &&
(array_fix_type_field(arr) & ~BIT_STRING)) {
Pike_fatal("Bad result from division in constant(): type_field: 0x%08x "
"(expected array(string)).\n",
arr->type_field & ~BIT_STRING);
}
#endif /* PIKE_DEBUG */
if (arr->item[0].u.string->len) {
struct pike_string *str = arr->item[0].u.string;
struct svalue *sv;
if((sv=low_mapping_string_lookup(get_builtin_constants(), str)))
{
/* efun */
push_svalue(sv);
res=1;
} else if(get_master()) {
/* Module. */
ref_push_string(str);
ref_push_string(this->current_file);
if (this->handler) {
ref_push_object(this->handler);
} else {
push_int(0);
}
if (safe_apply_handler("resolv", this->handler,
this->compat_handler, 3, 0)) {
if ((TYPEOF(Pike_sp[-1]) == T_OBJECT &&
Pike_sp[-1].u.object == placeholder_object) ||
(TYPEOF(Pike_sp[-1]) == T_PROGRAM &&
Pike_sp[-1].u.program == placeholder_program)) {
cpp_error_sprintf (this, "Got placeholder %s (resolver problem) "
"when resolving %S.",
get_name_of_type(TYPEOF(Pike_sp[-1])),
str);
}
else
res = !(SAFE_IS_ZERO(sp-1) && SUBTYPEOF(sp[-1]) == NUMBER_UNDEFINED);
}
else if (TYPEOF(throw_value) == T_STRING &&
!throw_value.u.string->size_shift) {
cpp_error(this, throw_value.u.string->str);
free_svalue(&throw_value);
mark_free_svalue (&throw_value);
res = 0;
} else if(this->picky_cpp) {
cpp_warning (this, "Error resolving %S.", str);
res = 0;
}
}
} else {
/* Handle constant(.foo) */
push_constant_text(".");
ref_push_string(this->current_file);
if (this->handler) {
ref_push_object(this->handler);
} else {
push_int(0);
}
if (safe_apply_handler("handle_import", this->handler,
this->compat_handler, 3,
BIT_MAPPING|BIT_OBJECT|BIT_PROGRAM))
res = !(SAFE_IS_ZERO(sp-1) && SUBTYPEOF(sp[-1]) == NUMBER_UNDEFINED);
else {
cpp_handle_exception (this, "Error importing '.'.");
}
}
for (n = 1; res && (n < arr->size); n++) {
res = do_safe_index_call(this, arr->item[n].u.string);
}
if (value && res) {
if (TYPEOF(sp[-1]) == T_INT)
res = sp[-1].u.integer;
else
res = 0;
}
pop_n_elems(1 + sp - save_stack);
push_int(res);
}
void cpp_func_constant(struct cpp *this, INT32 args)
{
if (args != 1) {
cpp_error(this, "Bad number of arguments to constant().");
pop_n_elems(args);
push_int(0);
return;
}
#ifdef PIKE_DEBUG
if (TYPEOF(Pike_sp[-1]) != T_STRING) {
Pike_fatal("Bad argument 1 to constant(): %s (expected string).\n",
get_name_of_type(TYPEOF(Pike_sp[-1])));
}
#endif /* PIKE_DEBUG */
cpp_low_constant(this, 0);
}
void cpp_resolv_constant(struct cpp *this, struct pike_string *identifier)
{
ref_push_string (identifier);
cpp_low_constant(this, 1);
}
/* Macro handling. */
static struct mapping *initial_predefs_mapping(void)
{
struct pike_predef_s *def;
struct mapping *map = allocate_mapping (0);
#ifdef PIKE_DEBUG
if (!use_initial_predefs) Pike_fatal ("Initial predefs has been taken over.\n");
#endif
for (def = first_predef; def; def = def->next) {
push_text (def->name);
push_text (def->value);
mapping_insert (map, sp - 2, sp - 1);
pop_n_elems (2);
}
return map;
}
/* devours one reference to 'name'! */
static struct define *alloc_empty_define(struct pike_string *name,
ptrdiff_t parts)
{
struct define *def;
def=(struct define *)xalloc(sizeof(struct define)+
sizeof(struct define_part) * (parts -1));
def->magic=0;
def->args=-1;
def->inside=0;
def->varargs=0;
def->num_parts=parts;
def->first=0;
def->link.s=name;
debug_malloc_touch(name);
return def;
}
static void undefine(struct cpp *this,
struct pike_string *name)
{
INT32 e;
struct define *d;
d=find_define(name);
if(!d) return;
if (d->inside) {
cpp_error(this, "Illegal to undefine a macro during its expansion.");
return;
}
this->defines=hash_unlink(this->defines, & d->link);
for(e=0;e<d->num_parts;e++)
free_string(d->parts[e].postfix);
free_string(d->link.s);
if(d->first)
free_string(d->first);
free((char *)d);
}
static void do_magic_define(struct cpp *this,
char *name,
magic_define_fun fun)
{
struct define* def=alloc_empty_define(make_shared_string(name),0);
def->magic=fun;
this->defines=hash_insert(this->defines, & def->link);
}
static void add_define(struct cpp *this,
struct pike_string *name,
struct pike_string *what)
{
struct define* def;
add_ref (name);
def=alloc_empty_define(name,0);
add_ref (def->first = what);
this->defines=hash_insert(this->defines, & def->link);
}
static void simple_add_define(struct cpp *this,
char *name,
char *what)
{
struct define* def=alloc_empty_define(make_shared_string(name),0);
def->first=make_shared_string(what);
this->defines=hash_insert(this->defines, & def->link);
}
/* Who needs inline functions anyway? /Hubbe */
#define FIND_END_OF_STRING() do { \
while(1) \
{ \
if(pos>=len) \
{ \
cpp_error(this,"End of file in string."); \
break; \
} \
switch(data[pos++]) \
{ \
case '\n': \
cpp_error(this,"Newline in string."); \
this->current_line++; \
PUTNL(); \
break; \
case '"': break; \
case '\\': \
if(data[pos]=='\n') { \
this->current_line++; \
PUTNL(); \
} \
else if ((data[pos] == '\r') && (data[pos+1] == '\n')) { \
this->current_line++; \
pos++; \
PUTNL(); \
} \
pos++; \
default: continue; \
} \
break; \
} } while(0)
#define FIND_END_OF_STRING2() do { \
while(1) \
{ \
if(pos>=len) \
{ \
cpp_error(this,"End of file in string."); \
break; \
} \
switch(data[pos++]) \
{ \
case '\n': \
this->current_line++; \
PUTNL(); \
continue; \
case '"': break; \
case '\\': \
if(data[pos]=='\n') { \
this->current_line++; \
PUTNL(); \
} \
else if ((data[pos] == '\r') && (data[pos+1] == '\n')) { \
this->current_line++; \
pos++; \
PUTNL(); \
} \
pos++; \
default: continue; \
} \
break; \
} } while(0)
#define FIND_END_OF_CHAR() do { \
int e=0; \
while(1) \
{ \
if(pos>=len) \
{ \
cpp_error(this,"End of file in character constant."); \
break; \
} \
\
if(e++>32) \
{ \
cpp_error(this,"Too long character constant."); \
break; \
} \
\
switch(data[pos++]) \
{ \
case '\n': \
cpp_error(this,"Newline in char."); \
this->current_line++; \
PUTNL(); \
break; \
case '\'': break; \
case '\\': \
if(data[pos]=='\n') { \
this->current_line++; \
PUTNL(); \
} \
else if ((data[pos] == '\r') && (data[pos+1] == '\n')) { \
this->current_line++; \
pos++; \
PUTNL(); \
} \
pos++; \
default: continue; \
} \
break; \
} } while(0)
#define DUMPPOS(X) \
fprintf(stderr,"\nSHIFT:%d, POS(%s):",SHIFT,X); \
fflush(stderr); \
write(2,data+pos,20<<SHIFT); \
fprintf(stderr,"\n"); \
fflush(stderr)
#define FIND_EOL() do { \
while(pos < len && data[pos]!='\n') pos++; \
if (data[pos] == '\\') { \
if (data[pos+1] == '\n') { \
pos+=2; \
} else if ((data[pos+1] == '\r') && \
(data[pos+2] == '\n')) { \
pos+=3; \
} else { \
break; \
} \
} else { \
break; \
} \
PUTNL(); \
this->current_line++; \
} while (1)
/* Skips horizontal whitespace and newlines. */
#define SKIPWHITE() do { \
if(!WC_ISSPACE(data[pos])) { \
if (data[pos] == '\\') { \
if (data[pos+1] == '\n') { \
pos += 2; \
PUTNL(); \
this->current_line++; \
continue; \
} else if ((data[pos+1] == '\r') && \
(data[pos+2] == '\n')) { \
pos += 3; \
PUTNL(); \
this->current_line++; \
continue; \
} \
} \
break; \
} \
if(data[pos]=='\n') { PUTNL(); this->current_line++; } \
pos++; \
} while(1)
/* Skips horizontal whitespace and escaped newlines. */
#define SKIPSPACE() \
do { \
while (WC_ISSPACE(data[pos]) && data[pos]!='\n') { \
pos++; \
} \
if (data[pos] == '\\') { \
if (data[pos+1] == '\n') { \
pos+=2; \
} else if ((data[pos+1] == '\r') && \
(data[pos+2] == '\n')) { \
pos+=3; \
} else { \
break; \
} \
} else { \
break; \
} \
PUTNL(); \
this->current_line++; \
} while (1)
/* Skips horizontal whitespace and escaped newlines,
* does not touch buffer. */
#define SKIPSPACE_PRETEND() \
do { \
while (WC_ISSPACE(data[pos]) && data[pos]!='\n') { \
pos++; \
} \
if (data[pos] == '\\') { \
if (data[pos+1] == '\n') { \
pos+=2; \
} else if ((data[pos+1] == '\r') && \
(data[pos+2] == '\n')) { \
pos+=3; \
} else { \
break; \
} \
} else { \
break; \
} \
} while (1)
/* The current char is assumed to be '*', the previous '/'. */
#define SKIPCOMMENT() do{ \
pos++; \
while(data[pos]!='*' || data[pos+1]!='/') \
{ \
if(pos+2>=len) \
{ \
cpp_error(this,"End of file in comment."); \
break; \
} \
\
if(data[pos]=='\n') \
{ \
this->current_line++; \
PUTNL(); \
} \
\
pos++; \
} \
pos+=2; \
}while(0)
/* pos is assumed to be at the backslash. pos it at the last char in
* the escape afterwards. */
#define READCHAR(C) do { \
ptrdiff_t l; \
switch (parse_esc_seq (data + pos + 1, &C, &l)) { \
case 0: \
pos += l; \
break; \
case 1: \
C = '\r'; \
pos++; \
break; \
case 3: \
/* The eof will get caught in the next round. */ \
C = 0; \
pos++; \
break; \
case 4: case 5: case 6: \
cpp_warning (this, "Too large character value in escape."); \
C = (int) MAX_UINT32; \
pos += l; \
break; \
case 7: \
cpp_warning (this, "Too few hex digits in \\u escape."); \
C = '\\'; \
break; \
case 8: \
cpp_warning (this, "Too few hex digits in \\U escape."); \
C = '\\'; \
break; \
DO_IF_DEBUG ( \
case 2: Pike_fatal ("Not supposed to happen.\n"); \
default: Pike_fatal ("Unknown error from parse_esc_seq.\n"); \
); \
} \
} while (0)
/* At entry pos points to the start-quote.
* At end pos points past the end-quote.
*/
#define READSTRING(nf) \
while(1) \
{ \
pos++; \
if(pos>=len) \
{ \
cpp_error(this,"End of file in string."); \
break; \
} \
\
switch(data[pos]) \
{ \
case '\n': \
cpp_error(this,"Newline in string."); \
this->current_line++; \
break; \
case '"': break; \
case '\\': \
{ \
p_wchar2 tmp; \
if(data[pos+1]=='\n') \
{ \
pos++; \
this->current_line++; \
continue; \
} \
if(data[pos+1]=='\r' && data[pos+2]=='\n') \
{ \
pos+=2; \
this->current_line++; \
continue; \
} \
READCHAR(tmp); \
string_builder_putchar(&nf, tmp); \
continue; \
} \
\
default: \
string_builder_putchar(&nf, data[pos]); \
continue; \
} \
pos++; \
break; \
}
/* At entry pos points past the start quote.
* At exit pos points past the end quote.
*/
#define FIXSTRING(nf,outp) do { \
int trailing_newlines=0; \
if(outp) string_builder_putchar(&nf, '"'); \
while(1) \
{ \
if(pos>=len) \
{ \
cpp_error(this,"End of file in string."); \
break; \
} \
\
switch(data[pos++]) \
{ \
case '\n': \
cpp_error(this,"Newline in string."); \
this->current_line++; \
break; \
case '"': break; \
case '\\': \
if(data[pos]=='\n') \
{ \
pos++; \
trailing_newlines++; \
this->current_line++; \
continue; \
} \
if(data[pos]=='\r' && data[pos+1]=='\n') \
{ \
pos+=2; \
trailing_newlines++; \
this->current_line++; \
continue; \
} \
if(outp) string_builder_putchar(&nf, '\\'); \
pos++; \
/* Fall through. */ \
\
default: \
if(outp) string_builder_putchar(&nf, data[pos-1]); \
continue; \
} \
break; \
} \
if(outp) string_builder_putchar(&nf, '"'); \
while(trailing_newlines--) PUTNL(); \
}while(0)
#define READSTRING2(nf) \
while(1) \
{ \
pos++; \
if(pos>=len) \
{ \
cpp_error(this,"End of file in string."); \
break; \
} \
\
switch(data[pos]) \
{ \
case '"': break; \
case '\\': \
{ \
p_wchar2 tmp; \
if(data[pos+1]=='\n') \
{ \
pos++; \
this->current_line++; \
PUTNL(); \
continue; \
} \
if(data[pos+1]=='\r' && data[pos+2]=='\n') \
{ \
pos+=2; \
this->current_line++; \
PUTNL(); \
continue; \
} \
READCHAR(tmp); \
string_builder_putchar(&nf, tmp); \
continue; \
} \
\
case '\r': continue; /* ignored */ \
case '\n': \
PUTNL(); \
this->current_line++; \
/* Fall through. */ \
default: \
string_builder_putchar(&nf, data[pos]); \
continue; \
} \
pos++; \
break; \
}
static struct pike_string *recode_string(struct cpp *this, struct pike_string *data)
{
/* Observations:
*
* * At least a prefix of two bytes need to be 7bit in a valid
* Pike program.
*
* * NUL isn't valid in a Pike program.
*/
/* Heuristic:
*
* Index 0 | Index 1 | Interpretation
* --------+---------+------------------------------------------
* 0 | 0 | 32bit wide string.
* 0 | >0 | 16bit Unicode string.
* >0 | 0 | 16bit Unicode string reverse byte order.
* 0xfe | 0xff | 16bit Unicode string.
* 0xff | 0xfe | 16bit Unicode string reverse byte order.
* 0x7b | 0x83 | EBCDIC-US ("#c").
* 0x7b | 0x40 | EBCDIC-US ("# ").
* 0x7b | 0x09 | EBCDIC-US ("#\t").
* --------+---------+------------------------------------------
* Other | Other | 8bit standard string.
*
* Note that the tests below are more lenient than the table above.
* This shouldn't matter, since the other cases would be erroneus
* anyway.
*/
/* Add an extra reference to data, since we may return it as is. */
add_ref(data);
if ((!((unsigned char *)data->str)[0]) ||
(((unsigned char *)data->str)[0] == 0xfe) ||
(((unsigned char *)data->str)[0] == 0xff) ||
(!((unsigned char *)data->str)[1])) {
/* Unicode */
if ((!((unsigned char *)data->str)[0]) &&
(!((unsigned char *)data->str)[1])) {
/* 32bit Unicode (UCS4) */
struct pike_string *new_str;
ptrdiff_t len;
ptrdiff_t i;
ptrdiff_t j;
p_wchar0 *orig = STR0(data);
p_wchar2 *dest;
if (data->len & 3) {
/* String len is not a multiple of 4 */
return data;
}
len = data->len/4;
new_str = begin_wide_shared_string(len, 2);
dest = STR2(new_str);
j = 0;
for(i=0; i<len; i++) {
dest[i] = (orig[j]<<24) | (orig[j+1]<<16) | (orig[j+2]<<8) | orig[j+3];
j += 4;
}
free_string(data);
return(end_shared_string(new_str));
} else {
/* 16bit Unicode (UCS2) */
if (data->len & 1) {
/* String len is not a multiple of 2 */
return data;
}
if ((!((unsigned char *)data->str)[1]) ||
(((unsigned char *)data->str)[1] == 0xfe)) {
/* Reverse Byte-order */
struct pike_string *new_str = begin_shared_string(data->len);
int i;
for(i=0; i<data->len; i++) {
new_str->str[i^1] = data->str[i];
}
free_string(data);
data = end_shared_string(new_str);
}
/* Note: We lose the extra reference to data here. */
push_string(data);
f_unicode_to_string(1);
add_ref(data = sp[-1].u.string);
pop_stack();
return data;
}
} else if (data->str[0] == '{') {
/* EBCDIC */
/* Notes on EBCDIC:
*
* * EBCDIC conversion needs to first convert the first line
* according to EBCDIC-US, and then the rest of the string
* according to the encoding specified by the first line.
*
* * It's an error for a program written in EBCDIC not to
* start with a #charset directive.
*
* Obfuscation note:
*
* * This still allows the rest of the file to be written in
* another encoding than EBCDIC.
*/
/* First split out the first line.
*
* Note that codes 0x00 - 0x1f are the same in ASCII and EBCDIC.
*/
struct pike_string *new_str;
char *p = strchr(data->str, '\n');
char *p2;
size_t len;
if (!p) {
return data;
}
len = p - data->str;
if (len < CONSTANT_STRLEN("#charset ")) {
return data;
}
new_str = begin_shared_string(len);
MEMCPY(new_str->str, data->str, len);
push_string(end_shared_string(new_str));
push_constant_text("ebcdic-us");
if (safe_apply_handler ("decode_charset", this->handler, this->compat_handler,
2, BIT_STRING)) {
/* Various consistency checks. */
if ((sp[-1].u.string->size_shift) ||
(((size_t)sp[-1].u.string->len) < CONSTANT_STRLEN("#charset")) ||
(sp[-1].u.string->str[0] != '#')) {
pop_stack();
return data;
}
}
else {
cpp_handle_exception (this, "Error decoding with charset 'ebcdic-us'");
return data;
}
/* At this point the decoded first line is on the stack. */
/* Extract the charset name */
p = sp[-1].u.string->str + 1;
while (*p && isspace(*((unsigned char *)p))) {
p++;
}
if (strncmp(p, "charset", CONSTANT_STRLEN("charset")) ||
!isspace(((unsigned char *)p)[CONSTANT_STRLEN("charset")])) {
pop_stack();
return data;
}
p += CONSTANT_STRLEN("charset") + 1;
while (*p && isspace(*((unsigned char *)p))) {
p++;
}
if (!*p) {
pop_stack();
return data;
}
/* Build a string of the trailing data
* NOTE:
* Keep the newline, so the linenumber info stays correct.
*/
new_str = begin_shared_string(data->len - len);
MEMCPY(new_str->str, data->str + len, data->len - len);
push_string(end_shared_string(new_str));
stack_swap();
/* Build a string of the charset name */
p2 = p;
while(*p2 && !isspace(*((unsigned char *)p2))) {
p2++;
}
len = p2 - p;
new_str = begin_shared_string(len);
MEMCPY(new_str->str, p, len);
pop_stack();
ref_push_string(new_str = end_shared_string(new_str));
/* Decode the string */
if (!safe_apply_handler ("decode_charset", this->handler, this->compat_handler,
2, BIT_STRING)) {
cpp_handle_exception (this, "Error decoding with charset %S", new_str);
free_string (new_str);
return data;
}
free_string (new_str);
/* Accept the new string */
free_string(data);
add_ref(data = sp[-1].u.string);
pop_stack();
}
return data;
}
static struct pike_string *filter_bom(struct pike_string *data)
{
/* More notes:
*
* * Character 0xfeff (ZERO WIDTH NO-BREAK SPACE = BYTE ORDER MARK = BOM)
* needs to be filtered away before processing continues.
*/
ptrdiff_t i;
ptrdiff_t j = 0;
ptrdiff_t len = data->len;
struct string_builder buf;
/* Add an extra reference to data here, since we may return it as is. */
add_ref(data);
if (!data->size_shift) {
return(data);
}
init_string_builder(&buf, data->size_shift);
if (data->size_shift == 1) {
/* 16 bit string */
p_wchar1 *ptr = STR1(data);
for(i = 0; i<len; i++) {
if (ptr[i] == 0xfeff) {
if (i != j) {
string_builder_binary_strcat1 (&buf, ptr + j, i - j);
j = i+1;
}
}
}
if ((j) && (i != j)) {
/* Add the trailing string */
string_builder_binary_strcat1 (&buf, ptr + j, i - j);
free_string(data);
data = finish_string_builder(&buf);
} else {
/* String didn't contain 0xfeff */
free_string_builder(&buf);
}
} else {
/* 32 bit string */
p_wchar2 *ptr = STR2(data);
for(i = 0; i<len; i++) {
if (ptr[i] == 0xfeff) {
if (i != j) {
string_builder_binary_strcat2 (&buf, ptr + j, i - j);
j = i+1;
}
}
}
if ((j) && (i != j)) {
/* Add the trailing string */
string_builder_binary_strcat2 (&buf, ptr + j, i - j);
free_string(data);
data = finish_string_builder(&buf);
} else {
/* String didn't contain 0xfeff */
free_string_builder(&buf);
}
}
return(data);
}
void free_one_define(struct hash_entry *h)
{
int e;
struct define *d=BASEOF(h, define, link);
for(e=0;e<d->num_parts;e++)
free_string(d->parts[e].postfix);
if(d->first)
free_string(d->first);
free((char *)d);
}
static ptrdiff_t low_cpp(struct cpp *this, void *data, ptrdiff_t len,
int shift, int flags, int auto_convert,
struct pike_string *charset);
#define SHIFT 0
#include "preprocessor.h"
#undef SHIFT
#define SHIFT 1
#include "preprocessor.h"
#undef SHIFT
#define SHIFT 2
#include "preprocessor.h"
#undef SHIFT
static ptrdiff_t low_cpp(struct cpp *this, void *data, ptrdiff_t len,
int shift, int flags, int auto_convert,
struct pike_string *charset)
{
switch(shift) {
case 0:
return lower_cpp0(this, (p_wchar0 *)data, len,
flags, auto_convert, charset);
case 1:
return lower_cpp1(this, (p_wchar1 *)data, len,
flags, auto_convert, charset);
case 2:
return lower_cpp2(this, (p_wchar2 *)data, len,
flags, auto_convert, charset);
#ifdef PIKE_DEBUG
default:
Pike_fatal("low_cpp(): Bad shift: %d\n", shift);
#endif
}
/* NOT_REACHED */
return 0;
}
/*** Magic defines ***/
static void insert_current_line(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
string_builder_sprintf(tmp, " %ld ", (long)this->current_line);
}
static void insert_current_file_as_string(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
PUSH_STRING_SHIFT(this->current_file->str, this->current_file->len,
this->current_file->size_shift, tmp);
}
static void insert_current_dir_as_string(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
ref_push_string(this->current_file);
/* FIXME: This isn't safe if the master hasn't been compiled yet. */
SAFE_APPLY_MASTER("dirname",1);
PUSH_STRING_SHIFT(Pike_sp[-1].u.string->str, Pike_sp[-1].u.string->len,
Pike_sp[-1].u.string->size_shift, tmp);
pop_stack();
}
static void insert_current_time_as_string(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
/* FIXME: Is this code safe? */
time_t tmp2;
char *buf;
time(&tmp2);
buf=ctime(&tmp2);
PUSH_STRING0((p_wchar0 *)buf+11, 8, tmp);
}
static void insert_current_date_as_string(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
/* FIXME: Is this code safe? */
time_t tmp2;
char *buf;
time(&tmp2);
buf=ctime(&tmp2);
PUSH_STRING0((p_wchar0 *)buf+4, 6, tmp);
PUSH_STRING0((p_wchar0 *)buf+19, 5, tmp);
}
static void insert_current_version(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
string_builder_sprintf(tmp, " %d.%d ", this->compat_major,
this->compat_minor);
}
static void insert_current_minor(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
string_builder_sprintf(tmp, " %d ", this->compat_minor);
}
static void insert_current_major(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
string_builder_sprintf(tmp, " %d ", this->compat_major);
}
static void insert_callback_define(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
ref_push_string( def->link.s );
push_string( make_shared_binary_pcharp( args[0].arg, args[0].len ) );
safe_apply_handler( "evaluate_define", this->handler, this->compat_handler, 2, 0 );
if( TYPEOF(sp[-1]) == T_STRING )
string_builder_shared_strcat(tmp, sp[-1].u.string);
pop_stack();
}
static void insert_callback_define_no_args(struct cpp *this,
struct define *def,
struct define_argument *args,
struct string_builder *tmp)
{
ref_push_string( def->link.s );
safe_apply_handler( "evaluate_define", this->handler, this->compat_handler, 1, 0 );
if( TYPEOF(sp[-1]) == T_STRING )
string_builder_shared_strcat(tmp, sp[-1].u.string);
pop_stack();
}
/*! @namespace predef:: */
/*! @decl import cpp:: */
/*! @endnamespace */
/*! @namespace cpp:: */
/*! @decl constant __VERSION__
*!
*! This define contains the current Pike version as a float. If
*! another Pike version is emulated, this define is updated
*! accordingly.
*!
*! @seealso
*! @[__REAL_VERSION__]
*/
/*! @decl constant __REAL_VERSION__
*!
*! This define always contains the version of the current Pike,
*! represented as a float.
*!
*! @seealso
*! @[__VERSION__]
*/
/*! @decl constant __MAJOR__
*!
*! This define contains the major part of the current Pike version,
*! represented as an integer. If another Pike version is emulated,
*! this define is updated accordingly.
*!
*! @seealso
*! @[__REAL_MAJOR__]
*/
/*! @decl constant __REAL_MAJOR__
*!
*! This define always contains the major part of the version of the
*! current Pike, represented as an integer.
*!
*! @seealso
*! @[__MAJOR__]
*/
/*! @decl constant __MINOR__
*! This define contains the minor part of the current Pike version,
*! represented as an integer. If another Pike version is emulated,
*! this define is updated accordingly.
*!
*! @seealso
*! @[__REAL_MINOR__]
*/
/*! @decl constant __REAL_MINOR__
*!
*! This define always contains the minor part of the version of the
*! current Pike, represented as an integer.
*!
*! @seealso
*! @[__MINOR__]
*/
/*! @decl constant __BUILD__
*! This constant contains the build number of the current Pike version,
*! represented as an integer. If another Pike version is emulated,
*! this constant remains unaltered.
*!
*! @seealso
*! @[__REAL_MINOR__]
*/
/*! @decl constant __REAL_BUILD__
*!
*! This define always contains the minor part of the version of the
*! current Pike, represented as an integer.
*!
*! @seealso
*! @[__BUILD__]
*/
/*! @decl constant __LINE__
*!
*! This define contains the current line number, represented as an
*! integer, in the source file.
*/
/*! @decl constant __FILE__
*!
*! This define contains the file path and name of the source file.
*/
/*! @decl constant __DIR__
*!
*! This define contains the directory path of the source file.
*/
/*! @decl constant __DATE__
*!
*! This define contains the current date at the time of compilation,
*! e.g. "Jul 28 2001".
*/
/*! @decl constant __TIME__
*!
*! This define contains the current time at the time of compilation,
*! e.g. "12:20:51".
*/
/*! @decl constant __PIKE__
*!
*! This define is always true.
*/
/*! @decl constant __AUTO_BIGNUM__
*!
*! This define is defined when automatic bignum conversion is enabled.
*! When enabled all integers will automatically be converted to
*! bignums when they get bigger than what can be represented by
*! an integer, hampering performance slightly instead of crashing
*! the program.
*/
/*! @decl constant __NT__
*!
*! This define is defined when the Pike is running on a Microsoft Windows OS,
*! not just Microsoft Windows NT, as the name implies.
*/
/*! @decl constant __amigaos__
*!
*! This define is defined when the Pike is running on Amiga OS.
*/
/*! @endnamespace */
/*! @decl string cpp(string data, string|void current_file, @
*! int|string|void charset, object|void handler, @
*! void|int compat_major, void|int compat_minor, @
*! void|int picky_cpp)
*!
*! Run a string through the preprocessor.
*!
*! Preprocesses the string @[data] with Pike's builtin ANSI-C look-alike
*! preprocessor. If the @[current_file] argument has not been specified,
*! it will default to @expr{"-"@}. @[charset] defaults to @expr{"ISO-10646"@}.
*!
*! @seealso
*! @[compile()]
*/
/* Doesn't free string_builder buf! */
static void free_cpp(struct cpp *this)
{
if(this->defines)
free_hashtable(this->defines, free_one_define);
if(this->current_file)
free_string(this->current_file);
if(this->handler) {
free_object(this->handler);
this->handler = 0;
}
if(this->compat_handler) {
free_object(this->compat_handler);
this->compat_handler=0;
}
if(this->data)
free_string(this->data);
}
void f_cpp(INT32 args)
{
struct cpp this;
struct svalue *save_sp = sp - args;
struct mapping *predefs = NULL;
struct pike_string *data;
struct pike_string *current_file = 0;
struct svalue *charset_sv = 0;
int auto_convert = 0;
struct pike_string *charset = NULL;
struct object *handler = 0;
int compat_major = 0, compat_minor = 0, picky_cpp = 0;
ONERROR err;
#ifdef PIKE_DEBUG
ONERROR tmp;
#endif /* PIKE_DEBUG */
get_all_args("cpp", args, "%t.%T%*%O%d%d%d", &data, ¤t_file,
&charset_sv, &handler, &compat_major, &compat_minor,
&picky_cpp);
this.current_line=1;
this.compile_errors=0;
this.defines=0;
this.data = data;
add_ref(data);
if(current_file)
add_ref(current_file);
else
current_file = make_shared_string("-");
this.current_file = current_file;
this.compat_major=PIKE_MAJOR_VERSION;
this.compat_minor=PIKE_MINOR_VERSION;
this.compat_handler = 0;
this.handler = handler;
if(handler)
add_ref(handler);
/* Don't call free_cpp before all variables are cleared or set. */
SET_ONERROR(err, free_cpp, &this);
if(charset_sv) {
if(TYPEOF(*charset_sv) == T_STRING) {
charset = charset_sv->u.string;
push_string(data);
ref_push_string(charset);
if (!safe_apply_handler ("decode_charset", this.handler,
this.compat_handler, 2, BIT_STRING)) {
cpp_handle_exception (&this, "Error decoding with charset %S",
charset);
Pike_error("Unknown charset.\n");
}
free(data);
this.data = data = sp[-1].u.string;
sp--;
dmalloc_touch_svalue(sp);
}
else if(TYPEOF(*charset_sv) == T_INT)
auto_convert = charset_sv->u.integer;
else {
SIMPLE_BAD_ARG_ERROR("cpp", 3, "string|int");
}
}
if(compat_major)
cpp_change_compat(&this, compat_major, compat_minor);
this.picky_cpp = picky_cpp;
if (use_initial_predefs)
/* Typically compiling the master here. */
predefs = initial_predefs_mapping();
else {
low_unsafe_apply_handler ("get_predefines", this.handler,
this.compat_handler, 0);
if (!UNSAFE_IS_ZERO (sp - 1)) {
struct keypair *k;
int e, sprintf_args = 0;
if (TYPEOF(sp[-1]) != T_MAPPING) {
push_constant_text ("Invalid return value from get_predefines\n");
push_constant_text ("Invalid return value from get_predefines, got %O\n");
push_svalue (sp - 3);
sprintf_args = 2;
}
else {
predefs = copy_mapping (sp[-1].u.mapping);
NEW_MAPPING_LOOP (predefs->data) {
if (TYPEOF(k->ind) != T_STRING || !k->ind.u.string->len) {
push_constant_text ("Expected nonempty string as predefine name\n");
push_constant_text ("Expected nonempty string as predefine name, got %O\n");
push_svalue (&k->ind);
sprintf_args = 2;
free_mapping (predefs);
predefs = NULL;
goto predef_map_error;
}
}
}
if (!predefs) {
predef_map_error:
f_sprintf (sprintf_args);
Pike_error("%S", sp[-1].u.string);
}
}
pop_stack();
}
if (auto_convert && (!data->size_shift) && (data->len > 1)) {
/* Try to determine if we need to recode the string */
struct pike_string *new_data = recode_string(&this, data);
free_string(data);
this.data = data = new_data;
}
if (data->size_shift) {
/* Get rid of any byte order marks (0xfeff) */
struct pike_string *new_data = filter_bom(data);
free_string(data);
this.data = data = new_data;
}
init_string_builder(&this.buf, 0);
do_magic_define(&this,"__LINE__",insert_current_line);
do_magic_define(&this,"__FILE__",insert_current_file_as_string);
do_magic_define(&this,"__DIR__",insert_current_dir_as_string);
do_magic_define(&this,"__DATE__",insert_current_date_as_string);
do_magic_define(&this,"__TIME__",insert_current_time_as_string);
do_magic_define(&this,"__VERSION__",insert_current_version);
do_magic_define(&this,"__MAJOR__",insert_current_major);
do_magic_define(&this,"__MINOR__",insert_current_minor);
{
#if 0
/* Left in place for documentation reference purposes. */
struct define *def =
alloc_empty_define(make_shared_string("__deprecated__"), 1);
def->args = 1;
REF_MAKE_CONST_STRING(def->first, "__attribute__(\"deprecated\", ");
def->parts[0].argument = 0;
REF_MAKE_CONST_STRING(def->parts[0].postfix, ")");
this.defines = hash_insert(this.defines, &def->link);
#endif /* 0 */
simple_add_define(&this, "__PIKE__", " 1 ");
simple_add_define(&this, "__REAL_VERSION__",
" " DEFINETOSTR(PIKE_MAJOR_VERSION) "."
DEFINETOSTR(PIKE_MINOR_VERSION) " ");
simple_add_define(&this, "__REAL_MAJOR__",
" " DEFINETOSTR(PIKE_MAJOR_VERSION) " ");
simple_add_define(&this, "__REAL_MINOR__",
" " DEFINETOSTR(PIKE_MINOR_VERSION) " ");
simple_add_define(&this, "__BUILD__",
" " DEFINETOSTR(PIKE_BUILD_VERSION) " ");
simple_add_define(&this, "__REAL_BUILD__",
" " DEFINETOSTR(PIKE_BUILD_VERSION) " ");
#ifdef AUTO_BIGNUM
simple_add_define(&this, "__AUTO_BIGNUM__", " 1 ");
#endif
#ifdef __NT__
simple_add_define(&this, "__NT__", " 1 ");
#endif
#ifdef __amigaos__
simple_add_define(&this, "__amigaos__", " 1 ");
#endif
#ifdef __APPLE__
simple_add_define(&this, "__APPLE__", " 1 ");
#endif
simple_add_define(&this, "SIZEOF_INT",
" " DEFINETOSTR(SIZEOF_INT) " ");
simple_add_define(&this, "SIZEOF_FLOAT",
" " DEFINETOSTR(SIZEOF_FLOAT) " ");
}
if (predefs) {
struct keypair *k;
int e;
NEW_MAPPING_LOOP (predefs->data) {
if (TYPEOF(k->val) == T_STRING)
add_define (&this, k->ind.u.string, k->val.u.string);
else if(TYPEOF(k->val) != T_INT || k->val.u.integer )
{
struct define *def;
if( index_shared_string( k->ind.u.string, k->ind.u.string->len-1) == ')' )
{
struct pike_string *s = string_slice( k->ind.u.string, 0, k->ind.u.string->len-2);
def = alloc_empty_define( s, 0 );
def->magic = insert_callback_define;
def->varargs=1;
def->args=1;
}
else
{
def = alloc_empty_define( k->ind.u.string, 0 );
k->ind.u.string->refs++;
def->magic = insert_callback_define_no_args;
}
this.defines = hash_insert( this.defines, &def->link );
}
else
add_define (&this, k->ind.u.string, empty_pike_string);
}
free_mapping (predefs);
}
string_builder_binary_strcat(&this.buf, "# 1 ", 4);
PUSH_STRING_SHIFT(this.current_file->str, this.current_file->len,
this.current_file->size_shift, &this.buf);
string_builder_putchar(&this.buf, '\n');
#ifdef PIKE_DEBUG
SET_ONERROR(tmp, fatal_on_error, "Preprocessor exited with longjump!\n");
#endif /* PIKE_DEBUG */
low_cpp(&this, data->str, data->len, data->size_shift,
0, auto_convert, charset);
#ifdef PIKE_DEBUG
UNSET_ONERROR(tmp);
#endif /* PIKE_DEBUG */
UNSET_ONERROR(err);
free_cpp(&this);
if(this.compile_errors)
{
free_string_builder(&this.buf);
throw_error_object(fast_clone_object(cpp_error_program), 0, 0, 0,
"Cpp() failed\n");
}else{
pop_n_elems(sp - save_sp);
push_string(finish_string_builder(&this.buf));
}
}
void f__take_over_initial_predefines (INT32 args)
{
pop_n_elems (args);
if (use_initial_predefs) {
struct pike_predef_s *tmp;
push_mapping (initial_predefs_mapping());
use_initial_predefs = 0;
while((tmp=first_predef))
{
free(tmp->name);
free(tmp->value);
first_predef=tmp->next;
free((char *)tmp);
}
last_predef = 0;
}
else Pike_error ("Initial predefines already taken over.\n");
}
void init_cpp()
{
struct svalue s;
defined_macro=alloc_empty_define(make_shared_string("defined"),0);
defined_macro->magic=check_defined;
defined_macro->args=1;
efun_str = make_shared_string ("efun");
constant_str = make_shared_string ("constant");
defined_str = make_shared_string ("defined");
use_initial_predefs = 1;
/* function(string, string|void, int|string|void, object|void, int|void, int|void:string) */
ADD_EFUN("cpp", f_cpp, tFunc(tStr tOr(tStr,tVoid)
tOr(tInt,tOr(tStr,tVoid))
tOr(tObj,tVoid)
tOr(tInt,tVoid)
tOr(tInt,tVoid)
tOr(tInt,tVoid)
, tStr),
/* OPT_SIDE_EFFECT since we might instantiate modules etc. */
OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
/* Somewhat tricky to add a _constant_ function in _static_modules.Builtin. */
SET_SVAL(s, T_FUNCTION, FUNCTION_BUILTIN, efun,
make_callable (f__take_over_initial_predefines,
"_take_over_initial_predefines",
"function(void:mapping(string:string))",
OPT_SIDE_EFFECT, NULL, NULL));
simple_add_constant ("_take_over_initial_predefines", &s, 0);
free_svalue (&s);
}
void add_predefine(char *s)
{
struct pike_predef_s *tmp=ALLOC_STRUCT(pike_predef_s);
char * pos=STRCHR(s,'=');
if(pos)
{
tmp->name=(char *)xalloc(pos-s+1);
MEMCPY(tmp->name,s,pos-s);
tmp->name[pos-s]=0;
tmp->value=(char *)xalloc(s+strlen(s)-pos);
MEMCPY(tmp->value,pos+1,s+strlen(s)-pos);
}else{
tmp->name=(char *)xalloc(strlen(s)+1);
MEMCPY(tmp->name,s,strlen(s)+1);
tmp->value=(char *)xalloc(4);
MEMCPY(tmp->value," 1 ",4);
}
tmp->next = NULL;
if (first_predef) {
last_predef->next = tmp;
last_predef = tmp;
}
else first_predef = last_predef = tmp;
}
void exit_cpp(void)
{
#ifdef DO_PIKE_CLEANUP
struct pike_predef_s *tmp;
while((tmp=first_predef))
{
free(tmp->name);
free(tmp->value);
first_predef=tmp->next;
free((char *)tmp);
}
free_string(defined_macro->link.s);
free((char *)defined_macro);
free_string (efun_str);
free_string (constant_str);
free_string (defined_str);
#endif
}