1 /*
2   Copyright (c) 1995-2004 Nick Ing-Simmons. All rights reserved.
3   This program is free software; you can redistribute it and/or
4   modify it under the same terms as Perl itself.
5 */
6 #define PERL_NO_GET_CONTEXT
7 #include <EXTERN.h>
8 #include <perl.h>
9 #include <XSUB.h>
10 #include <patchlevel.h>
11 #ifdef __CYGWIN__
12 #  undef XS
13 #  define XS(name) void name(pTHXo_ CV* cv)
14 #endif
15 
16 #define Tkgv_fullname(x,y,z) gv_fullname3(x,y,z)
17 
18 #include "tkGlue.def"
19 #include "pTk/tkPort.h"
20 #include "pTk/tkInt.h"
21 #include "pTk/tix.h"  /* for form */
22 #include "pTk/tkImgPhoto.h"
23 #include "pTk/tkImgPhoto.m"
24 #include "pTk/imgInt.h"
25 #include "pTk/imgInt.m"
26 #include "pTk/tkOption.h"
27 #include "pTk/tkOption_f.h"
28 #include "pTk/Lang_f.h"
29 #include "pTk/Xlib.h"
30 #include "pTk/tk_f.h"
31 #include "pTk/tkInt_f.h"
32 #include "pTk/Xlib_f.h"
33 #include "pTk/tclDecls_f.h"
34 #include "pTk/tkDecls_f.h"
35 #include "pTk/tkIntDecls_f.h"
36 #include "pTk/tkEvent.h"
37 #include "pTk/tkEvent.m"
38 #if defined(WIN32) || (defined(__WIN32__) && defined(__CYGWIN__))
39 #include "pTk/tkWin.h"
40 #include "pTk/tkWinInt.h"
41 #include "pTk/tkIntXlibDecls_f.h"
42 #include "pTk/tkIntPlatDecls_f.h"
43 #include "pTk/tkPlatDecls_f.h"
44 #else
45 #  ifdef OS2
46 #    include "pTk/tkOS2Int.h"
47 #  else
48 #    include "pTk/tkUnixInt.h"
49 #  endif
50 #endif
51 #include "tkGlue.h"
52 #include "tkGlue_f.h"
53 
54 DECLARE_EVENT;
55 
56 /* #define DEBUG_REFCNT /* */
57 
58 #ifdef WIN32
59 long DCcount = 0;
60 void
LangNoteDC(HDC dc,int inc)61 LangNoteDC(HDC dc,int inc)
62 {
63 #ifdef DEBUGGING
64  DCcount += inc;
65 #endif
66 }
67 
68 void
LangCheckDC(const char * file,int line)69 LangCheckDC(const char *file,int line)
70 {
71 #ifdef DEBUGGING
72  if (DCcount)
73     LangDebug("%s:%d DCcount %ld\n",file,line,DCcount);
74 #endif
75 }
76 #else
77 void
LangCheckDC(const char * file,int line)78 LangCheckDC(const char *file,int line)
79 {
80 }
81 #endif
82 
83 
84 extern Tk_PhotoImageFormat	imgFmtBMP;
85 #if 0
86 extern Tk_PhotoImageFormat	imgFmtGIF;
87 #else
88 extern Tk_PhotoImageFormat	tkImgFmtGIF;
89 #endif
90 extern Tk_PhotoImageFormat	imgFmtXBM;
91 extern Tk_PhotoImageFormat	imgFmtXPM;
92 
93 typedef struct
94 {
95  Lang_VarTraceProc *proc;
96  ClientData         clientData;
97  Tcl_Interp        *interp;
98  char              *part2;
99  SV                *sv;
100 } Tk_TraceInfo;
101 
102 typedef struct
103 {
104  Tcl_Interp *interp;
105  SV *cb;
106 } GenericInfo;
107 
108 typedef struct Assoc_s
109 {
110  Tcl_InterpDeleteProc *proc;
111  ClientData clientData;
112 } Assoc_t;
113 
114 static int initialized = 0;
115 
116 
117 static I32 ec = 0;
118 static SV *my_watch;
119 
120 static char XEVENT_KEY[]   = "_XEvent_";
121 static char GEOMETRY_KEY[] = "_ManageGeometry_";
122 static char CM_KEY[]       = "_ClientMessage_";
123 static char ASSOC_KEY[]    = "_AssocData_";
124 static char FONTS_KEY[]    = "_Fonts_";
125 static char CMD_KEY[]      = "_CmdInfo_";
126 
127 #ifndef BASEEXT
128 #define BASEEXT "Tk"
129 #endif
130 
131 typedef XSdec((*XSptr));
132 
133 static XSdec(XStoSubCmd);
134 static XSdec(XStoDisplayof);
135 static XSdec(XStoTk);
136 static XSdec(XStoBind);
137 static XSdec(XStoEvent);
138 
139 extern XSdec(XS_Tk__Widget_SelectionGet);
140 extern XSdec(XS_Tk__Widget_ManageGeometry);
141 extern XSdec(XS_Tk__MainWindow_Create);
142 extern XSdec(XS_Tk__Interp_DESTROY);
143 extern XSdec(XS_Tk__Widget_BindClientMessage);
144 extern XSdec(XS_Tk__Widget_PassEvent);
145 extern XSdec(XS_Tk_INIT);
146 extern XSdec(XS_Tk_DoWhenIdle);
147 extern XSdec(XS_Tk_CreateGenericHandler);
148 
149 #ifdef PERL_MG_UFUNC
150 #define DECL_MG_UFUNC(name,a,b) PERL_MG_UFUNC(name,a,b)
151 #else
152 #define DECL_MG_UFUNC(name,a,b) I32 name(IV a, SV *b)
153 #endif
154 
155 extern void  LangPrint _((SV *sv));
156 
157 static void handle_idle _((ClientData clientData));
158 static void LangCatArg _((SV * out, SV * sv, int refs));
159 static SV *NameFromCv _((CV * cv));
160 static AV *FindAv _((pTHX_ Tcl_Interp *interp, char *who, int create, char *key));
161 static HV *FindHv _((pTHX_ HV *interp, char *who, int create, char *key));
162 static SV *Blessed _((char *package, SV * sv));
163 static int PushObjCallbackArgs _((Tcl_Interp *interp, SV **svp,EventAndKeySym *obj));
164 static int Check_Eval _((Tcl_Interp *interp));
165 static int handle_generic _((ClientData clientData, XEvent * eventPtr));
166 static void HandleBgErrors _((ClientData clientData));
167 static void SetTclResult _((Tcl_Interp *interp,int count));
168 static int InfoFromArgs _((Lang_CmdInfo *info,Tcl_ObjCmdProc *proc,int mwcd, int items, SV **args));
169 static I32 InsertArg _((SV **mark,I32 posn,SV *sv));
170 extern Tk_Window TkToMainWindow _((Tk_Window tkwin));
171 static int isSwitch _((char *arg));
172 static void Lang_ClearErrorInfo _((Tcl_Interp *interp));
173 static void Lang_MaybeError _((Tcl_Interp *interp,int code,char *why));
174 static void Set_widget _((SV *widget));
175 static SV *tilde_magic _((SV *hv, SV *sv));
176 static SV *struct_sv   _((void *ptr, STRLEN sz));
177 static int SelGetProc _((ClientData clientData,
178 			Tcl_Interp *interp,
179 			long *portion,
180 			int numItems,
181 			int format,
182 			Atom type,
183 			Tk_Window tkwin));
184 static void Perl_GeomRequest _((ClientData clientData,Tk_Window tkwin));
185 static void Perl_GeomLostSlave _((ClientData clientData, Tk_Window tkwin));
186 
187 Tcl_ObjCmdProc *LangOptionCommand = (Tcl_ObjCmdProc *)Tk_OptionObjCmd;
188 
189 static GV *current_widget;
190 static GV *current_event;
191 
192 static int
Expire(int code)193 Expire(int code)
194 {
195  return code;
196 }
197 
198 #define EXPIRE(args) \
199   ( Tcl_SprintfResult args, Expire(TCL_ERROR) )
200 
201 #ifdef DEBUG_TAINT
202 #define do_watch() do { if (PL_tainting) taint_proper("tainted", __FUNCTION__); } while (0)
203 #else
204 extern void do_watch _((void));
205 void
do_watch()206 do_watch()
207 {
208 
209 }
210 #endif
211 
212 static void
LangCatAv(pTHX_ SV * out,AV * av,int refs,char * bra)213 LangCatAv(pTHX_ SV *out, AV *av, int refs, char *bra)
214 {
215  int n = av_len(av) + 1;
216  int i = 0;
217  sv_catpvn(out, bra, 1);
218  while (i < n)
219   {
220    SV **x = av_fetch(av, i, 0);
221    LangCatArg(out, (x) ? (*x) : &PL_sv_undef, refs);
222    if (++i < n)
223     sv_catpv(out, ",");
224   }
225  sv_catpvn(out, bra+1, 1);
226 }
227 
228 static void
LangCatArg(out,sv,refs)229 LangCatArg(out, sv, refs)
230 SV *out;
231 SV *sv;
232 int refs;
233 {
234  dTHX;
235  char buf[80];
236  if (sv)
237   {
238    STRLEN na;
239    switch(SvTYPE(sv))
240     {
241      case SVt_PVAV:
242       LangCatAv(aTHX_ out, (AV *) sv, refs,"()");
243       break;
244      case SVt_PVGV:
245       {SV *tmp = newSVpv("", 0);
246        Tkgv_fullname(tmp,(GV *) sv, Nullch);
247        sv_catpv(out,"*");
248        sv_catpv(out,SvPV(tmp,na));
249        SvREFCNT_dec(tmp);
250       }
251       break;
252      case SVt_PVCV:
253       if (CvGV(sv))
254        {
255         SV *tmp = newSVpv("", 0);
256         Tkgv_fullname(tmp, CvGV(sv), Nullch);
257         sv_catpv(out,"&");
258         sv_catpv(out,SvPV(tmp,na));
259         SvREFCNT_dec(tmp);
260         break;
261        }
262      default:
263       if (SvOK(sv))
264        {
265         char *s = "";
266         if (SvROK(sv))
267          {
268           if (SvTYPE(SvRV(sv)) == SVt_PVAV)
269            LangCatAv(aTHX_ out, (AV *) SvRV(sv), refs,"[]");
270           else if (SvTYPE(SvRV(sv)) == SVt_PVHV)
271            {
272             SV *hv = SvRV(sv);
273             sv_catpv(out,"{}");
274             if (refs)
275              {
276               sprintf(buf, "(%ld%s", (long) SvREFCNT(hv), SvTEMP(hv) ? "t)" : ")");
277               sv_catpv(out, buf);
278              }
279            }
280           else
281            {
282             sv_catpv(out,"\\");
283             LangCatArg(out, SvRV(sv), refs);
284            }
285          }
286         else
287          {
288           if (refs && !SvPOK(sv))
289            {
290             sprintf(buf, "f=%08lX ", (unsigned long) SvFLAGS(sv));
291             sv_catpv(out, buf);
292            }
293           s = SvPV(sv, na);
294          }
295         sv_catpv(out, s);
296        }
297       else
298        {
299         sv_catpv(out, "undef");
300        }
301      break;
302     }
303   }
304  if (refs)
305   {
306    sprintf(buf, "(%ld%s", (long) SvREFCNT(sv), SvTEMP(sv) ? "t)" : ")");
307    sv_catpv(out, buf);
308   }
309 }
310 
311 int
LangNull(sv)312 LangNull(sv)
313 Tcl_Obj * sv;
314 {
315  STRLEN len = 0;
316  if (!sv || !SvOK(sv) || (SvPOK(sv) && !SvCUR(sv)))
317   return 1;
318  return 0;
319 }
320 
321 char *
LangMergeString(argc,args)322 LangMergeString(argc, args)
323 int argc;
324 SV **args;
325 {
326  dTHX;
327  SV *sv = newSVpv("", 0);
328  STRLEN i = 0;
329  STRLEN na;
330  char *s;
331  while (i < (STRLEN) argc)
332   {
333    LangCatArg(sv, args[i++], 0);
334    if (i < (STRLEN) argc)
335     sv_catpvn(sv, " ", 1);
336   }
337  SvPV(sv, i);
338  s = strncpy(ckalloc(i + 1), SvPV(sv, na), i);
339  s[i] = '\0';
340  SvREFCNT_dec(sv);
341  return s;
342 }
343 
344 void
LangPrint(sv)345 LangPrint(sv)
346 SV *sv;
347 {
348  dTHX;
349  static char *type_name[] =
350  {
351   "NULL",
352   "IV",
353   "NV",
354   "RV",
355   "PV",
356   "PVIV",
357   "PVNV",
358   "PVMG",
359   "PVBM",
360   "PVLV",
361   "PVAV",
362   "PVHV",
363   "PVCV",
364   "PVGV",
365   "PVFM",
366   "PVIO"
367  };
368  if (sv)
369   {
370    SV *tmp = newSVpv("", 0);
371    int type = SvTYPE(sv);
372    STRLEN na;
373    LangCatArg(tmp, sv, 1);
374    PerlIO_printf(PerlIO_stderr(), "0x%p %4s f=%08lx %s\n",
375                  sv, (type < 16) ? type_name[type] : "?",
376                  (unsigned long) SvFLAGS(sv), SvPV(tmp, na));
377    SvREFCNT_dec(tmp);
378   }
379  else
380   {
381    PerlIO_printf(PerlIO_stderr(), "0x%p <<!!!\n",sv);
382   }
383 }
384 
385 
386 #ifdef DEBUG_REFCNT
387 static Tcl_Interp *IncInterp(Tcl_Interp *interp,char *why);
388 static Tcl_Interp *DecInterp(Tcl_Interp *interp,char *why);
389 
390 static Tcl_Interp *
IncInterp(interp,why)391 IncInterp(interp,why)
392 Tcl_Interp *interp;
393 char *why;
394 {
395  dTHX;
396  SvREFCNT_inc((SV *) interp);
397  PerlIO_printf(PerlIO_stdout(),"%s %p %ld\n",why,interp,SvREFCNT((SV *) interp));
398  return interp;
399 }
400 
401 static Tcl_Interp *
DecInterp(interp,why)402 DecInterp(interp,why)
403 Tcl_Interp *interp;
404 char *why;
405 {
406  dTHX;
407  SvREFCNT_dec((SV *) interp);
408  PerlIO_printf(PerlIO_stdout(),"%s %p %ld\n",why,interp,SvREFCNT((SV *) interp));
409  return interp;
410 }
411 #else
412 #define DecInterp(interp,why) SvREFCNT_dec(interp)
413 #define IncInterp(interp,why) SvREFCNT_inc(interp)
414 #endif
415 
416 #ifdef DEBUG_REFCNT
417 
418 static SV *
Decrement(SV * sv,char * who)419 Decrement(SV * sv, char *who)
420 {
421  do_watch();
422  if (sv)
423   {
424    fprintf(stderr, "DEC %s ", who);
425    LangPrint(sv);
426    SvREFCNT_dec(sv);
427    do_watch();
428   }
429  else
430   Tcl_Panic("No sv");
431  return sv;
432 }
433 
434 static SV *
Increment(SV * sv,char * who)435 Increment(SV * sv, char *who)
436 {
437  do_watch();
438  if (sv)
439   {
440    fprintf(stderr, "INC %s ", who);
441    LangPrint(sv);
442    SvREFCNT_inc(sv);
443   }
444  else
445   Tcl_Panic("No sv");
446  return sv;
447 }
448 #else
449 #define Decrement(x,y) SvREFCNT_dec(x)
450 #define Increment(x,y) SvREFCNT_inc(x)
451 #endif
452 
453 SV *
MakeReference(sv)454 MakeReference(sv)
455 SV *sv;
456 {
457  dTHX;
458  SV *rv = newRV(sv);              /* REFCNT of sv now 2 */
459  SvREFCNT_dec(sv);
460  return rv;
461 }
462 
463 static SV *
Blessed(package,sv)464 Blessed(package, sv)
465 char *package;
466 SV *sv;
467 {
468  dTHX;
469  HV *stash = gv_stashpv(package, TRUE);
470  return sv_bless(sv, stash);
471 }
472 
473 #if 0
474 SV *
475 TagIt(SV *sv, char *type)
476 {
477  char buffer[1024];
478  sprintf(buffer,"Tk::%s_Type",type);
479  if (SvROK(sv))
480   Blessed(buffer,sv);
481  else
482   {
483    SV *rv = newRV(sv);
484    Blessed(buffer,rv);
485    SvREFCNT_dec(rv);
486   }
487  return sv;
488 }
489 #else
490 #define TagIt(sv,type) (sv)
491 #endif
492 
493 Tcl_Interp *
Tcl_CreateInterp(void)494 Tcl_CreateInterp  _((void))
495 {
496  dTHX;
497  HV *hv = newHV();
498 
499 #ifdef switch_to_global_locale
500  /* X uses setlocale(), which is not thread safe, and is incompatible with the
501   * POSIX 2008 thread-safe locale handling functions that perl normally uses on
502   * threaded POSIX builds.  The function below does nothing except in
503   * situations where needed, it tells this thread to pay attention to
504   * setlocale().  As long as only one thread calls it, everything works. */
505  switch_to_global_locale();
506 #endif
507 
508  SvREFCNT_dec(Blessed("Tk::Interp",newRV((SV *) hv)));
509  return hv;
510 }
511 
512 HV *
InterpHv(interp,fatal)513 InterpHv(interp,fatal)
514 Tcl_Interp *interp;
515 int fatal;
516 {
517  if (interp && SvTYPE((SV *) interp) == SVt_PVHV)
518   {
519    return interp;
520   }
521  else if (fatal)
522   {
523    dTHX;
524    STRLEN na;
525    warn("%p is not a hash", interp);
526    abort();
527   }
528  return NULL;
529 }
530 
531 typedef SV *(*createProc_t)(pTHX);
532 
533 static SV *
FindXv(pTHX_ Tcl_Interp * interp,char * who,int create,char * key,U32 type,createProc_t createProc)534 FindXv(pTHX_ Tcl_Interp *interp, char *who, int create,
535        char *key, U32 type , createProc_t createProc)
536 {
537  STRLEN len = strlen(key);
538  HV *hv = InterpHv(interp,create != 0);
539  if (hv)
540   {
541    if (hv_exists(hv, key, len))
542     {
543      SV **x = hv_fetch(hv, key, len, 0);
544      if (x)
545       {
546        SV *sv = *x;
547        if (type >= SVt_PVAV)
548         {
549          if (!SvROK(sv) || SvTYPE(SvRV(sv)) != type)
550           {
551            Tcl_Panic("%s not a %u reference %s", key, type, SvPV_nolen(sv));
552           }
553          else
554           {
555            sv = SvRV(sv);
556           }
557         }
558        if (create < 0)
559         {
560          SvREFCNT_inc((SV *) sv);
561          hv_delete(hv, key, len, G_DISCARD);
562         }
563        return sv;
564       }
565      else
566       Tcl_Panic("%s exists but can't be fetched", key);
567     }
568    else if (create > 0)
569     {
570      SV *sv = (*createProc)(aTHX);
571      if (sv)
572       {
573        TagIt(sv,key);
574        if (type >= SVt_PVAV)
575         {
576          hv_store(hv, key, len, MakeReference(sv), 0);
577         }
578        else
579         hv_store(hv, key, len, sv, 0);
580       }
581      return sv;
582     }
583   }
584  return NULL;
585 }
586 
587 static SV *
createHV(pTHX)588 createHV(pTHX)
589 {
590  return (SV *) newHV();
591 }
592 
593 static HV *
FindHv(pTHX_ HV * hv,char * who,int create,char * key)594 FindHv(pTHX_ HV *hv, char *who, int create, char *key)
595 {
596  return (HV *) FindXv(aTHX_ hv, who, create, key, SVt_PVHV, createHV);
597 }
598 
599 static SV *
createAV(pTHX)600 createAV(pTHX)
601 {
602  return (SV *) newAV();
603 }
604 
605 static AV *
FindAv(pTHX_ HV * hv,char * who,int create,char * key)606 FindAv(pTHX_ HV *hv, char *who, int create, char *key)
607 {
608  return (AV *) FindXv(aTHX_ hv, who, create, key, SVt_PVAV, createAV);
609 }
610 
611 static SV *
createSV(pTHX)612 createSV(pTHX)
613 {
614  return newSVsv(&PL_sv_undef);
615 }
616 
617 static SV *
FindSv(pTHX_ HV * hv,char * who,int create,char * key)618 FindSv(pTHX_ HV *hv, char *who, int create, char *key)
619 {
620  return FindXv(aTHX_ hv, who, create, key, SVt_NULL, createSV);
621 }
622 
623 
624 /* Result return handling
625    Use the FindXv scheme to create an SV in the interp.
626 */
627 Tcl_Obj *
Tcl_GetObjResult(interp)628 Tcl_GetObjResult(interp)
629 Tcl_Interp *interp;
630 {
631  dTHX;
632  return FindSv(aTHX_ interp, "Tcl_GetObjResult", 1, "_TK_RESULT_");
633 }
634 
635 void
Tcl_ResetResult(interp)636 Tcl_ResetResult(interp)
637 Tcl_Interp *interp;
638 {
639  dTHX;
640  if (InterpHv(interp,0))
641   {
642    /* We delete the entry in the interp.
643       This means we are forever create/delete.
644       Leaving an SV in the interp might be better, as might
645       having Tcl_SetObjResult() which everything now uses
646       just store the SV.
647     */
648    SV *sv = FindSv(aTHX_ interp, "Tcl_ResetResult", -1, "_TK_RESULT_");
649    if (sv)
650     {
651      SvREFCNT_dec(sv);
652     }
653   }
654 }
655 
656 void
Tcl_SetObjResult(interp,sv)657 Tcl_SetObjResult(interp, sv)
658 Tcl_Interp *interp;
659 SV *sv;
660 {
661  dTHX;
662  if (InterpHv(interp,0))
663   {
664    SV *result = Tcl_GetObjResult(interp);
665    if (result == sv)
666     {
667      /* Recent Tk does
668          save = Tcl_GetObjResult();
669          Tcl_IncrRefCount(save);
670          ...
671          Tcl_SetObjResult(save);
672          Tcl_DecrRefCount(save);
673          So nothing more to do here.
674      */
675      return;
676     }
677    else
678     {
679      Tcl_ResetResult(interp);
680      SvSetMagicSV(Tcl_GetObjResult(interp), sv);
681     }
682   }
683  /* normal coding in Tk is equivalent to
684     Tcl_SetObjResult(interp,Tcl_NewXxxObj());
685     and then forget about the Tcl_Obj - i.e. ownership
686     is handed to the interp.
687     As we have taken a _copy_ we no longer need the
688     original.
689   */
690  Tcl_DecrRefCount(sv);
691 }
692 
693 void
Lang_SetBinaryResult(interp,string,len,freeProc)694 Lang_SetBinaryResult(interp, string, len, freeProc)
695 Tcl_Interp *interp;
696 char *string;
697 int len;
698 Tcl_FreeProc *freeProc;
699 {
700  dTHX;
701  do_watch();
702  if (string)
703   {
704    SV *sv = newSVpv(string, len);
705    Tcl_SetObjResult(interp, sv);
706    if (freeProc != TCL_STATIC && freeProc != TCL_VOLATILE)
707     (*freeProc) (string);
708   }
709  else
710   Tcl_ResetResult(interp);
711  do_watch();
712 }
713 
714 void
Tcl_SetResult(interp,string,freeProc)715 Tcl_SetResult(interp, string, freeProc)
716 Tcl_Interp *interp;
717 char *string;
718 Tcl_FreeProc *freeProc;
719 {
720  STRLEN len = (string) ? strlen(string) : 0;
721  Lang_SetBinaryResult(interp, string, len, freeProc);
722 }
723 
724 void
Tcl_CallWhenDeleted(interp,proc,clientData)725 Tcl_CallWhenDeleted(interp, proc, clientData)
726 Tcl_Interp *interp;
727 Tcl_InterpDeleteProc *proc;
728 ClientData clientData;
729 {
730  dTHX;
731  HV *hv = InterpHv(interp,1);
732  AV *av = FindAv(aTHX_ interp, "Tcl_CallWhenDeleted", 1, "_When_Deleted_");
733  av_push(av, newSViv(PTR2IV(proc)));
734  av_push(av, newSViv(PTR2IV(clientData)));
735 }
736 
XS(XS_Tk__Interp_DESTROY)737 XS(XS_Tk__Interp_DESTROY)
738 {
739  dXSARGS;
740  Tcl_Interp *interp = (Tcl_Interp *) SvRV(ST(0));
741 #if 0
742  fprintf(stderr,"InterpDestroy %ld\n",SvREFCNT((SV *) interp));
743 #endif
744  /* Tk_CheckHash((SV *)interp,NULL); */
745  hv_undef(interp);
746 }
747 
748 static void
DeleteInterp(char * cd)749 DeleteInterp(char *cd)
750 {
751  Tcl_Interp *interp = (Tcl_Interp *) cd;
752  dTHX;
753  SV *exiting = FindSv(aTHX_ interp, "DeleteInterp", -1, "_TK_EXIT_");
754  AV *av = FindAv(aTHX_ interp, "DeleteInterp", -1, "_When_Deleted_");
755  HV *hv = FindHv(aTHX_ interp, "DeleteInterp", -1, ASSOC_KEY);
756  if (av)
757   {
758    while (av_len(av) > 0)
759     {
760      SV *cd = av_pop(av);
761      SV *pr = av_pop(av);
762      Tcl_InterpDeleteProc *proc = INT2PTR(Tcl_InterpDeleteProc *, SvIV(pr));
763      ClientData clientData = INT2PTR(ClientData, SvIV(cd));
764      (*proc) (clientData, interp);
765      SvREFCNT_dec(cd);
766      SvREFCNT_dec(pr);
767     }
768    SvREFCNT_dec((SV *) av);
769   }
770  if (hv)
771   {HE *he;
772    /* Tk_CheckHash((SV *)hv,NULL); */
773    hv_iterinit(hv);
774    while ((he = hv_iternext(hv)))
775     {
776      STRLEN sz;
777      SV *val = hv_iterval(hv,he);
778      Assoc_t *info = (Assoc_t *) SvPV(val,sz);
779      if (sz != sizeof(*info))
780       croak("%s corrupted",ASSOC_KEY);
781      if (info->proc)
782       (*info->proc)(info->clientData, interp);
783     }
784    hv_undef(hv);
785   }
786  DecInterp(interp, "DeleteInterp");
787  if (exiting)
788   {
789    sv_2mortal(exiting);
790    my_exit(SvIV(exiting));
791   }
792 }
793 
794 int
Tcl_InterpDeleted(Tcl_Interp * interp)795 Tcl_InterpDeleted(Tcl_Interp *interp)
796 {
797  dTHX;
798  SV *sv = FindSv(aTHX_ interp, "Tcl_InterpDeleted", 0, "_DELETED_");
799  if (sv)
800   {
801    return SvTRUE(sv);
802   }
803  return 0;
804 }
805 
806 void
Tcl_DeleteInterp(interp)807 Tcl_DeleteInterp(interp)
808 Tcl_Interp *interp;
809 {
810  dTHX;
811  SV *del = FindSv(aTHX_ interp, "Tcl_DeleteInterp", 1, "_DELETED_");
812  sv_setiv(del,1);
813  Tcl_EventuallyFree((ClientData) interp, DeleteInterp);
814 }
815 
816 /*
817  * We just deleted the last window in the application.  Delete
818  * the TkMainInfo structure too and replace all of Tk's commands
819  * with dummy commands that return errors (except don't replace
820  * the "exit" command, since it may be needed for the application
821  * to exit).
822  */
823 
824 void
Lang_DeadMainWindow(interp,tkwin)825 Lang_DeadMainWindow(interp,tkwin)
826 Tcl_Interp *interp;
827 Tk_Window tkwin;
828 {
829  dTHX;
830  HV *hv    = InterpHv(interp,1);
831  HV *fonts = FindHv(aTHX_ interp, "Lang_DeadMainWindow", 0, FONTS_KEY);
832  Display *dpy = Tk_Display(tkwin);
833  STRLEN na;
834  if (dpy)
835   XSync(dpy,FALSE);
836  if (0 && fonts)
837   {HE *he;
838    hv_iterinit(fonts);
839    while ((he = hv_iternext(fonts)))
840     {
841      SV *val = hv_iterval(fonts,he);
842      Lang_CmdInfo *info = WindowCommand(val,NULL,0);
843      if (info && info->tkfont)
844       {
845        Tk_FreeFont(info->tkfont);
846        info->tkfont = NULL;
847       }
848     }
849    /* Tk_CheckHash((SV *)fonts,NULL); */
850    hv_undef(fonts);
851   }
852  sv_unmagic((SV *) hv, PERL_MAGIC_ext);
853  Tcl_DeleteInterp(interp);
854 
855 #ifdef sync_locale
856  /* Restore normal locale handling (see the switch_to_global_locale call
857   * elsewhere in this file) */
858  sync_locale();
859 #endif
860 
861 }
862 
863 static SV *
struct_sv(ptr,sz)864 struct_sv(ptr,sz)
865 void *ptr;
866 STRLEN sz;
867 {
868  dTHX;
869  SV *sv = (ptr) ? newSVpv((char *) ptr, sz) : newSV(sz);
870  if (ptr)
871   {
872    SvREADONLY_on(sv);
873   }
874  else
875   {
876    Zero(SvPVX(sv),sz+1,char);
877    SvCUR_set(sv,sz);
878    SvPOK_only(sv);
879   }
880  return sv;
881 }
882 
883 static int
TkGlue_mgFree(pTHX_ SV * sv,MAGIC * mg)884 TkGlue_mgFree(pTHX_ SV *sv, MAGIC *mg)
885 {
886  STRLEN na;
887  return 0;
888 }
889 
890 MGVTBL TkGlue_vtab = {
891  NULL,
892  NULL,
893  NULL,
894  NULL,
895  TkGlue_mgFree
896 };
897 
898 static SV *
tilde_magic(hv,sv)899 tilde_magic(hv,sv)
900 SV *hv;
901 SV *sv;
902 {
903  dTHX;
904  MAGIC *mg;
905  sv_magic(hv, sv, PERL_MAGIC_ext, NULL, 0);
906  SvRMAGICAL_off(hv);
907  mg = mg_find(hv, PERL_MAGIC_ext);
908  if (mg->mg_obj != sv)
909   abort();
910  mg->mg_virtual = &TkGlue_vtab;
911  mg_magical(hv);
912  return sv;
913 }
914 
915 #define mSVPV(sv,na) (SvOK(sv) ? SvPV(sv,na) : "undef")
916 
917 void
LangDumpVec(CONST char * who,int count,SV ** data)918 LangDumpVec(CONST char *who, int count, SV **data)
919 {
920  dTHX;
921  int i;
922  PerlIO_printf(PerlIO_stderr(), "%s (%d):\n", who, count);
923  for (i = 0; i < count; i++)
924   {
925    SV *sv = data[i];
926    if (sv)
927     {
928      PerlIO_printf(PerlIO_stderr(), "%2d ", i);
929      LangPrint(sv);
930      sv_dump(sv);
931     }
932   }
933  if (SvTRUE(get_sv("Tk::_AbortOnLangDump",0)))
934   {
935    abort();
936   }
937 }
938 
939 void
DumpStack(CONST char * who)940 DumpStack(CONST char *who)
941 {
942  dTHX;
943  do_watch();
944  LangDumpVec(who, PL_stack_sp - PL_stack_base, PL_stack_base + 1);
945 }
946 
947 void
LangSetString(sp,s)948 LangSetString(sp, s)
949 SV **sp;
950 CONST char *s;
951 {
952  dTHX;
953  SV *sv = *sp;
954  do_watch();
955  if (!s)
956   {
957    /* tkOldConfig uses LangSetString when TK_CONFIG_NULL_OK is _NOT_ set
958       we must set something.
959     */
960    s = "";
961   }
962  if (sv)
963   {
964    sv_setpv(sv, s);
965    SvSETMAGIC(sv_maybe_utf8(sv));
966    return;
967   }
968  *sp = Tcl_NewStringObj(s, -1);
969 }
970 
971 void
LangSetDefault(sp,s)972 LangSetDefault(sp, s)
973 SV **sp;
974 CONST char *s;
975 {
976  dTHX;
977  SV *sv = *sp;
978  do_watch();
979  if (sv)
980   {
981    if (!s || !*s || SvREADONLY(sv))
982     {
983      Decrement(sv, "LangSetDefault");
984     }
985    else
986     {
987      if (s && *s)
988       {
989        sv_setpv(sv, s);
990        SvSETMAGIC(sv);
991        return;
992       }
993     }
994   }
995  *sp = sv = (s && *s) ? TagIt(newSVpv(s, strlen(s)),"LangSetDefault") : &PL_sv_undef;
996 }
997 
998 void
LangSetObj(sp,arg)999 LangSetObj(sp, arg)
1000 SV **sp;
1001 SV *arg;
1002 {
1003  dTHX;
1004  SV *sv = *sp;
1005  do_watch();
1006  if (!arg)
1007   arg = &PL_sv_undef;
1008  if (SvTYPE(arg) == SVt_PVAV)
1009   arg = newRV_noinc(arg);
1010  if (sv && SvMAGICAL(sv))
1011   {
1012    SvSetMagicSV(sv, arg);
1013    SvREFCNT_dec(arg);
1014   }
1015  else
1016   {
1017    *sp = arg;
1018    if (sv)
1019     SvREFCNT_dec(sv);
1020   }
1021 }
1022 
1023 static void
Deprecated(char * what,char * file,int line)1024 Deprecated(char *what, char *file, int line)
1025 {
1026  LangDebug("%s:%d: %s is deprecated\n",file,line,what);
1027 }
1028 
1029 void
LangOldSetArg(sp,arg,file,line)1030 LangOldSetArg(sp, arg, file, line)
1031 SV **sp;
1032 SV *arg;
1033 char *file;
1034 int line;
1035 {
1036  dTHX;
1037  Deprecated("LangSetArg",file,line);
1038  LangSetObj(sp,(arg) ? SvREFCNT_inc(arg) : arg);
1039 }
1040 
1041 /* This replaces LangSetArg(sp,LangVarArg(var)) which leaked RVs */
1042 void
LangSetVar(sp,sv)1043 LangSetVar(sp,sv)
1044 SV **sp;
1045 Var sv;
1046 {
1047  dTHX;
1048  if (sv)
1049   {
1050    SV *rv = newRV(sv);
1051    LangSetObj(sp,rv);
1052   }
1053  else
1054   LangSetObj(sp,NULL);
1055 }
1056 
1057 void
LangSetInt(sp,v)1058 LangSetInt(sp, v)
1059 SV **sp;
1060 int v;
1061 {
1062  dTHX;
1063  SV *sv = *sp;
1064  do_watch();
1065  if (sv && sv != &PL_sv_undef)
1066   {
1067    sv_setiv(sv, v);
1068    SvSETMAGIC(sv);
1069   }
1070  else
1071   *sp = sv = newSViv(v);
1072 }
1073 
1074 void
LangSetDouble(sp,v)1075 LangSetDouble(sp, v)
1076 SV **sp;
1077 double v;
1078 {
1079  dTHX;
1080  SV *sv = *sp;
1081  do_watch();
1082  if (sv && sv != &PL_sv_undef)
1083   {
1084    sv_setnv(sv, v);
1085    SvSETMAGIC(sv);
1086   }
1087  else
1088   *sp = sv = newSVnv(v);
1089 }
1090 
1091 static void
die_with_trace(SV * sv,char * msg)1092 die_with_trace(SV *sv,char *msg)
1093 {
1094  dTHX;
1095  dSP;
1096  if (!sv)
1097   {
1098    sv = newSVpv("Tk",2);
1099   }
1100  ENTER;
1101  SAVETMPS;
1102  PUSHMARK(sp);
1103  XPUSHs(sv);
1104  XPUSHs(sv_2mortal(newSVpv(msg,0)));
1105  PUTBACK;
1106  perl_call_method("die_with_trace",G_VOID);
1107  FREETMPS;
1108  LEAVE;
1109 }
1110 
1111 Lang_CmdInfo *
WindowCommand(sv,hv_ptr,need)1112 WindowCommand(sv, hv_ptr, need)
1113 SV *sv;
1114 HV **hv_ptr;
1115 int need;
1116 {
1117  dTHX;
1118  STRLEN na;
1119  char *msg = "not a Tk object";
1120  if (SvROK(sv))
1121   {
1122    HV *hash = (HV *) SvRV(sv);
1123    MAGIC *mg = mg_find((SV *) hash,PERL_MAGIC_ext);
1124    if (hv_ptr)
1125     *hv_ptr = hash;
1126    if (mg)
1127     {
1128      Lang_CmdInfo *info = (Lang_CmdInfo *) SvPV(mg->mg_obj,na);
1129      if (info)
1130       {
1131        if ((need & 1) && !info->interp)
1132         croak("%s is not a Tk object",SvPV(sv,na));
1133        if ((need & 2) && !info->tkwin)
1134         croak("WindowCommand:%s is not a Tk Window",SvPV(sv,na));
1135        if ((need & 4) && !info->image)
1136         croak("%s is not a Tk Image",SvPV(sv,na));
1137        if ((need & 8) && !info->tkfont)
1138         croak("%s is not a Tk Font",SvPV(sv,na));
1139        return info;
1140       }
1141     }
1142   }
1143  else
1144   msg = "not a reference";
1145  if (need)  /* Cannot always do this - after() does this a lot ! */
1146   {
1147    die_with_trace(sv,msg);
1148   }
1149  return NULL;
1150 }
1151 
1152 
1153 
1154 Tk_Window
SVtoWindow(sv)1155 SVtoWindow(sv)
1156 SV *sv;
1157 {
1158  Lang_CmdInfo *info = WindowCommand(sv, NULL, 2);
1159  if (info && info->tkwin)
1160   return info->tkwin;
1161  return NULL;
1162 }
1163 
1164 HWND
SVtoHWND(sv)1165 SVtoHWND(sv)
1166 SV *sv;
1167 {
1168  Tk_Window tkwin = SVtoWindow(sv);
1169  if (tkwin)
1170   {
1171 #ifdef WIN32
1172    Tk_MakeWindowExist(tkwin);
1173    return Tk_GetHWND(Tk_WindowId(tkwin));
1174 #endif
1175   }
1176  return NULL;
1177 }
1178 
1179 void
1180 #ifdef STANDARD_C
Tcl_SprintfResult(Tcl_Interp * interp,char * fmt,...)1181 Tcl_SprintfResult(Tcl_Interp * interp, char *fmt,...)
1182 #else
1183 Tcl_SprintfResult(interp, fmt, va_alist)
1184 Tcl_Interp *interp;
1185 char *fmt;
1186 va_dcl
1187 #endif
1188 {
1189  dTHX;
1190  SV *sv = newSVpv("",0);
1191  va_list ap;
1192 #ifdef I_STDARG
1193  va_start(ap, fmt);
1194 #else
1195  va_start(ap);
1196 #endif
1197  sv_vsetpvfn(sv, fmt, strlen(fmt), &ap, Null(SV**), 0, NULL);
1198  Tcl_SetObjResult(interp, sv);
1199  va_end(ap);
1200 }
1201 
1202 #ifdef STANDARD_C
1203 void
Tcl_IntResults(Tcl_Interp * interp,int count,int append,...)1204 Tcl_IntResults
1205 _ANSI_ARGS_((Tcl_Interp * interp, int count, int append,...))
1206 #else
1207 /*VARARGS0 */
1208 void
1209 Tcl_IntResults(interp, count, append, va_alist)
1210 Tcl_Interp *interp;
1211 int count;
1212 int append;
1213 va_dcl
1214 #endif
1215 {
1216  dTHX;
1217  va_list ap;
1218  Tcl_Obj *result;
1219 #ifdef I_STDARG
1220  va_start(ap, append);
1221 #else
1222  va_start(ap);
1223 #endif
1224  if (!append)
1225   {
1226    Tcl_ResetResult(interp);
1227   }
1228  result = Tcl_GetObjResult(interp);
1229  if (count == 1 && !append)
1230   abort();
1231  while (count--)
1232   {
1233    int value = va_arg(ap, int);
1234    Tcl_Obj *vObj = Tcl_NewIntObj(value);
1235    Tcl_ListObjAppendElement(interp,result,vObj);
1236   }
1237  va_end(ap);
1238 }
1239 
1240 #ifdef STANDARD_C
1241 void
Tcl_DoubleResults(Tcl_Interp * interp,int count,int append,...)1242 Tcl_DoubleResults
1243 _ANSI_ARGS_((Tcl_Interp * interp, int count, int append,...))
1244 #else
1245 void
1246 Tcl_DoubleResults(interp, count, append, va_alist)
1247 Tcl_Interp *interp;
1248 int count;
1249 int append;
1250 va_dcl
1251 #endif
1252 {
1253  dTHX;
1254  va_list ap;
1255  Tcl_Obj *result;
1256 #ifdef I_STDARG
1257  va_start(ap, append);
1258 #else
1259  va_start(ap);
1260 #endif
1261  if (!append)
1262   Tcl_ResetResult(interp);
1263  result = Tcl_GetObjResult(interp);
1264  if (!count)
1265   {
1266    LangDebug("%s - No Results\n",__FUNCTION__);
1267    abort();
1268    Tcl_Panic("No results");
1269   }
1270  while (count--)
1271   {
1272    double value = va_arg(ap, double);
1273    Tcl_ListObjAppendElement(interp,result,Tcl_NewDoubleObj(value));
1274   }
1275  va_end(ap);
1276 }
1277 
1278 
1279 #ifdef STANDARD_C
1280 void
Tcl_AppendResult(Tcl_Interp * interp,...)1281 Tcl_AppendResult
1282 _ANSI_ARGS_((Tcl_Interp * interp,...))
1283 #else
1284 void
1285 Tcl_AppendResult(interp, va_alist)
1286 Tcl_Interp *interp;
1287 va_dcl
1288 #endif
1289 {
1290  SV *result = Tcl_GetObjResult(interp);
1291  va_list ap;
1292  char *s;
1293 #ifdef I_STDARG
1294  va_start(ap, interp);
1295 #else
1296  va_start(ap);
1297 #endif
1298  while ((s = va_arg(ap, char *)))
1299   {
1300    Tcl_AppendStringsToObj(result,s, NULL);
1301   }
1302  va_end(ap);
1303 }
1304 
1305 SV *
ObjectRef(interp,path)1306 ObjectRef(interp, path)
1307 Tcl_Interp *interp;
1308 char *path;
1309 {
1310  dTHX;
1311  if (path)
1312   {
1313    HV *hv = InterpHv(interp,1);
1314    SV **x = hv_fetch(hv, path, strlen(path), 0);
1315    if (x)
1316     return *x;
1317   }
1318  return &PL_sv_undef;
1319 }
1320 
1321 SV *
WidgetRef(interp,path)1322 WidgetRef(interp, path)
1323 Tcl_Interp *interp;
1324 char *path;
1325 {
1326  dTHX;
1327  HV *hv = InterpHv(interp,1);
1328  SV **x = hv_fetch(hv, path, strlen(path), 0);
1329  if (x)
1330   {
1331    SV *w = *x;
1332    if (SvROK(w) && SvTYPE(SvRV(w)) == SVt_PVHV)
1333     return w;
1334    LangDumpVec(path,1,&w);
1335    abort();
1336   }
1337  return &PL_sv_undef;
1338 }
1339 
1340 SV *
TkToWidget(tkwin,pinterp)1341 TkToWidget(tkwin,pinterp)
1342 Tk_Window tkwin;
1343 Tcl_Interp **pinterp;
1344 {
1345  dTHX;
1346  Tcl_Interp *junk;
1347  if (!pinterp)
1348   pinterp = &junk;
1349  *pinterp = NULL;
1350  if (tkwin)
1351   {
1352    TkWindow *winPtr = (TkWindow *) tkwin;
1353    TkMainInfo *mainInfo = winPtr->mainPtr;
1354    if (mainInfo)
1355     {
1356      Tcl_Interp *interp = mainInfo->interp;
1357      if (interp)
1358       {
1359        *pinterp = interp;
1360        if (Tk_PathName(tkwin))
1361          return WidgetRef(interp, Tk_PathName(tkwin));
1362       }
1363     }
1364   }
1365  return &PL_sv_undef;
1366 }
1367 
1368 
1369 Tk_Window
TkToMainWindow(tkwin)1370 TkToMainWindow(tkwin)
1371 Tk_Window tkwin;
1372 {
1373  if (tkwin)
1374   {
1375    TkWindow *winPtr = (TkWindow *) tkwin;
1376    TkMainInfo *mainInfo = winPtr->mainPtr;
1377    if (mainInfo)
1378     {
1379      return (Tk_Window) mainInfo->winPtr;
1380     }
1381   }
1382  return NULL;
1383 }
1384 
1385 Tcl_Obj *
LangWidgetObj(interp,tkwin)1386 LangWidgetObj(interp, tkwin)
1387 Tcl_Interp *interp;
1388 Tk_Window tkwin;
1389 {
1390  dTHX;
1391  return SvREFCNT_inc(TkToWidget(tkwin,NULL));
1392 }
1393 
1394 Tcl_Obj *
LangObjectObj(interp,name)1395 LangObjectObj(interp, name)
1396 Tcl_Interp *interp;
1397 char *name;
1398 {
1399  dTHX;
1400  return SvREFCNT_inc(ObjectRef(interp, name));
1401 }
1402 
1403 Tk_Font
SVtoFont(SV * sv)1404 SVtoFont(SV *sv)
1405 {
1406  dTHX;
1407  if (sv_isobject(sv) && SvPOK(SvRV(sv)))
1408   {
1409    Lang_CmdInfo *info = WindowCommand(sv, (HV **) &sv, 0);
1410    if (info)
1411     {
1412      if (!info->tkfont && info->interp)
1413       {
1414        Tk_Window tkwin = Tk_MainWindow(info->interp);
1415        if (tkwin)
1416         info->tkfont = Tk_GetFontFromObj(tkwin, sv);
1417       }
1418      if (info->tkfont)
1419       {
1420        STRLEN len;
1421        CONST char *s = Tk_NameOfFont(info->tkfont);
1422        if (strcmp(s,SvPV(sv,len)) != 0)
1423         {
1424          croak("Font %p name '%s' string '%s'",info->tkfont,s,SvPV(sv,len));
1425         }
1426       }
1427      return info->tkfont;
1428     }
1429   }
1430  return NULL;
1431 }
1432 
1433 Tcl_Obj *
LangFontObj(interp,tkfont,name)1434 LangFontObj(interp, tkfont, name)
1435 Tcl_Interp *interp;
1436 Tk_Font tkfont;
1437 char *name;
1438 {
1439  dTHX;
1440  HV *fonts = FindHv(aTHX_ interp, "LangFontArg", 1, FONTS_KEY);
1441  STRLEN na;
1442  SV *sv;
1443  SV **x;
1444  if (!name)
1445   name = (char *) Tk_NameOfFont(tkfont);
1446  x = hv_fetch(fonts, name, strlen(name), 0);
1447  if (x)
1448   {
1449    sv = *x;
1450   }
1451  else
1452   {
1453    Tk_Window tkwin = Tk_MainWindow(interp);
1454    Lang_CmdInfo info;
1455    SV *isv;
1456    sv = newSVpv(name,0);
1457    memset(&info,0,sizeof(info));
1458    info.interp = interp;
1459    info.tkfont = tkfont;
1460    IncInterp(interp,name);
1461    isv = struct_sv(&info,sizeof(info));
1462    tilde_magic(sv, isv);
1463    sv = Blessed("Tk::Font", MakeReference(sv));
1464    hv_store(fonts, name, strlen(name), sv, 0);
1465   }
1466  return SvREFCNT_inc(sv);
1467 }
1468 
1469 void
Font_DESTROY(SV * arg)1470 Font_DESTROY(SV *arg)
1471 {
1472  dTHX;
1473  STRLEN na;
1474  SV *sv;
1475  Lang_CmdInfo *info = WindowCommand(arg,(HV **) &sv,0);
1476  if (info)
1477   {
1478    if (info->interp)
1479     DecInterp(info->interp,SvPV(sv,na));
1480    sv_unmagic(sv,PERL_MAGIC_ext);
1481   }
1482 }
1483 
1484 static void
Lang_ClearErrorInfo(interp)1485 Lang_ClearErrorInfo(interp)
1486 Tcl_Interp *interp;
1487 {
1488  dTHX;
1489  AV *av = FindAv(aTHX_ interp, "Lang_ClearErrorInfo", -1, "_ErrorInfo_");
1490  if (av)
1491   {
1492    SvREFCNT_dec((SV *) av);
1493   }
1494 }
1495 
1496 void
Tcl_AddErrorInfo(interp,message)1497 Tcl_AddErrorInfo(interp, message)
1498 Tcl_Interp *interp;
1499 CONST char *message;
1500 {
1501  dTHX;
1502  if (InterpHv(interp,0))
1503   {
1504    AV *av = FindAv(aTHX_ interp, "Tcl_AddErrorInfo", 1, "_ErrorInfo_");
1505    SV *sv;
1506    while (isspace(UCHAR(*message)))
1507     message++;
1508    if (*message)
1509     av_push(av,newSVpv((char *)message,0));
1510   }
1511 }
1512 
1513 static int
Check_Eval(interp)1514 Check_Eval(interp)
1515 Tcl_Interp *interp;
1516 {
1517  dTHX;
1518  SV *sv = ERRSV;
1519  if (FindSv(aTHX_ interp, "Check_Eval", 0, "_TK_EXIT_"))
1520   return TCL_BREAK;
1521  if (SvTRUE(sv))
1522   {
1523    STRLEN len;
1524    char *s = SvPV(sv, len);
1525    if (!strncmp("_TK_EXIT_(",s,10))
1526     {
1527      Tk_Window tkwin = Tk_MainWindow(interp);
1528      SV *sv = FindSv(aTHX_ interp, "Check_Eval", 1, "_TK_EXIT_");
1529      char *e = strchr(s+=10,')');
1530      sv_setpvn(sv,s,e-s);
1531      if (tkwin)
1532       Tk_DestroyWindow(tkwin);
1533      return TCL_BREAK;
1534     }
1535    else if (!strcmp("_TK_BREAK_\n",s))
1536     {
1537      sv_setpv(sv,"");
1538      return TCL_BREAK;
1539     }
1540    else
1541     {
1542      SV *save = sv_2mortal(newSVsv(sv));
1543      s = SvPV(save, len);
1544 #if 0
1545      LangDebug("%s error:%.*s\n",__FUNCTION__,na,s);
1546 #endif
1547      if (!interp)
1548       croak("%s",s);
1549      Tcl_SetResult(interp, s, TCL_VOLATILE);
1550      sv_setpv(sv,"");
1551      return TCL_ERROR;
1552     }
1553   }
1554  return TCL_OK;
1555 }
1556 
1557 static void
Restore_widget(pTHX_ void * arg)1558 Restore_widget(pTHX_ void *arg)
1559 {
1560  SV *widget = (SV *) arg;
1561  SV * sv = GvSV(current_widget);
1562  SvSetMagicSV(sv,widget);
1563  SvREFCNT_dec(widget);
1564 #if 0
1565  LangDumpVec("Restore Tk::widget",1,&sv);
1566 #endif
1567 }
1568 
1569 static void
Set_widget(widget)1570 Set_widget(widget)
1571 SV *widget;
1572 {
1573  dTHX;
1574  if (!current_widget)
1575   current_widget = gv_fetchpv("Tk::widget",GV_ADD|GV_ADDWARN, SVt_PV);
1576  if (widget && SvROK(widget))
1577   {
1578    SV * sv = GvSV(current_widget);
1579    /* We used to use save_item() here but that and other
1580       generic perl save routines make assumptions about REFCNT
1581       and magic which we don't obey.
1582       Our REFCNT may be high, and both old an new SVs may have
1583       '~' magic for Tcl_Obj internal stuff.
1584     */
1585 #if 0
1586    LangDumpVec("save Tk::widget",1,&sv);
1587 #endif
1588    save_destructor_x(Restore_widget,LangCopyArg(sv));
1589    SvSetMagicSV(sv,widget);
1590   }
1591 }
1592 
1593 static void
Set_event(SV * event)1594 Set_event(SV *event)
1595 {
1596  dTHX;
1597  if (!current_event)
1598   current_event = gv_fetchpv("Tk::event",GV_ADD|GV_ADDWARN, SVt_PV);
1599  if (event && SvROK(event))
1600   {
1601    SV * sv = GvSV(current_event);
1602    save_item(sv);
1603    SvSetMagicSV(sv,event);
1604   }
1605 }
1606 
1607 static int
PushObjCallbackArgs(interp,svp,obj)1608 PushObjCallbackArgs(interp, svp ,obj)
1609 Tcl_Interp *interp;
1610 SV **svp;
1611 EventAndKeySym *obj;
1612 {
1613  SV *sv = *svp;
1614  dTHX;
1615  dSP;
1616  STRLEN na;
1617  if (SvTAINTED(sv))
1618   {
1619    croak("Tainted callback %"SVf,sv);
1620   }
1621  if (1 && interp && !sv_isa(sv,"Tk::Callback") && !sv_isa(sv,"Tk::Ev"))
1622   {
1623    return EXPIRE((interp,"Not a Callback '%s'",SvPV(sv,na)));
1624   }
1625  else
1626   {
1627    if (SvROK(sv) && SvTYPE(SvRV(sv)) != SVt_PVCV)
1628     sv = SvRV(sv);
1629   }
1630 
1631  PUSHMARK(sp);
1632  if (SvTYPE(sv) == SVt_PVAV)
1633   {
1634    AV *av = (AV *) sv;
1635    int n = av_len(av) + 1;
1636    SV **x = av_fetch(av, 0, 0);
1637    if (n && x)
1638     {
1639      int i = 1;
1640      sv = *x;
1641      if (SvTAINTED(sv))
1642       {
1643        croak("Callback slot 0 tainted %"SVf,sv);
1644       }
1645      /* FIXME:
1646         POE would like window passed to its callback objects
1647         Pending suggestion is:
1648          if ($object->can('_Tk_passWidget') &&
1649              $object->_Tk_passWidget($widget)
1650           {
1651            proceed_as_non_object();
1652           }
1653       */
1654      if (!sv_isobject(sv))
1655       {
1656        if (obj && obj->window) {
1657          XPUSHs(sv_mortalcopy(obj->window));
1658        }
1659       }
1660      for (i = 1; i < n; i++)
1661       {
1662        x = av_fetch(av, i, 0);
1663        if (x)
1664         {SV *arg = *x;
1665          if (SvTAINTED(arg))
1666           {
1667            croak("Callback slot %d tainted %"SVf,i,arg);
1668           }
1669          if (obj && sv_isa(arg,"Tk::Ev"))
1670           {
1671            SV *what = SvRV(arg);
1672            if (SvPOK(what))
1673             {STRLEN len;
1674              char *s = SvPV(what,len);
1675              if (len == 1)
1676               {
1677  	       PUTBACK;
1678                arg = XEvent_Info(obj, s);
1679 	       SPAGAIN;
1680               }
1681              else
1682               {char *x;
1683                arg = sv_newmortal();
1684                sv_setpv(arg,"");
1685                while ((x = strchr(s,'%')))
1686                 {
1687                  if (x > s)
1688                   sv_catpvn(arg,s,(unsigned) (x-s));
1689                  if (*++x)
1690                   {SV *f = XEvent_Info(obj, x++);
1691                    STRLEN len;
1692                    char *p = SvPV(f,len);
1693                    sv_catpvn(arg,p,len);
1694                   }
1695                  s = x;
1696                 }
1697                sv_catpv(arg,s);
1698               }
1699             }
1700            else
1701             {
1702              switch(SvTYPE(what))
1703               {
1704                case SVt_NULL:
1705                 arg = &PL_sv_undef;
1706                 break;
1707                case SVt_PVAV:
1708                 {
1709                  int code;
1710                  PUTBACK;
1711                  if ((code = PushObjCallbackArgs(interp,&arg,obj)) == TCL_OK)
1712                   {
1713                    int count = LangCallCallback(arg,G_ARRAY|G_EVAL);
1714                    if ((code = Check_Eval(interp)) != TCL_OK)
1715                     return code;
1716                    SPAGAIN;
1717                    arg = NULL;
1718                    break;
1719                   }
1720                  else
1721                   return code;
1722                 }
1723                default:
1724                 LangDumpVec("Ev",1,&arg);
1725                 LangDumpVec("  ",1,&what);
1726                 warn("Unexpected type %d %s",SvTYPE(what),SvPV(arg,na));
1727                 arg = sv_mortalcopy(arg);
1728                 break;
1729               }
1730             }
1731            if (arg) {
1732             XPUSHs(arg);
1733 	   }
1734           }
1735          else
1736           XPUSHs(sv_mortalcopy(arg));
1737         }
1738        else
1739         XPUSHs(&PL_sv_undef);
1740       }
1741     }
1742    else
1743     {
1744      if (interp)
1745       {
1746        return EXPIRE((interp,"No 0th element of %s", SvPV(sv, na)));
1747       }
1748      else
1749       sv = &PL_sv_undef;
1750     }
1751   }
1752  else
1753   {
1754    if (obj && obj->window)
1755     XPUSHs(sv_mortalcopy(obj->window));
1756   }
1757  *svp = sv;
1758  PUTBACK;
1759  return TCL_OK;
1760 }
1761 
1762 static int
PushCallbackArgs(interp,svp)1763 PushCallbackArgs(interp, svp)
1764 Tcl_Interp *interp;
1765 SV **svp;
1766 {
1767  SV *sv = *svp;
1768  dTHX;
1769  dSP;
1770  if (0 && interp && !sv_isa(sv,"Tk::Callback") && !sv_isa(sv,"Tk::Ev"))
1771   {
1772    return EXPIRE((interp,"Not a Callback '%s'",SvPV_nolen(sv)));
1773   }
1774  LangPushCallbackArgs(svp);
1775  if (interp && (sv = *svp) == &PL_sv_undef)
1776   {
1777    return EXPIRE((interp,"No 0th element of %s", SvPV_nolen(sv)));
1778   }
1779  return TCL_OK;
1780 }
1781 
1782 static void
SetTclResult(interp,count)1783 SetTclResult(interp,count)
1784 Tcl_Interp *interp;
1785 int count;
1786 {
1787  dTHX;
1788  dSP;
1789  int offset = count;
1790  Tcl_ResetResult(interp);
1791  if (count)
1792   {
1793    Tcl_Obj *result = Tcl_GetObjResult(interp);
1794    SV **p = sp - count;
1795    if (count > 1)
1796     {
1797      while (count-- > 0)
1798       {
1799        Tcl_ListObjAppendElement(interp, result, newSVsv(*++p));
1800       }
1801     }
1802    else
1803     {
1804      SvSetMagicSV(result,p[1]);
1805     }
1806   }
1807  sp -= offset;
1808  PUTBACK;
1809 }
1810 
1811 static void
PushVarArgs(ap,argc)1812 PushVarArgs(ap,argc)
1813 va_list ap;
1814 int argc;
1815 {
1816  dTHX;
1817  dSP;
1818  int i;
1819  char *fmt = va_arg(ap, char *);
1820  char *s = fmt;
1821  unsigned char ch = '\0';
1822  int lng = 0;
1823  for (i = 0; i < argc; i++)
1824   {
1825    s = strchr(s, '%');
1826    if (s)
1827     {
1828      ch  = UCHAR(*++s);
1829      lng = 0;
1830      while (isdigit(ch) || ch == '.' || ch == '-' || ch == '+')
1831       ch = *++s;
1832      if (ch == 'l')
1833       {
1834        lng = 1;
1835        ch = *++s;
1836       }
1837      switch (ch)
1838       {
1839        case 'u':
1840        case 'i':
1841        case 'd':
1842         {IV val = (lng) ? va_arg(ap, long) : va_arg(ap, int);
1843          XPUSHs(sv_2mortal(newSViv(val)));
1844         }
1845         break;
1846        case 'g':
1847        case 'e':
1848        case 'f':
1849         XPUSHs(sv_2mortal(newSVnv(va_arg(ap, double))));
1850         break;
1851        case 's':
1852         {
1853          char *x = va_arg(ap, char *);
1854          if (x)
1855           XPUSHs(sv_2mortal(Tcl_NewStringObj(x, -1)));  /* for UTF-8-ness */
1856          else
1857           XPUSHs(&PL_sv_undef);
1858         }
1859         break;
1860        case '_':
1861         {
1862          SV *x = va_arg(ap, SV *);
1863          if (x)
1864           XPUSHs(sv_mortalcopy(x));
1865          else
1866           XPUSHs(&PL_sv_undef);
1867         }
1868         break;
1869        case 'L':
1870         {
1871          Tcl_Obj *x = va_arg(ap, Tcl_Obj *);
1872          Tcl_Obj **argv;
1873          int argc;
1874          if (Tcl_ListObjGetElements(NULL,x,&argc,&argv) == TCL_OK)
1875            {
1876             int i;
1877             for (i=0; i < argc; i++)
1878              {
1879 	      XPUSHs(sv_mortalcopy((SV *) (argv[i])));
1880              }
1881            }
1882         }
1883         break;
1884        default:
1885         croak("Unimplemented format char '%c' in '%s'", ch, fmt);
1886         break;
1887       }
1888     }
1889    else
1890     croak("Not enough %%s (need %d) in '%s'", argc, fmt);
1891   }
1892  if (strchr(s,'%'))
1893   {
1894    croak("Too many %%s (need %d) in '%s'", argc, fmt);
1895   }
1896  PUTBACK;
1897 }
1898 
1899 
1900 
1901 #ifdef STANDARD_C
1902 int
LangDoCallback(Tcl_Interp * interp,LangCallback * sv,int result,int argc,...)1903 LangDoCallback
1904 _ANSI_ARGS_((Tcl_Interp * interp, LangCallback * sv, int result, int argc,...))
1905 #else
1906 int
1907 LangDoCallback(interp, sv, result, argc, va_alist)
1908 Tcl_Interp *interp;
1909 SV *sv;
1910 int result;
1911 int argc;
1912 va_dcl
1913 #endif
1914 {
1915  STRLEN na;
1916  static int flags[3] = { G_DISCARD, G_SCALAR, G_ARRAY };
1917  int count = 0;
1918  int code;
1919  SV *cb    = sv;
1920  dTHX;
1921  ENTER;
1922  SAVETMPS;
1923  if (interp)
1924   {
1925    Tcl_ResetResult(interp);
1926    Lang_ClearErrorInfo(interp);
1927   }
1928  code = PushCallbackArgs(interp,&sv);
1929  if (code != TCL_OK)
1930   return code;
1931  if (argc)
1932   {
1933    va_list ap;
1934 #ifdef I_STDARG
1935    va_start(ap, argc);
1936 #else
1937    va_start(ap);
1938 #endif
1939    PushVarArgs(ap,argc);
1940    va_end(ap);
1941   }
1942  count = LangCallCallback(sv, flags[result] | G_EVAL);
1943  if (interp && result)
1944   SetTclResult(interp,count);
1945  FREETMPS;
1946  LEAVE;
1947  count = Check_Eval(interp);
1948  if (count == TCL_ERROR && interp)
1949   {
1950    SV *tmp = newSVpv("", 0);
1951    LangCatArg(tmp,cb,0);
1952    Tcl_AddErrorInfo(interp,SvPV(tmp,na));
1953    SvREFCNT_dec(tmp);
1954   }
1955  return count;
1956 }
1957 
1958 static
HandleBgErrors(clientData)1959 void HandleBgErrors(clientData)
1960 ClientData clientData;
1961 {
1962  dTHX;
1963  Tcl_Interp *interp = (Tcl_Interp *) clientData;
1964  AV *pend   = FindAv(aTHX_ interp, "HandleBgErrors", 0, "_PendingErrors_");
1965  ENTER;
1966  SAVETMPS;
1967  TAINT_NOT;
1968  if (pend)
1969   {
1970    Set_widget( WidgetRef(interp,"."));
1971    while (av_len(pend) >= 0)
1972     {
1973      SV *sv = av_shift(pend);
1974      if (sv && SvOK(sv))
1975       {
1976        int result = PushCallbackArgs(interp,&sv);
1977        if (result == TCL_OK)
1978         {
1979          LangCallCallback(sv, G_DISCARD | G_EVAL);
1980          result = Check_Eval(interp);
1981         }
1982        if (result == TCL_BREAK)
1983         break;
1984        else if (result == TCL_ERROR)
1985         {
1986          warn("Background Error: %s",Tcl_GetStringResult(interp));
1987         }
1988       }
1989     }
1990    av_clear(pend);
1991   }
1992  FREETMPS;
1993  LEAVE;
1994  Tcl_ResetResult(interp);
1995  DecInterp(interp,"HandleBgErrors");
1996 }
1997 
1998 void
Tcl_BackgroundError(interp)1999 Tcl_BackgroundError(interp)
2000 Tcl_Interp *interp;
2001 {
2002  dTHX;
2003  int old_taint = PL_tainted;
2004  TAINT_NOT;
2005 #if 0
2006  warn(__FUNCTION__);
2007 #endif
2008  if (InterpHv(interp,0))
2009   {
2010    AV *pend = FindAv(aTHX_ interp, "Tcl_BackgroundError", 1, "_PendingErrors_");
2011    AV *av   = FindAv(aTHX_ interp, "Tcl_BackgroundError", -1, "_ErrorInfo_");
2012    SV *obj  = WidgetRef(interp,".");
2013    if (obj && SvROK(obj))
2014     obj = SvREFCNT_inc(obj);
2015    else
2016     obj = newSVpv(BASEEXT,0);
2017    if (!av)
2018     {
2019      av = newAV();
2020      TagIt((SV *) av, "Tcl_BackgroundError");
2021     }
2022    av_unshift(av,3);
2023    av_store(av, 0, newSVpv("Tk::Error",0));
2024    av_store(av, 1, obj);
2025    av_store(av, 2, newSVpv(Tcl_GetStringResult(interp),0));
2026    av_push( pend, LangMakeCallback(MakeReference((SV *) av)));
2027    if (av_len(pend) <= 0)
2028     {
2029      /* 1st one - setup callback */
2030      IncInterp(interp,"Tk_BackgroundError");
2031      Tcl_DoWhenIdle(HandleBgErrors, (ClientData) interp);
2032     }
2033    Tcl_ResetResult(interp);
2034   }
2035  TAINT_IF(old_taint);
2036 }
2037 
2038 static void
Lang_MaybeError(interp,code,why)2039 Lang_MaybeError(interp,code,why)
2040 Tcl_Interp *interp;
2041 int code;
2042 char *why;
2043 {
2044  if (code != TCL_OK)
2045   {
2046    Tcl_AddErrorInfo(interp,why);
2047    Tcl_BackgroundError(interp);
2048   }
2049  else
2050   Lang_ClearErrorInfo(interp);
2051 }
2052 
2053 void
ClearErrorInfo(win)2054 ClearErrorInfo(win)
2055 SV *win;
2056 {Lang_CmdInfo *info = WindowCommand(win,NULL,1);
2057  Lang_ClearErrorInfo(info->interp);
2058 }
2059 
2060 
2061 static int
Return_Object(int items,int offset,Tcl_Obj * sv)2062 Return_Object(int items, int offset, Tcl_Obj *sv)
2063 {
2064  dTHX;
2065  int gimme = GIMME_V;
2066  int count = 0;
2067  int i;
2068  SV **objv = NULL;
2069  SV **args = NULL;
2070  /* Get stack as it is now */
2071  dSP;
2072  switch(gimme)
2073   {
2074    case G_VOID :
2075     count = 0;
2076     objv  = NULL;
2077     break;
2078    case G_ARRAY:
2079     if (!SvOK(sv))
2080      {
2081       count = 0;
2082       break;
2083      }
2084     else if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV &&
2085         !SvMAGICAL(sv) && !sv_isobject(sv))
2086      {
2087       Tcl_ListObjGetElements(NULL,sv,&count,&objv);
2088       break;
2089      }
2090     else
2091      {
2092       /* warn("Special obj in list context"); */
2093      }
2094    default:
2095     count = 1;
2096     objv  = &sv;
2097 #if 0
2098     /* Breaks Canvas group members return */
2099     if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV && !sv_isobject(sv))
2100      {
2101       if (av_len((AV *)SvRV(sv)) == 0)
2102        {
2103         die_with_trace(NULL,"One element array in scalar context");
2104         objv = av_fetch((AV *)SvRV(sv),0,0);
2105        }
2106      }
2107 #endif
2108     break;
2109   }
2110  SPAGAIN;
2111  if (count > items)
2112   {
2113    EXTEND(sp, (count - items));
2114   }
2115  /* Now move 'args' to 0'th arg position in current stack */
2116  args = sp + offset;
2117  for (i = count-1; i >= 0; i--)
2118   {
2119    args[i] = sv_mortalcopy(objv[i]);
2120   }
2121  /* Copy stack pointer back to global */
2122  PUTBACK;
2123  return count;
2124 }
2125 
2126 static int
Return_Results(Tcl_Interp * interp,int items,int offset)2127 Return_Results(Tcl_Interp *interp,int items, int offset)
2128 {
2129  Tcl_Obj *result = Tcl_GetObjResult(interp);
2130  int count = Return_Object(items,offset,result);
2131  Tcl_ResetResult(interp);
2132  return count;
2133 }
2134 
2135 static void
Lang_TaintCheck(char * s,int items,SV ** args)2136 Lang_TaintCheck(char *s, int items, SV **args)
2137 {
2138  dTHX;
2139  if (PL_tainting)
2140   {
2141    int i;
2142    for (i=0; i < items; i++)
2143     {
2144      if (SvTAINTED(args[i]))
2145       croak("Tcl_Obj * %d to `%s' (%"SVf") is tainted",i,s,args[i]);
2146     }
2147   }
2148 }
2149 
2150 struct pTkCheckChain
2151 {
2152  struct pTkCheckChain *link;
2153  SV *sv;
2154 };
2155 
2156 void
Tk_CheckHash(SV * sv,struct pTkCheckChain * tail)2157 Tk_CheckHash(SV *sv,struct pTkCheckChain *tail)
2158 {
2159  dTHX;
2160  struct pTkCheckChain chain;
2161  HE *he;
2162  HV *hv;
2163  SV **svp;
2164  if (SvROK(sv))
2165   sv = SvRV(sv);
2166  chain.link = tail;
2167  chain.sv   = sv;
2168  if (SvTYPE(sv) != SVt_PVHV)
2169   return;
2170  hv = (HV *) sv;
2171  hv_iterinit(hv);
2172  while ((he = hv_iternext(hv)))
2173   {
2174    SV *val = hv_iterval(hv,he);
2175    if (val)
2176     {
2177      if (SvREFCNT(val) <= 0)
2178       {I32 len;
2179        char *key = hv_iterkey(he,&len);
2180        LangDebug("%.*s has 0 REFCNT\n",(int) len, key);
2181        sv_dump((SV *)hv);
2182        abort();
2183       }
2184      else
2185       {
2186        if (SvROK(val))
2187         val = SvRV(val);
2188        if (SvTYPE((SV *) val) == SVt_PVHV /*  && SvOBJECT(val) */)
2189         {
2190          struct pTkCheckChain *p = &chain;
2191          I32 len;
2192          while (p)
2193           {
2194            if (p->sv == val)
2195             {I32 len;
2196              char *key = hv_iterkey(he,&len);
2197              LangDebug("Check Loop %.*s %p - %p\n",(int) len, key, hv, val);
2198              goto skip;
2199             }
2200            p = p->link;
2201           }
2202          /* LangDebug("Check %p{%s}\n",hv,hv_iterkey(he,&len)); */
2203          Tk_CheckHash(val,&chain);
2204          skip:
2205           /* do nothing */;
2206         }
2207       }
2208     }
2209   }
2210 }
2211 
2212 int
Call_Tk(info,items,args)2213 Call_Tk(info, items, args)
2214 Lang_CmdInfo *info;
2215 int items;
2216 SV **args;
2217 {
2218  int count = 1;
2219  STRLEN na;
2220  if (info)
2221   {
2222    dTHX;
2223    dSP;
2224    SV *what = SvREFCNT_inc(args[0]);
2225    SV *exiting;
2226    Tcl_Interp *interp = info->interp;
2227    int old_taint = PL_tainted;
2228    IncInterp(interp,"Call_Tk");
2229    PL_tainted = 0;
2230    do_watch();
2231 
2232    Tcl_ResetResult(interp);
2233    if (info->Tk.proc || info->Tk.objProc)
2234     {
2235      int i;
2236      /* Must find offset of 0'th arg now in case
2237         stack moves as a result of the call
2238       */
2239      int offset = args - sp;
2240      int code;
2241      SV **our_sp = sp;
2242 
2243      Tcl_ObjCmdProc *proc = info->Tk.objProc;
2244      ClientData cd = info->Tk.objClientData;
2245 
2246      if (!proc)
2247       {
2248        proc = (Tcl_ObjCmdProc *) (info->Tk.proc);
2249        cd   = info->Tk.clientData;
2250       }
2251      if (PL_tainting)
2252       {
2253        Lang_TaintCheck(Tcl_GetString(args[0]),items, args);
2254       }
2255      for (i=0; i < items; i++)
2256       {
2257        if (SvPOK(args[i]))
2258         Tcl_GetString(args[i]);
2259       }
2260 
2261      Tcl_Preserve(interp);
2262 
2263      /* BEWARE if Tk code does a callback to perl and perl grows the
2264         stack then args that Tk code has will still point at old stack.
2265         Thus if Tk tests args[i] *after* the callback it will get junk.
2266         (Note it is only vector that is at risk, SVs themselves will stay put.)
2267 
2268         So we pre-emptively swap perl stack so any callbacks
2269         which grow their stack don't move our "args"
2270       */
2271      ENTER;
2272      SAVETMPS;
2273      SPAGAIN;
2274      PUSHSTACK;
2275      PUTBACK;
2276 
2277      code = (*proc) (cd, interp, items, args);
2278 
2279      POPSTACK;
2280      SPAGAIN;
2281      FREETMPS;
2282      LEAVE;
2283 
2284      if (sp != our_sp)
2285       abort();
2286 
2287      Tcl_Release(interp);
2288 
2289      /* info stucture may have been free'ed now ... */
2290 #ifdef WIN32
2291      if (DCcount)
2292        {
2293         warn("DCcount %ld for %s",DCcount, Tcl_GetString(what));
2294         // LangDumpVec("DCcount",items,args);
2295         DCcount = 0;
2296        }
2297 #endif
2298      if ((exiting = FindSv(aTHX_ interp, "Check_Eval", 0, "_TK_EXIT_")))
2299       {
2300        PL_tainted = old_taint;
2301        DecInterp(interp, "Call_Tk");
2302        SvREFCNT_dec(what);
2303        TclpExit(SvIV(exiting));
2304       }
2305      else if (code == TCL_OK)
2306       {
2307        count = Return_Results(interp,items,offset);
2308       }
2309      else if (code == TCL_BREAK)
2310       {
2311        PL_tainted = old_taint;
2312        DecInterp(interp, "Call_Tk");
2313        SvREFCNT_dec(what);
2314        croak("_TK_BREAK_\n");
2315       }
2316      else
2317       {
2318        SV *msg = sv_newmortal();
2319        sv_setpv(msg,"Tk callback for ");
2320        sv_catpv(msg,Tcl_GetString(what));
2321        Tcl_AddErrorInfo(interp, SvPV(msg,na));
2322        sv_setpv(msg,Tcl_GetStringResult(interp));
2323 
2324        PL_tainted = old_taint;
2325        DecInterp(interp, "Call_Tk");
2326        SvREFCNT_dec(what);
2327        croak("%s",SvPV(msg,na));
2328       }
2329     }
2330    else
2331     {
2332      /* call after DeleteWidget */
2333      if (info->tkwin)
2334       croak("%s has been deleted",Tk_PathName(info->tkwin));
2335     }
2336    PL_tainted = old_taint;
2337    DecInterp(interp, "Call_Tk");
2338    SvREFCNT_dec(what);
2339   }
2340  else
2341   {
2342    /* Could be an "after" when mainwindow has been destroyed */
2343   }
2344  do_watch();
2345  return count;
2346 }
2347 
2348 static void
InitVtabs(void)2349 InitVtabs(void)
2350 {
2351  dTHX;
2352  /* Called by Boot_Glue below, re-called in 5.004_50+ at start of run phase.
2353   * If we have been "Compiled" then module this code is defined in
2354   * will have been re-linked, so the 'static' above will be 0 again
2355   * which will cause us to re-set vtables with addresses where
2356   * we happen to be loaded now, as opposed to where we were loaded
2357   * at compile time.
2358   */
2359  if (!initialized)
2360   {
2361    IMPORT_EVENT;
2362    install_vtab("LangVtab",LangVGet(),sizeof(LangVtab));
2363    install_vtab("TcldeclsVtab",TcldeclsVGet(),sizeof(TcldeclsVtab));
2364    install_vtab("TkVtab",TkVGet(),sizeof(TkVtab));
2365    install_vtab("TkdeclsVtab",TkdeclsVGet(),sizeof(TkdeclsVtab));
2366    install_vtab("TkglueVtab",TkglueVGet(),sizeof(TkglueVtab));
2367    install_vtab("TkintVtab",TkintVGet(),sizeof(TkintVtab));
2368    install_vtab("TkintdeclsVtab",TkintdeclsVGet(),sizeof(TkintdeclsVtab));
2369    install_vtab("TkoptionVtab",TkoptionVGet(),sizeof(TkoptionVtab));
2370    install_vtab("TkimgphotoVtab",TkimgphotoVGet(),sizeof(TkimgphotoVtab));
2371    install_vtab("ImgintVtab",ImgintVGet(),sizeof(ImgintVtab));
2372 #ifdef WIN32
2373    install_vtab("TkintplatdeclsVtab",TkintplatdeclsVGet(),sizeof(TkintplatdeclsVtab));
2374    install_vtab("TkplatdeclsVtab",TkplatdeclsVGet(),sizeof(TkplatdeclsVtab));
2375    install_vtab("TkintxlibdeclsVtab",TkintxlibdeclsVGet(),sizeof(TkintxlibdeclsVtab));
2376 #else
2377    install_vtab("XlibVtab",XlibVGet(),sizeof(XlibVtab));
2378 #endif
2379    Boot_Tix(aTHX);
2380   }
2381  initialized++;
2382 }
2383 
XS(XS_Tk__MainWindow_Create)2384 XS(XS_Tk__MainWindow_Create)
2385 {
2386  dXSARGS;
2387  STRLEN na;
2388  Tcl_Interp *interp = Tcl_CreateInterp();
2389  SV **args = &ST(0);
2390  char *appName = items >= 1 ? SvPV(ST(1),na) : "";
2391  int offset = args - sp;
2392  int code;
2393  if (!initialized)
2394   InitVtabs();
2395  code = TkCreateFrame(NULL, interp, items, &ST(0), 1, appName);
2396  if (code != TCL_OK)
2397   {
2398    Tcl_AddErrorInfo(interp, "Tk::MainWindow::Create");
2399    croak("%s",Tcl_GetStringResult(interp));
2400   }
2401 #if !defined(WIN32) && !defined(__PM__) && !(defined(OS2) && defined(__WIN32__))
2402  TkCreateXEventSource();
2403 #endif
2404  TKXSRETURN(Return_Results(interp,items,offset));
2405 }
2406 
2407 
2408 static int
SelGetProc(clientData,interp,portion,numItems,format,type,tkwin)2409 SelGetProc(clientData,interp,portion,numItems,format,type,tkwin)
2410 ClientData clientData;
2411 Tcl_Interp *interp;
2412 long *portion;
2413 int numItems;
2414 int format;
2415 Atom type;
2416 Tk_Window tkwin;
2417 {
2418  dTHX;
2419  Tcl_Obj *result = (Tcl_Obj *) clientData;
2420  char *p = (char *) portion;
2421  SV *sv  = Nullsv;
2422  if (format == 8)
2423   {
2424    TkWindow *winPtr = (TkWindow *) tkwin;
2425    TkDisplay *dispPtr = winPtr->dispPtr;
2426    /* Whole can-of-worms here:
2427       Mozilla has various text/... targets with no charset
2428       data which are in some 16-bit Unicode UCS-2/utf-16 style
2429       for which this would be correct:
2430         format = 16;
2431         numItems /= 2;
2432       (It is a little-endian 16-bit on Linux-x86.)
2433 
2434       Note that the is_utf8_string test will _PASS_ for
2435       the 16-bit case with plain ASCII as '\0' is legitimate UTF-8
2436 
2437       KDE's Konsole has text/plain;charset=xxxx
2438       charsets are mostly really 8-bit but also has
2439       ISO-10646-UCS-2 which is 16-bit with a leading BOM
2440       For KDE there is no real gain in using one of these
2441       as UTF8_STRING returns same information.
2442       The mozilla targets might be interesting.
2443 
2444       Bare is_utf8_string() test may be wrong as well as
2445       we may get partial characters ?
2446    */
2447    if ((dispPtr->utf8Atom != None && type == dispPtr->utf8Atom) ||
2448         is_utf8_string((U8 *) p, numItems))
2449     {
2450      Tcl_AppendToObj(result, p, numItems);
2451     }
2452    else
2453     {
2454      const char *strType = Tk_GetAtomName(tkwin, type);
2455      /* Do NOT use NewStringObj on it that assumes UTF-8-ness
2456         and we have established it isn't
2457       */
2458      sv = newSVpvn(p, numItems);
2459 #if 0
2460      LangDebug("%s %d '%.*s'\n",__FUNCTION__,numItems,numItems,p);
2461      LangDumpVec(strType,1,&sv);
2462      abort();
2463 #endif
2464      Tcl_ListObjAppendElement(interp,result,sv);
2465     }
2466   }
2467  else
2468   {
2469    if (type == Tk_InternAtom(tkwin,"TARGETS"))
2470     type = XA_ATOM;
2471    while (numItems-- > 0)
2472     {
2473      IV value = 0;
2474      sv = Nullsv;
2475      if (8 * sizeof(unsigned char) == format)
2476       {
2477        value = *((unsigned char *) p);
2478       }
2479      else if (8 * sizeof(unsigned short) == format)
2480       {
2481        value = *((unsigned short *) p);
2482       }
2483      else if (8 * sizeof(unsigned int) == format)
2484       {
2485        value = *((unsigned int *) p);
2486       }
2487      else if (8 * sizeof(unsigned long) == format)
2488       {
2489        value = *((unsigned long *) p);
2490       }
2491      else
2492       {
2493        return EXPIRE((interp, "No C type for format %d", format));
2494       }
2495      p += (format / 8);
2496      if (type == XA_ATOM)
2497       {
2498        if (value)
2499         {
2500          sv = newSVpv(Tk_GetAtomName(tkwin,(Atom) value),0);
2501          sv_setiv(sv,value);
2502          SvPOK_on(sv);
2503         }
2504       }
2505      else
2506       sv = newSViv(value);
2507      if (sv)
2508       Tcl_ListObjAppendElement(interp,result,sv);
2509     }
2510   }
2511  return TCL_OK;
2512 }
2513 
2514 static int
isSwitch(s)2515 isSwitch(s)
2516 char *s;
2517 {int ch;
2518  if (*s++ != '-')
2519   return 0;
2520  if (!isalpha(UCHAR(*s)))
2521   return 0;
2522  while ((ch = UCHAR(*++s)))
2523   {
2524    if (!isalnum(ch) && ch != '_')
2525     return 0;
2526   }
2527  return 1;
2528 }
2529 
XS(XS_Tk__Widget_SelectionGet)2530 XS(XS_Tk__Widget_SelectionGet)
2531 {
2532  dXSARGS;
2533  STRLEN na;
2534  int offset = &ST(0) - sp;
2535  Lang_CmdInfo *info = WindowCommand(ST(0), NULL, 3);
2536  TkWindow *winPtr = (TkWindow *) info->tkwin;
2537  TkDisplay *dispPtr = winPtr->dispPtr;
2538  Atom selection = XA_PRIMARY;
2539  Atom target    = None;
2540  int i = 1;
2541  Tcl_Obj *result = NULL;
2542  int retval = TCL_ERROR;
2543  while (i < items)
2544   {STRLEN len;
2545    char *s = SvPV(ST(i),len);
2546    if (len && !isSwitch(s))
2547     {
2548      target = Tk_InternAtom(info->tkwin,s);
2549      i += 1;
2550     }
2551    else if (len >= 2 && !strncmp(s,"-type",len))
2552     {
2553      if (i+1 < items)
2554       target = Tk_InternAtom(info->tkwin,SvPV(ST(i+1),na));
2555      i += 2;
2556     }
2557    else if (len >= 2 && !strncmp(s,"-selection",len))
2558     {
2559      if (i+1 < items)
2560       selection = Tk_InternAtom(info->tkwin,SvPV(ST(i+1),na));
2561      i += 2;
2562     }
2563    else
2564     croak("Bad option '%s'",s);
2565   }
2566  result = Tcl_NewObj();
2567  if (target == None)
2568   {
2569    /* Caller did not specify a target
2570        Try UTF8_STRING and if that fails try STRING
2571 
2572        But if they _ask_ for STRING then target will be set
2573        so we don't come here and just go for STRING below.
2574 
2575        We could get TARGETS list and then only ask for UTF8_STRING
2576        if owner supports it. But that would still be two requests
2577        and involves either a direct call to XConvertSelection()
2578        which is at best faked on Win32, or poking about in list of
2579        strings returned for the atoms.
2580 
2581        The more sophisticated TARGETS approach might start to win if we
2582        want to try TEXT COMPOUND_TEXT text/plain and other legacy
2583        ways of passing non-ASCII. But it seems like most applications
2584        are doing UTF8_STRING these days.
2585 
2586        We do UTF8_STRING first as owner may advertise STRING but
2587        fail to return it if selection contains high characters,
2588        or it may return STRING with a lot of '?' or '#' or other
2589        "marker" for non-converted chars. In contrast UTF-8 gives
2590        then no excuses ;-)
2591 
2592     */
2593    if (dispPtr->utf8Atom != None)
2594     {
2595      /* Try for UTF8_STRING */
2596      retval = Tk_GetXSelection(info->interp, info->tkwin, selection,
2597                               dispPtr->utf8Atom, SelGetProc,
2598                               (ClientData) result);
2599     }
2600    target = XA_STRING;
2601   }
2602  if (retval != TCL_OK)
2603   {
2604    retval = Tk_GetXSelection(info->interp, info->tkwin, selection, target,
2605                              SelGetProc, (ClientData) result);
2606   }
2607  if (retval != TCL_OK)
2608   {
2609    Tcl_DecrRefCount(result);
2610    croak("%s", Tcl_GetString(Tcl_GetObjResult(info->interp)));
2611   }
2612  retval = Return_Object(items,offset,result);
2613  Tcl_DecrRefCount(result);
2614  XSRETURN(retval);
2615 }
2616 
2617 static I32
InsertArg(mark,posn,sv)2618 InsertArg(mark,posn,sv)
2619 SV **mark;
2620 I32 posn;
2621 SV *sv;
2622 {
2623  dTHX;
2624  dSP;
2625  I32 items = sp - mark;
2626  MEXTEND(sp, 1);                     /* May not be room ? */
2627  while (sp > mark + posn)            /* Move all but one args up 1 */
2628   {
2629    sp[1] = sp[0];
2630    sp--;
2631   }
2632  mark[posn+1] = sv;
2633  sp = mark + (++items);
2634  PUTBACK;
2635  return items;
2636 }
2637 
XS(XStoWidget)2638 XS(XStoWidget)
2639 {
2640  dXSARGS;
2641  Lang_CmdInfo *info = WindowCommand(ST(0), NULL, 1);
2642  do_watch();
2643  items = InsertArg(mark,1,XSANY.any_ptr);
2644  TKXSRETURN(Call_Tk(info, items, &ST(0)));
2645 }
2646 
2647 static SV *
NameFromCv(cv)2648 NameFromCv(cv)
2649 CV *cv;
2650 {
2651  dTHX;
2652  SV *sv = NULL;
2653  if (cv)
2654   {
2655    GV *gv = CvGV(cv);
2656    char *s = GvNAME(gv);
2657    STRLEN l = GvNAMELEN(gv);
2658    sv = sv_newmortal();
2659    sv_setpvn(sv, s, l);
2660 #ifdef DEBUG_GLUE
2661    fprintf(stderr, "Recovered name '%s'\n", Tcl_GetString(sv));
2662 #endif
2663   }
2664  else
2665   croak("No CV passed");
2666  return sv;
2667 }
2668 
2669 Tk_Window
Tk_MainWindow(interp)2670 Tk_MainWindow(interp)
2671 Tcl_Interp *interp;
2672 {
2673  dTHX;
2674  HV *hv = InterpHv(interp,0);
2675  if (hv)
2676   {
2677    MAGIC *mg = mg_find((SV *) hv, PERL_MAGIC_ext);
2678    if (mg)
2679     {
2680      return INT2PTR(Tk_Window, SvIV(mg->mg_obj));
2681     }
2682   }
2683  return NULL;
2684 }
2685 
2686 static int
InfoFromArgs(info,proc,mwcd,items,args)2687 InfoFromArgs(info,proc,mwcd,items,args)
2688 Lang_CmdInfo *info;
2689 Tcl_ObjCmdProc *proc;
2690 int mwcd;
2691 int items;
2692 SV **args;
2693 {
2694  dTHX;
2695  SV *fallback = NULL;
2696  int i;
2697  memset(info,0,sizeof(*info));
2698  info->Tk.objProc = proc;
2699  for (i=0; i < items; i++)
2700   {
2701    SV *sv = args[i];
2702    if (SvROK(sv) && sv_isobject(sv))
2703     {
2704      Lang_CmdInfo *winfo = WindowCommand(sv,NULL,0);
2705      if (winfo && winfo->interp)
2706       {
2707        if (winfo->interp != info->interp)
2708         info->interp = winfo->interp;
2709        if (mwcd)
2710         {
2711          Tk_Window mw;
2712          if (winfo->tkwin)
2713           mw = TkToMainWindow(winfo->tkwin);
2714          else
2715           mw = Tk_MainWindow(winfo->interp);
2716          if (mw)
2717           {
2718            if ((ClientData) mw != info->Tk.objClientData)
2719             {
2720              if (info->Tk.objClientData)
2721               {
2722                PerlIO_printf(PerlIO_stderr(),"cmd %p/%p using %p/%p\n",
2723                        info->Tk.objClientData,info->interp,
2724                        mw, winfo->interp);
2725               }
2726              info->Tk.objClientData = (ClientData) mw;
2727             }
2728           }
2729         }
2730        return i;
2731       }
2732     }
2733   }
2734  fallback = perl_get_sv("Tk::_Interp",TRUE);
2735  if (!SvROK(fallback))
2736   {
2737    Tcl_Interp *interp = Tcl_CreateInterp();
2738    SV *sv = sv_2mortal(MakeReference((SV *) interp));
2739 #if 0
2740    Tcl_CallWhenDeleted(interp, TkEventCleanupProc, (ClientData) NULL);
2741 #endif
2742    SvSetMagicSV(fallback,sv);
2743   }
2744  info->interp = (Tcl_Interp *) SvRV(fallback);
2745  return -1;
2746 }
2747 
2748 static
XS(XStoSubCmd)2749 XS(XStoSubCmd)
2750 {
2751  dXSARGS;
2752  STRLEN na;
2753  Lang_CmdInfo info;
2754  SV *name = NameFromCv(cv);
2755  int posn = InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
2756  if (posn < 0)
2757   {
2758 #if 0
2759    LangDumpVec(Tcl_GetString(name),items,&ST(0));
2760 #endif
2761    die_with_trace(ST(0),"XStoSubCmd: Not a Tk Window");
2762   }
2763  if (posn == 0)
2764   {
2765    /* Do arg re-ordering to covert grab/wm like calls from
2766       perl method call form to that expected by Tk
2767               0   1   2
2768       have [ win sub ?-opt? ....     ]
2769       need [ cv  sub ?-opt? win ...  ]
2770 
2771     */
2772 
2773    MEXTEND(sp, 1);                /* May not be room ? */
2774    while (sp > mark + 2)          /* Move all but two args up 1 */
2775     {
2776      if (SvPOK(*sp) && isSwitch(SvPV(*sp, na)))
2777       break;
2778      sp[1] = sp[0];
2779      sp--;
2780     }
2781    sp[1] = mark[1];               /* Move object = window arg */
2782    sp = mark + (++items);         /* move sp past the lot */
2783    PUTBACK;                       /* and reset the global */
2784   }
2785  ST(0) = name;          /* Fill in command name */
2786  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
2787 }
2788 
2789 static
XS(XStoEvent)2790 XS(XStoEvent)
2791 {
2792  dXSARGS;
2793  STRLEN na;
2794  Lang_CmdInfo info;
2795  SV *name = NameFromCv(cv);
2796  int posn = InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
2797  if (posn < 0)
2798   {
2799    croak("XStoEvent:%s is not a Tk Window",SvPV(ST(0),na));
2800   }
2801  if (posn == 0)
2802   {
2803    if (SvPOK(mark[2]) && strcmp(SvPV(mark[2], na), "generate") == 0)
2804     {
2805       /* Do arg re-ordering to convert calls from
2806 	 perl method call form to that expected by Tk
2807 	        0   1   2
2808 	 have [ win sub ?-opt? ....     ]
2809 	 need [ cv  sub win ?-opt? ...  ]
2810 
2811 	 */
2812      MEXTEND(sp, 1);                /* May not be room ? */
2813      while (sp > mark + 2)          /* Move all but two args up 1 */
2814       {
2815        sp[1] = sp[0];
2816        sp--;
2817       }
2818      sp[1] = mark[1];               /* Move object = window arg */
2819      sp = mark + (++items);         /* move sp past the lot */
2820      PUTBACK;                       /* and reset the global */
2821     }
2822   }
2823  ST(0) = name;          /* Fill in command name */
2824  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
2825 }
2826 
2827 
2828 static
XS(XStoAfterSub)2829 XS(XStoAfterSub)
2830 {
2831  dXSARGS;
2832  STRLEN na;
2833  Lang_CmdInfo info;
2834  SV *name = NameFromCv(cv);
2835  int posn = InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
2836  if (posn != 0)
2837   {
2838    LangDumpVec(SvPV(name,na),items,&ST(0));
2839    croak("Usage $widget->%s(...)",SvPV(name,na));
2840   }
2841  /* Find a place for the widget arg after a possible subcommands */
2842  posn = 1;
2843  if (posn < items && SvPOK(ST(posn)) && !isSwitch(SvPV(ST(posn),na)))
2844   posn++;
2845  items = InsertArg(mark,posn,ST(0));
2846  ST(0) = name;          /* Fill in command name */
2847  Tcl_GetCommandInfo(info.interp,Tcl_GetString(name),&info.Tk);
2848  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
2849 }
2850 
2851 static
XS(XStoGrid)2852 XS(XStoGrid)
2853 {
2854  dXSARGS;
2855  STRLEN na;
2856  Lang_CmdInfo info;
2857  SV *name = NameFromCv(cv);
2858  int posn = InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
2859  if (posn == 0 && 0)
2860   {
2861    /* Find a place for the widget arg after a possible subcommands */
2862    posn = 1;
2863    if (posn < items && SvPOK(ST(posn)) && !isSwitch(SvPV(ST(posn),na)))
2864     posn++;
2865    items = InsertArg(mark,posn,ST(0));
2866    ST(0) = name;          /* Fill in command name */
2867   }
2868  items = InsertArg(mark,0, name);
2869 #if 0
2870  LangDumpVec("grid", items, &ST(0));
2871 #endif
2872  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
2873 }
2874 
2875 
2876 static
XS(XStoDisplayof)2877 XS(XStoDisplayof)
2878 {
2879  dXSARGS;
2880  STRLEN na;
2881  Lang_CmdInfo info;
2882  SV *name = NameFromCv(cv);
2883  int posn = InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
2884  if (posn != 0)
2885   {
2886    LangDumpVec(SvPV(name,na),items,&ST(0));
2887    croak("Usage $widget->%s(...)",SvPV(name,na));
2888   }
2889  posn = 1;
2890  if (posn < items && SvPOK(ST(posn)) && !isSwitch(SvPV(ST(posn),na)))
2891   posn++;
2892  items = InsertArg(mark,posn++,sv_2mortal(newSVpv("-displayof",0)));
2893  SPAGAIN;
2894  mark = sp-items;
2895  items = InsertArg(mark,posn,ST(0));
2896  ST(0) = name;          /* Fill in command name */
2897  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
2898 }
2899 
2900 static
XS(XStoTk)2901 XS(XStoTk)
2902 {
2903  dXSARGS;
2904  STRLEN na;
2905  SV *name = NameFromCv(cv);
2906  Lang_CmdInfo info;
2907  int posn =  InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
2908  if (posn < 0)
2909   {
2910    LangDumpVec(SvPV(name,na),items,&ST(0));
2911    croak("Usage $widget->%s(...)",SvPV(name,na));
2912   }
2913  if (items == 0 || !SvPOK(ST(0)) || strcmp(SvPV(ST(0),na),BASEEXT) != 0)
2914   {
2915    items = InsertArg(mark,0,name);
2916   }
2917  ST(0) = name;                      /* Fill in command name */
2918  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
2919 }
2920 
2921 static
XS(XStoOption)2922 XS(XStoOption)
2923 {
2924  dXSARGS;
2925  STRLEN na;
2926  SV *name = NameFromCv(cv);
2927  Lang_CmdInfo info;
2928  int posn =  InfoFromArgs(&info, LangOptionCommand, 1, items, &ST(0));
2929  if (posn < 0)
2930   {
2931    LangDumpVec(SvPV(name,na),items,&ST(0));
2932    croak("Usage $widget->%s(...)",SvPV(name,na));
2933   }
2934  if (items > 1 && SvPOK(ST(1)) && !strcmp(SvPV(ST(1),na),"get"))
2935   {
2936    items = InsertArg(mark,2,ST(0));
2937   }
2938  ST(0) = name;                      /* Fill in command name */
2939  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
2940 }
2941 
2942 static
XS(XStoImage)2943 XS(XStoImage)
2944 {
2945  dXSARGS;
2946  STRLEN na;
2947  SV *name = NameFromCv(cv);
2948  Lang_CmdInfo info;
2949  int posn =  InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
2950  if (posn < 0)
2951   {
2952    LangDumpVec(SvPV(name,na),items,&ST(0));
2953    croak("Usage $widget->%s(...)",SvPV(name,na));
2954   }
2955  if (items > 1 && SvPOK(ST(1)))
2956   {
2957    char *opt = SvPV(ST(1),na);
2958    if (strcmp(opt,"create") && strcmp(opt,"names") && strcmp(opt,"types"))
2959     {
2960     items = InsertArg(mark,2,ST(0));
2961     }
2962   }
2963  ST(0) = name;                      /* Fill in command name */
2964 #if 0
2965  LangDumpVec("Image",items,&ST(0));
2966 #endif
2967  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
2968 }
2969 
2970 static
XS(XStoFont)2971 XS(XStoFont)
2972 {
2973  dXSARGS;
2974  STRLEN na;
2975  SV *name = NameFromCv(cv);
2976  Lang_CmdInfo info;
2977  int posn =  InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
2978  if (posn < 0)
2979   {
2980    LangDumpVec(SvPV(name,na),items,&ST(0));
2981    croak("Usage $widget->%s(...)",SvPV(name,na));
2982   }
2983  if (items > 1 && SvPOK(ST(1)))
2984   {
2985    char *opt = SvPV(ST(1),na);
2986    if (strcmp(opt,"create") && strcmp(opt,"names") && strcmp(opt,"families"))
2987     {
2988      /* FIXME: would be better to use hint from info rather than fact that
2989         object is not hash-based */
2990      if (SvROK(ST(0)) && SvTYPE(SvRV(ST(0))) != SVt_PVHV)
2991       {
2992        items = InsertArg(mark,2,ST(0));
2993       }
2994      else if (ST(2) == &PL_sv_undef)
2995       {
2996 #if 0
2997        LangDumpVec("Font undef",items,&ST(0));
2998 #endif
2999        croak("Cannot use undef as font object");
3000       }
3001     }
3002   }
3003 
3004  ST(0) = name;                      /* Fill in command name */
3005 #if 0
3006  LangDumpVec("Font Post",items,&ST(0));
3007 #endif
3008  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
3009 }
3010 
3011 int
XSTkCommand(CV * cv,int mwcd,Tcl_ObjCmdProc * proc,int items,SV ** args)3012 XSTkCommand (CV *cv, int mwcd, Tcl_ObjCmdProc *proc, int items, SV **args)
3013 {
3014  dTHX;
3015  STRLEN na;
3016  Lang_CmdInfo info;
3017  SV *name = NameFromCv(cv);
3018  if (InfoFromArgs(&info,proc,mwcd,items,args) != 0)
3019   {
3020    croak("Usage $widget->%s(...)\n%s is not a Tk object",
3021          SvPV_nolen(name),SvPV_nolen(args[0]));
3022   }
3023  /* Having established a widget was passed in ST(0) overwrite
3024     with name of command Tk is expecting
3025   */
3026  args[0] = name;          /* Fill in command name */
3027  if (1 || !mwcd)
3028   {
3029    char *s = Tcl_GetString(name);
3030    Tcl_GetCommandInfo(info.interp,s,&info.Tk);
3031    if (!proc && info.Tk.objProc)
3032     {
3033      proc = info.Tk.objProc;
3034     }
3035    CvXSUBANY(cv).any_ptr = proc;
3036    if (!info.Tk.objProc && !info.Tk.proc)
3037     {
3038      info.Tk.objProc = proc;
3039      Tcl_SetCommandInfo(info.interp,s,&info.Tk);
3040     }
3041   }
3042  return Call_Tk(&info, items, args);
3043 }
3044 
3045 static
XS(XStoTclCmd)3046 XS(XStoTclCmd)
3047 {
3048  dXSARGS;
3049  TKXSRETURN(XSTkCommand(cv,1,(Tcl_ObjCmdProc *) XSANY.any_ptr, items, &ST(0)));
3050 }
3051 
3052 static
XS(XStoTclCmdNull)3053 XS(XStoTclCmdNull)
3054 {
3055  dXSARGS;
3056  TKXSRETURN(XSTkCommand(cv,0,(Tcl_ObjCmdProc *) XSANY.any_ptr, items, &ST(0)));
3057 }
3058 
3059 static
XS(XStoNoWindow)3060 XS(XStoNoWindow)
3061 {
3062  dXSARGS;
3063  STRLEN na;
3064  Lang_CmdInfo info;
3065  SV *name = NameFromCv(cv);
3066  HV *cm;
3067  STRLEN sz;
3068  char *cmdName = SvPV(name,sz);
3069  SV **x  ;
3070  InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,0,items,&ST(0));
3071  cm = FindHv(aTHX_ info.interp, "XStoNoWindow", 0, CMD_KEY);
3072  Tcl_GetCommandInfo(info.interp,cmdName,&info.Tk);
3073  if (items > 0 && (sv_isobject(ST(0)) || !strcmp(SvPV(ST(0),na),BASEEXT)))
3074   ST(0) = name;         /* Fill in command name */
3075  else
3076   items = InsertArg(mark,0,name);
3077  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
3078 }
3079 
3080 static CV *
TkXSUB(const char * name,XSptr xs,Tcl_ObjCmdProc * proc)3081 TkXSUB(const char *name,XSptr xs,Tcl_ObjCmdProc *proc)
3082 {
3083  dTHX;
3084  STRLEN na;
3085  SV *sv = newSVpv(BASEEXT,0);
3086  CV *cv;
3087  sv_catpv(sv,"::");
3088  sv_catpv(sv,name);
3089  if (xs && proc)
3090   {
3091    cv = newXS(SvPV(sv,na),xs,__FILE__);
3092    CvXSUBANY(cv).any_ptr = (VOID *) proc;
3093   }
3094  else
3095   {
3096    cv = perl_get_cv(SvPV(sv,na),0);
3097   }
3098  SvREFCNT_dec(sv);
3099  return cv;
3100 }
3101 
3102 void
Lang_TkCommand(name,proc)3103 Lang_TkCommand(name,proc)
3104 char *name;
3105 Tcl_ObjCmdProc *proc;
3106 {
3107  TkXSUB(name,XStoTclCmd,proc);
3108 }
3109 
3110 void
Lang_TkSubCommand(name,proc)3111 Lang_TkSubCommand(name,proc)
3112 char *name;
3113 Tcl_ObjCmdProc *proc;
3114 {
3115  TkXSUB(name,XStoAfterSub,proc);
3116 }
3117 
3118 
3119 /*
3120   The bind command is handled specially, it must *always* be called
3121   with a widget object. And only the <> form of sequence is allowed
3122   so that the following forms of call can be spotted:
3123 
3124   $widget->bind();
3125   $widget->bind('tag');
3126   $widget->bind('<...>');
3127   $widget->bind('tag','<...>');
3128   $widget->bind('<...>',command);
3129   $widget->bind('tag','<...>',command);
3130 
3131 */
3132 
3133 static
XS(XStoBind)3134 XS(XStoBind)
3135 {
3136  dXSARGS;
3137  STRLEN na;
3138  Lang_CmdInfo info;
3139  SV *name = NameFromCv(cv);
3140  int posn = InfoFromArgs(&info,(Tcl_ObjCmdProc *) XSANY.any_ptr,1,items,&ST(0));
3141  STRLEN len;
3142  if (posn < 0)
3143   {
3144    LangDumpVec(SvPV(name,na),items,&ST(0));
3145    croak("Usage $widget->%s(...)",SvPV(name,na));
3146   }
3147  if (items < 2 || *SvPV(ST(1),len) == '<')
3148   {
3149    /* Looks like $widget->bind([<..>])
3150     * i.e. bind command to widget itself
3151     * Standard move up of all the args to make room for 'bind'
3152     * as argv[0]
3153     */
3154    items = InsertArg(mark,0,name);
3155   }
3156  else
3157   {
3158    /* Looks like $widget->bind('tag',...)
3159     * simply overwrite 0'th argument with 'bind'
3160     */
3161    ST(0) = name;          /* Fill in command name */
3162 #if 0
3163    if (dowarn)
3164     {
3165      if (items == 4)
3166       {
3167        SV *sv = ST(3);
3168        if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)
3169         {
3170          LangDumpVec("bind",items,&ST(0));
3171          warn("Subreference for class binding");
3172         }
3173       }
3174     }
3175 #endif
3176   }
3177  TKXSRETURN(Call_Tk(&info, items, &ST(0)));
3178 }
3179 
3180 
3181 void
LangDeadWindow(interp,tkwin)3182 LangDeadWindow(interp, tkwin)
3183 Tcl_Interp *interp;
3184 Tk_Window tkwin;
3185 {
3186  dTHX;
3187  STRLEN na;
3188  HV *hv = InterpHv(interp,0);
3189  if (hv)
3190   {
3191    /* This is last hook before tkwin disapears
3192        - LangDeleteWidget has happened
3193        - <Destroy> bindings have happened
3194     */
3195    char *cmdName = Tk_PathName(tkwin);
3196    STRLEN cmdLen = strlen(cmdName);
3197    SV *obj = hv_delete(hv, cmdName, cmdLen, G_SCALAR);
3198    if (obj && SvROK(obj) && SvTYPE(SvRV(obj)) == SVt_PVHV)
3199     {
3200      HV *hash = (HV *) SvRV(obj);
3201      MAGIC *mg   = mg_find((SV *) hash,PERL_MAGIC_ext);
3202 
3203      /* Tk_CheckHash((SV *) hash, NULL); */
3204      if (SvREFCNT(hash) < 1)
3205       {
3206        LangDebug("%s %s has REFCNT=%d\n",__FUNCTION__,cmdName,(int) SvREFCNT(hash));
3207        sv_dump(obj);
3208       }
3209 
3210      if (mg)
3211       {
3212        Lang_CmdInfo *info = (Lang_CmdInfo *) SvPV_nolen(mg->mg_obj);
3213        if (info->interp != interp)
3214         Tcl_Panic("%s->interp=%p expected %p", cmdName, info->interp, interp);
3215        DecInterp(info->interp, cmdName);
3216        /* mg->mg_obj is SV holding the Lang_CmdInfo struct
3217           we have now finished with it
3218         */
3219        SvREFCNT_dec(mg->mg_obj);
3220        sv_unmagic((SV *) hash,PERL_MAGIC_ext);
3221       }
3222     }
3223   }
3224 }
3225 
3226 
3227 int
Tcl_DeleteCommandFromToken(interp,info)3228 Tcl_DeleteCommandFromToken(interp, info)
3229 Tcl_Interp *interp;
3230 Tcl_Command info;
3231 {
3232  if (info)
3233   {
3234    if (info->Tk.deleteProc)
3235     {
3236      (*info->Tk.deleteProc) (info->Tk.deleteData);
3237      info->Tk.deleteProc = NULL;
3238      info->Tk.deleteData = NULL;
3239     }
3240    info->Tk.clientData    = NULL;
3241    info->Tk.proc          = NULL;
3242    info->Tk.objClientData = NULL;
3243    info->Tk.objProc       = NULL;
3244   }
3245  return TCL_OK;
3246 }
3247 
3248 void
Lang_DeleteWidget(interp,info)3249 Lang_DeleteWidget(interp, info)
3250 Tcl_Interp *interp;
3251 Tcl_Command info;
3252 {
3253  dTHX;
3254  Tk_Window tkwin = info->tkwin;
3255  char *cmdName = Tk_PathName(tkwin);
3256  SV *win = WidgetRef(interp, cmdName);
3257  /* This is first sign of disapearing widget, <Destroy> bindings
3258     are still to come.
3259   */
3260  LangMethodCall(interp,win,"_Destroyed",0,0);
3261  Tcl_DeleteCommandFromToken(interp,info);
3262  if (win && SvOK(win))
3263   {
3264    HV *hash = NULL;
3265    Lang_CmdInfo *info = WindowCommand(win,&hash,1);
3266    if (info->interp != interp)
3267     Tcl_Panic("%s->interp=%p expected %p", cmdName, info->interp, interp);
3268    if (hash)
3269     hv_delete(hash, XEVENT_KEY, strlen(XEVENT_KEY), G_DISCARD);
3270    /* Tk_CheckHash((SV *) hash, NULL); */
3271    if (SvREFCNT(hash) < 2)
3272     {
3273      LangDebug("%s %s has REFCNT=%d",__FUNCTION__,cmdName,(int) SvREFCNT(hash));
3274     }
3275    SvREFCNT_dec(hash);
3276   }
3277 }
3278 
3279 void
Lang_DeleteObject(interp,info)3280 Lang_DeleteObject(interp, info)
3281 Tcl_Interp *interp;
3282 Tcl_Command info;
3283 {
3284  dTHX;
3285  STRLEN na;
3286  char *cmdName = SvPV(info->image,na);
3287  if (info->interp != interp)
3288   Tcl_Panic("%s->interp=%p expected %p", cmdName, info->interp, interp);
3289  Tcl_DeleteCommandFromToken(interp, info);
3290  DecInterp(info->interp,cmdName);
3291 }
3292 
3293 void
Lang_NewMainWindow(interp,tkwin)3294 Lang_NewMainWindow(interp,tkwin)
3295 Tcl_Interp *interp;
3296 Tk_Window tkwin;
3297 {
3298  dTHX;
3299  tilde_magic((SV *) InterpHv(interp,1),newSViv(PTR2IV(tkwin)));
3300 }
3301 
3302 Tcl_Command
Lang_CreateWidget(interp,tkwin,proc,clientData,deleteProc)3303 Lang_CreateWidget(interp, tkwin, proc, clientData, deleteProc)
3304 Tcl_Interp *interp;
3305 Tk_Window tkwin;
3306 Tcl_ObjCmdProc *proc;
3307 ClientData clientData;
3308 Tcl_CmdDeleteProc *deleteProc;
3309 {
3310  dTHX;
3311  STRLEN na;
3312  HV *hv = InterpHv(interp,1);
3313  char *cmdName = (tkwin) ? Tk_PathName(tkwin) : ".";
3314  STRLEN cmdLen = strlen(cmdName);
3315  HV *hash = newHV();
3316  SV *tmp;
3317  Lang_CmdInfo info;
3318  SV *sv;
3319  do_watch();
3320  memset(&info,0,sizeof(info));
3321  info.Tk.objProc = proc;
3322  info.Tk.deleteProc = deleteProc;
3323  info.Tk.objClientData = info.Tk.deleteData = clientData;
3324  info.interp = interp;
3325  info.tkwin = tkwin;
3326  info.image = NULL;
3327  sv = struct_sv(&info,sizeof(info));
3328 
3329  /* Record the object in the main hash */
3330  IncInterp(interp, cmdName);
3331 
3332  hv_store(hv, cmdName, cmdLen, newRV((SV *) hash), 0);
3333  /* At this point hash REFCNT should be 2, one for what is stored
3334     in interp and one representing Tk's use
3335   */
3336  tilde_magic((SV *) hash, sv);
3337  return (Lang_CmdInfo *) SvPV(sv,na);
3338 }
3339 
3340 Tcl_Command
Lang_CreateObject(interp,cmdName,proc,clientData,deleteProc)3341 Lang_CreateObject(interp, cmdName, proc, clientData, deleteProc)
3342 Tcl_Interp *interp;
3343 char *cmdName;
3344 Tcl_ObjCmdProc *proc;
3345 ClientData clientData;
3346 Tcl_CmdDeleteProc *deleteProc;
3347 {
3348  dTHX;
3349  STRLEN na;
3350  HV *hv = InterpHv(interp,1);
3351  STRLEN cmdLen = strlen(cmdName);
3352  HV *hash = newHV();
3353  SV *sv;
3354  Lang_CmdInfo info;
3355  do_watch();
3356  memset(&info,0,sizeof(info));
3357  info.Tk.objProc = proc;
3358  info.Tk.deleteProc = deleteProc;
3359  info.Tk.objClientData = info.Tk.deleteData = clientData;
3360  info.interp = interp;
3361  info.tkwin = NULL;
3362  info.image = newSVpv(cmdName,cmdLen);
3363  sv =  struct_sv(&info,sizeof(info));
3364  /* Record the object in the main hash */
3365  IncInterp(interp, cmdName);
3366  hv_store(hv, cmdName, cmdLen, MakeReference((SV *) hash), 0);
3367  tilde_magic((SV *)hash, sv);
3368  return (Lang_CmdInfo *) SvPV(sv,na);
3369 }
3370 
3371 Tcl_Command
Lang_CreateImage(interp,cmdName,proc,clientData,deleteProc,typePtr)3372 Lang_CreateImage(interp, cmdName, proc, clientData, deleteProc, typePtr)
3373 Tcl_Interp *interp;
3374 char *cmdName;
3375 Tcl_ObjCmdProc *proc;
3376 ClientData clientData;
3377 Tcl_CmdDeleteProc *deleteProc;
3378 Tk_ImageType *typePtr;
3379 {
3380  return Lang_CreateObject(interp, cmdName, proc, clientData, deleteProc);
3381 }
3382 
3383 Tcl_Command
Tcl_CreateObjCommand(interp,cmdName,proc,clientData,deleteProc)3384 Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc)
3385 Tcl_Interp *interp;
3386 CONST char *cmdName;
3387 Tcl_ObjCmdProc *proc;
3388 ClientData clientData;
3389 Tcl_CmdDeleteProc *deleteProc;
3390 {
3391  Tk_Window mw = Tk_MainWindow(interp);
3392  if (cmdName[0] == '.')
3393   {
3394    Tk_Window tkwin;
3395    if (cmdName[1] == '\0')
3396     {
3397      tkwin = mw;
3398     }
3399    else
3400     {
3401      tkwin = Tk_NameToWindow(interp, (char *) cmdName, mw);
3402     }
3403    return Lang_CreateWidget(interp, tkwin, proc, clientData, deleteProc);
3404   }
3405  else
3406   {
3407    Tcl_CmdInfo info;
3408    CV *cv;
3409    char *kind = "NULL";
3410    if (clientData)
3411     {
3412      kind = (clientData == (ClientData) mw) ? "mw" : "custom";
3413     }
3414    memset(&info,0,sizeof(info));
3415    info.objProc = proc;
3416    info.objClientData = clientData;
3417    info.deleteProc = deleteProc;
3418    if (!strcmp(cmdName,"menu"))
3419     {
3420      cmdName = "_menu";
3421     }
3422    /* We cannot test sanity of clientData vs XStoXxxxx at this point
3423       as when 1st called XSs are still pointing a B::C friendly re-directors.
3424       Also CVs for "loaded" commands may not exist yet.
3425     */
3426 #if 0
3427    if ((cv = TkXSUB(cmdName,NULL,NULL)))
3428     {
3429      if (clientData)
3430       {
3431        if (clientData == (ClientData) mw)
3432         {
3433          if (CvXSUB(cv) == XStoTclCmdNull)
3434           {
3435            warn("Wrong xsub %s cd=%p (mw)",cmdName, clientData);
3436            CvXSUB(cv) = XStoTclCmd;
3437           }
3438         }
3439        else
3440         {
3441          if (CvXSUB(cv) == XStoTclCmd)
3442           {
3443            warn("Wrong xsub %s cd=%p",cmdName, clientData);
3444            CvXSUB(cv) = XStoTclCmdNull;
3445          }
3446         }
3447       }
3448      else
3449       {
3450        if (CvXSUB(cv) == XStoTclCmd)
3451         {
3452          warn("Wrong xsub %s cd=%p",cmdName, clientData);
3453          CvXSUB(cv) = XStoTclCmdNull;
3454         }
3455       }
3456     }
3457    else
3458     {
3459      warn("No cv for %s",cmdName);
3460     }
3461 #endif
3462    Tcl_SetCommandInfo(interp,cmdName,&info);
3463    if (deleteProc)
3464     {
3465      HV *hv = InterpHv(interp,1);
3466      Tcl_CallWhenDeleted(interp,(Tcl_InterpDeleteProc *)deleteProc,clientData);
3467     }
3468   }
3469  return NULL;
3470 }
3471 
3472 int
Tcl_IsSafe(interp)3473 Tcl_IsSafe(interp)
3474 Tcl_Interp *interp;
3475 {
3476  return 0; /* Is this interp in a 'safe' compartment - not yet implemented */
3477 }
3478 
3479 int
Tcl_HideCommand(Tcl_Interp * interp,CONST char * cmdName,CONST char * hiddenCmdName)3480 Tcl_HideCommand (Tcl_Interp *interp, CONST char *cmdName, CONST char *hiddenCmdName)
3481 {
3482  CV *cv = TkXSUB(cmdName,NULL,NULL);
3483  warn("Tcl_HideCommand %s => %s called",cmdName,hiddenCmdName);
3484  if (!cv)
3485   {
3486    return EXPIRE((interp,"Cannot find %s", cmdName));
3487   }
3488  return TCL_OK;
3489 }
3490 
3491 int
Tcl_SetCommandInfo(interp,cmdName,infoPtr)3492 Tcl_SetCommandInfo(interp,cmdName,infoPtr)
3493 Tcl_Interp *interp;
3494 CONST char *cmdName;
3495 CONST Tcl_CmdInfo *infoPtr;
3496 {
3497  dTHX;
3498  HV *cm = FindHv(aTHX_ interp, "Tcl_SetCommandInfo", 1, CMD_KEY);
3499  hv_store(cm,cmdName,strlen(cmdName),
3500           struct_sv((char *) infoPtr,sizeof(*infoPtr)),0);
3501  return TCL_OK;
3502 }
3503 
3504 int
Tcl_GetCommandInfo(Tcl_Interp * interp,CONST char * cmdName,Tcl_CmdInfo * infoPtr)3505 Tcl_GetCommandInfo (Tcl_Interp *interp, CONST char *cmdName, Tcl_CmdInfo *infoPtr)
3506 {
3507  dTHX;
3508  HV *hv = InterpHv(interp,1);
3509  SV **svp = hv_fetch(hv,cmdName,strlen(cmdName),0);
3510  /* Widgets, images and named fonts get put in main hash */
3511  if (svp && *svp)
3512   {
3513    Lang_CmdInfo *info = WindowCommand(*svp,NULL,0);
3514    *infoPtr = info->Tk;
3515    return 1;
3516   }
3517  /* widgets are special cased elsewhere */
3518  else if (*cmdName != '.')
3519   {
3520    HV *cm = FindHv(aTHX_ interp, "Tcl_GetCommandInfo", 1, CMD_KEY);
3521    SV **svp = hv_fetch(cm,cmdName,strlen(cmdName),0);
3522    if (svp && *svp)
3523     {
3524      memcpy(infoPtr,SvPVX(*svp),sizeof(Tcl_CmdInfo));
3525      return 1;
3526     }
3527    else if (0)
3528     {
3529      /* If we didn't find the info but this is supposed to
3530         be a known Tk builtin then something may have gone wrong
3531         but "after" seems to occur regularly
3532       */
3533      CV *cv = TkXSUB(cmdName,NULL,NULL);
3534      if (cv)
3535       {
3536        LangDebug("No Tcl_GetCommandInfo for %s\n",cmdName);
3537       }
3538     }
3539   }
3540  return 0;
3541 }
3542 
3543 Tcl_Command
Tcl_CreateCommand(interp,cmdName,proc,clientData,deleteProc)3544 Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc)
3545 Tcl_Interp *interp;
3546 CONST char *cmdName;
3547 Tcl_CmdProc *proc;
3548 ClientData clientData;
3549 Tcl_CmdDeleteProc *deleteProc;
3550 {
3551  return Tcl_CreateObjCommand(interp, cmdName, (Tcl_ObjCmdProc *) proc, clientData, deleteProc);
3552 }
3553 
3554 static SV *LangVar2 _((Tcl_Interp *interp, SV *sv, char *part2, int flags));
3555 
3556 static SV *
LangVar2(interp,sv,part2,store)3557 LangVar2(interp, sv, part2, store)
3558 Tcl_Interp *interp;
3559 SV *sv;
3560 char *part2;
3561 int store;
3562 {
3563  if (part2)
3564   {
3565    dTHX;
3566    if (SvTYPE(sv) == SVt_PVHV)
3567     {HV *hv = (HV *) sv;
3568      SV **x = hv_fetch(hv, part2, strlen(part2), store);
3569      if (x)
3570       return *x;
3571     }
3572    else
3573     {
3574      Tcl_Panic("two part %s not implemented", "Tcl_GetVar2");
3575     }
3576    return NULL;
3577   }
3578  else
3579   return sv;
3580 }
3581 
3582 Tcl_Obj *
Tcl_ObjGetVar2(interp,sv,part2,flags)3583 Tcl_ObjGetVar2(interp, sv, part2, flags)
3584 Tcl_Interp *interp;
3585 SV *sv;
3586 SV *part2;
3587 int flags;
3588 {
3589  dTHX;
3590  if (sv)
3591   {
3592    if (SvPOK(sv))
3593     {
3594      STRLEN len;
3595      char *s = SvPV(sv,len);
3596      if (len > 6 && !strncmp(s,"::tk::",6))
3597       {
3598        sv = FindTkVarName(s+6,0);
3599       }
3600     }
3601    if (SvROK(sv) && SvTYPE(SvRV(sv)) != SVt_PVAV)
3602     {
3603      sv = SvRV(sv);
3604     }
3605    if (part2)
3606     {
3607      sv = LangVar2(interp, sv, Tcl_GetString(part2), 0);
3608     }
3609   }
3610  else
3611   {
3612    sv = newSV(0);
3613   }
3614  return sv;
3615 }
3616 
3617 Tcl_Obj *
Tcl_ObjSetVar2(interp,sv,part2,newValue,flags)3618 Tcl_ObjSetVar2(interp, sv, part2, newValue, flags)
3619 Tcl_Interp *interp;
3620 SV *sv;
3621 SV *part2;
3622 SV *newValue;
3623 int flags;
3624 {
3625  dTHX;
3626  if (SvROK(sv))
3627   sv = SvRV(sv);
3628  if (part2)
3629   sv = LangVar2(interp, sv , Tcl_GetString(part2), 1);
3630  SvSetMagicSV(sv, newValue);
3631  return sv;
3632 }
3633 
3634 
3635 
3636 char *
Tcl_SetVarArg(interp,sv,newValue,flags)3637 Tcl_SetVarArg(interp, sv, newValue, flags)
3638 Tcl_Interp *interp;
3639 SV *sv;
3640 Tcl_Obj * newValue;
3641 int flags;
3642 {
3643  dTHX;
3644  STRLEN na;
3645  if (!newValue)
3646   newValue = &PL_sv_undef;
3647  SvSetMagicSV(sv, newValue);
3648  return SvPV(sv, na);
3649 }
3650 
3651 int
LangCmpOpt(opt,arg,len)3652 LangCmpOpt(opt,arg,len)
3653 CONST char *opt;
3654 CONST char *arg;
3655 size_t len;
3656 {
3657  int result = 0;
3658  if (!len)
3659   len = strlen(arg);
3660  if (*opt == '-')
3661   opt++;
3662  if (*arg == '-')
3663   {
3664    arg++;
3665    if (len)
3666     len--;
3667   }
3668  while (len--)
3669   {char ch = *arg++;;
3670    if ((result = *opt++ - ch) || !ch)
3671     break;
3672   }
3673  return result;
3674 }
3675 
3676 int
LangCmpArg(a,b)3677 LangCmpArg(a,b)
3678 CONST SV *a;
3679 CONST SV *b;
3680 {
3681  dTHX;
3682  STRLEN na;
3683  char *as;
3684  char *bs;
3685  if (a && SvGMAGICAL(a))
3686   mg_get((SV *) a);
3687  if (b && SvGMAGICAL(b))
3688   mg_get((SV *) b);
3689  as = (a && SvOK(a)) ? SvPV((SV *)a,na) : "";
3690  bs = (b && SvOK(b)) ? SvPV((SV *)b,na) : "";
3691  return strcmp(as,bs);
3692 }
3693 
3694 static I32
Perl_Value(pTHX_ IV ix,SV * sv)3695 Perl_Value(pTHX_ IV ix, SV *sv)
3696 {
3697  Tk_TraceInfo *p = INT2PTR(Tk_TraceInfo *, ix);
3698  char *result;
3699 
3700  /* We are a "magic" set processor, whether we like it or not
3701     because this is the hook we use to get called.
3702     So we are (I think) supposed to look at "private" flags
3703     and set the public ones if appropriate.
3704     e.g. "chop" sets SvPOKp as a hint but not SvPOK
3705 
3706     presumably other operators set other private bits.
3707 
3708     Question are successive "magics" called in correct order?
3709 
3710     i.e. if we are tracing a tied variable should we call
3711     some magic list or be careful how we insert ourselves in the list?
3712 
3713   */
3714   /* This seems also to be wrong in some cases --- see t/sv.t and
3715      RT #121528
3716   */
3717 #if 0
3718  if (!SvPOK(sv) && SvPOKp(sv))
3719   SvPOK_on(sv);
3720 
3721  if (!SvNOK(sv) && SvNOKp(sv))
3722   SvNOK_on(sv);
3723 
3724  if (!SvIOK(sv) && SvIOKp(sv))
3725   SvIOK_on(sv);
3726 #endif
3727  return 0;
3728 }
3729 
3730 static void
TraceExitHandler(ClientData clientData)3731 TraceExitHandler(ClientData clientData)
3732 {
3733  dTHX;
3734  Tk_TraceInfo *p = (Tk_TraceInfo *) clientData;
3735  char *result;
3736  ENTER;
3737  SvREFCNT_inc(p->sv);
3738  save_freesv(p->sv);
3739  result = (*p->proc) (p->clientData, p->interp, p->sv, p->part2, 0);
3740  if (result)
3741   Tcl_Panic("Tcl_VarTraceProc returned '%s'", result);
3742  LEAVE;
3743 }
3744 
3745 
DECL_MG_UFUNC(Perl_Trace,ix,sv)3746 static DECL_MG_UFUNC(Perl_Trace, ix, sv)
3747 {
3748  Tk_TraceInfo *p = INT2PTR(Tk_TraceInfo *, ix);
3749  char *result;
3750 
3751  /* We are a "magic" set processor, whether we like it or not
3752     because this is the hook we use to get called.
3753     So we are (I think) supposed to look at "private" flags
3754     and set the public ones if appropriate.
3755     e.g. "chop" sets SvPOKp as a hint but not SvPOK
3756 
3757     presumably other operators set other private bits.
3758 
3759     Question are successive "magics" called in correct order?
3760 
3761     i.e. if we are tracing a tied variable should we call
3762     some magic list or be careful how we insert ourselves in the list?
3763 
3764   */
3765 
3766  /* This seems to be wrong in at least one case --- see t/Trace.t and
3767     Message-ID: <3ef348b.0304240510.299e5519@posting.google.com>
3768  */
3769 #if 0
3770  if (!SvPOK(sv) && SvPOKp(sv))
3771   SvPOK_on(sv);
3772 
3773  if (!SvNOK(sv) && SvNOKp(sv))
3774   SvNOK_on(sv);
3775 
3776  if (!SvIOK(sv) && SvIOKp(sv))
3777   SvIOK_on(sv);
3778 #endif
3779 
3780  ENTER;
3781  SvREFCNT_inc(sv);
3782  save_freesv(sv);
3783  result = (*p->proc) (p->clientData, p->interp, sv, p->part2, 0);
3784  if (result)
3785   Tcl_Panic("Tcl_VarTraceProc returned '%s'", result);
3786  LEAVE;
3787  return 0;
3788 }
3789 
3790 int
Lang_TraceVar2(interp,sv,part2,flags,tkproc,clientData)3791 Lang_TraceVar2(interp, sv, part2, flags, tkproc, clientData)
3792 Tcl_Interp *interp;
3793 Tcl_Obj * sv;
3794 char *part2;
3795 int flags;
3796 Lang_VarTraceProc *tkproc;
3797 ClientData clientData;
3798 {
3799  dTHX;
3800  Tk_TraceInfo *p;
3801  struct ufuncs *ufp;
3802  MAGIC **mgp;
3803  MAGIC *mg;
3804  MAGIC *mg_list;
3805  SV *exiting;
3806  int mgType = PERL_MAGIC_uvar;
3807 
3808  if (SvROK(sv))
3809   sv = SvRV(sv);
3810 
3811  if (SvTHINKFIRST(sv))
3812   {
3813    if (SvREADONLY(sv))
3814     {
3815      return EXPIRE((interp, "Cannot trace readonly variable"));
3816     }
3817   }
3818  (void)SvUPGRADE(sv, SVt_PVMG);
3819 
3820  if (SvTYPE(sv) == SVt_PVAV)
3821   {
3822    mgType = PERL_MAGIC_ext;
3823   }
3824 
3825  /*
3826   * We can't use sv_magic() because it won't add in another magical struct
3827   * of type 'U' if there is already one there. We need multiple 'U'
3828   * magics hanging from one sv or else things like radiobuttons will
3829   * not work. That's because each radiobutton widget group needs to track
3830   * the same sv and update itself as necessary.
3831   */
3832 
3833  New(601, p, 1, Tk_TraceInfo);
3834 
3835  p->proc = tkproc;
3836  p->clientData = clientData;
3837  p->interp = interp;
3838  p->part2 = part2;
3839  p->sv    = sv;
3840 
3841  Tcl_CreateExitHandler(TraceExitHandler, (ClientData) p);
3842 
3843  /* We want to be last in the chain so that any
3844     other magic has been called first
3845     save the list so that this magic can be moved to the end
3846   */
3847  mg_list = SvMAGIC(sv);
3848  SvMAGIC(sv) = NULL;
3849 
3850  /* Add 'U' magic to sv with all NULL args */
3851  sv_magic(sv, 0, mgType, 0, 0);
3852 
3853  Newz(666, ufp, 1, struct ufuncs);
3854  ufp->uf_val = Perl_Value;
3855  ufp->uf_set = Perl_Trace;
3856  ufp->uf_index = PTR2IV(p);
3857 
3858  mg = SvMAGIC(sv);
3859  mg->mg_ptr = (char *) ufp;
3860  mg->mg_len = sizeof(struct ufuncs);
3861 
3862 
3863  /* put list back and add mg to end */
3864 
3865  SvMAGIC(sv) = mg_list;
3866  mgp = &SvMAGIC(sv);
3867  while ((mg_list = *mgp))
3868   {
3869    mgp = &mg_list->mg_moremagic;
3870   }
3871  *mgp = mg;
3872 
3873  if (mgType == PERL_MAGIC_ext)
3874   {
3875    /* We are not doing a real tie to an AV so
3876       we need to set the vtable and re-calc magic flags
3877     */
3878    mg->mg_virtual = &PL_vtbl_uvar;
3879    mg_magical(sv);
3880   }
3881 
3882  if (!SvMAGICAL(sv))
3883   abort();
3884 
3885  return TCL_OK;
3886 }
3887 
3888 SV *
FindTkVarName(varName,flags)3889 FindTkVarName(varName,flags)
3890 CONST char *varName;
3891 int flags;
3892 {
3893  dTHX;
3894  STRLEN na;
3895  SV *name = newSVpv(BASEEXT,strlen(BASEEXT));
3896  SV *sv;
3897  sv_catpv(name,"::");
3898  if (!strncmp(varName,"tk_",3))
3899   varName += 3;
3900  sv_catpv(name,varName);
3901  sv = get_sv(SvPV(name,na),flags);
3902  SvREFCNT_dec(name);
3903  return sv;
3904 }
3905 
3906 char *
LangLibraryDir()3907 LangLibraryDir()
3908 {
3909  dTHX;
3910  STRLEN na;
3911  SV *sv = FindTkVarName("library",0);
3912  if (sv && SvPOK(sv))
3913   return SvPV(sv,na);
3914  return NULL;
3915 }
3916 
3917 static
DECL_MG_UFUNC(LinkIntSet,ix,sv)3918 DECL_MG_UFUNC(LinkIntSet,ix,sv)
3919 {
3920  int *p = INT2PTR(int *, ix);
3921  (*p) = SvIV(sv);
3922  return 0;
3923 }
3924 
3925 static
DECL_MG_UFUNC(LinkDoubleSet,ix,sv)3926 DECL_MG_UFUNC(LinkDoubleSet,ix,sv)
3927 {
3928  double *p = INT2PTR(double *, ix);
3929  (*p) = SvNV(sv);
3930  return 0;
3931 }
3932 
3933 static
DECL_MG_UFUNC(LinkCannotSet,ix,sv)3934 DECL_MG_UFUNC(LinkCannotSet,ix,sv)
3935 {
3936  croak("Attempt to set readonly linked variable");
3937  return 0;
3938 }
3939 
3940 static
DECL_MG_UFUNC(LinkIntVal,ix,sv)3941 DECL_MG_UFUNC(LinkIntVal,ix,sv)
3942 {
3943  int *p = INT2PTR(int *, ix);
3944  sv_setiv(sv,*p);
3945  return 0;
3946 }
3947 
3948 static
DECL_MG_UFUNC(LinkDoubleVal,ix,sv)3949 DECL_MG_UFUNC(LinkDoubleVal,ix,sv)
3950 {
3951  double *p = INT2PTR(double *, ix);
3952  sv_setnv(sv,*p);
3953  return 0;
3954 }
3955 
3956 int
Tcl_LinkVar(interp,varName,addr,type)3957 Tcl_LinkVar(interp,varName,addr,type)
3958 Tcl_Interp *interp;
3959 CONST char *varName;
3960 char *addr;
3961 int type;
3962 {
3963  dTHX;
3964  SV *sv = FindTkVarName(varName,0);
3965  if (sv)
3966   {
3967    struct ufuncs uf;
3968    uf.uf_index = PTR2IV(addr);
3969    switch(type & ~TCL_LINK_READ_ONLY)
3970     {
3971      case TCL_LINK_INT:
3972      case TCL_LINK_BOOLEAN:
3973       uf.uf_val   = LinkIntVal;
3974       uf.uf_set   = LinkIntSet;
3975       *((int *) addr) = SvIV(sv);
3976       break;
3977      case TCL_LINK_DOUBLE:
3978       uf.uf_val   = LinkDoubleVal;
3979       uf.uf_set   = LinkDoubleSet;
3980       *((double *) addr) = SvNV(sv);
3981       break;
3982      case TCL_LINK_STRING:
3983      default:
3984       return EXPIRE((interp,"Cannot link %s type %d\n",varName,type));
3985     }
3986    if (type & TCL_LINK_READ_ONLY)
3987     {
3988      uf.uf_set   = LinkCannotSet;
3989     }
3990    sv_magic(sv,NULL, PERL_MAGIC_uvar, (char *) (&uf), sizeof(uf));
3991    return TCL_OK;
3992   }
3993  else
3994   {
3995    return EXPIRE((interp,"No variable %s\n",varName));
3996   }
3997 }
3998 
3999 void
Tcl_UnlinkVar(interp,varName)4000 Tcl_UnlinkVar(interp,varName)
4001 Tcl_Interp *interp;
4002 CONST char *varName;
4003 {
4004  dTHX;
4005  SV *sv = FindTkVarName(varName,0);
4006  if (sv)
4007   {
4008    sv_unmagic(sv,PERL_MAGIC_uvar);
4009   }
4010 }
4011 
4012 void
Lang_UntraceVar(interp,sv,flags,tkproc,clientData)4013 Lang_UntraceVar(interp, sv, flags, tkproc, clientData)
4014 Tcl_Interp *interp;
4015 SV *sv;
4016 int flags;
4017 Lang_VarTraceProc *tkproc;
4018 ClientData clientData;
4019 {
4020  int mgType = PERL_MAGIC_uvar;
4021  MAGIC **mgp;
4022  /* it may not be magical i.e. it may never have been traced
4023     This occurs for example when cascade Menu gets untraced
4024     by same code that untraces checkbutton menu items.
4025     If it is not magical just ignore it.
4026   */
4027 
4028  if (SvROK(sv))
4029   sv = SvRV(sv);
4030 
4031  if (SvTYPE(sv) == SVt_PVAV)
4032   {
4033    mgType = PERL_MAGIC_ext;
4034   }
4035 
4036  if (SvMAGICAL(sv) && (mgp = &SvMAGIC(sv)))
4037   {
4038    MAGIC *mg;
4039    for (mg = *mgp; mg; mg = *mgp)
4040     {
4041      /*
4042       * Trawl through the linked list of magic looking
4043       * for the 'U' one which is our proc and ix.
4044       */
4045      if (mg->mg_type == mgType && mg->mg_ptr &&
4046          mg->mg_len  == sizeof(struct ufuncs) &&
4047          ((struct ufuncs *) (mg->mg_ptr))->uf_set == Perl_Trace)
4048       {
4049        struct ufuncs *uf = (struct ufuncs *) (mg->mg_ptr);
4050        Tk_TraceInfo *p = INT2PTR(Tk_TraceInfo *, uf->uf_index);
4051        if (p && p->proc == tkproc && p->interp == interp &&
4052            p->clientData == clientData)
4053         {
4054          *mgp = mg->mg_moremagic;
4055          Tcl_DeleteExitHandler(TraceExitHandler, (ClientData) p);
4056          Safefree(p);
4057          uf->uf_index = 0;
4058          Safefree(mg->mg_ptr);
4059          mg->mg_ptr = NULL;
4060          Safefree(mg);
4061         }
4062        else
4063         mgp = &mg->mg_moremagic;
4064       }
4065      else
4066       mgp = &mg->mg_moremagic;
4067     }
4068    if (!SvMAGIC(sv))
4069     {
4070      SvMAGICAL_off(sv);
4071      if ((SvFLAGS(sv) & (SVp_IOK|SVp_NOK)) == (SVp_IOK|SVp_NOK))
4072       {
4073        /* RT #90077: if both SVp_IOK and SVp_NOK are set, then the
4074 	* SVf_IOK must not be set, otherwise arithmetic operations
4075 	* may use the wrong integer value
4076 	*/
4077        SvFLAGS(sv) |= (SvFLAGS(sv) & (SVp_NOK|SVp_POK)) >> PRIVSHIFT;
4078       }
4079      else
4080       {
4081        SvFLAGS(sv) |= (SvFLAGS(sv) & (SVp_IOK|SVp_NOK|SVp_POK)) >> PRIVSHIFT;
4082       }
4083     }
4084   }
4085 }
4086 
4087 int
Lang_TraceVar(interp,varName,flags,proc,clientData)4088 Lang_TraceVar(interp, varName, flags, proc, clientData)
4089 Tcl_Interp *interp;
4090 Var varName;
4091 int flags;
4092 Lang_VarTraceProc *proc;
4093 ClientData clientData;
4094 {
4095  return Lang_TraceVar2(interp, varName, NULL, flags, proc, clientData);
4096 }
4097 
4098 Tcl_Obj *
LangFindVar(interp,tkwin,name)4099 LangFindVar(interp, tkwin, name)
4100 Tcl_Interp *interp;
4101 Tk_Window tkwin;
4102 CONST char *name;
4103 {
4104  dTHX;
4105  if (tkwin)
4106   {
4107    SV *sv = TkToWidget(tkwin,NULL);
4108    if (name == Tk_Name(tkwin))
4109     name = "Value";
4110    if (sv && SvROK(sv))
4111     {
4112      HV *hv = (HV *) SvRV(sv);
4113      STRLEN l = strlen(name);
4114      SV **x = hv_fetch(hv, name, l, 1);
4115      if (!x)
4116       x = hv_store(hv, name, l, newSVpv("", 0), 0);
4117      if (x)
4118       return SvREFCNT_inc(*x);
4119     }
4120   }
4121  else
4122   {
4123    SV *sv = FindTkVarName(name,1);
4124    if (sv)
4125     return SvREFCNT_inc(sv);
4126   }
4127  return newSVpv("", 0);
4128 }
4129 
4130 int
LangStringMatch(string,match)4131 LangStringMatch(string, match)
4132 char *string;
4133 SV *match;
4134 {
4135  dTHX;
4136  STRLEN na;
4137  /* match could be a callback to perl sub to do re match */
4138  return Tcl_StringMatch(string, SvPV(match, na));
4139 }
4140 
4141 int
LangSaveVar(interp,sv,vp,type)4142 LangSaveVar(interp,sv,vp,type)
4143 Tcl_Interp *interp;
4144 Tcl_Obj * sv;
4145 Var *vp;
4146 int type;
4147 {
4148  dTHX;
4149  STRLEN na;
4150  int old_taint = PL_tainted;
4151  TAINT_NOT;
4152  *vp = NULL;
4153  if (!sv)
4154   {
4155    return TCL_OK;
4156   }
4157  if (SvGMAGICAL(sv))
4158   mg_get(sv);
4159  if (SvROK(sv))
4160   {
4161    sv = SvRV(sv);
4162    if (sv == &PL_sv_undef)
4163     warn("variable is 'undef'");
4164    switch(type)
4165     {
4166      case TK_CONFIG_HASHVAR:
4167       if (SvTYPE(sv) != SVt_PVHV)
4168        EXPIRE((interp,"%s is not a hash",SvPV(sv,na)));
4169       break;
4170      case TK_CONFIG_ARRAYVAR:
4171       if (SvTYPE(sv) != SVt_PVAV)
4172        EXPIRE((interp,"%s is not an array",SvPV(sv,na)));
4173       break;
4174      default:
4175      case TK_CONFIG_SCALARVAR:
4176       break;
4177     }
4178    *vp = SvREFCNT_inc(sv);
4179    PL_tainted = old_taint;
4180    return TCL_OK;
4181   }
4182  else if (SvPOK(sv))
4183   {
4184    dTHX;
4185    HV *old_stash = CopSTASH(PL_curcop);
4186    char *name;
4187    SV *x = NULL;
4188    int prefix = '?';
4189    name = SvPV(sv,na);
4190 #ifdef CAN_COPSTASH_SET_NULL
4191    CopSTASH_set(PL_curcop, NULL);
4192 #else
4193 #  ifdef USE_ITHREADS
4194    CopSTASHPV(PL_curcop) = NULL;
4195 #  else
4196    CopSTASH(PL_curcop) = NULL;
4197 #  endif
4198 #endif
4199    switch (type)
4200     {
4201      case TK_CONFIG_SCALARVAR:
4202       prefix = '$';
4203      default:
4204       if (!strchr(name,':'))
4205        {
4206         x = FindTkVarName(name,1);
4207        }
4208       else
4209        {
4210         x = perl_get_sv(name,1);
4211        }
4212       break;
4213      case TK_CONFIG_ARRAYVAR:
4214       x = (SV *) perl_get_av(name,TRUE);
4215       prefix = '@';
4216       break;
4217      case TK_CONFIG_HASHVAR:
4218       x = (SV *) perl_get_hv(name,TRUE);
4219       prefix = '%';
4220       break;
4221     }
4222    CopSTASH_set(PL_curcop, old_stash);
4223    if (x)
4224     {
4225      *vp = SvREFCNT_inc(x);
4226      PL_tainted = old_taint;
4227      return TCL_OK;
4228     }
4229    else
4230     Tcl_SprintfResult(interp,"%c%s does not exist",prefix,name);
4231   }
4232  else
4233   {
4234    Tcl_SprintfResult(interp,"Not a reference %s",SvPV(sv,na));
4235   }
4236  PL_tainted = old_taint;
4237  return TCL_ERROR;
4238 }
4239 
4240 void
LangFreeVar(sv)4241 LangFreeVar(sv)
4242 Var sv;
4243 {
4244  dTHX;
4245  SvREFCNT_dec(sv);
4246 }
4247 
4248 int
LangConfigObj(Tcl_Interp * interp,Tcl_Obj ** save,Tcl_Obj * obj,int type)4249 LangConfigObj(Tcl_Interp *interp, Tcl_Obj **save, Tcl_Obj *obj, int type)
4250 {
4251  dTHX;
4252  *save = Nullsv;
4253  switch (type)
4254   {
4255    case TK_OPTION_OBJ:
4256      if (obj)
4257       *save = LangCopyArg(obj);
4258      return TCL_OK;
4259    case TK_OPTION_CALLBACK:
4260      if (obj)
4261       *save = LangMakeCallback(obj);
4262      return TCL_OK;
4263    case TK_OPTION_SCALARVAR:
4264      return LangSaveVar(interp,obj,save,TK_CONFIG_SCALARVAR);
4265    case TK_OPTION_ARRAYVAR:
4266      return LangSaveVar(interp,obj,save,TK_CONFIG_ARRAYVAR);
4267    case TK_OPTION_HASHVAR:
4268      return LangSaveVar(interp,obj,save,TK_CONFIG_HASHVAR);
4269    default:
4270      Tcl_SprintfResult(interp,"Unexpected type %d for LangConfigObj(%"SVf")",
4271                        type,obj);
4272   }
4273  return TCL_ERROR;
4274 }
4275 
4276 int
Lang_CallWithArgs(interp,sub,argc,argv)4277 Lang_CallWithArgs(interp, sub, argc, argv)
4278 Tcl_Interp *interp;
4279 char *sub;
4280 int argc;
4281 SV *CONST *argv;
4282 {
4283  dTHX;
4284  dSP;
4285  STRLEN len;
4286  int count;
4287  SV *sv = newSVpv("",0);
4288  if (!strncmp(sub,"tk",2))
4289   {
4290    sv_catpv(sv,"Tk::");
4291    sub += 2;
4292   }
4293  sv_catpv(sv,sub);
4294  sub = SvPV(sv,len);
4295  ENTER;
4296  SAVETMPS;
4297  EXTEND(sp, argc);
4298  PUSHMARK(sp);
4299  while (argc-- > 0)
4300   {
4301    XPUSHs(*argv++);
4302   }
4303  PUTBACK;
4304  count = perl_call_pv(sub, G_EVAL|G_SCALAR);
4305  SetTclResult(interp,count);
4306  SvREFCNT_dec(sv);
4307  FREETMPS;
4308  LEAVE;
4309  return Check_Eval(interp);
4310 }
4311 
4312 int
LangMethodCall(Tcl_Interp * interp,Tcl_Obj * sv,char * method,int result,int argc,...)4313 LangMethodCall
4314 #ifdef STANDARD_C
4315 _((Tcl_Interp * interp, Tcl_Obj * sv, char *method, int result, int argc,...))
4316 #else
4317 (interp, sv, method, result, argc, va_alist)
4318 Tcl_Interp *interp;
4319 SV *sv;
4320 char *method;
4321 int result;
4322 int argc;
4323 va_dcl
4324 #endif
4325 {
4326  dTHX;
4327  dSP;
4328  int flags = (result) ? 0 : G_DISCARD;
4329  int count = 0;
4330  int old_taint = PL_tainted;
4331  ENTER;
4332  SAVETMPS;
4333  PUSHMARK(sp);
4334  XPUSHs(sv_mortalcopy(sv));
4335  PUTBACK;
4336  if (argc)
4337   {
4338    va_list ap;
4339 #ifdef I_STDARG
4340    va_start(ap, argc);
4341 #else
4342    va_start(ap);
4343 #endif
4344    PushVarArgs(ap,argc);
4345    va_end(ap);
4346   }
4347  PL_tainted = 0;
4348  sv = sv_newmortal();
4349  sv_setpv(sv,method);
4350  PL_tainted = old_taint;
4351  count = LangCallCallback(sv, flags | G_EVAL);
4352  if (result)
4353   SetTclResult(interp,count);
4354  FREETMPS;
4355  LEAVE;
4356  return Check_Eval(interp);
4357 }
4358 
4359 int
Tcl_EvalObjEx(Tcl_Interp * interp,Tcl_Obj * objPtr,int flags)4360 Tcl_EvalObjEx (Tcl_Interp *interp,Tcl_Obj *objPtr, int flags)
4361 {
4362  dTHX;
4363  int code;
4364  SV *cb = LangMakeCallback(objPtr);
4365  SV *sv = cb;
4366  SvREFCNT_inc(interp);
4367  ENTER;
4368  SAVETMPS;
4369  if (PushCallbackArgs(interp,&sv) == TCL_OK)
4370   {
4371    int count = LangCallCallback(sv, G_SCALAR | G_EVAL);
4372    SetTclResult(interp,count);
4373   }
4374  FREETMPS;
4375  LEAVE;
4376  SvREFCNT_dec(cb);
4377  code = Check_Eval(interp);
4378  SvREFCNT_dec(interp);
4379  return code;
4380 }
4381 
4382 int
Tcl_EvalObj(Tcl_Interp * interp,Tcl_Obj * objPtr)4383 Tcl_EvalObj(Tcl_Interp *interp,Tcl_Obj *objPtr)
4384 {
4385  return Tcl_EvalObjEx(interp,objPtr,0);
4386 }
4387 
4388 /*
4389  * Tcl_EvalObjv is used by tkMenu.c's CloneMenu
4390  * In order to allow Tk::Menu::tkMenuDup to return
4391  * the "object" for the created menu we pass actual
4392  * objects not mortal copies.
4393  * We also avoid the overhead of creating, blessing and destroying
4394  * "Callback" object.
4395  */
4396 
4397 int
Tcl_EvalObjv(Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[],int flags)4398 Tcl_EvalObjv(Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[], int flags)
4399 {
4400  SV *sv = objv[0];
4401  int i;
4402  dTHX;
4403  dSP;
4404  ENTER;
4405  SAVETMPS;
4406  PUSHMARK(sp);
4407  for (i=1; i < objc; i++)
4408   {
4409    XPUSHs(objv[i]);
4410   }
4411  PUTBACK;
4412  i = LangCallCallback(sv, G_SCALAR | G_EVAL);
4413  SetTclResult(interp,i);
4414  FREETMPS;
4415  LEAVE;
4416  return Check_Eval(interp);
4417 }
4418 
4419 int
Tcl_GlobalEval(Tcl_Interp * interp,CONST char * command)4420 Tcl_GlobalEval(Tcl_Interp *interp, CONST char *command)
4421 {
4422  dTHX;
4423  if (!PL_tainting)
4424   {
4425    warn("Receive from Tk's 'send' ignored (no taint checking)\n");
4426    return EXPIRE((interp,"send to non-secure perl/Tk application rejected\n"));
4427   }
4428  else
4429   {
4430    dSP;
4431    int count = 0;
4432    int old_taint = PL_tainted;
4433    SV *sv;
4434    PL_tainted = 0;
4435    ENTER;
4436    SAVETMPS;
4437    PUSHMARK(sp);
4438    Set_widget(sv = WidgetRef(interp,"."));
4439    XPUSHs(sv_mortalcopy(sv));
4440    PL_tainted = 1;
4441    sv = newSVpv(command,strlen(command));
4442    SvTAINT(sv);
4443    PL_tainted = 0;
4444    XPUSHs(sv_2mortal(sv));
4445    PUTBACK;
4446    Tcl_ResetResult(interp);
4447    Lang_ClearErrorInfo(interp);
4448    sv = sv_2mortal(newSVpv("Receive",0));
4449    PL_tainted = old_taint;
4450    count = LangCallCallback(sv, G_ARRAY | G_EVAL);
4451    SetTclResult(interp,count);
4452    FREETMPS;
4453    LEAVE;
4454    return Check_Eval(interp);
4455   }
4456 }
4457 
XS(XS_Tk__Widget_BindClientMessage)4458 XS(XS_Tk__Widget_BindClientMessage)
4459 {
4460  dXSARGS;
4461  if (items >= 1)
4462   {
4463    HV *hv = NULL;
4464    Lang_CmdInfo *info = WindowCommand(ST(0), &hv, 2);
4465    if (info)
4466     {
4467      HV *cm = FindHv(aTHX_ hv, "BindClientMessage", (items > 2), CM_KEY);
4468      if (items >= 2)
4469       {
4470        STRLEN len;
4471        char *key = SvPV(ST(1),len);
4472        if (items > 2)
4473         {
4474          SV *cb = LangMakeCallback(ST(2));
4475          hv_store(cm, key, len, cb ,0);
4476         }
4477        else
4478         {
4479          if (cm)
4480           {
4481            SV **x = hv_fetch(cm, key, len, 0);
4482            if (x)
4483             ST(0) = sv_mortalcopy(*x);
4484           }
4485         }
4486       }
4487      else
4488       {
4489        if (cm)
4490         ST(0) = sv_2mortal(newRV((SV *) cm));
4491       }
4492     }
4493   }
4494  else
4495   croak("Usage: $w->BindClientMessage(atom,callback)");
4496  XSRETURN(1);
4497 }
4498 
4499 #ifdef WIN32
4500 int
Lang_WinEvent(tkwin,message,wParam,lParam,resultPtr)4501 Lang_WinEvent(tkwin, message, wParam, lParam, resultPtr)
4502     Tk_Window tkwin;
4503     UINT message;
4504     WPARAM wParam;
4505     LPARAM lParam;
4506     LRESULT *resultPtr;
4507 {
4508  dTHX;
4509  Tcl_Interp *interp = NULL;
4510  SV *w = TkToWidget(tkwin,&interp);
4511  char key[32];
4512  HV *cm = NULL;
4513  STRLEN na;
4514  int code = 0;
4515   if ( !interp || !w || !SvROK(w))
4516   {
4517     return 0;
4518   }
4519  sprintf(key,"%d",message);
4520  if (SvROK(w))
4521   cm = FindHv(aTHX_ (HV *) SvRV(w),"Lang_WinMessage",0,CM_KEY);
4522  if (cm)
4523   {
4524    SV **x = hv_fetch(cm,key,strlen(key),0);
4525    SV *sv;
4526    if (!x)
4527     x = hv_fetch(cm,"0",1,0);
4528    if (x && (sv = *x))
4529     {
4530      dSP;
4531      SV *data = struct_sv(NULL, sizeof(EventAndKeySym));
4532      EventAndKeySym *info = (EventAndKeySym *) SvPVX(data);
4533      int result;
4534 #if 0
4535      LangDebug("%s %d '%s'\n",Tk_PathName(tkwin), message,SvPV(sv,na));
4536 #endif
4537      info->keySym = 0;
4538      info->interp = interp;
4539      info->window = w;
4540      info->tkwin  = tkwin;
4541      ENTER;
4542      SAVETMPS;
4543      Tcl_ResetResult(interp);
4544      Lang_ClearErrorInfo(interp);
4545      Set_widget(w);
4546      result = PushObjCallbackArgs(interp,&sv,info);
4547      SPAGAIN;
4548      if (result == TCL_OK)
4549       {
4550        XPUSHs(sv_2mortal(newSViv(message)));
4551        XPUSHs(sv_2mortal(newSViv(wParam)));
4552        XPUSHs(sv_2mortal(newSViv(lParam)));
4553        PUTBACK;
4554        result = LangCallCallback(sv, G_DISCARD | G_EVAL);
4555        if (result)
4556         {
4557          SPAGAIN;
4558          sv = POPs;
4559          PUTBACK;
4560          if (SvIOK(sv))
4561           {
4562            *resultPtr = SvIV(sv);
4563            code = 1;
4564           }
4565         }
4566       }
4567      Lang_MaybeError(interp,Check_Eval(interp),"ClientMessage handler");
4568      FREETMPS;
4569      LEAVE;
4570     }
4571   }
4572  return code;
4573 }
4574 #endif /* WIN32 */
4575 
4576 void
LangClientMessage(interp,tkwin,event)4577 LangClientMessage(interp, tkwin, event)
4578 Tcl_Interp *interp;
4579 Tk_Window tkwin;
4580 XEvent *event;
4581 {
4582  dTHX;
4583  SV *w = TkToWidget(tkwin,NULL);
4584  CONST char *key;
4585  HV *cm = NULL;
4586  if (!SvROK(w))
4587   {
4588    Tk_Window mainwin = (Tk_Window)((((TkWindow*)tkwin)->mainPtr)->winPtr);
4589    w = TkToWidget(mainwin,NULL);
4590   }
4591  key = Tk_GetAtomName(tkwin, event->xclient.message_type);
4592  if (SvROK(w))
4593   cm = FindHv(aTHX_ (HV *) SvRV(w),"LangClientMessage",0,CM_KEY);
4594  if (cm)
4595   {
4596    SV **x = hv_fetch(cm,key,strlen(key),0);
4597    SV *sv;
4598    if (!x)
4599     x = hv_fetch(cm,"any",3,0);
4600    if (x && (sv = *x))
4601     {
4602      dSP;
4603      SV *data = struct_sv(NULL, sizeof(EventAndKeySym));
4604      EventAndKeySym *info = (EventAndKeySym *) SvPVX(data);
4605      SV *e  = Blessed("XEvent", MakeReference(data));
4606      int result;
4607      info->event  = *event;
4608      info->keySym = 0;
4609      info->interp = interp;
4610      info->window = w;
4611      info->tkwin  = tkwin;
4612      ENTER;
4613      SAVETMPS;
4614      Tcl_ResetResult(interp);
4615      Lang_ClearErrorInfo(interp);
4616      Set_widget(w);
4617      Set_event(e);
4618      if (SvROK(w))
4619       {
4620        HV *hash = (HV *) SvRV(w);
4621        hv_store(hash, XEVENT_KEY, strlen(XEVENT_KEY), e, 0);
4622       }
4623      else
4624       Decrement(e,"Unused Event");
4625      result = PushObjCallbackArgs(interp,&sv,info);
4626      if (result == TCL_OK)
4627       LangCallCallback(sv, G_DISCARD | G_EVAL);
4628      Lang_MaybeError(interp,Check_Eval(interp),"ClientMessage handler");
4629      if (0 && SvROK(w))
4630       {
4631        HV *hash = (HV *) SvRV(w);
4632        hv_delete(hash, XEVENT_KEY, strlen(XEVENT_KEY), G_DISCARD);
4633       }
4634      FREETMPS;
4635      LEAVE;
4636     }
4637 #if 0
4638    else
4639     {
4640      warn("%s has no handler for '%s'\n",Tk_PathName(tkwin),key);
4641     }
4642 #endif
4643   }
4644 #if 0
4645  else
4646   {
4647    warn("ClientMessage '%s' for %s\n", key, Tk_PathName(tkwin));
4648   }
4649 #endif
4650 }
4651 
4652 int
LangEventCallback(cdata,interp,event,tkwin,keySym)4653 LangEventCallback(cdata, interp, event, tkwin, keySym)
4654 ClientData cdata;
4655 Tcl_Interp *interp;
4656 Tk_Window tkwin;
4657 XEvent *event;
4658 KeySym keySym;
4659 {
4660  dTHX;
4661  SV *sv = (SV *) cdata;
4662  int result = TCL_ERROR;
4663  Tk_Window ewin = Tk_EventWindow(event);
4664 #ifdef LEAK_CHECKING
4665  hash_ptr *save = NULL;
4666  long hwm = note_used(&save);
4667  fprintf(stderr, "Event Entry count=%ld hwm=%ld\n", ec = sv_count, hwm);
4668 #endif
4669  Tcl_ResetResult(interp);
4670  Lang_ClearErrorInfo(interp);
4671  if (!SvOK(sv))
4672   {
4673    Tcl_SetResult(interp,"Call of undefined callback",TCL_STATIC);
4674    return TCL_ERROR;
4675   }
4676  if (ewin && tkwin)
4677   {
4678    dSP;
4679    int code;
4680    SV *data = struct_sv(NULL, sizeof(EventAndKeySym));
4681    EventAndKeySym *info = (EventAndKeySym *) SvPVX(data);
4682    SV *e = Blessed("XEvent", MakeReference(data));
4683    SV *w = TkToWidget(tkwin,NULL);
4684 #ifdef DEBUG_GLUE
4685    fprintf(stderr, "%s:%s(%s) = %p\n", "LangEventCallback", SvPV_nolen(sv), Tk_PathName(tkwin), info);
4686 #endif
4687    info->event = *event;
4688    info->keySym = keySym;
4689    info->interp = interp;
4690    info->window = w;
4691    info->tkwin  = tkwin;
4692    ENTER;
4693    PUSHSTACKi(PERLSI_MAGIC);
4694    SAVETMPS;
4695    PUTBACK;
4696    Tcl_ResetResult(interp);
4697    Lang_ClearErrorInfo(interp);
4698    Set_widget(w);
4699    Set_event(e);
4700    result = PushObjCallbackArgs(interp,&sv,info);
4701    if (SvROK(w))
4702     {
4703      HV *hash = (HV *) SvRV(w);
4704      hv_store(hash, XEVENT_KEY, strlen(XEVENT_KEY), e, 0);
4705     }
4706    else
4707     Decrement(e,"Unused Event");
4708    if (result == TCL_OK)
4709     {
4710      LangCallCallback(sv, G_DISCARD | G_EVAL);
4711      FREETMPS;
4712      result = Check_Eval(interp);
4713     }
4714    if (0 && SvROK(w))
4715     {
4716      HV *hash = (HV *) SvRV(w);
4717      hv_delete(hash, XEVENT_KEY, strlen(XEVENT_KEY), G_DISCARD);
4718     }
4719    POPSTACK;
4720    LEAVE;
4721   }
4722  else
4723   {
4724    /*
4725     * Event pertains to a window which has been/is being deleted.
4726     * Although we may be able to call perl code we cannot make
4727     * any method calls because the widget hash object has probably vanished.
4728     *
4729     * Quietly return "OK" having done nothing
4730     */
4731    result = TCL_OK;
4732   }
4733 #ifdef LEAK_CHECKING
4734  fprintf(stderr, "sv_count was %ld, now %ld (%ld)\n", ec, sv_count, sv_count - ec);
4735  check_used(&save);
4736 #endif
4737  return result;
4738 }
4739 
4740 void
LangFreeArg(sv,freeProc)4741 LangFreeArg(sv, freeProc)
4742 Tcl_Obj * sv;
4743 Tcl_FreeProc *freeProc;
4744 {
4745  dTHX;
4746  Decrement(sv, "LangFreeArg");
4747 }
4748 
4749 static int
handle_generic(clientData,eventPtr)4750 handle_generic(clientData, eventPtr)
4751 ClientData clientData;
4752 XEvent *eventPtr;
4753 {
4754  int code        = 0;
4755  Tk_Window tkwin = Tk_EventWindow(eventPtr);
4756  if (tkwin)
4757   {
4758    GenericInfo *p = (GenericInfo *) clientData;
4759    Tcl_Interp *interp = p->interp;
4760    SV *sv = p->cb;
4761    dTHX;
4762    dSP;
4763    SV *data = struct_sv(NULL, sizeof(EventAndKeySym));
4764    EventAndKeySym *info = (EventAndKeySym *) SvPVX(data);
4765    SV *e = Blessed("XEvent", MakeReference(data));
4766    SV *w = NULL;
4767    int count = 0;
4768    int result;
4769    info->event = *eventPtr;
4770    info->keySym = None;
4771    info->interp = interp;
4772    info->tkwin  = tkwin;
4773    do_watch();
4774    Tcl_ResetResult(interp);
4775    Lang_ClearErrorInfo(interp);
4776    ENTER;
4777    SAVETMPS;
4778    if (tkwin)
4779     w = TkToWidget(tkwin,&info->interp);  /* Pending REFCNT */
4780    if (!SvROK(w))
4781     w = Blessed("Window", MakeReference(newSViv((IV) (eventPtr->xany.window))));
4782    else
4783     Set_widget(w);
4784    result = PushObjCallbackArgs(interp, &sv,info);
4785    if (result == TCL_OK)
4786     {
4787      SPAGAIN;
4788      Set_event(e);
4789      XPUSHs(sv_mortalcopy(e));
4790      XPUSHs(sv_mortalcopy(w));
4791      PUTBACK;
4792      count = LangCallCallback(sv, G_EVAL);
4793      result = Check_Eval(interp);
4794     }
4795    if (count)
4796     {
4797      SPAGAIN;
4798      code = TOPi;
4799      sp -= count;
4800      PUTBACK;
4801     }
4802    else
4803     code = 0;
4804    Lang_MaybeError(interp,result,"Generic Event");
4805 
4806    FREETMPS;
4807    LEAVE;
4808   }
4809  return code;
4810 }
4811 
4812 static void
Perl_GeomRequest(clientData,tkwin)4813 Perl_GeomRequest(clientData,tkwin)
4814 ClientData clientData;
4815 Tk_Window tkwin;
4816 {
4817  Lang_CmdInfo *info = (Lang_CmdInfo *) clientData;
4818  SV *master = TkToWidget(info->tkwin,NULL);
4819  SV *slave  = TkToWidget(tkwin,NULL);
4820  dTHX;
4821  dSP;
4822  ENTER;
4823  SAVETMPS;
4824  Set_widget(master);
4825  PUSHMARK(sp);
4826  XPUSHs(sv_mortalcopy(master));
4827  XPUSHs(sv_mortalcopy(slave));
4828  PUTBACK;
4829  LangCallCallback(sv_2mortal(newSVpv("SlaveGeometryRequest",0)),G_DISCARD);
4830  FREETMPS;
4831  LEAVE;
4832 }
4833 
4834 static void
Perl_GeomLostSlave(clientData,tkwin)4835 Perl_GeomLostSlave(clientData,tkwin)
4836 ClientData clientData;
4837 Tk_Window tkwin;
4838 {
4839  Lang_CmdInfo *info = (Lang_CmdInfo *) clientData;
4840  SV *master = TkToWidget(info->tkwin,NULL);
4841  SV *slave  = TkToWidget(tkwin,NULL);
4842  dTHX;
4843  dSP;
4844  ENTER;
4845  SAVETMPS;
4846  PUSHMARK(sp);
4847  Set_widget(master);
4848  XPUSHs(sv_mortalcopy(master));
4849  XPUSHs(sv_mortalcopy(slave));
4850  PUTBACK;
4851  LangCallCallback(sv_2mortal(newSVpv("LostSlave",0)),G_DISCARD);
4852  FREETMPS;
4853  LEAVE;
4854 }
4855 
XS(XS_Tk__Widget_ManageGeometry)4856 XS(XS_Tk__Widget_ManageGeometry)
4857 {
4858  dXSARGS;
4859  STRLEN na;
4860  if (items == 2)
4861   {
4862    HV *hash = NULL;
4863    Lang_CmdInfo *info   = WindowCommand(ST(0), &hash, 0);
4864    if (info && info->tkwin)
4865     {
4866      Lang_CmdInfo *slave  = WindowCommand(ST(1), NULL, 0);
4867      if (slave && slave->tkwin)
4868       {
4869        SV **x = hv_fetch(hash,GEOMETRY_KEY,strlen(GEOMETRY_KEY),0);
4870        SV *mgr_sv = NULL;
4871        if (!x)
4872         {
4873          Tk_GeomMgr mgr;
4874          mgr.name          = Tk_PathName(info->tkwin);
4875          mgr.requestProc   = Perl_GeomRequest;
4876          mgr.lostSlaveProc = Perl_GeomLostSlave;
4877          mgr_sv = struct_sv((char *) &mgr,sizeof(mgr));
4878          hv_store(hash,GEOMETRY_KEY,strlen(GEOMETRY_KEY),mgr_sv, 0);
4879         }
4880        else
4881         mgr_sv = *x;
4882        Tk_ManageGeometry(slave->tkwin, (Tk_GeomMgr *) SvPV(mgr_sv,na), (ClientData) info);
4883       }
4884      else
4885       croak("Not a (slave) widget %s",SvPV(ST(1),na));
4886     }
4887    else
4888     croak("Not a (master) widget %s",SvPV(ST(0),na));
4889   }
4890  else
4891   croak("usage $master->ManageGeometry($slave)");
4892  XSRETURN(1);
4893 }
4894 
4895 static void
handle_idle(clientData)4896 handle_idle(clientData)
4897 ClientData clientData;
4898 {
4899  dTHX;
4900  GenericInfo *p = (GenericInfo *) clientData;
4901  SV *sv = p->cb;
4902  dSP;
4903  int count = 0;
4904  int code = 0;
4905  ENTER;
4906  SAVETMPS;
4907  Tcl_ResetResult(p->interp);
4908  Lang_ClearErrorInfo(p->interp);
4909  Set_widget(WidgetRef(p->interp,"."));
4910  code = PushCallbackArgs(p->interp,&sv);
4911  if (code == TCL_OK)
4912   {
4913    LangCallCallback(sv, G_DISCARD | G_EVAL);
4914    code = Check_Eval(p->interp);
4915   }
4916  Lang_MaybeError(p->interp,code,"Idle Callback");
4917  FREETMPS;
4918  LEAVE;
4919  LangFreeCallback(p->cb);
4920  DecInterp(p->interp, "handle_idle");
4921  ckfree((char *) p);
4922 }
4923 
4924 
XS(XS_Tk_DoWhenIdle)4925 XS(XS_Tk_DoWhenIdle)
4926 {
4927  dXSARGS;
4928  STRLEN na;
4929  if (items == 2)
4930   {
4931    Lang_CmdInfo *info = WindowCommand(ST(0), NULL, 0);
4932    if (info && info->interp && (info->tkwin || info->image))
4933     {
4934      /* Try to get result to prove things are "still alive" */
4935      if (Tcl_GetObjResult(info->interp))
4936       {
4937        GenericInfo *p = (GenericInfo *) ckalloc(sizeof(GenericInfo));
4938        IncInterp(info->interp,"Tk_DoWhenIdle");
4939        p->interp = info->interp;
4940        p->cb = LangMakeCallback(ST(1));
4941        Tcl_DoWhenIdle(handle_idle, (ClientData) p);
4942       }
4943     }
4944    else
4945     croak("Not a widget %s",SvPV(ST(0),na));
4946   }
4947  else
4948   croak("Usage $w->DoWhenIdle(callback)");
4949  XSRETURN(1);
4950 }
4951 
XS(XS_Tk_CreateGenericHandler)4952 XS(XS_Tk_CreateGenericHandler)
4953 {
4954  dXSARGS;
4955  STRLEN na;
4956  if (items == 2)
4957   {
4958    Lang_CmdInfo *info = WindowCommand(ST(0), NULL, 0);
4959    if (info && info->interp && (info->tkwin || info->image))
4960     {
4961      if (Tcl_GetObjResult(info->interp))
4962       {
4963        GenericInfo *p = (GenericInfo *) ckalloc(sizeof(GenericInfo));
4964        IncInterp(info->interp,"Tk_CreateGenericHandler");
4965        p->interp = info->interp;
4966        p->cb = LangMakeCallback(ST(1));
4967        Tk_CreateGenericHandler(handle_generic, (ClientData) p);
4968       }
4969     }
4970    else
4971     croak("Not a widget %s",SvPV(ST(0),na));
4972   }
4973  else
4974   croak("Usage $w->CreateGenericHandler(callback)");
4975  XSRETURN(1);
4976 }
4977 
4978 
4979 SV *
XEvent_Info(obj,s)4980 XEvent_Info(obj,s)
4981 EventAndKeySym *obj;
4982 char *s;
4983 {
4984  dTHX;
4985  SV *eventSv = sv_newmortal();
4986  I32 ix = (I32) *s;
4987  char scratch[256];
4988  if (obj)
4989   {
4990    if (ix == '@' || strncmp(s,"xy",2) == 0)
4991     {
4992      char result[80];
4993      strcpy(result, "@");
4994      strcat(result, Tk_EventInfo('x', obj->tkwin, &obj->event, obj->keySym, NULL, NULL, NULL, sizeof(scratch) - 1, scratch));
4995      strcat(result, ",");
4996      strcat(result, Tk_EventInfo('y', obj->tkwin, &obj->event, obj->keySym, NULL, NULL, NULL, sizeof(scratch) - 1, scratch));
4997      sv_setpv(eventSv, result);
4998     }
4999    else
5000     {
5001      int isNum = 0;
5002      int number = 0;
5003      int type = TK_EVENTTYPE_NONE;
5004      char *result = Tk_EventInfo(ix, obj->tkwin, &obj->event, obj->keySym, &number, &isNum, &type, sizeof(scratch) - 1, scratch);
5005      switch (type)
5006       {
5007        case TK_EVENTTYPE_WINDOW:
5008         {
5009          SV *w = &PL_sv_undef;
5010          if (result && result[0] == '.')
5011           w = WidgetRef(obj->interp, result);
5012          if (SvROK(w))
5013           SvSetMagicSV(eventSv, w);
5014          else
5015           {
5016            if (number)
5017             sv_setref_iv(eventSv, "Window", number);
5018           }
5019         }
5020         break;
5021 
5022        case TK_EVENTTYPE_DISPLAY:
5023         sv_setref_pv(eventSv, "DisplayPtr", (void *) number);
5024         break;
5025 
5026        case TK_EVENTTYPE_DATA:
5027         sv_setpvn(eventSv, result, (unsigned) number);
5028         break;
5029 
5030        default:
5031         if (result) {
5032          sv_setpv(eventSv, result);
5033 	}
5034         if (isNum)
5035          {
5036           sv_setiv(eventSv, number);
5037           if (result)
5038            SvPOK_on(eventSv);
5039          }
5040         break;
5041       }
5042     }
5043   }
5044  return sv_maybe_utf8(eventSv);
5045 }
5046 
5047 EventAndKeySym *
SVtoEventAndKeySym(SV * arg)5048 SVtoEventAndKeySym(SV *arg)
5049 {
5050  dTHX;
5051  SV *sv;
5052  if (sv_isobject(arg) && (sv = SvRV(arg)) &&
5053      SvPOK(sv) && SvCUR(sv) == sizeof(EventAndKeySym))
5054   {
5055    return (EventAndKeySym *) SvPVX(sv);
5056   }
5057  else
5058   croak("obj is not an XEvent");
5059  return NULL;
5060 }
5061 
XS(XS_Tk__Widget_PassEvent)5062 XS(XS_Tk__Widget_PassEvent)
5063 {
5064  dXSARGS;
5065  Tk_Window tkwin = NULL;
5066  EventAndKeySym *obj = NULL;
5067  if (items == 2
5068      && (tkwin = (Tk_Window) SVtoWindow(ST(0)))
5069      && (obj = SVtoEventAndKeySym(ST(1)))
5070     )
5071   {
5072    if (Tk_WindowId(tkwin) == None)
5073     Tk_MakeWindowExist(tkwin);
5074    TkBindEventProc((TkWindow *)tkwin, &obj->event);
5075   }
5076  else
5077   croak("Usage: $widget->PassEvent($event)");
5078  ST(0) = &PL_sv_undef;
5079  XSRETURN(1);
5080 }
5081 
5082 
5083 void
Tk_ChangeScreen(interp,dispName,screenIndex)5084 Tk_ChangeScreen(interp, dispName, screenIndex)
5085 Tcl_Interp *interp;
5086 char *dispName;
5087 int screenIndex;
5088 {
5089 
5090 }
5091 
5092 
5093 /* These are for file name handling which needs further abstraction */
5094 
5095 char *
Tcl_TranslateFileName(interp,name,bufferPtr)5096 Tcl_TranslateFileName(interp, name, bufferPtr)
5097 Tcl_Interp *interp;
5098 CONST char *name;
5099 Tcl_DString *bufferPtr;
5100 {
5101  dTHX;
5102  dSP;
5103  IV count;
5104  ENTER;
5105  SAVETMPS;
5106  PUSHMARK(sp);
5107  XPUSHs(sv_2mortal(newSVpv((char *) name,0)));
5108  PUTBACK;
5109  perl_call_pv("Tk::TranslateFileName",G_EVAL|G_SCALAR);
5110  SPAGAIN;
5111  *bufferPtr = POPs;
5112  PUTBACK;
5113  SvREFCNT_inc(*bufferPtr);
5114  FREETMPS;
5115  LEAVE;
5116  return Tcl_DStringValue(bufferPtr);
5117 }
5118 
5119 CONST char *
Tcl_PosixError(interp)5120 Tcl_PosixError(interp)
5121 Tcl_Interp *interp;
5122 {
5123  dTHX;
5124  return Strerror(errno);
5125 }
5126 
5127 #ifdef STANDARD_C
5128 void
EnterWidgetMethods(char * package,...)5129 EnterWidgetMethods(char *package,...)
5130 #else
5131 /*VARARGS0 */
5132 void
5133 EnterWidgetMethods(package, va_alist)
5134 char *package;
5135 va_dcl
5136 #endif
5137 {
5138  dTHX;
5139  va_list ap;
5140  char buf[80];
5141  char *method;
5142 #ifdef I_STDARG
5143  va_start(ap, package);
5144 #else
5145  va_start(ap);
5146 #endif
5147  while ((method = va_arg(ap, char *)))
5148   {
5149    CV *cv;
5150    if (strcmp(method, "configure") && strcmp(method, "cget"))
5151     {
5152      sprintf(buf, "Tk::%s::%s", package, method);
5153      cv = newXS(buf, XStoWidget, __FILE__);
5154      CvXSUBANY(cv).any_ptr = newSVpv(method, 0);
5155     }
5156   }
5157 }
5158 
5159 void
Lang_SetErrorCode(interp,code)5160 Lang_SetErrorCode(interp, code)
5161 Tcl_Interp *interp;
5162 char *code;
5163 {
5164 
5165 }
5166 
5167 void
Tcl_SetObjErrorCode(Tcl_Interp * interp,Tcl_Obj * errorObjPtr)5168 Tcl_SetObjErrorCode (Tcl_Interp * interp,Tcl_Obj * errorObjPtr)
5169 {
5170 
5171 }
5172 
5173 char *
Lang_GetErrorCode(interp)5174 Lang_GetErrorCode(interp)
5175 Tcl_Interp *interp;
5176 {
5177  warn("Lang_GetErrorCode not implemented");
5178  return "";
5179 }
5180 
5181 char *
Lang_GetErrorInfo(interp)5182 Lang_GetErrorInfo(interp)
5183 Tcl_Interp *interp;
5184 {
5185  warn("Lang_GetErrorInfo not implemented");
5186  return "";
5187 }
5188 
5189 void
LangBadFile(fd)5190 LangBadFile(fd)
5191 int fd;
5192 {
5193  warn("File (%d) closed without deleting handler",fd);
5194 }
5195 
5196 int
LangEventHook(flags)5197 LangEventHook(flags)
5198 int flags;
5199 /* Used by Tcl_Async stuff for signal handling */
5200 {
5201 #if 0
5202 #if defined(WNOHANG) && (defined(HAS_WAITPID) || defined(HAS_WAIT4))
5203  int status = -1;
5204  I32 pid = wait4pid(-1,&status,WNOHANG);
5205  if (pid > 0)
5206   {
5207    pidgone(pid, status);
5208    warn("Child process %d status=%d",pid,status);
5209    return 1;
5210   }
5211 #endif
5212 #endif
5213  return 0;
5214 }
5215 
5216 /* Tcl caches compiled regexps so does not free them */
5217 
5218 struct WrappedRegExp
5219 {
5220 #if HAS_PMOP_EXTRA_FLAGS
5221  PMOP   op;
5222 #else
5223  U32 flags;
5224 #endif
5225 #if USE_REGEXP_511
5226  REGEXP *pat;
5227 #else
5228  regexp *pat;
5229 #endif
5230  SV    *source;
5231 };
5232 
5233 void
Lang_FreeRegExp(re)5234 Lang_FreeRegExp(re)
5235 Tcl_RegExp re;
5236 {
5237  dTHX;
5238  if (re->pat)
5239   ReREFCNT_dec(re->pat);
5240  if (re->source)
5241   SvREFCNT_dec(re->source);
5242  Safefree(re);
5243 }
5244 
5245 /* An "XS" routine to call with G_EVAL set */
5246 static void
do_comp(pTHX_ CV * cv)5247 do_comp(pTHX_ CV *cv)
5248 {
5249  dMARK;
5250  dAX;
5251  struct WrappedRegExp *p = (struct WrappedRegExp *) CvXSUBANY(cv).any_ptr;
5252 #if USE_PREGCOMP_31027
5253  p->pat = pregcomp(p->source,p->flags);
5254 #else /* USE_PREGCOMP_31027 */
5255  int len = 0;
5256  char *string = Tcl_GetStringFromObj(p->source,&len);
5257 
5258 #if HAS_PMOP_EXTRA_FLAGS
5259  p->op.op_pmdynflags |= PMdf_DYN_UTF8;
5260  p->pat = pregcomp(string,string+len,&p->op);
5261 #else
5262  p->pat = pregcomp(string,string+len,p->flags);
5263 #endif
5264 #if 0
5265  LangDebug("/%.*s/ => %p\n",len,string,p->pat);
5266 #endif
5267 #endif /* USE_PREGCOMP_31027 */
5268  XSRETURN(0);
5269 }
5270 
5271 I32
Lang_catch(pTHX_ XSUBADDR_t subaddr,void * any,I32 flags,char * filename)5272 Lang_catch(pTHX_ XSUBADDR_t subaddr, void *any, I32 flags,char *filename)
5273 {
5274  dSP;
5275  CV *cv = (CV *) sv_newmortal();
5276  int count;
5277  SV **oldSP = sp;
5278  sv_upgrade((SV *)cv, SVt_PVCV);
5279  CvFILE(cv) = filename;
5280  CvXSUB(cv) = subaddr;
5281  CvXSUBANY(cv).any_ptr = any;
5282 #ifdef CvISXSUB_on
5283  CvISXSUB_on(cv); /* this is needed for perl5.9@27244 */
5284 #endif
5285  count = call_sv((SV *)cv,flags|G_EVAL);
5286  SPAGAIN;
5287  if (sp != oldSP)
5288   {
5289    LangDebug("Stack moved %p => %p\n",oldSP,sp);
5290   }
5291  return count;
5292 }
5293 
5294 Tcl_RegExp
Tcl_GetRegExpFromObj(Tcl_Interp * interp,Tcl_Obj * obj,int flags)5295 Tcl_GetRegExpFromObj(Tcl_Interp *interp, Tcl_Obj *obj, int flags)
5296 {
5297  dTHX;
5298  Tcl_RegExp re;
5299  MAGIC *mg = Null(MAGIC*);
5300 
5301  Newz('R', re, 1, struct WrappedRegExp);
5302 
5303  re->source = Tcl_DuplicateObj(obj);
5304 
5305  /* If source is a reference and thing refrered to has right
5306     magic we can use regexp from the qr//
5307   */
5308  if (SvROK(re->source)) {
5309 	SV *sv = SvRV(re->source);
5310 	if(SvMAGICAL(sv))
5311 	    mg = mg_find(sv, PERL_MAGIC_qr);
5312  }
5313 
5314 #if HAS_PMOP_EXTRA_FLAGS
5315  /* Could do more conversions here
5316     Not sure how/if to override case-ness of qr// pattern
5317   */
5318  if (flags & TCL_REG_NOCASE) {
5319    re->op.op_pmflags |= PMf_FOLD;
5320  }
5321 #else
5322 #if USE_REGEXP_511
5323  re->flags =            (flags & TCL_REG_NOCASE ? RXf_PMf_FOLD : 0);
5324 #else
5325  re->flags = RXf_UTF8 | (flags & TCL_REG_NOCASE ? RXf_PMf_FOLD : 0);
5326 #endif
5327 #endif
5328 
5329  if (mg)
5330   {
5331 #if USE_REGEXP_511
5332    re->pat = (REGEXP *)mg->mg_obj;
5333 #else
5334    re->pat = (regexp *)mg->mg_obj;
5335 #endif
5336    /* Guess wildly ... */
5337    ReREFCNT_inc(re->pat);
5338   }
5339  else
5340   {
5341    dSP;
5342    SV *err;
5343    ENTER;
5344    SAVETMPS;
5345    PUSHMARK(sp);
5346    Lang_catch(aTHX_ do_comp, re, G_VOID, __FILE__);
5347    FREETMPS;
5348    LEAVE;
5349    err = ERRSV;
5350    if (SvTRUE(err))
5351     {
5352      Lang_FreeRegExp(re);
5353      Tcl_SetResult(interp,SvPV_nolen(err),TCL_VOLATILE);
5354      return NULL;
5355     }
5356   }
5357  return re;
5358 }
5359 
5360 int
Tcl_RegExpExec(interp,re,cstring,cstart)5361 Tcl_RegExpExec(interp, re, cstring, cstart)
5362 Tcl_Interp *interp;
5363 Tcl_RegExp re;
5364 CONST char *cstring;
5365 CONST char *cstart;
5366 {
5367  dTHX;
5368  SV *tmp = sv_newmortal();
5369  int code;
5370  sv_upgrade(tmp,SVt_PV);
5371  SvCUR_set(tmp,strlen(cstring));
5372  SvPVX(tmp) = (char *) cstring;
5373  SvLEN(tmp) = 0;
5374  SvREADONLY_on(tmp);
5375  SvPOK_on(tmp);
5376  /* From Tk all strings are UTF-8 */
5377  SvUTF8_on(tmp);
5378 #ifdef ROPT_MATCH_UTF8
5379  RX_MATCH_UTF8_on(re->pat);
5380 #else
5381  /* eeek what do we do now ... */
5382 #endif
5383  code = pregexec(re->pat,SvPVX(tmp),SvEND(tmp),(char *) cstart,0,
5384                  tmp,REXEC_COPY_STR);
5385 #if 0
5386  LangDebug("%d '%.*s'\n",code,SvCUR(tmp),SvPVX(tmp));
5387  sv_dump(tmp);
5388  regdump(re->pat);
5389 #endif
5390  return code;
5391 }
5392 
5393 void
Tcl_RegExpRange(wrap,index,startPtr,endPtr)5394 Tcl_RegExpRange(wrap, index, startPtr, endPtr)
5395 Tcl_RegExp wrap;
5396 int index;
5397 CONST84 char **startPtr;
5398 CONST84 char **endPtr;
5399 {
5400 #if USE_REGEXP_511
5401  REGEXP *rx = wrap->pat;
5402  regexp *const re = (struct regexp *)SvANY(rx);
5403 #else
5404  regexp *re = wrap->pat;
5405 #endif
5406 #if USE_NEWSTYLE_REGEXP_STRUCT
5407  if (re->offs[index].start != -1 && re->offs[index].end != -1)
5408   {
5409    *startPtr = re->subbeg+re->offs[index].start;
5410    *endPtr   = re->subbeg+re->offs[index].end;
5411   }
5412 #else
5413  if (re->startp[index] != -1 && re->endp[index] != -1)
5414   {
5415    *startPtr = re->subbeg+re->startp[index];
5416    *endPtr   = re->subbeg+re->endp[index];
5417   }
5418 #endif
5419  else
5420   {
5421    *startPtr = NULL;
5422    *endPtr   = NULL;
5423   }
5424 }
5425 
5426 void
Lang_BuildInImages()5427 Lang_BuildInImages()
5428 {
5429 #if 0
5430 	Tk_CreateImageType(&tkBitmapImageType);
5431 	Tk_CreateImageType(&tkPixmapImageType);
5432 	Tk_CreateImageType(&tkPhotoImageType);
5433 
5434 	/*
5435 	 * Create built-in photo image formats.
5436 	 */
5437 
5438 	Tk_CreatePhotoImageFormat(&tkImgFmtPPM);
5439 #endif
5440 }
5441 
5442 
5443 ClientData
Tcl_GetAssocData(interp,name,procPtr)5444 Tcl_GetAssocData(interp,name,procPtr)
5445 Tcl_Interp *interp;
5446 CONST char *name;
5447 Tcl_InterpDeleteProc **procPtr;
5448 {
5449  dTHX;
5450  HV *cm = FindHv(aTHX_ interp, "Tcl_GetAssocData", 0, ASSOC_KEY);
5451  SV **x  = hv_fetch(cm, name, strlen(name), 0);
5452  if (x)
5453   {
5454    STRLEN sz;
5455    Assoc_t *info = (Assoc_t *) SvPV(*x,sz);
5456    if (sz != sizeof(*info))
5457     croak("%s corrupted",ASSOC_KEY);
5458    if (procPtr)
5459     *procPtr = info->proc;
5460    return info->clientData;
5461   }
5462  return NULL;
5463 }
5464 
5465 void
Tcl_SetAssocData(interp,name,proc,clientData)5466 Tcl_SetAssocData(interp,name,proc,clientData)
5467 Tcl_Interp *interp;
5468 CONST char *name;
5469 Tcl_InterpDeleteProc *proc;
5470 ClientData clientData;
5471 {
5472  dTHX;
5473  HV *cm = FindHv(aTHX_ interp, "Tcl_SetAssocData", 1, ASSOC_KEY);
5474  Assoc_t info;
5475  SV *d;
5476  info.proc = proc;
5477  info.clientData = clientData;
5478  d = struct_sv((char *) &info,sizeof(info));
5479  hv_store(cm,name,strlen(name),d,0);
5480 }
5481 
5482 #define MkXSUB(str,name,xs,proc)                  \
5483 extern XSdec(name);                               \
5484 XS(name)                                          \
5485 {                                                 \
5486  CvXSUB(cv) = xs;                                 \
5487  CvXSUBANY(cv).any_ptr = (VOID *) proc;           \
5488  xs(aTHX_ cv);                                    \
5489 }
5490 #include "TkXSUB.def"
5491 #undef MkXSUB
5492 
5493 
5494 void
install_vtab(name,table,size)5495 install_vtab(name, table, size)
5496 char *name;
5497 void *table;
5498 size_t size;
5499 {
5500  dTHX;
5501  if (table)
5502   {
5503    typedef unsigned (*fptr)_((void));
5504    fptr *q = table;
5505    unsigned i;
5506    if ((*q[0])() != size)
5507     {
5508      croak("%s table is %u not %u",name,(*q[0])(),(unsigned) size);
5509     }
5510    sv_setiv(FindTkVarName(name,GV_ADD|GV_ADDMULTI),PTR2IV(table));
5511    if (size % sizeof(fptr))
5512     {
5513      warn("%s is strange size %d",name,size);
5514     }
5515    size /= sizeof(void *);
5516    for (i=0; i < size; i++)
5517     {
5518      if (!q[i])
5519       warn("%s slot %d is NULL",name,i);
5520     }
5521   }
5522  else
5523   {
5524    croak("%s pointer is NULL",name);
5525   }
5526 }
5527 
5528 
5529 
XS(XS_Tk_INIT)5530 XS(XS_Tk_INIT)
5531 {
5532  dXSARGS;
5533  InitVtabs();
5534  XSRETURN_EMPTY;
5535 }
5536 
5537 void
Boot_Glue(pTHX)5538 Boot_Glue
5539 _((pTHX))
5540 {
5541  dSP;
5542  /* A wonder how you call $e-># ? */
5543  char *XEventMethods = "abcdfhkmopstvwxyABDEKNRSTWXY#";
5544  char buf[128];
5545  CV *cv;
5546 #if PERL_REVISION > 5 || (PERL_REVISION == 5 && PERL_VERSION >= 9)
5547 #define COP_WARNINGS_TYPE STRLEN*
5548 #else
5549 #define COP_WARNINGS_TYPE SV*
5550 #endif
5551 #ifdef pWARN_NONE
5552  COP_WARNINGS_TYPE old_warn = PL_curcop->cop_warnings;
5553  PL_curcop->cop_warnings = pWARN_NONE;
5554 #endif
5555 
5556  /* Arrange to call initialization code - an XSUB called INIT */
5557  cv = newXS("Tk::INIT", XS_Tk_INIT, __FILE__);
5558 
5559 #ifdef pWARN_NONE
5560  PL_curcop->cop_warnings = old_warn;
5561 #endif
5562 
5563  initialized = 0;
5564  InitVtabs();
5565 
5566 #ifdef VERSION
5567  sprintf(buf, "%s::VERSION", BASEEXT);
5568  sv_setpv(perl_get_sv(buf,1),VERSION);
5569 #endif
5570 
5571  sprintf(buf, "%s::Widget::%s", BASEEXT, "BindClientMessage");
5572  cv = newXS(buf, XS_Tk__Widget_BindClientMessage, __FILE__);
5573 
5574  sprintf(buf, "%s::Widget::%s", BASEEXT, "PassEvent");
5575  cv = newXS(buf, XS_Tk__Widget_PassEvent, __FILE__);
5576 
5577  sprintf(buf, "%s::Widget::%s", BASEEXT, "SelectionGet");
5578  cv = newXS(buf, XS_Tk__Widget_SelectionGet, __FILE__);
5579 
5580  cv = newXS("Tk::MainWindow::Create", XS_Tk__MainWindow_Create, __FILE__);
5581 
5582 
5583  newXS("Tk::DoWhenIdle", XS_Tk_DoWhenIdle, __FILE__);
5584  newXS("Tk::CreateGenericHandler", XS_Tk_CreateGenericHandler, __FILE__);
5585 
5586 
5587  sprintf(buf, "%s::Widget::%s", BASEEXT, "ManageGeometry");
5588  cv = newXS(buf, XS_Tk__Widget_ManageGeometry, __FILE__);
5589 
5590  cv = newXS("Tk::Interp::DESTROY", XS_Tk__Interp_DESTROY, __FILE__);
5591 
5592 #define MkXSUB(str,name,xs,proc) \
5593  newXS(str, name, __FILE__);
5594 #include "TkXSUB.def"
5595 #undef MkXSUB
5596 
5597  Tk_CreateImageType(&tkPhotoImageType);
5598  Tk_CreatePhotoImageFormat(&tkImgFmtPPM);
5599 #if 0
5600  Tk_CreatePhotoImageFormat(&imgFmtGIF);
5601 #else
5602  Tk_CreatePhotoImageFormat(&tkImgFmtGIF);
5603 #endif
5604  Tk_CreatePhotoImageFormat(&imgFmtXBM);
5605  Tk_CreatePhotoImageFormat(&imgFmtXPM);
5606  Tk_CreatePhotoImageFormat(&imgFmtBMP);
5607 }
5608 
5609 void
Tcl_AllowExceptions(Tcl_Interp * interp)5610 Tcl_AllowExceptions (Tcl_Interp *interp)
5611 {
5612  /* FIXME: What should this do ? */
5613 }
5614 
5615 
5616 static HV *uidHV;
5617 
5618 Tk_Uid
Tk_GetUid(key)5619 Tk_GetUid(key)
5620     CONST char *key;		/* String to convert. */
5621 {
5622     dTHX;
5623     STRLEN klen;
5624     SV *svkey = newSVpv((char *)key,strlen(key));
5625     HE *he;
5626     if (!uidHV)
5627      uidHV = newHV();
5628     he = hv_fetch_ent(uidHV,svkey,0,0); /* added by SRT: prevents leak of auto-created SVs */
5629     if (!he)
5630      he = hv_store_ent(uidHV,svkey,Nullsv,0); /* ... */
5631     SvREFCNT_dec(svkey);
5632     return (Tk_Uid) HePV(he,klen);
5633 }
5634 
5635 
5636 Tcl_Obj*
Tcl_FSGetCwd(interp)5637 Tcl_FSGetCwd(interp)
5638     Tcl_Interp *interp;
5639 {
5640  dTHX;
5641  dSP;
5642  SV *ret = Nullsv;
5643  ENTER;
5644  SAVETMPS;
5645  PUSHMARK(sp);
5646  PUTBACK;
5647  if (call_pv("Cwd::getcwd",G_SCALAR) == 1)
5648   {
5649    SPAGAIN;
5650    ret = POPs;
5651    PUTBACK;
5652    SvREFCNT_inc(ret);
5653   }
5654  else
5655   {
5656    SPAGAIN;
5657   }
5658  FREETMPS;
5659  LEAVE;
5660  return ret;
5661 }
5662 
5663 
5664 char *
Tcl_GetCwd(interp,cwdPtr)5665 Tcl_GetCwd(interp, cwdPtr)
5666     Tcl_Interp *interp;
5667     Tcl_DString *cwdPtr;
5668 {
5669     Tcl_Obj *cwd;
5670     cwd = Tcl_FSGetCwd(interp);
5671     if (cwd == NULL) {
5672 	return NULL;
5673     } else {
5674 	Tcl_DStringInit(cwdPtr);
5675 	Tcl_DStringAppend(cwdPtr, Tcl_GetString(cwd), -1);
5676 	Tcl_DecrRefCount(cwd);
5677 	return Tcl_DStringValue(cwdPtr);
5678     }
5679 }
5680 
5681 void
LangSelectHook(CONST char * what,Tk_Window tkwin,Atom selection,Atom target,Atom type)5682 LangSelectHook(CONST char *what,Tk_Window tkwin,
5683                Atom selection, Atom target, Atom type)
5684 {
5685 #if 0
5686  /* There is still something not-quite-right about Selection
5687     but we don't want all this noise in the release
5688   */
5689  TkWindow *winPtr = (TkWindow *)tkwin;
5690  char *name = (tkwin == winPtr->dispPtr->clipWindow)
5691               ? "ClipWindow" : Tk_PathName(tkwin);
5692  LangDebug("%s sel=%s target=%s type=%s win=%p '%s'\n", what,
5693 	      Tk_GetAtomName(tkwin, selection),
5694 	      Tk_GetAtomName(tkwin, target),
5695 	      (type == None) ? "None" : Tk_GetAtomName(tkwin, type),
5696               tkwin, name);
5697 #endif
5698 }
5699 
5700 
5701