34#include "cling/Interpreter/Interpreter.h"
35#include "cling/Interpreter/LookupHelper.h"
36#include "cling/Utils/AST.h"
38#include "clang/AST/ASTContext.h"
39#include "clang/AST/Decl.h"
40#include "clang/AST/DeclCXX.h"
41#include "clang/AST/DeclTemplate.h"
42#include "clang/AST/GlobalDecl.h"
43#include "clang/AST/PrettyPrinter.h"
44#include "clang/AST/RecordLayout.h"
45#include "clang/AST/Type.h"
46#include "clang/Basic/Specifiers.h"
47#include "clang/Frontend/CompilerInstance.h"
48#include "clang/Sema/Sema.h"
50#include "llvm/ExecutionEngine/GenericValue.h"
51#include "llvm/Support/Casting.h"
52#include "llvm/Support/raw_ostream.h"
63 if (
const NamedDecl* ND = llvm::dyn_cast<NamedDecl>(decl)) {
64 PrintingPolicy Policy(decl->getASTContext().getPrintingPolicy());
65 llvm::raw_string_ostream stream(buf);
66 ND->getNameForDiagnostic(stream, Policy,
true);
72 :
TClingDeclInfo(nullptr), fInterp(interp), fFirstTime(true), fDescend(false), fIterAll(all),
73 fIsIter(true), fType(0), fOffsetCache(0)
75 TranslationUnitDecl *TU =
76 interp->getCI()->getASTContext().getTranslationUnitDecl();
83 :
TClingDeclInfo(nullptr), fInterp(interp), fFirstTime(true), fDescend(false), fIterAll(
kTRUE), fIsIter(false),
84 fType(0), fTitle(
""), fOffsetCache(0)
86 const cling::LookupHelper& lh =
fInterp->getLookupHelper();
88 const Decl *decl = lh.findScope(
name,
89 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
90 : cling::LookupHelper::NoDiagnostics,
91 &
type, intantiateTemplate);
95 decl = lh.findScope(buf,
96 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
97 : cling::LookupHelper::NoDiagnostics,
98 &
type, intantiateTemplate);
102 const TagType *tagtype =
type->getAs<TagType>();
104 decl = tagtype->getDecl();
109 if (decl && decl->isInvalidDecl()) {
110 Error(
"TClingClassInfo",
"Found an invalid decl for %s.",
name);
118 :
TClingDeclInfo(nullptr), fInterp(interp), fFirstTime(true), fDescend(false), fIterAll(
kTRUE),
119 fIsIter(false), fType(0), fTitle(
""), fOffsetCache(0)
126 :
TClingDeclInfo(nullptr), fInterp(interp), fFirstTime(true), fDescend(false), fIterAll(
kTRUE),
127 fIsIter(false), fType(0), fTitle(
""), fOffsetCache(0)
139 fOffsetCache[decl] = std::make_pair(offset, executableFunc);
148 const RecordDecl *RD = llvm::dyn_cast<RecordDecl>(
GetDecl());
151 cling::Interpreter::PushTransactionRAII RAII(
fInterp);
163 const CXXRecordDecl *CRD =
164 llvm::dyn_cast<CXXRecordDecl>(
GetDecl());
166 if (CRD->isAbstract()) {
169 if (CRD->hasUserDeclaredConstructor()) {
173 !CRD->hasUserDeclaredConstructor() &&
174 !CRD->hasTrivialDefaultConstructor()
179 CRD->hasUserProvidedDefaultConstructor() ||
180 !CRD->hasTrivialDefaultConstructor()
184 if (CRD->hasUserDeclaredDestructor()) {
187 else if (!CRD->hasTrivialDestructor()) {
190 if (CRD->hasUserDeclaredCopyAssignment()) {
193 if (CRD->isPolymorphic()) {
204 Error(
"TClingClassInfo::Delete()",
"Called while invalid!");
208 Error(
"TClingClassInfo::Delete()",
"Class is not loaded: %s",
229 Error(
"DeleteArray",
"Placement delete of an array is unsupported!\n");
256 const TypedefType *TT = llvm::dyn_cast<TypedefType>(
fType);
258 llvm::StringRef tname(TT->getDecl()->getName());
259 if (tname.equals(fname)) {
260 const NamedDecl *ndecl = llvm::dyn_cast<NamedDecl>(
GetDecl());
261 if (ndecl && !ndecl->getName().equals(fname)) {
268 const cling::LookupHelper &lh =
fInterp->getLookupHelper();
269 const FunctionTemplateDecl *fd
270 = lh.findFunctionTemplate(
GetDecl(), fname,
271 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
272 : cling::LookupHelper::NoDiagnostics,
false);
273 if (fd)
return fd->getCanonicalDecl();
282 const cling::LookupHelper &lh =
fInterp->getLookupHelper();
285 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
286 : cling::LookupHelper::NoDiagnostics);
287 if (vd)
return llvm::dyn_cast<ValueDecl>(vd->getCanonicalDecl());
303 const TypedefType *TT = llvm::dyn_cast<TypedefType>(
fType);
305 llvm::StringRef tname(TT->getDecl()->getName());
306 if (tname.equals(fname)) {
307 const NamedDecl *ndecl = llvm::dyn_cast<NamedDecl>(
GetDecl());
308 if (ndecl && !ndecl->getName().equals(fname)) {
310 return GetMethod(ndecl->getName().str().c_str());
315 const cling::LookupHelper &lh =
fInterp->getLookupHelper();
316 const FunctionDecl *fd
317 = lh.findAnyFunction(
GetDecl(), fname,
318 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
319 : cling::LookupHelper::NoDiagnostics,
339 const char *
proto,
bool objectIsConst,
354 const TypedefType *TT = llvm::dyn_cast<TypedefType>(
fType);
356 llvm::StringRef tname(TT->getDecl()->getName());
357 if (tname.equals(fname)) {
358 const NamedDecl *ndecl = llvm::dyn_cast<NamedDecl>(
GetDecl());
359 if (ndecl && !ndecl->getName().equals(fname)) {
362 objectIsConst,poffset,
369 const cling::LookupHelper& lh =
fInterp->getLookupHelper();
370 const FunctionDecl *fd;
373 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
374 : cling::LookupHelper::NoDiagnostics,
378 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
379 : cling::LookupHelper::NoDiagnostics,
382 Error(
"TClingClassInfo::GetMethod",
383 "The MatchMode %d is not supported.", mode);
401 const clang::DeclContext* ourDC = llvm::dyn_cast<clang::DeclContext>(
GetDecl());
402 if (!fd->getDeclContext()->Equals(ourDC)
403 && !(fd->getDeclContext()->isTransparentContext()
404 && fd->getDeclContext()->getParent()->Equals(ourDC)))
408 if (poffset) *poffset = 0;
412 if (
const CXXMethodDecl *md =
413 llvm::dyn_cast<CXXMethodDecl>(fd)) {
425 const llvm::SmallVectorImpl<clang::QualType> &
proto,
433 const llvm::SmallVectorImpl<clang::QualType> &
proto,
bool objectIsConst,
448 const TypedefType *TT = llvm::dyn_cast<TypedefType>(
fType);
450 llvm::StringRef tname(TT->getDecl()->getName());
451 if (tname.equals(fname)) {
452 const NamedDecl *ndecl = llvm::dyn_cast<NamedDecl>(
GetDecl());
453 if (ndecl && !ndecl->getName().equals(fname)) {
455 return GetMethod(ndecl->getName().str().c_str(),
proto,objectIsConst,poffset,
462 const cling::LookupHelper& lh =
fInterp->getLookupHelper();
463 const FunctionDecl *fd;
466 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
467 : cling::LookupHelper::NoDiagnostics,
471 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
472 : cling::LookupHelper::NoDiagnostics,
475 Error(
"TClingClassInfo::GetMethod",
476 "The MatchMode %d is not supported.", mode);
487 if (
const CXXMethodDecl *md =
488 llvm::dyn_cast<CXXMethodDecl>(fd)) {
506 const char *arglist,
bool objectIsConst,
514 const TypedefType *TT = llvm::dyn_cast<TypedefType>(
fType);
516 llvm::StringRef tname(TT->getDecl()->getName());
517 if (tname.equals(fname)) {
518 const NamedDecl *ndecl = llvm::dyn_cast<NamedDecl>(
GetDecl());
519 if (ndecl && !ndecl->getName().equals(fname)) {
521 return GetMethod(ndecl->getName().str().c_str(),arglist,
522 objectIsConst,poffset
536 if (!strcmp(arglist,
")")) {
540 const cling::LookupHelper &lh =
fInterp->getLookupHelper();
541 const FunctionDecl *fd
542 = lh.findFunctionArgs(
GetDecl(), fname, arglist,
543 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
544 : cling::LookupHelper::NoDiagnostics,
553 if (
const CXXMethodDecl *md =
554 llvm::dyn_cast<CXXMethodDecl>(fd)) {
579 clang_val =
static_cast<int>(num_params);
590 const CXXRecordDecl* definer = md->getParent();
591 const CXXRecordDecl* accessor =
592 llvm::cast<CXXRecordDecl>(
GetDecl());
593 if (definer != accessor) {
600 if (bci->
GetDecl() == definer) {
620 std::pair<ptrdiff_t, OffsetPtrFunc_t> offsetCache = (*iter).second;
623 return (*executableFunc)(address, isDerivedObject);
626 Error(
"TClingBaseClassInfo::Offset",
"The address of the object for virtual base offset calculation is not valid.");
631 return offsetCache.first;
639 return binfo.
Offset(address, isDerivedObject);
645 std::vector<std::string> res;
649 cling::Interpreter::PushTransactionRAII RAII(
fInterp);
650 const auto DC = dyn_cast<DeclContext>(
fDecl);
654 clang::PrintingPolicy policy(
fDecl->getASTContext().getPrintingPolicy());
655 for (
auto UD : DC->using_directives()) {
656 NamespaceDecl *NS = UD->getNominatedNamespace();
659 llvm::raw_string_ostream stream(nsName);
661 NS->getNameForDiagnostic(stream, policy,
true);
664 res.push_back(nsName);
685 return EIOCtorCategory::kAbsent;
687 auto CRD = llvm::dyn_cast<CXXRecordDecl>(
GetDecl());
690 return EIOCtorCategory::kAbsent;
693 auto kind = CheckIOConstructor(CRD,
"TRootIOCtor",
nullptr, *
fInterp);
694 if ((kind == EIOCtorCategory::kIORefType) || (kind == EIOCtorCategory::kIOPtrType)) {
695 if (type_name) *type_name =
"TRootIOCtor";
699 kind = CheckIOConstructor(CRD,
"__void__",
nullptr, *
fInterp);
700 if (kind == EIOCtorCategory::kIORefType) {
701 if (type_name) *type_name =
"__void__";
706 return CheckDefaultConstructor(CRD, *
fInterp) ? EIOCtorCategory::kDefault : EIOCtorCategory::kAbsent;
713 return fInterp->getLookupHelper()
715 gDebug > 5 ? cling::LookupHelper::WithDiagnostics
716 : cling::LookupHelper::NoDiagnostics);
726 fIter = DeclContext::decl_iterator();
730 const cling::LookupHelper& lh =
fInterp->getLookupHelper();
732 : cling::LookupHelper::NoDiagnostics,
737 SetDecl(lh.findScope(buf,
gDebug > 5 ? cling::LookupHelper::WithDiagnostics
738 : cling::LookupHelper::NoDiagnostics,
743 const TagType *tagtype =
fType->getAs<TagType>();
755 fIter = DeclContext::decl_iterator();
763 Fatal(
"TClingClassInfo::Init(tagnum)",
"Should no longer be called");
773 const TagType *tagtype =
fType->getAs<TagType>();
781 QualType qType(
fType,0);
782 static PrintingPolicy printPol(
fInterp->getCI()->getLangOpts());
783 printPol.SuppressScope =
false;
784 Error(
"TClingClassInfo::Init(const Type&)",
785 "The given type %s does not point to a Decl",
786 qType.getAsString(printPol).c_str());
802 const CXXRecordDecl *CRD =
803 llvm::dyn_cast<CXXRecordDecl>(
GetDecl());
809 const CXXRecordDecl *baseCRD =
810 llvm::dyn_cast<CXXRecordDecl>(base.
GetDecl());
811 return CRD->isDerivedFrom(baseCRD);
827 if (
auto *ED = llvm::dyn_cast<clang::EnumDecl>(
GetDecl()))
828 return ED->isScoped();
839 if (
auto ED = llvm::dyn_cast<EnumDecl>(
GetDecl())) {
841 auto Ty = ED->getIntegerType().getTypePtrOrNull();
842 if (
auto BTy = llvm::dyn_cast_or_null<BuiltinType>(Ty)) {
843 switch (BTy->getKind()) {
844 case BuiltinType::Bool:
847 case BuiltinType::Char_U:
848 case BuiltinType::UChar:
851 case BuiltinType::Char_S:
852 case BuiltinType::SChar:
855 case BuiltinType::UShort:
857 case BuiltinType::Short:
859 case BuiltinType::UInt:
861 case BuiltinType::Int:
863 case BuiltinType::ULong:
865 case BuiltinType::Long:
867 case BuiltinType::ULongLong:
869 case BuiltinType::LongLong:
895 const CXXRecordDecl *CRD = llvm::dyn_cast<CXXRecordDecl>(
GetDecl());
897 if (!CRD->hasDefinition()) {
901 const TagDecl *TD = llvm::dyn_cast<TagDecl>(
GetDecl());
902 if (TD && TD->getDefinition() == 0) {
933 cling::Interpreter::PushTransactionRAII RAII(
fInterp);
936 const clang::DeclContext *DC = cast<DeclContext>(
GetDecl());
938 fIter = DC->decls_begin();
940 fIter = DC->noload_decls_begin();
947 if (
const NamedDecl* ND =
948 llvm::dyn_cast<NamedDecl>(
GetDecl())) {
949 PrintingPolicy Policy(
GetDecl()->getASTContext().
950 getPrintingPolicy());
951 llvm::raw_string_ostream stream(buf);
952 ND->getNameForDiagnostic(stream, Policy,
false);
954 Error(
"TClingClassInfo::InternalNext",
955 "Next called but iteration not prepared for %s!", buf.c_str());
957 Error(
"TClingClassInfo::InternalNext",
958 "Next called but iteration not prepared!");
986 DeclContext *DC = llvm::cast<DeclContext>(*
fIter);
988 fIter = DC->decls_begin();
990 fIter = DC->noload_decls_begin();
1010 Decl::Kind DK =
fIter->getKind();
1011 if ((DK == Decl::Namespace) || (DK == Decl::Enum) ||
1012 (DK == Decl::CXXRecord) ||
1013 (DK == Decl::ClassTemplateSpecialization)) {
1014 const TagDecl *TD = llvm::dyn_cast<TagDecl>(*
fIter);
1015 if (TD && !TD->isCompleteDefinition()) {
1019 if (DK == Decl::Namespace) {
1021 if (!
fIter->isCanonicalDecl()) {
1027 if (DK != Decl::Enum) {
1029 DeclContext *DC = llvm::cast<DeclContext>(*
fIter);
1030 if ((
fIterAll && *DC->decls_begin())
1031 || (!
fIterAll && *DC->noload_decls_begin())) {
1041 if (
GetDecl()->isInvalidDecl()) {
1042 Warning(
"TClingClassInfo::Next()",
"Reached an invalid decl.");
1044 if (
const RecordDecl *RD =
1045 llvm::dyn_cast<RecordDecl>(
GetDecl())) {
1046 fType = RD->getASTContext().getRecordType(RD).getTypePtr();
1064 Error(
"TClingClassInfo::New()",
"Called while invalid!");
1068 Error(
"TClingClassInfo::New()",
"Class is not loaded: %s",
1074 std::string type_name;
1078 auto RD = dyn_cast<CXXRecordDecl>(
GetDecl());
1080 Error(
"TClingClassInfo::New()",
"This is a namespace!: %s",
1094 void* obj =
nullptr;
1099 Error(
"TClingClassInfo::New()",
"Call of default constructor "
1100 "failed to return an object for class: %s",
1113 Error(
"TClingClassInfo::New(n)",
"Called while invalid!");
1117 Error(
"TClingClassInfo::New(n)",
"Class is not loaded: %s",
1123 std::string type_name;
1128 auto RD = dyn_cast<CXXRecordDecl>(
GetDecl());
1130 Error(
"TClingClassInfo::New(n)",
"This is a namespace!: %s",
1144 void* obj =
nullptr;
1147 nullptr, (
unsigned long)
n);
1149 Error(
"TClingClassInfo::New(n)",
"Call of default constructor "
1150 "failed to return an array of class: %s",
1164 Error(
"TClingClassInfo::New(n, arena)",
"Called while invalid!");
1168 Error(
"TClingClassInfo::New(n, arena)",
"Class is not loaded: %s",
1174 std::string type_name;
1179 auto RD = dyn_cast<CXXRecordDecl>(
GetDecl());
1181 Error(
"TClingClassInfo::New(n, arena)",
"This is a namespace!: %s",
1195 void* obj =
nullptr;
1199 arena, (
unsigned long)
n);
1209 Error(
"TClingClassInfo::New(arena)",
"Called while invalid!");
1213 Error(
"TClingClassInfo::New(arena)",
"Class is not loaded: %s",
1219 std::string type_name;
1224 auto RD = dyn_cast<CXXRecordDecl>(
GetDecl());
1226 Error(
"TClingClassInfo::New(arena)",
"This is a namespace!: %s",
1240 void* obj =
nullptr;
1260 cling::Interpreter::PushTransactionRAII RAII(
fInterp);
1262 const clang::DeclContext *ctxt =
GetDecl()->getDeclContext();
1263 clang::NamespaceDecl *std_ns =
fInterp->getSema().getStdNamespace();
1264 while (! ctxt->isTranslationUnit()) {
1265 if (ctxt->Equals(std_ns)) {
1269 ctxt = ctxt->getParent();
1271 Decl::Kind DK =
GetDecl()->getKind();
1272 if ((DK == Decl::Namespace) || (DK == Decl::TranslationUnit)) {
1277 const TagDecl *TD = llvm::dyn_cast<TagDecl>(
GetDecl());
1286 const CXXRecordDecl *CRD =
1287 llvm::dyn_cast<CXXRecordDecl>(
GetDecl());
1288 if (CRD->isClass()) {
1291 else if (CRD->isStruct()) {
1294 else if (CRD->isUnion()) {
1297 if (CRD->hasDefinition() && CRD->isAbstract()) {
1324 Decl::Kind DK =
GetDecl()->getKind();
1325 if (DK == Decl::Namespace) {
1329 else if (DK == Decl::Enum) {
1333 const RecordDecl *RD = llvm::dyn_cast<RecordDecl>(
GetDecl());
1338 if (!RD->getDefinition()) {
1342 ASTContext &Context =
GetDecl()->getASTContext();
1343 cling::Interpreter::PushTransactionRAII RAII(
fInterp);
1344 const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
1345 int64_t size = Layout.getSize().getQuantity();
1346 int clang_size =
static_cast<int>(size);
1355 return reinterpret_cast<long>(
GetDecl());
1380 if (
const NamedDecl* ND =
1381 llvm::dyn_cast<NamedDecl>(
GetDecl())) {
1382 PrintingPolicy Policy(
GetDecl()->getASTContext().
1383 getPrintingPolicy());
1384 llvm::raw_string_ostream stream(
output);
1385 ND->getNameForDiagnostic(stream, Policy,
true);
1403 if (
const TagDecl *TD = llvm::dyn_cast<TagDecl>(
GetDecl())) {
1405 if (AnnotateAttr *A = TD->getAttr<AnnotateAttr>()) {
1406 std::string attr = A->getAnnotation().str();
1422 const CXXRecordDecl *CRD =
1423 llvm::dyn_cast<CXXRecordDecl>(
GetDecl());
1424 if (CRD && !CRD->isFromASTFile()) {
1439 TTHREAD_TLS_DECL( std::string, buf);
1441 if (
const NamedDecl* ND = llvm::dyn_cast<NamedDecl>(
GetDecl())) {
1443 buf = ND->getNameAsString();
static std::string FullyQualifiedName(const Decl *decl)
ptrdiff_t(* OffsetPtrFunc_t)(void *, bool)
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
void Fatal(const char *location, const char *msgfmt,...)
Use this function in case of a fatal error. It will abort the program.
R__EXTERN TVirtualMutex * gInterpreterMutex
#define R__LOCKGUARD(mutex)
#define R__WRITE_LOCKGUARD(mutex)
Emulation of the CINT BaseClassInfo class.
ptrdiff_t Offset(void *address=0, bool isDerivedObject=true) const
TClingClassInfo * GetBase() const
Emulation of the CINT CallFunc class.
void * ExecDefaultConstructor(const TClingClassInfo *info, ROOT::TMetaUtils::EIOCtorCategory kind, const std::string &type_name, void *address=nullptr, unsigned long nary=0UL)
void ExecDestructor(const TClingClassInfo *info, void *address=nullptr, unsigned long nary=0UL, bool withFree=true)
Emulation of the CINT ClassInfo class.
clang::DeclContext::decl_iterator fIter
static bool IsEnum(cling::Interpreter *interp, const char *name)
long ClassProperty() const
void Init(const char *name)
void FullName(std::string &output, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
llvm::DenseMap< const clang::Decl *, std::pair< ptrdiff_t, OffsetPtrFunc_t > > fOffsetCache
EDataType GetUnderlyingType() const
std::mutex fOffsetCacheMutex
TClingMethodInfo GetMethodWithArgs(const char *fname, const char *arglist, long *poffset, ROOT::EFunctionMatchMode mode=ROOT::kConversionMatch, EInheritanceMode imode=kWithInheritance) const
const char * TmpltName() const
void AddBaseOffsetValue(const clang::Decl *decl, ptrdiff_t offset)
ptrdiff_t GetBaseOffset(TClingClassInfo *toBase, void *address, bool isDerivedObject)
void SetDecl(const clang::Decl *D)
bool IsScopedEnum() const
ROOT::TMetaUtils::EIOCtorCategory HasDefaultConstructor(bool checkio=false, std::string *type_name=nullptr) const
const clang::FunctionTemplateDecl * GetFunctionTemplate(const char *fname) const
int GetMethodNArg(const char *method, const char *proto, Bool_t objectIsConst, ROOT::EFunctionMatchMode mode=ROOT::kConversionMatch) const
bool HasMethod(const char *name) const
std::string fDeclFileName
void DeleteArray(void *arena, bool dtorOnly, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
void * New(const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
bool IsValidMethod(const char *method, const char *proto, Bool_t objectIsConst, long *offset, ROOT::EFunctionMatchMode mode=ROOT::kConversionMatch) const
TClingMethodInfo GetMethod(const char *fname) const
long GetOffset(const clang::CXXMethodDecl *md) const
const clang::ValueDecl * GetDataMember(const char *name) const
void Destruct(void *arena, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
std::vector< std::string > GetUsingNamespaces()
cling::Interpreter * fInterp
std::vector< clang::DeclContext::decl_iterator > fIterStack
bool IsBase(const char *name) const
const clang::Type * fType
void Delete(void *arena, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
const clang::Decl * fDecl
virtual bool IsValid() const
virtual const clang::Decl * GetDecl() const
Emulation of the CINT MethodInfo class.
const clang::FunctionDecl * GetTargetFunctionDecl() const
Get the FunctionDecl, or if this represents a UsingShadowDecl, the underlying target FunctionDecl.
void Init(const clang::FunctionDecl *)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
R__EXTERN TVirtualRWMutex * gCoreMutex
std::string InsertStd(const char *tname)
static void output(int code)