commit 7a78284129d7c7ef62b0347cae7d355293a7e4b4 Author: David Rose Date: Wed Oct 4 00:15:23 2000 +0000 Initial revision diff --git a/dtool/Config.Irix.pp b/dtool/Config.Irix.pp new file mode 100644 index 0000000000..c01560fce3 --- /dev/null +++ b/dtool/Config.Irix.pp @@ -0,0 +1,49 @@ +// +// Config.Irix.pp +// +// This file defines some custom config variables for the SGI/Irix +// platform. It makes some initial guesses about compiler features, +// etc. +// + +// Is the platform big-endian (like an SGI workstation) or +// little-endian (like a PC)? Define this to the empty string to +// indicate little-endian, or nonempty to indicate big-endian. +#define WORDS_BIGENDIAN 1 + +// Does the C++ compiler support namespaces? +#define HAVE_NAMESPACE 1 + +// Does the C++ compiler support ios::binary? +#define HAVE_IOS_BINARY + +// Do we have a gettimeofday() function? +#define HAVE_GETTIMEOFDAY 1 + +// Does gettimeofday() take only one parameter? +#define GETTIMEOFDAY_ONE_PARAM + +// Do we have getopt() and/or getopt_long_only() built into the +// system? +#define HAVE_GETOPT 1 +#define HAVE_GETOPT_LONG_ONLY + +// Should we include or ? Define HAVE_IOSTREAM +// to nonempty if we should use , or empty if we should use +// . +#define HAVE_IOSTREAM + +// Do we have a true stringstream class defined in ? +#define HAVE_SSTREAM + +// Do we have ? +#define HAVE_MALLOC_H 1 + +// Do we have ? +#define HAVE_ALLOCA_H 1 + +// Do we have ? +#define HAVE_SYS_TYPES_H 1 + +// Do we have ? +#define HAVE_UNISTD_H 1 diff --git a/dtool/Config.Linux.pp b/dtool/Config.Linux.pp new file mode 100644 index 0000000000..f33593d9a4 --- /dev/null +++ b/dtool/Config.Linux.pp @@ -0,0 +1,49 @@ +// +// Config.Linux.pp +// +// This file defines some custom config variables for the Linux +// platform. It makes some initial guesses about compiler features, +// etc. +// + +// Is the platform big-endian (like an SGI workstation) or +// little-endian (like a PC)? Define this to the empty string to +// indicate little-endian, or nonempty to indicate big-endian. +#define WORDS_BIGENDIAN + +// Does the C++ compiler support namespaces? +#define HAVE_NAMESPACE 1 + +// Does the C++ compiler support ios::binary? +#define HAVE_IOS_BINARY 1 + +// Do we have a gettimeofday() function? +#define HAVE_GETTIMEOFDAY 1 + +// Does gettimeofday() take only one parameter? +#define GETTIMEOFDAY_ONE_PARAM + +// Do we have getopt() and/or getopt_long_only() built into the +// system? +#define HAVE_GETOPT 1 +#define HAVE_GETOPT_LONG_ONLY 1 + +// Should we include or ? Define HAVE_IOSTREAM +// to nonempty if we should use , or empty if we should use +// . +#define HAVE_IOSTREAM + +// Do we have a true stringstream class defined in ? +#define HAVE_SSTREAM + +// Do we have ? +#define HAVE_MALLOC_H 1 + +// Do we have ? +#define HAVE_ALLOCA_H 1 + +// Do we have ? +#define HAVE_SYS_TYPES_H 1 + +// Do we have ? +#define HAVE_UNISTD_H 1 diff --git a/dtool/Config.Win32.pp b/dtool/Config.Win32.pp new file mode 100644 index 0000000000..748945306f --- /dev/null +++ b/dtool/Config.Win32.pp @@ -0,0 +1,49 @@ +// +// Config.Win32.pp +// +// This file defines some custom config variables for the Windows +// platform. It makes some initial guesses about compiler features, +// etc. +// + +// Is the platform big-endian (like an SGI workstation) or +// little-endian (like a PC)? Define this to the empty string to +// indicate little-endian, or nonempty to indicate big-endian. +#define WORDS_BIGENDIAN + +// Does the C++ compiler support namespaces? +#define HAVE_NAMESPACE 1 + +// Does the C++ compiler support ios::binary? +#define HAVE_IOS_BINARY 1 + +// Do we have a gettimeofday() function? +#define HAVE_GETTIMEOFDAY + +// Does gettimeofday() take only one parameter? +#define GETTIMEOFDAY_ONE_PARAM + +// Do we have getopt() and/or getopt_long_only() built into the +// system? +#define HAVE_GETOPT +#define HAVE_GETOPT_LONG_ONLY + +// Should we include or ? Define HAVE_IOSTREAM +// to nonempty if we should use , or empty if we should use +// . +#define HAVE_IOSTREAM 1 + +// Do we have a true stringstream class defined in ? +#define HAVE_SSTREAM 1 + +// Do we have ? +#define HAVE_MALLOC_H 1 + +// Do we have ? +#define HAVE_ALLOCA_H + +// Do we have ? +#define HAVE_SYS_TYPES_H 1 + +// Do we have ? +#define HAVE_UNISTD_H diff --git a/dtool/Config.pp b/dtool/Config.pp new file mode 100644 index 0000000000..9e27543718 --- /dev/null +++ b/dtool/Config.pp @@ -0,0 +1,167 @@ +// +// Config.pp +// +// This file defines certain configuration variables that are written +// into the various make scripts. It is processed by ppremake (along +// with the Sources.pp files in each of the various directories) to +// generate build scripts appropriate to each environment. +// +// ppremake is capable of generating generic Unix autoconf/automake +// style build scripts, as well as makefiles customized for SGI's +// MipsPRO compiler or for Microsoft's Visual C++. It can also +// generate Microsoft Developer's Studio project files directly. In +// principle, it can be extended to generate suitable build script +// files for any number of different build environments. +// +// All of these build scripts can be tuned for a particular +// environment via this file. This is the place for the user to +// specify which external packages are installed and where, or to +// enable or disable certain optional features. However, it is +// suggested that rather than modify this file directly, you create a +// custom file in your home directory and there redefine whatever +// variables are appropriate, and set the environment variable +// PPREMAKE_CONFIG to refer to it. In this way, you can easily get an +// updated source tree (including a new Config.pp) without risking +// accidentally losing your customizations. This also avoids having +// to redefine the same variables in different packages (for instance, +// in dtool and in panda). +// +// If you *do* decide to make changes directly to this file, you +// should also comment out the line near the bottom that includes the +// file $[TOPDIRPREFIX]Config.$[PLATFORM].pp, to avoid stomping on the +// changes you make. +// +// The syntax in this file resembles some hybrid between C++ +// preprocessor declarations and GNU make variables. This is the same +// syntax used in the various ppremake system configure files; it's +// designed to be easy to use as a macro language to generate +// makefiles and their ilk. +// + +// What kind of build scripts are we generating? This selects a +// suitable template file from the ppremake system files. The +// allowable choices, at present, are: +// +// autoconf - Generate configure.in and a series of Makefile.am files, +// suitable for using with autoconf/automake. Not quite +// there yet. +// stopgap - Generate original Cary-style Makefile/Makefile.install/etc. +// files, to ease transition to the new system. +// +#define BUILD_TYPE stopgap + +// Define the directory in which the system ppremake files are +// installed. +#define PPREMAKE_DIR /usr/local/panda/share + + +// In which directory should this package be installed when you type +// "make install"? This has no meaning when BUILD_TYPE is "stopgap". +#define INSTALL_DIR /usr/local/panda + + +// What level of compiler optimization/debug symbols should we build? +// The various optimize levels are defined as follows: +// +// 1 - No compiler optimizations, full debug symbols +// 2 - Full compiler optimizations, full debug symbols +// (if the compiler supports this) +// 3 - Full compiler optimizations, no debug symbols +// 4 - Full optimizations, no debug symbols, and asserts removed +// +// Setting this has no effect when BUILD_TYPE is "stopgap". In this +// case, the compiler optimizations are selected by setting the +// environment variable OPTIMIZE accordingly at compile time. +#define OPTIMIZE 1 + + + +//////////////////////////////////////////////////////////////////// +// The remaining variables are considered only if BUILD_TYPE is not +// "autoconf". (Autoconf can determine these directly.) +//////////////////////////////////////////////////////////////////// + +// NOTE: In the following, to indicate "yes" to a yes/no question, +// define the variable to be a nonempty string. To indicate "no", +// define the variable to be an empty string. + +// Is Python installed, and should Python interfaces be generated? If +// Python is installed, which directory is it in? (If the directory +// is someplace standard like /usr/include, you may leave it blank.) +#define HAVE_PYTHON 1 +#define PYTHON_INCLUDE /usr/local/include/python1.6 +#define PYTHON_LIB + +// Is NSPR installed, and where? +#define HAVE_NSPR 1 +#define NSPR_INCLUDE /usr/local/mozilla/dist/*/include +#define NSPR_LIB + +// Is VRPN installed, and where? +#define HAVE_VRPN +#define VRPN_INCLUDE +#define VRPN_LIB + +// Is ZLIB installed, and where? +#define HAVE_ZLIB 1 +#define ZLIB_INCLUDE +#define ZLIB_LIB + +// Is OpenGL installed, and where? +#define HAVE_GL 1 +#define GL_INCLUDE +#define GL_LIB +#define GLU_INCLUDE +#define GLU_LIB + +// How about GLX? +#define HAVE_GLX 1 +#define GLX_INCLUDE +#define GLX_LIB + +// Glut? +#define HAVE_GLUT +#define GLUT_INCLUDE +#define GLUT_LIB + +// Should we try to build the WGL interface? +#define HAVE_WGL + +// Should we try to build the DirectX interface? +#define HAVE_DX + +// Do you want to build the Renderman interface? +#define HAVE_RIB + +// Is Mikmod installed? +#define HAVE_MIKMOD +#define MIKMOD_CFLAGS +#define MIKMOD_INCLUDE +#define MIKMOD_LIB + + +////////////////////////////////////////////////////////////////////// +// There are also some additional variables that control specific +// compiler/platform features or characteristics, defined in the +// platform specific file Config.platform.pp. Be sure to inspect +// these variables for correctness too. As above, these are +// unnecessary when BUILD_TYPE is "autoconf". +////////////////////////////////////////////////////////////////////// +#include $[TOPDIRPREFIX]Config.$[PLATFORM].pp + + +// Also pull in whatever package-specific variables there may be. +#include $[TOPDIRPREFIX]Package.pp + + +// If the environment variable PPREMAKE_CONFIG is set, it points to a +// user-customized Config.pp file, for instance in the user's home +// directory. This file might redefine any of the variables defined +// above. +#if $[ne $[PPREMAKE_CONFIG],] + #include $[PPREMAKE_CONFIG] +#endif + + +// Finally, include the system configure file. +#include $[PPREMAKE_DIR]/System.pp diff --git a/dtool/LocalSetup.pp b/dtool/LocalSetup.pp new file mode 100644 index 0000000000..f3506cc238 --- /dev/null +++ b/dtool/LocalSetup.pp @@ -0,0 +1,211 @@ +// +// LocalSetup.pp +// +// This file contains further instructions to set up the DTOOL package +// when using ppremake. In particular, it creates the dtool_config.h +// file based on the user's selected configure variables. This script +// need not execute when BUILD_TYPE is "autoconf"; in this case, the +// dtool_config.h file will automatically be correctly generated by +// configure. +// + +#if $[ne $[BUILD_TYPE],autoconf] + +// A couple of variables to output the C #define and #undef +// directives, since we can't type them literally. +#define define #define +#define undef #undef + +#output dtool_config.h +#format straight +/* dtool_config.h. Generated automatically by $[PROGRAM] $[PROGVER] from $[SOURCEFILE]. */ + +/* Define if you have the ANSI C header files. */ +$[define] STDC_HEADERS 1 + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#if $[WORDS_BIGENDIAN] +$[define] WORDS_BIGENDIAN 1 +#else +$[undef] WORDS_BIGENDIAN +#endif + +/* Define if the X Window System is missing or not being used. */ +$[undef] X_DISPLAY_MISSING + +/* Define if lex declares yytext as a char * by default, not a char[]. */ +$[undef] YYTEXT_POINTER + +/* Define if the C++ compiler uses namespaces. */ +#if $[HAVE_NAMESPACE] +$[define] HAVE_NAMESPACE 1 +#else +$[undef] HAVE_NAMESPACE +#endif + +/* Define if the C++ iostream library supports ios::binary. */ +#if $[HAVE_IOS_BINARY] +$[define] HAVE_IOS_BINARY 1 +#else +$[undef] HAVE_IOS_BINARY +#endif + +/* Define if we have Python installed. */ +#if $[HAVE_PYTHON] +$[define] HAVE_PYTHON 1 +#else +$[undef] HAVE_PYTHON +#endif + +/* Define if we have NSPR installed. */ +#if $[HAVE_NSPR] +$[define] HAVE_NSPR 1 +#else +$[undef] HAVE_NSPR +#endif + +/* Define if we have VRPN installed. */ +#if $[HAVE_VRPN] +$[define] HAVE_VRPN 1 +#else +$[undef] HAVE_VRPN +#endif + +/* Define if we have zlib installed. */ +#if $[HAVE_ZLIB] +$[define] HAVE_ZLIB 1 +#else +$[undef] HAVE_ZLIB +#endif + +/* Define if we have OpenGL installed and want to build for GL. */ +#if $[HAVE_GL] +$[define] HAVE_GL 1 +#else +$[undef] HAVE_GL +#endif + +/* Define if we have GLU installed. */ +#if $[HAVE_GLU] +$[define] HAVE_GLU 1 +#else +$[undef] HAVE_GLU +#endif + +/* Define if we have GLX installed and want to build for GLX. */ +#if $[HAVE_GLX] +$[define] HAVE_GLX 1 +#else +$[undef] HAVE_GLX +#endif + +/* Define if we have Glut installed and want to build for Glut. */ +#if $[HAVE_GLUT] +$[define] HAVE_GLUT 1 +#else +$[undef] HAVE_GLUT +#endif + +/* Define if we want to build the Renderman interface. */ +#if $[HAVE_RIB] +$[define] HAVE_RIB 1 +#else +$[undef] HAVE_RIB +#endif + +/* Define if we want to use mikmod for audio. */ +#if $[HAVE_MIKMOD] +$[define] HAVE_MIKMOD 1 +#else +$[undef] HAVE_MIKMOD +#endif + +/* Define if we have a gettimeofday() function. */ +#if $[HAVE_GETTIMEOFDAY] +$[define] HAVE_GETTIMEOFDAY 1 +#else +$[undef] HAVE_GETTIMEOFDAY +#endif + +/* Define if gettimeofday() takes only one parameter. */ +#if $[GETTIMEOFDAY_ONE_PARAM] +$[define] GETTIMEOFDAY_ONE_PARAM 1 +#else +$[undef] GETTIMEOFDAY_ONE_PARAM +#endif + +/* Define if you have the getopt function. */ +#if $[HAVE_GETOPT] +$[define] HAVE_GETOPT 1 +#else +$[undef] HAVE_GETOPT +#endif + +/* Define if you have the getopt_long_only function. */ +#if $[HAVE_GETOPT_LONG_ONLY] +$[define] HAVE_GETOPT_LONG_ONLY 1 +#else +$[undef] HAVE_GETOPT_LONG_ONLY +#endif + +/* Define if you have the header file. */ +$[define] HAVE_ALLOCA_H 1 + +/* Define if you have the header file. */ +$[undef] HAVE_IO_H + +/* Define if you have the header file. */ +#if $[HAVE_IOSTREAM] +$[define] HAVE_IOSTREAM 1 +#else +$[undef] HAVE_IOSTREAM +#endif + +/* Define if you have the header file. */ +#if $[HAVE_MALLOC_H] +$[define] HAVE_MALLOC_H 1 +#else +$[undef] HAVE_MALLOC_H +#endif + +/* Define if you have the header file. */ +#if $[HAVE_ALLOCA_H] +$[define] HAVE_ALLOCA_H 1 +#else +$[undef] HAVE_ALLOCA_H +#endif + +/* Define if you have the header file. */ +$[undef] HAVE_MINMAX_H + +/* Define if you have the header file. */ +#if $[HAVE_SSTREAM] +$[define] HAVE_SSTREAM 1 +#else +$[undef] HAVE_SSTREAM +#endif + +/* Define if you have the header file. */ +#if $[HAVE_SYS_TYPES] +$[define] HAVE_SYS_TYPES 1 +#else +$[undef] HAVE_SYS_TYPES +#endif + +/* Define if you have the header file. */ +#if $[HAVE_UNISTD_H] +$[define] HAVE_UNISTD_H 1 +#else +$[undef] HAVE_UNISTD_H +#endif + +/* Name of package */ +$[define] PACKAGE $[PACKAGE] + +/* Version number of package */ +$[define] VERSION $[VERSION] + +#end dtool_config.h + +#endif // BUILD_TYPE diff --git a/dtool/Package.pp b/dtool/Package.pp new file mode 100644 index 0000000000..1591757600 --- /dev/null +++ b/dtool/Package.pp @@ -0,0 +1,11 @@ +// +// Package.pp +// +// This file defines a few more configuration variables that are +// inconvenient to store in Config.pp, simply because they are more +// specific to a particular package and not likely to be edited by a +// configuring user. + +// What is the name and version of this source tree? +#define PACKAGE dtool +#define VERSION 0.80 diff --git a/dtool/Sources.pp b/dtool/Sources.pp new file mode 100644 index 0000000000..5600d1bee5 --- /dev/null +++ b/dtool/Sources.pp @@ -0,0 +1,10 @@ +// This is the toplevel directory. It contains configure.in and other +// stuff. + +#define DIR_TYPE toplevel + +#define CONFIG_HEADER dtool_config.h +#define SAMPLE_SOURCE_FILE src/dtoolbase/dtoolbase.cxx + +#define EXTRA_DIST \ + Config.Irix.pp Config.Linux.pp Config.Win32.pp LocalSetup.pp Package.pp diff --git a/dtool/acconfig.h b/dtool/acconfig.h new file mode 100644 index 0000000000..b59d7a570c --- /dev/null +++ b/dtool/acconfig.h @@ -0,0 +1,68 @@ +/* acconfig.h + This file is in the public domain. + + Descriptive text for the C preprocessor macros that + the distributed Autoconf macros can define. + No software package will use all of them; autoheader copies the ones + your configure.in uses into your configuration header file templates. + + The entries are in sort -df order: alphabetical, case insensitive, + ignoring punctuation (such as underscores). Although this order + can split up related entries, it makes it easier to check whether + a given entry is in the file. + + Leave the following blank line there!! Autoheader needs it. */ + + +/* Define if the C++ compiler uses namespaces. */ +#undef HAVE_NAMESPACE + +/* Define if the C++ iostream library supports ios::binary. */ +#undef HAVE_IOS_BINARY + +/* The current version number. */ +#define VERSION 0.0 + +/* Define if we have Python installed. */ +#undef HAVE_PYTHON + +/* Define if we have NSPR installed. */ +#undef HAVE_NSPR + +/* Define if we have zlib installed. */ +#undef HAVE_ZLIB + +/* Define if we have OpenGL installed and want to build for GL. */ +#undef HAVE_GL + +/* Define if we have GLU installed. */ +#undef HAVE_GLU + +/* Define if we have GLX installed and want to build for GLX. */ +#undef HAVE_GLX + +/* Define if we have Glut installed and want to build for Glut. */ +#undef HAVE_GLUT + +/* Define if we have DirectX installed and want to build for DirectX. */ +#undef HAVE_DX + +/* Define if we want to build the Renderman interface. */ +#undef HAVE_RIB + +/* Define if we want to use mikmod for audio. */ +#undef HAVE_MIKMOD + +/* Define if we have a gettimeofday() function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if gettimeofday() takes only one parameter. */ +#undef GETTIMEOFDAY_ONE_PARAM + + +/* Leave that blank line there!! Autoheader needs it. + If you're adding to this file, keep in mind: + The entries are in sort -df order: alphabetical, case insensitive, + ignoring punctuation (such as underscores). */ + + diff --git a/dtool/acinclude.m4 b/dtool/acinclude.m4 new file mode 100644 index 0000000000..1b43f1b250 --- /dev/null +++ b/dtool/acinclude.m4 @@ -0,0 +1,933 @@ +AC_DEFUN(AC_GETTIMEOFDAY, +[AC_CACHE_CHECK([for gettimeofday()], + ac_cv_gettimeofday, +[ + AC_TRY_COMPILE([ +#include + ],[ + gettimeofday((struct timeval *)NULL, (struct timezone *)NULL); + ], ac_cv_gettimeofday="2 params", ac_cv_gettimeofday=no) + if test "$ac_cv_gettimeofday" = no; then + AC_TRY_COMPILE([ +#include + ],[ + gettimeofday((struct timeval *)NULL, (struct timezone *)NULL); + ], ac_cv_gettimeofday="1 param", ac_cv_gettimeofday=no) + fi +]) +if test "$ac_cv_gettimeofday" = "1 param"; then + AC_DEFINE(GETTIMEOFDAY_ONE_PARAM) + AC_DEFINE(HAVE_GETTIMEOFDAY) +elif test "$ac_cv_gettimeofday" = "2 params"; then + AC_DEFINE(HAVE_GETTIMEOFDAY) +fi +]) + + +AC_DEFUN(AC_HEADER_IOSTREAM, +[AC_CHECK_HEADERS(iostream,[have_iostream=yes],[have_iostream=no])]) + +AC_DEFUN(AC_IOS_BINARY, +[AC_CACHE_CHECK([for ios::binary], + ac_cv_ios_binary, +[ +if test $have_iostream = yes; then + AC_TRY_COMPILE([ +#include + ],[ + int x; x = ios::binary; + ], ac_cv_ios_binary=yes, ac_cv_ios_binary=no) +else + AC_TRY_COMPILE([ +#include + ],[ + int x; x = ios::binary; + ], ac_cv_ios_binary=yes, ac_cv_ios_binary=no) +fi + +]) +if test $ac_cv_ios_binary = yes; then + AC_DEFINE(HAVE_IOS_BINARY) +fi +]) + + +AC_DEFUN(AC_NAMESPACE, +[AC_CACHE_CHECK([for compiler namespace support], + ac_cv_namespace, +[AC_TRY_COMPILE( +[namespace std { }; +using namespace std;], +[], + ac_cv_namespace=yes, ac_cv_namespace=no)]) +if test $ac_cv_namespace = yes; then + AC_DEFINE(HAVE_NAMESPACE) +fi +]) + + +dnl A handy function to see if a library is in a particular directory. +dnl AC_CHECK_LIB_LOC(directory, library, function, action-if-found, action-if-not-found, other-libraries) +dnl +AC_DEFUN(AC_CHECK_LIB_LOC, +[AC_MSG_CHECKING([for lib$2 in $1]) +ac_lib_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` +AC_CACHE_VAL(ac_cv_lib_loc_$ac_lib_var, +[ac_save_LIBS="$LIBS" +LIBS="-L$1 -l$2 $6 $LIBS" +AC_TRY_LINK(dnl +ifelse(AC_LANG, CPLUSPLUS, [#ifdef __cplusplus +extern "C" +#endif +])dnl +[/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $3(); +], + [$3()], + eval "ac_cv_lib_loc_$ac_lib_var=yes", + eval "ac_cv_lib_loc_$ac_lib_var=no") +LIBS="$ac_save_LIBS" +])dnl +if eval "test \"`echo '$ac_cv_lib_loc_'$ac_lib_var`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$4], , +[LIBS="-L$1 -l$2 $LIBS" +], [$4]) +else + AC_MSG_RESULT(no) +ifelse([$5], , , [$5 +])dnl +fi +]) + +dnl A handy function to search a number of possible locations for a library. +dnl AC_SEARCH_LIB(search-dirs, library, function, package, other-libraries) +dnl +dnl Sets $package_LIB to the directory containing the library, or to the +dnl string "no" if the library cannot be found. +AC_DEFUN(AC_SEARCH_LIB, [ +ac_found_lib="no" +if test "$1" = ""; then + AC_CHECK_LIB($2, $3, [ ac_found_lib=""; ],, $5) +else + for ac_check_dir in $1; do + if test "$ac_found_lib" = "no"; then + AC_CHECK_LIB_LOC($ac_check_dir, $2, $3, [ ac_found_lib="$ac_check_dir"; ],, $5) + fi + done +fi +$4_LIB="$ac_found_lib" +]) + +dnl A handy function to see if a header file is in a particular directory. +dnl AC_CHECK_HEADER_LOC(directory, header, action-if-found, action-if-not-found) +dnl +AC_DEFUN(AC_CHECK_HEADER_LOC, [ + AC_MSG_CHECKING([for $2 in $1]) + ac_include_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` + AC_CACHE_VAL(ac_cv_include_loc_$ac_include_var, [ + ac_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="-I$1 $CPPFLAGS" + AC_TRY_CPP([#include <$2>], ac_ch_found_it="yes", ac_ch_found_it="no") + if test "$ac_ch_found_it" = "yes"; then + AC_MSG_RESULT(yes) + ifelse([$3], , :, [$3]) + else + AC_MSG_RESULT(no) + ifelse([$4], , , [$4]) + fi + CPPFLAGS="$ac_save_CPPFLAGS" + ]) +]) + + +dnl A handy function to search a number of possible locations for a header +dnl file. +dnl +dnl AC_SEARCH_HEADER(search-dirs, header, package) +dnl +dnl Sets $package_INCLUDE to the directory containing the header, or to +dnl the string "no" if the header cannot be found. +AC_DEFUN(AC_SEARCH_HEADER, [ +ac_found_header="no" +if test "$1" = ""; then + AC_CHECK_HEADER($2, [ ac_found_header="";]) +else + for ac_check_dir in $1; do + if test "$ac_found_header" = "no"; then + AC_CHECK_HEADER_LOC($ac_check_dir, $2, [ ac_found_header="$ac_check_dir";]) + fi + done +fi +$3_INCLUDE="$ac_found_header" +]) + + +dnl A handy function to scan for a third-party package, consisting of at +dnl least a library and an include file. A few assumptions are made about +dnl the relationships between lib and include directories. +dnl +dnl AC_SEARCH_PACKAGE(search-dirs, package-names, header, library, function, package, other-libraries) +dnl +dnl search-dirs is the whitespace-separated list of directory prefixes to +dnl check. +dnl package-names is a whitespace-separated list of possible names the +dnl package may have been installed under. +dnl +dnl For each combination of ${search-dir} and ${package-name}, the following +dnl directories are searched: +dnl +dnl ${search-dir} +dnl ${search-dir}/lib +dnl ${search-dir}/${package-name} +dnl ${search-dir}/${package-name}/lib +dnl ${search-dir}/lib/${package-name} +dnl +dnl And similarly for include. +dnl +dnl Sets the variables $package_INCLUDE and $package_LIB to the directories +dnl containing the header file and library, respectively. If both pieces +dnl are located, also sets the variable $package_PKG to "yes"; otherwise, +dnl sets the variable $package_PKG to "no". +dnl +AC_DEFUN(AC_SEARCH_PACKAGE, [ +$6_LIB="no" +$6_INCLUDE="no" +$6_PKG="no" + +dnl Look for the library. +AC_SEARCH_LIB("", $4, $5, $6, $7) +for ac_sp_dir in $1; do + if test "[$]$6_LIB" = "no"; then + AC_SEARCH_LIB("$ac_sp_dir" "$ac_sp_dir/lib", $4, $5, $6, $7) + for ac_sp_pkg in $2; do + if test "[$]$6_LIB" = "no"; then + AC_SEARCH_LIB("$ac_sp_dir/$ac_sp_pkg" "$ac_sp_dir/$ac_sp_pkg/lib" \ + "$ac_sp_dir/lib/$ac_sp_pkg", $4, $5, $6, $7) + fi + done + fi +done + +dnl Now look for the header file. Don't bother looking if the library +dnl wasn't found. +if test "[$]$6_LIB" != "no"; then + dnl First look in the obvious directory corresponding to the lib dir. + ac_sp_testinc=`echo [$]$6_LIB | sed 's:/lib:/include:'` + AC_SEARCH_HEADER("$ac_sp_testinc", $3, $6) + + dnl If it wasn't found there, cast about. + if test "[$]$6_INCLUDE" = "no"; then + for ac_sp_dir in $1; do + if test "[$]$6_INCLUDE" = "no"; then + AC_SEARCH_HEADER("$ac_sp_dir" "$ac_sp_dir/include", $3, $6) + for ac_sp_pkg in $2; do + if test "[$]$6_INCLUDE" = "no"; then + AC_SEARCH_HEADER("$ac_sp_dir/$ac_sp_pkg" \ + "$ac_sp_dir/$ac_sp_pkg/include" \ + "$ac_sp_dir/include/$ac_sp_pkg", $3, $6) + fi + done + fi + done + fi + + dnl If we got both a header and a library, set the PKG variable. + if test "[$]$6_INCLUDE" != "no"; then + $6_PKG="yes" + fi +fi +]) + +dnl A handy function, similar to AC_SEARCH_PACKAGE, above, that looks for a +dnl package that only consists of header files, no libraries. +dnl +dnl AC_SEARCH_HPACKAGE(search-dirs, package-names, header, package) +dnl +dnl search-dirs is the whitespace-separated list of directory prefixes to +dnl check. +dnl package-names is a whitespace-separated list of possible names the +dnl package may have been installed under. +dnl +dnl For each combination of ${search-dir} and ${package-name}, the following +dnl directories are searched: +dnl +dnl ${search-dir} +dnl ${search-dir}/include +dnl ${search-dir}/${package-name} +dnl ${search-dir}/${package-name}/include +dnl ${search-dir}/include/${package-name} +dnl +dnl Sets the variable $package_INCLUDE to the directory containing the +dnl header file, and sets the variable $package_PKG to "yes"; if the +dnl directory is not found, sets the variable $package_PKG to "no". +dnl +AC_DEFUN(AC_SEARCH_HPACKAGE, [ +$4_INCLUDE="no" +$4_PKG="no" + +dnl Look for the header. +AC_SEARCH_HEADER("", $3, $4) +for ac_sp_dir in $1; do + if test "[$]$4_INCLUDE" = "no"; then + AC_SEARCH_HEADER("$ac_sp_dir" "$ac_sp_dir/include", $3, $4) + for ac_sp_pkg in $2; do + if test "[$]$4_INCLUDE" = "no"; then + AC_SEARCH_HEADER("$ac_sp_dir/$ac_sp_pkg" \ + "$ac_sp_dir/$ac_sp_pkg/include" \ + "$ac_sp_dir/include/$ac_sp_pkg", $3, $4) + fi + done + fi +done + +dnl If we got both a header file, set the PKG variable. +if test "[$]$4_INCLUDE" != "no"; then + $4_PKG="yes" +fi +]) + +# Define a conditional. + +AC_DEFUN(AM_CONDITIONAL, +[AC_SUBST($1_TRUE) +AC_SUBST($1_FALSE) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi]) + + + + +# Configure paths for GTK-- +# Erik Andersen 30 May 1998 +# Modified by Tero Pulkkinen (added the compiler checks... I hope they work..) + +dnl Test for GTKMM, and define GTKMM_CFLAGS and GTKMM_LIBS +dnl to be used as follows: +dnl AM_PATH_GTKMM([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl +AC_DEFUN(AM_PATH_GTKMM, +[dnl +dnl Get the cflags and libraries from the gtkmm-config script +dnl +AC_ARG_WITH(gtkmm-prefix,[ --with-gtkmm-prefix=PREFIX + Prefix where GTK-- is installed (optional)], + gtkmm_config_prefix="$withval", gtkmm_config_prefix="") +AC_ARG_WITH(gtkmm-exec-prefix,[ --with-gtkmm-exec-prefix=PREFIX + Exec prefix where GTK-- is installed (optional)], + gtkmm_config_exec_prefix="$withval", gtkmm_config_exec_prefix="") +AC_ARG_ENABLE(gtkmmtest, [ --disable-gtkmmtest Do not try to compile and run a test GTK-- program], + , enable_gtkmmtest=yes) + + if test x$gtkmm_config_exec_prefix != x ; then + gtkmm_config_args="$gtkmm_config_args --exec-prefix=$gtkmm_config_exec_prefix" + if test x${GTKMM_CONFIG+set} != xset ; then + GTKMM_CONFIG=$gtkmm_config_exec_prefix/bin/gtkmm-config + fi + fi + if test x$gtkmm_config_prefix != x ; then + gtkmm_config_args="$gtkmm_config_args --prefix=$gtkmm_config_prefix" + if test x${GTKMM_CONFIG+set} != xset ; then + GTKMM_CONFIG=$gtkmm_config_prefix/bin/gtkmm-config + fi + fi + + AC_PATH_PROG(GTKMM_CONFIG, gtkmm-config, no) + min_gtkmm_version=ifelse([$1], ,0.10.0,$1) + + AC_MSG_CHECKING(for GTK-- - version >= $min_gtkmm_version) + no_gtkmm="" + if test "$GTKMM_CONFIG" = "no" ; then + no_gtkmm=yes + else + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + GTKMM_CFLAGS=`$GTKMM_CONFIG $gtkmm_config_args --cflags` + GTKMM_LIBS=`$GTKMM_CONFIG $gtkmm_config_args --libs` + gtkmm_config_major_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + gtkmm_config_minor_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + gtkmm_config_micro_version=`$GTKMM_CONFIG $gtkmm_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_gtkmmtest" = "xyes" ; then + ac_save_CXXFLAGS="$CXXFLAGS" + ac_save_LIBS="$LIBS" + CXXFLAGS="$CXXFLAGS $GTKMM_CFLAGS" + LIBS="$LIBS $GTKMM_LIBS" +dnl +dnl Now check if the installed GTK-- is sufficiently new. (Also sanity +dnl checks the results of gtkmm-config to some extent +dnl + rm -f conf.gtkmmtest + AC_TRY_RUN([ +#include +#include +#include + +int +main () +{ + int major, minor, micro; + char *tmp_version; + + system ("touch conf.gtkmmtest"); + + /* HP/UX 0 (%@#!) writes to sscanf strings */ + tmp_version = g_strdup("$min_gtkmm_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_gtkmm_version"); + exit(1); + } + + if ((gtkmm_major_version != $gtkmm_config_major_version) || + (gtkmm_minor_version != $gtkmm_config_minor_version) || + (gtkmm_micro_version != $gtkmm_config_micro_version)) + { + printf("\n*** 'gtkmm-config --version' returned %d.%d.%d, but GTK-- (%d.%d.%d)\n", + $gtkmm_config_major_version, $gtkmm_config_minor_version, $gtkmm_config_micro_version, + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + printf ("*** was found! If gtkmm-config was correct, then it is best\n"); + printf ("*** to remove the old version of GTK--. You may also be able to fix the error\n"); + printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); + printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); + printf("*** required on your system.\n"); + printf("*** If gtkmm-config was wrong, set the environment variable GTKMM_CONFIG\n"); + printf("*** to point to the correct copy of gtkmm-config, and remove the file config.cache\n"); + printf("*** before re-running configure\n"); + } +/* GTK-- does not have the GTKMM_*_VERSION constants */ +/* + else if ((gtkmm_major_version != GTKMM_MAJOR_VERSION) || + (gtkmm_minor_version != GTKMM_MINOR_VERSION) || + (gtkmm_micro_version != GTKMM_MICRO_VERSION)) + { + printf("*** GTK-- header files (version %d.%d.%d) do not match\n", + GTKMM_MAJOR_VERSION, GTKMM_MINOR_VERSION, GTKMM_MICRO_VERSION); + printf("*** library (version %d.%d.%d)\n", + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + } +*/ + else + { + if ((gtkmm_major_version > major) || + ((gtkmm_major_version == major) && (gtkmm_minor_version > minor)) || + ((gtkmm_major_version == major) && (gtkmm_minor_version == minor) && (gtkmm_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of GTK-- (%d.%d.%d) was found.\n", + gtkmm_major_version, gtkmm_minor_version, gtkmm_micro_version); + printf("*** You need a version of GTK-- newer than %d.%d.%d. The latest version of\n", + major, minor, micro); + printf("*** GTK-- is always available from ftp://ftp.gtk.org.\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the gtkmm-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of GTK--, but you can also set the GTKMM_CONFIG environment to point to the\n"); + printf("*** correct copy of gtkmm-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + } + return 1; +} +],, no_gtkmm=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_gtkmm" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$GTKMM_CONFIG" = "no" ; then + echo "*** The gtkmm-config script installed by GTK-- could not be found" + echo "*** If GTK-- was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the GTKMM_CONFIG environment variable to the" + echo "*** full path to gtkmm-config." + echo "*** The gtkmm-config script was not available in GTK-- versions" + echo "*** prior to 0.9.12. Perhaps you need to update your installed" + echo "*** version to 0.9.12 or later" + else + if test -f conf.gtkmmtest ; then + : + else + echo "*** Could not run GTK-- test program, checking why..." + CXXFLAGS="$CFLAGS $GTKMM_CXXFLAGS" + LIBS="$LIBS $GTKMM_LIBS" + AC_TRY_LINK([ +#include +#include +], [ return ((gtkmm_major_version) || (gtkmm_minor_version) || (gtkmm_micro_version)); ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding GTK-- or finding the wrong" + echo "*** version of GTK--. If it is not finding GTK--, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means GTK-- was incorrectly installed" + echo "*** or that you have moved GTK-- since it was installed. In the latter case, you" + echo "*** may want to edit the gtkmm-config script: $GTKMM_CONFIG" ]) + CXXFLAGS="$ac_save_CXXFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + GTKMM_CFLAGS="" + GTKMM_LIBS="" + ifelse([$3], , :, [$3]) + AC_LANG_RESTORE + fi + AC_SUBST(GTKMM_CFLAGS) + AC_SUBST(GTKMM_LIBS) + rm -f conf.gtkmmtest +]) + + +## libtool.m4 - Configure libtool for the target system. -*-Shell-script-*- +## Copyright (C) 1996-1999 Free Software Foundation, Inc. +## Originally by Gordon Matzigkeit , 1996 +## +## This program 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 of the License, or +## (at your option) any later version. +## +## This program 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 this program; if not, write to the Free Software +## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +## +## As a special exception to the GNU General Public License, if you +## distribute this file as part of a program that contains a +## configuration script generated by Autoconf, you may include it under +## the same distribution terms that you use for the rest of that program. + +# serial 40 AC_PROG_LIBTOOL +AC_DEFUN(AC_PROG_LIBTOOL, +[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl + +# Save cache, so that ltconfig can load it +AC_CACHE_SAVE + +# Actually configure libtool. ac_aux_dir is where install-sh is found. +CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" \ +LD="$LD" LDFLAGS="$LDFLAGS" LIBS="$LIBS" \ +LN_S="$LN_S" NM="$NM" RANLIB="$RANLIB" \ +DLLTOOL="$DLLTOOL" AS="$AS" OBJDUMP="$OBJDUMP" \ +${CONFIG_SHELL-/bin/sh} $ac_aux_dir/ltconfig --no-reexec \ +$libtool_flags --no-verify $ac_aux_dir/ltmain.sh $host \ +|| AC_MSG_ERROR([libtool configure failed]) + +# Reload cache, that may have been modified by ltconfig +AC_CACHE_LOAD + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ac_aux_dir/ltconfig $ac_aux_dir/ltmain.sh" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +# Redirect the config.log output again, so that the ltconfig log is not +# clobbered by the next message. +exec 5>>./config.log +]) + +AC_DEFUN(AC_LIBTOOL_SETUP, +[AC_PREREQ(2.13)dnl +AC_REQUIRE([AC_ENABLE_SHARED])dnl +AC_REQUIRE([AC_ENABLE_STATIC])dnl +AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([AC_PROG_RANLIB])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_LD])dnl +AC_REQUIRE([AC_PROG_NM])dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +dnl + +# Check for any special flags to pass to ltconfig. +# +# the following will cause an existing older ltconfig to fail, so +# we ignore this at the expense of the cache file... Checking this +# will just take longer ... bummer! +#libtool_flags="--cache-file=$cache_file" +# +test "$enable_shared" = no && libtool_flags="$libtool_flags --disable-shared" +test "$enable_static" = no && libtool_flags="$libtool_flags --disable-static" +test "$enable_fast_install" = no && libtool_flags="$libtool_flags --disable-fast-install" +test "$ac_cv_prog_gcc" = yes && libtool_flags="$libtool_flags --with-gcc" +test "$ac_cv_prog_gnu_ld" = yes && libtool_flags="$libtool_flags --with-gnu-ld" +ifdef([AC_PROVIDE_AC_LIBTOOL_DLOPEN], +[libtool_flags="$libtool_flags --enable-dlopen"]) +ifdef([AC_PROVIDE_AC_LIBTOOL_WIN32_DLL], +[libtool_flags="$libtool_flags --enable-win32-dll"]) +AC_ARG_ENABLE(libtool-lock, + [ --disable-libtool-lock avoid locking (might break parallel builds)]) +test "x$enable_libtool_lock" = xno && libtool_flags="$libtool_flags --disable-lock" +test x"$silent" = xyes && libtool_flags="$libtool_flags --silent" + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case "$host" in +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line __oline__ "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case "`/usr/bin/file conftest.o`" in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; + +ifdef([AC_PROVIDE_AC_LIBTOOL_WIN32_DLL], +[*-*-cygwin* | *-*-mingw*) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +]) +esac +]) + +# AC_LIBTOOL_DLOPEN - enable checks for dlopen support +AC_DEFUN(AC_LIBTOOL_DLOPEN, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])]) + +# AC_LIBTOOL_WIN32_DLL - declare package support for building win32 dll's +AC_DEFUN(AC_LIBTOOL_WIN32_DLL, [AC_BEFORE([$0], [AC_LIBTOOL_SETUP])]) + +# AC_ENABLE_SHARED - implement the --enable-shared flag +# Usage: AC_ENABLE_SHARED[(DEFAULT)] +# Where DEFAULT is either `yes' or `no'. If omitted, it defaults to +# `yes'. +AC_DEFUN(AC_ENABLE_SHARED, [dnl +define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE(shared, +changequote(<<, >>)dnl +<< --enable-shared[=PKGS] build shared libraries [default=>>AC_ENABLE_SHARED_DEFAULT], +changequote([, ])dnl +[p=${PACKAGE-default} +case "$enableval" in +yes) enable_shared=yes ;; +no) enable_shared=no ;; +*) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:," + for pkg in $enableval; do + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$ac_save_ifs" + ;; +esac], +enable_shared=AC_ENABLE_SHARED_DEFAULT)dnl +]) + +# AC_DISABLE_SHARED - set the default shared flag to --disable-shared +AC_DEFUN(AC_DISABLE_SHARED, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_SHARED(no)]) + +# AC_ENABLE_STATIC - implement the --enable-static flag +# Usage: AC_ENABLE_STATIC[(DEFAULT)] +# Where DEFAULT is either `yes' or `no'. If omitted, it defaults to +# `yes'. +AC_DEFUN(AC_ENABLE_STATIC, [dnl +define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE(static, +changequote(<<, >>)dnl +<< --enable-static[=PKGS] build static libraries [default=>>AC_ENABLE_STATIC_DEFAULT], +changequote([, ])dnl +[p=${PACKAGE-default} +case "$enableval" in +yes) enable_static=yes ;; +no) enable_static=no ;; +*) + enable_static=no + # Look at the argument we got. We use all the common list separators. + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:," + for pkg in $enableval; do + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$ac_save_ifs" + ;; +esac], +enable_static=AC_ENABLE_STATIC_DEFAULT)dnl +]) + +# AC_DISABLE_STATIC - set the default static flag to --disable-static +AC_DEFUN(AC_DISABLE_STATIC, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_STATIC(no)]) + + +# AC_ENABLE_FAST_INSTALL - implement the --enable-fast-install flag +# Usage: AC_ENABLE_FAST_INSTALL[(DEFAULT)] +# Where DEFAULT is either `yes' or `no'. If omitted, it defaults to +# `yes'. +AC_DEFUN(AC_ENABLE_FAST_INSTALL, [dnl +define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl +AC_ARG_ENABLE(fast-install, +changequote(<<, >>)dnl +<< --enable-fast-install[=PKGS] optimize for fast installation [default=>>AC_ENABLE_FAST_INSTALL_DEFAULT], +changequote([, ])dnl +[p=${PACKAGE-default} +case "$enableval" in +yes) enable_fast_install=yes ;; +no) enable_fast_install=no ;; +*) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:," + for pkg in $enableval; do + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$ac_save_ifs" + ;; +esac], +enable_fast_install=AC_ENABLE_FAST_INSTALL_DEFAULT)dnl +]) + +# AC_ENABLE_FAST_INSTALL - set the default to --disable-fast-install +AC_DEFUN(AC_DISABLE_FAST_INSTALL, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl +AC_ENABLE_FAST_INSTALL(no)]) + +# AC_PROG_LD - find the path to the GNU or non-GNU linker +AC_DEFUN(AC_PROG_LD, +[AC_ARG_WITH(gnu-ld, +[ --with-gnu-ld assume the C compiler uses GNU ld [default=no]], +test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no) +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +ac_prog=ld +if test "$ac_cv_prog_gcc" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by GCC]) + ac_prog=`($CC -print-prog-name=ld) 2>&5` + case "$ac_prog" in + # Accept absolute paths. +changequote(,)dnl + [\\/]* | [A-Za-z]:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' +changequote([,])dnl + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(ac_cv_path_LD, +[if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + ac_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + if "$ac_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then + test "$with_gnu_ld" != no && break + else + test "$with_gnu_ld" != yes && break + fi + fi + done + IFS="$ac_save_ifs" +else + ac_cv_path_LD="$LD" # Let the user override the test with a path. +fi]) +LD="$ac_cv_path_LD" +if test -n "$LD"; then + AC_MSG_RESULT($LD) +else + AC_MSG_RESULT(no) +fi +test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) +AC_SUBST(LD) +AC_PROG_LD_GNU +]) + +AC_DEFUN(AC_PROG_LD_GNU, +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld, +[# I'd rather use --version here, but apparently some GNU ld's only accept -v. +if $LD -v 2>&1 &5; then + ac_cv_prog_gnu_ld=yes +else + ac_cv_prog_gnu_ld=no +fi]) +]) + +# AC_PROG_NM - find the path to a BSD-compatible name lister +AC_DEFUN(AC_PROG_NM, +[AC_MSG_CHECKING([for BSD-compatible nm]) +AC_CACHE_VAL(ac_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + ac_cv_path_NM="$NM" +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH /usr/ccs/bin /usr/ucb /bin; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/nm || test -f $ac_dir/nm$ac_exeext ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + if ($ac_dir/nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + ac_cv_path_NM="$ac_dir/nm -B" + break + elif ($ac_dir/nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + ac_cv_path_NM="$ac_dir/nm -p" + break + else + ac_cv_path_NM=${ac_cv_path_NM="$ac_dir/nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + fi + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_path_NM" && ac_cv_path_NM=nm +fi]) +NM="$ac_cv_path_NM" +AC_MSG_RESULT([$NM]) +AC_SUBST(NM) +]) + +# AC_CHECK_LIBM - check for math library +AC_DEFUN(AC_CHECK_LIBM, +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case "$host" in +*-*-beos* | *-*-cygwin*) + # These system don't have libm + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, main, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, main, LIBM="-lm") + ;; +esac +]) + +# AC_LIBLTDL_CONVENIENCE[(dir)] - sets LIBLTDL to the link flags for +# the libltdl convenience library, adds --enable-ltdl-convenience to +# the configure arguments. Note that LIBLTDL is not AC_SUBSTed, nor +# is AC_CONFIG_SUBDIRS called. If DIR is not provided, it is assumed +# to be `${top_builddir}/libltdl'. Make sure you start DIR with +# '${top_builddir}/' (note the single quotes!) if your package is not +# flat, and, if you're not using automake, define top_builddir as +# appropriate in the Makefiles. +AC_DEFUN(AC_LIBLTDL_CONVENIENCE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + case "$enable_ltdl_convenience" in + no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; + "") enable_ltdl_convenience=yes + ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; + esac + LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdlc.la + INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl']) +]) + +# AC_LIBLTDL_INSTALLABLE[(dir)] - sets LIBLTDL to the link flags for +# the libltdl installable library, and adds --enable-ltdl-install to +# the configure arguments. Note that LIBLTDL is not AC_SUBSTed, nor +# is AC_CONFIG_SUBDIRS called. If DIR is not provided, it is assumed +# to be `${top_builddir}/libltdl'. Make sure you start DIR with +# '${top_builddir}/' (note the single quotes!) if your package is not +# flat, and, if you're not using automake, define top_builddir as +# appropriate in the Makefiles. +# In the future, this macro may have to be called after AC_PROG_LIBTOOL. +AC_DEFUN(AC_LIBLTDL_INSTALLABLE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl + AC_CHECK_LIB(ltdl, main, + [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no], + [if test x"$enable_ltdl_install" = xno; then + AC_MSG_WARN([libltdl not installed, but installation disabled]) + else + enable_ltdl_install=yes + fi + ]) + if test x"$enable_ltdl_install" = x"yes"; then + ac_configure_args="$ac_configure_args --enable-ltdl-install" + LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdl.la + INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl']) + else + ac_configure_args="$ac_configure_args --enable-ltdl-install=no" + LIBLTDL="-lltdl" + INCLTDL= + fi +]) + +dnl old names +AC_DEFUN(AM_PROG_LIBTOOL, [indir([AC_PROG_LIBTOOL])])dnl +AC_DEFUN(AM_ENABLE_SHARED, [indir([AC_ENABLE_SHARED], $@)])dnl +AC_DEFUN(AM_ENABLE_STATIC, [indir([AC_ENABLE_STATIC], $@)])dnl +AC_DEFUN(AM_DISABLE_SHARED, [indir([AC_DISABLE_SHARED], $@)])dnl +AC_DEFUN(AM_DISABLE_STATIC, [indir([AC_DISABLE_STATIC], $@)])dnl +AC_DEFUN(AM_PROG_LD, [indir([AC_PROG_LD])])dnl +AC_DEFUN(AM_PROG_NM, [indir([AC_PROG_NM])])dnl + +dnl This is just to silence aclocal about the macro not being used +ifelse([AC_DISABLE_FAST_INSTALL])dnl diff --git a/dtool/dtool_config.h b/dtool/dtool_config.h new file mode 100644 index 0000000000..91eca522e9 --- /dev/null +++ b/dtool/dtool_config.h @@ -0,0 +1,96 @@ +/* dtool_config.h. Generated automatically by ppremake 0.20 from Sources.pp. */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if the X Window System is missing or not being used. */ +#undef X_DISPLAY_MISSING + +/* Define if lex declares yytext as a char * by default, not a char[]. */ +#undef YYTEXT_POINTER + +/* Define if the C++ compiler uses namespaces. */ +#define HAVE_NAMESPACE 1 + +/* Define if the C++ iostream library supports ios::binary. */ +#define HAVE_IOS_BINARY 1 + +/* Define if we have Python installed. */ +#define HAVE_PYTHON 1 + +/* Define if we have NSPR installed. */ +#define HAVE_NSPR 1 + +/* Define if we have VRPN installed. */ +#undef HAVE_VRPN + +/* Define if we have zlib installed. */ +#define HAVE_ZLIB 1 + +/* Define if we have OpenGL installed and want to build for GL. */ +#define HAVE_GL 1 + +/* Define if we have GLU installed. */ +#undef HAVE_GLU + +/* Define if we have GLX installed and want to build for GLX. */ +#define HAVE_GLX 1 + +/* Define if we have Glut installed and want to build for Glut. */ +#undef HAVE_GLUT + +/* Define if we want to build the Renderman interface. */ +#undef HAVE_RIB + +/* Define if we want to use mikmod for audio. */ +#undef HAVE_MIKMOD + +/* Define if we have a gettimeofday() function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if gettimeofday() takes only one parameter. */ +#undef GETTIMEOFDAY_ONE_PARAM + +/* Define if you have the getopt function. */ +#define HAVE_GETOPT 1 + +/* Define if you have the getopt_long_only function. */ +#define HAVE_GETOPT_LONG_ONLY 1 + +/* Define if you have the header file. */ +#define HAVE_ALLOCA_H 1 + +/* Define if you have the header file. */ +#undef HAVE_IO_H + +/* Define if you have the header file. */ +#undef HAVE_IOSTREAM + +/* Define if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define if you have the header file. */ +#define HAVE_ALLOCA_H 1 + +/* Define if you have the header file. */ +#undef HAVE_MINMAX_H + +/* Define if you have the header file. */ +#undef HAVE_SSTREAM + +/* Define if you have the header file. */ +#undef HAVE_SYS_TYPES + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE dtool + +/* Version number of package */ +#define VERSION 0.80 + diff --git a/dtool/metalibs/Sources.pp b/dtool/metalibs/Sources.pp new file mode 100644 index 0000000000..93e28f9e7c --- /dev/null +++ b/dtool/metalibs/Sources.pp @@ -0,0 +1,7 @@ +// This is a group directory: a directory level above a number of +// source subdirectories. + +#define DIR_TYPE group + +// The metalibs directory always depends on the src directory. +#define DEPENDS src diff --git a/dtool/metalibs/dtool/Sources.pp b/dtool/metalibs/dtool/Sources.pp new file mode 100644 index 0000000000..fb0ed72444 --- /dev/null +++ b/dtool/metalibs/dtool/Sources.pp @@ -0,0 +1,17 @@ +// DIR_TYPE "metalib" indicates we are building a shared library that +// consists mostly of references to other shared libraries. Under +// Windows, this directly produces a DLL (as opposed to the regular +// src libraries, which don't produce anything but a pile of OBJ files +// under Windows). + +#define DIR_TYPE metalib +#define BUILDING_DLL BUILDING_DTOOL + +#define LOCAL_LIBS interrogatedb dconfig dtoolutil dtoolbase + +#begin metalib_target + #define TARGET dtool + + #define SOURCES dtool.cxx + #define IF_PYTHON_SOURCES pydtool.cxx +#end metalib_target diff --git a/dtool/metalibs/dtool/dtool.cxx b/dtool/metalibs/dtool/dtool.cxx new file mode 100644 index 0000000000..9599e11936 --- /dev/null +++ b/dtool/metalibs/dtool/dtool.cxx @@ -0,0 +1,11 @@ +// Filename: dtool.C +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +// This is a dummy file whose sole purpose is to give the compiler +// something to compile when making libdtool.so in NO_DEFER mode, +// which generates an empty library that itself links with all the +// other shared libraries that make up libdtool. + + diff --git a/dtool/metalibs/dtool/pydtool.cxx b/dtool/metalibs/dtool/pydtool.cxx new file mode 100644 index 0000000000..02e2f8ebed --- /dev/null +++ b/dtool/metalibs/dtool/pydtool.cxx @@ -0,0 +1,1680 @@ +/* + * This file generated by: + * interrogate -nodb -python -promiscuous -I /usr/local/dtool/include -module dtool -library libdtool -true-names -do-module -oc pydtool.cc interrogate_interface.h + * + */ + + +#include "interrogate_interface.h" +#include + +#undef HAVE_LONG_LONG +#include + +extern "C" { + +/* + * Python wrapper for + * int interrogate_number_of_manifests(); + */ +static PyObject * +_inPCPDJwgip(PyObject *, PyObject *args) { + if (PyArg_ParseTuple(args, "")) { + int return_value = interrogate_number_of_manifests(); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_manifest(int n); + */ +static PyObject * +_inPCPDJKkDz(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_get_manifest((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_manifest_by_name(char const *manifest_name); + */ +static PyObject * +_inPCPDJZz9N(PyObject *, PyObject *args) { + char *param0; + if (PyArg_ParseTuple(args, "s", ¶m0)) { + int return_value = interrogate_get_manifest_by_name((char const *)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_manifest_name(int manifest); + */ +static PyObject * +_inPCPDJKqIx(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_manifest_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_manifest_definition(int manifest); + */ +static PyObject * +_inPCPDJ_uSj(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_manifest_definition((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_manifest_has_type(int manifest); + */ +static PyObject * +_inPCPDJsBN6(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_manifest_has_type((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_manifest_get_type(int manifest); + */ +static PyObject * +_inPCPDJc8Lg(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_manifest_get_type((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_manifest_has_getter(int manifest); + */ +static PyObject * +_inPCPDJ3MkE(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_manifest_has_getter((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_manifest_getter(int manifest); + */ +static PyObject * +_inPCPDJsk9F(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_manifest_getter((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_manifest_has_int_value(int manifest); + */ +static PyObject * +_inPCPDJC9u_(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_manifest_has_int_value((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_manifest_get_int_value(int manifest); + */ +static PyObject * +_inPCPDJS_tk(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_manifest_get_int_value((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_element_name(int element); + */ +static PyObject * +_inPCPDJ_U2J(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_element_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_element_scoped_name(int element); + */ +static PyObject * +_inPCPDJZ4Us(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_element_scoped_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_element_by_name(char const *element_name); + */ +static PyObject * +_inPCPDJd695(PyObject *, PyObject *args) { + char *param0; + if (PyArg_ParseTuple(args, "s", ¶m0)) { + int return_value = interrogate_get_element_by_name((char const *)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_element_by_scoped_name(char const *element_name); + */ +static PyObject * +_inPCPDJeISc(PyObject *, PyObject *args) { + char *param0; + if (PyArg_ParseTuple(args, "s", ¶m0)) { + int return_value = interrogate_get_element_by_scoped_name((char const *)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_element_type(int element); + */ +static PyObject * +_inPCPDJpHUf(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_element_type((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_element_has_getter(int element); + */ +static PyObject * +_inPCPDJk5ao(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_element_has_getter((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_element_getter(int element); + */ +static PyObject * +_inPCPDJV4dk(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_element_getter((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_element_has_setter(int element); + */ +static PyObject * +_inPCPDJQApo(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_element_has_setter((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_element_setter(int element); + */ +static PyObject * +_inPCPDJV_Io(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_element_setter((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_number_of_globals(); + */ +static PyObject * +_inPCPDJNQAC(PyObject *, PyObject *args) { + if (PyArg_ParseTuple(args, "")) { + int return_value = interrogate_number_of_globals(); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_global(int n); + */ +static PyObject * +_inPCPDJAfO2(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_get_global((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_number_of_global_functions(); + */ +static PyObject * +_inPCPDJV5jm(PyObject *, PyObject *args) { + if (PyArg_ParseTuple(args, "")) { + int return_value = interrogate_number_of_global_functions(); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_global_function(int n); + */ +static PyObject * +_inPCPDJwsjw(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_get_global_function((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_number_of_functions(); + */ +static PyObject * +_inPCPDJZjcX(PyObject *, PyObject *args) { + if (PyArg_ParseTuple(args, "")) { + int return_value = interrogate_number_of_functions(); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_function(int n); + */ +static PyObject * +_inPCPDJR_w6(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_get_function((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_function_name(int function); + */ +static PyObject * +_inPCPDJw6ns(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_function_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_function_scoped_name(int function); + */ +static PyObject * +_inPCPDJGX78(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_function_scoped_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_function_has_comment(int type); + */ +static PyObject * +_inPCPDJ_iVJ(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_function_has_comment((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_function_comment(int type); + */ +static PyObject * +_inPCPDJg3TW(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_function_comment((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_function_prototype(int type); + */ +static PyObject * +_inPCPDJi9Wj(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_function_prototype((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_function_is_method(int function); + */ +static PyObject * +_inPCPDJG2QW(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_function_is_method((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_function_class(int function); + */ +static PyObject * +_inPCPDJIZTX(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_function_class((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_function_has_module_name(int function); + */ +static PyObject * +_inPCPDJu8Rq(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_function_has_module_name((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_function_module_name(int function); + */ +static PyObject * +_inPCPDJ9_el(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_function_module_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_function_is_virtual(int function); + */ +static PyObject * +_inPCPDJaus9(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_function_is_virtual((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_function_number_of_c_wrappers(int function); + */ +static PyObject * +_inPCPDJaf25(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_function_number_of_c_wrappers((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_function_c_wrapper(int function, int n); + */ +static PyObject * +_inPCPDJnPrB(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_function_c_wrapper((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_function_number_of_python_wrappers(int function); + */ +static PyObject * +_inPCPDJDmkY(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_function_number_of_python_wrappers((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_function_python_wrapper(int function, int n); + */ +static PyObject * +_inPCPDJKL_W(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_function_python_wrapper((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_wrapper_name(int wrapper); + */ +static PyObject * +_inPCPDJtc9G(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_wrapper_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_wrapper_is_callable_by_name(int wrapper); + */ +static PyObject * +_inPCPDJa0Pp(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_wrapper_is_callable_by_name((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_wrapper_has_return_value(int wrapper); + */ +static PyObject * +_inPCPDJ9GJl(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_wrapper_has_return_value((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_wrapper_return_type(int wrapper); + */ +static PyObject * +_inPCPDJKfmo(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_wrapper_return_type((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_wrapper_caller_manages_return_value(int wrapper); + */ +static PyObject * +_inPCPDJPhA0(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_wrapper_caller_manages_return_value((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_wrapper_return_value_destructor(int wrapper); + */ +static PyObject * +_inPCPDJVMJl(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_wrapper_return_value_destructor((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_wrapper_number_of_parameters(int wrapper); + */ +static PyObject * +_inPCPDJ6ACv(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_wrapper_number_of_parameters((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_wrapper_parameter_type(int wrapper, int n); + */ +static PyObject * +_inPCPDJNJX7(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_wrapper_parameter_type((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_wrapper_parameter_has_name(int wrapper, int n); + */ +static PyObject * +_inPCPDJI3Xf(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + bool return_value = interrogate_wrapper_parameter_has_name((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_wrapper_parameter_name(int wrapper, int n); + */ +static PyObject * +_inPCPDJWOel(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + char const *return_value = interrogate_wrapper_parameter_name((int)param0, (int)param1); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_wrapper_parameter_is_this(int wrapper, int n); + */ +static PyObject * +_inPCPDJWvbH(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + bool return_value = interrogate_wrapper_parameter_is_this((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_wrapper_has_pointer(int wrapper); + */ +static PyObject * +_inPCPDJ6JG0(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_wrapper_has_pointer((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_wrapper_unique_name(int wrapper); + */ +static PyObject * +_inPCPDJauwR(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_wrapper_unique_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_wrapper_by_unique_name(char const *unique_name); + */ +static PyObject * +_inPCPDJ5OfF(PyObject *, PyObject *args) { + char *param0; + if (PyArg_ParseTuple(args, "s", ¶m0)) { + int return_value = interrogate_get_wrapper_by_unique_name((char const *)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_number_of_global_types(); + */ +static PyObject * +_inPCPDJlLys(PyObject *, PyObject *args) { + if (PyArg_ParseTuple(args, "")) { + int return_value = interrogate_number_of_global_types(); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_global_type(int n); + */ +static PyObject * +_inPCPDJFt0z(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_get_global_type((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_number_of_types(); + */ +static PyObject * +_inPCPDJbw3v(PyObject *, PyObject *args) { + if (PyArg_ParseTuple(args, "")) { + int return_value = interrogate_number_of_types(); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_type(int n); + */ +static PyObject * +_inPCPDJxpx8(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_get_type((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_type_by_name(char const *type_name); + */ +static PyObject * +_inPCPDJGhcs(PyObject *, PyObject *args) { + char *param0; + if (PyArg_ParseTuple(args, "s", ¶m0)) { + int return_value = interrogate_get_type_by_name((char const *)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_type_by_scoped_name(char const *type_name); + */ +static PyObject * +_inPCPDJjhrb(PyObject *, PyObject *args) { + char *param0; + if (PyArg_ParseTuple(args, "s", ¶m0)) { + int return_value = interrogate_get_type_by_scoped_name((char const *)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_get_type_by_true_name(char const *type_name); + */ +static PyObject * +_inPCPDJToqq(PyObject *, PyObject *args) { + char *param0; + if (PyArg_ParseTuple(args, "s", ¶m0)) { + int return_value = interrogate_get_type_by_true_name((char const *)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_type_name(int type); + */ +static PyObject * +_inPCPDJoIf5(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_type_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_type_scoped_name(int type); + */ +static PyObject * +_inPCPDJ6Xo_(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_type_scoped_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_type_true_name(int type); + */ +static PyObject * +_inPCPDJ8eET(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_type_true_name((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_nested(int type); + */ +static PyObject * +_inPCPDJNMOE(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_nested((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_outer_class(int type); + */ +static PyObject * +_inPCPDJidlm(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_outer_class((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_has_comment(int type); + */ +static PyObject * +_inPCPDJ5FdF(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_has_comment((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_type_comment(int type); + */ +static PyObject * +_inPCPDJR9bU(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + char const *return_value = interrogate_type_comment((int)param0); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_atomic(int type); + */ +static PyObject * +_inPCPDJqZbO(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_atomic((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * AtomicToken interrogate_type_atomic_token(int type); + */ +static PyObject * +_inPCPDJiCgZ(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + AtomicToken return_value = interrogate_type_atomic_token((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_unsigned(int type); + */ +static PyObject * +_inPCPDJvj9C(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_unsigned((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_signed(int type); + */ +static PyObject * +_inPCPDJ5Tjz(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_signed((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_long(int type); + */ +static PyObject * +_inPCPDJTV9E(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_long((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_longlong(int type); + */ +static PyObject * +_inPCPDJPd3W(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_longlong((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_short(int type); + */ +static PyObject * +_inPCPDJRIvd(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_short((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_wrapped(int type); + */ +static PyObject * +_inPCPDJeAKS(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_wrapped((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_pointer(int type); + */ +static PyObject * +_inPCPDJE_rr(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_pointer((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_const(int type); + */ +static PyObject * +_inPCPDJpZxy(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_const((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_wrapped_type(int type); + */ +static PyObject * +_inPCPDJZ__l(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_wrapped_type((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_enum(int type); + */ +static PyObject * +_inPCPDJQDIY(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_enum((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_number_of_enum_values(int type); + */ +static PyObject * +_inPCPDJA7Ig(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_number_of_enum_values((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_type_enum_value_name(int type, int n); + */ +static PyObject * +_inPCPDJY50T(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + char const *return_value = interrogate_type_enum_value_name((int)param0, (int)param1); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * char const *interrogate_type_enum_value_scoped_name(int type, int n); + */ +static PyObject * +_inPCPDJypPX(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + char const *return_value = interrogate_type_enum_value_scoped_name((int)param0, (int)param1); + return Py_BuildValue("s", return_value); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_enum_value(int type, int n); + */ +static PyObject * +_inPCPDJIKx5(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_enum_value((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_struct(int type); + */ +static PyObject * +_inPCPDJI_e3(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_struct((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_class(int type); + */ +static PyObject * +_inPCPDJCAx3(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_class((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_union(int type); + */ +static PyObject * +_inPCPDJKOMc(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_union((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_fully_defined(int type); + */ +static PyObject * +_inPCPDJVUhE(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_fully_defined((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_is_unpublished(int type); + */ +static PyObject * +_inPCPDJu8T0(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_is_unpublished((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_number_of_constructors(int type); + */ +static PyObject * +_inPCPDJsPNn(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_number_of_constructors((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_constructor(int type, int n); + */ +static PyObject * +_inPCPDJP_f2(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_get_constructor((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_has_destructor(int type); + */ +static PyObject * +_inPCPDJuE9q(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_has_destructor((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_destructor_is_inherited(int type); + */ +static PyObject * +_inPCPDJh1o6(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + bool return_value = interrogate_type_destructor_is_inherited((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_destructor(int type); + */ +static PyObject * +_inPCPDJc4vK(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_get_destructor((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_number_of_elements(int type); + */ +static PyObject * +_inPCPDJXbpM(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_number_of_elements((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_element(int type, int n); + */ +static PyObject * +_inPCPDJYAfN(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_get_element((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_number_of_methods(int type); + */ +static PyObject * +_inPCPDJDbCl(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_number_of_methods((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_method(int type, int n); + */ +static PyObject * +_inPCPDJ2R7K(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_get_method((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_number_of_casts(int type); + */ +static PyObject * +_inPCPDJg6iq(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_number_of_casts((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_cast(int type, int n); + */ +static PyObject * +_inPCPDJNxR0(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_get_cast((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_number_of_derivations(int type); + */ +static PyObject * +_inPCPDJ8n7D(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_number_of_derivations((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_derivation(int type, int n); + */ +static PyObject * +_inPCPDJazFz(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_get_derivation((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_derivation_has_upcast(int type, int n); + */ +static PyObject * +_inPCPDJgkPe(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + bool return_value = interrogate_type_derivation_has_upcast((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_upcast(int type, int n); + */ +static PyObject * +_inPCPDJIRmS(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_get_upcast((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_derivation_downcast_is_impossible(int type, int n); + */ +static PyObject * +_inPCPDJN_Lp(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + bool return_value = interrogate_type_derivation_downcast_is_impossible((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * bool interrogate_type_derivation_has_downcast(int type, int n); + */ +static PyObject * +_inPCPDJ2xei(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + bool return_value = interrogate_type_derivation_has_downcast((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_downcast(int type, int n); + */ +static PyObject * +_inPCPDJTgkE(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_get_downcast((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_number_of_nested_types(int type); + */ +static PyObject * +_inPCPDJh4_l(PyObject *, PyObject *args) { + int param0; + if (PyArg_ParseTuple(args, "i", ¶m0)) { + int return_value = interrogate_type_number_of_nested_types((int)param0); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +/* + * Python wrapper for + * int interrogate_type_get_nested_type(int type, int n); + */ +static PyObject * +_inPCPDJnedH(PyObject *, PyObject *args) { + int param0; + int param1; + if (PyArg_ParseTuple(args, "ii", ¶m0, ¶m1)) { + int return_value = interrogate_type_get_nested_type((int)param0, (int)param1); + return Py_BuildValue("i", (int)(return_value)); + } + return (PyObject *)NULL; +} + +} /* close extern "C" */ + +static PyMethodDef python_methods[] = { + { "interrogate_type_get_method", &_inPCPDJ2R7K, METH_VARARGS }, + { "interrogate_type_derivation_has_downcast", &_inPCPDJ2xei, METH_VARARGS }, + { "interrogate_manifest_has_getter", &_inPCPDJ3MkE, METH_VARARGS }, + { "interrogate_type_has_comment", &_inPCPDJ5FdF, METH_VARARGS }, + { "interrogate_get_wrapper_by_unique_name", &_inPCPDJ5OfF, METH_VARARGS }, + { "interrogate_type_is_signed", &_inPCPDJ5Tjz, METH_VARARGS }, + { "interrogate_wrapper_number_of_parameters", &_inPCPDJ6ACv, METH_VARARGS }, + { "interrogate_wrapper_has_pointer", &_inPCPDJ6JG0, METH_VARARGS }, + { "interrogate_type_scoped_name", &_inPCPDJ6Xo_, METH_VARARGS }, + { "interrogate_type_true_name", &_inPCPDJ8eET, METH_VARARGS }, + { "interrogate_type_number_of_derivations", &_inPCPDJ8n7D, METH_VARARGS }, + { "interrogate_wrapper_has_return_value", &_inPCPDJ9GJl, METH_VARARGS }, + { "interrogate_function_module_name", &_inPCPDJ9_el, METH_VARARGS }, + { "interrogate_type_number_of_enum_values", &_inPCPDJA7Ig, METH_VARARGS }, + { "interrogate_get_global", &_inPCPDJAfO2, METH_VARARGS }, + { "interrogate_manifest_has_int_value", &_inPCPDJC9u_, METH_VARARGS }, + { "interrogate_type_is_class", &_inPCPDJCAx3, METH_VARARGS }, + { "interrogate_type_number_of_methods", &_inPCPDJDbCl, METH_VARARGS }, + { "interrogate_function_number_of_python_wrappers", &_inPCPDJDmkY, METH_VARARGS }, + { "interrogate_type_is_pointer", &_inPCPDJE_rr, METH_VARARGS }, + { "interrogate_get_global_type", &_inPCPDJFt0z, METH_VARARGS }, + { "interrogate_function_is_method", &_inPCPDJG2QW, METH_VARARGS }, + { "interrogate_function_scoped_name", &_inPCPDJGX78, METH_VARARGS }, + { "interrogate_get_type_by_name", &_inPCPDJGhcs, METH_VARARGS }, + { "interrogate_wrapper_parameter_has_name", &_inPCPDJI3Xf, METH_VARARGS }, + { "interrogate_type_enum_value", &_inPCPDJIKx5, METH_VARARGS }, + { "interrogate_type_get_upcast", &_inPCPDJIRmS, METH_VARARGS }, + { "interrogate_function_class", &_inPCPDJIZTX, METH_VARARGS }, + { "interrogate_type_is_struct", &_inPCPDJI_e3, METH_VARARGS }, + { "interrogate_function_python_wrapper", &_inPCPDJKL_W, METH_VARARGS }, + { "interrogate_type_is_union", &_inPCPDJKOMc, METH_VARARGS }, + { "interrogate_wrapper_return_type", &_inPCPDJKfmo, METH_VARARGS }, + { "interrogate_get_manifest", &_inPCPDJKkDz, METH_VARARGS }, + { "interrogate_manifest_name", &_inPCPDJKqIx, METH_VARARGS }, + { "interrogate_wrapper_parameter_type", &_inPCPDJNJX7, METH_VARARGS }, + { "interrogate_type_is_nested", &_inPCPDJNMOE, METH_VARARGS }, + { "interrogate_number_of_globals", &_inPCPDJNQAC, METH_VARARGS }, + { "interrogate_type_derivation_downcast_is_impossible", &_inPCPDJN_Lp, METH_VARARGS }, + { "interrogate_type_get_cast", &_inPCPDJNxR0, METH_VARARGS }, + { "interrogate_type_get_constructor", &_inPCPDJP_f2, METH_VARARGS }, + { "interrogate_type_is_longlong", &_inPCPDJPd3W, METH_VARARGS }, + { "interrogate_wrapper_caller_manages_return_value", &_inPCPDJPhA0, METH_VARARGS }, + { "interrogate_element_has_setter", &_inPCPDJQApo, METH_VARARGS }, + { "interrogate_type_is_enum", &_inPCPDJQDIY, METH_VARARGS }, + { "interrogate_type_comment", &_inPCPDJR9bU, METH_VARARGS }, + { "interrogate_type_is_short", &_inPCPDJRIvd, METH_VARARGS }, + { "interrogate_get_function", &_inPCPDJR_w6, METH_VARARGS }, + { "interrogate_manifest_get_int_value", &_inPCPDJS_tk, METH_VARARGS }, + { "interrogate_type_is_long", &_inPCPDJTV9E, METH_VARARGS }, + { "interrogate_type_get_downcast", &_inPCPDJTgkE, METH_VARARGS }, + { "interrogate_get_type_by_true_name", &_inPCPDJToqq, METH_VARARGS }, + { "interrogate_element_getter", &_inPCPDJV4dk, METH_VARARGS }, + { "interrogate_number_of_global_functions", &_inPCPDJV5jm, METH_VARARGS }, + { "interrogate_wrapper_return_value_destructor", &_inPCPDJVMJl, METH_VARARGS }, + { "interrogate_type_is_fully_defined", &_inPCPDJVUhE, METH_VARARGS }, + { "interrogate_element_setter", &_inPCPDJV_Io, METH_VARARGS }, + { "interrogate_wrapper_parameter_name", &_inPCPDJWOel, METH_VARARGS }, + { "interrogate_wrapper_parameter_is_this", &_inPCPDJWvbH, METH_VARARGS }, + { "interrogate_type_number_of_elements", &_inPCPDJXbpM, METH_VARARGS }, + { "interrogate_type_enum_value_name", &_inPCPDJY50T, METH_VARARGS }, + { "interrogate_type_get_element", &_inPCPDJYAfN, METH_VARARGS }, + { "interrogate_element_scoped_name", &_inPCPDJZ4Us, METH_VARARGS }, + { "interrogate_type_wrapped_type", &_inPCPDJZ__l, METH_VARARGS }, + { "interrogate_number_of_functions", &_inPCPDJZjcX, METH_VARARGS }, + { "interrogate_get_manifest_by_name", &_inPCPDJZz9N, METH_VARARGS }, + { "interrogate_element_name", &_inPCPDJ_U2J, METH_VARARGS }, + { "interrogate_function_has_comment", &_inPCPDJ_iVJ, METH_VARARGS }, + { "interrogate_manifest_definition", &_inPCPDJ_uSj, METH_VARARGS }, + { "interrogate_wrapper_is_callable_by_name", &_inPCPDJa0Pp, METH_VARARGS }, + { "interrogate_function_number_of_c_wrappers", &_inPCPDJaf25, METH_VARARGS }, + { "interrogate_function_is_virtual", &_inPCPDJaus9, METH_VARARGS }, + { "interrogate_wrapper_unique_name", &_inPCPDJauwR, METH_VARARGS }, + { "interrogate_type_get_derivation", &_inPCPDJazFz, METH_VARARGS }, + { "interrogate_number_of_types", &_inPCPDJbw3v, METH_VARARGS }, + { "interrogate_type_get_destructor", &_inPCPDJc4vK, METH_VARARGS }, + { "interrogate_manifest_get_type", &_inPCPDJc8Lg, METH_VARARGS }, + { "interrogate_get_element_by_name", &_inPCPDJd695, METH_VARARGS }, + { "interrogate_type_is_wrapped", &_inPCPDJeAKS, METH_VARARGS }, + { "interrogate_get_element_by_scoped_name", &_inPCPDJeISc, METH_VARARGS }, + { "interrogate_function_comment", &_inPCPDJg3TW, METH_VARARGS }, + { "interrogate_type_number_of_casts", &_inPCPDJg6iq, METH_VARARGS }, + { "interrogate_type_derivation_has_upcast", &_inPCPDJgkPe, METH_VARARGS }, + { "interrogate_type_destructor_is_inherited", &_inPCPDJh1o6, METH_VARARGS }, + { "interrogate_type_number_of_nested_types", &_inPCPDJh4_l, METH_VARARGS }, + { "interrogate_function_prototype", &_inPCPDJi9Wj, METH_VARARGS }, + { "interrogate_type_atomic_token", &_inPCPDJiCgZ, METH_VARARGS }, + { "interrogate_type_outer_class", &_inPCPDJidlm, METH_VARARGS }, + { "interrogate_get_type_by_scoped_name", &_inPCPDJjhrb, METH_VARARGS }, + { "interrogate_element_has_getter", &_inPCPDJk5ao, METH_VARARGS }, + { "interrogate_number_of_global_types", &_inPCPDJlLys, METH_VARARGS }, + { "interrogate_function_c_wrapper", &_inPCPDJnPrB, METH_VARARGS }, + { "interrogate_type_get_nested_type", &_inPCPDJnedH, METH_VARARGS }, + { "interrogate_type_name", &_inPCPDJoIf5, METH_VARARGS }, + { "interrogate_element_type", &_inPCPDJpHUf, METH_VARARGS }, + { "interrogate_type_is_const", &_inPCPDJpZxy, METH_VARARGS }, + { "interrogate_type_is_atomic", &_inPCPDJqZbO, METH_VARARGS }, + { "interrogate_manifest_has_type", &_inPCPDJsBN6, METH_VARARGS }, + { "interrogate_type_number_of_constructors", &_inPCPDJsPNn, METH_VARARGS }, + { "interrogate_manifest_getter", &_inPCPDJsk9F, METH_VARARGS }, + { "interrogate_wrapper_name", &_inPCPDJtc9G, METH_VARARGS }, + { "interrogate_function_has_module_name", &_inPCPDJu8Rq, METH_VARARGS }, + { "interrogate_type_is_unpublished", &_inPCPDJu8T0, METH_VARARGS }, + { "interrogate_type_has_destructor", &_inPCPDJuE9q, METH_VARARGS }, + { "interrogate_type_is_unsigned", &_inPCPDJvj9C, METH_VARARGS }, + { "interrogate_function_name", &_inPCPDJw6ns, METH_VARARGS }, + { "interrogate_number_of_manifests", &_inPCPDJwgip, METH_VARARGS }, + { "interrogate_get_global_function", &_inPCPDJwsjw, METH_VARARGS }, + { "interrogate_get_type", &_inPCPDJxpx8, METH_VARARGS }, + { "interrogate_type_enum_value_scoped_name", &_inPCPDJypPX, METH_VARARGS }, + { NULL, NULL } +}; + +#ifdef _WIN32 +extern "C" __declspec(dllexport) void initlibdtool(); +#else +extern "C" void initlibdtool(); +#endif + +void initlibdtool() { + Py_InitModule("libdtool", python_methods); +} + diff --git a/dtool/src/Sources.pp b/dtool/src/Sources.pp new file mode 100644 index 0000000000..fb8681dab2 --- /dev/null +++ b/dtool/src/Sources.pp @@ -0,0 +1,4 @@ +// This is a group directory: a directory level above a number of +// source subdirectories. + +#define DIR_TYPE group diff --git a/dtool/src/attach/Sources.pp b/dtool/src/attach/Sources.pp new file mode 100644 index 0000000000..a2c2d03766 --- /dev/null +++ b/dtool/src/attach/Sources.pp @@ -0,0 +1,13 @@ +#define INSTALL_HEADERS \ + ctattch.pl ctattch.pl.rnd ctunattach.pl ctquery.pl ctdelta.pl \ + ctdelta.pl.rnd unco.pl ctvspec.pl ctcm.pl ctccase.pl ctntool.pl \ + ctcvs.pl + +#define INSTALL_SCRIPTS \ + ctattach.drv ctunattach.drv ctquery ctdelta ctihave ctallihave \ + ctunco ctattachcc cttimewarp get-cttree update-cttree get-delta \ + ctsanity ctmkelem ctmkdir ctci ctco ctrm ctmv ctmake neartool + +#define INSTALL_DATA \ + dtool.cshrc dtool.init dtool.emacs dtool.alias \ + dtool.sh dtool.alias-sh diff --git a/dtool/src/attach/ctallihave b/dtool/src/attach/ctallihave new file mode 100755 index 0000000000..f0a25630f6 --- /dev/null +++ b/dtool/src/attach/ctallihave @@ -0,0 +1,33 @@ +#!/usr/local/bin/perl + +if ($#ARGV != -1) { + exit print "Usage: ctihave\n" ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "not configured for using CTtools\n" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$projs = $ENV{"CTPROJS"} ; +@projsplit = split( / +/, $projs ) ; + +foreach $item ( @projsplit ) { + @items = split( /:/, $item ) ; + $thisproj = $items[0] ; + $thisflav = $items[1] ; + $thisspec = &CTResolveSpec( $thisproj, $thisflav ) ; + $result = $result . &CTCMIHave( $thisproj, $thisflav, $thisspec ) ; +} +if ( $result ne "" ) { + @splitlist = split( /\n/, $result ) ; + foreach $item ( @splitlist ) { + print $item . "\n" ; + } +} diff --git a/dtool/src/attach/ctattach.drv b/dtool/src/attach/ctattach.drv new file mode 100755 index 0000000000..b3960ea9c7 --- /dev/null +++ b/dtool/src/attach/ctattach.drv @@ -0,0 +1,149 @@ +#!/usr/local/bin/perl + +# acceptable forms: +# ctattach - give usage message +# ctattach project - attach to the personal flavor of the project +# ctattach project flavor - attach to a specific flavor of the project +# ctattach - - list projects that can be attached to +# ctattach project - - list flavors of a given project +# ctattach - flavor - list projects with a certain flavor +# ctattach -def project flavor - attach to project, setting CTDEFAULT_FLAV +# to flavor for the scope of this attach + +sub CTAttachUsage { + print STDERR "Usage: ctattach -def project flavor -or-\n" ; + print STDERR " ctattach project [flavor] -or-\n" ; + print STDERR " ctattach project - -or-\n" ; + print STDERR " ctattach - [flavor]\n" ; + &CTAttachWriteNullScript( $tmpname ) ; + print $tmpname . "\n" ; + exit; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "$DTOOL environment must be set to use CTtools\n" ; +} + +require "$tool/inc/ctattch.pl" ; + +$tmpname = "/tmp/script.$$" ; + +if ( $#ARGV == -1 ) { + &CTUDebug( "got no arguments\n" ) ; + &CTAttachUsage ; +} + +$idx = 0 ; +$proj = "" ; +$flav = "" ; +$noflav = 0 ; +$defflav = "" ; +$spread = 0 ; +$anydef = 0 ; + +# +# parse arguemnts +# + +if ( $ARGV[$idx] eq "-def" ) { + &CTUDebug( "got '-def' parameter\n" ) ; + if ( $#ARGV < ($idx + 2) ) { + &CTUDebug( "not enough arguments after -def\n" ) ; + &CTAttachUsage ; + } + $defflav = $ARGV[$idx+2] ; + $spread = 1; + &CTUDebug( "spread default flavor is '$defflav'\n" ) ; + $idx++ ; +} else { + if ( $ENV{"CTDEFAULT_FLAV"} ne "" ) { + $defflav = $ENV{"CTDEFAULT_FLAV"} ; + &CTUDebug( "environment default flavor is '$defflav'\n" ) ; + } +} + +$proj = $ARGV[$idx] ; +&CTUDebug( "project is '$proj'\n" ) ; + +if ( $defflav eq "" ) { + $defflav = "default" ; + &CTUDebug( "no environmental default, using 'default'\n" ) ; +} + +if ( $#ARGV > $idx ) { + $flav = $ARGV[$idx+1] ; + &CTUDebug( "provided flavor is '$flav'\n" ) ; +} else { + if ( $proj ne "-" ) { + $flav = $defflav; + &CTUDebug( "using environment default flavor '$flav'\n" ) ; + $noflav = 1 ; + } +} + +if (( $noflav == 1 ) || ( $flav eq "default" )) { + $anydef = 1 ; +} + +# +# act on the arguments we got +# + +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctvspec.pl" ; + +if (( $proj eq "-" ) || ( $flav eq "-" )) { + if ( $#ARGV == 0 ) { + # list projects that can be attached to + print STDERR "Projects that can be attached to:\n" ; + $_ = &CTListAllProjects ; + @projlist = split ; + foreach $item ( @projlist ) { + print STDERR " $item\n" ; + } + } elsif ( $proj eq "-" ) { + # list project that have a given flavor + print STDERR "Projects that have a '$flav' flavor:\n" ; + $_ = &CTListAllProjects ; + @projlist = split ; + foreach $item ( @projlist ) { + $tmp = &CTResolveSpec( $item, $flav ) ; + if ( $tmp ne "" ) { + print STDERR " $item\n" ; + } + } + } else { + # list flavors of a given project + print STDERR "Flavors of project '$proj':\n" ; + $_ = &CTListAllFlavors( $proj ) ; + @flavlist = split ; + foreach $item ( @flavlist ) { + print STDERR " $item\n" ; + } + } + &CTAttachWriteNullScript( $tmpname ) ; + print $tmpname . "\n" ; +} else { + # output a real attachment + $curflav = &CTQueryProj( $proj ) ; + if (( $curflav eq "" ) || ( $noflav == 0 )) { + $envsep{"PATH"} = ":" ; + $envsep{"LD_LIBRARY_PATH"} = ":" ; + $envsep{"PFPATH"} = ":" ; + $envsep{"SSPATH"} = ":" ; + $envsep{"STKPATH"} = ":" ; + $envsep{"DC_PATH"} = ":" ; + $spec = &CTAttachCompute( $proj, $flav, $anydef ) ; + if ( $spec eq "" ) { + &CTAttachWriteNullScript( $tmpname ) ; + print $tmpname . "\n" ; + } else { + &CTAttachWriteScript( $tmpname ) ; + print $tmpname . "\n" ; + } + } else { + &CTAttachWriteNullScript( $tmpname ) ; + print $tmpname . "\n" ; + } +} diff --git a/dtool/src/attach/ctattachcc b/dtool/src/attach/ctattachcc new file mode 100755 index 0000000000..4143fd38e3 --- /dev/null +++ b/dtool/src/attach/ctattachcc @@ -0,0 +1,86 @@ +#!/usr/local/bin/perl + +if ( $#ARGV != 5 ) { + print STDERR "This is for internal use by attach ONLY\n" ; + exit ; +} + +$root = $ARGV[0] ; +$view = $ARGV[1] ; +$branch = $ARGV[2] ; +$label = $ARGV[3] ; +$vobname = $ARGV[4] ; +$proj = $ARGV[5] ; +$tmpname = "/tmp/config.$$" ; + +$emitted = 0 ; + +$ctdebug = $ENV{"CTATTACH_DEBUG"} ; + +if ($ctdebug) { + print STDERR "Params:\n 0: '$root'\n 1: '$view'\n 2: '$branch'\n" ; + print STDERR " 3: '$label'\n 4: '$vobname'\n 5: '$proj'\n" ; + print STDERR "making branch and label types for view " . $view . "\n" ; + print STDERR "executing: /usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null\n" ; + print STDERR "executing: /usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null\n" ; +} +system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null\n" ; +system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null\n" ; + +if ($ctdebug) { + print STDERR "creating/updating the config-spec for view " . $view . "\n" ; +} +open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; +open( TMPFILE, "> $tmpname" ) ; +while ( ) { + if ( $_ =~ "CHECKEDOUT" ) { + if ($ctdebug) { + print STDERR "case 1:\noutputting: '$_'\n" ; + } + print TMPFILE "$_" ; + } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && + !( $_ =~ /\/$proj\// )) { + if ( ! $emitted ) { + $emitted = 1 ; + print TMPFILE "element $root/... .../$branch/LATEST\n" ; + print TMPFILE "element $root/... $label -mkbranch $branch\n" ; + print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; + if ($ctdebug) { + print STDERR "case 2:\n" ; + print STDERR "outputting: 'element $root/... .../$branch/LATEST'\n" ; + print STDERR "outputting: 'element $root/... $label -mkbranch $branch'\n" ; + print STDERR "outputting: 'element $root/... /main/LATEST -mkbranch $branch'\n" ; + } + } + if ($ctdebug) { + print STDERR "case 3:\n" ; + print STDERR "outputting: '$_'\n" ; + } + print TMPFILE "$_" ; + } elsif ( $_ =~ /\/$proj\// ) { + if ( ! $emitted ) { + $emitted = 1 ; + print TMPFILE "element $root/... .../$branch/LATEST\n" ; + print TMPFILE "element $root/... $label -mkbranch $branch\n" ; + print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; + if ($ctdebug) { + print STDERR "case 4:\n" ; + print STDERR "outputting: 'element $root/... .../$branch/LATEST'\n" ; + print STDERR "outputting: 'element $root/... $label -mkbranch $branch'\n" ; + print STDERR "outputting: 'element $root/... /main/LATEST -mkbranch $branch'\n" ; + } + } + } else { + if ($ctdebug) { + print STDERR "case 5:\n" ; + print STDERR "outputting: '$_'\n" ; + } + print TMPFILE "$_" ; + } +} +close( CTINTERFACE ) ; +close( TMPFILE ) ; +if ($ctdebug) { + print STDERR "output to execute: '/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname'\n" ; +} +system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname\n" ; diff --git a/dtool/src/attach/ctattch.pl b/dtool/src/attach/ctattch.pl new file mode 100644 index 0000000000..4c6df5a9c8 --- /dev/null +++ b/dtool/src/attach/ctattch.pl @@ -0,0 +1,474 @@ +require "$tool/inc/ctutils.pl" ; + +$shell_type = "csh" ; +if ( $ENV{"SHELL_TYPE"} ne "" ) { + if ( $ENV{"SHELL_TYPE"} eq "sh" ) { + $shell_type = "sh" ; + } +} + +$docnt = 0 ; +@attachqueue = () ; + +require "$tool/inc/ctquery.pl" ; + +# force set a variable in the 'new' environment +# input is in: +# $_[0] = variable +# $_[1] = value +# +# output is in: +# %newenv = variable marked to be set to value +sub CTAttachSet { + if ( ( $_[0] ne "" ) && ( $_[1] ne "" ) ) { + &CTUDebug( "setting " . $_[0] . " to '" . $_[1] . "'\n" ) ; + $newenv{$_[0]} = $_[1] ; + } +} + +# get a variable from the environment and split it out to unified format +# (ie: space separated) +# input is in: +# $_[0] = variable to get +# +# output is in: +# string returned with value +sub CTSpoolEnv { + local( $ret ) = $ENV{$_[0]} ; + if ( $envsep{$_[0]} ne "" ) { + local( @splitlist ) = split( $envsep{$_[0]}, $ret ); + $ret = join( " ", @splitlist ) ; + } + $ret ; +} + +# modify a possibly existing variable to have a value in the 'new' environment +# input is in: +# $_[0] = variable +# $_[1] = value +# $_[2] = root +# $_[3] = project +# +# output is in: +# %newenv = variable adjusted to have the new value +sub CTAttachMod { + &CTUDebug( "in CTAttachMod\n" ) ; + if ( $_[0] eq "CTPROJS" ) { + # as part of the system, this one is special + &CTUDebug( "doing a mod on $CTPROJS\n" ) ; + if ( $newenv{$_[0]} eq "" ) { + $newenv{$_[0]} = $ENV{$_[0]} ; + } + local( $proj ) = $_[3] ; + $proj =~ tr/A-Z/a-z/ ; + local( $curflav ) = &CTQueryProj( $proj ) ; + if ( $curflav ne "" ) { + local( $tmp ) = $_[3] . ":" . $curflav ; + if ( $newenv{$_[0]} =~ /$tmp/ ) { + local( $hold ) = $newenv{$_[0]} ; + $hold =~ s/$tmp/$_[1]/ ; + &CTUDebug( "already attached to " . $_[3] . " changing '" . + $tmp . "' to '" . $_[1] . "' yielding '" . $hold . + "'\n" ) ; + $newenv{$_[0]} = $hold ; + } else { + &CTUDebug( "prepending '" . $_[1] . "' to CTPROJS\n" ) ; + $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; + } + } else { + &CTUDebug( "writing '" . $_[1] . "' to CTPROJS\n" ) ; + if ( $newenv{$_[0]} eq "" ) { + $newenv{$_[0]} = $_[1] ; + } else { + $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; + } + } + } elsif ( ( $_[0] ne "" ) && ( $_[1] ne "" ) ) { + local( $dosimple ) = 0 ; + if ( $newenv{$_[0]} eq "" ) { + # not in our 'new' environment yet, add it. + # may still be empty + $newenv{$_[0]} = &CTSpoolEnv( $_[0] ) ; + } + if ( ! ( $newenv{$_[0]} =~ /$_[1]/ )) { + # if it's in there already, we're done before we started. + if ( $_[1] =~ /^$_[2]/ ) { + # damn, might need to do an in-place edit + local( $curroot ) = $ENV{$_[3]} ; + if ( $curroot eq "" ) { + $dosimple = 1 ; + } else { + local( $test ) = $_[1] ; + $test =~ s/^$_[2]// ; + $test = $curroot . $test ; + if ( $newenv{$_[0]} =~ /$test/ ) { + # there it is. in-place edit + local( $foo ) = $newenv{$_[0]} ; + $foo =~ s/$test/$_[1]/ ; + &CTUDebug( "doing in-place edit on " . $_[0] . + " changing '" . $test . "' to '" . + $_[1] . "' yielding '" . $foo . "'\n" ) ; + $newenv{$_[0]} = $foo ; + } else { + $dosimple = 1 ; + } + } + } else { + # don't have to sweat in-place edits + $dosimple = 1 ; + } + } + if ( $dosimple ) { + if ( $newenv{$_[0]} eq "" ) { + &CTUDebug( "no pre-existing value in " . $_[0] . + " setting it to '" . $_[1] . "'\n" ) ; + $newenv{$_[0]} = $_[1] ; + } elsif ( $envpostpend{$_[0]} ) { + &CTUDebug( "post-pending '" . $_[1] . "' to " . $_[0] . + "\n" ) ; + $newenv{$_[0]} = $newenv{$_[0]} . " " . $_[1] ; + } else { + &CTUDebug( "pre-pending '" . $_[1] . "' to " . $_[0] . + "\n" ) ; + $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ; + } + } + } +} + +require "$tool/inc/ctcm.pl" ; + +# given the project and flavor, build the lists of variables to set/modify +# input is in: +# $_[0] = project +# $_[1] = flavor +# $_[2] = is some kind of default? +# +# output is in: +# return value is config line +# %newenv = an image of those parts of the environment we want to change +# %envsep = seperator +# %envcmd = set or setenv +# %envdo = direct commands to add to attach script +# %envpostpend = flag that variable should be postpended +sub CTAttachCompute { + &CTUDebug( "in CTAttachCompute\n" ) ; + local( $done ) = 0 ; + local( $flav ) = $_[1] ; + local( $prevflav ) = &CTQueryProj( $_[0] ) ; + local( $spec ) ; + local( $root ) ; + if ( $_[2] && ( $prevflav ne "" )) { + # want some form of default attachment, and are already attached. Short + # circuit. + $done = 1 ; + } + + # + # choose real flavor and find/validate root + # + while ( ! $done ) { + $spec = &CTResolveSpec( $_[0], $flav ) ; + &CTUDebug( "spec line = '$spec'\n" ) ; + if ( $spec ne "" ) { + $root = &CTComputeRoot( $_[0], $flav, $spec ) ; + &CTCMSetup( $_[0], $spec, $flav ) ; + if ( -e $root ) { + $done = 1 ; + } + } + if (( ! $done ) && $_[2] ) { + if ( $flav eq "install" ) { + # oh my! are we ever in trouble + # want some sort of default, but couldn't get to what we wanted + print STDERR "you are in a strange alien universe\n" ; + $spec = "" ; + $done = 1 ; + } elsif ( $flav eq "release" ) { + $flav = "install" ; + } elsif ( $flav eq "ship" ) { + $flav = "release" ; + } else { + $flav = "ship" ; + } + } + } + + # + # start real work + # + if ( $spec ne "" ) { + local( $proj ) = $_[0] ; + $proj =~ tr/a-z/A-Z/ ; + local( $item ) ; + + # we scan the .init file first because if there are needed sub-attaches + # they must happen before the rest of our work + local( $init ) = "$root/etc/$_[0].init" ; + local( %localmod ); + local( %localset ); + local( %localsep ); + local( %localcmd ); + local( %localdo ); + local( $localdocnt ) = 0 ; + local( %localpost ); + if ( -e $init ) { + &CTUDebug( "scanning " . $_[0] . ".init\n" ) ; + local( @linesplit ) ; + local( $linetmp ) ; + local( $loop ) ; + local( $looptmp ) ; + local( *INITFILE ) ; + if ( -x $init ) { + open( INITFILE, "$init $_[0] $_[1] $root |" ) ; + } else { + open( INITFILE, "< $init" ) ; + } + while ( ) { + s/\n$// ; + @linesplit = split( /\#/ ) ; + $_ = $linesplit[0] ; + if ( $_ =~ /^MODABS/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + if ( $localmod{$linetmp} eq "" ) { + $localmod{$linetmp} = join( " ", @linesplit ) ; + } else { + $localmod{$linetmp} = $localmod{$linetmp} . " " . + join( " ", @linesplit ) ; + } + } elsif ( $_ =~ /^MODREL/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + foreach $loop ( @linesplit ) { + $looptmp = $root . "/" . &CTUShellEval($loop) ; + if ( -e $looptmp ) { + if ( $localmod{$linetmp} eq "" ) { + $localmod{$linetmp} = $looptmp ; + } else { + $localmod{$linetmp} = $localmod{$linetmp} . " " . + $looptmp ; + } + } + } + } elsif ( $_ =~ /^SETABS/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + if ( $localset{$linetmp} eq "" ) { + $localset{$linetmp} = join( " ", @linesplit ) ; + } else { + $localset{$linetmp} = $localset{$linetmp} . " " . + join( " ", @linesplit ) ; + } + } elsif ( $_ =~ /^SETREL/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + foreach $loop ( @linesplit ) { + $looptmp = $root . "/" . &CTUShellEval($loop) ; + if ( -e $looptmp ) { + if ( $localset{$linetmp} eq "" ) { + $localset{$linetmp} = $looptmp ; + } else { + $localset{$linetmp} = $localset{$linetmp} . " " . + $looptmp ; + } + } + } + } elsif ( $_ =~ /^SEP/ ) { + @linesplit = split ; + $localsep{$linesplit[1]} = $linesplit[2] ; + } elsif ( $_ =~ /^CMD/ ) { + @linesplit = split ; + $localcmd{$linesplit[1]} = $linesplit[2] ; + } elsif ( $_ =~ /^DOCSH/ ) { + if ( $shell_type ne "sh" ) { + @linesplit = split ; + shift( @linesplit ) ; + $localdo{$localdocnt} = join( " ", @linesplit ) ; + $localdocnt++ ; + } + } elsif ( $_ =~ /^DOSH/ ) { + if ( $shell_type eq "sh" ) { + @linesplit = split ; + shift( @linesplit ) ; + $localdo{$localdocnt} = join( " ", @linesplit ) ; + $localdocnt++ ; + } + } elsif ( $_ =~ /^DO/ ) { + @linesplit = split ; + shift( @linesplit ) ; + $localdo{$localdocnt} = join( " ", @linesplit ) ; + $localdocnt++ ; + } elsif ( $_ =~ /^POSTPEND/ ) { + @linesplit = split ; + $localpost{$linesplit[1]} = 1 ; + } elsif ( $_ =~ /^ATTACH/ ) { + @linesplit = split ; + shift( @linesplit ) ; + foreach $loop ( @linesplit ) { + push( @attachqueue, $loop ) ; + } + } elsif ( $_ ne "" ) { + print STDERR "Unknown .init directive '$_'\n" ; + } + } + close( INITFILE ) ; + } + + # now handle sub-attaches + &CTUDebug( "performing sub-attaches\n" ) ; + while ( @attachqueue != () ) { + $item = shift( @attachqueue ) ; + &CTUDebug( "attaching to " . $item . "\n" ) ; + &CTAttachCompute( $item, $defflav, 1 ) ; + } + + # now we will do our extentions, then apply the mods from the .init + # file, if any + &CTUDebug( "extending paths\n" ) ; + local( $type ) = &CTSpecType( $spec ) ; + if ( $type eq "vroot" ) { + &CTAttachMod( "PATH", "/usr/atria/bin", $root, $proj ) ; + } + + # For now, we will not check whether the various /bin, /lib, + # /inc directories exist before adding them to the paths. This + # helps when attaching to unitialized trees that do not have + # these directories yet (but will shortly). + + $item = $root . "/bin" ; + #if ( -e $item ) { + &CTAttachMod( "PATH", $item, $root, $proj ) ; + #} + $item = $root . "/lib" ; + #if ( -e $item ) { + if ( $ENV{"PENV"} eq "WIN32" ) { + &CTAttachMod( "PATH", $item, $root, $proj ) ; + } + &CTAttachMod( "LD_LIBRARY_PATH", $item, $root, $proj ) ; + #} + $item = $root . "/src" ; + #if ( -e $item ) { + &CTAttachMod( "CDPATH", $item, $root, $proj ) ; + #} + $item = $root . "/inc" ; + #if ( -e $item ) { + &CTAttachMod( "CT_INCLUDE_PATH", $item, $root, $proj ) ; + #} + $item = $root . "/etc" ; + #if ( -e $item ) { + &CTAttachMod( "ETC_PATH", $item, $root, $proj ) ; + #} + &CTAttachMod( "CTPROJS", $proj . ":" . $flav, $root, $proj ) ; + &CTAttachSet( $proj, $root ) ; + + # run thru the stuff saved up from the .init file + foreach $item ( keys %localsep ) { + $envsep{$item} = $localsep{$item} ; + } + foreach $item ( keys %localpost ) { + $envpostpend{$item} = $localpost{$item} ; + } + foreach $item ( keys %localmod ) { + local( @splitthis ) = split( / +/, $localmod{$item} ) ; + local( $thing ) ; + foreach $thing ( @splitthis ) { + &CTAttachMod( $item, $thing, $root, $proj ) ; + } + } + foreach $item ( keys %localset ) { + &CTAttachSet( $item, $localset{$item} ) ; + } + foreach $item ( keys %localcmd ) { + $envcmd{$item} = $localcmd{$item} ; + } + foreach $item ( keys %localdo ) { + $envdo{$docnt} = $localdo{$item} ; + $docnt++ ; + } + } + + &CTUDebug( "out of CTAttachCompute\n" ) ; + $spec ; +} + +# write a script to NOT change the environment +# Input is: +# $_[0] = filename +sub CTAttachWriteNullScript { + &CTUDebug( "in CTAttachWriteNullScript\n" ) ; + local( *OUTFILE ) ; + open( OUTFILE, ">$_[0]" ) ; + print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; + print OUTFILE "echo No attachment actions performed\n" ; + print OUTFILE "rm -f $_[0]\n" ; + close( OUTFILE ) ; + &CTUDebug( "out of CTAtachWriteNullScript\n" ) ; +} + +# write a script to setup the environment +# Input is: +# $_[0] = filename +sub CTAttachWriteScript { + &CTUDebug( "in CTAttachWriteScript\n" ) ; + local( *OUTFILE ) ; + open( OUTFILE, ">$_[0]" ) ; + print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; + local( $item ) ; + + foreach $item ( keys %newenv ) { + local( $sep ) = " " ; + if ( $envsep{$item} ne "" ) { + $sep = $envsep{$item} ; + } + local( @splitlist ) = split( / +/, $newenv{$item} ) ; + local( $outval ) = join( $sep, @splitlist ) ; + + if ( $shell_type eq "sh" ) { + print OUTFILE "$item=\"" . $outval . "\"\n" ; + if ( $envcmd{$item} ne "set" ) { + print OUTFILE "export $item\n" ; + } + } else { + if ( $envcmd{$item} ne "" ) { + print OUTFILE $envcmd{$item} . " $item " ; + if ( $envcmd{$item} eq "set" ) { + print OUTFILE "= ( " ; + } + print OUTFILE $outval ; + if ( $envcmd{$item} eq "set" ) { + print OUTFILE ")" ; + } + print OUTFILE "\n" ; + } else { + print OUTFILE "setenv $item \"$outval\"\n" ; + } + } + } + + if ( $newenv{"CDPATH"} ne "" ) { + if ( $shell_type ne "sh" ) { + print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; + } + } + foreach $item ( keys %envdo ) { + print OUTFILE $envdo{$item} . "\n" ; + } + if (! $ctdebug) { + print OUTFILE "rm -f $_[0]\n" ; + } else { + print STDERR "no self-destruct script '" . $_[0] . "'\n" ; + } + close( OUTFILE ) ; + &CTUDebug( "out of CTAttachWriteScript\n" ) ; +} + +1; diff --git a/dtool/src/attach/ctattch.pl.rnd b/dtool/src/attach/ctattch.pl.rnd new file mode 100644 index 0000000000..6284b795fd --- /dev/null +++ b/dtool/src/attach/ctattch.pl.rnd @@ -0,0 +1,953 @@ +require "$tool/inc/ctutils.pl" ; + +# get list of all projects +sub CTAttachListProj { + if ($ctdebug ne "") { + print STDERR "in CTAttachListProj\n" ; + } + local( $ret ) = "" ; + local( $done ) = 0 ; + local( *DIRFILES ) ; + open( DIRFILES, "(cd /var/etc ; /bin/ls -1 *.vspec ; echo blahblah) |" ) ; + while ( ! $done ) { + $_ = ; + s/\n$// ; + if ( $_ eq "blahblah" ) { + $done = 1 ; + } else { + s/.vspec$// ; + $ret = $ret . " " . $_ ; + } + } + close( DIRFILES ) ; + if ($ctdebug ne "") { + print STDERR "out of CTAttachListProj\n" ; + } + $ret ; +} + +# get list of flavors for a project +# $_[0] = project +sub CTAttachListFlav { + if ($ctdebug) { + print STDERR "in CTAttachListFlav\n" ; + } + local( $ret ) = "" ; + $vobname = $_[0] ; + if ( -e "/var/etc/$_[0].vspec" ) { + local( *SPECFILE ) ; + open( SPECFILE, " ) { + if ( $_ =~ /^VOBNAME/ ) { + @partlist = split( /=/ ) ; + $vobname = $partlist[1] ; + $vobname =~ s/\n$// ; + } else { + @partlist = split( /:/ ) ; + $ret = $ret . " " . $partlist[0] ; + } + } + close( SPECFILE ) ; + } else { + print STDERR "CTAttachListFlav: cannot locate '/var/etc/$_[0]'\n" ; + } + if ($ctdebug) { + print STDERR "out of CTAttachListFlav\n" ; + } + $ret ; +} + +# get the flavor line for the given project +# $_[0] = project +# $_[1] = flavor +sub CTAttachFindFlav { + if ($ctdebug) { + print STDERR "in CTAttachFindFlav\n" ; + } + local( $ret ) = "" ; + $vobname = $_[0] ; + if ( -e "/var/etc/$_[0].vspec" ) { + local( *SPECFILE ) ; + open( SPECFILE, " ) && ! $done ) { + s/\n$// ; + if ( $_ =~ /^VOBNAME/ ) { + @partlist = split( /=/ ) ; + $vobname = $partlist[1] ; + } else { + @partlist = split( /:/ ) ; + if ( $partlist[0] eq $_[1] ) { + $done = 1 ; + $ret = join( " ", @partlist ); + } + } + } + close( SPECFILE ) ; + } else { + print STDERR "CTAttachFindFlav: cannot locate '/var/etc/$_[0]'\n" ; + } + if ($ctdebug) { + if ($ret ne "") { + print STDERR "found flavor " . $_[1] . " of project " . $_[0] . "\n" ; + } else { + print STDERR "did not find flavor " . $_[1] . " of project " . $_[0] . "\n" ; + } + print STDERR "out of CTAttachFindFlav\n" ; + } + $ret ; +} + +# given the project and flavor, resolve the final config line +# $_[0] = project +# $_[1] = flavor +sub CTAttachResolve { + if ($ctdebug) { + print STDERR "in CTAttachResolve\n" ; + } + local( $spec ) = &CTAttachFindFlav( $_[0], $_[1] ) ; + local( $ret ) = "" ; + if ( $spec ne "" ) { + local( @speclist ) ; + @speclist = split( / +/, $spec ) ; + if ( $speclist[1] eq "root" ) { + $ret = join( " " , @speclist ) ; + if ($ctdebug) { + print STDERR "resolved to a 'root'\n" ; + } + } elsif ( $speclist[1] eq "vroot" ) { + if ( $ENV{"HAVE_ATRIA"} ne "" ) { + $ret = join( " " , @speclist ) ; + if ($ctdebug) { + print STDERR "resolved to a 'vroot'\n" ; + } + } + } elsif ( $speclist[1] eq "ref" ) { + local( $tmp ) = &CTUShellEval( $speclist[2] ) ; + if ($ctdebug) { + print STDERR "resolved to a 'ref', recursing\n" ; + } + $ret = &CTAttachResolve( $_[0], $tmp ) ; + } else { + print STDERR "CTAttachResolve: unknown flavor type '$speclist[1]'\n" ; + } + } + if ($ctdebug) { + print STDERR "out of CTAttachResolve\n" ; + } + $ret ; +} + +# given the config line, determine the view name +# $_[0] = config line +sub CTAttachComputeView { + if ($ctdebug) { + print STDERR "in CTAttachComputeView\n" ; + } + local( $ret ) = "" ; + if ( $_[0] ne "" ) { + local( @speclist ) ; + @speclist = split( / +/, $_[0] ) ; + if ( $speclist[1] eq "vroot" ) { + local( $vname ) = $speclist[0] ; + shift( @speclist ) ; + shift( @speclist ) ; + local( $item ) ; + local( @itemlist ) ; + foreach $item ( @speclist ) { + @itemlist = split( /=/, $item ) ; + if ( $itemlist[0] eq "VN" ) { + $vname = $itemlist[1] ; + } + } + $ret = &CTUShellEval( $vname ) ; + } + } + if ($ctdebug) { + print STDERR "config line '" . $_[0] . "' yields view name '" . $ret . "'\n" ; + print STDERR "out of CTAttachComputeView\n" ; + } + $ret ; +} + +# given the config line, determine the branch name +# $_[0] = config line +sub CTAttachComputeBranch { + if ($ctdebug) { + print STDERR "in CTAttachComputeBranch\n" ; + } + local( $ret ) = "" ; + if ( $_[0] ne "" ) { + local( @speclist ) ; + @speclist = split( / +/, $_[0] ) ; + if ( $speclist[1] eq "vroot" ) { + local( $bname ) = &CTAttachComputeView( $_[0] ) ; + shift( @speclist ) ; + shift( @speclist ) ; + local( $item ) ; + local( @itemlist ) ; + foreach $item ( @speclist ) { + @itemlist = split( /=/, $item ) ; + if ( $itemlist[0] eq "BN" ) { + $bname = $itemlist[1] ; + } + } + $ret = &CTUShellEval( $bname ) ; + } + } + if ($ctdebug) { + print STDERR "config line '" . $_[0] . "' yields branch name '" . $ret . "'\n" ; + print STDERR "out of CTAttachComputeBranch\n" ; + } + $ret ; +} + +# given the config line, determine the label name +# $_[0] = config line +sub CTAttachComputeLabel { + if ($ctdebug) { + print STDERR "in CTAttachComputeLabel\n" ; + } + local( $ret ) = "" ; + if ( $_[0] ne "" ) { + local( @speclist ) ; + @speclist = split( / +/, $_[0] ) ; + if ( $speclist[1] eq "vroot" ) { + local( $lname ) = &CTAttachComputeView( $_[0] ) ; + shift( @speclist ) ; + shift( @speclist ) ; + local( $item ) ; + local( @itemlist ) ; + foreach $item ( @speclist ) { + @itemlist = split( /=/, $item ) ; + if ( $itemlist[0] eq "LB" ) { + $lname = $itemlist[1] ; + } + } + $ret = &CTUShellEval( $lname ) ; + $ret =~ tr/a-z/A-Z/ ; + } + } + if ($ctdebug) { + print STDERR "config line '" . $_[0] . "' yields label name '" . $ret . "'\n" ; + print STDERR "out of CTAttachComputeLabel\n" ; + } + $ret ; +} + +# given the project name and config line, determine the root of the project +# $_[0] = project +# $_[1] = config line +sub CTAttachComputeRoot { + if ($ctdebug) { + print STDERR "in CTAttachComputeRoot\n" ; + } + local( $ret ) = "" ; + if ( $_[1] ne "" ) { + local( @speclist ) ; + @speclist = split( / +/, $_[1] ) ; + if ( $speclist[1] eq "root" ) { + $ret = $speclist[2] ; + } elsif ( $speclist[1] eq "vroot" ) { + $ret = &CTAttachComputeView( $_[1] ) ; + $ret = "/view/$ret/vobs/$vobname" ; + } else { + print STDERR "CTAttachComputeRoot: unknown flavor type '$speclist[1]'\n" ; + } + } + if ($ctdebug) { + print STDERR "out of CTAttachComputeRoot\n" ; + } + $ret ; +} + +# given the project name and config line, determine the root of the project as +# needed by the config spec. +# $_[0] = project +# $_[1] = config line +sub CTAttachComputeElemRoot { + if ($ctdebug) { + print STDERR "in CTAttachComputeElemRoot\n" ; + } + local( $ret ) = "" ; + if ( $_[1] ne "" ) { + local( @speclist ) ; + @speclist = split( / +/, $_[1] ) ; + if ( $speclist[1] eq "root" ) { + $ret = $speclist[2] ; + } elsif ( $speclist[1] eq "vroot" ) { + $ret = &CTAttachComputeView( $_[1] ) ; + $ret = "/vobs/$vobname" ; + } else { + print STDERR "CTAttachComputeElemRoot: unknown flavor type '$speclist[1]'\n" ; + } + } + if ($ctdebug) { + print STDERR "out of CTAttachComputeElemRoot\n" ; + } + $ret ; +} + +# do whatever setup is needed for ClearCase +# input is in: +# $_[0] = project +# $_[1] = $spec +sub CTAttachCCSetup { + if ($ctdebug) { + print STDERR "in CTAttachCCSetup\n" ; + } + local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1] ) ; + local( $view ) = &CTAttachComputeView( $_[1] ) ; + local( $branch ) = &CTAttachComputeBranch( $_[1] ) ; + local( $label ) = &CTAttachComputeLabel( $_[1] ) ; + local( *CTINTERFACE ) ; + local( *TMPFILE ) ; + local( $tmpname ) = "/tmp/config.$$" ; + local( $emitted ) = 0 ; + + if ($ctdebug) { + print STDERR "checking for existance of view '" . $view . "'\n" ; + } + open( CTINTERFACE, "/usr/atria/bin/cleartool lsview $view |" ) ; + $_ = ; + close( CTINTERFACE ) ; + if ( $_ eq "" ) { # need to make the view + if ($ctdebug) { + print STDERR "creating view '" . $view . "'\n" ; + } + system "umask 2 ; /usr/atria/bin/cleartool mkview -tag $view /var/views/$view.vws 2> /dev/null > /dev/null ; /usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null\n" ; + } elsif ( ! ( $_ =~ /\*/ )) { # need to start the view + if ($ctdebug) { + print STDERR "starting view '" . $view . "'\n" ; + } + system "/usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null &\n" ; + } + + if ($ctdebug) { + print STDERR "making branch and label types for view " . $view . "\n" ; + } + system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null &\n" ; + system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null &\n" ; + + if ($ctdebug) { + print STDERR "creating/updating the config-spec for view " . $view . "\n" ; + } + open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; + open( TMPFILE, "> $tmpname" ) ; + while ( ) { + if ( $_ =~ "CHECKEDOUT" ) { + print TMPFILE "$_" ; + } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && + !( $_ =~ /$_[0]/ )) { + if ( ! $emitted ) { + $emitted = 1 ; + print TMPFILE "element $root/... .../$branch/LATEST\n" ; + print TMPFILE "element $root/... $label -mkbranch $branch\n" ; + print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; + } + print TMPFILE "$_" ; + } elsif ( $_ =~ /$_[0]/ ) { + if ( ! $emitted ) { + $emitted = 1 ; + print TMPFILE "element $root/... .../$branch/LATEST\n" ; + print TMPFILE "element $root/... $label -mkbranch $branch\n" ; + print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; + } + } else { + print TMPFILE "$_" ; + } + } + close( CTINTERFACE ) ; + close( TMPFILE ) ; + system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname &\n" ; + if ($ctdebug) { + print STDERR "out of CTAttachCCSetup\n" ; + } +} + +# do whatever setup is needed for ClearCase, but do it in the background +# input is in: +# $_[0] = project +# $_[1] = $spec +sub CTAttachCCSetupBG { + if ($ctdebug) { + print STDERR "in CTAttachCCSetupBG\n" ; + } + local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1] ) ; + local( $view ) = &CTAttachComputeView( $_[1] ) ; + local( $branch ) = &CTAttachComputeBranch( $_[1] ) ; + local( $label ) = &CTAttachComputeLabel( $_[1] ) ; + + system "$tool/bin/ctattachcc $root $view $branch $label $vobname $_[0]\n" ; + + if ($ctdebug) { + print STDERR "out of CTAttachCCSetupBG\n" ; + } +} + +# prepend an entry onto the envmod of the given key. +# input is in: +# $_[0] = key +# $_[1] = data +# +# output is in: +# %envmod = has 'data' prepended at 'key' +sub CTAttachPrependMod { + if ( $envmod{$_[0]} eq "" ) { + $envmod{$_[0]} = $_[1] ; + } else { + $envmod{$_[0]} = $_[1] . " " . $envmod{$_[0]} ; + } +} + +# postpend an entry onto the envmod of the given key. +# input is in: +# $_[0] = key +# $_[1] = data +# +# output is in: +# %envmod = has 'data' postpended at 'key' +sub CTAttachPostpendMod { + if ( $envmod{$_[0]} eq "" ) { + $envmod{$_[0]} = $_[1] ; + } else { + $envmod{$_[0]} = $envmod{$_[0]} . " " . $_[1] ; + } +} + +# pre/post-pend an entry onto the envmod of the given key, as set/controlled +# by envpospend, et al. +# input is in: +# $_[0] = key +# $_[1] = data +# +# output is in: +# %envmod = data pre/post pended at the given key +sub CTAttachAddToMod { + if ($envpostpend{$_[0]} ne "") { + &CTAttachPostpendMod( $_[0], $_[1] ) ; + } else { + &CTAttachPrependMod( $_[0], $_[1] ) ; + } +} + +# prepend the given entry to the envset of the given key +# input is in: +# $_[0] = key +# $_[1] = data +# +# output is in: +# %envset = prepended at key with data +sub CTAttachPrependSet { + local( $sep ) = " " ; + if ( $envsep{$_[0]} ne "" ) { + $sep = $envsep{$_[0]} ; + } + if ($envset{$_[0]} ne "") { + $envset{$_[0]} = $_[1] . $sep . $envset{$_[0]} ; + } else { + $envset{$_[0]} = $_[1] ; + } +} + +# postpend the given entry to the envset of the given key +# input is in: +# $_[0] = key +# $_[1] = data +# +# output is in: +# %envset = postpended at key with data +sub CTAttachPostpendSet { + local( $sep ) = " " ; + if ( $envsep{$_[0]} ne "" ) { + $sep = $envsep{$_[0]} ; + } + if ($envset{$_[0]} ne "") { + $envset{$_[0]} = $envset{$_[0]} . $sep . $_[1] ; + } else { + $envset{$_[0]} = $_[1] ; + } +} + +# pre/post-pend an entry onto the envset of the given key, as set/controlled +# by envpospend, et al. +# input is in: +# $_[0] = key +# $_[1] = data +# +# output is in: +# %envset = data pre/post pended at the given key +sub CTAttachAddToSet { + if ($envpostpend{$_[0]} ne "") { + &CTAttachPostpendSet( $_[0], $_[1] ) ; + } else { + &CTAttachPrependSet( $_[0], $_[1] ) ; + } +} + +$docnt = 0 ; +@attachqueue = () ; + +require "$tool/inc/ctquery.pl" ; + +# given the project and flavor, build the lists of variables to set/modify +# input is in: +# $_[0] = project +# $_[1] = flavor +# $_[2] = is some kind of default? +# +# output is in: +# return value is config line +# %envmod = environment variables to modify +# %envset = environment variables to outright set +# %envsep = seperator +# %envcmd = set or setenv +# %envdo = direct commands to add to attach script +# %envpostpend = flag that variable should be postpended +sub CTAttachCompute { + if ($ctdebug) { + print STDERR "in CTAttachCompute\n" ; + } + local( $done ) = 0 ; + local( $flav ) = $_[1] ; + local( $prevflav ) = &CTQueryProj( $_[0] ) ; + local( $spec ) ; + local( $root ) ; + if ( $_[2] && ( $prevflav ne "" )) { + # short circuit attaching, we're already there. + $done = 1 ; + } + while ( ! $done ) { + $spec = &CTAttachResolve( $_[0], $flav ) ; + if ( $ctdebug ne "" ) { + print STDERR "spec line = '$spec'\n" ; + } + if ( $spec ne "" ) { + $root = &CTAttachComputeRoot( $_[0], $spec ) ; + if ( -e $root ) { + $done = 1 ; + if ( $spec =~ /vroot/ ) { + &CTAttachCCSetupBG( $_[0], $spec ) ; + } + } elsif ( $spec =~ /vroot/ ) { + &CTAttachCCSetup( $_[0], $spec ) ; + if ( -e $root ) { + $done = 1 ; + } + } + } + if (( ! $done ) && $_[2] ) { + if ( $flav eq "install" ) { + # oh my! are we ever in trouble + print STDERR "you are in a strange alien universe\n" ; + $spec = "" ; + $done = 1 ; + } elsif ( $flav eq "release" ) { + $flav = "install" ; + } elsif ( $flav eq "ship" ) { + $flav = "release" ; + } else { + $flav = "ship" ; + } + } + } + + if ( $spec ne "" ) { + local( $proj ) = $_[0] ; + $proj =~ tr/a-z/A-Z/ ; + local( $view ) = &CTAttachComputeView( $spec ) ; + + if ($ctdebug) { + print STDERR "extending paths\n" ; + } + + &CTAttachAddToMod( "PATH", $root . "/bin" ) ; + &CTAttachAddToMod( "LD_LIBRARY_PATH", $root . "/lib" ) ; + &CTAttachAddToMod( "CDPATH", $root . "/src/all" ) ; + &CTAttachAddToMod( "CT_INCLUDE_PATH", $root . "/inc" ) ; + &CTAttachAddToMod( "DC_PATH", $root . "/etc" ) ; + &CTAttachAddToMod( "PFPATH", $root . "/etc/models" ) ; + &CTAttachAddToMod( "SSPATH", $root . "/lib/ss" ) ; + &CTAttachAddToMod( "STKPATH", $root . "/lib/stk" ) ; + &CTAttachAddToMod( "CTPROJS", $proj . ":" . $flav ) ; + $envset{$proj} = $root; + +# if ( $view ne "" ) { +# &CTAttachCCSetup( $_[0], $spec ) ; +# } + + if ( -e "$root/etc/$_[0].init" ) { + if ($ctdebug) { + print STDERR "scanning .init file\n" ; + } + local( @linesplit ) ; + local( $linetmp ) ; + local( $loop ); + local( *INITFILE ) ; + if ( -x "$root/etc/$_[0].init" ) { + open( INITFILE, "$root/etc/$_[0].init $_[0] $_[1] $root |" ) ; + } else { + open( INITFILE, "< $root/etc/$_[0].init" ) ; + } + while ( ) { + s/\n$// ; + if ( $_ =~ /^MODABS/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + &CTAttachPostpendMod( $linetmp, &CTUShellEval(join(" ", @linesplit))) ; + } elsif ( $_ =~ /^MODREL/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + foreach $loop ( @linesplit ) { + &CTAttachPostpendMod( $linetmp, $root . "/" . &CTUShellEval($loop)) ; + } + } elsif ( $_ =~ /^SETABS/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + &CTAttachPrependSet( $linetmp, &CTUShellEval(join(" ", @linesplit))) ; + } elsif ( $_ =~ /^SETREL/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + foreach $loop ( @linesplit ) { + &CTAttachPrependSet( $linetmp, $root . "/" . &CTUShellEval($loop)) ; + } + } elsif ( $_ =~ /^SEP/ ) { + @linesplit = split ; + $envsep{$linesplit[1]} = $linesplit[2] ; + } elsif ( $_ =~ /^CMD/ ) { + @linesplit = split ; + $envcmd{$linesplit[1]} = $linesplit[2] ; + } elsif ( $_ =~ /^DO/ ) { + @linesplit = split ; + shift( @linesplit ) ; + $envdo{$docnt} = join( " ", @linesplit ) ; + $docnt++ ; + } elsif ( $_ =~ /^POSTPEND/ ) { + @linesplit = split ; + $envpospend{$linesplit[1]} = 1 ; + } elsif ( $_ =~ /^ATTACH/ ) { + @linesplit = split ; + shift( @linesplit ) ; + foreach $loop ( @linesplit ) { + push( @attachqueue, $loop ) ; + } + } else { + print STDERR "Unknown .init directive '$_'\n" ; + } + } + close( INITFILE ) ; + } + + # save mods away until after sub-attach + local( %locmod ) ; + local( $item ) ; + + foreach $item ( keys %envmod ) { + $locmod{$item} = $envmod{$item} ; + delete $envmod{$item} ; + } + + # do sub-attaches + while ( @attachqueue != () ) { + $item = shift( @attachqueue ) ; + &CTAttachCompute( $item, $defflav, 1 ) ; + } + + # restore saved mods and merge them in with existing + foreach $item ( keys %locmod ) { + $envmod{$item} = $locmod{$item} ; + delete $locmod{$item} ; + } + + &CTAttachCheckVars( $_[0], $spec ) ; + } + if ($ctdebug) { + print STDERR "out of CTAttachCompute\n" ; + } + $spec ; +} + +# take a mod list and merge it into set. Uniqueifying as we go. +# input is in: +# $_[0] = mod list +# $_[1] = key +# +# output is: +# %envset = now has the mod line merged in with it. +sub CTAttachMergeToSet { + if ( $ctdebug ) { + print STDERR "trying to add '$_[0]' to '$envset{$_[1]}'\n" ; + } + local( @splitlist ) ; + local( $loop ) ; + local( $sep ) = " " ; + if ( $envsep{$_[1]} ne "" ) { + $sep = $envsep{$_[1]} ; + } + @splitlist = split( / /, $_[0] ) ; + foreach $loop ( @splitlist ) { + if ( ! (( $envset{$_[1]} eq $loop ) || + ( $envset{$_[1]} =~ /^$loop$sep/ ) || + ( $envset{$_[1]} =~ /$sep$loop$/ ) || + ( $envset{$_[1]} =~ /$sep$loop$sep/ ))) { + &CTAttachPostpendSet( $_[1], $loop ) ; + } + } + if ( $ctdebug ) { + print STDERR "yielding '$envset{$_[1]}'\n" ; + } +} + +# Perform cleanup operations on the variable that are going to be set/modified +# eg: +# * check to see if we're already attached to the project, and alter sets +# based on that +# * move mods of pre-existing variables to sets w/ the changes included +# * move mods of non-existing variables to sets +# +# input: +# $_[0] = project +# $_[1] = config line +sub CTAttachCheckVars { + if ($ctdebug) { + print STDERR "in CTAttachCheckVars\n" ; + } + local( $prevflav ) = &CTQueryProj( $_[0] ) ; + local( $proj ) = $_[0] ; + $proj =~ tr/a-z/A-Z/ ; + local( $atria ) = "/usr/atria/bin" ; + if ( $ENV{"HAVE_ATRIA"} ne "" ) { + if ( !( $ENV{"PATH"} =~ /$atria/ )) { + $envmod{"PATH"} = "$atria " . $envmod{"PATH"} ; + } + } + if ( $prevflav ne "" ) { # are already attached to the project + if ( $ctdebug ne "" ) { + print STDERR "am already attached\n" ; + } + local( $prevspec ) = &CTAttachResolve( $_[0], $prevflav ) ; + local( $prevroot ) = &CTAttachComputeRoot( $_[0], $prevspec ) ; + local( $root ) = &CTAttachComputeRoot( $_[0], $_[1] ) ; + local( $loop ) ; + local( $item ) ; + local( @splitlist ) ; + local( $modsave ) ; + foreach $item ( keys %envmod ) { + if ( $ENV{$item} ne "" ) { + if ( $ctdebug ne "" ) { + print STDERR "'$item' is already in the environment\n" ; + } + if ( $item eq "CTPROJS" ) { + local( $prevmark ) = $proj . ":" . $prevflav ; + local( $curmark ) = $envmod{$item} ; + if ( $ctdebug ne "" ) { + print STDERR "changing '$prevmark' to '$curmark' yielding " ; + } + if ( ! $gotenv{$item} ) { + $envset{$item} = $ENV{$item} ; + } + $envset{$item} =~ s/$prevmark/$curmark/ ; + if ( $ctdebug ne "" ) { + print STDERR "'$envset{$item}'\n" ; + } + delete $envmod{$item} ; + } else { + local( $src ) ; + if ( $gotenv{$item} ) { + $src = $envset{$item} ; + } else { + $src = $ENV{$item} ; + } + if ( $envsep{$item} ne "" ) { + @splitlist = split( $envsep{$item}, $src ) ; + } else { + @splitlist = split( / +/, $src ) ; + } + $modsave = $envmod{$item} ; + delete $envmod{$item} ; + foreach $loop ( @splitlist ) { + $loop =~ s/$prevroot/$root/ ; + &CTAttachPostpendMod( $item, $loop ) ; + } + if ( $ctdebug ne "" ) { + print STDERR "env '$src' -> '$envmod{$item}'\n" ; + } + @splitlist = split( / +/, $modsave ) ; + foreach $loop ( @splitlist ) { + if ( ! (( $envmod{$item} eq $loop ) || + ( $envmod{$item} =~ /^$loop / ) || + ( $envmod{$item} =~ / $loop$/ ) || + ( $envmod{$item} =~ / $loop / ))) { + &CTAttachAddToMod( $item, $loop ) ; + } + } + if ( $ctdebug ne "" ) { + print STDERR "env final = '$envmod{$item}'\n" ; + } + } + } + if ( $envmod{$item} ne "" ) { + $envset{$item} = $envmod{$item} ; + if ( $envsep{$item} ne "" ) { + $envset{$item} =~ s/ /$envsep{$item}/g ; + } + # &CTAttachMergeToSet( $envmod{$item}, $item ) ; + delete $envmod{$item} ; + $gotenv{$item} = 1 ; + } + } + } else { # not already attached. mods -> sets + if ( $ctdebug ne "" ) { + print STDERR "am not already attached\n" ; + } + local( $item ) ; + local( $loop ) ; + local( $modsave ) ; + local( @splitlist ) ; + foreach $item ( keys %envmod ) { + if ( $ENV{$item} ne "" ) { + local( $src ) ; + if ( $gotenv{$item} ) { + $src = $envset{$item} ; + } else { + $src = $ENV{$item} ; + } + if ( $envsep{$item} ne "" ) { + @splitlist = split( $envsep{$item}, $src ) ; + } else { + @splitlist = split( / +/, $src ) ; + } + $modsave = $envmod{$item} ; + delete $envmod{$item} ; + foreach $loop ( @splitlist ) { + &CTAttachPostpendMod( $item, $loop ) ; + } + if ( $ctdebug ne "" ) { + print STDERR "env '$src' -> '$envmod{$item}'\n" ; + } + @splitlist = split( / +/, $modsave ) ; + foreach $loop ( @splitlist ) { + if ( ! (( $envmod{$item} eq $loop ) || + ( $envmod{$item} =~ /^$loop / ) || + ( $envmod{$item} =~ / $loop$/ ) || + ( $envmod{$item} =~ / $loop / ))) { + &CTAttachAddToMod( $item, $loop ) ; + } + } + if ( $ctdebug ne "" ) { + print STDERR "env final = '$envmod{$item}'\n" ; + } + } + $envset{$item} = $envmod{$item} ; + if ( $envsep{$item} ne "" ) { + $envset{$item} =~ s/ /$envsep{$item}/g ; + } + # &CTAttachMergeToSet( $envmod{$item}, $item ) ; + delete $envmod{$item} ; + $gotenv{$item} = 1 ; + } + } + if ($ctdebug) { + print STDERR "out of CTAttachCheckVars\n" ; + } +} + +# write a script to NOT change the environment +# Input is: +# $_[0] = filename +sub CTAttachWriteNullScript { + if ($ctdebug) { + print STDERR "in CTAttachWriteNullScript\n" ; + } + local( *OUTFILE ) ; + open( OUTFILE, ">$_[0]" ) ; + print OUTFILE "#!/bin/csh -f\n" ; + print OUTFILE "echo No attachment actions performed\n" ; + print OUTFILE "/sbin/rm $_[0]\n" ; + close( OUTFILE ) ; + if ($ctdebug) { + print STDERR "out of CTAtachWriteNullScript\n" ; + } +} + +# write a script to setup the environment +# Input is: +# $_[0] = filename +sub CTAttachWriteScript { + if ($ctdebug) { + print STDERR "in CTAttachWriteScript\n" ; + } + local( *OUTFILE ) ; + open( OUTFILE, ">$_[0]" ) ; + print OUTFILE "#!/bin/csh -f\n" ; + local( $item ) ; + foreach $item ( keys %envset ) { + if ( $envcmd{$item} ne "" ) { + print OUTFILE $envcmd{$item} . " $item " ; + if ( $envcmd{$item} eq "set" ) { + print OUTFILE "= " ; + } + print OUTFILE $envset{$item} . "\n" ; + } else { + print OUTFILE "setenv $item \"$envset{$item}\"\n" ; + } + } + foreach $item ( keys %envmod ) { + print STDERR "SHOULD NOT BE HERE\n" ; + if ( $envcmd{$item} ne "" ) { + print OUTFILE $envcmd{$item} . " $item " ; + if ( $envcmd{$item} eq "set" ) { + print OUTFILE "= ( " ; + } else { + print OUTFILE "\"" ; + } + } else { + print OUTFILE "setenv $item \"" ; + } + if ( $envsep{$item} ne "" ) { + @itemlist = split( / +/, $envmod{$item} ) ; + foreach $tmp ( @itemlist ) { + print OUTFILE $tmp . $envsep{$item} ; + } + } else { + print OUTFILE $envmod{$item} ; + } + if ( $envcmd{$item} ne "" ) { + if ( $envcmd{$item} eq "set" ) { + print OUTFILE ")" ; + } else { + print OUTFILE "\"" ; + } + print OUTFILE "\n" ; + } else { + print OUTFILE $ENV{$item} . "\"\n" ; + } + } + if (( $envset{"CDPATH"} ne "" ) || ( $envmod{"CDPATH"} ne "" )) { + print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; + } + foreach $item ( keys %envdo ) { + print OUTFILE $envdo{$item} . "\n" ; + } + if (! $ctdebug) { + print OUTFILE "/sbin/rm $_[0]\n" ; + } else { + print STDERR "no self-destruct script '" . $_[0] . "'\n" ; + } + close( OUTFILE ) ; + if ($ctdebug) { + print STDERR "out of CTAttachWriteScript\n" ; + } +} + +1; diff --git a/dtool/src/attach/ctccase.pl b/dtool/src/attach/ctccase.pl new file mode 100644 index 0000000000..6e95c52b34 --- /dev/null +++ b/dtool/src/attach/ctccase.pl @@ -0,0 +1,439 @@ +# given the config line, determine the view name +# $_[0] = config line +# $_[1] = flavor +# $_[2] = project +sub CTAttachComputeView { + &CTUDebug( "in CTAttachComputeView\n" ) ; + local( $ret ) = &CTResolveSpecName( $_[2], $_[1] ) ; + local( $options ) = &CTSpecOptions( $_[0] ) ; + if ( $options ne "" ) { + local( $name ) = &CTSpecFindOption( $options, "name" ) ; + if ( $name ne "" ) { + &CTUDebug( "found a name '" . $name . "'\n" ) ; + $ret = $name ; + } else { + &CTUDebug( "no name option found, going with default\n" ) ; + } + } + &CTUDebug( "config line '" . $_[0] . "' yields view name '" . $ret . + "'\n" . "out of CTAttachComputeView\n" ) ; + $ret ; +} + +# given the config line, determine the branch name +# $_[0] = config line +# $_[1] = flavor +# $_[2] = project +sub CTAttachComputeBranch { + &CTUDebug( "in CTAttachComputeBranch\n" ) ; + local( $ret ) = &CTAttachComputeView( $_[0], $_[1], $_[2] ) ; + &CTUDebug( "config line '" . $_[0] . "' yields branch name '" . $ret . + "'\n" . "out of CTAttachComputeBranch\n" ) ; + $ret ; +} + +# given the config line, determine the label name +# $_[0] = config line +# $_[1] = flavor +# $_[2] = project +sub CTAttachComputeLabel { + &CTUDebug( "in CTAttachComputeLabel\n" ) ; + local( $ret ) = &CTAttachComputeView( $_[0], $_[1], $_[2] ) ; + $ret =~ tr/a-z/A-Z/ ; + &CTUDebug( "config line '" . $_[0] . "' yields label name '" . $ret . + "'\n" . "out of CTAttachComputeLabel\n" ) ; + $ret ; +} + +# given the project name and config line, determine the root of the project as +# needed by the config spec. +# $_[0] = project +# $_[1] = config line +# $_[2] = flavor +sub CTAttachComputeElemRoot { + &CTUDebug( "in CTAttachComputeElemRoot\n" ) ; + local( $ret ) = "/vobs/$_[0]" ; + &CTUDebug( "out of CTAttachComputeElemRoot\n" ) ; + $ret ; +} + +# do whatever setup is needed for ClearCase +# input is in: +# $_[0] = project +# $_[1] = $spec +# $_[2] = flavor +sub CTAttachCCSetup { + &CTUDebug( "in CTAttachCCSetup\n" ) ; + local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1], $_[2] ) ; + local( $view ) = &CTAttachComputeView( $_[1], $_[2], $_[0] ) ; + local( $branch ) = &CTAttachComputeBranch( $_[1], $_[2], $_[0] ) ; + local( $label ) = &CTAttachComputeLabel( $_[1], $_[2], $_[0] ) ; + local( *CTINTERFACE ) ; + local( *TMPFILE ) ; + local( $tmpname ) = "/tmp/config.$$" ; + local( $emitted ) = 0 ; + + &CTUDebug( "checking for existance of view '" . $view . "'\n" ) ; + open( CTINTERFACE, "/usr/atria/bin/cleartool lsview $view |" ) ; + $_ = ; + close( CTINTERFACE ) ; + if ( $_ eq "" ) { # need to make the view + &CTUDebug( "creating view '" . $view . "'\n" ) ; + system "umask 2 ; /usr/atria/bin/cleartool mkview -tag $view /var/views/$view.vws 2> /dev/null > /dev/null ; /usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null\n" ; + } elsif ( ! ( $_ =~ /\*/ )) { # need to start the view + &CTUDebug( "starting view '" . $view . "'\n" ) ; + system "/usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null &\n" ; + } + + &CTUDebug( "making branch and label types for view " . $view . "\n" ) ; + system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null &\n" ; + system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null &\n" ; + + &CTUDebug( "creating/updating the config-spec for view " . $view . "\n" ) ; + open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ; + open( TMPFILE, "> $tmpname" ) ; + while ( ) { + if ( $_ =~ "CHECKEDOUT" ) { + print TMPFILE "$_" ; + } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) && + !( $_ =~ /$_[0]/ )) { + if ( ! $emitted ) { + $emitted = 1 ; + print TMPFILE "element $root/... .../$branch/LATEST\n" ; + print TMPFILE "element $root/... $label -mkbranch $branch\n" ; + print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; + } + print TMPFILE "$_" ; + } elsif ( $_ =~ /$_[0]/ ) { + if ( ! $emitted ) { + $emitted = 1 ; + print TMPFILE "element $root/... .../$branch/LATEST\n" ; + print TMPFILE "element $root/... $label -mkbranch $branch\n" ; + print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ; + } + } else { + print TMPFILE "$_" ; + } + } + close( CTINTERFACE ) ; + close( TMPFILE ) ; + system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm -f $tmpname &\n" ; + &CTUDebug( "out of CTAttachCCSetup\n" ) ; +} + +# do whatever setup is needed for ClearCase, but do it in the background +# input is in: +# $_[0] = project +# $_[1] = $spec +# $_[2] = flavor +sub CTAttachCCSetupBG { + &CTUDebug( "in CTAttachCCSetupBG\n" ) ; + local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1], $_[2] ) ; + local( $view ) = &CTAttachComputeView( $_[1], $_[2], $_[0] ) ; + local( $branch ) = &CTAttachComputeBranch( $_[1], $_[2], $_[0] ) ; + local( $label ) = &CTAttachComputeLabel( $_[1], $_[2], $_[0] ) ; + + system "$tool/bin/ctattachcc $root $view $branch $label $vobname $_[0]\n" ; + + &CTUDebug( "out of CTAttachCCSetupBG\n" ) ; +} + +# given a possibly empty string, format it into a comment or -nc +# input is in: +# $_[0] = possible comment string +# +# output is: +# string for use by ClearCase functions +sub CTCcaseFormatComment { + local( $ret ) = "" ; + if ( $_[0] eq "" ) { + $ret = "-nc" ; + } else { + $ret = "-c " . $_[0] ; + } + $ret ; +} + +# make a versioned directory +# input is in: +# $_[0] = directory to create +# $_[1] = curr dir +# $_[2] = possible comment +# +# output: +# return success or failure +sub CTCcaseMkdir { + &CTUDebug( "in CTCcaseMkdir\n" ) ; + local( $ret ) = 0 ; + local( $dir ) = $_[0] ; + if ( ! ( $dir =~ /^\// )) { + $dir = $_[1] . "/" . $dir ; + } + local( $comment) = &CTCcaseFormatComment( $_[2] ) ; + # first we have to check out the parent directory + local( @alist ) = split( /\//, $dir ) ; + pop( @alist ) ; + local( $parent ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $dir . "' is '" . $parent . "'\n" ) ; + $ret = system( "cleartool co -nc $parent\n" ) ; + if ( $ret == 0 ) { + # now make the dir + $ret = &CTURetCode( system( "cleartool mkdir " . $comment . + " $dir\n" )) ; + } else { + $ret = 0 ; + } + &CTUDebug( "out of CTCcaseMkdir\n" ) ; + $ret ; +} + +# make a versioned element +# input is in: +# $_[0] = element to version +# $_[1] = curr dir +# $_[2] = possible comment +# $_[3] = possible eltype +# +# output: +# return success or failure +sub CTCcaseMkelem { + &CTUDebug( "in CTCcaseMkelem\n" ) ; + local( $ret ) = 0 ; + local( $elem ) = $_[0] ; + if ( ! ( $elem =~ /^\// )) { + $elem = $_[1] . "/" . $elem ; + } + local( $comment) = &CTCcaseFormatComment( $_[2] ) ; + local( $eltype ) = $_[3] ; + if ( $eltype ne "" ) { + $eltype = "-eltype " . $eltype ; + } + # first we have to check out the parent directory + local( @alist ) = split( /\//, $elem ) ; + pop( @alist ) ; + local( $parent ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; + $ret = system( "cleartool co -nc $parent\n" ) ; + if ( $ret != 0 ) { + &CTUDebug( "checking out the dirctory gave return code: " . $ret . + "\n" ) ; + $ret = 0 ; + } + # now make the elem + $ret = &CTURetCode( system( "cleartool mkelem " . $comment . " " . + $eltype . " $elem\n" )) ; + &CTUDebug( "out of CTCcaseMkelem\n" ) ; + $ret ; +} + +# done here so there will be coherence if multiple deltas are done +require "ctime.pl" ; +$timestamp = &ctime(time) ; +$timestamp =~ s/\n$// ; +@timelist = split( /\s+/, $timestamp ) ; +$timestamp = $timelist[2] . $timelist[1] . $timelist[5] . "_" . $timelist[3] ; +$timestamp =~ s/:/_/g ; + +# delta an element +# input is in: +# $_[0] = element to delta +# +# output: +# return success or failure +sub CTCcaseDelta { + require "$tool/inc/ctdelta.pl" ; + + &CTUDebug( "in CTCcaseDelta\n" ) ; + local( $ret ) = 0 ; + # this is ripped from the old ctdelta script + &CTDeltaCheckin( $_[0] ) ; + local( $ver ) = &CTDeltaGetVersion( $_[0] ) ; + &CTUDebug( "got version '" . $ver . "'\n" ) ; + if ( &CTDeltaOk( $ver )) { + local( @verlist ) = split( /\//, $ver ) ; + pop( @verlist ) ; + pop( @verlist ) ; + local( $ver2 ) = join( "/", @verlist ) ; + &CTUDebug( "ver2 = '" . $ver2 . "'\n" ) ; + &CTDeltaSafeMerge( $_[0], $ver, $ver2 ) ; + system "cleartool checkin -nc $_[0] 2> /dev/null > /dev/null" ; + &CTUDebug( "merge complete, doing branch check\n" ) ; + &CTDeltaBranchCheck( $_[0], $ver, $timestamp ) ; + &CTUDebug( "logging potentially felonious activity for future" . + " incrimination\n" ) ; + &CTDeltaLog( $_[0], $ver, $ver2 ) ; + # better detection needs to be done + $ret = 1 ; + } else { + &CTUDebug( "cannot merge '" . $_[0] . "', no branches.\n" ) ; + } + &CTUDebug( "out of CTCcaseDelta\n" ) ; + $ret ; +} + +# checkout an element +# input is in: +# $_[0] = element to checkout +# $_[1] = possible comment +# +# output: +# return success or failure +sub CTCcaseCheckout { + &CTUDebug( "in CTCcaseCheckout\n" ) ; + local( $comment) = &CTCcaseFormatComment( $_[1] ) ; + local( $ret ) = &CTURetCode( system( "cleartool co " . $comment . + " $_[0]\n" )) ; + &CTUDebug( "out of CTCcaseCheckout\n" ) ; + $ret ; +} + +# checkin an element +# input is in: +# $_[0] = element to checkin + +# +# output: +# return success or failure +sub CTCcaseCheckin { + &CTUDebug( "in CTCcaseCheckin\n" ) ; + local( $comment) = &CTCcaseFormatComment( $_[1] ) ; + local( $ret ) = &CTURetCode( system( "cleartool ci " . $comment . + " $_[0]\n" )) ; + &CTUDebug( "out of CTCcaseCheckin\n" ) ; + $ret ; +} + +# uncheckout an element +# input is in: +# $_[0] = element to uncheckout +# +# output: +# return success or failure +sub CTCcaseUncheckout { + require "$tool/inc/unco.pl" ; + &CTUDebug( "in CTCcaseUncheckout\n" ) ; + local( $ret ) = 1 ; + # need better error checking on this + system( "cleartool unco -rm $_[0]\n" ) ; + &CTUncoDoIt( $_[0] ) ; + &CTUDebug( "out of CTCcaseUncheckout\n" ) ; + $ret ; +} + +# figure out what all I have checked out or on my branch +# input is in: +# $_[0] = project +# $_[1] = flavor +# $_[2] = spec line +# +# output: +# return a \n serperated list of elements checked out +sub CTCcaseIHave { + &CTUDebug( "in CTCcaseIHave\n" ) ; + local( $ret ) = "" ; + local( $branch ) = &CTAttachComputeBranch( $_[2], $_[1], $_[0] ) ; + local( $root ) = &CTProjRoot( $_[0] ) ; + local( *OUTPUT ) ; + open( OUTPUT, "cleartool find " . $root . " -element \"brtype(" . + $branch . ")\" -nxn -print |" ) ; + while ( ) { + $ret = $ret . $_ ; + } + close( OUTPUT ) ; + &CTUDebug( "out of CTCcaseIHave\n" ) ; + $ret ; +} + +# remove a versioned element +# input is in: +# $_[0] = element to remove +# $_[1] = curr dir +# +# output: +# return success or failure +sub CTCcaseRmElem { + &CTUDebug( "in CTCcaseRmElem\n" ) ; + local( $ret ) = 0 ; + local( $elem ) = $_[0] ; + if ( ! ( $elem =~ /^\// )) { + $elem = $_[1] . "/" . $elem ; + } + # first we have to check out the parent directory + local( @alist ) = split( /\//, $elem ) ; + pop( @alist ) ; + local( $parent ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; + $ret = system( "cleartool co -nc $parent\n" ) ; + if ( $ret == 0 ) { + # now nuke the element + $ret = &CTURetCode( system( "cleartool rmname $elem\n" )) ; + } else { + $ret = 0 ; + } + &CTUDebug( "out of CTCcaseRmElem\n" ) ; + $ret ; +} + +# mv a versioned element from one name to another +# input is in: +# $_[0] = from element +# $_[1] = to element +# $_[2] = current directory +# +# output: +# return success or failure +sub CTCcaseMv { + &CTUDebug( "in CTCcaseMv\n" ) ; + local( $ret ) = 0 ; + local( $elem ) = $_[0] ; + if ( ! ( $elem =~ /^\// )) { + $elem = $_[2] . "/" . $elem ; + } + # first we have to check out the parent directory + local( @alist ) = split( /\//, $elem ) ; + pop( @alist ) ; + local( $parent ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; + local( $elem2 ) = $_[1] ; + if ( ! ( $elem2 =~ /^\// )) { + $elem2 = $_[2] . "/" . $elem2 ; + } + local( @alist ) = split( /\//, $elem2 ) ; + pop( @alist ) ; + local( $parent2 ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $elem2 . "' is '" . $parent2 . + "'\n" ) ; + $ret = system( "cleartool co -nc $parent\n" ) ; + if ( $ret == 0 ) { + $ret = system( "cleartool co -nc $parent2\n" ) ; + if ( $ret == 0 ) { + # now move the element + $ret = &CTURetCode( system( "cleartool mv $elem $elem2\n" )) ; + } else { + $ret = 0 ; + } + } else { + $ret = 0 ; + } + &CTUDebug( "out of CTCcaseMv\n" ) ; + $ret ; +} + +# build a list of targets +# input is in: +# $_[0] = targets +# +# output: +# return success or failure +sub CTCcaseMake { + &CTUDebug( "in CTCcaseMake\n" ) ; + local( $ret ) = 0 ; + local( $line ) = "clearmake -C gnu " . $_[0] . + " |& grep -v \"^clearmake: Warning: Config\"\n" ; + &CTUDebug( "line = '" . $line . "'\n" ) ; + $ret = &CTURetCode( system( $line )) ; + &CTUDebug( "out of CTCcaseMake\n" ) ; + $ret ; +} + +1; diff --git a/dtool/src/attach/ctci b/dtool/src/attach/ctci new file mode 100644 index 0000000000..f950e53a56 --- /dev/null +++ b/dtool/src/attach/ctci @@ -0,0 +1,55 @@ +#!/usr/local/bin/perl + +sub CTCiUsage { + print STDERR "Usage: ctci [-c \"comment\"] [-nc] element-name [...]\n" ; + print STDERR "Options:\n" ; + print STDERR " -c \"comment\" : provide a comment about this action\n" ; + print STDERR " -nc : expect no comment on this action\n" ; + exit; +} + +if ( $#ARGV < 0 ) { + &CTCiUsage ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured for CTtools" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$comment = "" ; +if ( $ARGV[0] eq "-nc" ) { + shift( @ARGV ) ; + &CTUDebug( "-nc processed\n" ) ; +} +if ( $ARGV[0] eq "-c" ) { + shift( @ARGV ) ; + $comment = $ARGV[0] ; + shift( @ARGV ) ; + &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; +} + +if ( $#ARGV < 0 ) { + &CTCiUsage ; +} + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +foreach $item ( @ARGV ) { + if ( -e $item ) { + if ( ! &CTCMCheckin( $item, $projname, $spec, $comment ) ) { + print STDERR "Could not checkin '$item'\n" ; + } + } else { + print STDERR "No such file '$item'.\n" ; + } +} diff --git a/dtool/src/attach/ctcm.pl b/dtool/src/attach/ctcm.pl new file mode 100644 index 0000000000..1b721cc22f --- /dev/null +++ b/dtool/src/attach/ctcm.pl @@ -0,0 +1,580 @@ +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; + +# given a spec line, do the 'correct' setup for it +# input is in: +# $_[0] = project +# $_[1] = spec line +# $_[2] = flavor +sub CTCMSetup { + local( $type ) = &CTSpecType( $_[1] ) ; + if ( $type eq "vroot" ) { + &CTUDebug( "running setup for an atria tree\n" ) ; + if ( $ENV{"HAVE_ATRIA"} eq "yes" ) { + require "$tool/inc/ctccase.pl" ; + &CTAttachCCSetup( $_[0], $_[1], $_[2] ) ; + } else { + &CTUDebug( "don't HAVE_ATRIA!\n" ) ; + } + # if we don't have atria, and it's a vroot, well.. + } elsif ( $type eq "croot" ) { + &CTUDebug( "running setup for CVS\n" ) ; + require "$tool/inc/ctcvs.pl" ; + local( $serve ) = &CTCvsServerLine( $_[0], $_[1] ) ; + local( $thing ) = &CTCvsLogin( $serve ) ; + if ( ! $thing ) { + print STDERR "CVS login failed given server line '" . $serve . + "'\n" ; + } + print STDERR "I don't know how to setup CVS stuff yet!!\n" ; + } + # no other types have any work that needs to be done at this time +} + +# given a directory, make sure it's versioned +# input is in: +# $_[0] = directory +# $_[1] = project +# $_[2] = spec line +# $_[3] = comment (optional, "" if none) +# +# output: +# return success or failure +sub CTCMMkdir { + &CTUDebug( "in CTCMMkdir\n" ) ; + local( $ret ) = 0 ; + # first check that the directory is in the project, and is not the root + local( $flav ) = &CTQueryProj( $_[1] ) ; + local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; + local( $pwd ) = &CTUCurrDir() ; + local( $isok ) = 0 ; + if ( $_[0] =~ /^\// ) { + # starts with a /, might not be in the project we are + if (( $_[0] =~ /^$root/ ) && ( $_[0] ne $root )) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + if ( $isok ) { + # ok, it is. Does one already exist? + if ( -e $_[0] ) { + # already one there, nothing to do + &CTUDebug( "directory '" . $_[0] . "' already exists\n" ) ; + $ret = 1 ; + } else { + # now switch off on how to actually do it + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseMkdir( $_[0], $pwd, $_[3] ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolMkdir( $_[0], $pwd, $_[3] ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsMkdir( $_[0], $_[1], $_[2], $_[3] ) ; + } else { + print STDERR "CTCMMkdir::error! got invalid spec type '" . + $type . "'\n" ; + } + } + } else { + print STDERR "directory '" . $_[0] . "' not in project '" . $_[1] . + "' or is the root.\n" ; + } + &CTUDebug( "out of CTCMMkdir\n" ) ; + $ret ; +} + +# given a file, make sure it's versioned +# input is in: +# $_[0] = file +# $_[1] = project +# $_[2] = spec line +# $_[3] = comment (optional, "" if none) +# $_[4] = eltype (optional, "" if none) +# +# output: +# return success or failure +sub CTCMMkelem { + &CTUDebug( "in CTCMMkelem\n" ) ; + local( $ret ) = 0; + # first check that the directory is in the project + local( $flav ) = &CTQueryProj( $_[1] ) ; + local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; + local( $isok ) = 0 ; + local( $pwd ) = &CTUCurrDir() ; + # synth an eltype if there is none + if ( ! -e $_[0] ) { + # need it to already exist + $isok = 0 ; + } else { + if ( -d $_[0] ) { + # wrong command for a directory + $isok = 0 ; + } else { + if ( $_[0] =~ /^\// ) { + # starts with a /, might not be in the project we are + if ( $_[0] =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + } + } + if ( $isok ) { + # now switch off on how to actually do the work + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseMkelem( $_[0], $pwd, $_[3], $_[4] ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolMkelem( $_[0], $pwd, $_[3], $_[4] ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsMkelem( $_[0], $_[1], $_[2], $_[3] ) ; + } else { + print STDERR "CTCMMkelem::error! got invalid spec type '" . + $type . "'\n" ; + } + } + &CTUDebug( "out of CTCMMkelem\n" ) ; + $ret ; +} + +# given an element, delta it in +# input is in: +# $_[0] = element +# $_[1] = project +# $_[2] = spec line +# +# output: +# return success or failure +sub CTCMDelta { + &CTUDebug( "in CTCMDelta\n" ) ; + local( $ret ) = 0 ; + # first check that the element is in the project + local( $flav ) = &CTQueryProj( $_[1] ) ; + local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; + local( $pwd ) = &CTUCurrDir() ; + local( $isok ) = 0 ; + if ( ! -e $_[0] ) { + # can't delta something that doesn't exist + $isok = 0 ; + } else { + if ( $_[0] =~ /^\// ) { + # starts with a /, might not be in the project we are + if ( $_[0] =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + } + if ( $isok ) { + # now switch off on how to actually do the work + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseDelta( $_[0] ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolDelta( $_[0] ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsDelta( $_[0], $_[1], $_[2] ) ; + } else { + print STDERR "CTCMDelta::error! got invalid spec type '" . $type . + "'\n" ; + } + } else { + &CTUDebug( "failed delta pre-checks\n" ) ; + } + &CTUDebug( "out of CTCMDelta\n" ) ; + $ret ; +} + +# given an element, check it out +# input is in: +# $_[0] = element +# $_[1] = project +# $_[2] = spec line +# $_[3] = comment (optional, "" if none) +# +# output: +# return success or failure +sub CTCMCheckout { + &CTUDebug( "in CTCMCheckout\n" ) ; + local( $ret ) = 0 ; + # first check that the element is in the project + local( $flav ) = &CTQueryProj( $_[1] ) ; + local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; + local( $pwd ) = &CTUCurrDir() ; + local( $isok ) = 0 ; + if ( ! -e $_[0] ) { + # can't checkout something that doesn't exist + $isok = 0 ; + } else { + if ( $_[0] =~ /^\// ) { + # starts with a /, might not be in the project we are + if ( $_[0] =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + } + if ( $isok ) { + # now switch off on how to actually do the work + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseCheckout( $_[0], $_[3] ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolCheckout( $_[0], $_[3] ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsCheckout( $_[0], $_[1], $_[2], $_[3] ) ; + } else { + print STDERR "CTCMCheckout::error! got invalid spec type '" . + $type . "'\n" ; + } + } + &CTUDebug( "out of CTCMCheckout\n" ) ; + $ret ; +} + +# given an element, check it in +# input is in: +# $_[0] = element +# $_[1] = project +# $_[2] = spec line +# $_[3] = comment (optional, "" if none) +# +# output: +# return success or failure +sub CTCMCheckin { + &CTUDebug( "in CTCMCheckin\n" ) ; + local( $ret ) = 0 ; + # first check that the element is in the project + local( $flav ) = &CTQueryProj( $_[1] ) ; + local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; + local( $pwd ) = &CTUCurrDir() ; + local( $isok ) = 0 ; + if ( ! -e $_[0] ) { + # can't checkin something that doesn't exist + $isok = 0 ; + } else { + if ( $_[0] =~ /^\// ) { + # starts with a /, might not be in the project we are + if ( $_[0] =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + } + if ( $isok ) { + # now switch off on how to actually do the work + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseCheckin( $_[0], $_[3] ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolCheckin( $_[0], $_[3] ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsCheckin( $_[0], $_[1], $_[2], $_[3] ) ; + } else { + print STDERR "CTCMCheckin::error! got invalid spec type '" . + $type . "'\n" ; + } + } + &CTUDebug( "out of CTCMCheckin\n" ) ; + $ret ; +} + +# given an element, uncheck it out +# input is in: +# $_[0] = element +# $_[1] = project +# $_[2] = spec line +# +# output: +# return success or failure +sub CTCMUncheckout { + &CTUDebug( "in CTCMUncheckout\n" ) ; + local( $ret ) = 0 ; + # first check that the element is in the project + local( $flav ) = &CTQueryProj( $_[1] ) ; + local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; + local( $pwd ) = &CTUCurrDir() ; + local( $isok ) = 0 ; + if ( ! -e $_[0] ) { + # can't uncheckout something that doesn't exist + $isok = 0 ; + } else { + if ( $_[0] =~ /^\// ) { + # starts with a /, might not be in the project we are + if ( $_[0] =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + } + if ( $isok ) { + # now switch off on how to actually do the work + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseUncheckout( $_[0] ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolUncheckout( $_[0] ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsUncheckout( $_[0], $_[1], $_[2] ) ; + } else { + print STDERR "CTCMUncheckout::error! got invalid spec type '" . + $type . "'\n" ; + } + } + &CTUDebug( "out of CTCMUncheckout\n" ) ; + $ret ; +} + +# figure out what all I have checked out in a project +# input is in: +# $_[0] = project +# $_[1] = flavor +# $_[2] = spec line +# +# output: +# return a \n serperated list of elements checked out +sub CTCMIHave { + &CTUDebug( "in CTCMIHave\n" ) ; + local( $ret ) = "" ; + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseIHave( $_[0], $_[1], $_[2] ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolIHave( $_[0], $_[1], $_[2] ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsIHave( $_[0], $_[1], $_[2] ) ; + } else { + print STDERR "CTCMIHave::error! got invalid spec type '" . $type . + "'\n" ; + } + &CTUDebug( "out of CTCMIHave\n" ) ; + $ret ; +} + +# given an element, remove it from the repository +# input is in: +# $_[0] = element +# $_[1] = project +# $_[2] = spec line +# +# output: +# return success or failure +sub CTCMRmElem { + &CTUDebug( "in CTCMRmElem\n" ) ; + local( $ret ) = 0 ; + # first check that the element is in the project + local( $flav ) = &CTQueryProj( $_[1] ) ; + local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; + local( $pwd ) = &CTUCurrDir() ; + local( $isok ) = 0 ; + if ( ! -e $_[0] ) { + # can't rmname something that doesn't exist + $isok = 0 ; + } else { + if ( $_[0] =~ /^\// ) { + # starts with a /, might not be in the project we are + if ( $_[0] =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + } + if ( $isok ) { + # now switch off on how to actually do the work + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseRnElem( $_[0], $pwd ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolRmElem( $_[0], $pwd ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsRmElem( $_[0], $_[1], $_[2] ) ; + } else { + print STDERR "CTCMRmElem::error! got invalid spec type '" . + $type . "'\n" ; + } + } + &CTUDebug( "out of CTCMRmElem\n" ) ; + $ret ; +} + +# move an element from one name to another +# input is in: +# $_[0] = from element +# $_[1] = to element +# $_[2] = project +# $_[3] = spec line +# +# output: +# return success or failure +sub CTCMMv { + &CTUDebug( "in CTCMMv\n" ) ; + local( $ret ) = 0 ; + # first check that the from and to are in the project + local( $flav ) = &CTQueryProj( $_[1] ) ; + local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ; + local( $pwd ) = &CTUCurrDir() ; + local( $isok ) = 0 ; + if ( $_[0] =~ /^\// ) { + # starts with a /, might not be in the project we are + if ( $_[0] =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + if ( $isok ) { + if ( $_[1] =~ /^\// ) { + # starts with a /, might not be in the project we are + if ( $_[1] =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } else { + # are we sitting in the project? + if ( $pwd =~ /^$root/ ) { + $isok = 1 ; + } else { + $isok = 0 ; + } + } + } + if ( $isok ) { + # now switch off on how to actually do the work + local( $type ) = &CTSpecType( $_[3] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseMv( $_[0], $_[1], $pwd ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolMv( $_[0], $_[1], $pwd ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsMv( $_[0], $_[1], $_[2], $_[3] ) ; + } else { + print STDERR "CTCMMv::error! got invalid spec type '" . + $type . "'\n" ; + } + } + &CTUDebug( "out of CTCMMv\n" ) ; + $ret ; +} + +# give a list of targets, build them +# input is in: +# $_[0] = targets +# $_[1] = project +# $_[2] = spec line +# +# output: +# return success or failure +sub CTCMMake { + &CTUDebug( "in CTCMMake\n" ) ; + local( $ret ) = 0 ; + # now switch off on how to actually do the work + local( $type ) = &CTSpecType( $_[2] ) ; + if ( $type eq "vroot" ) { + require "$tool/inc/ctccase.pl" ; + $ret = &CTCcaseMake( $_[0] ) ; + } elsif ( $type eq "root" ) { + require "$tool/inc/ctntool.pl" ; + $ret = &CTNtoolMake( $_[0] ) ; + } elsif ( $type eq "croot" ) { + require "$tool/inc/ctcvs.pl" ; + $ret = &CTCvsMake( $_[0] ) ; + } else { + print STDERR "CTCMMake::error! got invalid spec type '" . $type . + "'\n" ; + } + &CTUDebug( "out of CTCMMake\n" ) ; + $ret ; +} + +1; diff --git a/dtool/src/attach/ctco b/dtool/src/attach/ctco new file mode 100644 index 0000000000..0bc9fb98cc --- /dev/null +++ b/dtool/src/attach/ctco @@ -0,0 +1,55 @@ +#!/usr/local/bin/perl + +sub CTCoUsage { + print STDERR "Usage: ctco [-c \"comment\"] [-nc] element-name [...]\n" ; + print STDERR "Options:\n" ; + print STDERR " -c \"comment\" : provide a comment about this action\n" ; + print STDERR " -nc : expect no comment on this action\n" ; + exit; +} + +if ( $#ARGV < 0 ) { + &CTCoUsage ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured for CTtools" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$comment = "" ; +if ( $ARGV[0] eq "-nc" ) { + shift( @ARGV ) ; + &CTUDebug( "-nc processed\n" ) ; +} +if ( $ARGV[0] eq "-c" ) { + shift( @ARGV ) ; + $comment = $ARGV[0] ; + shift( @ARGV ) ; + &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; +} + +if ( $#ARGV < 0 ) { + &CTCoUsage ; +} + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +foreach $item ( @ARGV ) { + if ( -e $item ) { + if ( ! &CTCMCheckout( $item, $projname, $spec, $comment ) ) { + print STDERR "Could not checkout '$item'\n" ; + } + } else { + print STDERR "No such file '$item'.\n" ; + } +} diff --git a/dtool/src/attach/ctcvs.pl b/dtool/src/attach/ctcvs.pl new file mode 100644 index 0000000000..2099c70c1b --- /dev/null +++ b/dtool/src/attach/ctcvs.pl @@ -0,0 +1,408 @@ +# given a possibly empty string, format it into a comment or -nc +# input is in: +# $_[0] = possible comment string +# +# output is: +# string for use by CVS functions +sub CTCvsFormatComment { + local( $ret ) = "" ; + if ( $_[0] ne "" ) { + $ret = "-m " . $_[0] ; + } + $ret ; +} + +# given a project and spec line, compute the server line +# input is in: +# $_[0] = project +# $_[1] = spec line +# +# output: +# return a sever line, or "" if not a croot +sub CTCvsServerLine { + &CTUDebug( "in CTCvsServerLine\n" ) ; + local( $ret ) = "" ; + local( $type ) = &CTSpecType( $_[1] ) ; + if ( $type eq "croot" ) { + local( $options ) = &CTSpecOptions( $_[1] ) ; + local( $sline ) = &CTSpecFindOption( $options, "server" ) ; + if ( $sline ne "" ) { + $ret = join( ":", split( /,/, $sline )); + } + } + &CTUDebug( "out of CTCvsServerLine\n" ) ; + $ret ; +} + +# if needed log into a cvs server +# input is in: +# $_[0] = server line +# +# output: +# return success or failure +sub CTCvsLogin { + &CTUDebug( "in CTCvsLogin\n" ) ; + local( $ret ) = 0 ; + &CTUDebug( "server line is '" . $_[0] . "'\n" ) ; + if ( $_[0] ne "" ) { + # ok. we actually have something, lets look in .cvspass + local( $path ) ; + local( *PASSFILE ) ; + if ( $ENV{"PENV"} eq "WIN32" ) { + $path = $ENV{"HOME"} . "/.cvspass" ; + } else { + # $path = "~/.cvspass" ; + $path = $ENV{"HOME"} . "/.cvspass" ; + } + &CTUDebug( "looking for '" . $path . "'\n" ) ; + if ( -e $path ) { + open( PASSFILE, "< $path" ) ; + $_ = ; + s/\n$// ; + local( @line ) = split ; + # ok, the server line is in [0] and the password in [1]. + &CTUDebug( "server line from .cvspass is '" . $line[0] . "'\n" ) ; + if ( $line[0] ne $_[0] ) { + local( $line ) = "cvs -d " . $_[0] . " login" ; + &CTUDebug( "about to run '" . $line . "'\n" ) ; + $ret = &CTURetCode( system( $line )) ; + } else { + # we're fine, we're already logged in to that + $ret = 1 ; + } + } else { + &CTUDebug( $path . " file does not exist\n" ) ; + local( $line ) = "cvs -d " . $_[0] . " login" ; + &CTUDebug( "about to run '" . $line . "'\n" ) ; + $ret = &CTURetCode( system( $line )) ; + } + } + &CTUDebug( "out of CTCvsLogin\n" ) ; + $ret ; +} + +require "$tool/inc/ctproj.pl" ; + +# add a versioned element to the repository +# input is in: +# $_[0] = element +# $_[1] = project +# $_[2] = spec line +# $_[3] = possible comment +# +# output: +# return success or failure +sub CTCvsAdd { + &CTUDebug( "in CTCvsAdd\n" ) ; + # first we need to 'login' to the repository + local( $comment ) = &CTCvsFormatComment( $_[3] ) ; + local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; + local( $ret ) = &CTCvsLogin( $serve ) ; + if ( $ret ) { + # now issue the add command + local( $root ) = &CTProjRoot( $_[1] ) ; + local( $line ) = "" ; + local( $elem ) = $_[0] ; + if ( $elem =~ /^\// ) { + local( $proj ) = $_[1] ; + $proj =~ tr/a-z/A-Z/ ; + $line = "cd \$" . $proj . "; " ; + $elem =~ s/^$root\/// ; + } + $line = $line . "cvs -d " . $serve . " add " . $comment . " $elem" ; + &CTUDebug( "about to execute '" . $line . "'\n" ) ; + $ret = &CTURetCode( system( $line )) ; + } + &CTUDebug( "out of CTCvsAdd\n" ) ; + $ret ; +} + +# ci a versioned element to the repository +# input is in: +# $_[0] = element +# $_[1] = project +# $_[2] = spec line +# $_[3] = possible comment +# +# output: +# return success or failure +sub CTCvsCi { + &CTUDebug( "in CTCvsCi\n" ) ; + # first we need to 'login' to the repository + local( $comment ) = &CTCvsFormatComment( $_[3] ) ; + local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; + local( $ret ) = &CTCvsLogin( $serve ) ; + if ( $ret ) { + # now issue the add command + local( $root ) = &CTProjRoot( $_[1] ) ; + local( $line ) = "" ; + local( $elem ) = $_[0] ; + if ( $elem =~ /^\// ) { + local ( $proj ) = $_[1] ; + $proj =~ tr/a-z/A-Z/ ; + $line = "cd \$" . $proj . "; " ; + $elem =~ s/^$root\/// ; + } + $line = $line . "cvs -d " . $serve . " ci " . $comment . " $elem" ; + &CTUDebug( "about to execute '" . $line . "'\n" ) ; + $ret = &CTURetCode( system( $line )) ; + } + &CTUDebug( "out of CTCvsCi\n" ) ; + $ret ; +} + +# rm a versioned element from the repository +# input is in: +# $_[0] = element +# $_[1] = project +# $_[2] = spec line +# +# output: +# return success or failure +sub CTCvsRm { + &CTUDebug( "in CTCvsRm\n" ) ; + # first we need to 'login' to the repository + local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; + local( $ret ) = &CTCvsLogin( $serve ) ; + if ( $ret ) { + # now issue the add command + $ret = &CTURetCode( system( "cvs -d " . $serve . " rm $_[0]\n" )) ; + } + &CTUDebug( "out of CTCvsRm\n" ) ; + $ret ; +} + +# make a versioned directory +# input is in: +# $_[0] = directory to create +# $_[1] = project +# $_[2] = spec line +# $_[3] = possible comment +# +# output: +# return success or failure +sub CTCvsMkdir { + &CTUDebug( "in CTCvsMkdir\n" ) ; + local( $ret ) = 0 ; + # first make the dir + $ret = &CTURetCode( system( "mkdir $_[0]\n" )) ; + if ( $ret ) { + # now version it + $ret = &CTCvsAdd( $_[0], $_[1], $_[2], $_[3] ) ; + } else { + &CTUDebug( "could not create directory '" . $_[0] . "'\n" ) ; + $ret = 0 ; + } + &CTUDebug( "out of CTCvsMkdir\n" ) ; + $ret ; +} + +# make a versioned element +# input is in: +# $_[0] = element to version +# $_[1] = project +# $_[2] = spec line +# $_[3] = possible comment +# +# output: +# return success or failure +sub CTCvsMkelem { + &CTUDebug( "in CTCvsMkelem\n" ) ; + # first cvs add the file + local( $ret ) = &CTCvsAdd( $_[0], $_[1], $_[2], $_[3] ) ; + if ( $ret ) { + # now commit it + $ret = &CTCvsCi( $_[0], $_[1], $_[2], $_[3] ) ; + } else { + &CTUDebug( "could not CVS add '" . $_[0] . "'\n" ) ; + $ret = 0 ; + } + &CTUDebug( "out of CTCvsMkelem\n" ) ; + $ret ; +} + +# delta an element +# input is in: +# $_[0] = element to delta +# $_[1] = project +# $_[2] = spec line +# +# output: +# return success or failure +sub CTCvsDelta { + &CTUDebug( "in CTCvsDelta\n" ) ; + local( $ret ) = 0 ; + # for lack of better idea, this is going to be just checkin for now + if ( -d $_[0] ) { + # we don't version directories in CVS + $ret = 1 ; + } else { + $ret = &CTCvsCi( $_[0], $_[1], $_[2] ) ; + } + &CTUDebug( "out of CTCvsDelta\n" ) ; + $ret ; +} + +# checkout an element +# input is in: +# $_[0] = element to checkout +# $_[1] = project +# $_[2] = spec line +# $_[3] = possible comment +# +# output: +# return success or failure +sub CTCvsCheckout { + &CTUDebug( "in CTCvsCheckout\n" ) ; + local( $ret ) = 1 ; + # for my limited understanding of CVS, there doesn't seem to be any + # 'checkout' for it. + &CTUDebug( "out of CTCvsCheckout\n" ) ; + $ret ; +} + +# checkin an element +# input is in: +# $_[0] = element to checkin +# $_[1] = project +# $_[2] = spec line +# $_[3] = possible comment +# +# output: +# return success or failure +sub CTCvsCheckin { + &CTUDebug( "in CTCvsCheckin\n" ) ; + local( $ret ) = 0 ; + if ( -d $_[0] ) { + # we don't version directories in CVS + $ret = 1 ; + } else { + $ret = &CTCvsCi( $_[0], $_[1], $_[2], $_[3] ) ; + } + &CTUDebug( "out of CTCvsCheckin\n" ) ; + $ret ; +} + +# uncheckout an element +# input is in: +# $_[0] = element to uncheckout +# $_[1] = project +# $_[2] = spec line +# +# output: +# return success or failure +sub CTCvsUncheckout { + &CTUDebug( "in CTCvsUncheckout\n" ) ; + local( $ret ) = 0 ; + if ( -d $_[0] ) { + # we don't version directories in CVS + $ret = 1 ; + } else { + $ret = &CTURetCode( system( "rm $_[0]" ) ) ; + if ( $ret ) { + local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ; + $ret = &CTCvsLogin( $serve ) ; + if ( $ret ) { + $ret = &CTURetCode( system( "cvs -d " . $serve . " update " . + $_[0] )) ; + } + } + } + &CTUDebug( "out of CTCvsUncheckout\n" ) ; + $ret ; +} + +# figure out what all I have checked out +# input is in: +# $_[0] = project +# $_[1] = flavor +# $_[2] = spec line +# +# output: +# return a \n serperated list of elements checked out +sub CTCvsIHave { + &CTUDebug( "in CTCvsIHave\n" ) ; + local( $ret ) = "" ; + print STDERR "I don't know how to do IHave yet!\n" ; + &CTUDebug( "out of CTCvsIHave\n" ) ; + $ret ; +} + +# remove an element from the repository +# input is in: +# $_[0] = element to uncheckout +# $_[1] = project +# $_[2] = spec line +# +# output: +# return success or failure +sub CTCvsRmElem { + &CTUDebug( "in CTCvsRmElem\n" ) ; + local( $ret ) = 0 ; + if ( -d $_[0] ) { + # CVS doesn't really do this. If there are no files in the directory, + # the next time an update -P is run, it will be deleted. + $ret = 1 ; + } else { + $ret = &CTURetCode( system( "rm $_[0]" ) ) ; + if ( $ret ) { + $ret = &CTCvsRm( $_[0], $_[1], $_[2] ) ; + if ( $ret ) { + $ret = &CTCvsCi( $_[0], $_[1], $_[2] ) ; + } + } + } + &CTUDebug( "out of CTCvsRmElem\n" ) ; + $ret ; +} + +# move a versioned element from one name to another +# input is in: +# $_[0] = from element +# $_[1] = to element +# $_[2] = project +# $_[3] = spec line +# +# output: +# return success or failure +sub CTCvsMv { + &CTUDebug( "in CTCvsMv\n" ) ; + local( $ret ) = 0 ; + if ( -d $_[0] ) { + # don't have code to do directories yet. See pp 54 of the CVS book + $ret = 0 ; + } else { + $ret = &CTURetCode( system( "mv $_[0] $_[1]" ) ) ; + if ( $ret ) { + $ret = &CTCvsRm( $_[0], $_[2], $_[3] ) ; + if ( $ret ) { + $ret = &CTCvsAdd( $_[1], $_[2], $_[3] ); + if ( $ret ) { + $ret = &CTCvsCi( $_[0], $_[2], $_[3] ) ; + if ( $ret ) { + $ret = &CTCvsCi( $_[1], $_[2], $_[3] ) ; + } + } + } + } + } + &CTUDebug( "out of CTCvsMv\n" ) ; + $ret ; +} + +# build a list of targets +# input is in: +# $_[0] = targets +# +# output: +# return success or failure +sub CTCvsMake { + &CTUDebug( "in CTCvsMake\n" ) ; + local( $ret ) = 0 ; + local( $line ) = "make " . $_[0] . "\n" ; + $ret = &CTURetCode( system( $line )) ; + &CTUDebug( "out of CTCvsMake\n" ) ; + $ret ; +} + +1; diff --git a/dtool/src/attach/ctdelta b/dtool/src/attach/ctdelta new file mode 100755 index 0000000000..c26193d149 --- /dev/null +++ b/dtool/src/attach/ctdelta @@ -0,0 +1,32 @@ +#!/usr/local/bin/perl + +if ($#ARGV < 0) { + exit print "Usage: ctdelta element-name [...]\n" ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "not configured for using ct-tools\n" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +foreach $item ( @ARGV ) { + if ( -e $item ) { + &CTCMCheckin( $item, $projname, $spec ) ; + if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + print STDERR "Could not delta '$item'\n" ; + } + } else { + print STDERR "No such file '$item'.\n" ; + } +} diff --git a/dtool/src/attach/ctdelta.pl b/dtool/src/attach/ctdelta.pl new file mode 100644 index 0000000000..2b870fa495 --- /dev/null +++ b/dtool/src/attach/ctdelta.pl @@ -0,0 +1,232 @@ +# Check in element if needed +# Input is: +# $_[0] = element name +sub CTDeltaCheckin { + local( $cmd ) = "cleartool ci -nc $_[0] 2> /dev/null > /dev/null" ; + system $cmd ; +} + +# get the version of an element +# Input is: +# $_[0] = element name +sub CTDeltaGetVersion { + local( *CMDFILE ) ; + open( CMDFILE, "cleartool describe -short $_[0] |" ) ; + $_ = ; + close( CMDFILE ) ; + s/\n$// ; + s/^.*@@// ; + s/\"$// ; + $_ ; +} + +# Is it ok to try a merge on this version? +# Input is: +# $_[0] = version +sub CTDeltaOk { + local( $ret ) ; + local( @verlist ) ; + @verlist = split( /\//, $_[0] ) ; + pop( @verlist ) ; + if ( $#verlist > 1 ) { + $ret = 1 ; + } else { + $ret = 0 ; + } + $ret ; +} + +# get the comments from a version of an element +# Input is: +# $_[0] = element name +# $_[1] = version +# +# output in: +# @CTDeltaComments +sub CTDeltaGetComments { + local( *CMDFILE ) ; + local( $done ) = 0 ; + local( $end ) = " element type:" ; + local( $tmp ) = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; + open( CMDFILE, $tmp ) ; + $_ = ; + $_ = ; + while ( ! $done ) { + $_ = ; + if ( $_ =~ /^$end/ ) { + $done = 1 ; + } else { + s/^ // ; + s/^ // ; + s/^\"// ; + s/\n$// ; + s/\"$// ; + push( @CTDeltaComments, $_ ) ; + } + } + close( CMDFILE ) ; +} + +# try automatic merge. If it fails, use xmerge +# Input is: +# $_[0] = element name +# $_[1] = source version +# $_[2] = target version +sub CTDeltaSafeMerge { + @CTDeltaComments = (); + &CTDeltaGetComments($_[0], $_[1]); + local( $ret ) ; + $ret = "cleartool checkout -branch $_[2] -nc $_[0] 2> /dev/null > /dev/null" ; + $ret = system $ret ; + if ( $ret != 0 ) { + print STDERR "got return value $ret from checkout on '$_[0]" . "@@" . "$_[2]'\n" ; + exit -1; + } + local( $item ) ; + foreach $item ( @CTDeltaComments ) { + $ret = "cleartool chevent -append -c \"" . $item . "\" $_[0]" . "@@" . "$_[2]" . "/LATEST 2> /dev/null > /dev/null" ; + system $ret ; + } + print STDERR "merging '$_[0]'...\n" ; + $ret = "cleartool merge -abort -to $_[0] -version $_[1] 2> /dev/null > /dev/null" ; + $ret = system $ret ; + if ( $ret != 0 ) { + $ret = system "cleartool xmerge -to $_[0] -version $_[1]" ; + } + if ( ! -d $_[0] ) { + system "rm $_[0]" . ".contrib" ; + } + $ret ; +} + +# test a branch for 'triviality' +# Input is: +# $_[0] = element name +# $_[1] = branch name +# +# Output is: +# true/false +sub CTDeltaTestBranch { + local( *CTCMD ) ; + local( $ret ) ; + local( $done ) = 0 ; + local( $bfrom ) ; + local( @blist ) ; + local( $bto ) ; + local( $bdiff ) ; + local( $blast ) ; + @blist = split( /\//, $_[1] ) ; + pop( @blist ) ; + $ret = join( "/", @blist ) ; + $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; + open( CTCMD, $ret ) ; + while ( ! $done ) { + $_ = ; + if ( $_ =~ /^ branched from version/ ) { + $done = 1 ; + } + } + close( CTCMD ) ; + s/^ branched from version: // ; + s/\n$// ; + $bfrom = $_ ; + @blist = split( /\//, $_ ) ; + pop( @blist ) ; + push( @blist, "LATEST" ) ; + $ret = join( "/", @blist ) ; + $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; + open( CTCMD, $ret ) ; + $_ = ; + close( CTCMD ) ; + s/\n$// ; + s/^.*@@// ; + s/\"$// ; + $bto = $_ ; + @blist = split( /\//, $bfrom ) ; + $bfrom = pop( @blist ) ; + @blist = split( /\//, $bto ) ; + $bto = pop( @blist ) ; + $bdiff = $bto - $bfrom ; + $ret = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; + open( CTCMD, $ret ) ; + $_ = ; + close( CTCMD ) ; + s/\n$// ; + s/^.*@@// ; + s/\"$// ; + @blist = split( /\//, $_ ) ; + $blast = pop( @blist ) ; + if (( $bdiff > 1 ) || ( $blast > 1 )) { + $ret = 0 ; + } else { + $ret = 1 ; + } +} + +# check for trivial branch elimination +# Input is: +# $_[0] = element name +# $_[1] = last branch version +# $_[2] = timestamp string +sub CTDeltaBranchCheck { + local( $test ) = &CTDeltaTestBranch( $_[0], $_[1] ) ; + local( $cmd ) ; + local( @blist ) ; + local( $branch ) ; + @blist = split( /\//, $_[1] ) ; + if ( $test ) { + pop( @blist ) ; + $cmd = join( "/", @blist ) ; + $branch = join( "/", @blist ) ; + $cmd = "cleartool rmbranch -force $_[0]" . "@@" . "$cmd 2> /dev/null > /dev/null" ; + print STDERR "deleting branch '$branch'...\n" ; + system $cmd ; + } else { + pop( @blist ) ; + $branch = join( "/", @blist ) ; + $test = pop( @blist ) ; + $test = $test . $_[2] ; + $cmd = "cleartool mkbrtype -c \"non-trivial branch\" $test 2> /dev/null > /dev/null" ; + system $cmd ; + $cmd = "cleartool chtype -c \"renaming non-trivial branch\" $test $_[0]" . "@@" . "$branch 2> /dev/null > /dev/null" ; + print STDERR "renaming branch '$branch'...\n" ; + system $cmd ; + } +} + +# log merge to /usr/local/etc/delta_log +# Input is: +# $_[0] = element name +# $_[1] = source version +# $_[2] = target version +sub CTDeltaLog { + local( *LOGFILE ) ; + local( *CMDFILE ) ; + local( $cmd ) ; + open( LOGFILE, ">>/usr/local/etc/delta_log" ) ; + print LOGFILE $_[0] . ": " . $_[1] . " -> " . $_[2] . " : " ; + if ( $ctdebug ne "" ) { + print STDERR "CTDeltaLog: outputting '" . $_[0] . ": " . $_[1] . " -> " . $_[2] . " : '\n" ; + } + $cmd = "ypmatch `whoami` passwd | cut -d: -f5 |" ; + open( CMDFILE, $cmd ) ; + $_ = ; + s/\n$//; + print LOGFILE $_ . " " ; + if ( $ctdebug ne "" ) { + print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; + } + close( CMDFILE ) ; + $cmd = "/bin/date '+%m/%d/%y %H:%M:%S' |" ; + open( CMDFILE, $cmd ) ; + $_ = ; + s/\n$//; + print LOGFILE $_ . "\n" ; + if ( $ctdebug ne "" ) { + print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; + } + close( CMDFILE ) ; + close( LOGFILE ) ; +} + +1; diff --git a/dtool/src/attach/ctdelta.pl.rnd b/dtool/src/attach/ctdelta.pl.rnd new file mode 100644 index 0000000000..dde5feb574 --- /dev/null +++ b/dtool/src/attach/ctdelta.pl.rnd @@ -0,0 +1,232 @@ +# Check in element if needed +# Input is: +# $_[0] = element name +sub CTDeltaCheckin { + local( $cmd ) = "cleartool ci -nc $_[0] 2> /dev/null > /dev/null" ; + system $cmd ; +} + +# get the version of an element +# Input is: +# $_[0] = element name +sub CTDeltaGetVersion { + local( *CMDFILE ) ; + open( CMDFILE, "cleartool describe -short $_[0] |" ) ; + $_ = ; + close( CMDFILE ) ; + s/\n$// ; + s/^.*@@// ; + s/\"$// ; + $_ ; +} + +# Is it ok to try a merge on this version? +# Input is: +# $_[0] = version +sub CTDeltaOk { + local( $ret ) ; + local( @verlist ) ; + @verlist = split( /\//, $_[0] ) ; + pop( @verlist ) ; + if ( $#verlist > 1 ) { + $ret = 1 ; + } else { + $ret = 0 ; + } + $ret ; +} + +# get the comments from a version of an element +# Input is: +# $_[0] = element name +# $_[1] = version +# +# output in: +# @CTDeltaComments +sub CTDeltaGetComments { + local( *CMDFILE ) ; + local( $done ) = 0 ; + local( $end ) = " element type:" ; + local( $tmp ) = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; + open( CMDFILE, $tmp ) ; + $_ = ; + $_ = ; + while ( ! $done ) { + $_ = ; + if ( $_ =~ /^$end/ ) { + $done = 1 ; + } else { + s/^ // ; + s/^ // ; + s/^\"// ; + s/\n$// ; + s/\"$// ; + push( @CTDeltaComments, $_ ) ; + } + } + close( CMDFILE ) ; +} + +# try automatic merge. If it fails, use xmerge +# Input is: +# $_[0] = element name +# $_[1] = source version +# $_[2] = target version +sub CTDeltaSafeMerge { + @CTDeltaComments = (); + &CTDeltaGetComments($_[0], $_[1]); + local( $ret ) ; + $ret = "cleartool checkout -branch $_[2] -nc $_[0] 2> /dev/null > /dev/null" ; + $ret = system $ret ; + if ( $ret != 0 ) { + print STDERR "got return value $ret from checkout on '$_[0]" . "@@" . "$_[2]'\n" ; + exit -1; + } + local( $item ) ; + foreach $item ( @CTDeltaComments ) { + $ret = "cleartool chevent -append -c \"" . $item . "\" $_[0]" . "@@" . "$_[2]" . "/LATEST 2> /dev/null > /dev/null" ; + system $ret ; + } + print STDERR "merging '$_[0]'...\n" ; + $ret = "cleartool merge -abort -to $_[0] -version $_[1] 2> /dev/null > /dev/null" ; + $ret = system $ret ; + if ( $ret != 0 ) { + $ret = system "cleartool xmerge -to $_[0] -version $_[1]" ; + } + if ( ! -d $_[0] ) { + system "rm $_[0]" . ".contrib" ; + } + $ret ; +} + +# test a branch for 'triviality' +# Input is: +# $_[0] = element name +# $_[1] = branch name +# +# Output is: +# true/false +sub CTDeltaTestBranch { + local( *CTCMD ) ; + local( $ret ) ; + local( $done ) = 0 ; + local( $bfrom ) ; + local( @blist ) ; + local( $bto ) ; + local( $bdiff ) ; + local( $blast ) ; + @blist = split( /\//, $_[1] ) ; + pop( @blist ) ; + $ret = join( "/", @blist ) ; + $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; + open( CTCMD, $ret ) ; + while ( ! $done ) { + $_ = ; + if ( $_ =~ /^ branched from version/ ) { + $done = 1 ; + } + } + close( CTCMD ) ; + s/^ branched from version: // ; + s/\n$// ; + $bfrom = $_ ; + @blist = split( /\//, $_ ) ; + pop( @blist ) ; + push( @blist, "LATEST" ) ; + $ret = join( "/", @blist ) ; + $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ; + open( CTCMD, $ret ) ; + $_ = ; + close( CTCMD ) ; + s/\n$// ; + s/^.*@@// ; + s/\"$// ; + $bto = $_ ; + @blist = split( /\//, $bfrom ) ; + $bfrom = pop( @blist ) ; + @blist = split( /\//, $bto ) ; + $bto = pop( @blist ) ; + $bdiff = $bto - $bfrom ; + $ret = "cleartool describe $_[0]" . "@@" . "$_[1] |" ; + open( CTCMD, $ret ) ; + $_ = ; + close( CTCMD ) ; + s/\n$// ; + s/^.*@@// ; + s/\"$// ; + @blist = split( /\//, $_ ) ; + $blast = pop( @blist ) ; + if (( $bdiff > 1 ) || ( $blast > 1 )) { + $ret = 0 ; + } else { + $ret = 1 ; + } +} + +# check for trivial branch elimination +# Input is: +# $_[0] = element name +# $_[1] = last branch version +# $_[2] = timestamp string +sub CTDeltaBranchCheck { + local( $test ) = &CTDeltaTestBranch( $_[0], $_[1] ) ; + local( $cmd ) ; + local( @blist ) ; + local( $branch ) ; + @blist = split( /\//, $_[1] ) ; + if ( $test ) { + pop( @blist ) ; + $cmd = join( "/", @blist ) ; + $branch = join( "/", @blist ) ; + $cmd = "cleartool rmbranch -force $_[0]" . "@@" . "$cmd 2> /dev/null > /dev/null" ; + print STDERR "deleting branch '$branch'...\n" ; + system $cmd ; + } else { + pop( @blist ) ; + $branch = join( "/", @blist ) ; + $test = pop( @blist ) ; + $test = $test . $_[2] ; + $cmd = "cleartool mkbrtype -c \"non-trivial branch\" $test 2> /dev/null > /dev/null" ; + system $cmd ; + $cmd = "cleartool chtype -c \"renaming non-trivial branch\" $test $_[0]" . "@@" . "$branch 2> /dev/null > /dev/null" ; + print STDERR "renaming branch '$branch'...\n" ; + system $cmd ; + } +} + +# log merge to /var/etc/delta_log +# Input is: +# $_[0] = element name +# $_[1] = source version +# $_[2] = target version +sub CTDeltaLog { + local( *LOGFILE ) ; + local( *CMDFILE ) ; + local( $cmd ) ; + open( LOGFILE, ">>/var/etc/delta_log" ) ; + print LOGFILE $_[0] . ": " . $_[1] . " -> " . $_[2] . " : " ; + if ( $ctdebug ne "" ) { + print STDERR "CTDeltaLog: outputting '" . $_[0] . ": " . $_[1] . " -> " . $_[2] . " : '\n" ; + } + $cmd = "ypmatch `whoami` passwd | cut -d: -f5 |" ; + open( CMDFILE, $cmd ) ; + $_ = ; + s/\n$//; + print LOGFILE $_ . " " ; + if ( $ctdebug ne "" ) { + print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; + } + close( CMDFILE ) ; + $cmd = "/bin/date '+%m/%d/%y %H:%M:%S' |" ; + open( CMDFILE, $cmd ) ; + $_ = ; + s/\n$//; + print LOGFILE $_ . "\n" ; + if ( $ctdebug ne "" ) { + print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ; + } + close( CMDFILE ) ; + close( LOGFILE ) ; +} + +1; diff --git a/dtool/src/attach/ctihave b/dtool/src/attach/ctihave new file mode 100755 index 0000000000..e91d7d16de --- /dev/null +++ b/dtool/src/attach/ctihave @@ -0,0 +1,33 @@ +#!/usr/local/bin/perl + +if ($#ARGV != -1) { + exit print "Usage: ctihave\n" ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "not configured for using CTtools\n" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +if ( $projname eq "" ) { + exit print "Not currently in any project tree\n" ; +} + +$result = &CTCMIHave( $projname, $flav, $spec ) ; +if ( $result ne "" ) { + @splitlist = split( /\n/, $result ) ; + foreach $item ( @splitlist ) { + print $item . "\n" ; + } +} diff --git a/dtool/src/attach/ctmake b/dtool/src/attach/ctmake new file mode 100644 index 0000000000..e918739017 --- /dev/null +++ b/dtool/src/attach/ctmake @@ -0,0 +1,23 @@ +#!/usr/local/bin/perl + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "not configured for using ct-tools\n" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +$line = join( " ", @ARGV ) ; + +if ( ! &CTCMMake( $line, $projname, $spec ) ) { + print STDERR "Could not make '$line'\n" ; +} diff --git a/dtool/src/attach/ctmkdir b/dtool/src/attach/ctmkdir new file mode 100644 index 0000000000..e05dadf625 --- /dev/null +++ b/dtool/src/attach/ctmkdir @@ -0,0 +1,55 @@ +#!/usr/local/bin/perl + +sub CTMkDirUsage { + print STDERR "Usage: ctmkdir [-c \"comment\"] [-nc] dir-name [...]\n" ; + print STDERR "Options:\n" ; + print STDERR " -c \"comment\" : provide a comment about this action\n" ; + print STDERR " -nc : expect no comment on this action\n" ; + exit ; +} + +if ( $#ARGV < 0 ) { + &CTMkDirUsage ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured for CTtools" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$comment = "" ; +if ( $ARGV[0] eq "-nc" ) { + shift( @ARGV ) ; + &CTUDebug( "-nc processed\n" ) ; +} +if ( $ARGV[0] eq "-c" ) { + shift( @ARGV ) ; + $comment = $ARGV[0] ; + shift( @ARGV ) ; + &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; +} + +if ( $#ARGV < 0 ) { + &CTMkDirUsage ; +} + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +foreach $item ( @ARGV ) { + if ( -e $item ) { + print STDERR "Name collision on directory '$item'\n" ; + } else { + if ( ! &CTCMMkdir( $item, $projname, $spec, $comment ) ) { + print STDERR "Could name make directory '$item'\n" ; + } + } +} diff --git a/dtool/src/attach/ctmkelem b/dtool/src/attach/ctmkelem new file mode 100644 index 0000000000..cb842c3d2c --- /dev/null +++ b/dtool/src/attach/ctmkelem @@ -0,0 +1,78 @@ +#!/usr/local/bin/perl + +sub CTMkElemUsage { + print STDERR "Usage: ctmkelem [-c \"comment\"] [-nc] [-eltype type] element-name [...]\n" ; + print STDERR "Options:\n" ; + print STDERR " -c \"comment\" : provide a comment about this action\n" ; + print STDERR " -nc : expect no comment on this action\n" ; + print STDERR " -eltype type : element type\n" ; + exit ; +} + +if ( $#ARGV < 0 ) { + &CTMkElemUsage ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured for CTtools" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$comment = "" ; +$eltype = "" ; + +$done = 0 ; + +while ( ! $done ) { + $done = 1 ; + if ( $ARGV[0] eq "-nc" ) { + shift( @ARGV ) ; + &CTUDebug( "-nc processed\n" ) ; + $done = 0 ; + } + if ( $ARGV[0] eq "-c" ) { + shift( @ARGV ) ; + $comment = $ARGV[0] ; + shift( @ARGV ) ; + &CTUDebug( "setting comment to '" . $comment . "'\n" ) ; + $done = 0 ; + } + if ( $ARGV[0] eq "-eltype" ) { + shift( @ARGV ) ; + $eltype = $ARGV[0] ; + shift( @ARGV ) ; + &CTUDebug( "setting eltype to '" . $eltype . "'\n" ) ; + $done = 0 ; + } +} + +if ( $#ARGV < 0 ) { + &CTMkElemUsage ; +} + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +foreach $item ( @ARGV ) { + if ( -e $item ) { + if ( -d $item ) { + print STDERR "Cannot mkelem on an existing directory." . + " Ctmkdir it first.\n" ; + } else { + if ( ! &CTCMMkelem( $item, $projname, $spec, $comment, $eltype )) { + print STDERR "Could not make a versioned element of '" . + $item . "'\n" ; + } + } + } else { + print STDERR "No such file '$item'.\n" ; + } +} diff --git a/dtool/src/attach/ctmv b/dtool/src/attach/ctmv new file mode 100644 index 0000000000..7e50b0a9d4 --- /dev/null +++ b/dtool/src/attach/ctmv @@ -0,0 +1,35 @@ +#!/usr/local/bin/perl + +if ( $#ARGV != 1 ) { + exit print "Usage: ctmv from-element to-element\n" ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured for CTtools" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +$from = $ARGV[0] ; +$to = $ARGV[1] ; + +if ( -e $from ) { + if ( -e $to ) { + print STDERR "'$to' already exists.\n" ; + } else { + if ( ! &CTCMMv( $from, $to, $projname, $spec ) ) { + } + } +} else { + print STDERR "No such element '$from'.\n" ; +} diff --git a/dtool/src/attach/ctntool.pl b/dtool/src/attach/ctntool.pl new file mode 100644 index 0000000000..a4d4352645 --- /dev/null +++ b/dtool/src/attach/ctntool.pl @@ -0,0 +1,258 @@ +# given a possibly empty string, format it into a comment or -nc +# input is in: +# $_[0] = possible comment string +# +# output is: +# string for use by neartool functions +sub CTNtoolFormatComment { + local( $ret ) = "" ; + if ( $_[0] eq "" ) { + $ret = "-nc" ; + } else { + $ret = "-c " . $_[0] ; + } + $ret ; +} + +# make a versioned directory +# input is in: +# $_[0] = directory to create +# $_[1] = curr dir +# $_[2] = possible comment +# +# output: +# return success or failure +sub CTNtoolMkdir { + &CTUDebug( "in CTNtoolMkdir\n" ) ; + local( $ret ) = 0 ; + local( $dir ) = $_[0] ; + if ( ! ( $dir =~ /^\// )) { + $dir = $_[1] . "/" . $dir ; + } + local( $comment ) = &CTNtoolFormatComment( $_[2] ) ; + # first we have to check out the parent directory + local( @alist ) = split( /\//, $dir ) ; + pop( @alist ) ; + local( $parent ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $dir . "' is '" . $parent . "'\n" ) ; + $ret = system( "neartool co -nc $parent\n" ) ; + if ( $ret == 0 ) { + # now make the dir + $ret = &CTURetCode( system( "neartool mkdir " . $comment . + " $dir\n" )) ; + } else { + $ret = 0 ; + } + &CTUDebug( "out of CTNtoolMkdir\n" ) ; + $ret ; +} + +# make a versioned element +# input is in: +# $_[0] = element to version +# $_[1] = curr dir +# $_[2] = possible comment +# $_[3] = possible eltype +# +# output: +# return success or failure +sub CTNtoolMkelem { + &CTUDebug( "in CTNtoolMkelem\n" ) ; + local( $ret ) = 0 ; + local( $elem ) = $_[0] ; + if ( ! ( $elem =~ /^\// )) { + $elem = $_[1] . "/" . $elem ; + } + local( $comment ) = &CTNtoolFormatComment( $_[2] ) ; + local( $eltype ) = $_[3] ; + if ( $eltype ne "" ) { + $eltype = "-eltype " . $eltype ; + } + local( $line ) = "neartool mkelem " . $comment . " " . $eltype . " " . + $elem . "\n" ; + &CTUDebug( $line ) ; + $ret = &CTURetCode( system( $line )) ; + &CTUDebug( "out of CTNtoolMkelem\n" ) ; + $ret ; +} + +# delta an element +# input is in: +# $_[0] = element to delta +# +# output: +# return success or failure +sub CTNtoolDelta { + &CTUDebug( "in CTNtoolDelta\n" ) ; + local( $ret ) = 0 ; + # as Dave points out, when working off-line, delta is the same as checkin + $ret = &CTURetCode( system( "neartool ci " . $_[0] )) ; + &CTUDebug( "out of CTNtoolDelta\n" ) ; + $ret ; +} + +# checkout an element +# input is in: +# $_[0] = element to checkout +# $_[1] = possible comment +# +# output: +# return success or failure +sub CTNtoolCheckout { + &CTUDebug( "in CTNtoolCheckout\n" ) ; + local( $ret ) = 0 ; + local( $comment ) = &CTNtoolFormatComment( $_[1] ) ; + if ( ! -d $_[0] ) { + $ret = &CTURetCode( system( "neartool co " . $comment . " " . + $_[0] )) ; + } else { + # neartool doesn't do anything about checking out directories + $ret = 1 ; + } + &CTUDebug( "out of CTNtoolCheckout\n" ) ; + $ret ; +} + +# checkin an element +# input is in: +# $_[0] = element to checkin +# $_[1] = possible comment +# +# output: +# return success or failure +sub CTNtoolCheckin { + &CTUDebug( "in CTNtoolCheckin\n" ) ; + local( $ret ) = 0 ; + local( $comment ) = &CTNtoolFormatComment( $_[1] ) ; + $ret = &CTURetCode( system( "neartool ci " . $comment . " " . $_[0] )) ; + &CTUDebug( "out of CTNtoolCheckin\n" ) ; + $ret ; +} + +# uncheckout an element +# input is in: +# $_[0] = element to uncheckout +# +# output: +# return success or failure +sub CTNtoolUncheckout { + &CTUDebug( "in CTNtoolUncheckout\n" ) ; + local( $ret ) = 0 ; + $ret = &CTURetCode( system( "neartool unco " . $_[0] )) ; + &CTUDebug( "out of CTNtoolUncheckout\n" ) ; + $ret ; +} + +# figure out what all I have checked out +# input is in: +# $_[0] = project +# $_[1] = flavor +# $_[2] = spec line +# +# output: +# return a \n serperated list of elements checked out +sub CTNtoolIHave { + &CTUDebug( "in CTNtoolIHave\n" ) ; + local( $ret ) = "" ; + local( $root ) = &CTProjRoot( $_[0] ) ; + local( *OUTPUT ) ; + open( OUTPUT, "neartool find " . $root . " |" ) ; + while ( ) { + $ret = $ret . $_ ; + } + close( OUTPUT ) ; + &CTUDebug( "out of CTNToolIHave\n" ) ; + $ret ; +} + +# remove a versioned element +# input is in: +# $_[0] = element to remove +# $_[1] = curr dir +# +# output: +# return success or failure +sub CTNtoolRmElem { + &CTUDebug( "in CTNtoolRmElem\n" ) ; + local( $ret ) = 0 ; + local( $elem ) = $_[0] ; + if ( ! ( $elem =~ /^\// )) { + $elem = $_[1] . "/" . $elem ; + } + # first we have to check out the parent directory + local( @alist ) = split( /\//, $elem ) ; + pop( @alist ) ; + local( $parent ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; + $ret = system( "neartool co -nc $parent\n" ) ; + if ( $ret == 0 ) { + # now nuke the element + $ret = &CTURetCode( system( "neartool rmname $elem\n" )) ; + } else { + $ret = 0 ; + } + &CTUDebug( "out of CTNtoolRmElem\n" ) ; + $ret ; +} + +# mv a versioned element from one name to another +# input is in: +# $_[0] = from element +# $_[1] = to element +# $_[2] = current directory +# +# output: +# return success or failure +sub CTCcaseMv { + &CTUDebug( "in CTNtoolMv\n" ) ; + local( $ret ) = 0 ; + local( $elem ) = $_[0] ; + if ( ! ( $elem =~ /^\// )) { + $elem = $_[2] . "/" . $elem ; + } + # first we have to check out the parent directory + local( @alist ) = split( /\//, $elem ) ; + pop( @alist ) ; + local( $parent ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ; + local( $elem2 ) = $_[1] ; + if ( ! ( $elem2 =~ /^\// )) { + $elem2 = $_[2] . "/" . $elem2 ; + } + @alist = split( /\//, $elem2 ) ; + pop( @alist ) ; + local( $parent2 ) = join( "/", @alist ) ; + &CTUDebug( "parent directory of '" . $elem2 . "' is '" . $parent2 . + "'\n" ) ; + $ret = system( "neartool co -nc $parent\n" ) ; + if ( $ret == 0 ) { + $ret = system( "neartool co -nc $parent2\n" ) ; + if ( $ret == 0 ) { + # now move the element + $ret = &CTURetCode( system( "neartool mv $elem $elem2\n" )) ; + } else { + $ret = 0 ; + } + } else { + $ret = 0 ; + } + &CTUDebug( "out of CTNtoolMv\n" ) ; + $ret ; +} + +# build a list of targets +# input is in: +# $_[0] = targets +# +# output: +# return success or failure +sub CTNtoolMake { + &CTUDebug( "in CTNtoolMake\n" ) ; + local( $ret ) = 0 ; + local( $line ) = "make " . $_[0] . "\n" ; + $ret = &CTURetCode( system( $line )) ; + &CTUDebug( "out of CTNtoolMake\n" ) ; + $ret ; +} + +1; diff --git a/dtool/src/attach/ctquery b/dtool/src/attach/ctquery new file mode 100755 index 0000000000..039fabaafc --- /dev/null +++ b/dtool/src/attach/ctquery @@ -0,0 +1,42 @@ +#!/usr/local/bin/perl + +# acceptable forms: +# ctquery - list all attached projects and flavors +# ctquery project - list the attached flavor of the named project +# ctquery - flavor - list all attached projects who are attached with a +# given flavor + +$projs = $ENV{"CTPROJS"} ; +@projlist = split( / +/, $projs ) ; + +if ( $#ARGV == -1 ) { + # list all projects and flavors + print "Currently attached projects (and flavors):\n" ; + foreach $pair ( @projlist ) { + @pairlist = split( /:/, $pair ) ; + ( $pairtmp = $pairlist[0] ) =~ tr/A-Z/a-z/ ; + print " $pairtmp ($pairlist[1])\n" ; + } +} elsif (( $#ARGV == 0 ) && !($ARGV[0] =~ /^\-/)) { + # list the attached flavor of the named project + foreach $pair ( @projlist ) { + @pairlist = split( /:/, $pair ) ; + ( $pairtmp = $pairlist[0] ) =~ tr/A-Z/a-z/ ; + if ( $pairtmp eq $ARGV[0] ) { + print "$pairlist[1]\n" ; + } + } +} elsif (( $#ARGV == 1 ) && ( $ARGV[0] eq "-" )){ + # list all attached projects who are attached with a given flavor + foreach $pair ( @projlist ) { + @pairlist = split( /:/, $pair ) ; + if ( $pairlist[1] eq $ARGV[1] ) { + $pairlist[0] =~ tr/A-Z/a-z/ ; + print "$pairlist[0]\n" ; + } + } +} else { + print "Usage: ctquery [project] -or-\n" ; + print " ctquery - flavor\n" ; + exit ; +} diff --git a/dtool/src/attach/ctquery.pl b/dtool/src/attach/ctquery.pl new file mode 100644 index 0000000000..d93d42e9c7 --- /dev/null +++ b/dtool/src/attach/ctquery.pl @@ -0,0 +1,37 @@ +# return the attached flavor of given project (or empty string) +sub CTQueryProj { + local( $projs ) = $ENV{"CTPROJS"} ; + local( @projlist ) ; + @projlist = split( / +/, $projs ) ; + local( $pair ) ; + local( @pairlist ) ; + local( $ret ) = "" ; + foreach $pair ( @projlist ) { + @pairlist = split( /:/, $pair ) ; + $pairlist[0] =~ tr/A-Z/a-z/ ; + if ( $pairlist[0] eq $_[0] ) { + $ret = $pairlist[1] ; + } + } + $ret ; +} + +# return all projects attached with a given flavor +sub CTQueryFlav { + local( $projs ) = $ENV{"CTPROJS"} ; + local( @projlist ) ; + @projlist = split( / +/, $projs ) ; + local( $pair ) ; + local( @pairlist ) ; + local( $ret ) = "" ; + foreach $pair ( @projlist ) { + @pairlist = split( /:/, $pair ) ; + if ( $pairlist[1] eq $_[0] ) { + $pairlist[0] =~ tr/A-Z/a-z/ ; + $ret = $ret . " $pairlist[0]" ; + } + } + $ret ; +} + +1; diff --git a/dtool/src/attach/ctrm b/dtool/src/attach/ctrm new file mode 100644 index 0000000000..954c3c4c83 --- /dev/null +++ b/dtool/src/attach/ctrm @@ -0,0 +1,31 @@ +#!/usr/local/bin/perl + +if ( $#ARGV < 0 ) { + exit print "Usage: ctrmelem element-name [...]\n" ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured for CTtools" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +foreach $item ( @ARGV ) { + if ( -e $item ) { + if ( ! &CTCMRmElem( $item, $projname, $spec ) ) { + print STDERR "Could not rmname '$item'\n" ; + } + } else { + print STDERR "No such file '$item'.\n" ; + } +} diff --git a/dtool/src/attach/ctsanity b/dtool/src/attach/ctsanity new file mode 100644 index 0000000000..1cf01c93ef --- /dev/null +++ b/dtool/src/attach/ctsanity @@ -0,0 +1,43 @@ +#!/usr/local/bin/perl + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured to run CTtools" ; +} + +sub CTSanityUsage { + print STDERR "Usage: ctsanity [-v]\n" ; + print STDERR "Options:\n" ; + print STDERR " -v : sanity check the .vspec files \n" ; + exit ; +} + +if ( $#ARGV == -1 ) { + &CTSanityUsage ; +} + +$check_vspecs = 0 ; + +foreach $item ( @ARGV ) { + if ( $item eq "-v" ) { + $check_vspecs = 1 ; + } else { + print STDERR "unknown option '" . $item . "'\n" ; + $CTSanityUsage ; + } +} + +require "$tool/inc/ctvspec.pl" ; + +if ( $check_vspecs ) { + local( $projs ) = &CTListAllProjects ; + local( @projlist ) = split( / +/, $projs ) ; + local( $item ) ; + foreach $item ( @projlist ) { + print STDERR "checking " . $item . ".vspec:\n" ; + local( $ctsavedebug ) = $ctdebug ; + $ctdebug = 1 ; + &CTReadVSpec( $item ) ; + $ctdebug = $ctsavedebug ; + } +} diff --git a/dtool/src/attach/cttimewarp b/dtool/src/attach/cttimewarp new file mode 100755 index 0000000000..0826a31a49 --- /dev/null +++ b/dtool/src/attach/cttimewarp @@ -0,0 +1,43 @@ +#!/usr/local/bin/perl + +if ($#ARGV < 0) { + exit print "Usage: cttimewarp [-clear] label [time]\n" ; +} + +@arglist = @ARGV ; + +$clear = 0 ; + +if ( $arglist[0] =~ /^-c/ ) { + $clear = 1 ; + shift( @arglist ) ; +} + +if ( @arglist == () ) { + if ( $clear ) { + exit print "Usage: cttimewarp -clear label\n" ; + } else { + exit print "Usage: cttimewarp label time\n" ; + } +} + +$label = $arglist[0] ; +shift( @arglist ) ; + +if (( ! $clear ) && ( @arglist == () )) { + exit print "Usage: cttimewarp label time\n" ; +} + +$time = $arglist[0] ; + +if ( $clear ) { + $cmd = "cleartool find . -version \"lbtype(" . $label . + ")\" -exec 'cleartool rmlabel -c \"untimewarping\" " . $label . + ' $CLEARCASE_XPN' . "'\n" ; + system( $cmd ) ; +} else { + $cmd = "cleartool mklabel -replace -recurse -c \"rolling time back to " . + $time . "\" -version /main/'{\!created_since(" . $time . ")}' " . + $label . " .\n" ; + system( $cmd ) ; +} diff --git a/dtool/src/attach/ctunattach.drv b/dtool/src/attach/ctunattach.drv new file mode 100644 index 0000000000..3dc8ece04c --- /dev/null +++ b/dtool/src/attach/ctunattach.drv @@ -0,0 +1,48 @@ +#!/usr/local/bin/perl + +# acceptable forms: +# ctunattach project - attach to the personal flavor of the project + +sub CTUnattachUsage { + print STDERR "Usage: ctattach project(s)\n" ; + &CTAttachWriteNullScript( $tmpname ) ; + print $tmpname . "\n" ; + exit; +} + +$tool = $ENV{"DTOOL"} ; + +require "$tool/inc/ctattch.pl" ; +require "$tool/inc/ctunattach.pl" ; +require "$tool/inc/ctquery.pl" ; + +$tmpname = "/tmp/script.$$" ; + +if ( $#ARGV == -1 ) { + &CTUnattachUsage ; +} + +foreach $proj ( @ARGV ) { + &CTUDebug( "project is '$proj'\n" ) ; + + $curflav = &CTQueryProj( $proj ) ; + if ( $curflav ne "" ) { + $envsep{"PATH"} = ":" ; + $envsep{"LD_LIBRARY_PATH"} = ":" ; + $envsep{"PFPATH"} = ":" ; + $envsep{"SSPATH"} = ":" ; + $envsep{"STKPATH"} = ":" ; + $envsep{"DC_PATH"} = ":" ; + $spec = &CTUnattachCompute( $proj, $curflav ) ; + if ( $spec eq "" ) { + &CTAttachWriteNullScript( $tmpname ) ; + print $tmpname . "\n" ; + } else { + &CTUnattachWriteScript( $tmpname ) ; + print $tmpname . "\n" ; + } + } else { + &CTAttachWriteNullScript( $tmpname ) ; + print $tmpname . "\n" ; + } +} diff --git a/dtool/src/attach/ctunattach.pl b/dtool/src/attach/ctunattach.pl new file mode 100644 index 0000000000..fb035f8d0a --- /dev/null +++ b/dtool/src/attach/ctunattach.pl @@ -0,0 +1,250 @@ +require "$tool/inc/ctquery.pl" ; + +$shell_type = "csh" ; +if ( $ENV{"SHELL_TYPE"} ne "" ) { + if ( $ENV{"SHELL_TYPE"} eq "sh" ) { + $shell_type = "sh" ; + } +} + +# remove a value from a variable. If it is the only thing remaining in the +# variable, add it to the unset list. +# input is in: +# $_[0] = variable +# $_[1] = value +# +# output is in: +# %newenv = an image of how we want the environment to be +# @unset = a list of variables to unset +sub CTUnattachMod { + &CTUDebug( "in CTUnattachMod\n" ) ; + local( $done ) = 0 ; + # if we didn't get any data, nothing really to do + if ( $_[0] eq "" ) { $done = 1 ; } + if ( $_[1] eq "" ) { $done = 1 ; } + # if the variable is already set to be unset, nothing really to do + if ( join( " ", @unset ) =~ /$_[0]/ ) { $done = 1 ; } + # if the variable isn't in newenv, move it there, if it's empty mark it + # for unsetting + if ( $newenv{$_[0]} eq "" ) { + $newenv{$_[0]} = &CTSpoolEnv( $_[0] ) ; + if ( $newenv{$_[0]} eq "" ) { + push( @unset, $_[0] ) ; + delete $newenv{$_[0]} ; + $done = 1 ; + } + } + # if the value does not appear in the variable, nothing really to do + if ( ! ( $newenv{$_[0]} =~ /$_[1]/ ) ) { $done = 1 ; } + # now down to the real work + if ( ! $done ) { + # if the variable is exactly the value, mark it for unsetting + if ( $newenv{$_[0]} eq $_[1] ) { + push( @unset, $_[0] ) ; + delete $newenv{$_[0]} ; + } elsif ( $newenv{$_[0]} =~ / $_[1]/ ) { + local( $tmp ) = $newenv{$_[0]} ; + $tmp =~ s/ $_[1]// ; + $newenv{$_[0]} = $tmp ; + } elsif ( $newenv{$_[0]} =~ /$_[1] / ) { + local( $tmp ) = $newenv{$_[0]} ; + $tmp =~ s/$_[1] // ; + $newenv{$_[0]} = $tmp ; + } else { + print STDERR "ERROR: variable '" . $_[0] . "' contains '" . + $_[1] . "' (in '" . $newenv{$_[0]} . + "'), but I am too stupid to figure out how to remove it.\n" ; + } + } +} + +# given the project and flavor, build the lists of variables to set/modify +# input is in: +# $_[0] = project +# $_[1] = flavor +# +# output is in: +# return value is config line +# %newenv = an image of what we want the environment to look like +# @unset = list of variables to be unset +# %envsep = seperator +# %envcmd = set or setenv +# %envpostpend = flag that variable should be postpended +sub CTUnattachCompute { + &CTUDebug( "in CTUnattachCompute\n" ) ; + local( $flav ) = $_[1] ; + local( $spec ) = &CTResolveSpec( $_[0], $flav ) ; + local( $root ) = &CTComputeRoot( $_[0], $flav, $spec ) ; + + if ( $spec ne "" ) { + local( $proj ) = $_[0] ; + $proj =~ tr/a-z/A-Z/ ; + local( $item ) ; + + # since we don't have to worry about sub-attaches, it doesn't matter + # if we scan the .init file first or not. So we won't. + &CTUDebug( "extending paths\n" ) ; + + $item = $root . "/bin" ; + &CTUnattachMod( "PATH", $item ) ; + $item = $root . "/lib" ; + if ( $ENV{"PENV"} eq "WIN32" ) { + &CTUnattachMod( "PATH", $item ) ; + } + &CTUnattachMod( "LD_LIBRARY_PATH", $item ) ; + $item = $root . "/src/all" ; + &CTUnattachMod( "CDPATH", $item ) ; + $item = $root . "/inc" ; + &CTUnattachMod( "CT_INCLUDE_PATH", $item ) ; + $item = $root . "/etc" ; + &CTUnattachMod( "ETC_PATH", $item ) ; + $item = $proj . ":" . $flav ; + &CTUnattachMod( "CTPROJS", $item ) ; + push( @unset, $proj ) ; + + if ( -e "$root/etc/$_[0].init" ) { + &CTUDebug( "scanning $_[0].init file\n" ) ; + local( @linesplit ) ; + local( $linetmp ) ; + local( $loop ); + local( *INITFILE ) ; + if ( -x "$root/etc/$_[0].init" ) { + open( INITFILE, "$root/etc/$_[0].init $_[0] $_[1] $root |" ) ; + } else { + open( INITFILE, "< $root/etc/$_[0].init" ) ; + } + while ( ) { + s/\n$// ; + @linesplit = split( /\#/ ) ; + $_ = $linesplit[0] ; + if ( $_ =~ /^MODABS/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + foreach $loop ( @linesplit ) { + &CTUnattachMod( $linetmp, $loop ) ; + } + } elsif ( $_ =~ /^MODREL/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + shift( @linesplit ) ; + shift( @linesplit ) ; + foreach $loop ( @linesplit ) { + &CTUnattachMod( $linetmp, $root . "/" . $loop ) ; + } + } elsif ( $_ =~ /^SETABS/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + push( @unset, $linetmp ) ; + } elsif ( $_ =~ /^SETREL/ ) { + @linesplit = split ; + $linetmp = $linesplit[1] ; + push( @unset, $linetmp ) ; + } elsif ( $_ =~ /^SEP/ ) { + @linesplit = split ; + $envsep{$linesplit[1]} = $linesplit[2] ; + } elsif ( $_ =~ /^CMD/ ) { + @linesplit = split ; + $envcmd{$linesplit[1]} = $linesplit[2] ; + } elsif ( $_ =~ /^DOCSH/ ) { + &CTUDebug( "ignoring DO command in .init file\n" ) ; + } elsif ( $_ =~ /^DOSH/ ) { + &CTUDebug( "ignoring DO command in .init file\n" ) ; + } elsif ( $_ =~ /^DO/ ) { + &CTUDebug( "ignoring DO command in .init file\n" ) ; + } elsif ( $_ =~ /^POSTPEND/ ) { + @linesplit = split ; + $envpospend{$linesplit[1]} = 1 ; + } elsif ( $_ =~ /^ATTACH/ ) { + &CTUDebug( "ignoring ATTACH command in .init file\n" ) ; + } else { + print STDERR "Unknown .init directive '$_'\n" ; + } + } + close( INITFILE ) ; + } + } + &CTUDebug( "out of CTUnattachCompute\n" ) ; + $spec ; +} + +# write a script to setup the environment +# Input is: +# $_[0] = filename +sub CTUnattachWriteScript { + &CTUDebug( "in CTAttachWriteScript\n" ) ; + local( *OUTFILE ) ; + open( OUTFILE, ">$_[0]" ) ; + print OUTFILE "#!/bin/" . $shell_type . " -f\n" ; + local( $item ) ; + local( $unsetcdpath ) = 0 ; + local( $modcdpath ) = 0 ; + + foreach $item ( @unset ) { + if ( $item eq "CDPATH" ) { $unsetcdpath = 1 ; } + + if ( $shell_type eq "sh" ) { + print OUTFILE "$item=\n" ; + if ( $envcmd{$item} ne "set" ) { + print OUTFILE "export $item\n" ; + } + } else { + if ( $envcmd{$item} ne "" ) { + print OUTFILE "un" . $envcmd{$item} . " $item\n" ; + } else { + print OUTFILE "unsetenv $item\n" ; + } + } + } + foreach $item ( keys %newenv ) { + if ( $item eq "CDPATH" ) { $modcdpath = 1 ; } + + local( $sep ) = " " ; + if ( $envsep{$item} ne "" ) { + $sep = $envsep{$item} ; + } + local( @splitlist ) = split( / +/, $newenv{$item} ) ; + local( $outval ) = join( $sep, @splitlist ) ; + + if ( $shell_type eq "sh" ) { + print OUTFILE "$item=\"" . $outval . "\"\n" ; + if ( $envcmd{$item} ne "set" ) { + print OUTFILE "export $item\n" ; + } + } else { + if ( $envcmd{$item} ne "" ) { + PRINT OUTFILE $envcmd{$item} . " $item " ; + if ( $envcmd{$item} eq "set" ) { + print OUTFILE " = ( " ; + } + print OUTFILE $outval ; + if ( $envcmd{$item} eq "set" ) { + print OUTFILE ")" ; + } + print OUTFILE "\n" ; + } else { + print OUTFILE "setenv $item \"$outval\"\n" ; + } + } + } + if ( $unsetcdpath ) { + if ( $shell_type ne "sh" ) { + print OUTFILE "unset cdpath\n" ; + } + } elsif ( $modcdpath ) { + if ( $shell_type ne "sh" ) { + print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ; + } + } + + if (! $ctdebug) { + print OUTFILE "rm -f $_[0]\n" ; + } else { + print STDERR "no self-destruct script '" . $_[0] . "'\n" ; + } + close( OUTFILE ) ; + &CTUDebug( "out of CTUnattachWriteScript\n" ) ; +} + +1; diff --git a/dtool/src/attach/ctunco b/dtool/src/attach/ctunco new file mode 100644 index 0000000000..479529933a --- /dev/null +++ b/dtool/src/attach/ctunco @@ -0,0 +1,31 @@ +#!/usr/local/bin/perl + +if ( $#ARGV < 0 ) { + exit print "Usage ctunco element-name [...]\n" ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "not configured for using CTtools" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctcm.pl" ; + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +foreach $item ( @ARGV ) { + if ( -e $item ) { + if ( ! &CTCMUncheckout( $item, $projname, $spec ) ) { + print STDERR "Could not uncheckout '$item'\n" ; + } + } else { + print STDERR "No such file '$item'.\n" ; + } +} diff --git a/dtool/src/attach/ctvspec.pl b/dtool/src/attach/ctvspec.pl new file mode 100644 index 0000000000..4708f19e7f --- /dev/null +++ b/dtool/src/attach/ctvspec.pl @@ -0,0 +1,359 @@ +require "$tool/inc/ctutils.pl" ; + +# read a .vspec file into a map +# $_[0] = project +# on exit $ctvspecs{} will contain the data +# +# vspec format: +# tag:type:other data +# +# type: ref, root, vroot, croot +# other data: +# ref: name=_____ - required, take to refference +# root: path=_____ - required, path of tree root +# vroot: name=_____ - optional, name of view to use (if not tag) +# croot: path=_____ - required, local path of tree root +# server=_____ - required, CVS server string, ',' for ':' + +sub CTReadVSpec { + &CTUDebug( "reading vspec file for project " . $_[0] . "\n" ) ; + local( $ret ) = "" ; + local( $thisproj ) = $_[0] ; + if ( -e "$ctvspec_path/$thisproj.vspec" ) { + %ctvspecs = () ; + local( *SPECFILE ) ; + open( SPECFILE, "<$ctvspec_path/$thisproj.vspec" ) ; + local( @partlist ) ; + while ( $_ = ) { + s/\n$// ; + @partlist = split( /\#/ ) ; + $_ = $partlist[0] ; + if ( $_ ne "" ) { + @partlist = split( /:/ ); + local( $tag ) = $partlist[0] ; + shift( @partlist ) ; + local( $spec ) = join( ":", @partlist ) ; + if ( &CTValidateSpec( $spec ) ) { + $ctvspecs{$tag} = $spec ; + if ( $ctdebug ) { + print STDERR "tag(" . $tag . ") = " . $spec . "\n" ; + } + } + } + } + close( SPECFILE ) ; + $ctvspec_read = $_[0] ; + } else { + print STDERR "CTReadVSpec: cannot locate '$ctvspec_path/$thisproj.vspec'\n" ; + } +} + +# given a spec line return it's type +# $_[0] = spec line + +sub CTSpecType { + local( @speclist ) = split( /:/, $_[0] ) ; + $speclist[0] ; +} + +# given a spec line return it's options if any +# $_[0] = spec line + +sub CTSpecOptions { + local( @speclist ) = split( /:/, $_[0] ) ; + shift( @speclist ) ; + join( ":", @speclist ) ; +} + +# given the options part of a spec line, find a given option +# $_[0] = options line +# $_[1] = desired option + +sub CTSpecFindOption { + local( $ret ) = "" ; + local( @options ) = split( /:/, $_[0] ) ; + local( $item ) ; + local( @itemlist ) ; + foreach $item ( @options ) { + @itemlist = split( /=/, $item ) ; + if ( $itemlist[0] eq $_[1] ) { + $ret = $itemlist[1] ; + } + } + $ret ; +} + +# resolve a final spec line for a given flavor +# $_[0] = project +# $_[1] = flavor + +sub CTResolveSpec { + &CTUDebug( "in CTResolveSpec\n" ) ; + local( $proj ) = $_[0] ; + $proj =~ tr/A-Z/a-z/ ; + if ( $ctvspec_read ne $proj ) { + &CTReadVSpec( $proj ) ; + } + local( $spec ) = $ctvspecs{$_[1]} ; + local( $ret ) = "" ; + if ( $spec ne "" ) { + local( $type ) = &CTSpecType( $spec ) ; + local( @speclist ) = split( /:/, &CTSpecOptions( $spec ) ) ; + if ( $type eq "ref" ) { + local( @optionlist ) = split( /=/, $speclist[0] ) ; + if ( $optionlist[0] ne "name" ) { + print STDERR "bad data attached to flavor " . $_[1] . + " of project " . $proj . "\n" ; + } else { + local( $tmp ) = &CTUShellEval( $optionlist[1] ) ; + if ( $ctdebug ) { + print STDERR "resolved a 'ref' to " . $tmp . + ", recuring\n" ; + } + $ret = &CTResolveSpec( $proj, $tmp ) ; + } + } else { + $ret = $spec ; + } + } + if ( $ret eq "" ) { + print STDERR "unknown flavor " . $_[1] . " of project " . $proj . + "\n" ; + } + &CTUDebug( "out of CTResolveSpec\n" ) ; + $ret ; +} + +# resolve the final name for a given flavor +# $_[0] = project +# $_[1] = flavor + +sub CTResolveSpecName { + &CTUDebug( "in CTResolveSpecName\n" ) ; + local( $proj ) = $_[0] ; + $proj =~ tr/A-Z/a-z/ ; + if ( $ctvspec_read ne $proj ) { + &CTReadVSpec( $proj ) ; + } + local( $spec ) = $ctvspecs{$_[1]} ; + local( $ret ) = $_[1] ; + if ( $spec ne "" ) { + local( $type ) = &CTSpecType( $spec ) ; + local( @speclist ) = split( /:/, &CTSpecOptions( $spec ) ) ; + if ( $type eq "ref" ) { + local( @optionlist ) = split( /=/, $speclist[0] ) ; + if ( $optionlist[0] ne "name" ) { + print STDERR "bad data attached to flavor " . $_[1] . + " of project " . $proj . "\n" ; + } else { + local( $tmp ) = &CTUShellEval( $optionlist[1] ) ; + if ( $ctdebug ) { + print STDERR "resolved a 'ref' to " . $tmp . + ", recuring\n" ; + } + $ret = &CTResolveSpecName( $proj, $tmp ) ; + } + } + } + if ( $ret eq "" ) { + print STDERR "unknown flavor " . $_[1] . " of project " . $proj . + "\n" ; + } + &CTUDebug( "out of CTResolveSpecName\n" ) ; + $ret ; +} + +# validate a spec line +# $_[0] = spec line + +sub CTValidateSpec { + local( $ret ) = 0 ; + local( $type ) = &CTSpecType( $_[0] ) ; + local( @speclist ) = split( /:/, &CTSpecOptions( $_[0] ) ) ; + local( $have_error ) = 0 ; + local( $item ) ; + local( @itemlist ) ; + if ( $type eq "ref" ) { + local( $have_name ) = 0 ; + foreach $item ( @speclist ) { + @itemlist = split( /=/, $item ) ; + if ( $itemlist[0] eq "name" ) { + if ( $have_name ) { + $have_error = 1; + &CTUDebug( "multiple name options on 'ref'\n" ) ; + } + $have_name = 1; + } else { + &CTUDebug( "invalid option on 'ref' = " . $item . "\n" ) ; + $have_error = 1 ; + } + } + if ( ! $have_error ) { + if ( $have_name ) { + $ret = 1 ; + } + } + } elsif ( $type eq "root" ) { + local( $have_path ) = 0 ; + foreach $item ( @speclist ) { + @itemlist = split( /=/, $item ) ; + if ( $itemlist[0] eq "path" ) { + if ( $have_path ) { + $have_error = 1 ; + &CTUDebug( "multiple path options on 'root'\n" ) ; + } + $have_path = 1 ; + } else { + &CTUDebug( "invalid option on 'root' = " . $item . "\n" ) ; + $have_error = 1 ; + } + } + if ( ! $have_error ) { + if ( $have_path ) { + $ret = 1 ; + } + } + } elsif ( $type eq "vroot" ) { + local( $have_name ) = 0 ; + foreach $item ( @speclist ) { + @itemlist = split( /=/, $item ) ; + if ( $itemlist[0] eq "name" ) { + if ( $have_name ) { + $have_error = 1 ; + &CTUDebug( "multiple name options on 'vroot'\n" ) ; + } + $have_name = 1 ; + } else { + &CTUDebug( "invalid option on 'vroot' = " . $item . "\n" ) ; + $have_error = 1 ; + } + } + if ( ! $have_error ) { + $ret = 1 ; + } + } elsif ( $type eq "croot" ) { + local( $have_path ) = 0 ; + local( $have_server ) = 0 ; + foreach $item ( @speclist ) { + @itemlist = split( /=/, $item ) ; + if ( $itemlist[0] eq "path" ) { + if ( $have_path ) { + $have_error = 1 ; + &CTUDebug( "multiple path options on 'croot'\n" ) ; + } + $have_path = 1 ; + } elsif ( $itemlist[0] eq "server" ) { + if ( $have_server ) { + $have_error = 1 ; + &CTUDebug( "multiple server options on 'croot'\n" ) ; + } + $have_server = 1 ; + } else { + &CTUDebug( "invalid option on 'croot' = " . $item . "\n" ) ; + $have_error = 1 ; + } + } + if ( ! $have_error ) { + if ( $have_path && $have_server ) { + $ret = 1 ; + } + } + } else { + &CTUDebug( "unknow spec type '" . $speclist[0] . "'\n" ) ; + } + $ret ; +} + +# get a list of all projects + +sub CTListAllProjects { + &CTUDebug( "in CTListAllProjects\n" ) ; + local( $ret ) = "" ; + local( $done ) = 0 ; + local( *DIRFILES ) ; + open( DIRFILES, "(cd $ctvspec_path ; /bin/ls -1 *.vspec ; echo blahblah) |" ) ; + while ( ! $done ) { + $_ = ; + s/\n$// ; + if ( $_ eq "blahblah" ) { + $done = 1 ; + } else { + s/.vspec$// ; + if ( $_ ne "" ) { + if ( $ret eq "" ) { + $ret = $_ ; + } else { + $ret = $ret . " " . $_ ; + } + } + } + } + close( DIRFILES ) ; + &CTUDebug( "final list of projects '" . $ret . "'\n" . + "out of CTListAllProjects\n" ) ; + $ret ; +} + +# list all flavors of a project +# $_[0] = project + +sub CTListAllFlavors { + &CTUDebug( "in CTListAllFlavors\n" ) ; + local( $proj ) = $_[0] ; + $proj =~ tr/A-Z/a-z/ ; + if ( $ctvspec_read ne $proj ) { + &CTReadVSpec( $proj ) ; + } + local( $ret ) = ""; + local( $item ) ; + foreach $item ( keys %ctvspecs ) { + if ( $ret eq "" ) { + $ret = $item ; + } else { + $ret = $ret . " " . $item ; + } + } + &CTUDebug( "out of CTListAllFlavors\n" ) ; + $ret ; +} + +# given a project and a spec, determine the local root of the project +# $_[0] = project +# $_[1] = flavor +# $_[2] = spec line + +sub CTComputeRoot { + &CTUDebug( "in CTComputeRoot\n" ) ; + local( $proj ) = $_[0] ; + $proj =~ tr/A-Z/a-z/ ; + if ( $ctvspec_read ne $proj ) { + &CTReadVSpec( $proj ) ; + } + local( $ret ) = "" ; + local( $type ) = &CTSpecType( $_[2] ) ; + local( $options ) = &CTSpecOptions( $_[2] ) ; + local( $vname ) = &CTResolveSpecName( $proj, $_[1] ) ; + &CTUDebug( "type = '" . $type . "' with options '" . $options . "'\n" ) ; + if ( $type eq "root" ) { + $ret = &CTSpecFindOption( $options, "path" ) ; + } elsif ( $type eq "vroot" ) { + local( $name ) = &CTSpecFindOption( $options, "name" ) ; + if ( $name ne "" ) { + $ret = "/view/$name/vobs/$proj" ; + } else { + $ret = "/view/$vname/vobs/$proj" ; + } + } elsif ( $type eq "croot" ) { + $ret = &CTSpecFindOption( $options, "path" ) ; + } elsif ( $ctdebug) { + print STDERR "unknown flavor type '" . $type . "'\n" ; + } + &CTUDebug( "returning '" . $ret . "'\n" ) ; + &CTUDebug( "out of CTComputeRoot\n" ) ; + $ret ; +} + +%ctvspecs = () ; +$ctvspec_read = "" ; + +1; diff --git a/dtool/src/attach/dtool.alias b/dtool/src/attach/dtool.alias new file mode 100644 index 0000000000..441f757e6f --- /dev/null +++ b/dtool/src/attach/dtool.alias @@ -0,0 +1,36 @@ +#!/bin/csh -f + +setenv OS `uname` +setenv USER `whoami` + +if ( -e $DTOOL/bin/neartool ) setenv HAVE_NEARTOOL "yes" +if ( ! $?HAVE_NEARTOOL ) setenv HAVE_NEARTOOL "no" +if ( ! $?HAVE_ATRIA ) setenv HAVE_ATRIA "no" + +set host=$HOST +if ( $TERM == "iris-ansi" || $TERM == "iris-ansi-net" ) then + alias ctshowprojs 'echo -n "\033P1.y"$USER"@"$host" -- "$CTPROJS"\033\\"; echo -n "\033P3.y"`echo $CTPROJS | cut -f1 -d:`\($host\)"\033\\"' +else if ( $TERM == "xterm" || $TERM == "color-xterm" || $TERM == "cygwin" ) then + alias ctshowprojs 'echo -n "\033]2;"$USER"@"$host" -- "$CTPROJS"\007"; echo -n "\033]1;"`echo $CTPROJS | cut -f1 -d:`\($host\)"\007"' +else + alias ctshowprojs 'echo $CTPROJS' +endif + +alias ctattach 'source `ctattach.drv \!*`; ctshowprojs' +alias cta 'ctattach' +alias cta-ship 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV ship ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' +alias cta-release 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV release ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' +alias cta-install 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV install ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' +alias ctunattach 'source `ctunattach.drv \!*`; ctshowprojs' +alias ctuna 'ctunattach' + +#Modifications to emacs alias by Jason +#To allow for NTEmacs to run like emacs on unix boxes +if (($OS == "CYGWIN_NT-4.0") || ($OS == "CYGWIN_NT-5.0" )) then + alias emacs 'emacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" -bg #002040 -fg #00C0FF -cr yellow -ms yellow -l `cygpath -w ~/.emacs` $CTEMACS_OPTS' +else + alias emacs 'emacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' +endif + +alias rlogin 'rlogin \!*; ctshowprojs' +alias telnet 'telnet \!*; ctshowprojs' diff --git a/dtool/src/attach/dtool.alias-sh b/dtool/src/attach/dtool.alias-sh new file mode 100644 index 0000000000..54dddf782b --- /dev/null +++ b/dtool/src/attach/dtool.alias-sh @@ -0,0 +1,46 @@ +#! /bin/sh + +if [ -e $DTOOL/bin/neartool ]; then HAVE_NEARTOOL="yes"; fi +if [ -z "$HAVE_NEARTOOL" ]; then HAVE_NEARTOOL="no"; fi +export HAVE_NEARTOOL + +if [ -z "$HAVE_ATRIA" ]; then HAVE_ATRIA="no"; fi +export HAVE_ATRIA + +if [ $HAVE_NEARTOOL = "yes" ]; then + alias ct='neartool' +else + alias ct='cleartool' +fi + +alias ctshowprojs='echo -n "\033P1.y"$USER"@"$HOST" -- "$CTPROJS"\033\\"; echo -n "\033P3.y"`echo $CTPROJS | cut -f1 -d:`\(`uname -n`\)"\033\\"' +alias ctattach='source `ctattach.drv $*`; ctshowprojs' +alias cta='ctattach' +alias cta-ship='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV ship ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' +alias cta-release='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV release ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' +alias cta-install='setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV install ; ctattach $* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE' +alias ctunattach='source `ctunattach.drv $*`; ctshowprojs' +alias ctuna='ctunattach' + +if [ "$PENV" = "WIN32_DREAMCAST" ]; then + alias ctci='neartool ci' + alias ctco='neartool co' + alias ctmake='gmake' + alias emacs='/emacs/bin/runemacs' + alias emacs='/emacs/bin/runemacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' + +elif [ "$HAVE_NEARTOOL" = "yes" ]; then + alias ctci='neartool ci' + alias ctco='neartool co' + alias ctmake='make' + alias emacs='emacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' + +elif [ "$HAVE_ATRIA" = "yes" ]; then + alias ctci='cleartool ci' + alias ctco='cleartool co' + alias ctmake 'clearmake -C gnu $* |& grep -v "^clearmake: Warning: Config"' + alias emacs='emacs -T "`logname`@`uname -n` $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`(`uname -n`)" $CTEMACS_OPTS' +fi + +alias rlogin='rlogin $*; ctshowprojs' +alias telnet='telnet $*; ctshowprojs' diff --git a/dtool/src/attach/dtool.cshrc b/dtool/src/attach/dtool.cshrc new file mode 100644 index 0000000000..96fb268fc3 --- /dev/null +++ b/dtool/src/attach/dtool.cshrc @@ -0,0 +1,85 @@ +#!/bin/csh -f + +setenv OS `uname` + +setenv LD_LIBRARY_PATH "." + +# Setup the initial path +if ( $OS == "Linux" ) then + set path = ( /var/local/bin ~/bin . /usr/sbin /sbin /usr/bin /bin \ + /usr/bin/X11 /usr/etc /usr/local/bin ) +else if ( $OS == "IRIX64" ) then + set path = ( /var/local/bin /usr/local/bin/ptools ~/bin /usr/local/prman/bin \ + . /usr/sbin /usr/bsd /sbin /usr/bin /bin /usr/bin/X11 /usr/etc \ + /usr/demos/bin /usr/local/bin ) + setenv LD_LIBRARY_PATH ".:/usr/local/lib" +else if (( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "WINNT" ) || ($OS == "CYGWIN_NT-5.0")) then + set path = ( /usr/local/bin /contrib/bin . /usr/lib $path ) + if ( $?LIB ) then + setenv LIB "$LIB;"`cygpath -w /usr/lib` + else + setenv LIB `cygpath -w /usr/lib` + endif +else if (( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" )) then + set path = ( /bin /usr/local/bin /contrib/bin /msvc98/Bin \ + /mscommon/MSDev98/Bin /mscommon/Tools . /usr/lib $path ) + setenv LIB `cygpath -w /msvc98/mfc/lib`\;`cygpath -w /msvc98/lib`\;`cygpath -w /usr/lib` + setenv INCLUDE `cygpath -w /msvc98/Include` +else + set path = ( /var/local/bin /usr/local/bin/ptools ~/bin /usr/local/prman/bin \ + . /usr/sbin /usr/bsd /sbin /usr/bin /bin /usr/bin/X11 /usr/etc \ + /usr/demos/bin /usr/local/bin ) +endif + +# Setup the initial manpath +if ( $OS == "Linux" ) then +setenv MANPATH "/usr/local/man:/usr/man/preformat:/usr/man:/usr/X11R6/man" +else if ( $OS == "IRIX64" ) then +setenv MANPATH "/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man" +else if (( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" ) || ( $OS == "WINNT" ) || ( $OS == "CYGWIN_NT-5.0")) then +setenv MANPATH "/usr/man:/contrib/man" +else +setenv MANPATH "/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man" +endif + +setenv CT_INCLUDE_PATH "." +set cdpath = ( . ) +setenv CDPATH "." +setenv DC_PATH "." +setenv SSPATH "." +setenv STKPATH "." + +if ( ! $?HAVE_ATRIA ) then + if ( -e /usr/atria ) then + /usr/atria/bin/cleartool mount -all >& /dev/null + if ( $status == 0 ) setenv HAVE_ATRIA "yes" + endif +endif + +if ( ! $?CTDEFAULT_FLAV ) setenv CTDEFAULT_FLAV "default" +if ( ! $?CTEMACS_OPTS ) setenv CTEMACS_OPTS "" + +if ( -e /usr/atria/bin ) set path = ( /usr/atria/bin $path ) +rehash + +if ( ! $?DTOOL ) setenv DTOOL /beta/player/bootstrap/dtool +if ( $#argv == 0 ) then + source `$DTOOL/bin/ctattach.drv dtool default` +else + source `$DTOOL/bin/ctattach.drv dtool $argv[1]` +endif + +if ( ! $?PENV ) then + if ( $OS == "Linux" ) then + setenv PENV "Linux" + else if ( $OS == "IRIX64" ) then + setenv PENV "SGI" + else if (( $OS == "CYGWIN_NT-4.0" ) || ( $OS == "CYGWIN_98-4.10" ) || ( $OS == "WIN95" ) || ( $OS == "CYGWIN_NT-5.0")) then + setenv PENV "WIN32" + else + setenv PENV "SGI" + endif +endif + +setenv CTEMACS_FOREHIGHLIGHT white +setenv CTEMACS_BACKHIGHLIGHT blue diff --git a/dtool/src/attach/dtool.emacs b/dtool/src/attach/dtool.emacs new file mode 100644 index 0000000000..9032651bfd --- /dev/null +++ b/dtool/src/attach/dtool.emacs @@ -0,0 +1,286 @@ +;; make the mouse pointer avoid the text point +(cond (window-system + (require 'avoid) + (mouse-avoidance-mode 'cat-and-mouse))) + +;; make sure we have the compile library available to us +(load-library "compile") +;; Comment given for last checkout command +(setq last-co-comment "") +;; Comment given for last checkin command +(setq last-ci-comment "") +;; Target given for the last local make +(setq last-lm-target "realinstall") +;; Target given for the last global make +(setq last-gm-target "install") +;; Host given for the last ctrelease +(setq last-rel-host "") +;; Host given for the last ctship +(setq last-ship-host "") + +;; check the environment +(setq ct-tool (getenv "DTOOL")) +(setq have-atria (let ((h-a (getenv "HAVE_ATRIA"))) + (if (string= h-a "yes") t '()))) +;; (setq have-neartool (let ((h-n (getenv "HAVE_NEARTOOL"))) +;; (if (string= h-n "yes") t '()))) +(setq is-cygwin (string= (getenv "OS") "CYGWIN_NT-4.0")) + +;; (setq ct-command (cond +;; (is-cygwin "bash /install/tool/bin/neartool") +;; (have-atria "cleartool") +;; (have-neartool "neartool") +;; t nil)) + +;; Load the Hightlight coloring scheme +(if is-cygwin +;; (let ((filename (concat (getenv "CYGWIN_ROOT") "install\\tool\\etc\\color.emacs"))) + (let ((filename (concat (getenv "CYGWIN_ROOT") ct-tool "\\etc\\color.emacs"))) + (if (file-readable-p filename) (load filename)))) + +;; Checkout element in the current buffer +(defun ct-checkout-curr (comment) + "Checkout version in current buffer with COMMENT." + (interactive (list (read-string "Comment: " last-co-comment))) + (setq last-co-comment comment) + (setq pname (file-name-nondirectory (buffer-file-name))) + (ct-shell-command-verbose + (concat "ctco -c " (ct-quote-string comment) " " pname)) + (ct-find-curr-file-again nil) +) + +;; Uncheckout element in the current buffer +(defun ct-uncheckout-curr () + "Uncheckout version in current buffer and remove private data." + (interactive) + (if (y-or-n-p "Ok to un-checkout? ") + (progn + (setq pname (file-name-nondirectory (buffer-file-name))) + (ct-shell-command-verbose (concat "ctunco " pname)) + (ct-find-curr-file-again t) + ) + (progn + (message "Uncheckout canceled.") + ) + ) +) + +;; Checkin element in the current buffer +(defun ct-checkin-curr () + "Checkin version in current buffer." + (interactive) + (setq pname (file-name-nondirectory (buffer-file-name))) + (setq option nil) + (while (not option) + (setq choice (read-string "Comment: s (same), n (new), l (list): " "s")) + (cond + ((equal choice "s") + (setq option "-nc")) + ((equal choice "n") + (setq comment (read-string "Comment: " last-ci-comment)) + (setq last-ci-comment comment) + (setq option (concat "-c " (ct-quote-string comment)))) + ((equal choice "l") + (ct-shell-command-verbose (concat "ctihave " pname))) + (t + (message (concat "Unrecognized choice: " choice ".")) + (sleep-for 2)))) + + (ct-shell-command-verbose (concat "ctci " option " " pname)) + (ct-find-curr-file-again t) +) + +;; Delta element in the current buffer +(defun ct-delta-curr () + "Delta element in current buffer." + (interactive) + (if (y-or-n-p "Ok to delta? ") + (progn + (setq pname (file-name-nondirectory (buffer-file-name))) + (ct-shell-command-verbose (concat "ctdelta " pname)) + (ct-find-curr-file-again t) + ) + (progn + (message "Delta canceled.") + ) + ) +) + +;; List element checkout data for the current buffer +(defun ct-lscheckout-curr () + "List checkout for the current buffer." + (interactive) + (setq pname (file-name-nondirectory (buffer-file-name))) + (ct-shell-command-verbose (concat "ctihave &")) +) + +;; List elements in the current directory that are checked out +(defun ct-lscheckout-curr-dir () + "List checkouts for the current directory." + (ct-shell-command-verbose (concat "ctihave &")) +) + +;; call clearmake in the local directory +(defun ct-local-make () + "Build TARGET from the current directory." + (interactive) + (setq target (read-string "Local build target: " last-lm-target)) + (setq last-lm-target target) + (if have-atria + (compile-internal + (concat "clearmake -C gnu " target + " |& grep -v \"clearmake: Warning: Config\"") + "No more errors.") + (compile-internal + (concat "make " target) "No more errors.")) +) + +;; call clearmake in the project root directory +(defun ct-global-make () + "Build TARGET from the project root." + (interactive) + (setq target (read-string "Global build target: " last-gm-target)) + (setq last-gm-target target) + (cond + (have-atria + (compile-internal + (concat "cd `ctproj -r` ; clearmake -C gnu " target + " |& grep -v \"clearmake: Warning: Config\"") + "No more errors.")) + (is-cygwin + (compile-internal + (concat "bash -f 'cd `ctproj -r` ; make " target "'") "No more errors.")) + (t + (compile-internal + (concat "cd `ctproj -r` ; make " target) "No more errors.")) + ) + ) + +;; Do an xdiff on the current buffer to see what is different about this +;; file from the previous version. +(defun ct-xdiff-curr () + "Show changes to element in current buffer from the previous version." + (interactive) + (setq pname (file-name-nondirectory (buffer-file-name))) + (if is-cygwin + (ct-shell-command-verbose (concat ct-command " xdiff -pre " pname)) + + ; The is a hack to deal with the fact that diff returns 1 if the + ; two files do not match. + (ct-shell-command-verbose (concat ct-command " xdiff -pre " pname ";:&"))) + ) + + +;; Make a new element for the current buffer +(defun ct-mk-elem () + (interactive) + (if (y-or-n-p (format "Make new element for %s? " + (file-name-nondirectory (buffer-name)))) + (progn + (write-file (buffer-file-name)) + (ct-shell-command-verbose + (concat "ctmkelem -eltype text_file -c '' " + (file-name-nondirectory (buffer-name)))) + ) + (progn + (message "Make element canceled.") + ) + ) + ) + +;; utility functions +(defun ct-shell-command-verbose (command) + "Execute COMMAND in shell with message." + (interactive "Shell command: \n") + (message (concat "Executing: " command " ...")) + (shell-command command) + (message "Done.") +) + +(defun ct-find-curr-file-again (read-only) + "Read in the currect file again, READONLY (t) or not (nil)." + (setq pname (buffer-file-name)) + (setq linenum (1+ (count-lines 1 (point)))) + (kill-buffer (buffer-name)) + (if read-only + (find-file-read-only pname) + (find-file pname)) + (goto-line linenum) +) + +(defun ct-quote-string (string) + "Enclose STRING in single or double quotes." + (setq has-double (string-match "\"" string)) + (setq has-single (string-match "'" string)) + (cond + ((or (and (not has-single) (not has-double)) + (and has-double (not has-single))) + (concat "'" string "'")) + ((and has-single (not has-double)) + (concat "\"" string "\"")) + (t + (message (concat "Can't quote string correctly: " string)) + (sleep-for 3) + (concat "\"" string "\""))) +) + +;; default key bindings +(global-set-key "\C-xco" 'ct-checkout-curr) +(global-set-key "\C-xcu" 'ct-uncheckout-curr) +(global-set-key "\C-xci" 'ct-checkin-curr) +(global-set-key "\C-xcd" 'ct-delta-curr) +(global-set-key "\C-xcl" 'ct-lscheckout-curr) +(global-set-key "\C-xcL" 'ct-lscheckout-curr-dir) +(global-set-key "\C-xcm" 'ct-local-make) +(global-set-key "\C-xcM" 'ct-global-make) +(global-set-key "\C-xcx" 'ct-xdiff-curr) +(global-set-key "\C-xce" 'ct-mk-elem) + +;; ok, lets make sure we load all other .emacs files we might need. This is +;; attach related code. + +(defun ct-load-project-emacs-file (proj-name) + (if (string= proj-name "DTOOL") nil + (let ((pre-name (getenv proj-name))) + (if pre-name + (let ((filename (concat pre-name "/etc/" + (downcase proj-name) ".emacs"))) + (if (file-readable-p filename) + (load filename)) + ))) + ) + ) + +(defun ct-break-space-colon-str (string) + (if (string= string "") + '() + (let ((substr-end (string-match ":" string 0))) + (cons (substring string 0 substr-end) + (let ((new-string-start (string-match " " string 0))) + (if (eq nil new-string-start) + '() + (ct-break-space-colon-str + (strip-spaces (substring string (match-end 0))))) + ))) + ) + ) + +(defun strip-spaces (string) + (if (= (string-to-char string) 32) + (if (= (length string) 1) "" + (strip-spaces (substring string 1))) + string) + ) + +(defun ct-load-project-emacs-files () + "Load project specific .emacs files" + (let ((ctprojs (getenv "CTPROJS"))) + (if ctprojs + (mapcar 'ct-load-project-emacs-file + (reverse (ct-break-space-colon-str ctprojs))) + )) + ) + +;; get all of the project specific .emacs files +(ct-load-project-emacs-files) + diff --git a/dtool/src/attach/dtool.init b/dtool/src/attach/dtool.init new file mode 100644 index 0000000000..fec335ebf6 --- /dev/null +++ b/dtool/src/attach/dtool.init @@ -0,0 +1,10 @@ +MODREL ETC_PATH etc +DOCSH source $DTOOL/etc/dtool.alias +DOCSH unsetenv LASTLOGIN +DOCSH setenv OS_VER `uname -r` +DOCSH ctshowprojs +DOSH source $DTOOL/etc/dtool.alias-sh +DOSH LASTLOGIN= +DOSH export LASTLOGIN +DOSH OS_VER=`uname -r` +DOSH export OS_VER diff --git a/dtool/src/attach/dtool.sh b/dtool/src/attach/dtool.sh new file mode 100755 index 0000000000..c4d5cf2a72 --- /dev/null +++ b/dtool/src/attach/dtool.sh @@ -0,0 +1,79 @@ +#! /bin/sh + +OS=`uname` +export OS + +# Setup the initial path +if [ $OS = "Linux" ]; then + PATH=/var/local/bin:~/bin:.:/usr/sbin:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/local/bin +elif [ $OS = "IRIX64" ]; then + PATH=/var/local/bin:/usr/local/bin/ptools:~/bin:/usr/local/prman/bin:.:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/demos/bin:/usr/local/bin +elif [ $OS = "CYGWIN_98-4.10" ]; then + PATH=/usr/local/bin:/CYGNUS/CYGWIN~1/H-I586~1/BIN:/WINDOWS:/WINDOWS:/WINDOWS/COMMAND:/DMI/BIN:/KATANA/UTL/DEV/MAKE:/KATANA/UTL/DEV/HITACHI +else + PATH=/var/local/bin:/usr/local/bin/ptools:~/bin:/usr/local/prman/bin:.:/usr/sbin:/usr/bsd:/sbin:/usr/bin:/bin:/usr/bin/X11:/usr/etc:/usr/demos/bin:/usr/local/bin +fi + +# Setup the initial manpath +if [ $OS = "Linux" ]; then + MANPATH=/usr/local/man:/usr/man/preformat:/usr/man:/usr/X11R6/man +elif [ $OS = "IRIX64" ]; then + MANPATH=/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man +elif [ $OS = "CYGWIN_98-4.10" ]; then + MANPATH=/usr/local/man +else + MANPATH=/usr/share/catman:/usr/catman:/usr/local/share/catman:/usr/local/share/man:/usr/local/man +fi +export MANPATH + +LD_LIBRARY_PATH="." +export LD_LIBRARY_PATH +CT_INCLUDE_PATH="." +export CT_INCLUDE_PATH +cdpath=. +CDPATH="." +export CDPATH +DC_PATH="." +export DC_PATH +SSPATH="." +export SSPATH +STKPATH="." +export STKPATH +SHELL_TYPE="sh" +export SHELL_TYPE + +if [ -e /usr/atria ]; then + if /usr/atria/bin/cleartool mount -all > /dev/null 2>&1; then + HAVE_ATRIA=yes + export HAVE_ATRIA + fi +fi + +if [ -z "$CTDEFAULT_FLAV" ]; then + CTDEFAULT_FLAV="default" + export CTDEFAULT_FLAV +fi + +if [ -z "$DTOOL" ]; then + DTOOL=/beta/player/bootstrap/tool + export DTOOL +fi + +if [ -z "$1" ]; then + source `$DTOOL/bin/ctattach.drv dtool default` +else + source `$DTOOL/bin/ctattach.drv dtool $1` +fi + +if [ -z "$PENV" ]; then + if [ $OS = "Linux" ]; then + PENV="Linux" + elif [ $OS = "IRIX64" ]; then + PENV="SGI" + elif [ $OS = "CYGWIN_98-4.10" ]; then + PENV="WIN32_DREAMCAST" + else + PENV="SGI" + fi +fi +export PENV diff --git a/dtool/src/attach/get-cttree b/dtool/src/attach/get-cttree new file mode 100755 index 0000000000..d0e68e4f3f --- /dev/null +++ b/dtool/src/attach/get-cttree @@ -0,0 +1,104 @@ +#! /bin/sh +# +# get-cttree.sh +# +# Usage: +# +# get-cttree.sh [opts] output-file.tgz +# +# This script must be executed from within a project tree. +# +# Options: +# +# None at present. +# +#ENDCOMMENT + +while getopts "h" flag; do + case $flag in + h) sed '/#ENDCOMMENT/,$d' <$0 >&2 + exit 1;; + \?) exit 1; + esac +done + +shift `expr $OPTIND - 1` +output=$1 +projroot=`ctproj -r` + +if [ -z "$projroot" ]; then + echo "" + echo "You must execute this script in a project tree." + echo "" + exit 1 +fi + +if [ -z "$output" ]; then + sed '/#ENDCOMMENT/,$d' <$0 >&2 + exit 1 +fi + + +# Perform some sanity checks on input parameters. + +if [ ! -d "$projroot" ]; then + echo "" + echo "$projroot is not a directory!" + echo "" + exit 1 +fi + +if [ `basename $output .tgz` = `basename $output` ]; then + echo "" + echo "$output should end in .tgz" + echo "" + exit 1 +fi + +if [ ! -d /usr/atria ]; then + echo "" + echo "This script is intended to be run on an actual ClearCase vobs." + echo "" + exit 1 +fi + +projname=`basename $projroot` +projtop=`dirname $projroot` + +if [ "$projname" = "tool" ]; then + echo "" + echo "This script should not be used on the tool tree." + echo "" + exit 1 +fi + +if [ -f "$output" ]; then + if rm -i $output; then + echo "" + else + echo "Not overwriting $output" + exit 1 + fi +elif [ -r "$output" -o -w "$output" ]; then + echo "Cannot overwrite $output" + exit 1 +else + echo "" +fi + +# Check to make sure the local machine doesn't have anything checked out. +cd $projroot +outfile=/tmp/gc.$username.$projname.out +cleartool lsco -s -me -recurse >$outfile +if [ -s $outfile ]; then + echo "" + echo "Cannot build tarball; files still checked out in vobs:" + sed 's/^/ /;s/\.ct0\.//' $outfile + rm -f $outfile + echo "" + exit 1 +fi +rm -f $outfile + +(cd $projtop; cleartool find $projname -nxn -print | grep -v '/lost+found' | cpio -H tar -v -o | gzip) >$output + diff --git a/dtool/src/attach/get-delta b/dtool/src/attach/get-delta new file mode 100755 index 0000000000..5ddb09a6a1 --- /dev/null +++ b/dtool/src/attach/get-delta @@ -0,0 +1,481 @@ +#! /usr/local/bin/bash +# +# get-delta.sh +# +# Usage: +# +# get-delta.sh [opts] output-file.sh [file ...] +# +# This script must be executed from within a project tree. It +# examines the set of files that have been checked out (via neartool) +# and modified, and generates a script file that can be used to apply +# the changes made back to the main ClearCase vobs. +# +# By default, it generates a script for all checked-out files. You +# can restrict its operation to certain files and/or directories by +# listing them on the command line. +# +# Options: +# +# -c collapse versioning information on local copy after completion. +# Use this option with caution, as it is irreversible (and +# noninterruptible). Once the versioning information has been +# collapsed, it will be impossible to regenerate a script +# representing the changes that have been made locally; you +# should only do this when you are sure that your changes have +# been successfully applied to the other end. +# +# On the other hand, if you forget to run get-delta with -c after +# you have successfully applied your changes, you may +# inadvertently attempt to apply them again if you subsequently +# try to apply more changes. +# +#ENDCOMMENT + +function usage { + sed '/#ENDCOMMENT/,$d' <$0 >&2 + exit 1 +} + +# +# list_comments ( dirname basename ) +# +# Writes to stdout any comments associated with checked-out versions of +# the indicated file, in order. +# +function list_comments { + local dirname=$1 + local basename=$2 + local filename=$dirname/$basename + local file comment version + + if [ -f $dirname/.ct0.$basename ]; then + # Now look for comments, in version-number order. + + # We use a series of ls commands so we don't try to sort the + # filenames between the one-, two-, and three-digit version + # numbers. + + for file in `(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null`; do + version=`echo $file | sed "s/^\.ct\([0-9]*\).*$/\1/"` + comment=$dirname/.ct${version}comment.$basename + if [ -f $comment ]; then + cat $comment + fi + done + fi +} + +# +# get_fullpath ( local_dir ) +# +# Sets $fullpath to the fully-qualified pathname associated with $local_dir. +# +function get_fullpath { + local local_dir=$1 + + if [ -z "$local_dir" ]; then + fullpath=`pwd` + else + if [ ! -d "$local_dir" ]; then + echo "Invalid directory: $local_dir" 1>&2 + exit 1 + fi + # If we use pwd instead of /bin/pwd, $PWD will be used, which will give + # the wrong answer + fullpath=`(cd $local_dir; /bin/pwd)` + fi +} + + +# +# get_rel_dir ( root_dir local_dir ) +# +# Sets $rel_dir to the string which represents $local_dir relative to +# $root_dir. This is a simple string-prefix operation, and could fail +# in some obscure cases. +# +function get_rel_dir { + get_fullpath $1 + local root_dir=$fullpath + + get_fullpath $2 + local local_dir=$fullpath + + # Now remove the initial prefix. + if [ "$root_dir" = "$local_dir" ]; then + rel_dir="." + else + rel_dir=`echo $local_dir | sed 's:^'$root_dir/'::'` + + if [ "$rel_dir" = "$local_dir" ]; then + echo "$local_dir is not a directory within $root_dir." 1>&2 + exit 1 + fi + fi +} + + +collapse= +while getopts "ch" flag; do + case $flag in + c) collapse=y;; + h) usage;; + \?) exit 1; + esac +done + +shift `expr $OPTIND - 1` +output=$1 +shift +projroot=`ctproj -r` + +if [ -z "$projroot" ]; then + echo "You must execute this script in a project tree." + exit 1 +fi + +if [ -z "$output" ]; then + usage +fi + + +# Perform some sanity checks on input parameters. + +if [ ! -d "$projroot" ]; then + echo "$projroot is not a directory!" + exit 1 +fi + +if [ `basename $output .sh` = `basename $output` ]; then + echo "$output should end in .sh" + exit 1 +fi + +if [ -f "$output" ]; then + rm -i $output + if [ -f "$output" ]; then + echo "Not overwriting $output" + exit 1 + fi +elif [ -e "$output" ]; then + echo "Cannot overwrite $output" + exit 1 +fi + +echo "" + +# Temporary files we'll build up as we process the files. + +base=`basename $output` +temp_ct0=/tmp/gd.ct0.$base +temp_checkout=/tmp/gd.checkout.$base +temp_dirs=/tmp/gd.dirs.$base +temp_files=/tmp/gd.files.$base +temp_diffs=/tmp/gd.diffs.$base + +rm -f $temp_ct0 $temp_checkout $temp_dirs $temp_files $temp_diffs +touch $temp_ct0 $temp_dirs $temp_files + + +# Get the list of files we'll want to delta in. + +if [ $# -eq 0 ]; then + # No explicit files, get all of them. + (cd $projroot; find . -name .ct0.\* -print) >>$temp_ct0 +else + # An explicit list of files. + for filename in $*; do + if [ -f $filename ]; then + dirname=`dirname $filename` + basename=`basename $filename` + + if [ -f $dirname/.ct0.$basename ]; then + get_rel_dir $projroot $dirname + echo ./$rel_dir/.ct0.$basename >>$temp_ct0 + else + echo $filename has no versions. + fi + + elif [ -d $filename ]; then + get_rel_dir $projroot $filename + + (cd $projroot; find ./$rel_dir -name .ct0.\* -print) >>$temp_ct0 + + else + echo $filename not found. + fi + done +fi + + +# Now start to build up the script. + +echo "#! /bin/sh" >$output +chmod 755 $output + +if [ ! -w $output ]; then + echo "Cannot write to $output!" + exit 1 +fi + +projname=`basename $projroot` + +# This part we cat in quoted, verbatim. +cat << 'EOF' >>$output + +any_opts= +list= +checkout= +patch= +cleanup= +checkin= +delta= +help= +while getopts "lopcidfh" flag; do + any_opts=y + only_list=y + case $flag in + l) list=y;; + o) checkout=y; only_list=;; + p) patch=y; only_list=;; + c) cleanup=y; only_list=;; + i) checkin=y; only_list=;; + d) delta=y; only_list=;; + f) checkout=y + patch=y + cleanup=y + checkin=y + delta=y + only_list=;; + h) help=y;; + \?) exit 1; + esac +done + +EOF + +# This part we cat in unquoted, so we can substitute the projname +# variable. + +cat << EOFOUTER >>$output +if [ \$help ]; then +cat << 'EOF' + +This patch file was generated using get-delta on a remote $projname tree. +It's designed to be run one time to apply the changes made remotely +back to the main branch of the tree. + +It should be run from within your own view, somewhere within the +$projname hierarchy on the ClearCase system. + +Options: + + -l List information about the patch file, including the creation + date and the list of modified files. + + -o Checkout all the relevant files and perform other ClearCase + operations (like renaming, creating, and removing files). + + -p Apply the relevant patches to all files after they have been + checked out. If this operation fails, the rest of the script + will not continue. + + -c Cleanup after successfully patching by removing .orig and .rej + files. + + -i Checkin modified files after successfully patching. + + -d Perform final merge by executing ctdelta on modified files. + + -f Perform full checkout/patch/merge cycle. This is equivalent to + specifying -opcid. + + -h This help page. + +If no options are specified, the default is -opc. + +EOF +exit 0 +fi + +if [ -z "\$any_opts" ]; then + checkout=y + patch=y + cleanup=y +fi +EOFOUTER + +any_merged= + +# We start with the commands given in the project's .ctcmds file. +ctcmds=$projroot/.ctcmds +if [ -f $ctcmds ]; then + any_merged=y + cat $ctcmds >>$temp_checkout + if [ $collapse ]; then + rm $ctcmds + fi +fi + + +for ct0 in `cat $temp_ct0`; do + dir=`dirname $ct0` + base=`basename $ct0 | sed 's/^\.ct0\.//'` + file=$dir/$base + ctnew=$projroot/$dir/.ctnew.$base + + if (cd $projroot; diff -u $ct0 $file >>$temp_diffs); then + # If diff returned success, it means the files were identical. In + # this case, don't bother checking it out. + echo "$file is unchanged." + + else + # Otherwise, the files were genuinely different. Check it out on + # the remote end. + echo $file + + if [ -f $ctnew ]; then + # This file is newly created. We need to create an element for + # it on the remote end. + eltype=`cat $ctnew` + if grep -x $dir $temp_dirs >/dev/null; then + echo directory already checked out >/dev/null + else + echo "ctco -nc $dir" >>$temp_checkout + echo $dir >> $temp_dirs + fi + echo "touch $file" >>$temp_checkout + echo "ctmkelem -eltype $eltype $file << 'EOF' 2>/dev/null" >>$temp_checkout + list_comments $projroot/$dir $base >>$temp_checkout + echo EOF >>$temp_checkout + + echo $file >> $temp_files + else + echo "ctco $file << 'EOF' 2>/dev/null" >>$temp_checkout + list_comments $projroot/$dir $base >>$temp_checkout + echo EOF >>$temp_checkout + + echo $file >> $temp_files + fi + fi + + if [ $collapse ]; then + echo Collapsing $file + neartool collapse $projroot/$file + fi +done +echo "" + +# Handle -l, list files modified +echo "" >>$output +echo 'if [ $list ]; then' >>$output +echo ' echo ""' >>$output +echo ' echo Patch file for '$projname' tree, built on '`date` >> $output +echo ' echo File was built by '`whoami` on `hostname` >>$output +echo ' echo ""' >>$output +echo ' echo Affected files are:' >>$output +echo ' cat <>$output +sed "s/^\./ $projname/" $temp_files >>$output +echo 'EOF' >>$output +echo ' echo ""' >>$output +echo 'fi' >>$output + +# Everything else depends on being in the proper tree. + +cat <>$output +if [ \$only_list ]; then + exit 0 +fi + +projroot=\`ctproj -r\` +if [ -z "\$projroot" ]; then + echo "" + echo "You must execute this script within the $projname tree." + echo "" + exit 1 +fi +if [ \`basename \$projroot\` != "$projname" ]; then + echo "" + echo "This script is intended for the $projname tree." + echo "" + exit 1 +fi +if [ ! -d /usr/atria ]; then + echo "" + echo "This script is intended to be run on an actual ClearCase vobs." + echo "" + exit 1 +fi +tmpfile=\`whoami\`-merge-$projname.tmp +cd \$projroot +touch \$tmpfile +EOF + + +# Handle -o, checkout stuff (and perform general ClearCase changes) +echo "" >>$output +echo 'if [ $checkout ]; then' >>$output +if [ -f $temp_checkout ]; then + cat $temp_checkout >> $output +else + echo 'echo Nothing to checkout.' >>$output +fi +echo 'fi' >>$output + +# Handle -p, apply patch +echo "" >>$output +echo 'if [ $patch ]; then' >>$output +if [ -f $temp_diffs ]; then + any_merged=y; + + echo " echo ''" >> $output + echo " echo Applying patches." >> $output + echo " if sed 's/^X//' << 'EOF' | patch -fsu; then" >>$output + + sed 's/^/X/' < $temp_diffs >>$output + echo EOF >>$output + + echo " echo All patches applied successfully." >>$output + echo " echo ''" >>$output + echo " else" >>$output + echo " echo Some conflicts detected:" >>$output + echo " find . -name '*.rej' -newer \$tmpfile -print" >>$output + echo " rm -f \$tmpfile" >>$output + echo " exit 1" >>$output + echo " fi" >>$output +else + echo ' echo No patches to apply.' >>$output +fi +echo 'fi' >>$output +echo "rm -f \$tmpfile" >>$output + +# Handle -c, cleanup +echo "" >>$output +echo 'if [ $cleanup ]; then' >>$output +sed 's/^\(.*\)$/ rm -f \1.orig \1.rej/' <$temp_files >>$output +echo 'fi' >>$output + +# Handle -i, checkin +echo "" >>$output +echo 'if [ $checkin ]; then' >>$output +sed 's/^\(.*\)$/ ctci -nc \1/' <$temp_files >>$output +sed 's/^\(.*\)$/ ctci -nc \1/' <$temp_dirs >>$output +echo 'fi' >>$output + +# Handle -d, delta +echo "" >>$output +echo 'if [ $delta ]; then' >>$output +sed 's/^\(.*\)$/ ctdelta \1/' <$temp_files >>$output +sed 's/^\(.*\)$/ ctdelta \1/' <$temp_dirs >>$output +echo 'fi' >>$output + +rm -f $temp_ct0 $temp_checkout $temp_dirs $temp_files $temp_diffs + +if [ -z "$any_merged" ]; then + echo "Nothing to do!" + echo "" + rm -f $output + exit 1 +fi + diff --git a/dtool/src/attach/neartool b/dtool/src/attach/neartool new file mode 100755 index 0000000000..977c74fafe --- /dev/null +++ b/dtool/src/attach/neartool @@ -0,0 +1,1055 @@ +#! /usr/local/bin/bash +# +# neartool - a Clearcase emulator for working off-line. +# +# Usage: +# +# neartool mkelem [-c "comment"] [-nc] [-eltype type] filename +# Marks a new file as a versioned element and checks it out. +# +# neartool mkdir [-c "comment"] [-nc] new_dir +# Creates a new Clearcase directory. +# +# neartool mv old-file new-file +# Renames or moves a versioned element. +# +# neartool mv file1 file2 file3 ... to-dir +# Moves a number of versioned elements into a new directory. +# +# neartool (co|checkout) [-c "comment"] [-nc] filename +# "checks out" a file by saving its current version and making a new +# version writable. The file is also marked as "checked-out". +# +# neartool (unco|uncheckout) filename +# Reverses the effect of the previous checkout. +# +# neartool (ci|checkin) [-c "comment"] [-nc] filename +# Marks the file as "checked in" and finalizes its most recent +# version, making it read-only. +# +# neartool revert filename +# Backs up a version on a checked-in filename. +# +# neartool revertall filename +# Reverts a filename all the way to its original state as extracted +# from the Clearcase vobs. +# +# neartool collapse filename +# Collapse all versioning information and mark the file as +# "checked-in" and merged as is. Presumably this should only be +# done after merging the changes back into the actual clearcase +# vobs. +# +# neartool rmname filename +# Removes a filename from a Clearcase directory. +# +# neartool find root [opts] +# Finds all elements with versions at root or below. The options +# are ignored. This is just a hack to support ctihave. +# +# neartool (lsco|lscheckout) [-l] file1 file2 file3... +# Lists which of the files are currently checked out, if any. If no +# filenames are given, lists all of the checked out files in the +# current directory. The -l option is ignored. +# +# neartool diff [-pre] filename +# Performs a diff between the indicated filename and its previous +# version. The option -pre is ignored. +# +# neartool xdiff [-pre] filename +# Performs an xdiff between the indicated filename and its previous +# version. The option -pre is ignored. +# +#ENDCOMMENT + + +# +# get_nth_param ( param-no param1 param2 param3 ... ) +# +# Sets the variable nth_param to the value of the parameter +# corresponding to param-no. +# +function get_nth_param { + local param_no=$1 + shift $param_no + nth_param=$1 +} + +# +# get_last_param ( param1 param2 param3 ... ) +# +# Sets the variable last_param to the value of the last non-blank +# parameter. +# +function get_last_param { + last_param="$1" + shift + while [ ! -z "$*" ]; do + last_param="$1" + shift + done +} + +# +# format_comment ( comment_str ) +# +# Writes the parameter -c '$comment_str', or -nc if the comment is +# empty, to the standard output. Handles nested quotes appropriately. +# +function format_comment { + local comment="$*" + + if [ -z "$comment" ]; then + echo "-nc" + else + # Escaping single quotes inside a single-quoted string doesn't + # seem to work too reliably. Instead, we'll just remove single + # quotes. So there. + + # This absurd number of backslashes is needed to output a single + # backslash through all this nesting. + # echo "-c '"`echo $comment | sed "s/'/\\\\\\\\'/g"`"'" + echo "-c '"`echo $comment | sed "s/'//g"`"'" + fi +} + +# +# list_comments ( dirname basename ) +# +# Writes to stdout any comments associated with checked-out versions of +# the indicated file, in order. +# +function list_comments { + local dirname=$1 + local basename=$2 + local filename=$dirname/$basename + local file comment version + + if [ -f $dirname/.ct0.$basename ]; then + # Now look for comments, in version-number order. + + # We use a series of ls commands so we don't try to sort the + # filenames between the one-, two-, and three-digit version + # numbers. + + for file in `(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null`; do + version=`echo $file | sed "s/^\.ct\([0-9]*\).*$/\1/"` + comment=$dirname/.ct${version}comment.$basename + if [ -f $comment ]; then + sed s'/^/ /' <$comment + fi + done + fi +} + +# +# get_highest_version ( dirname basename ) +# +# Sets the variable $version to the numeric value that is the highest +# existing version number defined for the given filename. If the +# filename has no previous version numbers, sets $version to -1. +# +function get_highest_version { + local dirname=$1 + local basename=$2 + local filename=$dirname/$basename + local last + + if [ -f $dirname/.ct0.$basename ]; then + # If there are any versions at all, get the highest-numbered one + # and return it. + + # We use a series of ls commands so we don't try to sort the + # filenames between the one-, two-, and three-digit version + # numbers. + + last=`(cd $dirname; ls .ct[0-9].$basename; ls .ct[0-9][0-9].$basename; ls .ct[0-9][0-9][0-9].$basename) 2>/dev/null | tail -1` + version=`echo $last | sed "s/^\.ct\([0-9]*\).*$/\1/"` + else + # If there aren't any versions yet, the highest-numbered existing + # version number is -1. + version=-1 + fi +} + +# +# is_ctinternal ( basename ) +# +# Returns success if the filename matches one of the internal filenames +# generated by this script, failure if it is a perfectly ordinary +# non-ct file. +# +function is_ctinternal { + local basename=$1 + + case $basename in + .ct[0-9].*|ct[0-9][0-9].*|ct[0-9][0-9][0-9].*|\ + .ct[0-9]comment.*|.ct[0-9][0-9]comment.*|.ct[0-9][0-9][0-9]comment*|\ + .ctco.*|.ctnew.*) + return 0;; + esac + return 1 +} + +# +# is_install_dir ( dirname ) +# +# Returns success if the directory is any of the directories known to +# be install directories, such as inc, lib, lib/ss, etc. +# +function is_install_dir { + local dirname=$1 + + get_rel_dir $projroot $dirname + + case $rel_dir in + inc) return 0;; + inc/*) return 0;; + lib) return 0;; + lib/*) return 0;; + esac + return 1 +} + +# +# rm_all_ctinternals ( dirname basename ) +# +# Removes all the ctinternal files associated with the indicated file. +# +function rm_all_ctinternals { + local dirname=$1 + local basename=$2 + + rm -f $dirname/.ct[0-9].$basename + rm -f $dirname/.ct[0-9][0-9].$basename + rm -f $dirname/.ct[0-9][0-9][0-9].$basename + rm -f $dirname/.ct[0-9]comment.$basename + rm -f $dirname/.ct[0-9][0-9]comment.$basename + rm -f $dirname/.ct[0-9][0-9][0-9]comment.$basename + rm -f $dirname/.ctnew.$basename + rm -f $dirname/.ctco.$basename +} + +# +# sane_filename ( dirname basename ) +# +# Performs some sanity checks on the filename. If the file is +# unusable, exits the script with a status of 1. +# +function sane_filename { + local dirname=$1 + local basename=$2 + local filename=$dirname/$basename + + if [ -z "$basename" ]; then + echo "Filename unspecified." 1>&2 + exit 1 + fi + + if is_ctinternal $basename; then + echo "$filename is an internal filename and should not be checked out." 1>&2 + exit 1 + fi + + # It's easy to accidentally attempt to check something out from the + # inc directory. Let's detect this. + + if is_install_dir $dirname; then + echo "$basename is in an install directory." 1>&2 + echo "It should probably not be checked out." 1>&2 + exit 1 + fi + + if [ -d $filename ]; then + # Let's just quietly ignore directories. If we issue the warning + # statement, it makes ctaddtgt and ctaddpkg seem like they're + # failing. + # echo "Cannot operate on directories." 1>&2 + exit 0 + fi +} + +# +# get_fullpath ( local_dir ) +# +# Sets $fullpath to the fully-qualified pathname associated with $local_dir. +# +function get_fullpath { + local local_dir=$1 + + if [ -z "$local_dir" ]; then + fullpath=`pwd` + else + if [ ! -d "$local_dir" ]; then + echo "Invalid directory: $local_dir" 1>&2 + exit 1 + fi + # If we use pwd instead of /bin/pwd, $PWD will be used, which will give + # the wrong answer + fullpath=`(cd $local_dir; /bin/pwd)` + fi +} + + +# +# get_rel_dir ( root_dir local_dir ) +# +# Sets $rel_dir to the string which represents $local_dir relative to +# $root_dir. This is a simple string-prefix operation, and could fail +# in some obscure cases. +# +function get_rel_dir { + get_fullpath $1 + local root_dir=$fullpath + + get_fullpath $2 + local local_dir=$fullpath + + # Now remove the initial prefix. + if [ "$root_dir" = "$local_dir" ]; then + rel_dir="." + else + rel_dir=`echo $local_dir | sed 's:^'$root_dir/'::'` + + if [ "$rel_dir" = "$local_dir" ]; then + echo "$local_dir is not a directory within $root_dir." 1>&2 + exit 1 + fi + fi +} + +function ctmkelem { + local filename dirname basename + local comment eltype + + eltype=text_file + while getopts c:n:e: flag; do + case $flag in + c) comment="$OPTARG";; + n) comment="";; + e) case $OPTARG in + ltype) get_nth_param $OPTIND "$@" + eltype=$nth_param + OPTIND=`expr $OPTIND + 1`;; + *) echo Invalid switch -e$OPTARG + exit 1; + esac;; + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + + ctco=$dirname/.ctco.$basename + if [ -f $ctco ]; then + echo "$filename is already checked out." 1>&2 + exit 1 + fi + + if [ ! -f $filename ]; then + # If the file doesn't exist, create it. + echo -n "" >$filename + fi + + get_highest_version $dirname $basename + + if [ $version -ne -1 ]; then + echo "$filename already has versions." 1>&2 + exit 1 + fi + + if [ ! -w $filename ]; then + echo "$filename has no write permission, it's probably already a versioned element." 1>&2 + exit 1 + fi + + # Create a ctnew file to indicate the file is newly created. + ctnew=$dirname/.ctnew.$basename + echo $eltype >$ctnew + chmod uga-w $ctnew + + # Now "check out" the new filename by creating an empty first version. + + next=$dirname/.ct0.$basename + nextcomment=$dirname/.ct0comment.$basename + + if [ ! -z "$comment" ]; then + echo $comment >$nextcomment + chmod uga-w $nextcomment + fi + echo -n "" > $next + + date >$ctco + done +} + +function ctmkdir { + local filename dirname basename + local comment + + while getopts c:n: flag; do + case $flag in + c) comment="$OPTARG";; + n) comment="";; + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + if [ -z "$basename" ]; then + echo "Filename unspecified." 1>&2 + exit 1 + fi + + if is_ctinternal $basename; then + echo "$filename is an internal filename and should not be checked out." 1>&2 + exit 1 + fi + + if mkdir $filename; then + # The directory is successfully made; record this in the + # Clearcase instructions. + get_rel_dir $projroot $dirname + echo "ctco -nc $rel_dir" >>$projroot/.ctcmds + echo "ctmkdir `format_comment $comment` $rel_dir/$basename" >>$projroot/.ctcmds + else + echo "Unable to create directory $filename." 1>&2 + fi + done +} + +# +# do_ctmv ( oldname newname ) +# +# The implementation of ctmv. This renames a single file, possibly +# placing it in a new directory. +# +function do_ctmv { + local oldfilename olddirname oldbasename + local newfilename newdirname newbasename + + oldfilename=$1 + olddirname=`dirname $oldfilename` + oldbasename=`basename $oldfilename` + + newfilename=$2 + newdirname=`dirname $newfilename` + newbasename=`basename $newfilename` + + sane_filename $olddirname $oldbasename + sane_filename $newdirname $newbasename + + if [ ! -f $oldfilename ]; then + echo "$oldfilename does not exist." 1>&2 + exit 1 + fi + + if [ -f $newfilename ]; then + echo "$newfilename already exists--remove it first." 1>&2 + exit 1 + fi + + oldctnew=$olddirname/.ctnew.$oldfilename + + if [ ! -f $oldctnew ]; then + # The filename exists on the actual Clearcase vobs. We need to + # record the renaming. + + get_rel_dir $projroot $olddirname + local oldroot=$rel_dir + get_rel_dir $projroot $newdirname + local newroot=$rel_dir + + echo "ctco -nc $oldroot" >> $projroot/.ctcmds + if [ "$oldroot" != "$newroot" ]; then + echo "ctco -nc $newroot" >> $projroot/.ctcmds + fi + echo "ctmv $oldroot/$oldbasename $newroot/$newbasename" >> $projroot/.ctcmds + fi + + # Now rename our local copy, and all of its version tracking stuff. + get_fullpath $newdirname + local newroot=$fullpath + + (cd $olddirname; + for file in \ + `ls .ct[0-9].$oldbasename \ + .ct[0-9][0-9].$oldbasename \ + .ct[0-9][0-9][0-9].$oldbasename \ + .ct[0-9]comment.$oldbasename \ + .ct[0-9][0-9]comment.$oldbasename \ + .ct[0-9][0-9][0-9]comment.$oldbasename \ + .ctnew.$oldbasename \ + .ctco.$oldbasename \ + $oldbasename 2>/dev/null`; do + ctprefix=`echo $file | sed 's:'$oldbasename'$::'` + mv -i $file $newroot/$ctprefix$newbasename + done) +} + +function ctmv { + local source destination + + while getopts "" flag; do + case $flag in + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + get_last_param "$@" + destination=$last_param; + + if [ ! -d "$destination" ]; then + # If we're not moving to a directory, we are renaming a file--and + # thus we can only allow two parameters. + source=$1 + shift 2 + if [ ! -z "$*" ]; then + echo Last filename must be a directory. + exit 1 + fi + + do_ctmv $source $destination + else + + # Otherwise, we're moving a slew of files to a directory. Get + # each filename and move it. + for source in "$@"; do + if [ "$source" != "$destination" ]; then + do_ctmv $source $destination/$source + fi + done + fi +} + +function ctco { + local filename dirname basename + + while getopts c:n: flag; do + case $flag in + c) comment="$OPTARG";; + n) comment="";; + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + okflag=y + + if [ ! -f $filename ]; then + echo "$filename does not exist." 1>&2 + okflag= + else + ctco=$dirname/.ctco.$basename + if [ -f $ctco ]; then + echo "$filename is already checked out." 1>&2 + okflag= + else + if [ -w $filename ]; then + echo "$filename has write permission, it's probably not a versioned element." 1>&2 + echo "Try neartool mkelem $filename." 1>&2 + okflag= + fi + fi + fi + + if [ ! -z "$okflag" ]; then + get_highest_version $dirname $basename + next=$dirname/.ct`expr $version + 1`.$basename + nextcomment=$dirname/.ct`expr $version + 1`comment.$basename + + if [ ! -z "$comment" ]; then + echo $comment >$nextcomment + chmod uga-w $nextcomment + fi + + mv $filename $next + cp $next $filename + chmod ug+w $filename + + date >$ctco + fi + done +} + + +function ctunco { + local ignore filename dirname basename + + while getopts "r:" flag; do + case $flag in + r) ignore=;; + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + + if [ ! -f $filename ]; then + echo "$filename does not exist." 1>&2 + exit 1 + fi + + ctco=$dirname/.ctco.$basename + if [ ! -f $ctco ]; then + echo "$filename is not checked out." 1>&2 + exit 1 + fi + + get_highest_version $dirname $basename + last=$dirname/.ct$version.$basename + lastcomment=$dirname/.ct${version}comment.$basename + + mv -f $last $filename + rm -f $lastcomment + rm $ctco + touch $filename # For emacs, and correct make behavior. + done +} + + +function ctci { + local filename dirname basename + local comment + + while getopts c:n: flag; do + case $flag in + c) comment="$OPTARG";; + n) comment="";; + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + + if [ ! -f $filename ]; then + echo "$filename does not exist." 1>&2 + exit 1 + fi + + + ctco=$dirname/.ctco.$basename + if [ ! -f $ctco ]; then + echo "$filename is not checked out." 1>&2 + exit 1 + fi + + # We touch the file, so emacs will note that it has changed. + touch $filename + chmod uga-w $filename + + rm $ctco + done +} + + +function ctrevert { + local filename dirname basename + + while getopts "" flag; do + case $flag in + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + + if [ ! -f $filename ]; then + echo "$filename does not exist." 1>&2 + exit 1 + fi + + ctco=$dirname/.ctco.$basename + if [ -f $ctco ]; then + echo "$filename is currently checked out. Use neartool unco $filename." 1>&2 + exit 1 + fi + + get_highest_version $dirname $basename + + if [ $version -eq -1 ]; then + echo "$filename has no versions." 1>&2 + exit 1 + fi + + last=$dirname/.ct$version.$basename + lastcomment=$dirname/.ct${version}comment.$basename + + mv -f $last $filename + rm -f $lastcomment + + if [ $version -eq 0 ]; then + ctnew=$dirname/.ctnew.$basename + if [ -f $ctnew ]; then + echo "Removing newly created element $filename." 1>&2 + rm -f $ctnew $filename + fi + fi + done +} + +function ctrevertall { + local filename dirname basename + + while getopts "" flag; do + case $flag in + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + + if [ ! -f $filename ]; then + echo "$filename does not exist." 1>&2 + exit 1 + fi + + ct0=$dirname/.ct0.$basename + if [ ! -f $ct0 ]; then + echo "$filename has no versions." 1>&2 + else + ctnew=$dirname/.ctnew.$basename + if [ ! -f $ctnew ]; then + # If we didn't newly create this file, preserve its original version. + mv -f $ct0 $filename + fi + rm_all_ctinternals $dirname $basename + fi + done +} + +function ctcollapse { + local filename dirname basename + + while getopts "" flag; do + case $flag in + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + + if [ ! -f $filename ]; then + echo "$filename does not exist." 1>&2 + exit 1 + fi + + rm_all_ctinternals $dirname $basename + + chmod uga-w $filename + touch $filename # For Emacs' benefit. + done +} + +function ctrmname { + local filename dirname basename + while getopts "" flag; do + case $flag in + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + + if [ ! -f $filename ]; then + echo "$filename does not exist." 1>&2 + exit 1 + fi + + ctnew=$dirname/.ctnew.$basename + + if [ ! -f $ctnew ]; then + # The filename exists on the actual Clearcase vobs. We need to + # record the removing. + + get_rel_dir $projroot $dirname + local root=$rel_dir + + echo "ctco -nc $root" >> $projroot/.ctcmds + echo "ctrm $root/$basename" >> $projroot/.ctcmds + fi + + # Now remove all of the versioned stuff. + ctcollapse $filename + + # And remove the file itself. + rm -f $filename + done +} + + +function ctfind { + local root=$1 + + if [ -z "$root" ]; then + echo "Specify a starting point of the find." 1>&2 + exit 1 + fi + + find $root -name .ct0.\* -print | sed 's/\.ct0\.//' +} + +function ctdescribe { + local filename dirname basename + + while getopts "" flag; do + case $flag in + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in `ls "$@"`; do + dirname=`dirname $filename` + basename=`basename $filename` + + ctco=$dirname/.ctco.$basename + get_highest_version $dirname $basename + version=`expr $version + 1` + + if [ -f $ctco ]; then + if [ $version -eq 0 ]; then + echo "$filename is checked out with no versions." + else + echo "$filename is checked out as version $version." + list_comments $dirname $basename + fi + else + if [ $version -eq 0 ]; then + echo "$filename has not been checked out and has no versions." + else + echo "$filename is checked in as version $version." + list_comments $dirname $basename + fi + fi + done +} + +function ctlsco { + local filename dirname basename + local ignore + while getopts "l" flag; do + case $flag in + l) ignore=;; + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + for filename in `ls "$@"`; do + dirname=`dirname $filename` + basename=`basename $filename` + + ctco=$dirname/.ctco.$basename + if [ -f $ctco ]; then + ctdescribe $filename + fi + done +} + +function ctdiff { + local filename dirname basename + local graphical + local ignore + local diff + + while getopts "gp:" flag; do + case $flag in + g) graphical=y;; + p) ignore=;; + \?) exit 1; + esac + done + shift `expr $OPTIND - 1` + + diff=diff + if [ $graphical ]; then + # The user requested a graphical difference; use gdiff if we can + # find it. Otherwise, fall back to diff. + if which gdiff 2>/dev/null >/dev/null; then + diff=gdiff + fi + fi + + if [ -z "$1" ]; then + echo "No filenames given." + exit 1 + fi + + for filename in "$@"; do + dirname=`dirname $filename` + basename=`basename $filename` + + sane_filename $dirname $basename + + if [ ! -f $filename ]; then + echo "$filename does not exist." 1>&2 + exit 1 + fi + + get_highest_version $dirname $basename + + if [ $version -eq -1 ]; then + echo "$filename has no versions." 1>&2 + exit 1 + fi + + last=$dirname/.ct$version.$basename + + # First, call diff (the real diff) just to see if the files are + # different at all. + + if diff $last $filename >/dev/null; then + echo Files are identical. + + else + # If the files ARE different, then invoke diff again (which + # might be gdiff, this time) to actually report the differences. + # We must do it twice like this because gdiff might not return + # non-zero if the files are different. + + $diff $last $filename + fi + done +} + + +function usage { + sed '/#ENDCOMMENT/,$d' <$0 >&2 + exit 1 +} + + +# +# Main entry point +# + +command=$1 +if [ -z "$command" ]; then + usage +fi + +shift + +projroot=`ctproj -r` + +if [ -z "$projroot" ]; then + echo "Not currently in a project tree." 1>&2 + exit 1 +fi + +case $command in + mkelem) ctmkelem "$@";; + mkdir) ctmkdir "$@";; + mv) ctmv "$@";; + co|checkout) ctco "$@";; + unco|uncheckout) ctunco "$@";; + ci|checkin) ctci "$@";; + revert) ctrevert "$@";; + revertall) ctrevertall "$@";; + collapse) ctcollapse "$@";; + rmname) ctrmname "$@";; + find) ctfind "$@";; + lsco|lscheckout) ctlsco "$@";; + describe) ctdescribe "$@";; + diff) ctdiff "$@";; + xdiff) ctdiff -g "$@";; + h|help|-h) usage;; + *) echo "Invalid option: $command" 1>&2 + exit 1; +esac diff --git a/dtool/src/attach/unco.pl b/dtool/src/attach/unco.pl new file mode 100644 index 0000000000..4e1b7aa7bd --- /dev/null +++ b/dtool/src/attach/unco.pl @@ -0,0 +1,31 @@ +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctdelta.pl" ; + +# Remove a branch for an element if needed +# input is in: +# $_[0] = element +sub CTUncoDoIt { + local( $elem ) = $_[0] ; + local( $ver ) = &CTDeltaGetVersion( $elem ) ; + if ( $ctdebug ne "" ) { + print STDERR "Unco script: got version '" . $ver . "'\n" ; + } + local( @verlist ) ; + @verlist = split( /\//, $ver ) ; + local( $vlast ) = pop( @verlist ) ; + if ( $ctdebug ne "" ) { + print STDERR "Unco script: last part of version is '" . $vlast . "'\n" ; + } + if ( $#verlist > 1 ) { + local( $branch ) = join( "/", @verlist ) ; + if ( $vlast == 0 ) { + local( $cmd ) = "cleartool rmbranch -force -nc $elem" . "@@" . "$branch" ; + if ( $ctdebug ne "" ) { + print STDERR "Unco script: command is '" . $cmd . "'\n" ; + } + system $cmd ; + } + } +} + +1; diff --git a/dtool/src/attach/update-cttree b/dtool/src/attach/update-cttree new file mode 100755 index 0000000000..2abf87ee15 --- /dev/null +++ b/dtool/src/attach/update-cttree @@ -0,0 +1,189 @@ +#! /bin/sh +# +# update-cttree.sh +# +# Usage: +# +# update-cttree.sh [opts] hostname +# +# Uses rsh and rdist to update the indicated host with a fresh copy of the +# current project tree. +# +# This script must be executed from within a project tree. +# +# Options: +# +# -u username Specify the login name on the remote host. +# +# -d dir Specify the player install dir on the remote host. This +# the directory above the project-tree-specific directory +# like 'panda' or 'tool'. The default is 'player'. +# +# -t Touch the build-request timestamp file after updating. +# This assumes there's a cron job running on the remote +# machine checking the date on this file from time to time. +# +# -f Assume the user knows what he/she is doing, and don't bother +# to check that there are no files checked out in the vobs +# before releasing. This can save considerable time when the +# system is extremely slow; however, it can be dangerous +# to accidentally release a checked-out file (because the +# file will then be write-access on the remote host, and +# neartool will not be able to track local changes made to it.) +# +#ENDCOMMENT + +username=`whoami` +dirname=player +touch_request= +cocky_user= + +while getopts "u:d:tfh" flag; do + case $flag in + u) username=$OPTARG;; + d) dirname=$OPTARG;; + t) touch_request=y;; + f) cocky_user=y;; + h) sed '/#ENDCOMMENT/,$d' <$0 >&2 + exit 1;; + \?) exit 1; + esac +done + +shift `expr $OPTIND - 1` +remote_host=$1 +projroot=`ctproj -r` + +if [ -z "$projroot" ]; then + echo "" + echo "You must execute this script in a project tree." + echo "" + exit 1 +fi + +if [ -z "$remote_host" ]; then + echo "" + echo "You must specify a remote hostname. -h for help." + echo "" + exit 1 +fi + +if [ ! -d /usr/atria ]; then + echo "" + echo "This script is intended to be run on an actual ClearCase vobs." + echo "" + exit 1 +fi + +projname=`basename $projroot` +projtop=`dirname $projroot` + +if [ "$projname" = "tool" ]; then + echo "" + echo "This script should not be used on the tool tree." + echo "" + exit 1 +fi + +outfile=/tmp/uc.$username.$projname.$remote_host.out +errfile=/tmp/uc.$username.$projname.$remote_host.err +rm -f $outfile $errfile + +# Check to make sure we can run rsh to the remote machine, and that +# the remote machine doesn't have anything checked out. + +if rsh $remote_host -l $username "cd $dirname; find $projname -name .ct0.\* -print" >$outfile 2>$errfile; then + if [ ! -f $outfile ]; then + echo "" + echo "Error in processing; unable to generate $outfile." + echo "" + rm -f $outfile $errfile + exit 1 + fi + if [ ! -f $errfile ]; then + echo "" + echo "Error in processing; unable to generate $errfile." + echo "" + rm -f $outfile $errfile + exit 1 + fi + if [ -s $errfile ]; then + echo "" + echo "Unable to scan project tree $dirname/$projname on $remote_host." + echo "" + rm -f $outfile $errfile + exit 1 + fi + if [ -s $outfile ]; then + echo "" + echo "Cannot update $remote_host; files still checked out on remote:" + sed 's/^/ /;s/\.ct0\.//' $outfile + rm -f $outfile $errfile + echo "" + exit 1 + fi +else + echo "" + echo "Cannot rsh to $remote_host as $username." + echo "" + rm -f $outfile $errfile + exit 1 +fi + +# Check to make sure the local machine doesn't have anything checked out. +if [ -z "$cocky_user" ]; then + cd $projroot + cleartool lsco -s -me -recurse >$outfile + if [ -s $outfile ]; then + echo "" + echo "Cannot update from "`hostname`"; files still checked out in vobs:" + sed 's/^/ /;s/\.ct0\.//' $outfile + rm -f $outfile $errfile + echo "" + exit 1 + fi +fi + +rm -f $outfile $errfile + + +# +# Get the complete list of files in the tree we need to update. +# +cd $projtop +filelist=${outfile}.files +rm -f $filelist +cleartool find $projname -nxn -print | grep -v '/lost+found' > $filelist + +# +# Now build up a number of rdist files, as needed, to update these files. +# We have to do this in stages because there seems to be a limit of about +# 2000 files in one rdist file. +# +numlines=`wc -l $filelist | awk '{ print $1 }'` +echo $projname contains $numlines files. + +startline=1 +while [ $startline -le $numlines ]; do + echo "FILES = (" >> $outfile + tail +$startline $filelist | head -2000 >> $outfile + echo ")" >> $outfile + + echo '${FILES} -> '$username@$remote_host >>$outfile + echo " install $dirname;" >> $outfile + + if [ $touch_request ]; then + echo " cmdspecial \"touch $dirname/$projname/build-request\" ;" >>$outfile + fi + + if rdist -onochkowner,nochkgroup,numchkgroup,whole,nodescend -f $outfile; then + rm -f $outfile + else + echo "Error in rdist." + rm -f $outfile $filelist $errfile + exit 1 + fi + startline=`expr $startline + 2000` +done + +rm -f $filelist $errfile diff --git a/dtool/src/build/Makefile.a.rules b/dtool/src/build/Makefile.a.rules new file mode 100644 index 0000000000..f0229ae594 --- /dev/null +++ b/dtool/src/build/Makefile.a.rules @@ -0,0 +1,50 @@ +# Makefile for .a archive libraries + +# We *do* need -prelink when making an archive of C++ code. To +# facilitate this, we use the C++ compiler to build the archive. This +# is only necessary, of course, on SGI. + +ALLOFILES := $(ALLOFILES) $(OFILES) + + +ifeq (,$(USE_DEFERRED)) + DEFERRED_TARGET := +endif + +ifneq (,$(DEFERRED_TARGET)) + + # If the makefile requests a "deferred target", it means that we'll + # just write out a mini-makefile that defines the .o files that go + # into the target, rather than actually building the target now. + # Another Makefile later will presumably do the actual building. + + DEFERRED_MAKEFILE := Deferred.$(DEFERRED_TARGET).$(BASETARGET) + THISDIR := $(notdir $(shell pwd)) + +$(TARGET) : $(DEFERRED_MAKEFILE) +$(DEFERRED_MAKEFILE): + rm -f $@ + echo ALLOFILES += $(patsubst ./%,../../$(THISDIR)/%,$(ALLOFILES)) > $@ + +else # DEFERRED_TARGET + + ifneq (,$(strip $(ALLOFILES))) + $(TARGET): $(OFILES) # *not* ALLOFILES + ifeq (SGI,$(PENV)) + $(C++) -ar -o $@ $(ALLOFILES) + else + $(AR) $(ARFLAGS) $@ $(ALLOFILES) + endif + endif + +endif # DEFERRED_TARGET + +clean:: + @rm -f a.out.$(TARGET) + +cleanall: clean + rm -rf ii_files + rm -f $(TARGET) + +list: + @echo $(TARGET) diff --git a/dtool/src/build/Makefile.a.template b/dtool/src/build/Makefile.a.template new file mode 100644 index 0000000000..bd614aa1f6 --- /dev/null +++ b/dtool/src/build/Makefile.a.template @@ -0,0 +1,23 @@ +#### Makefile for archive libraries. Any fields commented out are optional. + +#### Target's name: +#CTTARGET + +#CTINCLUDE $DTOOL/lib/Makefile.o.include + +#### Pull in standard binary make variables. +include $(DTOOL)/inc/Makefile.bin.vars + +#### Other .o files. +# OFILES = + +#### Libs and flags for template instantiation. +# LIBS = +# LDFLAGS = + +#### Archiver flags +# ARFLAGS = + +#### The .a action is here. +include $(DTOOL)/inc/Makefile.a.rules + diff --git a/dtool/src/build/Makefile.bin.rules b/dtool/src/build/Makefile.bin.rules new file mode 100644 index 0000000000..b2bdb4c0a0 --- /dev/null +++ b/dtool/src/build/Makefile.bin.rules @@ -0,0 +1,69 @@ +# Rules to construct a binary from .o files + +ALLOFILES := $(ALLOFILES) $(OFILES) + +ifeq (,$(USE_DEFERRED)) + LIBS := $(WHEN_NO_DEFER_LIBS) $(LIBS) +else + LIBS := $(WHEN_DEFER_LIBS) $(LIBS) +endif + +# Eliminate -lm and -ldl on NT because they don't exist +WINLIBS := $(filter-out -lm -ldl,$(SYSLIBS)) +# On NT -l needs to map to lib.lib +WINLIBS := $(LIBS:-l%=lib%$(DEBUGNAME).lib) $(WINLIBS:-l%=lib%.lib) + +ifeq (PS2,$(PENV)) + LDFLAGS += -lm -nostartfiles +endif # (PS2,$(PENV)) + +ifneq (,$(strip $(ALLOFILES))) +$(TARGET): $(OFILES) # *not* ALLOFILES + +# PS2 MODIFICATION ################### + +ifeq (PS2, $(PENV)) + $(LD) $(LDFLAGS) $(ALLOFILES) $(LPATH) $(LIBS) $(SYSLIBS) -T /for/program/sce/ee/lib/app.cmd -o a.out.$@ + mv a.out.$@ $@.elf +else + +# END PS2 MOD ######################## + +ifeq (WIN32_VC,$(PENV_COMPILER)) + $(LD) $(LDFLAGS) $(LPATH) $(LINKFLAGS) $(ALLOFILES) $(WINLIBS) -OUT:a.out.$@ + mv a.out.$@ $@ +else # NON-WIN32_VC + $(LD) $(LDFLAGS) $(LPATH) $(LINKFLAGS) $(ALLOFILES) $(LIBS) $(SYSLIBS) -o a.out.$@ + mv a.out.$@ $@ +endif # WIN32_VC +endif # PS2 + +ifeq (4,$(OPTIMIZE)) + strip $@ +endif +endif + +EXPBIN = +ILKBIN = +PDBBIN = +LIBBIN = +VCFILES = +ifeq (WIN32_VC,$(PENV_COMPILER)) + EXPBIN := $(BASETARGET:%=a.out.%.exp) + ILKBIN := $(BASETARGET:%=a.out.%.ilk) + PDBBIN := $(BASETARGET:%=a.out.%.pdb) + LIBBIN := $(BASETARGET:%=a.out.%.lib) + VCFILES := vc60.idb vc60.pdb +endif # WIN32_VC + +clean:: + @rm -f a.out.$(TARGET) $(EXPBIN) $(ILKBIN) $(PDBBIN) $(LIBBIN) \ + $(VCFILES) + +cleanall: clean + rm -rf ii_files + rm -f $(TARGET) $(EXPBIN) $(ILKBIN) $(PDBBIN) $(LIBBIN) \ + $(VCFILES) + +list: + @echo $(TARGET) diff --git a/dtool/src/build/Makefile.bin.template b/dtool/src/build/Makefile.bin.template new file mode 100644 index 0000000000..5cdcc4e669 --- /dev/null +++ b/dtool/src/build/Makefile.bin.template @@ -0,0 +1,23 @@ +#### Makefile for binaries. Any fields commented out are optional. + +#### Target's name: +#CTTARGET + +#CTINCLUDE $DTOOL/lib/Makefile.o.include + +#### Pull in standard binary make variables. +include $(DTOOL)/inc/Makefile.bin.vars + +#### Other files and lib. Include $(ODIR) in any .o names. +# OFILES = +# LIBS = + +#### Additional search directories for lib: +# LPATH := $(LPATH) -L/my/directory -L/my/other/directory + +#### Other linker flags. +# LDFLAGS = + +#### The bin action is here. +include $(DTOOL)/inc/Makefile.bin.rules + diff --git a/dtool/src/build/Makefile.bin.vars b/dtool/src/build/Makefile.bin.vars new file mode 100644 index 0000000000..d37745c917 --- /dev/null +++ b/dtool/src/build/Makefile.bin.vars @@ -0,0 +1,121 @@ +# Pull in the .o vars if they haven't been read already +ifeq (,$(MAKEFILE_O_VARS)) +include $(DTOOL)/inc/Makefile.o.vars +endif + +# Get the project root +ifeq (,$(CTPROJROOT)) +CTPROJROOT := $(shell $(DTOOL)/bin/ctproj -root) +export CTPROJROOT +endif + +# Set the SOLOCATIONS file +SOLOCATIONS := ../../etc/so_locations + +# Make sure CT_LIBRARY_PATH is set. +ifeq (,$(CT_LIBRARY_PATH)) +ifneq (,$(LD_LIBRARY_PATH)) +export CT_LIBRARY_PATH := $(LD_LIBRARY_PATH) +else +export CT_LIBRARY_PATH = . +endif +endif + +LPATH_DIRS := $(strip $(shell ctpathadjust $(subst :, ,$(CT_LIBRARY_PATH)))) $(patsubst -L%,%,$(LPATH)) + +ifeq (WIN32_VC,$(PENV_COMPILER)) + lt := $(foreach path,$(LPATH_DIRS),$(shell cygpath -w $(path))) + LPATH := $(patsubst %,-LIBPATH:"%",$(lt)) +else # NON-WIN32_VC + LPATH := $(patsubst %,-L%,$(LPATH_DIRS)) +endif # WIN32_VC + +ifeq (SGI,$(PENV)) + CLDFLAGS = -Wl,-force_load -Wl,-transitive_link + C++LDFLAGS = -Wl,-demangle -Wl,-force_load -Wl,-transitive_link +endif + +# Figure out what the linker is. Start with simple defaults. + +ifeq (PS2, $(PENV)) + AR = ee-ar + LD = ee-ld +else + AR = ar + LD = ld +endif + +LDFLAGS = +LINKFLAGS = + +# C files +ifneq (,$(CFILES)) +LD := $(CC) +LDFLAGS := $(CLDFLAGS) $(CFLAGS) +endif + +# C++ files +ifneq (,$(C++FILES)) # If there are C++ files, make it CC +LD := $(C++) +LDFLAGS := $(C++LDFLAGS) $(C++FLAGS) +endif + +# C files in the OFILES list +ifneq (,$(findstring .c,$(suffix $(OFILES)))) +LD := $(CC) +LDFLAGS := $(IPATH) $(CLDFLAGS) $(CFLAGS) +endif + +# C++ files in the OFILES list +ifneq (,$(findstring .cxx,$(suffix $(OFILES)))) +LD := $(C++) +LDFLAGS := $(IPATH) $(C++LDFLAGS) $(C++FLAGS) +endif + +# Making a DSO - use C++ for now +ifeq (.so,$(suffix $(TARGET))) +LD := $(C++) +LDFLAGS := $(IPATH) $(C++LDFLAGS) $(C++FLAGS) +endif + +# Making a DLL - use C++ for now +ifeq (.dll,$(suffix $(TARGET))) +LD := $(C++) +LDFLAGS := $(C++LDFLAGS) $(C++FLAGS) +endif + +# Making a WIN binary file +ifeq (WIN32,$(PENV)) + LD = link + LDFLAGS = -NOLOGO + + ifeq (4,$(OPTIMIZE)) + LINKFLAGS += -fixed:no + else + ifeq (3,$(OPTIMIZE)) + LINKFLAGS += -fixed:no + else + LINKFLAGS += -debug -incremental:no + endif + endif +endif + +ARFLAGS = ruv + + + + + + + + + + + + + + + + + + diff --git a/dtool/src/build/Makefile.foreign.rules b/dtool/src/build/Makefile.foreign.rules new file mode 100644 index 0000000000..ae4f44aded --- /dev/null +++ b/dtool/src/build/Makefile.foreign.rules @@ -0,0 +1,74 @@ +############################################################################## +# +# Foreign package building rules +# + +# Clear ALLTARGETS +ALLTARGETS = + +ifneq (,$(COMMAND1)) +ALLTARGETS := $(ALLTARGETS) $(TARGET)_COMMAND1_RULE +ifeq (,$(DIR1)) +DIR1 = . +endif +$(TARGET)_COMMAND1_RULE: + @ echo "**** executing subcommand '$(COMMAND1)'" + @ cd $(STARTDIR)/$(DIR1) ; $(COMMAND1) +.PHONY: $(TARGET)_COMMAND1_RULE +endif + +ifneq (,$(COMMAND2)) +ALLTARGETS := $(ALLTARGETS) $(TARGET)_COMMAND2_RULE +ifeq (,$(DIR2)) +DIR2 = . +endif +$(TARGET)_COMMAND2_RULE: + @ echo "**** executing subcommand '$(COMMAND2)'" + @ cd $(STARTDIR)/$(DIR2) ; $(COMMAND2) +.PHONY: $(TARGET)_COMMAND2_RULE +endif + +ifneq (,$(COMMAND3)) +ALLTARGETS := $(ALLTARGETS) $(TARGET)_COMMAND3_RULE +ifeq (,$(DIR3)) +DIR3 = . +endif +$(TARGET)_COMMAND3_RULE: + @ echo "**** executing subcommand '$(COMMAND3)'" + @ cd $(STARTDIR)/$(DIR3) ; $(COMMAND3) +.PHONY: $(TARGET)_COMMAND3_RULE +endif + +ifneq (,$(COMMAND4)) +ALLTARGETS := $(ALLTARGETS) $(TARGET)_COMMAND4_RULE +ifeq (,$(DIR4)) +DIR4 = . +endif +$(TARGET)_COMMAND4_RULE: + @ echo "**** executing subcommand '$(COMMAND4)'" + @ cd $(STARTDIR)/$(DIR4) ; $(COMMAND4) +.PHONY: $(TARGET)_COMMAND4_RULE +endif + +ifneq (,$(COMMAND5)) +ALLTARGETS := $(ALLTARGETS) $(TARGET)_COMMAND5_RULE +ifeq (,$(DIR5)) +DIR5 = . +endif +$(TARGET)_COMMAND5_RULE: + @ echo "**** executing subcommand '$(COMMAND5)'" + @ cd $(STARTDIR)/$(DIR5) ; $(COMMAND5) +.PHONY: $(TARGET)_COMMAND5_RULE +endif + +# now if there's anything to do, do it +ifneq (,$(ALLTARGETS)) +$(TARGET): $(ALLTARGETS) +endif + +clean:: + +cleanall: clean + +list: + @ echo $(TARGET) diff --git a/dtool/src/build/Makefile.foreign.template b/dtool/src/build/Makefile.foreign.template new file mode 100644 index 0000000000..69a9c4317c --- /dev/null +++ b/dtool/src/build/Makefile.foreign.template @@ -0,0 +1,28 @@ +#### Makefile for foreign packages. Any fields commented out are optional. + +#### Target's name: +#CTTARGET + +#### Pull in standard foreign package make variables +include $(DTOOL)/inc/Makefile.foreign.vars + +#### commands for building a foreign package. DIR?, if ommitted, defaults to +#### '.'. COMMAND?, if ommitted, does nothing. +# DIR1 = +# COMMAND1 = + +# DIR2 = +# COMMAND2 = + +# DIR3 = +# COMMAND3 = + +# DIR4 = +# COMMAND4 = + +# DIR5 = +# COMMAND5 = + + +#### The foreign package build action is here. +include $(DTOOL)/inc/Makefile.foreign.rules diff --git a/dtool/src/build/Makefile.foreign.vars b/dtool/src/build/Makefile.foreign.vars new file mode 100644 index 0000000000..fe1bcd0227 --- /dev/null +++ b/dtool/src/build/Makefile.foreign.vars @@ -0,0 +1,19 @@ +# Standard variables for building foreign packages + +ifeq (,$(MAKEFILE_FOREIGN_VARS)) +MAKEFILE_FOREIGN_VARS = TRUE + +ifeq (,$(MAKEFILE_PENV_VARS)) +include $(DTOOL)/inc/Makefile.penv.vars +endif + +# Delete the default suffixes +.SUFFIXES: + +# get a dir to cd to +STARTDIR = `ctproj -r`/src/all/$(TARGET) + +# Set the default target +install all : $(TARGET) + +endif diff --git a/dtool/src/build/Makefile.install.rules b/dtool/src/build/Makefile.install.rules new file mode 100644 index 0000000000..bb094d0dc6 --- /dev/null +++ b/dtool/src/build/Makefile.install.rules @@ -0,0 +1,891 @@ +#### Makefile install rules. Setup. +strip-target = `echo $@ | sed 's/^[^-]*-//'` + +# Yank out any whitespace in the package name. +PACKAGE := $(strip $(PACKAGE)) + +# Pull in standard install make variables. +ifeq (,$(MAKEFILE_INSTALL_VARS)) +include $(DTOOL)/inc/Makefile.install.vars +endif + +# We need this one to define DEBUGNAME for us. +ifeq (,$(MAKEFILE_O_VARS)) +include $(DTOOL)/inc/Makefile.o.vars +endif + +# Infer the package root from the package name, if not already set. +ifeq (,$(PKGROOT)) +PKGROOT = src/all/$(PACKAGE) +else +PKGROOT := $(strip $(PKGROOT)) +endif + + +# First target. Convenient synonym for install. +$(PACKAGE): install-$(PACKAGE) +$(PKGROOT): install-$(PKGROOT) + + + +#### File install actions +# +# This section constructs lists of installed files, and the source files +# from which they are copied. Then the user supplied list is cleared. +# This is because this rules file is included in the project Makefile +# more than once, and we'd rather not have one package's file list +# affecting the installation of another package that follows it. +# +# Each installed file depends on the existence of a source file from +# which to copy it. Each block below is for a different installation +# directory. The ALLSRC and ALLINST lists will be used in the next sections +# of the rules. More such blocks may be added by following the pattern. +# + +ALLSRC = +ALLINST = + + +# DEFERRED supplied by package's Makefile.install. If defined (and +# not overridden in Makefile.penv.vars), this indicates that a +# deferred build is requested by the makefiles in this package, and we +# should install only the output of these deferred builds. This, +# therefore, overrides any request to install LIBS. + +ifeq (,$(USE_DEFERRED)) + DEFERRED := +endif + +ifeq (PS2, $(PENV)) + SOLIBS := $(filter %.so,$(LIBS)) + SOLIBS := $(SOLIBS:%.so=%.a) + ALIBS := $(filter %.a,$(LIBS)) + LIBS := $(SOLIBS) + LIBS += $(ALIBS) +endif + +SRCDEFERRED := $(addprefix $(PKGROOT)/,$(DEFERRED)) +ALLSRC := $(ALLSRC) $(SRCDEFERRED) +INSTDEFERRED := $(addprefix deferred/,$(DEFERRED)) +ALLINST := $(ALLINST) $(INSTDEFERRED) +DEFERRED = + +ifneq (,$(INSTDEFERRED)) +$(INSTDEFERRED): deferred/% : $(PKGROOT)/% + $(MKINSTALL) +endif + +# LIBS supplied by package's Makefile.install. +ifeq (WIN32,$(PENV)) + SOLIBS := $(filter %.so,$(LIBS)) + SOLIBS := $(SOLIBS:%.so=%$(DEBUGNAME).dll) + ALIBS := $(filter %.a,$(LIBS)) + ALIBS := $(ALIBS:%.a=%$(DEBUGNAME).lib) + LIBS := $(SOLIBS:%.dll=%.lib) + LIBS += $(ALIBS) + LIBS += $(SOLIBS) +endif # WIN32 +ifeq (OSX,$(PENV)) + SOLIBS := $(filter %.so,$(LIBS)) + SOLIBS := $(SOLIBS:%.so=%.dylib) + ALIBS := $(filter %.a,$(LIBS)) + LIBS := $(ALIBS) + LIBS += $(SOLIBS) +endif # OSX + +SRCLIBS := $(addprefix $(PKGROOT)/,$(LIBS)) +ALLSRC := $(ALLSRC) $(SRCLIBS) +INSTLIBS := $(addprefix lib/,$(LIBS)) +ALLINST := $(ALLINST) $(INSTLIBS) +LIBS = + +ifeq (,$(INSTDEFERRED)) + ifneq (,$(INSTLIBS)) + $(INSTLIBS): lib/% : $(PKGROOT)/% + $(MKINSTALL) + endif +else + ifneq (,$(INSTLIBS)) + $(INSTLIBS): + endif +endif + + +# INCLUDE supplied by package's Makefile.install. +INCLUDE := $(strip $(INCLUDE)) +SRCINCLUDE := $(addprefix $(PKGROOT)/,$(INCLUDE)) +ALLSRC := $(ALLSRC) $(SRCINCLUDE) +INSTINCLUDE := $(addprefix inc/,$(INCLUDE)) +ALLINST := $(ALLINST) $(INSTINCLUDE) +INCLUDE = + +ifneq (,$(INSTINCLUDE)) +$(INSTINCLUDE): inc/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# BINS supplied by package's Makefile.install. +BINS := $(strip $(BINS)) +ifeq (WIN32,$(PENV)) + BINS := $(BINS:%=%.exe) +endif +SRCBINS := $(addprefix $(PKGROOT)/,$(BINS)) +ALLSRC := $(ALLSRC) $(SRCBINS) +INSTBINS := $(addprefix bin/,$(BINS)) +ALLINST := $(ALLINST) $(INSTBINS) +BINS = + +ifneq (,$(INSTBINS)) + $(INSTBINS): bin/% : $(PKGROOT)/% + $(MKINSTALL) + @chmod +x $@ +endif + + +# SCRIPTS supplied by package's Makefile.install. +SCRIPTS := $(strip $(SCRIPTS)) +SRCSCRIPTS := $(addprefix $(PKGROOT)/,$(SCRIPTS)) +ALLSRC := $(ALLSRC) $(SRCSCRIPTS) +INSTSCRIPTS := $(addprefix bin/,$(SCRIPTS)) +ALLINST := $(ALLINST) $(INSTSCRIPTS) +SCRIPTS = + +ifneq (,$(INSTSCRIPTS)) +$(INSTSCRIPTS): bin/% : $(PKGROOT)/% + $(MKINSTALL) + @chmod +x $@ +endif + + +# SS supplied by package's Makefile.install. +SS := $(strip $(SS)) +SRCSS := $(addprefix $(PKGROOT)/,$(SS)) +ALLSRC := $(ALLSRC) $(SRCSS) +INSTSS := $(addprefix lib/ss/,$(SS)) +ALLINST := $(ALLINST) $(INSTSS) +SS = + +ifneq (,$(INSTSS)) +$(INSTSS): lib/ss/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# ST supplied by package's Makefile.install. +ST := $(strip $(ST)) +SRCST := $(addprefix $(PKGROOT)/,$(ST)) +ALLSRC := $(ALLSRC) $(SRCST) +INSTST := $(addprefix lib/st/,$(ST)) +ALLINST := $(ALLINST) $(INSTST) +ST = + +ifneq (,$(INSTST)) +$(INSTST): lib/st/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# STK supplied by package's Makefile.install. +STK := $(strip $(STK)) +SRCSTK := $(addprefix $(PKGROOT)/,$(STK)) +ALLSRC := $(ALLSRC) $(SRCSTK) +INSTSTK := $(addprefix lib/stk/,$(STK)) +ALLINST := $(ALLINST) $(INSTSTK) +STK = + +ifneq (,$(INSTSTK)) +$(INSTSTK): lib/stk/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# ETC supplied by package's Makefile.install. +ETC := $(strip $(ETC)) +SRCETC := $(addprefix $(PKGROOT)/,$(ETC)) +ALLSRC := $(ALLSRC) $(SRCETC) +INSTETC := $(addprefix etc/,$(ETC)) +ALLINST := $(ALLINST) $(INSTETC) +ETC = + +ifneq (,$(INSTETC)) +$(INSTETC): etc/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# IGATEDB supplied by package's Makefile.install. +ifeq (,$(NO_INTERROGATE)) +IGATEDB := $(strip $(IGATEDB)) +SRCIGATEDB := $(addprefix $(PKGROOT)/,$(IGATEDB)) +ALLSRC := $(ALLSRC) $(SRCIGATEDB) +INSTIGATEDB := $(addprefix etc/,$(IGATEDB)) +ALLINST := $(ALLINST) $(INSTIGATEDB) +IGATEDB = + +ifneq (,$(INSTIGATEDB)) +$(INSTIGATEDB): etc/% : $(PKGROOT)/% + $(MKINSTALL) +endif +endif + + +# MODELS supplied by package's Makefile.install. +MODELS := $(strip $(MODELS)) +SRCMODELS := $(addprefix $(PKGROOT)/,$(MODELS)) +ALLSRC := $(ALLSRC) $(SRCMODELS) +INSTMODELS := $(addprefix etc/models/, $(MODELS)) +ALLINST := $(ALLINST) $(INSTMODELS) +MODELS = + +ifneq (, $(INSTMODELS)) +$(INSTMODELS): etc/models/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# DOC supplied by package's Makefile.install. +DOC := $(strip $(DOC)) +SRCDOC := $(addprefix $(PKGROOT)/,$(DOC)) +ALLSRC := $(ALLSRC) $(SRCDOC) +INSTDOC := $(addprefix doc/,$(DOC)) +ALLINST := $(ALLINST) $(INSTDOC) +DOC = + +ifneq (,$(INSTDOC)) +$(INSTDOC): doc/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# MAN supplied by package's Makefile.install. +MAN := $(strip $(MAN)) +SRCMAN := $(addprefix $(PKGROOT)/,$(MAN)) +ALLSRC := $(ALLSRC) $(SRCMAN) +INSTMAN := $(addprefix doc/man/,$(MAN)) +ALLINST := $(ALLINST) $(INSTMAN) +MAN = + +ifneq (,$(INSTMAN)) +$(INSTMAN): doc/man/% : $(PKGROOT)/% + $(MKINSTALL) +endif + +# TXT supplied by package's Makefile.install. +TXT := $(strip $(TXT)) +SRCTXT := $(addprefix $(PKGROOT)/,$(TXT)) +ALLSRC := $(ALLSRC) $(SRCTXT) +INSTTXT := $(addprefix doc/txt/,$(TXT)) +ALLINST := $(ALLINST) $(INSTTXT) +TXT = + +ifneq (,$(INSTTXT)) +$(INSTTXT): doc/txt/% : $(PKGROOT)/% + $(MKINSTALL) +endif + +# HTML supplied by package's Makefile.install. +HTML := $(strip $(HTML)) +SRCHTML := $(addprefix $(PKGROOT)/,$(HTML)) +ALLSRC := $(ALLSRC) $(SRCHTML) +INSTHTML := $(addprefix doc/html/,$(HTML)) +ALLINST := $(ALLINST) $(INSTHTML) +HTML = + +ifneq (,$(INSTHTML)) +$(INSTHTML): doc/html/% : $(PKGROOT)/% + $(MKINSTALL) +endif + +# PS supplied by package's Makefile.install. +PS := $(strip $(PS)) +SRCPS := $(addprefix $(PKGROOT)/,$(PS)) +ALLSRC := $(ALLSRC) $(SRCPS) +INSTPS := $(addprefix doc/ps/,$(PS)) +ALLINST := $(ALLINST) $(INSTPS) +PS = + +ifneq (,$(INSTPS)) +$(INSTPS): doc/ps/% : $(PKGROOT)/% + $(MKINSTALL) +endif + +# FONTS supplied by package's Makefile.install. +FONTS := $(strip $(FONTS)) +SRCFONTS := $(addprefix $(PKGROOT)/,$(FONTS)) +ALLSRC := $(ALLSRC) $(SRCFONTS) +INSTFONTS := $(addprefix etc/fonts/,$(FONTS)) +ALLINST := $(ALLINST) $(INSTFONTS) +FONTS = + +ifneq (,$(INSTFONTS)) +$(INSTFONTS): etc/fonts/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# ICONS supplied by package's Makefile.install. +ICONS := $(strip $(ICONS)) +SRCICONS := $(addprefix $(PKGROOT)/,$(ICONS)) +ALLSRC := $(ALLSRC) $(SRCICONS) +INSTICONS := $(addprefix etc/icons/,$(ICONS)) +ALLINST := $(ALLINST) $(INSTICONS) +ICONS = + +ifneq (,$(INSTICONS)) +$(INSTICONS): etc/icons/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# APPDEFAULTS supplied by package's Makefile.install. +APPDEFAULTS := $(strip $(APPDEFAULTS)) +SRCAPPDEFAULTS := $(addprefix $(PKGROOT)/,$(APPDEFAULTS)) +ALLSRC := $(ALLSRC) $(SRCAPPDEFAULTS) +INSTAPPDEFAULTS := $(addprefix etc/app-defaults/,$(APPDEFAULTS)) +ALLINST := $(ALLINST) $(INSTAPPDEFAULTS) +APPDEFAULTS = + +ifneq (,$(INSTAPPDEFAULTS)) +$(INSTAPPDEFAULTS): etc/app-defaults/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# TCL supplied by package's Makefile.install. +TCL := $(strip $(TCL)) +SRCTCL := $(addprefix $(PKGROOT)/,$(TCL)) +ALLSRC := $(ALLSRC) $(SRCTCL) +INSTTCL := $(addprefix etc/tcl/,$(TCL)) +ALLINST := $(ALLINST) $(INSTTCL) +TCL = + +ifneq (,$(INSTTCL)) +$(INSTTCL): etc/tcl/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# TELEUSE supplied by package's Makefile.install. +TELEUSE := $(strip $(TELEUSE)) +SRCTELEUSE := $(addprefix $(PKGROOT)/,$(TELEUSE)) +ALLSRC := $(ALLSRC) $(SRCTELEUSE) +INSTTELEUSE := $(addprefix etc/teleuse/,$(TELEUSE)) +ALLINST := $(ALLINST) $(INSTTELEUSE) +TELEUSE = + +ifneq (,$(INSTTELEUSE)) +$(INSTTELEUSE): etc/teleuse/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# SHADERS supplied by package's Makefile.install. +SHADERS := $(strip $(SHADERS)) +SRCSHADERS := $(addprefix $(PKGROOT)/,$(SHADERS)) +ALLSRC := $(ALLSRC) $(SRCSHADERS) +INSTSHADERS := $(addprefix etc/shaders/,$(SHADERS)) +ALLINST := $(ALLINST) $(INSTSHADERS) +SHADERS = + +ifneq (,$(INSTSHADERS)) +$(INSTSHADERS): etc/shaders/% : $(PKGROOT)/% + $(MKINSTALL) +endif + + +# OTHER supplied by package's Makefile.install. +OTHER := $(strip $(OTHER)) +SRCOTHER := $(addprefix $(PKGROOT)/,$(notdir $(OTHER))) +ALLSRC := $(ALLSRC) $(SRCOTHER) +INSTOTHER := $(OTHER) +ALLINST := $(ALLINST) $(INSTOTHER) +OTHER = +# Install actions for these files come from package's Makefile.install + + + +#### Dependencies on files from other packages (same project). + +ALLUSES = + +# USESLIBS supplied by package's Makefile.install. + +ifeq (PS2, $(PENV)) + USESLIBS := $(USESLIBS:%.so=%.a) +endif + +ifeq (WIN32,$(PENV)) + USESLIBS := $(USESLIBS:%.so=%$(DEBUGNAME).dll) + USESLIBS := $(USESLIBS:%.a=%$(DEBUGNAME).lib) +endif # WIN32 +ifeq (OSX,$(PENV)) + USESLIBS := $(USESLIBS:%.so=%.dylib) +endif # OSX +ALLUSES := $(ALLUSES) $(addprefix lib/,$(strip $(USESLIBS))) +USESLIBS = + +# USESINCLUDE supplied by package's Makefile.install. +ALLUSES := $(ALLUSES) $(addprefix inc/,$(strip $(USESINCLUDE))) +USESINCLUDE = + +# USESDEFERREDLIBS supplied by package's Makefile.install, but only +# respected if USE_DEFERRED is set. +ifneq (,$(USE_DEFERRED)) + ifeq (WIN32,$(PENV)) + USESDEFERREDLIBS := $(USESDEFERREDLIBS:%.so=%$(DEBUGNAME).dll) + USESDEFERREDLIBS := $(USESDEFERREDLIBS:%.a=%$(DEBUGNAME).lib) + endif # WIN32 + ifeq (OSX,$(PENV)) + USESDEFERREDLIBS := $(USESDEFERREDLIBS:%.so=%.dylib) + endif # OS X + ALLUSES := $(ALLUSES) $(addprefix lib/,$(strip $(USESDEFERREDLIBS))) + USESDEFERREDLIBS = +endif + +# USESOTHER supplied by package's Makefile.install. +ALLUSES := $(ALLUSES) $(strip $(USESOTHER)) +USESOTHER = + + + +#### Action targets +# +# The following series of targets all cause some action to take place in +# the project tree. All of these actions may be done on a single +# package or on the entire project. By default, any action done on a +# package is also done first on any packages it depends on. This can be +# circumvented by setting NODEPEND to true. +# +# One of the tricky things done in all the following rules was to not use +# any variables in the build scripts. This is necessary because this +# rules files is read in multiple times in a project build, and the +# variable are set multiple times. However, build scripts are executed +# at the end of the read process, after dependency resolution. At that +# time, variables have the value they were last set to. Instead, the +# approach is to place the variables in the dependency lists and target +# names, since these are resolved as the file is read. +# + +#### Installation and build rules. + +# To install project, install all packages. +install-$(CTPROJECT): install-$(PKGROOT) + +# To install package, must build all installed files. +install-$(PKGROOT): announce-install-$(PKGROOT) $(ALLINST) + +# Synonym for installing a package. +install-$(PACKAGE): install-$(PKGROOT) + +# $(ALLINST) : $(ALLSRC) done in install actions section above. + +## Before installing, announce that installation is starting. +#ifneq (,$(ALLSRC)) +#$(ALLSRC): announce-install-$(PKGROOT) +#endif + +# State that we're installing files from the package. Build first. +announce-install-$(PKGROOT): build-$(PKGROOT) + @echo '***' Installing from $(strip-target) '...' + +# Check for Makefile in package. If found, make it. +ifeq (,$(NODEPEND)) +build-$(PKGROOT) : $(addprefix install-,$(ALLUSES)) +else +build-$(PKGROOT) : +endif + @cd $(CTPROJROOT)/$(strip-target); if [ -r Makefile ]; then echo '***' Building $(strip-target) '...'; \ + $(MAKE) install; fi + +# Convenient synonym for build. +build-$(PACKAGE): build-$(PKGROOT) + +# Provide hooks for other packages to transmit build dependencies. +ifneq (,$(ALLINST)) +$(addprefix install-,$(ALLINST)): install-$(PKGROOT) +endif + + + +#### Clean rules. + +# To clean project, clean each package. +clean-$(CTPROJECT): clean-$(PKGROOT) + +# Synonym to clean a particular package. +clean-$(PACKAGE): clean-$(PKGROOT) + +ifneq (,$(ALLINST)) +$(addprefix clean-,$(ALLINST)): clean-$(PKGROOT) +endif + +# To clean package, cd to it and do 'make clean' (if there's a Makefile). +ifeq (,$(NODEPEND)) +clean-$(PKGROOT) : $(addprefix clean-,$(ALLUSES)) +else +clean-$(PKGROOT) : +endif + @cd $(CTPROJROOT)/$(strip-target); if [ -r Makefile ]; then echo '***' Cleaning $(strip-target) '...'; \ + $(MAKE) clean; fi + + + +#### Cleanall rules. + +# To clean project, clean each package. +cleanall-$(CTPROJECT): cleanall-$(PKGROOT) + +# Synonym to cleanall a particular package. +cleanall-$(PACKAGE): cleanall-$(PKGROOT) + +ifneq (,$(ALLINST)) +$(addprefix cleanall-,$(ALLINST)): cleanall-$(PKGROOT) +endif + +# To cleanall package, cd to it and do 'make cleanall' (if there's a Makefile). +ifeq (,$(NODEPEND)) +cleanall-$(PKGROOT) : $(addprefix cleanall-,$(ALLUSES)) +else +cleanall-$(PKGROOT) : +endif + @cd $(CTPROJROOT)/$(strip-target); if [ -r Makefile ]; then echo '***' Wiping $(strip-target) '...'; \ + $(MAKE) cleanall; fi + + + +#### Uninstall rules. + +# To uninstall project, must uninstall each package. +uninstall-$(CTPROJECT): uninstall-$(PKGROOT) + +# To uninstall package, must remove each installed file. +uninstall-$(PKGROOT): $(addprefix uninstall-,$(ALLINST)) + +# Synonym for uninstalling a package. +uninstall-$(PACKAGE): uninstall-$(PKGROOT) + +# To remove file, check if it's there and rm it. +ifneq (,$(ALLINST)) +$(addprefix uninstall-,$(ALLINST)): announce-uninstall-$(PKGROOT) + @if [ -r $(strip-target) ]; then echo $(strip-target); rm -rf $(strip-target); fi + +$(addprefix touninstall-,$(ALLINST)): uninstall-$(PKGROOT) +endif + +# Announce uninstallation. First uninstall dependent packages. +ifeq (,$(NODEPEND)) +announce-uninstall-$(PKGROOT): $(addprefix touninstall-,$(ALLUSES)) +else +announce-uninstall-$(PKGROOT): +endif + @echo '***' Uninstalling from package $(strip-target) '...' + + + +#### Touch rules. + +# To touch project, must touch each package. +touch-$(CTPROJECT): touch-$(PKGROOT) + +# To touch package, must touch each installed file. +touch-$(PKGROOT): $(addprefix touch-,$(ALLINST)) + +# Synonym for touching a package. +touch-$(PACKAGE): touch-$(PKGROOT) + +# To touch file, check if it's there and touch it. Works on directories. +ifneq (,$(ALLINST)) +$(addprefix touch-,$(ALLINST)): announce-touch-$(PKGROOT) + @if [ -r $(strip-target) ]; then find $(strip-target) -print -exec touch {} \; ; fi + +$(addprefix totouch-,$(ALLINST)): touch-$(PKGROOT) +endif + +# Announce touch. First touch dependent packages. +ifeq (,$(NODEPEND)) +announce-touch-$(PKGROOT): $(addprefix totouch-,$(ALLUSES)) +else +announce-touch-$(PKGROOT): +endif + @echo '***' Touching package $(strip-target) '...' + + + +#### Copy rules. + +# To copy project, must copy each package. +copy-$(CTPROJECT): copy-$(PKGROOT) + +# To copy package, must copy each installed file. +copy-$(PKGROOT): $(addprefix copy-,$(ALLINST)) + +# Synonym for copying a package. +copy-$(PACKAGE): copy-$(PKGROOT) + +ifneq (,$(ALLINST)) +$(addprefix copy-,$(ALLINST)): announce-copy-$(PKGROOT) +ifneq (,$(CTFROM)) + @if [ ! -d $(*D) ]; then mkdir $(*D); fi + @cp -r $(CTFROM)/$(strip-target) $(strip-target) && echo $(strip-target) +else + @echo CTFROM variable not set; exit 1 +endif + +$(addprefix tocopy-,$(ALLINST)): copy-$(PKGROOT) +endif + +# Announce copy. First copy dependent packages. +ifeq (,$(NODEPEND)) +announce-copy-$(PKGROOT): $(addprefix tocopy-,$(ALLUSES)) +else +announce-copy-$(PKGROOT): +endif + @echo '***' Copying package $(strip-target) '...' + + + +#### Query targets. +# +# The following rules use the project makefile as a database, to which +# various questions can be put, such as 'what files does this package +# install?', 'which files are currently installed?', and 'what packages +# must be built before this package can be built?'. These targets take +# no action in the project, they just produce a list of one or more +# answers. As in the action targets, interpackage dependencies are used +# to automatically propagate the question from a package to its +# dependency packages (those it needs to build). + +#### Query files to be installed by a package. + +# To find files installed by project, query each package. +toinstall-$(CTPROJECT): toinstall-$(PKGROOT) + +# Convenient synonym for toinstall. +toinstall-$(PACKAGE): toinstall-$(PKGROOT) + +# To list package, list each file installed by package. +toinstall-$(PKGROOT): $(addprefix toinstall-,$(ALLINST)) + +# Echo each installed file's name. First announce the package. +ifneq (,$(ALLINST)) +$(addprefix toinstall-,$(ALLINST)): announce-toinstall-$(PKGROOT) + @echo $(strip-target) + +$(addprefix totoinstall-,$(ALLINST)): toinstall-$(PKGROOT) +endif + +ifeq (,$(NODEPEND)) +announce-toinstall-$(PKGROOT): $(addprefix totoinstall-,$(ALLUSES)) +else +announce-toinstall-$(PKGROOT): +endif + @echo '***' To be installed from $(strip-target) '...' + + + + +#### Query files that have been installed by a package. + +isinstalled-$(CTPROJECT): isinstalled-$(PKGROOT) + +isinstalled-$(PKGROOT): $(addprefix isinstalled-,$(ALLINST)) + +isinstalled-$(PACKAGE): isinstalled-$(PKGROOT) + +ifneq (,$(ALLINST)) +$(addprefix isinstalled-,$(ALLINST)): announce-isinstalled-$(PKGROOT) + @if [ -r $(strip-target) ]; then echo $(strip-target); fi + +$(addprefix toisinstalled-,$(ALLINST)): isinstalled-$(PKGROOT) +endif + +ifeq (,$(NODEPEND)) +announce-isinstalled-$(PKGROOT): $(addprefix toisinstalled-,$(ALLUSES)) +else +announce-isinstalled-$(PKGROOT): +endif + @echo '***' Currently installed from $(strip-target) '...' + + + +#### Query files that have not been installed by a package. + +notinstalled-$(CTPROJECT): notinstalled-$(PKGROOT) + +notinstalled-$(PKGROOT): $(addprefix notinstalled-,$(ALLINST)) + +notinstalled-$(PACKAGE): notinstalled-$(PKGROOT) + +ifneq (,$(ALLINST)) +$(addprefix notinstalled-,$(ALLINST)): announce-notinstalled-$(PKGROOT) + @if [ ! -r $(strip-target) ]; then echo $(strip-target); fi + +$(addprefix tonotinstalled-,$(ALLINST)): notinstalled-$(PKGROOT) +endif + +ifeq (,$(NODEPEND)) +announce-notinstalled-$(PKGROOT): $(addprefix tonotinstalled-,$(ALLUSES)) +else +announce-notinstalled-$(PKGROOT): +endif + @echo '***' Not currently installed from $(strip-target) '...' + + + +#### Check that a package is completely installed. + +checkinstall-$(CTPROJECT): checkinstall-$(PKGROOT) + +checkinstall-$(PACKAGE): checkinstall-$(PKGROOT) + +checkinstall-$(PKGROOT): $(addprefix checkinstall-,$(ALLINST)) + +ifneq (,$(ALLINST)) +$(addprefix checkinstall-,$(ALLINST)): announce-checkinstall-$(PKGROOT) + @if [ ! -r $(strip-target) ]; then exit 1; fi + +$(addprefix tocheckinstall-,$(ALLINST)): checkinstall-$(PKGROOT) +endif + +ifeq (,$(NODEPEND)) +announce-checkinstall-$(PKGROOT): $(addprefix tocheckinstall-,$(ALLUSES)) +else +announce-checkinstall-$(PKGROOT): +endif + @echo '***' Checking installation from $(strip-target) '...' + + + +#### Query files used to build a package. + +# To find files needed in project, query each package. +showuses-$(CTPROJECT): showuses-$(PKGROOT) + +# Convenient synonym for showuses. +showuses-$(PACKAGE): showuses-$(PKGROOT) + +showuses-$(PKGROOT): $(addprefix showuses-,$(ALLUSES)) + @echo '***' Files declared as needed to build $(strip-target) '...' + @if expr "$^" != "" > /dev/null; then echo $^ | sed -e 's/showuses-//g' | tr " " "\012"; fi + +ifneq (,$(NODEPEND)) + +ifneq (,$(ALLUSES)) +$(addprefix showuses-,$(ALLUSES)): + +endif + +else + +ifneq (,$(ALLINST)) +$(addprefix showuses-,$(ALLINST)): showuses-$(PKGROOT) + +endif + +endif + + + +#### Find package location given installed file, or package name. + +ifneq (,$(ALLINST)) +$(addprefix find-,$(ALLINST)): find-$(PKGROOT) +endif + +find-$(PKGROOT): + @echo $(strip-target) + +find-$(PACKAGE): find-$(PKGROOT) + + + +#### List packages that need this package in order to build successfully. + +# A package needs this package if it needs any of its files. +needs-$(PACKAGE): $(addprefix needs-,$(ALLINST)) + +# Define empty targets for needs-installed-files. Ensures successful +# completion. +ifneq (,$(ALLINST)) +$(addprefix needs-,$(ALLINST)): +endif + +# Needing the file mean the package has a prerequisite. +ifneq (,$(ALLUSES)) +$(addprefix needs-,$(ALLUSES)): prereq-$(PKGROOT) +endif + +# Print all packages with prerequisites triggered by original package. +# Possibly propagate prerequisites on to other packages. +ifeq (,$(NODEPEND)) +prereq-$(PKGROOT): $(addprefix needs-,$(ALLINST)) +else +prereq-$(PKGROOT): +endif + @echo $(strip-target) + + + +#### List packages used to build this package (opposite of above). + +depends-$(CTPROJECT): depends-$(PKGROOT) + +depends-$(PKGROOT): + +ifneq (,$(ALLINST)) +$(addprefix depends-,$(ALLINST)): requires-$(PKGROOT) +endif + +ifneq (,$(ALLUSES)) + +depends-$(PKGROOT): $(addprefix depends-,$(ALLUSES)) + +ifeq (,$(NODEPEND)) +requires-$(PKGROOT): $(addprefix depends-,$(ALLUSES)) +else +requires-$(PKGROOT): +endif + @echo $(strip-target) + +endif + +depends-$(PACKAGE): depends-$(PKGROOT) + + + +#### List packages that do not depend on other packages. + +nodepends-$(CTPROJECT): nodepends-$(PKGROOT) + +ifeq (,$(strip $(ALLUSES))) +nodepends-$(PKGROOT): + @echo $(strip-target) + +else +nodepends-$(PKGROOT): + +endif + + + +#### List files included by a package. + +# To clean project, clean each package. +includes-$(CTPROJECT): includes-$(PKGROOT) + +# Synonym to includes a particular package. +includes-$(PACKAGE): includes-$(PKGROOT) + +ifneq (,$(ALLINST)) +$(addprefix includes-,$(ALLINST)): includes-$(PKGROOT) +endif + +# Call ctshowdep on package +ifeq (,$(NODEPEND)) +includes-$(PKGROOT) : $(addprefix includes-,$(ALLUSES)) +else +includes-$(PKGROOT) : +endif + @echo '***' Files included by $(strip-target) '...' + -ctshowdep $(CTINC) $(strip-target) diff --git a/dtool/src/build/Makefile.install.template b/dtool/src/build/Makefile.install.template new file mode 100644 index 0000000000..724bf3521f --- /dev/null +++ b/dtool/src/build/Makefile.install.template @@ -0,0 +1,61 @@ +#### Installation makefile +# Note: This file is included by the project-wide Makefile so the current +# directory is the project root. Also, commented-out fields are optional. + +#### Package name and location (if not src/all/$(PACKAGE)): +#CTPACKAGE +PKGROOT = # Leave this blank if no change needed + +ifneq (,$(PACKAGE)) + +#### Package dependencies (USESOTHER needs relative paths from project root): +# USESLIBS = +# USESINCLUDE = +# USESOTHER = + +#### Installed files: +# LIBS = +# INCLUDE = +# BINS = +# SCRIPTS = +# SS = +# ST = +# STK = +# MODELS = +# ETC = +# DOC = +# MAN = +# TXT = +# HTML = +# PS = +# FONTS = +# ICONS = +# APPDEFAULTS = +# TCL = +# TELEUSE = +# SHADERS = + +#### Other files to be installed (use relative pathname from project root): +# OTHER = + +#### Where the action happens. +include $(DTOOL)/inc/Makefile.install.rules + +#### Install actions for OTHER files (source must be in $(PKGROOT)): +# [ installed file ] : $(PKGROOT)/[ source file ] # Files must have same name +# $(INSTALL) # Copies from source to dest +# +# [ installed file ] : $(PKGROOT)/[ source file ] +# $(MKINSTALL) # Also makes directory if needed + +#### Other install/uninstall actions: +# install-$(PKGROOT): #Add dependencies here +# Add actions here +# +# uninstall-$(PKGROOT): #Add dependencies here +# Add actions here + +#### Sub-package Makefile.install inclusions: +# include foo/Makefile.install + +endif diff --git a/dtool/src/build/Makefile.install.vars b/dtool/src/build/Makefile.install.vars new file mode 100644 index 0000000000..59e5d0dd28 --- /dev/null +++ b/dtool/src/build/Makefile.install.vars @@ -0,0 +1,25 @@ +# Standard variables for installation + +ifeq (,$(MAKEFILE_INSTALL_VARS)) +MAKEFILE_INSTALL_VARS = TRUE + +# This makes the variables available to sub-Makes. +ifeq (,$(MAKEFILE_PENV_VARS)) +include $(DTOOL)/inc/Makefile.penv.vars +endif + +ifeq (,$(MAKEFILE_PROJECT_VARS)) +include $(DTOOL)/inc/Makefile.project.vars +endif + +# Delete the standard suffixes +.SUFFIXES: + +# The following macros accomplish single file installation. The second +# will also create any necessary subdirectories. +INSTALL = @echo $@; rm -rf $@; cp -r $< $@; +MKINSTALL = @if [ ! -d $(dir $@) ]; then mkdir $(dir $@); fi; \ + echo $@; rm -rf $@; cp -r $< $@; +LNINSTALL = @echo $@; rm -rf $@; ln -s ../../$< $@; + +endif diff --git a/dtool/src/build/Makefile.meta.rules b/dtool/src/build/Makefile.meta.rules new file mode 100644 index 0000000000..b4307f1d0b --- /dev/null +++ b/dtool/src/build/Makefile.meta.rules @@ -0,0 +1,103 @@ +# Meta makefile rules. + +# Delete the default suffixes +.SUFFIXES: + +# Define macro to strip characters preceding '-' in the target variable ($*) +strip-target = `echo $@ | sed 's/^[^-]*-//'` + +ifeq (,$(CTPROJROOT)) +CTPROJROOT := $(shell $(DTOOL)/bin/ctproj -root) +endif +export CTPROJROOT + +# Make sure that CT_INCLUDE_PATH is set. +ifeq (,$(CT_INCLUDE_PATH)) +export CT_INCLUDE_PATH = . +endif + +# Make sure CT_LIBRARY_PATH is set. +ifeq (,$(CT_LIBRARY_PATH)) +ifneq (,$(LD_LIBRARY_PATH)) +export CT_LIBRARY_PATH := $(LD_LIBRARY_PATH) +else +export CT_LIBRARY_PATH = . +endif +endif + +# export the OPTIMIZE variable +ifneq (,$(OPTIMIZE)) +export OPTIMIZE +endif + +ifeq (,$(INSTALL)) +INSTALL := $(SUBMAKES) +endif + +all: $(SUBMAKES) + +install: $(INSTALL) + +ifeq (yes,$(HAVE_ATRIA)) +realinstall: + @cd `ctproj -r` ; clearmake -C gnu install-`ctproj -p` +else +realinstall: + @proj=`ctproj -p` ; cd `ctproj -r` ; $(MAKE) install-$$proj +endif + +ifneq (,$(SUBMAKES)) +.PHONY: $(SUBMAKES) +.PHONY: $(addprefix egg-,$(SUBMAKES)) +.PHONY: $(addprefix unopt-,$(SUBMAKES)) +.PHONY: $(addprefix pfb-,$(SUBMAKES)) +endif + +ifneq (,$(NUMBER_OF_PROCESSORS)) +# on NT, parallelize using NUMBER_OF_PROCESSORS env-var +PARALLEL_MAKE = -j $(NUMBER_OF_PROCESSORS) +endif + +$(SUBMAKES): + @echo '***' Executing $(MAKE) for sub-target $@... + @echo $(MAKE) $(PARALLEL_MAKE) -f $(MAKEDIR)/Makefile.$@ + @$(MAKE) $(PARALLEL_MAKE) -f $(MAKEDIR)/Makefile.$@ + +clean: $(addprefix clean-,$(SUBMAKES)) + +$(addprefix clean-,$(SUBMAKES)): + @echo '***' Executing $(MAKE) clean for sub-target $(strip-target)... + @$(MAKE) $(PARALLEL_MAKE) -f $(MAKEDIR)/Makefile.$(strip-target) clean + @rm -f *~ + +cleanall: $(addprefix cleanall-,$(SUBMAKES)) + +$(addprefix cleanall-,$(SUBMAKES)): + @echo '***' Executing $(MAKE) cleanall for sub-target $(strip-target)... + @$(MAKE) $(PARALLEL_MAKE) -f $(MAKEDIR)/Makefile.$(strip-target) cleanall + +list: + @echo $(SUBMAKES) + +# These are used in src/all/model subdirectories. + +egg: $(addprefix egg-,$(SUBMAKES)) +$(addprefix egg-,$(SUBMAKES)): + @echo '***' Executing $(MAKE) egg for sub-target $(strip-target)... + @$(MAKE) -f $(MAKEDIR)/Makefile.$(strip-target) egg + +unopt: $(addprefix unopt-,$(SUBMAKES)) +$(addprefix unopt-,$(SUBMAKES)): + @echo '***' Executing $(MAKE) unopt for sub-target $(strip-target)... + @$(MAKE) -f $(MAKEDIR)/Makefile.$(strip-target) unopt + +pfb: $(addprefix pfb-,$(SUBMAKES)) +$(addprefix pfb-,$(SUBMAKES)): + @echo '***' Executing $(MAKE) pfb for sub-target $(strip-target)... + @$(MAKE) -f $(MAKEDIR)/Makefile.$(strip-target) pfb + +uninstall: $(addprefix uninstall-,$(SUBMAKES)) +$(addprefix uninstall-,$(SUBMAKES)): + @echo '***' Executing $(MAKE) uninstall for sub-target $(strip-target)... + @$(MAKE) -f $(MAKEDIR)/Makefile.$(strip-target) uninstall + diff --git a/dtool/src/build/Makefile.meta.template b/dtool/src/build/Makefile.meta.template new file mode 100644 index 0000000000..88f9ca072a --- /dev/null +++ b/dtool/src/build/Makefile.meta.template @@ -0,0 +1,16 @@ +#### Meta Makefile. + +#### Sub make targets (extension of sub Makefile, eg 'foo' for Makefile.foo): +SUBMAKES = + +#### List the minimal set of sub makes on the list above required to install. +INSTALL = + +#### Location of sub Makefiles. +MAKEDIR = . + +#### The action is here. +include $(DTOOL)/inc/Makefile.meta.rules + +#### Sub-make build order dependencies: +# foo: bar diff --git a/dtool/src/build/Makefile.o.include b/dtool/src/build/Makefile.o.include new file mode 100644 index 0000000000..096542b365 --- /dev/null +++ b/dtool/src/build/Makefile.o.include @@ -0,0 +1,43 @@ +# Standard .o file conversion information. + +#### Pull in standard .o make variables +include $(DTOOL)/inc/Makefile.o.vars + +#### Lex files +# LFILES = +# LFLAGS = +# LEXTENSION = +# LSUBST = + +#### Yacc files +# YFILES = +# YFLAGS = +# YEXTENSION = +# YSUBST = + +#### C files +# CFILES = +# CFLAGS = + +#### C++ files +# C++FILES = +# C++FLAGS = +# USETEMPLATES = TRUE +# PTREPOSITORY = # Specify only if you want a specific name + +#### Interrogate info +# IGATESCAN = +# IGATEFLAGS = +# IGATEFILE = # Specify only if you want a specific name + +#### Additional search directories for C/C++ header files: +# IPATH := $(IPATH) -I/my/directory -I/my/other/directory + +#### Location to put .o files: +# ODIR = + +#### Source file dependencies (unnecessary with clearmake) +# foo.c: foo.h + +#### The .o action is here. +include $(DTOOL)/inc/Makefile.o.rules diff --git a/dtool/src/build/Makefile.o.rules b/dtool/src/build/Makefile.o.rules new file mode 100644 index 0000000000..67c8c15b8f --- /dev/null +++ b/dtool/src/build/Makefile.o.rules @@ -0,0 +1,358 @@ +# Makefile rules for making .o files from source files + +############################################################################# +# +# Yacc conversion rules. +# +# Files are converted to source form, then added to CFILES or C++FILES to +# be compiled into .o files. A file foo.y becomes foo.$(YEXTENSION) (see +# variable list below). If YEXTENSION contains a 'C' then the file is added +# to C++FILES, otherwise it goes to CFILES. A substitution string may be +# added to all 'yy' and 'YY' symbols in the yacc output so that multiple yacc +# parsers can be linked into one program. +# +# Variables: +# YFILES = list of .y files to be yacc'ed +# YEXTENSION = extension to put on yacc'ed files (default = tab.c) +# YFLAGS = command line flags to yacc +# +ifneq (,$(YFILES)) + +# Instead of adding the list of yacc-generated files to the CFILES or +# C++FILES lists, we must generate a new list that's compiled via a +# separate rule. This allows us to use different options (for +# instance, to eliminate optimization, which seems to trouble some +# compilers, particularly gcc). + +# We must then process YCFILES and YC++FILES before we process CFILES +# and C++FILES, so yacc will get called before any of the C files are +# compiled, in case one of the C files wants to include y.tab.h. + +ifneq (,$(findstring cxx,$(YEXTENSION))) + YC++FILES := $(patsubst %.yxx, %.$(YEXTENSION), $(YFILES)) +else + YCFILES := $(patsubst %.yxx, %.$(YEXTENSION), $(YFILES)) +endif + +$(YCFILES) $(YC++FILES) : %.$(YEXTENSION) : %.yxx + $(YACC) $(YFLAGS) $< + mv y.tab.c $@ + mv y.tab.h $(patsubst %.$(YEXTENSION), %.h, $@) + +clean:: + rm -f $(YCFILES) $(YC++FILES) + +endif + + + +############################################################################# +# +# Lex conversion rules. +# +# Files are converted to source form, then added to CFILES or C++FILES to +# be compiled into .o files. A file foo.l becomes foo.$(LEXTENSION) (see +# variable list below). If LEXTENSION contains a 'C' then the file is added +# to C++FILES, otherwise it goes to CFILES. A substitution string may be +# added to all 'yy' and 'YY' symbols in the lex output so that multiple lex +# analyzers can be linked into one program. +# +# Variables: +# LFILES = list of .l files to be lex'ed (from user Makefile) +# LEXTENSION = extension to put on lex'ed files (default = yy.c) +# LFLAGS = command line flags to lex +# +ifneq (,$(LFILES)) + +LCFILES := $(patsubst %.lxx, %.$(LEXTENSION), $(LFILES)) +ifneq (,$(findstring cxx,$(LEXTENSION))) +C++FILES := $(C++FILES) $(LCFILES) +else +CFILES := $(CFILES) $(LCFILES) +endif + +$(LCFILES) : %.$(LEXTENSION) : %.lxx + $(LEX) $(LFLAGS) $< + mv lex.yy.c $@ + +clean:: + rm -f $(LCFILES) + +endif + +############################################################################# +# +# .cpp -> .cxx conversion rules. +# + +ifneq (,$(CPPFILES)) + +CPPCFILES := $(patsubst %.cpp, %.cxx, $(CPPFILES)) +C++FILES := $(C++FILES) $(CPPCFILES) + +$(CPPCFILES) : %.cxx : %.cpp + cp $< $@ + chmod 666 $@ + +clean:: + rm -f $(CPPCFILES) + +endif + + + +############################################################################# +# +# C/C++ Conversion rules. +# + +# Clear ALLOFILES +ALLOFILES = +SUPERALLOFILES = + +ifneq (,$(YCFILES)) + SUPERALLOFILES += $(YCFILES:%.c=__%.o_file_find_me) +endif +ifneq (,$(YC++FILES)) + SUPERALLOFILES += $(YC++FILES:%.cxx=__%.o_file_find_me) +endif +ifneq (,$(CFILES)) + SUPERALLOFILES += $(CFILES:%.c=__%.o_file_find_me) +endif +ifneq (,$(C++FILES)) + SUPERALLOFILES += $(C++FILES:%.cxx=__%.o_file_find_me) +endif +ifeq (,$(NO_INTERROGATE)) + ifneq (,$(IGATESCAN)) + ifeq (,$(IGATEFILE)) + SUPERALLOFILES += __$(TARGET).o_file_find_me + else + SUPERALLOFILES += $(IGATEFILE:%.cxx=__%.o_file_find_me) + endif + endif +endif + +NTCFILETYPE = +NTC++FILETYPE = + +ifneq (,$(YCFILES)) + +ifeq (WIN32_VC,$(PENV_COMPILER)) + YCOFILES := $(YCFILES:%.c=$(ODIR)/%.obj) + NTCFILETYPE := -Tc + $(YCOFILES): $(ODIR)/%.obj : %.c +else # NON-WIN32_VC + YCOFILES := $(YCFILES:%.c=$(ODIR)/%.o) + $(YCOFILES): $(ODIR)/%.o : %.c +endif # WIN32_VC + @if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi + $(CC) -c $(IPATH) $(SYS_IPATH) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(CFLAGS) $(NTCFILETYPE) $< -o $@ + +ALLOFILES := $(ALLOFILES) $(YCOFILES) +endif + +ifneq (,$(YC++FILES)) + +ifeq (WIN32_VC,$(PENV_COMPILER)) + YC++OFILES := $(YC++FILES:%.cxx=$(ODIR)/%.obj) + NTC++FILETYPE := -Tp + $(YC++OFILES): $(ODIR)/%.obj : %.cxx +else # NON-WIN32_VC + YC++OFILES := $(YC++FILES:%.cxx=$(ODIR)/%.o) + $(YC++OFILES): $(ODIR)/%.o : %.cxx +endif # WIN32_VC + @if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi + $(C++) -c $(IPATH) $(SYS_IPATH) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $(NTC++FILETYPE) $< -o $@ + +ALLOFILES := $(ALLOFILES) $(YC++OFILES) +endif + +ifneq (,$(CFILES)) + +ifeq (WIN32_VC,$(PENV_COMPILER)) + COFILES := $(CFILES:%.c=$(ODIR)/%.obj) + NTCFILETYPE := -Tc + $(COFILES): $(ODIR)/%.obj : %.c +else # NON-WIN32_VC + COFILES := $(CFILES:%.c=$(ODIR)/%.o) + $(COFILES): $(ODIR)/%.o : %.c +endif # WIN32_VC + @if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi + $(CC) -c $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(CFLAGS) $(NTCFILETYPE) $< -o $@ + +ALLOFILES := $(ALLOFILES) $(COFILES) +endif + + +ifneq (,$(C++FILES)) + +ifeq (WIN32_VC,$(PENV_COMPILER)) + C++OFILES := $(C++FILES:%.cxx=$(ODIR)/%.obj) + NTC++FILETYPE := -Tp + $(C++OFILES): $(ODIR)/%.obj : %.cxx +else # NON-WIN32_VC + C++OFILES := $(C++FILES:%.cxx=$(ODIR)/%.o) + $(C++OFILES): $(ODIR)/%.o : %.cxx +endif # WIN32_VC + @if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi +ifeq (OSX,$(PENV)) + $(C++) -c $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $(NTC++FILETYPE) -DFILE_SYM_NAME=__$(subst .,_,$@)_file_find_me -DALL_FILE_SYMS_NAME="\"$(subst .,_,$(SUPERALLOFILES))\"" $< -o $@ +else # OSX + $(C++) -c $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $(NTC++FILETYPE) $< -o $@ +endif # OSX + +ALLOFILES := $(ALLOFILES) $(C++OFILES) +endif + +ifneq (,$(NO_INTERROGATE)) + IGATESCAN := + IGATEMSCAN := +endif + +ifeq (,$(IGATELIBRARY)) +IGATELIBRARY := $(basename $(BASETARGET)) +endif + +ifneq (,$(IGATESCAN)) + +ifeq (,$(IGATEFILE)) +IGATEFILE := $(BASETARGET).cxx +endif + +ifeq (,$(IGATEMODULE)) +IGATEMODULE := $(DEFERRED_TARGET) +endif +ifeq (,$(IGATEMODULE)) +IGATEMODULE := $(IGATELIBRARY) +endif + +ifeq (,$(IGATEDBFILE)) +IGATEDBFILE := $(basename $(BASETARGET)).in +endif + +ifeq (WIN32_VC,$(PENV_COMPILER)) + IGATEOFILE := $(IGATEFILE:%.cxx=$(ODIR)/%.obj) + NTC++FILETYPE := -Tp +else # NON-WIN32_VC + IGATEOFILE := $(IGATEFILE:%.cxx=$(ODIR)/%.o) +endif + +IGATENFILES := $(wildcard $(IGATESCAN:%.h=%.N)) +$(IGATEFILE): $(IGATESCAN) $(IGATENFILES) + $(INTERROGATE) $(IGATESYSPATH) $(IPATH) $(IGATEFLAGS) $(COMPILE_DEFS) -module "$(IGATEMODULE)" -library "$(IGATELIBRARY)" -oc $@ -od $(IGATEDBFILE) -fnames -string -refcount -assert -promiscuous -python $(IGATESCAN) +$(IGATEOFILE): $(IGATEFILE) + @if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi +ifeq (OSX,$(PENV)) + $(C++) -c $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $(NTC++FILETYPE) -DFILE_SYM_NAME=__$(subst .,_,$@)_file_find_me -DALL_FILE_SYMS_NAME="\"$(subst .,_,$(SUPERALLOFILES))\"" $< -o $@ +else # OSX + $(C++) -c $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $(NTC++FILETYPE) $< -o $@ +endif # OSX +ALLOFILES := $(ALLOFILES) $(IGATEOFILE) + +clean:: + rm -f $(IGATEOFILE) $(IGATEFILE) $(IGATEDBFILE) + +endif + +ifneq (,$(IGATEMSCAN)) +IGATEMC := $(basename $(BASETARGET)).module.cxx +ifeq (WIN32_VC,$(PENV_COMPILER)) + IGATEMO := $(IGATEMC:%.cxx=%.obj) +else + IGATEMO := $(IGATEMC:%.cxx=%.o) +endif +$(IGATEMC): $(wildcard $(CTPROJROOT)/etc/*.in) + interrogate_module -oc $@ -module "$(IGATEMSCAN)" -library "$(IGATELIBRARY)" -python $(CTPROJROOT)/etc/*.in + +$(IGATEMO): $(IGATEMC) + @if [ ! -d $(ODIR) ]; then mkdir $(ODIR); fi +ifeq (OSX,$(PENV)) + $(C++) -c $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $(NTC++FILETYPE) -DFILE_SYM_NAME=__$(subst .,_,$@)_file_find_me -DALL_FILE_SYMS_NAME="\"$(subst .,_,$(SUPERALLOFILES))\"" $< -o $@ +else # OSX + $(C++) -c $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $(NTC++FILETYPE) $< -o $@ +endif # OSX +ALLOFILES := $(ALLOFILES) $(IGATEMO) + +clean:: + rm -f $(IGATEMO) $(IGATEMC) +endif # IGATEMSCAN + +ifeq (,$(USE_DEFERRED)) + DEFERRED_FILES := +endif + +ifneq (,$(DEFERRED_FILES)) +include $(wildcard $(patsubst %,$(CTPROJROOT)/deferred/Deferred.%.*,$(DEFERRED_FILES))) +endif + + +ifneq (,$(ALLOFILES)) +clean:: + rm -f $(ALLOFILES) +endif + + +$(TARGET): $(ALLOFILES) + +ifeq (Linux,$(PENV)) +DO_GNU_AUTO_DEP = yes +endif + +ifneq (,$(DO_GNU_AUTO_DEP)) + +############################################################################# +# +# GNU Auto-dependency rules. +# + +%.d: %.c + $(SHELL) -ec '$(CC) -M $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(CFLAGS) $< \ + | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \ + [ -s $@ ] || rm -f $@' + +%.d: %.cxx + $(SHELL) -ec '$(C++) -M $(IPATH) $(SYS_IPATH) $(OPTIMIZE_FLAGS) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $< \ + | sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \ + [ -s $@ ] || rm -f $@' + +ifneq (,$(IGATEOFILE)) +include $(IGATEOFILE:.o=.d) + +$(IGATEOFILE:.o=.d) : $(IGATEOFILE:.o=.cxx) + $(SHELL) -ec '$(C++) -M $(IPATH) $(SYS_IPATH) $(COMPILE_FLAGS) $(COMPILE_DEFS) $(C++FLAGS) $< \ + | sed '\''s/\($*\)\.o[ :]*$ $@; \ + [ -s $@ ] || rm -f $@' + +clean:: + rm -f $(IGATEOFILE:.o=.d) +endif + +ifneq (,$(YCFILES)) +include $(YCFILES:.c=.d) + +clean:: + rm -f $(YCFILES:.c=.d) +endif + +ifneq (,$(YC++FILES)) +include $(YC++FILES:.cxx=.d) + +clean:: + rm -f $(YC++FILES:.cxx=.d) +endif + +ifneq (,$(CFILES)) +include $(CFILES:.c=.d) + +clean:: + rm -f $(CFILES:.c=.d) +endif + +ifneq (,$(C++FILES)) +include $(C++FILES:.cxx=.d) + +clean:: + rm -f $(C++FILES:.cxx=.d) +endif + +endif # (,$(DO_GNU_AUTO_DEP)) diff --git a/dtool/src/build/Makefile.o.template b/dtool/src/build/Makefile.o.template new file mode 100644 index 0000000000..0263305dd6 --- /dev/null +++ b/dtool/src/build/Makefile.o.template @@ -0,0 +1,11 @@ +# Makefile for a collection of .o files + +#### State the target. No need to change this. +#TARGET = ofiles +#CTTARGET + +#CTINCLUDE $DTOOL/lib/Makefile.o.include + +#### Add a cleanall target +cleanall: clean + rm -rf $(PTREPOSITORY) diff --git a/dtool/src/build/Makefile.o.vars b/dtool/src/build/Makefile.o.vars new file mode 100644 index 0000000000..00d99ee8ed --- /dev/null +++ b/dtool/src/build/Makefile.o.vars @@ -0,0 +1,354 @@ +# Standard variable for creation of .o files and things that use .o +# files (lib, bin). + +ifeq (,$(MAKEFILE_O_VARS)) +MAKEFILE_O_VARS = TRUE + +ifeq (,$(MAKEFILE_PENV_VARS)) + include $(DTOOL)/inc/Makefile.penv.vars +endif + +# First, examine the OPTIMIZE variable. Make sure it is set to one +# of the allowed values. +ifeq (1,$(OPTIMIZE)) + # Optimize 1 : no compiler optimizations, full debugging symbols. + DEBUGSYMBOLS = yes +else + ifeq (2,$(OPTIMIZE)) + # Optimize 2 : full compiler optimizations, full debugging symbols. + DEBUGSYMBOLS = yes + else + ifeq (3,$(OPTIMIZE)) + # Optimize 3 : full compiler optimizations, no debugging symbols. + else + ifeq (4,$(OPTIMIZE)) + # Optimize 4 : full compiler optimizations, no debugging symbols, + # and assertion tests eliminated via NDEBUG. + else + ifeq (,$(OPTIMIZE)) + # OPTIMIZE unspecified: assume Optimize 1. + OPTIMIZE = 1 + DEBUGSYMBOLS = yes + else + # OPTIMIZE is none of the above: assume Optimize 4. + OPTIMIZE = 4 + endif # old opt + endif # opt4 + endif # opt3 + endif # opt2 +endif # opt1 + +BASETARGET := $(TARGET) +DEBUGNAME := + +ifeq (WIN32,$(PENV)) + # If we are compiling with symbols, we rename the target to target_d. + # This helps us keep debug vs. nondebug libraries separate, and also + # helps us play along with Python's importing conventions. But it's + # a big mess here in the makefiles. + + ifeq (yes,$(DEBUGSYMBOLS)) + DEBUGNAME := _d + endif + + ifneq (,$(findstring .so,$(suffix $(TARGET)))) + TARGET := $(TARGET:%.so=%$(DEBUGNAME).dll) + else + ifneq (,$(findstring .a,$(suffix $(TARGET)))) + TARGET := $(TARGET:%.a=%$(DEBUGNAME).lib) + else + TARGET := $(TARGET:%=%.exe) + endif # .a + endif # .so +endif # WIN32 +ifeq (OSX,$(PENV)) + ifneq (,$(findstring .so,$(suffix $(TARGET)))) + TARGET := $(TARGET:%.so=%.dylib) + endif # .so +endif # OSX + +IGATESYSPATH = -S$(DTOOL)/inc/parser-inc -DCPPPARSER -D__cplusplus $(SYSTEM_IGATE_FLAGS) + +# Delete the default suffixes +.SUFFIXES: + +# Make sure that CT_INCLUDE_PATH is set. +ifeq (,$(CT_INCLUDE_PATH)) + export CT_INCLUDE_PATH = . +endif + +IPATH_DIRS := $(strip $(shell ctpathadjust $(CT_INCLUDE_PATH))) $(patsubst -I%,%,$(IPATH)) + +# Add one more for dtool_config.h +IPATH_DIRS += $(DTOOL) + +# Convert CT_INCLUDE_PATH to cc/CC compatible flag string +ifeq (WIN32_VC,$(PENV_COMPILER)) + IPATH_DIRS := $(foreach path,$(IPATH_DIRS),$(shell cygpath -w $(path))) + SYS_IPATH := -I"$(shell cygpath -w /msvc98/Include)" +endif + +IPATH := $(patsubst %,-I"%",$(IPATH_DIRS)) + +##### Playstation2 can't handle .so's. The rename to .a's is handled here +##### and acted upon in Makefile.so.rules. (CSN) + +ifeq (PS2, $(PENV)) + IPATH += -I/for/program/sce/ee/include + + # Uncommenting this line will make the ps2 look for files on the dvd + # instead of on the host dev machine. + + PS2_FILE_PREFIX := #### PS2_DVD_FILES + + ifneq (,$(findstring .so,$(suffix $(TARGET)))) + TARGET := $(TARGET:%.so=%.a) + endif +endif + +##### End ps2 + +COMPILE_FLAGS = +COMPILE_DEFS = +OPTIMIZE_FLAGS = + +ifeq (SGI,$(PENV)) + # Standard applications and flags. + CC = cc $(SGI_BINFORMAT_CC) + C++FLAGS = -fullwarn $(WOFF_LIST) + + # MIPSPro compiler cannot simultaneously optimize and encode + # debugging symbols. + + ifeq (1,$(OPTIMIZE)) + COMPILE_FLAGS = -g + else + ifeq (2,$(OPTIMIZE)) + COMPILE_FLAGS = + OPTIMIZE_FLAGS = -O2 -OPT:Olimit=2500 + else + ifeq (3,$(OPTIMIZE)) + COMPILE_DEFS = -DOPTIMIZE + OPTIMIZE_FLAGS = -O2 -OPT:Olimit=2500 + else + ifeq (4,$(OPTIMIZE)) + COMPILE_DEFS = -DOPTIMIZE -DNDEBUG + OPTIMIZE_FLAGS = -O2 -OPT:Olimit=2500 + endif # opt4 + endif # opt3 + endif # opt2 + endif # opt1 + + C++ = CC $(SGI_BINFORMAT_CC) + + ifeq ($(SGI_BINFORMAT),o32) + + # These are the warning numbers according to the o32 compiler + # 3317 - controlling expression is constant, e.g. if (0) { ... } + # 3262 - function 'blah' was declared but never used + WOFF_LIST = -woff 3317,3262 + + else # ($(SGI_BINFORMAT),n32) + + # These are the warning numbers according to the n32 compiler + # 1174 - function 'blah' was declared but never used + # 1201 - trailing comma is nonstandard. + # 1209 - controlling expression is constant, e.g. if (0) { ... } + # 1234 - access control not specified, 'public' by default + # 1355 - extra ";" ignored + # 1375 - destructor for base class is not virtual. + # this one actually is bad. But we got alot of them from the classes + # that we've derived from STL collections. Beware of this. + # 3322 - omission of explicit type is nonstandard ("int" assumed) + WOFF_LIST = -woff 1174,1201,1209,1234,1355,1375,3322 + + # Linker warnings + # 85 - definition of SOMESYMBOL in SOMELIB preempts that of definition in + # SOMEOTHERLIB. + WOFF_LIST += -Wl,-LD_MSG:off=85 + + # We'll also woff this one if we are compiling DELIVERY_PLAYER, which + # leaves out large chunks of code. + # 3201 - parameter was never referenced. + ifneq (,$(DELIVERY_PLAYER)) + WOFF_LIST += -woff 3201 + endif + endif # $(SGI_BINFORMAT) + + CFLAGS += -DPENV_SGI + C++FLAGS += -DPENV_SGI +endif # (SGI,$(PENV)) + +ifeq (OSX,$(PENV)) + # Standard applications and flags + CC = cc + + ifeq (1,$(OPTIMIZE)) + COMPILE_FLAGS = -g + else + ifeq (2,$(OPTIMIZE)) + COMPILE_FLAGS = -g + OPTIMIZE_FLAGS = -O2 + else + ifeq (3,$(OPTIMIZE)) + COMPILE_FLAGS = -g + COMPILE_DEFS = -DOPTIMIZE + OPTIMIZE_FLAGS = -O2 + else + ifeq (4,$(OPTIMIZE)) + COMPILE_DEFS = -DOPTIMIZE -DNDEBUG + OPTIMIZE_FLAGS = -O2 + endif # (4,$(OPTIMIZE)) + endif # (3,$(OPTIMIZE)) + endif # (2,$(OTPIMIZE)) + endif # (1,$(OPTIMIZE)) + + C++ = cc + TMPTARGETTHING := __$(BASETARGET:%.so=%_so)_find_me__ + CFLAGS += -DPENV_OSX -DLIB_SYMBOL_NAME=$(TMPTARGETTHING) + C++FLAGS += -DPENV_OSX -DLIB_SYMBOL_NAME=$(TMPTARGETTHING) -ftemplate-depth-30 +endif # (OSX,$(PENV)) + +ifeq (Linux,$(PENV)) + # Standard applications and flags. + CC = gcc + + ifeq (1,$(OPTIMIZE)) + COMPILE_FLAGS = -g + else + ifeq (2,$(OPTIMIZE)) + COMPILE_FLAGS = -g + OPTIMIZE_FLAGS = -O2 + else + ifeq (3,$(OPTIMIZE)) + COMPILE_FLAGS = -g + COMPILE_DEFS = -DOPTIMIZE + OPTIMIZE_FLAGS = -O2 + else + ifeq (4,$(OPTIMIZE)) + COMPILE_DEFS = -DOPTIMIZE -DNDEBUG + OPTIMIZE_FLAGS = -O2 + endif # opt4 + endif # opt3 + endif # opt2 + endif # opt1 + + C++ = g++ + + CFLAGS += -DPENV_LINUX + C++FLAGS += -DPENV_LINUX -ftemplate-depth-30 +endif # $(Linux,$(PENV)) + +ifeq (PS2,$(PENV)) + # Playstation2 apps and flags. + + CC = ee-gcc + C++ = ee-g++ + + ifeq (1,$(OPTIMIZE)) + COMPILE_FLAGS = -g + else + ifeq (2,$(OPTIMIZE)) + COMPILE_FLAGS = -g + OPTIMIZE_FLAGS = -O2 + else + ifeq (3,$(OPTIMIZE)) + COMPILE_FLAGS = -g + COMPILE_DEFS = -DOPTIMIZE + OPTIMIZE_FLAGS = -O2 + else + ifeq (4,$(OPTIMIZE)) + COMPILE_DEFS = -DOPTIMIZE -DNDEBUG + OPTIMIZE_FLAGS = -O2 + endif # opt4 + endif # opt3 + endif # opt2 + endif # opt1 + + CFLAGS += -DPENV_PS2 + C++FLAGS += -DPENV_PS2 -ftemplate-depth-30 + +endif # $(PS2,$(PENV)) + +ifeq (WIN32,$(PENV)) + # Standard applications and flags. + # Note: You must also set LDFLAGS and LINKFLAGS in Makefile.bin.vars + CC = cl + C++ = cl + + # tell cl.exe to generate .sbr files, which bscmake can compile into Msdev source browser info + ifeq (1,$(CL_MAKE_BROWSE_INFO)) + BROWSEINFO_FLAG := -Fr + endif + + ifeq (1,$(OPTIMIZE)) + # -Fd is change to write out per-obj .pdb, to allow parallelization + COMPILE_FLAGS = -MDd -GZ -Zi $(BROWSEINFO_FLAG) -Fd$(basename $@) + COMPILE_DEFS = -D_DEBUG + LDTYPE := -DLL -DEBUG + else + ifeq (2,$(OPTIMIZE)) + COMPILE_FLAGS = -MDd -Zi -Fd$(basename $@) + COMPILE_DEFS = -D_DEBUG + OPTIMIZE_FLAGS = -O2 -Ob1 -Ogity -G6 + LDTYPE := -DLL -DEBUG + else + ifeq (3,$(OPTIMIZE)) + COMPILE_FLAGS = -MD + COMPILE_DEFS = -DOPTIMIZE + OPTIMIZE_FLAGS = -O2 -Ob1 -Ogity -G6 -Gi- + LDTYPE := -DLL + else + ifeq (4,$(OPTIMIZE)) + COMPILE_FLAGS = -MD + COMPILE_DEFS = -DOPTIMIZE -DNDEBUG + OPTIMIZE_FLAGS = -O2 -Ob1 -Ogity -G6 -Gi- + LDTYPE := -DLL + endif # opt4 + endif # opt3 + endif # opt2 + endif # opt1 + + # removed -FD (generate file-depedency vc60.idb files) to allow parallelization + CFLAGS += -nologo -W3 -EHsc -Zm250 -D_WINDOWS -DWIN32 -D_WINDLL -DSTRICT -DPENV_WIN32 -DWIN32_VC + C++FLAGS += -nologo -W3 -EHsc -Zm250 -D_WINDOWS -DWIN32 -D_WINDLL -DSTRICT -DPENV_WIN32 -DWIN32_VC + +endif # $(WIN32,$(PENV)) + +ifeq (SunOS,$(PENV)) + # Standard applications and flags. + CC = egcc + + ifeq (1,$(OPTIMIZE)) + OPTIMIZE_FLAGS = -g + else + ifeq (2,$(OPTIMIZE)) + OPTIMIZE_FLAGS = -O2 -g + else + ifeq (3,$(OPTIMIZE)) + OPTIMIZE_FLAGS = -O2 -g -DOPTIMIZE -DNDEBUG + else + ifeq (4,$(OPTIMIZE)) + OPTIMIZE_FLAGS = -O2 -DOPTIMIZE -DNDEBUG + endif # opt4 + endif # opt3 + endif # opt2 + endif # opt1 + + C++ = eg++-new -DUsePthreads + + CFLAGS += -DPENV_SunOS + C++FLAGS += -DPENV_SunOS +endif # $(SunOS,$(PENV)) + +INTERROGATE = interrogate +IGATEFLAGS = + + +ODIR = . + +# Set the default target +install all: $(TARGET) + +endif # MAKEFILE_O_VARS + diff --git a/dtool/src/build/Makefile.penv.vars b/dtool/src/build/Makefile.penv.vars new file mode 100644 index 0000000000..ab5a2926e6 --- /dev/null +++ b/dtool/src/build/Makefile.penv.vars @@ -0,0 +1,57 @@ +# Variable(s) to declare the project compilation and execution environment. + +ifeq (,$(MAKEFILE_PENV_VARS)) +MAKEFILE_PENV_VARS = TRUE + +ifeq (,$(PENV)) +PENV = SGI +endif + +ifeq (SGI,$(PENV)) + # Defines the binary format (o32 vs. n32) for compiling. + + # This is the default binformat if it is not overridden in a sub-Makefile. + DEFAULT_BINFORMAT = n32 + + ifeq ($(SGI_BINFORMAT),) + SGI_BINFORMAT = $(DEFAULT_BINFORMAT) + endif + + ifeq ($(SGI_BINFORMAT),o32) + SGI_BINFORMAT_CC = -mips2 -o32 + SGI_BINFORMAT_SALIVATE = -o32 + SGI_BINFORMAT_LIB = lib + else + SGI_BINFORMAT_CC = -mips3 -n32 + SGI_BINFORMAT_SALIVATE = -n32 + SGI_BINFORMAT_LIB = lib32 + endif + + SYSTEM_IGATE_FLAGS = -D__mips__ -D__MIPSEB__ -DPENV_SGI +endif + +ifeq (OSX,$(PENV)) + SYSTEM_IGATE_FLAGS = +endif + +ifeq (Linux,$(PENV)) + + SYSTEM_IGATE_FLAGS = -D__i386__ -DPENV_LINUX + +endif + +ifeq (WIN32,$(PENV)) + + # Guarantee that PENV_COMPILER gets set to default to VC + PENV_COMPILER = WIN32_VC + + SYSTEM_IGATE_FLAGS = -longlong __int64 -D_X86_ -DPENV_WIN32 -DWIN32_VC -D"_declspec(param)=" + + # Windows is always a deferred build. + USE_DEFERRED := yes + +endif # WIN32 + + +endif # MAKEFILE_PENV_VARS + diff --git a/dtool/src/build/Makefile.project.template b/dtool/src/build/Makefile.project.template new file mode 100644 index 0000000000..a90fe8fc45 --- /dev/null +++ b/dtool/src/build/Makefile.project.template @@ -0,0 +1,18 @@ +# Makefile for project. +# Should live at project root directory, and be called Makefile. + +# Do not delete the line below. It tells ctaddpkg to automatically +# append install Makefiles when packages are created. +# CT-STANDARD-PROJECT-MAKEFILE + +# Specify project name and project root directory. +#CTPROJECT +#CTPROJROOT + +include $(DTOOL)/inc/Makefile.project.vars + +# Add packages to end of file as follows: +# include src/all/PACKAGE/Makefile.install +# ... or whatever path to install Makefile + +#CTINSTPKG diff --git a/dtool/src/build/Makefile.project.vars b/dtool/src/build/Makefile.project.vars new file mode 100644 index 0000000000..98d7aca5e4 --- /dev/null +++ b/dtool/src/build/Makefile.project.vars @@ -0,0 +1,68 @@ +# Standard variables and targets for projects. + +ifeq (,$(MAKEFILE_PROJECT_VARS)) +MAKEFILE_PROJECT_VARS = TRUE + +# Guarantee these are all empty (we don't want a conflict with any env vars) +LIBS := +INCLUDE := +BINS := +SCRIPTS := +SS := +STK := +MODELS := +ETC := +DOC := +MAN := +FONTS := +ICONS := +APPDEFAULTS := +TCL := +TELEUSE := +SHADERS := + +ifeq (,$(MAKEFILE_PENV_VARS)) +include $(DTOOL)/inc/Makefile.penv.vars +endif + +ifeq (,$(CTPROJECT)) +CTPROJECT = project +endif + +ifeq (,$(CTPROJROOT)) +CTPROJROOT := $(shell $(DTOOL)/bin/ctproj -r) +endif + +ifneq (,$(OPTIMIZE)) +export OPTIMIZE +endif + +$(CTPROJECT): install + +install: install-$(CTPROJECT) + +uninstall: uninstall-$(CTPROJECT) + +clean: clean-$(CTPROJECT) + +cleanall: cleanall-$(CTPROJECT) + +touch: touch-$(CTPROJECT) + +copy: copy-$(CTPROJECT) + +isinstalled: isinstalled-$(CTPROJECT) + +notinstalled: notinstalled-$(CTPROJECT) + +checkinstall: checkinstall-$(CTPROJECT) + +toinstall: toinstall-$(CTPROJECT) + +depends: depends-$(CTPROJECT) + +nodepends: nodepends-$(CTPROJECT) + +showuses: showuses-$(CTPROJECT) + +endif # MAKEFILE_PROJECT_VARS diff --git a/dtool/src/build/Makefile.so.rules b/dtool/src/build/Makefile.so.rules new file mode 100644 index 0000000000..a469eda8e7 --- /dev/null +++ b/dtool/src/build/Makefile.so.rules @@ -0,0 +1,158 @@ +# Rules to construct .so's from component files. + +ifneq (,$(SOLOCATIONS)) +.PRECIOUS: $(SOLOCATIONS) + ifeq (SGI,$(PENV)) + ifeq (ld,$(LD)) + SLOC := -update_registry $(SOLOCATIONS) + else + SLOC := -Wl,-update_registry,$(SOLOCATIONS) + endif + endif # (SGI,$(PENV)) +endif # $(SOLOCATIONS) + + +ifeq (SGI,$(PENV)) + ifeq (ld,$(LD)) + NONE = -none + else + NONE = -Wl,-none + endif + + DEFINE_SONAME = -soname $@ +endif # (SGI,$(PENV)) + +ifeq (SunOS,$(PENV)) + SHARED_COMMAND = -G +else + ifeq (OSX,$(PENV)) + SHARED_COMMAND = -dynamiclib + else + SHARED_COMMAND = -shared + endif +endif # (SunOS,$(PENV)) + +ifeq (OSX,$(PENV)) +ALL = +else +ALL = -all +endif # (OSX,$(PENV)) +BASEFILE = + +ALLOFILES := $(ALLOFILES) $(OFILES) + +ifeq (,$(USE_DEFERRED)) + LIBS := $(WHEN_NO_DEFER_LIBS) $(LIBS) +else + LIBS := $(WHEN_DEFER_LIBS) $(LIBS) +endif + +ifeq (,$(USE_DEFERRED)) + SHOULD_DEFER_TARGET := +else + SHOULD_DEFER_TARGET := $(DEFERRED_TARGET) +endif + +ifneq (,$(SHOULD_DEFER_TARGET)) + + # If the makefile requests a "deferred target", it means that we'll + # just write out a mini-makefile that defines the .o files that go + # into the target, rather than actually building the target now. + # Another Makefile later will presumably do the actual building. + + DEFERRED_MAKEFILE := Deferred.$(DEFERRED_TARGET).$(BASETARGET) + THISDIR := $(notdir $(shell pwd)) + +$(TARGET) : $(DEFERRED_MAKEFILE) +$(DEFERRED_MAKEFILE): $(wildcard Makefile.*) + rm -f $@ + echo ALLOFILES += $(patsubst ./%,../../src/$(THISDIR)/%,$(ALLOFILES)) > $@ + +else # DEFERRED_TARGET + + ifneq (,$(strip $(ALLOFILES))) + + # This is less than ideal. This means that the .so file doesn't get + # added to the configuration record, and hence gets none of the + # clearmake magic like derived-object status and winking in. + # However, it also means that clearmake doesn't get tricked by the + # so_locations file, so it seems to be a better choice than the + # alternative for the moment. + .NO_CONFIG_REC: $(TARGET) + + TARGETLIB = + BASELIB = + DEFLIB = + EXPLIB = + PDBLIB = + LIBLIB = + VCFILES = + + # Eliminate -lm and -ldl on NT because they don't exist + WINLIBS := $(filter-out -lm -ldl,$(SYSLIBS)) + # On NT -l needs to map to lib.lib + WINLIBS := $(LIBS:-l%=lib%$(DEBUGNAME).lib) $(WINLIBS:-l%=lib%.lib) + # We also need to handle any explicitly listed .a libraries + WINLIBS := $(WINLIBS:%.a=%.lib) + ifeq (WIN32_VC,$(PENV_COMPILER)) + PDBLIB := $(TARGET:%.dll=%.pdb) + EXPLIB := $(TARGET:%.dll=%.exp) + LIBLIB := $(TARGET:%.dll=%.lib) + VCFILES := vc60.idb vc60.pdb + endif # WIN32_VC + + $(TARGET): $(OFILES) # *not* ALLOFILES + + #### The PlayStation2 doesn't do .so files AT ALL. + #### Solution: Every time panda/tool wants a .so, we make a .a + + ifeq (PS2, $(PENV)) + ee-ar -r a.out.$@ $(ALLOFILES) + else + ifeq (WIN32,$(PENV)) + ifeq (WIN32_VC,$(PENV_COMPILER)) + $(LD) $(LDFLAGS) $(LDTYPE) \ + $(LPATH) $(LINKFLAGS) $(ALLOFILES) $(WINLIBS) -OUT:$@ + endif # PENV_COMPILER + else # NON-WIN32 + ifeq (OSX,$(PENV)) + $(LD) $(SHARED_COMMAND) $(PTR) $(PTFLAGS) $(SLOC) $(LDFLAGS) \ + $(LPATH) $(ALL) $(ALLOFILES) \ + $(NONE) $(LIBS) $(SYSLIBS) -o $@ $(DEFINE_SONAME) \ + $(BASEFILE) $(BASELIB) -lstdc++ + else # non-win32 and non-OSX + $(LD) $(SHARED_COMMAND) $(PTR) $(PTFLAGS) $(SLOC) $(LDFLAGS) \ + $(LPATH) $(ALL) $(ALLOFILES) \ + $(NONE) $(LIBS) $(SYSLIBS) -o a.out.$@ $(DEFINE_SONAME) \ + $(BASEFILE) $(BASELIB) + endif # non-win32 and non-OSX + endif # WIN32 + endif # PS2 + + ifneq (WIN32,$(PENV)) + ifneq (OSX,$(PENV)) + mv a.out.$@ $@ + endif # non-win32 and non-OSX + ifneq (SGI,$(PENV)) + ifeq (4,$(OPTIMIZE)) + strip $@ + endif # (4,$(OPTIMIZE)) + endif # (SGI,$(PENV)) + endif + + endif # (,$(ALLOFILES)) + +endif # DEFERRED_TARGET + +clean:: + @rm -f $(TARGET) $(TARGETLIB) $(BASELIB) $(DEFLIB) $(EXPLIB) \ + $(WINLIBS) $(PDBLIB) $(LIBLIB) $(VCFILES) + +cleanall: clean + rm -fr ii_files + rm -f $(TARGET) $(TARGETLIB) $(BASELIB) $(DEFLIB) $(EXPLIB) \ + $(WINLIBS) $(PDBLIB) $(LIBLIB) $(VCFILES) + +list: + @echo $(TARGET) $(TARGETLIB) $(BASELIB) $(DEFLIB) $(EXPLIB) \ + $(WINLIBS) $(PDBLIB) $(LIBLIB) diff --git a/dtool/src/build/Makefile.so.template b/dtool/src/build/Makefile.so.template new file mode 100644 index 0000000000..27af5d18ab --- /dev/null +++ b/dtool/src/build/Makefile.so.template @@ -0,0 +1,22 @@ +#### Makefile for DSO's. Any fields commented out are optional. + +#### Target's name: +#CTTARGET + +#CTINCLUDE $DTOOL/lib/Makefile.o.include + +#### Pull in standard binary make variables. +include $(DTOOL)/inc/Makefile.bin.vars + +#### Other files and lib. Include $(ODIR) in any .o names. +# OFILES = +# LIBS = + +#### Additional search directories for lib: +# LPATH := $(LPATH) -L/my/directory -L/my/other/directory + +#### Other linker flags. +# LDFLAGS = + +#### The .so action is here. +include $(DTOOL)/inc/Makefile.so.rules diff --git a/dtool/src/build/Makefile.ss.rules b/dtool/src/build/Makefile.ss.rules new file mode 100644 index 0000000000..ef466c00a3 --- /dev/null +++ b/dtool/src/build/Makefile.ss.rules @@ -0,0 +1,55 @@ +# Rules to construct .ss's from component files. + +############################################################################## +# +# .h -> .ss conversion rules +# + +# Clear ALLSSFILES +ALLSSFILES = + +ifneq (,$(HFILES)) +HSSFILES := $(HFILES:%.h=%.ss) +$(HSSFILES) : %.ss : %.h + $(SALIVATE) $(SALIVATE-FLAGS) $< > $@ +ALLSSFILES := $(ALLSSFILES) $(HSSFILES) +endif + +ifneq (,$(HHFILES)) +HHSSFILES := $(HHFILES:%.hh=%.ss) +$(HHSSFILES) : %.ss : %.hh + @ cat $< > $(sstotmp) + @ for i in ${HHDIRS} ; do if test -r $$i/$(sstoh) ; then \ + cat $$i/$(sstoh) >> $(sstotmp) ; fi ; done + $(SALIVATE) $(SALIVATE-FLAGS) $(sstotmp) > $@ + @ rm -f $(sstotmp) +ALLSSFILES := $(ALLSSFILES) $(HHSSFILES) +endif + +TOTALSSFILES := $(ALLSSFILES) $(OTHERSSFILES) + +LIBTARGET := $(addprefix lib,$(addsuffix .so,$(basename $(TARGET)))) + +ifneq (,$(TOTALSSFILES)) +$(TARGET): $(TOTALSSFILES) + @ echo -n "**** Creating Meta-ss file "$(TARGET)" ... " + @ echo '(load-shared-object "'$(LIBTARGET)'")' > $@ +ifeq (,$(STRAIGHTINCLUDE)) + @ echo '(define dl-handle (dlopen "'$(LIBTARGET)'" 2))' >> $@ + @ for i in ${TOTALSSFILES} ; do echo ' ('load-once '"'$$i'")' >> $@ ; done +else + @ for i in ${TOTALSSFILES} ; do echo ' ('load-once '"'$$i'")' >> $@ ; done +endif + @ echo "done" +endif + +ifneq (,$(ALLSSFILES)) +clean:: + rm -f $(TARGET) $(ALLSSFILES) +endif + +cleanall: clean + rm -f $(TARGET) + +list: + @ echo $(TARGET) diff --git a/dtool/src/build/Makefile.ss.template b/dtool/src/build/Makefile.ss.template new file mode 100644 index 0000000000..de408c2407 --- /dev/null +++ b/dtool/src/build/Makefile.ss.template @@ -0,0 +1,21 @@ +#### Makefile for ss interfaces. Any fields commented out are optional. + +#### Target's name: +#CTTARGET + +#### Pull in standard .ss make variables +include $(DTOOL)/inc/Makefile.ss.vars + +#### Source files +# HFILES = +# HHFILES = +# HHDIRS = + +#### Additional ss files to be inlcuded. +# OTHERSSFILES = + +#### Flags to control meta-ss creation. +# STRAIGHTINCLUDE = TRUE + +#### The .ss action is here. +include $(DTOOL)/inc/Makefile.ss.rules diff --git a/dtool/src/build/Makefile.ss.vars b/dtool/src/build/Makefile.ss.vars new file mode 100644 index 0000000000..d0e560ed73 --- /dev/null +++ b/dtool/src/build/Makefile.ss.vars @@ -0,0 +1,28 @@ +# Standard variable for creation of .ss files + +ifeq (,$(MAKEFILE_SS_VARS)) +MAKEFILE_SS_VARS = TRUE + +ifeq (,$(MAKEFILE_PENV_VARS)) +include $(DTOOL)/inc/Makefile.penv.vars +endif + +# Delete the default suffixes +.SUFFIXES: + +# Standard applications and flags. +SALIVATE = salivate $(SGI_BINFORMAT_SALIVATE) +SALIVATE-FLAGS = + +ifneq (,$(DELIVERY_PLAYER)) +SALIVATE-FLAGS += -DDELIVERY_PLAYER +endif + +# Some conversion macros +sstotmp = `echo \`basename $@ ss\`tmp` +sstoh = `echo \`basename $@ ss\`h` + +# Set the default target +install all : $(TARGET) + +endif # MAKEFILE_SS_VARS diff --git a/dtool/src/build/Makefile.uxb.rules b/dtool/src/build/Makefile.uxb.rules new file mode 100644 index 0000000000..8ee843e52e --- /dev/null +++ b/dtool/src/build/Makefile.uxb.rules @@ -0,0 +1,20 @@ +# UXB (teleuse) Makefile rules + +ifneq (,$(OPTIMIZE)) +UFLAGS := $(UFLAGS) -DOPTIMIZE +endif + +all: + uxb $(UFLAGS) + +clean: + uxb $(UFLAGS) clean + +cleanall: + uxb $(UFLAGS) delete + rm -rf ii_files + +list: + @echo -n + +install: all diff --git a/dtool/src/build/Makefile.uxb.template b/dtool/src/build/Makefile.uxb.template new file mode 100644 index 0000000000..28fac2d06d --- /dev/null +++ b/dtool/src/build/Makefile.uxb.template @@ -0,0 +1,7 @@ +# Makefile for uxb (teleuse) + +#### flags to pass to uxb +UFLAGS = + +#### The uxb action is here. +include $(DTOOL)/inc/Makefile.uxb.rules diff --git a/dtool/src/build/Sources.pp b/dtool/src/build/Sources.pp new file mode 100644 index 0000000000..e15030b90d --- /dev/null +++ b/dtool/src/build/Sources.pp @@ -0,0 +1,20 @@ +#define INSTALL_LIBS \ + Makefile.a.template Makefile.bin.template Makefile.foreign.template \ + Makefile.install.template Makefile.meta.template Makefile.o.include \ + Makefile.o.template Makefile.project.template Makefile.so.template \ + Makefile.ss.template Makefile.uxb.template + +#define INSTALL_HEADERS \ + Makefile.a.rules Makefile.bin.rules Makefile.bin.vars \ + Makefile.foreign.rules Makefile.foreign.vars Makefile.install.rules \ + Makefile.install.vars Makefile.meta.rules Makefile.o.rules \ + Makefile.o.vars Makefile.project.vars Makefile.so.rules \ + Makefile.ss.rules Makefile.ss.vars Makefile.uxb.rules \ + Makefile.penv.vars \ + ctinstmake.pl ctproj.pl ctutils.pl + +#define INSTALL_SCRIPTS \ + ctaddpkg ctaddtgt ctinitproj ctproj ctpathadjust + +#define EXTRA_DIST \ + initialize diff --git a/dtool/src/build/ctaddpkg b/dtool/src/build/ctaddpkg new file mode 100755 index 0000000000..8581198e09 --- /dev/null +++ b/dtool/src/build/ctaddpkg @@ -0,0 +1,92 @@ +#!/usr/local/bin/perl + +if ($#ARGV != 0) { + exit print "Usage: ctaddpkg package-name\n" ; +} + +$pkgname = $ARGV[0] ; + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured for CTtools" ; +} + +#pull in tools for getting information on the project +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; + +#get what project we're currently in, and where it's root is. +$proj = &CTProj ; +$projroot = &CTProjRoot($proj) ; +$projname = $proj ; +$projname =~ tr/A-Z/a-z/ ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +#see if such a package already exists, if so STOP +if ( -e "$projroot/src/all/$pkgname" ) { + die "Package '$pkgname' already exists in this project ('$proj')" ; +} + +#make sure there is a project-wide makefile +if ( ! -e "$projroot/Makefile" ) { + die "No project-wide Makefile for project '$proj'" ; +} + +#make sure there is a package Makefile template +if ( ! -e "$tool/lib/Makefile.meta.template" ) { + die "No package Makefile template in $tool/lib/." ; +} + +#make sure there is a package install Makefile template +if ( ! -e "$tool/lib/Makefile.install.template" ) { + die "No package install Makefile template in $tool/lib/." ; +} + +#make the package directory +require "$tool/inc/ctcm.pl" ; +$item = "$projroot/src/all/$pkgname" ; +if ( ! -e $item ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} + +#pull in tools for installing Makefiles +require "$tool/inc/ctinstmake.pl" ; + +#install a package makefile and package install makefile into the package +$item = "$projroot/src/all/$pkgname/Makefile" ; +&CTInstallMake( "$tool/lib/Makefile.meta.template", $item ) ; +if ( ! &CTCMMkelem( $item, $projname, $spec ) ) { + die "Could not make a verioned element of the project makefile\n" ; +} +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} + +$item = "$projroot/src/all/$pkgname/Makefile.install" ; +&CTInstallMake( "$tool/lib/Makefile.install.template", $item ) ; +if ( ! &CTCMMkelem( $item, $projname, $spec ) ) { + die "Could not make a verioned element of the project install makefile\n" ; +} +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} + +# delta the package in +$item = "$projroot/src/all/$pkgname" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} + +# update the master makefile +$item = "$projroot/Makefile" ; +if ( ! &CTCMCheckout( $item, $projname, $spec ) ) { + die "Could not checkout master project makefile\n" ; +} +&CTInstallScanMake( $item ) ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta master project makefile\n" ; +} diff --git a/dtool/src/build/ctaddtgt b/dtool/src/build/ctaddtgt new file mode 100755 index 0000000000..3ac36f8ae8 --- /dev/null +++ b/dtool/src/build/ctaddtgt @@ -0,0 +1,91 @@ +#!/usr/local/bin/perl + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured to run CTtools" ; +} + +if ($#ARGV == -1) { + exit print "Usage: ctaddtgt [{bin,a,so,ss,foreign}] target-name ...\n" ; +} + +require "$tool/inc/ctproj.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; + +@arglist = @ARGV ; + +$projname = &CTProj ; +$projname =~ tr/A-Z/a-z/ ; +$projroot = &CTProjRoot( $projname ) ; +$pkgname = &CTProjPkg( $projname ) ; +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +# check for package makefile and package install makefile + +while ( @arglist != () ) { + # initialize target type to default + $type = "bin" ; + + # potentially get the target type + if ( $arglist[0] eq "bin" ) { + shift( @arglist ) ; # already set + } elsif (( $arglist[0] eq "foreign" ) || ( $arglist[0] eq "a" ) || + ( $arglist[0] eq "so" ) || ( $arglist[0] eq "ss" )) { + $type = $arglist[0] ; + shift( @arglist ) ; + } + + # get the target name + $tgtname = $arglist[0] ; + $fulltgtname = $tgtname ; + shift( @arglist ) ; + + require "$tool/inc/ctinstmake.pl" ; + require "$tool/inc/ctcm.pl" ; + + $item = "$projroot/src/all/$pkgname" ; + if ( ! &CTCMCheckout( $item, $projname, $spec )) { + die "Could not checkout package '" . $pkgname . "'\n" ; + } + $item = "$projroot/src/all/$pkgname/Makefile" ; + if ( ! &CTCMCheckout( $item, $projname, $spec )) { + die "Could not checkout package '" . $pkgname . "' Makefile\n" ; + } + + $curdir = "$projroot/src/all/$pkgname" ; + # install target makefile, and update package makefile + if ( $type eq "bin" ) { + &CTInstallMake( "$tool/lib/Makefile.bin.template", + "$curdir/Makefile.$tgtname" ) ; + &CTInstallScanMake( "$curdir/Makefile" ) ; + } elsif ( $type eq "a" ) { + $fulltgtname = "lib" . $tgtname ; + &CTInstallMake( "$tool/lib/Makefile.a.template", + "$curdir/Makefile.$tgtname" ) ; + &CTInstallScanMake( "$curdir/Makefile" ) ; + } elsif ( $type eq "so" ) { + $fulltgtname = "lib" . $tgtname ; + &CTInstallMake( "$tool/lib/Makefile.so.template", + "$curdir/Makefile.$tgtname" ) ; + &CTInstallScanMake( "$curdir/Makefile" ) ; + } elsif ( $type eq "ss" ) { + &CTInstallMake( "$tool/lib/Makefile.ss.template", + "$curdir/Makefile.$tgtname" ) ; + &CTInstallScanMake( "$curdir/Makefile" ) ; + } elsif ( $type eq "foreign" ) { + &CTInstallMake( "$tool/lib/Makefile.foreign.template", + "$curdir/Makefile.$tgtname" ) ; + &CTInstallScanMake( "$curdir/Makefile" ) ; + } + + $item = "$curdir/Makefile.tgtname" ; + if ( ! &CTCMMkelem( $item, $projname, $spec ) ) { + die "Could not make a verioned element of the target makefile\n" ; + } +} + +print "You will want to edit Makefile, Makefile.install, and the target makefile(s)\n" ; +print "manually. Also, do not forget to delta the Makefile, target Makefile, and\n" ; +print "directory when you are done.\n" ; diff --git a/dtool/src/build/ctinitproj b/dtool/src/build/ctinitproj new file mode 100755 index 0000000000..91f820a399 --- /dev/null +++ b/dtool/src/build/ctinitproj @@ -0,0 +1,128 @@ +#!/usr/local/bin/perl + +if ( $#ARGV != 1 ) { + print "Usage: ctinitproj project-name project-root\n" ; + print " You should already be attached to the project\n" ; + exit print " ex: ctinitproj dtool $DTOOL\n" ; +} + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured for CTtools" ; +} + +require "$tool/inc/ctutils.pl" ; +require "$tool/inc/ctvspec.pl" ; +require "$tool/inc/ctquery.pl" ; + +$projname = $ARGV[0] ; +$projroot = &CTUShellEval( $ARGV[1] ); +$flav = &CTQueryProj( $projname ) ; +$spec = &CTResolveSpec( $projname, $flav ) ; + +# make sure the directory exists +if ( ! -e $projroot ) { + die "Project root ('$projroot') does not exist" ; +} + +require "$tool/inc/ctcm.pl" ; + +# make all the default subdirectories +$item = "$projroot/bin" ; +if ( ! -e $item ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} +$item = "$projroot/etc" ; +if ( ! -e $item ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} +$item = "$projroot/inc" ; +if ( ! -e $item ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} +$item = "$projroot/lib" ; +if ( ! -e $item ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} +$item = "$projroot/lib/ss" ; +if ( ! -e $item ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} +$item = "$projroot/lib/stk" ; +if ( ! -e $item ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} +$item = "$projroot/src" ; +if ( ! -e "$projroot/src" ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} +$item = "$projroot/src/all" ; +if ( ! -e "$projroot/src/all" ) { + if ( ! &CTCMMkdir( $item, $projname, $spec ) ) { + die "Could not create directory '" . $item . "'\n" ; + } +} + +# install the project-wide Makefile +require "$tool/inc/ctinstmake.pl" ; + +$item = "$projroot/Makefile" ; +&CTInstallMake( "$tool/lib/Makefile.project.template", $item ) ; +if ( ! &CTCMMkelem( $item, $projname, $spec ) ) { + die "Could not make a versioned element of the master makefile\n" ; +} + +# now delta everything in +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} + $item = "$projroot/src/all" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} +$item = "$projroot/src" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} +$item = "$projroot/lib/stk" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} +$item = "$projroot/lib/ss" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} +$item = "$projroot/lib" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} +$item = "$projroot/inc" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} +$item = "$projroot/etc" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} +$item = "$projroot/bin" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} +$item = "$projroot" ; +if ( ! &CTCMDelta( $item, $projname, $spec ) ) { + die "Could not delta '" . $item . "'\n" ; +} diff --git a/dtool/src/build/ctinstmake.pl b/dtool/src/build/ctinstmake.pl new file mode 100644 index 0000000000..263c8ac975 --- /dev/null +++ b/dtool/src/build/ctinstmake.pl @@ -0,0 +1,54 @@ +# copy the template makefile to output, recursively expanding marked inclusions +sub CTInstallMakeWrite { + local( *INMAKE ) ; + open( INMAKE, "<$_[0]" ) ; + while ( ) { + if ( /^#CTINCLUDE/ ) { + s/\n$// ; + local( @CTIMWlist ) ; + @CTIMWlist = split ; + local( $tool ) = $ENV{ "DTOOL" } ; + require "$tool/inc/ctutils.pl" ; + local( $CTIMWtmp ) = &CTUShellEval( $CTIMWlist[1] ) ; + &CTInstallMakeWrite( $CTIMWtmp ) ; + } elsif (( /^#CTPROJECT/ ) && ( $projname ne "" )){ + local( $CTIMWtmp ) = $projname ; + $CTIMWtmp =~ tr/A-Z/a-z/ ; # tolower + print OUTMAKE "CTPROJECT = $CTIMWtmp\n" ; + } elsif (( /^#CTPROJROOT/ ) && ( $projname ne "" )) { + local( $CTIMWtmp ) = $projname ; + $CTIMWtmp =~ tr/a-z/A-Z/ ; # toupper + print OUTMAKE "CTPROJROOT = \$($CTIMWtmp)\n" ; + } elsif (( /^#CTPACKAGE/ ) && ( $pkgname ne "" )) { + print OUTMAKE "PACKAGE = $pkgname\n" ; + } elsif (( /^#CTINSTPKG/ ) && ( $pkgname ne "" )) { + print OUTMAKE "include src/all/$pkgname/Makefile.install\n" ; + print OUTMAKE "#CTINSTPKG\n" ; + } elsif (( /^#CTTARGET/ ) && ( $fulltgtname ne "" )) { + print OUTMAKE "TARGET = $fulltgtname\n" ; + } elsif (( /^SUBMAKES/ ) && ( $tgtname ne "" )) { + s/\n$// ; + print OUTMAKE "$_ $tgtname\n" ; + } else { + print OUTMAKE $_ ; + } + } + close( INMAKE ) ; +} + +# install a given makefile template, expanding any internal directives +sub CTInstallMake { + local( *OUTMAKE ) ; + open( OUTMAKE, ">$_[1]" ) ; + &CTInstallMakeWrite( $_[0] ) ; + close( OUTMAKE ); +} + +# Scan the given makefile for CT markers to be expanded +sub CTInstallScanMake { + local( $CTISMname ) = "/tmp/make.$$" ; + &CTInstallMake( $_[0], $CTISMname ) ; + system( "mv $CTISMname $_[0]" ) ; +} + +1; diff --git a/dtool/src/build/ctpathadjust b/dtool/src/build/ctpathadjust new file mode 100755 index 0000000000..f201d92d94 --- /dev/null +++ b/dtool/src/build/ctpathadjust @@ -0,0 +1,74 @@ +#!/usr/local/bin/perl + +# find the longest common directory prefix of the 2 input paths +# input is: +# $_[0] = path 1 +# $_[1] = path 2 +# +# output is: +# relative path +sub CTCommonPrefix { + local( @path1 ) ; + local( @path2 ) ; + local( $count ) = 0 ; + local( $thresh ) ; + local( $tmp ) = $_[0] ; + $tmp =~ s/^\/// ; + @path1 = split ( /\//, $tmp ) ; + $tmp = $_[1] ; + $tmp =~ s/^\/// ; + @path2 = split ( /\//, $tmp ) ; + if ( $path1[0] eq "view" ) { + $thresh = 1 ; + } else { + $thresh = 0 ; + } + while (( $path1[0] eq $path2[0] ) && + ( $#path1 >= 0 ) && + ( $#path2 > 0 )) { + shift @path1; + shift @path2; + $count++ ; + } + local( $ret ) ; + if ( $count > $thresh ) { + while ( $#path1 >= 0 ) { + $ret = $ret . "../" ; + shift @path1 ; + } + while ( $#path2 >= 0 ) { + $ret = $ret . $path2[0] . "/" ; + shift @path2 ; + } + $ret =~ s/\/$// ; + } else { + $ret = $_[1] ; + } + $ret ; +} + +# $pwd = $ENV{"PWD"} ; +# sometimes the PWD environment variable lies, and getcwd by itself doesn't +# report the view extention. +use Cwd ; +$pwd = getcwd() ; +if ( $pwd =~ /^\/vobs/ ) { + open( VFILE, "/usr/atria/bin/cleartool pwv -short |" ) ; + $view = ; + close( VFILE ) ; + $view =~ s/\n$// ; + $pwd = "/view/" . $view . $pwd ; +} +$output = "" ; + +foreach $item ( @ARGV ) { + $common = &CTCommonPrefix( $pwd, $item ) ; + if ( length( $common ) > 0 ) { + if ( $output ne "" ) { + $output = $output . " " ; + } + $output = $output . $common ; + } +} + +print $output . "\n" ; diff --git a/dtool/src/build/ctproj b/dtool/src/build/ctproj new file mode 100755 index 0000000000..86dcfd684d --- /dev/null +++ b/dtool/src/build/ctproj @@ -0,0 +1,35 @@ +#!/usr/local/bin/perl + +$tool = $ENV{"DTOOL"} ; +if ( $tool eq "" ) { + die "Environment not configured to run CTtools" ; +} + +require "$tool/inc/ctproj.pl" ; + +@arglist = @ARGV ; + +$prntroot = 0 ; +if ( $arglist[0] =~ /^-r/ ) { + $prntroot = 1 ; + shift( @arglist ) ; +} elsif ( $arglist[0] =~ /^-p/ ) { + $prntpkg = 1 ; + shift( @arglist ) ; +} + +if ( @arglist == () ) { + $proj = &CTProj ; +} else { + $proj = &CTProj( $arglist[0] ) ; +} + +if ( $prntroot ) { + print &CTProjRoot( $proj ) ; +} elsif ( $prntpkg ) { + print &CTProjPkg( $proj ) ; +} else { + print $proj ; +} + +print "\n" ; diff --git a/dtool/src/build/ctproj.pl b/dtool/src/build/ctproj.pl new file mode 100644 index 0000000000..301435bc78 --- /dev/null +++ b/dtool/src/build/ctproj.pl @@ -0,0 +1,60 @@ +# return the root of the given project. +sub CTProjRoot { + local( $CTPRtmp ) = $_[0] ; + $CTPRtmp =~ tr/a-z/A-Z/ ; + local( $CTPRret ) = $ENV{ $CTPRtmp } ; + $CTPRret ; +} + +# return the package we're currently in. +# input: +# $_[0] = project +sub CTProjPkg { + local( $CTPPret ) = `pwd`; + chop $CTPPret; + local( $CTPPtmp ) = $_[0] ; + $CTPPtmp =~ tr/a-z/A-Z/ ; + $CTPPret =~ s/$ENV{ $CTPPtmp }// ; + $CTPPret =~ s/\/src\/// ; + $CTPPret =~ s/\/metalib\/// ; + $CTPPret ; +} + +# reutrn the project containing the given directory. If no directory is given, +# return the project containing the current directory. +sub CTProj { + local( $CTPdir ) ; + if ($_[0] eq "") { + $CTPdir = `pwd`; + chop $CTPdir; + } else { + # provided directory + $CTPdir = $_[0] ; + } + local( $CTPprojs ) = $ENV{"CTPROJS"} ; + local( $CTPdone ) = "" ; + local( @CTPlist ) ; + @CTPlist = split( / /, $CTPprojs ) ; + local( @CTPlist2 ) ; + local( $CTPtry ) ; + while (( $CTPdone eq "" ) && ( @CTPlist != () )){ + # pop the first one off the list + $CTPtmp = $CTPlist[0] ; + shift( @CTPlist ) ; + # split the project from it's flavor + @CTPlist2 = split( /:/, $CTPtmp ); + $CTPtry = &CTProjRoot( $CTPlist2[0] ) ; + # is CTPtry prefix of CTPdir? if so we have our winner + if ( $CTPdir =~ /^$CTPtry/ ) { + $CTPdone = "yep" ; + } + } + if ( $CTPdone eq "" ) { + $CTPtry = "" ; + } else { + $CTPtry = $CTPlist2[0] ; + } + $CTPtry ; +} + +1; diff --git a/dtool/src/build/ctutils.pl b/dtool/src/build/ctutils.pl new file mode 100644 index 0000000000..a32dd5a6f3 --- /dev/null +++ b/dtool/src/build/ctutils.pl @@ -0,0 +1,47 @@ +# evaluate the given parameter to expand shell variables +sub CTUShellEval { + local( *CTUSEFILE ) ; + open( CTUSEFILE, "echo $_[0] |" ) ; + local( $CTUSEret ) = ; + close( CTUSEFILE ) ; + $CTUSEret =~ s/\n$// ; + $CTUSEret ; +} + +# if debug is on, print the argument +sub CTUDebug { + if ( $ctdebug ) { + print STDERR $_[0] ; + } +} + +use Cwd ; +# get current directory +sub CTUCurrDir { + local( $pwd ) = getcwd() ; + if ( $pwd =~ /^\/vobs/ ) { + local( *VFILE ) ; + open( VFILE, "cleartool pwv -short |" ) ; + local( $view ) = ; + close( VFILE ) ; + $view =~ s/\n$// ; + $pwd = "/view/" . $view . $pwd ; + } + $pwd ; +} + +# turn a shell return code into a success/fail flag +sub CTURetCode { + local( $ret ) ; + if ( $_[0] == 0 ) { + $ret = 1 ; + } else { + $ret = 0 ; + } + $ret ; +} + +$ctdebug = $ENV{"CTATTACH_DEBUG"} ; +$ctvspec_path = '/usr/local/etc' unless $ctvspec_path = $ENV{'CTVSPEC_PATH'}; + +1; diff --git a/dtool/src/build/initialize b/dtool/src/build/initialize new file mode 100755 index 0000000000..8bb0a5d396 --- /dev/null +++ b/dtool/src/build/initialize @@ -0,0 +1,21 @@ +#! /bin/sh + +if test "$1" = ""; then + echo "must give a make command as the argument." + echo "(eg: initialize /usr/atria/bin/clearmake -C gnu)" + exit 1 +fi + +test -d ../../inc || mkdir ../../inc +test -d ../../bin || mkdir ../../bin + +cp Makefile.install.rules Makefile.meta.rules Makefile.install.vars Makefile.project.vars Makefile.penv.vars Makefile.o.vars ctproj.pl ../../inc +cp ctproj ctpathadjust ../../bin +cd ../.. +if test "$DTOOL" = ""; then + DTOOL=`pwd` + export DTOOL +fi +PATH=$DTOOL/bin:$PATH +export PATH +$* build attach diff --git a/dtool/src/cppparser/Sources.pp b/dtool/src/cppparser/Sources.pp new file mode 100644 index 0000000000..2e6ec5277a --- /dev/null +++ b/dtool/src/cppparser/Sources.pp @@ -0,0 +1,46 @@ +#define LOCAL_LIBS dtoolutil dtoolbase +#define YACC_PREFIX cppyy + +#begin static_lib_target + #define TARGET cppParser + + #define SOURCES \ + cppArrayType.cxx cppArrayType.h cppBison.h \ + cppBison.yxx cppBisonDefs.h \ + cppClassTemplateParameter.cxx cppClassTemplateParameter.h \ + cppCommentBlock.cxx cppCommentBlock.h cppConstType.cxx \ + cppConstType.h cppDeclaration.cxx cppDeclaration.h cppEnumType.cxx \ + cppEnumType.h cppExpression.cxx cppExpression.h \ + cppExpressionParser.cxx cppExpressionParser.h cppExtensionType.cxx \ + cppExtensionType.h cppFile.cxx cppFile.h cppFunctionGroup.cxx \ + cppFunctionGroup.h cppFunctionType.cxx cppFunctionType.h \ + cppGlobals.cxx cppGlobals.h cppIdentifier.cxx cppIdentifier.h \ + cppInstance.cxx cppInstance.h cppInstanceIdentifier.cxx \ + cppInstanceIdentifier.h cppManifest.cxx cppManifest.h \ + cppNameComponent.cxx cppNameComponent.h cppNamespace.cxx \ + cppNamespace.h cppParameterList.cxx cppParameterList.h cppParser.cxx \ + cppParser.h cppPointerType.cxx cppPointerType.h cppPreprocessor.cxx \ + cppPreprocessor.h cppReferenceType.cxx cppReferenceType.h \ + cppScope.cxx cppScope.h cppSimpleType.cxx cppSimpleType.h \ + cppStructType.cxx cppStructType.h cppTBDType.cxx cppTBDType.h \ + cppTemplateParameterList.cxx cppTemplateParameterList.h \ + cppTemplateScope.cxx cppTemplateScope.h cppToken.cxx cppToken.h \ + cppType.cxx cppType.h cppTypeDeclaration.cxx cppTypeDeclaration.h \ + cppTypeParser.cxx cppTypeParser.h cppTypeProxy.cxx cppTypeProxy.h \ + cppTypedef.cxx cppTypedef.h cppUsing.cxx cppUsing.h cppVisibility.cxx \ + cppVisibility.h indent.cxx indent.h + +// These are temporary; they need not be installed in the future. These are +// necessary only when using template stopgap. + #define INSTALL_HEADERS \ + cppDeclaration.h cppExtensionType.h cppIdentifier.h cppInstance.h \ + cppManifest.h cppPreprocessor.h cppScope.h cppToken.h cppType.h \ + cppVisibility.h cppBisonDefs.h cppParser.h cppInstanceIdentifier.h \ + cppFunctionType.h cppSimpleType.h cppParameterList.h cppTypedef.h \ + cppTypeDeclaration.h \ + cppPointerType.h cppReferenceType.h cppConstType.h cppArrayType.h \ + cppEnumType.h cppStructType.h cppFile.h cppTemplateParameterList.h \ + cppFunctionGroup.h cppNameComponent.h cppTypeProxy.h cppTBDType.h \ + cppExpressionParser.h cppExpression.h cppGlobals.h cppCommentBlock.h + +#end static_lib_target diff --git a/dtool/src/cppparser/cppArrayType.cxx b/dtool/src/cppparser/cppArrayType.cxx new file mode 100644 index 0000000000..c504449bda --- /dev/null +++ b/dtool/src/cppparser/cppArrayType.cxx @@ -0,0 +1,227 @@ +// Filename: cppArrayType.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppArrayType.h" +#include "cppExpression.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPArrayType:: +CPPArrayType(CPPType *element_type, CPPExpression *bounds) : + CPPType(CPPFile()), + _element_type(element_type), + _bounds(bounds) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPArrayType:: +is_fully_specified() const { + return CPPType::is_fully_specified() && + _element_type->is_fully_specified(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPArrayType:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + CPPType *ptype = _element_type->resolve_type(current_scope, global_scope); + + if (ptype != _element_type) { + CPPArrayType *rep = new CPPArrayType(*this); + rep->_element_type = ptype; + return CPPType::new_type(rep); + } + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPArrayType:: +is_tbd() const { + return _element_type->is_tbd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::is_equivalent +// Access: Public, Virtual +// Description: This is a little more forgiving than is_equal(): it +// returns true if the types appear to be referring to +// the same thing, even if they may have different +// pointers or somewhat different definitions. It's +// useful for parameter matching, etc. +//////////////////////////////////////////////////////////////////// +bool CPPArrayType:: +is_equivalent(const CPPType &other) const { + const CPPArrayType *ot = ((CPPType *)&other)->as_array_type(); + if (ot == (CPPArrayType *)NULL) { + return CPPType::is_equivalent(other); + } + + return _element_type->is_equivalent(*ot->_element_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPArrayType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + return (*si).second; + } + + CPPArrayType *rep = new CPPArrayType(*this); + rep->_element_type = + _element_type->substitute_decl(subst, current_scope, global_scope) + ->as_type(); + + if (_bounds != NULL) { + rep->_bounds = + _bounds->substitute_decl(subst, current_scope, global_scope) + ->as_expression(); + } + + if (rep->_element_type == _element_type && + rep->_bounds == _bounds) { + delete rep; + rep = this; + } + rep = CPPType::new_type(rep)->as_array_type(); + subst.insert(SubstDecl::value_type(this, rep)); + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPArrayType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + _element_type->output(out, indent_level, scope, complete); + out << "["; + if (_bounds != NULL) { + out << *_bounds; + } + out << "]"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::output_instance +// Access: Public, Virtual +// Description: Formats a C++-looking line that defines an instance +// of the given type, with the indicated name. In most +// cases this will be "type name", but some types have +// special exceptions. +//////////////////////////////////////////////////////////////////// +void CPPArrayType:: +output_instance(ostream &out, int indent_level, CPPScope *scope, + bool complete, const string &prename, + const string &name) const { + ostringstream brackets; + brackets << "["; + if (_bounds != NULL) { + brackets << *_bounds; + } + brackets << "]"; + string bracketsstr = brackets.str(); + + _element_type->output_instance(out, indent_level, scope, complete, + prename, name + bracketsstr); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPArrayType:: +get_subtype() const { + return ST_array; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::as_array_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPArrayType *CPPArrayType:: +as_array_type() { + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPArrayType:: +is_equal(const CPPDeclaration *other) const { + const CPPArrayType *ot = ((CPPDeclaration *)other)->as_array_type(); + assert(ot != NULL); + + if (_bounds != NULL && ot->_bounds != NULL) { + if (*_bounds != *ot->_bounds) { + return false; + } + } else if (_bounds == NULL || ot->_bounds == NULL) { + return false; + } + + return _element_type == ot->_element_type; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPArrayType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPArrayType:: +is_less(const CPPDeclaration *other) const { + const CPPArrayType *ot = ((CPPDeclaration *)other)->as_array_type(); + assert(ot != NULL); + + if (_bounds != NULL && ot->_bounds != NULL) { + if (*_bounds != *ot->_bounds) { + return *_bounds < *ot->_bounds; + } + } else if (_bounds == NULL || ot->_bounds == NULL) { + return _bounds < ot->_bounds; + } + + return _element_type < ot->_element_type; +} + diff --git a/dtool/src/cppparser/cppArrayType.h b/dtool/src/cppparser/cppArrayType.h new file mode 100644 index 0000000000..d840e5a992 --- /dev/null +++ b/dtool/src/cppparser/cppArrayType.h @@ -0,0 +1,52 @@ +// Filename: cppArrayType.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPARRAYTYPE_H +#define CPPARRAYTYPE_H + +#include + +#include "cppType.h" + +class CPPExpression; + +/////////////////////////////////////////////////////////////////// +// Class : CPPArrayType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPArrayType : public CPPType { +public: + CPPArrayType(CPPType *element_type, CPPExpression *bounds); + + CPPType *_element_type; + CPPExpression *_bounds; + + virtual bool is_fully_specified() const; + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + virtual bool is_tbd() const; + virtual bool is_equivalent(const CPPType &other) const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual void output_instance(ostream &out, int indent_level, + CPPScope *scope, + bool complete, const string &prename, + const string &name) const; + + virtual SubType get_subtype() const; + + virtual CPPArrayType *as_array_type(); + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +#endif diff --git a/dtool/src/cppparser/cppBison.yxx b/dtool/src/cppparser/cppBison.yxx new file mode 100644 index 0000000000..9de089eb8f --- /dev/null +++ b/dtool/src/cppparser/cppBison.yxx @@ -0,0 +1,2527 @@ +// Filename: cppBison.y +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +%{ + +#include "cppBisonDefs.h" +#include "cppParser.h" +#include "cppExpression.h" +#include "cppSimpleType.h" +#include "cppExtensionType.h" +#include "cppStructType.h" +#include "cppEnumType.h" +#include "cppFunctionType.h" +#include "cppTBDType.h" +#include "cppParameterList.h" +#include "cppInstance.h" +#include "cppClassTemplateParameter.h" +#include "cppTemplateParameterList.h" +#include "cppInstanceIdentifier.h" +#include "cppTypedef.h" +#include "cppTypeDeclaration.h" +#include "cppVisibility.h" +#include "cppIdentifier.h" +#include "cppScope.h" +#include "cppTemplateScope.h" +#include "cppNamespace.h" +#include "cppUsing.h" + +//////////////////////////////////////////////////////////////////// +// Defining the interface to the parser. +//////////////////////////////////////////////////////////////////// + +CPPScope *current_scope = NULL; +CPPScope *global_scope = NULL; +CPPPreprocessor *current_lexer = NULL; + +static CPPStructType *current_struct = NULL; +static CPPEnumType *current_enum = NULL; +static int current_storage_class = 0; +static CPPType *current_type = NULL; +static CPPExpression *current_expr = NULL; +static int publish_nest_level = 0; +static CPPVisibility publish_previous; +static YYLTYPE publish_loc; + +static vector last_scopes; +static vector last_storage_classes; +static vector last_structs; + +int yyparse(); + +#define YYERROR_VERBOSE + +static void +yyerror(const string &msg) { + current_lexer->error(msg); +} + +static void +yyerror(const string &msg, YYLTYPE &loc) { + current_lexer->error(msg, loc.first_line, loc.first_column); +} + +static void +yywarning(const string &msg, YYLTYPE &loc) { + current_lexer->warning(msg, loc.first_line, loc.first_column); +} + +static int +yylex(YYSTYPE *lval, YYLTYPE *lloc) { + CPPToken token = current_lexer->get_next_token(); + *lval = token._lval; + *lloc = token._lloc; + return token._token; +} + +void +parse_cpp(CPPParser *cp) { + CPPScope *old_scope = current_scope; + CPPScope *old_global_scope = global_scope; + CPPPreprocessor *old_lexer = current_lexer; + + current_scope = cp; + global_scope = cp; + current_lexer = cp; + publish_nest_level = 0; + yyparse(); + + if (publish_nest_level != 0) { + yyerror("Unclosed __begin_publish", publish_loc); + publish_nest_level = 0; + } + + current_scope = old_scope; + global_scope = old_global_scope; + current_lexer = old_lexer; + +} + +CPPExpression * +parse_const_expr(CPPPreprocessor *pp, CPPScope *new_current_scope, + CPPScope *new_global_scope) { + CPPScope *old_scope = current_scope; + CPPScope *old_global_scope = global_scope; + CPPPreprocessor *old_lexer = current_lexer; + CPPExpression *old_expr = current_expr; + + current_scope = new_current_scope; + global_scope = new_global_scope; + current_expr = (CPPExpression *)NULL; + current_lexer = pp; + yyparse(); + + CPPExpression *result = current_expr; + + current_scope = old_scope; + global_scope = old_global_scope; + current_lexer = old_lexer; + current_expr = old_expr; + + return result; +} + +CPPType * +parse_type(CPPPreprocessor *pp, CPPScope *new_current_scope, + CPPScope *new_global_scope) { + CPPScope *old_scope = current_scope; + CPPScope *old_global_scope = global_scope; + CPPPreprocessor *old_lexer = current_lexer; + CPPType *old_type = current_type; + + current_scope = new_current_scope; + global_scope = new_global_scope; + current_type = (CPPType *)NULL; + current_lexer = pp; + yyparse(); + + CPPType *result = current_type; + + current_scope = old_scope; + global_scope = old_global_scope; + current_lexer = old_lexer; + current_type = old_type; + + return result; +} + +static void +push_scope(CPPScope *new_scope) { + last_scopes.push_back(current_scope); + if (new_scope != NULL) { + current_scope = new_scope; + } +} + +static void +pop_scope() { + assert(!last_scopes.empty()); + current_scope = last_scopes.back(); + last_scopes.pop_back(); +} + +static void +push_storage_class(int new_storage_class) { + last_storage_classes.push_back(current_storage_class); + current_storage_class = new_storage_class; +} + +static void +pop_storage_class() { + assert(!last_storage_classes.empty()); + current_storage_class = last_storage_classes.back(); + last_storage_classes.pop_back(); +} + +static void +push_struct(CPPStructType *new_struct) { + last_structs.push_back(current_struct); + current_struct = new_struct; +} + +static void +pop_struct() { + assert(!last_structs.empty()); + current_struct = last_structs.back(); + last_structs.pop_back(); +} + +%} + +/* This is a bison-specific declaration to enable recursive calls to + yyparse(). It changes the calling sequence to yylex(), passing + pointers to the current yylval and yylloc. */ +%pure_parser + +%token REAL +%token INTEGER +%token CHAR +%token STRING SIMPLE_IDENTIFIER +%token IDENTIFIER TYPENAME_IDENTIFIER SCOPING +%token TYPEDEFNAME + +%token ELLIPSIS +%token OROR +%token ANDAND +%token EQCOMPARE +%token NECOMPARE +%token LECOMPARE +%token GECOMPARE +%token LSHIFT +%token RSHIFT +%token POINTSAT_STAR +%token DOT_STAR +%token UNARY +%token UNARY_NOT +%token UNARY_NEGATE +%token UNARY_MINUS +%token UNARY_STAR +%token UNARY_REF +%token POINTSAT +%token SCOPE +%token PLUSPLUS +%token MINUSMINUS +%token TIMESEQUAL +%token DIVIDEEQUAL +%token MODEQUAL +%token PLUSEQUAL +%token MINUSEQUAL +%token OREQUAL +%token ANDEQUAL +%token XOREQUAL +%token LSHIFTEQUAL +%token RSHIFTEQUAL +%token TOKENPASTE + +%token KW_BEGIN_PUBLISH +%token KW_BOOL +%token KW_CATCH +%token KW_CHAR +%token KW_CLASS +%token KW_CONST +%token KW_DELETE +%token KW_DOUBLE +%token KW_DYNAMIC_CAST +%token KW_ELSE +%token KW_END_PUBLISH +%token KW_ENUM +%token KW_EXTERN +%token KW_EXPLICIT +%token KW_PUBLISHED +%token KW_FALSE +%token KW_FLOAT +%token KW_FRIEND +%token KW_FOR +%token KW_GOTO +%token KW_IF +%token KW_INLINE +%token KW_INT +%token KW_LONG +%token KW_LONGLONG +%token KW_MUTABLE +%token KW_NAMESPACE +%token KW_NEW +%token KW_OPERATOR +%token KW_PRIVATE +%token KW_PROTECTED +%token KW_PUBLIC +%token KW_REGISTER +%token KW_RETURN +%token KW_SHORT +%token KW_SIGNED +%token KW_SIZEOF +%token KW_STATIC +%token KW_STATIC_CAST +%token KW_STRUCT +%token KW_TEMPLATE +%token KW_THROW +%token KW_TRUE +%token KW_TRY +%token KW_TYPEDEF +%token KW_TYPENAME +%token KW_UNION +%token KW_UNSIGNED +%token KW_USING +%token KW_VIRTUAL +%token KW_VOID +%token KW_VOLATILE +%token KW_WHILE + +/* These special tokens are used to set the starting state of the + parser. The lexer places the appropriate one of these on the head + of the input stream. */ +%token START_CPP +%token START_CONST_EXPR +%token START_TYPE + +%type storage_class +%type function_prototype +%type function_post +%type function_operator +%type template_formal_parameter +%type template_formal_parameter_type +%type instance_identifier +%type formal_parameter_list +%type formal_parameters +%type template_parameter_maybe_initialize +%type maybe_initialize +%type maybe_initialize_or_function_body +%type formal_parameter +%type not_paren_formal_parameter_identifier +%type formal_parameter_identifier +%type empty_instance_identifier +%type type +%type type_decl +%type predefined_type +%type full_type +%type anonymous_struct +%type named_struct +%type anonymous_enum +%type named_enum +%type enum_keyword +%type struct_keyword +%type simple_type +%type simple_int_type +%type simple_float_type +%type simple_void_type +%type class_derivation_name +/*%type typedefname*/ +%type name +%type string + +/* We need to treat KW_OPERATOR as a scopable keyword. */ +%type KW_OPERATOR + +%type optional_const_expr +%type optional_const_expr_comma +%type const_expr_comma +%type no_angle_bracket_const_expr +%type const_expr +%type const_operand + + +/* Precedence rules. */ + +%left IDENTIFIER TYPENAME_IDENTIFIER TYPEDEFNAME KW_ENUM ELLIPSIS KW_OPERATOR KW_TYPENAME KW_INT KW_SHORT KW_UNSIGNED KW_SIGNED KW_LONG KW_FLOAT KW_DOUBLE KW_CHAR KW_BOOL + +%left '{' ',' ';' + +%nonassoc KW_THROW +%right ':' +%right '=' +%right '?' +%left OROR +%left ANDAND +%left '|' +%left '^' +%left '&' +%left EQCOMPARE NECOMPARE +%left LECOMPARE GECOMPARE '<' '>' +%left LSHIFT RSHIFT +%left '+' '-' +%left '*' '/' '%' +%left POINTSAT_STAR DOT_STAR +%right UNARY PLUSPLUS MINUSMINUS '~' +%left POINTSAT '.' '(' '[' + +%right SCOPE +%nonassoc KW_NEW KW_DELETE KW_TRY KW_CATCH + +%% + +grammar: + START_CPP cpp + | START_CONST_EXPR const_expr +{ + current_expr = $2; +} + | START_TYPE full_type +{ + current_type = $2; +} + ; + +cpp: + empty + | cpp ';' + | cpp declaration + ; + +constructor_inits: + constructor_init + | constructor_inits ',' constructor_init + ; + +constructor_init: + name '(' optional_const_expr_comma ')' +{ + delete $3; +} + ; + +/* This is principally for the syntax: extern "C" { ... }. + + We use storage_class instead of simply KW_EXTERN to avoid + shift/reduce conflicts with yacc's limited differentiation + ability. */ +extern_c: + storage_class '{' +{ + push_storage_class((current_storage_class & ~CPPInstance::SC_c_binding) | + ($1 & CPPInstance::SC_c_binding)); +} + cpp '}' +{ + pop_storage_class(); +} + ; + +declaration: + type_like_declaration + | template_declaration + | extern_c + | namespace_declaration + | using_declaration + | friend_declaration + | KW_TYPEDEF typedef_declaration + | KW_BEGIN_PUBLISH +{ + if (publish_nest_level != 0) { + yyerror("Unclosed __begin_publish", publish_loc); + publish_nest_level = 0; + current_scope->set_current_vis(V_public); + } + + publish_previous = current_scope->get_current_vis(); + publish_loc = @1; + publish_nest_level++; + current_scope->set_current_vis(V_published); +} + | KW_END_PUBLISH +{ + if (publish_nest_level != 1) { + yyerror("Unmatched __end_publish", @1); + } else { + current_scope->set_current_vis(publish_previous); + } + publish_nest_level = 0; +} + | KW_PUBLISHED ':' +{ + current_scope->set_current_vis(V_published); +} + | KW_PUBLIC ':' +{ + current_scope->set_current_vis(V_public); +} + | KW_PROTECTED ':' +{ + current_scope->set_current_vis(V_protected); +} + | KW_PRIVATE ':' +{ + current_scope->set_current_vis(V_private); +} + ; + +friend_declaration: + KW_FRIEND +{ + CPPScope *new_scope = new CPPScope(current_scope, CPPNameComponent("temp"), + V_public); + push_scope(new_scope); +} + declaration +{ + delete current_scope; + pop_scope(); +} + ; + + +storage_class: + empty +{ + $$ = 0; +} + | storage_class KW_EXTERN +{ + $$ = $1 | (int)CPPInstance::SC_extern; +} + | storage_class KW_EXTERN string +{ + $$ = $1 | (int)CPPInstance::SC_extern; + if ($3 == "C") { + $$ |= (int)CPPInstance::SC_c_binding; + } else if ($3 == "C++") { + $$ &= ~(int)CPPInstance::SC_c_binding; + } else { + yywarning("Ignoring unknown linkage type \"" + $3 + "\"", @3); + } +} + | storage_class KW_STATIC +{ + $$ = $1 | (int)CPPInstance::SC_static; +} + | storage_class KW_INLINE +{ + $$ = $1 | (int)CPPInstance::SC_inline; +} + | storage_class KW_VIRTUAL +{ + $$ = $1 | (int)CPPInstance::SC_virtual; +} + | storage_class KW_EXPLICIT +{ + $$ = $1 | (int)CPPInstance::SC_explicit; +} + | storage_class KW_VOLATILE +{ + $$ = $1 | (int)CPPInstance::SC_volatile; +} + | storage_class KW_MUTABLE +{ + $$ = $1 | (int)CPPInstance::SC_mutable; +} + | storage_class KW_REGISTER +{ + $$ = $1 | (int)CPPInstance::SC_register; +} + ; + +type_like_declaration: + multiple_var_declaration +{ + /* multiple_var_declaration adds itself to the scope. */ +} + | storage_class type_decl ';' +{ + // We don't really care about the storage class here. In fact, it's + // not actually legal to define a class or struct using a particular + // storage class, but we require it just to help yacc out in its + // parsing. + + current_scope->add_declaration($2, global_scope, current_lexer, @2); +} + | storage_class function_prototype maybe_initialize_or_function_body +{ + if ($2 != (CPPInstance *)NULL) { + $2->_storage_class |= (current_storage_class | $1); + current_scope->add_declaration($2, global_scope, current_lexer, @2); + $2->set_initializer($3); + } +} + ; + +multiple_var_declaration: + storage_class type_decl +{ + // We don't need to push/pop type, because we can't nest + // multiple_var_declarations. + if ($2->as_type_declaration()) { + current_type = $2->as_type_declaration()->_type; + } else { + current_type = $2->as_type(); + } + push_storage_class($1); +} + multiple_instance_identifiers +{ + pop_storage_class(); +} + | storage_class KW_CONST type +{ + // We don't need to push/pop type, because we can't nest + // multiple_var_declarations. + current_type = $3; + push_storage_class($1); +} + multiple_const_instance_identifiers +{ + pop_storage_class(); +} + + /* We don't need to include a rule for variables that point to + functions, because we get those from the function_prototype + definition. */ + ; + +multiple_instance_identifiers: + instance_identifier maybe_initialize_or_function_body +{ + CPPInstance *inst = new CPPInstance(current_type, $1, + current_storage_class, + @1.file); + inst->set_initializer($2); + current_scope->add_declaration(inst, global_scope, current_lexer, @1); +} + | instance_identifier maybe_initialize ',' multiple_instance_identifiers +{ + CPPInstance *inst = new CPPInstance(current_type, $1, + current_storage_class, + @1.file); + inst->set_initializer($2); + current_scope->add_declaration(inst, global_scope, current_lexer, @1); +} + ; + +multiple_const_instance_identifiers: + instance_identifier maybe_initialize_or_function_body +{ + $1->add_modifier(IIT_const); + CPPInstance *inst = new CPPInstance(current_type, $1, + current_storage_class, + @1.file); + inst->set_initializer($2); + current_scope->add_declaration(inst, global_scope, current_lexer, @1); +} + | instance_identifier maybe_initialize ',' multiple_const_instance_identifiers +{ + $1->add_modifier(IIT_const); + CPPInstance *inst = new CPPInstance(current_type, $1, + current_storage_class, + @1.file); + inst->set_initializer($2); + current_scope->add_declaration(inst, global_scope, current_lexer, @1); +} + ; + + +typedef_declaration: + storage_class type_decl +{ + // We don't need to push/pop type, because we can't nest + // multiple_var_declarations. + if ($2->as_type_declaration()) { + current_type = $2->as_type_declaration()->_type; + } else { + current_type = $2->as_type(); + } + push_storage_class($1); +} + typedef_instance_identifiers +{ + pop_storage_class(); +} + | storage_class KW_CONST type +{ + // We don't need to push/pop type, because we can't nest + // multiple_var_declarations. + current_type = $3; + push_storage_class($1); +} + typedef_const_instance_identifiers +{ + pop_storage_class(); +} + | storage_class function_prototype maybe_initialize_or_function_body +{ + if ($2 != (CPPDeclaration *)NULL) { + CPPInstance *inst = $2->as_instance(); + if (inst != (CPPInstance *)NULL) { + inst->_storage_class |= (current_storage_class | $1); + current_scope->add_declaration(inst, global_scope, current_lexer, @2); + current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @2); + } + } +} + ; + +typedef_instance_identifiers: + instance_identifier maybe_initialize_or_function_body +{ + CPPInstance *inst = new CPPInstance(current_type, $1, + current_storage_class, + @1.file); + inst->set_initializer($2); + current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1); +} + | instance_identifier maybe_initialize ',' typedef_instance_identifiers +{ + CPPInstance *inst = new CPPInstance(current_type, $1, + current_storage_class, + @1.file); + inst->set_initializer($2); + current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1); +} + ; + +typedef_const_instance_identifiers: + instance_identifier maybe_initialize_or_function_body +{ + $1->add_modifier(IIT_const); + CPPInstance *inst = new CPPInstance(current_type, $1, + current_storage_class, + @1.file); + inst->set_initializer($2); + current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1); +} + | instance_identifier maybe_initialize ',' typedef_const_instance_identifiers +{ + $1->add_modifier(IIT_const); + CPPInstance *inst = new CPPInstance(current_type, $1, + current_storage_class, + @1.file); + inst->set_initializer($2); + current_scope->add_declaration(new CPPTypedef(inst, current_scope == global_scope), global_scope, current_lexer, @1); +} + ; + +function_prototype: + +/* Functions with implicit return types, and constructors */ + IDENTIFIER '(' +{ + push_scope($1->get_scope(current_scope, global_scope)); +} + formal_parameter_list ')' function_post +{ + pop_scope(); + CPPType *type; + if ($1->get_simple_name() == current_scope->get_simple_name()) { + // This is a constructor, and has no return. + type = new CPPSimpleType(CPPSimpleType::T_void); + } else { + // This isn't a constructor, so it has an implicit return type of + // int. + type = new CPPSimpleType(CPPSimpleType::T_int); + } + + CPPInstanceIdentifier *ii = new CPPInstanceIdentifier($1); + ii->add_func_modifier($4, $6); + + $$ = new CPPInstance(type, ii, 0, @1.file); +} + | TYPENAME_IDENTIFIER '(' +{ + push_scope($1->get_scope(current_scope, global_scope)); +} + formal_parameter_list ')' function_post +{ + pop_scope(); + CPPType *type; + if ($1->get_simple_name() == current_scope->get_simple_name()) { + // This is a constructor, and has no return. + type = new CPPSimpleType(CPPSimpleType::T_void); + } else { + // This isn't a constructor, so it has an implicit return type of + // int. + type = new CPPSimpleType(CPPSimpleType::T_int); + } + + CPPInstanceIdentifier *ii = new CPPInstanceIdentifier($1); + ii->add_func_modifier($4, $6); + + $$ = new CPPInstance(type, ii, 0, @1.file); +} + +/* Destructors */ + | '~' name '(' +{ + push_scope($2->get_scope(current_scope, global_scope)); +} + formal_parameter_list ')' function_post +{ + pop_scope(); + if ($2->is_scoped()) { + yyerror("Invalid destructor name: ~" + $2->get_fully_scoped_name(), @2); + } else { + CPPIdentifier *ident = + new CPPIdentifier("~" + $2->get_simple_name(), @2.file); + delete $2; + + CPPType *type; + type = new CPPSimpleType(CPPSimpleType::T_void); + + CPPInstanceIdentifier *ii = new CPPInstanceIdentifier(ident); + ii->add_func_modifier($5, $7); + + $$ = new CPPInstance(type, ii, 0, @2.file); + } +} + +/* This is a special case: a function pointer declaration that looks + at first a lot like a constructor declaration. This is provided to + help yacc sort out the differences. It isn't an ideal solution, + because it doesn't catch a lot of subtle variants on this form--but + this will get at least the 99% most common uses. */ + + | TYPENAME_IDENTIFIER '(' '*' instance_identifier ')' '(' +{ + push_scope($4->get_scope(current_scope, global_scope)); +} + formal_parameter_list ')' function_post +{ + pop_scope(); + CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer); + assert(type != NULL); + + CPPInstanceIdentifier *ii = $4; + ii->add_modifier(IIT_pointer); + ii->add_func_modifier($8, $10); + $$ = new CPPInstance(type, ii, 0, @1.file); +} + | TYPENAME_IDENTIFIER '(' SCOPING '*' instance_identifier ')' '(' +{ + push_scope($5->get_scope(current_scope, global_scope)); +} + formal_parameter_list ')' function_post +{ + pop_scope(); + CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer); + assert(type != NULL); + + CPPInstanceIdentifier *ii = $5; + ii->add_scoped_pointer_modifier($3); + ii->add_func_modifier($9, $11); + $$ = new CPPInstance(type, ii, 0, @1.file); +} + +/* Typecast operators */ + | KW_OPERATOR type not_paren_formal_parameter_identifier '(' +{ + if ($1 != NULL) { + push_scope($1->get_scope(current_scope, global_scope)); + } +} + formal_parameter_list ')' function_post +{ + if ($1 != NULL) { + pop_scope(); + } + + // We use formal_parameter_identifier, because that can match a type + // name with or without an identifier, but especially without, which + // is what follows the keyword "operator" in a typecast function. + // As an added bonus, the type of the formal_parameter will be the + // typecast type, i.e. the return type of the typecast function. + + // We give all typecast operators the name "operator typecast". + // Only their return type will differentiate them. (Naturally, this + // violates actual C++ rules, but we're not an actual C++ compiler + // so we don't care. And this is the easiest way to do it.) + CPPIdentifier *ident = $1; + if (ident == NULL) { + ident = new CPPIdentifier("operator typecast", @1.file); + } else { + ident->add_name("operator typecast"); + } + $$ = CPPInstance::make_typecast_function + (new CPPInstance($2, $3, 0, @3.file), ident, $6, $8); +} + | KW_OPERATOR KW_CONST type not_paren_formal_parameter_identifier '(' +{ + if ($1 != NULL) { + push_scope($1->get_scope(current_scope, global_scope)); + } +} + formal_parameter_list ')' function_post +{ + if ($1 != NULL) { + pop_scope(); + } + + CPPIdentifier *ident = $1; + if (ident == NULL) { + ident = new CPPIdentifier("operator typecast", @1.file); + } else { + ident->add_name("operator typecast"); + } + $4->add_modifier(IIT_const); + $$ = CPPInstance::make_typecast_function + (new CPPInstance($3, $4, 0, @4.file), ident, $7, $9); +} + +/* Not actually a function prototype, but maybe a template + instantiation. Just included here (instead of somewhere else) to + avoid shift/reduce conflicts. */ + | IDENTIFIER +{ + CPPDeclaration *decl = + $1->find_symbol(current_scope, global_scope, current_lexer); + if (decl != (CPPDeclaration *)NULL) { + $$ = decl->as_instance(); + } else { + $$ = (CPPInstance *)NULL; + } +} + ; + +function_post: + empty +{ + $$ = 0; +} + | KW_CONST +{ + $$ = (int)CPPFunctionType::F_const_method; +} + | function_post KW_THROW '(' ')' +{ + $$ = $1; +} + | function_post KW_THROW '(' name ')' +{ + $$ = $1; +} + ; + +function_operator: + '!' +{ + $$ = "!"; +} + | '~' +{ + $$ = "~"; +} + | '*' +{ + $$ = "*"; +} + | '/' +{ + $$ = "/"; +} + | '%' +{ + $$ = "%"; +} + | '+' +{ + $$ = "+"; +} + | '-' +{ + $$ = "-"; +} + | '|' +{ + $$ = "|"; +} + | '&' +{ + $$ = "&"; +} + | '^' +{ + $$ = "^"; +} + | OROR +{ + $$ = "||"; +} + | ANDAND +{ + $$ = "&&"; +} + | EQCOMPARE +{ + $$ = "=="; +} + | NECOMPARE +{ + $$ = "!="; +} + | LECOMPARE +{ + $$ = "<="; +} + | GECOMPARE +{ + $$ = ">="; +} + | '<' +{ + $$ = "<"; +} + | '>' +{ + $$ = ">"; +} + | LSHIFT +{ + $$ = "<<"; +} + | RSHIFT +{ + $$ = ">>"; +} + | '=' +{ + $$ = "="; +} + | ',' +{ + $$ = ","; +} + | PLUSPLUS +{ + $$ = "++"; +} + | MINUSMINUS +{ + $$ = "--"; +} + | TIMESEQUAL +{ + $$ = "*="; +} + | DIVIDEEQUAL +{ + $$ = "/="; +} + | MODEQUAL +{ + $$ = "%="; +} + | PLUSEQUAL +{ + $$ = "+="; +} + | MINUSEQUAL +{ + $$ = "-="; +} + | OREQUAL +{ + $$ = "|="; +} + | ANDEQUAL +{ + $$ = "&="; +} + | XOREQUAL +{ + $$ = "^="; +} + | LSHIFTEQUAL +{ + $$ = "<<="; +} + | RSHIFTEQUAL +{ + $$ = ">>="; +} + | POINTSAT +{ + $$ = "->"; +} + | '[' ']' +{ + $$ = "[]"; +} + | '(' ')' +{ + $$ = "()"; +} + | KW_NEW +{ + $$ = "new"; +} + | KW_DELETE +{ + $$ = "delete"; +} + ; + +more_template_declaration: + type_like_declaration + | template_declaration + ; + +template_declaration: + KW_TEMPLATE +{ + push_scope(new CPPTemplateScope(current_scope)); +} + '<' template_formal_parameters '>' more_template_declaration +{ + pop_scope(); +} + ; + +template_formal_parameters: + empty + | template_nonempty_formal_parameters + ; + +template_nonempty_formal_parameters: + template_formal_parameter +{ + CPPTemplateScope *ts = current_scope->as_template_scope(); + assert(ts != NULL); + ts->add_template_parameter($1); +} + | template_nonempty_formal_parameters ',' template_formal_parameter +{ + CPPTemplateScope *ts = current_scope->as_template_scope(); + assert(ts != NULL); + ts->add_template_parameter($3); +} + ; + +template_formal_parameter: + KW_CLASS name +{ + $$ = CPPType::new_type(new CPPClassTemplateParameter($2)); +} + | KW_CLASS name '=' full_type +{ + $$ = CPPType::new_type(new CPPClassTemplateParameter($2, $4)); +} + | template_formal_parameter_type formal_parameter_identifier template_parameter_maybe_initialize +{ + CPPInstance *inst = new CPPInstance($1, $2, 0, @2.file); + inst->set_initializer($3); + $$ = inst; +} + | KW_CONST template_formal_parameter_type formal_parameter_identifier template_parameter_maybe_initialize +{ + $3->add_modifier(IIT_const); + CPPInstance *inst = new CPPInstance($2, $3, 0, @3.file); + inst->set_initializer($4); + $$ = inst; +} + ; + +template_formal_parameter_type: + simple_type +{ + $$ = CPPType::new_type($1); +} + | IDENTIFIER +{ + yywarning("Not a type: " + $1->get_fully_scoped_name(), @1); + $$ = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown)); +} + | TYPENAME_IDENTIFIER +{ + $$ = $1->find_type(current_scope, global_scope, false, current_lexer); + assert($$ != NULL); +} + | KW_TYPENAME name +{ + $$ = CPPType::new_type(new CPPTBDType($2)); +} + ; + + +instance_identifier: + name +{ + $$ = new CPPInstanceIdentifier($1); +} + | KW_OPERATOR function_operator +{ + // For an operator function. We implement this simply by building a + // ficticious name for the function; in other respects it's just + // like a regular function. + CPPIdentifier *ident = $1; + if (ident == NULL) { + ident = new CPPIdentifier("operator "+$2, @2.file); + } else { + ident->_names.push_back("operator "+$2); + } + + $$ = new CPPInstanceIdentifier(ident); +} + | KW_CONST instance_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_const); +} + | '*' instance_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_pointer); +} + | '&' instance_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_reference); +} + | SCOPING '*' instance_identifier %prec UNARY +{ + $$ = $3; + $$->add_scoped_pointer_modifier($1); +} + | instance_identifier '[' optional_const_expr ']' +{ + $$ = $1; + $$->add_array_modifier($3); +} + | '(' instance_identifier ')' +{ + $$ = $2; + $$->add_modifier(IIT_paren); +} + | instance_identifier '(' +{ + push_scope($1->get_scope(current_scope, global_scope)); +} + formal_parameter_list ')' function_post +{ + pop_scope(); + $$ = $1; + $$->add_func_modifier($4, $6); +} + ; + + +formal_parameter_list: + empty +{ + $$ = new CPPParameterList; +} + | ELLIPSIS +{ + $$ = new CPPParameterList; + $$->_includes_ellipsis = true; +} + | formal_parameters +{ + $$ = $1; +} + | formal_parameters ',' ELLIPSIS +{ + $$ = $1; + $$->_includes_ellipsis = true; +} + | formal_parameters ELLIPSIS +{ + $$ = $1; + $$->_includes_ellipsis = true; +} + ; + +formal_parameters: + formal_parameter +{ + $$ = new CPPParameterList; + $$->_parameters.push_back($1); +} + | formal_parameters ',' formal_parameter +{ + $$ = $1; + $$->_parameters.push_back($3); +} + ; + +template_parameter_maybe_initialize: + empty +{ + $$ = (CPPExpression *)NULL; +} + | '=' no_angle_bracket_const_expr +{ + $$ = $2; +} + ; + +maybe_initialize: + empty +{ + $$ = (CPPExpression *)NULL; +} + | '=' const_expr +{ + $$ = $2; +} + ; + +maybe_initialize_or_function_body: + ';' +{ + $$ = (CPPExpression *)NULL; +} + | '{' code '}' +{ + $$ = (CPPExpression *)NULL; +} + | ':' constructor_inits '{' code '}' +{ + $$ = (CPPExpression *)NULL; +} + | '=' const_expr ';' +{ + $$ = $2; +} + | '=' '{' structure_init '}' +{ + $$ = (CPPExpression *)NULL; +} + ; + +structure_init: + empty + | structure_init_body + | structure_init_body ',' + ; + +structure_init_body: + const_expr +{ +} + | '{' structure_init '}' + | structure_init_body ',' const_expr + | structure_init_body ',' '{' structure_init '}' + ; + +formal_parameter: + type formal_parameter_identifier maybe_initialize +{ + $$ = new CPPInstance($1, $2, 0, @2.file); + $$->set_initializer($3); +} + | IDENTIFIER formal_parameter_identifier maybe_initialize +{ + yywarning("Not a type: " + $1->get_fully_scoped_name(), @1); + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown)); + $$ = new CPPInstance(type, $2, 0, @2.file); + $$->set_initializer($3); +} + | KW_CONST type formal_parameter_identifier maybe_initialize +{ + $3->add_modifier(IIT_const); + $$ = new CPPInstance($2, $3, 0, @3.file); + $$->set_initializer($4); +} + ; + +not_paren_formal_parameter_identifier: + empty +{ + $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL); +} + | IDENTIFIER +{ + $$ = new CPPInstanceIdentifier($1); +} + | TYPENAME_IDENTIFIER +{ + $$ = new CPPInstanceIdentifier($1); +} + | KW_CONST not_paren_formal_parameter_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_const); +} + | '*' not_paren_formal_parameter_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_pointer); +} + | '&' not_paren_formal_parameter_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_reference); +} + | SCOPING '*' not_paren_formal_parameter_identifier %prec UNARY +{ + $$ = $3; + $$->add_scoped_pointer_modifier($1); +} + | not_paren_formal_parameter_identifier '[' optional_const_expr ']' +{ + $$ = $1; + $$->add_array_modifier($3); +} + ; + +formal_parameter_identifier: + empty +{ + $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL); +} + | IDENTIFIER +{ + $$ = new CPPInstanceIdentifier($1); +} + | TYPENAME_IDENTIFIER +{ + $$ = new CPPInstanceIdentifier($1); +} + | KW_CONST formal_parameter_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_const); +} + | '*' formal_parameter_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_pointer); +} + | '&' formal_parameter_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_reference); +} + | SCOPING '*' formal_parameter_identifier %prec UNARY +{ + $$ = $3; + $$->add_scoped_pointer_modifier($1); +} + | formal_parameter_identifier '[' optional_const_expr ']' +{ + $$ = $1; + $$->add_array_modifier($3); +} + | '(' formal_parameter_identifier ')' '(' formal_parameter_list ')' function_post +{ + $$ = $2; + $$->add_modifier(IIT_paren); + $$->add_func_modifier($5, $7); +} + ; + +empty_instance_identifier: + empty +{ + $$ = new CPPInstanceIdentifier((CPPIdentifier *)NULL); +} + | KW_CONST empty_instance_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_const); +} + | '*' empty_instance_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_pointer); +} + | '&' empty_instance_identifier %prec UNARY +{ + $$ = $2; + $$->add_modifier(IIT_reference); +} + | SCOPING '*' empty_instance_identifier %prec UNARY +{ + $$ = $3; + $$->add_scoped_pointer_modifier($1); +} + | empty_instance_identifier '[' optional_const_expr ']' +{ + $$ = $1; + $$->add_array_modifier($3); +} + | '(' empty_instance_identifier ')' '(' formal_parameter_list ')' function_post +{ + $$ = $2; + $$->add_modifier(IIT_paren); + $$->add_func_modifier($5, $7); +} + ; + +type: + simple_type +{ + $$ = CPPType::new_type($1); +} + | TYPENAME_IDENTIFIER +{ + $$ = $1->find_type(current_scope, global_scope, false, current_lexer); + assert($$ != NULL); +} + | KW_TYPENAME name +{ + $$ = CPPType::new_type(new CPPTBDType($2)); +} + | anonymous_struct +{ + $$ = CPPType::new_type($1); +} + | named_struct +{ + $$ = CPPType::new_type($1); +} + | anonymous_enum +{ + $$ = CPPType::new_type($1); +} + | named_enum +{ + $$ = CPPType::new_type($1); +} + | struct_keyword name +{ + CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + if (type != NULL) { + $$ = type; + } else { + CPPExtensionType *et = + CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + ->as_extension_type(); + CPPScope *scope = $2->get_scope(current_scope, global_scope); + if (scope != NULL) { + scope->define_extension_type(et); + } + $$ = et; + } +} + | enum_keyword name +{ + CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + if (type != NULL) { + $$ = type; + } else { + CPPExtensionType *et = + CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + ->as_extension_type(); + CPPScope *scope = $2->get_scope(current_scope, global_scope); + if (scope != NULL) { + scope->define_extension_type(et); + } + $$ = et; + } +} + ; + +type_decl: + simple_type +{ + $$ = CPPType::new_type($1); +} + | TYPENAME_IDENTIFIER +{ + $$ = $1->find_type(current_scope, global_scope, false, current_lexer); + assert($$ != NULL); +} + | KW_TYPENAME name +{ + $$ = CPPType::new_type(new CPPTBDType($2)); +} + | anonymous_struct +{ + $$ = CPPType::new_type($1); +} + | named_struct +{ + $$ = new CPPTypeDeclaration(CPPType::new_type($1)); +} + | anonymous_enum +{ + $$ = CPPType::new_type($1); +} + | named_enum +{ + $$ = new CPPTypeDeclaration(CPPType::new_type($1)); +} + | struct_keyword name +{ + CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + if (type != NULL) { + $$ = type; + } else { + CPPExtensionType *et = + CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + ->as_extension_type(); + CPPScope *scope = $2->get_scope(current_scope, global_scope); + if (scope != NULL) { + scope->define_extension_type(et); + } + $$ = et; + } +} + | enum_keyword name +{ + CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + if (type != NULL) { + $$ = type; + } else { + CPPExtensionType *et = + CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + ->as_extension_type(); + CPPScope *scope = $2->get_scope(current_scope, global_scope); + if (scope != NULL) { + scope->define_extension_type(et); + } + $$ = et; + } +} + ; + +predefined_type: + simple_type +{ + $$ = CPPType::new_type($1); +} + | TYPENAME_IDENTIFIER +{ + $$ = $1->find_type(current_scope, global_scope, false, current_lexer); + assert($$ != NULL); +} + | KW_TYPENAME name +{ + $$ = CPPType::new_type(new CPPTBDType($2)); +} + | struct_keyword name +{ + CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + if (type != NULL) { + $$ = type; + } else { + CPPExtensionType *et = + CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + ->as_extension_type(); + CPPScope *scope = $2->get_scope(current_scope, global_scope); + if (scope != NULL) { + scope->define_extension_type(et); + } + $$ = et; + } +} + | enum_keyword name +{ + CPPType *type = $2->find_type(current_scope, global_scope, false, current_lexer); + if (type != NULL) { + $$ = type; + } else { + CPPExtensionType *et = + CPPType::new_type(new CPPExtensionType($1, $2, current_scope, @1.file)) + ->as_extension_type(); + CPPScope *scope = $2->get_scope(current_scope, global_scope); + if (scope != NULL) { + scope->define_extension_type(et); + } + $$ = et; + } +} + ; + +full_type: + type empty_instance_identifier +{ + CPPInstance *inst = new CPPInstance($1, $2, 0, @1.file); + $$ = inst->_type; + delete inst; +} + | KW_CONST type empty_instance_identifier +{ + $3->add_modifier(IIT_const); + CPPInstance *inst = new CPPInstance($2, $3, 0, @1.file); + $$ = inst->_type; + delete inst; +} + ; + +anonymous_struct: + struct_keyword '{' +{ + CPPVisibility starting_vis = + ($1 == CPPExtensionType::T_class) ? V_private : V_public; + + CPPScope *new_scope = new CPPScope(current_scope, CPPNameComponent("anon"), + starting_vis); + CPPStructType *st = new CPPStructType($1, NULL, current_scope, + new_scope, @1.file); + new_scope->set_struct_type(st); + + push_scope(new_scope); + push_struct(st); +} + cpp '}' +{ + $$ = current_struct; + current_struct->_incomplete = false; + pop_struct(); + pop_scope(); +} + ; + +named_struct: + struct_keyword name +{ + CPPVisibility starting_vis = + ($1 == CPPExtensionType::T_class) ? V_private : V_public; + + CPPScope *scope = $2->get_scope(current_scope, global_scope, current_lexer); + if (scope == NULL) { + scope = current_scope; + } + CPPScope *new_scope = new CPPScope(scope, $2->_names.back(), + starting_vis); + + CPPStructType *st = new CPPStructType($1, $2, current_scope, + new_scope, @1.file); + new_scope->set_struct_type(st); + current_scope->define_extension_type(st); + + push_scope(new_scope); + push_struct(st); +} + maybe_class_derivation '{' cpp '}' +{ + $$ = current_struct; + current_struct->_incomplete = false; + pop_struct(); + pop_scope(); +} + ; + +maybe_class_derivation: + empty + | class_derivation + ; + +class_derivation: + ':' base_specification + | class_derivation ',' base_specification + ; + +base_specification: + KW_PUBLIC class_derivation_name +{ + current_struct->append_derivation($2, V_public, false); +} + | KW_PROTECTED class_derivation_name +{ + current_struct->append_derivation($2, V_protected, false); +} + | KW_PRIVATE class_derivation_name +{ + current_struct->append_derivation($2, V_private, false); +} + | KW_VIRTUAL KW_PUBLIC class_derivation_name +{ + current_struct->append_derivation($3, V_public, true); +} + | KW_VIRTUAL KW_PROTECTED class_derivation_name +{ + current_struct->append_derivation($3, V_protected, true); +} + | KW_VIRTUAL KW_PRIVATE class_derivation_name +{ + current_struct->append_derivation($3, V_private, true); +} + | KW_PUBLIC KW_VIRTUAL class_derivation_name +{ + current_struct->append_derivation($3, V_public, true); +} + | KW_PROTECTED KW_VIRTUAL class_derivation_name +{ + current_struct->append_derivation($3, V_protected, true); +} + | KW_PRIVATE KW_VIRTUAL class_derivation_name +{ + current_struct->append_derivation($3, V_private, true); +} + ; + +anonymous_enum: + enum_keyword '{' +{ + current_enum = new CPPEnumType(NULL, current_scope, @1.file); +} + enum_body '}' +{ + $$ = current_enum; + current_enum = NULL; +} + ; + +named_enum: + enum_keyword name '{' +{ + current_enum = new CPPEnumType($2, current_scope, @1.file); +} + enum_body '}' +{ + $$ = current_enum; + current_enum = NULL; +} + ; + +enum_body: + empty + | enum_body_no_trailing_comma + | enum_body_no_trailing_comma ',' + ; + +enum_body_no_trailing_comma: + name +{ + assert(current_enum != NULL); + current_enum->add_element($1->get_simple_name(), current_scope); +} + | name '=' const_expr +{ + assert(current_enum != NULL); + current_enum->add_element($1->get_simple_name(), current_scope, $3); +} + | enum_body_no_trailing_comma ',' name +{ + assert(current_enum != NULL); + current_enum->add_element($3->get_simple_name(), current_scope); +} + | enum_body_no_trailing_comma ',' name '=' const_expr +{ + assert(current_enum != NULL); + current_enum->add_element($3->get_simple_name(), current_scope, $5); +} + ; + +enum_keyword: + KW_ENUM +{ + $$ = CPPExtensionType::T_enum; +} + ; + +struct_keyword: + KW_CLASS +{ + $$ = CPPExtensionType::T_class; +} + | KW_STRUCT +{ + $$ = CPPExtensionType::T_struct; +} + | KW_UNION +{ + $$ = CPPExtensionType::T_union; +} + ; + +namespace_declaration: + KW_NAMESPACE name '{' +{ + CPPScope *scope = $2->find_scope(current_scope, global_scope, current_lexer); + if (scope == NULL) { + // This must be a new namespace declaration. + CPPScope *parent_scope = + $2->get_scope(current_scope, global_scope, current_lexer); + if (parent_scope == NULL) { + parent_scope = current_scope; + } + scope = new CPPScope(parent_scope, $2->_names.back(), V_public); + } + + CPPNamespace *nspace = new CPPNamespace($2, scope, @1.file); + current_scope->add_declaration(nspace, global_scope, current_lexer, @1); + current_scope->define_namespace(nspace); + push_scope(scope); +} + cpp '}' +{ + pop_scope(); +} + | KW_NAMESPACE '{' cpp '}' + ; + +using_declaration: + KW_USING name +{ + CPPUsing *using_decl = new CPPUsing($2, false, @1.file); + current_scope->add_declaration(using_decl, global_scope, current_lexer, @1); + current_scope->add_using(using_decl, global_scope, current_lexer); +} + | KW_USING KW_NAMESPACE name +{ + CPPUsing *using_decl = new CPPUsing($3, true, @1.file); + current_scope->add_declaration(using_decl, global_scope, current_lexer, @1); + current_scope->add_using(using_decl, global_scope, current_lexer); +} + ; + +simple_type: + simple_int_type + | simple_float_type + | simple_void_type + ; + +simple_int_type: + KW_BOOL +{ + $$ = new CPPSimpleType(CPPSimpleType::T_bool); +} + | KW_CHAR +{ + $$ = new CPPSimpleType(CPPSimpleType::T_char); +} + | KW_SHORT +{ + $$ = new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_short); +} + | KW_LONG +{ + $$ = new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_long); +} + | KW_LONGLONG +{ + $$ = new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_longlong); +} + | KW_UNSIGNED +{ + $$ = new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_unsigned); +} + | KW_SIGNED +{ + $$ = new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_signed); +} + | KW_INT +{ + $$ = new CPPSimpleType(CPPSimpleType::T_int); +} + | KW_SHORT simple_int_type +{ + $$ = $2; + $$->_flags |= CPPSimpleType::F_short; +} + | KW_LONG simple_int_type +{ + $$ = $2; + if ($$->_flags & CPPSimpleType::F_long) { + $$->_flags |= CPPSimpleType::F_longlong; + } else { + $$->_flags |= CPPSimpleType::F_long; + } +} + | KW_UNSIGNED simple_int_type +{ + $$ = $2; + $$->_flags |= CPPSimpleType::F_unsigned; +} + | KW_SIGNED simple_int_type +{ + $$ = $2; + $$->_flags |= CPPSimpleType::F_signed; +} + ; + +simple_float_type: + KW_FLOAT +{ + $$ = new CPPSimpleType(CPPSimpleType::T_float); +} + | KW_LONG KW_FLOAT +{ + $$ = new CPPSimpleType(CPPSimpleType::T_float, + CPPSimpleType::F_long); +} + | KW_DOUBLE +{ + $$ = new CPPSimpleType(CPPSimpleType::T_double); +} + | KW_LONG KW_LONG KW_FLOAT +{ + $$ = new CPPSimpleType(CPPSimpleType::T_float, + CPPSimpleType::F_longlong); +} + | KW_LONG KW_DOUBLE +{ + $$ = new CPPSimpleType(CPPSimpleType::T_double, + CPPSimpleType::F_long); +} + ; + +simple_void_type: + KW_VOID +{ + $$ = new CPPSimpleType(CPPSimpleType::T_void); +} + ; + +/* We don't care what the code is. We just want to be sure we match + up opening and closing braces properly. For anything else, we'll + accept just token salad. */ +code: +{ + current_lexer->_resolve_identifiers = false; +} + code_block +{ + current_lexer->_resolve_identifiers = true; +} + ; + +code_block: + empty + | code_block element + ; + +element: + REAL +{ +} + | INTEGER +{ +} + | STRING +{ +} + | CHAR +{ +} + | IDENTIFIER +{ +} + | TYPENAME_IDENTIFIER +{ +} + | SCOPING +{ +} + | SIMPLE_IDENTIFIER +{ +} + | ELLIPSIS | OROR | ANDAND + | EQCOMPARE | NECOMPARE | LECOMPARE | GECOMPARE + | LSHIFT | RSHIFT | POINTSAT_STAR | DOT_STAR | POINTSAT + | SCOPE | PLUSPLUS | MINUSMINUS + | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL + | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL + | KW_BOOL | KW_CATCH | KW_CHAR | KW_CLASS | KW_CONST + | KW_DELETE | KW_DOUBLE | KW_DYNAMIC_CAST | KW_ELSE | KW_ENUM + | KW_EXTERN | KW_EXPLICIT | KW_FALSE + | KW_FLOAT | KW_FRIEND | KW_FOR | KW_GOTO + | KW_IF | KW_INLINE | KW_INT + | KW_LONG | KW_MUTABLE | KW_NEW | KW_PRIVATE | KW_PROTECTED + | KW_PUBLIC | KW_PUBLISHED | KW_REGISTER | KW_RETURN + | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC | KW_STATIC_CAST + | KW_STRUCT | KW_THROW | KW_TRUE | KW_TRY | KW_TYPEDEF | KW_TYPENAME + | KW_UNION | KW_UNSIGNED | KW_VIRTUAL | KW_VOID | KW_VOLATILE + | KW_WHILE + | KW_OPERATOR +{ +} + | '+' | '-' | '*' | '/' | '&' | '|' | '^' | '!' | '~' | '=' | '%' + | '<' | '>' | '(' | ')' | '.' | ',' | ';' | ':' | '[' | ']' + | '?' | '{' code_block '}' + ; + +optional_const_expr: + empty +{ + $$ = (CPPExpression *)NULL; +} + | const_expr +{ + $$ = $1; +} + ; + +optional_const_expr_comma: + empty +{ + $$ = (CPPExpression *)NULL; +} + | const_expr_comma +{ + $$ = $1; +} + ; + +const_expr_comma: + const_expr +{ + $$ = $1; +} + | const_expr_comma ',' const_expr +{ + $$ = new CPPExpression(',', $1, $3) +} + ; + +no_angle_bracket_const_expr: + const_operand +{ + $$ = $1; +} + | '(' full_type ')' no_angle_bracket_const_expr %prec UNARY +{ + $$ = new CPPExpression(CPPExpression::typecast_op($2, $4)); +} + | KW_STATIC_CAST '<' full_type '>' '(' const_expr_comma ')' +{ + $$ = new CPPExpression(CPPExpression::typecast_op($3, $6)); +} + | KW_DYNAMIC_CAST '<' full_type '>' '(' const_expr_comma ')' +{ + $$ = new CPPExpression(CPPExpression::typecast_op($3, $6)); +} + | KW_SIZEOF '(' full_type ')' %prec UNARY +{ + $$ = new CPPExpression(CPPExpression::sizeof_func($3)); +} + | '!' no_angle_bracket_const_expr %prec UNARY +{ + $$ = new CPPExpression(UNARY_NOT, $2); +} + | '~' no_angle_bracket_const_expr %prec UNARY +{ + $$ = new CPPExpression(UNARY_NEGATE, $2); +} + | '-' no_angle_bracket_const_expr %prec UNARY +{ + if ($2->_type == CPPExpression::T_integer) { + $$ = $2; + $$->_u._integer = -$$->_u._integer; + } else if ($2->_type == CPPExpression::T_real) { + $$ = $2; + $$->_u._real = -$$->_u._real; + } else { + $$ = new CPPExpression(UNARY_MINUS, $2); + } +} + | '*' no_angle_bracket_const_expr %prec UNARY +{ + $$ = new CPPExpression(UNARY_STAR, $2); +} + | '&' no_angle_bracket_const_expr %prec UNARY +{ + $$ = new CPPExpression(UNARY_REF, $2); +} + | no_angle_bracket_const_expr '*' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('*', $1, $3) +} + | no_angle_bracket_const_expr '/' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('/', $1, $3) +} + | no_angle_bracket_const_expr '%' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('%', $1, $3) +} + | no_angle_bracket_const_expr '+' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('+', $1, $3) +} + | no_angle_bracket_const_expr '-' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('-', $1, $3) +} + | no_angle_bracket_const_expr '|' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('|', $1, $3) +} + | no_angle_bracket_const_expr '&' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('&', $1, $3) +} + | no_angle_bracket_const_expr OROR no_angle_bracket_const_expr +{ + $$ = new CPPExpression(OROR, $1, $3) +} + | no_angle_bracket_const_expr ANDAND no_angle_bracket_const_expr +{ + $$ = new CPPExpression(ANDAND, $1, $3) +} + | no_angle_bracket_const_expr EQCOMPARE no_angle_bracket_const_expr +{ + $$ = new CPPExpression(EQCOMPARE, $1, $3) +} + | no_angle_bracket_const_expr NECOMPARE no_angle_bracket_const_expr +{ + $$ = new CPPExpression(NECOMPARE, $1, $3) +} + | no_angle_bracket_const_expr LECOMPARE no_angle_bracket_const_expr +{ + $$ = new CPPExpression(LECOMPARE, $1, $3) +} + | no_angle_bracket_const_expr GECOMPARE no_angle_bracket_const_expr +{ + $$ = new CPPExpression(GECOMPARE, $1, $3) +} + | no_angle_bracket_const_expr LSHIFT no_angle_bracket_const_expr +{ + $$ = new CPPExpression(LSHIFT, $1, $3) +} + | no_angle_bracket_const_expr RSHIFT no_angle_bracket_const_expr +{ + $$ = new CPPExpression(RSHIFT, $1, $3) +} + | no_angle_bracket_const_expr '?' no_angle_bracket_const_expr ':' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('?', $1, $3, $5) +} + | no_angle_bracket_const_expr '[' const_expr ']' +{ + $$ = new CPPExpression('[', $1, $3); +} + | no_angle_bracket_const_expr '(' const_expr_comma ')' +{ + $$ = new CPPExpression('f', $1, $3); +} + | no_angle_bracket_const_expr '(' ')' +{ + $$ = new CPPExpression('f', $1); +} + | no_angle_bracket_const_expr '.' no_angle_bracket_const_expr +{ + $$ = new CPPExpression('.', $1, $3) +} + | no_angle_bracket_const_expr POINTSAT no_angle_bracket_const_expr +{ + $$ = new CPPExpression(POINTSAT, $1, $3) +} + | '(' const_expr_comma ')' +{ + $$ = $2; +} + ; + + +const_expr: + const_operand +{ + $$ = $1; +} + | '(' full_type ')' const_expr %prec UNARY +{ + $$ = new CPPExpression(CPPExpression::typecast_op($2, $4)); +} + | KW_STATIC_CAST '<' full_type '>' '(' const_expr_comma ')' +{ + $$ = new CPPExpression(CPPExpression::typecast_op($3, $6)); +} + | KW_DYNAMIC_CAST '<' full_type '>' '(' const_expr_comma ')' +{ + $$ = new CPPExpression(CPPExpression::typecast_op($3, $6)); +} + | TYPENAME_IDENTIFIER '(' optional_const_expr_comma ')' +{ + // A constructor call. + CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer); + assert(type != NULL); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_INT '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_CHAR '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_BOOL '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_bool)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_SHORT '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_short)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_LONG '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_long)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_UNSIGNED '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_unsigned)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_SIGNED '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_signed)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_FLOAT '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_float)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_DOUBLE '(' optional_const_expr_comma ')' +{ + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_double)); + $$ = new CPPExpression(CPPExpression::construct_op(type, $3)); +} + | KW_SIZEOF '(' full_type ')' %prec UNARY +{ + $$ = new CPPExpression(CPPExpression::sizeof_func($3)); +} + | KW_NEW predefined_type %prec UNARY +{ + $$ = new CPPExpression(CPPExpression::new_op($2)); +} + | KW_NEW predefined_type '(' optional_const_expr_comma ')' %prec UNARY +{ + $$ = new CPPExpression(CPPExpression::new_op($2, $4)); +} + | '!' const_expr %prec UNARY +{ + $$ = new CPPExpression(UNARY_NOT, $2); +} + | '~' const_expr %prec UNARY +{ + $$ = new CPPExpression(UNARY_NEGATE, $2); +} + | '-' const_expr %prec UNARY +{ + if ($2->_type == CPPExpression::T_integer) { + $$ = $2; + $$->_u._integer = -$$->_u._integer; + } else if ($2->_type == CPPExpression::T_real) { + $$ = $2; + $$->_u._real = -$$->_u._real; + } else { + $$ = new CPPExpression(UNARY_MINUS, $2); + } +} + | '*' const_expr %prec UNARY +{ + $$ = new CPPExpression(UNARY_STAR, $2); +} + | '&' const_expr %prec UNARY +{ + $$ = new CPPExpression(UNARY_REF, $2); +} + | const_expr '*' const_expr +{ + $$ = new CPPExpression('*', $1, $3) +} + | const_expr '/' const_expr +{ + $$ = new CPPExpression('/', $1, $3) +} + | const_expr '%' const_expr +{ + $$ = new CPPExpression('%', $1, $3) +} + | const_expr '+' const_expr +{ + $$ = new CPPExpression('+', $1, $3) +} + | const_expr '-' const_expr +{ + $$ = new CPPExpression('-', $1, $3) +} + | const_expr '|' const_expr +{ + $$ = new CPPExpression('|', $1, $3) +} + | const_expr '&' const_expr +{ + $$ = new CPPExpression('&', $1, $3) +} + | const_expr OROR const_expr +{ + $$ = new CPPExpression(OROR, $1, $3) +} + | const_expr ANDAND const_expr +{ + $$ = new CPPExpression(ANDAND, $1, $3) +} + | const_expr EQCOMPARE const_expr +{ + $$ = new CPPExpression(EQCOMPARE, $1, $3) +} + | const_expr NECOMPARE const_expr +{ + $$ = new CPPExpression(NECOMPARE, $1, $3) +} + | const_expr LECOMPARE const_expr +{ + $$ = new CPPExpression(LECOMPARE, $1, $3) +} + | const_expr GECOMPARE const_expr +{ + $$ = new CPPExpression(GECOMPARE, $1, $3) +} + | const_expr '<' const_expr +{ + $$ = new CPPExpression('<', $1, $3) +} + | const_expr '>' const_expr +{ + $$ = new CPPExpression('>', $1, $3) +} + | const_expr LSHIFT const_expr +{ + $$ = new CPPExpression(LSHIFT, $1, $3) +} + | const_expr RSHIFT const_expr +{ + $$ = new CPPExpression(RSHIFT, $1, $3) +} + | const_expr '?' const_expr ':' const_expr +{ + $$ = new CPPExpression('?', $1, $3, $5) +} + | const_expr '[' const_expr ']' +{ + $$ = new CPPExpression('[', $1, $3); +} + | const_expr '(' const_expr_comma ')' +{ + $$ = new CPPExpression('f', $1, $3); +} + | const_expr '(' ')' +{ + $$ = new CPPExpression('f', $1); +} + | const_expr '.' const_expr +{ + $$ = new CPPExpression('.', $1, $3) +} + | const_expr POINTSAT const_expr +{ + $$ = new CPPExpression(POINTSAT, $1, $3) +} + | '(' const_expr_comma ')' +{ + $$ = $2; +} + ; + +const_operand: + INTEGER +{ + $$ = new CPPExpression($1); +} + | KW_TRUE +{ + $$ = new CPPExpression(true); +} + | KW_FALSE +{ + $$ = new CPPExpression(false); +} + | CHAR +{ + $$ = new CPPExpression($1); +} + | REAL +{ + $$ = new CPPExpression($1); +} + | string +{ + $$ = new CPPExpression($1); +} + | IDENTIFIER +{ + $$ = new CPPExpression($1, current_scope, global_scope, current_lexer); +} + ; + +class_derivation_name: + name +{ + CPPType *type = $1->find_type(current_scope, global_scope, true); + if (type == NULL) { + type = CPPType::new_type(new CPPTBDType($1)); + } + $$ = type; +} + | struct_keyword name +{ + CPPType *type = $2->find_type(current_scope, global_scope, true, current_lexer); + if (type == NULL) { + type = CPPType::new_type(new CPPTBDType($2)); + } + $$ = type; +} + | KW_TYPENAME name +{ + $$ = CPPType::new_type(new CPPTBDType($2)); +} + ; + +/* +typedefname: + TYPENAME_IDENTIFIER +{ + CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer); + assert(type != NULL); + $$ = type; +} + | KW_TYPENAME name +{ + $$ = CPPType::new_type(new CPPTBDType($2)); +} + ; +*/ + + +name: + IDENTIFIER +{ + $$ = $1; +} + | TYPENAME_IDENTIFIER +{ + $$ = $1; +} + ; + +string: + STRING +{ + $$ = $1; +} + | string STRING +{ + $$ = $1 + $2; +} + ; + +empty: + ; diff --git a/dtool/src/cppparser/cppBisonDefs.h b/dtool/src/cppparser/cppBisonDefs.h new file mode 100644 index 0000000000..825f19513e --- /dev/null +++ b/dtool/src/cppparser/cppBisonDefs.h @@ -0,0 +1,96 @@ +// Filename: cppBison.h +// Created by: drose (17Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPBISON_H +#define CPPBISON_H + +// This header file defines the interface to the yacc (actually, +// bison) parser and grammar. None of these interfaces are intended +// to be used directly; they're defined here strictly to be used by +// the CPPParser and CPPExpressionParser classes. + +#include + +#include + +#include "cppExtensionType.h" +#include "cppFile.h" + +using namespace std; + +class CPPParser; +class CPPExpression; +class CPPPreprocessor; +class CPPDeclaration; +class CPPInstance; +class CPPType; +class CPPStructType; +class CPPEnumType; +class CPPSimpleType; +class CPPInstanceIdentifier; +class CPPParameterList; +class CPPTemplateParameterList; +class CPPScope; +class CPPIdentifier; + +void parse_cpp(CPPParser *cp); +CPPExpression *parse_const_expr(CPPPreprocessor *pp, + CPPScope *new_current_scope, + CPPScope *new_global_scope); +CPPType *parse_type(CPPPreprocessor *pp, + CPPScope *new_current_scope, + CPPScope *new_global_scope); + +extern CPPScope *current_scope; +extern CPPScope *global_scope; +extern CPPPreprocessor *current_lexer; + + +// This structure holds the return value for each token. +// Traditionally, this is a union, and is declared with the %union +// declaration in the parser.y file, but unions are pretty worthless +// in C++ (you can't include an object that has member functions in a +// union), so we'll use a class instead. That means we need to +// declare it externally, here. + +class YYSTYPE { +public: + string str; + union { + int integer; + double real; + CPPScope *scope; + CPPDeclaration *decl; + CPPInstance *instance; + CPPType *type; + CPPStructType *struct_type; + CPPEnumType *enum_type; + CPPSimpleType *simple_type; + CPPInstanceIdentifier *inst_ident; + CPPParameterList *param_list; + CPPTemplateParameterList *template_param_list; + CPPExtensionType::Type extension_enum; + CPPExpression *expr; + CPPIdentifier *identifier; + } u; +}; + +// This structure takes advantage of a bison feature to track the +// exact location in the file of each token, for more useful error +// reporting. We define it up here so we can reference it in the +// lexer. + +struct cppyyltype { + int timestamp; + int first_line; + int first_column; + int last_line; + int last_column; + char *text; + CPPFile file; +}; +#define YYLTYPE cppyyltype + +#endif diff --git a/dtool/src/cppparser/cppClassTemplateParameter.cxx b/dtool/src/cppparser/cppClassTemplateParameter.cxx new file mode 100644 index 0000000000..32e74d9b01 --- /dev/null +++ b/dtool/src/cppparser/cppClassTemplateParameter.cxx @@ -0,0 +1,112 @@ +// Filename: cppClassTemplateParameter.C +// Created by: drose (28Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppClassTemplateParameter.h" +#include "cppIdentifier.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPClassTemplateParameter::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPClassTemplateParameter:: +CPPClassTemplateParameter(CPPIdentifier *ident, CPPType *default_type) : + CPPType(CPPFile()), + _ident(ident), + _default_type(default_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPClassTemplateParameter::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPClassTemplateParameter:: +is_fully_specified() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPClassTemplateParameter::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPClassTemplateParameter:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + if (complete) { + out << "class "; + _ident->output(out, scope); + if (_default_type) { + out << " = "; + _default_type->output(out, indent_level, scope, false); + } + } else { + _ident->output(out, scope); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPClassTemplateParameter::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPClassTemplateParameter:: +get_subtype() const { + return ST_class_template_parameter; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPClassTemplateParameter::as_classTemplateParameter +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPClassTemplateParameter *CPPClassTemplateParameter:: +as_class_template_parameter() { + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPClassTemplateParameter::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPClassTemplateParameter:: +is_equal(const CPPDeclaration *other) const { + const CPPClassTemplateParameter *ot = ((CPPDeclaration *)other)->as_class_template_parameter(); + assert(ot != NULL); + + if (_default_type != ot->_default_type) { + return false; + } + + return *_ident == *ot->_ident; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPClassTemplateParameter::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPClassTemplateParameter:: +is_less(const CPPDeclaration *other) const { + const CPPClassTemplateParameter *ot = ((CPPDeclaration *)other)->as_class_template_parameter(); + assert(ot != NULL); + + if (_default_type != ot->_default_type) { + return _default_type < ot->_default_type; + } + + return *_ident < *ot->_ident; +} diff --git a/dtool/src/cppparser/cppClassTemplateParameter.h b/dtool/src/cppparser/cppClassTemplateParameter.h new file mode 100644 index 0000000000..622c0c9891 --- /dev/null +++ b/dtool/src/cppparser/cppClassTemplateParameter.h @@ -0,0 +1,40 @@ +// Filename: cppClassTemplateParameter.h +// Created by: drose (28Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPCLASSTEMPLATEPARAMETER_H +#define CPPCLASSTEMPLATEPARAMETER_H + +#include + +#include "cppType.h" + +class CPPIdentifier; + +/////////////////////////////////////////////////////////////////// +// Class : CPPClassTemplateParameter +// Description : +//////////////////////////////////////////////////////////////////// +class CPPClassTemplateParameter : public CPPType { +public: + CPPClassTemplateParameter(CPPIdentifier *ident, + CPPType *default_type = NULL); + + virtual bool is_fully_specified() const; + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPClassTemplateParameter *as_class_template_parameter(); + + CPPIdentifier *_ident; + CPPType *_default_type; + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +#endif + diff --git a/dtool/src/cppparser/cppCommentBlock.cxx b/dtool/src/cppparser/cppCommentBlock.cxx new file mode 100644 index 0000000000..f0cb9aeb31 --- /dev/null +++ b/dtool/src/cppparser/cppCommentBlock.cxx @@ -0,0 +1,7 @@ +// Filename: cppCommentBlock.C +// Created by: drose (15Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "cppCommentBlock.h" + diff --git a/dtool/src/cppparser/cppCommentBlock.h b/dtool/src/cppparser/cppCommentBlock.h new file mode 100644 index 0000000000..122bde4ecd --- /dev/null +++ b/dtool/src/cppparser/cppCommentBlock.h @@ -0,0 +1,35 @@ +// Filename: cppCommentBlock.h +// Created by: drose (15Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPCOMMENTBLOCK_H +#define CPPCOMMENTBLOCK_H + +#include + +#include "cppFile.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : CPPCommentBlock +// Description : This represents a comment appearing in the source +// code. The CPPPreprocessor collects these, and saves +// the complete list of comments encountered; it also +// stores a list of the comment blocks appearing before +// each declaration. +//////////////////////////////////////////////////////////////////// +class CPPCommentBlock { +public: + CPPFile _file; + int _line_number; + int _col_number; + int _last_line; + bool _c_style; + string _comment; +}; + +typedef list CPPComments; + +#endif diff --git a/dtool/src/cppparser/cppConstType.cxx b/dtool/src/cppparser/cppConstType.cxx new file mode 100644 index 0000000000..df991b3590 --- /dev/null +++ b/dtool/src/cppparser/cppConstType.cxx @@ -0,0 +1,190 @@ +// Filename: cppConstType.C +// Created by: drose (28Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppConstType.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPConstType:: +CPPConstType(CPPType *wrapped_around) : + CPPType(CPPFile()), + _wrapped_around(wrapped_around) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPConstType:: +is_fully_specified() const { + return CPPType::is_fully_specified() && + _wrapped_around->is_fully_specified(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPConstType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + return (*si).second; + } + + CPPConstType *rep = new CPPConstType(*this); + rep->_wrapped_around = + _wrapped_around->substitute_decl(subst, current_scope, global_scope) + ->as_type(); + + if (rep->_wrapped_around == _wrapped_around) { + delete rep; + rep = this; + } + rep = CPPType::new_type(rep)->as_const_type(); + subst.insert(SubstDecl::value_type(this, rep)); + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPConstType:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + CPPType *ptype = _wrapped_around->resolve_type(current_scope, global_scope); + + if (ptype != _wrapped_around) { + CPPConstType *rep = new CPPConstType(*this); + rep->_wrapped_around = ptype; + return CPPType::new_type(rep); + } + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPConstType:: +is_tbd() const { + return _wrapped_around->is_tbd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::is_equivalent +// Access: Public, Virtual +// Description: This is a little more forgiving than is_equal(): it +// returns true if the types appear to be referring to +// the same thing, even if they may have different +// pointers or somewhat different definitions. It's +// useful for parameter matching, etc. +//////////////////////////////////////////////////////////////////// +bool CPPConstType:: +is_equivalent(const CPPType &other) const { + const CPPConstType *ot = ((CPPType *)&other)->as_const_type(); + if (ot == (CPPConstType *)NULL) { + return CPPType::is_equivalent(other); + } + + return _wrapped_around->is_equivalent(*ot->_wrapped_around); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPConstType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + _wrapped_around->output(out, indent_level, scope, complete); + out << " const"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::output_instance +// Access: Public, Virtual +// Description: Formats a C++-looking line that defines an instance +// of the given type, with the indicated name. In most +// cases this will be "type name", but some types have +// special exceptions. +//////////////////////////////////////////////////////////////////// +void CPPConstType:: +output_instance(ostream &out, int indent_level, CPPScope *scope, + bool complete, const string &prename, + const string &name) const { + _wrapped_around->output_instance(out, indent_level, scope, complete, + "const " + prename, name); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPConstType:: +get_subtype() const { + return ST_const; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::as_const_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPConstType *CPPConstType:: +as_const_type() { + return this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPConstType:: +is_equal(const CPPDeclaration *other) const { + const CPPConstType *ot = ((CPPDeclaration *)other)->as_const_type(); + assert(ot != NULL); + + return _wrapped_around == ot->_wrapped_around; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPConstType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPConstType:: +is_less(const CPPDeclaration *other) const { + const CPPConstType *ot = ((CPPDeclaration *)other)->as_const_type(); + assert(ot != NULL); + + return _wrapped_around < ot->_wrapped_around; +} diff --git a/dtool/src/cppparser/cppConstType.h b/dtool/src/cppparser/cppConstType.h new file mode 100644 index 0000000000..b6361bf761 --- /dev/null +++ b/dtool/src/cppparser/cppConstType.h @@ -0,0 +1,50 @@ +// Filename: cppConstType.h +// Created by: drose (28Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPCONSTTYPE_H +#define CPPCONSTTYPE_H + +#include + +#include "cppType.h" + +/////////////////////////////////////////////////////////////////// +// Class : CPPConstType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPConstType : public CPPType { +public: + CPPConstType(CPPType *wrapped_around); + + CPPType *_wrapped_around; + + virtual bool is_fully_specified() const; + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_tbd() const; + virtual bool is_equivalent(const CPPType &other) const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual void output_instance(ostream &out, int indent_level, + CPPScope *scope, + bool complete, const string &prename, + const string &name) const; + + virtual SubType get_subtype() const; + + virtual CPPConstType *as_const_type(); + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +#endif diff --git a/dtool/src/cppparser/cppDeclaration.cxx b/dtool/src/cppparser/cppDeclaration.cxx new file mode 100644 index 0000000000..43b1dfabb0 --- /dev/null +++ b/dtool/src/cppparser/cppDeclaration.cxx @@ -0,0 +1,373 @@ +// Filename: cppDeclaration.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppDeclaration.h" +#include "cppPreprocessor.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration:: +CPPDeclaration(const CPPFile &file) : + _file(file) +{ + _vis = V_unknown; + _template_scope = NULL; + _leading_comment = (CPPCommentBlock *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration:: +CPPDeclaration(const CPPDeclaration ©) : + _vis(copy._vis), + _template_scope(copy._template_scope), + _file(copy._file), + _leading_comment(copy._leading_comment) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration:: +~CPPDeclaration() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::Equivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPDeclaration:: +operator == (const CPPDeclaration &other) const { + if (get_subtype() != other.get_subtype()) { + return false; + } + return is_equal(&other); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::Nonequivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPDeclaration:: +operator != (const CPPDeclaration &other) const { + return !(*this == other); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPDeclaration:: +operator < (const CPPDeclaration &other) const { + if (get_subtype() != other.get_subtype()) { + return get_subtype() < other.get_subtype(); + } + return is_less(&other); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::is_template +// Access: Public +// Description: Returns true if this is a template declaration of +// some kind: a template function or a template class, +// typically. +//////////////////////////////////////////////////////////////////// +bool CPPDeclaration:: +is_template() const { + return _template_scope != NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::get_template_scope +// Access: Public +// Description: If is_template(), above, returns true, this returns +// the CPPTemplateScope in which this particular +// template declaration is defined. This scope includes +// the information about the template parameters. +//////////////////////////////////////////////////////////////////// +CPPTemplateScope *CPPDeclaration:: +get_template_scope() const { + return _template_scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPDeclaration:: +is_fully_specified() const { + return !is_template(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::instantiate +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPDeclaration:: +instantiate(const CPPTemplateParameterList *, + CPPScope *, CPPScope *, + CPPPreprocessor *error_sink) const { + if (error_sink != NULL) { + error_sink->warning("Ignoring template parameters"); + } + return (CPPDeclaration *)this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPDeclaration:: +substitute_decl(SubstDecl &subst, CPPScope *, CPPScope *) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + return (*si).second; + } + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_instance +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstance *CPPDeclaration:: +as_instance() { + return (CPPInstance *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_class_template_parameter +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPClassTemplateParameter *CPPDeclaration:: +as_class_template_parameter() { + return (CPPClassTemplateParameter *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_typedef +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypedef *CPPDeclaration:: +as_typedef() { + return (CPPTypedef *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_type_declaration +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypeDeclaration *CPPDeclaration:: +as_type_declaration() { + return (CPPTypeDeclaration *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_expression +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression *CPPDeclaration:: +as_expression() { + return (CPPExpression *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPType *CPPDeclaration:: +as_type() { + return (CPPType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_namespace +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPNamespace *CPPDeclaration:: +as_namespace() { + return (CPPNamespace *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_using +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPUsing *CPPDeclaration:: +as_using() { + return (CPPUsing *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_simple_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPSimpleType *CPPDeclaration:: +as_simple_type() { + return (CPPSimpleType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_pointer_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPPointerType *CPPDeclaration:: +as_pointer_type() { + return (CPPPointerType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_reference_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPReferenceType *CPPDeclaration:: +as_reference_type() { + return (CPPReferenceType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_array_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPArrayType *CPPDeclaration:: +as_array_type() { + return (CPPArrayType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_const_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPConstType *CPPDeclaration:: +as_const_type() { + return (CPPConstType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_function_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionType *CPPDeclaration:: +as_function_type() { + return (CPPFunctionType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_function_group +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionGroup *CPPDeclaration:: +as_function_group() { + return (CPPFunctionGroup *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_extension_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPExtensionType *CPPDeclaration:: +as_extension_type() { + return (CPPExtensionType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_struct_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPStructType *CPPDeclaration:: +as_struct_type() { + return (CPPStructType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_enum_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPEnumType *CPPDeclaration:: +as_enum_type() { + return (CPPEnumType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_tbd_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTBDType *CPPDeclaration:: +as_tbd_type() { + return (CPPTBDType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::as_type_proxy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypeProxy *CPPDeclaration:: +as_type_proxy() { + return (CPPTypeProxy *)NULL; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration to determine whether this +// type is equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPDeclaration:: +is_equal(const CPPDeclaration *other) const { + return this == other; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPDeclaration::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration to determine whether this +// type should be ordered before another type of the +// same type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPDeclaration:: +is_less(const CPPDeclaration *other) const { + return this < other; +} + diff --git a/dtool/src/cppparser/cppDeclaration.h b/dtool/src/cppparser/cppDeclaration.h new file mode 100644 index 0000000000..432d7dae7e --- /dev/null +++ b/dtool/src/cppparser/cppDeclaration.h @@ -0,0 +1,149 @@ +// Filename: cppDeclaration.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPDECLARATION_H +#define CPPDECLARATION_H + +#include + +#include "cppVisibility.h" +#include "cppFile.h" +#include "cppCommentBlock.h" + +#include +#include +#include +#include + +using namespace std; + +class CPPInstance; +class CPPTemplateParameterList; +class CPPTypedef; +class CPPTypeDeclaration; +class CPPExpression; +class CPPType; +class CPPNamespace; +class CPPUsing; +class CPPSimpleType; +class CPPPointerType; +class CPPReferenceType; +class CPPArrayType; +class CPPConstType; +class CPPFunctionType; +class CPPFunctionGroup; +class CPPExtensionType; +class CPPStructType; +class CPPEnumType; +class CPPTypeProxy; +class CPPClassTemplateParameter; +class CPPTBDType; +class CPPScope; +class CPPTemplateScope; +class CPPPreprocessor; + +/////////////////////////////////////////////////////////////////// +// Class : CPPDeclaration +// Description : +//////////////////////////////////////////////////////////////////// +class CPPDeclaration { +public: + enum SubType { + // Subtypes of CPPDeclaration + ST_instance, + ST_typedef, + ST_type_declaration, + ST_expression, + ST_type, + ST_namespace, + ST_using, + + // Subtypes of CPPType + ST_simple, + ST_pointer, + ST_reference, + ST_array, + ST_const, + ST_function, + ST_function_group, + ST_extension, + ST_struct, + ST_enum, + ST_class_template_parameter, + ST_tbd, + ST_type_proxy, + }; + + CPPDeclaration(const CPPFile &file); + CPPDeclaration(const CPPDeclaration ©); + virtual ~CPPDeclaration(); + + bool operator == (const CPPDeclaration &other) const; + bool operator != (const CPPDeclaration &other) const; + bool operator < (const CPPDeclaration &other) const; + + bool is_template() const; + CPPTemplateScope *get_template_scope() const; + virtual bool is_fully_specified() const; + virtual CPPDeclaration * + instantiate(const CPPTemplateParameterList *actual_params, + CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + + typedef map SubstDecl; + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + typedef set Instantiations; + Instantiations _instantiations; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const=0; + + virtual SubType get_subtype() const=0; + + virtual CPPInstance *as_instance(); + virtual CPPClassTemplateParameter *as_class_template_parameter(); + virtual CPPTypedef *as_typedef(); + virtual CPPTypeDeclaration *as_type_declaration(); + virtual CPPExpression *as_expression(); + virtual CPPType *as_type(); + virtual CPPNamespace *as_namespace(); + virtual CPPUsing *as_using(); + virtual CPPSimpleType *as_simple_type(); + virtual CPPPointerType *as_pointer_type(); + virtual CPPReferenceType *as_reference_type(); + virtual CPPArrayType *as_array_type(); + virtual CPPConstType *as_const_type(); + virtual CPPFunctionType *as_function_type(); + virtual CPPFunctionGroup *as_function_group(); + virtual CPPExtensionType *as_extension_type(); + virtual CPPStructType *as_struct_type(); + virtual CPPEnumType *as_enum_type(); + virtual CPPTBDType *as_tbd_type(); + virtual CPPTypeProxy *as_type_proxy(); + + CPPVisibility _vis; + CPPTemplateScope *_template_scope; + CPPFile _file; + CPPCommentBlock *_leading_comment; + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +inline ostream & +operator << (ostream &out, const CPPDeclaration &decl) { + decl.output(out, 0, (CPPScope *)NULL, false); + return out; +} + + +#endif + + + diff --git a/dtool/src/cppparser/cppEnumType.cxx b/dtool/src/cppparser/cppEnumType.cxx new file mode 100644 index 0000000000..7dcb94e3bb --- /dev/null +++ b/dtool/src/cppparser/cppEnumType.cxx @@ -0,0 +1,139 @@ +// Filename: cppEnumType.C +// Created by: drose (25Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppEnumType.h" +#include "cppTypedef.h" +#include "cppExpression.h" +#include "cppSimpleType.h" +#include "cppScope.h" +#include "cppParser.h" +#include "indent.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPEnumType:: +CPPEnumType(CPPIdentifier *ident, CPPScope *current_scope, + const CPPFile &file) : + CPPExtensionType(T_enum, ident, current_scope, file) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::add_element +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPEnumType:: +add_element(const string &name, CPPScope *scope, CPPExpression *value) { + CPPType *type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int, + CPPSimpleType::F_unsigned)); + CPPIdentifier *ident = new CPPIdentifier(name); + CPPInstance *inst = new CPPInstance(type, ident); + inst->_initializer = value; + _elements.push_back(inst); + scope->add_enum_value(inst); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::is_incomplete +// Access: Public, Virtual +// Description: Returns true if the type has not yet been fully +// specified, false if it has. +//////////////////////////////////////////////////////////////////// +bool CPPEnumType:: +is_incomplete() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPEnumType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + return (*si).second; + } + + CPPEnumType *rep = new CPPEnumType(*this); + if (_ident != NULL) { + rep->_ident = + _ident->substitute_decl(subst, current_scope, global_scope); + } + + if (rep->_ident == _ident) { + delete rep; + rep = this; + } + rep = CPPType::new_type(rep)->as_enum_type(); + subst.insert(SubstDecl::value_type(this, rep)); + + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPEnumType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + if (!complete && _ident != NULL) { + // If we have a name, use it. + if (cppparser_output_class_keyword) { + out << _type << " "; + } + out << _ident->get_local_name(scope); + + } else if (!complete && !_typedefs.empty()) { + // If we have a typedef name, use it. + out << _typedefs.front()->get_local_name(scope); + + } else { + out << _type; + if (_ident != NULL) { + out << " " << _ident->get_local_name(scope); + } + + out << " {\n"; + Elements::const_iterator ei; + for (ei = _elements.begin(); ei != _elements.end(); ++ei) { + indent(out, indent_level + 2) << (*ei)->get_local_name(); + if ((*ei)->_initializer != NULL) { + out << " = " << *(*ei)->_initializer; + } + out << ",\n"; + } + indent(out, indent_level) << "}"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPEnumType:: +get_subtype() const { + return ST_enum; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPEnumType::as_enum_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPEnumType *CPPEnumType:: +as_enum_type() { + return this; +} diff --git a/dtool/src/cppparser/cppEnumType.h b/dtool/src/cppparser/cppEnumType.h new file mode 100644 index 0000000000..9aaf6118a7 --- /dev/null +++ b/dtool/src/cppparser/cppEnumType.h @@ -0,0 +1,49 @@ +// Filename: cppEnumType.h +// Created by: drose (25Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPENUMTYPE_H +#define CPPENUMTYPE_H + +#include + +#include "cppExtensionType.h" + +#include + +class CPPExpression; +class CPPInstance; +class CPPScope; + + +/////////////////////////////////////////////////////////////////// +// Class : CPPEnumType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPEnumType : public CPPExtensionType { +public: + CPPEnumType(CPPIdentifier *ident, CPPScope *current_scope, + const CPPFile &file); + + void add_element(const string &name, CPPScope *scope, + CPPExpression *value = (CPPExpression *)NULL); + + virtual bool is_incomplete() const; + + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPEnumType *as_enum_type(); + + typedef vector Elements; + Elements _elements; +}; + + +#endif diff --git a/dtool/src/cppparser/cppExpression.cxx b/dtool/src/cppparser/cppExpression.cxx new file mode 100644 index 0000000000..8c73cf0937 --- /dev/null +++ b/dtool/src/cppparser/cppExpression.cxx @@ -0,0 +1,1300 @@ +// Filename: cppExpression.C +// Created by: drose (25Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppExpression.h" +#include "cppToken.h" +#include "cppIdentifier.h" +#include "cppType.h" +#include "cppSimpleType.h" +#include "cppPointerType.h" +#include "cppConstType.h" +#include "cppArrayType.h" +#include "cppPreprocessor.h" +#include "cppInstance.h" +#include "cppFunctionGroup.h" +#include "cppFunctionType.h" +#include "cppBison.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Result::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression::Result:: +Result() { + _type = RT_error; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Result::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression::Result:: +Result(int value) { + _type = RT_integer; + _u._integer = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Result::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression::Result:: +Result(double value) { + _type = RT_real; + _u._real = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Result::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression::Result:: +Result(void *value) { + _type = RT_pointer; + _u._pointer = value; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Result::as_integer +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int CPPExpression::Result:: +as_integer() const { + switch (_type) { + case RT_integer: + return _u._integer; + + case RT_real: + return (int)_u._real; + + case RT_pointer: + return (int)_u._pointer; + + default: + cerr << "Invalid type\n"; + assert(false); + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Result::as_real +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +double CPPExpression::Result:: +as_real() const { + switch (_type) { + case RT_integer: + return (double)_u._integer; + + case RT_real: + return _u._real; + + case RT_pointer: + return (double)(int)_u._pointer; + + default: + cerr << "Invalid type\n"; + assert(false); + return 0.0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Result::as_pointer +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void *CPPExpression::Result:: +as_pointer() const { + switch (_type) { + case RT_integer: + return (void *)_u._integer; + + case RT_real: + return (void *)(int)_u._real; + + case RT_pointer: + return _u._pointer; + + default: + cerr << "Invalid type\n"; + assert(false); + return (void *)NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Result::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPExpression::Result:: +output(ostream &out) const { + switch (_type) { + case RT_integer: + out << _u._integer; + break; + + case RT_real: + out << _u._real; + break; + + case RT_pointer: + out << _u._pointer; + break; + + case RT_error: + out << "(error)"; + break; + + default: + out << "(**invalid type**)\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpresion::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression:: +CPPExpression(int value) : + CPPDeclaration(CPPFile()) +{ + _type = T_integer; + _u._integer = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression:: +CPPExpression(double value) : + CPPDeclaration(CPPFile()) +{ + _type = T_real; + _u._real = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression:: +CPPExpression(const string &value) : + CPPDeclaration(CPPFile()) +{ + _type = T_string; + _str = value; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression:: +CPPExpression(CPPIdentifier *ident, CPPScope *current_scope, + CPPScope *global_scope, CPPPreprocessor *error_sink) : + CPPDeclaration(CPPFile()) +{ + CPPDeclaration *decl = + ident->find_symbol(current_scope, global_scope, error_sink); + + if (decl != NULL) { + CPPInstance *inst = decl->as_instance(); + if (inst != NULL) { + _type = T_variable; + _u._variable = inst; + return; + } + CPPFunctionGroup *fgroup = decl->as_function_group(); + if (fgroup != NULL) { + _type = T_function; + _u._fgroup = fgroup; + return; + } + } + + _type = T_unknown_ident; + _u._ident = ident; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression:: +CPPExpression(int unary_operator, CPPExpression *op1) : + CPPDeclaration(CPPFile()) +{ + _type = T_unary_operation; + _u._op._operator = unary_operator; + _u._op._op1 = op1; + _u._op._op2 = NULL; + _u._op._op3 = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression:: +CPPExpression(int binary_operator, CPPExpression *op1, CPPExpression *op2) : + CPPDeclaration(CPPFile()) +{ + _type = T_binary_operation; + _u._op._operator = binary_operator; + _u._op._op1 = op1; + _u._op._op2 = op2; + _u._op._op3 = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression:: +CPPExpression(int trinary_operator, CPPExpression *op1, CPPExpression *op2, + CPPExpression *op3) : + CPPDeclaration(CPPFile()) +{ + _type = T_trinary_operation; + _u._op._operator = trinary_operator; + _u._op._op1 = op1; + _u._op._op2 = op2; + _u._op._op3 = op3; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::named typecast_op constructor +// Access: Public, Static +// Description: Creates an expression that represents a typecast +// operation. +//////////////////////////////////////////////////////////////////// +CPPExpression CPPExpression:: +typecast_op(CPPType *type, CPPExpression *op1) { + CPPExpression expr(0); + expr._type = T_typecast; + expr._u._typecast._to = type; + expr._u._typecast._op1 = op1; + return expr; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::named construct_op constructor +// Access: Public, Static +// Description: Creates an expression that represents a constructor +// call. +//////////////////////////////////////////////////////////////////// +CPPExpression CPPExpression:: +construct_op(CPPType *type, CPPExpression *op1) { + CPPExpression expr(0); + if (op1 == NULL) { + // A default constructor call--no parameters. + expr._type = T_default_construct; + expr._u._typecast._to = type; + expr._u._typecast._op1 = NULL; + } else { + // A normal constructor call, with parameters. + expr._type = T_construct; + expr._u._typecast._to = type; + expr._u._typecast._op1 = op1; + } + return expr; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::named new_op constructor +// Access: Public, Static +// Description: Creates an expression that represents a use of the +// new operator. +//////////////////////////////////////////////////////////////////// +CPPExpression CPPExpression:: +new_op(CPPType *type, CPPExpression *op1) { + CPPExpression expr(0); + if (op1 == NULL) { + // A default new operation--no parameters. + expr._type = T_default_new; + expr._u._typecast._to = type; + expr._u._typecast._op1 = NULL; + } else { + // A normal new operation, with parameters. + expr._type = T_new; + expr._u._typecast._to = type; + expr._u._typecast._op1 = op1; + } + return expr; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::named sizeof_func constructor +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression CPPExpression:: +sizeof_func(CPPType *type) { + CPPExpression expr(0); + expr._type = T_sizeof; + expr._u._typecast._to = type; + expr._u._typecast._op1 = NULL; + return expr; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression:: +~CPPExpression() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::evaluate +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression::Result CPPExpression:: +evaluate() const { + Result r1, r2; + + switch (_type) { + case T_integer: + return Result(_u._integer); + + case T_real: + return Result(_u._real); + + case T_string: + return Result(); + + case T_variable: + case T_function: + return Result(); + + case T_unknown_ident: + return Result(); + + case T_typecast: + assert(_u._typecast._op1 != NULL); + r1 = _u._typecast._op1->evaluate(); + if (r1._type != RT_error) { + CPPSimpleType *stype = _u._typecast._to->as_simple_type(); + if (stype != NULL) { + if (stype->_type == CPPSimpleType::T_int) { + return Result(r1.as_integer()); + } else if (stype->_type == CPPSimpleType::T_float || + stype->_type == CPPSimpleType::T_double) { + return Result(r1.as_real()); + } + } + if (_u._typecast._to->as_pointer_type()) { + return Result(r1.as_pointer()); + } + } + return Result(); + + case T_construct: + case T_default_construct: + case T_new: + case T_default_new: + case T_sizeof: + return Result(); + + case T_binary_operation: + assert(_u._op._op2 != NULL); + r2 = _u._op._op2->evaluate(); + + // The operators && and || are special cases: these are + // shirt-circuiting operators. Thus, if we are using either of + // these it might be acceptable for the second operand to be + // invalid, since we might never evaluate it. + + // In all other cases, both operands must be valid in order for + // the operation to be valid. + if (r2._type == RT_error && + (_u._op._operator != OROR && _u._op._operator != ANDAND)) { + return r2; + } + // Fall through + + + case T_trinary_operation: + // The trinary operator is also a short-circuiting operator: we + // don't test the second or third operands until we need them. + // The only critical one is the first operand. + + // Fall through + + case T_unary_operation: + assert(_u._op._op1 != NULL); + r1 = _u._op._op1->evaluate(); + if (r1._type == RT_error) { + return r1; + } + + switch (_u._op._operator) { + case UNARY_NOT: + return Result(!r1.as_integer()); + + case UNARY_NEGATE: + return Result(~r1.as_integer()); + + case UNARY_MINUS: + return (r1._type == RT_real) ? Result(-r1.as_real()) : Result(-r1.as_integer()); + + case UNARY_STAR: + case UNARY_REF: + return Result(); + + case '*': + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() * r2.as_real()); + } else { + return Result(r1.as_integer() * r2.as_integer()); + } + + case '/': + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() / r2.as_real()); + } else { + return Result(r1.as_integer() / r2.as_integer()); + } + + case '%': + return Result(r1.as_integer() % r2.as_integer()); + + case '+': + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() + r2.as_real()); + } else { + return Result(r1.as_integer() + r2.as_integer()); + } + + case '-': + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() - r2.as_real()); + } else { + return Result(r1.as_integer() - r2.as_integer()); + } + + case '|': + return Result(r1.as_integer() | r2.as_integer()); + + case '&': + return Result(r1.as_integer() & r2.as_integer()); + + case OROR: + if (r1.as_integer()) { + return r1; + } else { + return r2; + } + + case ANDAND: + if (r1.as_integer()) { + return r2; + } else { + return r1; + } + + case EQCOMPARE: + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() == r2.as_real()); + } else { + return Result(r1.as_integer() == r2.as_integer()); + } + + case NECOMPARE: + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() != r2.as_real()); + } else { + return Result(r1.as_integer() != r2.as_integer()); + } + + case LECOMPARE: + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() <= r2.as_real()); + } else { + return Result(r1.as_integer() <= r2.as_integer()); + } + + case GECOMPARE: + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() >= r2.as_real()); + } else { + return Result(r1.as_integer() >= r2.as_integer()); + } + + case '<': + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() < r2.as_real()); + } else { + return Result(r1.as_integer() < r2.as_integer()); + } + + case '>': + if (r1._type == RT_real || r2._type == RT_real) { + return Result(r1.as_real() > r2.as_real()); + } else { + return Result(r1.as_integer() > r2.as_integer()); + } + + case LSHIFT: + return Result(r1.as_integer() << r2.as_integer()); + + case RSHIFT: + return Result(r1.as_integer() >> r2.as_integer()); + + case '?': + return r1.as_integer() ? + _u._op._op2->evaluate() : _u._op._op3->evaluate(); + + case '.': + case POINTSAT: + return Result(); + + case '[': // Array element reference + return Result(); + + case 'f': // Function evaluation + return Result(); + + case ',': + return r2; + + default: + cerr << "**unexpected operator**\n"; + abort(); + } + + default: + cerr << "**invalid operand**\n"; + abort(); + } + + return Result(); // Compiler kludge; can't get here. +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::determine_type +// Access: Public +// Description: Returns the type of the expression, if it is known, +// or NULL if the type cannot be determined. +//////////////////////////////////////////////////////////////////// +CPPType *CPPExpression:: +determine_type() const { + CPPType *t1, *t2; + + CPPType *int_type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int)); + + CPPType *bool_type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_bool)); + + CPPType *float_type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_double)); + + CPPType *char_type = + CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char)); + + CPPType *const_char_type = + CPPType::new_type(new CPPConstType(char_type)); + + CPPType *char_star_type = + CPPType::new_type(new CPPPointerType(const_char_type)); + + switch (_type) { + case T_integer: + return int_type; + + case T_real: + return float_type; + + case T_string: + return char_star_type; + + case T_variable: + return _u._variable->_type; + + case T_function: + if (_u._fgroup->get_return_type() == (CPPType *)NULL) { + // There are multiple functions by this name that have different + // return types. We could attempt to differentiate them based + // on the parameter list, but that's a lot of work. Let's just + // give up. + return (CPPType *)NULL; + } + return _u._fgroup->_instances.front()->_type; + + case T_unknown_ident: + return (CPPType *)NULL; + + case T_typecast: + case T_construct: + case T_default_construct: + return _u._typecast._to; + + case T_new: + case T_default_new: + return CPPType::new_type(new CPPPointerType(_u._typecast._to)); + + case T_sizeof: + return int_type; + + case T_binary_operation: + case T_trinary_operation: + assert(_u._op._op2 != NULL); + t2 = _u._op._op2->determine_type(); + // Fall through + + case T_unary_operation: + assert(_u._op._op1 != NULL); + t1 = _u._op._op1->determine_type(); + + switch (_u._op._operator) { + case UNARY_NOT: + return bool_type; + + case UNARY_NEGATE: + return int_type; + + case UNARY_MINUS: + return t1; + + case UNARY_STAR: + case '[': // Array element reference + if (t1 != NULL) { + if (t1->as_pointer_type()) { + return t1->as_pointer_type()->_pointing_at; + } + if (t1->as_array_type()) { + return t1->as_array_type()->_element_type; + } + } + return NULL; + + case UNARY_REF: + return t1; + + case '*': + case '/': + case '+': + case '-': + if (t1 == NULL) { + return t2; + } else if (t2 == NULL) { + return t1; + } else if (t1->as_pointer_type()) { + if (t2->as_pointer_type()) { + return int_type; + } + return t1; + } + return elevate_type(t1, t2); + + case '%': + case '|': + case '&': + case LSHIFT: + case RSHIFT: + return int_type; + + case OROR: + case ANDAND: + case EQCOMPARE: + case NECOMPARE: + case LECOMPARE: + case GECOMPARE: + case '<': + case '>': + return bool_type; + + case '?': + return t2; + + case '.': + case POINTSAT: + return NULL; + + case 'f': // Function evaluation + if (t1 != NULL) { + CPPFunctionType *ftype = t1->as_function_type(); + if (ftype != (CPPFunctionType *)NULL) { + return ftype->_return_type; + } + } + return NULL; + + case ',': + return t2; + + default: + cerr << "**unexpected operator**\n"; + abort(); + } + + default: + cerr << "**invalid operand**\n"; + abort(); + } + + return NULL; // Compiler kludge; can't get here. +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPExpression:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + CPPDeclaration *top = + CPPDeclaration::substitute_decl(subst, current_scope, global_scope); + if (top != this) { + return top; + } + + CPPExpression *rep = new CPPExpression(*this); + bool any_changed = false; + CPPDeclaration *decl; + + switch (_type) { + case T_variable: + decl = _u._variable->substitute_decl(subst, current_scope, global_scope); + if (decl != rep->_u._variable) { + if (decl->as_instance()) { + // Replacing the variable reference with another variable reference. + rep->_u._variable = decl->as_instance(); + any_changed = true; + } else if (decl->as_expression()) { + // Replacing the variable reference with an expression. + delete rep; + rep = decl->as_expression(); + any_changed = true; + } + } + break; + + case T_typecast: + case T_construct: + case T_new: + rep->_u._typecast._op1 = + _u._typecast._op1->substitute_decl(subst, current_scope, global_scope) + ->as_expression(); + any_changed = any_changed || (rep->_u._typecast._op1 != _u._typecast._op1); + // fall through + + case T_default_construct: + case T_default_new: + case T_sizeof: + rep->_u._typecast._to = + _u._typecast._to->substitute_decl(subst, current_scope, global_scope) + ->as_type(); + any_changed = any_changed || (rep->_u._typecast._to != _u._typecast._to); + break; + + case T_trinary_operation: + rep->_u._op._op3 = + _u._op._op3->substitute_decl(subst, current_scope, global_scope) + ->as_expression(); + any_changed = any_changed || (rep->_u._op._op3 != _u._op._op3); + // fall through + + case T_binary_operation: + rep->_u._op._op2 = + _u._op._op2->substitute_decl(subst, current_scope, global_scope) + ->as_expression(); + any_changed = any_changed || (rep->_u._op._op2 != _u._op._op2); + // fall through + + case T_unary_operation: + rep->_u._op._op1 = + _u._op._op1->substitute_decl(subst, current_scope, global_scope) + ->as_expression(); + any_changed = any_changed || (rep->_u._op._op1 != _u._op._op1); + break; + + default: + break; + } + + if (!any_changed) { + delete rep; + rep = this; + } + subst.insert(SubstDecl::value_type(this, rep)); + return rep; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::is_tbd +// Access: Public +// Description: Returns true if any type within the expression list is +// a CPPTBDType and thus isn't fully determined right +// now. +//////////////////////////////////////////////////////////////////// +bool CPPExpression:: +is_tbd() const { + switch (_type) { + case T_variable: + return true; + + case T_typecast: + case T_construct: + case T_new: + case T_default_construct: + case T_default_new: + case T_sizeof: + return _u._typecast._to->is_tbd(); + + case T_trinary_operation: + if (_u._op._op3->is_tbd()) { + return true; + } + // fall through + + case T_binary_operation: + if (_u._op._op2->is_tbd()) { + return true; + } + // fall through + + case T_unary_operation: + if (_u._op._op1->is_tbd()) { + return true; + } + return false; + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPExpression:: +output(ostream &out, int indent_level, CPPScope *scope, bool) const { + switch (_type) { + case T_integer: + out << _u._integer; + break; + + case T_real: + out << _u._real; + break; + + case T_string: + out << '"'; + { + string::const_iterator si; + for (si = _str.begin(); si != _str.end(); ++si) { + switch (*si) { + case '\n': + out << "\\n"; + break; + + case '\t': + out << "\\t"; + break; + + case '\r': + out << "\\r"; + break; + + case '\a': + out << "\\a"; + break; + + case '"': + out << "\\\""; + break; + + default: + if (isprint(*si)) { + out << *si; + } else { + out << '\\' << oct << setw(3) << setfill('0') << (int)(*si) + << dec << setw(0); + } + } + } + } + out << '"'; + break; + + case T_variable: + out << *_u._variable->_ident; + break; + + case T_function: + out << _u._fgroup->_name; + break; + + case T_unknown_ident: + out << *_u._ident; + break; + + case T_typecast: + out << "("; + _u._typecast._to->output(out, indent_level, scope, false); + out << ")(" << *_u._typecast._op1 << ")"; + break; + + case T_construct: + out << "(" << _u._typecast._to->get_typedef_name(scope) + << "(" << *_u._typecast._op1 << "))"; + break; + + case T_default_construct: + out << "(" << _u._typecast._to->get_typedef_name(scope) << "())"; + break; + + case T_new: + out << "(new " << *_u._typecast._to + << "(" << *_u._typecast._op1 << "))"; + break; + + case T_default_new: + out << "(new " << *_u._typecast._to << "())"; + break; + + case T_sizeof: + out << "sizeof("; + _u._typecast._to->output(out, indent_level, scope, false); + out << ")"; + break; + + case T_unary_operation: + switch (_u._op._operator) { + case UNARY_NOT: + out << "(! " << *_u._op._op1 << ")"; + break; + + case UNARY_NEGATE: + out << "(~ " << *_u._op._op1 << ")"; + break; + + case UNARY_MINUS: + out << "(- " << *_u._op._op1 << ")"; + break; + + case UNARY_STAR: + out << "(* " << *_u._op._op1 << ")"; + break; + + case UNARY_REF: + out << "(& " << *_u._op._op1 << ")"; + break; + + case 'f': // Function evaluation, no parameters. + out << "(" << *_u._op._op1 << "())"; + break; + + default: + out << "(" << (char)_u._op._operator << " " << *_u._op._op1 << ")"; + break; + } + break; + + case T_binary_operation: + switch (_u._op._operator) { + case OROR: + out << "(" << *_u._op._op1 << " || " << *_u._op._op2 << ")"; + break; + + case ANDAND: + out << "(" << *_u._op._op1 << " && " << *_u._op._op2 << ")"; + break; + + case EQCOMPARE: + out << "(" << *_u._op._op1 << " == " << *_u._op._op2 << ")"; + break; + + case NECOMPARE: + out << "(" << *_u._op._op1 << " != " << *_u._op._op2 << ")"; + break; + + case LECOMPARE: + out << "(" << *_u._op._op1 << " <= " << *_u._op._op2 << ")"; + break; + + case GECOMPARE: + out << "(" << *_u._op._op1 << " >= " << *_u._op._op2 << ")"; + break; + + case LSHIFT: + out << "(" << *_u._op._op1 << " << " << *_u._op._op2 << ")"; + break; + + case RSHIFT: + out << "(" << *_u._op._op1 << " >> " << *_u._op._op2 << ")"; + break; + + case '.': + out << "(" << *_u._op._op1 << "." << *_u._op._op2 << ")"; + break; + + case POINTSAT: + out << "(" << *_u._op._op1 << "->" << *_u._op._op2 << ")"; + break; + + case '[': // Array element reference + out << "(" << *_u._op._op1 << "[" << *_u._op._op2 << "])"; + break; + + case 'f': // Function evaluation + out << "(" << *_u._op._op1 << "(" << *_u._op._op2 << "))"; + break; + + case ',': // Comma, no parens are used + out << *_u._op._op1 << ", " << *_u._op._op2; + break; + + default: + out << "(" << *_u._op._op1 << " " << (char)_u._op._operator + << " " << *_u._op._op2 << ")"; + } + break; + + case T_trinary_operation: + out << "(" << *_u._op._op1 << " ? " << *_u._op._op2 + << " : " << *_u._op._op3 << ")"; + break; + + default: + out << "(** invalid operand type " << (int)_type << " **)"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPExpression:: +get_subtype() const { + return ST_expression; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::as_expression +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpression *CPPExpression:: +as_expression() { + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::elevate_type +// Access: Public, Static +// Description: Returns the most general of the two given types. +//////////////////////////////////////////////////////////////////// +CPPType *CPPExpression:: +elevate_type(CPPType *t1, CPPType *t2) { + CPPSimpleType *st1 = t1->as_simple_type(); + CPPSimpleType *st2 = t2->as_simple_type(); + + if (st1 == NULL || st2 == NULL) { + // Nothing we can do about this. Who knows? + return NULL; + } + + if (st1->_type == st2->_type) { + // They have the same type, so return the one with the largest + // flag bits. + if (st1->_flags & CPPSimpleType::F_longlong) { + return st1; + } else if (st2->_flags & CPPSimpleType::F_longlong) { + return st2; + } else if (st1->_flags & CPPSimpleType::F_long) { + return st1; + } else if (st2->_flags & CPPSimpleType::F_long) { + return st2; + } else if (st1->_flags & CPPSimpleType::F_short) { + return st2; + } else if (st2->_flags & CPPSimpleType::F_short) { + return st1; + } + return st1; + } + + // They have different types. + if (st1->_type == CPPSimpleType::T_float || + st1->_type == CPPSimpleType::T_double) { + return st1; + } else if (st2->_type == CPPSimpleType::T_float || + st2->_type == CPPSimpleType::T_double) { + return st2; + } else if (st1->_type == CPPSimpleType::T_int) { + return st1; + } else if (st2->_type == CPPSimpleType::T_int) { + return st2; + } else if (st1->_type == CPPSimpleType::T_bool) { + return st1; + } else if (st2->_type == CPPSimpleType::T_bool) { + return st2; + } + return st1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration to determine whether this +// expr is equivalent to another expr. +//////////////////////////////////////////////////////////////////// +bool CPPExpression:: +is_equal(const CPPDeclaration *other) const { + const CPPExpression *ot = ((CPPDeclaration *)other)->as_expression(); + assert(ot != NULL); + + if (_type != ot->_type) { + return false; + } + + switch (_type) { + case T_integer: + return _u._integer == ot->_u._integer; + + case T_real: + return _u._real == ot->_u._real; + + case T_string: + return _str == ot->_str; + + case T_variable: + return _u._variable == ot->_u._variable; + + case T_function: + return _u._fgroup == ot->_u._fgroup; + + case T_unknown_ident: + return *_u._ident == *ot->_u._ident; + + case T_typecast: + case T_construct: + case T_new: + return _u._typecast._to == ot->_u._typecast._to && + *_u._typecast._op1 == *ot->_u._typecast._op1; + + case T_default_construct: + case T_default_new: + case T_sizeof: + return _u._typecast._to == ot->_u._typecast._to; + + case T_unary_operation: + return *_u._op._op1 == *ot->_u._op._op1; + + case T_binary_operation: + return *_u._op._op1 == *ot->_u._op._op1 && + *_u._op._op2 == *ot->_u._op._op2; + + case T_trinary_operation: + return *_u._op._op1 == *ot->_u._op._op1 && + *_u._op._op2 == *ot->_u._op._op2; + + default: + cerr << "(** invalid operand type " << (int)_type << " **)"; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpression::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration to determine whether this +// expr should be ordered before another expr of the +// same type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPExpression:: +is_less(const CPPDeclaration *other) const { + const CPPExpression *ot = ((CPPDeclaration *)other)->as_expression(); + assert(ot != NULL); + + if (_type != ot->_type) { + return (int)_type < (int)ot->_type; + } + + switch (_type) { + case T_integer: + return _u._integer < ot->_u._integer; + + case T_real: + return _u._real < ot->_u._real; + + case T_string: + return _str < ot->_str; + + case T_variable: + return _u._variable < ot->_u._variable; + + case T_function: + return *_u._fgroup < *ot->_u._fgroup; + + case T_unknown_ident: + return *_u._ident < *ot->_u._ident; + + case T_typecast: + case T_construct: + case T_new: + if (_u._typecast._to != ot->_u._typecast._to) { + return _u._typecast._to < ot->_u._typecast._to; + } + return *_u._typecast._op1 < *ot->_u._typecast._op1; + + case T_default_construct: + case T_default_new: + case T_sizeof: + return _u._typecast._to < ot->_u._typecast._to; + + case T_trinary_operation: + if (*_u._op._op3 != *ot->_u._op._op3) { + return *_u._op._op3 < *ot->_u._op._op3; + } + // Fall through + + case T_binary_operation: + if (*_u._op._op2 != *ot->_u._op._op2) { + return *_u._op._op2 < *ot->_u._op._op2; + } + // Fall through + + case T_unary_operation: + return *_u._op._op1 < *ot->_u._op._op1; + + default: + cerr << "(** invalid operand type " << (int)_type << " **)"; + } + + return false; +} + diff --git a/dtool/src/cppparser/cppExpression.h b/dtool/src/cppparser/cppExpression.h new file mode 100644 index 0000000000..bee8a4da05 --- /dev/null +++ b/dtool/src/cppparser/cppExpression.h @@ -0,0 +1,139 @@ +// Filename: cppExpression.h +// Created by: drose (25Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPEXPRESSION_H +#define CPPEXPRESSION_H + +#include + +#include "cppDeclaration.h" + +class CPPIdentifier; +class CPPType; +class CPPPreprocessor; +class CPPFunctionGroup; + +/////////////////////////////////////////////////////////////////// +// Class : CPPExpression +// Description : +//////////////////////////////////////////////////////////////////// +class CPPExpression : public CPPDeclaration { +public: + CPPExpression(int value); + CPPExpression(const string &value); + CPPExpression(double value); + CPPExpression(CPPIdentifier *ident, CPPScope *current_scope, + CPPScope *global_scope, CPPPreprocessor *error_sink = NULL); + CPPExpression(int unary_operator, CPPExpression *op1); + CPPExpression(int binary_operator, CPPExpression *op1, CPPExpression *op2); + CPPExpression(int trinary_operator, CPPExpression *op1, CPPExpression *op2, CPPExpression *op3); + + static CPPExpression typecast_op(CPPType *type, CPPExpression *op1); + static CPPExpression construct_op(CPPType *type, CPPExpression *op1); + static CPPExpression new_op(CPPType *type, CPPExpression *op1 = NULL); + static CPPExpression sizeof_func(CPPType *type); + + ~CPPExpression(); + + enum ResultType { + RT_integer, + RT_real, + RT_pointer, + RT_error + }; + + class Result { + public: + Result(); + Result(int value); + Result(double value); + Result(void *value); + + int as_integer() const; + double as_real() const; + void *as_pointer() const; + void output(ostream &out) const; + + ResultType _type; + union { + int _integer; + double _real; + void *_pointer; + } _u; + }; + + + Result evaluate() const; + CPPType *determine_type() const; + bool is_tbd() const; + + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPExpression *as_expression(); + + + enum Type { + T_integer, + T_real, + T_string, + T_variable, + T_function, + T_unknown_ident, + T_typecast, + T_construct, + T_default_construct, + T_new, + T_default_new, + T_sizeof, + T_unary_operation, + T_binary_operation, + T_trinary_operation, + }; + + Type _type; + string _str; + union { + int _integer; + double _real; + CPPInstance *_variable; + CPPFunctionGroup *_fgroup; + CPPIdentifier *_ident; + class { + public: + CPPType *_to; + CPPExpression *_op1; + } _typecast; + class { + public: + // One of the yytoken values: a character, or something + // like EQCOMPARE. + int _operator; + CPPExpression *_op1; + CPPExpression *_op2; + CPPExpression *_op3; + } _op; + } _u; + +protected: + static CPPType *elevate_type(CPPType *t1, CPPType *t2); + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +inline ostream & +operator << (ostream &out, const CPPExpression::Result &result) { + result.output(out); + return out; +} + +#endif + + diff --git a/dtool/src/cppparser/cppExpressionParser.cxx b/dtool/src/cppparser/cppExpressionParser.cxx new file mode 100644 index 0000000000..2d841404b4 --- /dev/null +++ b/dtool/src/cppparser/cppExpressionParser.cxx @@ -0,0 +1,80 @@ +// Filename: cppExpressionParser.C +// Created by: drose (25Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppExpressionParser.h" +#include "cppExpression.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpressionParser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpressionParser:: +CPPExpressionParser(CPPScope *current_scope, CPPScope *global_scope) : + _current_scope(current_scope), + _global_scope(global_scope) +{ + _expr = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpressionParser::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExpressionParser:: +~CPPExpressionParser() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpressionParser::parse_expr +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPExpressionParser:: +parse_expr(const string &expr) { + if (!init_const_expr(expr)) { + cerr << "Unable to parse expression\n"; + return false; + } + + _expr = parse_const_expr(this, _current_scope, _global_scope); + + return get_error_count() == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpressionParser::parse_expr +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPExpressionParser:: +parse_expr(const string &expr, const CPPPreprocessor &filepos) { + if (!init_const_expr(expr)) { + cerr << "Unable to parse expression\n"; + return false; + } + + copy_filepos(filepos); + + _expr = parse_const_expr(this, _current_scope, _global_scope); + + return get_error_count() == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExpressionParser::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPExpressionParser:: +output(ostream &out) const { + if (_expr == NULL) { + out << "(null expr)"; + } else { + out << *_expr; + } +} diff --git a/dtool/src/cppparser/cppExpressionParser.h b/dtool/src/cppparser/cppExpressionParser.h new file mode 100644 index 0000000000..be8cd380fe --- /dev/null +++ b/dtool/src/cppparser/cppExpressionParser.h @@ -0,0 +1,43 @@ +// Filename: cppExpressionParser.h +// Created by: drose (25Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPEXPRESSIONPARSER_H +#define CPPEXPRESSIONPARSER_H + +#include + +#include "cppPreprocessor.h" + +class CPPExpression; +class CPPScope; + +/////////////////////////////////////////////////////////////////// +// Class : CPPExpressionParser +// Description : +//////////////////////////////////////////////////////////////////// +class CPPExpressionParser : public CPPPreprocessor { +public: + CPPExpressionParser(CPPScope *current_scope, CPPScope *global_scope); + ~CPPExpressionParser(); + + bool parse_expr(const string &expr); + bool parse_expr(const string &expr, const CPPPreprocessor &filepos); + + void output(ostream &out) const; + + CPPScope *_current_scope; + CPPScope *_global_scope; + CPPExpression *_expr; +}; + +inline ostream & +operator << (ostream &out, const CPPExpressionParser &ep) { + ep.output(out); + return out; +} + +#endif + + diff --git a/dtool/src/cppparser/cppExtensionType.cxx b/dtool/src/cppparser/cppExtensionType.cxx new file mode 100644 index 0000000000..16374bd1ca --- /dev/null +++ b/dtool/src/cppparser/cppExtensionType.cxx @@ -0,0 +1,208 @@ +// Filename: cppExtensionType.C +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppExtensionType.h" +#include "cppTypedef.h" +#include "cppIdentifier.h" +#include "cppParser.h" +#include "indent.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::Conextensionor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPExtensionType:: +CPPExtensionType(CPPExtensionType::Type type, + CPPIdentifier *ident, CPPScope *current_scope, + const CPPFile &file) : + CPPType(file), + _type(type), _ident(ident) +{ + if (_ident != NULL) { + _ident->_native_scope = current_scope; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::get_simple_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPExtensionType:: +get_simple_name() const { + if (_ident == NULL) { + return ""; + } + return _ident->get_simple_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::get_local_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPExtensionType:: +get_local_name(CPPScope *scope) const { + if (_ident == NULL) { + return ""; + } + return _ident->get_local_name(scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::get_fully_scoped_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPExtensionType:: +get_fully_scoped_name() const { + if (_ident == NULL) { + return ""; + } + return _ident->get_fully_scoped_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::is_incomplete +// Access: Public, Virtual +// Description: Returns true if the type has not yet been fully +// specified, false if it has. +//////////////////////////////////////////////////////////////////// +bool CPPExtensionType:: +is_incomplete() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPExtensionType:: +is_tbd() const { + if (_ident != (CPPIdentifier *)NULL) { + return _ident->is_tbd(); + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPExtensionType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + return (*si).second; + } + + CPPExtensionType *rep = new CPPExtensionType(*this); + if (_ident != NULL) { + rep->_ident = + _ident->substitute_decl(subst, current_scope, global_scope); + } + + if (rep->_ident == _ident) { + delete rep; + rep = this; + } + rep = CPPType::new_type(rep)->as_extension_type(); + subst.insert(SubstDecl::value_type(this, rep)); + return rep; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::is_equivalent_type +// Access: Public, Virtual +// Description: This is a little more forgiving than is_equal(): it +// returns true if the types appear to be referring to +// the same thing, even if they may have different +// pointers or somewhat different definitions. It's +// useful for parameter matching, etc. +//////////////////////////////////////////////////////////////////// +bool CPPExtensionType:: +is_equivalent(const CPPType &other) const { + const CPPExtensionType *ot = ((CPPType *)&other)->as_extension_type(); + if (ot == (CPPExtensionType *)NULL) { + return CPPType::is_equivalent(other); + } + + // We consider two different extension types to be equivalent if + // they have the same name. + + return *_ident == *ot->_ident; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPExtensionType:: +output(ostream &out, int, CPPScope *scope, bool) const { + if (_ident != NULL) { + // If we have a name, use it. + if (cppparser_output_class_keyword) { + out << _type << " "; + } + out << _ident->get_local_name(scope); + + } else if (!_typedefs.empty()) { + // If we have a typedef name, use it. + out << _typedefs.front()->get_local_name(scope); + + } else { + out << "(**unknown forward-reference type**)"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPExtensionType:: +get_subtype() const { + return ST_extension; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPExtensionType::as_extension_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPExtensionType *CPPExtensionType:: +as_extension_type() { + return this; +} + +ostream & +operator << (ostream &out, CPPExtensionType::Type type) { + switch (type) { + case CPPExtensionType::T_enum: + return out << "enum"; + + case CPPExtensionType::T_class: + return out << "class"; + + case CPPExtensionType::T_struct: + return out << "struct"; + + case CPPExtensionType::T_union: + return out << "union"; + + default: + return out << "***invalid extension type***"; + } +} diff --git a/dtool/src/cppparser/cppExtensionType.h b/dtool/src/cppparser/cppExtensionType.h new file mode 100644 index 0000000000..166c90f194 --- /dev/null +++ b/dtool/src/cppparser/cppExtensionType.h @@ -0,0 +1,60 @@ +// Filename: cppExtensionType.h +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPEXTENSIONTYPE_H +#define CPPEXTENSIONTYPE_H + +#include + +#include "cppType.h" +#include "cppInstance.h" + +class CPPScope; +class CPPIdentifier; + +/////////////////////////////////////////////////////////////////// +// Class : CPPExtensionType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPExtensionType : public CPPType { +public: + enum Type { + T_enum, + T_class, + T_struct, + T_union, + }; + + CPPExtensionType(Type type, CPPIdentifier *ident, CPPScope *current_scope, + const CPPFile &file); + + virtual string get_simple_name() const; + virtual string get_local_name(CPPScope *scope = NULL) const; + virtual string get_fully_scoped_name() const; + + virtual bool is_incomplete() const; + virtual bool is_tbd() const; + + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_equivalent(const CPPType &other) const; + + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPExtensionType *as_extension_type(); + + + Type _type; + CPPIdentifier *_ident; +}; + +ostream &operator << (ostream &out, CPPExtensionType::Type type); + +#endif diff --git a/dtool/src/cppparser/cppFile.cxx b/dtool/src/cppparser/cppFile.cxx new file mode 100644 index 0000000000..4750f4bc20 --- /dev/null +++ b/dtool/src/cppparser/cppFile.cxx @@ -0,0 +1,161 @@ +// Filename: cppFile.C +// Created by: drose (11Nov99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppFile.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPFile:: +CPPFile(const Filename &filename, const Filename &filename_as_referenced, + Source source) : + _filename(filename), _filename_as_referenced(filename_as_referenced), + _source(source) +{ + _filename.set_text(); + _filename_as_referenced.set_text(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPFile:: +CPPFile(const CPPFile ©) : + _filename(copy._filename), + _filename_as_referenced(copy._filename_as_referenced), + _source(copy._source) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPFile:: +operator = (const CPPFile ©) { + _filename = copy._filename; + _filename_as_referenced = copy._filename_as_referenced; + _source = copy._source; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPFile:: +~CPPFile() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::is_c_file +// Access: Public +// Description: Returns true if the file appears to be a C or C++ +// source code file based on its extension. That is, +// returns true if the filename ends in .c, .C, .cc, +// .cpp, or any of a series of likely extensions. +//////////////////////////////////////////////////////////////////// +bool CPPFile:: +is_c_file() const { + return is_c_file(_filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::is_c_file +// Access: Public, Static +// Description: Returns true if the file appears to be a C or C++ +// source code file based on its extension. That is, +// returns true if the filename ends in .c, .C, .cc, +// .cpp, or any of a series of likely extensions. +//////////////////////////////////////////////////////////////////// +bool CPPFile:: +is_c_file(const Filename &filename) { + string extension = filename.get_extension(); + // downcase the extension. + for (string::iterator ei = extension.begin(); + ei != extension.end(); + ++ei) { + (*ei) = tolower(*ei); + } + + return (extension == "c" || extension == "cc" || + extension == "cpp" || extension == "c++" || extension == "cxx" || + extension == "i"); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::replace_nearer +// Access: Public +// Description: If the other file is "nearer" than this file (in the +// sense that a file in the local directory is nearer +// than a file in the system directory, etc.), replaces +// this file's information with that of the other. +// Otherwise, does nothing. +//////////////////////////////////////////////////////////////////// +void CPPFile:: +replace_nearer(const CPPFile &other) { + if ((int)_source > (int)other._source) { + (*this) = other; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPFile:: +operator < (const CPPFile &other) const { + return _filename < other._filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPFile:: +operator == (const CPPFile &other) const { + return _filename == other._filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPFile:: +operator != (const CPPFile &other) const { + return _filename != other._filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::c_str +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +const char *CPPFile:: +c_str() const { + return _filename.c_str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFile::empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPFile:: +empty() const { + return _filename.empty(); +} diff --git a/dtool/src/cppparser/cppFile.h b/dtool/src/cppparser/cppFile.h new file mode 100644 index 0000000000..fad4a085ef --- /dev/null +++ b/dtool/src/cppparser/cppFile.h @@ -0,0 +1,57 @@ +// Filename: cppFile.h +// Created by: drose (11Nov99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPFILE_H +#define CPPFILE_H + +#include +#include + +/////////////////////////////////////////////////////////////////// +// Class : CPPFile +// Description : This defines a source file (typically a C++ header +// file) that is parsed by the CPPParser. Each +// declaration indicates the source file where it +// appeared. +//////////////////////////////////////////////////////////////////// +class CPPFile { +public: + enum Source { + S_local, // File resides in the current directory + S_alternate, // File resides in some other directory + S_system, // File resides in a system directory + S_none, // File is internally generated + }; + + CPPFile(const Filename &filename = "", + const Filename &filename_as_referenced = "", + Source source = S_none); + CPPFile(const CPPFile ©); + void operator = (const CPPFile ©); + ~CPPFile(); + + bool is_c_file() const; + static bool is_c_file(const Filename &filename); + + void replace_nearer(const CPPFile &other); + + bool operator < (const CPPFile &other) const; + bool operator == (const CPPFile &other) const; + bool operator != (const CPPFile &other) const; + + const char *c_str() const; + bool empty() const; + + Filename _filename; + Filename _filename_as_referenced; + Source _source; +}; + +inline ostream &operator << (ostream &out, const CPPFile &file) { + return out << file._filename; +} + +#endif + diff --git a/dtool/src/cppparser/cppFunctionGroup.cxx b/dtool/src/cppparser/cppFunctionGroup.cxx new file mode 100644 index 0000000000..292dedbe7c --- /dev/null +++ b/dtool/src/cppparser/cppFunctionGroup.cxx @@ -0,0 +1,98 @@ +// Filename: cppFunctionGroup.C +// Created by: drose (11Nov99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppFunctionGroup.h" +#include "cppFunctionType.h" +#include "cppInstance.h" +#include "indent.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionGroup::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionGroup:: +CPPFunctionGroup(const string &name) : + CPPDeclaration(CPPFile()), + _name(name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionGroup::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionGroup:: +~CPPFunctionGroup() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionGroup::get_return_type +// Access: Public +// Description: If all the functions that share this name have the +// same return type, returns that type. Otherwise, if +// some functions have different return types, returns +// NULL. +//////////////////////////////////////////////////////////////////// +CPPType *CPPFunctionGroup:: +get_return_type() const { + CPPType *return_type = NULL; + + if (!_instances.empty()) { + Instances::const_iterator ii = _instances.begin(); + return_type = (*ii)->_type->as_function_type()->_return_type; + ++ii; + while (ii != _instances.end()) { + if ((*ii)->_type->as_function_type()->_return_type != return_type) { + return (CPPType *)NULL; + } + ++ii; + } + } + + return return_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionGroup::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPFunctionGroup:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + if (!_instances.empty()) { + Instances::const_iterator ii = _instances.begin(); + (*ii)->output(out, indent_level, scope, complete); + ++ii; + while (ii != _instances.end()) { + out << ";\n"; + indent(out, indent_level); + (*ii)->output(out, indent_level, scope, complete); + ++ii; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionGroup::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPFunctionGroup:: +get_subtype() const { + return ST_function_group; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionGroup::as_function_group +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionGroup *CPPFunctionGroup:: +as_function_group() { + return this; +} diff --git a/dtool/src/cppparser/cppFunctionGroup.h b/dtool/src/cppparser/cppFunctionGroup.h new file mode 100644 index 0000000000..e4c1851dd5 --- /dev/null +++ b/dtool/src/cppparser/cppFunctionGroup.h @@ -0,0 +1,44 @@ +// Filename: cppFunctionGroup.h +// Created by: drose (11Nov99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPFUNCTIONGROUP_H +#define CPPFUNCTIONGROUP_H + +#include + +#include "cppDeclaration.h" + +class CPPInstance; + +/////////////////////////////////////////////////////////////////// +// Class : CPPFunctionGroup +// Description : This class is simply a container for one or more +// CPPInstances for functions of the same name. It's +// handy for storing in the CPPScope, so that +// CPPScope::find_symbol() can return a single pointer +// to indicate all of the functions that may share a +// given name. +//////////////////////////////////////////////////////////////////// +class CPPFunctionGroup : public CPPDeclaration { +public: + CPPFunctionGroup(const string &name); + ~CPPFunctionGroup(); + + CPPType *get_return_type() const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPFunctionGroup *as_function_group(); + + typedef vector Instances; + Instances _instances; + string _name; +}; + +#endif + + diff --git a/dtool/src/cppparser/cppFunctionType.cxx b/dtool/src/cppparser/cppFunctionType.cxx new file mode 100644 index 0000000000..8b273c5173 --- /dev/null +++ b/dtool/src/cppparser/cppFunctionType.cxx @@ -0,0 +1,352 @@ +// Filename: cppFunctionType.C +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppFunctionType.h" +#include "cppParameterList.h" +#include "cppSimpleType.h" +#include "cppInstance.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionType:: +CPPFunctionType(CPPType *return_type, CPPParameterList *parameters, + int flags) : + CPPType(CPPFile()), + _return_type(return_type), + _parameters(parameters), + _flags(flags) +{ + _class_owner = NULL; + + // If the parameter list contains just the token "void", it means no + // parameters. + if (_parameters->_parameters.size() == 1 && + _parameters->_parameters.front()->_type->as_simple_type() != NULL && + _parameters->_parameters.front()->_type->as_simple_type()->_type == + CPPSimpleType::T_void && + _parameters->_parameters.front()->_ident == NULL) { + _parameters->_parameters.clear(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionType:: +CPPFunctionType(const CPPFunctionType ©) : + CPPType(copy), + _return_type(copy._return_type), + _parameters(copy._parameters), + _flags(copy._flags), + _class_owner(copy._class_owner) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPFunctionType:: +operator = (const CPPFunctionType ©) { + CPPType::operator = (copy); + _return_type = copy._return_type; + _parameters = copy._parameters; + _flags = copy._flags; + _class_owner = copy._class_owner; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPFunctionType:: +is_fully_specified() const { + return CPPType::is_fully_specified() && + _return_type->is_fully_specified() && + _parameters->is_fully_specified(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPFunctionType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + return (*si).second; + } + + CPPFunctionType *rep = new CPPFunctionType(*this); + rep->_return_type = + _return_type->substitute_decl(subst, current_scope, global_scope) + ->as_type(); + + rep->_parameters = + _parameters->substitute_decl(subst, current_scope, global_scope); + + if (rep->_return_type == _return_type && + rep->_parameters == _parameters) { + delete rep; + rep = this; + } + rep = CPPType::new_type(rep)->as_function_type(); + + subst.insert(SubstDecl::value_type(this, rep)); + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPFunctionType:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + CPPType *rtype = _return_type->resolve_type(current_scope, global_scope); + CPPParameterList *params = + _parameters->resolve_type(current_scope, global_scope); + + if (rtype != _return_type || params != _parameters) { + CPPFunctionType *rep = new CPPFunctionType(*this); + rep->_return_type = rtype; + rep->_parameters = params; + return CPPType::new_type(rep); + } + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPFunctionType:: +is_tbd() const { + if (_return_type->is_tbd()) { + return true; + } + return _parameters->is_tbd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPFunctionType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + output(out, indent_level, scope, complete, -1); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::output +// Access: Public +// Description: The additional parameter allows us to specify the +// number of parameters we wish to show the default +// values for. If num_default_parameters is >= 0, it +// indicates the number of default parameter values to +// show on output. Otherwise, all parameter values are +// shown. +//////////////////////////////////////////////////////////////////// +void CPPFunctionType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete, + int num_default_parameters) const { + _return_type->output(out, indent_level, scope, complete); + out << "("; + _parameters->output(out, scope, true, num_default_parameters); + out << ")"; + if (_flags & F_const_method) { + out << " const"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::output_instance +// Access: Public, Virtual +// Description: Formats a C++-looking line that defines an instance +// of the given type, with the indicated name. In most +// cases this will be "type name", but some types have +// special exceptions. +//////////////////////////////////////////////////////////////////// +void CPPFunctionType:: +output_instance(ostream &out, int indent_level, CPPScope *scope, + bool complete, const string &prename, + const string &name) const { + output_instance(out, indent_level, scope, complete, prename, name, -1); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::output_instance +// Access: Public +// Description: The additional parameter allows us to specify the +// number of parameters we wish to show the default +// values for. If num_default_parameters is >= 0, it +// indicates the number of default parameter values to +// show on output. Otherwise, all parameter values are +// shown. +//////////////////////////////////////////////////////////////////// +void CPPFunctionType:: +output_instance(ostream &out, int indent_level, CPPScope *scope, + bool complete, const string &prename, + const string &name, int num_default_parameters) const { + ostringstream parm_string; + parm_string << "("; + _parameters->output(parm_string, scope, true, num_default_parameters); + parm_string << ")"; + string str = parm_string.str(); + + if (_flags & (F_constructor | F_destructor)) { + // No return type for constructors and destructors. + out << prename << name << str; + + } else { + if (prename.empty()) { + _return_type->output_instance(out, indent_level, scope, complete, + "", prename + name + str); + } else { + _return_type->output_instance(out, indent_level, scope, complete, + "", "(" + prename + name + ")" + str); + } + } + + if (_flags & F_const_method) { + out << " const"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::get_num_default_parameters +// Access: Public +// Description: Returns the number of parameters in the list that may +// take default values. +//////////////////////////////////////////////////////////////////// +int CPPFunctionType:: +get_num_default_parameters() const { + // The trick is just to count, beginning from the end and working + // towards the front, the number of parameters that have some + // initializer. + + const CPPParameterList::Parameters ¶ms = _parameters->_parameters; + CPPParameterList::Parameters::const_reverse_iterator pi; + int count = 0; + for (pi = params.rbegin(); + pi != params.rend() && (*pi)->_initializer != (CPPExpression *)NULL; + ++pi) { + count++; + } + + return count; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPFunctionType:: +get_subtype() const { + return ST_function; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::as_function_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionType *CPPFunctionType:: +as_function_type() { + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::is_equivalent_function +// Access: Public +// Description: This is similar to is_equal(), except it is more +// forgiving: it considers the functions to be +// equivalent only if the return type and the types of +// all parameters match. +//////////////////////////////////////////////////////////////////// +bool CPPFunctionType:: +is_equivalent_function(const CPPFunctionType &other) const { + if (!_return_type->is_equivalent(*other._return_type)) { + return false; + } + + if (_flags != other._flags) { + return false; + } + + if (!_parameters->is_equivalent(*other._parameters)) { + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPFunctionType:: +is_equal(const CPPDeclaration *other) const { + const CPPFunctionType *ot = ((CPPDeclaration *)other)->as_function_type(); + assert(ot != NULL); + + if (_return_type != ot->_return_type) { + return false; + } + if (_flags != ot->_flags) { + return false; + } + if (*_parameters != *ot->_parameters) { + return false; + } + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPFunctionType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPFunctionType:: +is_less(const CPPDeclaration *other) const { + const CPPFunctionType *ot = ((CPPDeclaration *)other)->as_function_type(); + assert(ot != NULL); + + if (_return_type != ot->_return_type) { + return _return_type < ot->_return_type; + } + if (_flags != ot->_flags) { + return _flags < ot->_flags; + } + + return *_parameters < *ot->_parameters; +} diff --git a/dtool/src/cppparser/cppFunctionType.h b/dtool/src/cppparser/cppFunctionType.h new file mode 100644 index 0000000000..335276fa8f --- /dev/null +++ b/dtool/src/cppparser/cppFunctionType.h @@ -0,0 +1,78 @@ +// Filename: cppFunctionType.h +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPFUNCTIONTYPE_H +#define CPPFUNCTIONTYPE_H + +#include + +#include "cppType.h" + +class CPPParameterList; +class CPPIdentifier; + +/////////////////////////////////////////////////////////////////// +// Class : CPPFunctionType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPFunctionType : public CPPType { +public: + enum Flags { + F_const_method = 0x01, + F_operator_typecast = 0x02, + F_constructor = 0x04, + F_destructor = 0x08, + F_method_pointer = 0x10, + }; + + CPPFunctionType(CPPType *return_type, CPPParameterList *parameters, + int flags); + CPPFunctionType(const CPPFunctionType ©); + void operator = (const CPPFunctionType ©); + + CPPType *_return_type; + CPPParameterList *_parameters; + int _flags; + + virtual bool is_fully_specified() const; + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_tbd() const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + void output(ostream &out, int indent_level, CPPScope *scope, + bool complete, int num_default_parameters) const; + virtual void output_instance(ostream &out, int indent_level, + CPPScope *scope, + bool complete, const string &prename, + const string &name) const; + void output_instance(ostream &out, int indent_level, + CPPScope *scope, + bool complete, const string &prename, + const string &name, + int num_default_parameters) const; + int get_num_default_parameters() const; + + virtual SubType get_subtype() const; + + virtual CPPFunctionType *as_function_type(); + + bool is_equivalent_function(const CPPFunctionType &other) const; + + CPPIdentifier *_class_owner; + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +#endif + diff --git a/dtool/src/cppparser/cppGlobals.cxx b/dtool/src/cppparser/cppGlobals.cxx new file mode 100644 index 0000000000..3099235c3c --- /dev/null +++ b/dtool/src/cppparser/cppGlobals.cxx @@ -0,0 +1,8 @@ +// Filename: cppGlobals.C +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#include "cppGlobals.h" + +string cpp_longlong_keyword; diff --git a/dtool/src/cppparser/cppGlobals.h b/dtool/src/cppparser/cppGlobals.h new file mode 100644 index 0000000000..6aafc64354 --- /dev/null +++ b/dtool/src/cppparser/cppGlobals.h @@ -0,0 +1,20 @@ +// Filename: cppGlobals.h +// Created by: drose (16May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPGLOBALS_H +#define CPPGLOBALS_H + +#include + +// Some compilers (notably VC++) define a special keyword to represent +// a 64-bit integer, but don't recognize "long long int". To parse +// (and generate) code for these compilers, set this string to the +// 64-bit integer typename keyword. +extern string cpp_longlong_keyword; + + +#endif + + diff --git a/dtool/src/cppparser/cppIdentifier.cxx b/dtool/src/cppparser/cppIdentifier.cxx new file mode 100644 index 0000000000..f370a49ba6 --- /dev/null +++ b/dtool/src/cppparser/cppIdentifier.cxx @@ -0,0 +1,560 @@ +// Filename: cppIdentifier.C +// Created by: drose (26Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppIdentifier.h" +#include "cppScope.h" +#include "cppTemplateScope.h" +#include "cppPreprocessor.h" +#include "cppTemplateParameterList.h" +#include "cppTBDType.h" +#include "cppStructType.h" + + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPIdentifier:: +CPPIdentifier(const string &name, const CPPFile &file) : _file(file) { + _names.push_back(CPPNameComponent(name)); + _native_scope = (CPPScope *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPIdentifier:: +CPPIdentifier(const CPPNameComponent &name) { + _names.push_back(name); + _native_scope = (CPPScope *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::add_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPIdentifier:: +add_name(const string &name) { + _names.push_back(CPPNameComponent(name)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::add_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPIdentifier:: +add_name(const CPPNameComponent &name) { + _names.push_back(name); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::Equivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPIdentifier:: +operator == (const CPPIdentifier &other) const { + if (_names.size() != other._names.size()) { + return false; + } + for (int i = 0; i < (int)_names.size(); i++) { + if (_names[i] != other._names[i]) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::Nonequivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPIdentifier:: +operator != (const CPPIdentifier &other) const { + return !(*this == other); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPIdentifier:: +operator < (const CPPIdentifier &other) const { + if (_names.size() != other._names.size()) { + return _names.size() < other._names.size(); + } + for (int i = 0; i < (int)_names.size(); i++) { + if (_names[i] != other._names[i]) { + return _names[i] < other._names[i]; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::is_scoped +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPIdentifier:: +is_scoped() const { + return _names.size() > 1; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::get_simple_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPIdentifier:: +get_simple_name() const { + return _names.back().get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::get_local_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPIdentifier:: +get_local_name(CPPScope *scope) const { + assert(!_names.empty()); + + string result; + + if (scope == NULL || (_native_scope == NULL && _names.size() == 1)) { + result = _names.back().get_name_with_templ(scope); + } else if (_names.front().empty()) { + result = get_fully_scoped_name(); + } else { + // Determine the scope of everything up until but not including the + // last name. + CPPScope *my_scope = get_scope(scope, NULL); + + if (my_scope == NULL) { + result = get_fully_scoped_name(); + } else if (my_scope == scope) { + return _names.back().get_name_with_templ(scope); + } else { + result = my_scope->get_local_name(scope); + if (!result.empty()) { + result += "::"; + } + result += _names.back().get_name_with_templ(scope); + } + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::get_fully_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPIdentifier:: +get_fully_scoped_name() const { + assert(!_names.empty()); + Names::const_iterator ni = _names.begin(); + string name = (*ni).get_name_with_templ(); + ++ni; + while (ni != _names.end()) { + name += "::" + (*ni).get_name_with_templ(); + ++ni; + } + return name; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::is_fully_specified +// Access: Public +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPIdentifier:: +is_fully_specified() const { + Names::const_iterator ni; + for (ni = _names.begin(); ni != _names.end(); ++ni) { + if ((*ni).has_templ() && !(*ni).get_templ()->is_fully_specified()) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::is_tbd +// Access: Public +// Description: Returns true if the identifier includes a +// template parameter list that includes some +// not-yet-defined type. +//////////////////////////////////////////////////////////////////// +bool CPPIdentifier:: +is_tbd() const { + Names::const_iterator ni; + for (ni = _names.begin(); ni != _names.end(); ++ni) { + if ((*ni).is_tbd()) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::get_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPIdentifier:: +get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + assert(!_names.empty()); + + CPPScope *scope = _native_scope; + if (scope == (CPPScope *)NULL) { + scope = current_scope; + } + int i = 0; + + if (_names[i].empty()) { + // This identifier starts with a ::, thus it begins at the global + // scope. + scope = global_scope; + i++; + } + + while (i + 1 < (int)_names.size() && scope != NULL) { + CPPScope *next_scope = scope->find_scope(_names[i].get_name()); + if (next_scope == (CPPScope *)NULL) { + if (error_sink != NULL) { + error_sink->error("Symbol " + _names[i].get_name() + + " is not a known scope in " + + scope->get_fully_scoped_name()); + } + return (CPPScope *)NULL; + } + if (_names[i].has_templ()) { + next_scope = next_scope->instantiate(_names[i].get_templ(), + current_scope, global_scope); + } + scope = next_scope; + i++; + } + + return scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::get_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPIdentifier:: +get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPDeclaration::SubstDecl &subst, + CPPPreprocessor *error_sink) const { + assert(!_names.empty()); + + CPPScope *scope = _native_scope; + if (scope == (CPPScope *)NULL) { + scope = current_scope; + } + int i = 0; + + if (_names[i].empty()) { + // This identifier starts with a ::, thus it begins at the global + // scope. + scope = global_scope; + i++; + } + + while (i + 1 < (int)_names.size() && scope != NULL) { + CPPScope *next_scope = scope->find_scope(_names[i].get_name(), subst, + global_scope); + if (next_scope == (CPPScope *)NULL) { + if (error_sink != NULL) { + error_sink->error("Symbol " + _names[i].get_name() + + " is not a known scope in " + + scope->get_fully_scoped_name()); + } + return (CPPScope *)NULL; + } + if (_names[i].has_templ()) { + next_scope = next_scope->instantiate(_names[i].get_templ(), + current_scope, global_scope); + } + scope = next_scope; + i++; + } + + return scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::find_type +// Access: Public +// Description: Looks up the identifier in the current and/or global +// scopes, and returns a CPPType pointer if it seems to +// refer to a type, or NULL if it does not. If +// force_instantiate is true, the type will be +// instantiated as fully as possible right now, even if +// it means instantiating it into an identical template +// type. Otherwise, the instantiation may be delayed +// for optimization reasons, and a CPPTBDType +// placeholder may be returned instead. +//////////////////////////////////////////////////////////////////// +CPPType *CPPIdentifier:: +find_type(CPPScope *current_scope, CPPScope *global_scope, + bool force_instantiate, + CPPPreprocessor *error_sink) const { + CPPScope *scope = get_scope(current_scope, global_scope, error_sink); + if (scope == NULL) { + return NULL; + } + + CPPType *type = NULL; + if (!_names.back().has_templ()) { + type = scope->find_type(get_simple_name()); + + } else { + CPPDeclaration *decl = find_symbol(current_scope, global_scope, error_sink); + type = decl->as_type(); + /* + if (type != NULL) { + if (!type->is_incomplete() || force_instantiate) { + type = type->instantiate(_names.back().get_templ(), + current_scope, global_scope, + error_sink)->as_type(); + + // If we ended up with another template, instantiate later. + if (type->is_template() && !force_instantiate) { + type = CPPType::new_type(new CPPTBDType((CPPIdentifier *)this)); + } + + } else { + // Otherwise, we'll have to instantiate the type later. + type = CPPType::new_type(new CPPTBDType((CPPIdentifier *)this)); + } + // type->_file.replace_nearer(_file); + } + */ + } + return type; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::find_type +// Access: Public +// Description: This flavor of find_type() will instantiate any scope +// names in the identifier. It's useful for fully +// defining a type while instantiating a class. +//////////////////////////////////////////////////////////////////// +CPPType *CPPIdentifier:: +find_type(CPPScope *current_scope, CPPScope *global_scope, + CPPDeclaration::SubstDecl &subst, + CPPPreprocessor *error_sink) const { + CPPScope *scope = get_scope(current_scope, global_scope, subst, error_sink); + if (scope == NULL) { + return NULL; + } + CPPType *type = scope->find_type(get_simple_name(), subst, global_scope); + if (type != NULL && _names.back().has_templ()) { + // This is a template type. + + if (is_fully_specified()) { + // If our identifier fully specifies the instantiation, then + // apply it. + CPPDeclaration *decl = + type->instantiate(_names.back().get_templ(), + current_scope, global_scope, + error_sink); + assert(decl != NULL); + CPPType *new_type = decl->as_type(); + assert(new_type != NULL); + if (new_type == type) { + type = CPPType::new_type(new CPPTBDType((CPPIdentifier *)this)); + // type = new_type; + } else { + type = new_type; + } + } else { + // Otherwise, we'll have to instantiate the type later. + type = CPPType::new_type(new CPPTBDType((CPPIdentifier *)this)); + } + // type->_file.replace_nearer(_file); + } + return type; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::find_symbol +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPIdentifier:: +find_symbol(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + CPPScope *scope = get_scope(current_scope, global_scope, error_sink); + if (scope == NULL) { + return NULL; + } + CPPDeclaration *sym; + if (!_names.back().has_templ()) { + sym = scope->find_symbol(get_simple_name()); + + } else { + sym = scope->find_template(get_simple_name()); + if (sym != NULL) { + CPPType *type = sym->as_type(); + if (type != NULL && type->is_incomplete()) { + // We can't instantiate an incomplete type. + sym = CPPType::new_type(new CPPTBDType((CPPIdentifier *)this)); + } else { + // Instantiate the symbol. + sym = sym->instantiate(_names.back().get_templ(), current_scope, + global_scope, error_sink); + } + } + } + + return sym; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::find_template +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPIdentifier:: +find_template(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + CPPScope *scope = get_scope(current_scope, global_scope, error_sink); + if (scope == NULL) { + return NULL; + } + return scope->find_template(get_simple_name()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::find_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPIdentifier:: +find_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + CPPScope *scope = get_scope(current_scope, global_scope, error_sink); + if (scope == NULL) { + return NULL; + } + return scope->find_scope(get_simple_name()); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::substitute_decl +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPIdentifier *CPPIdentifier:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + CPPIdentifier *rep = new CPPIdentifier(*this); + + bool anything_changed = false; + for (int i = 0; i < (int)rep->_names.size(); i++) { + if (_names[i].has_templ()) { + rep->_names[i].set_templ + (_names[i].get_templ()->substitute_decl(subst, current_scope, global_scope)); + if (rep->_names[i].get_templ() != _names[i].get_templ()) { + anything_changed = true; + } + } + } + + if (!anything_changed) { + delete rep; + rep = this; + } + + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPIdentifier:: +output(ostream &out, CPPScope *scope) const { + if (scope == NULL) { + output_fully_scoped_name(out); + } else { + output_local_name(out, scope); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::output_local_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPIdentifier:: +output_local_name(ostream &out, CPPScope *scope) const { + assert(!_names.empty()); + + if (scope == NULL || (_native_scope == NULL && _names.size() == 1)) { + out << _names.back(); + } else if (_names.front().empty()) { + output_fully_scoped_name(out); + } else { + // Determine the scope of everything up until but not including the + // last name. + CPPScope *my_scope = get_scope(scope, NULL); + + if (my_scope == NULL) { + output_fully_scoped_name(out); + } else { + out << my_scope->get_local_name(scope) << "::" << _names.back(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPIdentifier::output_fully_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPIdentifier:: +output_fully_scoped_name(ostream &out) const { + if (_native_scope != NULL) { + _native_scope->output(out, (CPPScope *)NULL); + out << "::"; + } + Names::const_iterator ni = _names.begin(); + out << (*ni); + ++ni; + while (ni != _names.end()) { + out << "::" << (*ni); + ++ni; + } +} + diff --git a/dtool/src/cppparser/cppIdentifier.h b/dtool/src/cppparser/cppIdentifier.h new file mode 100644 index 0000000000..9d47d6fedf --- /dev/null +++ b/dtool/src/cppparser/cppIdentifier.h @@ -0,0 +1,90 @@ +// Filename: cppIdentifier.h +// Created by: drose (26Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPIDENTIFIER_H +#define CPPIDENTIFIER_H + +#include + +#include "cppDeclaration.h" +#include "cppNameComponent.h" +#include "cppFile.h" + +#include +#include + +class CPPScope; +class CPPType; +class CPPPreprocessor; +class CPPTemplateParameterList; + +/////////////////////////////////////////////////////////////////// +// Class : CPPIdentifier +// Description : +//////////////////////////////////////////////////////////////////// +class CPPIdentifier { +public: + CPPIdentifier(const string &name, const CPPFile &file = CPPFile()); + CPPIdentifier(const CPPNameComponent &name); + void add_name(const string &name); + void add_name(const CPPNameComponent &name); + + bool operator == (const CPPIdentifier &other) const; + bool operator != (const CPPIdentifier &other) const; + bool operator < (const CPPIdentifier &other) const; + + bool is_scoped() const; + + string get_simple_name() const; + string get_local_name(CPPScope *scope = NULL) const; + string get_fully_scoped_name() const; + + bool is_fully_specified() const; + bool is_tbd() const; + + + CPPScope *get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + CPPScope *get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPDeclaration::SubstDecl &subst, + CPPPreprocessor *error_sink = NULL) const; + + CPPType *find_type(CPPScope *current_scope, CPPScope *global_scope, + bool force_instantiate = false, + CPPPreprocessor *error_sink = NULL) const; + CPPType *find_type(CPPScope *current_scope, CPPScope *global_scope, + CPPDeclaration::SubstDecl &subst, + CPPPreprocessor *error_sink = NULL) const; + CPPDeclaration *find_symbol(CPPScope *current_scope, + CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + CPPDeclaration *find_template(CPPScope *current_scope, + CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + CPPScope *find_scope(CPPScope *current_scope, + CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + + CPPIdentifier *substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + void output(ostream &out, CPPScope *scope) const; + void output_local_name(ostream &out, CPPScope *scope) const; + void output_fully_scoped_name(ostream &out) const; + + typedef vector Names; + Names _names; + CPPScope *_native_scope; + CPPFile _file; +}; + +inline ostream &operator << (ostream &out, const CPPIdentifier &identifier) { + identifier.output(out, (CPPScope *)NULL); + return out; +} + + +#endif diff --git a/dtool/src/cppparser/cppInstance.cxx b/dtool/src/cppparser/cppInstance.cxx new file mode 100644 index 0000000000..5be002fb32 --- /dev/null +++ b/dtool/src/cppparser/cppInstance.cxx @@ -0,0 +1,552 @@ +// Filename: cppInstance.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppInstance.h" +#include "cppInstanceIdentifier.h" +#include "cppIdentifier.h" +#include "cppTemplateScope.h" +#include "cppFunctionType.h" +#include "cppSimpleType.h" +#include "cppExpression.h" +#include "cppPreprocessor.h" +#include "indent.h" + +#include "cppReferenceType.h" +#include "cppConstType.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstance:: +CPPInstance(CPPType *type, const string &name, int storage_class) : + CPPDeclaration(CPPFile()), + _type(type), + _ident(new CPPIdentifier(name)), + _storage_class(storage_class) +{ + _initializer = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstance:: +CPPInstance(CPPType *type, CPPIdentifier *ident, int storage_class) : + CPPDeclaration(CPPFile()), + _type(type), + _ident(ident), + _storage_class(storage_class) +{ + _initializer = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::Constructor +// Access: Public +// Description: Constructs a new CPPInstance object that defines a +// variable of the indicated type according to the type +// and the InstanceIdentifier. The InstanceIdentifier +// pointer is deallocated. +//////////////////////////////////////////////////////////////////// +CPPInstance:: +CPPInstance(CPPType *type, CPPInstanceIdentifier *ii, int storage_class, + const CPPFile &file) : + CPPDeclaration(file) +{ + _type = ii->unroll_type(type); + _ident = ii->_ident; + ii->_ident = NULL; + _storage_class = storage_class; + delete ii; + _initializer = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstance:: +CPPInstance(const CPPInstance ©) : + CPPDeclaration(copy), + _type(copy._type), + _ident(copy._ident), + _initializer(copy._initializer), + _storage_class(copy._storage_class) +{ + assert(_type != NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstance:: +~CPPInstance() { + // Can't delete the identifier. Don't try. +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::make_typecast_function +// Access: Public, Static +// Description: Constructs and returns a new CPPInstance object that +// corresponds to a function prototype declaration for a +// typecast method, whose return type is implicit in the +// identifier type. +//////////////////////////////////////////////////////////////////// +CPPInstance *CPPInstance:: +make_typecast_function(CPPInstance *inst, CPPIdentifier *ident, + CPPParameterList *parameters, int function_flags) { + CPPType *type = CPPType::new_type(inst->_type); + delete inst; + + function_flags |= (int)CPPFunctionType::F_operator_typecast; + + CPPType *ft = + CPPType::new_type(new CPPFunctionType(type, parameters, function_flags)); + + return new CPPInstance(ft, ident); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::Equivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPInstance:: +operator == (const CPPInstance &other) const { + if (_type != other._type) { + return false; + } + if (_storage_class != other._storage_class) { + return false; + } + + // We *do* care about the identifier. We need to differentiate + // types of function variables, among possibly other things, based + // on the identifier. + if ((_ident == NULL && other._ident != NULL) || + (_ident != NULL && other._ident == NULL) || + (_ident != NULL && other._ident != NULL && *_ident != *other._ident)) + { + return false; + } + + // We similarly care about the initializer. + if ((_initializer == NULL && other._initializer != NULL) || + (_initializer != NULL && other._initializer == NULL) || + (_initializer != NULL && other._initializer != NULL && + *_initializer != *other._initializer)) + { + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::Nonequivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPInstance:: +operator != (const CPPInstance &other) const { + return !operator == (other); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPInstance:: +operator < (const CPPInstance &other) const { + if (_type != other._type) { + return _type < other._type; + } + if (_storage_class != other._storage_class) { + return _storage_class < other._storage_class; + } + + // We *do* care about the identifier. We need to differentiate + // types of function variables, among possibly other things, based + // on the identifier. + if ((_ident == NULL && other._ident != NULL) || + (_ident != NULL && other._ident == NULL) || + (_ident != NULL && other._ident != NULL && *_ident != *other._ident)) + { + if (_ident == NULL || other._ident == NULL) { + return _ident < other._ident; + } + return *_ident < *other._ident; + } + + // We similarly care about the initializer. + if ((_initializer == NULL && other._initializer != NULL) || + (_initializer != NULL && other._initializer == NULL) || + (_initializer != NULL && other._initializer != NULL && + *_initializer != *other._initializer)) + { + if (_initializer == NULL || other._initializer == NULL) { + return _initializer < other._initializer; + } + return *_initializer < *other._initializer; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::set_initializer +// Access: Public +// Description: Sets the value of the expression that is used to +// initialize the variable, or the default value for a +// parameter. If a non-null expression is set on a +// function declaration, it implies that the function is +// pure virtual. +//////////////////////////////////////////////////////////////////// +void CPPInstance:: +set_initializer(CPPExpression *initializer) { + if (_type->as_function_type() != (CPPFunctionType *)NULL) { + // This is a function declaration. + if (initializer == (CPPExpression *)NULL) { + _storage_class &= ~SC_pure_virtual; + } else { + _storage_class |= SC_pure_virtual; + } + _initializer = (CPPExpression *)NULL; + } else { + _initializer = initializer; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::is_scoped +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPInstance:: +is_scoped() const { + if (_ident == NULL) { + return false; + } else { + return _ident->is_scoped(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::get_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPInstance:: +get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + if (_ident == NULL) { + return current_scope; + } else { + return _ident->get_scope(current_scope, global_scope, error_sink); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::get_simple_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPInstance:: +get_simple_name() const { + if (_ident == NULL) { + return ""; + } else { + return _ident->get_simple_name(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::get_local_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPInstance:: +get_local_name(CPPScope *scope) const { + if (_ident == NULL) { + return ""; + } else { + return _ident->get_local_name(scope); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::get_fully_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPInstance:: +get_fully_scoped_name() const { + if (_ident == NULL) { + return ""; + } else { + return _ident->get_fully_scoped_name(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::check_for_constructor +// Access: Public +// Description: If this is a function type instance, checks whether +// the function name matches the class name (or ~name), +// and if so, flags it as a constructor (or destructor). +//////////////////////////////////////////////////////////////////// +void CPPInstance:: +check_for_constructor(CPPScope *current_scope, CPPScope *global_scope) { + CPPScope *scope = get_scope(current_scope, global_scope); + if (scope == NULL) { + scope = current_scope; + } + + CPPFunctionType *func = _type->as_function_type(); + if (func != NULL) { + string method_name = get_local_name(scope); + string class_name = scope->get_local_name(); + + if (!method_name.empty() && !class_name.empty()) { + if (method_name == class_name) { + CPPType *void_type = CPPType::new_type + (new CPPSimpleType(CPPSimpleType::T_void)); + + _type = CPPType::new_type + (new CPPFunctionType(void_type, func->_parameters, + func->_flags | CPPFunctionType::F_constructor)); + + } else if (method_name == "~" + class_name) { + CPPType *void_type = CPPType::new_type + (new CPPSimpleType(CPPSimpleType::T_void)); + + _type = CPPType::new_type + (new CPPFunctionType(void_type, func->_parameters, + func->_flags | CPPFunctionType::F_destructor)); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::instantiate +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPInstance:: +instantiate(const CPPTemplateParameterList *actual_params, + CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + if (!is_template()) { + if (error_sink != NULL) { + error_sink->warning("Ignoring template parameters for instance " + + _ident->get_local_name()); + } + return (CPPInstance *)this; + } + + Instantiations::const_iterator ii; + ii = _instantiations.find(actual_params); + if (ii != _instantiations.end()) { + // We've already instantiated this instance with these parameters. + // Return that. + return (*ii).second; + } + + + CPPTemplateScope *tscope = get_template_scope(); + + CPPDeclaration::SubstDecl subst; + actual_params->build_subst_decl(tscope->_parameters, subst, + current_scope, global_scope); + + CPPInstance *inst = + ((CPPInstance *)this)->substitute_decl(subst, current_scope, global_scope) + ->as_instance(); + if (inst == this) { + // Hmm, nothing to substitute. Make a new instance anyway, so we + // can change the name. + inst = new CPPInstance(*this); + } + assert(inst != NULL); + inst->_ident = inst->_ident->substitute_decl(subst, current_scope, global_scope); + if (inst->_ident == _ident) { + inst->_ident = new CPPIdentifier(*inst->_ident); + } + inst->_ident->_names.back().set_templ + (new CPPTemplateParameterList(*actual_params)); + + inst->_template_scope = NULL; + + ((CPPInstance *)this)->_instantiations.insert(Instantiations::value_type(actual_params, inst)); + return inst; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPInstance:: +is_fully_specified() const { + if (_ident != NULL && !_ident->is_fully_specified()) { + return false; + } + if (_initializer != NULL && !_initializer->is_fully_specified()) { + return false; + } + return CPPDeclaration::is_fully_specified() && + _type->is_fully_specified(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPInstance:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + CPPDeclaration *top = + CPPDeclaration::substitute_decl(subst, current_scope, global_scope); + if (top != this) { + return top; + } + + CPPInstance *rep = new CPPInstance(*this); + CPPDeclaration *new_type = + _type->substitute_decl(subst, current_scope, global_scope); + rep->_type = new_type->as_type(); + + if (rep->_type == NULL) { + cerr << "Type " << *_type << " became " << *new_type << " which isn't a type\n"; + rep->_type = _type; + } + + if (_initializer != NULL) { + rep->_initializer = + _initializer->substitute_decl(subst, current_scope, global_scope) + ->as_expression(); + } + + if (rep->_type == _type && + rep->_initializer == _initializer) { + delete rep; + rep = this; + } + + subst.insert(SubstDecl::value_type(this, rep)); + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPInstance:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + output(out, indent_level, scope, complete, -1); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::output +// Access: Public +// Description: The extra parameter comes into play only when we +// happen to be outputting a function prototype. See +// CPPFunctionType::output(). +//////////////////////////////////////////////////////////////////// +void CPPInstance:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete, + int num_default_parameters) const { + if (is_template()) { + get_template_scope()->_parameters.write_formal(out, scope); + indent(out, indent_level); + } + if (_storage_class & SC_static) { + out << "static "; + } + if (_storage_class & SC_extern) { + out << "extern "; + } + if (_storage_class & SC_c_binding) { + out << "\"C\" "; + } + if (_storage_class & SC_virtual) { + out << "virtual "; + } + if (_storage_class & SC_inline) { + out << "inline "; + } + if (_storage_class & SC_explicit) { + out << "explicit "; + } + if (_storage_class & SC_register) { + out << "register "; + } + if (_storage_class & SC_volatile) { + out << "volatile "; + } + if (_storage_class & SC_mutable) { + out << "mutable "; + } + + string name; + if (_ident != NULL) { + name = _ident->get_local_name(scope); + } + assert(_type != NULL); + + if (_type->as_function_type()) { + _type->as_function_type()-> + output_instance(out, indent_level, scope, complete, "", name, + num_default_parameters); + } else { + _type->output_instance(out, indent_level, scope, complete, "", name); + } + + if (_storage_class & SC_pure_virtual) { + out << " = 0"; + } + if (_initializer != NULL) { + out << " = (" << *_initializer << ")"; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPInstance:: +get_subtype() const { + return ST_instance; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstance::as_instance +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstance *CPPInstance:: +as_instance() { + return this; +} diff --git a/dtool/src/cppparser/cppInstance.h b/dtool/src/cppparser/cppInstance.h new file mode 100644 index 0000000000..1a621c3216 --- /dev/null +++ b/dtool/src/cppparser/cppInstance.h @@ -0,0 +1,103 @@ +// Filename: cppInstance.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPINSTANCE_H +#define CPPINSTANCE_H + +#include + +#include "cppDeclaration.h" +#include "cppType.h" +#include "cppIdentifier.h" +#include "cppTemplateParameterList.h" + +class CPPInstanceIdentifier; +class CPPIdentifier; +class CPPParameterList; +class CPPScope; +class CPPExpression; + +/////////////////////////////////////////////////////////////////// +// Class : CPPInstance +// Description : +//////////////////////////////////////////////////////////////////// +class CPPInstance : public CPPDeclaration { +public: + // Some of these flags clearly only make sense in certain contexts, + // e.g. for a function or method. + enum StorageClass { + SC_static = 0x001, + SC_extern = 0x002, + SC_c_binding = 0x004, + SC_virtual = 0x008, + SC_inline = 0x010, + SC_explicit = 0x020, + SC_register = 0x040, + SC_pure_virtual = 0x080, + SC_volatile = 0x100, + SC_mutable = 0x200, + + // This bit is only set by CPPStructType::check_virtual(). + SC_inherited_virtual = 0x400, + }; + + CPPInstance(CPPType *type, const string &name, int storage_class = 0); + CPPInstance(CPPType *type, CPPIdentifier *ident, int storage_class = 0); + CPPInstance(CPPType *type, CPPInstanceIdentifier *ii, + int storage_class, const CPPFile &file); + CPPInstance(const CPPInstance ©); + ~CPPInstance(); + + static CPPInstance * + make_typecast_function(CPPInstance *inst, CPPIdentifier *ident, + CPPParameterList *parameters, int function_flags); + + bool operator == (const CPPInstance &other) const; + bool operator != (const CPPInstance &other) const; + bool operator < (const CPPInstance &other) const; + + void set_initializer(CPPExpression *initializer); + + bool is_scoped() const; + CPPScope *get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + + string get_simple_name() const; + string get_local_name(CPPScope *scope = NULL) const; + string get_fully_scoped_name() const; + + void check_for_constructor(CPPScope *current_scope, CPPScope *global_scope); + + virtual CPPDeclaration * + instantiate(const CPPTemplateParameterList *actual_params, + CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + + virtual bool is_fully_specified() const; + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + void output(ostream &out, int indent_level, CPPScope *scope, + bool complete, int num_default_parameters) const; + virtual SubType get_subtype() const; + + virtual CPPInstance *as_instance(); + + CPPType *_type; + CPPIdentifier *_ident; + CPPExpression *_initializer; + + int _storage_class; + +private: + typedef map Instantiations; + Instantiations _instantiations; +}; + +#endif + diff --git a/dtool/src/cppparser/cppInstanceIdentifier.cxx b/dtool/src/cppparser/cppInstanceIdentifier.cxx new file mode 100644 index 0000000000..5c6048604c --- /dev/null +++ b/dtool/src/cppparser/cppInstanceIdentifier.cxx @@ -0,0 +1,212 @@ +// Filename: cppInstanceIdentifier.C +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppInstanceIdentifier.h" +#include "cppPointerType.h" +#include "cppReferenceType.h" +#include "cppArrayType.h" +#include "cppConstType.h" +#include "cppFunctionType.h" +#include "cppParameterList.h" +#include "cppIdentifier.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::Modifier::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstanceIdentifier::Modifier:: +Modifier(CPPInstanceIdentifierType type) : + _type(type) +{ + _func_params = NULL; + _func_flags = 0; + _scoping = NULL; + _expr = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::Modifier::named func_type constructor +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstanceIdentifier::Modifier CPPInstanceIdentifier::Modifier:: +func_type(CPPParameterList *params, int flags) { + Modifier mod(IIT_func); + mod._func_params = params; + mod._func_flags = flags; + return mod; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::Modifier::named array_type constructor +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstanceIdentifier::Modifier CPPInstanceIdentifier::Modifier:: +array_type(CPPExpression *expr) { + Modifier mod(IIT_array); + mod._expr = expr; + return mod; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::Modifier::named scoped_pointer_type +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstanceIdentifier::Modifier CPPInstanceIdentifier::Modifier:: +scoped_pointer_type(CPPIdentifier *scoping) { + Modifier mod(IIT_scoped_pointer); + mod._scoping = scoping; + return mod; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPInstanceIdentifier:: +CPPInstanceIdentifier(CPPIdentifier *ident) : _ident(ident) { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::unroll_type +// Access: Public +// Description: Unrolls the list of type punctuation on either side +// of the identifier to determine the actual type +// represented by the identifier, given the indicated +// starting type (that is, the type name written to the +// left of the identifier). +//////////////////////////////////////////////////////////////////// +CPPType *CPPInstanceIdentifier:: +unroll_type(CPPType *start_type) { + CPPType *result = r_unroll_type(start_type, _modifiers.begin()); + return result; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::add_modifier +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPInstanceIdentifier:: +add_modifier(CPPInstanceIdentifierType type) { + _modifiers.push_back(Modifier(type)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::add_modifier +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPInstanceIdentifier:: +add_func_modifier(CPPParameterList *params, int flags) { + _modifiers.push_back(Modifier::func_type(params, flags)); +} + +void CPPInstanceIdentifier:: +add_scoped_pointer_modifier(CPPIdentifier *scoping) { + _modifiers.push_back(Modifier::scoped_pointer_type(scoping)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::add_modifier +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPInstanceIdentifier:: +add_array_modifier(CPPExpression *expr) { + _modifiers.push_back(Modifier::array_type(expr)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::get_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPInstanceIdentifier:: +get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + if (_ident == NULL) { + return current_scope; + } else { + return _ident->get_scope(current_scope, global_scope, error_sink); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPInstanceIdentifier::r_unroll_type +// Access: Private +// Description: The recursive implementation of unroll_type(). +//////////////////////////////////////////////////////////////////// +CPPType *CPPInstanceIdentifier:: +r_unroll_type(CPPType *start_type, + CPPInstanceIdentifier::Modifiers::const_iterator mi) { + start_type = CPPType::new_type(start_type); + + if (mi == _modifiers.end()) { + return start_type; + } + + const Modifier &mod = (*mi); + ++mi; + + CPPType *result = NULL; + + switch (mod._type) { + case IIT_pointer: + result = new CPPPointerType(r_unroll_type(start_type, mi)); + break; + + case IIT_reference: + result = new CPPReferenceType(r_unroll_type(start_type, mi)); + break; + + case IIT_scoped_pointer: + { + CPPType *type = r_unroll_type(start_type, mi); + CPPFunctionType *ftype = type->as_function_type(); + if (ftype != NULL) { + ftype = new CPPFunctionType(*ftype); + ftype->_class_owner = mod._scoping; + ftype->_flags |= CPPFunctionType::F_method_pointer; + type = ftype; + } + result = new CPPPointerType(type); + } + break; + + case IIT_array: + result = new CPPArrayType(r_unroll_type(start_type, mi), + mod._expr); + break; + + case IIT_const: + result = new CPPConstType(r_unroll_type(start_type, mi)); + break; + + case IIT_paren: + result = r_unroll_type(start_type, mi); + break; + + case IIT_func: + { + CPPType *return_type = r_unroll_type(start_type, mi); + result = new CPPFunctionType(return_type, mod._func_params, + mod._func_flags); + } + break; + + default: + cerr << "Internal error--invalid CPPInstanceIdentifier\n"; + abort(); + } + + return CPPType::new_type(result); +} diff --git a/dtool/src/cppparser/cppInstanceIdentifier.h b/dtool/src/cppparser/cppInstanceIdentifier.h new file mode 100644 index 0000000000..531e79bd3b --- /dev/null +++ b/dtool/src/cppparser/cppInstanceIdentifier.h @@ -0,0 +1,78 @@ +// Filename: cppInstanceIdentifier.h +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPINSTANCEIDENTIFIER_H +#define CPPINSTANCEIDENTIFIER_H + +#include + +#include +#include + +using namespace std; + +class CPPIdentifier; +class CPPParameterList; +class CPPType; +class CPPExpression; +class CPPScope; +class CPPPreprocessor; + +enum CPPInstanceIdentifierType { + IIT_pointer, + IIT_reference, + IIT_scoped_pointer, + IIT_array, + IIT_const, + IIT_paren, + IIT_func, +}; + +/////////////////////////////////////////////////////////////////// +// Class : CPPInstanceIdentifier +// Description : This class is used in parser.y to build up a variable +// instance definition. An instance is something like +// 'int *&a'; the InstanceIdentifier stores everything +// to the right of the typename. Later this can be +// passed to make_instance() to construct a CPPInstance. +//////////////////////////////////////////////////////////////////// +class CPPInstanceIdentifier { +public: + CPPInstanceIdentifier(CPPIdentifier *ident); + + CPPType *unroll_type(CPPType *start_type); + + void add_modifier(CPPInstanceIdentifierType type); + void add_func_modifier(CPPParameterList *params, int flags); + void add_scoped_pointer_modifier(CPPIdentifier *scoping); + void add_array_modifier(CPPExpression *expr); + + CPPScope *get_scope(CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + + CPPIdentifier *_ident; + + class Modifier { + public: + Modifier(CPPInstanceIdentifierType type); + static Modifier func_type(CPPParameterList *params, int flags); + static Modifier array_type(CPPExpression *expr); + static Modifier scoped_pointer_type(CPPIdentifier *scoping); + + CPPInstanceIdentifierType _type; + CPPParameterList *_func_params; + int _func_flags; + CPPIdentifier *_scoping; + CPPExpression *_expr; + }; + typedef vector Modifiers; + Modifiers _modifiers; + +private: + CPPType * + r_unroll_type(CPPType *start_type, Modifiers::const_iterator mi); +}; + +#endif diff --git a/dtool/src/cppparser/cppManifest.cxx b/dtool/src/cppparser/cppManifest.cxx new file mode 100644 index 0000000000..816252bb13 --- /dev/null +++ b/dtool/src/cppparser/cppManifest.cxx @@ -0,0 +1,260 @@ +// Filename: cppManifest.C +// Created by: drose (22Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppManifest.h" +#include "cppExpression.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::ExpansionNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPManifest::ExpansionNode:: +ExpansionNode(int parm_number) : + _parm_number(parm_number) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::ExpansionNode::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPManifest::ExpansionNode:: +ExpansionNode(const string &str) : + _parm_number(-1), _str(str) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPManifest:: +CPPManifest(const string &args, const CPPFile &file) : _file(file) { + assert(!args.empty()); + assert(!isspace(args[0])); + + _expr = (CPPExpression *)NULL; + _vis = V_public; + + // First, identify the manifest name. + + size_t p = 0; + while (p < args.size() && !isspace(args[p]) && args[p] != '(') { + p++; + } + + _name = args.substr(0, p); + + vector parameter_names; + + if (args[p] == '(') { + // Hmm, parameters. + _has_parameters = true; + parse_parameters(args, p, parameter_names); + _num_parameters = parameter_names.size(); + p++; + } else { + _has_parameters = false; + _num_parameters = 0; + } + + // Now identify the expansion. Skip whitespace. + while (p < args.size() && isspace(args[p])) { + p++; + } + + save_expansion(args.substr(p), parameter_names); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPManifest:: +~CPPManifest() { + if (_expr != (CPPExpression *)NULL) { + delete _expr; + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::expand +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPManifest:: +expand(const vector &args) const { + string result; + + Expansion::const_iterator ei; + for (ei = _expansion.begin(); ei != _expansion.end(); ++ei) { + if ((*ei)._parm_number >= 0) { + int i = (*ei)._parm_number; + if (i < (int)args.size()) { + result += " " + args[i] + " "; + } else { + result += " "; + } + } + if (!(*ei)._str.empty()) { + result += (*ei)._str; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::determine_type +// Access: Public +// Description: Returns the type of the manifest, if it is known, +// or NULL if the type cannot be determined. +//////////////////////////////////////////////////////////////////// +CPPType *CPPManifest:: +determine_type() const { + if (_expr != (CPPExpression *)NULL) { + return _expr->determine_type(); + } + return (CPPType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPManifest:: +output(ostream &out) const { + out << _name; + + if (_has_parameters) { + out << "("; + if (_num_parameters > 0) { + out << "$1"; + for (int i = 1; i < _num_parameters; i++) { + out << ", $" << i + 1; + } + } + out << ")"; + } + + out << " "; + + Expansion::const_iterator ei; + for (ei = _expansion.begin(); ei != _expansion.end(); ++ei) { + if ((*ei)._parm_number >= 0) { + out << " $" << (*ei)._parm_number + 1 << " "; + } + if (!(*ei)._str.empty()) { + out << (*ei)._str; + } + } +} + + + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::parse_parameters +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPManifest:: +parse_parameters(const string &args, size_t &p, + vector ¶meter_names) { + assert(p < args.size()); + assert(args[p] == '('); + + p++; + while (p < args.size() && isspace(args[p])) { + p++; + } + + while (p < args.size() && args[p] != ')') { + // Here's the beginning of a parm. + size_t q = p; + while (p < args.size() && !isspace(args[p]) && + args[p] != ')' && args[p] != ',') { + p++; + } + parameter_names.push_back(args.substr(q, p - q)); + + // Skip whitespace after the parameter name. + while (p < args.size() && isspace(args[p])) { + p++; + } + + if (p < args.size() && args[p] == ',') { + p++; + // Skip whitespace after a comma. + while (p < args.size() && isspace(args[p])) { + p++; + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPManifest::save_expansion +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPManifest:: +save_expansion(const string &exp, const vector ¶meter_names) { + if (parameter_names.empty()) { + // No parameters; this is an easy case. + _expansion.push_back(ExpansionNode(exp)); + return; + } + + // Walk through the expansion string. For each substring that is an + // identifier, check it against parameter_names. + size_t p = 0; + size_t last = 0; + while (p < exp.size()) { + if (isalpha(exp[p]) || exp[p] == '_') { + // Here's the start of an identifier. Find the end of it. + size_t q = p; + p++; + while (p < exp.size() && isalnum(exp[p]) || exp[p] == '_') { + p++; + } + + string ident = exp.substr(q, p - q); + + // Is this identifier one of our parameters? + int pnum = -1; + for (int i = 0; pnum == -1 && i < (int)parameter_names.size(); i++) { + if (parameter_names[i] == ident) { + pnum = i; + } + } + + if (pnum != -1) { + // Yep! + if (last != q) { + _expansion.push_back(ExpansionNode(exp.substr(last, q - last))); + } + _expansion.push_back(pnum); + last = p; + } + } else { + p++; + } + } + + if (last != p) { + _expansion.push_back(exp.substr(last, p - last)); + } +} diff --git a/dtool/src/cppparser/cppManifest.h b/dtool/src/cppparser/cppManifest.h new file mode 100644 index 0000000000..d06c95a273 --- /dev/null +++ b/dtool/src/cppparser/cppManifest.h @@ -0,0 +1,68 @@ +// Filename: cppManifest.h +// Created by: drose (22Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPMANIFEST_H +#define CPPMANIFEST_H + +#include + +#include "cppFile.h" +#include "cppVisibility.h" + +#include +#include + +class CPPExpression; +class CPPType; + +/////////////////////////////////////////////////////////////////// +// Class : CPPManifest +// Description : +//////////////////////////////////////////////////////////////////// +class CPPManifest { +public: + CPPManifest(const string &args, const CPPFile &file = CPPFile()); + ~CPPManifest(); + string expand(const vector &args = vector()) const; + + CPPType *determine_type() const; + + void output(ostream &out) const; + + string _name; + bool _has_parameters; + int _num_parameters; + CPPFile _file; + CPPExpression *_expr; + + // Manifests don't have a visibility in the normal sense. Normally + // this will be V_public. But a manifest that is defined between + // __begin_publish and __end_publish will have a visibility of + // V_published. + CPPVisibility _vis; + +private: + void parse_parameters(const string &args, size_t &p, + vector ¶meter_names); + void save_expansion(const string &exp, + const vector ¶meter_names); + + class ExpansionNode { + public: + ExpansionNode(int parm_number); + ExpansionNode(const string &str); + int _parm_number; + string _str; + }; + typedef vector Expansion; + Expansion _expansion; +}; + +inline ostream &operator << (ostream &out, const CPPManifest &manifest) { + manifest.output(out); + return out; +} + +#endif diff --git a/dtool/src/cppparser/cppNameComponent.cxx b/dtool/src/cppparser/cppNameComponent.cxx new file mode 100644 index 0000000000..ee8f6a4f78 --- /dev/null +++ b/dtool/src/cppparser/cppNameComponent.cxx @@ -0,0 +1,187 @@ +// Filename: cppNameComponent.C +// Created by: drose (12Nov99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppNameComponent.h" +#include "cppTemplateParameterList.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPNameComponent:: +CPPNameComponent(const string &name) : + _name(name) +{ + _templ = (CPPTemplateParameterList *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::Equivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPNameComponent:: +operator == (const CPPNameComponent &other) const { + if (_name != other._name) { + return false; + } + if (_templ == NULL && other._templ == NULL) { + return true; + } + if (_templ == NULL || other._templ == NULL) { + return false; + } + if (*_templ != *other._templ) { + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::Nonequivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPNameComponent:: +operator != (const CPPNameComponent &other) const { + return !(*this == other); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPNameComponent:: +operator < (const CPPNameComponent &other) const { + if (_name != other._name) { + return _name < other._name; + } + if (_templ == NULL && other._templ == NULL) { + return false; + } + if (_templ == NULL || other._templ == NULL) { + return _templ < other._templ; + } + return (*_templ) < (*other._templ); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPNameComponent:: +get_name() const { + return _name; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::get_name_with_templ +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPNameComponent:: +get_name_with_templ(CPPScope *scope) const { + ostringstream strm; + strm << _name; + if (_templ != NULL) { + strm << "< "; + _templ->output(strm, scope); + strm << " >"; + } + return strm.str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::get_templ +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTemplateParameterList *CPPNameComponent:: +get_templ() const { + return _templ; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPNameComponent:: +empty() const { + return _name.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::has_templ +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPNameComponent:: +has_templ() const { + return _templ != (CPPTemplateParameterList *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::is_tbd +// Access: Public +// Description: Returns true if the name component includes a +// template parameter list that includes some +// not-yet-defined type. +//////////////////////////////////////////////////////////////////// +bool CPPNameComponent:: +is_tbd() const { + if (_templ != (CPPTemplateParameterList *)NULL) { + return _templ->is_tbd(); + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::set_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPNameComponent:: +set_name(const string &name) { + _name = name; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::append_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPNameComponent:: +append_name(const string &name) { + _name += name; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::set_templ +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPNameComponent:: +set_templ(CPPTemplateParameterList *templ) { + _templ = templ; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNameComponent::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPNameComponent:: +output(ostream &out) const { + out << _name; + if (_templ != NULL) { + out << "< " << *_templ << " >"; + } +} diff --git a/dtool/src/cppparser/cppNameComponent.h b/dtool/src/cppparser/cppNameComponent.h new file mode 100644 index 0000000000..ca6deeaaf0 --- /dev/null +++ b/dtool/src/cppparser/cppNameComponent.h @@ -0,0 +1,49 @@ +// Filename: cppNameComponent.h +// Created by: drose (12Nov99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPNAMECOMPONENT_H +#define CPPNAMECOMPONENT_H + +#include + +#include + +using namespace std; + +class CPPTemplateParameterList; +class CPPScope; + +class CPPNameComponent { +public: + CPPNameComponent(const string &name); + bool operator == (const CPPNameComponent &other) const; + bool operator != (const CPPNameComponent &other) const; + bool operator < (const CPPNameComponent &other) const; + + string get_name() const; + string get_name_with_templ(CPPScope *scope = (CPPScope *)NULL) const; + CPPTemplateParameterList *get_templ() const; + bool empty() const; + bool has_templ() const; + + bool is_tbd() const; + + void set_name(const string &name); + void append_name(const string &name); + void set_templ(CPPTemplateParameterList *templ); + + void output(ostream &out) const; + +private: + string _name; + CPPTemplateParameterList *_templ; +}; + +inline ostream &operator << (ostream &out, const CPPNameComponent &name) { + name.output(out); + return out; +} + +#endif diff --git a/dtool/src/cppparser/cppNamespace.cxx b/dtool/src/cppparser/cppNamespace.cxx new file mode 100644 index 0000000000..7004309f34 --- /dev/null +++ b/dtool/src/cppparser/cppNamespace.cxx @@ -0,0 +1,114 @@ +// Filename: cppNamespace.C +// Created by: drose (16Nov99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppNamespace.h" +#include "cppIdentifier.h" +#include "cppScope.h" +#include "indent.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPNamespace::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPNamespace:: +CPPNamespace(CPPIdentifier *ident, CPPScope *scope, const CPPFile &file) : + CPPDeclaration(file), + _ident(ident), _scope(scope) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNamespace::get_simple_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPNamespace:: +get_simple_name() const { + if (_ident == NULL) { + return ""; + } + return _ident->get_simple_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNamespace::get_local_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPNamespace:: +get_local_name(CPPScope *scope) const { + if (_ident == NULL) { + return ""; + } + return _ident->get_local_name(scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNamespace::get_fully_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPNamespace:: +get_fully_scoped_name() const { + if (_ident == NULL) { + return ""; + } + return _ident->get_fully_scoped_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNamespace::get_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPNamespace:: +get_scope() const { + return _scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNamespace::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPNamespace:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + if (!complete && _ident != NULL) { + // If we have a name, use it. + out << "namespace " << _ident->get_local_name(scope); + + } else { + if (_ident != NULL) { + out << "namespace " << _ident->get_local_name(scope) << " {\n"; + } else { + out << "namespace {\n"; + } + + _scope->write(out, indent_level + 2, _scope); + indent(out, indent_level) << "}"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNamespace::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPNamespace:: +get_subtype() const { + return ST_namespace; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPNamespace::as_namespace +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPNamespace *CPPNamespace:: +as_namespace() { + return this; +} diff --git a/dtool/src/cppparser/cppNamespace.h b/dtool/src/cppparser/cppNamespace.h new file mode 100644 index 0000000000..fbf1907593 --- /dev/null +++ b/dtool/src/cppparser/cppNamespace.h @@ -0,0 +1,41 @@ +// Filename: cppNamespace.h +// Created by: drose (16Nov99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPNAMESPACE_H +#define CPPNAMESPACE_H + +#include + +#include "cppDeclaration.h" + +class CPPIdentifier; +class CPPScope; + +/////////////////////////////////////////////////////////////////// +// Class : CPPNamespace +// Description : +//////////////////////////////////////////////////////////////////// +class CPPNamespace : public CPPDeclaration { +public: + CPPNamespace(CPPIdentifier *ident, CPPScope *scope, + const CPPFile &file); + + string get_simple_name() const; + string get_local_name(CPPScope *scope = NULL) const; + string get_fully_scoped_name() const; + CPPScope *get_scope() const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPNamespace *as_namespace(); + +private: + CPPIdentifier *_ident; + CPPScope *_scope; +}; + +#endif diff --git a/dtool/src/cppparser/cppParameterList.cxx b/dtool/src/cppparser/cppParameterList.cxx new file mode 100644 index 0000000000..22c63e02f9 --- /dev/null +++ b/dtool/src/cppparser/cppParameterList.cxx @@ -0,0 +1,244 @@ +// Filename: cppParameterList.C +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// + + +#include "cppParameterList.h" +#include "cppInstance.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPParameterList:: +CPPParameterList() { + _includes_ellipsis = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::is_equivalent +// Access: Public +// Description: This is similar to operator == except it is more +// forgiving: it is true if only the length and order of +// types is the same, never minding the instance names +// or initial values. +//////////////////////////////////////////////////////////////////// +bool CPPParameterList:: +is_equivalent(const CPPParameterList &other) const { + if (_includes_ellipsis != other._includes_ellipsis) { + return false; + } + if (_parameters.size() != other._parameters.size()) { + return false; + } + for (int i = 0; i < (int)_parameters.size(); i++) { + if (!_parameters[i]->_type->is_equivalent(*other._parameters[i]->_type)) { + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::Equality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPParameterList:: +operator == (const CPPParameterList &other) const { + if (_includes_ellipsis != other._includes_ellipsis) { + return false; + } + if (_parameters.size() != other._parameters.size()) { + return false; + } + for (int i = 0; i < (int)_parameters.size(); i++) { + if (*_parameters[i] != *other._parameters[i]) { + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::Inequality Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPParameterList:: +operator != (const CPPParameterList &other) const { + return !(*this == other); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPParameterList:: +operator < (const CPPParameterList &other) const { + if (_includes_ellipsis != other._includes_ellipsis) { + return _includes_ellipsis < other._includes_ellipsis; + } + if (_parameters.size() != other._parameters.size()) { + return _parameters.size() < other._parameters.size(); + } + for (int i = 0; i < (int)_parameters.size(); i++) { + if (*_parameters[i] != *other._parameters[i]) { + return *_parameters[i] < *other._parameters[i]; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::is_tbd +// Access: Public +// Description: Returns true if any of the types in the parameter +// list are base on CPPTBDType. +//////////////////////////////////////////////////////////////////// +bool CPPParameterList:: +is_tbd() const { + for (int i = 0; i < (int)_parameters.size(); i++) { + if (_parameters[i]->_type->is_tbd()) { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::is_fully_specified +// Access: Public +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPParameterList:: +is_fully_specified() const { + for (int i = 0; i < (int)_parameters.size(); i++) { + if (!_parameters[i]->is_fully_specified()) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::substitute_decl +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPParameterList *CPPParameterList:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + CPPParameterList *rep = new CPPParameterList; + bool any_changed = false; + for (int i = 0; i < (int)_parameters.size(); i++) { + CPPInstance *inst = + _parameters[i]->substitute_decl(subst, current_scope, global_scope) + ->as_instance(); + if (inst != _parameters[i]) { + any_changed = true; + } + rep->_parameters.push_back(inst); + } + + if (!any_changed) { + delete rep; + rep = this; + } + return rep; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::resolve_type +// Access: Public +// Description: Returns an equivalent CPPParameterList, in which all +// of the individual types have been resolved. +//////////////////////////////////////////////////////////////////// +CPPParameterList *CPPParameterList:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + CPPParameterList *rep = new CPPParameterList; + bool any_changed = false; + for (int i = 0; i < (int)_parameters.size(); i++) { + CPPInstance *inst = _parameters[i]; + CPPType *new_type = inst->_type; + if (new_type->is_tbd()) { + new_type = new_type->resolve_type(current_scope, global_scope); + } + + if (new_type != inst->_type) { + any_changed = true; + CPPInstance *new_inst = new CPPInstance(*inst); + new_inst->_type = new_type; + rep->_parameters.push_back(new_inst); + } else { + rep->_parameters.push_back(inst); + } + } + + if (!any_changed) { + delete rep; + rep = this; + } + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParameterList::output +// Access: Public +// Description: If num_default_parameters is >= 0, it indicates the +// number of default parameter values to show on output. +// Otherwise, all parameter values are shown. +//////////////////////////////////////////////////////////////////// +void CPPParameterList:: +output(ostream &out, CPPScope *scope, bool parameter_names, + int num_default_parameters) const { + if (!_parameters.empty()) { + for (int i = 0; i < (int)_parameters.size(); i++) { + if (i != 0) { + out << ", "; + } + + // Save the default value expression; we might be about to + // temporarily clear it. + CPPExpression *expr = _parameters[i]->_initializer; + + if (num_default_parameters >= 0 && + i < (int)_parameters.size() - num_default_parameters) { + // Don't show the default value for this parameter. + _parameters[i]->_initializer = (CPPExpression *)NULL; + } + + if (parameter_names) { + _parameters[i]->output(out, 0, scope, false); + } else { + _parameters[i]->_type->output(out, 0, scope, false); + } + + // Restore the default value expression. + _parameters[i]->_initializer = expr; + } + if (_includes_ellipsis) { + out << ", ..."; + } + + } else if (_includes_ellipsis) { + out << "..."; + } +} diff --git a/dtool/src/cppparser/cppParameterList.h b/dtool/src/cppparser/cppParameterList.h new file mode 100644 index 0000000000..a44f5ba41f --- /dev/null +++ b/dtool/src/cppparser/cppParameterList.h @@ -0,0 +1,60 @@ +// Filename: cppParameterList.h +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPPARAMETERLIST_H +#define CPPPARAMETERLIST_H + +#include + +#include "cppDeclaration.h" + +#include + +class CPPInstance; +class CPPScope; + +/////////////////////////////////////////////////////////////////// +// Class : CPPParameterList +// Description : +//////////////////////////////////////////////////////////////////// +class CPPParameterList { +public: + CPPParameterList(); + + bool is_equivalent(const CPPParameterList &other) const; + + bool operator == (const CPPParameterList &other) const; + bool operator != (const CPPParameterList &other) const; + bool operator < (const CPPParameterList &other) const; + + bool is_tbd() const; + + bool is_fully_specified() const; + CPPParameterList *substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + CPPParameterList *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + // This vector contains a list of formal parameters, in order. A + // parameter may have an empty identifer name. + typedef vector Parameters; + Parameters _parameters; + bool _includes_ellipsis; + + void output(ostream &out, CPPScope *scope, bool parameter_names, + int num_default_parameters = -1) const; +}; + +inline ostream & +operator << (ostream &out, const CPPParameterList &plist) { + plist.output(out, (CPPScope *)NULL, true); + return out; +} + +#endif + + diff --git a/dtool/src/cppparser/cppParser.cxx b/dtool/src/cppparser/cppParser.cxx new file mode 100644 index 0000000000..cb271c889a --- /dev/null +++ b/dtool/src/cppparser/cppParser.cxx @@ -0,0 +1,85 @@ +// Filename: cppParser.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppParser.h" +#include "cppFile.h" +#include "cppTypeParser.h" +#include "cppBisonDefs.h" + +#include +#include + +bool cppparser_output_class_keyword = true; + +//////////////////////////////////////////////////////////////////// +// Function: CPPParser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPParser:: +CPPParser() : CPPScope((CPPScope *)NULL, CPPNameComponent(""), V_public) { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParser::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPParser:: +is_fully_specified() const { + // The global scope is always considered to be "fully specified", + // even if it contains some template declarations. + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParser::parse_file +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPParser:: +parse_file(const string &filename) { + if (!init_cpp(CPPFile(filename, filename, CPPFile::S_local))) { + cerr << "Unable to read " << filename << "\n"; + return false; + } + parse_cpp(this); + + return get_error_count() == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParser::parse_expr +// Access: Public +// Description: Given a string, expand all manifests within the +// string and evaluate it as an expression. Returns +// NULL if the string is not a valid expression. +//////////////////////////////////////////////////////////////////// +CPPExpression *CPPParser:: +parse_expr(const string &expr) { + return CPPPreprocessor::parse_expr(expr, this, this); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPParser::parse_type +// Access: Public +// Description: Given a string, interpret it as a type name and +// return the corresponding CPPType. Returns NULL if +// the string is not a valid type. +//////////////////////////////////////////////////////////////////// +CPPType *CPPParser:: +parse_type(const string &type) { + CPPTypeParser ep(this, this); + ep._verbose = 0; + if (ep.parse_type(type, *this)) { + return ep._type; + } else { + return (CPPType *)NULL; + } +} diff --git a/dtool/src/cppparser/cppParser.h b/dtool/src/cppparser/cppParser.h new file mode 100644 index 0000000000..051192e189 --- /dev/null +++ b/dtool/src/cppparser/cppParser.h @@ -0,0 +1,45 @@ +// Filename: cppParser.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPPARSER_H +#define CPPPARSER_H + +#include + +#include "cppScope.h" +#include "cppPreprocessor.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : CPPParser +// Description : +//////////////////////////////////////////////////////////////////// +class CPPParser : public CPPScope, public CPPPreprocessor { +public: + CPPParser(); + + virtual bool is_fully_specified() const; + + bool parse_file(const string &filename); + + CPPExpression *parse_expr(const string &expr); + CPPType *parse_type(const string &type); +}; + +// Normally, this variable should be left true, especially while +// parsing. However, after parsing has finished, and you want to +// output the results of parsing in a way that can be successfully +// compiled by VC++, you may need to set this variable to false. It +// controls the way typenames are written. When true, class names are +// written 'class X', which is the way the parser expects things to +// come, and which compiles successfully under every compiler except +// VC++. When false, class names are written simply 'X', which is the +// only way they'll compile under VC++. +extern bool cppparser_output_class_keyword; + +#endif + + diff --git a/dtool/src/cppparser/cppPointerType.cxx b/dtool/src/cppparser/cppPointerType.cxx new file mode 100644 index 0000000000..29699e5ef7 --- /dev/null +++ b/dtool/src/cppparser/cppPointerType.cxx @@ -0,0 +1,210 @@ +// Filename: cppPointerType.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppPointerType.h" +#include "cppFunctionType.h" +#include "cppIdentifier.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPPointerType:: +CPPPointerType(CPPType *pointing_at) : + CPPType(CPPFile()), + _pointing_at(pointing_at) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPPointerType:: +is_fully_specified() const { + return CPPType::is_fully_specified() && + _pointing_at->is_fully_specified(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPPointerType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + return (*si).second; + } + + CPPPointerType *rep = new CPPPointerType(*this); + rep->_pointing_at = + _pointing_at->substitute_decl(subst, current_scope, global_scope) + ->as_type(); + + if (rep->_pointing_at == _pointing_at) { + delete rep; + rep = this; + } + rep = CPPType::new_type(rep)->as_pointer_type(); + subst.insert(SubstDecl::value_type(this, rep)); + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPPointerType:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + CPPType *ptype = _pointing_at->resolve_type(current_scope, global_scope); + + if (ptype != _pointing_at) { + CPPPointerType *rep = new CPPPointerType(*this); + rep->_pointing_at = ptype; + return CPPType::new_type(rep); + } + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPPointerType:: +is_tbd() const { + return _pointing_at->is_tbd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::is_equivalent +// Access: Public, Virtual +// Description: This is a little more forgiving than is_equal(): it +// returns true if the types appear to be referring to +// the same thing, even if they may have different +// pointers or somewhat different definitions. It's +// useful for parameter matching, etc. +//////////////////////////////////////////////////////////////////// +bool CPPPointerType:: +is_equivalent(const CPPType &other) const { + const CPPPointerType *ot = ((CPPType *)&other)->as_pointer_type(); + if (ot == (CPPPointerType *)NULL) { + return CPPType::is_equivalent(other); + } + + return _pointing_at->is_equivalent(*ot->_pointing_at); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPointerType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + _pointing_at->output(out, indent_level, scope, complete); + CPPFunctionType *ftype = _pointing_at->as_function_type(); + if (ftype != NULL && + ((ftype->_flags & CPPFunctionType::F_method_pointer) != 0)) { + // We have to output pointers-to-method with a scoping before the + // '*'. + out << " " << *ftype->_class_owner << "::*"; + } else { + out << " *"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::output_instance +// Access: Public, Virtual +// Description: Formats a C++-looking line that defines an instance +// of the given type, with the indicated name. In most +// cases this will be "type name", but some types have +// special exceptions. +//////////////////////////////////////////////////////////////////// +void CPPPointerType:: +output_instance(ostream &out, int indent_level, CPPScope *scope, + bool complete, const string &prename, + const string &name) const { + string star = "*"; + + CPPFunctionType *ftype = _pointing_at->as_function_type(); + if (ftype != NULL && + ((ftype->_flags & CPPFunctionType::F_method_pointer) != 0)) { + // We have to output pointers-to-method with a scoping before the + // '*'. + star = ftype->_class_owner->get_fully_scoped_name() + "::*"; + } + + _pointing_at->output_instance(out, indent_level, scope, complete, + star + prename, name); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPPointerType:: +get_subtype() const { + return ST_pointer; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::as_pointer_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPPointerType *CPPPointerType:: +as_pointer_type() { + return this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPPointerType:: +is_equal(const CPPDeclaration *other) const { + const CPPPointerType *ot = ((CPPDeclaration *)other)->as_pointer_type(); + assert(ot != NULL); + + return _pointing_at == ot->_pointing_at; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPPointerType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPPointerType:: +is_less(const CPPDeclaration *other) const { + const CPPPointerType *ot = ((CPPDeclaration *)other)->as_pointer_type(); + assert(ot != NULL); + + return _pointing_at < ot->_pointing_at; +} diff --git a/dtool/src/cppparser/cppPointerType.h b/dtool/src/cppparser/cppPointerType.h new file mode 100644 index 0000000000..a2479c62ce --- /dev/null +++ b/dtool/src/cppparser/cppPointerType.h @@ -0,0 +1,59 @@ +// Filename: cppPointerType.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// + +#ifndef CPPPOINTERTYPE_H +#define CPPPOINTERTYPE_H + +#include + +#include "cppType.h" + +/////////////////////////////////////////////////////////////////// +// Class : CPPPointerType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPPointerType : public CPPType { +public: + CPPPointerType(CPPType *pointing_at); + + CPPType *_pointing_at; + + virtual bool is_fully_specified() const; + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_tbd() const; + virtual bool is_equivalent(const CPPType &other) const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual void output_instance(ostream &out, int indent_level, + CPPScope *scope, + bool complete, const string &prename, + const string &name) const; + + virtual SubType get_subtype() const; + + virtual CPPPointerType *as_pointer_type(); + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +#endif diff --git a/dtool/src/cppparser/cppPreprocessor.cxx b/dtool/src/cppparser/cppPreprocessor.cxx new file mode 100644 index 0000000000..eca2ea8ce3 --- /dev/null +++ b/dtool/src/cppparser/cppPreprocessor.cxx @@ -0,0 +1,2187 @@ +// Filename: cppPreprocessor.C +// Created by: drose (22Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppPreprocessor.h" +#include "cppExpressionParser.h" +#include "cppExpression.h" +#include "cppScope.h" +#include "cppIdentifier.h" +#include "cppTemplateScope.h" +#include "cppTemplateParameterList.h" +#include "cppSimpleType.h" +#include "cppGlobals.h" +#include "cppCommentBlock.h" +#include "cppBison.h" + +#include +#include + +// We manage our own visibility counter, in addition to that managed +// by cppBison.y. We do this just so we can define manifests with the +// correct visibility when they are declared. (Asking the parser for +// the current visibility is prone to error, since the parser might be +// several tokens behind the preprocessor.) +static CPPVisibility preprocessor_vis = V_public; + +static int +hex_val(int c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return (c - '0'); + + default: + return (tolower(c) - 'a' + 10); + } +} + +static string +trim_blanks(const string &str) { + size_t first, last; + + first = 0; + while (first < str.length() && isspace(str[first])) { + first++; + } + + last = str.length() - 1; + while (last > first && isspace(str[last])) { + last--; + } + + return str.substr(first, last - first + 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::InputFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPPreprocessor::InputFile:: +InputFile() { + _in = NULL; + _ignore_manifest = NULL; + _line_number = 1; + _col_number = 1; + _lock_position = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::InputFile::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPPreprocessor::InputFile:: +~InputFile() { + if (_in != NULL) { + delete _in; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::InputFile::open +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor::InputFile:: +open(const CPPFile &file) { + assert(_in == NULL); + + _file = file; + ifstream *in = new ifstream; + _in = in; + + return _file._filename.open_read(*in); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::InputFile::connect_input +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor::InputFile:: +connect_input(const string &input) { + assert(_in == NULL); + + _input = input; + _in = new istringstream(_input); + return !_in->fail(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::InputFile::get +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor::InputFile:: +get() { + assert(_in != NULL); + int c = _in->get(); + switch (c) { + case EOF: + break; + + case '\n': + if (!_lock_position) { + _line_number++; + _col_number = 1; + } + break; + + default: + if (!_lock_position) { + _col_number++; + } + } + + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPPreprocessor:: +CPPPreprocessor() { + _state = S_eof; + _paren_nesting = 0; + _angle_bracket_found = false; + _unget = '\0'; + _last_c = '\0'; + _start_of_line = true; + _last_cpp_comment = false; + _save_comments = true; + + _resolve_identifiers = true; + + _warning_count = 0; + _error_count = 0; +#ifdef CPP_VERBOSE_LEX + _token_index = 0; +#endif + _verbose = 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::set_verbose +// Access: Public +// Description: Sets the verbosity level of the parser. At 0, no +// warnings will be reported; at 1 or higher, expect to +// get spammed. +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +set_verbose(int verbose) { + _verbose = verbose; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_verbose +// Access: Public +// Description: Returns the verbosity level of the parser. +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +get_verbose() const { + return _verbose; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::copy_filepos +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +copy_filepos(const CPPPreprocessor &other) { + assert(!_files.empty()); + _files.back()._file = other.get_file(); + _files.back()._line_number = other.get_line_number(); + _files.back()._col_number = other.get_col_number(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_file +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPFile CPPPreprocessor:: +get_file() const { + if (_files.empty()) { + return CPPFile(""); + } + return _files.back()._file; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_line_number +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +get_line_number() const { + if (_files.empty()) { + return 0; + } + return _files.back()._line_number; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_col_number +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +get_col_number() const { + if (_files.empty()) { + return 0; + } + return _files.back()._col_number; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_next_token +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken CPPPreprocessor:: +get_next_token() { + +#ifdef CPP_VERBOSE_LEX + CPPToken tok = get_next_token0(); + cerr << _token_index++ << ". " << tok << "\n"; + return tok; +} + +CPPToken CPPPreprocessor:: +get_next_token0() { +#endif + + // We make a nested call to internal_get_next_token(), so we can + // combine sequences of identifiers and scoping symbols into a + // single identifier, for yacc's convenience. + + CPPToken token(0); + if (!_saved_tokens.empty()) { + token = _saved_tokens.back(); + _saved_tokens.pop_back(); + } else { + token = internal_get_next_token(); + } + + int first_line = token._lloc.first_line; + int first_col = token._lloc.first_column; + CPPFile first_file = token._lloc.file; + + if (token._token == '#') { + // Stringify. + token = internal_get_next_token(); + if (token._token == SIMPLE_IDENTIFIER || token._token == STRING) { + token._token = STRING; + } else { + // Stringify nothing. + + _saved_tokens.push_back(token); + token._token = STRING; + token._lval.str = ""; + } + } + + if (_resolve_identifiers && + (token._token == SIMPLE_IDENTIFIER || token._token == SCOPE)) { + // We will be returning a scoped identifier, or a scoping. Keep + // pulling off tokens until we reach the end of the + // scope/identifier sequence. + + string name; + + // If we started the ball with an identifier, use it and get the + // next token. Otherwise, we started with :: (global scope), and + // we indicate this with an empty string at the beginning of the + // scoping sequence. + if (token._token == SIMPLE_IDENTIFIER) { + name = token._lval.str; + token = internal_get_next_token(); + } + + CPPIdentifier *ident = new CPPIdentifier(name, token._lloc.file); + YYSTYPE result; + result.u.identifier = ident; + + if (token._token == '<') { + // If the next token is an angle bracket and the current + // identifier wants template instantiation, assume the angle + // bracket begins the instantiation and call yacc recursively to + // parse the template parameters. + CPPDeclaration *decl = ident->find_template(current_scope, global_scope); + if (decl != NULL) { + ident->_names.back().set_templ + (nested_parse_template_instantiation(decl->get_template_scope())); + token = internal_get_next_token(); + } + } + + while (token._token == SCOPE || token._token == TOKENPASTE) { + if (token._token == TOKENPASTE) { + // The token-pasting operator creates one continuous + // identifier across whitespace. + token = internal_get_next_token(); + if (token._token == SIMPLE_IDENTIFIER) { + name += token._lval.str; + ident->_names.back().append_name(token._lval.str); + + token = internal_get_next_token(); + + } else { + // Token-paste with nothing. + } + + } else { // token._token == SCOPE + name += "::"; + token = internal_get_next_token(); + string token_prefix; + + if (token._token == '~') { + // A scoping operator followed by a tilde can only be the + // start of a scoped destructor name. Make the tilde be part + // of the name. + name += "~"; + token_prefix = "~"; + token = internal_get_next_token(); + } + + if (token._token != SIMPLE_IDENTIFIER) { + // The last useful token was a SCOPE, thus this is a scoping + // token. + + if (token._token == KW_OPERATOR) { + // Unless the last token we came across was the "operator" + // keyword. We make a special case for this, because it's + // occasionally scoped in normal use. + token._lval = result; + return token; + } + _saved_tokens.push_back(token); + return CPPToken(SCOPING, first_line, first_col, first_file, + name, result); + } + + name += token._lval.str; + ident->_names.push_back(token_prefix + token._lval.str); + + token = internal_get_next_token(); + } + + if (token._token == '<') { + // If the next token is an angle bracket and the current + // indentifier wants template instantiation, assume the angle + // bracket begins the instantiation and call yacc recursively to + // parse the template parameters. + CPPDeclaration *decl = + ident->find_template(current_scope, global_scope); + if (decl != NULL) { + ident->_names.back().set_templ + (nested_parse_template_instantiation(decl->get_template_scope())); + token = internal_get_next_token(); + } + } + } + // The last useful token was a SIMPLE_IDENTIFIER, thus this is a + // normal scoped identifier. + _saved_tokens.push_back(token); + + int token_type = IDENTIFIER; + CPPDeclaration *decl = ident->find_symbol(current_scope, global_scope); + if (decl != NULL && + (decl->as_typedef() != NULL || decl->as_type() != NULL)) { + token_type = TYPENAME_IDENTIFIER; + } + + return CPPToken(token_type, first_line, first_col, first_file, + name, result); + } + + // This is the normal case: just pass through whatever token we got. + return token; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::warning +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +warning(const string &message, int line, int col, CPPFile file) { + if (_verbose >= 2) { + if (line == 0) { + line = get_line_number(); + col = get_col_number(); + } + if (file.empty()) { + file = get_file(); + } + cerr << "\nWarning in " << file + << " near line " << line << ", column " << col << ":\n" + << message << "\n"; + } + _warning_count++; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::error +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +error(const string &message, int line, int col, CPPFile file) { + if (_state == S_nested || _state == S_end_nested) { + // Don't report or log errors in the nested state. These will be + // reported when the nesting level collapses. + return; + }; + + if (_verbose >= 1) { + if (line == 0) { + line = get_line_number(); + col = get_col_number(); + } + if (file.empty()) { + file = get_file(); + } + cerr << "\nError in " << file + << " near line " << line << ", column " << col << ":\n" + << message << "\n"; + } + _error_count++; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_warning_count +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +get_warning_count() const { + return _warning_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_error_count +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +get_error_count() const { + return _error_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_comment_before +// Access: Public +// Description: Returns the CPPCommentBlock immediately preceding the +// indicated line, if any. If there is no such comment, +// returns NULL. +//////////////////////////////////////////////////////////////////// +CPPCommentBlock *CPPPreprocessor:: +get_comment_before(int line, CPPFile file) { + CPPComments::reverse_iterator ci; + ci = _comments.rbegin(); + + int wrong_file_count = 0; + while (ci != _comments.rend()) { + CPPCommentBlock *comment = (*ci); + if (comment->_file == file) { + wrong_file_count = 0; + if (comment->_last_line == line || comment->_last_line == line - 1) { + return comment; + } + + if (comment->_last_line < line) { + return (CPPCommentBlock *)NULL; + } + } else { + wrong_file_count++; + if (wrong_file_count > 10) { + return (CPPCommentBlock *)NULL; + } + } + + ++ci; + } + + return (CPPCommentBlock *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::init_cpp +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor:: +init_cpp(const CPPFile &file) { + _state = S_normal; + _saved_tokens.push_back(CPPToken(START_CPP)); + _last_c = '\0'; + + return push_file(file); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::init_const_expr +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor:: +init_const_expr(const string &expr) { + _state = S_normal; + _saved_tokens.push_back(CPPToken(START_CONST_EXPR)); + + return push_string(expr, false); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::init_type +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor:: +init_type(const string &type) { + _state = S_normal; + _saved_tokens.push_back(CPPToken(START_TYPE)); + + return push_string(type, false); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::push_file +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor:: +push_file(const CPPFile &file) { +#ifdef CPP_VERBOSE_LEX + cerr << "Pushing to file " << file << "\n"; +#endif + _files.push_back(InputFile()); + InputFile &infile = _files.back(); + + if (infile.open(file)) { + // Record the fact that we opened the file for the benefit of user + // code. + _parsed_files.insert(file); + + infile._prev_last_c = _last_c; + _last_c = '\0'; + return true; + } + +#ifdef CPP_VERBOSE_LEX + cerr << "Unable to read file\n"; +#endif + _files.pop_back(); + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::push_string +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor:: +push_string(const string &input, bool lock_position) { +#ifdef CPP_VERBOSE_LEX + cerr << "Pushing to string \"" << input + << "\"\nlock_position = " << lock_position << "\n"; +#endif + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + + _files.push_back(InputFile()); + InputFile &infile = _files.back(); + + if (infile.connect_input(input)) { + if (lock_position) { + infile._file = first_file; + infile._line_number = first_line; + infile._col_number = first_col; + infile._lock_position = true; + } + + infile._prev_last_c = _last_c; + _last_c = '\0'; + return true; + } + +#ifdef CPP_VERBOSE_LEX + cerr << "Unable to read string\n"; +#endif + + _files.pop_back(); + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::parse_expr +// Access: Protected +// Description: Given a string, expand all manifests within the +// string and evaluate it as an expression. Returns +// NULL if the string is not a valid expression. +// +// This is an internal support function for +// CPPPreprocessor; however, there is a public variant +// of this function defined for CPPParser. +//////////////////////////////////////////////////////////////////// +CPPExpression *CPPPreprocessor:: +parse_expr(const string &input_expr, CPPScope *current_scope, + CPPScope *global_scope) { + // Get a copy of the expression string we can modify. + string expr = input_expr; + + // Repeatedly scan the expr for any manifest names or defined() + // function. + + // We'll need to save the set of manifests we've already expanded, + // to guard against recursive references. + set already_expanded; + + bool manifest_found; + do { + manifest_found = false; + size_t p = 0; + while (p < expr.size()) { + if (isalpha(expr[p]) || expr[p] == '_') { + size_t q = p; + while (p < expr.size() && (isalnum(expr[p]) || expr[p] == '_')) { + p++; + } + string ident = expr.substr(q, p - q); + + // Here's an identifier. Is it "defined"? + if (ident == "defined") { + expand_defined_function(expr, q, p); + } else { + // Is it a manifest? + Manifests::const_iterator mi = _manifests.find(ident); + if (mi != _manifests.end()) { + const CPPManifest *manifest = (*mi).second; + if (already_expanded.insert(manifest).second) { + expand_manifest_inline(expr, q, p, (*mi).second); + manifest_found = true; + } + } + } + } else { + p++; + } + } + + // If we expanded any manifests at all that time, then go back + // through the string and look again--we might have a manifest + // that expands to another manifest. + } while (manifest_found); + + CPPExpressionParser ep(current_scope, global_scope); + ep._verbose = 0; + if (ep.parse_expr(expr, *this)) { + return ep._expr; + } else { + return (CPPExpression *)NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::internal_get_next_token +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken CPPPreprocessor:: +internal_get_next_token() { + if (_state == S_eof || _state == S_end_nested) { + return CPPToken::eof(); + } + + int c = _last_c; + _last_c = '\0'; + if (c == '\0' || c == EOF) { + c = get(); + } + + // Skip any whitespace, comments, and preprocessor directives before + // the token. + c = skip_whitespace(c); + while (c == '#' && _start_of_line && !should_ignore_preprocessor()) { + c = skip_whitespace(process_directive(c)); + } + + if (c == '\'') { + return get_quoted_char(c); + } else if (c == '"') { + return get_quoted_string(c); + } else if (isalpha(c) || c == '_') { + return get_identifier(c); + } else if (isdigit(c)) { + return get_number(c); + } + + _last_c = c; + if (c == EOF) { + _state = S_eof; + return CPPToken::eof(); + } + + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + + // Check for a number beginning with a decimal point. + int next_c = get(); + if (c == '.' && isdigit(next_c)) { + return get_number(c, next_c); + } + + // Check for two- or three-character tokens. + _last_c = get(); + + switch (c) { + case '+': + if (next_c == '+') return CPPToken(PLUSPLUS, first_line, first_col, first_file); + if (next_c == '=') return CPPToken(PLUSEQUAL, first_line, first_col, first_file); + break; + + case '-': + if (next_c == '-') return CPPToken(MINUSMINUS, first_line, first_col, first_file); + if (next_c == '=') return CPPToken(MINUSEQUAL, first_line, first_col, first_file); + if (next_c == '>' && _last_c == '*') { + _last_c = get(); + return CPPToken(POINTSAT_STAR, first_line, first_col, first_file); + } + if (next_c == '>') return CPPToken(POINTSAT, first_line, first_col, first_file); + break; + + case '<': + if (next_c == '<' && _last_c == '=') { + _last_c = get(); + return CPPToken(LSHIFTEQUAL, first_line, first_col, first_file); + } + if (next_c == '<') return CPPToken(LSHIFT, first_line, first_col, first_file); + if (next_c == '=') return CPPToken(LECOMPARE, first_line, first_col, first_file); + break; + + case '>': + if (next_c == '>' && _last_c == '=') { + _last_c = get(); + return CPPToken(RSHIFTEQUAL, first_line, first_col, first_file); + } + if (next_c == '>') return CPPToken(RSHIFT, first_line, first_col, first_file); + if (next_c == '=') return CPPToken(GECOMPARE, first_line, first_col, first_file); + break; + + case '|': + if (next_c == '|') return CPPToken(OROR, first_line, first_col, first_file); + if (next_c == '=') return CPPToken(OREQUAL, first_line, first_col, first_file); + break; + + case '&': + if (next_c == '&') return CPPToken(ANDAND, first_line, first_col, first_file); + if (next_c == '=') return CPPToken(ANDEQUAL, first_line, first_col, first_file); + break; + + case '^': + if (next_c == '=') return CPPToken(XOREQUAL, first_line, first_col, first_file); + break; + + case '=': + if (next_c == '=') return CPPToken(EQCOMPARE, first_line, first_col, first_file); + break; + + case '!': + if (next_c == '=') return CPPToken(NECOMPARE, first_line, first_col, first_file); + break; + + case '.': + if (next_c == '*') return CPPToken(DOT_STAR, first_line, first_col, first_file); + if (next_c == '.' && _last_c == '.') { + _last_c = get(); + return CPPToken(ELLIPSIS, first_line, first_col, first_file); + } + break; + + case ':': + if (next_c == ':') return CPPToken(SCOPE, first_line, first_col, first_file); + break; + + case '*': + if (next_c == '=') return CPPToken(TIMESEQUAL, first_line, first_col, first_file); + break; + + case '/': + if (next_c == '=') return CPPToken(DIVIDEEQUAL, first_line, first_col, first_file); + break; + + case '%': + if (next_c == '=') return CPPToken(MODEQUAL, first_line, first_col, first_file); + break; + + // These are actually preprocessor operators, but it's useful to + // treat them as tokens. + case '#': + if (next_c == '#') return CPPToken(TOKENPASTE, first_line, first_col, first_file); + } + + // It wasn't any of the two- or three-character tokens, so put back + // the lookahead character and return the one-character token. + unget(_last_c); + _last_c = next_c; + + if (_state == S_nested) { + // If we're running a nested lexer, keep track of the paren + // levels. When we encounter a comma or closing angle bracket at + // the bottom level, we stop. + + switch (c) { + case '(': + case '[': + _paren_nesting++; + break; + + case ')': + case ']': + _paren_nesting--; + break; + + case ',': + if (_paren_nesting <= 0) { + _state = S_end_nested; + return CPPToken::eof(); + } + break; + + case '>': + if (_paren_nesting <= 0) { + _angle_bracket_found = true; + _state = S_end_nested; + return CPPToken::eof(); + } + } + } + + return CPPToken(c, first_line, first_col, first_file); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::skip_whitespace +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +skip_whitespace(int c) { + while (c != EOF) { + c = skip_comment(c); + + if (c == '\\') { + // A backslash character is an unusual thing to encounter in the + // middle of unquoted C++ code. But it seems to be legal, and + // it seems to mean the same thing it does within quotes: to + // escape the following character. We simply ignore it. + c = get(); + } + + if (!isspace(c)) { + return c; + } + c = get(); + } + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::skip_comment +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +skip_comment(int c) { + if (c == '/') { + int next_c = get(); + if (next_c == '*') { + _last_cpp_comment = false; + c = skip_c_comment(get()); + } else if (next_c == '/') { + c = skip_cpp_comment(get()); + } else { + _last_cpp_comment = false; + unget(next_c); + return c; + } + } + if (!isspace(c)) { + _last_cpp_comment = false; + } + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::skip_c_comment +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +skip_c_comment(int c) { + if (_save_comments) { + CPPCommentBlock *comment = new CPPCommentBlock; + _comments.push_back(comment); + + comment->_file = get_file(); + comment->_line_number = get_line_number(); + comment->_last_line = get_line_number(); + comment->_col_number = get_col_number() - 2; + comment->_c_style = true; + comment->_comment = "/*"; + + while (c != EOF) { + if (c == '*') { + comment->_comment += c; + c = get(); + if (c == '/') { + comment->_comment += c; + comment->_last_line = get_line_number(); + return get(); + } + } else { + comment->_comment += c; + c = get(); + } + } + + warning("Comment is unterminated", + comment->_line_number, comment->_col_number, + comment->_file); + + } else { + CPPFile first_file = get_file(); + int first_line_number = get_line_number(); + int first_col_number = get_col_number() - 2; + + while (c != EOF) { + if (c == '*') { + c = get(); + if (c == '/') { + return get(); + } + } else { + c = get(); + } + } + + warning("Comment is unterminated", + first_line_number, first_col_number, + first_file); + } + + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::skip_cpp_comment +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +skip_cpp_comment(int c) { + if (_save_comments) { + CPPCommentBlock *comment; + + if (_last_cpp_comment) { + // If the last non-whitespace character read was also part of a + // C++ comment, then this is just a continuation of that comment + // block. + assert(!_comments.empty()); + comment = _comments.back(); + assert(!comment->_c_style); + comment->_comment += "//"; + + } else { + // Otherwise, this begins a new comment block. + comment = new CPPCommentBlock; + + comment->_file = get_file(); + comment->_line_number = get_line_number(); + comment->_last_line = get_line_number(); + comment->_col_number = get_col_number() - 2; + comment->_c_style = false; + comment->_comment = "//"; + + _comments.push_back(comment); + } + + while (c != EOF && c != '\n') { + comment->_comment += c; + c = get(); + } + + comment->_comment += '\n'; + comment->_last_line = get_line_number(); + + _last_cpp_comment = true; + + } else { + while (c != EOF && c != '\n') { + c = get(); + } + } + + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::process_directive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +process_directive(int c) { + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + + string command, args; + c = get_preprocessor_command(c, command); + c = get_preprocessor_args(c, args); + +#ifdef CPP_VERBOSE_LEX + cerr << "#" << command << " " << args << "\n"; +#endif + + if (command == "define") { + handle_define_directive(args, first_line, first_col, first_file); + } else if (command == "undef") { + handle_undef_directive(args, first_line, first_col, first_file); + } else if (command == "ifdef") { + handle_ifdef_directive(args, first_line, first_col, first_file); + } else if (command == "ifndef") { + handle_ifndef_directive(args, first_line, first_col, first_file); + } else if (command == "if") { + handle_if_directive(args, first_line, first_col, first_file); + } else if (command == "else" || command == "elif") { + // Presumably this follows some #if or #ifdef. We don't bother to + // check this, however. + skip_false_if_block(false); + } else if (command == "endif") { + // Presumably this follows some #if or #ifdef. We don't bother to + // check this, however. + } else if (command == "include") { + handle_include_directive(args, first_line, first_col, first_file); + } else if (command == "pragma") { + // Quietly ignore pragmas. + } else if (command == "ident") { + // Quietly ignore idents. + } else if (command == "error") { + handle_error_directive(args, first_line, first_col, first_file); + } else { + warning("Ignoring unknown directive #" + command, + first_line, first_col, first_file); + } + + _start_of_line = true; + return '\n'; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_preprocessor_command +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +get_preprocessor_command(int c, string &command) { + // Skip the hash mark. + assert(c == '#'); + c = get(); + + // Also skip any whitespace following the hash mark--but don't skip + // past a newline. + while (c != EOF && (c == ' ' || c == '\t')) { + c = get(); + } + + // The next sequence of characters is the command. + while (c != EOF && (isalnum(c) || c == '_')) { + command += c; + c = get(); + } + + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_preprocessor_args +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +get_preprocessor_args(int c, string &args) { + // Following the command, the rest of the line, as well as any text + // on successive lines, is part of the arguments to the command. + + while (c != EOF && c != '\n') { + if (c == '\\') { + int next_c = get(); + if (next_c == '\n') { + // Here we have an escaped newline: a continuation. + args += '\n'; + } else { + // Just a backslash followed by some non-backslash, keep both. + args += c; + if (next_c != EOF) { + args += next_c; + } + } + } else { + args += c; + } + c = skip_comment(get()); + } + + // Remove any leading and trailing whitespace from the args. + args = trim_blanks(args); + + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::handle_define_directive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +handle_define_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file) { + if (args.empty()) { + warning("Ignoring empty #define directive", + first_line, first_col, first_file); + } else { + CPPManifest *manifest = new CPPManifest(args, first_file); + manifest->_vis = preprocessor_vis; + if (!manifest->_has_parameters) { + string expr_string = manifest->expand(); + if (!expr_string.empty()) { + manifest->_expr = parse_expr(expr_string, global_scope, global_scope); + } + } + _manifests[manifest->_name] = manifest; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::handle_undef_directive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +handle_undef_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file) { + if (args.empty()) { + warning("Ignoring empty #undef directive", + first_line, first_col, first_file); + } else { + Manifests::iterator mi = _manifests.find(args); + if (mi != _manifests.end()) { + _manifests.erase(mi); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::handle_ifdef_directive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +handle_ifdef_directive(const string &args, int, int, const CPPFile &) { + Manifests::const_iterator mi = _manifests.find(args); + if (mi != _manifests.end()) { + // The macro is defined. We continue. + return; + } + + // The macro is undefined. Skip stuff. + skip_false_if_block(true); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::handle_ifndef_directive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +handle_ifndef_directive(const string &args, int, int, const CPPFile &) { + Manifests::const_iterator mi = _manifests.find(args); + if (mi == _manifests.end()) { + // The macro is undefined. We continue. + return; + } + + // The macro is defined. Skip stuff. + skip_false_if_block(true); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::handle_if_directive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +handle_if_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file) { + CPPExpression *expr = parse_expr(args, global_scope, global_scope); + + int expression_result = 0; + + if (expr != (CPPExpression *)NULL) { + CPPExpression::Result result = expr->evaluate(); + if (result._type == CPPExpression::RT_error) { + warning("Ignoring invalid expression " + args, + first_line, first_col, first_file); + } else { + expression_result = result.as_integer(); + } + } else { + warning("Ignoring invalid expression " + args, + first_line, first_col, first_file); + } + + if (expression_result) { + // The expression result is true. We continue. + return; + } + + // The expression result is false. Skip stuff. + skip_false_if_block(true); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::handle_include_directive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +handle_include_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file) { + bool okflag = false; + Filename filename; + Filename filename_as_referenced; + bool angle_quotes = false; + + if (!args.empty()) { + if (args[0] == '"' && args[args.size() - 1] == '"') { + filename = args.substr(1, args.size() - 2); + okflag = true; + + if (_files.size() == 1) { + // If we're currently processing a top-level file, record the + // include directive. We don't need to record includes from + // included files. + _quote_includes.insert(filename); + } + } else if (args[0] == '<' && args[args.size() - 1] == '>') { + filename = args.substr(1, args.size() - 2); + angle_quotes = true; + okflag = true; + + if (_files.size() == 1) { + // If we're currently processing a top-level file, record the + // include directive. We don't need to record includes from + // included files. + _angle_includes.insert(filename); + } + } + } + + filename.set_text(); + filename_as_referenced = filename; + + // Now look for the filename. If we didn't use angle quotes, look + // first in the current directory. + bool found_file = false; + CPPFile::Source source = CPPFile::S_none; + + if (okflag) { + if (!angle_quotes) { + found_file = filename.exists(); + source = CPPFile::S_local; + } + + // Now look for it on the include path. + if (!found_file && filename.resolve_filename(_include_path)) { + found_file = true; + source = CPPFile::S_alternate; + } + + if (!found_file && filename.resolve_filename(_system_include_path)) { + found_file = true; + source = CPPFile::S_system; + } + + if (!found_file) { + warning("Cannot find " + filename.get_fullpath(), + first_line, first_col, first_file); + } else { + _last_c = '\0'; + if (!push_file(CPPFile(filename, filename_as_referenced, source))) { + warning("Unable to read " + filename.get_fullpath(), + first_line, first_col, first_file); + } + } + } else { + warning("Ignoring invalid #include directive", + first_line, first_col, first_file); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::handle_error_directive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +handle_error_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file) { + error(args, first_line, first_col, first_file); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::skip_false_if_block +// Access: Private +// Description: We come here when we fail an #if or an #ifdef test, +// or when we reach the #else clause to something we +// didn't fail. This function skips all text up until +// the matching #endif. +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +skip_false_if_block(bool consider_elifs) { + int level = 0; + _save_comments = false; + + int c = skip_comment(get()); + while (c != EOF) { + if (c == '#' && _start_of_line) { + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + + // Is this it? + string command; + c = get_preprocessor_command(c, command); + if (command == "if" || command == "ifdef" || command == "ifndef") { + // Hmm, a nested if block. Even more to skip. + level++; + } else if (command == "else") { + if (level == 0 && consider_elifs) { + // This will do! + _save_comments = true; + return; + } + } else if (command == "elif") { + if (level == 0 && consider_elifs) { + // If we pass this test, we're in. + _save_comments = true; + string args; + c = get_preprocessor_args(c, args); + handle_if_directive(args, first_line, first_col, first_file); + return; + } + } else if (command == "endif") { + if (level == 0) { + // Here's the end! + _save_comments = true; + return; + } + level--; + } + } else { + c = skip_comment(get()); + } + } + + _save_comments = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_quoted_char +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken CPPPreprocessor:: +get_quoted_char(int c) { + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + string str = scan_quoted(c); + + YYSTYPE result; + if (!str.empty()) { + result.u.integer = (int)str[0]; + } else { + result.u.integer = 0; + } + return CPPToken(CHAR, first_line, first_col, first_file, str, result); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_quoted_string +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken CPPPreprocessor:: +get_quoted_string(int c) { + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + string str = scan_quoted(c); + return CPPToken(STRING, first_line, first_col, first_file, str); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_identifier +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken CPPPreprocessor:: +get_identifier(int c) { + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + string name(1, (char)c); + + c = get(); + while (c != EOF && (isalnum(c) || c == '_')) { + name += c; + c = get(); + } + + _last_c = c; + + // Is it a manifest? + Manifests::const_iterator mi = _manifests.find(name); + if (mi != _manifests.end() && !should_ignore_manifest((*mi).second)) { + return expand_manifest((*mi).second); + } + + // Check for keywords. + int kw = check_keyword(name); + + // Update our internal visibility flag. + switch (kw) { + case KW_BEGIN_PUBLISH: + preprocessor_vis = V_published; + break; + + case KW_END_PUBLISH: + preprocessor_vis = V_public; + break; + } + + if (kw != 0) { + YYSTYPE result; + result.u.identifier = (CPPIdentifier *)NULL; + return CPPToken(kw, first_line, first_col, first_file, name, result); + } + + return CPPToken(SIMPLE_IDENTIFIER, first_line, first_col, first_file, + name); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::expand_manifest +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken CPPPreprocessor:: +expand_manifest(const CPPManifest *manifest) { + vector args; + + if (manifest->_has_parameters) { + // Hmm, we're expecting arguments. + extract_manifest_args(manifest->_name, manifest->_num_parameters, args); + } + + string expanded = " " + manifest->expand(args) + " "; + push_string(expanded, true); + + if (!manifest->_has_parameters) { + // If the manifest does not use arguments, then disallow recursive + // expansion. + _files.back()._ignore_manifest = manifest; + } + +#ifdef CPP_VERBOSE_LEX + cerr << "Expanding " << manifest->_name << " to " << expanded << "\n"; +#endif + + return internal_get_next_token(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::extract_manifest_args +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +extract_manifest_args(const string &name, int num_args, + vector &args) { + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + + // Skip whitespace till paren. + int c = _last_c; + _last_c = '\0'; + while (c != EOF && isspace(c)) { + c = get(); + } + + if (c != '(') { + // No paren, so we have only one arg. + string arg; + while (c != EOF && (isalnum(c) || c == '_')) { + arg += c; + c = get(); + } + args.push_back(arg); + } else { + // Skip paren. + c = get(); + string arg; + while (c != EOF && c != ')') { + if (c == ',') { + args.push_back(arg); + arg = ""; + + } else if (c == '(') { + // Nested parens. + int paren_level = 1; + while (c != EOF && paren_level > 0) { + arg += c; + c = get(); + if (c == '(') { + paren_level++; + } else if (c == ')') { + paren_level--; + } + } + if (c != EOF) { + arg += c; + } + + } else { + arg += c; + } + c = get(); + } + if (num_args != 0 || !arg.empty()) { + args.push_back(arg); + } + } + + if ((int)args.size() != num_args) { + warning("Wrong number of arguments for manifest " + name, + first_line, first_col, first_file); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::expand_defined_function +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +expand_defined_function(string &expr, size_t q, size_t &p) { + string result; + + vector args; + extract_manifest_args_inline("defined", 1, args, expr, p); + if (args.size() >= 1) { + const string &manifest_name = args[0]; + Manifests::const_iterator mi = _manifests.find(manifest_name); + if (mi != _manifests.end()) { + // The macro is defined; the result is "1". + result = "1"; + } else { + // The macro is undefined; the result is "0". + result = "0"; + } + } + + expr = expr.substr(0, q) + result + expr.substr(p); + p = q + result.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::expand_manifest_inline +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +expand_manifest_inline(string &expr, size_t q, size_t &p, + const CPPManifest *manifest) { + vector args; + if (manifest->_has_parameters) { + extract_manifest_args_inline(manifest->_name, manifest->_num_parameters, + args, expr, p); + } + string result = manifest->expand(args); + + expr = expr.substr(0, q) + result + expr.substr(p); + p = q + result.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::extract_manifest_args_inline +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +extract_manifest_args_inline(const string &name, int num_args, + vector &args, + const string &expr, size_t &p) { + // Skip whitespace till paren. + while (p < expr.size() && isspace(expr[p])) { + p++; + } + if (p >= expr.size() || expr[p] != '(') { + // No paren, so we have only one arg. + size_t q = p; + while (p < expr.size() && (isalnum(expr[p]) || expr[p] == '_')) { + p++; + } + args.push_back(expr.substr(q, p - q)); + } else { + // Skip paren. + p++; + size_t q = p; + while (p < expr.size() && expr[p] != ')') { + if (expr[p] == ',') { + args.push_back(expr.substr(q, p - q)); + q = p+1; + + } else if (expr[p] == '(') { + // Nested parens. + int paren_level = 1; + while (p+1 < expr.size() && paren_level > 0) { + p++; + if (expr[p] == '(') { + paren_level++; + } else if (expr[p] == ')') { + paren_level--; + } + } + } + p++; + } + args.push_back(expr.substr(q, p - q)); + + if (p < expr.size() && expr[p] == ')') { + p++; + } + } + + if ((int)args.size() != num_args) { + warning("Wrong number of arguments for manifest " + name); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get_number +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken CPPPreprocessor:: +get_number(int c, int c2) { + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + + string num(1, (char)c); + bool leading_zero = (c == '0'); + bool decimal_point = (c == '.'); + + if (c2 == 0) { + c = get(); + } else { + c = c2; + } + if (leading_zero && c == 'x') { + // Here we have a hex number. + num += c; + c = get(); + + while (c != EOF && (isdigit(c) || (tolower(c) >= 'a' && tolower(c) <= 'f'))) { + num += c; + c = get(); + } + + _last_c = c; + YYSTYPE result; + result.u.integer = strtol(num.c_str(), (char **)NULL, 16); + return CPPToken(INTEGER, first_line, first_col, first_file, num, result); + } + + while (c != EOF && isdigit(c)) { + num += c; + c = get(); + } + + if (c == '.' && !decimal_point) { + // Now we have a floating-point number. + decimal_point = true; + num += c; + c = get(); + + while (c != EOF && isdigit(c)) { + num += c; + c = get(); + } + } + + if (decimal_point) { + if (tolower(c) == 'e') { + // An exponent is allowed. + num += c; + c = get(); + if (c == '-' || c == '+') { + num += c; + c = get(); + } + while (c != EOF && isdigit(c)) { + num += c; + c = get(); + } + } + + if (c == 'f') { + // We allow (and ignore) an 'f' following the number. + c = get(); + } + + _last_c = c; + YYSTYPE result; + result.u.real = strtod(num.c_str(), (char **)NULL); + return CPPToken(REAL, first_line, first_col, first_file, num, result); + } + + // This is a decimal or octal integer number. + + while (c == 'L' || c == 'U') { + // We allow (and ignore) an 'L' and/or 'U' following the number. + c = get(); + } + + _last_c = c; + YYSTYPE result; + + if (leading_zero) { + // A leading zero implies an octal number. strtol() is supposed + // to be able to make this distinction by itself, but we'll do it + // explicitly just to be sure. + result.u.integer = strtol(num.c_str(), (char **)NULL, 8); + + } else { + // A decimal (base 10) integer. + result.u.integer = strtol(num.c_str(), (char **)NULL, 10); + } + + return CPPToken(INTEGER, first_line, first_col, first_file, num, result); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::check_keyword +// Access: Private, Static +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +check_keyword(const string &name) { + if (name == "__begin_publish") return KW_BEGIN_PUBLISH; + if (name == "bool") return KW_BOOL; + if (name == "catch") return KW_CATCH; + if (name == "char") return KW_CHAR; + if (name == "class") return KW_CLASS; + if (name == "const") return KW_CONST; + if (name == "delete") return KW_DELETE; + if (name == "double") return KW_DOUBLE; + if (name == "dynamic_cast") return KW_DYNAMIC_CAST; + if (name == "else") return KW_ELSE; + if (name == "__end_publish") return KW_END_PUBLISH; + if (name == "enum") return KW_ENUM; + if (name == "extern") return KW_EXTERN; + if (name == "explicit") return KW_EXPLICIT; + if (name == "__published") return KW_PUBLISHED; + if (name == "false") return KW_FALSE; + if (name == "float") return KW_FLOAT; + if (name == "friend") return KW_FRIEND; + if (name == "for") return KW_FOR; + if (name == "goto") return KW_GOTO; + if (name == "if") return KW_IF; + if (name == "inline") return KW_INLINE; + if (name == "int") return KW_INT; + if (name == "long") return KW_LONG; + if (name == "mutable") return KW_MUTABLE; + if (name == "namespace") return KW_NAMESPACE; + if (name == "new") return KW_NEW; + if (name == "operator") return KW_OPERATOR; + if (name == "private") return KW_PRIVATE; + if (name == "protected") return KW_PROTECTED; + if (name == "public") return KW_PUBLIC; + if (name == "register") return KW_REGISTER; + if (name == "return") return KW_RETURN; + if (name == "short") return KW_SHORT; + if (name == "signed") return KW_SIGNED; + if (name == "sizeof") return KW_SIZEOF; + if (name == "static") return KW_STATIC; + if (name == "static_cast") return KW_STATIC_CAST; + if (name == "struct") return KW_STRUCT; + if (name == "template") return KW_TEMPLATE; + if (name == "throw") return KW_THROW; + if (name == "true") return KW_TRUE; + if (name == "try") return KW_TRY; + if (name == "typedef") return KW_TYPEDEF; + if (name == "typename") return KW_TYPENAME; + if (name == "union") return KW_UNION; + if (name == "unsigned") return KW_UNSIGNED; + if (name == "using") return KW_USING; + if (name == "virtual") return KW_VIRTUAL; + if (name == "void") return KW_VOID; + if (name == "volatile") return KW_VOLATILE; + if (name == "while") return KW_WHILE; + + if (!cpp_longlong_keyword.empty() && name == cpp_longlong_keyword) { + return KW_LONGLONG; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::scan_quoted +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +string CPPPreprocessor:: +scan_quoted(int c) { + int quote_mark = c; + + string str; + c = get(); + while (c != EOF && c != '\n' && c != quote_mark) { + if (c == '\\') { + // Backslash means a special character follows. + c = get(); + switch (c) { + case 'n': + c = '\n'; + break; + + case 't': + c = '\t'; + break; + + case 'r': + c = '\r'; + break; + + case 'x': + // hex character. + c = get(); + if (isxdigit(c)) { + int val = hex_val(c); + c = get(); + if (isxdigit(c)) { + val = (val << 4) | hex_val(c); + } else { + unget(c); + } + c = val; + } + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + // Octal character. + { + int val = (c - '0'); + c = get(); + if (c >= '0' && c <= '7') { + val = (val << 3) | (c - '0'); + c = get(); + if (c >= '0' && c <= '7') { + val = (val << 3) | (c - '0'); + } else { + unget(c); + } + } else { + unget(c); + } + c = val; + } + break; + } + } + + str += c; + c = get(); + } + + if (c != quote_mark) { + warning("Unclosed string"); + } + return str; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::should_ignore_manifest +// Access: Public +// Description: Returns true if the manifest is one that is being +// ignored right now (presumably because we are +// presently expanding it). +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor:: +should_ignore_manifest(const CPPManifest *manifest) const { + Files::const_iterator fi; + for (fi = _files.begin(); fi != _files.end(); ++fi) { + if ((*fi)._ignore_manifest == manifest) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::should_ignore_preprocessor +// Access: Public +// Description: Returns true if we should ignore any preprocessor +// directives (e.g. we're presently expanding a +// manifest). +//////////////////////////////////////////////////////////////////// +bool CPPPreprocessor:: +should_ignore_preprocessor() const { + Files::const_iterator fi; + for (fi = _files.begin(); fi != _files.end(); ++fi) { + if ((*fi)._ignore_manifest != NULL) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::get +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +int CPPPreprocessor:: +get() { + if (_unget != '\0') { + int c = _unget; + _unget = '\0'; + return c; + } + + if (_files.empty()) { + return EOF; + } + + int c = _files.back().get(); + + while (c == EOF && !_files.empty()) { +#ifdef CPP_VERBOSE_LEX + cerr << "End of input stream, restoring to previous input\n"; +#endif + int last_c = _files.back()._prev_last_c; + _files.pop_back(); + + if (last_c != '\0') { + c = last_c; + } else if (!_files.empty()) { + c = _files.back().get(); + } + } + + if (c == '\n') { + _start_of_line = true; + } else if (!isspace(c) && c != '#') { + _start_of_line = false; + } + + return c; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::unget +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +unget(int c) { + assert(_unget == '\0'); + _unget = c; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::nested_parse_template_instantiation +// Access: Private +// Description: Recursively invokes yacc to parse the stuff within +// angle brackets that's the template instantiation part +// of an identifier. This involves setting and +// restoring some state flags so we can return EOF when +// we reach the closing bracket. +//////////////////////////////////////////////////////////////////// +CPPTemplateParameterList *CPPPreprocessor:: +nested_parse_template_instantiation(CPPTemplateScope *scope) { +#ifdef CPP_VERBOSE_LEX + cerr << "Beginning nested parse\n"; +#endif + assert(scope != NULL); + + State old_state = _state; + int old_nesting = _paren_nesting; + + const CPPTemplateParameterList &formal_params = scope->_parameters; + CPPTemplateParameterList::Parameters::const_iterator pi; + _angle_bracket_found = false; + + CPPToken token = internal_get_next_token(); + if (token._token == '>') { + _angle_bracket_found = true; + } else { + _saved_tokens.push_back(token); + } + + CPPTemplateParameterList *actual_params = new CPPTemplateParameterList; + + for (pi = formal_params._parameters.begin(); + pi != formal_params._parameters.end() && !_angle_bracket_found; + ++pi) { + _state = S_nested; + _paren_nesting = 0; + + CPPFile first_file = get_file(); + int first_line = get_line_number(); + int first_col = get_col_number(); + + CPPDeclaration *decl = (*pi); + if (decl->as_type()) { + _saved_tokens.push_back(CPPToken(START_TYPE)); + CPPType *type = ::parse_type(this, current_scope, global_scope); + if (type == NULL) { + warning("Invalid type", first_line, first_col, first_file); + skip_to_end_nested(); + type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown)); + } + actual_params->_parameters.push_back(type); + } else { + _saved_tokens.push_back(CPPToken(START_CONST_EXPR)); + CPPExpression *expr = parse_const_expr(this, current_scope, global_scope); + if (expr == NULL) { + warning("Invalid expression", first_line, first_col, first_file); + skip_to_end_nested(); + expr = new CPPExpression(0); + } + actual_params->_parameters.push_back(expr); + } + } + + if (!_angle_bracket_found) { + warning("Ignoring extra parameters in template instantiation"); + skip_to_angle_bracket(); + } + + _state = old_state; + _paren_nesting = old_nesting; + _angle_bracket_found = false; + +#ifdef CPP_VERBOSE_LEX + cerr << "Ending nested parse\n"; +#endif + return actual_params; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::skip_to_end_nested +// Access: Private +// Description: This is an error-recovery function, called after +// returning from a nested parse. If the state is not +// S_end_nested, there was an error in parsing the +// nested tokens, and not all of the nested tokens may +// have been consumed. This function will consume the +// rest of the nested tokens. +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +skip_to_end_nested() { +#ifdef CPP_VERBOSE_LEX + cerr << "Skipping tokens:\n"; +#endif + + // Eat any eof tokens on the pushback stack. + while (!_saved_tokens.empty() && _saved_tokens.back().is_eof()) { + _saved_tokens.pop_back(); + } + + while (_state != S_end_nested && _state != S_eof) { + get_next_token(); + } + +#ifdef CPP_VERBOSE_LEX + cerr << "Done skipping tokens.\n"; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPPreprocessor::skip_to_angle_bracket +// Access: Private +// Description: This is an error-recovery function, called after +// returning from a nested parse. If we haven't yet +// consumed the closing angle bracket on the template +// instantiation, keep consuming tokens until we do. +//////////////////////////////////////////////////////////////////// +void CPPPreprocessor:: +skip_to_angle_bracket() { +#ifdef CPP_VERBOSE_LEX + cerr << "Skipping tokens:\n"; +#endif + + while (!_angle_bracket_found && _state != S_eof) { + _state = S_nested; + while (_state != S_end_nested && _state != S_eof) { + get_next_token(); + } + } + + // Eat any eof tokens on the pushback stack. + while (!_saved_tokens.empty() && _saved_tokens.back().is_eof()) { + _saved_tokens.pop_back(); + } + +#ifdef CPP_VERBOSE_LEX + cerr << "Done skipping tokens.\n"; +#endif +} diff --git a/dtool/src/cppparser/cppPreprocessor.h b/dtool/src/cppparser/cppPreprocessor.h new file mode 100644 index 0000000000..8f20826828 --- /dev/null +++ b/dtool/src/cppparser/cppPreprocessor.h @@ -0,0 +1,199 @@ +// Filename: cppPreprocessor.h +// Created by: drose (22Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPPREPROCESSOR_H +#define CPPPREPROCESSOR_H + +#include + +#include "cppManifest.h" +#include "cppToken.h" +#include "cppFile.h" +#include "cppCommentBlock.h" + +#include + +#include +#include +#include + +class CPPScope; +class CPPTemplateParameterList; +class CPPExpression; + +//#define CPP_VERBOSE_LEX + +/////////////////////////////////////////////////////////////////// +// Class : CPPPreprocessor +// Description : +//////////////////////////////////////////////////////////////////// +class CPPPreprocessor { +public: + CPPPreprocessor(); + + void set_verbose(int verbose); + int get_verbose() const; + + void copy_filepos(const CPPPreprocessor &other); + + CPPFile get_file() const; + int get_line_number() const; + int get_col_number() const; + + CPPToken get_next_token(); +#ifdef CPP_VERBOSE_LEX + CPPToken get_next_token0(); + int _token_index; +#endif + + void warning(const string &message, int line = 0, int col = 0, + CPPFile file = CPPFile()); + void error(const string &message, int line = 0, int col = 0, + CPPFile file = CPPFile()); + + CPPCommentBlock *get_comment_before(int line, CPPFile file); + + int get_warning_count() const; + int get_error_count() const; + + typedef map Manifests; + Manifests _manifests; + + DSearchPath _include_path; + DSearchPath _system_include_path; + + CPPComments _comments; + + typedef set ParsedFiles; + ParsedFiles _parsed_files; + + typedef set Includes; + Includes _quote_includes; + Includes _angle_includes; + + // This is normally true, to indicate that the preprocessor should + // decode identifiers like foo::bar into a single IDENTIFIER, + // TYPENAME_IDENTIFIER, or SCOPING token for yacc's convenience. + // When false, it leaves them alone and returns a sequence of + // SIMPLE_IDENTIFIER and SCOPE tokens instead. + bool _resolve_identifiers; + + // The default _verbose level is 1, which will output normal error + // and warning messages but nothing else. Set this to 0 to make the + // warning messages go away (although the counts will still be + // incremented), or set it higher to get more debugging information. + int _verbose; + +protected: + bool init_cpp(const CPPFile &file); + bool init_const_expr(const string &expr); + bool init_type(const string &type); + bool push_file(const CPPFile &file); + bool push_string(const string &input, bool lock_position); + + CPPExpression *parse_expr(const string &expr, CPPScope *current_scope, + CPPScope *global_scope); + +private: + CPPToken internal_get_next_token(); + int skip_whitespace(int c); + int skip_comment(int c); + int skip_c_comment(int c); + int skip_cpp_comment(int c); + int process_directive(int c); + + int get_preprocessor_command(int c, string &command); + int get_preprocessor_args(int c, string &args); + + void handle_define_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file); + void handle_undef_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file); + void handle_ifdef_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file); + void handle_ifndef_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file); + void handle_if_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file); + void handle_include_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file); + void handle_error_directive(const string &args, int first_line, + int first_col, const CPPFile &first_file); + + void skip_false_if_block(bool consider_elifs); + + CPPToken get_quoted_char(int c); + CPPToken get_quoted_string(int c); + CPPToken get_identifier(int c); + CPPToken expand_manifest(const CPPManifest *manifest); + void extract_manifest_args(const string &name, int num_args, + vector &args); + void expand_defined_function(string &expr, size_t q, size_t &p); + void expand_manifest_inline(string &expr, size_t q, size_t &p, + const CPPManifest *manifest); + void extract_manifest_args_inline(const string &name, int num_args, + vector &args, + const string &expr, size_t &p); + + CPPToken get_number(int c, int c2 = 0); + static int check_keyword(const string &name); + string scan_quoted(int c); + + bool should_ignore_manifest(const CPPManifest *manifest) const; + bool should_ignore_preprocessor() const; + + int get(); + void unget(int c); + + CPPTemplateParameterList * + nested_parse_template_instantiation(CPPTemplateScope *scope); + void skip_to_end_nested(); + void skip_to_angle_bracket(); + + class InputFile { + public: + InputFile(); + ~InputFile(); + + bool open(const CPPFile &file); + bool connect_input(const string &input); + int get(); + + const CPPManifest *_ignore_manifest; + CPPFile _file; + string _input; + istream *_in; + int _line_number; + int _col_number; + bool _lock_position; + int _prev_last_c; + }; + + // This must be a list and not a vector because we don't have a good + // copy constructor defined for InputFile. + typedef list Files; + Files _files; + + enum State { + S_normal, S_eof, S_nested, S_end_nested + }; + State _state; + int _paren_nesting; + bool _angle_bracket_found; + + bool _start_of_line; + int _unget; + + int _last_c; + bool _last_cpp_comment; + bool _save_comments; + + vector _saved_tokens; + + int _warning_count; + int _error_count; +}; + +#endif diff --git a/dtool/src/cppparser/cppReferenceType.cxx b/dtool/src/cppparser/cppReferenceType.cxx new file mode 100644 index 0000000000..4ad7526e1d --- /dev/null +++ b/dtool/src/cppparser/cppReferenceType.cxx @@ -0,0 +1,190 @@ +// Filename: cppReferenceType.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppReferenceType.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPReferenceType:: +CPPReferenceType(CPPType *pointing_at) : + CPPType(CPPFile()), + _pointing_at(pointing_at) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPReferenceType:: +is_fully_specified() const { + return CPPType::is_fully_specified() && + _pointing_at->is_fully_specified(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPReferenceType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + return (*si).second; + } + + CPPReferenceType *rep = new CPPReferenceType(*this); + rep->_pointing_at = + _pointing_at->substitute_decl(subst, current_scope, global_scope) + ->as_type(); + + if (rep->_pointing_at == _pointing_at) { + delete rep; + rep = this; + } + rep = CPPType::new_type(rep)->as_reference_type(); + subst.insert(SubstDecl::value_type(this, rep)); + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPReferenceType:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + CPPType *ptype = _pointing_at->resolve_type(current_scope, global_scope); + + if (ptype != _pointing_at) { + CPPReferenceType *rep = new CPPReferenceType(*this); + rep->_pointing_at = ptype; + return CPPType::new_type(rep); + } + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPReferenceType:: +is_tbd() const { + return _pointing_at->is_tbd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::is_equivalent +// Access: Public, Virtual +// Description: This is a little more forgiving than is_equal(): it +// returns true if the types appear to be referring to +// the same thing, even if they may have different +// pointers or somewhat different definitions. It's +// useful for parameter matching, etc. +//////////////////////////////////////////////////////////////////// +bool CPPReferenceType:: +is_equivalent(const CPPType &other) const { + const CPPReferenceType *ot = ((CPPType *)&other)->as_reference_type(); + if (ot == (CPPReferenceType *)NULL) { + return CPPType::is_equivalent(other); + } + + return _pointing_at->is_equivalent(*ot->_pointing_at); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPReferenceType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + _pointing_at->output(out, indent_level, scope, complete); + out << " &"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::output_instance +// Access: Public, Virtual +// Description: Formats a C++-looking line that defines an instance +// of the given type, with the indicated name. In most +// cases this will be "type name", but some types have +// special exceptions. +//////////////////////////////////////////////////////////////////// +void CPPReferenceType:: +output_instance(ostream &out, int indent_level, CPPScope *scope, + bool complete, const string &prename, + const string &name) const { + _pointing_at->output_instance(out, indent_level, scope, complete, + "&" + prename, name); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPReferenceType:: +get_subtype() const { + return ST_reference; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::as_reference_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPReferenceType *CPPReferenceType:: +as_reference_type() { + return this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPReferenceType:: +is_equal(const CPPDeclaration *other) const { + const CPPReferenceType *ot = ((CPPDeclaration *)other)->as_reference_type(); + assert(ot != NULL); + + return _pointing_at == ot->_pointing_at; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPReferenceType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPReferenceType:: +is_less(const CPPDeclaration *other) const { + const CPPReferenceType *ot = ((CPPDeclaration *)other)->as_reference_type(); + assert(ot != NULL); + + return _pointing_at < ot->_pointing_at; +} diff --git a/dtool/src/cppparser/cppReferenceType.h b/dtool/src/cppparser/cppReferenceType.h new file mode 100644 index 0000000000..5ba45a8430 --- /dev/null +++ b/dtool/src/cppparser/cppReferenceType.h @@ -0,0 +1,50 @@ +// Filename: cppReferenceType.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPREFERENCETYPE_H +#define CPPREFERENCETYPE_H + +#include + +#include "cppType.h" + +/////////////////////////////////////////////////////////////////// +// Class : CPPReferenceType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPReferenceType : public CPPType { +public: + CPPReferenceType(CPPType *pointing_at); + + CPPType *_pointing_at; + + virtual bool is_fully_specified() const; + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_tbd() const; + virtual bool is_equivalent(const CPPType &other) const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual void output_instance(ostream &out, int indent_level, + CPPScope *scope, + bool complete, const string &prename, + const string &name) const; + + virtual SubType get_subtype() const; + + virtual CPPReferenceType *as_reference_type(); + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +#endif diff --git a/dtool/src/cppparser/cppScope.cxx b/dtool/src/cppparser/cppScope.cxx new file mode 100644 index 0000000000..979669a748 --- /dev/null +++ b/dtool/src/cppparser/cppScope.cxx @@ -0,0 +1,1061 @@ +// Filename: cppScope.C +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppScope.h" +#include "cppDeclaration.h" +#include "cppNamespace.h" +#include "cppTypedef.h" +#include "cppTypeDeclaration.h" +#include "cppExtensionType.h" +#include "cppInstance.h" +#include "cppInstanceIdentifier.h" +#include "cppIdentifier.h" +#include "cppStructType.h" +#include "cppFunctionGroup.h" +#include "cppPreprocessor.h" +#include "cppTemplateScope.h" +#include "cppClassTemplateParameter.h" +#include "cppFunctionType.h" +#include "cppUsing.h" +#include "cppBisonDefs.h" +#include "indent.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope:: +CPPScope(CPPScope *parent_scope, + const CPPNameComponent &name, CPPVisibility starting_vis) : + _name(name), + _parent_scope(parent_scope), + _current_vis(starting_vis) +{ + _struct_type = NULL; + _is_fully_specified = false; + _fully_specified_known = false; + _is_fully_specified_recursive_protect = false; + _subst_decl_recursive_protect = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope:: +~CPPScope() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::set_struct_type +// Access: Public +// Description: Sets the struct or class that owns this scope. This +// should only be done once, when the scope and its +// associated struct are created. It's provided so the +// scope can check the struct's ancestry for inherited +// symbols. +//////////////////////////////////////////////////////////////////// +void CPPScope:: +set_struct_type(CPPStructType *struct_type) { + _struct_type = struct_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::get_struct_type +// Access: Public +// Description: Returns the class or struct that defines this scope, +// if any. +//////////////////////////////////////////////////////////////////// +CPPStructType *CPPScope:: +get_struct_type() const { + return _struct_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::get_parent_scope +// Access: Public +// Description: Returns the parent scope of this scope, if any. +//////////////////////////////////////////////////////////////////// +CPPScope *CPPScope:: +get_parent_scope() const { + return _parent_scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::set_current_vis +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPScope:: +set_current_vis(CPPVisibility current_vis) { + _current_vis = current_vis; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::get_current_vis +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPVisibility CPPScope:: +get_current_vis() const { + return _current_vis; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::add_declaration +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPScope:: +add_declaration(CPPDeclaration *decl, CPPScope *global_scope, + CPPPreprocessor *preprocessor, const cppyyltype &pos) { + decl->_vis = _current_vis; + + // Get the recent comments from the preprocessor. These are the + // comments that appeared preceding this particular declaration; + // they might be relevant to the declaration. + + if (decl->_leading_comment == (CPPCommentBlock *)NULL) { + decl->_leading_comment = + preprocessor->get_comment_before(pos.first_line, pos.file); + } + + _declarations.push_back(decl); + + handle_declaration(decl, global_scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::add_enum_value +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPScope:: +add_enum_value(CPPInstance *inst) { + inst->_vis = _current_vis; + + string name = inst->get_simple_name(); + if (!name.empty()) { + _enum_values[name] = inst; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::define_extension_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPScope:: +define_extension_type(CPPExtensionType *type) { + assert(type != NULL); + string name = type->get_simple_name(); + + switch (type->_type) { + case CPPExtensionType::T_class: + _classes[name] = type; + break; + + case CPPExtensionType::T_struct: + _structs[name] = type; + break; + + case CPPExtensionType::T_union: + _unions[name] = type; + + case CPPExtensionType::T_enum: + _enums[name] = type; + } + + // Create an implicit typedef for the extension. + CPPIdentifier *ident = new CPPIdentifier(name); + CPPTypedef *td = new CPPTypedef(new CPPInstance(type, ident), false); + pair result = + _typedefs.insert(Typedefs::value_type(name, td)); + + if (!result.second) { + // There's already a typedef for this extension. This one + // overrides if it has template parameters and the other one + // doesn't. + CPPType *other_type = (*result.first).second->_type; + if (type->is_template() && !other_type->is_template()) { + (*result.first).second = td; + + // Or if the other one is a forward reference. + } else if (other_type->get_subtype() == CPPDeclaration::ST_extension) { + (*result.first).second = td; + } + } + + if (type->is_template()) { + _templates.insert(Templates::value_type(name, type)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::define_namespace +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPScope:: +define_namespace(CPPNamespace *scope) { + string name = scope->get_simple_name(); + + _namespaces[name] = scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::add_using +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPScope:: +add_using(CPPUsing *using_decl, CPPScope *global_scope, + CPPPreprocessor *error_sink) { + if (using_decl->_full_namespace) { + CPPScope *scope = + using_decl->_ident->find_scope(this, global_scope); + if (scope != NULL) { + _using.insert(scope); + } else { + if (error_sink != NULL) { + error_sink->warning("Attempt to use undefined namespace: " + using_decl->_ident->get_fully_scoped_name()); + } + } + } else { + CPPDeclaration *decl = using_decl->_ident->find_symbol(this, global_scope); + if (decl != NULL) { + handle_declaration(decl, global_scope); + } else { + if (error_sink != NULL) { + error_sink->warning("Attempt to use unknown symbol: " + using_decl->_ident->get_fully_scoped_name()); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPScope:: +is_fully_specified() const { + if (_fully_specified_known) { + return _is_fully_specified; + } + + if (_is_fully_specified_recursive_protect) { + // We're already executing this block. + return true; + } + ((CPPScope *)this)->_is_fully_specified_recursive_protect = true; + + bool specified = true; + + if (_parent_scope != NULL && !_parent_scope->is_fully_specified()) { + specified = false; + } + + Declarations::const_iterator di; + for (di = _declarations.begin(); + di != _declarations.end() && specified; + ++di) { + if (!(*di)->is_fully_specified()) { + specified = false; + } + } + + ((CPPScope *)this)->_fully_specified_known = true; + ((CPPScope *)this)->_is_fully_specified = specified; + ((CPPScope *)this)->_is_fully_specified_recursive_protect = false; + + return specified; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::instantiate +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPScope:: +instantiate(const CPPTemplateParameterList *actual_params, + CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + CPPScope *this_scope = (CPPScope *)this; + + if (_parent_scope == NULL || + _parent_scope->as_template_scope() == NULL) { + if (error_sink != NULL) { + error_sink->warning("Ignoring template parameters for scope " + + get_local_name()); + } + return this_scope; + } + + if (is_fully_specified()) { + return this_scope; + } + + Instantiations::const_iterator ii; + ii = _instantiations.find(actual_params); + if (ii != _instantiations.end()) { + // We've already instantiated this scope with these parameters. + // Return that. + return (*ii).second; + } + + /* + cerr << "Instantiating " << get_simple_name() + << "<" << *actual_params << ">\n"; + */ + + // Build the mapping of formal parameters to actual parameters. + CPPTemplateScope *tscope = _parent_scope->as_template_scope(); + CPPDeclaration::SubstDecl subst; + actual_params->build_subst_decl(tscope->_parameters, subst, + current_scope, global_scope); + + CPPScope *scope; + if (subst.empty()) { + scope = (CPPScope *)this; + + } else { + CPPNameComponent name = _name; + name.set_templ(new CPPTemplateParameterList(*actual_params)); + // scope = new CPPScope(current_scope, name, V_public); + scope = new CPPScope(_parent_scope, name, V_public); + copy_substitute_decl(scope, subst, global_scope); + + // Also define any new template parameter types, in case we + // "instantiated" this scope with another template parameter. + CPPTemplateParameterList::Parameters::const_iterator pi; + for (pi = actual_params->_parameters.begin(); + pi != actual_params->_parameters.end(); + ++pi) { + CPPDeclaration *decl = (*pi); + CPPClassTemplateParameter *ctp = decl->as_class_template_parameter(); + if (ctp != NULL) { + CPPInstance *inst = new CPPInstance(ctp, ctp->_ident); + CPPTypedef *td = new CPPTypedef(inst, true); + scope->_typedefs.insert(Typedefs::value_type + (ctp->_ident->get_local_name(), + td)); + } + } + } + + // Finally, record this particular instantiation for future + // reference, so we don't have to do this again. + ((CPPScope *)this)->_instantiations.insert(Instantiations::value_type(actual_params, scope)); + + return scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPScope:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) const { + CPPScope *this_scope = (CPPScope *)this; + + if (is_fully_specified()) { + return this_scope; + } + + if (_subst_decl_recursive_protect) { + // We're already executing this block. + return this_scope; + } + ((CPPScope *)this)->_subst_decl_recursive_protect = true; + + CPPScope *rep = new CPPScope(current_scope, _name, V_public); + bool anything_changed; + + if (_parent_scope != NULL && + _parent_scope->as_template_scope() != NULL) { + // If the parent of this scope is a template scope--e.g. this + // scope has template parameters--then we must first remove any of + // the template parameters from the subst list. These will later + // get substituted properly during instantiation. + const CPPTemplateParameterList &p = + _parent_scope->as_template_scope()->_parameters; + + CPPDeclaration::SubstDecl new_subst = subst; + CPPTemplateParameterList::Parameters::const_iterator pi; + for (pi = p._parameters.begin(); pi != p._parameters.end(); ++pi) { + new_subst.erase(*pi); + } + anything_changed = copy_substitute_decl(rep, new_subst, global_scope); + } else { + anything_changed = copy_substitute_decl(rep, subst, global_scope); + } + + if (!anything_changed && rep->_parent_scope == _parent_scope) { + delete rep; + rep = (CPPScope *)this; + } + ((CPPScope *)this)->_subst_decl_recursive_protect = false; + + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::find_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPType *CPPScope:: +find_type(const string &name, bool recurse) const { + Typedefs::const_iterator ti; + ti = _typedefs.find(name); + if (ti != _typedefs.end()) { + return (*ti).second->_type; + } + + Using::const_iterator ui; + for (ui = _using.begin(); ui != _using.end(); ++ui) { + CPPType *type = (*ui)->find_type(name, false); + if (type != NULL) { + return type; + } + } + + if (_struct_type != NULL) { + CPPStructType::Derivation::const_iterator di; + for (di = _struct_type->_derivation.begin(); + di != _struct_type->_derivation.end(); + ++di) { + CPPStructType *st = (*di)._base->as_struct_type(); + if (st != NULL) { + CPPType *type = st->_scope->find_type(name, false); + if (type != NULL) { + return type; + } + } + } + } + + if (recurse && _parent_scope != NULL) { + return _parent_scope->find_type(name); + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::find_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPType *CPPScope:: +find_type(const string &name, CPPDeclaration::SubstDecl &subst, + CPPScope *global_scope, bool recurse) const { + Typedefs::const_iterator ti; + ti = _typedefs.find(name); + if (ti != _typedefs.end()) { + CPPScope *current_scope = (CPPScope *)this; + return (*ti).second->_type->substitute_decl + (subst, current_scope, global_scope)->as_type(); + } + + Using::const_iterator ui; + for (ui = _using.begin(); ui != _using.end(); ++ui) { + CPPType *type = (*ui)->find_type(name, subst, global_scope, false); + if (type != NULL) { + return type; + } + } + + if (_struct_type != NULL) { + CPPStructType::Derivation::const_iterator di; + for (di = _struct_type->_derivation.begin(); + di != _struct_type->_derivation.end(); + ++di) { + CPPStructType *st = (*di)._base->as_struct_type(); + if (st != NULL) { + CPPType *type = st->_scope->find_type(name, subst, global_scope, + false); + if (type != NULL) { + return type; + } + } + } + } + + if (recurse && _parent_scope != NULL) { + return _parent_scope->find_type(name, subst, global_scope); + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::find_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPScope:: +find_scope(const string &name, bool recurse) const { + Namespaces::const_iterator ni = _namespaces.find(name); + if (ni != _namespaces.end()) { + return (*ni).second->get_scope(); + } + + CPPType *type = (CPPType *)NULL; + + Typedefs::const_iterator ti; + ti = _typedefs.find(name); + if (ti != _typedefs.end()) { + type = (*ti).second->_type; + + } else if (_struct_type != NULL) { + CPPStructType::Derivation::const_iterator di; + for (di = _struct_type->_derivation.begin(); + di != _struct_type->_derivation.end(); + ++di) { + CPPStructType *st = (*di)._base->as_struct_type(); + if (st != NULL) { + type = st->_scope->find_type(name, false); + } + } + } + + if (type != NULL) { + CPPStructType *st = type->as_struct_type(); + if (st != NULL) { + return st->_scope; + } + } + + Using::const_iterator ui; + for (ui = _using.begin(); ui != _using.end(); ++ui) { + CPPScope *scope = (*ui)->find_scope(name, false); + if (scope != NULL) { + return scope; + } + } + + if (recurse && _parent_scope != NULL) { + return _parent_scope->find_scope(name); + } + + return (CPPScope *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::find_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPScope:: +find_scope(const string &name, CPPDeclaration::SubstDecl &subst, + CPPScope *global_scope, bool recurse) const { + CPPType *type = find_type(name, subst, global_scope, recurse); + if (type == NULL) { + return NULL; + } + CPPStructType *st = type->as_struct_type(); + if (st == NULL) { + return NULL; + } + return st->_scope; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::find_symbol +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPScope:: +find_symbol(const string &name, bool recurse) const { + if (_struct_type != NULL && name == get_simple_name()) { + return _struct_type; + } + + Typedefs::const_iterator ti; + ti = _typedefs.find(name); + if (ti != _typedefs.end()) { + return (*ti).second->_type; + } + + Variables::const_iterator vi; + vi = _variables.find(name); + if (vi != _variables.end()) { + return (*vi).second; + } + + vi = _enum_values.find(name); + if (vi != _enum_values.end()) { + return (*vi).second; + } + + Functions::const_iterator fi; + fi = _functions.find(name); + if (fi != _functions.end()) { + return (*fi).second; + } + + Using::const_iterator ui; + for (ui = _using.begin(); ui != _using.end(); ++ui) { + CPPDeclaration *decl = (*ui)->find_symbol(name, false); + if (decl != NULL) { + return decl; + } + } + + if (_struct_type != NULL) { + CPPStructType::Derivation::const_iterator di; + for (di = _struct_type->_derivation.begin(); + di != _struct_type->_derivation.end(); + ++di) { + CPPStructType *st = (*di)._base->as_struct_type(); + if (st != NULL) { + CPPDeclaration *decl = st->_scope->find_symbol(name, false); + if (decl != NULL) { + return decl; + } + } + } + } + + if (recurse && _parent_scope != NULL) { + return _parent_scope->find_symbol(name); + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::find_template +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPScope:: +find_template(const string &name, bool recurse) const { + Templates::const_iterator ti; + ti = _templates.find(name); + if (ti != _templates.end()) { + return (*ti).second; + } + + Using::const_iterator ui; + for (ui = _using.begin(); ui != _using.end(); ++ui) { + CPPDeclaration *decl = (*ui)->find_template(name, false); + if (decl != NULL) { + return decl; + } + } + + if (_struct_type != NULL) { + CPPStructType::Derivation::const_iterator di; + for (di = _struct_type->_derivation.begin(); + di != _struct_type->_derivation.end(); + ++di) { + CPPStructType *st = (*di)._base->as_struct_type(); + if (st != NULL) { + CPPDeclaration *decl = st->_scope->find_template(name, false); + if (decl != NULL) { + return decl; + } + } + } + } + + if (recurse && _parent_scope != NULL) { + return _parent_scope->find_template(name); + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::get_simple_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPScope:: +get_simple_name() const { + /* + if (_struct_type != (CPPStructType *)NULL) { + return _struct_type->get_simple_name(); + } + */ + return _name.get_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::get_local_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPScope:: +get_local_name(CPPScope *scope) const { + /* + if (_struct_type != (CPPStructType *)NULL) { + return _struct_type->get_local_name(scope); + } + */ + + if (scope != NULL && _parent_scope != NULL && _parent_scope != scope) { + return _parent_scope->get_local_name(scope) + "::" + + _name.get_name_with_templ(); + } else { + return _name.get_name_with_templ(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::get_fully_scoped_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPScope:: +get_fully_scoped_name() const { + /* + if (_struct_type != (CPPStructType *)NULL) { + return _struct_type->get_fully_scoped_name(); + } + */ + + if (_parent_scope != NULL) { + return _parent_scope->get_fully_scoped_name() + "::" + + _name.get_name_with_templ(); + } else { + return _name.get_name_with_templ(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPScope:: +output(ostream &out, CPPScope *scope) const { + // out << get_local_name(scope); + if (_parent_scope != NULL && _parent_scope != scope) { + _parent_scope->output(out, scope); + out << "::"; + } + out << _name; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPScope:: +write(ostream &out, int indent_level, CPPScope *scope) const { + CPPVisibility vis = V_unknown; + Declarations::const_iterator di; + for (di = _declarations.begin(); di != _declarations.end(); ++di) { + CPPDeclaration *cd = (*di); + if (cd->_vis != vis && indent_level > 0) { + vis = cd->_vis; + indent(out, indent_level - 2) << vis << ":\n"; + } + bool complete = false; + + if (cd->as_typedef() != NULL || cd->as_type() != NULL || + cd->as_namespace() != NULL) { + complete = true; + } + + indent(out, indent_level); + cd->output(out, indent_level, scope, complete); + out << ";\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::get_template_scope +// Access: Public +// Description: Returns the nearest ancestor of this scope that is a +// template scope, or NULL if the scope is fully +// specified. +//////////////////////////////////////////////////////////////////// +CPPTemplateScope *CPPScope:: +get_template_scope() { + if (as_template_scope()) { + return as_template_scope(); + } + if (_parent_scope != NULL) { + return _parent_scope->get_template_scope(); + } + return (CPPTemplateScope *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::as_template_scope +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTemplateScope *CPPScope:: +as_template_scope() { + return (CPPTemplateScope *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::copy_substitute_decl +// Access: Private +// Description: This is in support of both substitute_decl() and +// instantiate(). It's similar in purpose to +// substitute_decl(), but this function assumes the +// caller has already created a new, empty scope. All +// of the declarations in this scope are copied to the +// new scope, filtering through the subst decl. +// +// The return value is true if the scope is changed, +// false if it is not. +//////////////////////////////////////////////////////////////////// +bool CPPScope:: +copy_substitute_decl(CPPScope *to_scope, CPPDeclaration::SubstDecl &subst, + CPPScope *global_scope) const { + bool anything_changed = false; + + if (_struct_type != NULL) { + to_scope->_struct_type = + new CPPStructType(_struct_type->_type, + new CPPIdentifier(to_scope->_name), + _struct_type->_ident->_native_scope, + to_scope, _struct_type->_file); + to_scope->_struct_type->_incomplete = false; + + // Copy the derivation to the new type. + CPPStructType::Derivation::const_iterator di; + for (di = _struct_type->_derivation.begin(); + di != _struct_type->_derivation.end(); + ++di) { + CPPStructType::Base b = (*di); + b._base = + (*di)._base->substitute_decl(subst, to_scope, global_scope)->as_type(); + to_scope->_struct_type->_derivation.push_back(b); + if (b._base != (*di)._base) { + anything_changed = true; + } + } + } + + Declarations::const_iterator di; + for (di = _declarations.begin(); di != _declarations.end(); ++di) { + CPPDeclaration *decl = + (*di)->substitute_decl(subst, to_scope, global_scope); + to_scope->_declarations.push_back(decl); + if (decl != (*di)) { + anything_changed = true; + } + } + + ExtensionTypes::const_iterator ei; + for (ei = _structs.begin(); ei != _structs.end(); ++ei) { + string name = (*ei).first; + CPPType *source_type = (*ei).second; + CPPDeclaration *decl = + source_type->substitute_decl(subst, to_scope, global_scope); + assert(decl != NULL); + CPPType *new_type = decl->as_type(); + assert(new_type != NULL); + to_scope->_structs.insert(ExtensionTypes::value_type(name, new_type)); + if (new_type != source_type) { + anything_changed = true; + } + } + for (ei = _classes.begin(); ei != _classes.end(); ++ei) { + string name = (*ei).first; + CPPType *source_type = (*ei).second; + CPPDeclaration *decl = + source_type->substitute_decl(subst, to_scope, global_scope); + assert(decl != NULL); + CPPType *new_type = decl->as_type(); + assert(new_type != NULL); + to_scope->_classes.insert(ExtensionTypes::value_type(name, new_type)); + if (new_type != source_type) { + anything_changed = true; + } + } + for (ei = _unions.begin(); ei != _unions.end(); ++ei) { + string name = (*ei).first; + CPPType *source_type = (*ei).second; + CPPDeclaration *decl = + source_type->substitute_decl(subst, to_scope, global_scope); + assert(decl != NULL); + CPPType *new_type = decl->as_type(); + assert(new_type != NULL); + to_scope->_unions.insert(ExtensionTypes::value_type(name, new_type)); + if (new_type != source_type) { + anything_changed = true; + } + } + for (ei = _enums.begin(); ei != _enums.end(); ++ei) { + string name = (*ei).first; + CPPType *source_type = (*ei).second; + CPPDeclaration *decl = + source_type->substitute_decl(subst, to_scope, global_scope); + assert(decl != NULL); + CPPType *new_type = decl->as_type(); + assert(new_type != NULL); + to_scope->_enums.insert(ExtensionTypes::value_type(name, new_type)); + if (new_type != source_type) { + anything_changed = true; + } + } + Functions::const_iterator fi; + for (fi = _functions.begin(); fi != _functions.end(); ++fi) { + CPPFunctionGroup *fgroup = (*fi).second; + string name = fgroup->_name; + + CPPFunctionGroup *&to_fgroup = to_scope->_functions[name]; + if (to_fgroup == (CPPFunctionGroup *)NULL) { + to_fgroup = new CPPFunctionGroup(name); + } + + CPPFunctionGroup::Instances::const_iterator ii; + for (ii = fgroup->_instances.begin(); + ii != fgroup->_instances.end(); + ++ii) { + CPPInstance *inst = + (*ii)->substitute_decl(subst, to_scope, global_scope)->as_instance(); + to_fgroup->_instances.push_back(inst); + if (inst != (*ii)) { + anything_changed = true; + } + } + } + + Typedefs::const_iterator ti; + for (ti = _typedefs.begin(); ti != _typedefs.end(); ++ti) { + CPPTypedef *td = + (*ti).second->substitute_decl(subst, to_scope, global_scope)->as_typedef(); + to_scope->_typedefs.insert(Typedefs::value_type((*ti).first, td)); + if (td != (*ti).second) { + anything_changed = true; + } + } + Variables::const_iterator vi; + for (vi = _variables.begin(); vi != _variables.end(); ++vi) { + CPPInstance *inst = + (*vi).second->substitute_decl(subst, to_scope, global_scope)->as_instance(); + to_scope->_variables.insert(Variables::value_type((*vi).first, inst)); + if (inst != (*vi).second) { + anything_changed = true; + } + } + + Templates::const_iterator tmi; + for (tmi = _templates.begin(); tmi != _templates.end(); ++tmi) { + CPPDeclaration *decl = + (*tmi).second->substitute_decl(subst, to_scope, global_scope); + to_scope->_templates.insert(Templates::value_type((*tmi).first, decl)); + if (decl != (*tmi).second) { + anything_changed = true; + } + } + + return anything_changed; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPScope::handle_declaration +// Access: Private +// Description: Does the right thing with a newly given declaration: +// adds it to the typedef list, or variables or +// functions, or whatever. +//////////////////////////////////////////////////////////////////// +void CPPScope:: +handle_declaration(CPPDeclaration *decl, CPPScope *global_scope) { + CPPTypedef *def = decl->as_typedef(); + if (def != NULL) { + string name = def->get_simple_name(); + _typedefs[name] = def; + + CPPExtensionType *et = def->_type->as_extension_type(); + if (et != NULL) { + define_extension_type(et); + } + + if (!name.empty() && def->get_scope(this, global_scope) == this) { + // Don't add a new template definition if we already had one + // by the same name in another scope. + + if (find_template(name) == NULL) { + _templates.insert(Templates::value_type(name, def)); + } + } + return; + } + + CPPTypeDeclaration *typedecl = decl->as_type_declaration(); + if (typedecl != (CPPTypeDeclaration *)NULL) { + CPPExtensionType *et = typedecl->_type->as_extension_type(); + if (et != NULL) { + define_extension_type(et); + } + return; + } + + CPPInstance *inst = decl->as_instance(); + if (inst != NULL) { + inst->check_for_constructor(this, global_scope); + + string name = inst->get_simple_name(); + if (!name.empty() && inst->get_scope(this, global_scope) == this) { + if (inst->_type->as_function_type()) { + // This is a function declaration; hence it gets added to + // the _functions member. But we must be careful to share + // common-named functions. + + CPPFunctionGroup *fgroup; + Functions::const_iterator fi; + fi = _functions.find(name); + if (fi == _functions.end()) { + fgroup = new CPPFunctionGroup(name); + _functions.insert(Functions::value_type(name, fgroup)); + } else { + fgroup = (*fi).second; + } + fgroup->_instances.push_back(inst); + + } else { + // This is not a function declaration; hence it gets added + // to the _variables member. + _variables[name] = inst; + } + + if (inst->is_template()) { + // Don't add a new template definition if we already had one + // by the same name in another scope. + + if (find_template(name) == NULL) { + _templates.insert(Templates::value_type(name, inst)); + } + + /* + if (inst->_type->as_function_type() == NULL || + (inst->_type->as_function_type()->_flags & + CPPFunctionType::F_constructor) == 0) { + _templates.insert(Templates::value_type(name, inst)); + } + */ + } + } + return; + } + + CPPExtensionType *et = decl->as_extension_type(); + if (et != NULL) { + define_extension_type(et); + } +} diff --git a/dtool/src/cppparser/cppScope.h b/dtool/src/cppparser/cppScope.h new file mode 100644 index 0000000000..95f4ff1296 --- /dev/null +++ b/dtool/src/cppparser/cppScope.h @@ -0,0 +1,154 @@ +// Filename: cppScope.h +// Created by: drose (21Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPSCOPE_H +#define CPPSCOPE_H + +#include + +#include "cppVisibility.h" +#include "cppTemplateParameterList.h" +#include "cppNameComponent.h" + +#include +#include +#include +#include + +using namespace std; + +class CPPType; +class CPPDeclaration; +class CPPExtensionType; +class CPPStructType; +class CPPNamespace; +class CPPUsing; +class CPPTypedef; +class CPPInstance; +class CPPFunctionGroup; +class CPPTemplateScope; +class CPPTemplateParameterList; +class CPPPreprocessor; +class CPPNameComponent; +struct cppyyltype; + +/////////////////////////////////////////////////////////////////// +// Class : CPPScope +// Description : +//////////////////////////////////////////////////////////////////// +class CPPScope { +public: + CPPScope(CPPScope *parent_scope, + const CPPNameComponent &name, CPPVisibility starting_vis); + virtual ~CPPScope(); + + void set_current_vis(CPPVisibility current_vis); + CPPVisibility get_current_vis() const; + + void set_struct_type(CPPStructType *struct_type); + CPPStructType *get_struct_type() const; + CPPScope *get_parent_scope() const; + + virtual void add_declaration(CPPDeclaration *decl, CPPScope *global_scope, + CPPPreprocessor *preprocessor, + const cppyyltype &pos); + virtual void add_enum_value(CPPInstance *inst); + virtual void define_extension_type(CPPExtensionType *type); + virtual void define_namespace(CPPNamespace *scope); + virtual void add_using(CPPUsing *using_decl, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL); + + virtual bool is_fully_specified() const; + + CPPScope * + instantiate(const CPPTemplateParameterList *actual_params, + CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + + CPPScope * + substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope) const; + + CPPType *find_type(const string &name, bool recurse = true) const; + CPPType *find_type(const string &name, + CPPDeclaration::SubstDecl &subst, + CPPScope *global_scope, + bool recurse = true) const; + CPPScope *find_scope(const string &name, bool recurse = true) const; + CPPScope *find_scope(const string &name, + CPPDeclaration::SubstDecl &subst, + CPPScope *global_scope, + bool recurse = true) const; + CPPDeclaration *find_symbol(const string &name, + bool recurse = true) const; + CPPDeclaration *find_template(const string &name, + bool recurse = true) const; + + virtual string get_simple_name() const; + virtual string get_local_name(CPPScope *scope = NULL) const; + virtual string get_fully_scoped_name() const; + + virtual void output(ostream &out, CPPScope *scope) const; + void write(ostream &out, int indent, CPPScope *scope) const; + + CPPTemplateScope *get_template_scope(); + virtual CPPTemplateScope *as_template_scope(); + +private: + bool + copy_substitute_decl(CPPScope *to_scope, CPPDeclaration::SubstDecl &subst, + CPPScope *global_scope) const; + + void handle_declaration(CPPDeclaration *decl, CPPScope *global_scope); + +public: + typedef vector Declarations; + Declarations _declarations; + + typedef map ExtensionTypes; + ExtensionTypes _structs; + ExtensionTypes _classes; + ExtensionTypes _unions; + ExtensionTypes _enums; + + typedef map Namespaces; + Namespaces _namespaces; + + typedef map Typedefs; + Typedefs _typedefs; + typedef map Variables; + Variables _variables; + Variables _enum_values; + typedef map Functions; + Functions _functions; + typedef map Templates; + Templates _templates; + CPPNameComponent _name; + +protected: + CPPScope *_parent_scope; + CPPStructType *_struct_type; + typedef set Using; + Using _using; + CPPVisibility _current_vis; + +private: + typedef map Instantiations; + Instantiations _instantiations; + + bool _is_fully_specified; + bool _fully_specified_known; + bool _is_fully_specified_recursive_protect; + bool _subst_decl_recursive_protect; +}; + +inline ostream & +operator << (ostream &out, const CPPScope &scope) { + scope.output(out, (CPPScope *)NULL); + return out; +} + +#endif diff --git a/dtool/src/cppparser/cppSimpleType.cxx b/dtool/src/cppparser/cppSimpleType.cxx new file mode 100644 index 0000000000..7c191bc486 --- /dev/null +++ b/dtool/src/cppparser/cppSimpleType.cxx @@ -0,0 +1,163 @@ +// Filename: cppSimpleType.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include "cppSimpleType.h" +#include "cppGlobals.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPSimpleType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPSimpleType:: +CPPSimpleType(CPPSimpleType::Type type, int flags) : + CPPType(CPPFile()), + _type(type), _flags(flags) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPSimpleType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPSimpleType:: +is_tbd() const { + return (_type == T_unknown); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPSimpleType::get_preferred_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPSimpleType:: +get_preferred_name() const { + // Simple types always prefer to use their native types. + return get_local_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPSimpleType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPSimpleType:: +output(ostream &out, int, CPPScope *, bool) const { + if (_flags & F_unsigned) { + out << "unsigned "; + } + + if (_flags & F_signed) { + out << "signed "; + } + + if ((_type == T_int && (_flags & F_longlong) != 0) && + !cpp_longlong_keyword.empty()) { + // It's a long long, and we have a specific long long type name. + // This is to output code for compilers that don't recognize "long + // long int". + out << cpp_longlong_keyword; + return; + } + + if (_flags & F_longlong) { + out << "long long "; + } else if (_flags & F_long) { + out << "long "; + } else if (_flags & F_short) { + out << "short "; + } + + switch (_type) { + case T_bool: + out << "bool"; + break; + + case T_char: + out << "char"; + break; + + case T_int: + out << "int"; + break; + + case T_float: + out << "float"; + break; + + case T_double: + out << "double"; + break; + + case T_void: + out << "void"; + break; + + case T_unknown: + out << "unknown"; + break; + + default: + out << "***invalid type***"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPSimpleType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPSimpleType:: +get_subtype() const { + return ST_simple; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPSimpleType::as_simple_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPSimpleType *CPPSimpleType:: +as_simple_type() { + return this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPSimpleType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPSimpleType:: +is_equal(const CPPDeclaration *other) const { + const CPPSimpleType *ot = ((CPPDeclaration *)other)->as_simple_type(); + assert(ot != NULL); + + return _type == ot->_type && _flags == ot->_flags; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPSimpleType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPSimpleType:: +is_less(const CPPDeclaration *other) const { + const CPPSimpleType *ot = ((CPPDeclaration *)other)->as_simple_type(); + assert(ot != NULL); + + if (_type != ot->_type) { + return _type < ot->_type; + } + return _flags < ot->_flags; +} diff --git a/dtool/src/cppparser/cppSimpleType.h b/dtool/src/cppparser/cppSimpleType.h new file mode 100644 index 0000000000..5eb3a8200d --- /dev/null +++ b/dtool/src/cppparser/cppSimpleType.h @@ -0,0 +1,57 @@ +// Filename: cppSimpleType.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPSIMPLETYPE_H +#define CPPSIMPLETYPE_H + +#include + +#include "cppType.h" + +/////////////////////////////////////////////////////////////////// +// Class : CPPSimpleType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPSimpleType : public CPPType { +public: + enum Type { + T_bool, + T_char, + T_int, + T_float, + T_double, + T_void, + T_unknown, + }; + + enum Flags { + F_long = 0x001, + F_longlong = 0x002, + F_short = 0x004, + F_unsigned = 0x008, + F_signed = 0x010, + }; + + CPPSimpleType(Type type, int flags = 0); + + Type _type; + int _flags; + + virtual bool is_tbd() const; + + virtual string get_preferred_name() const; + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPSimpleType *as_simple_type(); + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; +}; + +#endif diff --git a/dtool/src/cppparser/cppStructType.cxx b/dtool/src/cppparser/cppStructType.cxx new file mode 100644 index 0000000000..7a6f41b497 --- /dev/null +++ b/dtool/src/cppparser/cppStructType.cxx @@ -0,0 +1,596 @@ +// Filename: cppStructType.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppStructType.h" +#include "cppTypedef.h" +#include "cppScope.h" +#include "cppTypeProxy.h" +#include "cppTemplateScope.h" +#include "cppFunctionGroup.h" +#include "cppFunctionType.h" +#include "cppTBDType.h" +#include "indent.h" +#include "cppParser.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::Base::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPStructType::Base:: +output(ostream &out) const { + if (_is_virtual) { + out << "virtual "; + } + out << _vis << " " << *_base; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPStructType:: +CPPStructType(CPPStructType::Type type, CPPIdentifier *ident, + CPPScope *current_scope, CPPScope *scope, + const CPPFile &file) : + CPPExtensionType(type, ident, current_scope, file), + _scope(scope) +{ + _subst_decl_recursive_protect = false; + _incomplete = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPStructType:: +CPPStructType(const CPPStructType ©) : + CPPExtensionType(copy), + _scope(copy._scope), + _incomplete(copy._incomplete), + _derivation(copy._derivation) +{ + _subst_decl_recursive_protect = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPStructType:: +operator = (const CPPStructType ©) { + CPPExtensionType::operator = (copy); + _scope = copy._scope; + _incomplete = copy._incomplete; + _derivation = copy._derivation; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::append_derivation +// Access: Public +// Description: A handy function used while parsing to add a new base +// class to the list of classes (or structs) this class +// derives from. +//////////////////////////////////////////////////////////////////// +void CPPStructType:: +append_derivation(CPPType *base, CPPVisibility vis, bool is_virtual) { + if (base != NULL) { + Base b; + b._base = base; + b._vis = vis; + b._is_virtual = is_virtual; + + _derivation.push_back(b); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::get_scope +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPScope *CPPStructType:: +get_scope() const { + return _scope; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::is_abstract +// Access: Public +// Description: Returns true if this struct declaration is abstract, +// e.g. it contains or inherits at least one method that +// is pure virtual. +//////////////////////////////////////////////////////////////////// +bool CPPStructType:: +is_abstract() const { + VFunctions funcs; + get_pure_virtual_funcs(funcs); + return !funcs.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::check_virtual +// Access: Public +// Description: Ensures all functions are correctly marked with the +// "virtual" flag if they are truly virtual by virtue of +// inheritance, rather than simply being labeled +// virtual. +// +// This also sets the CPPInstance::SC_inherited_virtual +// flags on those virtual methods that override a +// virtual method defined in a parent class (as opposed +// to those that appear for this first time in this +// class). It is sometimes useful to know whether a +// given virtual method represents the first time that +// particular method appears. +//////////////////////////////////////////////////////////////////// +void CPPStructType:: +check_virtual() { + VFunctions funcs; + get_virtual_funcs(funcs); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPStructType:: +is_fully_specified() const { + if (_scope != NULL && !_scope->is_fully_specified()) { + return false; + } + return CPPType::is_fully_specified(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::is_incomplete +// Access: Public, Virtual +// Description: Returns true if the type has not yet been fully +// specified, false if it has. +//////////////////////////////////////////////////////////////////// +bool CPPStructType:: +is_incomplete() const { + return _incomplete; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::get_destructor +// Access: Public +// Description: Returns the destructor defined for the struct type, +// if any, or NULL if no destructor is found. +//////////////////////////////////////////////////////////////////// +CPPInstance *CPPStructType:: +get_destructor() const { + // Iterate through all the functions that begin with '~' until we + // find one that claims to be a destructor. In theory, there should + // only be one such function. + CPPScope::Functions::const_iterator fi; + fi = _scope->_functions.lower_bound("~"); + + while (fi != _scope->_functions.end() && + (*fi).first[0] == '~') { + CPPFunctionGroup *fgroup = (*fi).second; + CPPFunctionGroup::Instances::const_iterator ii; + for (ii = fgroup->_instances.begin(); + ii != fgroup->_instances.end(); + ++ii) { + CPPInstance *inst = (*ii); + assert(inst->_type != (CPPType *)NULL); + + CPPFunctionType *ftype = inst->_type->as_function_type(); + assert(ftype != (CPPFunctionType *)NULL); + + if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { + return inst; + } + } + ++fi; + } + + return (CPPInstance *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::instantiate +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPStructType:: +instantiate(const CPPTemplateParameterList *actual_params, + CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink) const { + + // I *think* this assertion is no longer valid. Who knows. + // assert(!_incomplete); + + if (_scope == NULL) { + if (error_sink != NULL) { + error_sink->warning("Ignoring template parameters for class " + + get_local_name()); + } + return (CPPDeclaration *)this; + } + + CPPScope *scope = + _scope->instantiate(actual_params, current_scope, global_scope, error_sink); + + if (scope->get_struct_type()->get_scope() != scope) { + // Hmm, this type seems to be not completely defined. We must be + // in the middle of recursively instantiating the scope. Thus, we + // don't yet know what its associated struct type will be. + + // Postpone the evaluation of this type. + CPPIdentifier *ident = new CPPIdentifier(get_fully_scoped_name()); + + return CPPType::new_type(new CPPTBDType(ident)); + } + + CPPType *result = scope->get_struct_type(); + result = CPPType::new_type(result); + if (result != (CPPType *)this) { + // This really means the method ought to be non-const. But I'm + // too lazy to propagate this change all the way back right now, + // so this hack is here. + ((CPPStructType *)this)->_instantiations.insert(result); + } + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPStructType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + SubstDecl::const_iterator si = subst.find(this); + if (si != subst.end()) { + assert((*si).second != NULL); + return (*si).second; + } + + if (_incomplete) { + // We haven't finished defining the class yet. + return this; + } + + if (_subst_decl_recursive_protect) { + // We're already executing this block; we'll have to return a + // proxy to the type which we'll define later. + CPPTypeProxy *proxy = new CPPTypeProxy; + _proxies.push_back(proxy); + assert(proxy != NULL); + return proxy; + } + _subst_decl_recursive_protect = true; + + CPPStructType *rep = new CPPStructType(*this); + + if (_ident != NULL) { + rep->_ident = + _ident->substitute_decl(subst, current_scope, global_scope); + } + + if (_scope != NULL) { + rep->_scope = + _scope->substitute_decl(subst, current_scope, global_scope); + if (rep->_scope != _scope) { + rep->_scope->set_struct_type(rep); + + // If we just instantiated a template scope, write the template + // parameters into our identifier. + CPPScope *pscope = rep->_scope->get_parent_scope(); + + if (pscope != (CPPScope *)NULL && + pscope->_name.has_templ()) { + + // If the struct name didn't have an explicit template + // reference before, now it does. + if (!_ident->_names.empty() && !_ident->_names.back().has_templ()) { + if (rep->is_template()) { + rep->_template_scope = (CPPTemplateScope *)NULL; + CPPNameComponent nc(get_simple_name()); + nc.set_templ(pscope->_name.get_templ()); + rep->_ident = new CPPIdentifier(nc); + } + } + } + } + } + + bool unchanged = + (rep->_ident == _ident && rep->_scope == _scope); + + for (int i = 0; i < (int)_derivation.size(); i++) { + rep->_derivation[i]._base = + _derivation[i]._base->substitute_decl(subst, current_scope, global_scope)->as_type(); + if (rep->_derivation[i]._base != _derivation[i]._base) { + unchanged = false; + } + } + + if (unchanged) { + delete rep; + rep = this; + } + + subst.insert(SubstDecl::value_type(this, rep)); + + _subst_decl_recursive_protect = false; + // Now fill in all the proxies we created for our recursive + // references. + Proxies::iterator pi; + for (pi = _proxies.begin(); pi != _proxies.end(); ++pi) { + (*pi)->_actual_type = rep; + } + + assert(rep != NULL); + rep = CPPType::new_type(rep)->as_struct_type(); + assert(rep != NULL); + if (rep != this) { + _instantiations.insert(rep); + // cerr << "Subst for " << *this << " is " << *rep << "\n"; + } + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPStructType:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + if (!complete && _ident != NULL) { + // If we have a name, use it. + if (cppparser_output_class_keyword) { + out << _type << " "; + } + out << _ident->get_local_name(scope); + + if (is_template()) { + CPPTemplateScope *tscope = get_template_scope(); + out << "< "; + tscope->_parameters.output(out, scope); + out << " >"; + } + + } else if (!complete && !_typedefs.empty()) { + // If we have a typedef name, use it. + out << _typedefs.front()->get_local_name(scope); + + } else { + if (is_template()) { + get_template_scope()->_parameters.write_formal(out, scope); + indent(out, indent_level); + } + if (_ident != NULL) { + out << _type << " " << _ident->get_local_name(scope); + } else { + out << _type; + } + + // Show any derivation we may have + if (!_derivation.empty()) { + Derivation::const_iterator di = _derivation.begin(); + out << ": " << *di; + ++di; + while (di != _derivation.end()) { + out << ", " << *di; + ++di; + } + } + + out << " {\n"; + _scope->write(out, indent_level + 2, _scope); + indent(out, indent_level) << "}"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPStructType:: +get_subtype() const { + return ST_struct; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::as_struct_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPStructType *CPPStructType:: +as_struct_type() { + return this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::get_virtual_funcs +// Access: Public +// Description: Fills funcs up with a list of all the virtual +// function declarations (pure-virtual or otherwise) +// defined at or above this class. This is used to +// determine which functions in a given class are +// actually virtual, since a function is virtual whose +// parent class holds a virtual function by the same +// name, whether or not it is actually declared virtual +// in the derived class. +//////////////////////////////////////////////////////////////////// +void CPPStructType:: +get_virtual_funcs(VFunctions &funcs) const { + // First, get all the virtual funcs from our parents. + Derivation::const_iterator di; + for (di = _derivation.begin(); di != _derivation.end(); ++di) { + VFunctions vf; + CPPStructType *base = (*di)._base->as_struct_type(); + if (base != NULL) { + base->get_virtual_funcs(vf); + funcs.splice(funcs.end(), vf); + } + } + + // Now look for matching functions in this class that we can now + // infer are virtual. + VFunctions::iterator vfi, vfnext; + vfi = funcs.begin(); + while (vfi != funcs.end()) { + vfnext = vfi; + ++vfnext; + + CPPInstance *inst = (*vfi); + assert(inst->_type != (CPPType *)NULL); + CPPFunctionType *base_ftype = inst->_type->as_function_type(); + assert(base_ftype != (CPPFunctionType *)NULL); + + if ((base_ftype->_flags & CPPFunctionType::F_destructor) != 0) { + // Match destructor-for-destructor; don't try to match + // destructors up by name. + CPPInstance *destructor = get_destructor(); + if (destructor != (CPPInstance *)NULL) { + // It's a match! This destructor is virtual. + funcs.erase(vfi); + destructor->_storage_class |= + (CPPInstance::SC_virtual | CPPInstance::SC_inherited_virtual); + } + + } else { + // Non-destructors we can try to match up by name. + string fname = inst->get_local_name(); + CPPScope::Functions::const_iterator fi; + fi = _scope->_functions.find(fname); + + if (fi != _scope->_functions.end()) { + CPPFunctionGroup *fgroup = (*fi).second; + + // Look for a matching function amid this group. + bool match_found = false; + CPPFunctionGroup::Instances::const_iterator ii; + for (ii = fgroup->_instances.begin(); + ii != fgroup->_instances.end() && !match_found; + ++ii) { + CPPInstance *new_inst = (*ii); + assert(new_inst->_type != (CPPType *)NULL); + + CPPFunctionType *new_ftype = new_inst->_type->as_function_type(); + assert(new_ftype != (CPPFunctionType *)NULL); + + if (new_ftype->is_equivalent_function(*base_ftype)) { + // It's a match! We now know it's virtual. Erase this + // function from the list, so we can add it back in below. + funcs.erase(vfi); + match_found = true; + + // In fact, it's not only definitely virtual, but it's + // *inherited* virtual, which means only that the + // interface is defined in some parent class. Sometimes + // this is useful to know. + new_inst->_storage_class |= + (CPPInstance::SC_virtual | CPPInstance::SC_inherited_virtual); + } + } + } + } + vfi = vfnext; + } + + // Finally, look for more virtual function definitions. + CPPScope::Functions::const_iterator fi; + for (fi = _scope->_functions.begin(); + fi != _scope->_functions.end(); + ++fi) { + CPPFunctionGroup *fgroup = (*fi).second; + CPPFunctionGroup::Instances::const_iterator ii; + for (ii = fgroup->_instances.begin(); + ii != fgroup->_instances.end(); + ++ii) { + CPPInstance *inst = (*ii); + if ((inst->_storage_class & CPPInstance::SC_virtual) != 0) { + // Here's a virtual function. + funcs.push_back(inst); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::count_pure_virtual_funcs +// Access: Public +// Description: Fills funcs up with a list of all the pure virtual +// function declarations defined at or above this class +// that have not been given definitions. +//////////////////////////////////////////////////////////////////// +void CPPStructType:: +get_pure_virtual_funcs(VFunctions &funcs) const { + // First, get all the virtual functions. + VFunctions vfuncs; + get_virtual_funcs(vfuncs); + + // Now traverse the list, getting out those functions that are pure + // virtual. + VFunctions::iterator vfi; + for (vfi = vfuncs.begin(); vfi != vfuncs.end(); ++vfi) { + CPPInstance *inst = (*vfi); + if ((inst->_storage_class & CPPInstance::SC_pure_virtual) != 0) { + funcs.push_back(inst); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration to determine whether this +// type is equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPStructType:: +is_equal(const CPPDeclaration *other) const { + return CPPDeclaration::is_equal(other); + /* + const CPPStructType *ot = ((CPPDeclaration *)other)->as_struct_type(); + assert(ot != NULL); + + return this == ot || + (get_fully_scoped_name() == ot->get_fully_scoped_name()); + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPStructType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration to determine whether this +// type should be ordered before another type of the +// same type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPStructType:: +is_less(const CPPDeclaration *other) const { + return CPPDeclaration::is_less(other); + /* + const CPPStructType *ot = ((CPPDeclaration *)other)->as_struct_type(); + assert(ot != NULL); + + if (this == ot) { + return false; + } + + return + (get_fully_scoped_name() < ot->get_fully_scoped_name()); + */ +} + diff --git a/dtool/src/cppparser/cppStructType.h b/dtool/src/cppparser/cppStructType.h new file mode 100644 index 0000000000..33371b710e --- /dev/null +++ b/dtool/src/cppparser/cppStructType.h @@ -0,0 +1,93 @@ +// Filename: cppStructType.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPSTRUCTTYPE_H +#define CPPSTRUCTTYPE_H + +#include + +#include "cppExtensionType.h" +#include "cppVisibility.h" + +#include +#include + +class CPPScope; +class CPPTypeProxy; + +/////////////////////////////////////////////////////////////////// +// Class : CPPStructType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPStructType : public CPPExtensionType { +public: + CPPStructType(Type type, CPPIdentifier *ident, + CPPScope *current_scope, + CPPScope *scope, + const CPPFile &file); + CPPStructType(const CPPStructType ©); + void operator = (const CPPStructType ©); + + void append_derivation(CPPType *base, CPPVisibility vis, bool is_virtual); + + CPPScope *get_scope() const; + + bool is_abstract() const; + void check_virtual(); + virtual bool is_fully_specified() const; + virtual bool is_incomplete() const; + + CPPInstance *get_destructor() const; + + virtual CPPDeclaration * + instantiate(const CPPTemplateParameterList *actual_params, + CPPScope *current_scope, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL) const; + + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPStructType *as_struct_type(); + + CPPScope *_scope; + bool _incomplete; + + class Base { + public: + void output(ostream &out) const; + + CPPType *_base; + CPPVisibility _vis; + bool _is_virtual; + }; + + typedef vector Derivation; + Derivation _derivation; + + typedef list VFunctions; + void get_virtual_funcs(VFunctions &funcs) const; + void get_pure_virtual_funcs(VFunctions &funcs) const; + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; + + bool _subst_decl_recursive_protect; + typedef vector Proxies; + Proxies _proxies; +}; + +inline ostream &operator << (ostream &out, const CPPStructType::Base &base) { + base.output(out); + return out; +} + + +#endif diff --git a/dtool/src/cppparser/cppTBDType.cxx b/dtool/src/cppparser/cppTBDType.cxx new file mode 100644 index 0000000000..f5c3540b24 --- /dev/null +++ b/dtool/src/cppparser/cppTBDType.cxx @@ -0,0 +1,197 @@ +// Filename: cppTBDType.C +// Created by: drose (05Nov99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppTBDType.h" +#include "cppIdentifier.h" + +#include "cppSimpleType.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTBDType:: +CPPTBDType(CPPIdentifier *ident) : + CPPType(CPPFile()), + _ident(ident) +{ + _subst_decl_recursive_protect = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPTBDType:: +resolve_type(CPPScope *current_scope, CPPScope *global_scope) { + CPPType *type = _ident->find_type(current_scope, global_scope); + if (type != NULL) { + return type; + } + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPTBDType:: +is_tbd() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::get_simple_name +// Access: Public, Virtual +// Description: Returns a fundametal one-word name for the type. +// This name will not include any scoping operators or +// template parameters, so it may not be a compilable +// reference to the type. +//////////////////////////////////////////////////////////////////// +string CPPTBDType:: +get_simple_name() const { + return _ident->get_simple_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::get_local_name +// Access: Public, Virtual +// Description: Returns the compilable, correct name for this type +// within the indicated scope. If the scope is NULL, +// within the scope the type is declared in. +//////////////////////////////////////////////////////////////////// +string CPPTBDType:: +get_local_name(CPPScope *scope) const { + return _ident->get_local_name(scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::get_fully_scoped_name +// Access: Public, Virtual +// Description: Returns the compilable, correct name for the type, +// with completely explicit scoping. +//////////////////////////////////////////////////////////////////// +string CPPTBDType:: +get_fully_scoped_name() const { + return _ident->get_fully_scoped_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPTBDType:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + CPPDeclaration *top = + CPPDeclaration::substitute_decl(subst, current_scope, global_scope); + if (top != this) { + return top; + } + + // Protect against recursive entry into this function block. I know + // it's ugly--have you got any better suggestions? + if (_subst_decl_recursive_protect) { + // We're already executing this block. + return this; + } + _subst_decl_recursive_protect = true; + + CPPTBDType *rep = new CPPTBDType(*this); + rep->_ident = _ident->substitute_decl(subst, current_scope, global_scope); + + if (rep->_ident == _ident) { + delete rep; + rep = this; + } + + rep = CPPType::new_type(rep)->as_tbd_type(); + assert(rep != NULL); + + CPPType *result = rep; + + // Can we now define it as a real type? + CPPType *type = rep->_ident->find_type(current_scope, global_scope, subst); + if (type != NULL) { + result = type; + } + + subst.insert(SubstDecl::value_type(this, result)); + + _subst_decl_recursive_protect = false; + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTBDType:: +output(ostream &out, int, CPPScope *, bool) const { + out /* << "typename " */ << *_ident; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPTBDType:: +get_subtype() const { + return ST_tbd; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::as_tbd_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTBDType *CPPTBDType:: +as_tbd_type() { + return this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::is_equal +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type is +// equivalent to another type of the same type. +//////////////////////////////////////////////////////////////////// +bool CPPTBDType:: +is_equal(const CPPDeclaration *other) const { + const CPPTBDType *ot = ((CPPDeclaration *)other)->as_tbd_type(); + assert(ot != NULL); + + return (*_ident) == (*ot->_ident); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPTBDType::is_less +// Access: Protected, Virtual +// Description: Called by CPPDeclaration() to determine whether this type +// should be ordered before another type of the same +// type, in an arbitrary but fixed ordering. +//////////////////////////////////////////////////////////////////// +bool CPPTBDType:: +is_less(const CPPDeclaration *other) const { + const CPPTBDType *ot = ((CPPDeclaration *)other)->as_tbd_type(); + assert(ot != NULL); + + return (*_ident) < (*ot->_ident); +} diff --git a/dtool/src/cppparser/cppTBDType.h b/dtool/src/cppparser/cppTBDType.h new file mode 100644 index 0000000000..98eb09ace5 --- /dev/null +++ b/dtool/src/cppparser/cppTBDType.h @@ -0,0 +1,56 @@ +// Filename: cppTBDType.h +// Created by: drose (05Nov99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTBDTYPE_H +#define CPPTBDTYPE_H + +#include + +#include "cppType.h" + +class CPPIdentifier; + +/////////////////////////////////////////////////////////////////// +// Class : CPPTBDType +// Description : This represents a type whose exact meaning is still +// to-be-determined. It happens when a typename is +// referenced in a template class (especially using the +// 'typename' keyword) but the actual type cannot be +// known until the class is instantiated. +//////////////////////////////////////////////////////////////////// +class CPPTBDType : public CPPType { +public: + CPPTBDType(CPPIdentifier *ident); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_tbd() const; + + virtual string get_simple_name() const; + virtual string get_local_name(CPPScope *scope = NULL) const; + virtual string get_fully_scoped_name() const; + + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPTBDType *as_tbd_type(); + + CPPIdentifier *_ident; + +protected: + virtual bool is_equal(const CPPDeclaration *other) const; + virtual bool is_less(const CPPDeclaration *other) const; + +private: + bool _subst_decl_recursive_protect; +}; + +#endif diff --git a/dtool/src/cppparser/cppTemplateParameterList.cxx b/dtool/src/cppparser/cppTemplateParameterList.cxx new file mode 100644 index 0000000000..f97897ca67 --- /dev/null +++ b/dtool/src/cppparser/cppTemplateParameterList.cxx @@ -0,0 +1,253 @@ +// Filename: cppTemplateParameterList.C +// Created by: drose (28Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppTemplateParameterList.h" +#include "cppClassTemplateParameter.h" +#include "cppInstance.h" +#include "cppExpression.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTemplateParameterList:: +CPPTemplateParameterList() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +string CPPTemplateParameterList:: +get_string() const { + ostringstream strname; + strname << "< " << *this << " >"; + return strname.str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::build_subst_decl +// Access: Public +// Description: Matches up the actual parameters one-to-one with the +// formal parameters they are replacing, so the template +// may be instantiated by swapping out each occurrence +// of a template standin type with its appropriate +// replacement. +//////////////////////////////////////////////////////////////////// +void CPPTemplateParameterList:: +build_subst_decl(const CPPTemplateParameterList &formal_params, + CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) const { + Parameters::const_iterator pfi, pai; + for (pfi = formal_params._parameters.begin(), pai = _parameters.begin(); + pfi != formal_params._parameters.end() && pai != _parameters.end(); + ++pfi, ++pai) { + CPPDeclaration *formal = *pfi; + CPPDeclaration *actual = *pai; + + if (actual->as_type()) { + actual = actual->as_type()->resolve_type(current_scope, global_scope); + } + + if (!(formal == actual)) { + subst.insert(CPPDeclaration::SubstDecl::value_type(formal, actual)); + } + } + + // Fill in the default template parameters. + while (pfi != formal_params._parameters.end()) { + CPPDeclaration *decl = (*pfi); + if (decl->as_instance()) { + // A value template parameter. Its default is an expression. + CPPInstance *inst = decl->as_instance(); + if (inst->_initializer != NULL) { + CPPDeclaration *decl = + inst->_initializer->substitute_decl(subst, current_scope, + global_scope); + if (!(*decl == *inst)) { + subst.insert(CPPDeclaration::SubstDecl::value_type + (inst, decl)); + } + } + } else if (decl->as_class_template_parameter()) { + // A class template parameter. + CPPClassTemplateParameter *cparam = decl->as_class_template_parameter(); + if (cparam->_default_type != NULL) { + CPPDeclaration *decl = + cparam->_default_type->substitute_decl(subst, current_scope, + global_scope); + if (!(*cparam == *decl)) { + subst.insert(CPPDeclaration::SubstDecl::value_type + (cparam, decl)); + } + } + } + ++pfi; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::is_fully_specified +// Access: Public +// Description: This function returns true if all the parameters in +// the list are real expressions or classes, and not +// types yet to-be-determined or template parameter +// types. That is, this returns true for a normal +// template instantiation, and false for a template +// instantiation based on template parameters that have +// not yet been specified. +//////////////////////////////////////////////////////////////////// +bool CPPTemplateParameterList:: +is_fully_specified() const { + for (int i = 0; i < (int)_parameters.size(); i++) { + if (!_parameters[i]->is_fully_specified()) { + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::is_tbd +// Access: Public +// Description: Returns true if any type within the parameter list is +// a CPPTBDType and thus isn't fully determined right +// now. +//////////////////////////////////////////////////////////////////// +bool CPPTemplateParameterList:: +is_tbd() const { + for (int i = 0; i < (int)_parameters.size(); i++) { + CPPType *type = _parameters[i]->as_type(); + if (type != (CPPType *)NULL && + (type->is_tbd() || type->as_class_template_parameter() != NULL)) { + return true; + } + CPPExpression *expr = _parameters[i]->as_expression(); + if (expr != NULL && expr->is_tbd()) { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::Equivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPTemplateParameterList:: +operator == (const CPPTemplateParameterList &other) const { + if (_parameters.size() != other._parameters.size()) { + return false; + } + for (int i = 0; i < (int)_parameters.size(); i++) { + if (*_parameters[i] != *other._parameters[i]) { + return false; + } + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::Nonequivalence Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPTemplateParameterList:: +operator != (const CPPTemplateParameterList &other) const { + return !(*this == other); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPTemplateParameterList:: +operator < (const CPPTemplateParameterList &other) const { + if (_parameters.size() != other._parameters.size()) { + return _parameters.size() < other._parameters.size(); + } + for (int i = 0; i < (int)_parameters.size(); i++) { + if (*_parameters[i] != *other._parameters[i]) { + return *_parameters[i] < *other._parameters[i]; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::substitute_decl +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTemplateParameterList *CPPTemplateParameterList:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + CPPTemplateParameterList *rep = new CPPTemplateParameterList(*this); + + bool anything_changed = false; + for (int i = 0; i < (int)rep->_parameters.size(); i++) { + rep->_parameters[i] = + _parameters[i]->substitute_decl(subst, current_scope, global_scope); + if (rep->_parameters[i] != _parameters[i]) { + anything_changed = true; + } + } + + if (!anything_changed) { + delete rep; + rep = this; + } + + return rep; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTemplateParameterList:: +output(ostream &out, CPPScope *scope) const { + if (!_parameters.empty()) { + Parameters::const_iterator pi = _parameters.begin(); + (*pi)->output(out, 0, scope, false); + + ++pi; + while (pi != _parameters.end()) { + out << ", "; + (*pi)->output(out, 0, scope, false); + ++pi; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateParameterList::write_formal +// Access: Public +// Description: Writes the list as a set of formal parameters for a +// template scope. Includes the keyword "template" and +// the angle brackets, as well as the trailing newline. +//////////////////////////////////////////////////////////////////// +void CPPTemplateParameterList:: +write_formal(ostream &out, CPPScope *scope) const { + out << "template<"; + if (!_parameters.empty()) { + Parameters::const_iterator pi = _parameters.begin(); + (*pi)->output(out, 0, scope, true); + + ++pi; + while (pi != _parameters.end()) { + out << ", "; + (*pi)->output(out, 0, scope, true); + ++pi; + } + } + out << ">\n"; +} diff --git a/dtool/src/cppparser/cppTemplateParameterList.h b/dtool/src/cppparser/cppTemplateParameterList.h new file mode 100644 index 0000000000..8f9820aa41 --- /dev/null +++ b/dtool/src/cppparser/cppTemplateParameterList.h @@ -0,0 +1,72 @@ +// Filename: cppTemplateParameterList.h +// Created by: drose (28Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTEMPLATEPARAMETERLIST_H +#define CPPTEMPLATEPARAMETERLIST_H + +#include + +#include "cppDeclaration.h" + +#include +#include + +class CPPScope; + +/////////////////////////////////////////////////////////////////// +// Class : CPPTemplateParameterList +// Description : This class serves to store the parameter list for a +// template function or class, both for the formal +// parameter list (given when the template is defined) +// and for the actual parameter list (given when the +// template is instantiated). +//////////////////////////////////////////////////////////////////// +class CPPTemplateParameterList { +public: + CPPTemplateParameterList(); + + string get_string() const; + void build_subst_decl(const CPPTemplateParameterList &formal_params, + CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) const; + + bool is_fully_specified() const; + bool is_tbd() const; + + bool operator == (const CPPTemplateParameterList &other) const; + bool operator != (const CPPTemplateParameterList &other) const; + bool operator < (const CPPTemplateParameterList &other) const; + + CPPTemplateParameterList *substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + void output(ostream &out, CPPScope *scope) const; + void write_formal(ostream &out, CPPScope *scope) const; + + typedef vector Parameters; + Parameters _parameters; +}; + +inline ostream & +operator << (ostream &out, const CPPTemplateParameterList &plist) { + plist.output(out, (CPPScope *)NULL); + return out; +} + + +// This is an STL function object used to uniquely order +// CPPTemplateParameterList pointers. +class CPPTPLCompare { +public: + bool operator () (const CPPTemplateParameterList *a, + const CPPTemplateParameterList *b) const { + return (*a) < (*b); + } +}; + +#endif + + diff --git a/dtool/src/cppparser/cppTemplateScope.cxx b/dtool/src/cppparser/cppTemplateScope.cxx new file mode 100644 index 0000000000..c62fd34934 --- /dev/null +++ b/dtool/src/cppparser/cppTemplateScope.cxx @@ -0,0 +1,177 @@ +// Filename: cppTemplateScope.C +// Created by: drose (28Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppTemplateScope.h" +#include "cppExtensionType.h" +#include "cppClassTemplateParameter.h" +#include "cppIdentifier.h" +#include "cppTypedef.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTemplateScope:: +CPPTemplateScope(CPPScope *parent_scope) : + CPPScope(parent_scope, CPPNameComponent("template"), V_public) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::add_declaration +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTemplateScope:: +add_declaration(CPPDeclaration *decl, CPPScope *global_scope, + CPPPreprocessor *preprocessor, + const cppyyltype &pos) { + decl->_template_scope = this; + assert(_parent_scope != NULL); + _parent_scope->add_declaration(decl, global_scope, preprocessor, pos); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::add_enum_value +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTemplateScope:: +add_enum_value(CPPInstance *inst) { + inst->_template_scope = this; + assert(_parent_scope != NULL); + _parent_scope->add_enum_value(inst); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::define_extension_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTemplateScope:: +define_extension_type(CPPExtensionType *type) { + type->_template_scope = this; + assert(_parent_scope != NULL); + _parent_scope->define_extension_type(type); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::define_namespace +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTemplateScope:: +define_namespace(CPPNamespace *scope) { + assert(_parent_scope != NULL); + _parent_scope->define_namespace(scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::add_using +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTemplateScope:: +add_using(CPPUsing *using_decl, CPPScope *global_scope, + CPPPreprocessor *error_sink) { + assert(_parent_scope != NULL); + _parent_scope->add_using(using_decl, global_scope, error_sink); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::add_template_parameter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTemplateScope:: +add_template_parameter(CPPDeclaration *param) { + _parameters._parameters.push_back(param); + CPPClassTemplateParameter *cl = param->as_class_template_parameter(); + if (cl != NULL) { + // Create an implicit typedef for this class parameter. + string name = cl->_ident->get_local_name(); + _typedefs[name] = new CPPTypedef(new CPPInstance(cl, cl->_ident), false); + } + CPPInstance *inst = param->as_instance(); + if (inst != NULL) { + // Register the variable for this value parameter. + string name = inst->get_local_name(); + if (!name.empty()) { + _variables[name] = inst; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::is_fully_specified +// Access: Public, Virtual +// Description: Returns true if this declaration is an actual, +// factual declaration, or false if some part of the +// declaration depends on a template parameter which has +// not yet been instantiated. +//////////////////////////////////////////////////////////////////// +bool CPPTemplateScope:: +is_fully_specified() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::get_simple_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPTemplateScope:: +get_simple_name() const { + assert(_parent_scope != NULL); + return _parent_scope->get_simple_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::get_local_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPTemplateScope:: +get_local_name(CPPScope *scope) const { + assert(_parent_scope != NULL); + return _parent_scope->get_local_name(scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::get_fully_scoped_name +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +string CPPTemplateScope:: +get_fully_scoped_name() const { + assert(_parent_scope != NULL); + return _parent_scope->get_fully_scoped_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTemplateScope:: +output(ostream &out, CPPScope *scope) const { + CPPScope::output(out, scope); + out << "< "; + _parameters.output(out, scope); + out << " >"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTemplateScope::as_template_scope +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTemplateScope *CPPTemplateScope:: +as_template_scope() { + return this; +} diff --git a/dtool/src/cppparser/cppTemplateScope.h b/dtool/src/cppparser/cppTemplateScope.h new file mode 100644 index 0000000000..9a91e53c56 --- /dev/null +++ b/dtool/src/cppparser/cppTemplateScope.h @@ -0,0 +1,49 @@ +// Filename: cppTemplateScope.h +// Created by: drose (28Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTEMPLATESCOPE_H +#define CPPTEMPLATESCOPE_H + +#include + +#include "cppScope.h" +#include "cppTemplateParameterList.h" + +/////////////////////////////////////////////////////////////////// +// Class : CPPTemplateScope +// Description : This is an implicit scope that is created following +// the appearance of a "template" or +// some such line in a C++ file. It simply defines the +// template parameters. +//////////////////////////////////////////////////////////////////// +class CPPTemplateScope : public CPPScope { +public: + CPPTemplateScope(CPPScope *parent_scope); + + void add_template_parameter(CPPDeclaration *param); + + virtual void add_declaration(CPPDeclaration *decl, CPPScope *global_scope, + CPPPreprocessor *preprocessor, + const cppyyltype &pos); + virtual void add_enum_value(CPPInstance *inst); + virtual void define_extension_type(CPPExtensionType *type); + virtual void define_namespace(CPPNamespace *scope); + virtual void add_using(CPPUsing *using_decl, CPPScope *global_scope, + CPPPreprocessor *error_sink = NULL); + + virtual bool is_fully_specified() const; + + virtual string get_simple_name() const; + virtual string get_local_name(CPPScope *scope = NULL) const; + virtual string get_fully_scoped_name() const; + + virtual void output(ostream &out, CPPScope *scope) const; + + virtual CPPTemplateScope *as_template_scope(); + + CPPTemplateParameterList _parameters; +}; + +#endif diff --git a/dtool/src/cppparser/cppToken.cxx b/dtool/src/cppparser/cppToken.cxx new file mode 100644 index 0000000000..92d5825417 --- /dev/null +++ b/dtool/src/cppparser/cppToken.cxx @@ -0,0 +1,465 @@ +// Filename: cppToken.C +// Created by: drose (22Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppToken.h" +#include "cppIdentifier.h" +#include "cppBison.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: CPPToken::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken:: +CPPToken(int token, int line_number, int col_number, + const CPPFile &file, const string &str, + const YYSTYPE &lval) : + _token(token), _lval(lval) +{ + _lval.str = str; + _lloc.timestamp = 0; + _lloc.first_line = line_number; + _lloc.first_column = col_number; + _lloc.last_line = line_number; + _lloc.last_column = col_number; + _lloc.text = NULL; + _lloc.file = file; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPToken::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPToken:: +CPPToken(const CPPToken ©) : + _token(copy._token), + _lloc(copy._lloc) +{ + _lval.str = copy._lval.str; + _lval.u = copy._lval.u; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPToken::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPToken:: +operator = (const CPPToken ©) { + _token = copy._token; + _lval.str = copy._lval.str; + _lval.u = copy._lval.u; + _lloc = copy._lloc; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPToken::eof +// Access: Public, Static +// Description: A named constructor for the token returned when the +// end of file has been reached. +//////////////////////////////////////////////////////////////////// +CPPToken CPPToken:: +eof() { + return CPPToken(0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPToken::is_eof +// Access: Public +// Description: Returns true if this is the EOF token. +//////////////////////////////////////////////////////////////////// +bool CPPToken:: +is_eof() const { + return _token == 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPToken::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPToken:: +output(ostream &out) const { + switch (_token) { + case REAL: + out << "REAL " << _lval.u.real; + break; + + case INTEGER: + out << "INTEGER " << _lval.u.integer; + break; + + case CHAR: + out << "CHAR " << _lval.u.integer << " = " << _lval.str; + break; + + case STRING: + out << "STRING " << _lval.str; + break; + + case SIMPLE_IDENTIFIER: + out << "SIMPLE_IDENTIFIER " << _lval.str; + break; + + case IDENTIFIER: + out << "IDENTIFIER " << *_lval.u.identifier; + break; + + case TYPENAME_IDENTIFIER: + out << "TYPENAME_IDENTIFIER " << *_lval.u.identifier; + break; + + case SCOPING: + out << "SCOPING " << *_lval.u.identifier << "::"; + break; + + case TYPEDEFNAME: + out << "TYPEDEFNAME " << _lval.str; + break; + + case ELLIPSIS: + out << "ELLIPSIS"; + break; + + case OROR: + out << "OROR"; + break; + + case ANDAND: + out << "ANDAND"; + break; + + case EQCOMPARE: + out << "EQCOMPARE"; + break; + + case NECOMPARE: + out << "NECOMPARE"; + break; + + case LECOMPARE: + out << "LECOMPARE"; + break; + + case GECOMPARE: + out << "GECOMPARE"; + break; + + case LSHIFT: + out << "LSHIFT"; + break; + + case RSHIFT: + out << "RSHIFT"; + break; + + case POINTSAT_STAR: + out << "POINTSAT_STAR"; + break; + + case DOT_STAR: + out << "DOT_STAR"; + break; + + case UNARY_NOT: + out << "UNARY_NOT"; + break; + + case UNARY_MINUS: + out << "UNARY_MINUS"; + break; + + case UNARY_NEGATE: + out << "UNARY_NEGATE"; + break; + + case UNARY_STAR: + out << "UNARY_STAR"; + break; + + case UNARY_REF: + out << "UNARY_REF"; + break; + + case POINTSAT: + out << "POINTSAT"; + break; + + case SCOPE: + out << "SCOPE"; + break; + + case PLUSPLUS: + out << "PLUSPLUS"; + break; + + case MINUSMINUS: + out << "MINUSMINUS"; + break; + + case TIMESEQUAL: + out << "TIMESEQUAL"; + break; + + case DIVIDEEQUAL: + out << "DIVIDEEQUAL"; + break; + + case MODEQUAL: + out << "MODEQUAL"; + break; + + case PLUSEQUAL: + out << "PLUSEQUAL"; + break; + + case MINUSEQUAL: + out << "MINUSEQUAL"; + break; + + case OREQUAL: + out << "OREQUAL"; + break; + + case ANDEQUAL: + out << "ANDEQUAL"; + break; + + case LSHIFTEQUAL: + out << "LSHIFTEQUAL"; + break; + + case RSHIFTEQUAL: + out << "RSHIFTEQUAL"; + break; + + case TOKENPASTE: + out << "TOKENPASTE"; + break; + + case KW_BOOL: + out << "KW_BOOL"; + break; + + case KW_CATCH: + out << "KW_CATCH"; + break; + + case KW_CHAR: + out << "KW_CHAR"; + break; + + case KW_CLASS: + out << "KW_CLASS"; + break; + + case KW_CONST: + out << "KW_CONST"; + break; + + case KW_DELETE: + out << "KW_DELETE"; + break; + + case KW_DOUBLE: + out << "KW_DOUBLE"; + break; + + case KW_DYNAMIC_CAST: + out << "KW_DYNAMIC_CAST"; + break; + + case KW_ELSE: + out << "KW_ELSE"; + break; + + case KW_ENUM: + out << "KW_ENUM"; + break; + + case KW_EXPLICIT: + out << "KW_EXPLICIT"; + break; + + case KW_EXTERN: + out << "KW_EXTERN"; + break; + + case KW_FALSE: + out << "KW_FALSE"; + break; + + case KW_FLOAT: + out << "KW_FLOAT"; + break; + + case KW_FRIEND: + out << "KW_FRIEND"; + break; + + case KW_FOR: + out << "KW_FOR"; + break; + + case KW_GOTO: + out << "KW_GOTO"; + break; + + case KW_IF: + out << "KW_IF"; + break; + + case KW_INLINE: + out << "KW_INLINE"; + break; + + case KW_INT: + out << "KW_INT"; + break; + + case KW_LONG: + out << "KW_LONG"; + break; + + case KW_MUTABLE: + out << "KW_MUTABLE"; + break; + + case KW_NAMESPACE: + out << "KW_NAMESPACE"; + break; + + case KW_NEW: + out << "KW_NEW"; + break; + + case KW_OPERATOR: + if (_lval.u.identifier != NULL) { + out << *_lval.u.identifier << "::"; + } + out << "KW_OPERATOR"; + break; + + case KW_PRIVATE: + out << "KW_PRIVATE"; + break; + + case KW_PROTECTED: + out << "KW_PROTECTED"; + break; + + case KW_PUBLIC: + out << "KW_PUBLIC"; + break; + + case KW_REGISTER: + out << "KW_REGISTER"; + break; + + case KW_RETURN: + out << "KW_RETURN"; + break; + + case KW_SHORT: + out << "KW_SHORT"; + break; + + case KW_SIGNED: + out << "KW_SIGNED"; + break; + + case KW_SIZEOF: + out << "KW_SIZEOF"; + break; + + case KW_STATIC: + out << "KW_STATIC"; + break; + + case KW_STATIC_CAST: + out << "KW_STATIC_CAST"; + break; + + case KW_STRUCT: + out << "KW_STRUCT"; + break; + + case KW_TEMPLATE: + out << "KW_TEMPLATE"; + break; + + case KW_THROW: + out << "KW_THROW"; + break; + + case KW_TRUE: + out << "KW_TRUE"; + break; + + case KW_TRY: + out << "KW_TRY"; + break; + + case KW_TYPEDEF: + out << "KW_TYPEDEF"; + break; + + case KW_TYPENAME: + out << "KW_TYPENAME"; + break; + + case KW_USING: + out << "KW_USING"; + break; + + case KW_UNION: + out << "KW_UNION"; + break; + + case KW_UNSIGNED: + out << "KW_UNSIGNED"; + break; + + case KW_VIRTUAL: + out << "KW_VIRTUAL"; + break; + + case KW_VOID: + out << "KW_VOID"; + break; + + case KW_VOLATILE: + out << "KW_VOLATILE"; + break; + + case KW_WHILE: + out << "KW_WHILE"; + break; + + case START_CPP: + out << "START_CPP"; + break; + + case START_CONST_EXPR: + out << "START_CONST_EXPR"; + break; + + case START_TYPE: + out << "START_TYPE"; + break; + + default: + if (_token < 128 && isprint(_token)) { + out << "'" << (char)_token << "'"; + } else { + out << "token " << _token << "\n"; + } + } +} diff --git a/dtool/src/cppparser/cppToken.h b/dtool/src/cppparser/cppToken.h new file mode 100644 index 0000000000..5d5750b413 --- /dev/null +++ b/dtool/src/cppparser/cppToken.h @@ -0,0 +1,42 @@ +// Filename: cppToken.h +// Created by: drose (22Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTOKEN_H +#define CPPTOKEN_H + +#include + +#include "cppBisonDefs.h" + +/////////////////////////////////////////////////////////////////// +// Class : CPPToken +// Description : +//////////////////////////////////////////////////////////////////// +class CPPToken { +public: + CPPToken(int token, int line_number = 0, int col_number = 0, + const CPPFile &file = CPPFile(""), + const string &str = string(), + const YYSTYPE &lval = YYSTYPE()); + CPPToken(const CPPToken ©); + void operator = (const CPPToken ©); + + static CPPToken eof(); + bool is_eof() const; + + void output(ostream &out) const; + + int _token; + YYSTYPE _lval; + YYLTYPE _lloc; +}; + +inline ostream &operator << (ostream &out, const CPPToken &token) { + token.output(out); + return out; +} + + +#endif diff --git a/dtool/src/cppparser/cppType.cxx b/dtool/src/cppparser/cppType.cxx new file mode 100644 index 0000000000..93478c7c34 --- /dev/null +++ b/dtool/src/cppparser/cppType.cxx @@ -0,0 +1,284 @@ +// Filename: cppType.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppType.h" +#include "cppTypedef.h" + +CPPType::Types CPPType::_types; +CPPType::PreferredNames CPPType::_preferred_names; + +bool CPPTypeCompare:: +operator () (CPPType *a, CPPType *b) const { + return (*a) < (*b); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPType:: +CPPType(const CPPFile &file) : + CPPDeclaration(file) +{ + _declaration = (CPPTypeDeclaration *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPType:: +resolve_type(CPPScope *, CPPScope *) { + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPType:: +is_tbd() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::has_typedef_name +// Access: Public +// Description: Returns true if the type has even been typedef'ed and +// therefore has a simple name available to stand for +// it. Extension types are all implicitly typedef'ed on +// declaration. +//////////////////////////////////////////////////////////////////// +bool CPPType:: +has_typedef_name() const { + return !_typedefs.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::get_typedef_name +// Access: Public +// Description: Returns a string that can be used to name the type, +// if has_typedef_name() returned true. This will be +// the first typedef name applied to the type. +//////////////////////////////////////////////////////////////////// +string CPPType:: +get_typedef_name(CPPScope *scope) const { + if (_typedefs.empty()) { + return string(); + } else { + return _typedefs.front()->get_local_name(scope); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::get_simple_name +// Access: Public, Virtual +// Description: Returns a fundametal one-word name for the type. +// This name will not include any scoping operators or +// template parameters, so it may not be a compilable +// reference to the type. +//////////////////////////////////////////////////////////////////// +string CPPType:: +get_simple_name() const { + return get_local_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::get_local_name +// Access: Public, Virtual +// Description: Returns the compilable, correct name for this type +// within the indicated scope. If the scope is NULL, +// within the scope the type is declared in. +//////////////////////////////////////////////////////////////////// +string CPPType:: +get_local_name(CPPScope *scope) const { + ostringstream ostrm; + output(ostrm, 0, scope, false); + return ostrm.str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::get_fully_scoped_name +// Access: Public, Virtual +// Description: Returns the compilable, correct name for the type, +// with completely explicit scoping. +//////////////////////////////////////////////////////////////////// +string CPPType:: +get_fully_scoped_name() const { + return get_local_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::get_preferred_name +// Access: Public, Virtual +// Description: Returns the best name to use for the type from a +// programmer's point of view. This will typically be a +// typedef name if one is available, or the full C++ +// name if it is not. The typedef may or may not be +// visible within the current scope, so this type name +// may not be compilable. +//////////////////////////////////////////////////////////////////// +string CPPType:: +get_preferred_name() const { + string preferred_name = get_preferred_name_for(this); + if (!preferred_name.empty()) { + return preferred_name; + } + return get_local_name(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::is_incomplete +// Access: Public, Virtual +// Description: Returns true if the type has not yet been fully +// specified, false if it has. +//////////////////////////////////////////////////////////////////// +bool CPPType:: +is_incomplete() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::is_equivalent +// Access: Public, Virtual +// Description: This is a little more forgiving than is_equal(): it +// returns true if the types appear to be referring to +// the same thing, even if they may have different +// pointers or somewhat different definitions. It's +// useful for parameter matching, etc. +//////////////////////////////////////////////////////////////////// +bool CPPType:: +is_equivalent(const CPPType &other) const { + if (get_subtype() != other.get_subtype()) { + return false; + } + return is_equal(&other); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::output_instance +// Access: Public, Virtual +// Description: Formats a C++-looking line that defines an instance +// of the given type, with the indicated name. In most +// cases this will be "type name", but some types have +// special exceptions. +//////////////////////////////////////////////////////////////////// +void CPPType:: +output_instance(ostream &out, const string &name, CPPScope *scope) const { + output_instance(out, 0, scope, false, "", name); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::output_instance +// Access: Public, Virtual +// Description: Formats a C++-looking line that defines an instance +// of the given type, with the indicated name. In most +// cases this will be "type name", but some types have +// special exceptions. +//////////////////////////////////////////////////////////////////// +void CPPType:: +output_instance(ostream &out, int indent_level, CPPScope *scope, + bool complete, const string &prename, + const string &name) const { + output(out, indent_level, scope, complete); + out << " " << prename << name; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::as_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPType *CPPType:: +as_type() { + return this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::new_type +// Access: Public, Static +// Description: This should be called whenever a new CPPType object +// is created. It will uniquify the type pointers by +// checking to see if some equivalent CPPType object has +// previously been created; if it has, it returns the +// old object and deletes the new one. Otherwise, it +// stores the new one and returns it. +//////////////////////////////////////////////////////////////////// +CPPType *CPPType:: +new_type(CPPType *type) { + pair result = _types.insert(type); + if (result.second) { + // The insertion has taken place; thus, this is the first time + // this type has been declared. + assert(*result.first == type); + return type; + } + + // The insertion has not taken place; thus, there was previously + // another equivalent type declared. + if (*result.first != type) { + // *** Something wrong here. Deleting this should always be safe; + // however, it's not. Thus, someone failed to call new_type() on + // a type pointer before saving it somewhere. Fix me soon. **** + + //delete type; + } + return *result.first; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::record_preferred_name_for +// Access: Public, Static +// Description: Records a global typedef name associated with the +// indicated Type. This will be taken as the +// "preferred" name for this class, should anyone ask. +//////////////////////////////////////////////////////////////////// +void CPPType:: +record_preferred_name_for(const CPPType *type, const string &name) { + if (!name.empty()) { + string tname = type->get_fully_scoped_name(); + if (!tname.empty()) { + _preferred_names.insert(PreferredNames::value_type(tname, name)); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPType::get_preferred_name_for +// Access: Public, Static +// Description: Returns the previously-stored "preferred" name +// associated with the type, if any, or empty string if +// no name is associated. +//////////////////////////////////////////////////////////////////// +string CPPType:: +get_preferred_name_for(const CPPType *type) { + // We do a lookup based on the type's name, instead of its pointer, + // so we can resolve different expansions of the same type. + string tname = type->get_fully_scoped_name(); + + if (!tname.empty()) { + PreferredNames::const_iterator pi; + pi = _preferred_names.find(tname); + if (pi != _preferred_names.end()) { + return (*pi).second; + } + } + + return string(); +} diff --git a/dtool/src/cppparser/cppType.h b/dtool/src/cppparser/cppType.h new file mode 100644 index 0000000000..e492faba88 --- /dev/null +++ b/dtool/src/cppparser/cppType.h @@ -0,0 +1,79 @@ +// Filename: cppType.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTYPE_H +#define CPPTYPE_H + +#include + +#include "cppDeclaration.h" + +#include + +class CPPType; +class CPPTypedef; +class CPPTypeDeclaration; + + +// This is an STL function object used to uniquely order CPPType +// pointers. +class CPPTypeCompare { +public: + bool operator () (CPPType *a, CPPType *b) const; +}; + +/////////////////////////////////////////////////////////////////// +// Class : CPPType +// Description : +//////////////////////////////////////////////////////////////////// +class CPPType : public CPPDeclaration { +public: + typedef vector Typedefs; + Typedefs _typedefs; + + CPPType(const CPPFile &file); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_tbd() const; + + bool has_typedef_name() const; + string get_typedef_name(CPPScope *scope = NULL) const; + + virtual string get_simple_name() const; + virtual string get_local_name(CPPScope *scope = NULL) const; + virtual string get_fully_scoped_name() const; + virtual string get_preferred_name() const; + + virtual bool is_incomplete() const; + virtual bool is_equivalent(const CPPType &other) const; + + void output_instance(ostream &out, const string &name, + CPPScope *scope) const; + virtual void output_instance(ostream &out, int indent_level, + CPPScope *scope, + bool complete, const string &prename, + const string &name) const; + + virtual CPPType *as_type(); + + + static CPPType *new_type(CPPType *type); + + static void record_preferred_name_for(const CPPType *type, const string &name); + static string get_preferred_name_for(const CPPType *type); + + CPPTypeDeclaration *_declaration; + +protected: + typedef set Types; + static Types _types; + + typedef map PreferredNames; + static PreferredNames _preferred_names; +}; + +#endif diff --git a/dtool/src/cppparser/cppTypeDeclaration.cxx b/dtool/src/cppparser/cppTypeDeclaration.cxx new file mode 100644 index 0000000000..8651c4f8b4 --- /dev/null +++ b/dtool/src/cppparser/cppTypeDeclaration.cxx @@ -0,0 +1,71 @@ +// Filename: cppTypeDeclaration.C +// Created by: drose (14Aug00) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppTypeDeclaration.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeDeclaration::Constructor +// Access: Public +// Description: Constructs a new CPPTypeDeclaration object for the +// given type. +//////////////////////////////////////////////////////////////////// +CPPTypeDeclaration:: +CPPTypeDeclaration(CPPType *type) : + CPPInstance(type, (CPPIdentifier *)NULL) +{ + assert(_type != NULL); + if (_type->_declaration == (CPPTypeDeclaration *)NULL) { + _type->_declaration = this; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeDeclaration::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPTypeDeclaration:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + CPPDeclaration *decl = + CPPInstance::substitute_decl(subst, current_scope, global_scope); + assert(decl != NULL); + if (decl->as_type_declaration()) { + return decl; + } + assert(decl->as_instance() != NULL); + return new CPPTypeDeclaration(decl->as_instance()->_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeDeclaration::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTypeDeclaration:: +output(ostream &out, int indent_level, CPPScope *scope, bool) const { + _type->output(out, indent_level, scope, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeDeclaration::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPTypeDeclaration:: +get_subtype() const { + return ST_type_declaration; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeDeclaration::as_type_declaration +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypeDeclaration *CPPTypeDeclaration:: +as_type_declaration() { + return this; +} diff --git a/dtool/src/cppparser/cppTypeDeclaration.h b/dtool/src/cppparser/cppTypeDeclaration.h new file mode 100644 index 0000000000..8185c8e3b7 --- /dev/null +++ b/dtool/src/cppparser/cppTypeDeclaration.h @@ -0,0 +1,36 @@ +// Filename: cppTypeDeclaration.h +// Created by: drose (14Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTYPEDECLARATION_H +#define CPPTYPEDECLARATION_H + +#include + +#include "cppInstance.h" + +/////////////////////////////////////////////////////////////////// +// Class : CPPTypeDeclaration +// Description : A CPPTypeDeclaration is a special declaration that +// represents the top-level declaration of a type in a +// source file. Typically this is the first appearance +// of the type. +//////////////////////////////////////////////////////////////////// +class CPPTypeDeclaration : public CPPInstance { +public: + CPPTypeDeclaration(CPPType *type); + + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPTypeDeclaration *as_type_declaration(); +}; + +#endif + diff --git a/dtool/src/cppparser/cppTypeParser.cxx b/dtool/src/cppparser/cppTypeParser.cxx new file mode 100644 index 0000000000..d2829b068c --- /dev/null +++ b/dtool/src/cppparser/cppTypeParser.cxx @@ -0,0 +1,80 @@ +// Filename: cppTypeParser.C +// Created by: drose (14Dec99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppTypeParser.h" +#include "cppType.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeParser::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypeParser:: +CPPTypeParser(CPPScope *current_scope, CPPScope *global_scope) : + _current_scope(current_scope), + _global_scope(global_scope) +{ + _type = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeParser::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypeParser:: +~CPPTypeParser() { +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeParser::parse_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPTypeParser:: +parse_type(const string &type) { + if (!init_type(type)) { + cerr << "Unable to parse type\n"; + return false; + } + + _type = ::parse_type(this, _current_scope, _global_scope); + + return get_error_count() == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeParser::parse_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool CPPTypeParser:: +parse_type(const string &type, const CPPPreprocessor &filepos) { + if (!init_type(type)) { + cerr << "Unable to parse type\n"; + return false; + } + + copy_filepos(filepos); + + _type = ::parse_type(this, _current_scope, _global_scope); + + return get_error_count() == 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeParser::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTypeParser:: +output(ostream &out) const { + if (_type == NULL) { + out << "(null type)"; + } else { + out << *_type; + } +} diff --git a/dtool/src/cppparser/cppTypeParser.h b/dtool/src/cppparser/cppTypeParser.h new file mode 100644 index 0000000000..336382b07c --- /dev/null +++ b/dtool/src/cppparser/cppTypeParser.h @@ -0,0 +1,43 @@ +// Filename: cppTypeParser.h +// Created by: drose (14Dec99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTYPEPARSER_H +#define CPPTYPEPARSER_H + +#include + +#include "cppPreprocessor.h" + +class CPPType; +class CPPScope; + +/////////////////////////////////////////////////////////////////// +// Class : CPPTypeParser +// Description : +//////////////////////////////////////////////////////////////////// +class CPPTypeParser : public CPPPreprocessor { +public: + CPPTypeParser(CPPScope *current_scope, CPPScope *global_scope); + ~CPPTypeParser(); + + bool parse_type(const string &type); + bool parse_type(const string &type, const CPPPreprocessor &filepos); + + void output(ostream &out) const; + + CPPScope *_current_scope; + CPPScope *_global_scope; + CPPType *_type; +}; + +inline ostream & +operator << (ostream &out, const CPPTypeParser &ep) { + ep.output(out); + return out; +} + +#endif + + diff --git a/dtool/src/cppparser/cppTypeProxy.cxx b/dtool/src/cppparser/cppTypeProxy.cxx new file mode 100644 index 0000000000..861ddb851a --- /dev/null +++ b/dtool/src/cppparser/cppTypeProxy.cxx @@ -0,0 +1,360 @@ +// Filename: cppTypeProxy.C +// Created by: drose (07Dec99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppTypeProxy.h" +#include "cppFile.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypeProxy:: +CPPTypeProxy() : + CPPType(CPPFile()) +{ + _actual_type = (CPPType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::resolve_type +// Access: Public, Virtual +// Description: If this CPPType object is a forward reference or +// other nonspecified reference to a type that might now +// be known a real type, returns the real type. +// Otherwise returns the type itself. +//////////////////////////////////////////////////////////////////// +CPPType *CPPTypeProxy:: +resolve_type(CPPScope *, CPPScope *) { + if (_actual_type == (CPPType *)NULL) { + return this; + } + return _actual_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::is_tbd +// Access: Public, Virtual +// Description: Returns true if the type, or any nested type within +// the type, is a CPPTBDType and thus isn't fully +// determined right now. In this case, calling +// resolve_type() may or may not resolve the type. +//////////////////////////////////////////////////////////////////// +bool CPPTypeProxy:: +is_tbd() const { + if (_actual_type == (CPPType *)NULL) { + return false; + } + return _actual_type->is_tbd(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::has_typedef_name +// Access: Public +// Description: Returns true if the type has even been typedef'ed and +// therefore has a simple name available to stand for +// it. Extension types are all implicitly typedef'ed on +// declaration. +//////////////////////////////////////////////////////////////////// +bool CPPTypeProxy:: +has_typedef_name() const { + if (_actual_type == (CPPType *)NULL) { + return false; + } + return _actual_type->has_typedef_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::get_typedef_name +// Access: Public +// Description: Returns a string that can be used to name the type, +// if has_typedef_name() returned true. This will be +// the first typedef name applied to the type. +//////////////////////////////////////////////////////////////////// +string CPPTypeProxy:: +get_typedef_name(CPPScope *) const { + if (_actual_type == (CPPType *)NULL) { + return string(); + } + return _actual_type->get_typedef_name(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::get_simple_name +// Access: Public, Virtual +// Description: Returns a fundametal one-word name for the type. +// This name will not include any scoping operators or +// template parameters, so it may not be a compilable +// reference to the type. +//////////////////////////////////////////////////////////////////// +string CPPTypeProxy:: +get_simple_name() const { + if (_actual_type == (CPPType *)NULL) { + return "unknown"; + } + return _actual_type->get_simple_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::get_local_name +// Access: Public, Virtual +// Description: Returns the compilable, correct name for this type +// within the indicated scope. If the scope is NULL, +// within the scope the type is declared in. +//////////////////////////////////////////////////////////////////// +string CPPTypeProxy:: +get_local_name(CPPScope *scope) const { + if (_actual_type == (CPPType *)NULL) { + return "unknown"; + } + return _actual_type->get_local_name(scope); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::get_fully_scoped_name +// Access: Public, Virtual +// Description: Returns the compilable, correct name for the type, +// with completely explicit scoping. +//////////////////////////////////////////////////////////////////// +string CPPTypeProxy:: +get_fully_scoped_name() const { + if (_actual_type == (CPPType *)NULL) { + return "unknown"; + } + return _actual_type->get_fully_scoped_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::get_preferred_name +// Access: Public, Virtual +// Description: Returns the best name to use for the type from a +// programmer's point of view. This will typically be a +// typedef name if one is available, or the full C++ +// name if it is not. The typedef may or may not be +// visible within the current scope, so this type name +// may not be compilable. +//////////////////////////////////////////////////////////////////// +string CPPTypeProxy:: +get_preferred_name() const { + if (_actual_type == (CPPType *)NULL) { + return "unknown"; + } + return _actual_type->get_preferred_name(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::is_incomplete +// Access: Public, Virtual +// Description: Returns true if the type has not yet been fully +// specified, false if it has. +//////////////////////////////////////////////////////////////////// +bool CPPTypeProxy:: +is_incomplete() const { + if (_actual_type == (CPPType *)NULL) { + return true; + } + return _actual_type->is_incomplete(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::output_instance +// Access: Public, Virtual +// Description: Formats a C++-looking line that defines an instance +// of the given type, with the indicated name. In most +// cases this will be "type name", but some types have +// special exceptions. +//////////////////////////////////////////////////////////////////// +void CPPTypeProxy:: +output_instance(ostream &out, int indent_level, CPPScope *scope, + bool complete, const string &prename, + const string &name) const { + if (_actual_type == (CPPType *)NULL) { + out << "unknown " << prename << name; + return; + } + _actual_type->output_instance(out, indent_level, scope, complete, + prename, name); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTypeProxy:: +output(ostream &out, int indent_level, CPPScope *scope, bool complete) const { + if (_actual_type == (CPPType *)NULL) { + out << "unknown"; + return; + } + _actual_type->output(out, indent_level, scope, complete); +} + + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPTypeProxy:: +get_subtype() const { + return ST_type_proxy; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPType *CPPTypeProxy:: +as_type() { + if (_actual_type == (CPPType *)NULL) { + return this; + } + return _actual_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_simple_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPSimpleType *CPPTypeProxy:: +as_simple_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPSimpleType *)NULL; + } + return _actual_type->as_simple_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_pointer_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPPointerType *CPPTypeProxy:: +as_pointer_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPPointerType *)NULL; + } + return _actual_type->as_pointer_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_reference_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPReferenceType *CPPTypeProxy:: +as_reference_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPReferenceType *)NULL; + } + return _actual_type->as_reference_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_array_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPArrayType *CPPTypeProxy:: +as_array_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPArrayType *)NULL; + } + return _actual_type->as_array_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_const_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPConstType *CPPTypeProxy:: +as_const_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPConstType *)NULL; + } + return _actual_type->as_const_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_function_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPFunctionType *CPPTypeProxy:: +as_function_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPFunctionType *)NULL; + } + return _actual_type->as_function_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_extension_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPExtensionType *CPPTypeProxy:: +as_extension_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPExtensionType *)NULL; + } + return _actual_type->as_extension_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_struct_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPStructType *CPPTypeProxy:: +as_struct_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPStructType *)NULL; + } + return _actual_type->as_struct_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_enum_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPEnumType *CPPTypeProxy:: +as_enum_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPEnumType *)NULL; + } + return _actual_type->as_enum_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_tbd_type +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTBDType *CPPTypeProxy:: +as_tbd_type() { + if (_actual_type == (CPPType *)NULL) { + return (CPPTBDType *)NULL; + } + return _actual_type->as_tbd_type(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypeProxy::as_type_proxy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypeProxy *CPPTypeProxy:: +as_type_proxy() { + return this; +} + diff --git a/dtool/src/cppparser/cppTypeProxy.h b/dtool/src/cppparser/cppTypeProxy.h new file mode 100644 index 0000000000..da434f9cf7 --- /dev/null +++ b/dtool/src/cppparser/cppTypeProxy.h @@ -0,0 +1,65 @@ +// Filename: cppTypeProxy.h +// Created by: drose (07Dec99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTYPEPROXY_H +#define CPPTYPEPROXY_H + +#include + +#include "cppType.h" + + +/////////////////////////////////////////////////////////////////// +// Class : CPPTypeProxy +// Description : This is a special kind of type that is a placeholder +// for some type, currently unknown, that will be filled +// in later. It's used when a type that references +// itself must instantiate. +//////////////////////////////////////////////////////////////////// +class CPPTypeProxy : public CPPType { +public: + CPPTypeProxy(); + + virtual CPPType *resolve_type(CPPScope *current_scope, + CPPScope *global_scope); + + virtual bool is_tbd() const; + + bool has_typedef_name() const; + string get_typedef_name(CPPScope *scope = NULL) const; + + virtual string get_simple_name() const; + virtual string get_local_name(CPPScope *scope = NULL) const; + virtual string get_fully_scoped_name() const; + virtual string get_preferred_name() const; + + virtual bool is_incomplete() const; + + virtual void output_instance(ostream &out, int indent_level, + CPPScope *scope, + bool complete, const string &prename, + const string &name) const; + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + + virtual SubType get_subtype() const; + + virtual CPPType *as_type(); + virtual CPPSimpleType *as_simple_type(); + virtual CPPPointerType *as_pointer_type(); + virtual CPPReferenceType *as_reference_type(); + virtual CPPArrayType *as_array_type(); + virtual CPPConstType *as_const_type(); + virtual CPPFunctionType *as_function_type(); + virtual CPPExtensionType *as_extension_type(); + virtual CPPStructType *as_struct_type(); + virtual CPPEnumType *as_enum_type(); + virtual CPPTBDType *as_tbd_type(); + virtual CPPTypeProxy *as_type_proxy(); + + CPPType *_actual_type; +}; + +#endif diff --git a/dtool/src/cppparser/cppTypedef.cxx b/dtool/src/cppparser/cppTypedef.cxx new file mode 100644 index 0000000000..7323dd4476 --- /dev/null +++ b/dtool/src/cppparser/cppTypedef.cxx @@ -0,0 +1,82 @@ +// Filename: cppTypedef.C +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppTypedef.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedef::Constructor +// Access: Public +// Description: Constructs a new CPPTypedef object based on the +// indicated CPPInstance object. The CPPInstance is +// deallocated. +// +// If global is true, the typedef is defined at the +// global scope, and hence it's worth telling the type +// itself about. Otherwise, it's just a locally-scoped +// typedef. +//////////////////////////////////////////////////////////////////// +CPPTypedef:: +CPPTypedef(CPPInstance *inst, bool global) : CPPInstance(*inst) +{ + // Actually, we'll avoid deleting this for now. It causes problems + // for some reason to be determined later. + // delete inst; + + assert(_type != NULL); + if (global) { + _type->_typedefs.push_back(this); + CPPType::record_preferred_name_for(_type, inst->get_local_name()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedef::substitute_decl +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration *CPPTypedef:: +substitute_decl(CPPDeclaration::SubstDecl &subst, + CPPScope *current_scope, CPPScope *global_scope) { + CPPDeclaration *decl = + CPPInstance::substitute_decl(subst, current_scope, global_scope); + assert(decl != NULL); + if (decl->as_typedef()) { + return decl; + } + assert(decl->as_instance() != NULL); + return new CPPTypedef(new CPPInstance(*decl->as_instance()), false); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedef::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPTypedef:: +output(ostream &out, int indent_level, CPPScope *scope, bool) const { + out << "typedef "; + CPPInstance::output(out, indent_level, scope, false); +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedef::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPTypedef:: +get_subtype() const { + return ST_typedef; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPTypedef::as_typedef +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPTypedef *CPPTypedef:: +as_typedef() { + return this; +} diff --git a/dtool/src/cppparser/cppTypedef.h b/dtool/src/cppparser/cppTypedef.h new file mode 100644 index 0000000000..e027b671a5 --- /dev/null +++ b/dtool/src/cppparser/cppTypedef.h @@ -0,0 +1,33 @@ +// Filename: cppTypedef.h +// Created by: drose (19Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPTYPEDEF_H +#define CPPTYPEDEF_H + +#include + +#include "cppInstance.h" + +/////////////////////////////////////////////////////////////////// +// Class : CPPTypedef +// Description : +//////////////////////////////////////////////////////////////////// +class CPPTypedef : public CPPInstance { +public: + CPPTypedef(CPPInstance *instance, bool global); + + virtual CPPDeclaration *substitute_decl(SubstDecl &subst, + CPPScope *current_scope, + CPPScope *global_scope); + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPTypedef *as_typedef(); +}; + +#endif + diff --git a/dtool/src/cppparser/cppUsing.cxx b/dtool/src/cppparser/cppUsing.cxx new file mode 100644 index 0000000000..56f56084c7 --- /dev/null +++ b/dtool/src/cppparser/cppUsing.cxx @@ -0,0 +1,54 @@ +// Filename: cppUsing.C +// Created by: drose (16Nov99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppUsing.h" +#include "cppIdentifier.h" + +//////////////////////////////////////////////////////////////////// +// Function: CPPUsing::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CPPUsing:: +CPPUsing(CPPIdentifier *ident, bool full_namespace, const CPPFile &file) : + CPPDeclaration(file), + _ident(ident), _full_namespace(full_namespace) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPUsing::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CPPUsing:: +output(ostream &out, int, CPPScope *, bool) const { + out << "using "; + if (_full_namespace) { + out << "namespace "; + } + out << *_ident; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPUsing::get_subtype +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPDeclaration::SubType CPPUsing:: +get_subtype() const { + return ST_using; +} + +//////////////////////////////////////////////////////////////////// +// Function: CPPUsing::as_using +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CPPUsing *CPPUsing:: +as_using() { + return this; +} diff --git a/dtool/src/cppparser/cppUsing.h b/dtool/src/cppparser/cppUsing.h new file mode 100644 index 0000000000..9014bea635 --- /dev/null +++ b/dtool/src/cppparser/cppUsing.h @@ -0,0 +1,34 @@ +// Filename: cppUsing.h +// Created by: drose (16Nov99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPUSING_H +#define CPPUSING_H + +#include + +#include "cppDeclaration.h" + +class CPPIdentifier; +class CPPScope; + +/////////////////////////////////////////////////////////////////// +// Class : CPPUsing +// Description : +//////////////////////////////////////////////////////////////////// +class CPPUsing : public CPPDeclaration { +public: + CPPUsing(CPPIdentifier *ident, bool full_namespace, const CPPFile &file); + + virtual void output(ostream &out, int indent_level, CPPScope *scope, + bool complete) const; + virtual SubType get_subtype() const; + + virtual CPPUsing *as_using(); + + CPPIdentifier *_ident; + bool _full_namespace; +}; + +#endif diff --git a/dtool/src/cppparser/cppVisibility.cxx b/dtool/src/cppparser/cppVisibility.cxx new file mode 100644 index 0000000000..303b380348 --- /dev/null +++ b/dtool/src/cppparser/cppVisibility.cxx @@ -0,0 +1,29 @@ +// Filename: cppVisibility.C +// Created by: drose (22Oct99) +// +//////////////////////////////////////////////////////////////////// + + +#include "cppVisibility.h" + +ostream & +operator << (ostream &out, CPPVisibility vis) { + switch (vis) { + case V_published: + return out << "__published"; + + case V_public: + return out << "public"; + + case V_protected: + return out << "protected"; + + case V_private: + return out << "private"; + + case V_unknown: + return out << "unknown"; + } + + return out << "(**invalid visibility**)"; +} diff --git a/dtool/src/cppparser/cppVisibility.h b/dtool/src/cppparser/cppVisibility.h new file mode 100644 index 0000000000..d0d2dad78f --- /dev/null +++ b/dtool/src/cppparser/cppVisibility.h @@ -0,0 +1,22 @@ +// Filename: cppVisibility.h +// Created by: drose (22Oct99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CPPVISIBILITY_H +#define CPPVISIBILITY_H + +#include + +enum CPPVisibility { + V_published, + V_public, + V_protected, + V_private, + V_unknown +}; + +ostream &operator << (ostream &out, CPPVisibility vis); + +#endif + diff --git a/dtool/src/cppparser/indent.cxx b/dtool/src/cppparser/indent.cxx new file mode 100644 index 0000000000..575ce5d523 --- /dev/null +++ b/dtool/src/cppparser/indent.cxx @@ -0,0 +1,23 @@ +// Filename: indent.C +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + + +#include "indent.h" + +//////////////////////////////////////////////////////////////////// +// Function: indent +// Description: A handy function for doing text formatting. This +// function simply outputs the indicated number of +// spaces to the given output stream, returning the +// stream itself. Useful for indenting a series of +// lines of text by a given amount. +//////////////////////////////////////////////////////////////////// +ostream & +indent(ostream &out, int indent_level) { + for (int i = 0; i < indent_level; i++) { + out << ' '; + } + return out; +} diff --git a/dtool/src/cppparser/indent.h b/dtool/src/cppparser/indent.h new file mode 100644 index 0000000000..7de920556d --- /dev/null +++ b/dtool/src/cppparser/indent.h @@ -0,0 +1,24 @@ +// Filename: indent.h +// Created by: drose (16Jan99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INDENT_H +#define INDENT_H + +#include + +//////////////////////////////////////////////////////////////////// +// Function: indent +// Description: A handy function for doing text formatting. This +// function simply outputs the indicated number of +// spaces to the given output stream, returning the +// stream itself. Useful for indenting a series of +// lines of text by a given amount. +//////////////////////////////////////////////////////////////////// +ostream & +indent(ostream &out, int indent_level); + +#endif + + diff --git a/dtool/src/dconfig/Sources.pp b/dtool/src/dconfig/Sources.pp new file mode 100644 index 0000000000..e96d331243 --- /dev/null +++ b/dtool/src/dconfig/Sources.pp @@ -0,0 +1,53 @@ +#define LOCAL_LIBS dtoolutil dtoolbase + +#begin lib_target + #define TARGET dconfig + + #define SOURCES \ + configTable.I configTable.cxx configTable.h config_dconfig.cxx \ + config_dconfig.h config_notify.cxx config_notify.h config_setup.h \ + dconfig.I dconfig.cxx dconfig.h expand.I expand.h notify.I \ + notify.cxx notify.h notifyCategory.I notifyCategory.cxx \ + notifyCategory.h notifyCategoryProxy.I notifyCategoryProxy.h \ + notifySeverity.cxx notifySeverity.h serialization.I serialization.h \ + symbolEnt.I symbolEnt.cxx symbolEnt.h + + #define INSTALL_HEADERS \ + configTable.I configTable.h config_dconfig.h config_setup.h \ + dconfig.I dconfig.h \ + expand.I expand.h notify.I notify.h notifyCategory.I \ + notifyCategory.h notifyCategoryProxy.I notifyCategoryProxy.h \ + notifySeverity.h serialization.I serialization.h symbolEnt.I \ + symbolEnt.h + +#end lib_target + +#begin test_bin_target + #define TARGET test_config + #define SOURCES test_config.cxx + #define LOCAL_LIBS dconfig $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET test_expand + #define SOURCES test_expand.cxx + #define LOCAL_LIBS dconfig $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET test_pfstream + #define SOURCES test_pfstream.cxx + #define LOCAL_LIBS dconfig $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET test_searchpath + #define SOURCES test_searchpath.cxx + #define LOCAL_LIBS dconfig $[LOCAL_LIBS] +#end test_bin_target + +#begin test_bin_target + #define TARGET test_serialization + #define SOURCES test_serialization.cxx + #define LOCAL_LIBS dconfig $[LOCAL_LIBS] +#end test_bin_target diff --git a/dtool/src/dconfig/configTable.I b/dtool/src/dconfig/configTable.I new file mode 100644 index 0000000000..424982a651 --- /dev/null +++ b/dtool/src/dconfig/configTable.I @@ -0,0 +1,72 @@ +// Filename: configTable.I +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + + +INLINE void ConfigTable::ConfigDbgDefault(void) { +/* +#ifdef NDEBUG +*/ + + configdbg = false; + microconfig_cat->set_severity(NS_info); + dconfig_cat->set_severity(NS_info); + +/* +#else * NDEBUG * + + configdbg = true; + microconfig_cat->set_severity(NS_debug); + config_cat->set_severity(NS_debug); +#endif * NDEBUG * +*/ +} + +INLINE void ConfigTable::ReadArgsDefault(void) { + readargs = true; +} + +INLINE void ConfigTable::ReadEnvsDefault(void) { + readenvs = true; +} + +INLINE void ConfigTable::PathSepDefault(void) { + pathsep = ": "; +} + +INLINE void ConfigTable::FileSepDefault(void) { + filesep = "/"; +} + +INLINE void ConfigTable::ConfigNameDefault(void) { + configname = "Configrc"; +} + +INLINE void ConfigTable::ConfigSuffixDefault(void) { + configsuffix = ""; +} + +INLINE void ConfigTable::ConfigPathDefault(void) { +#ifdef PENV_PS2 + + // When this is empty, ReadConfigFile (ConfigTable.C) is forced + // to look in CONFIG_PATH compiler_flag (instead of env var) + + configpath = ""; +#else + configpath = "CONFIG_PATH"; +#endif +} + +INLINE void ConfigTable::ConfigCmtDefault(void) { + configcmt = "#"; +} + +INLINE void ConfigTable::ArgSuffixDefault(void) { + argsuffix = "_ARGS"; +} + +INLINE void ConfigTable::CommandStubDefault(void) { + commandstub = "CONFIG"; +} diff --git a/dtool/src/dconfig/configTable.cxx b/dtool/src/dconfig/configTable.cxx new file mode 100644 index 0000000000..d4470d4835 --- /dev/null +++ b/dtool/src/dconfig/configTable.cxx @@ -0,0 +1,698 @@ +// Filename: configTable.C +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#include "dconfig.h" +#include "configTable.h" +#include "dSearchPath.h" +#include "executionEnvironment.h" +#include "config_dconfig.h" +#include "pfstream.h" +#include "serialization.h" + +#ifdef PENV_PS2 + +#include +#include +#include + +#endif // PENV_PS2 + +//#define DISABLE_CONFIG + +using namespace Config; + +ConfigTable* ConfigTable::_instance = (ConfigTable*)0L; + +void ConfigTable::CropString(ConfigString& S) { + size_t i = S.find_first_not_of(" \t\r\f\n"); + if (i != ConfigString::npos) { + size_t j = S.find_last_not_of(" \t\r\f\n"); + if (j != ConfigString::npos) + S = S.substr(i, j-i+1); + else + S = S.substr(i, ConfigString::npos); + } else + S.erase(0, ConfigString::npos); +} + +bool ConfigTable::IsComment(const ConfigString& S) +{ + if (!S.empty()) { + for (ConfigString::iterator i=configcmt.begin(); + i!=configcmt.end(); ++i) + if (S[0] == (*i)) + return true; + } + return false; +} + +void ConfigTable::UpCase(ConfigString& S) +{ + for (ConfigString::iterator i=S.begin(); i!=S.end(); ++i) + (*i) = toupper(*i); +} + +ConfigString ConfigTable::NextWord(const ConfigString& S) { + int i(S.find_first_of(" \t\r\f\n")); + return S.substr(0, i); +} + +ConfigString ConfigTable::PopNextWord(ConfigString& S) { + int i(S.find_first_of(" \t\r\f\n")); + ConfigString ret(S.substr(0, i)); + S.erase(0, i); + CropString(S); + return ret; +} + +void ConfigTable::ParseConfigFile(istream& is, const ConfigString& Filename) +{ + ConfigString line; + + while (!is.eof()) { + std::getline(is, line); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "read from " << Filename << ": '" << line + << "'" << endl; + CropString(line); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "cropped line to: '" << line << "'" + << endl; + if (!IsComment(line)) { + ConfigString protosym(PopNextWord(line)); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "protosym is '" << protosym + << "' with value of '" << line << "'" + << endl; + size_t i(protosym.find(".")); + if (i == ConfigString::npos) { + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "this is an unqualified symbol" + << endl; + unqualified[protosym].push_back(SymEnt(SymEnt::ConfigFile, line, Filename)); + } else { + ConfigString scope(protosym.substr(0, i)); + ConfigString sym(protosym.substr(i+1, ConfigString::npos)); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "this is a qualified symbol." + << " scope '" << scope + << "', symbol '" << sym << "'" << endl; + (qualified[scope])[sym].push_back(SymEnt(SymEnt::ConfigFile, line, Filename)); + } + } else if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "line is detected as a comment" << endl; + } +} + +void ConfigTable::ReadConfigFile(void) { + if (configpath.empty()) { + +#ifdef PENV_PS2 + + // roight. PS2 has no environment variables, so the config path + // has to be explicitly set with the CONFIG_PATH compiler variable. + // this is currently done in makefile.config.so... + + // CSN. + +#ifndef CONFIG_PATH +#define CONFIG_PATH "." +#endif + configpath = CONFIG_PATH; +#else + configpath = "."; +#endif + } else { + ConfigString S; + while (!configpath.empty()) { + int i = configpath.find_first_of(" "); + ConfigString stmp = configpath.substr(0, i); + if (ExecutionEnvironment::has_environment_variable(stmp)) { + S += " "; + S += ExecutionEnvironment::get_environment_variable(stmp); + } + configpath.erase(0, i); + CropString(configpath); + } + if (S.empty()) + S = "."; + CropString(S); + configpath = S; + } + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "evaluated value of configpath '" + << configpath << "'" << endl; + + DSearchPath config_search(configpath); + DSearchPath::Results config_files; + + if (!configsuffix.empty()) { + config_search.find_all_files(configname + configsuffix, config_files); + } + + config_search.find_all_files(configname, config_files); + + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "configpath parsed and searched" + << endl; + + int num_config_files = config_files.get_num_files(); + for (int i = num_config_files - 1; i >= 0; i--) { + Filename config_file = config_files.get_file(i); + + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "examining file '" << config_file << "'" + << endl; + /* + if (file_access(convert_executable_pathname(config_file), X_OK) == 0) { + ConfigString line = config_file + " " + ExecutionEnvironment::get_binary_name(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "file is executable, running '" + << line << "'" << endl; + ipfstream ifs(line); + ParseConfigFile(ifs, config_file); + } else + */{ + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "file is not executable, reading normally" << endl; + +#ifdef PENV_PS2 + ConfigString line = PS2_FILE_PREFIX + convert_pathname(config_file); + + int fd = sceOpen((char *) line.c_str(), SCE_RDONLY); + char line_buffer[2048]; + + memset(line_buffer, 0, 2048); + + ConfigString file_buffer; + + while (sceRead(fd, line_buffer, 2048) > 0) + { + file_buffer += line_buffer; + memset(line_buffer, 0, 2048); + } + + sceClose(fd); + + istrstream ifs(file_buffer.c_str()); +#else + ifstream ifs(config_file.to_os_specific().c_str()); +#endif + ParseConfigFile(ifs, config_file); + } + } +} + +void ConfigTable::ParseCommandEnv(ConfigString& S, const ConfigString& sym) +{ + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "value of '" << sym << "' is '" << S << "'" + << endl; + while (!S.empty()) { + ConfigString protosym(PopNextWord(S)); + bool ok = false; + bool state = false; + if (protosym[0] == '-') + ok = true; + else if (protosym[0] == '+') { + ok = true; + state = true; + } + if (ok) { + protosym.erase(0, 1); + CropString(protosym); + size_t i(protosym.find(".")); + if (i == ConfigString::npos) { + unqualified[protosym].push_back(SymEnt(SymEnt::CommandEnv, + NextWord(S), sym, state)); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "unqualified symbol '" << protosym + << "' with value '" << NextWord(S) + << "'" << endl; + } else { + ConfigString scope(protosym.substr(0, i)); + ConfigString sym(protosym.substr(i+1, ConfigString::npos)); + (qualified[scope])[sym].push_back(SymEnt(SymEnt::CommandEnv, + NextWord(S), sym, state)); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "qualified symbol '" << sym + << "' in scope '" << scope + << "' and value '" << NextWord(S) + << "'" << endl; + } + } else if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "'" << protosym + << "' was not recognized as an option" + << endl; + } +} + +void ConfigTable::ParseArgs(void) +{ + int n = 0; + int num_args = ExecutionEnvironment::get_num_args(); + + while ( n < num_args) { + bool ok = false; + bool state = false; + ConfigString line(ExecutionEnvironment::get_arg(n)); + CropString(line); + if (line[0] == '-') + ok = true; + else if (line[0] == '+') { + ok = true; + state = true; + } + if (ok) { + line.erase(0, 1); + CropString(line); + size_t i(line.find(".")); + ConfigString aparam; + if (n + 1 < num_args) { + aparam = ExecutionEnvironment::get_arg(n + 1); + } + + if (i == ConfigString::npos) { + unqualified[line].push_back(SymEnt(SymEnt::Commandline, + aparam, "", state)); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "unqualified symbol '" << line + << "' with value '" << aparam + << "'" << endl; + } else { + ConfigString scope(line.substr(0, i)); + ConfigString sym(line.substr(i+1, ConfigString::npos)); + (qualified[scope])[sym].push_back(SymEnt(SymEnt::Commandline, + aparam, "", state)); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "qualified symbol '" << sym + << "' with scope '" << scope + << "' and value '" << aparam + << "'" << endl; + } + } else if (microconfig_cat->is_spam()) { + microconfig_cat->spam() << "argument #" << n << " ('" << line + << "') is not recognized as an option" + << endl; + } + ++n; + } +} + +void ConfigTable::MicroConfig(void) +{ +/* +#ifndef NDEBUG + NotifySeverity mcs = microconfig_cat->get_severity(); + microconfig_cat->set_severity(NS_spam); + + NotifySeverity cs = config_cat->get_severity(); + config_cat->set_severity(NS_spam); +#else * NDEBUG * +*/ + // NotifySeverity mcs = microconfig_cat->get_severity(); + microconfig_cat->set_severity(NS_info); + + // NotifySeverity cs = dconfig_cat->get_severity(); + dconfig_cat->set_severity(NS_info); +/* +#endif * NDEBUG * +*/ + string cc = ExecutionEnvironment::get_environment_variable("CONFIG_CONFIG"); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "CONFIG_CONFIG = '" << cc << "'" << endl; + bool cdbg = false; + bool psep = false; + bool fsep = false; + bool cname = false; + bool csuff = false; + bool cpath = false; + bool ccmt = false; + bool asuff = false; + bool cstub = false; + bool rdarg = false; + bool rdenv = false; + if (!cc.empty()) { + ConfigString configconfig(cc); + if (configconfig.length() > 1) { + ConfigString assign = "="; + ConfigString sep = configconfig.substr(0, 1); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "separator character is: '" << sep + << "'" << endl; + typedef std::vector strvec; + typedef Serialize::Deserializer > deser; + configconfig.erase(0, 1); + deser ds(configconfig, sep); + strvec sv = ds; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "extracted vector of microconfig options" << endl; + for (strvec::iterator i=sv.begin(); i!=sv.end(); ++i) { + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "parsing microconfig option '" + << *i << "'" << endl; + if ((*i).length() == 1) { + // new assignment character + assign += *i; + continue; + } + size_t j = (*i).find_first_of(assign); + if (j != ConfigString::npos) { + ConfigString tok = (*i).substr(0, j); + ConfigString rest = (*i).substr(j+1, ConfigString::npos); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "split microconfig option into '" + << tok << "' and '" << rest << "'" + << endl; + if (tok == "pathsep") { + pathsep = rest; + psep = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig pathsep directive, " + << "setting the path separator to '" << pathsep << "'" + << endl; + } else if (tok == "filesep") { + filesep = rest; + fsep = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig filesep directive, " + << "setting the file separator to '" << filesep << "'" + << endl; + } else if (tok == "configname") { + configname = rest; + cname = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig configname directive, " + << "setting the configfile name to '" << configname + << "'" << endl; + } else if (tok == "configsuffix") { + configsuffix = rest; + csuff = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig configsuffix directive, " + << "setting the config file suffix to '" + << configsuffix << "'" + << endl; + } else if (tok == "configpath") { + if (cpath) { + configpath += " " + rest; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig configpath directive, " + << "adding '" << rest << "' to the configpath" + << endl; + } else { + configpath = rest; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig configpath directive, " + << "setting the configpath to '" << configpath << "'" + << endl; + } + cpath = true; + } else if (tok == "configcmt") { + configcmt = rest; + ccmt = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig configcmt directive, " + << "setting the config comment to '" << configcmt + << "'" << endl; + } else if (tok == "argsuffix") { + argsuffix = rest; + asuff = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig argsuffix directive, " + << "setting the argument environment suffix to '" + << argsuffix << "'" << endl; + } else if (tok == "commandstub") { + commandstub = rest; + cstub = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig commandstub directive, " + << "setting the command environment stub " + << "to '" << commandstub << "'" << endl; + } else if (tok == "configdbg") { + configdbg = TrueOrFalse(rest); + cdbg = true; + if (configdbg) { + microconfig_cat->set_severity(NS_spam); + dconfig_cat->set_severity(NS_spam); + } else { + microconfig_cat->set_severity(NS_info); + dconfig_cat->set_severity(NS_info); + } + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig configdbg directive, " + << "setting the config spam state to " << configdbg + << endl; + } else if (tok == "readargs") { + readargs = TrueOrFalse(rest); + rdarg = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig readargs directive, " + << (readargs?"will":"will not") + << " read from the commandline." << endl; + } else if (tok == "readenv") { + readenvs = TrueOrFalse(rest); + rdenv = true; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "got a microconfig readenv directive, " + << (readargs?"will":"will not") + << " read the environment." << endl; + } + } else if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "no '=' in microconfig option, ignoring it" << endl; + } + } else if (microconfig_cat->is_spam()) + microconfig_cat->spam() + << "CONFIG_CONFIG contains only a single character" << endl; + } else if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "CONFIG_CONFIG is empty" << endl; + if (!cdbg) + ConfigDbgDefault(); + if (!psep) { + PathSepDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for pathsep, " + << "setting to default '" << pathsep << "'" + << endl; + } + if (!fsep) { + FileSepDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for filesep, " + << "setting to default '" << filesep << "'" + << endl; + } + if (!cname) { + ConfigNameDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for configname, " + << "setting to default '" << configname + << "'" << endl; + } + if (!csuff) { + ConfigSuffixDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for configsuffix, " + << "setting to default '" << configsuffix + << "'" << endl; + } + if (!cpath) { + ConfigPathDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for configpath, " + << "setting to default '" << configpath + << "'" << endl; + } + if (!ccmt) { + ConfigCmtDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for configcmt, " + << "setting to default '" << configcmt + << "'" << endl; + } + if (!asuff) { + ArgSuffixDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for argsuffix, " + << "setting to default '" << argsuffix + << "'" << endl; + } + if (!cstub) { + CommandStubDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for commandstub, " + << "setting to default '" << commandstub + << "'" << endl; + } + if (!rdarg) { + ReadArgsDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for readargs, " + << "setting to default: " + << (readargs?"true":"false") << endl; + } + if (!rdenv) { + ReadEnvsDefault(); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "no microconfig for readenv, " + << "setting to default: " + << (readargs?"true":"false") << endl; + } +} + +void ConfigTable::GetData(void) { + MicroConfig(); +#ifndef DISABLE_CONFIG + ReadConfigFile(); + if (readenvs) { + ConfigString comarg = commandstub + argsuffix; + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "comarg is '" << comarg << "'" + << endl; + if (ExecutionEnvironment::has_environment_variable(comarg)) { + ConfigString env = ExecutionEnvironment::get_environment_variable(comarg); + ParseCommandEnv(env, comarg); + } + ConfigString line = ExecutionEnvironment::get_binary_name() + argsuffix; + UpCase(line); + if (microconfig_cat->is_spam()) + microconfig_cat->spam() << "binarg is '" << line << "'" + << endl; + if (ExecutionEnvironment::has_environment_variable(line)) { + ConfigString env = ExecutionEnvironment::get_environment_variable(line); + ParseCommandEnv(env, line); + } + } + if (readargs) + ParseArgs(); +#endif // DISABLE_CONFIG +} + +ConfigTable* ConfigTable::Instance(void) { + if (_instance == (ConfigTable*)0L) { + _instance = new ConfigTable; + _instance->GetData(); + _instance->_initializing = false; + } + return _instance; +} + +bool ConfigTable::AmInitializing(void) { + return _initializing; +} + +bool ConfigTable::TrueOrFalse(const ConfigString& in, bool def) { + bool ret = def; + ConfigString S = in; + UpCase(S); + if (S[0] == '#') { + if (S[1] == 'F') + ret = false; + else if (S[1] == 'T') + ret = true; + } else if (S == "0") { + ret = false; + } else if (S == "1") { + ret = true; + } else if (S == "FALSE") { + ret = false; + } else if (S == "TRUE") { + ret = true; + } + return ret; +} + +bool ConfigTable::Defined(const ConfigString& sym, + const ConfigString qual) { +#ifdef DISABLE_CONFIG + return false; +#else + if (qual.empty()) { + return (unqualified.count(sym) != 0 || + ExecutionEnvironment::has_environment_variable(sym)); + + } else { + TableMap::const_iterator ti; + ti = qualified.find(qual); + if (ti != qualified.end()) { + const SymbolTable &table = (*ti).second; + if (table.count(sym) != 0) { + return true; + } + } + + return ExecutionEnvironment::has_environment_variable(qual + "." + sym); + } +#endif // DISABLE_CONFIG +} + +ConfigTable::SymEnt ConfigTable::Get(const ConfigString& sym, + const ConfigString qual) { +#ifndef DISABLE_CONFIG + const ConfigTable::Symbol &symbol = GetSym(sym, qual); + if (!symbol.empty()) { + return symbol.back(); + } + + // No explicit config definition; fall back to the environment. + string envvar = sym; + if (!qual.empty()) { + envvar = qual + "." + sym; + } + + if (ExecutionEnvironment::has_environment_variable(sym)) { + string def = ExecutionEnvironment::get_environment_variable(sym); + return ConfigTable::SymEnt(ConfigTable::SymEnt::Environment, def); + } +#endif // DISABLE_CONFIG + + // No definition for the variable. Too bad for you. + return ConfigTable::SymEnt(); +} + +const ConfigTable::Symbol& ConfigTable::GetSym(const ConfigString& sym, + const ConfigString qual) { + static ConfigTable::Symbol empty_symbol; + +#ifndef DISABLE_CONFIG + total_num_get++; + if (qual.empty()) { + SymbolTable::const_iterator si; + si = unqualified.find(sym); + if (si != unqualified.end()) { + return (*si).second; + } + + } else { + TableMap::const_iterator ti; + ti = qualified.find(qual); + if (ti != qualified.end()) { + const SymbolTable &table = (*ti).second; + SymbolTable::const_iterator si; + si = table.find(sym); + if (si != table.end()) { + return (*si).second; + } + } + } +#endif // DISABLE_CONFIG + + return empty_symbol; +} diff --git a/dtool/src/dconfig/configTable.h b/dtool/src/dconfig/configTable.h new file mode 100644 index 0000000000..6dc906eef3 --- /dev/null +++ b/dtool/src/dconfig/configTable.h @@ -0,0 +1,82 @@ +// Filename: configTable.h +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIGTABLE_H +#define CONFIGTABLE_H + +#include + +#include "config_setup.h" +#include "config_dconfig.h" +#include "symbolEnt.h" + +#include +#include + +namespace Config { + +class EXPCL_DTOOL ConfigTable { + private: + static ConfigTable* _instance; + public: + typedef SymbolEnt SymEnt; + typedef vector_SymbolEnt Symbol; + private: + typedef std::map SymbolTable; + typedef std::map TableMap; + SymbolTable unqualified; + TableMap qualified; + bool _initializing; + bool configdbg; + bool readargs; + bool readenvs; + ConfigString pathsep; + ConfigString filesep; + ConfigString configname; + ConfigString configsuffix; + ConfigString configpath; + ConfigString configcmt; + ConfigString argsuffix; + ConfigString commandstub; + + static void CropString(ConfigString& S); + bool IsComment(const ConfigString&); + static void UpCase(ConfigString&); + ConfigString NextWord(const ConfigString& S); + ConfigString PopNextWord(ConfigString& S); + void ParseConfigFile(istream&, const ConfigString&); + void ReadConfigFile(void); + void ParseCommandEnv(ConfigString&, const ConfigString&); + void ParseArgs(void); + INLINE void ConfigDbgDefault(void); + INLINE void ReadArgsDefault(void); + INLINE void ReadEnvsDefault(void); + INLINE void PathSepDefault(void); + INLINE void FileSepDefault(void); + INLINE void ConfigNameDefault(void); + INLINE void ConfigSuffixDefault(void); + INLINE void ConfigPathDefault(void); + INLINE void ConfigCmtDefault(void); + INLINE void ArgSuffixDefault(void); + INLINE void CommandStubDefault(void); + void MicroConfig(void); + void GetData(void); + protected: + ConfigTable(void) : _initializing(true) {} + public: + static ConfigTable* Instance(void); + bool AmInitializing(void); + static bool TrueOrFalse(const ConfigString& in, bool def = false); + bool Defined(const ConfigString& sym, const ConfigString qual=""); + SymEnt Get(const ConfigString& sym, const ConfigString qual = ""); + const Symbol& GetSym(const ConfigString& sym, + const ConfigString qual = ""); +}; + +#include "configTable.I" + +} // close Config namespace + +#endif diff --git a/dtool/src/dconfig/config_dconfig.cxx b/dtool/src/dconfig/config_dconfig.cxx new file mode 100644 index 0000000000..11b08aaf36 --- /dev/null +++ b/dtool/src/dconfig/config_dconfig.cxx @@ -0,0 +1,9 @@ +// Filename: config_dconfig.C +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_dconfig.h" + +NotifyCategoryDef(dconfig, ""); +NotifyCategoryDef(microconfig, "dconfig"); diff --git a/dtool/src/dconfig/config_dconfig.h b/dtool/src/dconfig/config_dconfig.h new file mode 100644 index 0000000000..45dbb04121 --- /dev/null +++ b/dtool/src/dconfig/config_dconfig.h @@ -0,0 +1,22 @@ +// Filename: config_dconfig.h +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_DCONFIG_H +#define CONFIG_DCONFIG_H + +#ifdef WIN32_VC +/* C4231: extern before template instantiation */ +/* MPG - For some reason, this one only works if it's here */ +#pragma warning (disable : 4231) +#endif + +#include + +#include "notifyCategoryProxy.h" + +NotifyCategoryDecl(dconfig, EXPCL_DTOOL, EXPTP_DTOOL); +NotifyCategoryDecl(microconfig, EXPCL_DTOOL, EXPTP_DTOOL); + +#endif diff --git a/dtool/src/dconfig/config_notify.cxx b/dtool/src/dconfig/config_notify.cxx new file mode 100644 index 0000000000..9931a295a5 --- /dev/null +++ b/dtool/src/dconfig/config_notify.cxx @@ -0,0 +1,24 @@ +// Filename: config_notify.C +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_notify.h" +#include "dconfig.h" + +ConfigureDef(config_notify); + +ConfigureFn(config_notify) { +} + +// We have to declare this as a function instead of a static const, +// because its value might be needed at static init time. +bool +get_assert_abort() { + static bool *assert_abort = (bool *)NULL; + if (assert_abort == (bool *)NULL) { + assert_abort = new bool; + *assert_abort = config_notify.GetBool("assert-abort", false); + } + return *assert_abort; +} diff --git a/dtool/src/dconfig/config_notify.h b/dtool/src/dconfig/config_notify.h new file mode 100644 index 0000000000..00e7f9e3ae --- /dev/null +++ b/dtool/src/dconfig/config_notify.h @@ -0,0 +1,16 @@ +// Filename: config_notify.h +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_NOTIFY_H +#define CONFIG_NOTIFY_H + +#include +#include "dconfig.h" + +ConfigureDecl(config_notify, EXPCL_DTOOL, EXPTP_DTOOL); + +bool get_assert_abort(); + +#endif /* __CONFIG_NOTIFY_H__ */ diff --git a/dtool/src/dconfig/config_setup.h b/dtool/src/dconfig/config_setup.h new file mode 100644 index 0000000000..ceb6e95db5 --- /dev/null +++ b/dtool/src/dconfig/config_setup.h @@ -0,0 +1,14 @@ +// Filename: config_setup.h +// Created by: cary (20Mar00) +// +/////////////////////////////////////////////////////////////////////// + +#ifndef __CONFIG_SETUP_H__ +#define __CONFIG_SETUP_H__ + +#include + +// use 8-bit chars for now. Change this line to make config switch to wchar +typedef std::string ConfigString; + +#endif /* __CONFIG_SETUP_H__ */ diff --git a/dtool/src/dconfig/dconfig.I b/dtool/src/dconfig/dconfig.I new file mode 100644 index 0000000000..d5f06dc754 --- /dev/null +++ b/dtool/src/dconfig/dconfig.I @@ -0,0 +1,17 @@ +// Filename: config.I +// Created by: cary (20Mar00) +// +/////////////////////////////////////////////////////////////////////// + + +INLINE double get_total_time_config_init() { + return (double)total_time_config_init / (double)CLOCKS_PER_SEC; +} + +INLINE double get_total_time_external_init() { + return (double)total_time_external_init / (double)CLOCKS_PER_SEC; +} + +INLINE double get_total_num_get() { + return total_num_get; +} diff --git a/dtool/src/dconfig/dconfig.cxx b/dtool/src/dconfig/dconfig.cxx new file mode 100644 index 0000000000..cc9bbf1053 --- /dev/null +++ b/dtool/src/dconfig/dconfig.cxx @@ -0,0 +1,15 @@ +// Filename: dconfig.C +// Created by: drose (08Feb99) +// +//////////////////////////////////////////////////////////////////// + +#include "dconfig.h" + +namespace Config { + + clock_t total_time_config_init = 0; + clock_t total_time_external_init = 0; + int total_num_get = 0; + +} + diff --git a/dtool/src/dconfig/dconfig.h b/dtool/src/dconfig/dconfig.h new file mode 100644 index 0000000000..a7f1e5abf1 --- /dev/null +++ b/dtool/src/dconfig/dconfig.h @@ -0,0 +1,424 @@ +// Filename: dconfig.h +// Created by: cary (14Jul98) +// +/////////////////////////////////////////////////////////////////////// + +#ifndef DCONFIG_H +#define DCONFIG_H + +#include + +#include "config_setup.h" +#include "config_dconfig.h" +#include "configTable.h" +#include "executionEnvironment.h" + +#include +#include +#include + +namespace Config { + +// Config is, itself, configurable. The things that can be set are: +// path separator ( ': ' ) [pathsep] +// filename separator ( / ) [filesep] +// config filename ( Configrc ) [configname] +// config file suffix ( ) [configsuffix] +// config file path ( $CONFIG_PATH ) [configpath] +// (can be multiple of these) +// config file comment ( '#' ) [configcmt] +// 'args' suffix ( _ARGS ) [argsuffix] +// command arg env ( CONFIG + _____ ) [commandstub] +// debugging output ( false ) [configdbg] +// read commandline ( true ) [readargs] +// read environment ( true ) [readenv] +// all of these can be set from the one unconfigurable thing in config, the +// environment variable called CONFIG_CONFIG. The format is: +// separator_char{{field_id}={field_value}separator_char}* + +// Call these functions to get stats on how Config is spending its +// time. +EXPCL_DTOOL INLINE double get_total_time_config_init(); +EXPCL_DTOOL INLINE double get_total_time_external_init(); +EXPCL_DTOOL INLINE double get_total_num_get(); + +// These globals are intended for internal bookkeeping by Config to +// compute the above functions' return values. +EXPCL_DTOOL extern clock_t total_time_config_init; +EXPCL_DTOOL extern clock_t total_time_external_init; +EXPCL_DTOOL extern int total_num_get; + +template +class Config { + protected: + static void ConfigFunc(void); + static void Flag(bool); + public: + Config(void); + ~Config(void); + static bool AmInitializing(void); + static ConfigString Name(void); + static bool Flag(void); + static void Init(void); + static bool Defined(const ConfigString& sym); + static ConfigString Get(const ConfigString sym); + static ConfigTable::Symbol& GetAll(const ConfigString, + ConfigTable::Symbol&); + static bool GetBool(const ConfigString sym, bool def = false); + static int GetInt(const ConfigString sym, int def = 0); + static float GetFloat(const ConfigString sym, float def = 0.); + static double GetDouble(const ConfigString sym, double def = 0.); + static ConfigString GetString(const ConfigString sym, + const ConfigString def = ""); +}; + +// Implementation follows + +template +void Config::ConfigFunc(void) +{ + GetConfig::config_func(); +} + +template +bool Config::AmInitializing(void) +{ + ConfigTable* tab = ConfigTable::Instance(); + if (tab == (ConfigTable *)0L) + return Flag(); + return tab->AmInitializing(); +} + +template +ConfigString Config::Name(void) +{ + return GetConfig::get_name(); +} + +template +bool Config::Flag(void) +{ + return GetConfig::_flag; +} + +template +void Config::Flag(bool f) +{ + GetConfig::_flag = f; +} + +template +void Config::Init(void) +{ + if (Flag()) + return; + total_time_config_init -= clock(); + Flag(true); + ConfigTable::Instance(); + + if (Defined("notify-level-config")) { + ConfigString s = Get("notify-level-config"); + NotifySeverity sev = Notify::string_severity(s); + if (sev != NS_unspecified) { + microconfig_cat->set_severity(sev); + dconfig_cat->set_severity(sev); + } else { + microconfig_cat->set_severity(NS_info); + dconfig_cat->set_severity(NS_info); + } + } else { + microconfig_cat->set_severity(NS_info); + dconfig_cat->set_severity(NS_info); + } + + total_time_config_init += clock(); + total_time_external_init -= clock(); + + if (dconfig_cat->is_spam()) + dconfig_cat->spam() << "calling ConfigFunc for '" << Name() << "'" + << endl; + ConfigFunc(); + if (dconfig_cat->is_spam()) + dconfig_cat->spam() << "back from ConfigFunc for '" << Name() << "'" + << endl; + total_time_external_init += clock(); +} + +template +Config::Config(void) +{ + Init(); +} + +template +Config::~Config(void) +{ +} + +template +bool Config::Defined(const ConfigString& sym) +{ + Init(); + ConfigTable* tab = ConfigTable::Instance(); + if (tab->Defined(sym, Name())) + return true; + else if (tab->Defined(sym, ExecutionEnvironment::get_binary_name())) + return true; + else if (tab->Defined(sym)) + return true; + return false; +} + +template +ConfigString Config::Get(ConfigString sym) +{ + Init(); + ConfigTable* tab = ConfigTable::Instance(); + if (tab->Defined(sym, Name())) + return (tab->Get(sym, Name())).Val(); + else if (tab->Defined(sym, ExecutionEnvironment::get_binary_name())) + return (tab->Get(sym, ExecutionEnvironment::get_binary_name())).Val(); + else if (tab->Defined(sym)) + return (tab->Get(sym)).Val(); + else + return ""; +} + +template +ConfigTable::Symbol& Config::GetAll(const ConfigString sym, + ConfigTable::Symbol& s) +{ + Init(); + ConfigTable* tab = ConfigTable::Instance(); + if (tab->Defined(sym, Name())) { + const ConfigTable::Symbol& tsym = tab->GetSym(sym, Name()); + s.insert(s.end(), tsym.begin(), tsym.end()); + } + if (tab->Defined(sym, ExecutionEnvironment::get_binary_name())) { + const ConfigTable::Symbol& tsym = tab->GetSym(sym, ExecutionEnvironment::get_binary_name()); + s.insert(s.end(), tsym.begin(), tsym.end()); + } + if (tab->Defined(sym)) { + const ConfigTable::Symbol& tsym = tab->GetSym(sym); + s.insert(s.end(), tsym.begin(), tsym.end()); + } + return s; +} + +template +bool Config::GetBool(const ConfigString sym, bool def) +{ + Init(); + if (Defined(sym)) { + ConfigString s = Get(sym); + bool ret = ConfigTable::TrueOrFalse(s, def); + if (dconfig_cat->is_spam()) + dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: " + << (ret?"true":"false") << endl; + return ret; + } else { + if (dconfig_cat->is_spam()) + dconfig_cat->spam() + << "symbol '" << sym + << "' is not defined, returning provided default: " + << (def?"true":"false") << endl; + return def; + } +} + +template +int Config::GetInt(const ConfigString sym, int def) +{ + Init(); + if (Defined(sym)) { + ConfigString v = Get(sym); + istringstream s(v); + int i; + s >> i; + if (dconfig_cat->is_spam()) + dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: " + << i << endl; + return i; + } else { + if (dconfig_cat->is_spam()) + dconfig_cat->spam() + << "symbol '" << sym + << "' is not defined, returning provided default: " << def << endl; + return def; + } +} + +template +float Config::GetFloat(const ConfigString sym, float def) +{ + Init(); + if (Defined(sym)) { + ConfigString v = Get(sym); + istringstream s(v); + float f; + s >> f; + if (dconfig_cat->is_spam()) + dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: " + << f << endl; + return f; + } else { + if (dconfig_cat->is_spam()) + dconfig_cat->spam() + << "symbol '" << sym + << "' is not defined, returning provided default: " << def << endl; + return def; + } +} + +template +double Config::GetDouble(const ConfigString sym, double def) +{ + Init(); + if (Defined(sym)) { + ConfigString v = Get(sym); + istringstream s(v); + double d; + s >> d; + if (dconfig_cat->is_spam()) + dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: " + << d << endl; + return d; + } else { + if (dconfig_cat->is_spam()) + dconfig_cat->spam() + << "symbol '" << sym + << "' is not defined, returning provided default: " << def << endl; + return def; + } +} + +template +ConfigString Config::GetString(const ConfigString sym, + const ConfigString def) +{ + Init(); + if (Defined(sym)) { + ConfigString ret = Get(sym); + if (dconfig_cat->is_spam()) + dconfig_cat->spam() << "symbol '" << sym << "' defined, returning: '" + << ret << "'" << endl; + return ret; + } else { + if (dconfig_cat->is_spam()) + dconfig_cat->spam() + << "symbol '" << sym + << "' is not defined, returning provided default: '" << def + << "'" << endl; + return def; + } +} + +#include "dconfig.I" + +} // close Config namespace + + +// Finally, here is a set of handy macros to define and reference a +// Configure object in each package. + +// This macro defines an external reference to a suitable Configure +// object; it should appear in the config_*.h file. The config object +// will be named name. + +#if defined(WIN32_VC) && !defined(CPPPARSER) + +#define ConfigureDecl(name, expcl, exptp) \ + class expcl ConfigureGetConfig_ ## name { \ + public: \ + static const char *get_name(); \ + static void config_func(); \ + static bool _flag; \ + }; \ + exptp template class expcl Config::Config; \ + extern expcl Config::Config name; + +#else // WIN32_VC + +#define ConfigureDecl(name, expcl, exptp) \ + class expcl ConfigureGetConfig_ ## name { \ + public: \ + static const char *get_name(); \ + static void config_func(); \ + static bool _flag; \ + }; \ + extern expcl Config::Config name; + +#endif // WIN32_VC + +// This macro defines the actual declaration of the Configure object +// defined above; it should appear in the config_*.C file. + +#if defined(PENV_OSX) +#define ConfigureDef(name) \ + Config::Config name; \ + bool ConfigureGetConfig_ ## name::_flag = false; \ + int FILE_SYM_NAME = 0; \ + const char *ConfigureGetConfig_ ## name:: \ + get_name() { \ + return #name; \ + } +#else /* PENV_OSX */ +#define ConfigureDef(name) \ + Config::Config name; \ + bool ConfigureGetConfig_ ## name::_flag = false; \ + const char *ConfigureGetConfig_ ## name:: \ + get_name() { \ + return #name; \ + } +#endif /* PENV_OSX */ + +// This macro can be used in lieu of the above two when the Configure +// object does not need to be visible outside of the current C file. + +#if defined(PENV_OSX) +#define Configure(name) \ + class ConfigureGetConfig_ ## name { \ + public: \ + static const char *get_name(); \ + static void config_func(); \ + static bool _flag; \ + }; \ + static Config::Config name; \ + bool ConfigureGetConfig_ ## name::_flag = false; \ + int FILE_SYM_NAME = 0; \ + const char *ConfigureGetConfig_ ## name:: \ + get_name() { \ + return #name; \ + } +#else /* PENV_OSX */ +#define Configure(name) \ + class ConfigureGetConfig_ ## name { \ + public: \ + static const char *get_name(); \ + static void config_func(); \ + static bool _flag; \ + }; \ + static Config::Config name; \ + bool ConfigureGetConfig_ ## name::_flag = false; \ + const char *ConfigureGetConfig_ ## name:: \ + get_name() { \ + return #name; \ + } +#endif /* PENV_OSX */ + +// This one defines a block of code that will be executed at static +// init time. It must always be defined (in the C file), even if no +// code is to be executed. + +#define ConfigureFn(name) \ + void ConfigureGetConfig_ ## name::config_func() + +#if defined(PENV_OSX) +// This is a hack to provide a unique locatable symbol in each lib. +#define ConfigureLibSym \ + char LIB_SYMBOL_NAME[] = ALL_FILE_SYMS_NAME ; +#else +#define ConfigureLibSym +#endif /* PENV_OSX */ + +#endif /* __CONFIG_H__ */ diff --git a/dtool/src/dconfig/expand.I b/dtool/src/dconfig/expand.I new file mode 100644 index 0000000000..70b96ea92a --- /dev/null +++ b/dtool/src/dconfig/expand.I @@ -0,0 +1,48 @@ +// Filename: expand.I +// Created by: cary (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +INLINE bool Base_Expander::isEnv(ConfigString S) { + return (getenv(S.c_str()) != (TYPENAME ConfigString::value_type *)0L); +} + +INLINE ConfigString Base_Expander::Env(ConfigString S) { + return isEnv(S)?getenv(S.c_str()):""; +} + +INLINE bool Base_Expander::isUser(ConfigString S) { + if (S.find_first_of(VChars) == 0) { + std::auto_ptr pw(getpwnam(S.c_str())); + return (pw.get() != (struct passwd *)0L); + } else + return false; +} + +INLINE ConfigString Base_Expander::GetMyDir() { + std::auto_ptr pw(getpwuid(geteuid())); + return pw->pw_dir; +} + +INLINE ConfigString Base_Expander::GetUserDir(ConfigString S) { + std::auto_ptr pw(getpwnam(S.c_str())); + return ((pw.get() != (struct passwd *)0L)?pw->pw_dir:""); +} + +INLINE ConfigString Base_Expander::operator()(void) { + return _result; +} + +INLINE ConfigString Base_Expander::operator()(ConfigString S) { + _result = Base_Expander::Expand(S); + return _result; +} + +INLINE Base_Expander::operator ConfigString() { + return _result; +} + +INLINE ConfigString Expand(ConfigString S) { + Base_Expander ex(S); + return ex; +} diff --git a/dtool/src/dconfig/expand.h b/dtool/src/dconfig/expand.h new file mode 100644 index 0000000000..c666540f39 --- /dev/null +++ b/dtool/src/dconfig/expand.h @@ -0,0 +1,52 @@ +// Filename: expand.h +// Created by: cary (26Aug98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __EXPAND_H__ +#define __EXPAND_H__ + +#include + +#include "pfstream.h" +#include "config_setup.h" + +#include +#include +#include + +namespace Expand { + +static const ConfigString VChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_."; + +class Base_Expander { + private: + ConfigString _result; + + ConfigString Strip(ConfigString S); + INLINE bool isEnv(ConfigString S); + INLINE ConfigString Env(ConfigString S); + istream& CopyStreamToString(istream& is, ConfigString& S); + INLINE bool isUser(ConfigString S); + INLINE ConfigString GetMyDir(void); + INLINE ConfigString GetUserDir(ConfigString S); + ConfigString Expand(ConfigString S); + Base_Expander() {} + public: + Base_Expander(ConfigString S) : _result(Base_Expander::Expand(S)) {} + Base_Expander(const Base_Expander& c) : _result(c._result) {} + ~Base_Expander() {} + INLINE ConfigString operator()(void); + INLINE ConfigString operator()(ConfigString); + INLINE operator ConfigString(); +}; + +typedef Base_Expander Expander; + +INLINE ConfigString Expand(ConfigString S); + +#include "expand.I" + +} // Close namespace Expand + +#endif /* __EXPAND_H__ */ diff --git a/dtool/src/dconfig/notify.I b/dtool/src/dconfig/notify.I new file mode 100644 index 0000000000..4c9752250c --- /dev/null +++ b/dtool/src/dconfig/notify.I @@ -0,0 +1,5 @@ +// Filename: notify.I +// Created by: drose (28Feb00) +// +//////////////////////////////////////////////////////////////////// + diff --git a/dtool/src/dconfig/notify.cxx b/dtool/src/dconfig/notify.cxx new file mode 100644 index 0000000000..1d349a9a9b --- /dev/null +++ b/dtool/src/dconfig/notify.cxx @@ -0,0 +1,425 @@ +// Filename: notify.C +// Created by: drose (28Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "notify.h" +#include "config_notify.h" +#include "dconfig.h" + +#include + +Notify *Notify::_global_ptr = (Notify *)NULL; + + +//////////////////////////////////////////////////////////////////// +// Function: Notify::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Notify:: +Notify() { + _ostream_ptr = &cerr; + _owns_ostream_ptr = false; + _null_ostream_ptr = new fstream; + + _assert_handler = (AssertHandler *)NULL; + _assert_failed = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +Notify:: +~Notify() { + if (_owns_ostream_ptr) { + delete _ostream_ptr; + } + delete _null_ostream_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::set_ostream_ptr +// Access: Public +// Description: Changes the ostream that all subsequent Notify +// messages will be written to. If the previous ostream +// was set with delete_later = true, this will delete +// the previous ostream. If ostream_ptr is NULL, this +// resets the default to cerr. +//////////////////////////////////////////////////////////////////// +void Notify:: +set_ostream_ptr(ostream *ostream_ptr, bool delete_later) { + if (_owns_ostream_ptr && ostream_ptr != _ostream_ptr) { + delete _ostream_ptr; + } + + if (ostream_ptr == (ostream *)NULL) { + _ostream_ptr = &cerr; + _owns_ostream_ptr = false; + } else { + _ostream_ptr = ostream_ptr; + _owns_ostream_ptr = delete_later; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_ostream_ptr +// Access: Public +// Description: Returns the system-wide ostream for all Notify +// messages. +//////////////////////////////////////////////////////////////////// +ostream *Notify:: +get_ostream_ptr() const { + return _ostream_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_literal_flag +// Access: Public +// Description: Returns a flag that may be set on the Notify stream +// via setf() that, when set, enables "literal" mode, +// which means the Notify stream will not attempt to do +// any fancy formatting (like word-wrapping). +// +// Notify does not itself respect this flag; this is +// left up to the ostream that Notify writes to. Note +// that Notify just maps to cerr by default, in which +// case this does nothing. But the flag is available in +// case any extended types want to make use of it. +//////////////////////////////////////////////////////////////////// +long Notify:: +get_literal_flag() { + static bool got_flag = false; + static long flag; + + if (!got_flag) { +#ifndef WIN32_VC + flag = ios::bitalloc(); +#else + // We lost bitalloc in the new iostream? This feature will just be + // disabled in Windows for now. No big deal. + flag = 0; +#endif + got_flag = true; + } + + return flag; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::set_assert_handler +// Access: Public +// Description: Sets a pointer to a C function that will be called +// when an assertion test fails. This function may +// decide what to do when that happens: it may choose to +// abort or return. If it returns, it should return +// true to indicate that the assertion should be +// respected (and the calling function should return out +// of its block of code), or false to indicate that the +// assertion should be completely ignored. +// +// If an assert handler is installed, it completely +// replaces the default behavior of nassertr() and +// nassertv(). +//////////////////////////////////////////////////////////////////// +void Notify:: +set_assert_handler(Notify::AssertHandler *assert_handler) { + _assert_handler = assert_handler; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::clear_assert_handler +// Access: Public +// Description: Removes the installed assert handler and restores +// default behavior of nassertr() and nassertv(). +//////////////////////////////////////////////////////////////////// +void Notify:: +clear_assert_handler() { + _assert_handler = (AssertHandler *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::has_assert_handler +// Access: Public +// Description: Returns true if a user assert handler has been +// installed, false otherwise. +//////////////////////////////////////////////////////////////////// +bool Notify:: +has_assert_handler() const { + return (_assert_handler != (AssertHandler *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_assert_handler +// Access: Public +// Description: Returns a pointer to the user-installed assert +// handler, if one was installed, or NULL otherwise. +//////////////////////////////////////////////////////////////////// +Notify::AssertHandler *Notify:: +get_assert_handler() const { + return _assert_handler; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::has_assert_failed +// Access: Public +// Description: Returns true if an assertion test has failed (and not +// been ignored) since the last call to +// clear_assert_failed(). +// +// When an assertion test fails, the assert handler +// may decide either to abort, return, or ignore the +// assertion. Naturally, if it decides to abort, this +// flag is irrelevant. If it chooses to ignore the +// assertion, the flag is not set. However, if the +// assert handler chooses to return out of the +// function (the normal case), it will also set this +// flag to indicate that an assertion failure has +// occurred. +// +// This will also be the behavior in the absence of a +// user-defined assert handler. +//////////////////////////////////////////////////////////////////// +bool Notify:: +has_assert_failed() const { + return _assert_failed; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_assert_error_message +// Access: Public +// Description: Returns the error message that corresponds to the +// assertion that most recently failed. +//////////////////////////////////////////////////////////////////// +const string &Notify:: +get_assert_error_message() const { + return _assert_error_message; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::clear_assert_failed +// Access: Public +// Description: Resets the assert_failed flag that is set whenever an +// assertion test fails. See has_assert_failed(). +//////////////////////////////////////////////////////////////////// +void Notify:: +clear_assert_failed() { + _assert_failed = false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_top_category +// Access: Public +// Description: Returns the topmost Category in the hierarchy. This +// may be used to traverse the hierarchy of available +// Categories. +//////////////////////////////////////////////////////////////////// +NotifyCategory *Notify:: +get_top_category() { + return get_category(string()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_category +// Access: Public +// Description: Finds or creates a new Category given the basename of +// the category and its parent in the category +// hierarchy. The parent pointer may be NULL to +// indicate this is a top-level Category. +//////////////////////////////////////////////////////////////////// +NotifyCategory *Notify:: +get_category(const string &basename, NotifyCategory *parent_category) { + // The string should not contain colons. + nassertr(basename.find(':') == string::npos, NULL); + + string fullname; + if (parent_category != (NotifyCategory *)NULL) { + fullname = parent_category->get_fullname() + ":" + basename; + } else { + // The parent_category is NULL. If basename is empty, that means + // we refer to the very top-level category (with an empty + // fullname); otherwise, it's a new category just below that top + // level. + if (!basename.empty()) { + parent_category = get_top_category(); + fullname = ":" + basename; + } + } + + NotifyCategory *category = + new NotifyCategory(fullname, basename, parent_category); + _categories.insert(Categories::value_type(fullname, category)); + return category; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_category +// Access: Public +// Description: Finds or creates a new Category given the basename of +// the category and the fullname of its parent. This is +// another way to create a category when you don't have +// a pointer to its parent handy, but you know the name +// of its parent. If the parent Category does not +// already exist, it will be created. +//////////////////////////////////////////////////////////////////// +NotifyCategory *Notify:: +get_category(const string &basename, const string &parent_fullname) { + return get_category(basename, get_category(parent_fullname)); +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::get_category +// Access: Public +// Description: Finds or creates a new Category given the fullname of +// the Category. This name should be a sequence of +// colon-separated names of parent Categories, ending in +// the basename of this Category, +// e.g. display:glxdisplay. This is a shorthand way to +// define a Category when a pointer to its parent is not +// handy. +//////////////////////////////////////////////////////////////////// +NotifyCategory *Notify:: +get_category(const string &fullname) { + Categories::const_iterator ci; + ci = _categories.find(fullname); + if (ci != _categories.end()) { + return (*ci).second; + } + + // No such Category; create one. First identify the parent name, + // based on the rightmost colon. + NotifyCategory *parent_category = (NotifyCategory *)NULL; + string basename = fullname; + + size_t colon = fullname.rfind(':'); + if (colon != string::npos) { + parent_category = get_category(fullname.substr(0, colon)); + basename = fullname.substr(colon + 1); + + } else if (!fullname.empty()) { + // The fullname didn't begin with a colon. Infer one. + parent_category = get_top_category(); + } + + return get_category(basename, parent_category); +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::out +// Access: Public, Static +// Description: A convenient way to get the ostream that should be +// written to for a Notify-type message. Also see +// Category::out() for a message that is specific to a +// particular Category. +//////////////////////////////////////////////////////////////////// +ostream &Notify:: +out() { + return *(ptr()->_ostream_ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::null +// Access: Public, Static +// Description: A convenient way to get an ostream that doesn't do +// anything. Returned by Category::out() when a +// particular Category and/or Severity is disabled. +//////////////////////////////////////////////////////////////////// +ostream &Notify:: +null() { + return *(ptr()->_null_ostream_ptr); +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::ptr +// Access: Public, Static +// Description: Returns the pointer to the global Notify object. +// There is only one of these in the world. +//////////////////////////////////////////////////////////////////// +Notify *Notify:: +ptr() { + if (_global_ptr == (Notify *)NULL) { + _global_ptr = new Notify; + } + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::assert_failure +// Access: Public +// Description: This function is not intended to be called directly +// by user code. It's called from the nassertr() and +// assertv() macros when an assertion test fails; it +// handles the job of printing the warning message and +// deciding what to do about it. +// +// If this function returns true, the calling function +// should return out of its function; if it returns +// false, the calling function should ignore the +// assertion. +//////////////////////////////////////////////////////////////////// +bool Notify:: +assert_failure(const char *expression, int line, + const char *source_file) { + _assert_failed = true; + + ostringstream message; + message + << expression << " at line " << line << " of " << source_file; + _assert_error_message = message.str(); + + if (has_assert_handler()) { + return (*_assert_handler)(expression, line, source_file); + } + + nout << "Assertion failed: " << _assert_error_message << "\n"; + + if (get_assert_abort()) { + abort(); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Notify::string_severity +// Access: Public +// Description: Given a string, one of "debug", "info", "warning", +// etc., return the corresponding Severity level, or +// NS_unspecified if none of the strings matches. +//////////////////////////////////////////////////////////////////// +NotifySeverity Notify:: +string_severity(const string &str) { + // Convert the string to lowercase for a case-insensitive + // comparison. + string lstring; + for (string::const_iterator si = str.begin(); + si != str.end(); + ++si) { + lstring += tolower(*si); + } + + if (lstring == "spam") { + return NS_spam; + + } else if (lstring == "debug") { + return NS_debug; + + } else if (lstring == "info") { + return NS_info; + + } else if (lstring == "warning") { + return NS_warning; + + } else if (lstring == "error") { + return NS_error; + + } else if (lstring == "fatal") { + return NS_fatal; + + } else { + return NS_unspecified; + } +} diff --git a/dtool/src/dconfig/notify.h b/dtool/src/dconfig/notify.h new file mode 100644 index 0000000000..d8114323dd --- /dev/null +++ b/dtool/src/dconfig/notify.h @@ -0,0 +1,145 @@ +// Filename: notify.h +// Created by: drose (28Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NOTIFY_H +#define NOTIFY_H + +#include + +#include "notifyCategory.h" +#include "notifySeverity.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : Notify +// Description : An object that handles general error reporting to the +// user. It contains a pointer to an ostream, initially +// cerr, which can be reset at will to point to +// different output devices, according to the needs of +// the application. All output generated within Panda +// should vector through the Notify ostream. +// +// This also includes a collection of Categories and +// Severities, which may be independently enabled or +// disabled, so that error messages may be squelched or +// respected according to the wishes of the user. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL Notify { +public: + Notify(); + ~Notify(); + + void set_ostream_ptr(ostream *ostream_ptr, bool delete_later); + ostream *get_ostream_ptr() const; + + typedef bool AssertHandler(const char *expression, int line, + const char *source_file); + + void set_assert_handler(AssertHandler *assert_handler); + void clear_assert_handler(); + bool has_assert_handler() const; + AssertHandler *get_assert_handler() const; + + bool has_assert_failed() const; + const string &get_assert_error_message() const; + void clear_assert_failed(); + + NotifyCategory *get_top_category(); + NotifyCategory *get_category(const string &basename, + NotifyCategory *parent_category); + NotifyCategory *get_category(const string &basename, + const string &parent_fullname); + NotifyCategory *get_category(const string &fullname); + + static ostream &out(); + static ostream &null(); + static Notify *ptr(); + + static long get_literal_flag(); + + bool assert_failure(const char *expression, int line, + const char *source_file); + + static NotifySeverity string_severity(const string &string); + +private: + ostream *_ostream_ptr; + bool _owns_ostream_ptr; + ostream *_null_ostream_ptr; + + AssertHandler *_assert_handler; + bool _assert_failed; + string _assert_error_message; + + typedef map Categories; + Categories _categories; + + static Notify *_global_ptr; +}; + + +// This defines the symbol nout in the same way that cerr and cout are +// defined, for compactness of C++ code that uses Notify in its +// simplest form. Maybe it's a good idea to define this symbol and +// maybe it's not, but it does seem that "nout" isn't likely to +// collide with any other name. + +#define nout (Notify::out()) + +// Here are a couple of assert-type functions. These are designed to +// avoid simply dumping core, since that's quite troublesome when the +// programmer is working in a higher-level environment that is calling +// into the C++ layer. + +// nassertr() is intended to be used in functions that have return +// values; it returns the indicated value if the assertion fails. + +// nassertv() is intended to be used in functions that do not have +// return values; it simply returns if the assertion fails. + +// nassertd() does not return from the function, but instead executes +// the following block of code (like an if statement) if the assertion +// fails. + +#ifdef NDEBUG + +#define nassertr(condition, return_value) +#define nassertv(condition) +#define nassertd(condition) if (false) +// We trust the compiler to optimize the above out. + +#else // NDEBUG + +#define nassertr(condition, return_value) \ + { \ + if (!(condition)) { \ + if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \ + return return_value; \ + } \ + } \ + } + +#define nassertv(condition) \ + { \ + if (!(condition)) { \ + if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \ + return; \ + } \ + } \ + } + +#define nassertd(condition) \ + if (!(condition) && \ + Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) + +#endif // NDEBUG + + +#include "notify.I" + +#endif diff --git a/dtool/src/dconfig/notifyCategory.I b/dtool/src/dconfig/notifyCategory.I new file mode 100644 index 0000000000..12ae3c1f61 --- /dev/null +++ b/dtool/src/dconfig/notifyCategory.I @@ -0,0 +1,178 @@ +// Filename: notifyCategory.I +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::get_fullname +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string NotifyCategory:: +get_fullname() const { + return _fullname; +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::get_basename +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE string NotifyCategory:: +get_basename() const { + return _basename; +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::get_severity +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE NotifySeverity NotifyCategory:: +get_severity() const { + return _severity; +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::set_severity +// Access: Public +// Description: Sets the severity level of messages that will be +// reported from this Category. This allows any message +// of this severity level or higher. +//////////////////////////////////////////////////////////////////// +INLINE void NotifyCategory:: +set_severity(NotifySeverity severity) { + _severity = severity; +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::is_on +// Access: Public +// Description: Returns true if messages of the indicated severity +// level ought to be reported for this Category. +//////////////////////////////////////////////////////////////////// +INLINE bool NotifyCategory:: +is_on(NotifySeverity severity) const { + return (int)severity >= (int)_severity; +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::is_spam +// Access: Public +// Description: A shorthand way to write is_on(NS_spam). +//////////////////////////////////////////////////////////////////// +INLINE bool NotifyCategory:: +is_spam() const { + return is_on(NS_spam); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::is_debug +// Access: Public +// Description: A shorthand way to write is_on(NS_debug). +//////////////////////////////////////////////////////////////////// +INLINE bool NotifyCategory:: +is_debug() const { + return is_on(NS_debug); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::is_info +// Access: Public +// Description: A shorthand way to write is_on(NS_info). +//////////////////////////////////////////////////////////////////// +INLINE bool NotifyCategory:: +is_info() const { + return is_on(NS_info); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::is_warning +// Access: Public +// Description: A shorthand way to write is_on(NS_warning). +//////////////////////////////////////////////////////////////////// +INLINE bool NotifyCategory:: +is_warning() const { + return is_on(NS_warning); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::is_error +// Access: Public +// Description: A shorthand way to write is_on(NS_error). +//////////////////////////////////////////////////////////////////// +INLINE bool NotifyCategory:: +is_error() const { + return is_on(NS_error); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::is_fatal +// Access: Public +// Description: A shorthand way to write is_on(NS_fatal). +//////////////////////////////////////////////////////////////////// +INLINE bool NotifyCategory:: +is_fatal() const { + return is_on(NS_fatal); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::spam +// Access: Public +// Description: A shorthand way to write out(NS_spam). +//////////////////////////////////////////////////////////////////// +INLINE ostream &NotifyCategory:: +spam(bool prefix) const { + return out(NS_spam, prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::debug +// Access: Public +// Description: A shorthand way to write out(NS_debug). +//////////////////////////////////////////////////////////////////// +INLINE ostream &NotifyCategory:: +debug(bool prefix) const { + return out(NS_debug, prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::info +// Access: Public +// Description: A shorthand way to write out(NS_info). +//////////////////////////////////////////////////////////////////// +INLINE ostream &NotifyCategory:: +info(bool prefix) const { + return out(NS_info, prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::warning +// Access: Public +// Description: A shorthand way to write out(NS_warning). +//////////////////////////////////////////////////////////////////// +INLINE ostream &NotifyCategory:: +warning(bool prefix) const { + return out(NS_warning, prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::error +// Access: Public +// Description: A shorthand way to write out(NS_error). +//////////////////////////////////////////////////////////////////// +INLINE ostream &NotifyCategory:: +error(bool prefix) const { + return out(NS_error, prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::fatal +// Access: Public +// Description: A shorthand way to write out(NS_fatal). +//////////////////////////////////////////////////////////////////// +INLINE ostream &NotifyCategory:: +fatal(bool prefix) const { + return out(NS_fatal, prefix); +} diff --git a/dtool/src/dconfig/notifyCategory.cxx b/dtool/src/dconfig/notifyCategory.cxx new file mode 100644 index 0000000000..d5aa40dc92 --- /dev/null +++ b/dtool/src/dconfig/notifyCategory.cxx @@ -0,0 +1,122 @@ +// Filename: notifyCategory.C +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "notifyCategory.h" +#include "notify.h" +#include "config_notify.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +NotifyCategory:: +NotifyCategory(const string &fullname, const string &basename, + NotifyCategory *parent) : + _fullname(fullname), + _basename(basename), + _parent(parent) +{ + if (_parent != (NotifyCategory *)NULL) { + _parent->_children.push_back(this); + } + + _severity = NS_unspecified; + + // See if there's a config option to set the severity level for this + // Category. + + string config_name; + + if (_fullname.empty()) { + config_name = "notify-level"; + + } else if (!_basename.empty()) { + config_name = "notify-level-" + _basename; + } + + if (!config_name.empty()) { + string severity_name; + if (!config_notify.AmInitializing()) + severity_name = config_notify.GetString(config_name, ""); + if (!severity_name.empty()) { + // The user specified a particular severity for this category at + // config time. Use it. + _severity = Notify::string_severity(severity_name); + + if (_severity == NS_unspecified) { + nout << "Invalid severity name for " << config_name << ": " + << severity_name << "\n"; + } + } + } + + if (_severity == NS_unspecified) { + // If we didn't get an explicit severity level, inherit our + // parent's. + if (_parent != (NotifyCategory *)NULL) { + _severity = _parent->_severity; + + } else { + // Unless, of course, we're the root. + _severity = NS_info; + } + } + + // Only the unnamed top category is allowed not to have a parent. + nassertv(_parent != (NotifyCategory *)NULL || _fullname.empty()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::out +// Access: Public +// Description: Begins a new message to this Category at the +// indicated severity level. If the indicated severity +// level is enabled, this writes a prefixing string to +// the Notify::out() stream and returns that. If the +// severity level is disabled, this returns +// Notify::null(). +//////////////////////////////////////////////////////////////////// +ostream &NotifyCategory:: +out(NotifySeverity severity, bool prefix) const { + if (is_on(severity)) { + if (prefix) { + if (severity == NS_info) { + return nout << *this << ": "; + } else { + return nout << *this << "(" << severity << "): "; + } + } else { + return nout; + } + } else { + return Notify::null(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::get_num_children +// Access: Public +// Description: Returns the number of child Categories of this +// particular Category. +//////////////////////////////////////////////////////////////////// +int NotifyCategory:: +get_num_children() const { + return _children.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategory::get_child +// Access: Public +// Description: Returns the nth child Category of this particular +// Category. +//////////////////////////////////////////////////////////////////// +NotifyCategory *NotifyCategory:: +get_child(int i) const { + assert(i >= 0 && i < (int)_children.size()); + return _children[i]; +} diff --git a/dtool/src/dconfig/notifyCategory.h b/dtool/src/dconfig/notifyCategory.h new file mode 100644 index 0000000000..2380790cfb --- /dev/null +++ b/dtool/src/dconfig/notifyCategory.h @@ -0,0 +1,72 @@ +// Filename: notifyCategory.h +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NOTIFYCATEGORY_H +#define NOTIFYCATEGORY_H + +#include + +#include "notifySeverity.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : NotifyCategory +// Description : A particular category of error messages. Typically +// there will be one of these per package, so that we +// can turn on or off error messages at least at a +// package level; further nested categories can be +// created within a package if a finer grain of control +// is required. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL NotifyCategory { +private: + NotifyCategory(const string &fullname, const string &basename, + NotifyCategory *parent); + +public: + INLINE string get_fullname() const; + INLINE string get_basename() const; + INLINE NotifySeverity get_severity() const; + INLINE void set_severity(NotifySeverity severity); + + INLINE bool is_on(NotifySeverity severity) const; + INLINE bool is_spam() const; + INLINE bool is_debug() const; + INLINE bool is_info() const; + INLINE bool is_warning() const; + INLINE bool is_error() const; + INLINE bool is_fatal() const; + + ostream &out(NotifySeverity severity, bool prefix = true) const; + INLINE ostream &spam(bool prefix = true) const; + INLINE ostream &debug(bool prefix = true) const; + INLINE ostream &info(bool prefix = true) const; + INLINE ostream &warning(bool prefix = true) const; + INLINE ostream &error(bool prefix = true) const; + INLINE ostream &fatal(bool prefix = true) const; + + int get_num_children() const; + NotifyCategory *get_child(int i) const; + +private: + string _fullname; + string _basename; + NotifyCategory *_parent; + NotifySeverity _severity; + typedef vector Children; + Children _children; + + friend class Notify; +}; + +INLINE ostream &operator << (ostream &out, const NotifyCategory &cat) { + return out << cat.get_fullname(); +} + +#include "notifyCategory.I" + +#endif diff --git a/dtool/src/dconfig/notifyCategoryProxy.I b/dtool/src/dconfig/notifyCategoryProxy.I new file mode 100644 index 0000000000..0f6dae62f1 --- /dev/null +++ b/dtool/src/dconfig/notifyCategoryProxy.I @@ -0,0 +1,250 @@ +// Filename: notifyCategoryProxy.I +// Created by: drose (04Mar00) +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::init() +// Access: Public +// Description: Initializes the proxy object by calling +// get_category() on the template class. +//////////////////////////////////////////////////////////////////// +template +NotifyCategory *NotifyCategoryProxy:: +init() { + if (_ptr == (NotifyCategory *)NULL) { + _ptr = GetCategory::get_category(); + } + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::get_unsafe_ptr() +// Access: Public +// Description: Returns a pointer which is assumed to have been +// already initialized. This function should only be +// used in functions that will certainly not execute at +// static init time. All of the category methods that +// are accessed via the dot operator, e.g. proxy.info(), +// use this method. +//////////////////////////////////////////////////////////////////// +template +INLINE NotifyCategory *NotifyCategoryProxy:: +get_unsafe_ptr() { + nassertr(_ptr != (NotifyCategory *)NULL, init()); + return _ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::get_safe_ptr() +// Access: Public +// Description: Returns a pointer which is *not* assumed to have been +// already initialized; if necessary, it will be +// initialized before it returns. This function may be +// used in functions that might execute at static init +// time. All of the category methods that are accessed +// via the arrow operator, e.g. proxy->info(), use this +// method. +//////////////////////////////////////////////////////////////////// +template +INLINE NotifyCategory *NotifyCategoryProxy:: +get_safe_ptr() { + return init(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::is_on +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool NotifyCategoryProxy:: +is_on(NotifySeverity severity) { + return get_unsafe_ptr()->is_on(severity); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::is_spam +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool NotifyCategoryProxy:: +is_spam() { + return get_unsafe_ptr()->is_spam(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::is_debug +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool NotifyCategoryProxy:: +is_debug() { + return get_unsafe_ptr()->is_debug(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::is_info +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool NotifyCategoryProxy:: +is_info() { + return get_unsafe_ptr()->is_info(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::is_warning +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool NotifyCategoryProxy:: +is_warning() { + return get_unsafe_ptr()->is_warning(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::is_error +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool NotifyCategoryProxy:: +is_error() { + return get_unsafe_ptr()->is_error(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::is_fatal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE bool NotifyCategoryProxy:: +is_fatal() { + return get_unsafe_ptr()->is_fatal(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::out +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ostream &NotifyCategoryProxy:: +out(NotifySeverity severity, bool prefix) { + return get_unsafe_ptr()->out(severity, prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::spam +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ostream &NotifyCategoryProxy:: +spam(bool prefix) { + return get_unsafe_ptr()->spam(prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::debug +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ostream &NotifyCategoryProxy:: +debug(bool prefix) { + return get_unsafe_ptr()->debug(prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::info +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ostream &NotifyCategoryProxy:: +info(bool prefix) { + return get_unsafe_ptr()->info(prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::warning +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ostream &NotifyCategoryProxy:: +warning(bool prefix) { + return get_unsafe_ptr()->warning(prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::error +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ostream &NotifyCategoryProxy:: +error(bool prefix) { + return get_unsafe_ptr()->error(prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::fatal +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE ostream &NotifyCategoryProxy:: +fatal(bool prefix) { + return get_unsafe_ptr()->fatal(prefix); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::Member Access Operator +// Access: Public +// Description: This magic operator function defines the syntax +// proxy->info(), etc., for all of the methods that are +// defined for NotifyCategory. It's designed to vector +// through get_safe_ptr(), so this syntax is safe for +// functions that may execute at static init time. +//////////////////////////////////////////////////////////////////// +template +INLINE NotifyCategory *NotifyCategoryProxy:: +operator -> () { + return get_safe_ptr(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::Dereference Operator +// Access: Public +// Description: This operator handles the case of dereferencing the +// proxy object as if it were a pointer, +// e.g. (*proxy).info(). It works the same way as the +// -> operator, above. +//////////////////////////////////////////////////////////////////// +template +INLINE NotifyCategory &NotifyCategoryProxy:: +operator * () { + return *get_safe_ptr(); +} + +//////////////////////////////////////////////////////////////////// +// Function: NotifyCategoryProxy::Typecast Operator +// Access: Public +// Description: This operator handles the case of passing the +// proxy object to a function that accepts a +// NotifyCategory pointer. It works the same way as the +// -> and * operators, above. +//////////////////////////////////////////////////////////////////// +template +INLINE NotifyCategoryProxy:: +operator NotifyCategory * () { + return get_safe_ptr(); +} diff --git a/dtool/src/dconfig/notifyCategoryProxy.h b/dtool/src/dconfig/notifyCategoryProxy.h new file mode 100644 index 0000000000..7cbe1a27bd --- /dev/null +++ b/dtool/src/dconfig/notifyCategoryProxy.h @@ -0,0 +1,162 @@ +// Filename: notifyCategoryProxy.h +// Created by: drose (04Mar00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NOTIFYCATEGORYPROXY_H +#define NOTIFYCATEGORYPROXY_H + +#include + +#include "notifyCategory.h" +#include "notifySeverity.h" +#include "notify.h" + +//////////////////////////////////////////////////////////////////// +// Class : NotifyCategoryProxy +// Description : A handy wrapper around a NotifyCategory pointer. +// This wrapper pretends to be a NotifyCategory object +// itself, except that it is capable of initializing its +// pointer if it is NULL. +// +// The advantage to this over a normal pointer is that +// it can be used in functions that run at static init +// time, without worrying about ordering issues among +// static init routines. If the pointer hasn't been +// initialized yet, no sweat; it can initialize itself. +// +// This must be a template class so it can do this +// magic; it templates on a class with a static method +// called get_category() that returns a new pointer to +// the NotifyCategory. This way the compiler can +// generate correct static-init-independent code to +// initialize the proxy. +// +// In general, if the proxy object is treated as if it +// were itself a NotifyCategory object, then it doesn't +// check whether its category is initialized, and so may +// not be run at static init time. That is, you may +// call proxy.info(), but only when you are not running +// at static init time. This is an optimization so you +// can avoid this unnecessary check when you know (as in +// most cases) the code does not run at static init. +// +// On the other hand, if the proxy object is treated as +// if it were a *pointer* to a NotifyCategory object, +// then it *does* check whether its category is +// initialized; you may safely use it in this way at +// static init time. Thus, you may call proxy->info() +// safely whenever you like. +//////////////////////////////////////////////////////////////////// +template +class NotifyCategoryProxy { +public: + // This should be set to be called at static init time; it + // initializes the pointer if it is not already. + NotifyCategory *init(); + + // You don't normally need to call these directly, but they're here + // anyway. get_unsafe_ptr() assumes the pointer has been + // initialized; it should be called only when you know static init + // has completed (i.e. in any function that is not executing at + // static init time). get_safe_ptr() should be called when it is + // possible that static init has not yet completed (i.e. in a + // function that might execute at static init time); it calls init() + // first. + INLINE NotifyCategory *get_unsafe_ptr(); + INLINE NotifyCategory *get_safe_ptr(); + + // The following functions, which may be accessed using the + // proxy.function() syntax, call get_unsafe_ptr(). They should be + // used only in non-static-init functions. + + INLINE bool is_on(NotifySeverity severity); + INLINE bool is_spam(); + INLINE bool is_debug(); + INLINE bool is_info(); + INLINE bool is_warning(); + INLINE bool is_error(); + INLINE bool is_fatal(); + + INLINE ostream &out(NotifySeverity severity, bool prefix = true); + INLINE ostream &spam(bool prefix = true); + INLINE ostream &debug(bool prefix = true); + INLINE ostream &info(bool prefix = true); + INLINE ostream &warning(bool prefix = true); + INLINE ostream &error(bool prefix = true); + INLINE ostream &fatal(bool prefix = true); + + // The same functions as above, when accessed using + // proxy->function() syntax, call get_safe_ptr(). These can be used + // safely either in static-init or non-static-init functions. + INLINE NotifyCategory *operator -> (); + INLINE NotifyCategory &operator * (); + INLINE operator NotifyCategory * (); + +private: + NotifyCategory *_ptr; +}; + +template +INLINE ostream &operator << (ostream &out, NotifyCategoryProxy &proxy) { + return out << proxy->get_fullname(); +} + +// Finally, here is a set of handy macros to define and reference a +// NotifyCategoryProxy object in each package. + +// Following the config convention, this macro defines an external +// reference to a suitable NotifyCategoryProxy object; it should +// appear in the config_*.h file. The proxy object will be named +// basename_cat. + +#if defined(WIN32_VC) && !defined(CPPPARSER) + +#define NotifyCategoryDecl(basename, expcl, exptp) \ + class expcl NotifyCategoryGetCategory_ ## basename { \ + public: \ + NotifyCategoryGetCategory_ ## basename(); \ + static NotifyCategory *get_category(); \ + }; \ + exptp template class expcl NotifyCategoryProxy; \ + extern expcl NotifyCategoryProxy basename ## _cat; + +#else // WIN32_VC + +#define NotifyCategoryDecl(basename, expcl, exptp) \ + class expcl NotifyCategoryGetCategory_ ## basename { \ + public: \ + NotifyCategoryGetCategory_ ## basename(); \ + static NotifyCategory *get_category(); \ + }; \ + extern expcl NotifyCategoryProxy basename ## _cat; + +#endif // WIN32_VC + +// This macro defines the actual declaration of the +// NotifyCategoryProxy object defined above; it should appear in the +// config_*.C file. In this macro, parent_category may either be the +// NotifyCategoryProxy object of the parent category +// (e.g. parent_cat), or it may be the quoted fullname of the parent. + +#ifdef CPPPARSER +#define NotifyCategoryDef(basename, parent_category) + +#else +#define NotifyCategoryDef(basename, parent_category) \ + NotifyCategoryProxy basename ## _cat; \ + static NotifyCategoryGetCategory_ ## basename force_init_ ## basename ## _cat; \ + NotifyCategoryGetCategory_ ## basename:: \ + NotifyCategoryGetCategory_ ## basename() { \ + basename ## _cat.init(); \ + } \ + NotifyCategory *NotifyCategoryGetCategory_ ## basename:: \ + get_category() { \ + return Notify::ptr()->get_category(string(#basename), parent_category); \ + } +#endif // CPPPARSER + + +#include "notifyCategoryProxy.I" + +#endif diff --git a/dtool/src/dconfig/notifySeverity.cxx b/dtool/src/dconfig/notifySeverity.cxx new file mode 100644 index 0000000000..05bb17d6c2 --- /dev/null +++ b/dtool/src/dconfig/notifySeverity.cxx @@ -0,0 +1,33 @@ +// Filename: notifySeverity.C +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + +#include "notifySeverity.h" + +ostream &operator << (ostream &out, NotifySeverity severity) { + switch (severity) { + case NS_spam: + return out << "spam"; + + case NS_debug: + return out << "debug"; + + case NS_info: + return out << "info"; + + case NS_warning: + return out << "warning"; + + case NS_error: + return out << "error"; + + case NS_fatal: + return out << "fatal"; + + case NS_unspecified: + return out << "unspecified"; + } + + return out << "**invalid severity**"; +} diff --git a/dtool/src/dconfig/notifySeverity.h b/dtool/src/dconfig/notifySeverity.h new file mode 100644 index 0000000000..1b60c92292 --- /dev/null +++ b/dtool/src/dconfig/notifySeverity.h @@ -0,0 +1,23 @@ +// Filename: notifySeverity.h +// Created by: drose (29Feb00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef NOTIFYSEVERITY_H +#define NOTIFYSEVERITY_H + +#include + +enum NotifySeverity { + NS_unspecified, // Never used, a special case internally. + NS_spam, + NS_debug, + NS_info, + NS_warning, + NS_error, // Specifically, a recoverable error. + NS_fatal // A nonrecoverable error--expect abort() or core dump. +}; + +EXPCL_DTOOL ostream &operator << (ostream &out, NotifySeverity severity); + +#endif diff --git a/dtool/src/dconfig/serialization.I b/dtool/src/dconfig/serialization.I new file mode 100644 index 0000000000..c777b3317a --- /dev/null +++ b/dtool/src/dconfig/serialization.I @@ -0,0 +1,12 @@ +// Filename: serialization.I +// Created by: cary (20Mar00) +// +//////////////////////////////////////////////////////////////////// + +INLINE int Length(char) { + return 1; +} + +INLINE int Length(char* x) { + return strlen(x); +} diff --git a/dtool/src/dconfig/serialization.h b/dtool/src/dconfig/serialization.h new file mode 100644 index 0000000000..73efccdbac --- /dev/null +++ b/dtool/src/dconfig/serialization.h @@ -0,0 +1,175 @@ +// Filename: serialization.h +// Created by: cary (26Aug98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __SERIALIZATION_H__ +#define __SERIALIZATION_H__ + +#include + +#include "config_setup.h" + +namespace Serialize { + +template +class StdIns { + public: + INLINE ConfigString operator()(const X& val) { + ostringstream oss; + oss << val; + return oss.str(); + } +}; + +template +class StdExt { + public: + INLINE X operator()(ConfigString S) { + istringstream iss(S); + X ret; + iss >> ret; + return ret; + } +}; + +template +INLINE int Length(X c) { + return c.length(); +} + +template > +class Serializer { + private: + ConfigString _result; + + ConfigString SerializeToString(const Collection&, const ConfigString&); + Serializer() {} + public: + Serializer(const Collection& C, ConfigString Delim = ":") : + _result(Serializer::SerializeToString(C, Delim)) {} + Serializer(const Serializer& c) : + _result(c._result) {} + ~Serializer() {} + INLINE ConfigString operator()() { return _result; } + INLINE ConfigString operator()(const Collection& C, + const ConfigString& Delim = ":") { + _result = SerializeToString(C, Delim); + return _result; + } + INLINE operator ConfigString() { return _result; } +}; + +template +ConfigString +Serializer::SerializeToString(const Collection& C, + const ConfigString& Delim) +{ + ConfigString ret; + Inserter in; + + for (TYPENAME Collection::const_iterator i=C.begin(); i!=C.end(); ++i) { + if (i != C.begin()) + ret += Delim; + ret += in(*i); + } + return ret; +} + +template > +class Deserializer { + private: + Collection _result; + + INLINE void Clear() { _result.erase(_result.begin(), _result.end()); } + template + int FindFirstOfInString(ConfigString S, ForwardIterator DelimBegin, + ForwardIterator DelimEnd) { + int i = ConfigString::npos; + ForwardIterator j = DelimBegin; + + while (j != DelimEnd) { + int k = S.find(*j); + if (k != ConfigString::npos) + if ((i == ConfigString::npos) || (i > k)) + i = k; + ++j; + } + return i; + } + template + int FindFirstNotOfInString(ConfigString S, ForwardIterator DelimBegin, + ForwardIterator DelimEnd) { + int i = ConfigString::npos; + ForwardIterator j = DelimBegin; + ForwardIterator k = DelimBegin; + + while (j != DelimEnd) { + int l = S.find(*j); + if (l != ConfigString::npos) + if ((i == ConfigString::npos) || (i > l)) { + i = l; + k = j; + } + ++j; + } + if (i != ConfigString::npos) { + i += Serialize::Length(*k); + if (i >= S.length()) + i = ConfigString::npos; + } + return i; + } + template + void DeserializeFromString(ConfigString S, ForwardIterator DelimBegin, + ForwardIterator DelimEnd) { + Clear(); + Extractor ex; + + while (!S.empty()) { + int i = FindFirstOfInString(S, DelimBegin, DelimEnd); + _result.push_back(ex(S.substr(0, i))); + S.erase(0, i); + i = FindFirstNotOfInString(S, DelimBegin, DelimEnd); + S.erase(0, i); + } + } + void DeserializeFromString(ConfigString S, ConfigString Delim) { + Clear(); + Extractor ex; + + while (!S.empty()) { + size_t i = S.find_first_of(Delim); + _result.push_back(ex(S.substr(0, i))); + if (i == ConfigString::npos) + S.erase(0, i); + else + S.erase(0, i+1); + } + } + Deserializer() {} + public: + Deserializer(ConfigString S, ConfigString Delim = ":") { + Deserializer::DeserializeFromString(S, Delim); + } + template + Deserializer(ConfigString S, ForwardIterator DelimBegin, + ForwardIterator DelimEnd) { + Deserializer::DeserializeFromString(S, DelimBegin, DelimEnd); + } + ~Deserializer() {} + INLINE const Collection& operator()() { return _result; } + template + INLINE const Collection& operator()(ConfigString S, + ForwardIterator DelimBegin, + ForwardIterator DelimEnd) { + Deserializer::DeserializeFromString(S, DelimBegin, DelimEnd); + } + INLINE operator const Collection&() { return _result; } +}; + +#include "serialization.I" + +} // close Serialize namespace + +#endif /* __SERIALIZATION_H__ */ diff --git a/dtool/src/dconfig/symbolEnt.I b/dtool/src/dconfig/symbolEnt.I new file mode 100644 index 0000000000..2948b7668f --- /dev/null +++ b/dtool/src/dconfig/symbolEnt.I @@ -0,0 +1,44 @@ +// Filename: symbolEnt.I +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + + +INLINE SymbolEnt& SymbolEnt::operator=(const SymbolEnt& c) { + _src = c._src; + _val = c._val; + _srctok = c._srctok; + _state = c._state; + return *this; +} + +INLINE bool SymbolEnt:: +operator == (const SymbolEnt &) const { + return true; +} + +INLINE bool SymbolEnt:: +operator != (const SymbolEnt &) const { + return false; +} + +INLINE bool SymbolEnt:: +operator < (const SymbolEnt &) const { + return false; +} + +INLINE ConfigString SymbolEnt::Src(void) const { + return _src; +} + +INLINE ConfigString SymbolEnt::Val(void) const { + return _val; +} + +INLINE SymbolEnt::SymbolEntSrc SymbolEnt::SrcTok(void) const { + return _srctok; +} + +INLINE bool SymbolEnt::State(void) const { + return _state; +} diff --git a/dtool/src/dconfig/symbolEnt.cxx b/dtool/src/dconfig/symbolEnt.cxx new file mode 100644 index 0000000000..a94de10417 --- /dev/null +++ b/dtool/src/dconfig/symbolEnt.cxx @@ -0,0 +1,11 @@ +// Filename: symbolEnt.C +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#include "symbolEnt.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/dtool/src/dconfig/symbolEnt.h b/dtool/src/dconfig/symbolEnt.h new file mode 100644 index 0000000000..24bf9520a7 --- /dev/null +++ b/dtool/src/dconfig/symbolEnt.h @@ -0,0 +1,60 @@ +// Filename: symbolEnt.h +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SYMBOLENT_H +#define SYMBOLENT_H + +#include + +#include "config_setup.h" + +#include + +namespace Config { + +class EXPCL_DTOOL SymbolEnt { + public: + enum SymbolEntSrc { ConfigFile, Environment, CommandEnv, Commandline, + Other, Invalid }; + private: + ConfigString _src; + ConfigString _val; + bool _state; + enum SymbolEntSrc _srctok; + public: + SymbolEnt() : _srctok(Invalid) {} + SymbolEnt(enum SymbolEntSrc tok, const ConfigString& val, + const ConfigString& src = "", bool state = false) + : _src(src), _val(val), _state(state), _srctok(tok) {} + SymbolEnt(const SymbolEnt& c) : _src(c._src), _val(c._val), + _state(c._state), _srctok(c._srctok) {} + INLINE SymbolEnt& operator=(const SymbolEnt&); + + // We need these so we can explicitly instantiate the vector, + // below. They're not actually used and do nothing useful. + INLINE bool operator == (const SymbolEnt &other) const; + INLINE bool operator != (const SymbolEnt &other) const; + INLINE bool operator < (const SymbolEnt &other) const; + + INLINE ConfigString Src(void) const; + INLINE ConfigString Val(void) const; + INLINE SymbolEntSrc SrcTok(void) const; + INLINE bool State(void) const; +}; + +EXPORT_TEMPLATE_CLASS(EXPCL_DTOOL, EXPTP_DTOOL, std::vector); +typedef std::vector vector_SymbolEnt; + +#include "symbolEnt.I" + +} // close Config namespace + + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/dtool/src/dconfig/test_config.cxx b/dtool/src/dconfig/test_config.cxx new file mode 100644 index 0000000000..5e68e82e35 --- /dev/null +++ b/dtool/src/dconfig/test_config.cxx @@ -0,0 +1,24 @@ +// Filename: test_config.C +// Created by: cary (10Sep98) +// +//////////////////////////////////////////////////////////////////// + +#include "dconfig.h" + +#define SNARF +Configure(test); + +std::string foo = test.GetString("user"); +std::string path = test.GetString("LD_LIBRARY_PATH"); + +ConfigureFn(test) +{ + cout << "AIEE! Doing work before main()! The sky is falling!" << endl; +} + +main() +{ + cout << "Testing Configuration functionality:" << endl; + cout << "foo = " << foo << endl; + cout << "path = " << path << endl; +} diff --git a/dtool/src/dconfig/test_expand.cxx b/dtool/src/dconfig/test_expand.cxx new file mode 100644 index 0000000000..b8e7d943bd --- /dev/null +++ b/dtool/src/dconfig/test_expand.cxx @@ -0,0 +1,76 @@ +// Filename: test_expand.C +// Created by: cary (31Aug98) +// +//////////////////////////////////////////////////////////////////// + +#include "expand.h" +#include + +void TestExpandFunction() +{ + std::string line; + + line = "foo"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << Expand::Expand(line) << "'" << endl; + line = "'foo'"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << Expand::Expand(line) << "'" << endl; + line = "'$USER'"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << Expand::Expand(line) << "'" << endl; + line = "$USER"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << Expand::Expand(line) << "'" << endl; + line = "\"$USER\""; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << Expand::Expand(line) << "'" << endl; + line = "`ls -l`"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << Expand::Expand(line) << "'" << endl; + line = "~"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << Expand::Expand(line) << "'" << endl; + line = "~cary"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << Expand::Expand(line) << "'" << endl; +} + +void TestExpandClass() +{ + std::string line; + + line = "foo"; + Expand::Expander ex(line); + cout << "input: '" << line << "'" << endl; + cout << "output: '" << ex() << "'" << endl; + line = "'foo'"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << ex(line) << "'" << endl; + line = "'$USER'"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << ex(line) << "'" << endl; + line = "$USER"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << ex(line) << "'" << endl; + line = "\"$USER\""; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << ex(line) << "'" << endl; + line = "`ls -l`"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << ex(line) << "'" << endl; + line = "~"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << ex(line) << "'" << endl; + line = "~cary"; + cout << "input: '" << line << "'" << endl; + cout << "output: '" << ex(line) << "'" << endl; +} + +main() +{ + cout << endl << "Testing shell expansion (function version):" << endl; + TestExpandFunction(); + cout << endl << "Testing shell expansion (class version):" << endl; + TestExpandClass(); +} diff --git a/dtool/src/dconfig/test_pfstream.cxx b/dtool/src/dconfig/test_pfstream.cxx new file mode 100644 index 0000000000..9001b6561e --- /dev/null +++ b/dtool/src/dconfig/test_pfstream.cxx @@ -0,0 +1,24 @@ +// Filename: test_pfstream.C +// Created by: cary (31Aug98) +// +//////////////////////////////////////////////////////////////////// + +#include +#include + +void ReadIt(istream& ifs) { + std::string line; + + while (!ifs.eof()) { + std::getline(ifs, line); + if (line.length() != 0) + cout << line << endl; + } +} + +main() +{ + ipfstream ipfs("ls -l"); + + ReadIt(ipfs); +} diff --git a/dtool/src/dconfig/test_searchpath.cxx b/dtool/src/dconfig/test_searchpath.cxx new file mode 100644 index 0000000000..7314018c73 --- /dev/null +++ b/dtool/src/dconfig/test_searchpath.cxx @@ -0,0 +1,37 @@ +// Filename: test_searchpath.C +// Created by: cary (01Sep98) +// +//////////////////////////////////////////////////////////////////// + +#include +//#include +#include + +void TestSearch() +{ + std::string line, path; + +// path = ".:~ /etc"; + path = ". /etc"; +// path = Expand::Expand(path); + line = "searchpath.h"; + cout << "looking for file '" << line << "' in path '" << path << "': '"; + line = DSearchPath::search_path(line, path); + cout << line << "'" << endl; + + line = ".cshrc"; + cout << "looking for file '" << line << "' in path '" << path << "': '"; + line = DSearchPath::search_path(line, path); + cout << line << "'" << endl; + + line = "passwd"; + cout << "looking for file '" << line << "' in path '" << path << "': '"; + line = DSearchPath::search_path(line, path); + cout << line << "'" << endl; +} + +main() +{ + cout << "Testing search path:" << endl; + TestSearch(); +} diff --git a/dtool/src/dconfig/test_serialization.cxx b/dtool/src/dconfig/test_serialization.cxx new file mode 100644 index 0000000000..b64667248a --- /dev/null +++ b/dtool/src/dconfig/test_serialization.cxx @@ -0,0 +1,74 @@ +// Filename: test_serialization.C +// Created by: cary (31Aug98) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +typedef std::list intlist; +typedef std::vector floatvec; + +intlist refints; +floatvec reffloats; + +int ints[10] = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3 }; +float floats[10] = { 0.1, 1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0 }; + +void TestToString() +{ + std::string line; + Serialize::Serializer intser(refints); + Serialize::Serializer floatser(reffloats); + + line = intser; + cout << "Int list serialization: '" << line << "'" << endl; + line = floatser; + cout << "Float vector serialization: '" << line << "'" << endl; +} + +void TestFromString() +{ + std::string line; + Serialize::Serializer intser(refints); + Serialize::Serializer floatser(reffloats); + + line = intser; + Serialize::Deserializer intdes1(line); + cout << "Int list deserialization: "; + intlist ides1 = intdes1; + for (intlist::iterator i=ides1.begin(); i!=ides1.end(); ++i) { + if (i != ides1.begin()) + cout << ", "; + cout << (*i); + } + cout << endl; + line = floatser; + Serialize::Deserializer floatdes1(line); + cout << "Float vector deserialization: "; + floatvec fdes1 = floatdes1; + for (floatvec::iterator j=fdes1.begin(); j!=fdes1.end(); ++j) { + if (j != fdes1.begin()) + cout << ", "; + cout << (*j); + } + cout << endl; +} + +main() +{ + // initialize the collections + for (int i=0; i<10; ++i) { + refints.push_back(ints[i]); + reffloats.push_back(floats[i]); + } + + // now run tests + cout << "Serialization to string:" << endl; + TestToString(); + cout << endl << "Deserialization from string:" << endl; + TestFromString(); +} diff --git a/dtool/src/dtoolbase/Sources.pp b/dtool/src/dtoolbase/Sources.pp new file mode 100644 index 0000000000..2326fbeb29 --- /dev/null +++ b/dtool/src/dtoolbase/Sources.pp @@ -0,0 +1,10 @@ +#begin lib_target + #define TARGET dtoolbase + + #define SOURCES \ + dtoolbase.cxx dtoolbase.h dtoolbase_cc.h dtoolsymbols.h \ + fakestringstream.h + + #define INSTALL_HEADERS \ + dtoolbase.h dtoolbase_cc.h dtoolsymbols.h fakestringstream.h +#end lib_target diff --git a/dtool/src/dtoolbase/dtoolbase.cxx b/dtool/src/dtoolbase/dtoolbase.cxx new file mode 100644 index 0000000000..77c08f5580 --- /dev/null +++ b/dtool/src/dtoolbase/dtoolbase.cxx @@ -0,0 +1,6 @@ +// Filename: dtoolbase.cc +// Created by: drose (12Sep00) +// +//////////////////////////////////////////////////////////////////// + +#include "dtoolbase.h" diff --git a/dtool/src/dtoolbase/dtoolbase.h b/dtool/src/dtoolbase/dtoolbase.h new file mode 100644 index 0000000000..3a15836f55 --- /dev/null +++ b/dtool/src/dtoolbase/dtoolbase.h @@ -0,0 +1,84 @@ +/* + * Filename: dtoolbase.h + * Created by: drose (12Sep00) + * + */ + +/* This file is included at the beginning of every header file and/or + C or C++ file. It must be compilable for C as well as C++ files, + so no C++-specific code or syntax can be put here. See + dtoolbase_cc.h for C++-specific stuff. */ + +#ifndef DTOOLBASE_H +#define DTOOLBASE_H + +#include + +#ifdef WIN32_VC +/* These warning pragmas must appear before anything else for VC++ to + respect them. Sheesh. */ + +/* C4231: extern before template instantiation */ +/* For some reason, this particular warning won't disable. */ +#pragma warning (disable : 4231) +/* C4786: 255 char debug symbols */ +#pragma warning (disable : 4786) +/* C4251: needs dll interface */ +#pragma warning (disable : 4251) +/* C4503: decorated name length exceeded */ +#pragma warning (disable : 4503) +/* C4305: truncation from 'const double' to 'float' */ +#pragma warning (disable : 4305) + +#endif /* WIN32_VC */ + +#include "dtoolsymbols.h" + +#ifdef HAVE_MALLOC_H +#include +#endif + +#ifdef HAVE_ALLOCA_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_IO_H +#include +#endif + +#ifdef HAVE_MINMAX_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + + +#ifdef CPPPARSER +#include +#endif + +/* + We define the macros BEGIN_PUBLISH and END_PUBLISH to bracket + functions and global variable definitions that are to be published + via interrogate to scripting languages. + */ +#ifdef CPPPARSER +#define BEGIN_PUBLISH __begin_publish +#define END_PUBLISH __end_publish +#else +#define BEGIN_PUBLISH +#define END_PUBLISH +#endif + +#ifdef __cplusplus +#include "dtoolbase_cc.h" +#endif + +#endif + diff --git a/dtool/src/dtoolbase/dtoolbase_cc.h b/dtool/src/dtoolbase/dtoolbase_cc.h new file mode 100644 index 0000000000..bba2d942fd --- /dev/null +++ b/dtool/src/dtoolbase/dtoolbase_cc.h @@ -0,0 +1,77 @@ +// Filename: dtoolbase_cc.h +// Created by: drose (13Sep00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PANDABASE_CC_H +#define PANDABASE_CC_H + +// This file should never be included directly; it's intended to be +// included only from dtoolbase.h. Include that file instead. + + +#ifdef CPPPARSER +#include +#include + +using namespace std; + +#define INLINE inline +#define TYPENAME typename + +#define EXPORT_TEMPLATE_CLASS(expcl, exptp, classname) + +// We define the macro PUBLISHED to mark C++ methods that are to be +// published via interrogate to scripting languages. However, if +// we're not running the interrogate pass (CPPPARSER isn't defined), +// this maps to public. +#define PUBLISHED __published + +#else // CPPPARSER + +#ifdef HAVE_IOSTREAM +#include +#include +#include +#else +#include +#include +#include +#endif + +#ifdef HAVE_SSTREAM +#include +#else +#include "fakestringstream.h" +#endif + +#include + +#ifdef HAVE_NAMESPACE +using namespace std; +#endif + +#define INLINE inline +#define TYPENAME typename + +#ifdef WIN32_VC +// This macro must be used to export an instantiated template class +// from a DLL. If the template class name itself contains commas, it +// may be necessary to first define a macro for the class name, to +// allow proper macro parameter passing. +#define EXPORT_TEMPLATE_CLASS(expcl, exptp, classname) \ + exptp template class expcl classname; +#else +#define EXPORT_TEMPLATE_CLASS(expcl, exptp, classname) +#endif + +// We define the macro PUBLISHED to mark C++ methods that are to be +// published via interrogate to scripting languages. However, if +// we're not running the interrogate pass (CPPPARSER isn't defined), +// this maps to public. +#define PUBLISHED public + +#endif // CPPPARSER + + +#endif diff --git a/dtool/src/dtoolbase/dtoolsymbols.h b/dtool/src/dtoolbase/dtoolsymbols.h new file mode 100644 index 0000000000..3b4ad021f6 --- /dev/null +++ b/dtool/src/dtoolbase/dtoolsymbols.h @@ -0,0 +1,54 @@ +/* +// Filename: dtoolsymbols.h +// Created by: drose (18Feb00) +// +//////////////////////////////////////////////////////////////////// +*/ + +#ifndef DTOOLSYMBOLS_H +#define DTOOLSYMBOLS_H + + +/* + This file defines a slew of symbols that have particular meaning + only when compiling in the WIN32 environment. These symbols are + prefixed to each class declaration, and each static data member + within a class, that is to visible outside of a DLL. + + We need to specify a particular string for *exporting* the symbols + when we are compiling a DLL, and for *importing* the symbols when + we are accessing the DLL (that is, compiling some code that will + link with the DLL, e.g. an executable program or a different DLL). + The trick is that we have to be particular about whether we're + exporting or importing the symbols for a specific DLL. + + We achieve this by defining a unique pair of symbols for each DLL. + When we are building a particular DLL, we define the manifest + BUILDING_libname on the command line. This file then discovers + that manifest and toggles the flag to *export* only for the symbols + that belong to that DLL; the symbols that belong in each other DLL + are set to *import*. + + Of course, this whole thing only matters under WIN32. In the rest + of the world we don't have to deal with this nonsense, and so we + can define all of these stupid symbols to the empty string. + */ + +#if defined(PENV_WIN32) && !defined(CPPPARSER) + +#ifdef BUILDING_DTOOL + #define EXPCL_DTOOL __declspec(dllexport) + #define EXPTP_DTOOL +#else + #define EXPCL_DTOOL __declspec(dllimport) + #define EXPTP_DTOOL extern +#endif + +#else /* !PENV_WIN32 */ + +#define EXPCL_DTOOL +#define EXPTP_DTOOL + +#endif /* PENV_WIN32 */ + +#endif diff --git a/dtool/src/dtoolbase/fakestringstream.h b/dtool/src/dtoolbase/fakestringstream.h new file mode 100644 index 0000000000..9d8f970512 --- /dev/null +++ b/dtool/src/dtoolbase/fakestringstream.h @@ -0,0 +1,85 @@ +// Filename: fakestringstream.h +// Created by: cary (04Feb99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FAKESTRINGSTREAM_H +#define FAKESTRINGSTREAM_H + +#include +#include +#include + +#ifdef HAVE_NAMESPACE +using namespace std; +#endif + +class fake_istream_buffer { +public: + fake_istream_buffer() { + _len = 0; + _str = ""; + } + fake_istream_buffer(const string &source) { + _len = source.length(); + if (_len == 0) { + _str = ""; + } else { + _str = new char[_len]; + memcpy(_str, source.data(), _len); + } + } + ~fake_istream_buffer() { + if (_len != 0) { + delete[] _str; + } + } + + int _len; + char *_str; +}; + +class istringstream : public fake_istream_buffer, public istrstream { +public: + istringstream(const string &input) : + fake_istream_buffer(input), + istrstream(_str, _len) { } +}; + +class ostringstream : public ostrstream { +public: + string str() { + // We must capture the length before we take the str(). + int length = pcount(); + char *s = ostrstream::str(); + string result(s, length); + delete[] s; + return result; + } +}; + +class stringstream : public fake_istream_buffer, public strstream { +public: + stringstream() : strstream() { + _owns_str = true; + } + stringstream(const string &input) : + fake_istream_buffer(input), + strstream(_str, _len, ios::in) + { + _owns_str = false; + } + string str() { + char *s = strstream::str(); + string result(s); + if (_owns_str) { + delete[] s; + } + return result; + } + +private: + bool _owns_str; +}; + +#endif diff --git a/dtool/src/dtoolutil/Sources.pp b/dtool/src/dtoolutil/Sources.pp new file mode 100644 index 0000000000..0540a716c3 --- /dev/null +++ b/dtool/src/dtoolutil/Sources.pp @@ -0,0 +1,18 @@ +#begin lib_target + #define TARGET dtoolutil + #define LOCAL_LIBS dtoolbase + #define UNIX_SYS_LIBS dl + + #define SOURCES \ + executionEnvironment.I executionEnvironment.cxx \ + executionEnvironment.h filename.I filename.cxx filename.h \ + load_dso.cxx load_dso.h dSearchPath.I dSearchPath.cxx \ + dSearchPath.h pfstream.h vector_string.cxx vector_string.h \ + gnu_getopt.c gnu_getopt.h gnu_getopt1.c + + #define INSTALL_HEADERS \ + executionEnvironment.I executionEnvironment.h filename.I \ + filename.h load_dso.h dSearchPath.I dSearchPath.h \ + pfstream.h vector_string.h gnu_getopt.h + +#end lib_target diff --git a/dtool/src/dtoolutil/dSearchPath.I b/dtool/src/dtoolutil/dSearchPath.I new file mode 100644 index 0000000000..770b74b999 --- /dev/null +++ b/dtool/src/dtoolutil/dSearchPath.I @@ -0,0 +1,21 @@ +// Filename: dSearchPath.I +// Created by: drose (01Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::search_path +// Access: Public, Static +// Description: A quick-and-easy way to search a searchpath for a +// file when you don't feel like building or keeping +// around a DSearchPath object. This simply +// constructs a temporary DSearchPath based on the +// indicated path string, and searches that. +//////////////////////////////////////////////////////////////////// +INLINE Filename DSearchPath:: +search_path(const Filename &filename, const string &path, + const string &delimiters) { + DSearchPath search(path, delimiters); + return search.find_file(filename); +} diff --git a/dtool/src/dtoolutil/dSearchPath.cxx b/dtool/src/dtoolutil/dSearchPath.cxx new file mode 100644 index 0000000000..bf8cd65293 --- /dev/null +++ b/dtool/src/dtoolutil/dSearchPath.cxx @@ -0,0 +1,270 @@ +// Filename: dSearchPath.C +// Created by: drose (01Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "dSearchPath.h" +#include "filename.h" + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DSearchPath::Results:: +Results() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DSearchPath::Results:: +Results(const DSearchPath::Results ©) : + _files(copy._files) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DSearchPath::Results:: +operator = (const DSearchPath::Results ©) { + _files = copy._files; +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DSearchPath::Results:: +~Results() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::clear +// Access: Public +// Description: Removes all the files from the list. +//////////////////////////////////////////////////////////////////// +void DSearchPath::Results:: +clear() { + _files.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::get_num_files +// Access: Public +// Description: Returns the number of files on the result list. +//////////////////////////////////////////////////////////////////// +int DSearchPath::Results:: +get_num_files() const { + return _files.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::get_file +// Access: Public +// Description: Returns the nth file on the result list. +//////////////////////////////////////////////////////////////////// +Filename DSearchPath::Results:: +get_file(int n) const { + assert(n >= 0 && n < (int)_files.size()); + return _files[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Default Constructor +// Access: Public +// Description: Creates an empty search path. +//////////////////////////////////////////////////////////////////// +DSearchPath:: +DSearchPath() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DSearchPath:: +DSearchPath(const string &path, const string &delimiters) { + append_path(path, delimiters); +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DSearchPath:: +DSearchPath(const DSearchPath ©) : + _directories(copy._directories) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DSearchPath:: +operator = (const DSearchPath ©) { + _directories = copy._directories; +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DSearchPath:: +~DSearchPath() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::clear +// Access: Public +// Description: Removes all the directories from the search list. +//////////////////////////////////////////////////////////////////// +void DSearchPath:: +clear() { + _directories.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::append_directory +// Access: Public +// Description: Adds a new directory to the end of the search list. +//////////////////////////////////////////////////////////////////// +void DSearchPath:: +append_directory(const Filename &directory) { + _directories.push_back(directory); +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::append_path +// Access: Public +// Description: Adds all of the directories listed in the search path +// to the end of the search list. +//////////////////////////////////////////////////////////////////// +void DSearchPath:: +append_path(const string &path, const string &delimiters) { + size_t p = 0; + while (p < path.length()) { + size_t q = path.find_first_of(delimiters, p); + if (q == string::npos) { + _directories.push_back(path.substr(p)); + return; + } + if (q != p) { + _directories.push_back(path.substr(p, q - p)); + } + p = q + 1; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::get_num_directories +// Access: Public +// Description: Returns the number of directories on the search list. +//////////////////////////////////////////////////////////////////// +int DSearchPath:: +get_num_directories() const { + return _directories.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::get_directory +// Access: Public +// Description: Returns the nth directory on the search list. +//////////////////////////////////////////////////////////////////// +Filename DSearchPath:: +get_directory(int n) const { + assert(n >= 0 && n < (int)_directories.size()); + return _directories[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::find_file +// Access: Public +// Description: Searches all the directories in the search list for +// the indicated file, in order. Returns the full +// matching pathname of the first match if found, or the +// empty string if not found. +//////////////////////////////////////////////////////////////////// +Filename DSearchPath:: +find_file(const Filename &filename) const { + Directories::const_iterator di; + for (di = _directories.begin(); di != _directories.end(); ++di) { + Filename match((*di), filename); + if (match.exists()) { + return match; + } + } + + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::find_all_files +// Access: Public +// Description: Searches all the directories in the search list for +// the indicated file, in order. Fills up the results +// list with *all* of the matching filenames found, if +// any. Returns the number of matches found. +//////////////////////////////////////////////////////////////////// +int DSearchPath:: +find_all_files(const Filename &filename, + DSearchPath::Results &results) const { + results._files.clear(); + + Directories::const_iterator di; + for (di = _directories.begin(); di != _directories.end(); ++di) { + Filename match((*di), filename); + if (match.exists()) { + results._files.push_back(match); + } + } + + return results._files.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DSearchPath:: +output(ostream &out, const string &separator) const { + if (!_directories.empty()) { + Directories::const_iterator di = _directories.begin(); + out << (*di); + ++di; + while (di != _directories.end()) { + out << separator << (*di); + ++di; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::write +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DSearchPath:: +write(ostream &out, int indent_level) const { + Directories::const_iterator di; + for (di = _directories.begin(); di != _directories.end(); ++di) { + for (int i = 0; i < indent_level; i++) { + out << ' '; + } + out << (*di) << "\n"; + } +} + + diff --git a/dtool/src/dtoolutil/dSearchPath.h b/dtool/src/dtoolutil/dSearchPath.h new file mode 100644 index 0000000000..fa6bcf6980 --- /dev/null +++ b/dtool/src/dtoolutil/dSearchPath.h @@ -0,0 +1,80 @@ +// Filename: dSearchPath.h +// Created by: drose (01Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PANDASEARCHPATH_H +#define PANDASEARCHPATH_H + +#include + +#include "filename.h" + +#include + +/////////////////////////////////////////////////////////////////// +// Class : DSearchPath +// Description : This class stores a list of directories that can be +// searched, in order, to locate a particular file. It +// is normally constructed by passing it a traditional +// searchpath-style string, e.g. a list of directory +// names delimited by spaces or colons, but it can also +// be built up explicitly. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL DSearchPath { +public: + class Results { + public: + Results(); + Results(const Results ©); + void operator = (const Results ©); + ~Results(); + + void clear(); + int get_num_files() const; + Filename get_file(int n) const; + + private: + typedef vector Files; + Files _files; + friend class DSearchPath; + }; + +public: + DSearchPath(); + DSearchPath(const string &path, const string &delimiters = ": \t\n"); + DSearchPath(const DSearchPath ©); + void operator = (const DSearchPath ©); + ~DSearchPath(); + + void clear(); + void append_directory(const Filename &directory); + void append_path(const string &path, + const string &delimiters = ": \t\n"); + + int get_num_directories() const; + Filename get_directory(int n) const; + + Filename find_file(const Filename &filename) const; + int find_all_files(const Filename &filename, Results &results) const; + + INLINE static Filename + search_path(const Filename &filename, const string &path, + const string &delimiters = ": \t\n"); + + void output(ostream &out, const string &separator = ":") const; + void write(ostream &out, int indent_level = 0) const; + +private: + typedef vector Directories; + Directories _directories; +}; + +INLINE ostream &operator << (ostream &out, const DSearchPath &sp) { + sp.output(out); + return out; +} + +#include "dSearchPath.I" + +#endif diff --git a/dtool/src/dtoolutil/executionEnvironment.I b/dtool/src/dtoolutil/executionEnvironment.I new file mode 100644 index 0000000000..992df26e48 --- /dev/null +++ b/dtool/src/dtoolutil/executionEnvironment.I @@ -0,0 +1,63 @@ +// Filename: executionEnvironment.I +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::has_environment_variable +// Access: Public, Static +// Description: Returns true if the indicated environment variable +// is defined. +//////////////////////////////////////////////////////////////////// +INLINE bool ExecutionEnvironment:: +has_environment_variable(const string &var) { + return get_ptr()->ns_has_environment_variable(var); +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::get_environment_variable +// Access: Public, Static +// Description: Returns the definition of the indicated environment +// variable, or the empty string if the variable is +// undefined. +//////////////////////////////////////////////////////////////////// +INLINE string ExecutionEnvironment:: +get_environment_variable(const string &var) { + return get_ptr()->ns_get_environment_variable(var); +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::get_num_args +// Access: Public, Static +// Description: Returns the number of command-line arguments +// available, not counting arg 0, the binary name. +//////////////////////////////////////////////////////////////////// +INLINE int ExecutionEnvironment:: +get_num_args() { + return get_ptr()->ns_get_num_args(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::get_arg +// Access: Public, Static +// Description: Returns the nth command-line argument. The index n +// must be in the range [0 .. get_num_args()). The +// first parameter, n == 0, is the first actual +// parameter, not the binary name. +//////////////////////////////////////////////////////////////////// +INLINE string ExecutionEnvironment:: +get_arg(int n) { + return get_ptr()->ns_get_arg(n); +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::get_binary_name +// Access: Public, Static +// Description: Returns the name of the binary executable that +// started this program, if it can be determined. +//////////////////////////////////////////////////////////////////// +INLINE string ExecutionEnvironment:: +get_binary_name() { + return get_ptr()->ns_get_binary_name(); +} diff --git a/dtool/src/dtoolutil/executionEnvironment.cxx b/dtool/src/dtoolutil/executionEnvironment.cxx new file mode 100644 index 0000000000..6eb583ca34 --- /dev/null +++ b/dtool/src/dtoolutil/executionEnvironment.cxx @@ -0,0 +1,249 @@ +// Filename: executionEnvironment.C +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#include "executionEnvironment.h" +#include + +// We define the symbol PREREAD_ENVIRONMENT if we cannot rely on +// getenv() to read environment variables at static init time. In +// this case, we must read all of the environment variables directly +// and cache them locally. + +#if defined(PENV_LINUX) +#define PREREAD_ENVIRONMENT +#endif + + +// We define the symbol USE_ARGV if we have global variables named +// ARGC/ARGV that we can read at static init time to determine our +// command-line arguments. + +#if defined(WIN32_VC) + #define USE_ARGV + #define ARGV __argv + #define ARGC __argc + +#elif defined(PENV_SGI) + #define USE_ARGV + extern char **__Argv; + extern int __Argc; + #define ARGV __Argv + #define ARGC __Argc +#endif + +// Linux with GNU libc does have global argv/argc variables, but we +// can't safely access them at stat init time--at least, not in libc5. +// (It does seem to work with glibc2, however.) + +ExecutionEnvironment *ExecutionEnvironment::_global_ptr = NULL; + + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::Constructor +// Access: Private +// Description: You shouldn't need to construct one of these; there's +// only one and it constructs itself. +//////////////////////////////////////////////////////////////////// +ExecutionEnvironment:: +ExecutionEnvironment() { + read_environment_variables(); + read_args(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::ns_has_environment_variable +// Access: Private +// Description: Returns true if the indicated environment variable +// is defined. The nonstatic implementation. +//////////////////////////////////////////////////////////////////// +bool ExecutionEnvironment:: +ns_has_environment_variable(const string &var) const { +#ifdef PREREAD_ENVIRONMENT + return _variables.count(var) != 0; +#else + return getenv(var.c_str()) != (char *)NULL; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::ns_get_environment_variable +// Access: Private +// Description: Returns the definition of the indicated environment +// variable, or the empty string if the variable is +// undefined. The nonstatic implementation. +//////////////////////////////////////////////////////////////////// +string ExecutionEnvironment:: +ns_get_environment_variable(const string &var) const { +#ifdef PREREAD_ENVIRONMENT + EnvironmentVariables::const_iterator evi; + evi = _variables.find(var); + if (evi != _variables.end()) { + return (*evi).second; + } + return string(); +#else + const char *def = getenv(var.c_str()); + if (def != (char *)NULL) { + return def; + } + return string(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::ns_get_num_args +// Access: Private +// Description: Returns the number of command-line arguments +// available, not counting arg 0, the binary name. The +// nonstatic implementation. +//////////////////////////////////////////////////////////////////// +int ExecutionEnvironment:: +ns_get_num_args() const { + return _args.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::ns_get_arg +// Access: Private +// Description: Returns the nth command-line argument. The index n +// must be in the range [0 .. get_num_args()). The +// first parameter, n == 0, is the first actual +// parameter, not the binary name. The nonstatic +// implementation. +//////////////////////////////////////////////////////////////////// +string ExecutionEnvironment:: +ns_get_arg(int n) const { + assert(n >= 0 && n < ns_get_num_args()); + return _args[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::ns_get_binary_name +// Access: Private +// Description: Returns the name of the binary executable that +// started this program, if it can be determined. The +// nonstatic implementation. +//////////////////////////////////////////////////////////////////// +string ExecutionEnvironment:: +ns_get_binary_name() const { + if (_binary_name.empty()) { + return "unknown"; + } + return _binary_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::get_ptr +// Access: Private, Static +// Description: Returns a static pointer that may be used to access +// the global ExecutionEnvironment object. +//////////////////////////////////////////////////////////////////// +ExecutionEnvironment *ExecutionEnvironment:: +get_ptr() { + if (_global_ptr == (ExecutionEnvironment *)NULL) { + _global_ptr = new ExecutionEnvironment; + } + return _global_ptr; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::read_environment_variables +// Access: Private +// Description: Fills up the internal table of existing environment +// variables, if we are in PREREAD_ENVIRONMENT mode. +// Otherwise, does nothing. +//////////////////////////////////////////////////////////////////// +void ExecutionEnvironment:: +read_environment_variables() { +#if defined(PENV_LINUX) + // In Linux, we might not be able to use getenv() at static init + // time. However, we may be lucky and have a file called + // /proc/self/environ that may be read to determine all of our + // environment variables. + + ifstream proc("/proc/self/environ"); + if (proc.fail()) { + cerr << "Cannot read /proc/self/environ; environment variables unavailable.\n"; + return; + } + + int ch = proc.get(); + while (!proc.eof() && !proc.fail()) { + string variable; + string value; + + while (!proc.eof() && !proc.fail() && ch != '=' && ch != '\0') { + variable += (char)ch; + ch = proc.get(); + } + + if (ch == '=') { + ch = proc.get(); + while (!proc.eof() && !proc.fail() && ch != '\0') { + value += (char)ch; + ch = proc.get(); + } + } + + if (!variable.empty()) { + _variables[variable] = value; + } + ch = proc.get(); + } +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::read_args +// Access: Private +// Description: Reads all the command-line arguments and the name of +// the binary file, if possible. +//////////////////////////////////////////////////////////////////// +void ExecutionEnvironment:: +read_args() { +#if defined(USE_ARGV) + int argc = ARGC; + if (argc > 0) { + _binary_name = ARGV[0]; + } + + for (int i = 1; i < argc; i++) { + _args.push_back(ARGV[i]); + } + +#elif defined(PENV_LINUX) + // In Linux, we might not be able to use the global ARGC/ARGV + // variables at static init time. However, we may be lucky and have + // a file called /proc/self/cmdline that may be read to determine + // all of our command-line arguments. + + ifstream proc("/proc/self/cmdline"); + if (proc.fail()) { + cerr << "Cannot read /proc/self/cmdline; command-line arguments unavailable to config.\n"; + return; + } + + int ch = proc.get(); + int index = 0; + while (!proc.eof() && !proc.fail()) { + string arg; + + while (!proc.eof() && !proc.fail() && ch != '\0') { + arg += (char)ch; + ch = proc.get(); + } + + if (index == 0) { + _binary_name = arg; + } else { + _args.push_back(arg); + } + index++; + + ch = proc.get(); + } +#endif +} diff --git a/dtool/src/dtoolutil/executionEnvironment.h b/dtool/src/dtoolutil/executionEnvironment.h new file mode 100644 index 0000000000..ce4cb195ea --- /dev/null +++ b/dtool/src/dtoolutil/executionEnvironment.h @@ -0,0 +1,64 @@ +// Filename: executionEnvironment.h +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef EXECUTIONENVIRONMENT_H +#define EXECUTIONENVIRONMENT_H + +#include + +#include "vector_string.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : ExecutionEnvironment +// Description : Encapsulates access to the environment variables and +// command-line arguments at the time of execution. +// This is encapsulated to support accessing these +// things during static init time, which seems to be +// risky at best. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL ExecutionEnvironment { +private: + ExecutionEnvironment(); + +public: + INLINE static bool has_environment_variable(const string &var); + INLINE static string get_environment_variable(const string &var); + + INLINE static int get_num_args(); + INLINE static string get_arg(int n); + + INLINE static string get_binary_name(); + +private: + bool ns_has_environment_variable(const string &var) const; + string ns_get_environment_variable(const string &var) const; + + int ns_get_num_args() const; + string ns_get_arg(int n) const; + + string ns_get_binary_name() const; + + static ExecutionEnvironment *get_ptr(); + + void read_environment_variables(); + void read_args(); + +private: + typedef map EnvironmentVariables; + EnvironmentVariables _variables; + + typedef vector_string CommandArguments; + CommandArguments _args; + + string _binary_name; + + static ExecutionEnvironment *_global_ptr; +}; + +#include "executionEnvironment.I" + +#endif diff --git a/dtool/src/dtoolutil/filename.I b/dtool/src/dtoolutil/filename.I new file mode 100644 index 0000000000..0002b4ddd1 --- /dev/null +++ b/dtool/src/dtoolutil/filename.I @@ -0,0 +1,428 @@ +// Filename: filename.I +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename:: +Filename(const string &filename) { + (*this) = filename; + _flags = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename:: +Filename(const char *filename) { + (*this) = filename; + _flags = 0; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename:: +Filename(const Filename ©) + : _filename(copy._filename), + _dirname_end(copy._dirname_end), + _basename_start(copy._basename_start), + _basename_end(copy._basename_end), + _extension_start(copy._extension_start), + _flags(copy._flags) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::text_filename named constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename Filename:: +text_filename(const string &filename) { + Filename result(filename); + result.set_text(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::binary_filename named constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename Filename:: +binary_filename(const string &filename) { + Filename result(filename); + result.set_binary(); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::dso_filename named constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename Filename:: +dso_filename(const string &filename) { + Filename result(filename); + result.set_type(T_dso); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::executable_filename named constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename Filename:: +executable_filename(const string &filename) { + Filename result(filename); + result.set_type(T_executable); + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename:: +~Filename() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename &Filename:: +operator = (const string &filename) { + _filename = filename; + + locate_basename(); + locate_extension(); + return *this; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename &Filename:: +operator = (const char *filename) { + assert(filename != NULL); + return (*this) = string(filename); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Copy assignment operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename &Filename:: +operator = (const Filename ©) { + _filename = copy._filename; + _dirname_end = copy._dirname_end; + _basename_start = copy._basename_start; + _basename_end = copy._basename_end; + _extension_start = copy._extension_start; + _flags = copy._flags; + return *this; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::string typecast operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Filename:: +operator const string & () const { + return _filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::c_str +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const char *Filename:: +c_str() const { + return _filename.c_str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::empty +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Filename:: +empty() const { + return _filename.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::length +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE size_t Filename:: +length() const { + return _filename.length(); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Indexing operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE char Filename:: +operator [] (int n) const { + assert(n >= 0 && n < (int)_filename.length()); + return _filename[n]; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_fullpath +// Access: Public +// Description: Returns the entire filename: directory, basename, +// extension. This is the same thing returned by the +// string typecast operator, so this function is a +// little redundant. +//////////////////////////////////////////////////////////////////// +INLINE string Filename:: +get_fullpath() const { + return _filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_dirname +// Access: Public +// Description: Returns the directory part of the filename. This is +// everything in the filename up to, but not including +// the rightmost slash. +//////////////////////////////////////////////////////////////////// +INLINE string Filename:: +get_dirname() const { + return _filename.substr(0, _dirname_end); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_basename +// Access: Public +// Description: Returns the basename part of the filename. This is +// everything in the filename after the rightmost slash, +// including any extensions. +//////////////////////////////////////////////////////////////////// +INLINE string Filename:: +get_basename() const { + return _filename.substr(_basename_start); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_fullpath_wo_extension +// Access: Public +// Description: Returns the full filename--directory and basename +// parts--except for the extension. +//////////////////////////////////////////////////////////////////// +INLINE string Filename:: +get_fullpath_wo_extension() const { + return _filename.substr(0, _basename_end); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_basename_wo_extension +// Access: Public +// Description: Returns the basename part of the filename, without +// the file extension. +//////////////////////////////////////////////////////////////////// +INLINE string Filename:: +get_basename_wo_extension() const { + if (_basename_end == string::npos) { + return _filename.substr(_basename_start); + } else { + return _filename.substr(_basename_start, _basename_end - _basename_start); + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_extension +// Access: Public +// Description: Returns the file extension. This is everything after +// the rightmost dot, if there is one, or the empty +// string if there is not. +//////////////////////////////////////////////////////////////////// +INLINE string Filename:: +get_extension() const { + if (_extension_start == string::npos) { + return string(); + } else { + return _filename.substr(_extension_start); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_binary +// Access: Public +// Description: Indicates that the filename represents a binary file. +// This is primarily relevant to the read_file() and +// write_file() methods, so they can set the appropriate +// flags to the OS. +//////////////////////////////////////////////////////////////////// +INLINE void Filename:: +set_binary() { + _flags = (_flags & ~F_text) | F_binary; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_text +// Access: Public +// Description: Indicates that the filename represents a text file. +// This is primarily relevant to the read_file() and +// write_file() methods, so they can set the appropriate +// flags to the OS. +//////////////////////////////////////////////////////////////////// +INLINE void Filename:: +set_text() { + _flags = (_flags & ~F_binary) | F_text; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::is_binary +// Access: Public +// Description: Returns true if the Filename has been indicated to +// represent a binary file via a previous call to +// set_binary(). It is possible that neither +// is_binary() nor is_text() will be true, if neither +// set_binary() nor set_text() was ever called. +//////////////////////////////////////////////////////////////////// +INLINE bool Filename:: +is_binary() const { + return ((_flags & F_binary) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::is_text +// Access: Public +// Description: Returns true if the Filename has been indicated to +// represent a text file via a previous call to +// set_text(). It is possible that neither is_binary() +// nor is_text() will be true, if neither set_binary() +// nor set_text() was ever called. +//////////////////////////////////////////////////////////////////// +INLINE bool Filename:: +is_text() const { + return ((_flags & F_text) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_type +// Access: Public +// Description: Sets the type of the file represented by the +// filename. This is useful for to_os_specific(), +// resolve_filename(), test_existence(), and all such +// real-world access functions. It helps the Filename +// know how to map the internal filename to the +// OS-specific filename (for instance, maybe executables +// should have an .exe extension). +//////////////////////////////////////////////////////////////////// +INLINE void Filename:: +set_type(Filename::Type type) { + _flags = (_flags & ~F_type) | type; + switch (type) { + case T_dso: + case T_executable: + set_binary(); + + default: + ; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_type +// Access: Public +// Description: Returns the type of the file represented by the +// filename, as previously set by set_type(). +//////////////////////////////////////////////////////////////////// +INLINE Filename::Type Filename:: +get_type() const { + return (Type)(_flags & (int)F_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::is_local +// Access: Public +// Description: Returns true if the filename is local, e.g. does not +// begin with a slash, or false if the filename is fully +// specified from the root. +//////////////////////////////////////////////////////////////////// +INLINE bool Filename:: +is_local() const { + return _filename.empty() || _filename[0] != '/'; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Equality operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Filename:: +operator == (const string &other) const { + return (*(string *)this) == other; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Inequality operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Filename:: +operator != (const string &other) const { + return (*(string *)this) != other; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Ordering operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool Filename:: +operator < (const string &other) const { + return (*(string *)this) < other; +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void Filename:: +output(ostream &out) const { + out << _filename; +} diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx new file mode 100644 index 0000000000..3ce530e1ed --- /dev/null +++ b/dtool/src/dtoolutil/filename.cxx @@ -0,0 +1,966 @@ +// Filename: filename.C +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// + +#include "filename.h" +#include "dSearchPath.h" + +#include // For rename() +#include + + +#if defined(WIN32) +/* begin Win32-specific code */ + +static string +front_to_back_slash(const string &str) { + string result = str; + string::iterator si; + for (si = result.begin(); si != result.end(); ++si) { + if ((*si) == '/') { + (*si) = '\\'; + } + } + + return result; +} + +static string +convert_pathname(const string &unix_style_pathname) { + if (unix_style_pathname.empty()) { + return string(); + } + + // To convert from a Unix-style pathname to a Windows-style + // pathname, we need to change all forward slashes to backslashes. + // We might need to add a prefix as well, since Windows pathnames + // typically begin with a drive letter. + + // By convention, if the top directory name consists of just one + // letter, we treat that as a drive letter and map the rest of the + // filename accordingly. On the other hand, if the top directory + // name consists of more than one letter, we assume this is a file + // within some predefined tree whose root is given by the + // environment variable "PANDA_ROOT", or if that is not defined, + // "CYGWIN_ROOT" (for backward compatibility). + string windows_pathname; + + if (unix_style_pathname[0] != '/') { + // It doesn't even start from the root, so we don't have to do + // anything fancy--relative pathnames are the same in Windows as + // in Unix, except for the direction of the slashes. + windows_pathname = front_to_back_slash(unix_style_pathname); + + } else if (unix_style_pathname.length() > 3 && + isalpha(unix_style_pathname[1]) && + unix_style_pathname[2] == '/') { + // This is a pathname that begins with a single letter. That must + // be the drive letter. + windows_pathname = + toupper(unix_style_pathname[1]) + ":" + + front_to_back_slash(unix_style_pathname.substr(2)); + + } else { + // It does not begin with a single letter, so prefix "PANDA_ROOT". + + static string panda_root; + static bool got_panda_root = false; + + if (!got_panda_root) { + const char *envvar = getenv("PANDA_ROOT"); + if (envvar == (const char *)NULL) { + envvar = getenv("CYGWIN_ROOT"); + } + + if (envvar != (const char *)NULL) { + panda_root = front_to_back_slash(envvar); + } + + if (!panda_root.empty() && panda_root[panda_root.length() - 1] != '\\') { + panda_root += '\\'; + } + + got_panda_root = true; + } + + windows_pathname = panda_root + front_to_back_slash(unix_style_pathname); + } + + return windows_pathname; +} + +string +convert_dso_pathname(const string &unix_style_pathname) { + // If the extension is .so, change it to .dll. + size_t dot = unix_style_pathname.rfind('.'); + if (dot == string::npos || + unix_style_pathname.find('/', dot) != string::npos) { + // No filename extension. + return convert_pathname(unix_style_pathname); + } + if (unix_style_pathname.substr(dot) != ".so") { + // Some other extension. + return convert_pathname(unix_style_pathname); + } + + string dll_basename = unix_style_pathname.substr(0, dot); + +#ifdef _DEBUG + // If we're building a debug version, all the dso files we link in + // must be named file_d.dll. This does prohibit us from linking in + // external dso files, generated outside of the Panda build system, + // that don't follow this _d convention. Maybe we need a separate + // convert_system_dso_pathname() function. + + // We can't simply check to see if the file exists, because this + // might not be a full path to the dso filename--it might be + // somewhere on the LD_LIBRARY_PATH, or on PATH, or any of a number + // of nutty places. + + return convert_pathname(dll_basename + "_d.dll"); +#else + return convert_pathname(dll_basename + ".dll"); +#endif +} + +string +convert_executable_pathname(const string &unix_style_pathname) { + // If the extension is not .exe, append .exe. + size_t dot = unix_style_pathname.rfind('.'); + if (dot == string::npos || + unix_style_pathname.find('/', dot) != string::npos) { + // No filename extension. + return convert_pathname(unix_style_pathname + ".exe"); + } + if (unix_style_pathname.substr(dot) != ".exe") { + // Some other extension. + return convert_pathname(unix_style_pathname + ".exe"); + } + + return convert_pathname(unix_style_pathname); +} +#endif //WIN32 + +//////////////////////////////////////////////////////////////////// +// Function: Filename::Constructor +// Access: Public +// Description: This constructor composes the filename out of a +// directory part and a basename part. It will insert +// an intervening '/' if necessary. +//////////////////////////////////////////////////////////////////// +Filename:: +Filename(const Filename &dirname, const Filename &basename) { + (*this) = dirname.get_fullpath() + "/" + basename.get_fullpath(); + _flags = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_fullpath +// Access: Public +// Description: Replaces the entire filename: directory, basename, +// extension. This can also be achieved with the +// assignment operator. +//////////////////////////////////////////////////////////////////// +void Filename:: +set_fullpath(const string &s) { + (*this) = s; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_dirname +// Access: Public +// Description: Replaces the directory part of the filename. This is +// everything in the filename up to, but not including +// the rightmost slash. +//////////////////////////////////////////////////////////////////// +void Filename:: +set_dirname(const string &s) { + if (s.empty()) { + // Remove the directory prefix altogether. + _filename.replace(0, _basename_start, ""); + + int length_change = -_basename_start; + + _dirname_end = 0; + _basename_start += length_change; + _basename_end += length_change; + _extension_start += length_change; + + } else { + // Replace the existing directory prefix, or insert a new one. + + // We build the string ss to include the terminal slash. + string ss; + if (s[s.length()-1] == '/') { + ss = s; + } else { + ss = s+'/'; + } + + int length_change = ss.length() - _basename_start; + + _filename.replace(0, _basename_start, ss); + + _dirname_end = ss.length() - 1; + + // An exception: if the dirname string was the single slash, the + // dirname includes that slash. + if (ss.length() == 1) { + _dirname_end = 1; + } + + _basename_start += length_change; + + if (_basename_end != string::npos) { + _basename_end += length_change; + _extension_start += length_change; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_basename +// Access: Public +// Description: Replaces the basename part of the filename. This is +// everything in the filename after the rightmost slash, +// including any extensions. +//////////////////////////////////////////////////////////////////// +void Filename:: +set_basename(const string &s) { + _filename.replace(_basename_start, string::npos, s); + locate_extension(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_fullpath_wo_extension +// Access: Public +// Description: Replaces the full filename--directory and basename +// parts--except for the extension. +//////////////////////////////////////////////////////////////////// +void Filename:: +set_fullpath_wo_extension(const string &s) { + int length_change = s.length() - _basename_end; + + _filename.replace(0, _basename_end, s); + + if (_basename_end != string::npos) { + _basename_end += length_change; + _extension_start += length_change; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_basename_wo_extension +// Access: Public +// Description: Replaces the basename part of the filename, without +// the file extension. +//////////////////////////////////////////////////////////////////// +void Filename:: +set_basename_wo_extension(const string &s) { + int length_change = s.length() - (_basename_end - _basename_start); + + if (_basename_end == string::npos) { + _filename.replace(_basename_start, string::npos, s); + + } else { + _filename.replace(_basename_start, _basename_end - _basename_start, s); + + _basename_end += length_change; + _extension_start += length_change; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::set_extension +// Access: Public +// Description: Replaces the file extension. This is everything after +// the rightmost dot, if there is one, or the empty +// string if there is not. +//////////////////////////////////////////////////////////////////// +void Filename:: +set_extension(const string &s) { + if (s.empty()) { + // Remove the extension altogether. + if (_basename_end != string::npos) { + _filename.replace(_basename_end, string::npos, ""); + _basename_end = string::npos; + _extension_start = string::npos; + } + + } else if (_basename_end == string::npos) { + // Insert an extension where there was none before. + _basename_end = _filename.length(); + _extension_start = _filename.length() + 1; + _filename += '.' + s; + + } else { + // Replace an existing extension. + _filename.replace(_extension_start, string::npos, s); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::standardize +// Access: Public +// Description: Converts the filename to standard form by replacing +// consecutive slashes with a single slash, removing a +// trailing slash if present, and backing up over ../ +// sequences within the filename where possible. +//////////////////////////////////////////////////////////////////// +void Filename:: +standardize() { + assert(!_filename.empty()); + + vector components; + + // Pull off the components of the filename one at a time. + bool global = (_filename[0] == '/'); + + size_t p = 0; + while (p < _filename.length() && _filename[p] == '/') { + p++; + } + while (p < _filename.length()) { + size_t slash = _filename.find('/', p); + string component = _filename.substr(p, slash - p); + if (component == ".") { + // Ignore /./. + } else if (component == ".." && !components.empty() && + !(components.back() == "..")) { + // Back up. + components.pop_back(); + } else { + components.push_back(component); + } + + p = slash; + while (p < _filename.length() && _filename[p] == '/') { + p++; + } + } + + // Now reassemble the filename. + string result; + if (global) { + result = "/"; + } + if (!components.empty()) { + result += components[0]; + for (int i = 1; i < (int)components.size(); i++) { + result += "/" + components[i]; + } + } + + (*this) = result; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::to_os_specific +// Access: Public +// Description: Converts the filename from our generic Unix-like +// convention (forward slashes starting with the root at +// '/') to the corresponding filename in the local +// operating system (slashes in the appropriate +// direction, starting with the root at C:\, for +// instance). Returns the string representing the +// converted filename, but does not change the Filename +// itself. +//////////////////////////////////////////////////////////////////// +string Filename:: +to_os_specific() const { + Filename standard(*this); + standard.standardize(); + +#ifdef WIN32 + switch (get_type()) { + case T_dso: + return convert_dso_pathname(standard.get_fullpath()); + case T_executable: + return convert_executable_pathname(standard.get_fullpath()); + default: + return convert_pathname(standard.get_fullpath()); + } +#else // WIN32 + return standard; +#endif // WIN32 +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::exists +// Access: Public +// Description: Returns true if the filename exists on the disk, +// false otherwise. If the type is indicated to be +// executable, this also tests that the file has execute +// permission. +//////////////////////////////////////////////////////////////////// +bool Filename:: +exists() const { +#ifdef WIN32_VC + // Windows puts underscores in front of most of the function and + // structure names it borrowed from Unix. Embrace and extend. + struct _stat this_buf; + bool exists = false; + + if (_stat(to_os_specific().c_str(), &this_buf) == 0) { + exists = true; + } +#else // WIN32_VC + struct stat this_buf; + bool exists = false; + + if (stat(to_os_specific().c_str(), &this_buf) == 0) { + exists = true; + } +#endif + + return exists; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::compare_timestamps +// Access: Public +// Description: Returns a number less than zero if the file named by +// this object is older than the given file, zero if +// they have the same timestamp, or greater than zero if +// this one is newer. +// +// If this_missing_is_old is true, it indicates that a +// missing file will be treated as if it were older than +// any other file; otherwise, a missing file will be +// treated as if it were newer than any other file. +// Similarly for other_missing_is_old. +//////////////////////////////////////////////////////////////////// +int Filename:: +compare_timestamps(const Filename &other, + bool this_missing_is_old, + bool other_missing_is_old) const { + +#ifdef WIN32_VC + struct _stat this_buf; + bool this_exists = false; + + if (_stat(to_os_specific().c_str(), &this_buf) == 0) { + this_exists = true; + } + + struct _stat other_buf; + bool other_exists = false; + + if (_stat(other.to_os_specific().c_str(), &other_buf) == 0) { + other_exists = true; + } +#else // WIN32_VC + struct stat this_buf; + bool this_exists = false; + + if (stat(to_os_specific().c_str(), &this_buf) == 0) { + this_exists = true; + } + + struct stat other_buf; + bool other_exists = false; + + if (stat(other.to_os_specific().c_str(), &other_buf) == 0) { + other_exists = true; + } +#endif + + if (this_exists && other_exists) { + // Both files exist, return the honest time comparison. + return (int)this_buf.st_mtime - (int)other_buf.st_mtime; + + } else if (!this_exists && !other_exists) { + // Neither file exists. + if (this_missing_is_old == other_missing_is_old) { + // Both files are either "very old" or "very new". + return 0; + } + if (this_missing_is_old) { + // This file is "very old", the other is "very new". + return -1; + } else { + // This file is "very new", the other is "very old". + return 1; + } + + } else if (!this_exists) { + // This file doesn't, the other one does. + return this_missing_is_old ? -1 : 1; + + } else { // !other_exists + assert(!other_exists); + + // This file exists, the other one doesn't. + return other_missing_is_old ? 1 : -1; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::resolve_filename +// Access: Public +// Description: Searches the given search path for the filename. If +// it is found, updates the filename to the full +// pathname found and returns true; otherwise, returns +// false. +//////////////////////////////////////////////////////////////////// +bool Filename:: +resolve_filename(const DSearchPath &searchpath, + const string &default_extension) { + string found; + + if (is_local()) { + found = searchpath.find_file(get_fullpath()); + + if (found.empty()) { + // We didn't find it with the given extension; can we try the + // default extension? + if (get_extension().empty() && !default_extension.empty()) { + Filename try_ext = *this; + try_ext.set_extension(default_extension); + found = searchpath.find_file(try_ext.get_fullpath()); + } + } + + } else { + if (exists()) { + // The full pathname exists. Return true. + return true; + + } else { + // The full pathname doesn't exist with the given extension; + // does it exist with the default extension? + if (get_extension().empty() && !default_extension.empty()) { + Filename try_ext = *this; + try_ext.set_extension(default_extension); + if (try_ext.exists()) { + found = try_ext; + } + } + } + } + + if (!found.empty()) { + (*this) = found; + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::make_relative_to +// Access: Public +// Description: Adjusts this filename, which must be a +// fully-specified pathname beginning with a slash, to +// make it a relative filename, relative to the +// fully-specified directory indicated (which must also +// begin with, and may or may not end with, a slash--a +// terminating slash is ignored). +// +// If allow_backups is false, the filename will only be +// adjusted to be made relative if it is already +// somewhere within or below the indicated directory. +// If allow_backups is true, it will be adjusted in all +// cases, even if this requires putting a series of ../ +// characters before the filename--unless it would have +// to back all the way up to the root. +// +// Returns true if the file was adjusted, false if it +// was not. +//////////////////////////////////////////////////////////////////// +bool Filename:: +make_relative_to(Filename directory, bool allow_backups) { + if (_filename.empty() || directory.empty() || + _filename[0] != '/' || directory[0] != '/') { + return false; + } + + standardize(); + directory.standardize(); + + if (directory == "/") { + // Don't be silly. + return false; + } + + string rel_to_file = directory.get_fullpath() + "/."; + + size_t common = get_common_prefix(rel_to_file); + if (common < 2) { + // Oh, never mind. + return false; + } + + string result; + int slashes = count_slashes(rel_to_file.substr(common)); + if (slashes > 0 && !allow_backups) { + // Too bad; the file's not under the indicated directory. + return false; + } + + for (int i = 0; i < slashes; i++) { + result += "../"; + } + result += _filename.substr(common); + (*this) = result; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::find_on_searchpath +// Access: Public +// Description: Performs the reverse of the resolve_filename() +// operation: assuming that the current filename is +// fully-specified pathname (i.e. beginning with '/'), +// look on the indicated search path for a directory +// under which the file can be found. When found, +// adjust the Filename to be relative to the indicated +// directory name. +// +// Returns the index of the directory on the searchpath +// at which the file was found, or -1 if it was not +// found. +//////////////////////////////////////////////////////////////////// +int Filename:: +find_on_searchpath(const DSearchPath &searchpath) { + if (_filename.empty() || _filename[0] != '/') { + return -1; + } + + int num_directories = searchpath.get_num_directories(); + for (int i = 0; i < num_directories; i++) { + if (make_relative_to(searchpath.get_directory(i), false)) { + return i; + } + } + + return -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::open_read +// Access: Public +// Description: Opens the indicated ifstream for reading the file, if +// possible. Returns true if successful, false +// otherwise. This requires the setting of the +// set_text()/set_binary() flags to open the file +// appropriately as indicated; it is an error to call +// open_read() without first calling one of set_text() +// or set_binary(). +//////////////////////////////////////////////////////////////////// +bool Filename:: +open_read(ifstream &stream) const { + assert(is_text() || is_binary()); + + int open_mode = ios::in; + +#ifdef HAVE_IOS_BINARY + // For some reason, some systems (like Irix) don't define + // ios::binary. + if (!is_text()) { + open_mode |= ios::binary; + } +#endif + + stream.open(to_os_specific().c_str(), open_mode); + return (!stream.fail()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::open_write +// Access: Public +// Description: Opens the indicated ifstream for writing the file, if +// possible. Returns true if successful, false +// otherwise. This requires the setting of the +// set_text()/set_binary() flags to open the file +// appropriately as indicated; it is an error to call +// open_read() without first calling one of set_text() +// or set_binary(). +//////////////////////////////////////////////////////////////////// +bool Filename:: +open_write(ofstream &stream) const { + assert(is_text() || is_binary()); + + int open_mode = ios::out; + +#ifdef HAVE_IOS_BINARY + // For some reason, some systems (like Irix) don't define + // ios::binary. + if (!is_text()) { + open_mode |= ios::binary; + } +#endif + +#ifdef WIN32_VC + stream.open(to_os_specific().c_str(), open_mode); +#else + stream.open(to_os_specific().c_str(), open_mode, 0666); +#endif + + return (!stream.fail()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::open_append +// Access: Public +// Description: Opens the indicated ifstream for writing the file, if +// possible. Returns true if successful, false +// otherwise. This requires the setting of the +// set_text()/set_binary() flags to open the file +// appropriately as indicated; it is an error to call +// open_read() without first calling one of set_text() +// or set_binary(). +//////////////////////////////////////////////////////////////////// +bool Filename:: +open_append(ofstream &stream) const { + assert(is_text() || is_binary()); + + int open_mode = ios::app; + +#ifdef HAVE_IOS_BINARY + // For some reason, some systems (like Irix) don't define + // ios::binary. + if (!is_text()) { + open_mode |= ios::binary; + } +#endif + +#ifdef WIN32_VC + stream.open(to_os_specific().c_str(), open_mode); +#else + stream.open(to_os_specific().c_str(), open_mode, 0666); +#endif + + return (!stream.fail()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::open_read_write +// Access: Public +// Description: Opens the indicated fstream for read/write access to +// the file, if possible. Returns true if successful, +// false otherwise. This requires the setting of the +// set_text()/set_binary() flags to open the file +// appropriately as indicated; it is an error to call +// open_read() without first calling one of set_text() +// or set_binary(). +//////////////////////////////////////////////////////////////////// +bool Filename:: +open_read_write(fstream &stream) const { + assert(is_text() || is_binary()); + + int open_mode = ios::in | ios::out; + +#ifdef HAVE_IOS_BINARY + // For some reason, some systems (like Irix) don't define + // ios::binary. + if (!is_text()) { + open_mode |= ios::binary; + } +#endif + +#ifdef WIN32_VC + stream.open(to_os_specific().c_str(), open_mode); +#else + stream.open(to_os_specific().c_str(), open_mode, 0666); +#endif + + return (!stream.fail()); +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::unlink +// Access: Public +// Description: Permanently deletes the file associated with the +// filename, if possible. Returns true if successful, +// false if failure (for instance, because the file did +// not exist, or because permissions were inadequate). +//////////////////////////////////////////////////////////////////// +bool Filename:: +unlink() const { + return (::unlink(to_os_specific().c_str()) == 0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::rename_to +// Access: Public +// Description: Renames the file to the indicated new filename. If +// the new filename is in a different directory, this +// will perform a move. Returns true if successful, +// false if failure. +//////////////////////////////////////////////////////////////////// +bool Filename:: +rename_to(const Filename &other) const { + return (rename(to_os_specific().c_str(), + other.to_os_specific().c_str()) == 0); +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::locate_basename +// Access: Private +// Description: After the string has been reassigned, search for the +// slash marking the beginning of the basename, and set +// _dirname_end and _basename_start correctly. +//////////////////////////////////////////////////////////////////// +void Filename:: +locate_basename() { + // Scan for the last slash, which marks the end of the directory + // part. + if (_filename.empty()) { + _dirname_end = 0; + _basename_start = 0; + + } else { + + string::size_type slash = _filename.rfind('/'); + if (slash != string::npos) { + _basename_start = slash + 1; + _dirname_end = _basename_start; + + // One exception: in case there are multiple slashes in a row, + // we want to treat them as a single slash. The directory + // therefore actually ends at the first of these; back up a bit. + while (_dirname_end > 0 && _filename[_dirname_end-1] == '/') { + _dirname_end--; + } + + // Another exception: if the dirname was nothing but slashes, it + // was the root directory, or / itself. In this case the dirname + // does include the terminal slash (of course). + if (_dirname_end == 0) { + _dirname_end = 1; + } + + } else { + _dirname_end = 0; + _basename_start = 0; + } + } + + // Now: + + // _dirname_end is the last slash character, or 0 if there are no + // slash characters. + + // _basename_start is the character after the last slash character, + // or 0 if there are no slash characters. +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::locate_extension +// Access: Private +// Description: Once the end of the directory prefix has been found, +// and _dirname_end and _basename_start are set +// correctly, search for the dot marking the beginning +// of the extension, and set _basename_end and +// _extension_start correctly. +//////////////////////////////////////////////////////////////////// +void Filename:: +locate_extension() { + // Now scan for the last dot after that slash. + if (_filename.empty()) { + _basename_end = string::npos; + _extension_start = string::npos; + + } else { + string::size_type dot = _filename.length() - 1; + + while (dot+1 > _basename_start && _filename[dot] != '.') { + --dot; + } + + if (dot+1 > _basename_start) { + _basename_end = dot; + _extension_start = dot + 1; + } else { + _basename_end = string::npos; + _extension_start = string::npos; + } + } + + // Now: + + // _basename_end is the last dot, or npos if there is no dot. + + // _extension_start is the character after the last dot, or npos if + // there is no dot. +} + + +//////////////////////////////////////////////////////////////////// +// Function: Filename::get_common_prefix +// Access: Private +// Description: Returns the length of the longest common initial +// substring of this string and the other one that ends +// in a slash. This is the lowest directory common to +// both filenames. +//////////////////////////////////////////////////////////////////// +size_t Filename:: +get_common_prefix(const string &other) const { + size_t len = 0; + + // First, get the length of the common initial substring. + while (len < length() && len < other.length() && + _filename[len] == other[len]) { + len++; + } + + // Now insist that it ends in a slash. + while (len > 0 && _filename[len-1] != '/') { + len--; + } + + return len; +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::count_slashes +// Access: Private, Static +// Description: Returns the number of non-consecutive slashes in the +// indicated string, not counting a terminal slash. +//////////////////////////////////////////////////////////////////// +int Filename:: +count_slashes(const string &str) { + int count = 0; + string::const_iterator si; + si = str.begin(); + + while (si != str.end()) { + if (*si == '/') { + count++; + + // Skip consecutive slashes. + ++si; + while (*si == '/') { + ++si; + } + if (si == str.end()) { + // Oops, that was a terminal slash. Don't count it. + count--; + } + + } else { + ++si; + } + } + + return count; +} + diff --git a/dtool/src/dtoolutil/filename.h b/dtool/src/dtoolutil/filename.h new file mode 100644 index 0000000000..e343c70ffb --- /dev/null +++ b/dtool/src/dtoolutil/filename.h @@ -0,0 +1,171 @@ +// Filename: filename.h +// Created by: drose (18Jan99) +// +//////////////////////////////////////////////////////////////////// +// Copyright (C) 1992,93,94,95,96,97 Walt Disney Imagineering, Inc. +// +// These coded instructions, statements, data structures and +// computer programs contain unpublished proprietary information of +// Walt Disney Imagineering and are protected by Federal copyright +// law. They may not be disclosed to third parties or copied or +// duplicated in any form, in whole or in part, without the prior +// written consent of Walt Disney Imagineering Inc. +//////////////////////////////////////////////////////////////////// + +#ifndef FILENAME_H +#define FILENAME_H + +#include + +#include + +class DSearchPath; + +//////////////////////////////////////////////////////////////////// +// Class : Filename +// Description : The name of a file, such as a texture file or an Egg +// file. Stores the full pathname, and includes +// functions for extracting out the directory prefix +// part and the file extension and stuff. +// +// A Filename is also aware of the mapping between the +// Unix-like filename convention we use internally, and +// the local OS's specific filename convention, and it +// knows how to perform basic OS-specific I/O, like +// testing for file existence and searching a +// searchpath, as well as the best way to open an +// fstream for reading or writing. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL Filename { +public: + enum Type { + // These type values must fit within the bits allocated for + // F_type, below. + T_general = 0x00, + T_dso = 0x01, + T_executable = 0x02, + // Perhaps other types will be added later. + }; + + enum Flags { + F_type = 0x0f, + F_binary = 0x10, + F_text = 0x20, + }; + +public: + INLINE Filename(const string &filename = ""); + INLINE Filename(const char *filename); + INLINE Filename(const Filename ©); + Filename(const Filename &dirname, const Filename &basename); + INLINE ~Filename(); + + // Static constructors to explicitly create a filename that refers + // to a text or binary file. This is in lieu of calling set_text() + // or set_binary() or set_type(). + INLINE static Filename text_filename(const string &filename); + INLINE static Filename binary_filename(const string &filename); + INLINE static Filename dso_filename(const string &filename); + INLINE static Filename executable_filename(const string &filename); + + // Assignment is via the = operator. + INLINE Filename &operator = (const string &filename); + INLINE Filename &operator = (const char *filename); + INLINE Filename &operator = (const Filename ©); + + // And retrieval is by any of the classic string operations. + INLINE operator const string & () const; + INLINE const char *c_str() const; + INLINE bool empty() const; + INLINE size_t length() const; + INLINE char operator [] (int n) const; + + // Or, you can use any of these. + INLINE string get_fullpath() const; + INLINE string get_dirname() const; + INLINE string get_basename() const; + INLINE string get_fullpath_wo_extension() const; + INLINE string get_basename_wo_extension() const; + INLINE string get_extension() const; + + // You can also use any of these to reassign pieces of the filename. + void set_fullpath(const string &s); + void set_dirname(const string &s); + void set_basename(const string &s); + void set_fullpath_wo_extension(const string &s); + void set_basename_wo_extension(const string &s); + void set_extension(const string &s); + + // Setting these flags appropriately is helpful when opening or + // searching for a file; it helps the Filename resolve OS-specific + // conventions (for instance, that dynamic library names should + // perhaps be changed from .so to .dll). + INLINE void set_binary(); + INLINE void set_text(); + INLINE bool is_binary() const; + INLINE bool is_text() const; + + INLINE void set_type(Type type); + INLINE Type get_type() const; + + void standardize(); + void extend_by(const Filename &local_name); + + // The following functions deal with the outside world. + + INLINE bool is_local() const; + + string to_os_specific() const; + + bool exists() const; + int compare_timestamps(const Filename &other, + bool this_missing_is_old = true, + bool other_missing_is_old = true) const; + bool resolve_filename(const DSearchPath &searchpath, + const string &default_extension = string()); + bool make_relative_to(Filename directory, bool allow_backups = true); + int find_on_searchpath(const DSearchPath &searchpath); + + bool open_read(ifstream &stream) const; + bool open_write(ofstream &stream) const; + bool open_append(ofstream &stream) const; + bool open_read_write(fstream &stream) const; + + bool unlink() const; + bool rename_to(const Filename &other) const; + + // Comparison operators are handy. + INLINE bool operator == (const string &other) const; + INLINE bool operator != (const string &other) const; + INLINE bool operator < (const string &other) const; + + INLINE void output(ostream &out) const; + +private: + void locate_basename(); + void locate_extension(); + size_t get_common_prefix(const string &other) const; + static int count_slashes(const string &str); + + string _filename; + // We'll make these size_t instead of string::size_type to help out + // cppParser. + size_t _dirname_end; + size_t _basename_start; + size_t _basename_end; + size_t _extension_start; + + int _flags; +}; + +INLINE ostream &operator << (ostream &out, const Filename &n) { + n.output(out); + return out; +} + +#include "filename.I" + +#endif + + + diff --git a/dtool/src/dtoolutil/gnu_getopt.c b/dtool/src/dtoolutil/gnu_getopt.c new file mode 100644 index 0000000000..13d540e0ae --- /dev/null +++ b/dtool/src/dtoolutil/gnu_getopt.c @@ -0,0 +1,755 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 + Free Software Foundation, Inc. + + This program 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. + + This program 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. */ + + +#include + +#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_LONG_ONLY) + +#ifdef WIN32_VC +/* This file seems particularly egregious with this particular warning, + but it's not clear why. Disable. */ +/* C4028: formal parameter N different from declaration */ +#pragma warning (disable : 4028) +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#ifndef __STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "gnu_getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#ifndef __STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +static const char * +_getopt_initialize (optstring) + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0) + optstring = _getopt_initialize (optstring); + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0')) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if (nameend - nextchar == (int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); + else + fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + +#endif /* HAVE_GETOPT */ diff --git a/dtool/src/dtoolutil/gnu_getopt.h b/dtool/src/dtoolutil/gnu_getopt.h new file mode 100644 index 0000000000..7b7b6803ba --- /dev/null +++ b/dtool/src/dtoolutil/gnu_getopt.h @@ -0,0 +1,125 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program 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. + + This program 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. */ + +#ifndef _GNU_GETOPT_H +#define _GNU_GETOPT_H 1 + +/* We don't want to collide with a system getopt() it if it exists. + Redefine our symbols accordingly. */ + +#define getopt gnu_getopt +#define optind gnu_optind +#define opterr gnu_opterr +#define optopt gnu_optopt +#define optarg gnu_optarg +#define getopt_long gnu_getopt_long +#define getopt_long_only gnu_getopt_long_only + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char EXPCL_DTOOL *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int EXPCL_DTOOL optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +extern EXPCL_DTOOL int +getopt (int argc, char *const *argv, const char *shortopts); +extern EXPCL_DTOOL int +getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *long_options, int *opt_index); +extern EXPCL_DTOOL int +getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *long_options, + int *opt_index); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/dtool/src/dtoolutil/gnu_getopt1.c b/dtool/src/dtoolutil/gnu_getopt1.c new file mode 100644 index 0000000000..7fb3fc1911 --- /dev/null +++ b/dtool/src/dtoolutil/gnu_getopt1.c @@ -0,0 +1,186 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program 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. + + This program 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. */ + + +#include + +#ifndef HAVE_GETOPT_LONG_ONLY + +#ifdef WIN32_VC +/* This file seems particularly egregious with this particular warning, + but it's not clear why. Disable. */ +/* C4028: formal parameter N different from declaration */ +#pragma warning (disable : 4028) +#endif + +#include "gnu_getopt.h" + +#ifndef __STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#else +char *getenv (); +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + +#endif /* HAVE_GETOPT_LONG_ONLY */ diff --git a/dtool/src/dtoolutil/load_dso.cxx b/dtool/src/dtoolutil/load_dso.cxx new file mode 100644 index 0000000000..47ad1d623a --- /dev/null +++ b/dtool/src/dtoolutil/load_dso.cxx @@ -0,0 +1,130 @@ +// Filename: load_dso.C +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +#include "load_dso.h" + +#if defined(PENV_PS2) + +// The playstation2 can't do any of this stuff, so these functions are B O G U S + +void * +load_dso(const Filename &) +{ + return (void *) NULL; +} + +string +load_dso_error() +{ + ostringstream ps2errmsg; + ps2errmsg << "load_dso_error() unsupported on PS2. (CSN)"; + + return ps2errmsg.str(); +} + +#else + + +#if defined(WIN32) +/* begin Win32-specific code */ + +#define WINDOWS_LEAN_AND_MEAN +#include +#undef WINDOWS_LEAN_AND_MEAN + +void * +load_dso(const Filename &filename) { + return LoadLibrary(filename.to_os_specific().c_str()); +} + +string +load_dso_error() { + DWORD last_error = GetLastError(); + switch (last_error) { + case 2: return "File not found"; + case 3: return "Path not found"; + case 4: return "Too many open files"; + case 5: return "Access denied"; + case 14: return "Out of memory"; + case 18: return "No more files"; + case 126: return "Module not found"; + case 998: return "Invalid access to memory location"; + } + + // Some unknown error code. + ostringstream errmsg; + errmsg << "Unknown error " << last_error; + return errmsg.str(); +} + +/* end Win32-specific code */ + +#elif defined(PENV_OSX) +/* begin Mac OS X code */ + +#include + +void * +load_dso(const Filename &filename) { + enum DYLD_BOOL result; + cerr << "_dyld_present() = " << _dyld_present() << endl; + cerr << "_dyld_image_count() = " << _dyld_image_count() << endl; + result = NSAddLibrary(filename.to_os_specific().c_str()); + if (result == FALSE) { + cerr << "Failed to load '" << filename << "'" << endl; + } + // need to reference a symbol in the lib in order for static init to happen + // need to figure out how to do this + cerr << "_dyld_present() = " << _dyld_present() << endl; + cerr << "_dyld_image_count() = " << _dyld_image_count() << endl; + for (unsigned long i=0; i<_dyld_image_count(); ++i) + cerr << "_dyld_get_image_name(" << i << ") = '" << _dyld_get_image_name(i) + << "'" << endl; + string stmp = filename; + cerr << "filename = '" << filename << "'" << endl; + int i = stmp.rfind(".dylib"); + stmp.erase(i, i+6); + stmp += "_so"; + cerr << "filename with tail patched = '" << stmp << "'" << endl; + i = stmp.rfind("lib"); + if (i != 0) { + if (stmp[i-1] != '/') + i = stmp.rfind("lib", i-1); + stmp.erase(0, i); + } + cerr << "final patched filename = '" << stmp << "'" << endl; + stmp = "___" + stmp + "_find_me__"; + cerr << "symbol name searching for = '" << stmp << "'" << endl; + unsigned long foo1; + void *foo2; + _dyld_lookup_and_bind(stmp.c_str(), &foo1, &foo2); + char *foo3 = (char*)foo1; + cerr << "symbol value = '" << foo3 << "'" << endl; + return (void*)0L; +} + +string +load_dso_error() { + return "No DSO loading yet!"; +} + +#else +/* begin generic code */ + +#include + +void * +load_dso(const Filename &filename) { + return dlopen(filename.to_os_specific().c_str(), RTLD_NOW); +} + +string +load_dso_error() { + return dlerror(); +} + +#endif + +#endif // PS2 diff --git a/dtool/src/dtoolutil/load_dso.h b/dtool/src/dtoolutil/load_dso.h new file mode 100644 index 0000000000..28d37849e3 --- /dev/null +++ b/dtool/src/dtoolutil/load_dso.h @@ -0,0 +1,25 @@ +// Filename: load_dso.h +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef LOAD_DSO_H +#define LOAD_DSO_H + +#include + +#include "filename.h" + +// Loads in a dynamic library like an .so or .dll. Returns NULL if +// failure, otherwise on success. + +EXPCL_DTOOL void * +load_dso(const Filename &filename); + +// Returns the error message from the last failed load_dso() call. + +EXPCL_DTOOL string +load_dso_error(); + +#endif + diff --git a/dtool/src/dtoolutil/pfstream.h b/dtool/src/dtoolutil/pfstream.h new file mode 100644 index 0000000000..d8c019ab18 --- /dev/null +++ b/dtool/src/dtoolutil/pfstream.h @@ -0,0 +1,101 @@ +// Filename: pfstream.h +// Created by: cary (27Aug98) +// +//////////////////////////////////////////////////////////////////// + +#ifndef __PFSTREAM_H__ +#define __PFSTREAM_H__ + +#include + +#include +#include + +#if defined(PENV_LINUX) || defined(PENV_WIN32) +extern "C" { +#if defined(PENV_WIN32) + #define popen _popen + #define pclose _pclose +#else + FILE* popen(const char*, const char*); + int pclose(FILE *); +#endif +} +#endif /* PENV_LINUX */ + +class EXPCL_DTOOL ipfstream { + private: + ifstream *ifs; + FILE *fd; + bool ok; + + ipfstream() {} + ipfstream(const ipfstream&) {} + public: + ipfstream(std::string cmd, int mode = ios::in, int = 0664) : ok(false) { +#ifndef PENV_PS2 + fd = popen(cmd.c_str(), (mode & ios::in)?"r":"w"); + if (fd != (FILE *)0l) { +// this can't work under windows until this is re-written +#ifdef WIN32_VC + cerr << "ipfstream: this class needs to be rewritten for windows!" + << endl; +#else + ifs = new ifstream(fileno(fd)); +#endif // WIN32_VC + ok = true; + } +#endif // PENV_PS2 + } + + ~ipfstream() { +#ifndef PENV_PS2 + if (ok) { + pclose(fd); + delete ifs; + } +#endif // PENV_PS2 + } + + INLINE operator istream&() { + return *ifs; + } +}; + +class EXPCL_DTOOL opfstream { + private: + ofstream *ofs; + FILE *fd; + bool ok; + + opfstream() {} + opfstream(const opfstream&) {} + public: + opfstream(std::string cmd, int mode = ios::out, int = 0664) : ok(false) { +#ifndef PENV_PS2 + fd = popen(cmd.c_str(), (mode & ios::out)?"w":"r"); + if (fd != (FILE *)0L) { +#ifndef PENV_WIN32 + ofs = new ofstream(fileno(fd)); +#endif + ok = true; + } +#endif // PENV_PS2 + } + ~opfstream() { +#ifndef PENV_PS2 + if (ok) { + pclose(fd); + delete ofs; + } +#endif + } + + INLINE operator ostream&() { + return *ofs; + } +}; + +#endif /* __PFSTREAM_H__ */ + + diff --git a/dtool/src/dtoolutil/vector_string.cxx b/dtool/src/dtoolutil/vector_string.cxx new file mode 100644 index 0000000000..f090140b79 --- /dev/null +++ b/dtool/src/dtoolutil/vector_string.cxx @@ -0,0 +1,11 @@ +// Filename: vector_string.C +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_string.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/dtool/src/dtoolutil/vector_string.h b/dtool/src/dtoolutil/vector_string.h new file mode 100644 index 0000000000..35d7b4372b --- /dev/null +++ b/dtool/src/dtoolutil/vector_string.h @@ -0,0 +1,30 @@ +// Filename: vector_string.h +// Created by: drose (15May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_STRING_H +#define VECTOR_STRING_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_string +// Description : A vector of ints. This class is defined once here, +// and exported to DTOOL.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_DTOOL, EXPTP_DTOOL, std::vector) +typedef vector vector_string; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/dtool/src/interrogate/Sources.pp b/dtool/src/interrogate/Sources.pp new file mode 100644 index 0000000000..92d37ec6d8 --- /dev/null +++ b/dtool/src/interrogate/Sources.pp @@ -0,0 +1,40 @@ +#define LOCAL_LIBS cppParser pystub interrogatedb dconfig dtoolutil dtoolbase + +#begin bin_target + #define TARGET interrogate + + #define SOURCES \ + interrogate.cxx interrogate.h \ + interrogateBuilder.cxx interrogateBuilder.h \ + parameterRemap.I parameterRemap.cxx parameterRemap.h \ + parameterRemapBasicStringRefToString.cxx \ + parameterRemapBasicStringRefToString.h \ + parameterRemapBasicStringToString.cxx \ + parameterRemapBasicStringToString.h \ + parameterRemapCharStarToString.cxx parameterRemapCharStarToString.h \ + parameterRemapConcreteToPointer.cxx \ + parameterRemapConcreteToPointer.h parameterRemapConstToNonConst.cxx \ + parameterRemapConstToNonConst.h parameterRemapEnumToInt.cxx \ + parameterRemapEnumToInt.h parameterRemapPTToPointer.cxx \ + parameterRemapPTToPointer.h parameterRemapReferenceToConcrete.cxx \ + parameterRemapReferenceToConcrete.h \ + parameterRemapReferenceToPointer.cxx \ + parameterRemapReferenceToPointer.h parameterRemapThis.cxx \ + parameterRemapThis.h parameterRemapToString.cxx \ + parameterRemapToString.h parameterRemapUnchanged.cxx \ + parameterRemapUnchanged.h typeManager.cxx \ + typeManager.h wrapperBuilder.cxx wrapperBuilder.h \ + wrapperBuilderC.cxx wrapperBuilderC.h wrapperBuilderPython.cxx \ + wrapperBuilderPython.h + +#end bin_target + +#begin bin_target + #define TARGET parse_file + #define SOURCES parse_file.cxx +#end bin_target + +#begin bin_target + #define TARGET interrogate_module + #define SOURCES interrogate_module.cxx +#end bin_target diff --git a/dtool/src/interrogate/interrogate.cxx b/dtool/src/interrogate/interrogate.cxx new file mode 100644 index 0000000000..75e8c3576c --- /dev/null +++ b/dtool/src/interrogate/interrogate.cxx @@ -0,0 +1,459 @@ +// Filename: interrogate.C +// Created by: drose (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogate.h" +#include "interrogateBuilder.h" + +#include +#include +#include +#include + +// If our system getopt() doesn't come with getopt_long_only(), then use +// the GNU flavor that we've got in tool for this purpose. +#ifndef HAVE_GETOPT_LONG_ONLY +#include +#else +#include +#endif + +CPPParser parser; + +Filename output_code_filename; +Filename output_data_filename; +bool output_module_specific = false; +bool output_function_pointers = false; +bool output_function_names = false; +bool convert_strings = false; +bool manage_reference_counts = false; +bool watch_asserts = false; +bool true_wrapper_names = false; +bool build_c_wrappers = false; +bool build_python_wrappers = false; +bool save_unique_names = false; +bool no_database = false; +CPPVisibility min_vis = V_published; +string library_name; +string module_name; + +// Short command-line options. +static const char *short_options = "I:S:D:F:vh"; + +// Long command-line options. +enum CommandOptions { + CO_oc = 256, + CO_od, + CO_module, + CO_library, + CO_do_module, + CO_fptrs, + CO_fnames, + CO_string, + CO_refcount, + CO_assert, + CO_true_names, + CO_c, + CO_python, + CO_unique_names, + CO_nodb, + CO_longlong, + CO_promiscuous, + CO_help, +}; + +static struct option long_options[] = { + { "oc", required_argument, NULL, CO_oc }, + { "od", required_argument, NULL, CO_od }, + { "module", required_argument, NULL, CO_module }, + { "library", required_argument, NULL, CO_library }, + { "do-module", no_argument, NULL, CO_do_module }, + { "fptrs", no_argument, NULL, CO_fptrs }, + { "fnames", no_argument, NULL, CO_fnames }, + { "string", no_argument, NULL, CO_string }, + { "refcount", no_argument, NULL, CO_refcount }, + { "assert", no_argument, NULL, CO_assert }, + { "true-names", no_argument, NULL, CO_true_names }, + { "c", no_argument, NULL, CO_c }, + { "python", no_argument, NULL, CO_python }, + { "unique-names", no_argument, NULL, CO_unique_names }, + { "nodb", no_argument, NULL, CO_nodb }, + { "longlong", required_argument, NULL, CO_longlong }, + { "promiscuous", no_argument, NULL, CO_promiscuous }, + { "help", no_argument, NULL, CO_help }, + { NULL } +}; + +void +show_usage() { + cerr + << "\nUsage:\n" + << " interrogate [opts] file.C [file.C ...]\n" + << " interrogate -h\n\n"; +} + +void show_help() { + show_usage(); + cerr + << "Interrogate is a program to parse a body of C++ code and build up a table\n" + << "of classes, methods, functions, and symbols found, for the purposes of\n" + << "calling into the codebase via a non-C++ scripting language like Scheme,\n" + << "Smalltalk, or Python.\n\n" + + << "In addition to identifying all the classes and their relationships,\n" + << "interrogate will generate a wrapper function for each callable function.\n" + << "The wrapper functions will be callable directly from the scripting language,\n" + << "with no understanding of C++ necessary; these wrapper functions will in turn\n" + << "call the actual C++ functions or methods.\n\n" + + << "Most exportable features of C++ are supported, including templates, default\n" + << "parameters, and function overloading.\n\n" + + << "Options:\n\n" + + << " -oc output.C\n" + << " Specify the name of the file to which generated code will be written.\n" + << " This includes all of the function wrappers, as well as those tables\n" + << " which must be compiled into the library.\n\n" + + << " -od output.in\n" + << " Specify the name of the file to which the non-compiled data tables\n" + << " will be written. This file describes the relationships between\n" + << " all the types and the functions, and associates the function wrappers\n" + << " above with this data. This file will be opened and read at runtime\n" + << " when the scripting language first calls some interrogate query\n" + << " function.\n\n" + + << " -module module_name\n" + << " Defines the name of the module this data is associated with. This\n" + << " is strictly a code-organizational tool. Conceptually, a module is\n" + << " the highest level of grouping for interrogate data; a module may\n" + << " contain several libraries. If this is omitted, no module name is\n" + << " specified.\n\n" + + << " Sometimes, depending on the type of wrappers being generated, there\n" + << " may be additional code that needs to be generated on the module\n" + << " level, above that which was already generated at the library level.\n" + << " Python, for instance, generates the table of python-callable function\n" + << " wrappers at the module level. Use the program interrogate-module\n" + << " to generate the appropriate code at the module level.\n\n" + + << " -library library_name\n" + << " Defines the name of the library this data is associated with. This\n" + << " is another code-organizational tool. Typically, there will be one\n" + << " invocation of interrogate for each library, and there will be\n" + << " multiple libraries per module. If this is omitted, no library name\n" + << " is specified.\n\n" + + << " -do-module\n" + << " Generate whatever module-level code should be generated immediately,\n" + << " rather than waiting for a special interrogate-module pass.\n" + << " This, of course, prohibits grouping several libraries together\n" + << " into a single module.\n\n" + + << " -fptrs\n" + << " Make void* pointers to the function wrappers directly available. A\n" + << " scripting language will be able to call the interrogate functions\n" + << " directly by pointer.\n\n" + + << " -fnames\n" + << " Make the names of the function wrappers public symbols so that the\n" + << " scripting language will be able to call the interrogate functions\n" + << " by name.\n\n" + + << " Either or both of -fptrs and/or -fnames may be specified. If both are\n" + << " omitted, the default is -fnames.\n\n" + + << " -string\n" + << " Treat char* and basic_string as special cases, and map\n" + << " parameters of these types to type atomic string. The scripting\n" + << " language will see only functions that receive and return strings,\n" + << " not pointers to character or structures of basic_string.\n" + << " If C calling convention wrappers are being generated, the atomic\n" + << " string type means type char*. In any other calling convention, the\n" + << " atomic string type is whatever the native string type is.\n\n" + + << " -refcount\n" + << " Treat classes that inherit from a class called ReferenceCount as a\n" + << " special case. Any wrapper function that returns a pointer to\n" + << " one of these classes will automatically increment the reference\n" + << " count by calling ref() on the object first, and any destructors\n" + << " that are generated will call unref_delete() on the object instead of\n" + << " simply delete.\n\n" + << " Furthermore, parameters of type PointerTo or ConstPointerTo\n" + << " will automatically be mapped to N * and const N *, respectively.\n\n" + + << " -assert\n" + << " Generate code in each wrapper that will check the state of the assert\n" + << " flag and trigger an exception in the scripting language when a\n" + << " C++ assertion fails. Presently, this only has meaning to the Python\n" + << " wrappers.\n\n" + + << " -true-names\n" + << " Use the actual name of the function being wrapped as the name of\n" + << " the generated wrapper function, instead of an ugly hash name.\n" + << " This means the wrapper functions may be called directly using a\n" + << " meaningful name (especially if -fnames is also given), but it\n" + << " also means that C++ function overloading (including default values\n" + << " for parameters) cannot be used, as it will lead to multiple wrapper\n" + << " functions with the same name.\n\n" + + << " -c\n" + << " Generate function wrappers using the C calling convention. Any\n" + << " scripting language that can call a C function should be able to\n" + << " make advantage of the interrogate database.\n\n" + << " -python\n" + << " Generate function wrappers using the Python calling convention.\n" + << " The shared library will be directly loadable as a Python module\n" + << " (especially if the module definitions are made available either by\n" + << " running interrogate-module later, or by specifying -do-module on\n" + << " the command line now).\n\n" + + << " Either or both of -c and/or -python may be specified. If both are\n" + << " omitted, the default is -c.\n\n" + + << " -unique-names\n" + << " Compile a table into the library (i.e. generate code into the -oc\n" + << " file) that defines a lookup of each function wrapper by its unique\n" + << " name. This makes it possible to consistently identify function\n" + << " wrappers between sessions, at the cost of having this additional\n" + << " table in memory.\n\n" + + << " -nodb\n" + << " Do not build a full interrogate database, but just generate function\n" + << " wrappers. It is assumed that the user will know how to call the\n" + << " function wrappers already, from some external source. This is most\n" + << " useful in conjunction with -true-names.\n\n" + + << " -longlong typename\n" + << " Specify the name of the 64-bit integer type for the current compiler.\n" + << " By default, this is \"long long\".\n\n" + + << " -promiscuous\n" + << " Export *all* public symbols, functions, and classes seen, even those\n" + << " not explicitly marked to be published.\n\n"; +} + +// handle commandline -D options +static void +predefine_macro(CPPParser& parser, const string& option) { + string macro_name, macro_def; + + size_t eq = option.find('='); + if (eq != string::npos) { + macro_name = option.substr(0, eq); + macro_def = option.substr(eq + 1); + } else { + macro_name = option; + } + + CPPManifest *macro = new CPPManifest(macro_name + " " + macro_def); + parser._manifests[macro->_name] = macro; +} + +int +main(int argc, char *argv[]) { + string command_line; + int i; + for (i = 0; i < argc; i++) { + command_line += string(argv[i]) + " "; + } + + extern char *optarg; + extern int optind; + int flag; + + flag = getopt_long_only(argc, argv, short_options, long_options, NULL); + while (flag != EOF) { + switch (flag) { + case 'I': + parser._include_path.append_directory(optarg); + break; + + case 'S': + parser._system_include_path.append_directory(optarg); + break; + + case 'D': + predefine_macro(parser, optarg); + break; + + case 'F': + // This is just a compile directive which we ignore. + break; + + case 'v': + parser.set_verbose(parser.get_verbose() + 1); + break; + + case CO_oc: + output_code_filename = optarg; + break; + + case CO_od: + output_data_filename = optarg; + break; + + case CO_module: + module_name = optarg; + break; + + case CO_library: + library_name = optarg; + break; + + case CO_do_module: + output_module_specific = true; + break; + + case CO_fptrs: + output_function_pointers = true; + break; + + case CO_fnames: + output_function_names = true; + break; + + case CO_string: + convert_strings = true; + break; + + case CO_refcount: + manage_reference_counts = true; + break; + + case CO_assert: + watch_asserts = true; + break; + + case CO_true_names: + true_wrapper_names = true; + break; + + case CO_c: + build_c_wrappers = true; + break; + + case CO_python: + build_python_wrappers = true; + break; + + case CO_unique_names: + save_unique_names = true; + break; + + case CO_nodb: + no_database = true; + break; + + case CO_longlong: + cpp_longlong_keyword = optarg; + break; + + case CO_promiscuous: + min_vis = V_public; + break; + + case 'h': + case CO_help: + show_help(); + exit(0); + + default: + exit(1); + } + flag = getopt_long_only(argc, argv, short_options, long_options, NULL); + } + + argc -= (optind-1); + argv += (optind-1); + + if (argc < 2) { + show_usage(); + exit(1); + } + + output_code_filename.set_text(); + output_data_filename.set_text(); + + if (output_function_names && true_wrapper_names) { + cerr + << "Cannot simultaneously export function names and report\n" + << "true wrapper names--wrapper names will clash with the\n" + << "wrapped functions!\n"; + exit(1); + } + + if (!build_c_wrappers && !build_python_wrappers) { + build_c_wrappers = true; + } + + // Get all of the .h files. + for (i = 1; i < argc; ++i) { + if (!parser.parse_file(argv[i])) { + cerr << "Error parsing file: '" << argv[i] << "'\n"; + exit(1); + } + builder.add_source_file(argv[i]); + } + + // Now that we've parsed all the source code, change the way things + // are output from now on so we can compile our generated code using + // VC++. Sheesh. + cppparser_output_class_keyword = false; + + + // Now look for the .N files. + for (i = 1; i < argc; ++i) { + Filename nfilename = argv[i]; + nfilename.set_extension("N"); + nfilename.set_text(); + ifstream nfile; + if (nfilename.open_read(nfile)) { + builder.read_command_file(nfile); + } + } + + + builder.build(); + + // Make up a file identifier. This is just some bogus number that + // should be the same in both the compiled-in code and in the + // database, so we can check synchronicity at load time. + int file_identifier = time((time_t *)NULL); + InterrogateModuleDef *def = builder.make_module_def(file_identifier); + + // Now output all of the wrapper functions. + if (!output_code_filename.empty()) { + ofstream output_code; + output_code_filename.open_write(output_code); + + output_code + << "/*\n" + << " * This file generated by:\n" + << " * " << command_line << "\n" + << " *\n" + << " */\n\n"; + + if (output_code.fail()) { + nout << "Unable to write to " << output_code_filename << "\n"; + } else { + builder.write_code(output_code, def); + } + } + + // And now output the bulk of the database. + if (!output_data_filename.empty()) { + ofstream output_data; + output_data_filename.open_write(output_data); + + if (output_data.fail()) { + nout << "Unable to write to " << output_data_filename << "\n"; + } else { + InterrogateDatabase::get_ptr()->write(output_data, def); + } + } + + return (0); +} diff --git a/dtool/src/interrogate/interrogate.h b/dtool/src/interrogate/interrogate.h new file mode 100644 index 0000000000..2a7362fd86 --- /dev/null +++ b/dtool/src/interrogate/interrogate.h @@ -0,0 +1,36 @@ +// Filename: interrogate.h +// Created by: drose (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATE_H +#define INTERROGATE_H + +#include + +#include +#include +#include + +extern CPPParser parser; + +// A few global variables that control the interrogate process. +extern Filename output_code_filename; +extern Filename output_data_filename; +extern bool output_module_specific; +extern bool output_function_pointers; +extern bool output_function_names; +extern bool convert_strings; +extern bool manage_reference_counts; +extern bool watch_asserts; +extern bool true_wrapper_names; +extern bool build_c_wrappers; +extern bool build_python_wrappers; +extern bool save_unique_names; +extern bool no_database; +extern CPPVisibility min_vis; +extern string library_name; +extern string module_name; + +#endif + diff --git a/dtool/src/interrogate/interrogateBuilder.cxx b/dtool/src/interrogate/interrogateBuilder.cxx new file mode 100644 index 0000000000..cbaa47d520 --- /dev/null +++ b/dtool/src/interrogate/interrogateBuilder.cxx @@ -0,0 +1,2430 @@ +// Filename: interrogateBuilder.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogateBuilder.h" +#include "interrogate.h" +#include "wrapperBuilder.h" +#include "wrapperBuilderC.h" +#include "wrapperBuilderPython.h" +#include "parameterRemap.h" +#include "typeManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +InterrogateBuilder builder; + +/* +static string +upcase_string(const string &str) { + string result; + for (string::const_iterator si = str.begin(); + si != str.end(); + ++si) { + result += toupper(*si); + } + return result; +} +*/ + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::add_source_file +// Access: Public +// Description: Adds the given source filename to the list of files +// that we are scanning. Those source files that appear +// to be header files will be #included in the generated +// code file. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +add_source_file(const string &filename) { + if (filename.empty()) { + return; + } + + if (!CPPFile::is_c_file(filename)) { + _include_files.insert('"' + filename + '"'); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::read_command_file +// Access: Public +// Description: Reads a .N file that might contain control +// information for the interrogate process. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +read_command_file(istream &in) { + string line; + getline(in, line); + while (!in.fail() && !in.eof()) { + // Strip out the comment. + size_t hash = line.find('#'); + if (hash != string::npos) { + line = line.substr(0, hash); + } + + // Skip leading whitespace. + size_t p = 0; + while (p < line.length() && isspace(line[p])) { + p++; + } + + if (p < line.length()) { + // Get the first word. + size_t q = p; + while (q < line.length() && !isspace(line[q])) { + q++; + } + string command = line.substr(p, q - p); + + // Get the rest. + p = q; + while (p < line.length() && isspace(line[p])) { + p++; + } + // Except for the trailing whitespace. + q = line.length(); + while (q > p && isspace(line[q - 1])) { + q--; + } + string params = line.substr(p, q - p); + + do_command(command, params); + } + getline(in, line); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::do_command +// Access: Public +// Description: Executes a single command as read from the .N file. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +do_command(const string &command, const string ¶ms) { + if (command == "forcetype") { + // forcetype explicitly exports the given type. + CPPType *type = parser.parse_type(params); + if (type == (CPPType *)NULL) { + nout << "Unknown type: forcetype " << params << "\n"; + } else { + type = type->resolve_type(&parser, &parser); + _forcetype.insert(type->get_local_name(&parser)); + } + + } else if (command == "ignoretype") { + // ignoretype explicitly ignores the given type. + CPPType *type = parser.parse_type(params); + if (type == (CPPType *)NULL) { + nout << "Unknown type: ignoretype " << params << "\n"; + } else { + type = type->resolve_type(&parser, &parser); + _ignoretype.insert(type->get_local_name(&parser)); + } + + } else if (command == "ignoreinvolved") { + _ignoreinvolved.insert(params); + + } else if (command == "ignorefile") { + insert_param_list(_ignorefile, params); + + } else if (command == "ignoremember") { + insert_param_list(_ignoremember, params); + + } else { + nout << "Ignoring " << command << " " << params << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::build +// Access: Public +// Description: Builds all of the interrogate data. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +build() { + _library_hash_name = hash_string(library_name); + + // Make sure we have the complete set of #includes we need. + CPPParser::Includes::const_iterator ii; + for (ii = parser._quote_includes.begin(); + ii != parser._quote_includes.end(); + ++ii) { + const string &filename = (*ii); + // Don't add any C files to the include list. + if (!CPPFile::is_c_file(filename)) { + _include_files.insert('"' + filename + '"'); + } + } + for (ii = parser._angle_includes.begin(); + ii != parser._angle_includes.end(); + ++ii) { + const string &filename = (*ii); + // Don't add any C files to the include list. + if (!CPPFile::is_c_file(filename)) { + _include_files.insert('<' + filename + '>'); + } + } + + // First, get all the types that were explicitly forced. + Commands::const_iterator ci; + for (ci = _forcetype.begin(); + ci != _forcetype.end(); + ++ci) { + CPPType *type = parser.parse_type(*ci); + assert(type != (CPPType *)NULL); + get_type(type, true); + } + + // Now go through all of the top-level declarations in the file(s). + + CPPScope::Declarations::const_iterator di; + for (di = parser._declarations.begin(); + di != parser._declarations.end(); + ++di) { + if ((*di)->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = (*di)->as_instance(); + if (inst->_type->get_subtype() == CPPDeclaration::ST_function) { + // Here's a function declaration. + scan_function(inst); + + } else { + // Here's a data element declaration. + scan_element(inst, (CPPStructType *)NULL, &parser); + } + + } else if ((*di)->get_subtype() == CPPDeclaration::ST_typedef) { + CPPTypedef *tdef = (*di)->as_typedef(); + if (tdef->_type->get_subtype() == CPPDeclaration::ST_struct) { + // A typedef counts as a declaration. This lets us pick up + // most template instantiations. + CPPStructType *struct_type = + tdef->_type->resolve_type(&parser, &parser)->as_struct_type(); + scan_struct_type(struct_type); + } + + } else if ((*di)->get_subtype() == CPPDeclaration::ST_type_declaration) { + CPPType *type = (*di)->as_type_declaration()->_type; + type->_vis = (*di)->_vis; + + if (type->get_subtype() == CPPDeclaration::ST_struct) { + CPPStructType *struct_type = + type->as_type()->resolve_type(&parser, &parser)->as_struct_type(); + scan_struct_type(struct_type); + + } else if (type->get_subtype() == CPPDeclaration::ST_enum) { + CPPEnumType *enum_type = + type->as_type()->resolve_type(&parser, &parser)->as_enum_type(); + scan_enum_type(enum_type); + } + } + } + + CPPPreprocessor::Manifests::const_iterator mi; + for (mi = parser._manifests.begin(); mi != parser._manifests.end(); ++mi) { + CPPManifest *manifest = (*mi).second; + scan_manifest(manifest); + } + + // Now that we've gone through all the code and generated all the + // functions and types, build the function wrappers. + make_wrappers(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::write_code +// Access: Public +// Description: Generates all the code necessary to the indicated +// output stream. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +write_code(ostream &out, InterrogateModuleDef *def) { + // Make sure all of the function wrappers appear first in the set of + // indices, and that they occupy consecutive index numbers, so we + // can build a simple array of function pointers by index. + remap_indices(); + + // Get the function wrappers in index-number order. + int num_wrappers = 0; + map wrappers_by_index; + + WrappersByHash::iterator hi; + for (hi = _wrappers_by_hash.begin(); + hi != _wrappers_by_hash.end(); + ++hi) { + WrapperBuilder *wbuilder = (*hi).second; + if (wbuilder != (WrapperBuilder *)NULL) { + wrappers_by_index[wbuilder->_wrapper_index] = wbuilder; + num_wrappers++; + } + } + + // Begin writing. First, we need to write all the necessary + // #include lines. + if (!no_database) { + out << "#include \n" + << "#include \n"; + } + if (watch_asserts) { + out << "#include \n"; + } + out << "\n"; + + IncludeFiles::const_iterator ifi; + for (ifi = _include_files.begin(); + ifi != _include_files.end(); + ++ifi) { + const string &filename = (*ifi); + // Much as I hate to do it, I'm going to code in a special-case + // for two particularly nasty header files that we probably don't + // want to actually ever include. + if (filename == "" || filename == "") { + // Ignoring these. + } else { + out << "#include " << (*ifi) << "\n"; + } + } + out << "\n"; + + if (build_python_wrappers) { + out << "#undef HAVE_LONG_LONG\n"; + out << "#include \n\n"; + } + + // Now, define all of our wrappers. + + out << "extern \"C\" {\n\n"; + + // Write the functions out in index-number order instead of hash + // order, because that's more natural to a human, and the file will + // be easier to read. + { + map::iterator ii; + for (ii = wrappers_by_index.begin(); + ii != wrappers_by_index.end(); + ++ii) { + WrapperBuilder *wbuilder = (*ii).second; + + string wrapper_name = wbuilder->get_wrapper_name(_library_hash_name); + wbuilder->write_wrapper(out, wrapper_name); + + // Record the wrapper's name in the database. + InterrogateFunctionWrapper &iwrapper = InterrogateDatabase::get_ptr()-> + update_wrapper(wbuilder->_wrapper_index); + + if (true_wrapper_names) { + // If we're reporting "true" names, it means we set the + // wrapper's name to the name of the function it wraps. That + // means we need to look up the function. + const InterrogateFunction &ifunction = InterrogateDatabase::get_ptr()-> + get_function(iwrapper.get_function()); + + iwrapper._name = clean_identifier(ifunction.get_scoped_name()); + } else { + iwrapper._name = wrapper_name; + } + + if (output_function_names) { + // If we're keeping the function names, record that the + // wrapper is callable. + iwrapper._flags |= InterrogateFunctionWrapper::F_callable_by_name; + } + } + } + + out << "} /* close extern \"C\" */\n\n"; + + if (output_function_pointers) { + // Write out the table of function pointers. + out << "static void *_in_fptrs[" << num_wrappers << "] = {\n"; + map::iterator ii; + int next_index = 1; + for (ii = wrappers_by_index.begin(); + ii != wrappers_by_index.end(); + ++ii) { + int this_index = (*ii).first; + while (next_index < this_index) { + out << " (void *)0,\n"; + next_index++; + } + assert(next_index == this_index); + WrapperBuilder *wbuilder = (*ii).second; + + out << " (void *)&" << wbuilder->get_wrapper_name(_library_hash_name) + << ",\n"; + next_index++; + } + while (next_index < num_wrappers + 1) { + out << " (void *)0,\n"; + next_index++; + } + out << "};\n\n"; + } + + if (save_unique_names) { + // Write out the table of unique names, in alphabetical order by name. + out << "static InterrogateUniqueNameDef _in_unique_names[" + << num_wrappers << "] = {\n"; + for (hi = _wrappers_by_hash.begin(); + hi != _wrappers_by_hash.end(); + ++hi) { + WrapperBuilder *wbuilder = (*hi).second; + if (wbuilder != (WrapperBuilder *)NULL) { + out << " { \"" + << (*hi).first << "\", " + << wbuilder->_wrapper_index - 1 << " },\n"; + } + } + out << "};\n\n"; + } + + if (output_module_specific) { + // Output whatever stuff we should output if this were a module. + // Presently, this is only the Python table. + if (build_python_wrappers) { + out << "static PyMethodDef python_methods[] = {\n"; + + for (hi = _wrappers_by_hash.begin(); + hi != _wrappers_by_hash.end(); + ++hi) { + WrapperBuilder *wbuilder = (*hi).second; + string wrapper_name = wbuilder->get_wrapper_name(_library_hash_name); + string true_name = wrapper_name; + if (true_wrapper_names) { + true_name = wbuilder->_function->get_simple_name(); + } + out << " { \"" + << true_name << "\", &" + << wrapper_name << ", METH_VARARGS },\n"; + } + + out << " { NULL, NULL }\n" + << "};\n\n" + + << "#ifdef _WIN32\n" + << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n" + << "#else\n" + << "extern \"C\" void init" << def->library_name << "();\n" + << "#endif\n\n" + + << "void init" << def->library_name << "() {\n" + << " Py_InitModule(\"" << def->library_name + << "\", python_methods);\n" + << "}\n\n"; + } + } + + if (!no_database) { + // Now build the module definition structure to add ourselves to + // the global interrogate database. + out << "static InterrogateModuleDef _in_module_def = {\n" + << " " << def->file_identifier << ", /* file_identifier */\n" + << " \"" << def->library_name << "\", /* library_name */\n" + << " \"" << def->library_hash_name << "\", /* library_hash_name */\n" + << " \"" << def->module_name << "\", /* module_name */\n"; + if (def->database_filename != (const char *)NULL) { + out << " \"" << def->database_filename + << "\", /* database_filename */\n"; + } else { + out << " (const char *)0, /* database_filename */\n"; + } + + if (save_unique_names) { + out << " _in_unique_names,\n" + << " " << num_wrappers << ", /* num_unique_names */\n"; + } else { + out << " (InterrogateUniqueNameDef *)0, /* unique_names */\n" + << " 0, /* num_unique_names */\n"; + } + + if (output_function_pointers) { + out << " _in_fptrs,\n" + << " " << num_wrappers << ", /* num_fptrs */\n"; + } else { + out << " (void **)0, /* fptrs */\n" + << " 0, /* num_fptrs */\n"; + } + + out << " 1, /* first_index */\n" + << " " << InterrogateDatabase::get_ptr()->get_next_index() + << " /* next_index */\n" + << "};\n\n"; + + // And now write the static-init code that tells the interrogate + // database to load up this module. + out << "Configure(_in_configure_" << library_name << ");\n" + << "ConfigureFn(_in_configure_" << library_name << ") {\n" + << " interrogate_request_module(&_in_module_def);\n" + << "}\n\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::make_module_def +// Access: Public +// Description: Allocates and returns a new InterrogateModuleDef +// structure that reflects the data we have just build, +// or at least that subset of the InterrogateModuleDef +// data that we have available at this time. +// +// The data in this structure may include pointers that +// reference directly into the InterrogateBuilder +// object; thus, this structure is only valid for as +// long as the builder itself remains in scope. +//////////////////////////////////////////////////////////////////// +InterrogateModuleDef *InterrogateBuilder:: +make_module_def(int file_identifier) { + InterrogateModuleDef *def = new InterrogateModuleDef; + memset(def, 0, sizeof(InterrogateModuleDef)); + + def->file_identifier = file_identifier; + def->library_name = library_name.c_str(); + def->library_hash_name = _library_hash_name.c_str(); + def->module_name = module_name.c_str(); + if (!output_data_filename.empty()) { + def->database_filename = output_data_filename.c_str(); + } + + return def; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::clean_identifier +// Access: Public, Static +// Description: Adjusts the given string to remove any characters we +// don't want to export as part of an identifier name. +// Returns the cleaned string. +// +// This replaces any consecutive invalid characters with +// an underscore. +//////////////////////////////////////////////////////////////////// +string InterrogateBuilder:: +clean_identifier(const string &name) { + string result; + + bool last_invalid = false; + + string::const_iterator ni; + for (ni = name.begin(); ni != name.end(); ++ni) { + if (isalnum(*ni)) { + if (last_invalid) { + result += '_'; + last_invalid = false; + } + result += (*ni); + } else { + last_invalid = true; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::descope +// Access: Public, Static +// Description: Removes the leading "::", if present, from a +// fully-scoped name. Sometimes CPPParser throws this +// on, and sometimes it doesn't. +//////////////////////////////////////////////////////////////////// +string InterrogateBuilder:: +descope(const string &name) { + if (name.length() >= 2 && name.substr(0, 2) == "::") { + return name.substr(2); + } + return name; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::get_destructor_for +// Access: Public +// Description: Returns the FunctionIndex for the destructor +// appropriate to destruct an instance of the indicated +// type, or 0 if no suitable destructor exists. +//////////////////////////////////////////////////////////////////// +FunctionIndex InterrogateBuilder:: +get_destructor_for(CPPType *type) { + TypeIndex type_index = get_type(type, false); + + const InterrogateType &itype = + InterrogateDatabase::get_ptr()->get_type(type_index); + + return itype.get_destructor(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::insert_param_list +// Access: Public +// Description: Inserts a list of space-separated parameters into the +// given command parameter list. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +insert_param_list(InterrogateBuilder::Commands &commands, + const string ¶ms) { + size_t p = 0; + while (p < params.length()) { + while (p < params.length() && isspace(params[p])) { + p++; + } + size_t q = p; + while (q < params.length() && !isspace(params[q])) { + q++; + } + if (p < q) { + commands.insert(params.substr(p, q - p)); + } + p = q; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::in_forcetype +// Access: Private +// Description: Returns true if the indicated name is one that the +// user identified with a forcetype command. +//////////////////////////////////////////////////////////////////// +bool InterrogateBuilder:: +in_forcetype(const string &name) const { + return (_forcetype.count(name) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::in_ignoretype +// Access: Private +// Description: Returns true if the indicated name is one that the +// user identified with an ignoretype command. +//////////////////////////////////////////////////////////////////// +bool InterrogateBuilder:: +in_ignoretype(const string &name) const { + return (_ignoretype.count(name) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::in_ignoreinvolved +// Access: Private +// Description: Returns true if the indicated name is one that the +// user identified with an ignoreinvolved command. +//////////////////////////////////////////////////////////////////// +bool InterrogateBuilder:: +in_ignoreinvolved(const string &name) const { + return (_ignoreinvolved.count(name) != 0); +} +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::in_ignoreinvolved +// Access: Private +// Description: Returns true if the indicated type involves some type +// name that the user identified with an ignoreinvolved +// command. +//////////////////////////////////////////////////////////////////// +bool InterrogateBuilder:: +in_ignoreinvolved(CPPType *type) const { + switch (type->get_subtype()) { + case CPPDeclaration::ST_pointer: + { + CPPPointerType *ptr = type->as_pointer_type(); + return in_ignoreinvolved(ptr->_pointing_at); + } + + case CPPDeclaration::ST_array: + { + CPPArrayType *ary = type->as_array_type(); + return in_ignoreinvolved(ary->_element_type); + } + + case CPPDeclaration::ST_reference: + { + CPPReferenceType *ref = type->as_reference_type(); + return in_ignoreinvolved(ref->_pointing_at); + } + + case CPPDeclaration::ST_const: + { + CPPConstType *cnst = type->as_const_type(); + return in_ignoreinvolved(cnst->_wrapped_around); + } + + case CPPDeclaration::ST_function: + { + CPPFunctionType *ftype = type->as_function_type(); + if (in_ignoreinvolved(ftype->_return_type)) { + return true; + } + const CPPParameterList::Parameters ¶ms = + ftype->_parameters->_parameters; + CPPParameterList::Parameters::const_iterator pi; + for (pi = params.begin(); pi != params.end(); ++pi) { + if (in_ignoreinvolved((*pi)->_type)) { + return true; + } + } + return false; + } + + default: + return in_ignoreinvolved(type->get_simple_name()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::in_ignorefile +// Access: Private +// Description: Returns true if the indicated name is one that the +// user identified with an ignorefile command. +//////////////////////////////////////////////////////////////////// +bool InterrogateBuilder:: +in_ignorefile(const string &name) const { + return (_ignorefile.count(name) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::in_ignoremember +// Access: Private +// Description: Returns true if the indicated name is one that the +// user identified with an ignoremember command. +//////////////////////////////////////////////////////////////////// +bool InterrogateBuilder:: +in_ignoremember(const string &name) const { + return (_ignoremember.count(name) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::remap_indices +// Access: Private +// Description: Resequences all of the index numbers so that +// function wrappers start at 1 and occupy consecutive +// positions, and everything else follows. This allows +// us to build a table of function wrappers by index +// number. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +remap_indices() { + IndexRemapper remap; + InterrogateDatabase::get_ptr()->remap_indices(1, remap); + + TypesByName::iterator ti; + for (ti = _types_by_name.begin(); ti != _types_by_name.end(); ++ti) { + (*ti).second = remap.map_from((*ti).second); + } + + FunctionsBySignature::iterator fi; + for (fi = _functions_by_signature.begin(); + fi != _functions_by_signature.end(); + ++fi) { + (*fi).second = remap.map_from((*fi).second); + } + + WrappersByHash::iterator hi; + for (hi = _wrappers_by_hash.begin(); + hi != _wrappers_by_hash.end(); + ++hi) { + if ((*hi).second != (WrapperBuilder *)NULL) { + (*hi).second->_wrapper_index = remap.map_from((*hi).second->_wrapper_index); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::scan_function +// Access: Private +// Description: Adds the indicated global function to the database, +// if warranted. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +scan_function(CPPFunctionGroup *fgroup) { + CPPFunctionGroup::Instances::const_iterator fi; + for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { + CPPInstance *function = (*fi); + scan_function(function); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::scan_function +// Access: Private +// Description: Adds the indicated global function to the database, +// if warranted. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +scan_function(CPPInstance *function) { + assert(function != (CPPInstance *)NULL); + assert(function->_type != (CPPType *)NULL && + function->_type->as_function_type() != (CPPFunctionType *)NULL); + CPPFunctionType *ftype = + function->_type->resolve_type(&parser, &parser)->as_function_type(); + assert(ftype != (CPPFunctionType *)NULL); + + CPPScope *scope = &parser; + if (function->is_scoped()) { + scope = function->get_scope(&parser, &parser); + if (scope == (CPPScope *)NULL) { + // Invalid scope. + nout << "Invalid scope: " << *function->_ident << "\n"; + return; + } + + if (scope->get_struct_type() != (CPPStructType *)NULL) { + // Wait, this is a method, not a function. This must be the + // declaration for the method (since it's appearing + // out-of-scope). We don't need to define a new method for it, + // but we'd like to update the comment, if we have a comment. + update_method_comment(function, scope->get_struct_type(), scope); + return; + } + } + + if (function->is_template()) { + // The function is a template function, not a true function. + return; + } + + if (function->_file.is_c_file()) { + // This function declaration appears in a .C file. We can only + // export functions whose prototypes appear in an .h file. + return; + } + + if (function->_file._source != CPPFile::S_local || + in_ignorefile(function->_file._filename_as_referenced)) { + // The function is defined in some other package or in an + // ignorable file. + return; + } + + if (function->_vis > min_vis) { + // The function is not marked to be exported. + return; + } + + if ((function->_storage_class & CPPInstance::SC_static) != 0) { + // The function is static, so can't be exported. + return; + } + + if (TypeManager::involves_protected(ftype)) { + // We can't export the function because it involves parameter + // types that are protected or private. + return; + } + + if (in_ignoreinvolved(ftype)) { + // The function or its parameters involves something that the + // user requested we ignore. + return; + } + + get_function(function, "", + (CPPStructType *)NULL, scope, true, + WrapperBuilder::T_normal); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::scan_struct_type +// Access: Private +// Description: Adds the indicated struct type to the database, if +// warranted. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +scan_struct_type(CPPStructType *type) { + if (type == (CPPStructType *)NULL) { + return; + } + + if (type->is_template()) { + // The type is a template declaration, not a true type. + return; + } + + if (type->_file.is_c_file()) { + // This type declaration appears in a .C file. We can only export + // types defined in a .h file. + return; + } + + if (type->_file._source != CPPFile::S_local || + in_ignorefile(type->_file._filename_as_referenced)) { + // The type is defined in some other package or in an + // ignorable file. + return; + } + + // Check if any of the members are exported. If none of them are, + // and the type itself is not marked for export, then never mind. + if (type->_vis > min_vis) { + CPPScope *scope = type->_scope; + + bool any_exported = false; + CPPScope::Declarations::const_iterator di; + for (di = scope->_declarations.begin(); + di != scope->_declarations.end() && !any_exported; + ++di) { + if ((*di)->_vis <= min_vis) { + any_exported = true; + } + } + + if (!any_exported) { + return; + } + } + + get_type(type, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::scan_enum_type +// Access: Private +// Description: Adds the indicated enum type to the database, if +// warranted. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +scan_enum_type(CPPEnumType *type) { + if (type == (CPPEnumType *)NULL) { + return; + } + + if (type->is_template()) { + // The type is a template declaration, not a true type. + return; + } + + if (type->_file.is_c_file()) { + // This type declaration appears in a .C file. We can only export + // types defined in a .h file. + return; + } + + if (type->_file._source != CPPFile::S_local || + in_ignorefile(type->_file._filename_as_referenced)) { + // The type is defined in some other package or in an + // ignorable file. + return; + } + + if (type->_vis > min_vis) { + // The type is not marked to be exported. + return; + } + + get_type(type, true); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::scan_manifest +// Access: Private +// Description: Adds the indicated manifest constant to the database, +// if warranted. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +scan_manifest(CPPManifest *manifest) { + if (manifest == (CPPManifest *)NULL) { + return; + } + + if (manifest->_file.is_c_file()) { + // This #define appears in a .C file. We can only export + // manifests defined in a .h file. + return; + } + + if (manifest->_file._source != CPPFile::S_local || + in_ignorefile(manifest->_file._filename_as_referenced)) { + // The manifest is defined in some other package or in an + // ignorable file. + return; + } + + if (manifest->_vis > min_vis) { + // The manifest is not marked for export. + return; + } + + if (manifest->_has_parameters) { + // We can't export manifest functions. + return; + } + + InterrogateManifest imanifest; + imanifest._name = manifest->_name; + imanifest._definition = manifest->expand(); + + CPPType *type = manifest->determine_type(); + if (type != (CPPType *)NULL) { + imanifest._flags |= InterrogateManifest::F_has_type; + imanifest._type = get_type(type, false); + + CPPExpression *expr = manifest->_expr; + CPPExpression::Result result = expr->evaluate(); + if (result._type == CPPExpression::RT_integer) { + // We have an integer-valued expression. + imanifest._flags |= InterrogateManifest::F_has_int_value; + imanifest._int_value = result.as_integer(); + + } else { + // We have a more complex expression. Generate a getter + // function. + FunctionIndex getter = + get_getter(type, manifest->_name, (CPPStructType *)NULL, &parser, + (CPPInstance *)NULL); + + if (getter != 0) { + imanifest._flags |= InterrogateManifest::F_has_getter; + imanifest._getter = getter; + } + } + } + + ManifestIndex index = + InterrogateDatabase::get_ptr()->get_next_index(); + InterrogateDatabase::get_ptr()->add_manifest(index, imanifest); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::scan_element +// Access: Private +// Description: Adds the indicated data element to the database, +// if warranted. +//////////////////////////////////////////////////////////////////// +ElementIndex InterrogateBuilder:: +scan_element(CPPInstance *element, CPPStructType *struct_type, + CPPScope *scope) { + if (element == (CPPInstance *)NULL) { + return 0; + } + + if (element->is_template()) { + // The element is a template element, not a true element. + return 0; + } + + if (element->is_scoped()) { + if (element->get_scope(scope, &parser) != scope) { + // This is an element defined out-of-scope. It's probably the + // definition for a data member. Ignore it. + return 0; + } + } + + if (element->_file.is_c_file()) { + // This element declaration appears in a .C file. We can only + // export elements declared in a .h file. + return 0; + } + + if (element->_file._source != CPPFile::S_local || + in_ignorefile(element->_file._filename_as_referenced)) { + // The element is defined in some other package or in an + // ignorable file. + return 0; + } + + if (element->_vis > min_vis) { + // The element is not marked for export. + return 0; + } + + if ((element->_storage_class & CPPInstance::SC_static) != 0) { + // The element is static, so can't be exported. + return 0; + } + + // Make sure the element knows what its scope is. + if (element->_ident->_native_scope != scope) { + element = new CPPInstance(*element); + element->_ident = new CPPIdentifier(*element->_ident); + element->_ident->_native_scope = scope; + } + + CPPType *element_type = TypeManager::resolve_type(element->_type, scope); + CPPType *parameter_type = element_type; + + InterrogateElement ielement; + ielement._name = element->get_local_name(scope); + ielement._scoped_name = descope(element->get_local_name(&parser)); + + ielement._type = get_type(TypeManager::unwrap_reference(element_type), false); + if (ielement._type == 0) { + // If we can't understand what type it is, forget it. + return 0; + } + + if (!TypeManager::involves_protected(element_type)) { + // We can only generate a getter and a setter if we can talk about + // the type it is. + + if (parameter_type->as_struct_type() != (CPPStructType *)NULL) { + // Wrap the type in a const reference. + parameter_type = TypeManager::wrap_const_reference(parameter_type); + } + + // Generate a getter and setter function for the element. + FunctionIndex getter = + get_getter(parameter_type, element->get_local_name(scope), + struct_type, scope, element); + if (getter != 0) { + ielement._flags |= InterrogateElement::F_has_getter; + ielement._getter = getter; + } + + if (TypeManager::is_assignable(element_type)) { + FunctionIndex setter = + get_setter(parameter_type, element->get_local_name(scope), + struct_type, scope, element); + if (setter != 0) { + ielement._flags |= InterrogateElement::F_has_setter; + ielement._setter = setter; + } + } + } + + if (struct_type == (CPPStructType *)NULL) { + // This is a global data element: not a data member. + ielement._flags |= InterrogateElement::F_global; + } + + ElementIndex index = + InterrogateDatabase::get_ptr()->get_next_index(); + InterrogateDatabase::get_ptr()->add_element(index, ielement); + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::get_getter +// Access: Private +// Description: Adds a function to return the value for the indicated +// expression. Returns the new function index. +//////////////////////////////////////////////////////////////////// +FunctionIndex InterrogateBuilder:: +get_getter(CPPType *expr_type, string expression, + CPPStructType *struct_type, CPPScope *scope, + CPPInstance *element) { + // Make up a name for the function. + string function_name = clean_identifier("get_" + expression); + + // Unroll the "const" from the expr_type, since that doesn't matter + // for a return type. + while (expr_type->as_const_type() != (CPPConstType *)NULL) { + expr_type = expr_type->as_const_type()->_wrapped_around; + assert(expr_type != (CPPType *)NULL); + } + + // Make up a CPPFunctionType. + CPPParameterList *params = new CPPParameterList; + CPPFunctionType *ftype = new CPPFunctionType(expr_type, params, 0); + + // Now make up an instance for the function. + CPPInstance *function = new CPPInstance(ftype, function_name); + function->_ident->_native_scope = scope; + + if (struct_type != (CPPStructType *)NULL) { + // This is a data member for some class. + assert(element != (CPPInstance *)NULL); + assert(scope != (CPPScope *)NULL); + + if ((element->_storage_class & CPPInstance::SC_static) != 0) { + // This is a static data member; therefore, the synthesized + // getter is also static. + function->_storage_class |= CPPInstance::SC_static; + + // And the expression is fully scoped. + expression = element->get_local_name(&parser); + + } else { + // This is a non-static data member, so it has a const + // synthesized getter method. + ftype->_flags |= CPPFunctionType::F_const_method; + + // And the expression is locally scoped. + expression = element->get_local_name(scope); + } + } + + // Now check to see if there's already a function matching this + // signature. If there is, we can't define a getter, and we + // shouldn't mistake this other function for a synthesized getter. + string function_signature = TypeManager::get_function_signature(function); + if (_functions_by_signature.count(function_signature) != 0) { + return 0; + } + + ostringstream desc; + desc << "getter for "; + if (element != (CPPInstance *)NULL) { + element->_initializer = (CPPExpression *)NULL; + element->output(desc, 0, &parser, false); + desc << ";"; + } else { + desc << expression; + } + string description = desc.str(); + + // It's clear; make a getter. + FunctionIndex index = + get_function(function, description, + struct_type, scope, false, + WrapperBuilder::T_getter, expression); + + InterrogateDatabase::get_ptr()->update_function(index)._comment = description; + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::get_setter +// Access: Private +// Description: Adds a function to return the value for the indicated +// expression. Returns the new function index. +//////////////////////////////////////////////////////////////////// +FunctionIndex InterrogateBuilder:: +get_setter(CPPType *expr_type, string expression, + CPPStructType *struct_type, CPPScope *scope, + CPPInstance *element) { + // Make up a name for the function. + string function_name = clean_identifier("set_" + expression); + + // Make up a CPPFunctionType. + CPPParameterList *params = new CPPParameterList; + CPPInstance *param0 = new CPPInstance(expr_type, "value"); + params->_parameters.push_back(param0); + CPPType *void_type = TypeManager::get_void_type(); + CPPFunctionType *ftype = new CPPFunctionType(void_type, params, 0); + + // Now make up an instance for the function. + CPPInstance *function = new CPPInstance(ftype, function_name); + function->_ident->_native_scope = scope; + + if (struct_type != (CPPStructType *)NULL) { + // This is a data member for some class. + assert(element != (CPPInstance *)NULL); + assert(scope != (CPPScope *)NULL); + + if ((element->_storage_class & CPPInstance::SC_static) != 0) { + // This is a static data member; therefore, the synthesized + // setter is also static. + function->_storage_class |= CPPInstance::SC_static; + + // And the expression is fully scoped. + expression = element->get_local_name(&parser); + + } else { + // This is a non-static data member. The expression is locally + // scoped. + expression = element->get_local_name(scope); + } + } + + // Now check to see if there's already a function matching this + // signature. If there is, we can't define a setter, and we + // shouldn't mistake this other function for a synthesized setter. + string function_signature = TypeManager::get_function_signature(function); + if (_functions_by_signature.count(function_signature) != 0) { + return 0; + } + + ostringstream desc; + desc << "setter for "; + if (element != (CPPInstance *)NULL) { + element->_initializer = (CPPExpression *)NULL; + element->output(desc, 0, &parser, false); + desc << ";"; + } else { + desc << expression; + } + string description = desc.str(); + + // It's clear; make a setter. + FunctionIndex index = + get_function(function, description, + struct_type, scope, false, + WrapperBuilder::T_setter, expression); + + InterrogateDatabase::get_ptr()->update_function(index)._comment = description; + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::get_cast_function +// Access: Private +// Description: Adds a function to cast from a pointer of the +// indicated type to a pointer of the indicated type to +// the database. Returns the new function index. +//////////////////////////////////////////////////////////////////// +FunctionIndex InterrogateBuilder:: +get_cast_function(CPPType *to_type, CPPType *from_type, + const string &prefix) { + CPPInstance *function; + CPPStructType *struct_type = from_type->as_struct_type(); + CPPScope *scope = &parser; + + if (struct_type != (CPPStructType *)NULL) { + // We'll make this a method of the from type. + scope = struct_type->get_scope(); + + // Make up a name for the method. + string function_name = + clean_identifier(prefix + "_to_" + to_type->get_preferred_name()); + + // Make up a CPPFunctionType. + CPPType *to_ptr_type = CPPType::new_type(new CPPPointerType(to_type)); + + CPPParameterList *params = new CPPParameterList; + CPPFunctionType *ftype = new CPPFunctionType(to_ptr_type, params, 0); + + // Now make up an instance for the function. + function = new CPPInstance(ftype, function_name); + + } else { + // The from type isn't a struct or a class, so this has to be an + // external function. + + // Make up a name for the function. + string function_name = + clean_identifier(prefix + "_" + from_type->get_preferred_name() + + "_to_" + to_type->get_preferred_name()); + + // Make up a CPPFunctionType. + CPPType *from_ptr_type = CPPType::new_type(new CPPPointerType(from_type)); + CPPType *to_ptr_type = CPPType::new_type(new CPPPointerType(to_type)); + + CPPInstance *param0 = new CPPInstance(from_ptr_type, "this"); + CPPParameterList *params = new CPPParameterList; + params->_parameters.push_back(param0); + CPPFunctionType *ftype = new CPPFunctionType(to_ptr_type, params, 0); + + // Now make up an instance for the function. + function = new CPPInstance(ftype, function_name); + } + + ostringstream desc; + desc << prefix << " from " << *from_type << " to " << *to_type; + string description = desc.str(); + + FunctionIndex index = + get_function(function, description, + struct_type, scope, false, + WrapperBuilder::T_typecast); + + InterrogateDatabase::get_ptr()->update_function(index)._comment = description; + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::get_function +// Access: Private +// Description: Adds the indicated function to the database, if it is +// not already present. In either case, returns the +// FunctionIndex of the function within the database. +//////////////////////////////////////////////////////////////////// +FunctionIndex InterrogateBuilder:: +get_function(CPPInstance *function, string description, + CPPStructType *struct_type, + CPPScope *scope, bool global, WrapperBuilder::Type wtype, + const string &expression) { + // Get a unique function signature. Make sure we tell the function + // where its native scope is, so we get a fully-scoped signature. + + if (function->_ident->_native_scope != scope) { + function = new CPPInstance(*function); + function->_ident = new CPPIdentifier(*function->_ident); + function->_ident->_native_scope = scope; + } + CPPFunctionType *ftype = + function->_type->resolve_type(scope, &parser)->as_function_type(); + function->_type = ftype; + + if ((ftype->_flags & CPPFunctionType::F_constructor) && + struct_type != (CPPStructType *)NULL && + struct_type->is_abstract()) { + // This is a constructor for an abstract class; forget it. + return 0; + } + + string function_signature = TypeManager::get_function_signature(function); + + // First, check to see if it's already there. + FunctionsBySignature::const_iterator tni = + _functions_by_signature.find(function_signature); + if (tni != _functions_by_signature.end()) { + FunctionIndex index = (*tni).second; + // It's already here, so update the global flag. + InterrogateFunction &ifunction = + InterrogateDatabase::get_ptr()->update_function(index); + + if (global) { + ifunction._flags |= InterrogateFunction::F_global; + } + + // And/or the comment. + if (function->_leading_comment != (CPPCommentBlock *)NULL) { + string comment = trim_blanks(function->_leading_comment->_comment); + if (!ifunction._comment.empty()) { + ifunction._comment += "\n\n"; + } + ifunction._comment += comment; + } + + return index; + } + + // It isn't here, so we'll have to define it. + FunctionIndex index = + InterrogateDatabase::get_ptr()->get_next_index(); + _functions_by_signature[function_signature] = index; + + InterrogateFunction ifunction; + ifunction._name = function->get_local_name(scope); + ifunction._scoped_name = descope(function->get_local_name(&parser)); + + if (function->_leading_comment != (CPPCommentBlock *)NULL) { + ifunction._comment = trim_blanks(function->_leading_comment->_comment); + } + + ostringstream prototype; + function->output(prototype, 0, &parser, false); + prototype << ";"; + ifunction._prototype = prototype.str(); + + if (struct_type != (CPPStructType *)NULL) { + // The function is a method. + ifunction._flags |= InterrogateFunction::F_method; + ifunction._class = get_type(struct_type, false); + } + + if (global) { + ifunction._flags |= InterrogateFunction::F_global; + } + + InterrogateDatabase::get_ptr()->add_function(index, ifunction); + + // Save a record of the fact that we just defined a new function, so + // we can go back later and make all of the wrappers for it. (We + // don't want to start making wrappers before all of the global + // types are defined.) + NewFunction nf; + nf._function = function; + nf._description = description; + nf._ftype = ftype; + nf._struct_type = struct_type; + nf._scope = scope; + nf._wtype = wtype; + nf._expression = expression; + nf._function_index = index; + + _new_functions.push_back(nf); + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::make_wrappers +// Access: Private +// Description: Makes the wrappers for all of the newly-defined +// functions. This might result in the definition of a +// few new incidental types necessary to support the +// wrappers' parameters. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +make_wrappers() { + while (!_new_functions.empty()) { + // Make a copy of the new_functions list, and iterate through the + // copy. This is just in case we end up defining a few *more* + // functions while we do this. + NewFunctions new_functions; + new_functions.swap(_new_functions); + + NewFunctions::iterator ni; + for (ni = new_functions.begin(); ni != new_functions.end(); ++ni) { + const NewFunction &nf = (*ni); + int num_default_parameters = nf._ftype->get_num_default_parameters(); + + InterrogateFunction &ifunction = + InterrogateDatabase::get_ptr()->update_function(nf._function_index); + + if (build_c_wrappers) { + // Make the C wrapper(s). + for (int dp = 0; dp <= num_default_parameters; dp++) { + FunctionWrapperIndex wrapper = + get_wrapper(nf._function_index, new WrapperBuilderC, + nf._function, nf._description, + nf._struct_type, nf._scope, + nf._wtype, nf._expression, dp); + if (wrapper != 0) { + ifunction._c_wrappers.push_back(wrapper); + } + } + } + + if (build_python_wrappers) { + // Make the Python wrapper. + for (int dp = 0; dp <= num_default_parameters; dp++) { + FunctionWrapperIndex wrapper = + get_wrapper(nf._function_index, new WrapperBuilderPython, + nf._function, nf._description, + nf._struct_type, nf._scope, + nf._wtype, nf._expression, dp); + if (wrapper != 0) { + ifunction._python_wrappers.push_back(wrapper); + } + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::get_wrapper +// Access: Private +// Description: Defines a particular wrapper for the given function. +// wbuilder is a newly-allocated WrapperBuilder of the +// appropriate type. +//////////////////////////////////////////////////////////////////// +FunctionWrapperIndex InterrogateBuilder:: +get_wrapper(FunctionIndex function_index, WrapperBuilder *wbuilder, + CPPInstance *function, string description, + CPPStructType *struct_type, + CPPScope *scope, WrapperBuilder::Type wtype, + const string &expression, + int num_default_parameters) { + + string function_signature = + TypeManager::get_function_signature(function, num_default_parameters); + + if (description.empty()) { + // Make up a description string if we weren't given one. + ostringstream desc; + function->output(desc, 0, &parser, false, num_default_parameters); + desc << ";"; + description = desc.str(); + } + + if (!wbuilder->set_function(function, description, struct_type, scope, + function_signature, wtype, + expression, num_default_parameters)) { + // This function can't be exported for some reason. + delete wbuilder; + return 0; + } + + FunctionWrapperIndex index = + InterrogateDatabase::get_ptr()->get_next_index(); + wbuilder->_wrapper_index = index; + + hash_function_signature(wbuilder); + + InterrogateFunctionWrapper iwrapper; + + iwrapper._function = function_index; + + // We do assume that two different libraries will not has to the + // same name. This is pretty unlikely, although there could be big + // problems if it ever happens. If it does, we'll probably need to + // add an interface so the user can specify a hash offset on a + // per-library basis or something like that. + iwrapper._unique_name = _library_hash_name + wbuilder->_hash; + + WrapperBuilder::Parameters::const_iterator pi; + for (pi = wbuilder->_parameters.begin(); + pi != wbuilder->_parameters.end(); + ++pi) { + InterrogateFunctionWrapper::Parameter param; + param._parameter_flags = 0; + if ((*pi)._remap->new_type_is_atomic_string()) { + param._type = get_atomic_string_type(); + } else { + param._type = get_type((*pi)._remap->get_new_type(), false); + } + param._name = (*pi)._name; + if ((*pi)._has_name) { + param._parameter_flags |= InterrogateFunctionWrapper::PF_has_name; + } + iwrapper._parameters.push_back(param); + } + + if (wbuilder->_has_this) { + // If one of the parameters is "this", it must be the first one. + assert(!iwrapper._parameters.empty()); + iwrapper._parameters.front()._parameter_flags |= + InterrogateFunctionWrapper::PF_is_this; + } + + if (wbuilder->_return_type->new_type_is_atomic_string()) { + iwrapper._return_type = get_atomic_string_type(); + } else { + iwrapper._return_type = + get_type(wbuilder->_return_type->get_new_type(), false); + } + + if (!wbuilder->_void_return) { + iwrapper._flags |= InterrogateFunctionWrapper::F_has_return; + + if (wbuilder->return_value_needs_management()) { + iwrapper._flags |= InterrogateFunctionWrapper::F_caller_manages; + iwrapper._return_value_destructor = + wbuilder->get_return_value_destructor(); + } + } + + InterrogateDatabase::get_ptr()->add_wrapper(index, iwrapper); + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::get_atomic_string_type +// Access: Private +// Description: Returns a TypeIndex for the "atomic string" type, +// which is a bogus type that might be used if -string +// is passed to interrogate. It means to translate +// basic_string and char * to some atomic string +// type, for the scripting language's convenience. +//////////////////////////////////////////////////////////////////// +TypeIndex InterrogateBuilder:: +get_atomic_string_type() { + // Make up a true name that can't possibly clash with an actual C++ + // type name. + string true_name = "atomic string"; + + TypesByName::const_iterator tni = _types_by_name.find(true_name); + if (tni != _types_by_name.end()) { + return (*tni).second; + } + + // This is the first time the atomic string has been requested; + // define it now. + + TypeIndex index = InterrogateDatabase::get_ptr()->get_next_index(); + _types_by_name[true_name] = index; + + InterrogateType itype; + itype._flags |= InterrogateType::F_atomic; + itype._atomic_token = AT_string; + itype._true_name = true_name; + itype._scoped_name = true_name; + itype._name = true_name; + + InterrogateDatabase::get_ptr()->add_type(index, itype); + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::get_type +// Access: Private +// Description: Adds the indicated type to the database, if it is not +// already present. In either case, returns the +// TypeIndex of the type within the database. +//////////////////////////////////////////////////////////////////// +TypeIndex InterrogateBuilder:: +get_type(CPPType *type, bool global) { + if (type->is_template()) { + // Can't do anything with a template type. + return 0; + } + + TypeIndex index = 0; + + // First, check to see if it's already there. + string true_name = type->get_local_name(&parser); + TypesByName::const_iterator tni = _types_by_name.find(true_name); + if (tni != _types_by_name.end()) { + // It's already here, so update the global flag. + index = (*tni).second; + InterrogateType &itype = InterrogateDatabase::get_ptr()->update_type(index); + if (global) { + itype._flags |= InterrogateType::F_global; + } + + if ((itype._flags & InterrogateType::F_fully_defined) != 0) { + return index; + } + + // But wait--it's not fully defined yet! We'll go ahead and + // define it now. + } + + bool forced = in_forcetype(true_name); + + if (index == 0) { + // It isn't already there, so we have to define it. + index = InterrogateDatabase::get_ptr()->get_next_index(); + _types_by_name[true_name] = index; + + InterrogateType itype; + if (global) { + itype._flags |= InterrogateType::F_global; + } + InterrogateDatabase::get_ptr()->add_type(index, itype); + } + + InterrogateType &itype = + InterrogateDatabase::get_ptr()->update_type(index); + + itype._name = type->get_preferred_name(); + itype._scoped_name = true_name; + itype._true_name = true_name; + + if (type->_declaration != (CPPTypeDeclaration *)NULL) { + // This type has a declaration; does the declaration have a + // comment? + CPPTypeDeclaration *decl = type->_declaration; + if (decl->_leading_comment != (CPPCommentBlock *)NULL) { + itype._comment = trim_blanks(decl->_leading_comment->_comment); + } + } + + CPPExtensionType *ext_type = type->as_extension_type(); + if (ext_type != (CPPExtensionType *)NULL) { + // If it's an extension type of some kind, it might be scoped. + if (ext_type->_ident != (CPPIdentifier *)NULL) { + CPPScope *scope = ext_type->_ident->get_scope(&parser, &parser); + while (scope->as_template_scope() != (CPPTemplateScope *)NULL) { + assert(scope->get_parent_scope() != scope); + scope = scope->get_parent_scope(); + assert(scope != (CPPScope *)NULL); + } + + if (scope != &parser) { + // We're scoped! + itype._scoped_name = + descope(scope->get_local_name(&parser) + "::" + itype._name); + CPPStructType *struct_type = scope->get_struct_type(); + + if (struct_type != (CPPStructType *)NULL) { + itype._flags |= InterrogateType::F_nested; + itype._outer_class = get_type(struct_type, false); + } + } + } + } + + if (forced || !in_ignoretype(true_name)) { + itype._flags |= InterrogateType::F_fully_defined; + + if (type->as_simple_type() != (CPPSimpleType *)NULL) { + define_atomic_type(itype, type->as_simple_type()); + + } else if (type->as_pointer_type() != (CPPPointerType *)NULL) { + define_wrapped_type(itype, type->as_pointer_type()); + + } else if (type->as_const_type() != (CPPConstType *)NULL) { + define_wrapped_type(itype, type->as_const_type()); + + } else if (type->as_struct_type() != (CPPStructType *)NULL) { + define_struct_type(itype, type->as_struct_type(), forced); + + } else if (type->as_enum_type() != (CPPEnumType *)NULL) { + define_enum_type(itype, type->as_enum_type()); + + } else if (type->as_extension_type() != (CPPExtensionType *)NULL) { + define_extension_type(itype, type->as_extension_type()); + + } else { + // nout << "Attempt to define invalid type " << true_name << "\n"; + + // Remove the type from the database. + InterrogateDatabase::get_ptr()->remove_type(index); + _types_by_name[true_name] = 0; + index = 0; + } + } + + return index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_atomic_type +// Access: Private +// Description: Builds up a definition for the indicated atomic type. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_atomic_type(InterrogateType &itype, CPPSimpleType *cpptype) { + itype._flags |= InterrogateType::F_atomic; + + switch (cpptype->_type) { + case CPPSimpleType::T_bool: + itype._atomic_token = AT_bool; + break; + + case CPPSimpleType::T_char: + itype._atomic_token = AT_char; + break; + + case CPPSimpleType::T_int: + itype._atomic_token = AT_int; + break; + + case CPPSimpleType::T_float: + itype._atomic_token = AT_float; + break; + + case CPPSimpleType::T_double: + itype._atomic_token = AT_double; + break; + + case CPPSimpleType::T_void: + itype._atomic_token = AT_void; + break; + + default: + nout << "Invalid CPPSimpleType: " << (int)cpptype->_type << "\n"; + itype._atomic_token = AT_not_atomic; + } + + if ((cpptype->_flags & CPPSimpleType::F_longlong) != 0) { + itype._flags |= InterrogateType::F_longlong; + + } else if ((cpptype->_flags & CPPSimpleType::F_long) != 0) { + itype._flags |= InterrogateType::F_long; + } + + if ((cpptype->_flags & CPPSimpleType::F_short) != 0) { + itype._flags |= InterrogateType::F_short; + } + if ((cpptype->_flags & CPPSimpleType::F_unsigned) != 0) { + itype._flags |= InterrogateType::F_unsigned; + } + if ((cpptype->_flags & CPPSimpleType::F_signed) != 0) { + itype._flags |= InterrogateType::F_signed; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_wrapped_type +// Access: Private +// Description: Builds up a definition for the indicated wrapped type. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_wrapped_type(InterrogateType &itype, CPPPointerType *cpptype) { + itype._flags |= (InterrogateType::F_wrapped | InterrogateType::F_pointer); + itype._wrapped_type = get_type(cpptype->_pointing_at, false); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_wrapped_type +// Access: Private +// Description: Builds up a definition for the indicated wrapped type. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_wrapped_type(InterrogateType &itype, CPPConstType *cpptype) { + itype._flags |= (InterrogateType::F_wrapped | InterrogateType::F_const); + itype._wrapped_type = get_type(cpptype->_wrapped_around, false); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_struct_type +// Access: Private +// Description: Builds up a definition for the indicated struct type. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_struct_type(InterrogateType &itype, CPPStructType *cpptype, + bool forced) { + cpptype = TypeManager::resolve_type(cpptype)->as_struct_type(); + assert(cpptype != (CPPStructType *)NULL); + cpptype->check_virtual(); + + switch (cpptype->_type) { + case CPPExtensionType::T_class: + itype._flags |= InterrogateType::F_class; + break; + + case CPPExtensionType::T_struct: + itype._flags |= InterrogateType::F_struct; + break; + + case CPPExtensionType::T_union: + itype._flags |= InterrogateType::F_union; + break; + + default: + break; + } + + if (cpptype->_file.is_c_file()) { + // This type declaration appears in a .C file. We can only export + // types defined in a .h file. + return; + } + + if (!forced && + (cpptype->_file._source != CPPFile::S_local || + in_ignorefile(cpptype->_file._filename_as_referenced))) { + // The struct type is defined in some other package or in an + // ignorable file; skip it. + itype._flags &= ~InterrogateType::F_fully_defined; + return; + } + + // Make sure the class declaration within its parent scope isn't + // private or protected. If it is, we can't export any of its + // members. + if (TypeManager::involves_unpublished(cpptype)) { + itype._flags &= ~InterrogateType::F_fully_defined; + itype._flags |= InterrogateType::F_unpublished; + return; + } + + // A struct type should always be global. + itype._flags |= InterrogateType::F_global; + + CPPScope *scope = cpptype->_scope; + + // Record the derivation of this class. Do we need to synthesize + // upcast/downcast functions? + bool generate_casts = false; + if (cpptype->_derivation.size() > 1) { + // If we have multiple inheritance, we need explicit cast operators. + generate_casts = true; + } + if (cpptype->_derivation.size() == 1) { + // If we have single inheritance, but it's a virtual inheritance, + // we also need explicit cast operators. + if (cpptype->_derivation.front()._is_virtual) { + generate_casts = true; + } + } + + CPPStructType::Derivation::const_iterator bi; + for (bi = cpptype->_derivation.begin(); + bi != cpptype->_derivation.end(); + ++bi) { + const CPPStructType::Base &base = (*bi); + if (base._vis <= V_public) { + CPPType *base_type = TypeManager::resolve_type(base._base, scope); + TypeIndex base_index = get_type(base_type, true); + + InterrogateType::Derivation d; + d._flags = 0; + d._base = base_index; + d._upcast = 0; + d._downcast = 0; + + if (generate_casts) { + d._upcast = get_cast_function(base_type, cpptype, "upcast"); + d._flags |= InterrogateType::DF_upcast; + + if ((*bi)._is_virtual) { + // If this is a virtual inheritance, we can't write a + // downcast. + d._flags |= InterrogateType::DF_downcast_impossible; + } else { + d._downcast = get_cast_function(cpptype, base_type, "downcast"); + d._flags |= InterrogateType::DF_downcast; + } + } + + itype._derivations.push_back(d); + } + } + + CPPScope::Declarations::const_iterator di; + for (di = scope->_declarations.begin(); + di != scope->_declarations.end(); + ++di) { + if ((*di)->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = (*di)->as_instance(); + if (inst->_type->get_subtype() == CPPDeclaration::ST_function) { + // Here's a method declaration. + define_method(inst, itype, cpptype, scope); + + } else { + // Here's a data member declaration. + ElementIndex data_member = scan_element(inst, cpptype, scope); + if (data_member != 0) { + itype._elements.push_back(data_member); + } + } + + } else if ((*di)->get_subtype() == CPPDeclaration::ST_type_declaration) { + CPPType *type = (*di)->as_type_declaration()->_type; + + if ((*di)->_vis <= min_vis) { + if (type->as_struct_type() != (CPPStructType *)NULL || + type->as_enum_type() != (CPPEnumType *)NULL) { + // Here's a nested class or enum definition. + type->_vis = (*di)->_vis; + + CPPExtensionType *nested_type = type->as_extension_type(); + assert(nested_type != (CPPExtensionType *)NULL); + + // Only try to export named types. + if (nested_type->_ident != (CPPIdentifier *)NULL) { + TypeIndex nested_index = get_type(nested_type, false); + itype._nested_types.push_back(nested_index); + } + } + } + } + } + + if ((itype._flags & InterrogateType::F_inherited_destructor) != 0) { + // If we have inherited our virtual destructor from our base + // class, go ahead and assign the same function index. + assert(!itype._derivations.empty()); + TypeIndex base_type_index = itype._derivations.front()._base; + InterrogateType &base_type = InterrogateDatabase::get_ptr()-> + update_type(base_type_index); + + itype._destructor = base_type._destructor; + + } else if ((itype._flags & + (InterrogateType::F_true_destructor | + InterrogateType::F_private_destructor | + InterrogateType::F_inherited_destructor | + InterrogateType::F_implicit_destructor)) == 0) { + // If we didn't get a destructor at all, we should make a wrapper + // for one anyway. + string function_name = "~" + cpptype->get_simple_name(); + + // Make up a CPPFunctionType. + CPPType *void_type = TypeManager::get_void_type(); + CPPParameterList *params = new CPPParameterList; + CPPFunctionType *ftype = new CPPFunctionType(void_type, params, 0); + ftype->_flags |= CPPFunctionType::F_destructor; + + // Now make up an instance for the destructor. + CPPInstance *function = new CPPInstance(ftype, function_name); + + itype._destructor = get_function(function, "", + cpptype, cpptype->get_scope(), + false, WrapperBuilder::T_normal); + itype._flags |= InterrogateType::F_implicit_destructor; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::update_method_comment +// Access: Private +// Description: Updates the method definition in the database to +// include whatever comment is associated with this +// declaration. This is called when we encounted a +// method definition outside of the class; the only new +// information this might include for us is the method +// comment. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +update_method_comment(CPPInstance *function, CPPStructType *struct_type, + CPPScope *scope) { + if (function->_leading_comment == (CPPCommentBlock *)NULL) { + // No comment anyway. Forget it. + return; + } + + // Get a function signature so we can look this method up. + if (function->_ident->_native_scope != scope) { + function = new CPPInstance(*function); + function->_ident = new CPPIdentifier(*function->_ident); + function->_ident->_native_scope = scope; + } + CPPFunctionType *ftype = + function->_type->resolve_type(scope, &parser)->as_function_type(); + function->_type = ftype; + + string function_signature = TypeManager::get_function_signature(function); + + // Now look it up. + FunctionsBySignature::const_iterator tni = + _functions_by_signature.find(function_signature); + if (tni != _functions_by_signature.end()) { + FunctionIndex index = (*tni).second; + + // Here it is! + InterrogateFunction &ifunction = + InterrogateDatabase::get_ptr()->update_function(index); + + // Update the comment. + string comment = trim_blanks(function->_leading_comment->_comment); + if (!ifunction._comment.empty()) { + ifunction._comment += "\n\n"; + } + ifunction._comment += comment; + /* + if (comment.length() > ifunction._comment.length()) { + ifunction._comment = comment; + } + */ + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_method +// Access: Private +// Description: Adds the indicated member function to the struct type, +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_method(CPPFunctionGroup *fgroup, InterrogateType &itype, + CPPStructType *struct_type, CPPScope *scope) { + CPPFunctionGroup::Instances::const_iterator fi; + for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { + CPPInstance *function = (*fi); + define_method(function, itype, struct_type, scope); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_method +// Access: Private +// Description: Adds the indicated member function to the struct type, +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_method(CPPInstance *function, InterrogateType &itype, + CPPStructType *struct_type, CPPScope *scope) { + assert(function != (CPPInstance *)NULL); + assert(function->_type != (CPPType *)NULL && + function->_type->as_function_type() != (CPPFunctionType *)NULL); + CPPFunctionType *ftype = + function->_type->resolve_type(scope, &parser)->as_function_type(); + + if (function->is_template()) { + // The function is a template function, not a true function. + return; + } + + if (function->_vis > min_vis) { + // The function is not marked to be exported. + if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { + itype._flags |= InterrogateType::F_private_destructor; + } + return; + } + + if (TypeManager::involves_protected(ftype)) { + // We can't export the function because it involves parameter + // types that are protected or private. + return; + } + + if (in_ignoreinvolved(ftype)) { + // The function or its parameters involves something that the + // user requested we ignore. + if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { + itype._flags |= InterrogateType::F_private_destructor; + } + return; + } + + if (in_ignoremember(function->get_simple_name())) { + // The user requested us to ignore members of this name. + if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { + itype._flags |= InterrogateType::F_private_destructor; + } + return; + } + + if ((function->_storage_class & CPPInstance::SC_inherited_virtual) != 0 && + struct_type->_derivation.size() == 1) { + // If this function is a virtual function whose first appearance + // is in some base class, we don't need to repeat its definition + // here, since we're already inheriting it properly. However, we + // may need to make an exception in the presence of multiple + // inheritance. + if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { + itype._flags |= InterrogateType::F_inherited_destructor; + } + return; + } + + + FunctionIndex index = get_function(function, "", struct_type, scope, + false, WrapperBuilder::T_normal); + if (index != 0) { + if ((ftype->_flags & CPPFunctionType::F_constructor) != 0) { + if (find(itype._constructors.begin(), itype._constructors.end(), + index) == itype._constructors.end()) { + itype._constructors.push_back(index); + } + + } else if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { + itype._flags |= InterrogateType::F_true_destructor; + itype._destructor = index; + + } else if ((ftype->_flags & CPPFunctionType::F_operator_typecast) != 0) { + if (find(itype._casts.begin(), itype._casts.end(), + index) == itype._casts.end()) { + itype._casts.push_back(index); + } + + } else { + if (find(itype._methods.begin(), itype._methods.end(), + index) == itype._methods.end()) { + itype._methods.push_back(index); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_enum_type +// Access: Private +// Description: Builds up a definition for the indicated enum type. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_enum_type(InterrogateType &itype, CPPEnumType *cpptype) { + itype._flags |= InterrogateType::F_enum; + + CPPScope *scope = &parser; + if (cpptype->_ident != (CPPIdentifier *)NULL) { + scope = cpptype->_ident->get_scope(&parser, &parser); + } + + // Make sure the enum declaration within its parent scope isn't + // private or protected. If it is, we can't export any of its + // members. + if (TypeManager::involves_unpublished(cpptype)) { + itype._flags &= ~InterrogateType::F_fully_defined; + itype._flags |= InterrogateType::F_unpublished; + return; + } + + int next_value = 0; + + CPPEnumType::Elements::const_iterator ei; + for (ei = cpptype->_elements.begin(); + ei != cpptype->_elements.end(); + ++ei) { + CPPInstance *element = (*ei); + + // Tell the enum element where its native scope is, so we can get + // a properly scoped name. + + if (element->_ident->_native_scope != scope) { + element = new CPPInstance(*element); + element->_ident = new CPPIdentifier(*element->_ident); + element->_ident->_native_scope = scope; + } + + InterrogateType::EnumValue evalue; + evalue._name = element->get_simple_name(); + evalue._scoped_name = descope(element->get_local_name(&parser)); + + if (element->_initializer != (CPPExpression *)NULL) { + CPPExpression::Result result = element->_initializer->evaluate(); + next_value = result.as_integer(); + } + evalue._value = next_value; + itype._enum_values.push_back(evalue); + + next_value++; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::define_extension_type +// Access: Private +// Description: Builds up a definition for the indicated extension type. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +define_extension_type(InterrogateType &itype, CPPExtensionType *cpptype) { + // An "extension type" as returned by CPPParser is really a forward + // reference to an undefined struct or class type. + itype._flags &= ~InterrogateType::F_fully_defined; + + // But we can at least indicate which of the various extension types + // it is. + switch (cpptype->_type) { + case CPPExtensionType::T_enum: + itype._flags |= InterrogateType::F_enum; + break; + + case CPPExtensionType::T_class: + itype._flags |= InterrogateType::F_class; + break; + + case CPPExtensionType::T_struct: + itype._flags |= InterrogateType::F_struct; + break; + + case CPPExtensionType::T_union: + itype._flags |= InterrogateType::F_union; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::hash_function_signature +// Access: Private +// Description: Generates a unique string that corresponds to the +// indicated function signature, and stores it in the +// WrapperBuilder object. +//////////////////////////////////////////////////////////////////// +void InterrogateBuilder:: +hash_function_signature(WrapperBuilder *wbuilder) { + string hash = hash_string(wbuilder->_function_signature, + wbuilder->get_calling_convention()); + + // Now make sure we don't have another function with the same hash. + WrappersByHash::iterator hi; + hi = _wrappers_by_hash.find(hash); + if (hi == _wrappers_by_hash.end()) { + // No other name; we're in the clear. + _wrappers_by_hash[hash] = wbuilder; + wbuilder->_hash = hash; + return; + } + + if ((*hi).second != (WrapperBuilder *)NULL && + (*hi).second->_function_signature == wbuilder->_function_signature) { + // The same function signature has already appeared. This + // shouldn't happen. + nout << "Internal error! Function signature " + << wbuilder->_function_signature << " repeated!\n"; + wbuilder->_hash = hash; + return; + } + + cerr << "hash conflict: " << wbuilder->_function_signature + << " maps to " << hash << "\n"; + + // We have a conflict. Extend both strings to resolve the + // ambiguity. + if ((*hi).second != (WrapperBuilder *)NULL) { + WrapperBuilder *other_wbuilder = (*hi).second; + cerr << "(already taken by " << other_wbuilder->_function_signature << ")\n"; + (*hi).second = (WrapperBuilder *)NULL; + other_wbuilder->_hash += + hash_string(other_wbuilder->_function_signature, + other_wbuilder->get_calling_convention(), + 11); + bool inserted = _wrappers_by_hash.insert + (WrappersByHash::value_type(other_wbuilder->_hash, other_wbuilder)).second; + if (!inserted) { + nout << "Internal error! Hash " << other_wbuilder->_hash + << " already appears!\n"; + } + cerr << "first to " << other_wbuilder->_hash << "\n"; + } + + hash += hash_string(wbuilder->_function_signature, + wbuilder->get_calling_convention(), + 11); + bool inserted = _wrappers_by_hash.insert + (WrappersByHash::value_type(hash, wbuilder)).second; + + cerr << "second to " << hash << "\n"; + + if (!inserted) { + // Huh. We still have a conflict. This should be extremely rare. + // Well, just tack on a letter until it's resolved. + string old_hash = hash; + for (char ch = 'a'; ch <= 'z' && !inserted; ch++) { + hash = old_hash + ch; + inserted = _wrappers_by_hash.insert + (WrappersByHash::value_type(hash, wbuilder)).second; + } + cerr << "New hash is " << hash << "\n"; + if (!inserted) { + nout << "Internal error! Too many conflicts with hash " + << hash << "\n"; + } + } + + wbuilder->_hash = hash; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::hash_string +// Access: Private +// Description: Hashes an arbitrary string into a four-character +// string using only the characters legal in a C +// identifier. +//////////////////////////////////////////////////////////////////// +string InterrogateBuilder:: +hash_string(const string &name, int additional_number, int shift_offset) { + int hash = 0; + + int shift = 0; + string::const_iterator ni; + for (ni = name.begin(); ni != name.end(); ++ni) { + int c = (int)(unsigned char)(*ni); + int shifted_c = (c << shift) & 0xffffff; + if (shift > 16) { + // We actually want a circular shift, not an arithmetic shift. + shifted_c |= ((c >> (24 - shift)) & 0xff) ; + } + hash = (hash + shifted_c) & 0xffffff; + shift = (shift + shift_offset) % 24; + } + + // Now multiply the hash by a biggish prime number and apply the + // high-order bits back at the bottom, to scramble up the bits a + // bit. This helps reduce hash conflicts from names that are + // similar to each other, by separating adjacent hash codes. + int prime = 4999; + int low_order = (hash * prime) & 0xffffff; + int high_order = (int)((double)hash * (double)prime / (double)(1 << 24)); + hash = low_order ^ high_order; + + // Also add in the additional_number, times some prime factor. + hash = (hash + additional_number * 1657) & 0xffffff; + + // Now turn the hash code into a four-character string. For each + // six bits, we choose a character in the set [A-Za-z0-9_]. Note + // that there are only 63 characters to choose from; we have to + // duplicate '_' for values 62 and 63. This introduces a small + // additional chance of hash conflicts. No big deal, since we have + // to resolve hash conflicts anyway. + + string result; + int extract_h = hash; + for (int i = 0; i < 4; i++) { + int value = (extract_h & 0x3f); + extract_h >>= 6; + if (value < 26) { + result += (char)('A' + value); + + } else if (value < 52) { + result += (char)('a' + value - 26); + + } else if (value < 62) { + result += (char)('0' + value - 52); + + } else { + result += '_'; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateBuilder::trim_blanks +// Access: Private, Static +// Description: +//////////////////////////////////////////////////////////////////// +string InterrogateBuilder:: +trim_blanks(const string &str) { + size_t start = 0; + while (start < str.length() && isspace(str[start])) { + start++; + } + + size_t end = str.length(); + while (end > start && isspace(str[end - 1])) { + end--; + } + + return str.substr(start, end - start); +} diff --git a/dtool/src/interrogate/interrogateBuilder.h b/dtool/src/interrogate/interrogateBuilder.h new file mode 100644 index 0000000000..d376ee8a84 --- /dev/null +++ b/dtool/src/interrogate/interrogateBuilder.h @@ -0,0 +1,160 @@ +// Filename: interrogateBuilder.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATEBUILDER_H +#define INTERROGATEBUILDER_H + +#include + +#include "interrogate_interface.h" +#include "interrogate_request.h" +#include "wrapperBuilder.h" + +#include +#include +#include + +class CPPFunctionGroup; +class CPPInstance; +class CPPType; +class CPPSimpleType; +class CPPPointerType; +class CPPConstType; +class CPPExtensionType; +class CPPStructType; +class CPPEnumType; +class CPPFunctionType; +class CPPScope; +class CPPIdentifier; +class CPPNameComponent; +class CPPManifest; +class InterrogateType; + +//////////////////////////////////////////////////////////////////// +// Class : InterrogateBuilder +// Description : This class builds up the InterrogateDatabase based on +// the data indicated by CPPParser after reading the +// source code. +//////////////////////////////////////////////////////////////////// +class InterrogateBuilder { +public: + void add_source_file(const string &filename); + void read_command_file(istream &in); + void do_command(const string &command, const string ¶ms); + void build(); + void write_code(ostream &out, InterrogateModuleDef *def); + InterrogateModuleDef *make_module_def(int file_identifier); + + static string clean_identifier(const string &name); + static string descope(const string &name); + FunctionIndex get_destructor_for(CPPType *type); + +private: + typedef set Commands; + void insert_param_list(InterrogateBuilder::Commands &commands, + const string ¶ms); + + bool in_forcetype(const string &name) const; + bool in_ignoretype(const string &name) const; + bool in_ignoreinvolved(const string &name) const; + bool in_ignoreinvolved(CPPType *type) const; + bool in_ignorefile(const string &name) const; + bool in_ignoremember(const string &name) const; + + void remap_indices(); + void scan_function(CPPFunctionGroup *fgroup); + void scan_function(CPPInstance *function); + void scan_struct_type(CPPStructType *type); + void scan_enum_type(CPPEnumType *type); + void scan_manifest(CPPManifest *manifest); + ElementIndex scan_element(CPPInstance *element, CPPStructType *struct_type, + CPPScope *scope); + + FunctionIndex get_getter(CPPType *expr_type, string expression, + CPPStructType *struct_type, CPPScope *scope, + CPPInstance *element); + FunctionIndex get_setter(CPPType *expr_type, string expression, + CPPStructType *struct_type, CPPScope *scope, + CPPInstance *element); + FunctionIndex get_cast_function(CPPType *to_type, CPPType *from_type, + const string &prefix); + FunctionIndex + get_function(CPPInstance *function, string description, + CPPStructType *struct_type, CPPScope *scope, + bool global, WrapperBuilder::Type wtype, + const string &expression = string()); + + void make_wrappers(); + FunctionWrapperIndex + get_wrapper(FunctionIndex function_index, + WrapperBuilder *wbuilder, CPPInstance *function, + string description, CPPStructType *struct_type, CPPScope *scope, + WrapperBuilder::Type wtype, const string &expression, + int num_default_parameters); + + TypeIndex get_atomic_string_type(); + TypeIndex get_type(CPPType *type, bool global); + + void define_atomic_type(InterrogateType &itype, CPPSimpleType *cpptype); + void define_wrapped_type(InterrogateType &itype, CPPPointerType *cpptype); + void define_wrapped_type(InterrogateType &itype, CPPConstType *cpptype); + void define_struct_type(InterrogateType &itype, CPPStructType *cpptype, + bool forced); + void update_method_comment(CPPInstance *function, CPPStructType *struct_type, + CPPScope *scope); + void define_method(CPPFunctionGroup *fgroup, InterrogateType &itype, + CPPStructType *struct_type, CPPScope *scope); + void define_method(CPPInstance *function, InterrogateType &itype, + CPPStructType *struct_type, CPPScope *scope); + void define_enum_type(InterrogateType &itype, CPPEnumType *cpptype); + void define_extension_type(InterrogateType &itype, + CPPExtensionType *cpptype); + + void hash_function_signature(WrapperBuilder *wbuilder); + string hash_string(const string &name, + int additional_number = 0, + int shift_offset = 5); + + static string trim_blanks(const string &str); + + class NewFunction { + public: + CPPInstance *_function; + string _description; + CPPFunctionType *_ftype; + CPPStructType *_struct_type; + CPPScope *_scope; + WrapperBuilder::Type _wtype; + string _expression; + int _function_index; + }; + typedef vector NewFunctions; + NewFunctions _new_functions; + + typedef map TypesByName; + typedef map FunctionsBySignature; + typedef map WrappersByHash; + + TypesByName _types_by_name; + FunctionsBySignature _functions_by_signature; + WrappersByHash _wrappers_by_hash; + + typedef set IncludeFiles; + IncludeFiles _include_files; + + Commands _forcetype; + Commands _ignoretype; + Commands _ignoreinvolved; + Commands _ignorefile; + Commands _ignoremember; + + string _library_hash_name; +}; + +extern InterrogateBuilder builder; + +#endif + + diff --git a/dtool/src/interrogate/interrogate_module.cxx b/dtool/src/interrogate/interrogate_module.cxx new file mode 100644 index 0000000000..24f36baeed --- /dev/null +++ b/dtool/src/interrogate/interrogate_module.cxx @@ -0,0 +1,245 @@ +// Filename: interrogate_module.C +// Created by: drose (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +// This program generates a module-level file for interrogate. This +// is a higher level than library, and groups several libraries +// together. Presently, the only thing that goes into the module file +// is a python table, but who knows what the future holds. + +#include +#include +#include +#include +#include + +// If our system getopt() doesn't come with getopt_long_only(), then use +// the GNU flavor that we've got in tool for this purpose. +#ifndef HAVE_GETOPT_LONG_ONLY +#include +#else +#include +#endif + +Filename output_code_filename; +string module_name; +string library_name; +bool build_c_wrappers = false; +bool build_python_wrappers = false; + +// Short command-line options. +static const char *short_options = ""; + +// Long command-line options. +enum CommandOptions { + CO_oc = 256, + CO_module, + CO_library, + CO_c, + CO_python, +}; + +static struct option long_options[] = { + { "oc", required_argument, NULL, CO_oc }, + { "module", required_argument, NULL, CO_module }, + { "library", required_argument, NULL, CO_library }, + { "c", no_argument, NULL, CO_c }, + { "python", no_argument, NULL, CO_python }, + { NULL } +}; + +/* +static string +upcase_string(const string &str) { + string result; + for (string::const_iterator si = str.begin(); + si != str.end(); + ++si) { + result += toupper(*si); + } + return result; +} +*/ + +int +write_python_table(ostream &out) { + out << "\n#include \n\n" + << "\n#include \n\n"; + + int count = 0; + + // First, we have to declare extern C prototypes for each of the + // function names. + + out << "extern \"C\" {\n"; + + // Walk through all of the Python functions. + int num_functions = interrogate_number_of_functions(); + int fi; + for (fi = 0; fi < num_functions; fi++) { + FunctionIndex function_index = interrogate_get_function(fi); + + // Consider only those that belong in the module we asked for. + if (interrogate_function_has_module_name(function_index) && + module_name == interrogate_function_module_name(function_index)) { + + // For each function, get all of the python wrappers. + int num_wrappers = + interrogate_function_number_of_python_wrappers(function_index); + + for (int wi = 0; wi < num_wrappers; wi++) { + FunctionWrapperIndex wrapper_index = + interrogate_function_python_wrapper(function_index, wi); + + if (interrogate_wrapper_is_callable_by_name(wrapper_index)) { + count++; + const char *wrapper_name = + interrogate_wrapper_name(wrapper_index); + out << " PyObject *" << wrapper_name + << "(PyObject *self, PyObject *args);\n"; + } + } + } + } + + out << "}\n\n"; + + // Now go back through and build the table of function names. + out << "static PyMethodDef python_methods[" << count + 1 << "] = {\n"; + + // Walk through all of the Python functions. + for (fi = 0; fi < num_functions; fi++) { + FunctionIndex function_index = interrogate_get_function(fi); + + // Consider only those that belong in the module we asked for. + if (interrogate_function_has_module_name(function_index) && + module_name == interrogate_function_module_name(function_index)) { + + // For each function, get all of the python wrappers. + int num_wrappers = + interrogate_function_number_of_python_wrappers(function_index); + for (int wi = 0; wi < num_wrappers; wi++) { + FunctionWrapperIndex wrapper_index = + interrogate_function_python_wrapper(function_index, wi); + + if (interrogate_wrapper_is_callable_by_name(wrapper_index)) { + const char *wrapper_name = + interrogate_wrapper_name(wrapper_index); + out << " { \"" + << wrapper_name << "\", &" + << wrapper_name << ", METH_VARARGS },\n"; + } + } + } + } + + if (library_name.empty()) { + library_name = module_name; + } + + out << " { NULL, NULL }\n" + << "};\n\n" + + << "#ifdef _WIN32\n" + << "extern \"C\" __declspec(dllexport) void init" << library_name << "();\n" + << "#else\n" + << "extern \"C\" void init" << library_name << "();\n" + << "#endif\n\n" + + << "void init" << library_name << "() {\n" + << " Py_InitModule(\"" << library_name << "\", python_methods);\n" + << "}\n\n"; + + return count; +} + +int +main(int argc, char *argv[]) { + extern char *optarg; + extern int optind; + int flag; + + flag = getopt_long_only(argc, argv, short_options, long_options, NULL); + while (flag != EOF) { + switch (flag) { + case CO_oc: + output_code_filename = optarg; + break; + + case CO_module: + module_name = optarg; + break; + + case CO_library: + library_name = optarg; + break; + + case CO_c: + build_c_wrappers = true; + break; + + case CO_python: + build_python_wrappers = true; + break; + + default: + exit(1); + } + flag = getopt_long_only(argc, argv, short_options, long_options, NULL); + } + + argc -= (optind-1); + argv += (optind-1); + + if (argc < 2) { + nout + << "\nUsage:\n" + << " interrogate-module [opts] libname.in [libname.in ...]\n\n"; + exit(1); + } + + output_code_filename.set_text(); + + if (!build_c_wrappers && !build_python_wrappers) { + build_c_wrappers = true; + } + + for (int i = 1; i < argc; i++) { + string param = argv[i]; + + + if (param.length() > 3 && param.substr(param.length() - 3) == ".in") { + // If the filename ends in ".in", it's an interrogate database + // file, not a shared library--read it directly. + interrogate_request_database(param.c_str()); + + } else { + // Otherwise, assume it's a shared library, and try to load it. + Filename pathname = argv[i]; + pathname.set_type(Filename::T_dso); + nout << "Loading " << pathname << "\n"; + void *dl = load_dso(pathname); + if (dl == NULL) { + nout << "Unable to load: " << load_dso_error() << "\n"; + exit(1); + } + } + } + + // Now output the table. + if (!output_code_filename.empty()) { + ofstream output_code; + + if (!output_code_filename.open_write(output_code)) { + nout << "Unable to write to " << output_code_filename << "\n"; + } else { + if (build_python_wrappers) { + int count = write_python_table(output_code); + nout << count << " python function wrappers exported.\n"; + } + } + } + + return (0); +} diff --git a/dtool/src/interrogate/parameterRemap.I b/dtool/src/interrogate/parameterRemap.I new file mode 100644 index 0000000000..6840a5a279 --- /dev/null +++ b/dtool/src/interrogate/parameterRemap.I @@ -0,0 +1,104 @@ +// Filename: parameterRemap.I +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ParameterRemap:: +ParameterRemap(CPPType *orig_type) : + _orig_type(orig_type), + _new_type(orig_type) +{ + _is_valid = true; + _temporary_type = (CPPType *)NULL; + _default_value = (CPPExpression *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::is_valid +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool ParameterRemap:: +is_valid() const { + return _is_valid; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::get_orig_type +// Access: Public +// Description: Returns the type of the original, C++ parameter or +// return value. +//////////////////////////////////////////////////////////////////// +INLINE CPPType *ParameterRemap:: +get_orig_type() const { + return _orig_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::get_new_type +// Access: Public +// Description: Returns the type of the wrapper's parameter or return +// value. This is the type that will be reported in the +// interrogate database, and the type that the scripting +// language is expected to deal with. +//////////////////////////////////////////////////////////////////// +INLINE CPPType *ParameterRemap:: +get_new_type() const { + return _new_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::get_temporary_type +// Access: Public +// Description: Returns the type of any temporary variables used to +// hold the return value before returning it. This is +// normally the same as get_new_type(), but in some +// circumstances it may need to be different. +//////////////////////////////////////////////////////////////////// +INLINE CPPType *ParameterRemap:: +get_temporary_type() const { + if (_temporary_type == (CPPType *)NULL) { + return _new_type; + } else { + return _temporary_type; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::has_default_value +// Access: Public +// Description: Returns true if this particular parameter has a +// default value defined. +//////////////////////////////////////////////////////////////////// +INLINE bool ParameterRemap:: +has_default_value() const { + return (_default_value != (CPPExpression *)NULL); +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::get_default_value +// Access: Public +// Description: Returns the expression corresponding to this parameter's +// default value. +//////////////////////////////////////////////////////////////////// +INLINE CPPExpression *ParameterRemap:: +get_default_value() const { + return _default_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::set_default_value +// Access: Public +// Description: Records a default value to be associated with this +// parameter. +//////////////////////////////////////////////////////////////////// +INLINE void ParameterRemap:: +set_default_value(CPPExpression *expr) { + _default_value = expr; +} diff --git a/dtool/src/interrogate/parameterRemap.cxx b/dtool/src/interrogate/parameterRemap.cxx new file mode 100644 index 0000000000..c7ed417068 --- /dev/null +++ b/dtool/src/interrogate/parameterRemap.cxx @@ -0,0 +1,140 @@ +// Filename: parameterRemap.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemap.h" + + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemap:: +~ParameterRemap() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the original type to the new type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemap:: +pass_parameter(ostream &out, const string &variable_name) { + out << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::prepare_return_expr +// Access: Public, Virtual +// Description: This will be called immediately before +// get_return_expr(). It outputs whatever lines the +// remapper needs to the function to set up its return +// value, e.g. to declare a temporary variable or +// something. It should return the modified expression. +//////////////////////////////////////////////////////////////////// +string ParameterRemap:: +prepare_return_expr(ostream &, int, const string &expression) { + return expression; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemap:: +get_return_expr(const string &expression) { + return expression; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::temporary_to_return +// Access: Public, Virtual +// Description: Returns the string that converts the expression +// stored in the indicated temporary variable to the +// appropriate return value type. This is normally a +// pass-through, but in cases when the temporary +// variable type must be different than the return type +// (i.e. get_temporary_type() != get_new_type()), this +// might perform some operation. +//////////////////////////////////////////////////////////////////// +string ParameterRemap:: +temporary_to_return(const string &temporary) { + return temporary; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::return_value_needs_management +// Access: Public, Virtual +// Description: Returns true if the return value represents a value +// that was newly allocated, and hence must be +// explicitly deallocated later by the caller. +//////////////////////////////////////////////////////////////////// +bool ParameterRemap:: +return_value_needs_management() { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::get_return_value_destructor +// Access: Public, Virtual +// Description: If return_value_needs_management() returns true, this +// should return the index of the function that should +// be called when it is time to destruct the return +// value. It will generally be the same as the +// destructor for the class we just returned a pointer +// to. +//////////////////////////////////////////////////////////////////// +FunctionIndex ParameterRemap:: +get_return_value_destructor() { + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::return_value_should_be_simple +// Access: Public, Virtual +// Description: This is a hack around a problem VC++ has with +// overly-complex expressions, particularly in +// conjunction with the 'new' operator. If this +// parameter type is one that will probably give VC++ a +// headache, this should be set true to indicate that +// the code generator should save the return value +// expression into a temporary variable first, and pass +// the temporary variable name in instead. +//////////////////////////////////////////////////////////////////// +bool ParameterRemap:: +return_value_should_be_simple() { + return false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::new_type_is_atomic_string +// Access: Public, Virtual +// Description: Returns true if the type represented by the +// conversion is now the atomic string type. We have to +// have this crazy method for representing atomic +// string, because there's no such type in C (and hence +// no corresponding CPPType *). +//////////////////////////////////////////////////////////////////// +bool ParameterRemap:: +new_type_is_atomic_string() { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemap::is_this +// Access: Public, Virtual +// Description: Returns true if this is the "this" parameter. +//////////////////////////////////////////////////////////////////// +bool ParameterRemap:: +is_this() { + return false; +} + diff --git a/dtool/src/interrogate/parameterRemap.h b/dtool/src/interrogate/parameterRemap.h new file mode 100644 index 0000000000..ee349641ae --- /dev/null +++ b/dtool/src/interrogate/parameterRemap.h @@ -0,0 +1,69 @@ +// Filename: parameterRemap.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAP_H +#define PARAMETERREMAP_H + +#include + +#include + +class CPPType; +class CPPExpression; + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemap +// Description : An abstract base class for a number of different +// kinds of ways to remap parameters for passing to +// wrapper functions. +// +// Certain kinds of function parameters that are legal +// in C++ (for instance, passing by reference, or +// passing structures as concrete values) are not legal +// for a typical scripting language. We map these types +// of parameters to something equivalent (for instance, +// a reference becomes a pointer). +// +// For each kind of possible remapping, we define a +// class derived from ParameterRemap that defines the +// exact nature of the remap. +//////////////////////////////////////////////////////////////////// +class ParameterRemap { +public: + INLINE ParameterRemap(CPPType *orig_type); + virtual ~ParameterRemap(); + + INLINE bool is_valid() const; + + INLINE CPPType *get_orig_type() const; + INLINE CPPType *get_new_type() const; + INLINE CPPType *get_temporary_type() const; + INLINE bool has_default_value() const; + INLINE CPPExpression *get_default_value() const; + INLINE void set_default_value(CPPExpression *expr); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string prepare_return_expr(ostream &out, int indent_level, + const string &expression); + virtual string get_return_expr(const string &expression); + virtual string temporary_to_return(const string &temporary); + virtual bool return_value_needs_management(); + virtual FunctionIndex get_return_value_destructor(); + virtual bool return_value_should_be_simple(); + virtual bool new_type_is_atomic_string(); + virtual bool is_this(); + +protected: + bool _is_valid; + + CPPType *_orig_type; + CPPType *_new_type; + CPPType *_temporary_type; + CPPExpression *_default_value; +}; + +#include "parameterRemap.I" + +#endif diff --git a/dtool/src/interrogate/parameterRemapBasicStringRefToString.cxx b/dtool/src/interrogate/parameterRemapBasicStringRefToString.cxx new file mode 100644 index 0000000000..90304ecbe0 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapBasicStringRefToString.cxx @@ -0,0 +1,41 @@ +// Filename: parameterRemapBasicStringRefToString.C +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapBasicStringRefToString.h" + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapBasicStringRefToString::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapBasicStringRefToString:: +ParameterRemapBasicStringRefToString(CPPType *orig_type) : + ParameterRemapToString(orig_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapBasicStringRefToString::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the original type to the new type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapBasicStringRefToString:: +pass_parameter(ostream &out, const string &variable_name) { + out << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapBasicStringRefToString::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapBasicStringRefToString:: +get_return_expr(const string &expression) { + return "(" + expression + ").c_str()"; +} diff --git a/dtool/src/interrogate/parameterRemapBasicStringRefToString.h b/dtool/src/interrogate/parameterRemapBasicStringRefToString.h new file mode 100644 index 0000000000..df9ecfd6f0 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapBasicStringRefToString.h @@ -0,0 +1,26 @@ +// Filename: parameterRemapBasicStringRefToString.h +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPBASICSTRINGREFTOSTRING_H +#define PARAMETERREMAPBASICSTRINGREFTOSTRING_H + +#include + +#include "parameterRemapToString.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapBasicStringRefToString +// Description : Maps a const reference to a basic_string to an +// atomic string. +//////////////////////////////////////////////////////////////////// +class ParameterRemapBasicStringRefToString : public ParameterRemapToString { +public: + ParameterRemapBasicStringRefToString(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapBasicStringToString.cxx b/dtool/src/interrogate/parameterRemapBasicStringToString.cxx new file mode 100644 index 0000000000..50cedfe869 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapBasicStringToString.cxx @@ -0,0 +1,58 @@ +// Filename: parameterRemapBasicStringToString.C +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapBasicStringToString.h" +#include "wrapperBuilder.h" + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapBasicStringToString::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapBasicStringToString:: +ParameterRemapBasicStringToString(CPPType *orig_type) : + ParameterRemapToString(orig_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapBasicStringToString::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the original type to the new type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapBasicStringToString:: +pass_parameter(ostream &out, const string &variable_name) { + out << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapBasicStringToString::prepare_return_expr +// Access: Public, Virtual +// Description: This will be called immediately before +// get_return_expr(). It outputs whatever lines the +// remapper needs to the function to set up its return +// value, e.g. to declare a temporary variable or +// something. It should return the modified expression. +//////////////////////////////////////////////////////////////////// +string ParameterRemapBasicStringToString:: +prepare_return_expr(ostream &out, int indent_level, const string &expression) { + WrapperBuilder::indent(out, indent_level) + << "static basic_string string_holder = " << expression << ";\n"; + return "string_holder"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapBasicStringToString::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapBasicStringToString:: +get_return_expr(const string &expression) { + return "string_holder.c_str()"; +} diff --git a/dtool/src/interrogate/parameterRemapBasicStringToString.h b/dtool/src/interrogate/parameterRemapBasicStringToString.h new file mode 100644 index 0000000000..1d3e0ac08a --- /dev/null +++ b/dtool/src/interrogate/parameterRemapBasicStringToString.h @@ -0,0 +1,28 @@ +// Filename: parameterRemapBasicStringToString.h +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPBASICSTRINGTOSTRING_H +#define PARAMETERREMAPBASICSTRINGTOSTRING_H + +#include + +#include "parameterRemapToString.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapBasicStringToString +// Description : Maps a concrete basic_string to an atomic +// string. +//////////////////////////////////////////////////////////////////// +class ParameterRemapBasicStringToString : public ParameterRemapToString { +public: + ParameterRemapBasicStringToString(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string prepare_return_expr(ostream &out, int indent_level, + const string &expression); + virtual string get_return_expr(const string &expression); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapCharStarToString.cxx b/dtool/src/interrogate/parameterRemapCharStarToString.cxx new file mode 100644 index 0000000000..94467222a5 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapCharStarToString.cxx @@ -0,0 +1,17 @@ +// Filename: parameterRemapCharStarToString.C +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapCharStarToString.h" + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapCharStarToString::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapCharStarToString:: +ParameterRemapCharStarToString(CPPType *orig_type) : + ParameterRemapToString(orig_type) +{ +} diff --git a/dtool/src/interrogate/parameterRemapCharStarToString.h b/dtool/src/interrogate/parameterRemapCharStarToString.h new file mode 100644 index 0000000000..93f20088ab --- /dev/null +++ b/dtool/src/interrogate/parameterRemapCharStarToString.h @@ -0,0 +1,23 @@ +// Filename: parameterRemapCharStarToString.h +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPCHARSTARTOSTRING_H +#define PARAMETERREMAPCHARSTARTOSTRING_H + +#include + +#include "parameterRemapToString.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapCharStarToString +// Description : Maps from (char *) or (const char *) to the atomic +// string type. +//////////////////////////////////////////////////////////////////// +class ParameterRemapCharStarToString : public ParameterRemapToString { +public: + ParameterRemapCharStarToString(CPPType *orig_type); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx b/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx new file mode 100644 index 0000000000..03cdcbb729 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapConcreteToPointer.cxx @@ -0,0 +1,96 @@ +// Filename: parameterRemapConcreteToPointer.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapConcreteToPointer.h" +#include "interrogate.h" +#include "interrogateBuilder.h" +#include "typeManager.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConcreteToPointer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapConcreteToPointer:: +ParameterRemapConcreteToPointer(CPPType *orig_type) : + ParameterRemap(orig_type) +{ + _new_type = TypeManager::wrap_pointer(orig_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConcreteToPointer::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the new type to the original type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapConcreteToPointer:: +pass_parameter(ostream &out, const string &variable_name) { + out << "*" << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConcreteToPointer::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapConcreteToPointer:: +get_return_expr(const string &expression) { + return + "new " + _orig_type->get_local_name(&parser) + + "(" + expression + ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConcreteToPointer::return_value_needs_management +// Access: Public, Virtual +// Description: Returns true if the return value represents a value +// that was newly allocated, and hence must be +// explicitly deallocated later by the caller. +//////////////////////////////////////////////////////////////////// +bool ParameterRemapConcreteToPointer:: +return_value_needs_management() { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConcreteToPointer::get_return_value_destructor +// Access: Public, Virtual +// Description: If return_value_needs_management() returns true, this +// should return the index of the function that should +// be called when it is time to destruct the return +// value. It will generally be the same as the +// destructor for the class we just returned a pointer +// to. +//////////////////////////////////////////////////////////////////// +FunctionIndex ParameterRemapConcreteToPointer:: +get_return_value_destructor() { + return builder.get_destructor_for(_orig_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConcreteToPointer::return_value_should_be_simple +// Access: Public, Virtual +// Description: This is a hack around a problem VC++ has with +// overly-complex expressions, particularly in +// conjunction with the 'new' operator. If this +// parameter type is one that will probably give VC++ a +// headache, this should be set true to indicate that +// the code generator should save the return value +// expression into a temporary variable first, and pass +// the temporary variable name in instead. +//////////////////////////////////////////////////////////////////// +bool ParameterRemapConcreteToPointer:: +return_value_should_be_simple() { + return true; +} diff --git a/dtool/src/interrogate/parameterRemapConcreteToPointer.h b/dtool/src/interrogate/parameterRemapConcreteToPointer.h new file mode 100644 index 0000000000..93b4dda292 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapConcreteToPointer.h @@ -0,0 +1,30 @@ +// Filename: parameterRemapConcreteToPointer.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPCONCRETETOPOINTER_H +#define PARAMETERREMAPCONCRETETOPOINTER_H + +#include + +#include "parameterRemap.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapConcreteToPointer +// Description : A ParameterRemap class that handles remapping a +// concrete structure or class parameter to a pointer +// parameter. +//////////////////////////////////////////////////////////////////// +class ParameterRemapConcreteToPointer : public ParameterRemap { +public: + ParameterRemapConcreteToPointer(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); + virtual bool return_value_needs_management(); + virtual FunctionIndex get_return_value_destructor(); + virtual bool return_value_should_be_simple(); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapConstToNonConst.cxx b/dtool/src/interrogate/parameterRemapConstToNonConst.cxx new file mode 100644 index 0000000000..645c502109 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapConstToNonConst.cxx @@ -0,0 +1,45 @@ +// Filename: parameterRemapConstToNonConst.C +// Created by: drose (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapConstToNonConst.h" +#include "typeManager.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConstToNonConst::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapConstToNonConst:: +ParameterRemapConstToNonConst(CPPType *orig_type) : + ParameterRemap(orig_type) +{ + _new_type = TypeManager::unwrap_const(orig_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConstToNonConst::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the new type to the original type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapConstToNonConst:: +pass_parameter(ostream &out, const string &variable_name) { + out << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapConstToNonConst::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapConstToNonConst:: +get_return_expr(const string &expression) { + return expression; +} diff --git a/dtool/src/interrogate/parameterRemapConstToNonConst.h b/dtool/src/interrogate/parameterRemapConstToNonConst.h new file mode 100644 index 0000000000..69c5ce7f4f --- /dev/null +++ b/dtool/src/interrogate/parameterRemapConstToNonConst.h @@ -0,0 +1,28 @@ +// Filename: parameterRemapConstToNonConst.h +// Created by: drose (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPCONSTTONONCONST_H +#define PARAMETERREMAPCONSTTONONCONST_H + +#include + +#include "parameterRemap.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapConstToNonConst +// Description : A ParameterRemap class that handles remapping a +// simple const parameter (like const int) to an +// ordinary parameter (line int). It doesn't apply to +// const references or const pointers, however. +//////////////////////////////////////////////////////////////////// +class ParameterRemapConstToNonConst : public ParameterRemap { +public: + ParameterRemapConstToNonConst(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapEnumToInt.cxx b/dtool/src/interrogate/parameterRemapEnumToInt.cxx new file mode 100644 index 0000000000..6c0e48d02b --- /dev/null +++ b/dtool/src/interrogate/parameterRemapEnumToInt.cxx @@ -0,0 +1,72 @@ +// Filename: parameterRemapEnumToInt.C +// Created by: drose (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapEnumToInt.h" +#include "interrogate.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapEnumToInt::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapEnumToInt:: +ParameterRemapEnumToInt(CPPType *orig_type) : + ParameterRemap(orig_type) +{ + _new_type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int)); + _enum_type = unwrap_type(_orig_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapEnumToInt::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the new type to the original type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapEnumToInt:: +pass_parameter(ostream &out, const string &variable_name) { + out << "(" << _enum_type->get_local_name(&parser) << ")" << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapEnumToInt::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapEnumToInt:: +get_return_expr(const string &expression) { + return "(int)(" + expression + ")"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapEnumToInt::unwrap_type +// Access: Private +// Description: Recursively walks through the type definition, +// and finds the enum definition under all the wrappers. +//////////////////////////////////////////////////////////////////// +CPPType *ParameterRemapEnumToInt:: +unwrap_type(CPPType *source_type) const { + switch (source_type->get_subtype()) { + case CPPDeclaration::ST_const: + return unwrap_type(source_type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return unwrap_type(source_type->as_reference_type()->_pointing_at); + + case CPPDeclaration::ST_pointer: + return unwrap_type(source_type->as_pointer_type()->_pointing_at); + + default: + return source_type; + } +} diff --git a/dtool/src/interrogate/parameterRemapEnumToInt.h b/dtool/src/interrogate/parameterRemapEnumToInt.h new file mode 100644 index 0000000000..d7e9f591c1 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapEnumToInt.h @@ -0,0 +1,31 @@ +// Filename: parameterRemapEnumToInt.h +// Created by: drose (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPENUMTOINT_H +#define PARAMETERREMAPENUMTOINT_H + +#include + +#include "parameterRemap.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapEnumToInt +// Description : A ParameterRemap class that handles remapping an +// enumerated type to an integer parameter. +//////////////////////////////////////////////////////////////////// +class ParameterRemapEnumToInt : public ParameterRemap { +public: + ParameterRemapEnumToInt(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); + +private: + CPPType *_enum_type; + + CPPType *unwrap_type(CPPType *source_type) const; +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapPTToPointer.cxx b/dtool/src/interrogate/parameterRemapPTToPointer.cxx new file mode 100644 index 0000000000..930980570d --- /dev/null +++ b/dtool/src/interrogate/parameterRemapPTToPointer.cxx @@ -0,0 +1,96 @@ +// Filename: parameterRemapPTToPointer.C +// Created by: drose (10Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapPTToPointer.h" +#include "interrogate.h" +#include "interrogateBuilder.h" +#include "typeManager.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapPTToPointer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapPTToPointer:: +ParameterRemapPTToPointer(CPPType *orig_type) : + ParameterRemap(orig_type) +{ + CPPStructType *pt_type = TypeManager::unwrap(_orig_type)->as_struct_type(); + assert(pt_type != (CPPStructType *)NULL); + + // A horrible hack around a CPPParser bug. We don't trust the + // CPPStructType pointer we were given; instead, we ask CPPParser to + // parse a new type of the same name. This has a better chance of + // fully resolving templates. + string name = pt_type->get_local_name(&parser); + CPPType *new_type = parser.parse_type(name); + if (new_type == (CPPType *)NULL) { + nout << "Type " << name << " is unknown to parser.\n"; + } else { + new_type = new_type->resolve_type(&parser, &parser); + pt_type = new_type->as_struct_type(); + assert(pt_type != (CPPStructType *)NULL); + } + + _pointer_type = TypeManager::get_pointer_type(pt_type); + if (_pointer_type == (CPPType *)NULL) { + // If we couldn't figure out the pointer type, forget it. + nout << "Couldn't figure out pointer type for " << *pt_type << "\n"; + _is_valid = false; + return; + } + + _new_type = _pointer_type; + + // We must use an actual PointerTo to hold any temporary values, + // until we can safely ref it. + _temporary_type = pt_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapPTToPointer::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the new type to the original type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapPTToPointer:: +pass_parameter(ostream &out, const string &variable_name) { + out << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapPTToPointer::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapPTToPointer:: +get_return_expr(const string &expression) { + return expression; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapPTToPointer::temporary_to_return +// Access: Public, Virtual +// Description: Returns the string that converts the expression +// stored in the indicated temporary variable to the +// appropriate return value type. This is normally a +// pass-through, but in cases when the temporary +// variable type must be different than the return type +// (i.e. get_temporary_type() != get_new_type()), this +// might perform some operation. +//////////////////////////////////////////////////////////////////// +string ParameterRemapPTToPointer:: +temporary_to_return(const string &temporary) { + return temporary + ".p()"; +} + diff --git a/dtool/src/interrogate/parameterRemapPTToPointer.h b/dtool/src/interrogate/parameterRemapPTToPointer.h new file mode 100644 index 0000000000..090e8d17f8 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapPTToPointer.h @@ -0,0 +1,33 @@ +// Filename: parameterRemapPTToPointer.h +// Created by: drose (10Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPPTTOPOINTER_H +#define PARAMETERREMAPPTTOPOINTER_H + +#include + +#include "parameterRemap.h" + +class CPPType; +class CPPStructType; + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapPTToPointer +// Description : A ParameterRemap class that handles remapping a +// PT(Type) or PointerTo to a Type *. +//////////////////////////////////////////////////////////////////// +class ParameterRemapPTToPointer : public ParameterRemap { +public: + ParameterRemapPTToPointer(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); + virtual string temporary_to_return(const string &temporary); + +private: + CPPType *_pointer_type; +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapReferenceToConcrete.cxx b/dtool/src/interrogate/parameterRemapReferenceToConcrete.cxx new file mode 100644 index 0000000000..179dae795b --- /dev/null +++ b/dtool/src/interrogate/parameterRemapReferenceToConcrete.cxx @@ -0,0 +1,50 @@ +// Filename: parameterRemapReferenceToConcrete.C +// Created by: drose (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapReferenceToConcrete.h" +#include "typeManager.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapReferenceToConcrete::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapReferenceToConcrete:: +ParameterRemapReferenceToConcrete(CPPType *orig_type) : + ParameterRemap(orig_type) +{ + _new_type = TypeManager::unwrap_const_reference(orig_type); +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapReferenceToConcrete::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the new type to the original type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapReferenceToConcrete:: +pass_parameter(ostream &out, const string &variable_name) { + out << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapReferenceToConcrete::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapReferenceToConcrete:: +get_return_expr(const string &expression) { + return expression; +} + diff --git a/dtool/src/interrogate/parameterRemapReferenceToConcrete.h b/dtool/src/interrogate/parameterRemapReferenceToConcrete.h new file mode 100644 index 0000000000..06eef96f0a --- /dev/null +++ b/dtool/src/interrogate/parameterRemapReferenceToConcrete.h @@ -0,0 +1,28 @@ +// Filename: parameterRemapReferenceToConcrete.h +// Created by: drose (04Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPREFERENCETOCONCRETE_H +#define PARAMETERREMAPREFERENCETOCONCRETE_H + +#include + +#include "parameterRemap.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapReferenceToConcrete +// Description : A ParameterRemap class that handles remapping a +// const reference parameter to a concrete. This only +// makes sense when we're talking about a const +// reference to a simple type. +//////////////////////////////////////////////////////////////////// +class ParameterRemapReferenceToConcrete : public ParameterRemap { +public: + ParameterRemapReferenceToConcrete(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx b/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx new file mode 100644 index 0000000000..aa5d19bba9 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapReferenceToPointer.cxx @@ -0,0 +1,49 @@ +// Filename: parameterRemapReferenceToPointer.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapReferenceToPointer.h" +#include "typeManager.h" + +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapReferenceToPointer::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapReferenceToPointer:: +ParameterRemapReferenceToPointer(CPPType *orig_type) : + ParameterRemap(orig_type) +{ + _new_type = TypeManager::wrap_pointer(TypeManager::unwrap_reference(orig_type)); +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapReferenceToPointer::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the new type to the original type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapReferenceToPointer:: +pass_parameter(ostream &out, const string &variable_name) { + out << "*" << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapReferenceToPointer::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapReferenceToPointer:: +get_return_expr(const string &expression) { + return "&(" + expression + ")"; +} diff --git a/dtool/src/interrogate/parameterRemapReferenceToPointer.h b/dtool/src/interrogate/parameterRemapReferenceToPointer.h new file mode 100644 index 0000000000..2676bd46a6 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapReferenceToPointer.h @@ -0,0 +1,27 @@ +// Filename: parameterRemapReferenceToPointer.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPREFERENCETOPOINTER_H +#define PARAMETERREMAPREFERENCETOPOINTER_H + +#include + +#include "parameterRemap.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapReferenceToPointer +// Description : A ParameterRemap class that handles remapping a +// reference (or a const reference) parameter to a +// pointer (or const pointer) parameter. +//////////////////////////////////////////////////////////////////// +class ParameterRemapReferenceToPointer : public ParameterRemap { +public: + ParameterRemapReferenceToPointer(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapThis.cxx b/dtool/src/interrogate/parameterRemapThis.cxx new file mode 100644 index 0000000000..e170b10683 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapThis.cxx @@ -0,0 +1,62 @@ +// Filename: parameterRemapThis.C +// Created by: drose (02Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapThis.h" +#include "typeManager.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapThis::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapThis:: +ParameterRemapThis(CPPStructType *type, bool is_const) : + ParameterRemap(TypeManager::get_void_type()) +{ + if (is_const) { + _new_type = TypeManager::wrap_const_pointer(type); + } else { + _new_type = TypeManager::wrap_pointer(type); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapThis::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the new type to the original type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapThis:: +pass_parameter(ostream &out, const string &) { + out << "**invalid**"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapThis::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapThis:: +get_return_expr(const string &) { + return "**invalid**"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapThis::is_this +// Access: Public, Virtual +// Description: Returns true if this is the "this" parameter. +//////////////////////////////////////////////////////////////////// +bool ParameterRemapThis:: +is_this() { + return true; +} diff --git a/dtool/src/interrogate/parameterRemapThis.h b/dtool/src/interrogate/parameterRemapThis.h new file mode 100644 index 0000000000..5e1c3ebc87 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapThis.h @@ -0,0 +1,29 @@ +// Filename: parameterRemapThis.h +// Created by: drose (02Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPTHIS_H +#define PARAMETERREMAPTHIS_H + +#include + +#include "parameterRemap.h" + +class CPPStructType; + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapThis +// Description : A ParameterRemap class that represents a generated +// "this" parameter. +//////////////////////////////////////////////////////////////////// +class ParameterRemapThis : public ParameterRemap { +public: + ParameterRemapThis(CPPStructType *type, bool is_const); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); + virtual bool is_this(); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapToString.cxx b/dtool/src/interrogate/parameterRemapToString.cxx new file mode 100644 index 0000000000..606fcd3f4c --- /dev/null +++ b/dtool/src/interrogate/parameterRemapToString.cxx @@ -0,0 +1,62 @@ +// Filename: parameterRemapToString.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapToString.h" +#include "interrogate.h" + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapToString::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapToString:: +ParameterRemapToString(CPPType *orig_type) : + ParameterRemap(orig_type) +{ + static CPPType *char_star_type = (CPPType *)NULL; + if (char_star_type == (CPPType *)NULL) { + char_star_type = parser.parse_type("const char *"); + } + + _new_type = char_star_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapToString::pass_parameter +// Access: Public, Virtual +// Description: Outputs an expression that converts the indicated +// variable from the original type to the new type, for +// passing into the actual C++ function. +//////////////////////////////////////////////////////////////////// +void ParameterRemapToString:: +pass_parameter(ostream &out, const string &variable_name) { + out << variable_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapToString::get_return_expr +// Access: Public, Virtual +// Description: Returns an expression that evalutes to the +// appropriate value type for returning from the +// function, given an expression of the original type. +//////////////////////////////////////////////////////////////////// +string ParameterRemapToString:: +get_return_expr(const string &expression) { + return expression; +} + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapToString::new_type_is_atomic_string +// Access: Public, Virtual +// Description: Returns true if the type represented by the +// conversion is now the atomic string type. We have to +// have this crazy method for representing atomic +// string, because there's no such type in C (and hence +// no corresponding CPPType *). +//////////////////////////////////////////////////////////////////// +bool ParameterRemapToString:: +new_type_is_atomic_string() { + return true; +} diff --git a/dtool/src/interrogate/parameterRemapToString.h b/dtool/src/interrogate/parameterRemapToString.h new file mode 100644 index 0000000000..7c7591aab0 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapToString.h @@ -0,0 +1,33 @@ +// Filename: parameterRemapToString.h +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPTOSTRING_H +#define PARAMETERREMAPTOSTRING_H + +#include + +#include "parameterRemap.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapToString +// Description : A base class for several different remapping types +// that convert to an atomic string class. +// +// The atomic string class is represented in the C +// interface as a (const char *). Other interfaces may +// be able to represent it differently, subverting the +// code defined here. +//////////////////////////////////////////////////////////////////// +class ParameterRemapToString : public ParameterRemap { +public: + ParameterRemapToString(CPPType *orig_type); + + virtual void pass_parameter(ostream &out, const string &variable_name); + virtual string get_return_expr(const string &expression); + + virtual bool new_type_is_atomic_string(); +}; + +#endif diff --git a/dtool/src/interrogate/parameterRemapUnchanged.cxx b/dtool/src/interrogate/parameterRemapUnchanged.cxx new file mode 100644 index 0000000000..cb68edbdc0 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapUnchanged.cxx @@ -0,0 +1,17 @@ +// Filename: parameterRemapUnchanged.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "parameterRemapUnchanged.h" + +//////////////////////////////////////////////////////////////////// +// Function: ParameterRemapUnchanged::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +ParameterRemapUnchanged:: +ParameterRemapUnchanged(CPPType *orig_type) : + ParameterRemap(orig_type) +{ +} diff --git a/dtool/src/interrogate/parameterRemapUnchanged.h b/dtool/src/interrogate/parameterRemapUnchanged.h new file mode 100644 index 0000000000..57443803b2 --- /dev/null +++ b/dtool/src/interrogate/parameterRemapUnchanged.h @@ -0,0 +1,23 @@ +// Filename: parameterRemapUnchanged.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PARAMETERREMAPUNCHANGED_H +#define PARAMETERREMAPUNCHANGED_H + +#include + +#include "parameterRemap.h" + +//////////////////////////////////////////////////////////////////// +// Class : ParameterRemapUnchanged +// Description : A ParameterRemap class that represents no change to +// the parameter: the parameter type is legal as is. +//////////////////////////////////////////////////////////////////// +class ParameterRemapUnchanged : public ParameterRemap { +public: + ParameterRemapUnchanged(CPPType *orig_type); +}; + +#endif diff --git a/dtool/src/interrogate/parse_file.cxx b/dtool/src/interrogate/parse_file.cxx new file mode 100644 index 0000000000..627d28c21c --- /dev/null +++ b/dtool/src/interrogate/parse_file.cxx @@ -0,0 +1,348 @@ +// Filename: parse_file.C +// Created by: drose (20Oct99) +// +//////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef HAVE_GETOPT +#include +#else +#include +#endif + +CPPParser parser; + +void +predefine_macro(CPPParser &parser, const string &option) { + string macro_name, macro_def; + + size_t eq = option.find('='); + if (eq != string::npos) { + macro_name = option.substr(0, eq); + macro_def = option.substr(eq + 1); + } else { + macro_name = option; + } + + cerr << "Predefining " << macro_name << " as " << macro_def << "\n"; + + CPPManifest *macro = new CPPManifest(macro_name + " " + macro_def); + parser._manifests[macro->_name] = macro; +} + +void +show_type_or_expression(const string &str) { + CPPExpression *expr = parser.parse_expr(str); + if (expr != NULL) { + cout << "\nExpression: " << *expr << "\n"; + CPPType *type = expr->determine_type(); + if (type == NULL) { + cout << "type is unknown\n"; + } else { + cout << "type is " << *type << "\n"; + } + cout << "value is " << expr->evaluate() << "\n\n"; + + } else { + CPPType *type = parser.parse_type(str); + if (type != NULL) { + cout << "\nType: " << *type << "\n" + << "Defined in: " << type->_file << "\n\n"; + + CPPStructType *stype = type->as_struct_type(); + if (stype != (CPPStructType *)NULL) { + stype->check_virtual(); + } + + type->output(cout, 0, &parser, true); + cout << "\n\n" + << "is_template = " << type->is_template() << "\n" + << "is_fully_specified = " << type->is_fully_specified() << "\n" + << "is_tbd = " << type->is_tbd() << "\n"; + if (type->has_typedef_name()) { + cout << "get_typedef_name = " << type->get_typedef_name() << "\n"; + } + cout << "get_simple_name = " << type->get_simple_name() << "\n" + << "get_local_name = " << type->get_local_name() << "\n" + << "get_fully_scoped_name = " << type->get_fully_scoped_name() << "\n" + << "get_preferred_name = " << type->get_preferred_name() << "\n" + << "is_incomplete = " << type->is_incomplete() << "\n"; + + if (stype != (CPPStructType *)NULL) { + cout << "scope = " << stype->get_scope()->get_fully_scoped_name() << "\n"; + bool is_abstract = stype->is_abstract(); + cout << "is_abstract = " << is_abstract << "\n"; + if (is_abstract) { + cout << "pure virtual functions:\n"; + CPPStructType::VFunctions vf; + stype->get_pure_virtual_funcs(vf); + CPPStructType::VFunctions::const_iterator fi; + for (fi = vf.begin(); fi != vf.end(); ++fi) { + cout << " " << *(*fi) << "\n"; + } + } + } + + cout << "\n"; + } else { + cout << "Invalid expression or type.\n"; + } + } +} + +void +show_methods(const string &str) { + CPPType *type = parser.parse_type(str); + if (type == NULL) { + cerr << "Invalid type: " << str << "\n"; + return; + } + + CPPStructType *stype = type->as_struct_type(); + if (stype == NULL) { + cerr << "Type is not a structure or class.\n"; + return; + } + + CPPScope *scope = stype->get_scope(); + assert(scope != (CPPScope *)NULL); + + cerr << "Methods in " << *stype << ":\n"; + + CPPScope::Functions::const_iterator fi; + for (fi = scope->_functions.begin(); fi != scope->_functions.end(); ++fi) { + CPPFunctionGroup *fgroup = (*fi).second; + + CPPFunctionGroup::Instances::const_iterator ii; + for (ii = fgroup->_instances.begin(); + ii != fgroup->_instances.end(); + ++ii) { + CPPInstance *inst = (*ii); + cerr << " " << *inst << "\n"; + } + } +} + +void +show_data_members(const string &str) { + CPPType *type = parser.parse_type(str); + if (type == NULL) { + cerr << "Invalid type: " << str << "\n"; + return; + } + + CPPStructType *stype = type->as_struct_type(); + if (stype == NULL) { + cerr << "Type is not a structure or class.\n"; + return; + } + + CPPScope *scope = stype->get_scope(); + assert(scope != (CPPScope *)NULL); + + cerr << "Data members in " << *stype << ":\n"; + + CPPScope::Variables::const_iterator vi; + for (vi = scope->_variables.begin(); vi != scope->_variables.end(); ++vi) { + CPPInstance *inst = (*vi).second; + cerr << " " << *inst << "\n"; + } +} + +void +show_typedefs(const string &str) { + CPPType *type = parser.parse_type(str); + if (type == NULL) { + cerr << "Invalid type: " << str << "\n"; + return; + } + + CPPStructType *stype = type->as_struct_type(); + if (stype == NULL) { + cerr << "Type is not a structure or class.\n"; + return; + } + + CPPScope *scope = stype->get_scope(); + assert(scope != (CPPScope *)NULL); + + cerr << "Typedefs in " << *stype << ":\n"; + + CPPScope::Typedefs::const_iterator ti; + for (ti = scope->_typedefs.begin(); ti != scope->_typedefs.end(); ++ti) { + CPPTypedef *td = (*ti).second; + cerr << " " << *td << "\n"; + } +} + + +int +main(int argc, char *argv[]) { + extern char *optarg; + extern int optind; + const char *optstr = "I:S:D:o:l:vp"; + + parser.set_verbose(2); + bool prompt = false; + + int flag = getopt(argc, argv, optstr); + + while (flag != EOF) { + switch (flag) { + case 'I': + parser._include_path.append_directory(optarg); + break; + + case 'S': + parser._system_include_path.append_directory(optarg); + break; + + case 'D': + predefine_macro(parser, optarg); + break; + + case 'o': + cerr << "Ignoring output file " << optarg << "\n"; + break; + + case 'l': + cpp_longlong_keyword = optarg; + break; + + case 'v': + parser.set_verbose(parser.get_verbose() + 1); + break; + + case 'p': + prompt = true; + break; + + default: + exit(1); + } + flag = getopt(argc, argv, optstr); + } + + argc -= (optind-1); + argv += (optind-1); + + + if (argc < 2) { + cerr << "parse-file [opts] file1.h [file2.h ... ]\n" + << "\nOptions:\n\n" + << " -I include_path\n" + << " -S system_include_path\n" + << " -D manifest_name\n" + << " -D manifest_name=manifest_definition\n" + << " -o output_file (ignored)\n" + << " -v (increase verbosity)\n" + << " -p (prompt for expression instead of dumping output)\n"; + + exit(1); + } + + for (int i = 1; i < argc; i++) { + if (!parser.parse_file(argv[i])) { + cerr << "Error in parsing.\n"; + exit(1); + } + } + + cerr << "Finished parsing.\n"; + + if (prompt) { + while (cin) { + string str; + cout << "Enter an expression or type name:\n"; + getline(cin, str); + if (!str.empty()) { + + size_t space = str.find(' '); + if (space != string::npos) { + string first_word = str.substr(0, space); + string remainder = str.substr(space + 1); + + if (first_word == "methods") { + show_methods(remainder); + } else if (first_word == "members") { + show_data_members(remainder); + } else if (first_word == "typedefs") { + show_typedefs(remainder); + } else { + show_type_or_expression(str); + } + } else { + show_type_or_expression(str); + } + } + } + } else { + parser.write(cout, 0, &parser); + cout << "\n"; + } + + /* + cout << "Opened the following files:\n"; + CPPParser::ParsedFiles::const_iterator fi; + for (fi = parser._parsed_files.begin(); + fi != parser._parsed_files.end(); + ++fi) { + cout << " "; + + switch ((*fi)._source) { + case CPPFile::S_local: + cout << "L"; + break; + case CPPFile::S_alternate: + cout << "A"; + break; + case CPPFile::S_system: + cout << "S"; + break; + default: + cout << " "; + } + + cout << " " << (*fi) << "\n"; + } + */ + + /* + CPPParser::Comments::const_iterator ci; + for (ci = parser._comments.begin(); ci != parser._comments.end(); ++ci) { + const CPPParser::CommentBlock &c = (*ci); + cout << "Comment in file " << c._file << " at line " << c._line_number + << ":\n" << c._comment << "\n\n"; + } + */ + + /* + CPPParser::Manifests::const_iterator mi; + for (mi = parser._manifests.begin(); mi != parser._manifests.end(); ++mi) { + const CPPManifest *m = (*mi).second; + cout << "Manifest " << m->_name << " defined in " << m->_file; + CPPType *type = m->determine_type(); + if (type == (CPPType *)NULL) { + cout << " (no type)\n"; + } else { + cout << " has type " << *type << "\n"; + } + } + */ + + return (0); +} + + + diff --git a/dtool/src/interrogate/typeManager.cxx b/dtool/src/interrogate/typeManager.cxx new file mode 100644 index 0000000000..5e2f5b104e --- /dev/null +++ b/dtool/src/interrogate/typeManager.cxx @@ -0,0 +1,1147 @@ +// Filename: typeManager.C +// Created by: drose (14Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "typeManager.h" +#include "interrogate.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::resolve_type +// Access: Public, Static +// Description: A horrible hack around a CPPParser bug. We don't +// trust the CPPType pointer we were given; instead, we +// ask CPPParser to parse a new type of the same name. +// This has a better chance of fully resolving +// templates. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +resolve_type(CPPType *type, CPPScope *scope) { + if (scope == (CPPScope *)NULL) { + scope = &parser; + } + + type = type->resolve_type(scope, &parser); + string name = type->get_local_name(&parser); + CPPType *new_type = parser.parse_type(name); + if (new_type == (CPPType *)NULL) { + nout << "Type " << name << " is unknown to parser.\n"; + } else { + type = new_type->resolve_type(&parser, &parser); + } + + return type; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_assignable +// Access: Public, Static +// Description: Returns true if the indicated type is something we +// can legitimately assign a value to, or false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_assignable(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + case CPPDeclaration::ST_reference: + case CPPDeclaration::ST_extension: + return false; + + case CPPDeclaration::ST_struct: + // In many cases, this is assignable, but there are some bizarre + // cases where it is not. Particularly in the event that the + // programmer has defined a private copy assignment operator for + // the class or struct. + + // We could try to figure out whether this has happened, but screw + // it. Concrete structure objects are not assignable, and so they + // don't get setters synthesized for them. If you want a setter, + // write it yourself. + + // We'll make an exception for basic_string, however, since + // this is nearly an atomic type. + if (is_basic_string_char(type)) { + return true; + } + + return false; + + default: + return true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_reference +// Access: Public, Static +// Description: Returns true if the indicated type is some kind of a +// reference or const reference type to something +// useful, false otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_reference(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_reference(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return is_pointable(type->as_reference_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_ref_to_anything +// Access: Public, Static +// Description: Returns true if the indicated type is some kind of a +// reference or const reference type at all, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_ref_to_anything(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_ref_to_anything(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return true; + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_ref_to_anything +// Access: Public, Static +// Description: Returns true if the indicated type is a const +// reference to something, false otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_ref_to_anything(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_const_ref_to_anything(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return is_const(type->as_reference_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_pointer +// Access: Public, Static +// Description: Returns true if the indicated type is some kind of a +// pointer or const pointer type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_pointer(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_pointer(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return is_pointable(type->as_pointer_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const +// Access: Public, Static +// Description: Returns true if the indicated type is some kind of a +// const type, false otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return true; + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_struct +// Access: Public, Static +// Description: Returns true if the indicated type is a concrete +// struct, class, or union type, or false otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_struct(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_struct(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_struct: + case CPPDeclaration::ST_extension: + return true; + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_enum +// Access: Public, Static +// Description: Returns true if the indicated type is some kind of +// enumerated type, const or otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_enum(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_enum: + return true; + + case CPPDeclaration::ST_const: + return is_enum(type->as_const_type()->_wrapped_around); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_enum +// Access: Public, Static +// Description: Returns true if the indicated type is a const +// enumerated type. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_enum(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_enum(type->as_const_type()->_wrapped_around); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_ref_to_enum +// Access: Public, Static +// Description: Returns true if the indicated type is a const +// reference to an enumerated type. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_ref_to_enum(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_reference: + return is_const_enum(type->as_reference_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_simple +// Access: Public, Static +// Description: Returns true if the indicated type is something that +// a scripting language can handle directly as a +// concrete, like an int or float, either const or +// non-const. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_simple(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_simple: + case CPPDeclaration::ST_enum: + return true; + + case CPPDeclaration::ST_const: + return is_simple(type->as_const_type()->_wrapped_around); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_simple +// Access: Public, Static +// Description: Returns true if the indicated type is a const wrapper +// around some simple type like int. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_simple(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_simple(type->as_const_type()->_wrapped_around); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_ref_to_simple +// Access: Public, Static +// Description: Returns true if the indicated type is a const +// reference to something that a scripting language can +// handle directly as a concrete. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_ref_to_simple(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_reference: + return is_const_simple(type->as_reference_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_pointable +// Access: Public, Static +// Description: Returns true if the indicated type is something +// ordinary that a scripting language can handle a +// pointer to, e.g. a class or a structure, but not an +// int or a function. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_pointable(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_pointable(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_extension: + return (type->as_extension_type()->_type != CPPExtensionType::T_enum); + + case CPPDeclaration::ST_struct: + return true; + + case CPPDeclaration::ST_simple: + return is_char(type); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_char +// Access: Public, Static +// Description: Returns true if the indicated type is char or const +// char, but not signed or unsigned char. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_char(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_char(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_simple: + { + CPPSimpleType *simple_type = type->as_simple_type(); + if (simple_type != (CPPSimpleType *)NULL) { + return + simple_type->_type == CPPSimpleType::T_char && + simple_type->_flags == 0; + } + } + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_char_pointer +// Access: Public, Static +// Description: Returns true if the indicated type is char * or const +// char * or some such. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_char_pointer(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_char_pointer(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return is_char(type->as_pointer_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_basic_string_char +// Access: Public, Static +// Description: Returns true if the type is basic_string. This +// is the standard C++ string class. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_basic_string_char(CPPType *type) { + CPPType *string_type = get_basic_string_char_type(); + if (string_type != (CPPType *)NULL && + string_type->get_local_name(&parser) == type->get_local_name(&parser)) { + return true; + } + + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_basic_string_char(type->as_const_type()->_wrapped_around); + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_basic_string_char +// Access: Public, Static +// Description: Returns true if the indicated type is a const wrapper +// around basic_string. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_basic_string_char(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_basic_string_char(type->as_const_type()->_wrapped_around); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_ref_to_basic_string_char +// Access: Public, Static +// Description: Returns true if the indicated type is a const +// reference to basic_string. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_ref_to_basic_string_char(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_reference: + return is_const_basic_string_char(type->as_reference_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_bool +// Access: Public, Static +// Description: Returns true if the indicated type is bool, or some +// trivial variant. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_bool(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_bool(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_simple: + { + CPPSimpleType *simple_type = type->as_simple_type(); + if (simple_type != (CPPSimpleType *)NULL) { + return + simple_type->_type == CPPSimpleType::T_bool; + } + } + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_integer +// Access: Public, Static +// Description: Returns true if the indicated type is one of the +// basic integer types: bool, char, short, int, or long, +// signed or unsigned, as well as enumerated types. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_integer(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_integer(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_enum: + return true; + + case CPPDeclaration::ST_simple: + { + CPPSimpleType *simple_type = type->as_simple_type(); + if (simple_type != (CPPSimpleType *)NULL) { + return + (simple_type->_type == CPPSimpleType::T_bool || + simple_type->_type == CPPSimpleType::T_char || + simple_type->_type == CPPSimpleType::T_int); + } + } + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_float +// Access: Public, Static +// Description: Returns true if the indicated type is one of the +// basic floating-point types: float, double, or some +// similar variant. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_float(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_float(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_simple: + { + CPPSimpleType *simple_type = type->as_simple_type(); + if (simple_type != (CPPSimpleType *)NULL) { + return + (simple_type->_type == CPPSimpleType::T_float || + simple_type->_type == CPPSimpleType::T_double); + } + } + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_void +// Access: Public, Static +// Description: Returns true if the indicated type is void. (Not +// void *, just void.) +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_void(CPPType *type) { + CPPSimpleType *simple_type = type->as_simple_type(); + if (simple_type != (CPPSimpleType *)NULL) { + return + simple_type->_type == CPPSimpleType::T_void && + simple_type->_flags == 0; + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_reference_count +// Access: Public, Static +// Description: Returns true if the indicated type is some class that +// derives from ReferenceCount, or false otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_reference_count(CPPType *type) { + CPPType *refcount_type = get_reference_count_type(); + if (refcount_type != (CPPType *)NULL && + refcount_type->get_local_name(&parser) == type->get_local_name(&parser)) { + return true; + } + + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_reference_count(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_struct: + { + CPPStructType *stype = type->as_struct_type(); + CPPStructType::Derivation::const_iterator di; + for (di = stype->_derivation.begin(); + di != stype->_derivation.end(); + ++di) { + if (is_reference_count((*di)._base)) { + return true; + } + } + } + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_reference_count_pointer +// Access: Public, Static +// Description: Returns true if the indicated type is a pointer to a +// class that derives from ReferenceCount. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_reference_count_pointer(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_reference_count_pointer(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return is_reference_count(type->as_pointer_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_pointer_to_base +// Access: Public, Static +// Description: Returns true if the indicated type is some class that +// derives from PointerToBase, or false otherwise. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_pointer_to_base(CPPType *type) { + // We only check the simple name of the type against PointerToBase, + // since we need to allow for the various template instantiations of + // this thing. + + // We also check explicitly for "PointerTo" and "ConstPointerTo", + // instead of actually checking for PointerToBase, because we don't + // want to consider PointerToArray in this category. + if (type->get_simple_name() == "PointerTo" || + type->get_simple_name() == "ConstPointerTo") { + return true; + } + + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_pointer_to_base(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_struct: + { + CPPStructType *stype = type->as_struct_type(); + CPPStructType::Derivation::const_iterator di; + for (di = stype->_derivation.begin(); + di != stype->_derivation.end(); + ++di) { + if (is_pointer_to_base((*di)._base)) { + return true; + } + } + } + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_pointer_to_base +// Access: Public, Static +// Description: Returns true if the indicated type is a const +// PointerToBase or some derivative. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_pointer_to_base(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_pointer_to_base(type->as_const_type()->_wrapped_around); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::is_const_ref_to_pointer_to_base +// Access: Public, Static +// Description: Returns true if the indicated type is a const +// reference to a class that derives from PointerToBase. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +is_const_ref_to_pointer_to_base(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return is_reference(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return is_const_pointer_to_base(type->as_reference_type()->_pointing_at); + + default: + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::involves_unpublished +// Access: Public, Static +// Description: Returns true if the type is an unpublished type, +// e.g. a protected or private nested class, or simply a +// type not marked as 'published', or if the type is a +// pointer or reference to such an unpublished type, or +// even if the type is a function type that includes a +// parameter of such an unpublished type. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +involves_unpublished(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return involves_unpublished(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return involves_unpublished(type->as_reference_type()->_pointing_at); + + case CPPDeclaration::ST_pointer: + return involves_unpublished(type->as_pointer_type()->_pointing_at); + + case CPPDeclaration::ST_function: + { + CPPFunctionType *ftype = type->as_function_type(); + if (involves_unpublished(ftype->_return_type)) { + return true; + } + const CPPParameterList::Parameters ¶ms = + ftype->_parameters->_parameters; + CPPParameterList::Parameters::const_iterator pi; + for (pi = params.begin(); pi != params.end(); ++pi) { + if (involves_unpublished((*pi)->_type)) { + return true; + } + } + return false; + } + + default: + if (type->_declaration != (CPPTypeDeclaration *)NULL) { + return (type->_declaration->_vis > min_vis); + } + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::involves_protected +// Access: Public, Static +// Description: Returns true if the type is an protected type, +// e.g. a protected or private nested class, or if the +// type is a pointer or reference to such a protected +// type, or even if the type is a function type that +// includes a parameter of such a protected type. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +involves_protected(CPPType *type) { + switch (type->get_subtype()) { + case CPPDeclaration::ST_const: + return involves_protected(type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return involves_protected(type->as_reference_type()->_pointing_at); + + case CPPDeclaration::ST_pointer: + return involves_protected(type->as_pointer_type()->_pointing_at); + + case CPPDeclaration::ST_function: + { + CPPFunctionType *ftype = type->as_function_type(); + if (involves_protected(ftype->_return_type)) { + return true; + } + const CPPParameterList::Parameters ¶ms = + ftype->_parameters->_parameters; + CPPParameterList::Parameters::const_iterator pi; + for (pi = params.begin(); pi != params.end(); ++pi) { + if (involves_protected((*pi)->_type)) { + return true; + } + } + return false; + } + + default: + if (type->_declaration != (CPPTypeDeclaration *)NULL) { + return (type->_declaration->_vis > V_public); + } + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::unwrap_pointer +// Access: Public, Static +// Description: Returns the type this pointer type points to. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +unwrap_pointer(CPPType *source_type) { + switch (source_type->get_subtype()) { + case CPPDeclaration::ST_const: + return unwrap_pointer(source_type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_pointer: + return source_type->as_pointer_type()->_pointing_at; + + default: + return source_type; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::unwrap_reference +// Access: Public, Static +// Description: Returns the type this reference type points to. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +unwrap_reference(CPPType *source_type) { + switch (source_type->get_subtype()) { + case CPPDeclaration::ST_const: + return unwrap_reference(source_type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return source_type->as_reference_type()->_pointing_at; + + default: + return source_type; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::unwrap_const +// Access: Public, Static +// Description: Removes the const declaration from the outside of the +// type. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +unwrap_const(CPPType *source_type) { + switch (source_type->get_subtype()) { + case CPPDeclaration::ST_const: + return unwrap_const(source_type->as_const_type()->_wrapped_around); + + default: + return source_type; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::unwrap_const_reference +// Access: Public, Static +// Description: Removes a reference or a const reference from the +// type. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +unwrap_const_reference(CPPType *source_type) { + switch (source_type->get_subtype()) { + case CPPDeclaration::ST_const: + return unwrap_const_reference(source_type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return unwrap_const(source_type->as_reference_type()->_pointing_at); + + default: + return source_type; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::unwrap +// Access: Public, Static +// Description: Removes all const, pointer, and reference wrappers, +// to get to the thing we're talking about. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +unwrap(CPPType *source_type) { + switch (source_type->get_subtype()) { + case CPPDeclaration::ST_const: + return unwrap(source_type->as_const_type()->_wrapped_around); + + case CPPDeclaration::ST_reference: + return unwrap(source_type->as_reference_type()->_pointing_at); + + case CPPDeclaration::ST_pointer: + return unwrap(source_type->as_pointer_type()->_pointing_at); + + default: + return source_type; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::get_pointer_type +// Access: Public, Static +// Description: Returns the type of pointer the given PointerTo class +// emulates. Essentially this just checks the return +// type of the method called 'p()'. Returns NULL if the +// PointerTo class has no method p(). +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +get_pointer_type(CPPStructType *pt_type) { + CPPScope *scope = pt_type->_scope; + + CPPScope::Functions::const_iterator fi; + fi = scope->_functions.find("p"); + if (fi != scope->_functions.end()) { + CPPFunctionGroup *fgroup = (*fi).second; + + // These are all the functions named "p". Now look for one that + // takes no parameters. + CPPFunctionGroup::Instances::iterator ii; + for (ii = fgroup->_instances.begin(); + ii != fgroup->_instances.end(); + ++ii) { + CPPInstance *function = (*ii); + CPPFunctionType *ftype = function->_type->as_function_type(); + assert(ftype != (CPPFunctionType *)NULL); + if (ftype->_parameters->_parameters.empty()) { + // Here's the function p(). What's its return type? + return resolve_type(ftype->_return_type); + } + } + } + + return (CPPType *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::wrap_pointer +// Access: Public, Static +// Description: Returns the type corresponding to a pointer to the +// given type. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +wrap_pointer(CPPType *source_type) { + return CPPType::new_type(new CPPPointerType(source_type)); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::wrap_const_pointer +// Access: Public, Static +// Description: Returns the type corresponding to a const pointer +// to the given type. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +wrap_const_pointer(CPPType *source_type) { + if (source_type->as_const_type() != (CPPConstType *)NULL) { + // It's already const. + return + CPPType::new_type(new CPPPointerType(source_type)); + } else { + return + CPPType::new_type(new CPPPointerType(new CPPConstType(source_type))); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::wrap_const_reference +// Access: Public, Static +// Description: Returns the type corresponding to a const reference +// to the given type. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +wrap_const_reference(CPPType *source_type) { + if (source_type->as_const_type() != (CPPConstType *)NULL) { + // It's already const. + return + CPPType::new_type(new CPPReferenceType(source_type)); + } else { + return + CPPType::new_type(new CPPReferenceType(new CPPConstType(source_type))); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::get_basic_string_char_type +// Access: Public, Static +// Description: Returns a CPPType that represents basic_string, +// or NULL if the type is unknown. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +get_basic_string_char_type() { + static bool got_type = false; + static CPPType *type = (CPPType *)NULL; + if (!got_type) { + type = parser.parse_type("basic_string"); + got_type = true; + } + return type; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::get_reference_count_type +// Access: Public, Static +// Description: Returns a CPPType that represents ReferenceCount, +// or NULL if the type is unknown. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +get_reference_count_type() { + static bool got_type = false; + static CPPType *type = (CPPType *)NULL; + if (!got_type) { + type = parser.parse_type("ReferenceCount"); + got_type = true; + } + return type; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::get_void_type +// Access: Public, Static +// Description: Returns a CPPType that represents void. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +get_void_type() { + static bool got_type = false; + static CPPType *type = (CPPType *)NULL; + if (!got_type) { + type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_void)); + got_type = true; + } + return type; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::get_int_type +// Access: Public, Static +// Description: Returns a CPPType that represents int. +//////////////////////////////////////////////////////////////////// +CPPType *TypeManager:: +get_int_type() { + static bool got_type = false; + static CPPType *type = (CPPType *)NULL; + if (!got_type) { + type = CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int)); + got_type = true; + } + return type; +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::get_function_signature +// Access: Public, Static +// Description: Returns a string corresponding to the given function +// signature. This is a unique string per each +// uniquely-callable C++ function or method. Basically +// it's the function prototype, sans the return type. +// +// If num_default_parameters is nonzero, it is the +// number of parameters to omit from the end of the +// parameter list. This in effect gets the function +// signature for an equivalent function with n +// parameters assuming default values. +//////////////////////////////////////////////////////////////////// +string TypeManager:: +get_function_signature(CPPInstance *function, + int num_default_parameters) { + CPPFunctionType *ftype = function->_type->as_function_type(); + assert(ftype != (CPPFunctionType *)NULL); + + ostringstream out; + + // It's tempting to mark static methods with a different function + // signature than non-static, because a static method doesn't have + // an implicit 'this' parameter. However, this breaks the lookup + // when we come across a method definition outside of the class + // body; since there's no clue at this point whether the method is + // static or not, we can't successfully look it up. Bummer. + /* + if ((function->_storage_class & CPPInstance::SC_static) != 0) { + out << "static "; + } + */ + + out << function->get_local_name(&parser) << "("; + + const CPPParameterList::Parameters ¶ms = + ftype->_parameters->_parameters; + CPPParameterList::Parameters::const_iterator pi; + + int num_params = params.size() - num_default_parameters; + pi = params.begin(); + for (int n = 0; n < num_params; n++) { + assert(pi != params.end()); + CPPType *ptype = (*pi)->_type; + + // One exception: if the type is a const reference to something, + // we build the signature with its corresponding concrete. C++ + // can't differentiate these two anyway. + if (is_const_ref_to_anything(ptype)) { + ptype = unwrap_const_reference(ptype); + } + + out << ptype->get_local_name(&parser); + + if (n + 1 < num_params) { + out << ", "; + } + + ++pi; + } + out << ")"; + + if (ftype->_flags & CPPFunctionType::F_const_method) { + out << " const"; + } + + return out.str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: TypeManager::has_protected_destructor +// Access: Public, Static +// Description: Returns true if the destructor for the given class or +// struct is protected or private, or false if the +// destructor is public or absent. +//////////////////////////////////////////////////////////////////// +bool TypeManager:: +has_protected_destructor(CPPType *type) { + CPPStructType *struct_type = type->as_struct_type(); + if (struct_type == (CPPStructType *)NULL) { + // It's not even a struct type! + return false; + } + + CPPScope *scope = struct_type->get_scope(); + + // Look for the destructor. + CPPScope::Declarations::const_iterator di; + for (di = scope->_declarations.begin(); + di != scope->_declarations.end(); + ++di) { + if ((*di)->get_subtype() == CPPDeclaration::ST_instance) { + CPPInstance *inst = (*di)->as_instance(); + if (inst->_type->get_subtype() == CPPDeclaration::ST_function) { + // Here's a function declaration. + CPPFunctionType *ftype = inst->_type->as_function_type(); + assert(ftype != (CPPFunctionType *)NULL); + if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { + // Here's the destructor! Is it protected? + return (inst->_vis > V_public); + } + } + } + } + + // No explicit destructor. + return false; +} diff --git a/dtool/src/interrogate/typeManager.h b/dtool/src/interrogate/typeManager.h new file mode 100644 index 0000000000..cc691e4173 --- /dev/null +++ b/dtool/src/interrogate/typeManager.h @@ -0,0 +1,94 @@ +// Filename: typeManager.h +// Created by: drose (14Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef TYPEMANAGER_H +#define TYPEMANAGER_H + +#include + +class CPPFunctionGroup; +class CPPInstance; +class CPPType; +class CPPSimpleType; +class CPPPointerType; +class CPPConstType; +class CPPExtensionType; +class CPPStructType; +class CPPEnumType; +class CPPFunctionType; +class CPPScope; +class CPPIdentifier; +class CPPNameComponent; +class CPPManifest; + +//////////////////////////////////////////////////////////////////// +// Class : TypeManager +// Description : This is just a collection of static methods that +// perform useful operations on CPPTypes for +// interrogate. The class is really just a namespace +// that groups these functions together. +//////////////////////////////////////////////////////////////////// +class TypeManager { +public: + + static CPPType *resolve_type(CPPType *type, CPPScope *scope = (CPPScope *)NULL); + + static bool is_assignable(CPPType *type); + + static bool is_reference(CPPType *type); + static bool is_ref_to_anything(CPPType *type); + static bool is_const_ref_to_anything(CPPType *type); + static bool is_pointer(CPPType *type); + static bool is_const(CPPType *type); + static bool is_struct(CPPType *type); + static bool is_enum(CPPType *type); + static bool is_const_enum(CPPType *type); + static bool is_const_ref_to_enum(CPPType *type); + static bool is_simple(CPPType *type); + static bool is_const_simple(CPPType *type); + static bool is_const_ref_to_simple(CPPType *type); + static bool is_pointable(CPPType *type); + static bool is_char(CPPType *type); + static bool is_char_pointer(CPPType *type); + static bool is_basic_string_char(CPPType *type); + static bool is_const_basic_string_char(CPPType *type); + static bool is_const_ref_to_basic_string_char(CPPType *type); + static bool is_bool(CPPType *type); + static bool is_integer(CPPType *type); + static bool is_float(CPPType *type); + static bool is_void(CPPType *type); + static bool is_reference_count(CPPType *type); + static bool is_reference_count_pointer(CPPType *type); + static bool is_pointer_to_base(CPPType *type); + static bool is_const_pointer_to_base(CPPType *type); + static bool is_const_ref_to_pointer_to_base(CPPType *type); + static bool involves_unpublished(CPPType *type); + static bool involves_protected(CPPType *type); + + static CPPType *unwrap_pointer(CPPType *type); + static CPPType *unwrap_reference(CPPType *type); + static CPPType *unwrap_const(CPPType *type); + static CPPType *unwrap_const_reference(CPPType *type); + static CPPType *unwrap(CPPType *type); + + static CPPType *get_pointer_type(CPPStructType *pt_type); + + static CPPType *wrap_pointer(CPPType *type); + static CPPType *wrap_const_pointer(CPPType *type); + static CPPType *wrap_const_reference(CPPType *type); + + static CPPType *get_basic_string_char_type(); + static CPPType *get_reference_count_type(); + static CPPType *get_void_type(); + static CPPType *get_int_type(); + + static string get_function_signature(CPPInstance *function, + int num_default_parameters = 0); + + static bool has_protected_destructor(CPPType *type); +}; + +#endif + diff --git a/dtool/src/interrogate/wrapperBuilder.cxx b/dtool/src/interrogate/wrapperBuilder.cxx new file mode 100644 index 0000000000..4f05a54cdc --- /dev/null +++ b/dtool/src/interrogate/wrapperBuilder.cxx @@ -0,0 +1,629 @@ +// Filename: wrapperBuilder.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "wrapperBuilder.h" +#include "interrogate.h" +#include "parameterRemap.h" +#include "parameterRemapThis.h" +#include "parameterRemapUnchanged.h" +#include "parameterRemapReferenceToPointer.h" +#include "parameterRemapConcreteToPointer.h" +#include "parameterRemapEnumToInt.h" +#include "parameterRemapConstToNonConst.h" +#include "parameterRemapReferenceToConcrete.h" +#include "parameterRemapCharStarToString.h" +#include "parameterRemapBasicStringToString.h" +#include "parameterRemapBasicStringRefToString.h" +#include "parameterRemapPTToPointer.h" +#include "interrogateBuilder.h" +#include "typeManager.h" + +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WrapperBuilder:: +WrapperBuilder() { + _return_type = (ParameterRemap *)NULL; + _void_return = true; + _has_this = false; + _type = T_normal; + + _function = (CPPInstance *)NULL; + _struct_type = (CPPStructType *)NULL; + _scope = (CPPScope *)NULL; + _ftype = (CPPFunctionType *)NULL; + _num_default_parameters = 0; + _wrapper_index = 0; + + _is_valid = false; + _return_value_needs_management = false; + _return_value_destructor = 0; + _manage_reference_count = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WrapperBuilder:: +~WrapperBuilder() { + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::clear +// Access: Public +// Description: Empties the builder and prepares it to receive a new +// function. +//////////////////////////////////////////////////////////////////// +void WrapperBuilder:: +clear() { + Parameters::iterator pi; + for (pi = _parameters.begin(); pi != _parameters.end(); ++pi) { + delete (*pi)._remap; + } + _parameters.clear(); + + if (_return_type != (ParameterRemap *)NULL) { + delete _return_type; + _return_type = (ParameterRemap *)NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::set_function +// Access: Public +// Description: Sets up the builder according to the indicated +// function. The value of num_default_parameters +// represents the number of parameters on the end to +// assign to their default values for this particular +// wrapper. +// +// Returns true if successful, false if the function +// cannot be wrapped for some reason. +//////////////////////////////////////////////////////////////////// +bool WrapperBuilder:: +set_function(CPPInstance *function, const string &description, + CPPStructType *struct_type, + CPPScope *scope, const string &function_signature, + WrapperBuilder::Type type, + const string &expression, + int num_default_parameters) { + clear(); + + _function = function; + _description = description; + _struct_type = struct_type; + _scope = scope; + _function_signature = function_signature; + _expression = expression; + _num_default_parameters = num_default_parameters; + _parameters.clear(); + + _ftype = _function->_type->resolve_type(scope, &parser)->as_function_type(); + assert(_ftype != (CPPFunctionType *)NULL); + + _is_valid = true; + _has_this = false; + _type = type; + _wrapper_index = 0; + + if ((_ftype->_flags & CPPFunctionType::F_constructor) != 0) { + _type = T_constructor; + + } else if ((_ftype->_flags & CPPFunctionType::F_destructor) != 0) { + _type = T_destructor; + + } else if ((_ftype->_flags & CPPFunctionType::F_operator_typecast) != 0) { + _type = T_typecast_method; + } + + if (_struct_type != (CPPStructType *)NULL && + ((function->_storage_class & CPPInstance::SC_static) == 0) && + _type != T_constructor) { + + // If this is a method, but not a static method, and not a + // constructor, then we need to synthesize a "this" parameter. + + Parameter param; + param._name = "this"; + param._has_name = true; + bool is_const = (_ftype->_flags & CPPFunctionType::F_const_method) != 0; + param._remap = new ParameterRemapThis(_struct_type, is_const); + _parameters.push_back(param); + _has_this = true; + } + + const CPPParameterList::Parameters ¶ms = + _ftype->_parameters->_parameters; + for (int i = 0; i < (int)params.size() - num_default_parameters; i++) { + CPPType *type = params[i]->_type->resolve_type(&parser, _scope); + Parameter param; + param._has_name = true; + param._name = params[i]->get_simple_name(); + + if (param._name.empty()) { + // If the parameter has no name, record it as being nameless, + // but also synthesize one in case someone asks anyway. + param._has_name = false; + ostringstream param_name; + param_name << "param" << i; + param._name = param_name.str(); + } + + param._remap = make_remap(type); + param._remap->set_default_value(params[i]->_initializer); + + if (!param._remap->is_valid()) { + _is_valid = false; + } + + _parameters.push_back(param); + } + + if (_type == T_constructor) { + // Constructors are a special case. + + if (_struct_type == (CPPStructType *)NULL) { + nout << "Method " << *_function << " has no struct type\n"; + _is_valid = false; + } else { + _return_type = make_remap(_struct_type); + _void_return = false; + } + + } else { + // The normal case. + CPPType *rtype = _ftype->_return_type->resolve_type(&parser, _scope); + _return_type = make_remap(rtype); + _void_return = TypeManager::is_void(rtype); + } + + if (!_return_type->is_valid()) { + _is_valid = false; + } + + // Do we need to manage the return value? + _return_value_needs_management = _return_type->return_value_needs_management(); + _return_value_destructor = _return_type->get_return_value_destructor(); + + // Should we manage a reference count? + CPPType *return_type = _return_type->get_new_type(); + CPPType *return_meat_type = TypeManager::unwrap_pointer(return_type); + + if (manage_reference_counts && + TypeManager::is_reference_count_pointer(return_type) && + !TypeManager::has_protected_destructor(return_meat_type)) { + // Yes! + _manage_reference_count = true; + _return_value_needs_management = true; + + // This is problematic, because we might not have the class in + // question fully defined here, particularly if the class is + // defined in some other library. + _return_value_destructor = builder.get_destructor_for(return_meat_type); + } + + return _is_valid; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::is_valid +// Access: Public +// Description: Returns true if the function was correctly mapped, or +// false if some parameter type is not supported and the +// remapped function is thus invalid. +//////////////////////////////////////////////////////////////////// +bool WrapperBuilder:: +is_valid() const { + return _is_valid; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::return_value_needs_management +// Access: Public +// Description: Returns true if the return value represents a value +// that was newly allocated, and hence must be +// explicitly deallocated later by the caller. +//////////////////////////////////////////////////////////////////// +bool WrapperBuilder:: +return_value_needs_management() const { + return _return_value_needs_management; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::get_return_value_destructor +// Access: Public +// Description: If return_value_needs_management() returns true, this +// should return the index of the function that should +// be called when it is time to destruct the return +// value. It will generally be the same as the +// destructor for the class we just returned a pointer +// to. +//////////////////////////////////////////////////////////////////// +FunctionIndex WrapperBuilder:: +get_return_value_destructor() const { + return _return_value_destructor; +} + + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::make_remap +// Access: Protected, Virtual +// Description: Allocates a new ParameterRemap object suitable to the +// indicated type. +//////////////////////////////////////////////////////////////////// +ParameterRemap *WrapperBuilder:: +make_remap(CPPType *orig_type) { + if (convert_strings) { + if (TypeManager::is_char_pointer(orig_type)) { + return new ParameterRemapCharStarToString(orig_type); + } + + // If we're exporting a method of basic_string itself, don't + // convert basic_string's to atomic strings. + + if (_struct_type == (CPPStructType *)NULL || + !TypeManager::is_basic_string_char(_struct_type)) { + if (TypeManager::is_basic_string_char(orig_type)) { + return new ParameterRemapBasicStringToString(orig_type); + + } else if (TypeManager::is_const_ref_to_basic_string_char(orig_type)) { + return new ParameterRemapBasicStringRefToString(orig_type); + } + } + } + + if (manage_reference_counts) { + if (TypeManager::is_pointer_to_base(orig_type) || + TypeManager::is_const_ref_to_pointer_to_base(orig_type)) { + CPPType *pt_type = TypeManager::unwrap_reference(orig_type); + + // Don't convert PointerTo<>'s to pointers for methods of the + // PointerTo itself! + if (_struct_type == (CPPStructType *)NULL || + !(pt_type->get_local_name(&parser) == _struct_type->get_local_name(&parser))) { + return new ParameterRemapPTToPointer(orig_type); + } + } + } + + if (TypeManager::is_reference(orig_type)) { + return new ParameterRemapReferenceToPointer(orig_type); + + } else if (TypeManager::is_struct(orig_type)) { + return new ParameterRemapConcreteToPointer(orig_type); + + /* + } else if (TypeManager::is_enum(orig_type) || TypeManager::is_const_ref_to_enum(orig_type)) { + return new ParameterRemapEnumToInt(orig_type); + */ + + } else if (TypeManager::is_const_simple(orig_type)) { + return new ParameterRemapConstToNonConst(orig_type); + + } else if (TypeManager::is_const_ref_to_simple(orig_type)) { + return new ParameterRemapReferenceToConcrete(orig_type); + + } else if (TypeManager::is_pointer(orig_type) || TypeManager::is_simple(orig_type)) { + return new ParameterRemapUnchanged(orig_type); + + } else { + // Here's something we have a problem with. + _is_valid = false; + return new ParameterRemapUnchanged(orig_type); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::manage_return_value +// Access: Protected +// Description: Does any additional processing that we might want to +// do on the return value for the function, just before +// we return it. Returns the string representing the +// new return value after processing. +//////////////////////////////////////////////////////////////////// +string WrapperBuilder:: +manage_return_value(ostream &out, int indent_level, + const string &return_expr) const { + if (_manage_reference_count) { + // If we're managing reference counts, and we're about to return a + // reference countable object, then increment its count. + if (return_expr == "return_value") { + // If the expression is just a variable name, we can just ref it + // directly. + output_ref(out, indent_level, return_expr); + return return_expr; + + } else { + // Otherwise, we should probably assign it to a temporary first, + // so we don't invoke the function twice or something. + CPPType *type = _return_type->get_temporary_type(); + indent(out, indent_level); + type->output_instance(out, "refcount", &parser); + out << " = " << return_expr << ";\n"; + + indent(out, indent_level) + << "if (" << return_expr << " != (" + << _return_type->get_new_type()->get_local_name(&parser) << ")0) {\n"; + indent(out, indent_level + 2) + << return_expr << "->ref();\n"; + indent(out, indent_level) + << "}\n"; + output_ref(out, indent_level, "refcount"); + return _return_type->temporary_to_return("refcount"); + } + } + + // Otherwise, just return the expression unchanged. + return return_expr; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::output_ref +// Access: Protected +// Description: Outputs the code to increment the reference count for +// the indicated variable name. +//////////////////////////////////////////////////////////////////// +void WrapperBuilder:: +output_ref(ostream &out, int indent_level, const string &varname) const { + if (_type == T_constructor || _type == T_typecast) { + // In either of these cases, we can safely assume the pointer will + // never be NULL. + indent(out, indent_level) + << varname << "->ref();\n"; + + } else { + // However, in the general case, we have to check for that before + // we attempt to ref it. + + indent(out, indent_level) + << "if (" << varname << " != (" + << _return_type->get_new_type()->get_local_name(&parser) << ")0) {\n"; + indent(out, indent_level + 2) + << varname << "->ref();\n"; + indent(out, indent_level) + << "}\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::get_parameter_name +// Access: Protected +// Description: Returns a suitable name for the nth parameter. +//////////////////////////////////////////////////////////////////// +string WrapperBuilder:: +get_parameter_name(int n) const { + // Just for the fun of it, we'll name the "this" parameter something + // different. + if (_has_this) { + if (n == 0) { + return "container"; + } + n--; + } + + ostringstream str; + str << "param" << n; + return str.str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::get_parameter_expr +// Access: Protected +// Description: Returns a string that represents the expression +// associated with the nth parameter. This is just the +// nth element of pexprs if it is nonempty, or the name +// of the nth parameter is it is empty. +//////////////////////////////////////////////////////////////////// +string WrapperBuilder:: +get_parameter_expr(int n, const vector &pexprs) const { + if (n < (int)pexprs.size()) { + return pexprs[n]; + } + return get_parameter_name(n); +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::get_call_str +// Access: Protected +// Description: Returns a string suitable for calling the wrapped +// function. If pexprs is nonempty, it represents +// the list of expressions that will evaluate to each +// parameter value. +//////////////////////////////////////////////////////////////////// +string WrapperBuilder:: +get_call_str(const vector &pexprs) const { + // Build up the call to the actual function. + ostringstream call; + + int pn = 0; + + // Getters and setters are a special case. + if (_type == T_getter) { + if (_has_this) { + call << "(" << get_parameter_expr(pn, pexprs) << ")->" + << _expression; + } else { + call << _expression; + } + + } else if (_type == T_setter) { + if (_has_this) { + call << "(" << get_parameter_expr(pn, pexprs) << ")->" + << _expression; + pn++; + } else { + call << _expression; + } + + call << " = "; + assert(pn + 1 == (int)_parameters.size()); + _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); + + } else { + if (_has_this) { + // If we have a synthesized "this" parameter, the calling + // convention is a bit different. + call << "(" << get_parameter_expr(pn, pexprs) << ")->" + << _function->get_local_name(); + pn++; + + } else if (_type == T_constructor) { + // Constructors are called differently too. + call << _struct_type->get_local_name(&parser); + + } else { + call << _function->get_local_name(&parser); + } + + call << "("; + if (pn < (int)_parameters.size()) { + _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); + pn++; + while (pn < (int)_parameters.size()) { + call << ", "; + _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); + pn++; + } + } + call << ")"; + } + + return call.str(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::call_function +// Access: Protected +// Description: Writes a sequence of commands to the given output +// stream to call the wrapped function. The parameter +// values are taken from pexprs, if it is nonempty, or +// are assumed to be simply the names of the parameters, +// if it is empty. +// +// The return value is the expression to return, if we +// are returning a value, or the empty string if we +// return nothing. +//////////////////////////////////////////////////////////////////// +string WrapperBuilder:: +call_function(ostream &out, int indent_level, bool convert_result, + const vector &pexprs) const { + string return_expr; + + if (_type == T_destructor) { + // A destructor wrapper is just a wrapper around the delete operator. + assert(_parameters.size() == 1); + assert(_has_this); + assert(_struct_type != (CPPStructType *)NULL); + + if (TypeManager::is_reference_count(_struct_type)) { + // Except for a reference-count type object, in which case the + // destructor is a wrapper around unref_delete(). + indent(out, indent_level) + << "unref_delete(" << get_parameter_expr(0, pexprs) << ");\n"; + } else { + indent(out, indent_level) + << "delete " << get_parameter_expr(0, pexprs) << ";\n"; + } + + } else if (_type == T_typecast_method) { + // A typecast method can be invoked implicitly. + assert(_parameters.size() == 1); + string cast_expr = + "(" + _return_type->get_orig_type()->get_local_name(&parser) + + ")(*" + get_parameter_expr(0, pexprs) + ")"; + + if (!convert_result) { + return_expr = cast_expr; + } else { + string new_str = + _return_type->prepare_return_expr(out, indent_level, cast_expr); + return_expr = _return_type->get_return_expr(new_str); + } + + } else if (_type == T_typecast) { + // A regular typecast converts from a pointer type to another + // pointer type. (This is different from the typecast method, + // above, which converts from the concrete type to some other + // type.) + assert(_parameters.size() == 1); + string cast_expr = + "(" + _return_type->get_orig_type()->get_local_name(&parser) + + ")" + get_parameter_expr(0, pexprs); + + if (!convert_result) { + return_expr = cast_expr; + } else { + string new_str = + _return_type->prepare_return_expr(out, indent_level, cast_expr); + return_expr = _return_type->get_return_expr(new_str); + } + + } else { + string call = get_call_str(pexprs); + + // Now output that call. + if (_type == T_constructor) { + // A special case for constructors. + return_expr = "new " + call; + + } else if (_void_return) { + indent(out, indent_level) + << call << ";\n"; + + } else { + if (!convert_result) { + return_expr = call; + + } else { + if (_return_type->return_value_should_be_simple()) { + // We have to assign the result to a temporary first; this makes + // it a bit easier on poor old VC++. + indent(out, indent_level); + _return_type->get_orig_type()->output_instance(out, "result", + &parser); + out << " = " << call << ";\n"; + + string new_str = + _return_type->prepare_return_expr(out, indent_level, "result"); + return_expr = _return_type->get_return_expr(new_str); + + } else { + // This should be simple enough that we can return it directly. + string new_str = + _return_type->prepare_return_expr(out, indent_level, call); + return_expr = _return_type->get_return_expr(new_str); + } + } + } + } + + return return_expr; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilder::indent +// Access: Protected, Static +// Description: +//////////////////////////////////////////////////////////////////// +ostream &WrapperBuilder:: +indent(ostream &out, int indent_level) { + for (int i = 0; i < indent_level; i++) { + out << ' '; + } + return out; +} diff --git a/dtool/src/interrogate/wrapperBuilder.h b/dtool/src/interrogate/wrapperBuilder.h new file mode 100644 index 0000000000..4981a8dc11 --- /dev/null +++ b/dtool/src/interrogate/wrapperBuilder.h @@ -0,0 +1,124 @@ +// Filename: wrapperBuilder.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef WRAPPERBUILDER_H +#define WRAPPERBUILDER_H + +#include + +#include + +#include + +class CPPInstance; +class CPPScope; +class CPPStructType; +class CPPType; +class CPPFunctionType; +class ParameterRemap; + +//////////////////////////////////////////////////////////////////// +// Class : WrapperBuilder +// Description : Contains all the information necessary to synthesize +// a wrapper around a particular function. This +// includes choosing appropriate parameter types to +// remap from the C++ function's parameter types, as +// well as actually generating wrapper code. +// +// This is an abstract class; it doesn't actually know +// how to choose parameter types and synthesize +// wrappers; see WrapperBuilderC and +// WrapperBuilderPython. +//////////////////////////////////////////////////////////////////// +class WrapperBuilder { +public: + enum Type { + T_normal, + T_constructor, + T_destructor, + T_typecast_method, + T_typecast, + T_getter, + T_setter + }; + + WrapperBuilder(); + virtual ~WrapperBuilder(); + + void clear(); + bool set_function(CPPInstance *function, const string &description, + CPPStructType *struct_type, CPPScope *scope, + const string &function_signature, Type type, + const string &expression, + int num_default_parameters); + + bool is_valid() const; + bool return_value_needs_management() const; + FunctionIndex get_return_value_destructor() const; + + virtual void + write_wrapper(ostream &out, const string &wrapper_name) const=0; + + virtual string + get_wrapper_name(const string &library_hash_name) const=0; + + virtual bool supports_atomic_strings() const=0; + + enum CallingConvention { + CC_c, + CC_python, + }; + + virtual CallingConvention get_calling_convention() const=0; + + class Parameter { + public: + bool _has_name; + string _name; + ParameterRemap *_remap; + }; + + typedef vector Parameters; + Parameters _parameters; + ParameterRemap *_return_type; + bool _void_return; + bool _has_this; + Type _type; + + CPPInstance *_function; + string _description; + CPPStructType *_struct_type; + CPPScope *_scope; + CPPFunctionType *_ftype; + string _function_signature; + string _expression; + int _num_default_parameters; + string _hash; + int _wrapper_index; + +protected: + virtual ParameterRemap *make_remap(CPPType *orig_type); + string manage_return_value(ostream &out, int indent_level, + const string &return_expr) const; + void output_ref(ostream &out, int indent_level, const string &varname) const; + + string get_parameter_name(int n) const; + string get_parameter_expr(int n, const vector &pexprs) const; + + string get_call_str(const vector &pexprs = vector()) const; + string call_function(ostream &out, int indent_level, + bool convert_result = true, + const vector &pexprs = vector()) const; + + bool _is_valid; + bool _return_value_needs_management; + FunctionIndex _return_value_destructor; + bool _manage_reference_count; + +public: + static ostream &indent(ostream &out, int indent_level); +}; + +#endif diff --git a/dtool/src/interrogate/wrapperBuilderC.cxx b/dtool/src/interrogate/wrapperBuilderC.cxx new file mode 100644 index 0000000000..969fcf1fd7 --- /dev/null +++ b/dtool/src/interrogate/wrapperBuilderC.cxx @@ -0,0 +1,111 @@ +// Filename: wrapperBuilderC.C +// Created by: drose (06Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "wrapperBuilderC.h" +#include "interrogate.h" +#include "parameterRemap.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderC::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WrapperBuilderC:: +WrapperBuilderC() { +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderC::write_wrapper +// Access: Public, Virtual +// Description: Generates a wrapper function to the indicated output +// stream. +//////////////////////////////////////////////////////////////////// +void WrapperBuilderC:: +write_wrapper(ostream &out, const string &wrapper_name) const { + out << "/*\n" + << " * C wrapper for\n" + << " * " << _description << "\n" + << " */\n"; + + if (!output_function_names) { + // If we're not saving the function names, don't export it from + // the library. + out << "static "; + } + + if (_void_return) { + out << "void\n"; + } else { + out << _return_type->get_new_type()->get_local_name(&parser) << "\n"; + } + + out << wrapper_name << "("; + int pn = 0; + if (pn < (int)_parameters.size()) { + _parameters[pn]._remap->get_new_type()-> + output_instance(out, get_parameter_name(pn), &parser); + pn++; + while (pn < (int)_parameters.size()) { + out << ", "; + _parameters[pn]._remap->get_new_type()-> + output_instance(out, get_parameter_name(pn), &parser); + pn++; + } + } + out << ") {\n"; + + string return_expr = call_function(out, 2); + return_expr = manage_return_value(out, 2, return_expr); + if (!return_expr.empty()) { + out << " return " << return_expr << ";\n"; + } + + out << "}\n\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderC::get_wrapper_name +// Access: Public, Virtual +// Description: Returns the callable name for this wrapper function. +//////////////////////////////////////////////////////////////////// +string WrapperBuilderC:: +get_wrapper_name(const string &library_hash_name) const { + return "_inC" + library_hash_name + _hash; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderC::supports_atomic_strings +// Access: Public, Virtual +// Description: Returns true if this kind of wrapper can support true +// atomic string objects (and not have to fiddle with +// char *). +//////////////////////////////////////////////////////////////////// +bool WrapperBuilderC:: +supports_atomic_strings() const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderC::get_calling_convention +// Access: Public, Virtual +// Description: Returns an indication of what kind of function we are +// building. +//////////////////////////////////////////////////////////////////// +WrapperBuilder::CallingConvention WrapperBuilderC:: +get_calling_convention() const { + return CC_c; +} + diff --git a/dtool/src/interrogate/wrapperBuilderC.h b/dtool/src/interrogate/wrapperBuilderC.h new file mode 100644 index 0000000000..c579fbed44 --- /dev/null +++ b/dtool/src/interrogate/wrapperBuilderC.h @@ -0,0 +1,32 @@ +// Filename: wrapperBuilderC.h +// Created by: drose (06Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef WRAPPERBUILDERC_H +#define WRAPPERBUILDERC_H + +#include + +#include "wrapperBuilder.h" + +//////////////////////////////////////////////////////////////////// +// Class : WrapperBuilderC +// Description : A specialization on WrapperBuilder that builds +// C-style wrapper functions. +//////////////////////////////////////////////////////////////////// +class WrapperBuilderC : public WrapperBuilder { +public: + WrapperBuilderC(); + + virtual void + write_wrapper(ostream &out, const string &wrapper_name) const; + + virtual string + get_wrapper_name(const string &library_hash_name) const; + + virtual bool supports_atomic_strings() const; + virtual CallingConvention get_calling_convention() const; +}; + +#endif diff --git a/dtool/src/interrogate/wrapperBuilderPython.cxx b/dtool/src/interrogate/wrapperBuilderPython.cxx new file mode 100644 index 0000000000..b6d93c9213 --- /dev/null +++ b/dtool/src/interrogate/wrapperBuilderPython.cxx @@ -0,0 +1,268 @@ +// Filename: wrapperBuilderPython.C +// Created by: drose (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "wrapperBuilderPython.h" +#include "interrogate.h" +#include "parameterRemap.h" +#include "typeManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderPython::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +WrapperBuilderPython:: +WrapperBuilderPython() { +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderPython::write_wrapper +// Access: Public, Virtual +// Description: Generates a wrapper function to the indicated output +// stream. +//////////////////////////////////////////////////////////////////// +void WrapperBuilderPython:: +write_wrapper(ostream &out, const string &wrapper_name) const { + out << "/*\n" + << " * Python wrapper for\n" + << " * " << _description << "\n" + << " */\n"; + + if (!output_function_names) { + // If we're not saving the function names, don't export it from + // the library. + out << "static "; + } + + out << "PyObject *\n" + << wrapper_name << "(PyObject *, PyObject *args) {\n"; + + int pn; + + string format_specifiers; + string parameter_list; + vector pexprs; + + // Make one pass through the parameter list. We will output a + // one-line temporary variable definition for each parameter, while + // simultaneously building the ParseTuple() function call and also + // the parameter expression list for call_function(). + + for (pn = 0; pn < (int)_parameters.size(); pn++) { + out << " "; + CPPType *orig_type = _parameters[pn]._remap->get_orig_type(); + CPPType *type = _parameters[pn]._remap->get_new_type(); + + // This is the string to convert our local variable to the + // appropriate C++ type. Normally this is just a cast. + string pexpr_string = + "(" + type->get_local_name(&parser) + ")" + get_parameter_name(pn); + + if (_parameters[pn]._remap->new_type_is_atomic_string()) { + if (TypeManager::is_char_pointer(orig_type)) { + out << "char *" << get_parameter_name(pn); + format_specifiers += "s"; + parameter_list += ", &" + get_parameter_name(pn); + + } else { + out << "char *" << get_parameter_name(pn) + << "_str; int " << get_parameter_name(pn) << "_len"; + format_specifiers += "s#"; + parameter_list += ", &" + get_parameter_name(pn) + + "_str, &" + get_parameter_name(pn) + "_len"; + pexpr_string = "basic_string(" + + get_parameter_name(pn) + "_str, " + + get_parameter_name(pn) + "_len)"; + } + + } else if (TypeManager::is_bool(type)) { + out << "PyObject *" << get_parameter_name(pn); + format_specifiers += "O"; + parameter_list += ", &" + get_parameter_name(pn); + pexpr_string = "PyObject_IsTrue(" + get_parameter_name(pn) + ")"; + + } else if (TypeManager::is_integer(type)) { + out << "int " << get_parameter_name(pn); + format_specifiers += "i"; + parameter_list += ", &" + get_parameter_name(pn); + + } else if (TypeManager::is_float(type)) { + out << "double " << get_parameter_name(pn); + format_specifiers += "d"; + parameter_list += ", &" + get_parameter_name(pn); + + } else if (TypeManager::is_char_pointer(type)) { + out << "char *" << get_parameter_name(pn); + format_specifiers += "s"; + parameter_list += ", &" + get_parameter_name(pn); + + } else if (TypeManager::is_pointer(type)) { + out << "int " << get_parameter_name(pn); + format_specifiers += "i"; + parameter_list += ", &" + get_parameter_name(pn); + + } else { + // Ignore a parameter. + out << "PyObject *" << get_parameter_name(pn); + format_specifiers += "O"; + parameter_list += ", &" + get_parameter_name(pn); + } + + out << ";\n"; + pexprs.push_back(pexpr_string); + } + + out << " if (PyArg_ParseTuple(args, \"" << format_specifiers + << "\"" << parameter_list << ")) {\n"; + + if (_return_type->new_type_is_atomic_string()) { + // Treat strings as a special case. We don't want to format the + // return expression. + string return_expr = call_function(out, 4, false, pexprs); + + CPPType *type = _return_type->get_orig_type(); + out << " "; + type->output_instance(out, "return_value", &parser); + out << " = " << return_expr << ";\n"; + return_expr = manage_return_value(out, 4, "return_value"); + test_assert(out, 4); + pack_return_value(out, return_expr); + + } else { + string return_expr = call_function(out, 4, true, pexprs); + if (return_expr.empty()) { + test_assert(out, 4); + out << " return Py_BuildValue(\"\");\n"; + + } else { + CPPType *type = _return_type->get_temporary_type(); + out << " "; + type->output_instance(out, "return_value", &parser); + out << " = " << return_expr << ";\n"; + + return_expr = manage_return_value(out, 4, "return_value"); + test_assert(out, 4); + pack_return_value(out, _return_type->temporary_to_return(return_expr)); + } + } + + out << " }\n"; + + out << " return (PyObject *)NULL;\n"; + out << "}\n\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderPython::get_wrapper_name +// Access: Public, Virtual +// Description: Returns the callable name for this wrapper function. +//////////////////////////////////////////////////////////////////// +string WrapperBuilderPython:: +get_wrapper_name(const string &library_hash_name) const { + return "_inP" + library_hash_name + _hash; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderPython::supports_atomic_strings +// Access: Public, Virtual +// Description: Returns true if this kind of wrapper can support true +// atomic string objects (and not have to fiddle with +// char *). +//////////////////////////////////////////////////////////////////// +bool WrapperBuilderPython:: +supports_atomic_strings() const { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderPython::get_calling_convention +// Access: Public, Virtual +// Description: Returns an indication of what kind of function we are +// building. +//////////////////////////////////////////////////////////////////// +WrapperBuilder::CallingConvention WrapperBuilderPython:: +get_calling_convention() const { + return CC_python; +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderPython::test_assert +// Access: Protected +// Description: Outputs code to check to see if an assertion has +// failed while the C++ code was executing, and report +// this failure back to Python. +//////////////////////////////////////////////////////////////////// +void WrapperBuilderPython:: +test_assert(ostream &out, int indent_level) const { + if (watch_asserts) { + out << "#ifndef NDEBUG\n"; + indent(out, indent_level) + << "Notify *notify = Notify::ptr();\n"; + indent(out, indent_level) + << "if (notify->has_assert_failed()) {\n"; + indent(out, indent_level + 2) + << "PyErr_SetString(PyExc_AssertionError, notify->get_assert_error_message().c_str());\n"; + indent(out, indent_level + 2) + << "notify->clear_assert_failed();\n"; + indent(out, indent_level + 2) + << "return (PyObject *)NULL;\n"; + indent(out, indent_level) + << "}\n"; + out << "#endif\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: WrapperBuilderPython::pack_return_value +// Access: Protected +// Description: Outputs a command to pack the indicated expression, +// of the return_type type, as a Python return value. +//////////////////////////////////////////////////////////////////// +void WrapperBuilderPython:: +pack_return_value(ostream &out, string return_expr) const { + CPPType *orig_type = _return_type->get_orig_type(); + CPPType *type = _return_type->get_new_type(); + + out << " return Py_BuildValue("; + + if (_return_type->new_type_is_atomic_string()) { + if (TypeManager::is_char_pointer(orig_type)) { + out << "\"s\", " << return_expr; + + } else { + out << "\"s#\", " << return_expr << ".data(), " + << return_expr << ".length()"; + } + + } else if (TypeManager::is_integer(type)) { + out << "\"i\", (int)(" << return_expr << ")"; + + } else if (TypeManager::is_float(type)) { + out << "\"d\", (double)(" << return_expr << ")"; + + } else if (TypeManager::is_char_pointer(type)) { + out << "\"s\", " << return_expr; + + } else if (TypeManager::is_pointer(type)) { + out << "\"i\", (int)" << return_expr; + + } else { + // Return None. + out << "\"\""; + } + out << ");\n"; +} diff --git a/dtool/src/interrogate/wrapperBuilderPython.h b/dtool/src/interrogate/wrapperBuilderPython.h new file mode 100644 index 0000000000..c36089f710 --- /dev/null +++ b/dtool/src/interrogate/wrapperBuilderPython.h @@ -0,0 +1,36 @@ +// Filename: wrapperBuilderPython.h +// Created by: drose (07Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef WRAPPERBUILDERPYTHON_H +#define WRAPPERBUILDERPYTHON_H + +#include + +#include "wrapperBuilder.h" + +//////////////////////////////////////////////////////////////////// +// Class : WrapperBuilderPython +// Description : A specialization on WrapperBuilder that builds +// Python-style wrapper functions. +//////////////////////////////////////////////////////////////////// +class WrapperBuilderPython : public WrapperBuilder { +public: + WrapperBuilderPython(); + + virtual void + write_wrapper(ostream &out, const string &wrapper_name) const; + + virtual string + get_wrapper_name(const string &library_hash_name) const; + + virtual bool supports_atomic_strings() const; + virtual CallingConvention get_calling_convention() const; + +protected: + void test_assert(ostream &out, int indent_level) const; + void pack_return_value(ostream &out, string return_expr) const; +}; + +#endif diff --git a/dtool/src/interrogatedb/Sources.pp b/dtool/src/interrogatedb/Sources.pp new file mode 100644 index 0000000000..be6ccb7861 --- /dev/null +++ b/dtool/src/interrogatedb/Sources.pp @@ -0,0 +1,39 @@ +#define LOCAL_LIBS dconfig dtoolutil dtoolbase + +#begin lib_target + #define TARGET interrogatedb + + #define SOURCES \ + config_interrogatedb.cxx config_interrogatedb.h indexRemapper.cxx \ + indexRemapper.h interrogateComponent.I interrogateComponent.cxx \ + interrogateComponent.h interrogateDatabase.I \ + interrogateDatabase.cxx interrogateDatabase.h interrogateElement.I \ + interrogateElement.cxx interrogateElement.h interrogateFunction.I \ + interrogateFunction.cxx interrogateFunction.h \ + interrogateFunctionWrapper.I interrogateFunctionWrapper.cxx \ + interrogateFunctionWrapper.h interrogateManifest.I \ + interrogateManifest.cxx interrogateManifest.h interrogateType.I \ + interrogateType.cxx interrogateType.h interrogate_datafile.I \ + interrogate_datafile.cxx interrogate_datafile.h \ + interrogate_interface.cxx interrogate_interface.h \ + interrogate_request.cxx interrogate_request.h vector_int.cxx \ + vector_int.h + + #define INSTALL_HEADERS \ + interrogate_interface.h interrogate_request.h \ + vector_int.h + +// These are temporary; they need not be installed in the future. These are +// necessary only when using template stopgap. + #define INSTALL_HEADERS \ + interrogate_interface.h interrogate_request.h \ + interrogateComponent.h interrogateComponent.I \ + interrogateType.h interrogateType.I \ + interrogateFunction.h interrogateFunction.I \ + interrogateFunctionWrapper.h interrogateFunctionWrapper.I \ + interrogateManifest.h interrogateManifest.I \ + interrogateElement.h interrogateElement.I \ + interrogateDatabase.h interrogateDatabase.I \ + indexRemapper.h vector_int.h + +#end lib_target diff --git a/dtool/src/interrogatedb/config_interrogatedb.cxx b/dtool/src/interrogatedb/config_interrogatedb.cxx new file mode 100644 index 0000000000..f692d3be29 --- /dev/null +++ b/dtool/src/interrogatedb/config_interrogatedb.cxx @@ -0,0 +1,28 @@ +// Filename: config_interrogatedb.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "config_interrogatedb.h" +#include "interrogate_request.h" + +#include + +Configure(config_interrogatedb); +NotifyCategoryDef(interrogatedb, ""); + +ConfigureFn(config_interrogatedb) { + // interrogate_request_library("types"); +} + +const DSearchPath & +get_interrogatedb_path() { + static DSearchPath *interrogatedb_path = NULL; + if (interrogatedb_path == (DSearchPath *)NULL) { + interrogatedb_path = new DSearchPath("."); + interrogatedb_path->append_path + (config_interrogatedb.GetString("ETC_PATH", ".")); + } + return *interrogatedb_path; +} + diff --git a/dtool/src/interrogatedb/config_interrogatedb.h b/dtool/src/interrogatedb/config_interrogatedb.h new file mode 100644 index 0000000000..ff16ce258c --- /dev/null +++ b/dtool/src/interrogatedb/config_interrogatedb.h @@ -0,0 +1,17 @@ +// Filename: config_interrogatedb.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_INTERROGATEDB_H +#define CONFIG_INTERROGATEDB_H + +#include +#include +#include + +NotifyCategoryDecl(interrogatedb, EXPCL_DTOOL, EXPTP_DTOOL); + +const DSearchPath &get_interrogatedb_path(); + +#endif diff --git a/dtool/src/interrogatedb/indexRemapper.cxx b/dtool/src/interrogatedb/indexRemapper.cxx new file mode 100644 index 0000000000..375a155671 --- /dev/null +++ b/dtool/src/interrogatedb/indexRemapper.cxx @@ -0,0 +1,73 @@ +// Filename: indexRemapper.C +// Created by: drose (05Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "indexRemapper.h" + + +//////////////////////////////////////////////////////////////////// +// Function: IndexRemapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +IndexRemapper:: +IndexRemapper() { +} + +//////////////////////////////////////////////////////////////////// +// Function: IndexRemapper::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +IndexRemapper:: +~IndexRemapper() { +} + +//////////////////////////////////////////////////////////////////// +// Function: IndexRemapper::clear +// Access: Public +// Description: Removes all mappings from the object. +//////////////////////////////////////////////////////////////////// +void IndexRemapper:: +clear() { + _map_int.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: IndexRemapper::add_mapping +// Access: Public +// Description: Adds a mapping from the integer 'from' to 'to'. +//////////////////////////////////////////////////////////////////// +void IndexRemapper:: +add_mapping(int from, int to) { + _map_int[from] = to; +} + +//////////////////////////////////////////////////////////////////// +// Function: IndexRemapper::in_map +// Access: Public +// Description: Returns true if the given 'from' integer has been +// assigned a mapping, false if it has not. +//////////////////////////////////////////////////////////////////// +bool IndexRemapper:: +in_map(int from) const { + return _map_int.count(from) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: IndexRemapper::map_from +// Access: Public +// Description: Returns the integer that the given 'from' integer had +// been set to map to, or the same integer if nothing +// had been set for it. +//////////////////////////////////////////////////////////////////// +int IndexRemapper:: +map_from(int from) const { + map::const_iterator mi; + mi = _map_int.find(from); + if (mi == _map_int.end()) { + return from; + } + return (*mi).second; +} diff --git a/dtool/src/interrogatedb/indexRemapper.h b/dtool/src/interrogatedb/indexRemapper.h new file mode 100644 index 0000000000..46beda1cce --- /dev/null +++ b/dtool/src/interrogatedb/indexRemapper.h @@ -0,0 +1,42 @@ +// Filename: indexRemapper.h +// Created by: drose (05Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INDEXREMAPPER_H +#define INDEXREMAPPER_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : IndexRemapper +// Description : This class manages a mapping of integers to integers. +// It's used in this package to resequence some or all +// of the index numbers in the database to a different +// sequence. +// +// This class is just a wrapper around STL map. The +// only reason it exists is because Microsoft can't +// export STL map outside of the DLL. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL IndexRemapper { +public: + IndexRemapper(); + ~IndexRemapper(); + + void clear(); + void add_mapping(int from, int to); + + bool in_map(int from) const; + int map_from(int from) const; + +private: + map _map_int; +}; + +#endif + + + diff --git a/dtool/src/interrogatedb/interrogateComponent.I b/dtool/src/interrogatedb/interrogateComponent.I new file mode 100644 index 0000000000..4578b4cfc9 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateComponent.I @@ -0,0 +1,118 @@ +// Filename: interrogateComponent.I +// Created by: drose (08Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateComponent:: +InterrogateComponent(InterrogateModuleDef *def) : + _def(def) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateComponent:: +InterrogateComponent(const InterrogateComponent ©) : + _def(copy._def), + _name(copy._name) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void InterrogateComponent:: +operator = (const InterrogateComponent ©) { + _def = copy._def; + _name = copy._name; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::has_library_name +// Access: Public +// Description: Returns true if we have a known library name, false if +// we do not. See get_library_name(). +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateComponent:: +has_library_name() const { + const char *name = get_library_name(); + return (name != (const char *)NULL && name[0] != '\0'); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::get_library_name +// Access: Public +// Description: Returns the library name, if it is known, or NULL if +// it is not. This is the name of the library that this +// particular component was built into. Typically this +// will be a one-to-one correspondance with an +// invocation of the interrogate command. Typical +// examples are "libutil" and "liblinmath". +//////////////////////////////////////////////////////////////////// +INLINE const char *InterrogateComponent:: +get_library_name() const { + if (_def != (InterrogateModuleDef *)NULL) { + return _def->library_name; + } + return (const char *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::has_module_name +// Access: Public +// Description: Returns true if we have a known module name, false if +// we do not. See get_module_name(). +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateComponent:: +has_module_name() const { + const char *name = get_module_name(); + return (name != (const char *)NULL && name[0] != '\0'); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::get_module_name +// Access: Public +// Description: Returns the module name, if it is known, or NULL if +// it is not. This is the name of the module that this +// particular component is associated with. This is a +// higher grouping than library. Typical examples are +// "panda" and "pandaegg". +//////////////////////////////////////////////////////////////////// +INLINE const char *InterrogateComponent:: +get_module_name() const { + if (_def != (InterrogateModuleDef *)NULL) { + return _def->module_name; + } + return (const char *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::has_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateComponent:: +has_name() const { + return !_name.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::get_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateComponent:: +get_name() const { + return _name; +} diff --git a/dtool/src/interrogatedb/interrogateComponent.cxx b/dtool/src/interrogatedb/interrogateComponent.cxx new file mode 100644 index 0000000000..f7a46a0bc7 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateComponent.cxx @@ -0,0 +1,29 @@ +// Filename: interrogateComponent.C +// Created by: drose (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogateComponent.h" +#include "interrogate_datafile.h" + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::output +// Access: Public +// Description: Formats the component for output to a data file. +//////////////////////////////////////////////////////////////////// +void InterrogateComponent:: +output(ostream &out) const { + idf_output_string(out, _name); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateComponent::input +// Access: Public +// Description: Reads the data file as previously formatted by +// output(). +//////////////////////////////////////////////////////////////////// +void InterrogateComponent:: +input(istream &in) { + idf_input_string(in, _name); +} diff --git a/dtool/src/interrogatedb/interrogateComponent.h b/dtool/src/interrogatedb/interrogateComponent.h new file mode 100644 index 0000000000..cd6445922d --- /dev/null +++ b/dtool/src/interrogatedb/interrogateComponent.h @@ -0,0 +1,53 @@ +// Filename: interrogateComponent.h +// Created by: drose (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATECOMPONENT_H +#define INTERROGATECOMPONENT_H + +#include + +#include "interrogate_interface.h" +#include "interrogate_request.h" + +#include + +class IndexRemapper; + +//////////////////////////////////////////////////////////////////// +// Class : InterrogateComponent +// Description : The base class for things that are part of the +// interrogate database. This includes types, +// functions, and function wrappers. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL InterrogateComponent { +public: + INLINE InterrogateComponent(InterrogateModuleDef *def = NULL); + INLINE InterrogateComponent(const InterrogateComponent ©); + INLINE void operator = (const InterrogateComponent ©); + + INLINE bool has_library_name() const; + INLINE const char *get_library_name() const; + + INLINE bool has_module_name() const; + INLINE const char *get_module_name() const; + + INLINE bool has_name() const; + INLINE const string &get_name() const; + + void output(ostream &out) const; + void input(istream &in); + +private: + InterrogateModuleDef *_def; + string _name; + + friend class InterrogateBuilder; +}; + +#include "interrogateComponent.I" + +#endif + + diff --git a/dtool/src/interrogatedb/interrogateDatabase.I b/dtool/src/interrogatedb/interrogateDatabase.I new file mode 100644 index 0000000000..7a290ecb81 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateDatabase.I @@ -0,0 +1,102 @@ +// Filename: interrogateDatabase.I +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::check_latest +// Access: Public +// Description: Checks that all the latest data for all the libraries +// have been loaded. Loads them if not. +//////////////////////////////////////////////////////////////////// +INLINE void InterrogateDatabase:: +check_latest() { + if (!_requests.empty()) { + load_latest(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::lookup_type_by_name +// Access: Public +// Description: Returns the TypeIndex associated with the first type +// found with the given name, or 0 if no type has this +// name. +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateDatabase:: +lookup_type_by_name(const string &name) { + check_latest(); + return lookup(name, _types_by_name, LT_type_name, + &InterrogateDatabase::freshen_types_by_name); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::lookup_type_by_scoped_name +// Access: Public +// Description: Returns the TypeIndex associated with the first type +// found with the given scoped name, or 0 if no type has +// this name. +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateDatabase:: +lookup_type_by_scoped_name(const string &name) { + check_latest(); + return lookup(name, _types_by_scoped_name, LT_type_scoped_name, + &InterrogateDatabase::freshen_types_by_scoped_name); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::lookup_type_by_true_name +// Access: Public +// Description: Returns the TypeIndex associated with the first type +// found with the given true name, or 0 if no type has +// this name. +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateDatabase:: +lookup_type_by_true_name(const string &name) { + check_latest(); + return lookup(name, _types_by_true_name, LT_type_true_name, + &InterrogateDatabase::freshen_types_by_true_name); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::lookup_manifest_by_name +// Access: Public +// Description: Returns the ManifestIndex associated with the first +// manifest found with the given name, or 0 if no +// manifest has this name. +//////////////////////////////////////////////////////////////////// +INLINE ManifestIndex InterrogateDatabase:: +lookup_manifest_by_name(const string &name) { + check_latest(); + return lookup(name, _manifests_by_name, LT_manifest_name, + &InterrogateDatabase::freshen_manifests_by_name); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::lookup_element_by_name +// Access: Public +// Description: Returns the ElementIndex associated with the first +// element found with the given name, or 0 if no element +// has this name. +//////////////////////////////////////////////////////////////////// +INLINE ElementIndex InterrogateDatabase:: +lookup_element_by_name(const string &name) { + check_latest(); + return lookup(name, _elements_by_name, LT_element_name, + &InterrogateDatabase::freshen_elements_by_name); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::lookup_element_by_scoped_name +// Access: Public +// Description: Returns the ElementIndex associated with the first +// element found with the given scoped name, or 0 if no +// element has this name. +//////////////////////////////////////////////////////////////////// +INLINE ElementIndex InterrogateDatabase:: +lookup_element_by_scoped_name(const string &name) { + check_latest(); + return lookup(name, _elements_by_scoped_name, LT_element_scoped_name, + &InterrogateDatabase::freshen_elements_by_scoped_name); +} diff --git a/dtool/src/interrogatedb/interrogateDatabase.cxx b/dtool/src/interrogatedb/interrogateDatabase.cxx new file mode 100644 index 0000000000..86b96f57c0 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateDatabase.cxx @@ -0,0 +1,1328 @@ +// Filename: interrogateDatabase.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogateDatabase.h" +#include "config_interrogatedb.h" +#include "indexRemapper.h" +#include "interrogate_datafile.h" + +InterrogateDatabase *InterrogateDatabase::_global_ptr = NULL; +int InterrogateDatabase::_file_major_version = 0; +int InterrogateDatabase::_file_minor_version = 0; +int InterrogateDatabase::_current_major_version = 2; +int InterrogateDatabase::_current_minor_version = 2; + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +InterrogateDatabase:: +InterrogateDatabase() { + _next_index = 1; + _lookups_fresh = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_ptr +// Access: Private +// Description: Returns the global pointer to the one +// InterrogateDatabase. +//////////////////////////////////////////////////////////////////// +InterrogateDatabase *InterrogateDatabase:: +get_ptr() { + if (_global_ptr == (InterrogateDatabase *)NULL) { + _global_ptr = new InterrogateDatabase; + } + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::request_module +// Access: Public +// Description: Requests that the interrogate data for the given +// module be made available. The function pointers will +// be made available immediately, while the database +// file will be read later, the next time someone asks +// for interrogate data that requires it. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +request_module(InterrogateModuleDef *def) { + if (interrogatedb_cat->is_debug()) { + if (def->library_name == (const char *)NULL) { + interrogatedb_cat->debug() + << "Got interrogate data for anonymous module\n"; + } else { + interrogatedb_cat->debug() + << "Got interrogate data for module " << def->library_name << "\n"; + } + } + + int num_indices = def->next_index - def->first_index; + if (num_indices > 0) { + // If the module def has any definitions--any index numbers + // used--assign it to its own unique range of index numbers. + def->first_index = _next_index; + _next_index += num_indices; + def->next_index = _next_index; + + // Assign a reference to the module def by index number. When we + // need to look up a function by its index number, we'll be able + // to use this. + _modules.push_back(def); + } + + if (def->num_unique_names > 0 && def->library_name != (const char *)NULL) { + // Define a lookup by hash for this module, mainly so we can look + // up functions by their unique names. + _modules_by_hash[def->library_hash_name] = def; + } + + if (def->database_filename != (const char *)NULL) { + _requests.push_back(def); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_num_global_types +// Access: Public +// Description: Returns the total number of "global" types known to +// the interrogate database. These are types defined at +// the global level that should be considered for +// exporting, but not the incidental types (like +// pointers, etc.) that must be defined to support +// these. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_num_global_types() { + check_latest(); + return _global_types.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_global_type +// Access: Public +// Description: Returns the index of the nth global type known to the +// interrogate database. +//////////////////////////////////////////////////////////////////// +TypeIndex InterrogateDatabase:: +get_global_type(int n) { + check_latest(); + if (n >= 0 && n < (int)_global_types.size()) { + return _global_types[n]; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_num_all_types +// Access: Public +// Description: Returns the total number of types known to the +// interrogate database. This includes all known types, +// global as well as incidental. See also +// get_num_global_types(). +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_num_all_types() { + check_latest(); + return _all_types.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_all_type +// Access: Public +// Description: Returns the index of the nth type known to the +// interrogate database. +//////////////////////////////////////////////////////////////////// +TypeIndex InterrogateDatabase:: +get_all_type(int n) { + check_latest(); + if (n >= 0 && n < (int)_all_types.size()) { + return _all_types[n]; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_num_global_functions +// Access: Public +// Description: Returns the total number of global functions known to +// the interrogate database. These are functions +// defined at the global level, e.g. non-member +// functions. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_num_global_functions() { + check_latest(); + return _global_functions.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_global_function +// Access: Public +// Description: Returns the index of the nth global function known to +// the interrogate database. +//////////////////////////////////////////////////////////////////// +FunctionIndex InterrogateDatabase:: +get_global_function(int n) { + check_latest(); + if (n >= 0 && n < (int)_global_functions.size()) { + return _global_functions[n]; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_num_all_functions +// Access: Public +// Description: Returns the total number of functions known to the +// interrogate database. This includes all known +// functions, global, method, or synthesized. See also +// get_num_global_functions(). +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_num_all_functions() { + check_latest(); + return _all_functions.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_all_function +// Access: Public +// Description: Returns the index of the nth function known to the +// interrogate database. +//////////////////////////////////////////////////////////////////// +FunctionIndex InterrogateDatabase:: +get_all_function(int n) { + check_latest(); + if (n >= 0 && n < (int)_all_functions.size()) { + return _all_functions[n]; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_num_global_manifests +// Access: Public +// Description: Returns the total number of global manifest constants +// known to the interrogate database. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_num_global_manifests() { + check_latest(); + return _global_manifests.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_global_manifest +// Access: Public +// Description: Returns the index of the nth global manifest constant +// known to the interrogate database. +//////////////////////////////////////////////////////////////////// +ManifestIndex InterrogateDatabase:: +get_global_manifest(int n) { + check_latest(); + if (n >= 0 && n < (int)_global_manifests.size()) { + return _global_manifests[n]; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_num_global_elements +// Access: Public +// Description: Returns the total number of global data elements +// known to the interrogate database. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_num_global_elements() { + check_latest(); + return _global_elements.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_global_element +// Access: Public +// Description: Returns the index of the nth global data element +// known to the interrogate database. +//////////////////////////////////////////////////////////////////// +ElementIndex InterrogateDatabase:: +get_global_element(int n) { + check_latest(); + if (n >= 0 && n < (int)_global_elements.size()) { + return _global_elements[n]; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_type +// Access: Public +// Description: Returns the type associated with the given TypeIndex, +// if there is one. +//////////////////////////////////////////////////////////////////// +const InterrogateType &InterrogateDatabase:: +get_type(TypeIndex type) { + static InterrogateType bogus_type; + + check_latest(); + TypeMap::const_iterator ti; + ti = _type_map.find(type); + if (ti == _type_map.end()) { + return bogus_type; + } + return (*ti).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_function +// Access: Public +// Description: Returns the function associated with the given +// FunctionIndex, if there is one. +//////////////////////////////////////////////////////////////////// +const InterrogateFunction &InterrogateDatabase:: +get_function(FunctionIndex function) { + static InterrogateFunction bogus_function; + + check_latest(); + FunctionMap::const_iterator fi; + fi = _function_map.find(function); + if (fi == _function_map.end()) { + return bogus_function; + } + return (*fi).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_wrapper +// Access: Public +// Description: Returns the function wrapper associated with the +// given FunctionWrapperIndex, if there is one. +//////////////////////////////////////////////////////////////////// +const InterrogateFunctionWrapper &InterrogateDatabase:: +get_wrapper(FunctionWrapperIndex wrapper) { + static InterrogateFunctionWrapper bogus_wrapper; + + check_latest(); + FunctionWrapperMap::const_iterator wi; + wi = _wrapper_map.find(wrapper); + if (wi == _wrapper_map.end()) { + return bogus_wrapper; + } + return (*wi).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_manifest +// Access: Public +// Description: Returns the manifest constant associated with the +// given ManifestIndex, if there is one. +//////////////////////////////////////////////////////////////////// +const InterrogateManifest &InterrogateDatabase:: +get_manifest(ManifestIndex manifest) { + static InterrogateManifest bogus_manifest; + + check_latest(); + ManifestMap::const_iterator mi; + mi = _manifest_map.find(manifest); + if (mi == _manifest_map.end()) { + return bogus_manifest; + } + return (*mi).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_element +// Access: Public +// Description: Returns the data element associated with the +// given ElementIndex, if there is one. +//////////////////////////////////////////////////////////////////// +const InterrogateElement &InterrogateDatabase:: +get_element(ElementIndex element) { + static InterrogateElement bogus_element; + + check_latest(); + ElementMap::const_iterator ei; + ei = _element_map.find(element); + if (ei == _element_map.end()) { + return bogus_element; + } + return (*ei).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::remove_type +// Access: Public +// Description: Erases the type from the database. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +remove_type(TypeIndex type) { + _type_map.erase(type); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_fptr +// Access: Public +// Description: Returns the function pointer associated with the +// given function wrapper, if it has a pointer +// available. Returns NULL if the pointer is not +// available. +//////////////////////////////////////////////////////////////////// +void *InterrogateDatabase:: +get_fptr(FunctionWrapperIndex wrapper) { + InterrogateModuleDef *def; + int module_index; + if (find_module(wrapper, def, module_index)) { + if (module_index >= 0 && module_index < def->num_fptrs) { + return def->fptrs[module_index]; + } + } + return (void *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_wrapper_by_unique_name +// Access: Public +// Description: Looks up the function wrapper corresponding to the +// given unique name, if available. Returns the +// corresponding wrapper index, or 0 if no such +// wrapper is found. +//////////////////////////////////////////////////////////////////// +FunctionWrapperIndex InterrogateDatabase:: +get_wrapper_by_unique_name(const string &unique_name) { + // First, split the unique_name into a library_hash_name and a + // wrapper_hash_name. + + // The first four characters are always the library_name. + string library_hash_name = unique_name.substr(0, 4); + string wrapper_hash_name = unique_name.substr(4); + + // Is the library_name defined? + ModulesByHash::const_iterator mi; + mi = _modules_by_hash.find(library_hash_name); + if (mi == _modules_by_hash.end()) { + return 0; + } + + InterrogateModuleDef *def = (*mi).second; + int index_offset = + binary_search_wrapper_hash(def->unique_names, + def->unique_names + def->num_unique_names, + wrapper_hash_name); + if (index_offset >= 0) { + return def->first_index + index_offset; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_file_major_version +// Access: Public +// Description: Returns the major version number of the interrogate +// database file currently being read. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_file_major_version() { + return _file_major_version; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_file_minor_version +// Access: Public +// Description: Returns the minor version number of the interrogate +// database file currently being read. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_file_minor_version() { + return _file_minor_version; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_current_major_version +// Access: Public +// Description: Returns the major version number currently expected +// in interrogate database files generated by this code +// base. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_current_major_version() { + return _current_major_version; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_current_minor_version +// Access: Public +// Description: Returns the minor version number currently expected +// in interrogate database files generated by this code +// base. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_current_minor_version() { + return _current_minor_version; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::get_next_index +// Access: Public +// Description: Returns a new index number suitable for the next +// thing, that will not be shared with any other index +// numbers. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +get_next_index() { + return _next_index++; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::add_type +// Access: Public +// Description: Adds the indicated type to the database at the given +// index number. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +add_type(TypeIndex index, const InterrogateType &type) { + bool inserted = + _type_map.insert(TypeMap::value_type(index, type)).second; + + if (!inserted) { + // If there was already a type at that index, maybe it was a + // forward reference. If its _fully_defined bit isn't set, then + // it's ok. + InterrogateType &old_type = _type_map[index]; + assert(!old_type.is_fully_defined()); + + // It was a forward reference. Merge it with the new one. + old_type.merge_with(type); + } + + if (type.is_global()) { + _global_types.push_back(index); + } + _all_types.push_back(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::add_function +// Access: Public +// Description: Adds the indicated function to the database at +// the given index number. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +add_function(FunctionIndex index, const InterrogateFunction &function) { + bool inserted = + _function_map.insert(FunctionMap::value_type(index, function)).second; + assert(inserted); + + if (function.is_global()) { + _global_functions.push_back(index); + } + _all_functions.push_back(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::add_wrapper +// Access: Public +// Description: Adds the indicated function wrapper to the database at +// the given index number. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +add_wrapper(FunctionWrapperIndex index, + const InterrogateFunctionWrapper &wrapper) { + bool inserted = + _wrapper_map.insert(FunctionWrapperMap::value_type(index, wrapper)).second; + assert(inserted); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::add_manifest +// Access: Public +// Description: Adds the indicated manifest constant to the database +// at the given index number. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +add_manifest(ManifestIndex index, const InterrogateManifest &manifest) { + bool inserted = + _manifest_map.insert(ManifestMap::value_type(index, manifest)).second; + assert(inserted); + + _global_manifests.push_back(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::add_element +// Access: Public +// Description: Adds the indicated data element to the database +// at the given index number. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +add_element(ElementIndex index, const InterrogateElement &element) { + bool inserted = + _element_map.insert(ElementMap::value_type(index, element)).second; + assert(inserted); + + if (element.is_global()) { + _global_elements.push_back(index); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::update_type +// Access: Public +// Description: Returns a non-const reference to the indicated type, +// allowing the user to update it. +//////////////////////////////////////////////////////////////////// +InterrogateType &InterrogateDatabase:: +update_type(TypeIndex type) { + check_latest(); + return _type_map[type]; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::update_function +// Access: Public +// Description: Returns a non-const reference to the indicated +// function, allowing the user to update it. +//////////////////////////////////////////////////////////////////// +InterrogateFunction &InterrogateDatabase:: +update_function(FunctionIndex function) { + check_latest(); + return _function_map[function]; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::update_wrapper +// Access: Public +// Description: Returns a non-const reference to the indicated +// function wrapper, allowing the user to update it. +//////////////////////////////////////////////////////////////////// +InterrogateFunctionWrapper &InterrogateDatabase:: +update_wrapper(FunctionWrapperIndex wrapper) { + check_latest(); + return _wrapper_map[wrapper]; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::update_manifest +// Access: Public +// Description: Returns a non-const reference to the indicated +// manifest constant, allowing the user to update it. +//////////////////////////////////////////////////////////////////// +InterrogateManifest &InterrogateDatabase:: +update_manifest(ManifestIndex manifest) { + check_latest(); + return _manifest_map[manifest]; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::update_element +// Access: Public +// Description: Returns a non-const reference to the indicated +// data element, allowing the user to update it. +//////////////////////////////////////////////////////////////////// +InterrogateElement &InterrogateDatabase:: +update_element(ElementIndex element) { + check_latest(); + return _element_map[element]; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::remap_indices +// Access: Public +// Description: Resequences all of the various index numbers so that +// all of the functions start at first_index and +// increment consecutively from there, and then all of +// the types follow. Returns the next available index +// number. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +remap_indices(int first_index) { + IndexRemapper remap; + return remap_indices(first_index, remap); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::remap_indices +// Access: Public +// Description: This flavor of remap_indices() accepts a map that +// should be empty on initial call, and will be filled +// with the mapping of old index number to new index +// number. This allows the caller to update its own +// data structures to match the new index numbers. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +remap_indices(int first_index, IndexRemapper &remap) { + remap.clear(); + + // First, build up the complete map. + + // Get the wrapper indices first. This is important, because the + // InterrogateBuilder wants these to be first, and consecutive. + FunctionWrapperMap new_wrapper_map; + FunctionWrapperMap::iterator wi; + for (wi = _wrapper_map.begin(); wi != _wrapper_map.end(); ++wi) { + remap.add_mapping((*wi).first, first_index); + new_wrapper_map[first_index] = (*wi).second; + first_index++; + } + + // Everything else can follow; it doesn't matter so much. + FunctionMap new_function_map; + FunctionMap::iterator fi; + for (fi = _function_map.begin(); fi != _function_map.end(); ++fi) { + remap.add_mapping((*fi).first, first_index); + new_function_map[first_index] = (*fi).second; + first_index++; + } + + TypeMap new_type_map; + TypeMap::iterator ti; + for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) { + remap.add_mapping((*ti).first, first_index); + new_type_map[first_index] = (*ti).second; + first_index++; + } + + ManifestMap new_manifest_map; + ManifestMap::iterator mi; + for (mi = _manifest_map.begin(); mi != _manifest_map.end(); ++mi) { + remap.add_mapping((*mi).first, first_index); + new_manifest_map[first_index] = (*mi).second; + first_index++; + } + + ElementMap new_element_map; + ElementMap::iterator ei; + for (ei = _element_map.begin(); ei != _element_map.end(); ++ei) { + remap.add_mapping((*ei).first, first_index); + new_element_map[first_index] = (*ei).second; + first_index++; + } + + _next_index = first_index; + + _wrapper_map.swap(new_wrapper_map); + _function_map.swap(new_function_map); + _type_map.swap(new_type_map); + _manifest_map.swap(new_manifest_map); + _element_map.swap(new_element_map); + + // Then, go back through and update all of the internal references. + for (wi = _wrapper_map.begin(); wi != _wrapper_map.end(); ++wi) { + (*wi).second.remap_indices(remap); + } + for (fi = _function_map.begin(); fi != _function_map.end(); ++fi) { + (*fi).second.remap_indices(remap); + } + for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) { + (*ti).second.remap_indices(remap); + } + for (mi = _manifest_map.begin(); mi != _manifest_map.end(); ++mi) { + (*mi).second.remap_indices(remap); + } + for (ei = _element_map.begin(); ei != _element_map.end(); ++ei) { + (*ei).second.remap_indices(remap); + } + GlobalFunctions::iterator gfi; + for (gfi = _global_functions.begin(); gfi != _global_functions.end(); ++gfi) { + (*gfi) = remap.map_from(*gfi); + } + for (gfi = _all_functions.begin(); gfi != _all_functions.end(); ++gfi) { + (*gfi) = remap.map_from(*gfi); + } + GlobalTypes::iterator gti; + for (gti = _global_types.begin(); gti != _global_types.end(); ++gti) { + (*gti) = remap.map_from(*gti); + } + for (gti = _all_types.begin(); gti != _all_types.end(); ++gti) { + (*gti) = remap.map_from(*gti); + } + GlobalManifests::iterator gmi; + for (gmi = _global_manifests.begin(); gmi != _global_manifests.end(); ++gmi) { + (*gmi) = remap.map_from(*gmi); + } + GlobalElements::iterator gei; + for (gei = _global_elements.begin(); gei != _global_elements.end(); ++gei) { + (*gei) = remap.map_from(*gei); + } + + return _next_index; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::write +// Access: Public +// Description: Writes the database to the indicated stream for later +// reading. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +write(ostream &out, InterrogateModuleDef *def) const { + // Write out the file header. + out << def->file_identifier << "\n" + << _current_major_version << " " << _current_minor_version << "\n"; + + // Write out the module definition. + idf_output_string(out, def->library_name); + idf_output_string(out, def->library_hash_name); + idf_output_string(out, def->module_name); + out << "\n"; + + // Now write out the components. + + out << _function_map.size() << "\n"; + FunctionMap::const_iterator fi; + for (fi = _function_map.begin(); fi != _function_map.end(); ++fi) { + out << (*fi).first << " " << (*fi).second << "\n"; + } + + out << _wrapper_map.size() << "\n"; + FunctionWrapperMap::const_iterator wi; + for (wi = _wrapper_map.begin(); wi != _wrapper_map.end(); ++wi) { + out << (*wi).first << " " << (*wi).second << "\n"; + } + + out << _type_map.size() << "\n"; + TypeMap::const_iterator ti; + for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) { + out << (*ti).first << " " << (*ti).second << "\n"; + } + + out << _manifest_map.size() << "\n"; + ManifestMap::const_iterator mi; + for (mi = _manifest_map.begin(); mi != _manifest_map.end(); ++mi) { + out << (*mi).first << " " << (*mi).second << "\n"; + } + + out << _element_map.size() << "\n"; + ElementMap::const_iterator ei; + for (ei = _element_map.begin(); ei != _element_map.end(); ++ei) { + out << (*ei).first << " " << (*ei).second << "\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::read +// Access: Public +// Description: Reads a database from the indicated stream, +// associated with the indicated module definition and +// merges it with any existing data in the database, +// according to the expected index numbers specified in +// the module def. The header information has already +// been read. +// +// Returns true if the file is read successfully, false +// if there is an error. +//////////////////////////////////////////////////////////////////// +bool InterrogateDatabase:: +read(istream &in, InterrogateModuleDef *def) { + InterrogateDatabase temp; + if (!temp.read_new(in, def)) { + return false; + } + + if (def->first_index == 0 && def->next_index == 0) { + _next_index = temp.remap_indices(_next_index); + + } else { + int next = temp.remap_indices(def->first_index); + if (next != def->next_index) { + interrogatedb_cat->error() + << "Module database file " << def->database_filename + << " is out of date.\n"; + return false; + } + } + + merge_from(temp); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::load_latest +// Access: Private +// Description: Reads in the latest interrogate data. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +load_latest() { + const DSearchPath &searchpath = get_interrogatedb_path(); + + Requests copy_requests; + copy_requests.swap(_requests); + + Requests::const_iterator ri; + for (ri = copy_requests.begin(); ri != copy_requests.end(); ++ri) { + InterrogateModuleDef *def = (*ri); + if (def->database_filename != (char *)NULL) { + Filename filename = def->database_filename; + Filename pathname = filename; + if (!pathname.empty() && pathname[0] != '/') { + pathname = searchpath.find_file(pathname); + } + + if (pathname.empty()) { + interrogatedb_cat->error() + << "Unable to find " << filename << " on " << searchpath << "\n"; + } else { + + ifstream input; + pathname.set_text(); + if (!pathname.open_read(input)) { + interrogatedb_cat->error() << "Unable to read " << pathname << ".\n"; + } else { + int file_identifier; + input >> file_identifier + >> _file_major_version >> _file_minor_version; + + if (def->file_identifier != 0 && + file_identifier != def->file_identifier) { + interrogatedb_cat->warning() + << "Interrogate data in " << pathname + << " is out of sync with the compiled-in data.\n"; + } + + if (_file_major_version != _current_major_version || + _file_minor_version > _current_minor_version) { + interrogatedb_cat->error() + << "Cannot read interrogate data in " << pathname + << "; database is version " << _file_major_version << "." + << _file_minor_version << " while we are expecting " + << _current_major_version << "." << _current_minor_version + << ".\n"; + } else { + if (interrogatedb_cat->is_debug()) { + interrogatedb_cat->debug() + << "Reading " << filename << "\n"; + } + if (!read(input, def)) { + interrogatedb_cat->error() + << "Error reading " << pathname << ".\n"; + } + } + } + } + } + } + + _requests.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::read_new +// Access: Private +// Description: Reads from the indicated stream (the header +// information has already been read) into the +// newly-created database. It is an error if the +// database already has some data in it. +//////////////////////////////////////////////////////////////////// +bool InterrogateDatabase:: +read_new(istream &in, InterrogateModuleDef *def) { + // We've already read the header. Read the module definition. + idf_input_string(in, def->library_name); + idf_input_string(in, def->library_hash_name); + idf_input_string(in, def->module_name); + + // Now read all of the components. + + { // Functions. + int num_functions; + in >> num_functions; + if (in.fail()) { + return false; + } + + while (num_functions > 0) { + FunctionIndex index; + InterrogateFunction function(def); + in >> index >> function; + if (in.fail()) { + return false; + } + + add_function(index, function); + num_functions--; + } + } + + { // Wrappers. + int num_wrappers; + in >> num_wrappers; + if (in.fail()) { + return false; + } + + while (num_wrappers > 0) { + FunctionWrapperIndex index; + InterrogateFunctionWrapper wrapper(def); + in >> index >> wrapper; + if (in.fail()) { + return false; + } + + add_wrapper(index, wrapper); + num_wrappers--; + } + } + + { // Types. + int num_types; + in >> num_types; + if (in.fail()) { + return false; + } + + while (num_types > 0) { + TypeIndex index; + InterrogateType type(def); + in >> index >> type; + if (in.fail()) { + return false; + } + + add_type(index, type); + num_types--; + } + } + + { // Manifests. + int num_manifests; + in >> num_manifests; + if (in.fail()) { + return false; + } + + while (num_manifests > 0) { + ManifestIndex index; + InterrogateManifest manifest(def); + in >> index >> manifest; + if (in.fail()) { + return false; + } + + add_manifest(index, manifest); + num_manifests--; + } + } + + { // Elements. + int num_elements; + in >> num_elements; + if (in.fail()) { + return false; + } + + while (num_elements > 0) { + ElementIndex index; + InterrogateElement element(def); + in >> index >> element; + if (in.fail()) { + return false; + } + + add_element(index, element); + num_elements--; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::merge_from +// Access: Private +// Description: Copies all the data from the indicated database into +// this one. It is an error if any index numbers are +// shared between the two databases. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +merge_from(const InterrogateDatabase &other) { + // We want to collapse shared types together. + IndexRemapper remap; + + // First, we need to build a set of types by name, so we know what + // types we already have. + map types_by_name; + + TypeMap::const_iterator ti; + for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) { + const InterrogateType &type = (*ti).second; + if (type.has_true_name()) { + types_by_name[type.get_true_name()] = (*ti).first; + } + } + + // Now go through the other set of types and determine the mapping + // into this set. + for (ti = other._type_map.begin(); ti != other._type_map.end(); ++ti) { + TypeIndex other_type_index = (*ti).first; + const InterrogateType &other_type = (*ti).second; + + if (other_type.has_name()) { + map::iterator ni; + ni = types_by_name.find(other_type.get_true_name()); + if (ni != types_by_name.end()) { + // Here's a type that we seem to have in common! We'll have + // to merge them. + TypeIndex this_type_index = (*ni).second; + remap.add_mapping(other_type_index, this_type_index); + } + } + } + + // Now that we know the full type-to-type mapping, we can copy the + // new types, one at a time. + for (ti = other._type_map.begin(); ti != other._type_map.end(); ++ti) { + TypeIndex other_type_index = (*ti).first; + const InterrogateType &other_type = (*ti).second; + + if (!remap.in_map(other_type_index)) { + // Here's a new type. + add_type(other_type_index, other_type); + update_type(other_type_index).remap_indices(remap); + + } else { + // Here's a type to merge. + TypeIndex this_type_index = remap.map_from(other_type_index); + + InterrogateType &this_type = update_type(this_type_index); + if (!this_type.is_global() && other_type.is_global()) { + // If the type is about to become global, we need to add it + // to our global_types list. + _global_types.push_back(this_type_index); + } + + InterrogateType merge_type = other_type; + merge_type.remap_indices(remap); + this_type.merge_with(merge_type); + } + } + + // And copy all of the functions, wrappers, manifests, and elements. + FunctionMap::const_iterator fi; + for (fi = other._function_map.begin(); + fi != other._function_map.end(); + ++fi) { + FunctionIndex other_function_index = (*fi).first; + const InterrogateFunction &other_function = (*fi).second; + add_function(other_function_index, other_function); + update_function(other_function_index).remap_indices(remap); + } + + FunctionWrapperMap::const_iterator wi; + for (wi = other._wrapper_map.begin(); + wi != other._wrapper_map.end(); + ++wi) { + FunctionWrapperIndex other_wrapper_index = (*wi).first; + const InterrogateFunctionWrapper &other_wrapper = (*wi).second; + add_wrapper(other_wrapper_index, other_wrapper); + update_wrapper(other_wrapper_index).remap_indices(remap); + } + + ManifestMap::const_iterator mi; + for (mi = other._manifest_map.begin(); + mi != other._manifest_map.end(); + ++mi) { + ManifestIndex other_manifest_index = (*mi).first; + const InterrogateManifest &other_manifest = (*mi).second; + add_manifest(other_manifest_index, other_manifest); + update_manifest(other_manifest_index).remap_indices(remap); + } + + ElementMap::const_iterator ei; + for (ei = other._element_map.begin(); + ei != other._element_map.end(); + ++ei) { + ElementIndex other_element_index = (*ei).first; + const InterrogateElement &other_element = (*ei).second; + add_element(other_element_index, other_element); + update_element(other_element_index).remap_indices(remap); + } + + _lookups_fresh = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::find_module +// Access: Private +// Description: Looks up the wrapper definition in the set of module +// defs that are loaded in at runtime and represent the +// part of the interrogate database that's compiled in. +// +// If the wrapper definition is not found, returns +// false. If it is found, returns true and sets def and +// module_index to the particular module and the index +// within the module where the wrapper is defined. +//////////////////////////////////////////////////////////////////// +bool InterrogateDatabase:: +find_module(FunctionWrapperIndex wrapper, InterrogateModuleDef *&def, + int &module_index) { + if (_modules.empty()) { + return false; + } + + int mi = binary_search_module(0, _modules.size(), wrapper); + assert(mi >= 0 && mi < (int)_modules.size()); + def = _modules[mi]; + module_index = wrapper - def->first_index; + + return (wrapper < def->next_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::binary_search_module +// Access: Private +// Description: Searches for the function module that includes the +// given function index by binary search. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +binary_search_module(int begin, int end, FunctionIndex function) { + int mid = begin + (end - begin) / 2; + if (mid == begin) { + return mid; + } + + int index = _modules[mid]->first_index; + if (index <= function) { + return binary_search_module(mid, end, function); + + } else { + return binary_search_module(begin, mid, function); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::binary_search_wrapper_hash +// Access: Private +// Description: Searches for the particular function wrapper's hash +// name within a given module. Returns the index number +// local to the module, or -1 if it is not found. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +binary_search_wrapper_hash(InterrogateUniqueNameDef *begin, + InterrogateUniqueNameDef *end, + const string &wrapper_hash_name) { + if (end <= begin) { + return -1; + } + + InterrogateUniqueNameDef *mid = begin + (end - begin) / 2; + string name = mid->name; + if (name < wrapper_hash_name) { + return binary_search_wrapper_hash(mid, end, wrapper_hash_name); + + } else if (wrapper_hash_name < name) { + return binary_search_wrapper_hash(begin, mid, wrapper_hash_name); + + } else { + return mid->index_offset; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::freshen_types_by_name +// Access: Private +// Description: Builds up the lookup of types by name. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +freshen_types_by_name() { + _types_by_name.clear(); + TypeMap::const_iterator ti; + for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) { + _types_by_name[(*ti).second.get_name()] = (*ti).first; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::freshen_types_by_scoped_name +// Access: Private +// Description: Builds up the lookup of types by scoped name. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +freshen_types_by_scoped_name() { + _types_by_scoped_name.clear(); + TypeMap::const_iterator ti; + for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) { + _types_by_scoped_name[(*ti).second.get_scoped_name()] = (*ti).first; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::freshen_types_by_true_name +// Access: Private +// Description: Builds up the lookup of types by true name. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +freshen_types_by_true_name() { + _types_by_true_name.clear(); + TypeMap::const_iterator ti; + for (ti = _type_map.begin(); ti != _type_map.end(); ++ti) { + _types_by_true_name[(*ti).second.get_true_name()] = (*ti).first; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::freshen_manifests_by_name +// Access: Private +// Description: Builds up the lookup of manifests by name. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +freshen_manifests_by_name() { + _manifests_by_name.clear(); + ManifestMap::const_iterator ti; + for (ti = _manifest_map.begin(); ti != _manifest_map.end(); ++ti) { + _manifests_by_name[(*ti).second.get_name()] = (*ti).first; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::freshen_elements_by_name +// Access: Private +// Description: Builds up the lookup of elements by name. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +freshen_elements_by_name() { + _elements_by_name.clear(); + ElementMap::const_iterator ti; + for (ti = _element_map.begin(); ti != _element_map.end(); ++ti) { + _elements_by_name[(*ti).second.get_name()] = (*ti).first; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::freshen_elements_by_scoped_name +// Access: Private +// Description: Builds up the lookup of elements by scoped name. +//////////////////////////////////////////////////////////////////// +void InterrogateDatabase:: +freshen_elements_by_scoped_name() { + _elements_by_scoped_name.clear(); + ElementMap::const_iterator ti; + for (ti = _element_map.begin(); ti != _element_map.end(); ++ti) { + _elements_by_scoped_name[(*ti).second.get_scoped_name()] = (*ti).first; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateDatabase::lookup +// Access: Private +// Description: Looks up a type, manifest, or element in the +// indicated lookup table by name. This is an internal +// support function. +//////////////////////////////////////////////////////////////////// +int InterrogateDatabase:: +lookup(const string &name, Lookup &lookup, LookupType type, + void (InterrogateDatabase::*freshen)()) { + if ((_lookups_fresh & (int)type) == 0) { + // The lookup table isn't fresh; we need to freshen it. + (this->*freshen)(); + _lookups_fresh |= (int)type; + } + + Lookup::const_iterator li; + li = lookup.find(name); + if (li != lookup.end()) { + return (*li).second; + } + return 0; +} diff --git a/dtool/src/interrogatedb/interrogateDatabase.h b/dtool/src/interrogatedb/interrogateDatabase.h new file mode 100644 index 0000000000..264a35ca3e --- /dev/null +++ b/dtool/src/interrogatedb/interrogateDatabase.h @@ -0,0 +1,187 @@ +// Filename: interrogateDatabase.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATEDATABASE_H +#define INTERROGATEDATABASE_H + +#include + +#include "interrogate_interface.h" +#include "interrogateType.h" +#include "interrogateFunction.h" +#include "interrogateFunctionWrapper.h" +#include "interrogateManifest.h" +#include "interrogateElement.h" +#include "interrogate_request.h" + +#include + +class IndexRemapper; + +//////////////////////////////////////////////////////////////////// +// Class : InterrogateDatabase +// Description : This stores all of the interrogate data and handles +// reading the data from a disk file when necessary. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL InterrogateDatabase { +private: + InterrogateDatabase(); + +public: + static InterrogateDatabase *get_ptr(); + void request_module(InterrogateModuleDef *def); + +public: + // Functions to read the database. + int get_num_global_types(); + TypeIndex get_global_type(int n); + int get_num_all_types(); + TypeIndex get_all_type(int n); + int get_num_global_functions(); + FunctionIndex get_global_function(int n); + int get_num_all_functions(); + FunctionIndex get_all_function(int n); + int get_num_global_manifests(); + ManifestIndex get_global_manifest(int n); + int get_num_global_elements(); + ElementIndex get_global_element(int n); + + const InterrogateType &get_type(TypeIndex type); + const InterrogateFunction &get_function(FunctionIndex function); + const InterrogateFunctionWrapper &get_wrapper(FunctionWrapperIndex wrapper); + const InterrogateManifest &get_manifest(ManifestIndex manifest); + const InterrogateElement &get_element(ElementIndex element); + + INLINE TypeIndex lookup_type_by_name(const string &name); + INLINE TypeIndex lookup_type_by_scoped_name(const string &name); + INLINE TypeIndex lookup_type_by_true_name(const string &name); + INLINE ManifestIndex lookup_manifest_by_name(const string &name); + INLINE ElementIndex lookup_element_by_name(const string &name); + INLINE ElementIndex lookup_element_by_scoped_name(const string &name); + + void remove_type(TypeIndex type); + + void *get_fptr(FunctionWrapperIndex wrapper); + + FunctionWrapperIndex get_wrapper_by_unique_name(const string &unique_name); + + static int get_file_major_version(); + static int get_file_minor_version(); + static int get_current_major_version(); + static int get_current_minor_version(); + +public: + // Functions to build the database. + int get_next_index(); + void add_type(TypeIndex index, const InterrogateType &type); + void add_function(FunctionIndex index, const InterrogateFunction &function); + void add_wrapper(FunctionWrapperIndex index, + const InterrogateFunctionWrapper &wrapper); + void add_manifest(ManifestIndex index, const InterrogateManifest &manifest); + void add_element(ElementIndex index, const InterrogateElement &element); + + InterrogateType &update_type(TypeIndex type); + InterrogateFunction &update_function(FunctionIndex function); + InterrogateFunctionWrapper &update_wrapper(FunctionWrapperIndex wrapper); + InterrogateManifest &update_manifest(ManifestIndex manifest); + InterrogateElement &update_element(ElementIndex element); + + int remap_indices(int first_index); + int remap_indices(int first_index, IndexRemapper &remap); + + void write(ostream &out, InterrogateModuleDef *def) const; + bool read(istream &in, InterrogateModuleDef *def); + +private: + INLINE void check_latest(); + void load_latest(); + + bool read_new(istream &in, InterrogateModuleDef *def); + void merge_from(const InterrogateDatabase &other); + + bool find_module(FunctionWrapperIndex wrapper, + InterrogateModuleDef *&def, int &module_index); + int binary_search_module(int begin, int end, FunctionIndex function); + int binary_search_wrapper_hash(InterrogateUniqueNameDef *begin, + InterrogateUniqueNameDef *end, + const string &wrapper_hash_name); + + // This data is loaded from the various database files. + typedef map TypeMap; + TypeMap _type_map; + typedef map FunctionMap; + FunctionMap _function_map; + typedef map FunctionWrapperMap; + FunctionWrapperMap _wrapper_map; + + typedef map ManifestMap; + ManifestMap _manifest_map; + typedef map ElementMap; + ElementMap _element_map; + + typedef vector GlobalTypes; + GlobalTypes _global_types; + GlobalTypes _all_types; + typedef vector GlobalFunctions; + GlobalFunctions _global_functions; + GlobalFunctions _all_functions; + typedef vector GlobalManifests; + GlobalManifests _global_manifests; + typedef vector GlobalElements; + GlobalElements _global_elements; + + // This data is compiled in directly to the shared libraries that we + // link with. + typedef vector Modules; + Modules _modules; + typedef map ModulesByHash; + ModulesByHash _modules_by_hash; + + // This records the set of database files that are still to be + // loaded. + typedef vector Requests; + Requests _requests; + + int _next_index; + + enum LookupType { + LT_type_name = 0x001, + LT_type_scoped_name = 0x002, + LT_type_true_name = 0x004, + LT_manifest_name = 0x008, + LT_element_name = 0x010, + LT_element_scoped_name = 0x020, + }; + + int _lookups_fresh; + typedef map Lookup; + Lookup _types_by_name; + Lookup _types_by_scoped_name; + Lookup _types_by_true_name; + Lookup _manifests_by_name; + Lookup _elements_by_name; + Lookup _elements_by_scoped_name; + + void freshen_types_by_name(); + void freshen_types_by_scoped_name(); + void freshen_types_by_true_name(); + void freshen_manifests_by_name(); + void freshen_elements_by_name(); + void freshen_elements_by_scoped_name(); + + int lookup(const string &name, + Lookup &lookup, LookupType type, + void (InterrogateDatabase::*freshen)()); + + static InterrogateDatabase *_global_ptr; + static int _file_major_version; + static int _file_minor_version; + static int _current_major_version; + static int _current_minor_version; +}; + +#include "interrogateDatabase.I" + +#endif diff --git a/dtool/src/interrogatedb/interrogateElement.I b/dtool/src/interrogatedb/interrogateElement.I new file mode 100644 index 0000000000..63eb724c2b --- /dev/null +++ b/dtool/src/interrogatedb/interrogateElement.I @@ -0,0 +1,140 @@ +// Filename: interrogateElement.I +// Created by: drose (11Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Element: InterrogateElement::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateElement:: +InterrogateElement(InterrogateModuleDef *def) : + InterrogateComponent(def) +{ + _flags = 0; + _type = 0; + _getter = 0; + _setter = 0; +} + +//////////////////////////////////////////////////////////////////// +// Element: InterrogateElement::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateElement:: +InterrogateElement(const InterrogateElement ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Element: InterrogateElement::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void InterrogateElement:: +operator = (const InterrogateElement ©) { + InterrogateComponent::operator = (copy); + _flags = copy._flags; + _scoped_name = copy._scoped_name; + _type = copy._type; + _getter = copy._getter; + _setter = copy._setter; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateElement::is_global +// Access: Public +// Description: Returns true if the element is marked as 'global'. +// This means only that it should appear in the global +// element list. +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateElement:: +is_global() const { + return (_flags & F_global) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateElement::has_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateElement:: +has_scoped_name() const { + return !_scoped_name.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateElement::get_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateElement:: +get_scoped_name() const { + return _scoped_name; +} + +//////////////////////////////////////////////////////////////////// +// Element: InterrogateElement::get_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateElement:: +get_type() const { + return _type; +} + +//////////////////////////////////////////////////////////////////// +// Element: InterrogateElement::has_getter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateElement:: +has_getter() const { + return (_flags & F_has_getter) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Element: InterrogateElement::get_getter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateElement:: +get_getter() const { + return _getter; +} + +//////////////////////////////////////////////////////////////////// +// Element: InterrogateElement::has_setter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateElement:: +has_setter() const { + return (_flags & F_has_setter) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Element: InterrogateElement::get_setter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateElement:: +get_setter() const { + return _setter; +} + + +INLINE ostream & +operator << (ostream &out, const InterrogateElement &element) { + element.output(out); + return out; +} + +INLINE istream & +operator >> (istream &in, InterrogateElement &element) { + element.input(in); + return in; +} diff --git a/dtool/src/interrogatedb/interrogateElement.cxx b/dtool/src/interrogatedb/interrogateElement.cxx new file mode 100644 index 0000000000..5a90c37795 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateElement.cxx @@ -0,0 +1,51 @@ +// Filename: interrogateElement.C +// Created by: drose (11Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogateElement.h" +#include "indexRemapper.h" +#include "interrogate_datafile.h" + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateElement::output +// Access: Public +// Description: Formats the InterrogateElement data for output to a data +// file. +//////////////////////////////////////////////////////////////////// +void InterrogateElement:: +output(ostream &out) const { + InterrogateComponent::output(out); + out << _flags << " " + << _type << " " + << _getter << " " + << _setter << " "; + idf_output_string(out, _scoped_name); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateElement::input +// Access: Public +// Description: Reads the data file as previously formatted by +// output(). +//////////////////////////////////////////////////////////////////// +void InterrogateElement:: +input(istream &in) { + InterrogateComponent::input(in); + in >> _flags >> _type >> _getter >> _setter; + idf_input_string(in, _scoped_name); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateElement::remap_indices +// Access: Public +// Description: Remaps all internal index numbers according to the +// indicated map. This called from +// InterrogateDatabase::remap_indices(). +//////////////////////////////////////////////////////////////////// +void InterrogateElement:: +remap_indices(const IndexRemapper &remap) { + _type = remap.map_from(_type); + _getter = remap.map_from(_getter); + _setter = remap.map_from(_setter); +} diff --git a/dtool/src/interrogatedb/interrogateElement.h b/dtool/src/interrogatedb/interrogateElement.h new file mode 100644 index 0000000000..29020436ee --- /dev/null +++ b/dtool/src/interrogatedb/interrogateElement.h @@ -0,0 +1,63 @@ +// Filename: interrogateElement.h +// Created by: drose (11Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATEELEMENT_H +#define INTERROGATEELEMENT_H + +#include + +#include "interrogateComponent.h" + +class IndexRemapper; + +//////////////////////////////////////////////////////////////////// +// Class : InterrogateElement +// Description : An internal representation of a data element, like a +// data member or a global variable. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL InterrogateElement : public InterrogateComponent { +public: + INLINE InterrogateElement(InterrogateModuleDef *def = NULL); + INLINE InterrogateElement(const InterrogateElement ©); + INLINE void operator = (const InterrogateElement ©); + + INLINE bool is_global() const; + + INLINE bool has_scoped_name() const; + INLINE const string &get_scoped_name() const; + + INLINE TypeIndex get_type() const; + INLINE bool has_getter() const; + INLINE FunctionIndex get_getter() const; + INLINE bool has_setter() const; + INLINE FunctionIndex get_setter() const; + + void output(ostream &out) const; + void input(istream &in); + + void remap_indices(const IndexRemapper &remap); + +private: + enum Flags { + F_global = 0x0001, + F_has_getter = 0x0002, + F_has_setter = 0x0004 + }; + + int _flags; + string _scoped_name; + TypeIndex _type; + FunctionIndex _getter; + FunctionIndex _setter; + + friend class InterrogateBuilder; +}; + +INLINE ostream &operator << (ostream &out, const InterrogateElement &element); +INLINE istream &operator >> (istream &in, InterrogateElement &element); + +#include "interrogateElement.I" + +#endif diff --git a/dtool/src/interrogatedb/interrogateFunction.I b/dtool/src/interrogatedb/interrogateFunction.I new file mode 100644 index 0000000000..b45b29646e --- /dev/null +++ b/dtool/src/interrogatedb/interrogateFunction.I @@ -0,0 +1,208 @@ +// Filename: interrogateFunction.I +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateFunction:: +InterrogateFunction(InterrogateModuleDef *def) : + InterrogateComponent(def) +{ + _flags = 0; + _class = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateFunction:: +InterrogateFunction(const InterrogateFunction ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void InterrogateFunction:: +operator = (const InterrogateFunction ©) { + InterrogateComponent::operator = (copy); + _flags = copy._flags; + _scoped_name = copy._scoped_name; + _comment = copy._comment; + _prototype = copy._prototype; + _class = copy._class; + _c_wrappers = copy._c_wrappers; + _python_wrappers = copy._python_wrappers; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::is_global +// Access: Public +// Description: Returns true if the function is marked as 'global'. +// This means only that it should appear in the global +// function list. +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunction:: +is_global() const { + return (_flags & F_global) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::is_virtual +// Access: Public +// Description: Returns true if the function is virtual, for whatever +// that's worth. +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunction:: +is_virtual() const { + return (_flags & F_virtual) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::is_method +// Access: Public +// Description: Returns true if the function is a class method. +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunction:: +is_method() const { + return (_flags & F_method) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::get_class +// Access: Public +// Description: Return the class that owns the method, if is_method() +// returns true. +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateFunction:: +get_class() const { + return _class; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::has_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunction:: +has_scoped_name() const { + return !_scoped_name.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::get_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateFunction:: +get_scoped_name() const { + return _scoped_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::has_comment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunction:: +has_comment() const { + return !_comment.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::get_comment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateFunction:: +get_comment() const { + return _comment; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::has_prototype +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunction:: +has_prototype() const { + return !_prototype.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::get_prototype +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateFunction:: +get_prototype() const { + return _prototype; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::number_of_c_wrappers +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateFunction:: +number_of_c_wrappers() const { + return _c_wrappers.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::get_c_wrapper +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionWrapperIndex InterrogateFunction:: +get_c_wrapper(int n) const { + if (n >= 0 && n < (int)_c_wrappers.size()) { + return _c_wrappers[n]; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::number_of_python_wrappers +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateFunction:: +number_of_python_wrappers() const { + return _python_wrappers.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::get_python_wrapper +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionWrapperIndex InterrogateFunction:: +get_python_wrapper(int n) const { + if (n >= 0 && n < (int)_python_wrappers.size()) { + return _python_wrappers[n]; + } + return 0; +} + + +INLINE ostream & +operator << (ostream &out, const InterrogateFunction &function) { + function.output(out); + return out; +} + +INLINE istream & +operator >> (istream &in, InterrogateFunction &function) { + function.input(in); + return in; +} diff --git a/dtool/src/interrogatedb/interrogateFunction.cxx b/dtool/src/interrogatedb/interrogateFunction.cxx new file mode 100644 index 0000000000..6ca4ac2458 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateFunction.cxx @@ -0,0 +1,66 @@ +// Filename: interrogateFunction.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogateFunction.h" +#include "indexRemapper.h" +#include "interrogate_datafile.h" +#include "interrogateDatabase.h" + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::output +// Access: Public +// Description: Formats the InterrogateFunction data for output to a data +// file. +//////////////////////////////////////////////////////////////////// +void InterrogateFunction:: +output(ostream &out) const { + InterrogateComponent::output(out); + out << _flags << " " + << _class << " "; + idf_output_string(out, _scoped_name); + idf_output_vector(out, _c_wrappers); + idf_output_vector(out, _python_wrappers); + idf_output_string(out, _comment, '\n'); + idf_output_string(out, _prototype, '\n'); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::input +// Access: Public +// Description: Reads the data file as previously formatted by +// output(). +//////////////////////////////////////////////////////////////////// +void InterrogateFunction:: +input(istream &in) { + InterrogateComponent::input(in); + in >> _flags >> _class; + idf_input_string(in, _scoped_name); + idf_input_vector(in, _c_wrappers); + idf_input_vector(in, _python_wrappers); + idf_input_string(in, _comment); + + if (InterrogateDatabase::get_file_minor_version() >= 2) { + idf_input_string(in, _prototype); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunction::remap_indices +// Access: Public +// Description: Remaps all internal index numbers according to the +// indicated map. This called from +// InterrogateDatabase::remap_indices(). +//////////////////////////////////////////////////////////////////// +void InterrogateFunction:: +remap_indices(const IndexRemapper &remap) { + _class = remap.map_from(_class); + Wrappers::iterator wi; + for (wi = _c_wrappers.begin(); wi != _c_wrappers.end(); ++wi) { + (*wi) = remap.map_from(*wi); + } + for (wi = _python_wrappers.begin(); wi != _python_wrappers.end(); ++wi) { + (*wi) = remap.map_from(*wi); + } +} diff --git a/dtool/src/interrogatedb/interrogateFunction.h b/dtool/src/interrogatedb/interrogateFunction.h new file mode 100644 index 0000000000..2cdb64137c --- /dev/null +++ b/dtool/src/interrogatedb/interrogateFunction.h @@ -0,0 +1,76 @@ +// Filename: interrogateFunction.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATEFUNCTION_H +#define INTERROGATEFUNCTION_H + +#include + +#include "interrogateComponent.h" + +#include + +class IndexRemapper; + +//////////////////////////////////////////////////////////////////// +// Class : InterrogateFunction +// Description : An internal representation of a function. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL InterrogateFunction : public InterrogateComponent { +public: + INLINE InterrogateFunction(InterrogateModuleDef *def = NULL); + INLINE InterrogateFunction(const InterrogateFunction ©); + INLINE void operator = (const InterrogateFunction ©); + + INLINE bool is_global() const; + INLINE bool is_virtual() const; + INLINE bool is_method() const; + INLINE TypeIndex get_class() const; + + INLINE bool has_scoped_name() const; + INLINE const string &get_scoped_name() const; + + INLINE bool has_comment() const; + INLINE const string &get_comment() const; + + INLINE bool has_prototype() const; + INLINE const string &get_prototype() const; + + INLINE int number_of_c_wrappers() const; + INLINE FunctionWrapperIndex get_c_wrapper(int n) const; + + INLINE int number_of_python_wrappers() const; + INLINE FunctionWrapperIndex get_python_wrapper(int n) const; + + void output(ostream &out) const; + void input(istream &in); + + void remap_indices(const IndexRemapper &remap); + +private: + enum Flags { + F_global = 0x0001, + F_virtual = 0x0002, + F_method = 0x0004 + }; + + int _flags; + string _scoped_name; + string _comment; + string _prototype; + TypeIndex _class; + + typedef vector Wrappers; + Wrappers _c_wrappers; + Wrappers _python_wrappers; + friend class InterrogateBuilder; +}; + +INLINE ostream &operator << (ostream &out, const InterrogateFunction &function); +INLINE istream &operator >> (istream &in, InterrogateFunction &function); + +#include "interrogateFunction.I" + +#endif diff --git a/dtool/src/interrogatedb/interrogateFunctionWrapper.I b/dtool/src/interrogatedb/interrogateFunctionWrapper.I new file mode 100644 index 0000000000..80115ae22b --- /dev/null +++ b/dtool/src/interrogatedb/interrogateFunctionWrapper.I @@ -0,0 +1,205 @@ +// Filename: interrogateFunctionWrapper.I +// Created by: drose (06Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateFunctionWrapper:: +InterrogateFunctionWrapper(InterrogateModuleDef *def) : + InterrogateComponent(def) +{ + _flags = 0; + _function = 0; + _return_type = 0; + _return_value_destructor = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateFunctionWrapper:: +InterrogateFunctionWrapper(const InterrogateFunctionWrapper ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void InterrogateFunctionWrapper:: +operator = (const InterrogateFunctionWrapper ©) { + InterrogateComponent::operator = (copy); + _flags = copy._flags; + _function = copy._function; + _return_type = copy._return_type; + _return_value_destructor = copy._return_value_destructor; + _unique_name = copy._unique_name; + _parameters = copy._parameters; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::get_function +// Access: Public +// Description: Returns the FunctionIndex of the function that this +// wrapper corresponds to. +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateFunctionWrapper:: +get_function() const { + return _function; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::is_callable_by_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunctionWrapper:: +is_callable_by_name() const { + return (_flags & F_callable_by_name) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::has_return_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunctionWrapper:: +has_return_value() const { + return (_flags & F_has_return) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::get_return_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateFunctionWrapper:: +get_return_type() const { + return _return_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::caller_manages_return_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunctionWrapper:: +caller_manages_return_value() const { + return (_flags & F_caller_manages) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::get_return_value_destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateFunctionWrapper:: +get_return_value_destructor() const { + return _return_value_destructor; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::number_of_parameters +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateFunctionWrapper:: +number_of_parameters() const { + return _parameters.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::parameter_get_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateFunctionWrapper:: +parameter_get_type(int n) const { + if (n >= 0 && n < (int)_parameters.size()) { + return _parameters[n]._type; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::parameter_has_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunctionWrapper:: +parameter_has_name(int n) const { + if (n >= 0 && n < (int)_parameters.size()) { + return (_parameters[n]._parameter_flags & PF_has_name) != 0; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::parameter_get_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateFunctionWrapper:: +parameter_get_name(int n) const { + static string bogus_string; + if (n >= 0 && n < (int)_parameters.size()) { + return _parameters[n]._name; + } + return bogus_string; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::parameter_is_this +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateFunctionWrapper:: +parameter_is_this(int n) const { + if (n >= 0 && n < (int)_parameters.size()) { + return (_parameters[n]._parameter_flags & PF_is_this) != 0; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::get_unique_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateFunctionWrapper:: +get_unique_name() const { + return _unique_name; +} + +INLINE ostream & +operator << (ostream &out, const InterrogateFunctionWrapper &wrapper) { + wrapper.output(out); + return out; +} + +INLINE istream & +operator >> (istream &in, InterrogateFunctionWrapper &wrapper) { + wrapper.input(in); + return in; +} + +INLINE ostream & +operator << (ostream &out, const InterrogateFunctionWrapper::Parameter &p) { + p.output(out); + return out; +} + +INLINE istream & +operator >> (istream &in, InterrogateFunctionWrapper::Parameter &p) { + p.input(in); + return in; +} + diff --git a/dtool/src/interrogatedb/interrogateFunctionWrapper.cxx b/dtool/src/interrogatedb/interrogateFunctionWrapper.cxx new file mode 100644 index 0000000000..9221583029 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateFunctionWrapper.cxx @@ -0,0 +1,84 @@ +// Filename: interrogateFunctionWrapper.C +// Created by: drose (06Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogateFunctionWrapper.h" +#include "indexRemapper.h" +#include "interrogate_datafile.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::Parameter::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void InterrogateFunctionWrapper::Parameter:: +output(ostream &out) const { + idf_output_string(out, _name); + out << _parameter_flags << " " << _type << " "; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::Parameter::input +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void InterrogateFunctionWrapper::Parameter:: +input(istream &in) { + idf_input_string(in, _name); + in >> _parameter_flags >> _type; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::output +// Access: Public +// Description: Formats the InterrogateFunctionWrapper data for +// output to a data file. +//////////////////////////////////////////////////////////////////// +void InterrogateFunctionWrapper:: +output(ostream &out) const { + InterrogateComponent::output(out); + out << _flags << " " + << _function << " " + << _return_type << " " + << _return_value_destructor << " "; + idf_output_string(out, _unique_name); + idf_output_vector(out, _parameters); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::input +// Access: Public +// Description: Reads the data file as previously formatted by +// output(). +//////////////////////////////////////////////////////////////////// +void InterrogateFunctionWrapper:: +input(istream &in) { + InterrogateComponent::input(in); + in >> _flags + >> _function + >> _return_type + >> _return_value_destructor; + idf_input_string(in, _unique_name); + idf_input_vector(in, _parameters); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateFunctionWrapper::remap_indices +// Access: Public +// Description: Remaps all internal index numbers according to the +// indicated map. This called from +// InterrogateDatabase::remap_indices(). +//////////////////////////////////////////////////////////////////// +void InterrogateFunctionWrapper:: +remap_indices(const IndexRemapper &remap) { + _return_value_destructor = remap.map_from(_return_value_destructor); + _return_type = remap.map_from(_return_type); + + Parameters::iterator pi; + for (pi = _parameters.begin(); pi != _parameters.end(); ++pi) { + (*pi)._type = remap.map_from((*pi)._type); + } +} diff --git a/dtool/src/interrogatedb/interrogateFunctionWrapper.h b/dtool/src/interrogatedb/interrogateFunctionWrapper.h new file mode 100644 index 0000000000..41a53dd3fb --- /dev/null +++ b/dtool/src/interrogatedb/interrogateFunctionWrapper.h @@ -0,0 +1,96 @@ +// Filename: interrogateFunctionWrapper.h +// Created by: drose (06Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATEFUNCTIONWRAPPER_H +#define INTERROGATEFUNCTIONWRAPPER_H + +#include + +#include "interrogateComponent.h" + +#include + +class IndexRemapper; + +//////////////////////////////////////////////////////////////////// +// Class : InterrogateFunctionWrapper +// Description : An internal representation of a function. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL InterrogateFunctionWrapper : public InterrogateComponent { +public: + INLINE InterrogateFunctionWrapper(InterrogateModuleDef *def = NULL); + INLINE InterrogateFunctionWrapper(const InterrogateFunctionWrapper ©); + INLINE void operator = (const InterrogateFunctionWrapper ©); + + INLINE FunctionIndex get_function() const; + + INLINE bool is_callable_by_name() const; + + INLINE bool has_return_value() const; + INLINE TypeIndex get_return_type() const; + INLINE bool caller_manages_return_value() const; + INLINE FunctionIndex get_return_value_destructor() const; + + INLINE int number_of_parameters() const; + INLINE TypeIndex parameter_get_type(int n) const; + INLINE bool parameter_has_name(int n) const; + INLINE const string ¶meter_get_name(int n) const; + INLINE bool parameter_is_this(int n) const; + + INLINE const string &get_unique_name() const; + + void output(ostream &out) const; + void input(istream &in); + + void remap_indices(const IndexRemapper &remap); + +private: + enum Flags { + F_caller_manages = 0x0001, + F_has_return = 0x0002, + F_callable_by_name = 0x0004 + }; + + enum ParameterFlags { + PF_has_name = 0x0001, + PF_is_this = 0x0002, + }; + + int _flags; + FunctionIndex _function; + TypeIndex _return_type; + FunctionIndex _return_value_destructor; + string _unique_name; + +public: + // This nested class must be declared public just so we can declare + // the external ostream and istream I/O operator functions, on the + // SGI compiler. Arguably a compiler bug, but what can you do. + class Parameter { + public: + void output(ostream &out) const; + void input(istream &in); + + int _parameter_flags; + TypeIndex _type; + string _name; + }; + +private: + typedef vector Parameters; + Parameters _parameters; + + friend class InterrogateBuilder; +}; + +INLINE ostream &operator << (ostream &out, const InterrogateFunctionWrapper &wrapper); +INLINE istream &operator >> (istream &in, InterrogateFunctionWrapper &wrapper); + +INLINE ostream &operator << (ostream &out, const InterrogateFunctionWrapper::Parameter &p); +INLINE istream &operator >> (istream &in, InterrogateFunctionWrapper::Parameter &p); + +#include "interrogateFunctionWrapper.I" + +#endif diff --git a/dtool/src/interrogatedb/interrogateManifest.I b/dtool/src/interrogatedb/interrogateManifest.I new file mode 100644 index 0000000000..9b664bf465 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateManifest.I @@ -0,0 +1,129 @@ +// Filename: interrogateManifest.I +// Created by: drose (11Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateManifest:: +InterrogateManifest(InterrogateModuleDef *def) : + InterrogateComponent(def) +{ + _flags = 0; + _int_value = 0; + _type = 0; + _getter = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateManifest:: +InterrogateManifest(const InterrogateManifest ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void InterrogateManifest:: +operator = (const InterrogateManifest ©) { + InterrogateComponent::operator = (copy); + _flags = copy._flags; + _definition = copy._definition; + _int_value = copy._int_value; + _type = copy._type; + _getter = copy._getter; +} + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::get_definition +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateManifest:: +get_definition() const { + return _definition; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::has_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateManifest:: +has_type() const { + return (_flags & F_has_type) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::get_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateManifest:: +get_type() const { + return _type; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::has_getter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateManifest:: +has_getter() const { + return (_flags & F_has_getter) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::get_getter +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateManifest:: +get_getter() const { + return _getter; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::has_int_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateManifest:: +has_int_value() const { + return (_flags & F_has_int_value) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::get_int_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateManifest:: +get_int_value() const { + return _int_value; +} + + +INLINE ostream & +operator << (ostream &out, const InterrogateManifest &manifest) { + manifest.output(out); + return out; +} + +INLINE istream & +operator >> (istream &in, InterrogateManifest &manifest) { + manifest.input(in); + return in; +} diff --git a/dtool/src/interrogatedb/interrogateManifest.cxx b/dtool/src/interrogatedb/interrogateManifest.cxx new file mode 100644 index 0000000000..1b30eb851a --- /dev/null +++ b/dtool/src/interrogatedb/interrogateManifest.cxx @@ -0,0 +1,50 @@ +// Filename: interrogateManifest.C +// Created by: drose (11Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogateManifest.h" +#include "indexRemapper.h" +#include "interrogate_datafile.h" + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::output +// Access: Public +// Description: Formats the InterrogateManifest data for output to a data +// file. +//////////////////////////////////////////////////////////////////// +void InterrogateManifest:: +output(ostream &out) const { + InterrogateComponent::output(out); + out << _flags << " " + << _int_value << " " + << _type << " " + << _getter << " "; + idf_output_string(out, _definition); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::input +// Access: Public +// Description: Reads the data file as previously formatted by +// output(). +//////////////////////////////////////////////////////////////////// +void InterrogateManifest:: +input(istream &in) { + InterrogateComponent::input(in); + in >> _flags >> _int_value >> _type >> _getter; + idf_input_string(in, _definition); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateManifest::remap_indices +// Access: Public +// Description: Remaps all internal index numbers according to the +// indicated map. This called from +// InterrogateDatabase::remap_indices(). +//////////////////////////////////////////////////////////////////// +void InterrogateManifest:: +remap_indices(const IndexRemapper &remap) { + _type = remap.map_from(_type); + _getter = remap.map_from(_getter); +} diff --git a/dtool/src/interrogatedb/interrogateManifest.h b/dtool/src/interrogatedb/interrogateManifest.h new file mode 100644 index 0000000000..296c832d5c --- /dev/null +++ b/dtool/src/interrogatedb/interrogateManifest.h @@ -0,0 +1,59 @@ +// Filename: interrogateManifest.h +// Created by: drose (11Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATEMANIFEST_H +#define INTERROGATEMANIFEST_H + +#include + +#include "interrogateComponent.h" + +class IndexRemapper; + +//////////////////////////////////////////////////////////////////// +// Class : InterrogateManifest +// Description : An internal representation of a manifest constant. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL InterrogateManifest : public InterrogateComponent { +public: + INLINE InterrogateManifest(InterrogateModuleDef *def = NULL); + INLINE InterrogateManifest(const InterrogateManifest ©); + INLINE void operator = (const InterrogateManifest ©); + + INLINE const string &get_definition() const; + INLINE bool has_type() const; + INLINE TypeIndex get_type() const; + INLINE bool has_getter() const; + INLINE FunctionIndex get_getter() const; + INLINE bool has_int_value() const; + INLINE int get_int_value() const; + + void output(ostream &out) const; + void input(istream &in); + + void remap_indices(const IndexRemapper &remap); + +private: + enum Flags { + F_has_type = 0x0001, + F_has_getter = 0x0002, + F_has_int_value = 0x0004 + }; + + int _flags; + string _definition; + int _int_value; + TypeIndex _type; + FunctionIndex _getter; + + friend class InterrogateBuilder; +}; + +INLINE ostream &operator << (ostream &out, const InterrogateManifest &manifest); +INLINE istream &operator >> (istream &in, InterrogateManifest &manifest); + +#include "interrogateManifest.I" + +#endif diff --git a/dtool/src/interrogatedb/interrogateType.I b/dtool/src/interrogatedb/interrogateType.I new file mode 100644 index 0000000000..b3f7ea158e --- /dev/null +++ b/dtool/src/interrogatedb/interrogateType.I @@ -0,0 +1,628 @@ +// Filename: interrogateType.I +// Created by: drose (31Jul00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateType:: +InterrogateType(InterrogateModuleDef *def) : + InterrogateComponent(def) +{ + _flags = 0; + _outer_class = 0; + _atomic_token = AT_not_atomic; + _wrapped_type = 0; + _destructor = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE InterrogateType:: +InterrogateType(const InterrogateType ©) { + (*this) = copy; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_global +// Access: Public +// Description: Returns true if the type is marked as 'global'. This +// means only that it should appear in the global type +// list. +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_global() const { + return (_flags & F_global) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::has_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +has_scoped_name() const { + return !_scoped_name.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateType:: +get_scoped_name() const { + return _scoped_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::has_true_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +has_true_name() const { + return !_true_name.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_true_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateType:: +get_true_name() const { + return _true_name; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::has_comment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +has_comment() const { + return !_comment.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_comment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateType:: +get_comment() const { + return _comment; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_nested +// Access: Public +// Description: Returns true if this type is nested within some class +// definition. +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_nested() const { + return (_flags & F_nested) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_outer_class +// Access: Public +// Description: If is_nested() returns true, this is the class within +// which this type is defined. +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateType:: +get_outer_class() const { + return _outer_class; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_atomic +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_atomic() const { + return (_flags & F_atomic) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_atomic_token +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE AtomicToken InterrogateType:: +get_atomic_token() const { + return _atomic_token; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_unsigned +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_unsigned() const { + return (_flags & F_unsigned) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_signed +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_signed() const { + return (_flags & F_signed) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_long +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_long() const { + return (_flags & F_long) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_longlong +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_longlong() const { + return (_flags & F_longlong) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_short +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_short() const { + return (_flags & F_short) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_wrapped +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_wrapped() const { + return (_flags & F_wrapped) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_pointer +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_pointer() const { + return (_flags & F_pointer) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_const +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_const() const { + return (_flags & F_const) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_wrapped_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateType:: +get_wrapped_type() const { + return _wrapped_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_enum +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_enum() const { + return (_flags & F_enum) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::number_of_enum_values +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +number_of_enum_values() const { + return _enum_values.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_enum_value_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateType:: +get_enum_value_name(int n) const { + if (n >= 0 && n < (int)_enum_values.size()) { + return _enum_values[n]._name; + } + return _empty_string; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_enum_value_scoped_name +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE const string &InterrogateType:: +get_enum_value_scoped_name(int n) const { + if (n >= 0 && n < (int)_enum_values.size()) { + return _enum_values[n]._scoped_name; + } + return _empty_string; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_enum_value +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +get_enum_value(int n) const { + if (n >= 0 && n < (int)_enum_values.size()) { + return _enum_values[n]._value; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_struct +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_struct() const { + return (_flags & F_struct) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_class +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_class() const { + return (_flags & F_class) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_union +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_union() const { + return (_flags & F_union) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_fully_defined +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_fully_defined() const { + return (_flags & F_fully_defined) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::is_unpublished +// Access: Public +// Description: Returns true if the type is an unpublished type. +// This either means the type is a nested type, and it +// is protected or private within its scope, or that its +// definition is simply not marked as 'published'. +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +is_unpublished() const { + return (_flags & F_unpublished) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::number_of_constructors +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +number_of_constructors() const { + return _constructors.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateType:: +get_constructor(int n) const { + if (n >= 0 && n < (int)_constructors.size()) { + return _constructors[n]; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::has_destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +has_destructor() const { + return (_destructor != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::destructor_is_inherited +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +destructor_is_inherited() const { + return (_flags & F_inherited_destructor) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateType:: +get_destructor() const { + return _destructor; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::number_of_elements +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +number_of_elements() const { + return _elements.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_element +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE ElementIndex InterrogateType:: +get_element(int n) const { + if (n >= 0 && n < (int)_elements.size()) { + return _elements[n]; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::number_of_methods +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +number_of_methods() const { + return _methods.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_method +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateType:: +get_method(int n) const { + if (n >= 0 && n < (int)_methods.size()) { + return _methods[n]; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::number_of_casts +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +number_of_casts() const { + return _casts.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_cast +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE FunctionIndex InterrogateType:: +get_cast(int n) const { + if (n >= 0 && n < (int)_casts.size()) { + return _casts[n]; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::number_of_derivations +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +number_of_derivations() const { + return _derivations.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_derivation +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateType:: +get_derivation(int n) const { + if (n >= 0 && n < (int)_derivations.size()) { + return _derivations[n]._base; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::derivation_has_upcast +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +derivation_has_upcast(int n) const { + if (n >= 0 && n < (int)_derivations.size()) { + return (_derivations[n]._flags & DF_upcast) != 0; + } else { + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::derivation_get_upcast +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateType:: +derivation_get_upcast(int n) const { + if (n >= 0 && n < (int)_derivations.size()) { + return _derivations[n]._upcast; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::derivation_downcast_is_impossible +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +derivation_downcast_is_impossible(int n) const { + if (n >= 0 && n < (int)_derivations.size()) { + return (_derivations[n]._flags & DF_downcast_impossible) != 0; + } else { + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::derivation_has_downcast +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool InterrogateType:: +derivation_has_downcast(int n) const { + if (n >= 0 && n < (int)_derivations.size()) { + return (_derivations[n]._flags & DF_downcast) != 0; + } else { + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::derivation_get_downcast +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateType:: +derivation_get_downcast(int n) const { + if (n >= 0 && n < (int)_derivations.size()) { + return _derivations[n]._downcast; + } else { + return 0; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::number_of_nested_types +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int InterrogateType:: +number_of_nested_types() const { + return _nested_types.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::get_nested_type +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TypeIndex InterrogateType:: +get_nested_type(int n) const { + if (n >= 0 && n < (int)_nested_types.size()) { + return _nested_types[n]; + } else { + return 0; + } +} + +INLINE ostream & +operator << (ostream &out, const InterrogateType &type) { + type.output(out); + return out; +} + +INLINE istream & +operator >> (istream &in, InterrogateType &type) { + type.input(in); + return in; +} + +INLINE ostream & +operator << (ostream &out, const InterrogateType::Derivation &d) { + d.output(out); + return out; +} + +INLINE istream & +operator >> (istream &in, InterrogateType::Derivation &d) { + d.input(in); + return in; +} + +INLINE ostream & +operator << (ostream &out, const InterrogateType::EnumValue &ev) { + ev.output(out); + return out; +} + +INLINE istream & +operator >> (istream &in, InterrogateType::EnumValue &ev) { + ev.input(in); + return in; +} + diff --git a/dtool/src/interrogatedb/interrogateType.cxx b/dtool/src/interrogatedb/interrogateType.cxx new file mode 100644 index 0000000000..cf02705f7c --- /dev/null +++ b/dtool/src/interrogatedb/interrogateType.cxx @@ -0,0 +1,210 @@ +// Filename: interrogateType.C +// Created by: drose (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogateType.h" +#include "indexRemapper.h" +#include "interrogate_datafile.h" + +#include + +// This static string is just kept around as a handy bogus return +// value for functions that must return a const string reference. +string InterrogateType::_empty_string; + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::Derivation::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void InterrogateType::Derivation:: +output(ostream &out) const { + out << _flags << " " << _base << " " << _upcast << " " << _downcast; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::Derivation::input +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void InterrogateType::Derivation:: +input(istream &in) { + in >> _flags >> _base >> _upcast >> _downcast; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::EnumValue::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void InterrogateType::EnumValue:: +output(ostream &out) const { + idf_output_string(out, _name); + idf_output_string(out, _scoped_name); + out << _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::EnumValue::input +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void InterrogateType::EnumValue:: +input(istream &in) { + idf_input_string(in, _name); + idf_input_string(in, _scoped_name); + in >> _value; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void InterrogateType:: +operator = (const InterrogateType ©) { + InterrogateComponent::operator = (copy); + _flags = copy._flags; + _scoped_name = copy._scoped_name; + _true_name = copy._true_name; + _comment = copy._comment; + _outer_class = copy._outer_class; + _atomic_token = copy._atomic_token; + _wrapped_type = copy._wrapped_type; + _constructors = copy._constructors; + _destructor = copy._destructor; + _elements = copy._elements; + _methods = copy._methods; + _casts = copy._casts; + _derivations = copy._derivations; + _enum_values = copy._enum_values; + _nested_types = copy._nested_types; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::merge_with +// Access: Public +// Description: Combines type with the other similar definition. If +// one type is "fully defined" and the other one isn't, +// the fully-defined type wins. +//////////////////////////////////////////////////////////////////// +void InterrogateType:: +merge_with(const InterrogateType &other) { + // The only thing we care about copying from the non-fully-defined + // type right now is the global flag. + + if (is_fully_defined()) { + // We win. + _flags |= (other._flags & F_global); + + } else { + // They win. + int old_flags = (_flags & F_global); + (*this) = other; + _flags |= old_flags; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::output +// Access: Public +// Description: Formats the InterrogateType data for output to a data +// file. +//////////////////////////////////////////////////////////////////// +void InterrogateType:: +output(ostream &out) const { + InterrogateComponent::output(out); + + out << _flags << " "; + idf_output_string(out, _scoped_name); + idf_output_string(out, _true_name); + out << _outer_class << " " + << (int)_atomic_token << " " + << _wrapped_type << " "; + idf_output_vector(out, _constructors); + out << _destructor << " "; + idf_output_vector(out, _elements); + idf_output_vector(out, _methods); + idf_output_vector(out, _casts); + idf_output_vector(out, _derivations); + idf_output_vector(out, _enum_values); + idf_output_vector(out, _nested_types); + idf_output_string(out, _comment, '\n'); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::input +// Access: Public +// Description: Reads the data file as previously formatted by +// output(). +//////////////////////////////////////////////////////////////////// +void InterrogateType:: +input(istream &in) { + InterrogateComponent::input(in); + + in >> _flags; + idf_input_string(in, _scoped_name); + idf_input_string(in, _true_name); + + in >> _outer_class; + int token; + in >> token; + _atomic_token = (AtomicToken)token; + in >> _wrapped_type; + + idf_input_vector(in, _constructors); + in >> _destructor; + + idf_input_vector(in, _elements); + idf_input_vector(in, _methods); + idf_input_vector(in, _casts); + idf_input_vector(in, _derivations); + idf_input_vector(in, _enum_values); + idf_input_vector(in, _nested_types); + idf_input_string(in, _comment); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterrogateType::remap_indices +// Access: Public +// Description: Remaps all internal index numbers according to the +// indicated map. This called from +// InterrogateDatabase::remap_indices(). +//////////////////////////////////////////////////////////////////// +void InterrogateType:: +remap_indices(const IndexRemapper &remap) { + _outer_class = remap.map_from(_outer_class); + _wrapped_type = remap.map_from(_wrapped_type); + + Functions::iterator fi; + for (fi = _constructors.begin(); fi != _constructors.end(); ++fi) { + (*fi) = remap.map_from(*fi); + } + _destructor = remap.map_from(_destructor); + + Elements::iterator ei; + for (ei = _elements.begin(); ei != _elements.end(); ++ei) { + (*ei) = remap.map_from(*ei); + } + + for (fi = _methods.begin(); fi != _methods.end(); ++fi) { + (*fi) = remap.map_from(*fi); + } + for (fi = _casts.begin(); fi != _casts.end(); ++fi) { + (*fi) = remap.map_from(*fi); + } + + Derivations::iterator di; + for (di = _derivations.begin(); di != _derivations.end(); ++di) { + (*di)._base = remap.map_from((*di)._base); + (*di)._upcast = remap.map_from((*di)._upcast); + (*di)._downcast = remap.map_from((*di)._downcast); + } + + Types::iterator ti; + for (ti = _nested_types.begin(); ti != _nested_types.end(); ++ti) { + (*ti) = remap.map_from(*ti); + } + +} diff --git a/dtool/src/interrogatedb/interrogateType.h b/dtool/src/interrogatedb/interrogateType.h new file mode 100644 index 0000000000..e83f129d69 --- /dev/null +++ b/dtool/src/interrogatedb/interrogateType.h @@ -0,0 +1,200 @@ +// Filename: interrogateType.h +// Created by: drose (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATETYPE_H +#define INTERROGATETYPE_H + +#include + +#include "interrogateComponent.h" + +#include + +class IndexRemapper; + +//////////////////////////////////////////////////////////////////// +// Class : InterrogateType +// Description : An internal representation of a type. +//////////////////////////////////////////////////////////////////// +class EXPCL_DTOOL InterrogateType : public InterrogateComponent { +public: + INLINE InterrogateType(InterrogateModuleDef *def = NULL); + INLINE InterrogateType(const InterrogateType ©); + void operator = (const InterrogateType ©); + + INLINE bool is_global() const; + + INLINE bool has_scoped_name() const; + INLINE const string &get_scoped_name() const; + + INLINE bool has_true_name() const; + INLINE const string &get_true_name() const; + + INLINE bool has_comment() const; + INLINE const string &get_comment() const; + + INLINE bool is_nested() const; + INLINE TypeIndex get_outer_class() const; + + INLINE bool is_atomic() const; + INLINE AtomicToken get_atomic_token() const; + INLINE bool is_unsigned() const; + INLINE bool is_signed() const; + INLINE bool is_long() const; + INLINE bool is_longlong() const; + INLINE bool is_short() const; + + INLINE bool is_wrapped() const; + INLINE bool is_pointer() const; + INLINE bool is_const() const; + INLINE TypeIndex get_wrapped_type() const; + + INLINE bool is_enum() const; + INLINE int number_of_enum_values() const; + INLINE const string &get_enum_value_name(int n) const; + INLINE const string &get_enum_value_scoped_name(int n) const; + INLINE int get_enum_value(int n) const; + + INLINE bool is_struct() const; + INLINE bool is_class() const; + INLINE bool is_union() const; + + INLINE bool is_fully_defined() const; + INLINE bool is_unpublished() const; + INLINE int number_of_constructors() const; + INLINE FunctionIndex get_constructor(int n) const; + INLINE bool has_destructor() const; + INLINE bool destructor_is_inherited() const; + INLINE FunctionIndex get_destructor() const; + INLINE int number_of_elements() const; + INLINE ElementIndex get_element(int n) const; + INLINE int number_of_methods() const; + INLINE FunctionIndex get_method(int n) const; + + INLINE int number_of_casts() const; + INLINE FunctionIndex get_cast(int n) const; + + INLINE int number_of_derivations() const; + INLINE TypeIndex get_derivation(int n) const; + + INLINE bool derivation_has_upcast(int n) const; + INLINE FunctionIndex derivation_get_upcast(int n) const; + + INLINE bool derivation_downcast_is_impossible(int n) const; + INLINE bool derivation_has_downcast(int n) const; + INLINE FunctionIndex derivation_get_downcast(int n) const; + + INLINE int number_of_nested_types() const; + INLINE TypeIndex get_nested_type(int n) const; + + void merge_with(const InterrogateType &other); + void output(ostream &out) const; + void input(istream &in); + + void remap_indices(const IndexRemapper &remap); + +private: + enum Flags { + F_global = 0x000001, + F_atomic = 0x000002, + F_unsigned = 0x000004, + F_signed = 0x000008, + F_long = 0x000010, + F_longlong = 0x000020, + F_short = 0x000040, + F_wrapped = 0x000080, + F_pointer = 0x000100, + F_const = 0x000200, + F_struct = 0x000400, + F_class = 0x000800, + F_union = 0x001000, + F_fully_defined = 0x002000, + F_true_destructor = 0x004000, + F_private_destructor = 0x008000, + F_inherited_destructor = 0x010000, + F_implicit_destructor = 0x020000, + F_nested = 0x040000, + F_enum = 0x080000, + F_unpublished = 0x100000, + }; + + int _flags; + string _scoped_name; + string _true_name; + string _comment; + TypeIndex _outer_class; + AtomicToken _atomic_token; + TypeIndex _wrapped_type; + + typedef vector Functions; + Functions _constructors; + FunctionIndex _destructor; + + typedef vector Elements; + Elements _elements; + Functions _methods; + Functions _casts; + + enum DerivationFlags { + DF_upcast = 0x01, + DF_downcast = 0x02, + DF_downcast_impossible = 0x04 + }; + +public: + // This nested class must be declared public just so we can declare + // the external ostream and istream I/O operator functions, on the + // SGI compiler. Arguably a compiler bug, but what can you do. + class Derivation { + public: + void output(ostream &out) const; + void input(istream &in); + + int _flags; + TypeIndex _base; + FunctionIndex _upcast; + FunctionIndex _downcast; + }; + +private: + typedef vector Derivations; + Derivations _derivations; + +public: + // This nested class must also be public, for the same reason. + class EnumValue { + public: + void output(ostream &out) const; + void input(istream &in); + + string _name; + string _scoped_name; + int _value; + }; + +private: + typedef vector EnumValues; + EnumValues _enum_values; + + typedef vector Types; + Types _nested_types; + + static string _empty_string; + + friend class InterrogateBuilder; +}; + +INLINE ostream &operator << (ostream &out, const InterrogateType &type); +INLINE istream &operator >> (istream &in, InterrogateType &type); + +INLINE ostream &operator << (ostream &out, const InterrogateType::Derivation &d); +INLINE istream &operator >> (istream &in, InterrogateType::Derivation &d); + +INLINE ostream &operator << (ostream &out, const InterrogateType::EnumValue &d); +INLINE istream &operator >> (istream &in, InterrogateType::EnumValue &d); + +#include "interrogateType.I" + +#endif diff --git a/dtool/src/interrogatedb/interrogate_datafile.I b/dtool/src/interrogatedb/interrogate_datafile.I new file mode 100644 index 0000000000..42bcb15f5a --- /dev/null +++ b/dtool/src/interrogatedb/interrogate_datafile.I @@ -0,0 +1,48 @@ +// Filename: interrogate_datafile.I +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: idf_output_vector +// Description: Writes the indicated vector to the output file. Each +// component is written using its normal ostream output +// operator. +//////////////////////////////////////////////////////////////////// +template +void +idf_output_vector(ostream &out, const vector &vec) { + out << vec.size() << " "; + vector::const_iterator vi; + for (vi = vec.begin(); vi != vec.end(); ++vi) { + out << (*vi) << " "; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: idf_input_vector +// Description: Reads the given vector from the input file, as +// previously written by output_string(). Each +// component is read using its normal istream input +// operator. +//////////////////////////////////////////////////////////////////// +template +void +idf_input_vector(istream &in, vector &vec) { + int length; + in >> length; + if (in.fail()) { + return; + } + + vec.clear(); + vec.reserve(length); + while (length > 0) { + Element elem; + in >> elem; + vec.push_back(elem); + length--; + } +} diff --git a/dtool/src/interrogatedb/interrogate_datafile.cxx b/dtool/src/interrogatedb/interrogate_datafile.cxx new file mode 100644 index 0000000000..0d58c059ae --- /dev/null +++ b/dtool/src/interrogatedb/interrogate_datafile.cxx @@ -0,0 +1,92 @@ +// Filename: interrogate_datafile.C +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogate_datafile.h" + + +//////////////////////////////////////////////////////////////////// +// Function: idf_output_string +// Description: Writes the indicated string to the output file. Uses +// the given whitespace character to separate the +// string's length and its contents. +//////////////////////////////////////////////////////////////////// +void +idf_output_string(ostream &out, const string &str, char whitespace) { + out << str.length() << whitespace; + if (!str.empty()) { + out << str << whitespace; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: idf_input_string +// Description: Reads the given string from the input file, as +// previously written by output_string(). +//////////////////////////////////////////////////////////////////// +void +idf_input_string(istream &in, string &str) { + int length; + in >> length; + if (in.fail()) { + return; + } + + // Skip one character of whitespace, and then read the string. + in.get(); + str = ""; + while (length > 0) { + str += in.get(); + length--; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: idf_output_string +// Description: Writes the indicated string to the output file. Uses +// the given whitespace character to separate the +// string's length and its contents. +//////////////////////////////////////////////////////////////////// +void +idf_output_string(ostream &out, const char *str, char whitespace) { + if (str == (const char *)NULL) { + out << "0 "; + } else { + out << strlen(str) << whitespace; + if (str[0] != '\0') { + out << str << whitespace; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: idf_input_string +// Description: Reads the given string from the input file, as +// previously written by output_string(). +//////////////////////////////////////////////////////////////////// +void +idf_input_string(istream &in, const char *&str) { + int length; + in >> length; + if (in.fail()) { + return; + } + + if (length == 0) { + // Don't change the string if the input length is zero. + return; + } + + // Skip one character of whitespace, and then read the string. + in.get(); + char *readstr = new char[length + 1]; + int p = 0; + while (p < length) { + readstr[p] = in.get(); + p++; + } + readstr[p] = '\0'; + + str = readstr; +} diff --git a/dtool/src/interrogatedb/interrogate_datafile.h b/dtool/src/interrogatedb/interrogate_datafile.h new file mode 100644 index 0000000000..3446a4dc70 --- /dev/null +++ b/dtool/src/interrogatedb/interrogate_datafile.h @@ -0,0 +1,29 @@ +// Filename: interrogate_datafile.h +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATE_DATAFILE_H +#define INTERROGATE_DATAFILE_H + +// This file defines some convenience functions for reading and +// writing the interrogate database files. + +#include +#include + +void idf_output_string(ostream &out, const string &str, char whitespace = ' '); +void idf_input_string(istream &in, string &str); + +void idf_output_string(ostream &out, const char *str, char whitespace = ' '); +void idf_input_string(istream &in, const char *&str); + +template +void idf_output_vector(ostream &out, const vector &vec); + +template +void idf_input_vector(istream &in, vector &vec); + +#include "interrogate_datafile.I" + +#endif diff --git a/dtool/src/interrogatedb/interrogate_interface.cxx b/dtool/src/interrogatedb/interrogate_interface.cxx new file mode 100644 index 0000000000..07da22463e --- /dev/null +++ b/dtool/src/interrogatedb/interrogate_interface.cxx @@ -0,0 +1,563 @@ +// Filename: interrogate_interface.C +// Created by: drose (31Jul00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogate_interface.h" +#include "interrogateDatabase.h" +#include "interrogateType.h" +#include "interrogateFunction.h" + +int +interrogate_number_of_manifests() { + return InterrogateDatabase::get_ptr()->get_num_global_manifests(); +} + +ManifestIndex +interrogate_get_manifest(int n) { + return InterrogateDatabase::get_ptr()->get_global_manifest(n); +} + +ManifestIndex +interrogate_get_manifest_by_name(const char *manifest_name) { + return InterrogateDatabase::get_ptr()->lookup_manifest_by_name(manifest_name); +} + +const char * +interrogate_manifest_name(ManifestIndex manifest) { + return InterrogateDatabase::get_ptr()->get_manifest(manifest).get_name().c_str(); +} + +const char * +interrogate_manifest_definition(ManifestIndex manifest) { + return InterrogateDatabase::get_ptr()->get_manifest(manifest).get_definition().c_str(); +} + +bool +interrogate_manifest_has_type(ManifestIndex manifest) { + return InterrogateDatabase::get_ptr()->get_manifest(manifest).has_type(); +} + +TypeIndex +interrogate_manifest_get_type(ManifestIndex manifest) { + return InterrogateDatabase::get_ptr()->get_manifest(manifest).get_type(); +} + +bool +interrogate_manifest_has_getter(ManifestIndex manifest) { + return InterrogateDatabase::get_ptr()->get_manifest(manifest).has_getter(); +} + +FunctionIndex +interrogate_manifest_getter(ManifestIndex manifest) { + return InterrogateDatabase::get_ptr()->get_manifest(manifest).get_getter(); +} + +bool +interrogate_manifest_has_int_value(ManifestIndex manifest) { + return InterrogateDatabase::get_ptr()->get_manifest(manifest).has_int_value(); +} + +int +interrogate_manifest_get_int_value(ManifestIndex manifest) { + return InterrogateDatabase::get_ptr()->get_manifest(manifest).get_int_value(); +} + +const char * +interrogate_element_name(ElementIndex element) { + return InterrogateDatabase::get_ptr()->get_element(element).get_name().c_str(); +} + +const char * +interrogate_element_scoped_name(ElementIndex element) { + return InterrogateDatabase::get_ptr()->get_element(element).get_scoped_name().c_str(); +} + +ElementIndex +interrogate_get_element_by_name(const char *element_name) { + return InterrogateDatabase::get_ptr()->lookup_element_by_name(element_name); +} + +ElementIndex +interrogate_get_element_by_scoped_name(const char *element_name) { + return InterrogateDatabase::get_ptr()->lookup_element_by_scoped_name(element_name); +} + +TypeIndex +interrogate_element_type(ElementIndex element) { + return InterrogateDatabase::get_ptr()->get_element(element).get_type(); +} + +bool +interrogate_element_has_getter(ElementIndex element) { + return InterrogateDatabase::get_ptr()->get_element(element).has_getter(); +} + +FunctionIndex +interrogate_element_getter(ElementIndex element) { + return InterrogateDatabase::get_ptr()->get_element(element).get_getter(); +} + +bool +interrogate_element_has_setter(ElementIndex element) { + return InterrogateDatabase::get_ptr()->get_element(element).has_setter(); +} + +FunctionIndex +interrogate_element_setter(ElementIndex element) { + return InterrogateDatabase::get_ptr()->get_element(element).get_setter(); +} + +int +interrogate_number_of_globals() { + return InterrogateDatabase::get_ptr()->get_num_global_elements(); +} + +ElementIndex +interrogate_get_global(int n) { + return InterrogateDatabase::get_ptr()->get_global_element(n); +} + +int +interrogate_number_of_global_functions() { + return InterrogateDatabase::get_ptr()->get_num_global_functions(); +} + +FunctionIndex +interrogate_get_global_function(int n) { + return InterrogateDatabase::get_ptr()->get_global_function(n); +} + +int +interrogate_number_of_functions() { + return InterrogateDatabase::get_ptr()->get_num_all_functions(); +} + +FunctionIndex +interrogate_get_function(int n) { + return InterrogateDatabase::get_ptr()->get_all_function(n); +} + +const char * +interrogate_function_name(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).get_name().c_str(); +} + +const char * +interrogate_function_scoped_name(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).get_scoped_name().c_str(); +} + +bool +interrogate_function_has_comment(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).has_comment(); +} + +const char * +interrogate_function_comment(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).get_comment().c_str(); +} + +const char * +interrogate_function_prototype(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).get_prototype().c_str(); +} + +bool +interrogate_function_is_method(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).is_method(); +} + +TypeIndex +interrogate_function_class(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).get_class(); +} + +bool +interrogate_function_has_module_name(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).has_module_name(); +} + +const char * +interrogate_function_module_name(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).get_module_name(); +} + +bool +interrogate_function_is_virtual(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).is_virtual(); +} + +int +interrogate_function_number_of_c_wrappers(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).number_of_c_wrappers(); +} + +FunctionWrapperIndex +interrogate_function_c_wrapper(FunctionIndex function, int n) { + return InterrogateDatabase::get_ptr()->get_function(function).get_c_wrapper(n); +} + +int +interrogate_function_number_of_python_wrappers(FunctionIndex function) { + return InterrogateDatabase::get_ptr()->get_function(function).number_of_python_wrappers(); +} + +FunctionWrapperIndex +interrogate_function_python_wrapper(FunctionIndex function, int n) { + return InterrogateDatabase::get_ptr()->get_function(function).get_python_wrapper(n); +} + +const char * +interrogate_wrapper_name(FunctionWrapperIndex wrapper) { + static string result; + result = InterrogateDatabase::get_ptr()->get_wrapper(wrapper).get_name(); + return result.c_str(); +} + +bool +interrogate_wrapper_is_callable_by_name(FunctionWrapperIndex wrapper) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).is_callable_by_name(); +} + +bool +interrogate_wrapper_has_return_value(FunctionWrapperIndex wrapper) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).has_return_value(); +} + +TypeIndex +interrogate_wrapper_return_type(FunctionWrapperIndex wrapper) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).get_return_type(); +} + +bool +interrogate_wrapper_caller_manages_return_value(FunctionWrapperIndex wrapper) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).caller_manages_return_value(); +} + +FunctionIndex +interrogate_wrapper_return_value_destructor(FunctionWrapperIndex wrapper) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).get_return_value_destructor(); +} + +int +interrogate_wrapper_number_of_parameters(FunctionWrapperIndex wrapper) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).number_of_parameters(); +} + +TypeIndex +interrogate_wrapper_parameter_type(FunctionWrapperIndex wrapper, int n) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_get_type(n); +} + +bool +interrogate_wrapper_parameter_has_name(FunctionWrapperIndex wrapper, int n) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_has_name(n); +} + +const char * +interrogate_wrapper_parameter_name(FunctionWrapperIndex wrapper, int n) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_get_name(n).c_str(); +} + +bool +interrogate_wrapper_parameter_is_this(FunctionWrapperIndex wrapper, int n) { + return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_is_this(n); +} + +bool +interrogate_wrapper_has_pointer(FunctionWrapperIndex wrapper) { + return (InterrogateDatabase::get_ptr()->get_fptr(wrapper) != (void *)NULL); +} + +void * +interrogate_wrapper_pointer(FunctionWrapperIndex wrapper) { + return InterrogateDatabase::get_ptr()->get_fptr(wrapper); +} + +const char * +interrogate_wrapper_unique_name(FunctionWrapperIndex wrapper) { + static string result; + result = InterrogateDatabase::get_ptr()->get_wrapper(wrapper).get_unique_name(); + return result.c_str(); +} + +FunctionWrapperIndex +interrogate_get_wrapper_by_unique_name(const char *unique_name) { + return InterrogateDatabase::get_ptr()->get_wrapper_by_unique_name(unique_name); +} + +int +interrogate_number_of_global_types() { + return InterrogateDatabase::get_ptr()->get_num_global_types(); +} + +TypeIndex +interrogate_get_global_type(int n) { + return InterrogateDatabase::get_ptr()->get_global_type(n); +} + +int +interrogate_number_of_types() { + return InterrogateDatabase::get_ptr()->get_num_all_types(); +} + +TypeIndex +interrogate_get_type(int n) { + return InterrogateDatabase::get_ptr()->get_all_type(n); +} + +TypeIndex +interrogate_get_type_by_name(const char *type_name) { + return InterrogateDatabase::get_ptr()->lookup_type_by_name(type_name); +} + +TypeIndex +interrogate_get_type_by_scoped_name(const char *type_name) { + return InterrogateDatabase::get_ptr()->lookup_type_by_scoped_name(type_name); +} + +TypeIndex +interrogate_get_type_by_true_name(const char *type_name) { + return InterrogateDatabase::get_ptr()->lookup_type_by_true_name(type_name); +} + +const char * +interrogate_type_name(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).get_name().c_str(); +} + +const char * +interrogate_type_scoped_name(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).get_scoped_name().c_str(); +} + +const char * +interrogate_type_true_name(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).get_true_name().c_str(); +} + +bool +interrogate_type_is_nested(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_nested(); +} + +TypeIndex +interrogate_type_outer_class(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).get_outer_class(); +} + +bool +interrogate_type_has_comment(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).has_comment(); +} + +const char * +interrogate_type_comment(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).get_comment().c_str(); +} + +bool +interrogate_type_is_atomic(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_atomic(); +} + +AtomicToken +interrogate_type_atomic_token(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).get_atomic_token(); +} + +bool +interrogate_type_is_unsigned(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_unsigned(); +} + +bool +interrogate_type_is_signed(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_signed(); +} + +bool +interrogate_type_is_long(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_long(); +} + +bool +interrogate_type_is_longlong(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_longlong(); +} + +bool +interrogate_type_is_short(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_short(); +} + +bool +interrogate_type_is_wrapped(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_wrapped(); +} + +bool +interrogate_type_is_pointer(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_pointer(); +} + +bool +interrogate_type_is_const(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_const(); +} + +TypeIndex +interrogate_type_wrapped_type(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).get_wrapped_type(); +} + +bool +interrogate_type_is_enum(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_enum(); +} + +int +interrogate_type_number_of_enum_values(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).number_of_enum_values(); +} + +const char * +interrogate_type_enum_value_name(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_enum_value_name(n).c_str(); +} + +const char * +interrogate_type_enum_value_scoped_name(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_enum_value_scoped_name(n).c_str(); +} + +int +interrogate_type_enum_value(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_enum_value(n); +} + +bool +interrogate_type_is_struct(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_struct(); +} + +bool +interrogate_type_is_class(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_class(); +} + +bool +interrogate_type_is_union(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_union(); +} + +bool +interrogate_type_is_fully_defined(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_fully_defined(); +} + +bool +interrogate_type_is_unpublished(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).is_unpublished(); +} + +int +interrogate_type_number_of_constructors(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).number_of_constructors(); +} + +FunctionIndex +interrogate_type_get_constructor(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_constructor(n); +} + +bool +interrogate_type_has_destructor(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).has_destructor(); +} + +bool +interrogate_type_destructor_is_inherited(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).destructor_is_inherited(); +} + +FunctionIndex +interrogate_type_get_destructor(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).get_destructor(); +} + +int +interrogate_type_number_of_elements(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).number_of_elements(); +} + +ElementIndex +interrogate_type_get_element(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_element(n); +} + +int +interrogate_type_number_of_methods(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).number_of_methods(); +} + +FunctionIndex +interrogate_type_get_method(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_method(n); +} + +int +interrogate_type_number_of_casts(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).number_of_casts(); +} + +FunctionIndex +interrogate_type_get_cast(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_cast(n); +} + +int +interrogate_type_number_of_derivations(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).number_of_derivations(); +} + +TypeIndex +interrogate_type_get_derivation(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_derivation(n); +} + +bool +interrogate_type_derivation_has_upcast(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).derivation_has_upcast(n); +} + +FunctionIndex +interrogate_type_get_upcast(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).derivation_get_upcast(n); +} + +bool +interrogate_type_derivation_downcast_is_impossible(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).derivation_downcast_is_impossible(n); +} + +bool +interrogate_type_derivation_has_downcast(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).derivation_has_downcast(n); +} + +FunctionIndex +interrogate_type_get_downcast(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).derivation_get_downcast(n); +} + +int +interrogate_type_number_of_nested_types(TypeIndex type) { + return InterrogateDatabase::get_ptr()->get_type(type).number_of_nested_types(); +} + +TypeIndex +interrogate_type_get_nested_type(TypeIndex type, int n) { + return InterrogateDatabase::get_ptr()->get_type(type).get_nested_type(n); +} diff --git a/dtool/src/interrogatedb/interrogate_interface.h b/dtool/src/interrogatedb/interrogate_interface.h new file mode 100644 index 0000000000..82ec1fdac2 --- /dev/null +++ b/dtool/src/interrogatedb/interrogate_interface.h @@ -0,0 +1,505 @@ +// Filename: interrogate_interface.h +// Created by: frang (09Nov99) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATE_INTERFACE_H +#define INTERROGATE_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// This file defines the interface to the interrogate database. This +// database is generated by running interrogate on a package's source +// code; interrogate parses the C++ syntax, determines the public +// interface, generates C-style wrapper functions where necessary, and +// builds up a table of functions and classes and their relationships. + +// Some of this data (in particular, the wrapper functions, and the +// table of unique names for these functions) is linked in along with +// the codebase, permanently a part of the library file, and is always +// available; the rest of it is stored in external files (named *.in) +// and read in when needed. For this reason, most of the interface +// functions defined here will force a load of the complete +// interrogate database the first time any of them are called. The +// three exceptions are noted below; they are +// interrogate_wrapper_has_pointer(), interrogate_wrapper_pointer(), +// and interrogate_get_wrapper_by_unique_name(). + + +// The interface here is intentionally made to be as simple as +// possible, to maximize portability. All that is required of a +// scripting language is a foreign function interface capable of +// calling C functions. + + +// In general, the interrogate database consists of a number of query +// functions that allow the caller to walk through the list of +// available types, functions, manifests, etc. For each of these, a +// unique index number is returned; this index number may then be used +// to query details about the type, function, etc. The index numbers +// are only guaranteed to remain unchanged during a particular +// session; from one session to another they may differ. + +// All index numbers are ordinary integers. Each has a unique typedef +// here for clarity of meaning, but they may be treated as ordinary +// integers by the caller. +typedef int ManifestIndex; +typedef int ElementIndex; +typedef int TypeIndex; +typedef int FunctionIndex; +typedef int FunctionWrapperIndex; + +// Atomic types are those that are built in to C. This enumerated +// value is returned by interrogate_type_atomic_token() when a type is +// known to be one of the atomic types. +enum AtomicToken { + AT_not_atomic = 0, + AT_int = 1, + AT_float = 2, + AT_double = 3, + AT_bool = 4, + AT_char = 5, + AT_void = 6, + + // There isn't an atomic string type in C, but there is one in + // almost all other languages. If -string is supplied to the + // interrogate command line, functions may be reported as returning + // and accepting objects of type atomic string. For the C calling + // convention wrappers, atomic string means (const char *); for + // other calling convention wrappers, atomic string means whatever + // the native string representation is. + AT_string = 7 +}; + +////////////////////////////////////////////////////////////////////////// +// +// Manifest Symbols +// +////////////////////////////////////////////////////////////////////////// + +// These correspond to #define constants that appear in the C code. +// (These are only the manifest constants--those #define's that take +// no parameters. Manifest functions, #define's that take one or more +// parameters, are not exported.) They cannot be set, of course, but +// they often have a meaningful value that may be get. The scripting +// language may choose to get the value as a literal string via +// interrogate_manifest_definition(), or as a value of a particular type +// (whatever type interrogate thinks it is), as returned by the getter +// function given by interrogate_manifest_getter(). + +EXPCL_DTOOL int interrogate_number_of_manifests(); +EXPCL_DTOOL ManifestIndex interrogate_get_manifest(int n); +EXPCL_DTOOL ManifestIndex interrogate_get_manifest_by_name(const char *manifest_name); +EXPCL_DTOOL const char *interrogate_manifest_name(ManifestIndex manifest); +EXPCL_DTOOL const char *interrogate_manifest_definition(ManifestIndex manifest); +EXPCL_DTOOL bool interrogate_manifest_has_type(ManifestIndex manifest); +EXPCL_DTOOL TypeIndex interrogate_manifest_get_type(ManifestIndex manifest); +EXPCL_DTOOL bool interrogate_manifest_has_getter(ManifestIndex manifest); +EXPCL_DTOOL FunctionIndex interrogate_manifest_getter(ManifestIndex manifest); + +// An exception is made for manifest constants that have an integer +// type value, since these are so common. The scripting language can +// query these values directly, which saves having to generate a +// wrapper function for each stupid little manifest. In this case, +// there will be no getter function available. +EXPCL_DTOOL bool interrogate_manifest_has_int_value(ManifestIndex manifest); +EXPCL_DTOOL int interrogate_manifest_get_int_value(ManifestIndex manifest); + + +////////////////////////////////////////////////////////////////////////// +// +// Data Elements +// +////////////////////////////////////////////////////////////////////////// + +// These correspond to data members of a class, or global data +// elements. Interrogate automatically generates a getter function +// and, if possible, a setter function. + +EXPCL_DTOOL const char *interrogate_element_name(ElementIndex element); +EXPCL_DTOOL const char *interrogate_element_scoped_name(ElementIndex element); +EXPCL_DTOOL ElementIndex interrogate_get_element_by_name(const char *element_name); +EXPCL_DTOOL ElementIndex interrogate_get_element_by_scoped_name(const char *element_name); + +// Be careful with this function. The element's bare type is not +// likely to be directly useful to the scripting language. This is a +// different answer than the return value of the getter. + +// The element type might well be something concrete that the +// scripting language can't handle directly, e.g. a Node, while the +// getter will return (and the setter accept) a pointer to a Node, +// which is what the scripting language actually works with. +EXPCL_DTOOL TypeIndex interrogate_element_type(ElementIndex element); + +EXPCL_DTOOL bool interrogate_element_has_getter(ElementIndex element); +EXPCL_DTOOL FunctionIndex interrogate_element_getter(ElementIndex element); +EXPCL_DTOOL bool interrogate_element_has_setter(ElementIndex element); +EXPCL_DTOOL FunctionIndex interrogate_element_setter(ElementIndex element); + +////////////////////////////////////////////////////////////////////////// +// +// Global Data +// +////////////////////////////////////////////////////////////////////////// + +// This is the list of global data elements. + +EXPCL_DTOOL int interrogate_number_of_globals(); +EXPCL_DTOOL ElementIndex interrogate_get_global(int n); + +////////////////////////////////////////////////////////////////////////// +// +// Functions +// +////////////////////////////////////////////////////////////////////////// + +// There is a unique FunctionIndex associated with each of the +// functions that interrogate knows about. This includes member +// functions, nonmember functions, synthesized getters and setters, +// and upcast/downcast functions. + + +// These are the global (nonmember) functions that appear outside of +// any class definition. +EXPCL_DTOOL int interrogate_number_of_global_functions(); +EXPCL_DTOOL FunctionIndex interrogate_get_global_function(int n); + +// This can be used to traverse through *all* the functions known to +// interrogate. It's usually not what you want, since this includes +// global functions, class methods, and synthesized functions like +// upcasts and downcasts. You probably want to use instead +// interrogate_number_of_global_functions(), above. +EXPCL_DTOOL int interrogate_number_of_functions(); +EXPCL_DTOOL FunctionIndex interrogate_get_function(int n); + +// This is the function's name. It is not unique; it may be shared +// between multiple different functions that have the same name but +// different parameter types (this is C++'s function overloading). +// Two different classes might also have member functions that have +// the same name, or the same name as a global function (but also see +// the scoped_name, below). +EXPCL_DTOOL const char *interrogate_function_name(FunctionIndex function); + +// The scoped name is the function name prefixed with the name of the +// class that includes the function, if the function is a class +// method. If it is a global function, the scoped name is the same as +// the name returned above. In the absence of C++ function +// overloading, this name will be unique to each function. +EXPCL_DTOOL const char *interrogate_function_scoped_name(FunctionIndex function); + +// This returns the C++ comment written for the function, either in +// the header file or in the .C file, or both. +EXPCL_DTOOL bool interrogate_function_has_comment(TypeIndex type); +EXPCL_DTOOL const char *interrogate_function_comment(TypeIndex type); + +// This defines the function prototype as it appears in the C++ +// source, useful primarily for documentation purposes. +EXPCL_DTOOL const char *interrogate_function_prototype(TypeIndex type); + +// This can be used to determine the class that the function is a +// method for, if the function is a class method. +EXPCL_DTOOL bool interrogate_function_is_method(FunctionIndex function); +EXPCL_DTOOL TypeIndex interrogate_function_class(FunctionIndex function); + +// This returns the module name reported for the function, if +// available. +EXPCL_DTOOL bool interrogate_function_has_module_name(FunctionIndex function); +EXPCL_DTOOL const char *interrogate_function_module_name(FunctionIndex function); + +// This is true for virtual member functions. It's not likely that +// this will be important to the scripting language. +EXPCL_DTOOL bool interrogate_function_is_virtual(FunctionIndex function); + + +// The actual callable function interface is defined via one or more +// wrappers for each function. (There might be multiple wrappers for +// the same function to allow for default parameter values.) + +// At present, interrogate can generate wrappers that use the C +// calling convention or the Python calling convention. The set of +// wrappers that will actually be available depends on the parameters +// passed to the interrogate command line. +EXPCL_DTOOL int interrogate_function_number_of_c_wrappers(FunctionIndex function); +EXPCL_DTOOL FunctionWrapperIndex interrogate_function_c_wrapper(FunctionIndex function, int n); + +EXPCL_DTOOL int interrogate_function_number_of_python_wrappers(FunctionIndex function); +EXPCL_DTOOL FunctionWrapperIndex interrogate_function_python_wrapper(FunctionIndex function, int n); + +////////////////////////////////////////////////////////////////////////// +// +// Function wrappers +// +////////////////////////////////////////////////////////////////////////// + +// These define the way to call a given function. Depending on the +// parameters supplied to interrogate, a function wrapper may be able +// to supply either a void * pointer to the function, or the name of +// the function in the library, or both. + + +// This returns the actual name of the wrapper function, as opposed to +// the name of the function it wraps. It's probably not terribly +// useful to the scripting language, unless the -fnames option was +// given to interrogate, in which case this name may be used to call +// the wrapper function (see is_callable_by_name, below). It will +// usually be an ugly hashed name, not intended for human consumption. + +// Don't confuse this with the unique_name, below. The two are +// related, but not identical. +EXPCL_DTOOL const char *interrogate_wrapper_name(FunctionWrapperIndex wrapper); + +// This returns true if -fnames was given to interrogate, making the +// wrapper function callable directly by its name. +EXPCL_DTOOL bool interrogate_wrapper_is_callable_by_name(FunctionWrapperIndex wrapper); + +// Every function wrapper has zero or more parameters and may or may +// not have a return value. Each parameter has a type and may or may +// not have a name. For member functions, the first parameter may be +// a 'this' parameter, which should receive a pointer to the class +// object. (If a member function does not have a 'this' parameter as +// its first parameter, it is a static member function, also called a +// class method.) + +EXPCL_DTOOL bool interrogate_wrapper_has_return_value(FunctionWrapperIndex wrapper); +EXPCL_DTOOL TypeIndex interrogate_wrapper_return_type(FunctionWrapperIndex wrapper); + +// Sometimes interrogate must synthesize a wrapper that allocates its +// return value from the free store. Other times (especially if +// -refcount is supplied to interrogate), interrogate will +// automatically increment the count of a reference-counted object +// that it returns. In cases like these, +// interrogate_wrapper_caller_manages_return_value() will return true, +// and it is the responsibility of the scripting language to +// eventually call the destructor supplied by +// interrogate_wrapper_return_value_destructor() on this value when it +// is no longer needed (which will generally be the same destructor as +// that for the class). Otherwise, this function will return false, +// and the scripting language should *not* call any destructor on this +// value. +EXPCL_DTOOL bool interrogate_wrapper_caller_manages_return_value(FunctionWrapperIndex wrapper); +EXPCL_DTOOL FunctionIndex interrogate_wrapper_return_value_destructor(FunctionWrapperIndex wrapper); + +// These define the parameters of the function. +EXPCL_DTOOL int interrogate_wrapper_number_of_parameters(FunctionWrapperIndex wrapper); +EXPCL_DTOOL TypeIndex interrogate_wrapper_parameter_type(FunctionWrapperIndex wrapper, int n); +EXPCL_DTOOL bool interrogate_wrapper_parameter_has_name(FunctionWrapperIndex wrapper, int n); +EXPCL_DTOOL const char *interrogate_wrapper_parameter_name(FunctionWrapperIndex wrapper, int n); +EXPCL_DTOOL bool interrogate_wrapper_parameter_is_this(FunctionWrapperIndex wrapper, int n); + +// This returns a pointer to a function that may be called to invoke +// the function, if the -fptrs option to return function pointers was +// specified to interrogate. Be sure to push the required parameters +// on the stack, according to the calling convention, before calling +// the function. + +// These two functions may be called without forcing a load of the +// complete interrogate database. +EXPCL_DTOOL bool interrogate_wrapper_has_pointer(FunctionWrapperIndex wrapper); +EXPCL_DTOOL void *interrogate_wrapper_pointer(FunctionWrapperIndex wrapper); + +// This function will return a name that is guaranteed to be unique to +// this particular function wrapper, and that will (usually) be +// consistent across multiple runtime sessions. (It will only change +// between sessions if the database was regenerated in the interim +// with some new function that happened to introduce a hash conflict.) + +// The unique name is an ugly hashed name, not safe for human +// consumption. Its sole purpose is to provide some consistent way to +// identify function wrappers between sessions. +EXPCL_DTOOL const char *interrogate_wrapper_unique_name(FunctionWrapperIndex wrapper); + +// This function provides a reverse-lookup on the above unique name, +// returning the wrapper index corresponding to the given name. It +// depends on data having been compiled directly into the library, and +// thus is only available if the option -unique-names was given to +// interrogate. + +// Thus function may be called without forcing a load of the complete +// interrogate database. +EXPCL_DTOOL FunctionWrapperIndex interrogate_get_wrapper_by_unique_name(const char *unique_name); + + +////////////////////////////////////////////////////////////////////////// +// +// Types +// +////////////////////////////////////////////////////////////////////////// + +// These are all the types that interrogate knows about. This +// includes atomic types like ints and floats, type wrappers like +// pointers and const pointers, enumerated types, and classes. + +// Two lists of types are maintained: the list of global types, which +// includes only those types intended to be wrapped in the API (for +// instance, all of the classes). The second list is the complete +// list of all types, which probably does not need to be +// traversed--this includes *all* types known to the interrogate +// database, including simple types and pointers and const pointers to +// classes. These types are necessary to fully define all of the +// function parameters, but need not themselves be wrapped. + +EXPCL_DTOOL int interrogate_number_of_global_types(); +EXPCL_DTOOL TypeIndex interrogate_get_global_type(int n); +EXPCL_DTOOL int interrogate_number_of_types(); +EXPCL_DTOOL TypeIndex interrogate_get_type(int n); +EXPCL_DTOOL TypeIndex interrogate_get_type_by_name(const char *type_name); +EXPCL_DTOOL TypeIndex interrogate_get_type_by_scoped_name(const char *type_name); +EXPCL_DTOOL TypeIndex interrogate_get_type_by_true_name(const char *type_name); +EXPCL_DTOOL const char *interrogate_type_name(TypeIndex type); +EXPCL_DTOOL const char *interrogate_type_scoped_name(TypeIndex type); +EXPCL_DTOOL const char *interrogate_type_true_name(TypeIndex type); + +// A given type might be a nested type, meaning it is entirely defined +// within (and scoped within) some different C++ class. In this case, +// the type_name() will return the local name of the type as seen +// within the class, while the scoped_name() will return the +// fully-qualified name of the type, and is_nested() and outer_class() +// can be used to determine the class it is nested within. +EXPCL_DTOOL bool interrogate_type_is_nested(TypeIndex type); +EXPCL_DTOOL TypeIndex interrogate_type_outer_class(TypeIndex type); + +EXPCL_DTOOL bool interrogate_type_has_comment(TypeIndex type); +EXPCL_DTOOL const char *interrogate_type_comment(TypeIndex type); + +// If interrogate_type_is_atomic() returns true, the type is one of +// the basic C types enumerated in AtomicToken, above. The type may +// then be further modified by one or more of unsigned, signed, long, +// longlong, or short. However, it will not be a pointer. +EXPCL_DTOOL bool interrogate_type_is_atomic(TypeIndex type); +EXPCL_DTOOL AtomicToken interrogate_type_atomic_token(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_unsigned(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_signed(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_long(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_longlong(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_short(TypeIndex type); + +// If interrogate_type_is_wrapped() returns true, this is a composite +// type "wrapped" around some simpler type, for instance a pointer to +// a class. The type will be either a pointer or a const wrapper--it +// cannot be a combination of these. (When combinations are required, +// they use multiple wrappers. A const char pointer, for example, is +// represented as a pointer wrapper around a const wrapper around an +// atomic char.) +EXPCL_DTOOL bool interrogate_type_is_wrapped(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_pointer(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_const(TypeIndex type); +EXPCL_DTOOL TypeIndex interrogate_type_wrapped_type(TypeIndex type); + +// If interrogate_type_is_enum() returns true, this is an enumerated +// type, which means it may take any one of a number of named integer +// values. +EXPCL_DTOOL bool interrogate_type_is_enum(TypeIndex type); +EXPCL_DTOOL int interrogate_type_number_of_enum_values(TypeIndex type); +EXPCL_DTOOL const char *interrogate_type_enum_value_name(TypeIndex type, int n); +EXPCL_DTOOL const char *interrogate_type_enum_value_scoped_name(TypeIndex type, int n); +EXPCL_DTOOL int interrogate_type_enum_value(TypeIndex type, int n); + +// If none of the above is true, the type is some extension type. It +// may be a struct, class, or union (and the distinction between these +// three is not likely to be important to the scripting language). In +// any case, it may contain zero or more constructors, zero or one +// destructor, zero or more member functions, and zero or more data +// members; all of the remaining type functions may apply. +EXPCL_DTOOL bool interrogate_type_is_struct(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_class(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_is_union(TypeIndex type); + +// If is_fully_defined() returns false, this class/struct was a +// forward reference, and we really don't know anything about it. (In +// this case, it will appear to have no methods or members.) +EXPCL_DTOOL bool interrogate_type_is_fully_defined(TypeIndex type); + +// If is_unpublished() returns false, the class/struct is unknown +// because it was not marked to be published (or, in promiscuous mode, +// it is a protected or private nested class). +EXPCL_DTOOL bool interrogate_type_is_unpublished(TypeIndex type); + +// Otherwise, especially if the type is a struct or class, we may have +// a number of member functions, including zero or more constructors +// and zero or one destructor. A constructor function may be called +// to allocate a new instance of the type; its return value will be a +// pointer to the new instance. The destructor may be called to +// destroy the instance; however, it usually should not be explicitly +// called by the user, since the proper support of the +// interrogate_caller_manages_return_value() interface, above, will +// ensure that the appropriate destructors are called when they should +// be. + +// In certain circumstances, the destructor might be inherited from a +// parent or ancestor class. This happens when the destructor wrapper +// from the ancestor class is an acceptable substitute for this +// destructor; this is only possible in the case of a virtual C++ +// destructor. In this case, the destructor returned here will be the +// same function index as the one returned by the ancestor class, and +// interrogate_type_destructor_is_inherited() will return true for +// this class. +EXPCL_DTOOL int interrogate_type_number_of_constructors(TypeIndex type); +EXPCL_DTOOL FunctionIndex interrogate_type_get_constructor(TypeIndex type, int n); +EXPCL_DTOOL bool interrogate_type_has_destructor(TypeIndex type); +EXPCL_DTOOL bool interrogate_type_destructor_is_inherited(TypeIndex type); +EXPCL_DTOOL FunctionIndex interrogate_type_get_destructor(TypeIndex type); + +// This is the set of exposed data elements in the struct or class. +EXPCL_DTOOL int interrogate_type_number_of_elements(TypeIndex type); +EXPCL_DTOOL ElementIndex interrogate_type_get_element(TypeIndex type, int n); + +// This is the set of exposed member functions in the struct or class. +EXPCL_DTOOL int interrogate_type_number_of_methods(TypeIndex type); +EXPCL_DTOOL FunctionIndex interrogate_type_get_method(TypeIndex type, int n); + +// A C++ class may also define a number of explicit cast operators, +// which define how to convert an object of this type to an object of +// some other type (the type can be inferred by the return type of the +// cast function). This is not related to upcast and downcast, +// defined below. +EXPCL_DTOOL int interrogate_type_number_of_casts(TypeIndex type); +EXPCL_DTOOL FunctionIndex interrogate_type_get_cast(TypeIndex type, int n); + +// A C++ class may inherit from zero or more base classes. This +// defines the list of base classes for this particular type. +EXPCL_DTOOL int interrogate_type_number_of_derivations(TypeIndex type); +EXPCL_DTOOL TypeIndex interrogate_type_get_derivation(TypeIndex type, int n); + +// For each base class, we might need to define an explicit upcast or +// downcast operation to convert the pointer to the derived class to +// an appropriate pointer to its base class (upcast) or vice-versa +// (downcast). This is particularly true in the presence of multiple +// inheritance or virtual inheritance, in which case you cannot simply +// use the same pointer as either type. + +// If interrogate_type_derivation_has_upcast() returns true for a +// particular type/derivation combination, you must use the indicated +// upcast function to convert pointers of this type to pointers of the +// base type before calling any of the inherited methods from the base +// class. If this returns false, you may simply use the same pointer +// as either a derived class pointer or a base class pointer without +// any extra step. +EXPCL_DTOOL bool interrogate_type_derivation_has_upcast(TypeIndex type, int n); +EXPCL_DTOOL FunctionIndex interrogate_type_get_upcast(TypeIndex type, int n); + +// Although it is always possible to upcast a pointer to a base class, +// it is not always possible to downcast from a base class to the +// derived class (particularly in the presence of virtual +// inheritance). If interrogate_type_derivation_downcast_is_impossible() +// returns true, forget it. Otherwise, downcasting works the same +// way as upcasting. (Of course, it is the caller's responsibility to +// guarantee that the pointer actually represents an object of the +// type being downcast to.) +EXPCL_DTOOL bool interrogate_type_derivation_downcast_is_impossible(TypeIndex type, int n); +EXPCL_DTOOL bool interrogate_type_derivation_has_downcast(TypeIndex type, int n); +EXPCL_DTOOL FunctionIndex interrogate_type_get_downcast(TypeIndex type, int n); + +// A C++ class may also define any number of nested types--classes or +// enums defined within the scope of this class. +EXPCL_DTOOL int interrogate_type_number_of_nested_types(TypeIndex type); +EXPCL_DTOOL TypeIndex interrogate_type_get_nested_type(TypeIndex type, int n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dtool/src/interrogatedb/interrogate_request.cxx b/dtool/src/interrogatedb/interrogate_request.cxx new file mode 100644 index 0000000000..ac66192d0d --- /dev/null +++ b/dtool/src/interrogatedb/interrogate_request.cxx @@ -0,0 +1,25 @@ +// Filename: interrogate_request.C +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "interrogate_request.h" +#include "interrogateDatabase.h" + +#include // for strdup + +void +interrogate_request_database(const char *database_filename) { + InterrogateModuleDef *def = new InterrogateModuleDef; + memset(def, 0, sizeof(InterrogateModuleDef)); + def->database_filename = strdup(database_filename); + + // Don't think of this as a leak; think of it as a one-time database + // allocation. + InterrogateDatabase::get_ptr()->request_module(def); +} + +void +interrogate_request_module(InterrogateModuleDef *def) { + InterrogateDatabase::get_ptr()->request_module(def); +} diff --git a/dtool/src/interrogatedb/interrogate_request.h b/dtool/src/interrogatedb/interrogate_request.h new file mode 100644 index 0000000000..654eff29f9 --- /dev/null +++ b/dtool/src/interrogatedb/interrogate_request.h @@ -0,0 +1,70 @@ +// Filename: interrogate_request.h +// Created by: drose (01Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef INTERROGATE_REQUEST +#define INTERROGATE_REQUEST + +#include + +// The functions here are simple functions that are intended to be +// called during static init time for the various libraries that +// contain interrogate data. They provide interfaces to add the +// module's interrogate data to the main interrogate database. + +// The interface is entirely C here--no C++--so that it may be called +// from C modules if required. + + +#ifdef __cplusplus +extern "C" { +#endif + + +// This is the simplest interface. It just requests that the given +// database filename (*.in) be read in. This makes the interrogate +// data available, but doesn't allow matching the database information +// up with any compiled-in function wrappers or anything. +EXPCL_DTOOL void interrogate_request_database(const char *database_filename); + + + +// The more sophisticated interface uses these structures. + +typedef struct { + const char *name; + int index_offset; +} InterrogateUniqueNameDef; + +typedef struct { + int file_identifier; + + const char *library_name; + const char *library_hash_name; + const char *module_name; + const char *database_filename; + + InterrogateUniqueNameDef *unique_names; + int num_unique_names; + + void **fptrs; + int num_fptrs; + + int first_index; + int next_index; +} InterrogateModuleDef; + + +// This requests that the given module be loaded and made available. +// This includes all of the function pointers and/or unique names that +// might be compiled in. +EXPCL_DTOOL void interrogate_request_module(InterrogateModuleDef *def); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/dtool/src/interrogatedb/vector_int.cxx b/dtool/src/interrogatedb/vector_int.cxx new file mode 100644 index 0000000000..4ec4c84095 --- /dev/null +++ b/dtool/src/interrogatedb/vector_int.cxx @@ -0,0 +1,11 @@ +// Filename: vector_int.C +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#include "vector_int.h" + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma implementation +#endif diff --git a/dtool/src/interrogatedb/vector_int.h b/dtool/src/interrogatedb/vector_int.h new file mode 100644 index 0000000000..5213439a44 --- /dev/null +++ b/dtool/src/interrogatedb/vector_int.h @@ -0,0 +1,30 @@ +// Filename: vector_int.h +// Created by: drose (10May00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_INT_H +#define VECTOR_INT_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_int +// Description : A vector of ints. This class is defined once here, +// and exported to DTOOL.DLL; other packages that want +// to use a vector of this type (whether they need to +// export it or not) should include this header file, +// rather than defining the vector again. +//////////////////////////////////////////////////////////////////// + +EXPORT_TEMPLATE_CLASS(EXPCL_DTOOL, EXPTP_DTOOL, std::vector) +typedef vector vector_int; + +// Tell GCC that we'll take care of the instantiation explicitly here. +#ifdef __GNUC__ +#pragma interface +#endif + +#endif diff --git a/dtool/src/parser-inc/Sources.pp b/dtool/src/parser-inc/Sources.pp new file mode 100644 index 0000000000..dbbc4e4afd --- /dev/null +++ b/dtool/src/parser-inc/Sources.pp @@ -0,0 +1,3 @@ +#define INSTALL_PARSER_INC \ + deque iostream list map pair queue set stack stdtypedefs.h \ + string vector windows.h zlib.h diff --git a/dtool/src/parser-inc/deque b/dtool/src/parser-inc/deque new file mode 100644 index 0000000000..3d2074e0c7 --- /dev/null +++ b/dtool/src/parser-inc/deque @@ -0,0 +1,34 @@ +// Filename: deque +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef DEQUE_H +#define DEQUE_H + +#include + +template +class deque { +public: + typedef element value_type; + + typedef element *pointer; + typedef const element *const_pointer; + typedef element &reference; + typedef const element &const_reference; + + typedef pointer iterator; + typedef const_pointer const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t difference_type; + typedef size_t size_type; +}; + +#endif diff --git a/dtool/src/parser-inc/iostream b/dtool/src/parser-inc/iostream new file mode 100644 index 0000000000..067675a2df --- /dev/null +++ b/dtool/src/parser-inc/iostream @@ -0,0 +1,32 @@ +// Filename: iostream +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef IOSTREAM_H +#define IOSTREAM_H + +// We don't care about the actual definition of the various iostream +// classes, but we do need to know the classnames that are available. + +class iostream {}; +class ostream {}; +class istream {}; +class fstream {}; +class ofstream {}; +class ifstream {}; +class stringstream {}; +class ostringstream {}; +class istringstream {}; +class streambuf {}; + +extern istream cin; +extern ostream cout; +extern ostream cerr; + +#endif diff --git a/dtool/src/parser-inc/list b/dtool/src/parser-inc/list new file mode 100644 index 0000000000..cf89db637c --- /dev/null +++ b/dtool/src/parser-inc/list @@ -0,0 +1,34 @@ +// Filename: list +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef LIST_H +#define LIST_H + +#include + +template +class list { +public: + typedef element value_type; + + typedef element *pointer; + typedef const element *const_pointer; + typedef element &reference; + typedef const element &const_reference; + + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t size_type; + class difference_type; +}; + +#endif diff --git a/dtool/src/parser-inc/map b/dtool/src/parser-inc/map new file mode 100644 index 0000000000..533e39b78f --- /dev/null +++ b/dtool/src/parser-inc/map @@ -0,0 +1,45 @@ +// Filename: map +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef MAP_H +#define MAP_H + +#include +#include + +class default_map_compare; + +template +class map { +public: + typedef key key_type; + typedef element data_type; + typedef element mapped_type; + typedef pair value_type; + typedef compare key_compare; + + typedef element *pointer; + typedef const element *const_pointer; + typedef element &reference; + typedef const element &const_reference; + + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t size_type; + class difference_type; +}; + +template +class multimap : public map { +}; + +#endif diff --git a/dtool/src/parser-inc/pair b/dtool/src/parser-inc/pair new file mode 100644 index 0000000000..719abbdae3 --- /dev/null +++ b/dtool/src/parser-inc/pair @@ -0,0 +1,22 @@ +// Filename: pair +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef PAIR_H +#define PAIR_H + +template +class pair { +public: + a first; + b second; +}; + +#endif + diff --git a/dtool/src/parser-inc/queue b/dtool/src/parser-inc/queue new file mode 100644 index 0000000000..331737d4a0 --- /dev/null +++ b/dtool/src/parser-inc/queue @@ -0,0 +1,34 @@ +// Filename: queue +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef QUEUE_H +#define QUEUE_H + +#include + +template +class queue { +public: + typedef element value_type; + + typedef element *pointer; + typedef const element *const_pointer; + typedef element &reference; + typedef const element &const_reference; + + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t size_type; + class difference_type; +}; + +#endif diff --git a/dtool/src/parser-inc/set b/dtool/src/parser-inc/set new file mode 100644 index 0000000000..c49bfaa5c3 --- /dev/null +++ b/dtool/src/parser-inc/set @@ -0,0 +1,43 @@ +// Filename: set +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef SET_H +#define SET_H + +#include + +class default_set_compare; + +template +class set { +public: + typedef key key_type; + typedef key value_type; + typedef compare key_compare; + typedef compare value_compare; + + typedef key *pointer; + typedef const key *const_pointer; + typedef key &reference; + typedef const key &const_reference; + + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t size_type; + class difference_type; +}; + +template +class multiset : public set { +}; + +#endif diff --git a/dtool/src/parser-inc/stack b/dtool/src/parser-inc/stack new file mode 100644 index 0000000000..bd9e73ebe3 --- /dev/null +++ b/dtool/src/parser-inc/stack @@ -0,0 +1,34 @@ +// Filename: stack +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef STACK_H +#define STACK_H + +#include + +template +class stack { +public: + typedef element value_type; + + typedef element *pointer; + typedef const element *const_pointer; + typedef element &reference; + typedef const element &const_reference; + + class iterator; + class const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t size_type; + class difference_type; +}; + +#endif diff --git a/dtool/src/parser-inc/stdtypedefs.h b/dtool/src/parser-inc/stdtypedefs.h new file mode 100644 index 0000000000..f9017397d2 --- /dev/null +++ b/dtool/src/parser-inc/stdtypedefs.h @@ -0,0 +1,33 @@ +// Filename: stdtypedefs.h +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef STDTYPEDEFS_H +#define STDTYPEDEFS_H + +typedef unsigned int size_t; +typedef int off_t; +typedef unsigned int time_t; +typedef int ptrdiff_t; +typedef int clock_t; + +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned short ushort; +typedef unsigned char uchar; + +#define NULL ((void *)0) + +struct FILE; + +namespace std { +} + +#endif + diff --git a/dtool/src/parser-inc/string b/dtool/src/parser-inc/string new file mode 100644 index 0000000000..149d7434f1 --- /dev/null +++ b/dtool/src/parser-inc/string @@ -0,0 +1,35 @@ +// Filename: string +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef STRING_H +#define STRING_H + +#include + +template +class basic_string { +public: + basic_string(); + basic_string(const basic_string ©); + void operator = (const basic_string ©); + basic_string(const ctype *string); + ~basic_string(); + + const ctype *c_str() const; + size_t length() const; + + ctype at(size_t pos) const; + ctype operator[](size_t pos) const; + ctype &operator[](size_t pos); +}; + +typedef basic_string string; + +#endif diff --git a/dtool/src/parser-inc/vector b/dtool/src/parser-inc/vector new file mode 100644 index 0000000000..7f41c83e51 --- /dev/null +++ b/dtool/src/parser-inc/vector @@ -0,0 +1,34 @@ +// Filename: vector +// Created by: drose (12May00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef VECTOR_H +#define VECTOR_H + +#include + +template +class vector { +public: + typedef element value_type; + + typedef element *pointer; + typedef const element *const_pointer; + typedef element &reference; + typedef const element &const_reference; + + typedef pointer iterator; + typedef const_pointer const_iterator; + class reverse_iterator; + class const_reverse_iterator; + typedef size_t difference_type; + typedef size_t size_type; +}; + +#endif diff --git a/dtool/src/parser-inc/windows.h b/dtool/src/parser-inc/windows.h new file mode 100644 index 0000000000..d27aea42df --- /dev/null +++ b/dtool/src/parser-inc/windows.h @@ -0,0 +1,18 @@ +// Filename: windows.h +// Created by: drose (17Aug00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef WINDOWS_H +#define WINDOWS_H + +typedef bool BOOL; + +union LARGE_INTEGER { + __int64 QuadPart; +}; diff --git a/dtool/src/parser-inc/zlib.h b/dtool/src/parser-inc/zlib.h new file mode 100644 index 0000000000..2290823b67 --- /dev/null +++ b/dtool/src/parser-inc/zlib.h @@ -0,0 +1,18 @@ +// Filename: zlib.h +// Created by: drose (14Sep00) +// +//////////////////////////////////////////////////////////////////// + +// This file, and all the other files in this directory, aren't +// intended to be compiled--they're just parsed by CPPParser (and +// interrogate) in lieu of the actual system headers, to generate the +// interrogate database. + +#ifndef ZLIB_H +#define ZLIB_H + +class z_stream { +}; + +#endif + diff --git a/dtool/src/pystub/Sources.pp b/dtool/src/pystub/Sources.pp new file mode 100644 index 0000000000..c5e5f10fd2 --- /dev/null +++ b/dtool/src/pystub/Sources.pp @@ -0,0 +1,13 @@ +#define BUILDING_DLL BUILDING_DTOOL +#define LOCAL_LIBS dtoolbase + +#begin lib_target + #define TARGET pystub + + #define SOURCES \ + pystub.cxx pystub.h + + #define INSTALL_HEADERS \ + pystub.h + +#end lib_target diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx new file mode 100644 index 0000000000..61209a85ca --- /dev/null +++ b/dtool/src/pystub/pystub.cxx @@ -0,0 +1,47 @@ +// Filename: pystub.C +// Created by: drose (09Aug00) +// +//////////////////////////////////////////////////////////////////// + +#include "pystub.h" + +extern "C" { + EXPCL_DTOOL int PyArg_ParseTuple(...); + EXPCL_DTOOL int Py_BuildValue(...); + EXPCL_DTOOL int Py_InitModule4(...); + EXPCL_DTOOL int PyObject_IsTrue(...); + EXPCL_DTOOL int PyErr_SetString(...); + EXPCL_DTOOL extern void *PyExc_AssertionError; +}; + +int +PyArg_ParseTuple(...) { + return 0; +} + +int +Py_BuildValue(...) { + return 0; +} + +int +Py_InitModule4(...) { + return 0; +} + +int +PyObject_IsTrue(...) { + return 0; +} + +int +PyErr_SetString(...) { + return 0; +} + +void *PyExc_AssertionError = (void *)NULL; + + +void +pystub() { +} diff --git a/dtool/src/pystub/pystub.h b/dtool/src/pystub/pystub.h new file mode 100644 index 0000000000..ad11c1cd32 --- /dev/null +++ b/dtool/src/pystub/pystub.h @@ -0,0 +1,33 @@ +// Filename: pystub.h +// Created by: drose (08Aug00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef PYSTUB_H +#define PYSTUB_H + +#include + +// The sole purpose of this header file is to allow a program other +// than Python to load in a module that includes Python wrappers. + +// We need this if we build the Panda libraries with Python wrappers, +// but want to run a standalone program with those libraries. + +// This header file just stubs out the Python functions that these +// wrappers will call. You should include this header file in exactly +// one .C file in your project, preferably in the .C file that defines +// main(), and then link with -lpystub. Do not include this header +// file in a .C or .h file that will become part of an .so that might +// eventually link with Python. + + +// You might need to call this function in main() or somewhere to +// force the .so to be linked in--some OS'es try to be smart about not +// pulling in shared libraries whose symbols aren't referenced +// anywhere. +EXPCL_DTOOL void pystub(); + + +#endif + diff --git a/dtool/src/test_interrogate/Sources.pp b/dtool/src/test_interrogate/Sources.pp new file mode 100644 index 0000000000..66cd7e528c --- /dev/null +++ b/dtool/src/test_interrogate/Sources.pp @@ -0,0 +1,13 @@ +#define LOCAL_LIBS pystub interrogatedb dconfig dtoolutil dtoolbase + +#begin bin_target + #define TARGET test_interrogate + + #define SOURCES \ + test_interrogate.cxx +#end bin_target + +#begin test_bin_target + #define TARGET test_lib + #define SOURCES test_lib.cxx test_lib.h +#end test_bin_target diff --git a/dtool/src/test_interrogate/test_interrogate.cxx b/dtool/src/test_interrogate/test_interrogate.cxx new file mode 100644 index 0000000000..2b53c7a13c --- /dev/null +++ b/dtool/src/test_interrogate/test_interrogate.cxx @@ -0,0 +1,541 @@ +// Filename: test_interrogate.C +// Created by: drose (09Dec99) +// +//////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include +#include + +#include + +#ifndef HAVE_GETOPT +#include +#else +#include +#endif + + + +static ostream & +indent(ostream &out, int indent_level) { + for (int i = 0; i < indent_level; i++) { + out << ' '; + } + return out; +} + +void +show_type(int type, bool verbose = false) { + cout << interrogate_type_name(type) << " "; + if (verbose) { + if (strcmp(interrogate_type_name(type), + interrogate_type_true_name(type)) != 0) { + cout << "(" << interrogate_type_true_name(type) << ") "; + } + } + cout << "(" << type << ")"; +} + +void +show_function(int function) { + cout << interrogate_function_scoped_name(function) << " (" << function << ")"; +} + +void +describe_wrapper(int wrapper, int indent_level) { + indent(cout, indent_level) + << "Wrapper (" << wrapper << ")"; + + if (interrogate_wrapper_has_return_value(wrapper)) { + cout << " returns "; + show_type(interrogate_wrapper_return_type(wrapper)); + } else { + cout << " no return value"; + } + + int num_params = interrogate_wrapper_number_of_parameters(wrapper); + cout << ", "; + if (num_params == 0) { + cout << "no parameters.\n"; + } else if (num_params == 1) { + cout << "1 parameter:\n"; + } else { + cout << num_params << " parameters:\n"; + } + + for (int i = 0; i < num_params; i++) { + indent(cout, indent_level + 4); + if (interrogate_wrapper_parameter_is_this(wrapper, i)) { + cout << "*"; + } else { + cout << i; + } + cout << ": "; + show_type(interrogate_wrapper_parameter_type(wrapper, i)); + if (interrogate_wrapper_parameter_has_name(wrapper, i)) { + cout << " '" << interrogate_wrapper_parameter_name(wrapper, i) << "'"; + } else { + cout << " (no name)"; + } + cout << "\n"; + } + + + if (interrogate_wrapper_caller_manages_return_value(wrapper)) { + indent(cout, indent_level + 2) + << "Caller manages return value using "; + show_function(interrogate_wrapper_return_value_destructor(wrapper)); + cout << "\n"; + } + + indent(cout, indent_level + 2) + << "Wrapper name: " << interrogate_wrapper_name(wrapper); + if (interrogate_wrapper_is_callable_by_name(wrapper)) { + cout << " (callable)"; + } + cout << "\n"; + if (interrogate_wrapper_has_pointer(wrapper)) { + indent(cout, indent_level + 2) + << "Has pointer: " << interrogate_wrapper_pointer(wrapper) << "\n"; + } + string unique_name = interrogate_wrapper_unique_name(wrapper); + indent(cout, indent_level + 2) + << "Unique name is " << unique_name; + int reverse_lookup = interrogate_get_wrapper_by_unique_name(unique_name.c_str()); + if (reverse_lookup == 0) { + cout << " (no reverse lookup)"; + } else if (reverse_lookup != wrapper) { + cout << " (*** reverse lookup returns " << reverse_lookup << "! ***)"; + } + cout << "\n"; +} + +void +describe_function(int function, int indent_level) { + indent(cout, indent_level) + << "Function " << interrogate_function_scoped_name(function) + << " (" << function << ")\n"; + + indent(cout, indent_level + 2) + << "In C: " << interrogate_function_prototype(function) << "\n"; + + if (interrogate_function_is_method(function)) { + indent(cout, indent_level + 2) + << "Method of "; + show_type(interrogate_function_class(function)); + cout << "\n"; + } + + if (interrogate_function_is_virtual(function)) { + indent(cout, indent_level + 2) << "is virtual.\n"; + } + + int w; + + int num_c_wrappers = interrogate_function_number_of_c_wrappers(function); + if (num_c_wrappers == 0) { + } else if (num_c_wrappers == 1) { + indent(cout, indent_level + 2) + << "1 C-style wrapper:\n"; + } else { + indent(cout, indent_level + 2) + << num_c_wrappers << " C-style wrappers:\n"; + } + for (w = 0; w < num_c_wrappers; w++) { + describe_wrapper(interrogate_function_c_wrapper(function, w), + indent_level + 4); + } + + int num_python_wrappers = + interrogate_function_number_of_python_wrappers(function); + if (num_python_wrappers == 0) { + } else if (num_python_wrappers == 1) { + indent(cout, indent_level + 2) + << "1 Python-style wrapper:\n"; + } else { + indent(cout, indent_level + 2) + << num_python_wrappers << " Python-style wrappers:\n"; + } + for (w = 0; w < num_python_wrappers; w++) { + describe_wrapper(interrogate_function_python_wrapper(function, w), + indent_level + 4); + } + +} + +void +report_manifests() { + int num_manifests = interrogate_number_of_manifests(); + cout << "\n" << num_manifests << " manifests:\n"; + for (int i = 0; i < num_manifests; i++) { + int manifest = interrogate_get_manifest(i); + + cout << " Manifest " << interrogate_manifest_name(manifest); + if (interrogate_manifest_has_type(manifest)) { + cout << " of type "; + show_type(interrogate_manifest_get_type(manifest)); + cout << "\n"; + } else { + cout << " of unknown type\n"; + } + cout << " definition is \"" + << interrogate_manifest_definition(manifest) << "\"\n"; + + if (interrogate_manifest_has_getter(manifest)) { + cout << " value getter: "; + show_function(interrogate_manifest_getter(manifest)); + cout << "\n"; + } + + if (interrogate_manifest_has_int_value(manifest)) { + cout << " int value = " + << interrogate_manifest_get_int_value(manifest) + << "\n"; + } + } +} + +void +describe_element(int element, int indent_level) { + indent(cout, indent_level) + << "Element " << interrogate_element_scoped_name(element) + << " of type "; + show_type(interrogate_element_type(element)); + cout << "\n"; + + if (interrogate_element_has_getter(element)) { + indent(cout, indent_level + 2) + << "Getter is "; + show_function(interrogate_element_getter(element)); + cout << "\n"; + } + + if (interrogate_element_has_setter(element)) { + indent(cout, indent_level + 2) + << "Setter is "; + show_function(interrogate_element_setter(element)); + cout << "\n"; + } +} + +void +report_globals() { + int num_globals = interrogate_number_of_globals(); + cout << "\n" << num_globals << " globals:\n"; + for (int i = 0; i < num_globals; i++) { + describe_element(interrogate_get_global(i), 2); + } +} + +void +describe_type(int type, int indent_level) { + indent(cout, indent_level) << "Type "; + show_type(type, true); + cout << "\n"; + if (interrogate_type_is_nested(type)) { + indent(cout, indent_level + 2) + << "Nested within "; + show_type(interrogate_type_outer_class(type)); + cout << "\n"; + } + if (interrogate_type_is_atomic(type)) { + indent(cout, indent_level + 2) + << "atomic " << (int)interrogate_type_atomic_token(type) << "\n"; + } + if (interrogate_type_is_unsigned(type)) { + indent(cout, indent_level + 2) + << "unsigned\n"; + } + if (interrogate_type_is_signed(type)) { + indent(cout, indent_level + 2) + << "signed\n"; + } + if (interrogate_type_is_long(type)) { + indent(cout, indent_level + 2) + << "long\n"; + } + if (interrogate_type_is_longlong(type)) { + indent(cout, indent_level + 2) + << "long long\n"; + } + if (interrogate_type_is_short(type)) { + indent(cout, indent_level + 2) + << "short\n"; + } + if (interrogate_type_is_wrapped(type)) { + indent(cout, indent_level + 2) + << "wrapped "; + show_type(interrogate_type_wrapped_type(type)); + cout << "\n"; + } + if (interrogate_type_is_pointer(type)) { + indent(cout, indent_level + 2) + << "pointer\n"; + } + if (interrogate_type_is_const(type)) { + indent(cout, indent_level + 2) + << "const\n"; + } + if (interrogate_type_is_fully_defined(type)) { + indent(cout, indent_level + 2) + << "fully defined\n"; + } + if (interrogate_type_is_unpublished(type)) { + indent(cout, indent_level + 2) + << "undefined because unpublished\n"; + } + if (interrogate_type_is_enum(type)) { + indent(cout, indent_level + 2) + << "is enum type\n"; + } + if (interrogate_type_is_struct(type)) { + indent(cout, indent_level + 2) + << "is struct type\n"; + } + if (interrogate_type_is_class(type)) { + indent(cout, indent_level + 2) + << "is class type\n"; + } + if (interrogate_type_is_union(type)) { + indent(cout, indent_level + 2) + << "is union type\n"; + } + int num_enum_values = interrogate_type_number_of_enum_values(type); + if (num_enum_values > 0) { + for (int i = 0; i < num_enum_values; i++) { + indent(cout, indent_level + 4) + << interrogate_type_enum_value_name(type, i) + << " = " << interrogate_type_enum_value(type, i) << "\n"; + } + } + int num_constructors = interrogate_type_number_of_constructors(type); + if (num_constructors > 0) { + indent(cout, indent_level + 2) + << num_constructors << " constructors:\n"; + for (int i = 0; i < num_constructors; i++) { + describe_function(interrogate_type_get_constructor(type, i), 6); + } + } + if (interrogate_type_has_destructor(type)) { + indent(cout, indent_level + 2) + << "destructor:\n"; + describe_function(interrogate_type_get_destructor(type), 6); + } + int num_casts = interrogate_type_number_of_casts(type); + if (num_casts > 0) { + indent(cout, indent_level + 2) + << num_casts << " casts:\n"; + for (int i = 0; i < num_casts; i++) { + describe_function(interrogate_type_get_cast(type, i), 6); + } + } + int num_methods = interrogate_type_number_of_methods(type); + if (num_methods > 0) { + indent(cout, indent_level + 2) + << num_methods << " methods:\n"; + for (int i = 0; i < num_methods; i++) { + describe_function(interrogate_type_get_method(type, i), 6); + } + } + int num_elements = interrogate_type_number_of_elements(type); + if (num_elements > 0) { + indent(cout, indent_level + 2) + << num_elements << " elements:\n"; + for (int i = 0; i < num_elements; i++) { + describe_element(interrogate_type_get_element(type, i), indent_level + 2); + } + } + int num_derivations = interrogate_type_number_of_derivations(type); + if (num_derivations > 0) { + indent(cout, indent_level + 2) + << num_derivations << " derivations:\n"; + for (int i = 0; i < num_derivations; i++) { + int derivation = interrogate_type_get_derivation(type, i); + indent(cout, indent_level + 4); + show_type(derivation); + if (interrogate_type_derivation_has_upcast(type, i)) { + cout << " (has upcast)"; + } + if (interrogate_type_derivation_downcast_is_impossible(type, i)) { + cout << " (downcast is impossible)"; + } + if (interrogate_type_derivation_has_downcast(type, i)) { + cout << " (has downcast)"; + } + cout << "\n"; + /* + if (interrogate_type_derivation_has_upcast(type, i)) { + describe_function(interrogate_type_get_upcast(type, i), 8); + } + if (interrogate_type_derivation_has_downcast(type, i)) { + describe_function(interrogate_type_get_downcast(type, i), 8); + } + */ + } + } + int num_nested_types = interrogate_type_number_of_nested_types(type); + if (num_nested_types > 0) { + indent(cout, indent_level + 2) + << num_nested_types << " nested types:\n"; + for (int i = 0; i < num_nested_types; i++) { + indent(cout, indent_level + 4); + show_type(interrogate_type_get_nested_type(type, i)); + cout << "\n"; + } + } +} + +void +report_global_types() { + int num_types = interrogate_number_of_global_types(); + cout << "\n" << num_types << " global types:\n"; + + for (int i = 0; i < num_types; i++) { + int type = interrogate_get_global_type(i); + describe_type(type, 2); + } +} + +void +report_global_functions() { + int num_functions = interrogate_number_of_global_functions(); + cout << "\n" << num_functions << " global functions:\n"; + for (int i = 0; i < num_functions; i++) { + int function = interrogate_get_global_function(i); + describe_function(function, 2); + } +} + +void +report_all_types() { + int num_types = interrogate_number_of_types(); + cout << "\n" << num_types << " total types:\n"; + + for (int i = 0; i < num_types; i++) { + int type = interrogate_get_type(i); + describe_type(type, 2); + } +} + +void +report_all_functions() { + int num_functions = interrogate_number_of_functions(); + cout << "\n" << num_functions << " total functions:\n"; + for (int i = 0; i < num_functions; i++) { + int function = interrogate_get_function(i); + describe_function(function, 2); + } +} + +void +usage() { + cerr << + "\n" + "test_interrogate [opts] libfile.so [libfile.so ...]\n\n" + + "Loads the given shared library or libraries, if possible, and reports the\n" + "symbols, types, and functions available within those libraries as reported\n" + "by interrogate.\n\n" + + "In lieu of loading a shared library, you may also read the interrogate\n" + "database file directly by specifying a filename like libfile.in. This will\n" + "report the symbols defined in that file only (without pulling in dependent\n" + "files), and will not have any function pointers available.\n\n" + + "Options:\n\n" + " -f Give a detailed report of each function in the database, including\n" + " synthesized functions like upcasts and downcasts.\n" + " -t Give a detailed report of every type in the database, including types\n" + " like pointers and const pointers.\n" + " -q Quickly load up each shared library, if possible, and then immediately\n" + " exit. Useful for quickly determining whether a library can even load.\n\n"; +} + +int +main(int argc, char *argv[]) { + // extern char *optarg; + extern int optind; + const char *optstr = "ftqh"; + + bool all_functions = false; + bool all_types = false; + bool quick_load = false; + int flag = getopt(argc, argv, optstr); + + while (flag != EOF) { + switch (flag) { + case 'f': + all_functions = true; + break; + + case 't': + all_types = true; + break; + + case 'q': + quick_load = true; + break; + + case 'h': + usage(); + exit(0); + + default: + exit(1); + } + flag = getopt(argc, argv, optstr); + } + + argc -= (optind-1); + argv += (optind-1); + + if (argc < 2) { + cerr << "No libraries specified.\n"; + exit(1); + } + + int return_status = 0; + + for (int i = 1; i < argc; i++) { + string param = argv[i]; + + if (param.length() > 3 && param.substr(param.length() - 3) == ".in") { + // If the filename ends in ".in", it's an interrogate database + // file, not a shared library--read it directly. + interrogate_request_database(param.c_str()); + + } else { + // Otherwise, assume it's a shared library, and try to load it. + Filename pathname = Filename::dso_filename(argv[i]); + cerr << "Loading " << pathname << "\n"; + void *dl = load_dso(pathname); + if (dl == NULL) { + cerr << "Unable to load: " << load_dso_error() << "\n"; + return_status++; + } + } + } + + if (!quick_load) { + if (all_types) { + report_all_types(); + } + if (all_functions) { + report_all_functions(); + } + + if (!all_types && !all_functions) { + report_manifests(); + report_globals(); + report_global_types(); + report_global_functions(); + } + } + + return (return_status); +} diff --git a/dtool/src/test_interrogate/test_lib.cxx b/dtool/src/test_interrogate/test_lib.cxx new file mode 100644 index 0000000000..c6fff15ff3 --- /dev/null +++ b/dtool/src/test_interrogate/test_lib.cxx @@ -0,0 +1,32 @@ +// Filename: test_lib.C +// Created by: frang (15Jun00) +// +/////////////////////////////////////////////////////////////////////// + +#include "test_lib.h" + +int non_member1(float x, float y) { + return (int)(x + y); +} + +my_class1::my_class1(void) {} + +my_class1::my_class1(int) {} + +my_class1::~my_class1(void) {} + +float my_class1::method1(int x) { return (float)(x); } + +int my_class1::method2(float x) { return (int)(x); } + +int stupid_global; + +#include + +Configure(test_lib); + +ConfigureFn(test_lib) { + cerr << "In test_lib configure function!" << endl; +} + +ConfigureLibSym; diff --git a/dtool/src/test_interrogate/test_lib.h b/dtool/src/test_interrogate/test_lib.h new file mode 100644 index 0000000000..304237ef39 --- /dev/null +++ b/dtool/src/test_interrogate/test_lib.h @@ -0,0 +1,27 @@ +// Filename: test_lib.h +// Created by: frang (15Jun00) +// +/////////////////////////////////////////////////////////////////////// + +#ifndef __TEST_LIB_H__ +#define __TEST_LIB_H__ + +int non_member1(float x, float); + +class my_class1 { +public: + my_class1(void); + my_class1(int); + + float method1(int); + + int _member1; +private: + ~my_class1(void); + + int method2(float); + + int _member2; +}; + +#endif /* __TEST_LIB_H__ */