home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / AP / JED / JED097-1.TAR / jed / src / vms.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-12  |  22.2 KB  |  927 lines

  1. /*
  2.  *  Copyright (c) 1992, 1994 John E. Davis  (davis@amy.tch.harvard.edu)
  3.  *  All Rights Reserved.
  4.  */
  5. #include <stdio.h>
  6. #include "config.h"
  7. #include "sysdep.h"
  8.  
  9. #include <ssdef.h>
  10. #include <rmsdef.h>
  11. #include <dvidef.h>
  12. #include <jpidef.h>
  13. #include <descrip.h>
  14. #include <iodef.h>
  15. #include <ttdef.h>
  16. #include <rms.h>
  17. #include <errno.h>
  18. #include <tt2def.h>
  19.  
  20. /* #include <libdef.h>  */
  21. /* #include <unixlib.h> */
  22.  
  23. typedef struct {                /* I/O status block     */
  24.         short i_cond;           /* Condition value      */
  25.         short i_xfer;           /* Transfer count     */
  26.         long  i_info;           /* Device information     */
  27. } Iosb_Type;
  28.  
  29. typedef struct {                /* Terminal characteristics   */
  30.         char  t_class;          /* Terminal class     */
  31.         char  t_type;           /* Terminal type      */
  32.         short t_width;          /* Terminal width in characters   */
  33.         long  t_mandl;          /* Terminal's mode and length   */
  34.         long  t_extend;         /* Extended terminal characteristics  */
  35. }  TermChar_Type;
  36.  
  37. TermChar_Type Old_Term_Char, New_Term_Char;
  38.  
  39. int Abort_Char = 7;               /* scan code for G (control) */
  40.  
  41. /* This serves to identify the channel we are reading input from.  */
  42. short This_Term;
  43.  
  44.  
  45. typedef struct
  46. {
  47.    short buflen;
  48.    short item_code;
  49.    int *buf_addr;
  50.    int *len_addr;
  51. } item_list_3;
  52.    
  53. static int TTY_Inited;
  54.  
  55. void vms_exit_handler(int not_used)
  56. {
  57.    if (TTY_Inited == 0) exit(0);
  58.  
  59.    auto_save_all();
  60.    reset_display(); 
  61.    reset_tty(); 
  62.    exit(1);
  63. }
  64.  
  65. /*
  66.  *      Exit Handler Control Block
  67.  */
  68. static struct argument_block
  69.   {
  70.       int forward_link;
  71.       int (*exit_routine)();
  72.       int arg_count;
  73.       int *status_address;
  74.       int exit_status;
  75.   }
  76. exit_block =
  77.   {
  78.       0,
  79.       NULL,
  80.       1,
  81.       &exit_block.exit_status,
  82.       0
  83.   };
  84.  
  85. void vms_cancel_exithandler()
  86. {
  87.    SYS$CANEXH(exit_block);
  88. }
  89.  
  90. #undef USING_INPUT_BUFFER
  91. #undef DONE_WITH_INPUT_BUFFER
  92.  
  93.  
  94. int vms_input_buffer;
  95.  
  96. static struct vms_ast_iosb
  97. {
  98.     short status;
  99.     short offset;
  100.     short termlen;
  101.     short term;
  102. } vms_ast_iosb;
  103.  
  104. extern void vms_que_key_ast();
  105. static int Ast_Fired_Event_Flag;
  106. static int Timer_Event_Flag;
  107. static int Event_Flag_Mask;
  108. static int Ast_Stop_Input;
  109. static int Waiting_For_Ast;
  110. static int Using_Keyboard_Buffer_Event_Flag;
  111.  
  112. #define USING_INPUT_BUFFER\
  113.    SYS$SETAST(0);
  114.    
  115. #define DONE_WITH_INPUT_BUFFER\
  116.    SYS$SETAST(1);
  117.  
  118. static int getkey_ast(int not_used)
  119. {
  120.    unsigned int c = 1000;
  121.    
  122.    if (vms_ast_iosb.offset)
  123.      {
  124.     c = (unsigned int) vms_input_buffer;
  125.      }
  126.  
  127.    if (c <= 255)
  128.      {
  129.     if (c == Abort_Char)
  130.       {
  131.          if (Ignore_User_Abort == 0) SLang_Error = 2;
  132.          SLKeyBoard_Quit = 1;
  133.       }
  134.  
  135.     USING_INPUT_BUFFER
  136.     
  137.     if (Input_Buffer_Len < MAX_INPUT_BUFFER_LEN - 3) 
  138.       Input_Buffer[Input_Buffer_Len++] = c;
  139.     
  140.     DONE_WITH_INPUT_BUFFER
  141.      }
  142.    if (Waiting_For_Ast)  SYS$SETEF (Ast_Fired_Event_Flag);
  143.    Waiting_For_Ast = 0;
  144.    vms_que_key_ast();
  145.    return (1);
  146. }
  147.  
  148. void vms_que_key_ast()
  149. {
  150.    static int trmmsk [2] = { 0, 0 };
  151.    int status;
  152.  
  153.    if (Ast_Stop_Input) return;
  154.    status = SYS$QIO (0, This_Term,
  155.              IO$_READVBLK | IO$M_NOECHO | IO$_TTYREADALL,
  156.              &vms_ast_iosb, getkey_ast, 1,
  157.              &vms_input_buffer, 1, 0, trmmsk, 0, 0);
  158. }
  159.  
  160. static char TTY_Name[8];
  161. static int This_Process_Pid;
  162.  
  163. void init_tty()
  164. {
  165.    Iosb_Type iostatus;
  166.    int tmp, name_len, status, lastppid, ppid;
  167.    item_list_3 itmlst[] =
  168.      {
  169.     {sizeof(int), JPI$_PID, &This_Process_Pid, &tmp},
  170.     {7, JPI$_TERMINAL, (int *) TTY_Name, &name_len},
  171.     {0, 0, 0, 0}
  172.      };
  173.    $DESCRIPTOR ( term, TTY_Name);
  174.    
  175.    TTY_Inited = 1;
  176.    ppid = 0, lastppid = -1;
  177.    
  178.    /* Here I get this process pid then I get the master process pid
  179.       and use the controlling terminal of that process. */
  180.    while (1)
  181.      {
  182.     status = SYS$GETJPIW(0,       /* event flag */
  183.                  &ppid,   /* pid address */
  184.                  0,       /* proc name address */
  185.                  itmlst,
  186.                  0, 0, 0);
  187.  
  188.     if (status != SS$_NORMAL) 
  189.       {
  190.          fprintf(stderr, "PID: %X, status: %X\n", This_Process_Pid, status);
  191.          exit(1);
  192.       }
  193.  
  194.     if (lastppid == ppid) break;
  195.     lastppid = ppid;
  196.  
  197.     itmlst[0].item_code =  JPI$_MASTER_PID;
  198.     itmlst[0].buf_addr =  &ppid;
  199.      }
  200.    
  201.    if (Batch) return;
  202.    if (X_Init_Term_Hook != NULL)
  203.      {
  204.     (void) (*X_Init_Term_Hook) ();
  205.     return;
  206.      }
  207.    
  208.    term.dsc$w_length = name_len;
  209.    status = sys$assign ( &term, &This_Term, 0, 0 );
  210.    if (status != SS$_NORMAL)
  211.      {
  212.     fprintf(stderr,"Unable to assign input channel\n");
  213.     fprintf(stderr,"PID: %X, DEV %s, status: %d\n", This_Process_Pid, TTY_Name, status);
  214.     exit(0);
  215.      }
  216.  
  217.    if (NULL == exit_block.exit_routine)
  218.      {
  219.     exit_block.exit_routine = (int (*)()) vms_exit_handler;
  220.     SYS$DCLEXH(&exit_block);
  221.      }
  222.  
  223.    /* allocate an event flag and clear it--- used by ast routines.  Since
  224.     * I am only using a few local event flags, there is really no need to 
  225.     * worry about freeing these.
  226.     * 
  227.     * The event flags are used to avoid timing problems with the getkey AST
  228.     * as well as for a form of time out.
  229.     */
  230.    if (!Ast_Fired_Event_Flag) LIB$GET_EF (&Ast_Fired_Event_Flag);
  231.    SYS$CLREF (Ast_Fired_Event_Flag);
  232.    
  233.    if (!Timer_Event_Flag) LIB$GET_EF (&Timer_Event_Flag);
  234.    SYS$CLREF (Timer_Event_Flag);
  235.    
  236.    /* When no thread is using the keyboard buffer, this ev is set.  It 
  237.     * gets cleared when the buffer is in use.
  238.     */
  239.    if (!Using_Keyboard_Buffer_Event_Flag) LIB$GET_EF (&Using_Keyboard_Buffer_Event_Flag);
  240.    SYS$SETEF (Using_Keyboard_Buffer_Event_Flag);
  241.    
  242.    /* The working assumption here is that the event flags are in the same
  243.     * cluster.  They need not be but it is very likely that they are.
  244.     */
  245.    Event_Flag_Mask = ((unsigned) 1 << (Ast_Fired_Event_Flag % 32));
  246.    Event_Flag_Mask |= ((unsigned) 1 << (Timer_Event_Flag % 32));
  247.  
  248.    Waiting_For_Ast = 0;
  249.    Ast_Stop_Input = 0;
  250.    
  251.    /* Get the startup terminal characteristics */
  252.    status = sys$qiow(0,               /* Wait on event flag zero      */
  253.              This_Term,           /* Channel to input terminal    */
  254.              IO$_SENSEMODE,    /* Get current characteristic   */
  255.              &iostatus,           /* Status after operation       */
  256.              0, 0,           /* No AST service               */
  257.                      &Old_Term_Char,   /* Terminal characteristics buf */
  258.              sizeof(Old_Term_Char),/* Size of the buffer           */
  259.              0, 0, 0, 0);
  260.    
  261.    New_Term_Char = Old_Term_Char;
  262.    New_Term_Char.t_mandl |= TT$M_EIGHTBIT | TT$M_NOECHO;
  263.    New_Term_Char.t_extend |= TT2$M_PASTHRU | TT2$M_XON;
  264.    
  265.    status = sys$qiow(0,               /* Wait on event flag zero      */
  266.              This_Term,           /* Channel to input terminal    */
  267.              IO$_SETMODE,      /* Set current characteristic   */
  268.              &iostatus,           /* Status after operation       */
  269.              0, 0,           /* No AST service               */
  270.                      &New_Term_Char,   /* Terminal characteristics buf */
  271.              sizeof(New_Term_Char),/* Size of the buffer           */
  272.              0, 0, 0, 0); 
  273.       
  274.    /* Enable the Application Keypad */
  275.    (*tt_write_string) ("\033=\033[?1l");   /* application keys/cursor keys */
  276.    vms_que_key_ast();   /* set up the key ast */
  277. }
  278.  
  279. void sys_flush(int fd)
  280. {
  281. }
  282.  
  283. static void cancel_ast (void)
  284. {
  285.    if (TTY_Inited == 0) return;
  286.    
  287.    /* stop the keyboard ast */
  288.    SYS$SETAST (0);               /* disable AST delivery */
  289.    SYS$CLREF (Ast_Fired_Event_Flag);
  290.    Waiting_For_Ast = 1;
  291.    Ast_Stop_Input = 1;
  292.  
  293.    /* cancel all i/o on this channel.  This canels pending, as well as those
  294.     * already in progress and queued.  In particular, according to the 
  295.     * manuals, cancelling I/O on the channel will cause the getkey AST
  296.     * to fire even though the SYS$QIO call was aborted.  This is crucial
  297.     * because below we wait for the AST to set the event flag.
  298.     */
  299.    SYS$CANCEL (This_Term);
  300.    SYS$SETAST (1);               /* enable ASTs again */
  301.    SYS$WAITFR (Ast_Fired_Event_Flag);  /* sleep until it fires */
  302.    Waiting_For_Ast = 0;
  303. }
  304.  
  305. static int keypad_state = 0;
  306. void reset_tty()
  307. {
  308.    Iosb_Type iostatus;
  309.    if (Batch) return;
  310.    if (!TTY_Inited) return;
  311.    if (X_Init_Term_Hook != NULL)
  312.      {
  313.     if (X_Reset_Term_Hook != NULL) (*X_Reset_Term_Hook) ();
  314.     TTY_Inited = 0;
  315.     return;
  316.      }
  317.    cancel_ast ();
  318.    TTY_Inited = 0;
  319.    
  320.    /* reset the terminal characteristics */
  321.    
  322.    sys$qiow(0,                   /* event flag 0 */
  323.         This_Term,               /* Channel to input terminal    */
  324.         IO$_SETMODE,           /* Set current characteristic   */
  325.         &iostatus,               /* Status after operation       */
  326.         0, 0,               /* No AST service               */
  327.         &Old_Term_Char,           /* Terminal characteristics buf */
  328.         sizeof(Old_Term_Char),     /* Size of the buffer           */
  329.         0, 0, 0, 0);            /* unused */
  330.  
  331.    if (keypad_state) SLtt_write_string("\033=\033[?1l");
  332.    else SLtt_write_string("\033>");
  333. }
  334.  
  335. unsigned char sys_getkey()
  336. {
  337.    unsigned char c;
  338.    int tsecs;
  339.  
  340.    if (SLKeyBoard_Quit) return((int) Abort_Char);
  341.    
  342.    /* Under DECWIndows, I do not know how to timeout so this will have to do
  343.       for now */
  344.    if (X_Read_Hook != NULL) return (c = X_Read_Hook ());
  345.  
  346.   /* On VMS, the keyboard ast routine should be stuffing the buffer, so
  347.    do nothing except sleep */
  348.  
  349.    /* clear the flag which ast will set */
  350.    Waiting_For_Ast = 0;
  351.    if (Input_Buffer_Len) return(my_getkey());
  352.    tsecs = 450;   /* 45 seconds */
  353.    
  354.    while (!sys_input_pending(&tsecs))
  355.      {
  356.     /* update status line incase user is displaying time */
  357.     if (Display_Time)
  358.       {
  359.          JWindow->trashed = 1;
  360.          update((Line *) NULL, 0, 1);
  361.       }
  362.      }
  363.    c = my_getkey();
  364.    return(c);
  365. }
  366.  
  367. /* waits *secs tenth of seconds for input */
  368. static int sys_input_pending(int *secs)
  369. {
  370.    unsigned long daytim[2];
  371.    
  372.    if (Batch) return(0);
  373.    if (Input_Buffer_Len) return(Input_Buffer_Len);
  374.    
  375.    if (X_Input_Pending_Hook != NULL) 
  376.      {
  377.     if ((*X_Input_Pending_Hook) ()) return 1;
  378.     /* I need to make this work with DECWIndows */
  379.     return 0;
  380.      }
  381.    
  382.    
  383.    if (*secs) 
  384.      {
  385.     /* takes a quad word time.  If negative, use a relative time. */
  386.     daytim[1] = 0xFFFFFFFF;
  387.     daytim[0] = -(*secs * 1000 * 1000);   /* 1000 * 1000 is a tenth of a sec */
  388.     
  389.     SYS$CLREF (Ast_Fired_Event_Flag);
  390.     /* SYS$CLREF (Timer_Event_Flag);  SYS$SETIMR call clears this */
  391.  
  392.     /* set up a flag for the ast so it knows to set the event flag */
  393.     Waiting_For_Ast = 1;
  394.     
  395.     SYS$SETIMR(Timer_Event_Flag, daytim, 0, 1);
  396.            
  397.     /* this will return when ast does its job or timer expires.
  398.      * The first argument simply serves to identify the cluster for 
  399.      * the event flag and that is all.  The second argument serves 
  400.      * to identify the event flags to wait for. 
  401.      */
  402.     SYS$WFLOR (Ast_Fired_Event_Flag, Event_Flag_Mask);
  403.     
  404.     Waiting_For_Ast = 0;
  405.     
  406.     /* cancel the timer */
  407.     SYS$CANTIM(1, 3);   /* 3 is user mode */
  408.      }
  409.    return (Input_Buffer_Len);
  410. }
  411.  
  412. /*  This is to get the size of the terminal  */
  413. void get_term_dimensions(int *cols, int *rows)
  414. {
  415.    int status, junk;
  416.    Iosb_Type iostatus;
  417.    $DESCRIPTOR(devnam, TTY_Name);
  418.    item_list_3 itmlst[] = 
  419.      {
  420.     {sizeof(*rows), DVI$_TT_PAGE, rows, &junk},
  421.     {sizeof(*cols), DVI$_DEVBUFSIZ, cols, &junk},
  422.     {sizeof(keypad_state), DVI$_TT_APP_KEYPAD, &keypad_state, &junk},
  423.     {0, 0, 0, 0}               /* end of list */
  424.      };
  425.  
  426.    if (X_Get_Term_Size_Hook != NULL)
  427.      {
  428.     (*X_Get_Term_Size_Hook)(cols, rows);
  429.     return;
  430.      }
  431.    
  432.    if (*rows <= 0) *rows = *tt_Screen_Rows;
  433.    if (*cols <= 0) *cols = *tt_Screen_Cols;
  434.  
  435.    /* Get current terminal characteristics */
  436.    status = sys$getdviw(0,           /* Wait on event flag zero  */
  437.             0,           /* Channel to input terminal  */
  438.             &devnam,     /* device name */
  439.             &itmlst,      /* Item descriptor List */
  440.             &iostatus,   /* Status after operation */
  441.             0, 0,        /* No AST service   */
  442.             0);          /* nullarg */
  443.  
  444.    if (status&1) status = iostatus.i_cond;
  445.    /* Jump out if bad status */
  446.    if ((status & 1) == 0) exit(status);
  447. }
  448.  
  449. /* returns 0 on failure, 1 on sucess */
  450. int sys_delete_file(char *filename)
  451. {
  452.     return (1 + delete(filename));   /* 0: sucess; -1 failure */
  453. }
  454.  
  455. int sys_rename(char *from, char *to)
  456. {
  457.     return(-1);
  458. }
  459.  
  460. /* This routine converts unix type names to vms names */
  461. int locate(char ch, char *string)
  462. {
  463.     int i;
  464.     char c;
  465.  
  466.     i = 0;
  467.     while (c = string[i++], (c != ch) && (c != '\0'));
  468.     if (c == ch) return(i); else return (0);
  469. }
  470.  
  471. char *unix2vms(char *file)
  472. {
  473.     int i,device,j,first,last;
  474.     static char vms_name[80];
  475.     char ch;
  476.  
  477.     if (locate('[',file)) return(file); /* vms_name syntax */
  478.     if (!locate('/',file)) return(file); /* vms_name syntax */
  479.  
  480.     /* search for the ':' which means a device is present */
  481.     device = locate(':',file);
  482.  
  483.     i = 0;
  484.     if (device)
  485.       {
  486.           while (ch = file[i], i < device) vms_name[i++] = ch;
  487.       }
  488.     j = i;
  489.  
  490.     /* go from the  end looking for a '/' and mark it */
  491.     i = strlen(file) - 1;
  492.     while(ch = file[i], ch != '/' && i-- >= 0);
  493.     if (ch == '/')
  494.       {
  495.           file[i] = ']';
  496.           last = 0;
  497.       }
  498.     else last = 1;
  499.  
  500.     i = j;
  501.     vms_name[j++] = '[';
  502.     vms_name[j++] = '.';
  503.     first = 0;
  504.     while(ch = file[i++], ch != '\0')
  505.       {
  506.           switch (ch)
  507.             {
  508.               case '.':
  509.                 if (last) vms_name[j++] = '.';
  510.                 if (last) break;
  511.                 ch = file[i++];
  512.                 if (ch == '.')
  513.                   {
  514.                       if (!first) j--;  /* overwrite the dot */
  515.                       vms_name[j++] = '-';
  516.                   }
  517.                 else if (ch == '/'); /*  './' combinations-- do nothing */
  518.                 else if (ch == ']')
  519.                   {
  520.                       last = 1;
  521.                       if (vms_name[j-1] == '.') j--;
  522.                       vms_name[j++] = ']';
  523.                   }
  524.  
  525.                 else vms_name[j++] = '.';
  526.                 break;
  527.               case '/':
  528.                 if (first)
  529.                   {
  530.                       vms_name[j++] = '.';
  531.                   }
  532.                 else
  533.                   {
  534.                       first = 1;
  535.                       /* if '/' is first char or follows a colon do nothing */
  536.                       if ((i!=1) && (file[i-2] != ':'))
  537.                         {
  538.                             vms_name[j++] = '.';
  539.                         }
  540.                       else j--; /* overwrite the '.' following '[' */
  541.                   }
  542.                 break;
  543.               case ']':
  544.                 last = 1;
  545.                 if (vms_name[j-1] == '.') j--;
  546.                 vms_name[j++] = ']';
  547.                 break;
  548.               default:
  549.                 vms_name[j++] = ch;
  550.             }
  551.       }
  552.     return (vms_name);
  553. }
  554.  
  555. /* these two from emacs source */
  556. void define_logical_name (char *varname, char *string)
  557. {
  558.     struct dsc$descriptor_s strdsc =
  559.       {strlen (string), DSC$K_DTYPE_T, DSC$K_CLASS_S, string};
  560.     struct dsc$descriptor_s envdsc =
  561.       {strlen (varname), DSC$K_DTYPE_T, DSC$K_CLASS_S, varname};
  562.     struct dsc$descriptor_s lnmdsc =
  563.       {7, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$JOB"};
  564.  
  565.     LIB$SET_LOGICAL (&envdsc, &strdsc, &lnmdsc, 0, 0);
  566. }
  567.  
  568. void delete_logical_name (char *varname)
  569. {
  570.     struct dsc$descriptor_s envdsc =
  571.       {strlen (varname), DSC$K_DTYPE_T, DSC$K_CLASS_S, varname};
  572.     struct dsc$descriptor_s lnmdsc =
  573.       {7, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$JOB"};
  574.  
  575.     LIB$DELETE_LOGICAL (&envdsc, &lnmdsc);
  576. }
  577.  
  578. int do_attach_cmd()
  579. {
  580.    unsigned long pid;
  581.    char *pidstr;
  582.  
  583.    if((pidstr = getenv("JED_ATTACH_TO")) != NULL)
  584.      {
  585.     delete_logical_name("JED_ATTACH_TO");
  586.     (void) sscanf(pidstr,"%X",&pid);
  587.     if (lib$attach(&pid) == SS$_NORMAL)
  588.       return(1);
  589.         else
  590.           return(0);
  591.      }
  592.    else return(0);
  593. }
  594.  
  595. unsigned long SHELL_PID = 0;
  596.  
  597. /* here we try to attach to the parent otherwise just spawn a new one */
  598. void sys_suspend()
  599. {
  600.    unsigned long parent_pid;
  601.    unsigned long status = 0;
  602.    char str[80];
  603.  
  604.    cancel_ast ();
  605.    parent_pid = getppid();
  606.  
  607.    /* try to attach to different process */
  608.    if (do_attach_cmd()) status = SS$_NORMAL;
  609.  
  610.    else if (parent_pid && parent_pid != 0xffffffff)
  611.    /* we attach to parent */
  612.       status = lib$attach(&parent_pid);
  613.  
  614.    else if (SHELL_PID && SHELL_PID != 0xffffffff)
  615.    /* try to attach to previous shell */
  616.       status = lib$attach (&SHELL_PID);
  617.  
  618.    if (status != SS$_NORMAL)        /* others fail so spawn a new shell */
  619.      {
  620.     status = 0;
  621.     SLtt_write_string("Spawning Subprocess...\n");
  622.     flush_output ();
  623.     status = lib$spawn(0,0,0,0,0,&SHELL_PID,0);
  624.     /* if we attach back, status may come back unchanged */
  625.     if (!(status & 1))  /* Thanks to Hunter Goatley for this suggestion */
  626.       {              
  627.          sprintf(str,"Unable to spawn subprocess. Error = X%X (%d)", status, status);
  628.          msg_error(str);
  629.          return;
  630.       }
  631.      }
  632.  
  633. /*   if((file = getenv("JED_FILE_NAME")) != NULL)
  634.      {
  635.     find_file_cmd(file);
  636.     delete_logical_name ("JED_FILE_NAME");
  637.      } */
  638. }
  639.  
  640. /* returns 0 on success, -1 on syntax error
  641.    -2 on bad dir, -3 on bad device, 1 if something else */
  642. int vms_parse_file(char *old)
  643. {
  644.    struct FAB fab = cc$rms_fab;
  645.    struct NAM nam = cc$rms_nam;
  646.    char neew[256];
  647.    int status;
  648.  
  649.    fab.fab$l_fna = old;
  650.    fab.fab$b_fns = strlen(old);
  651.    fab.fab$b_dns = 0;
  652.    fab.fab$w_ifi = 0;
  653.    fab.fab$l_nam = &nam;
  654.    nam.nam$l_esa = neew;
  655.    nam.nam$b_ess = 255;
  656.    nam.nam$b_nop = NAM$V_SYNCHK;   /* syntax only */
  657.    nam.nam$l_rlf = 0;
  658.  
  659.    status = sys$parse(&fab);
  660.    neew[nam.nam$b_esl] = 0;
  661.    strcpy(old, neew);
  662.    while(*old != 0)
  663.      {
  664.     if ((*old == ';') && (*(old + 1) == 0)) *old = 0;
  665.     else
  666.       {
  667.          if ((*old >= 'A') && (*old <= 'Z')) *old |= 0x20;
  668.          old++;
  669.       }
  670.      }
  671.  
  672.    switch(status)
  673.      {
  674.     case RMS$_NORMAL: return 0;
  675.     case RMS$_SYN: return -1;
  676.     case RMS$_DNF: return -2;
  677.     case RMS$_DEV: return -3;
  678.     return 1;
  679.      }
  680. }
  681.  
  682. char *expand_filename(char *file)
  683. {
  684.    char *p, *p1, *dir;
  685.    static char work[300];
  686.    int len;
  687.  
  688.    strcpy(work, get_cwd()); strcat(work, file); file = work;
  689.  
  690.     /*  start at end and look for ']' then look for ':' */
  691.     if (0 == (len = strlen(file))) return(file);
  692.     p = file + (len - 1);
  693.     while (p >= file) if (*p-- == ':')
  694.       {
  695.      while((p >= file) && (*p != ':') && (*p != ']')) p--;
  696.      p++;
  697.      p1 = file;
  698.          while(*p) *p1++ = *p++;
  699.      *p1 = 0;
  700.      break;
  701.       }
  702.    
  703.    dir = p = p1 = file;
  704.    
  705.    /* look for the start of the path */
  706.    while (*p != 0)
  707.      {
  708.     if (*p == ':')
  709.       {
  710.          p++;
  711.          if (*p == ':')
  712.            {
  713.           p++;
  714.            }
  715.          dir = p;
  716.          break;
  717.       }
  718.     if (*p == '[') 
  719.       {
  720.          dir = p++;
  721.          break;
  722.       }
  723.     p++;
  724.      }
  725.    
  726.    p1 = p = dir;
  727.    
  728.     while (*p != 0)
  729.       {
  730.      if (*p == '[')
  731.        {
  732.           /* if (*(p + 1) == '-') */
  733.           p1 = dir;
  734.        }
  735.      
  736.      if (p1 != p) *p1 = *p;
  737.      
  738.      p1++;
  739.      p++;
  740.       }
  741.    *p1 = 0;
  742.    
  743.    switch(vms_parse_file(file))
  744.       {
  745.        case 0:
  746.      break;
  747.      case -1: msg_error("Filename syntax error."); break; 
  748.      case -2: msg_error("Directory does not exist!"); 
  749.               msg_error (file); break;
  750.      case -3: msg_error("Bad device name."); break;
  751.        default: msg_error ("Unknown directory error:");
  752.      msg_error (file);
  753.       }
  754.    
  755.    p = file;
  756.    while(*p != 0) if (*p++ == ']')
  757.      {
  758.      if ((*p == '.') && (*(p + 1) == 0)) *p = 0;
  759.          break;
  760.       }
  761.    return(file);
  762.  
  763. }
  764.  
  765. int vms_expand_filename(char *file,char *expanded_file)
  766. {
  767.     static int context = 0;
  768.     static char inputname[256] = "";
  769.     $DESCRIPTOR(file_desc,inputname);
  770.     $DESCRIPTOR(default_dsc,"SYS$DISK:[]*.*;");
  771.     static struct dsc$descriptor_s  result =
  772.         {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL};
  773.  
  774.     if (strcmp(inputname, file))
  775.       {
  776.       if (context)
  777.         {
  778.         lib$find_file_end(&context);
  779.         }
  780.       context = 0;
  781.       strcpy(inputname, file);
  782.       file_desc.dsc$w_length = strlen(inputname);
  783.       }
  784.  
  785.     if (RMS$_NORMAL == lib$find_file(&file_desc,&result,&context,
  786.                             &default_dsc,0,0,&Number_Zero))
  787.       {
  788.       MEMCPY(expanded_file, result.dsc$a_pointer, result.dsc$w_length);
  789.       expanded_file[result.dsc$w_length] = '\0';
  790.           return (1);
  791.       }
  792.     else
  793.       {
  794.           /* expanded_file[0] = '\0'; */      /* so file comes back as zero width */
  795.           return(0);
  796.       }
  797. }
  798.  
  799. static int context = 0;
  800.  
  801. static char inputname[256] = "";
  802. $DESCRIPTOR(file_desc,inputname);
  803. $DESCRIPTOR(default_dsc,"SYS$DISK:[]*.*;");
  804.  
  805. static char *VMS_Star;
  806.  
  807. int sys_findnext(char *file)
  808. {
  809.    char *f;
  810.    static struct dsc$descriptor_s  result = {0, DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL};
  811.  
  812.    if (RMS$_NORMAL == lib$find_file(&file_desc,&result,&context,
  813.                     &default_dsc,0,0,&Number_Zero))
  814.      {
  815.     MEMCPY(file, result.dsc$a_pointer, result.dsc$w_length);
  816.     file[result.dsc$w_length] = 0;
  817.     f = &file[result.dsc$w_length];
  818.     if (*VMS_Star)
  819.       {
  820.          while ((f > file) && (*f != ';')) f--;
  821.          if (*f == ';') *f = 0;
  822.       }
  823.  
  824.     return (1);
  825.      }
  826.    else return(0);
  827. }
  828.  
  829. int sys_findfirst(char *thefile)
  830. {
  831.    char *f, *f1, *s2 = "*.*";
  832.    char *file;
  833.  
  834.    file = thefile;
  835.    f = extract_file(file);
  836.  
  837.    while (*f && (*f != '*')) f++;
  838.  
  839.    VMS_Star = "";
  840.  
  841.    /* let user choose what to match with or take mine */
  842.    if (! *f)
  843.      {
  844.     f = extract_file(file);
  845.     while (*f && (*f != '.')) f++;
  846.     if (*f) VMS_Star = "*"; else VMS_Star = s2;
  847.  
  848.     file = expand_filename(thefile);
  849.     f = f1 = extract_file(file);
  850.     if (VMS_Star == s2)
  851.       {
  852.          while (*f && (*f != '.')) f++;
  853.          *f = 0;
  854.       }
  855.     else
  856.       {
  857.          while (*f1 && (*f1 != ';')) f1++;
  858.          *f1 = 0;
  859.       }
  860.     strcpy(thefile, file);
  861.      }
  862.  
  863.    strcpy(inputname, file);
  864.    if (*VMS_Star) strcat(inputname, VMS_Star);
  865.    file_desc.dsc$w_length = strlen(inputname);
  866.  
  867.    if (context) lib$find_file_end(&context);
  868.    context = 0;
  869.    return sys_findnext(thefile);
  870. }
  871.  
  872. #include <stat.h>
  873.  
  874. /* returns 0 if file does not exist, 1 if it is not a dir, 2 if it is */
  875. int sys_chmod(char *file, int what, int *mode, short *uid, short *gid)
  876. {
  877.    struct stat buf;
  878.    int m;
  879.  
  880.    if (stat(file, &buf) < 0) switch (errno)
  881.      {
  882.     case EACCES: return(-1); /* es = "Access denied."; break; */
  883.     case ENOENT: return(0);  /* ms = "File does not exist."; */
  884.     case ENOTDIR: return(-2); /* es = "Invalid Path."; */
  885.     default: return(-3); /* "stat: unknown error."; break;*/
  886.      }
  887.  
  888.    m = buf.st_mode;
  889.    (void) uid; (void) gid;
  890.  
  891.    *mode = m & 0777;
  892.  
  893.    if (m & S_IFDIR) return (2);
  894.    return(1);
  895. }
  896.  
  897. #if 0
  898.    switch(vms_parse_file(file))
  899.      {
  900.     case 0: break;
  901.     case -1:               /* Filename syntax error. */
  902.     case -2:   return(-2);           /* Directory does not exist! */
  903.     case 1:
  904.     case -3:   return(-3);           /* Bad device name. */
  905.       default: return(-3);
  906.      }
  907.    return(1);
  908.  
  909.  
  910. #endif
  911.  
  912. unsigned long sys_file_mod_time(char *file)
  913. {
  914.    struct stat buf;
  915.  
  916.    if (stat(file, &buf) < 0) return(0);
  917.    return((unsigned long) buf.st_mtime);
  918. }
  919.  
  920.  
  921. int rmdir (char *d)
  922. {
  923.    return(-1);
  924. }
  925.  
  926.  
  927.