This has been something that has been bothering me for a while since seeing the Perl debugger complaining about being forked, and not being able to create an xterm on VMS.

Not having this ability has made debugging some scripts more difficult for me. So finally I am doing something about it.



In configure.com, vms/descrip.mms_template, vms/vms.c:

Add the smoke and mirrors to make the perl5db.pl xterm create request create a decterm for debugging.

Unlike UNIX, the Decterm does not exist as a separate process, just as a device that can be used.

[A decterm implements almost complete xterm functionality]

In vms/vms.c:

Fixed a couple of cases of an uninitialized variable "mode" being referenced.


In lib/perl5db.pl:

OpenVMS needs to have the $term structure exist and be filled out for sub TTY to work. I put the needed special case code in sub TTY, but there may be a better place to put it.

Note, I do not know if this affects why OpenVMS needs special handling here. OpenVMS does not fork(), so child processes do not inherit variables set by the parent, so each child must run any variable initialization required.



Using the xterms with the forked debugger is not obvious on OpenVMS, so eventually the VMS documentation will need to be updated with something similar to below:


Where the support has been built in for using DECTerms with the Perl debugger, the following environment variables need to be set:

    TERM needs to be set to "xterm".  Case sensitive.
    DISPLAY needs to be set.

OpenVMS normally does not set these values for you. The value of DISPLAY is currently ignored, however "0:0.0" is a value that is least likely to cause problems if it is ever honored.

TO DO: Get VMS Perl/Bash to look up the current display and set the environment variable if it is not already set, and to also report a DECterm terminal type as "xterm".

Note that this aliasing may enable color attributes on perl programs run on DECTerms.

Also, it is useful to have the debugger I/O off to a new xterm or DECTerm, even when not in a forked condition, as it avoids the debugger covering up or modifying I/O to STDOUT and STDERR.

By setting the environment variable "PERLDB_PIDS" to a value like "XXX", the perl debugger will create a separate terminal window.


-John
[EMAIL PROTECTED]
Personal Opinion Only

--
Need a senior system engineer?  I am looking for employment.
http://encompasserve.org/~malmberg/MALMBERG_CS1_RESUME.TXT
--- /rsync_root/perl/configure.com      Fri Nov 17 12:54:09 2006
+++ ./configure.com     Wed Nov 22 21:00:57 2006
@@ -51,6 +51,7 @@
 $ use64bitint = "n"
 $ uselargefiles = "n"
 $ usestdstat = "n"
+$ usedecterm = "y"
 $ usesitecustomize = "n"
 $ C_Compiler_Replace = "CC="
 $ thread_upcalls = "MTU="
@@ -905,7 +906,7 @@
 $   config_symbols2 
="|prefix|privlib|privlibexp|scriptdir|sitearch|sitearchexp|sitebin|sitelib|sitelib_stem|sitelibexp|try_cxx|use64bitall|use64bitint|"
 $   config_symbols3 
="|usecasesensitive|usedefaulttypes|usedevel|useieee|useithreads|usemultiplicity|usemymalloc|usedebugging_perl|useperlio|usesecurelog|"
 $   config_symbols4 
="|usethreads|usevmsdebug|usefaststdio|usemallocwrap|unlink_all_versions|uselargefiles|usesitecustomize|"
-$   config_symbols5 ="|buildmake|builder|usethreadupcalls|usekernelthreads"
+$   config_symbols5 
="|buildmake|builder|usethreadupcalls|usekernelthreads|usedecterm"
 $!  
 $   open/read CONFIG 'config_sh'
 $   rd_conf_loop:
@@ -2556,6 +2557,19 @@
 $ ELSE
 $     d_unlink_all_versions = "undef"
 $ ENDIF
+$ bool_dflt = "y"
+$ IF F$TYPE(usedecterm) .NES. "" 
+$   THEN
+$       dflt = f$search("SYS$SHARE:DECW$TERMINALSHR*.EXE")
+$       IF dflt .eqs. "" THEN bool_dflt = "n"
+$   ENDIF
+$   echo ""
+$   echo "Perl can be built to support using DecTerms for the Perl debugger"
+$   echo ""
+$   echo "If this does not make any sense to you, just accept the default '" + 
bool_dflt + "'."
+$   rp = "Build with DECterm Perl debugger support, if available? 
[''bool_dflt'] "
+$   GOSUB myread
+$   usedecterm=ans
 $! CC Flags
 $ echo ""
 $ echo "Your compiler may want other flags.  For this question you should 
include"
@@ -6684,11 +6698,19 @@
 $ ELSE
 $   LARGEFILE_REPLACE = "LARGEFILE="
 $ ENDIF
+$ if f$search("DECTERM.OPT") .nes. "" then delete DECTERM.OPT;*
+$ IF usedecterm .OR. usedecterm .EQS. "define"
+$ THEN
+$   DECTERM_REPLACE = "DECTERM=DECTERM=USE_VMS_DECTERM=1"
+$ ELSE
+$   DECTERM_REPLACE = "DECTERM="
+$ ENDIF
 $ echo4 "Extracting ''defmakefile' (with variable substitutions)"
 $ DEFINE/USER_MODE sys$output 'UUmakefile'
 $ mcr []munchconfig 'config_sh' 'Makefile_SH' "''DECC_REPLACE'" 
"''DECCXX_REPLACE'" "''ARCH_TYPE'" "''GNUC_REPLACE'" -
 "''SOCKET_REPLACE'" "''THREAD_REPLACE'" "''C_Compiler_Replace'" 
"''MALLOC_REPLACE'" -
-"''THREAD_UPCALLS'" "''THREAD_KERNEL'" "PV=''version'" 
"FLAGS=FLAGS=''extra_flags'" "''LARGEFILE_REPLACE'"
+"''THREAD_UPCALLS'" "''THREAD_KERNEL'" "PV=''version'" 
"FLAGS=FLAGS=''extra_flags'" "''LARGEFILE_REPLACE'" -
+"''DECTERM_REPLACE'"
 $! Clean up after ourselves
 $ DELETE/NOLOG/NOCONFIRM []munchconfig.exe;
 $!
@@ -6872,6 +6894,21 @@
 $ CALL Bad_environment "UTIL"
 $ CALL Bad_environment "TEST" "SYMBOL"
 $ IF f$search("config.msg") .eqs. "" THEN echo "OK."
+$!
+$! %Config-I-DECTERM, write decterm.opt
+$!
+$ if f$search("[-]DECTERM.OPT") .nes. "" then delete [-]DECTERM.OPT;*
+$ IF usedecterm .OR. usedecterm .EQS. "define"
+$ THEN
+$   open/write dopt [-]DECTERM.OPT
+$   if f$search("SYS$SHARE:DECW$TERMINALSHR12.EXE") .nes. ""
+$   then
+$      write dopt "SYS$SHARE:DECW$TERMINALSHR12.EXE/SHARE"
+$   else
+$      write dopt "SYS$SHARE:DECW$TERMINALSHR.EXE/SHARE"
+$   endif
+$   close dopt
+$ ENDIF
 $!
 $! %Config-I-VMS, write perl_setup.com here
 $!
--- /rsync_root/perl/vms/descrip_mms.template   Mon Oct  9 06:32:53 2006
+++ ./vms/descrip_mms.template  Wed Nov 22 19:21:42 2006
@@ -33,6 +33,7 @@
 ~MTU~
 ~FLAGS~
 ~LARGEFILE~
+~DECTERM~
 
 #: >>>>> Architecture-specific options <<<<<
 .ifdef IXE
@@ -221,15 +222,26 @@
 MTHREADLINKFLAGS = 
 .endif
 
+# Support of DECTERM for the Perl Debugger
+.IFDEF DECTERM
+DECTERM_OPT=,[]DECTERM.OPT/OPT
+DECTERM_DEF2=/Define=$(DECTERM)
+.IFDEF LARGEFILE
+DECTERM_DEF1=,$(DECTERM)
+.ELSE
+DECTERM_DEF1=$(DECTERM)
+.ENDIF
+.ENDIF
+
 # C preprocessor manifest "DEBUGGING" ==> perl -D, not the VMS debugger
 .IFDEF LARGEFILE
-CFLAGS = $(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)/Define=$(LARGEFILE)
+CFLAGS = $(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)/Define=($(LARGEFILE))
 X2PCFLAGS = 
$(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)/Define=(PERL_FOR_X2P,$(LARGEFILE))
-CORECFLAGS = 
$(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)/Define=(PERL_CORE,$(LARGEFILE))
+CORECFLAGS = 
$(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)/Define=(PERL_CORE,$(LARGEFILE)$(DECTERM_DEF1))
 .ELSE
 CFLAGS = $(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)
 X2PCFLAGS = $(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)/Define=PERL_FOR_X2P
-CORECFLAGS = $(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)/Define=PERL_CORE
+CORECFLAGS = $(XTRACCFLAGS)$(DBGCCFLAGS)$(FLAGS)/Define=(PERL_CORE 
$(DECTERM_DEF1)
 .ENDIF
 
 LINKFLAGS = $(DBGLINKFLAGS)
@@ -436,9 +448,13 @@
 miniperl : $(DBG)miniperl$(E)  vmspipe.com
        @ Continue
 $(MINIPERL_EXE) :  miniperlmain$(O), $(DBG)libperl$(OLB) $(CRTL)
-       Link $(LINKFLAGS)/NoDebug/Trace/NoMap/NoFull/NoCross/Exe=$(MMS$TARGET) 
miniperlmain$(O), $(DBG)libperl$(OLB)/Library/Include=globals $(CRTLOPTS)
+       Link $(LINKFLAGS)/NoDebug/Trace/NoMap/NoFull/NoCross/Exe=$(MMS$TARGET) \
+          miniperlmain$(O), $(DBG)libperl$(OLB)/Library/Include=globals \
+          $(CRTLOPTS) $(DECTERM_OPT)
 $(DBG)miniperl$(E) :  miniperlmain$(O), $(DBG)libperl$(OLB) $(CRTL)
-       Link $(LINKFLAGS)/Trace/Exe=$(MMS$TARGET) 
miniperlmain$(O),$(DBG)libperl$(OLB)/Library/Include=globals  $(CRTLOPTS)
+       Link $(LINKFLAGS)/Trace/Exe=$(MMS$TARGET) \
+       miniperlmain$(O),$(DBG)libperl$(OLB)/Library/Include=globals  \
+       $(CRTLOPTS) $(DECTERM_OPT)
 
 $(DBG)libperl$(OLB) : $(obj)
        @ If F$Search("$(MMS$TARGET)").eqs."" Then Library/Object/Create 
$(MMS$TARGET)
@@ -461,7 +477,9 @@
        Link $(LINKFLAGS)$(MTHREADLINKFLAGS)/Exe=$(MMS$TARGET) perlmain$(O), 
perlshr.opt/Option, perlshr_attr.opt/Option $(CRTLOPTS)
 
 $(DBG)perlshr$(E) : $(DBG)libperl$(OLB) $(extobj) $(DBG)perlshr_xtras.ts
-       Link $(LINKFLAGS)/Share=$(MMS$TARGET) $(extobj) 
[]$(DBG)perlshr_bld.opt/Option, perlshr_attr.opt/Option
+       Link $(LINKFLAGS)/Share=$(MMS$TARGET) $(extobj) \
+         []$(DBG)perlshr_bld.opt/Option, \
+         perlshr_attr.opt/Option $(DECTERM_OPT)
 
 # The following files are built in one go by gen_shrfls.pl:
 #  perlshr_attr.opt, $(DBG)perlshr_bld.opt - VAX and AXP
--- /rsync_root/perl/lib/perl5db.pl     Mon Nov 20 09:56:00 2006
+++ ./lib/perl5db.pl    Fri Nov 24 00:55:14 2006
@@ -1721,7 +1721,7 @@
         if ($console) {
 
             # If we have a console, check to see if there are separate ins and
-            # outs to open. (They are assumed identiical if not.)
+            # outs to open. (They are assumed identical if not.)
 
             my ( $i, $o ) = split /,/, $console;
             $o = $i unless defined $o;
@@ -6734,6 +6734,19 @@
 =cut
 
 sub TTY {
+
+    # With VMS we can get here with $term undefined, so we do not
+    # switch to this terminal.  There may be a better place to make
+    # sure that $term is defined on VMS
+    if ( @_ and ($^O eq 'VMS') and !defined($term) ) {
+       eval { require Term::ReadLine } or die $@;
+        if ( !$rl ) {
+           $term = new Term::ReadLine::Stub 'perldb', $IN, $OUT;
+       }
+       else {
+           $term = new Term::ReadLine 'perldb', $IN, $OUT;
+       }
+    }
     if ( @_ and $term and $term->Features->{newTTY} ) {
 
         # This terminal supports switching to a new TTY.
--- /rsync_root/perl/vms/vms.c  Sun Nov  5 20:38:13 2006
+++ ./vms/vms.c Fri Nov 24 00:08:39 2006
@@ -17,6 +17,7 @@
 #include <chpdef.h>
 #include <clidef.h>
 #include <climsgdef.h>
+#include <dcdef.h>
 #include <descrip.h>
 #include <devdef.h>
 #include <dvidef.h>
@@ -47,6 +48,7 @@
 #include <uicdef.h>
 #include <stsdef.h>
 #include <rmsdef.h>
+#include <smgdef.h>
 #if __CRTL_VER >= 70000000 /* FIXME to earliest version */
 #include <efndef.h>
 #define NO_EFN EFN$C_ENF
@@ -89,6 +91,21 @@
        void * astprm,
        void * nullarg);
 
+#ifdef USE_VMS_DECTERM
+
+/* Routine to create a decterm for use */
+/* No headers, this information was found in the Programming Concepts Manual */
+int decw$term_port
+   (const struct dsc$descriptor_s * display,
+    const struct dsc$descriptor_s * setup_file,
+    const struct dsc$descriptor_s * customization,
+    struct dsc$descriptor_s * result_device_name,
+    unsigned short * result_device_name_length,
+    void * controller,
+    void * char_buffer,
+    void * char_change_buffer);
+#endif
+
 #if __CRTL_VER >= 70300000 && !defined(__VAX)
 
 static int set_feature_default(const char *name, int value)
@@ -2759,6 +2776,8 @@
     int             in_done;        /* true when in pipe finished */
     int             out_done;
     int             err_done;
+    unsigned short  xchan;         /* channel to debug xterm */
+    unsigned short  xchan_valid;    /* channel is assigned */
 };
 
 struct exit_control_block
@@ -3724,6 +3743,213 @@
 }
 
 
+#ifdef USE_VMS_DECTERM
+
+static int vms_is_syscommand_xterm(void)
+{
+const static struct dsc$descriptor_s syscommand_dsc = 
+  { 11, DSC$K_DTYPE_T, DSC$K_CLASS_S, "SYS$COMMAND" };
+
+const static struct dsc$descriptor_s decwdisplay_dsc = 
+  { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "DECW$DISPLAY" };
+
+struct item_list_3 items[2];
+unsigned short dvi_iosb[4];
+unsigned long devchar;
+unsigned long devclass;
+int status;
+
+    /* Very simple check to guess if sys$command is a decterm? */
+    /* First see if the DECW$DISPLAY: device exists */
+    items[0].len = 4;
+    items[0].code = DVI$_DEVCHAR;
+    items[0].bufadr = &devchar;
+    items[0].retadr = NULL;
+    items[1].len = 0;
+    items[1].code = 0;
+
+    status = sys$getdviw
+       (NO_EFN, 0, &decwdisplay_dsc, items, dvi_iosb, NULL, NULL, NULL);
+    _ckvmssts(status);
+
+    if (!$VMS_STATUS_SUCCESS(dvi_iosb[0])) {
+       if (dvi_iosb[0] == SS$_NOSUCHDEV)
+           return -1;
+       _ckvmssts(status);
+
+    }
+
+    /* If it does, then for now assume that we are on a workstation */
+    /* Now verify that SYS$COMMAND is a terminal */
+    /* for creating the debugger DECTerm */
+
+    items[0].len = 4;
+    items[0].code = DVI$_DEVCLASS;
+    items[0].bufadr = &devclass;
+    items[0].retadr = NULL;
+    items[1].len = 0;
+    items[1].code = 0;
+
+    status = sys$getdviw
+       (NO_EFN, 0, &syscommand_dsc, items, dvi_iosb, NULL, NULL, NULL);
+    _ckvmssts(status);
+    if ($VMS_STATUS_SUCCESS(dvi_iosb[0])) {
+       if (devclass == DC$_TERM) {
+           return 0;
+       }
+    }
+    return -1;
+}
+
+/* If we are on a DECTerm, we can pretend to fork xterms when requested */
+static PerlIO * create_forked_xterm(aTHX_ const char *cmd, const char *mode)
+{
+int status;
+int ret_stat;
+char * ret_char;
+char device_name[65];
+unsigned short device_name_len;
+struct dsc$descriptor_s customization_dsc;
+struct dsc$descriptor_s device_name_dsc;
+const char * cptr;
+char * tptr;
+char customization[200];
+char title[40];
+pInfo info = NULL;
+char mbx1[64];
+unsigned short p_chan;
+int n;
+unsigned short iosb[4];
+struct item_list_3 items[2];
+const char * cust_str =
+    "DECW$TERMINAL.iconName:\tPerl Dbg\nDECW$TERMINAL.title:\t%40s\n";
+struct dsc$descriptor_s d_mbx1 = {sizeof mbx1, DSC$K_DTYPE_T,
+                                      DSC$K_CLASS_S, mbx1};
+
+    ret_char = strstr(cmd," xterm ");
+    if (ret_char == NULL)
+       return Nullfp;
+    cptr = ret_char + 7;
+    ret_char = strstr(cmd,"tty");
+    if (ret_char == NULL)
+       return Nullfp;
+    ret_char = strstr(cmd,"sleep");
+    if (ret_char == NULL)
+       return Nullfp;
+
+    /* Are we on a workstation? */
+    /* to do: capture the rows / columns and pass their properties */
+    ret_stat = vms_is_syscommand_xterm();
+    if (ret_stat < 0)
+       return Nullfp;
+
+    /* Make the title: */
+    ret_char = strstr(cptr,"-title");
+    if (ret_char != NULL) {
+       while ((*cptr != 0) && (*cptr != '\"')) {
+           cptr++;
+       }
+       if (*cptr == '\"')
+           cptr++;
+       n = 0;
+       while ((*cptr != 0) && (*cptr != '\"')) {
+           title[n] = *cptr;
+           n++;
+           if (n == 39) {
+               title[39] == 0;
+               break;
+           }
+           cptr++;
+       }
+       title[n] = 0;
+    }
+    else {
+           /* Default title */
+           strcpy(title,"Perl Debug DECTerm");
+    }
+    sprintf(customization,cust_str, title);
+
+    customization_dsc.dsc$a_pointer = customization;
+    customization_dsc.dsc$w_length = strlen(customization);
+    customization_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+    customization_dsc.dsc$b_class = DSC$K_CLASS_S;
+
+    device_name_dsc.dsc$a_pointer = device_name;
+    device_name_dsc.dsc$w_length = sizeof device_name -1;
+    device_name_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
+    device_name_dsc.dsc$b_class = DSC$K_CLASS_S;
+
+    device_name_len = 0;
+
+    /* Try to create the window */
+     status = decw$term_port
+       (NULL,
+       NULL,
+       &customization_dsc,
+       &device_name_dsc,
+       &device_name_len,
+       NULL,
+       NULL,
+       NULL);
+    if (!$VMS_STATUS_SUCCESS(status))
+       return Nullfp;
+
+    device_name[device_name_len] = '\0';
+
+    /* Need to set this up to look like a pipe for cleanup */
+    n = sizeof(Info);
+    _ckvmssts(lib$get_vm(&n, &info));
+
+    info->mode = *mode;
+    info->done = FALSE;
+    info->completion = 0;
+    info->closing    = FALSE;
+    info->in         = 0;
+    info->out        = 0;
+    info->err        = 0;
+    info->fp         = Nullfp;
+    info->useFILE    = 0;
+    info->waiting    = 0;
+    info->in_done    = TRUE;
+    info->out_done   = TRUE;
+    info->err_done   = TRUE;
+
+    /* Assign a channel on this so that it will persist, and not login */
+    /* We stash this channel in the info structure for reference. */
+    /* The created xterm self destructs when the last channel is removed */
+    /* and it appears that perl5db.pl (perl debugger) does this routinely */
+    /* So leave this assigned. */
+    device_name_dsc.dsc$w_length = device_name_len;
+    status = sys$assign(&device_name_dsc,&info->xchan,0,0);
+    _ckvmssts(status);
+    info->xchan_valid = 1;
+
+    /* Now create a mailbox to be read by the application */
+
+    create_mbx(aTHX_ &p_chan, &d_mbx1);
+
+    /* write the name of the created terminal to the mailbox */
+    status = sys$qiow(NO_EFN, p_chan, IO$_WRITEVBLK|IO$M_NOW,
+            iosb, NULL, NULL, device_name, device_name_len, 0, 0, 0, 0);
+
+    _ckvmssts(status);
+
+    info->fp  = PerlIO_open(mbx1, mode);
+
+    /* Done with this channel */
+    sys$dassgn(p_chan);
+
+    /* If any errors, then clean up */
+    if (!info->fp) {
+               n = sizeof(Info);
+       _ckvmssts(lib$free_vm(&n, &info));
+       return Nullfp;
+        }
+
+    /* All done */
+    return info->fp;
+}
+#endif
 
 static PerlIO *
 safe_popen(pTHX_ const char *cmd, const char *in_mode, int *psts)
@@ -3752,7 +3978,21 @@
     $DESCRIPTOR(d_sym_in ,"PERL_POPEN_IN");
     $DESCRIPTOR(d_sym_out,"PERL_POPEN_OUT");
     $DESCRIPTOR(d_sym_err,"PERL_POPEN_ERR");
-                            
+
+#ifdef USE_VMS_DECTERM
+    /* Check here for Xterm create request.  This means looking for
+     * "3>&1 xterm\b" and "\btty 1>&3\b$" in the command, and that it
+     *  is possible to create an xterm.
+     */
+    if (*in_mode == 'r') {
+        PerlIO * xterm_fd;
+
+       xterm_fd = create_forked_xterm(aTHX_ cmd, in_mode);
+       if (xterm_fd != Nullfp)
+           return xterm_fd;
+    }
+#endif
+
     if (!head_PLOC) store_pipelocs(aTHX);   /* at least TRY to use a static 
vmspipe file */
 
     /* once-per-program initialization...
@@ -3821,7 +4061,7 @@
           set_errno(EVMSERR); 
       }
       set_vaxc_errno(sts);
-      if (*mode != 'n' && ckWARN(WARN_PIPE)) {
+      if (*in_mode != 'n' && ckWARN(WARN_PIPE)) {
         Perl_warner(aTHX_ packWARN(WARN_PIPE),"Can't pipe \"%*s\": %s", 
strlen(cmd), cmd, Strerror(errno));
       }
       *psts = sts;
@@ -3844,6 +4084,8 @@
     info->in_done    = TRUE;
     info->out_done   = TRUE;
     info->err_done   = TRUE;
+    info->xchan      = 0;
+    info->xchan_valid = 0;
 
     in = PerlMem_malloc(VMS_MAXRSS);
     if (in == NULL) _ckvmssts(SS$_INSFMEM);
@@ -3872,7 +4114,7 @@
             info->out->info = info;
         }
         if (!info->useFILE) {
-        info->fp  = PerlIO_open(mbx, mode);
+           info->fp  = PerlIO_open(mbx, mode);
         } else {
             info->fp = (PerlIO *) freopen(mbx, mode, stdin);
             Perl_vmssetuserlnm(aTHX_ "SYS$INPUT",mbx);
@@ -4094,6 +4336,7 @@
     pInfo info, last = NULL;
     unsigned long int retsts;
     int done, iss, n;
+    int status;
     
     for (info = open_pipes; info != NULL; last = info, info = info->next)
         if (info->fp == fp) break;
@@ -11783,6 +12026,7 @@
     Safefree(rslt);
     return fp;
 }
+
 
 #ifdef HAS_SYMLINK
 static char *

Reply via email to