/* Copyright (c) 1997-2004 Nick Ing-Simmons. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. */ #define PERL_NO_GET_CONTEXT #include #include #include #include "tkGlue.def" #include "pTk/tkPort.h" #include "pTk/tkInt.h" #include "tkGlue.h" static int Expire(int code) { return code; } int has_highbit(CONST char *s,int l) { CONST char *e = s+l; while (s < e) { if (*s++ & 0x80) return 1; } return 0; } SV * sv_maybe_utf8(SV *sv) { #ifdef SvUTF8_on if (SvPOK(sv)) { if (has_highbit(SvPVX(sv),SvCUR(sv))) SvUTF8_on(sv); } #endif return sv; } #define EXPIRE(args) \ ( Tcl_SprintfResult args, Expire(TCL_ERROR) ) /* * This file maps Tcl_Obj * onto perl's SV * * They are very similar. * One area of worry is that Tcl_Obj are created with refCount = 0, * while SV's have SvREFCNT == 1 * None the less normal idiom is * * Tcl_Obj *obj = Tcl_NewFooObj(...) * ... * Tcl_DecrRefCount(obj) * * So difference should be transparent. * * Also : * * Tcl_Obj *obj = Tcl_NewFooObj(...) * Tcl_ListAppendElement(list,obj); * * Again this is consistent with perl's assumption that refcount is 1 * and that av_push() does not increment it. * */ int Tcl_IsShared(Tcl_Obj *objPtr) { return SvREFCNT(objPtr) > 1; } void Tcl_IncrRefCount(Tcl_Obj *objPtr) { dTHX; SvREFCNT_inc(objPtr); } void Tcl_DecrRefCount(Tcl_Obj *objPtr) { dTHX; SvREFCNT_dec(objPtr); } static SV *ForceScalar(pTHX_ SV *sv); static SV *ForceScalarLvalue(pTHX_ SV *sv); static void Scalarize(pTHX_ SV *sv, AV *av) { int n = av_len(av)+1; if (n == 0) sv_setpvn(sv,"",0); else { SV **svp; if (n == 1 && (svp = av_fetch(av, 0, 0))) { STRLEN len = 0; char *s = SvPV(*svp,len); #ifdef SvUTF8 int utf8 = SvUTF8(*svp); sv_setpvn(sv,s,len); if (utf8) SvUTF8_on(sv); #else sv_setpvn(sv,s,len); #endif } else { Tcl_DString ds; int i; Tcl_DStringInit(&ds); for (i=0; i < n; i++) { if ((svp = av_fetch(av, i, 0))) { SV *el = *svp; int temp = 0; if (SvROK(el) && !SvOBJECT(SvRV(el)) && SvTYPE(SvRV(el)) == SVt_PVAV) { el = newSVpv("",0); temp = 1; if ((AV *) SvRV(*svp) == av) abort(); Scalarize(aTHX_ el,(AV *) SvRV(*svp)); } Tcl_DStringAppendElement(&ds,Tcl_GetString(el)); if (temp) SvREFCNT_dec(el); } } sv_setpvn(sv,Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); sv_maybe_utf8(sv); Tcl_DStringFree(&ds); } } } static SV * ForceScalar(pTHX_ SV *sv) { if (SvGMAGICAL(sv)) mg_get(sv); if (SvTYPE(sv) == SVt_PVAV) { AV *av = (AV *) sv; SV *newsv = newSVpv("",0); Scalarize(aTHX_ newsv, (AV *) av); av_clear(av); av_store(av,0,newsv); return newsv; } else { if (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVAV) { /* Callbacks and lists often get stringified by mistake due to Tcl/Tk's string fixation - don't change the real value */ SV *newsv = newSVpv("",0); Scalarize(aTHX_ newsv, (AV *) SvRV(sv)); return sv_2mortal(newsv); } else if (!SvOK(sv)) { /* Map undef to null string */ if (SvREADONLY(sv)) { SV *newsv = newSVpv("",0); return sv_2mortal(newsv); } else sv_setpvn(sv,"",0); } return sv; } } static SV * ForceScalarLvalue(pTHX_ SV *sv) { if (SvTYPE(sv) == SVt_PVAV) { AV *av = (AV *) sv; SV *newsv = newSVpv("",0); av_clear(av); av_store(av,0,newsv); return newsv; } else { return sv; } } void Tcl_SetBooleanObj (Tcl_Obj *objPtr, int value) { dTHX; sv_setiv(ForceScalarLvalue(aTHX_ objPtr),value != 0); } void Tcl_SetDoubleObj (Tcl_Obj *objPtr, double value) { dTHX; sv_setnv(ForceScalarLvalue(aTHX_ objPtr),value); } void Tcl_SetIntObj (Tcl_Obj *objPtr, int value) { dTHX; sv_setiv(ForceScalarLvalue(aTHX_ objPtr),value); } void Tcl_SetLongObj (Tcl_Obj *objPtr, long value) { dTHX; sv_setiv(ForceScalarLvalue(aTHX_ objPtr),value); } void Tcl_SetStringObj (Tcl_Obj *objPtr, CONST char *bytes, int length) { dTHX; if (length < 0) length = strlen(bytes); objPtr = ForceScalarLvalue(aTHX_ objPtr); sv_setpvn(objPtr, bytes, length); sv_maybe_utf8(objPtr); } int Tcl_GetLongFromObj (Tcl_Interp *interp, Tcl_Obj *obj, long *longPtr) { dTHX; SV *sv = ForceScalar(aTHX_ obj); if (SvIOK(sv) || looks_like_number(sv)) *longPtr = SvIV(sv); else { *longPtr = 0; return EXPIRE((interp, "'%s' isn't numeric", SvPVX(sv))); } return TCL_OK; } int Tcl_GetBooleanFromObj (Tcl_Interp *interp, Tcl_Obj *obj, int *boolPtr) { dTHX; SV *sv = ForceScalar(aTHX_ obj); static char *yes[] = {"y", "yes", "true", "on", NULL}; static char *no[] = {"n", "no", "false", "off", NULL}; if (SvPOK(sv)) { STRLEN na; char *s = SvPV(sv, na); char **p = yes; while (*p) { if (!strcasecmp(s, *p++)) { *boolPtr = 1; return TCL_OK; } } p = no; while (*p) { if (!strcasecmp(s, *p++)) { *boolPtr = 0; return TCL_OK; } } } *boolPtr = SvTRUE(sv); return TCL_OK; } int Tcl_GetIntFromObj (Tcl_Interp *interp, Tcl_Obj *obj, int *intPtr) { dTHX; SV *sv = ForceScalar(aTHX_ obj); if (SvIOK(sv) || looks_like_number(sv)) *intPtr = SvIV(sv); else { *intPtr = 0; return EXPIRE((interp, "'%s' isn't numeric", SvPVX(sv))); } return TCL_OK; } int Tcl_GetDoubleFromObj (Tcl_Interp *interp, Tcl_Obj *obj, double *doublePtr) { dTHX; SV *sv = ForceScalar(aTHX_ obj); if (SvNOK(sv) || looks_like_number(sv)) *doublePtr = SvNV(sv); else { *doublePtr = 0; return EXPIRE((interp, "'%s' isn't numeric", SvPVX(sv))); } return TCL_OK; } Tcl_Obj * Tcl_NewIntObj (int value) { dTHX; return newSViv(value); } Tcl_Obj * Tcl_NewBooleanObj (int value) { dTHX; return newSViv(value); } Tcl_Obj * Tcl_NewObj(void) { dTHX; return newSVsv(&PL_sv_undef); } Tcl_Obj * Tcl_NewLongObj(long value) { dTHX; return newSViv(value); } Tcl_Obj * Tcl_NewDoubleObj(double value) { dTHX; return newSVnv(value); } Tcl_Obj * Tcl_NewStringObj (CONST char *bytes, int length) { dTHX; if (bytes) { SV *sv; if (length < 0) length = strlen(bytes); sv = newSV(length); sv_setpvn(sv,(char *)bytes,length); return sv_maybe_utf8(sv); } else return &PL_sv_undef; } Tcl_Obj * Tcl_NewListObj (int objc, Tcl_Obj *CONST objv[]) { dTHX; AV *av = newAV(); if (objc) { while (objc-- > 0) { SV *sv = objv[objc]; if (sv) { /* tkConfig.c passes Tcl_NewStringObj() or LangSetDefault() so REFCNT should be ok as-is */ if (SvREFCNT(sv) <= 0 || SvTEMP(sv)) { LangDebug("%s %d:\n",__FUNCTION__, objc); sv_dump(sv); } av_store(av,objc,sv); } } } return MakeReference((SV *) av); } static char * LangString(SV *sv) { dTHX; if (!sv) return ""; if (SvGMAGICAL(sv)) mg_get(sv); if (SvPOK(sv)) { if (!SvUTF8(sv)) sv_utf8_upgrade(sv); return SvPV_nolen(sv); } else { if (SvROK(sv)) { SV *rv = SvRV(sv); STRLEN len; char *s; if (SvOBJECT(rv)) { /* Special case "our" objects and certainb legacy hacks ... */ if (SvTYPE(rv) == SVt_PVHV) { SV **p = hv_fetch((HV *) rv,"_TkValue_",9,0); if (p) { return SvPV_nolen(*p); } else { Lang_CmdInfo *info = WindowCommand(sv, NULL, 0); if (info) { if (info->tkwin) { char *val = Tk_PathName(info->tkwin); hv_store((HV *) rv,"_TkValue_",9,Tcl_NewStringObj(val,strlen(val)),0); return val; } if (info->image) { return SvPV_nolen(info->image); } } } } else if (SvPOK(rv)) { /* ref to string is special cased for some reason ? */ if (!SvUTF8(rv)) sv_utf8_upgrade(rv); return SvPV_nolen(rv); } } /* Object */ s = SvPV(sv, len); if (!is_utf8_string(s,len)) { sv_setpvn(sv,s,len); sv_utf8_upgrade(sv); s = SvPV(sv, len); } if (!is_utf8_string(s,len)) { LangDebug("%s @ %d not utf8 '%.*s'\n",__FUNCTION__,__LINE__,(int) len, s); sv_dump(sv); abort(); } return s; } /* reference */ else if (SvOK(sv)) { if (SvROK(sv) && SvPOK(SvRV(sv)) && !SvUTF8(SvRV(sv))) sv_utf8_upgrade(SvRV(sv)); else if (SvPOKp(sv) && !SvPOK(sv)) { if (SvTYPE(sv) == SVt_PVLV && !SvUTF8(sv)) { /* LVs e.g. substr() don't upgrade */ SV *copy = newSVsv(sv); sv_utf8_upgrade(copy); sv_setsv(sv,copy); SvREFCNT_dec(copy); } else { /* Slaven's for magical (tied) SVs with only SvPOKp */ SvPOK_on(sv); sv_utf8_upgrade(sv); SvPOK_off(sv); SvPOKp_on(sv); } } return SvPVutf8_nolen(sv); } else return ""; } } void utf8Whoops(pTHX_ SV *objPtr) { sv_utf8_upgrade(objPtr); sv_dump(objPtr); } char * Tcl_GetStringFromObj (Tcl_Obj *objPtr, int *lengthPtr) { if (objPtr) { dTHX; char *s; if ((SvROK(objPtr) && !SvOBJECT(SvRV(objPtr)) && SvTYPE(SvRV(objPtr)) == SVt_PVAV) || (SvTYPE(objPtr) == SVt_PVAV)) objPtr = ForceScalar(aTHX_ objPtr); if (SvPOK(objPtr)) { STRLEN len; #ifdef SvUTF8 if (!SvUTF8(objPtr)) sv_utf8_upgrade(objPtr); #endif s = SvPV(objPtr, len); #ifdef SvUTF8 if (!is_utf8_string(s,len)) { LangDebug("%s @ %d not utf8\n",__FUNCTION__,__LINE__); sv_dump(objPtr); utf8Whoops(aTHX_ objPtr); s = SvPV(objPtr, len); if (!is_utf8_string(s,len)) { U8 *p = (U8 *) s; U8 *e = p + len; while (p < e) { if (*p > 0x7F) *p = '?'; p++; } } } #endif if (lengthPtr) *lengthPtr = len; } else { s = LangString(objPtr); #ifdef SvUTF8 if (!is_utf8_string(s,strlen(s))) { LangDebug("%s @ %d not utf8\n",__FUNCTION__,__LINE__); sv_dump(objPtr); abort(); } #endif if (lengthPtr) *lengthPtr = strlen(s); } return s; } return NULL; } char * Tcl_GetString(Tcl_Obj *objPtr) { return Tcl_GetStringFromObj(objPtr, NULL); } unsigned char * Tcl_GetByteArrayFromObj(Tcl_Obj * objPtr, int * lengthPtr) { /* FIXME: presumably should downgrade from UTF-8, what frees it ? */ return (unsigned char *) Tcl_GetStringFromObj (objPtr, lengthPtr); } AV * ForceList(pTHX_ Tcl_Interp *interp, Tcl_Obj *sv) { if (SvTYPE(sv) == SVt_PVAV) { return (AV *) sv; } else { int object = sv_isobject(sv); if (!object && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { return (AV *) SvRV(sv); } else { AV *av = newAV(); if (!object && (SvIOK(sv) || SvNOK(sv))) { /* Simple case of single number */ av_store(av,0,SvREFCNT_inc(sv)); } else { /* Parse TCL like strings {} are quotes - and can be nested \ quotes \ itself and whitespace Older Tk used this perl code ... local $_ = shift; my (@arr, $tmp); while (/\{([^{}]*)\}|((?:[^\s\\]|\\.)+)/gs) { if (defined $1) { push @arr, $1 } else { $tmp = $2 ; $tmp =~ s/\\([\s\\])/$1/g; push @arr, $tmp } } */ unsigned char *s = (unsigned char *) Tcl_GetString(sv); int i = 0; while (*s) { unsigned char *base; /* Skip leading whitespace */ while (isspace(*s)) s++; if (!*s) break; base = s; if (*s == '{') { /* Slurp chars till we find matching '}' */ int count = 1; /* number of open '{' */ base = ++s; while (*s) { if (*s == '{') count++; else if (*s == '}' && (--count <= 0)) break; s++; } if (*s != '}') { /* Found end of string before closing '}' TCL would set an error, we will just include the un-matched opening '{' in the string. */ base--; } } else if (*s) { /* Find a "word" */ while (*s && !isspace(*s)) { if (*s == '\\' && s[1]) /* \ quotes anything except end of string */ s++; s++; } } av_store(av,i++,Tcl_NewStringObj(base,(s-base))); if (*s == '}') s++; } } /* Now have an AV populated decide how to return */ if (SvREADONLY(sv)) { sv_2mortal((SV *) av); return av; } else { SV *ref = MakeReference((SV *) av); SvSetMagicSV(sv,ref); SvREFCNT_dec(ref); } return (AV *) SvRV(sv); } } } void Tcl_SetListObj(Tcl_Obj * objPtr,int objc, Tcl_Obj *CONST objv[]) { dTHX; AV *av = ForceList(aTHX_ NULL,objPtr); av_clear(av); while (objc-- > 0) { /* Used by tkListbox.c passing in array from Tcl_ListObjGetEelements() * so we need to increment REFCNT */ av_store(av,objc,SvREFCNT_inc(objv[objc])); } } int Tcl_ListObjAppendElement (Tcl_Interp *interp, Tcl_Obj *listPtr, Tcl_Obj *objPtr) { dTHX; AV *av = ForceList(aTHX_ interp,listPtr); if (!objPtr) objPtr = &PL_sv_undef; if (av) { av_push(av, objPtr); return TCL_OK; } return TCL_ERROR; } void Tcl_AppendElement(interp, string) Tcl_Interp *interp; CONST char *string; { dTHX; Tcl_Obj *result = Tcl_GetObjResult(interp); Tcl_Obj *value = Tcl_NewStringObj(string,-1); if (1 || SvOK(result)) { Tcl_ListObjAppendElement(interp,result,value); } else { SvSetMagicSV(result, value); LangDumpVec(__FUNCTION__,1,&result); } } AV * MaybeForceList(pTHX_ Tcl_Interp *interp, Tcl_Obj *sv) { AV *av; int object = sv_isobject(sv); if (!object && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) { return (AV *) SvRV(sv); } else if (!object && (SvIOK(sv) || SvNOK(sv))) { av = newAV(); av_store(av,0,SvREFCNT_inc(sv)); sv_2mortal((SV *) av); return av; } else if (SvREADONLY(sv)) { /* returns mortal list anyway */ return ForceList(aTHX_ interp,sv); } else { SvREADONLY_on(sv); av = ForceList(aTHX_ interp,sv); SvREADONLY_off(sv); /* If there was more than one element set the SV */ if (av && av_len(av) > 0) { /* AV is mortal - so we want newRV not MakeReference as we need extra REFCNT */ SV *ref = newRV((SV *) av); SvSetMagicSV(sv,ref); SvREFCNT_dec(ref); } return av; } } int Tcl_ListObjGetElements (Tcl_Interp *interp, Tcl_Obj *listPtr, int *objcPtr, Tcl_Obj ***objvPtr) { if (listPtr) { dTHX; AV *av = MaybeForceList(aTHX_ interp,listPtr); if (av) { *objcPtr = av_len(av)+1; *objvPtr = AvARRAY(av); return TCL_OK; } } *objcPtr = 0; *objvPtr = NULL; return TCL_OK; } int Tcl_ListObjIndex (Tcl_Interp *interp, Tcl_Obj *listPtr, int index, Tcl_Obj **objPtrPtr) { dTHX; AV *av = ForceList(aTHX_ interp,listPtr); if (av) { SV **svp = av_fetch(av, index, 0); if (svp) { *objPtrPtr = *svp; return TCL_OK; } return EXPIRE((interp, "No element %d",index)); } return TCL_ERROR; } int Tcl_ListObjLength (Tcl_Interp *interp, Tcl_Obj *listPtr, int *intPtr) { dTHX; AV *av = ForceList(aTHX_ interp,listPtr); if (av) { *intPtr = av_len(av)+1; return TCL_OK; } return TCL_ERROR; } int Tcl_ListObjReplace (Tcl_Interp *interp, Tcl_Obj *listPtr, int first, int count, int objc, Tcl_Obj *CONST objv[]) { dTHX; AV *av = ForceList(aTHX_ interp,listPtr); if (av) { int len = av_len(av)+1; int newlen; int i; if (first < 0) first = 0; if (first > len) first = len; if (first + count > len) count = first-len; newlen = len-count+objc; if (newlen > len) { /* Move entries beyond old range up to make room for new */ av_extend(av,newlen-1); for (i=len-1; i >= (first+count); i--) { SV **svp = av_fetch(av,i,0); if (svp) av_store(av,i+newlen-len,SvREFCNT_inc(*svp)); } } else if (newlen < len) { /* Move entries beyond old range down to new location */ for (i=first+count; i < len; i++) { SV **svp = av_fetch(av,i,0); if (svp) av_store(av,i+newlen-len,SvREFCNT_inc(*svp)); } #ifdef AvFILLp AvFILLp(av) = newlen-1; #else AvFILL(av) = newlen-1; #endif } /* Store new values */ for (i=0; i < objc; i++) { /* In tkListbox.c used with incoming objv * so we need to make copies */ av_store(av,first+i,newSVsv(objv[i])); } return TCL_OK; } return TCL_ERROR; } int Tcl_ListObjAppendList(Tcl_Interp * interp, Tcl_Obj * listPtr,Tcl_Obj * elemListPtr) { dTHX; Tcl_Obj **objv; int objc = 0; int code; AV *av = ForceList(aTHX_ interp,listPtr); if ((code = Tcl_ListObjGetElements(interp,elemListPtr,&objc,&objv)) == TCL_OK) { dTHX; int j = av_len(av)+1; int i; for (i=0; i < objc; i++) { av_store(av,j++,objv[i]); } } return code; } Tcl_Obj * Tcl_ConcatObj (int objc, Tcl_Obj *CONST objv[]) { /* This is very like Tcl_NewListObj() - but is typically called on a command's objv - which will not have REFCNT set way Tcl_NewListObj() is expecting. So correct that then call Tcl_NewListObj(). */ dTHX; int i; for (i=0; i < objc; i++) { SV *sv = (SV *)objv[i]; if (sv) { SvREFCNT_inc(sv); } } return Tcl_NewListObj (objc, objv); } char * Tcl_DStringAppendElement(dsPtr, string) Tcl_DString *dsPtr; /* Structure describing dynamic string. */ CONST char *string; /* String to append. Must be * null-terminated. */ { CONST char *s = string; int ch; while ((ch = *s)) { if (isspace(ch)) break; s++; } if (Tcl_DStringLength(dsPtr)) { Tcl_DStringAppend(dsPtr, " ", 1); } if (*s) { Tcl_DStringAppend(dsPtr, "{", 1); } Tcl_DStringAppend(dsPtr, string, -1); if (*s) { Tcl_DStringAppend(dsPtr, "}", 1); } return Tcl_DStringValue(dsPtr); } void Tcl_AppendStringsToObj (Tcl_Obj *obj,...) { dTHX; va_list ap; char *s; SV *sv = ForceScalar(aTHX_ obj); va_start(ap,obj); while ((s = va_arg(ap,char *))) { Tcl_AppendToObj(sv,s,-1); } va_end(ap); if (sv != obj && SvROK(obj)) { SvSetMagicSV(obj,sv); } } /* *---------------------------------------------------------------------- * * Tcl_GetIndexFromObj -- * * This procedure looks up an object's value in a table of strings * and returns the index of the matching string, if any. * * Results: * If the value of objPtr is identical to or a unique abbreviation * for one of the entries in objPtr, then the return value is * TCL_OK and the index of the matching entry is stored at * *indexPtr. If there isn't a proper match, then TCL_ERROR is * returned and an error message is left in interp's result (unless * interp is NULL). The msg argument is used in the error * message; for example, if msg has the value "option" then the * error message will say something flag 'bad option "foo": must be * ...' * * Side effects: * The result of the lookup is cached as the internal rep of * objPtr, so that repeated lookups can be done quickly. * *---------------------------------------------------------------------- */ int Tcl_GetIndexFromObj(interp, objPtr, tablePtr, msg, flags, indexPtr) Tcl_Interp *interp; /* Used for error reporting if not NULL. */ Tcl_Obj *objPtr; /* Object containing the string to lookup. */ CONST char **tablePtr; /* Array of strings to compare against the * value of objPtr; last entry must be NULL * and there must not be duplicate entries. */ CONST char *msg; /* Identifying word to use in error messages. */ int flags; /* 0 or TCL_EXACT */ int *indexPtr; /* Place to store resulting integer index. */ { int index, length, i, numAbbrev; CONST char *key, *p1, *p2, **entryPtr; Tcl_Obj *resultPtr; /* * Lookup the value of the object in the table. Accept unique * abbreviations unless TCL_EXACT is set in flags. */ key = Tcl_GetStringFromObj(objPtr, &length); index = -1; numAbbrev = 0; for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) { for (p1 = key, p2 = *entryPtr; *p1 == *p2; p1++, p2++) { if (*p1 == 0) { index = i; goto done; } } if (*p1 == 0) { /* * The value is an abbreviation for this entry. Continue * checking other entries to make sure it's unique. If we * get more than one unique abbreviation, keep searching to * see if there is an exact match, but remember the number * of unique abbreviations and don't allow either. */ numAbbrev++; index = i; } } if ((flags & TCL_EXACT) || (numAbbrev != 1)) { goto error; } done: *indexPtr = index; return TCL_OK; error: if (interp != NULL) { resultPtr = Tcl_GetObjResult(interp); Tcl_AppendStringsToObj(resultPtr, (numAbbrev > 1) ? "ambiguous " : "bad ", msg, " \"", key, "\": must be ", *tablePtr, (char *) NULL); for (entryPtr = tablePtr+1; *entryPtr != NULL; entryPtr++) { if (entryPtr[1] == NULL) { Tcl_AppendStringsToObj(resultPtr, ", or ", *entryPtr, (char *) NULL); } else { Tcl_AppendStringsToObj(resultPtr, ", ", *entryPtr, (char *) NULL); } } } return TCL_ERROR; } void Tcl_AppendToObj(objPtr, bytes, length) register Tcl_Obj *objPtr; /* Points to the object to append to. */ CONST char *bytes; /* Points to the bytes to append to the * object. */ register int length; /* The number of bytes to append from * "bytes". If < 0, then append all bytes * up to NULL byte. */ { dTHX; SV *sv = ForceScalar(aTHX_ objPtr); int hi; if (length < 0) length = strlen(bytes); #ifdef SvUTF8 if ((hi = has_highbit(bytes,length))) { sv_utf8_upgrade(sv); } sv_catpvn(sv, bytes, length); if (hi) SvUTF8_on(sv); #else sv_catpvn(sv, bytes, length); #endif if (sv != objPtr && SvROK(objPtr)) SvSetMagicSV(objPtr,sv); } void Tcl_AppendObjToObj(Tcl_Obj * objPtr,Tcl_Obj * appendObjPtr) { int len = 0; char *s = Tcl_GetStringFromObj(appendObjPtr,&len); Tcl_AppendToObj(objPtr,s,len); } void Tcl_WrongNumArgs(interp, objc, objv, message) Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments to print * from objv. */ Tcl_Obj *CONST objv[]; /* Initial argument objects, which * should be included in the error * message. */ CONST char *message; /* Error message to print after the * leading objects in objv. The * message may be NULL. */ { Tcl_Obj *objPtr; char **tablePtr; int i; objPtr = Tcl_GetObjResult(interp); Tcl_AppendToObj(objPtr, "wrong # args: should be \"", -1); for (i = 0; i < objc; i++) { Tcl_AppendStringsToObj(objPtr, Tcl_GetStringFromObj(objv[i], (int *) NULL), (char *) NULL); if (i < (objc - 1)) { Tcl_AppendStringsToObj(objPtr, " ", (char *) NULL); } } if (message) { Tcl_AppendStringsToObj(objPtr, " ", message, (char *) NULL); } Tcl_AppendStringsToObj(objPtr, "\"", (char *) NULL); } #define DStringSV(svp) ((*svp) ? (*svp = ForceScalar(aTHX_ *svp)) : (*svp = newSVpv("",0), *svp)) #undef Tcl_DStringInit void Tcl_DStringInit(Tcl_DString *svp) { *svp = NULL; } void Tcl_DbDStringInit(Tcl_DString *svp,char *file,int line) { Tcl_DStringInit(svp); } void Tcl_DStringFree(Tcl_DString *svp) { SV *sv; if ((sv = *svp)) { dTHX; SvREFCNT_dec(sv); *svp = Nullsv; } } void Tcl_DStringResult(Tcl_Interp *interp, Tcl_DString *svp) { dTHX; SV *sv = DStringSV(svp); /* Tcl8.1+ strings are UTF-8 */ Tcl_SetObjResult(interp,sv_maybe_utf8(sv)); /* Now "free" the DString - the SvREFCNT_dec has been done by SetObjResult */ *svp = Nullsv; } char * Tcl_DStringAppend(Tcl_DString *svp, CONST char *s, int len) { dTHX; SV *sv = DStringSV(svp); Tcl_AppendToObj(sv,(char *)s,len); return SvPVX(sv); } int Tcl_DStringLength(Tcl_DString *svp) { dTHX; return (int) ((*svp) ? SvCUR(DStringSV(svp)) : 0); } void Tcl_DStringSetLength(Tcl_DString *svp,int len) { dTHX; SV *sv = DStringSV(svp); char *s = SvGROW(sv,(Size_t)(len+1)); s[len] = '\0'; SvCUR(sv) = len; } char * Tcl_DStringValue(Tcl_DString *svp) { dTHX; SV *sv = DStringSV(svp); STRLEN len; return SvPV(sv,len); } void Tcl_DStringGetResult(Tcl_Interp *interp, Tcl_DString *svp) { int len; char *s = Tcl_GetStringFromObj(Tcl_GetObjResult(interp),&len); Tcl_DStringAppend(svp,s,len); } /* Now fake Tcl_Obj * internals routines */ static void DummyFreeProc(Tcl_Obj *obj) { } static void IntUpdateStringProc(Tcl_Obj *obj) { dTHX; STRLEN len; (void) SvPV(obj,len); } static void IntDupProc(Tcl_Obj *src,Tcl_Obj *dst) { dTHX; SvSetMagicSV(dst,src); TclObjSetType(dst,TclObjGetType(src)); } static int IntSetFromAnyProc(Tcl_Interp *interp, Tcl_Obj *obj) { Tcl_ObjType *typePtr; Tcl_GetString(obj); typePtr = TclObjGetType(obj); if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) { (*typePtr->freeIntRepProc)(obj); } TclObjSetType(obj,&tclIntType); return TCL_OK; } extern Tcl_ObjType tclDoubleType; static int DoubleSetFromAnyProc(Tcl_Interp *interp, Tcl_Obj *obj) { Tcl_ObjType *typePtr; Tcl_GetString(obj); typePtr = TclObjGetType(obj); if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) { (*typePtr->freeIntRepProc)(obj); } TclObjSetType(obj,&tclDoubleType); return TCL_OK; } Tcl_ObjType tclIntType = { "int", DummyFreeProc, IntDupProc, IntUpdateStringProc, IntSetFromAnyProc }; Tcl_ObjType tclDoubleType = { "double", DummyFreeProc, IntDupProc, IntUpdateStringProc, DoubleSetFromAnyProc }; Tcl_ObjType perlDummyType = { "scalar", DummyFreeProc, IntDupProc, IntUpdateStringProc, IntSetFromAnyProc }; typedef struct { Tcl_ObjType *type; Tcl_InternalRep internalRep; } TclObjMagic_t; static int TclObj_get(pTHX_ SV *sv, MAGIC *mg) { TclObjMagic_t *info = (TclObjMagic_t *)SvPVX(mg->mg_obj); if (info->type == &tclIntType) { SvIV_set(sv,info->internalRep.longValue); SvIOK_on(sv); LangDebug("%s %p %s %ld'\n",__FUNCTION__,sv,info->type->name,SvIV(sv)); return 0; } else if (info->type == &tclDoubleType) { SvNV_set(sv,info->internalRep.doubleValue); SvNOK_on(sv); LangDebug("%s %p %s %g'\n",__FUNCTION__,sv,info->type->name,SvNV(sv)); return 0; } else if (SvROK(sv) || info->type == &perlDummyType) { if (!SvPOK(sv) && SvPOKp(sv)) SvPOK_on(sv); if (!SvNOK(sv) && SvNOKp(sv)) SvNOK_on(sv); if (!SvIOK(sv) && SvIOKp(sv)) SvIOK_on(sv); } else { Tcl_GetString(sv); SvPOK_on(sv); #if 0 LangDebug("%s %p %s '%s'\n",__FUNCTION__,sv,info->type->name,SvPV_nolen(sv)); #endif } return 0; } static int TclObj_free(pTHX_ SV *sv, MAGIC *mg) { TclObjMagic_t *info = (TclObjMagic_t *)SvPVX(mg->mg_obj); if (info->type) { #ifdef DEBUG_TCLOBJ LangDebug("%s %p %s\n",__FUNCTION__,sv,info->type->name); #endif if (info->type->freeIntRepProc != NULL) { /* We _use_ MAGIC chain to locate interal rep so * re-link mg for duration of callback */ MAGIC *save = SvMAGIC(sv); SvMAGIC(sv) = mg; mg->mg_moremagic = NULL; (*info->type->freeIntRepProc)(sv); SvMAGIC(sv) = save; } } else { /* We can have pretened we are double or int without setting a type */ #if 0 LangDebug("%s %p NULL\n",__FUNCTION__,sv); sv_dump(sv); #endif } return 0; } static int TclObj_set(pTHX_ SV *sv, MAGIC *mg) { #ifdef DEBUG_TCLOBJ TclObjMagic_t *info = (TclObjMagic_t *)SvPVX(mg->mg_obj); LangDebug("%s %p %s\n",__FUNCTION__,sv,info->type->name); #endif sv_unmagic(sv,PERL_MAGIC_ext); /* sv_unmagic calls free proc */ return 0; } static U32 TclObj_len(pTHX_ SV *sv, MAGIC *mg) { #ifdef DEBUG_TCLOBJ TclObjMagic_t *info = (TclObjMagic_t *)SvPVX(mg->mg_obj); LangDebug("%s %s\n",__FUNCTION__,info->type->name); #endif return 0; } static int TclObj_clear(pTHX_ SV *sv, MAGIC *mg) { #ifdef DEBUG_TCLOBJ TclObjMagic_t *info = (TclObjMagic_t *)SvPVX(mg->mg_obj); LangDebug("%s %p %s\n",__FUNCTION__,sv,info->type->name); #endif sv_unmagic(sv,PERL_MAGIC_ext); /* sv_unmagic calls free proc */ return 0; } MGVTBL TclObj_vtab = { TclObj_get, TclObj_set, NULL, /* TclObj_len, */ TclObj_clear, TclObj_free }; static TclObjMagic_t * Tcl_ObjMagic(Tcl_Obj *obj,int add) { dTHX; MAGIC *mg = (SvTYPE(obj) >= SVt_PVMG) ? mg_find(obj,PERL_MAGIC_ext) : NULL; SV *data = NULL; TclObjMagic_t *iv; if (mg) { if (mg->mg_virtual == &TclObj_vtab) { data = mg->mg_obj; } else { if (add) { warn("Wrong kind of '~' magic on %_",obj); sv_dump(obj); abort(); } } } else if (add) { Tcl_ObjType *type = TclObjGetType(obj); int rdonly = SvREADONLY(obj); data = newSV(sizeof(TclObjMagic_t)); Zero(SvPVX(data),sizeof(TclObjMagic_t),char); if (rdonly) SvREADONLY_off(obj); sv_upgrade(obj,SVt_PVMG); sv_magic(obj,data,PERL_MAGIC_ext,NULL,0); SvREFCNT_dec(data); SvRMAGICAL_off(obj); mg = mg_find(obj,PERL_MAGIC_ext); if (mg->mg_obj != data) abort(); mg->mg_virtual = &TclObj_vtab; mg_magical(obj); if (rdonly) SvREADONLY_on(obj); iv = (TclObjMagic_t *) SvPVX(data); iv->type = type; if (iv->type == &tclIntType) { iv->internalRep.longValue = SvIV(obj); } else if (iv->type == &tclDoubleType) { iv->internalRep.doubleValue = SvNV(obj); } return iv; } if (data) { TclObjMagic_t *iv = (TclObjMagic_t *) SvPVX(data); return iv; } return NULL; } Tcl_Obj * Tcl_DuplicateObj(Tcl_Obj *src) { dTHX; /* We get AVs either from SvRV test below, or * "suspect" ResultAv scheme */ int object = sv_isobject(src); if (SvTYPE(src) == SVt_PVAV) { abort(); } else if (!object && SvROK(src) && SvTYPE(SvRV(src)) == SVt_PVAV) { AV *av = (AV *) SvRV(src); IV max = av_len(av); AV *dst = newAV(); int i; for (i=0; i <= max; i++) { /* Do a deep copy and hope there are no loops */ SV **svp = av_fetch(av,i,0); SV *d = (svp && *svp) ? Tcl_DuplicateObj(*svp) : &PL_sv_undef; av_store(dst,i,d); } return MakeReference((SV *) dst); } else { SV *dup = newSVsv(src); TclObjMagic_t *m = Tcl_ObjMagic(src,0); if (m && m->type) { if (m->type->dupIntRepProc) { (*m->type->dupIntRepProc)(src,dup); } else { TclObjMagic_t *n = Tcl_ObjMagic(dup,1); n->type = m->type; n->internalRep = m->internalRep; } } return dup; } } Tcl_ObjType * Tcl_GetObjType(CONST char *name) { if (strEQ(name,"int")) return &tclIntType; if (strEQ(name,"double")) return &tclDoubleType; LangDebug("%s wanted %s\n",__FUNCTION__,name); return &perlDummyType; } static void NoFreeProc(Tcl_Obj *obj) { TclObjMagic_t *m = Tcl_ObjMagic(obj,1); LangDebug("%s %p %s\n",__FUNCTION__,obj,m->type->name); } Tcl_ObjType * TclObjGetType(Tcl_Obj *obj) { TclObjMagic_t *m = Tcl_ObjMagic(obj,0); if (m) { #ifdef DEBUG_TCLOBJ if (!m->type->freeIntRepProc) m->type->freeIntRepProc = &NoFreeProc; #endif return m->type; } if (SvNOK(obj)) { return &tclDoubleType; } else if (SvIOK(obj)) { return &tclIntType; } return &perlDummyType; } int TclObjLength(Tcl_Obj *obj) { dTHX; STRLEN len; char *s = SvPV(obj,len); return len; } void TclObjSetType(Tcl_Obj *obj,Tcl_ObjType *type) { TclObjMagic_t *m = Tcl_ObjMagic(obj,1); #ifdef DEBUG_TCLOBJ if (m->type) { LangDebug("%s %p was %s\n",__FUNCTION__,obj,m->type->name); } LangDebug("%s %p now %s\n",__FUNCTION__,obj,type->name); #endif m->type = type; } int Tcl_ConvertToType(Tcl_Interp * interp, Tcl_Obj * objPtr, Tcl_ObjType * typePtr) { if (TclObjGetType(objPtr) == typePtr) { return TCL_OK; } /* * Use the target type's Tcl_SetFromAnyProc to set "objPtr"s internal * form as appropriate for the target type. This frees the old internal * representation. */ return typePtr->setFromAnyProc(interp, objPtr); } Tcl_InternalRep * TclObjInternal(Tcl_Obj *obj) { TclObjMagic_t *m = Tcl_ObjMagic(obj,1); return &(m->internalRep); } void Tcl_RegisterObjType(Tcl_ObjType *type) { } Tcl_Obj * LangCopyArg(sv) SV *sv; { if (sv) { dTHX; MAGIC *mg = (SvTYPE(sv) >= SVt_PVMG) ? mg_find(sv,PERL_MAGIC_ext) : NULL; if (mg && mg->mg_virtual == &TclObj_vtab) { return Tcl_DuplicateObj(sv); } if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV) { return LangMakeCallback(sv); } sv = newSVsv(sv); } return sv; }