diff src/os_vms.c @ 7:3fc0f57ecb91 v7.0001

updated for version 7.0001
author vimboss
date Sun, 13 Jun 2004 20:20:40 +0000
parents
children ba378d4f9cf9
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/os_vms.c
@@ -0,0 +1,657 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved		by Bram Moolenaar
+ * VMS port			by Henk Elbers
+ * VMS deport			by Zoltan Arpadffy
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+#include	"vim.h"
+
+typedef struct
+{
+    char	class;
+    char	type;
+    short	width;
+    union
+    {
+	struct
+	{
+	    char	_basic[3];
+	    char	length;
+	}	y;
+	int	basic;
+    }	x;
+    int		extended;
+}	TT_MODE;
+
+typedef struct
+{
+    short	buflen;
+    short	itemcode;
+    char	*bufadrs;
+    int		*retlen;
+}	ITEM;
+
+typedef struct
+{
+    ITEM	equ;
+    int		nul;
+}	ITMLST1;
+
+typedef struct
+{
+    ITEM	index;
+    ITEM	string;
+    int	nul;
+}	ITMLST2;
+
+static TT_MODE	orgmode;
+static short	iochan;			/* TTY I/O channel */
+static short	iosb[4];		/* IO status block */
+
+static int vms_match_num = 0;
+static int vms_match_free = 0;
+static char_u **vms_fmatch = NULL;
+static char *Fspec_Rms;		       /* rms file spec, passed implicitly between routines */
+
+
+
+static TT_MODE	get_tty __ARGS((void));
+static void	set_tty __ARGS((int row, int col));
+
+#define EXPL_ALLOC_INC 64
+
+#define EQN(S1,S2,LN) (strncmp(S1,S2,LN) == 0)
+#define SKIP_FOLLOWING_SLASHES(Str) while (Str[1] == '/') ++Str
+
+
+/*
+ *	vul_desc	vult een descriptor met een string en de lengte
+ *			hier van.
+ */
+    static void
+vul_desc(DESC *des, char *str)
+{
+    des->dsc$b_dtype = DSC$K_DTYPE_T;
+    des->dsc$b_class = DSC$K_CLASS_S;
+    des->dsc$a_pointer = str;
+    des->dsc$w_length = str ? strlen(str) : 0;
+}
+
+/*
+ *	vul_item	vult een item met een aantal waarden
+ */
+    static void
+vul_item(ITEM *itm, short len, short cod, char *adr, int *ret)
+{
+    itm->buflen   = len;
+    itm->itemcode = cod;
+    itm->bufadrs  = adr;
+    itm->retlen   = ret;
+}
+
+    void
+mch_settmode(int tmode)
+{
+    int	status;
+
+    if ( tmode == TMODE_RAW )
+	set_tty(0, 0);
+    else{
+	switch (orgmode.width)
+	{
+	    case 132:	OUT_STR_NF((char_u *)"\033[?3h\033>");	break;
+	    case 80:	OUT_STR_NF((char_u *)"\033[?3l\033>");	break;
+	    default:	break;
+	}
+	out_flush();
+	status = sys$qiow(0, iochan, IO$_SETMODE, iosb, 0, 0,
+					  &orgmode, sizeof(TT_MODE), 0,0,0,0);
+	if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
+	    return;
+	(void)sys$dassgn(iochan);
+	iochan = 0;
+    }
+}
+
+    static void
+set_tty(int row, int col)
+{
+    int		    status;
+    TT_MODE	    newmode;		/* New TTY mode bits		*/
+    static short    first_time = TRUE;
+
+    if (first_time)
+    {
+	orgmode = get_tty();
+	first_time = FALSE;
+    }
+    newmode = get_tty();
+    if (col)
+	newmode.width		 = col;
+    if (row)
+	newmode.x.y.length       = row;
+    newmode.x.basic		|= (TT$M_NOECHO | TT$M_HOSTSYNC);
+    newmode.x.basic		&= ~TT$M_TTSYNC;
+    newmode.extended		|= TT2$M_PASTHRU;
+    status = sys$qiow(0, iochan, IO$_SETMODE, iosb, 0, 0,
+			  &newmode, sizeof(newmode), 0, 0, 0, 0);
+    if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
+	return;
+}
+
+    static TT_MODE
+get_tty(void)
+{
+
+    static $DESCRIPTOR(odsc,"SYS$OUTPUT");   /* output descriptor */
+
+    int		status;
+    TT_MODE	tt_mode;
+
+    if (!iochan)
+	status = sys$assign(&odsc,&iochan,0,0);
+
+    status = sys$qiow(0, iochan, IO$_SENSEMODE, iosb, 0, 0,
+		      &tt_mode, sizeof(tt_mode), 0, 0, 0, 0);
+    if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL)
+    {
+	tt_mode.width		= 0;
+	tt_mode.type		= 0;
+	tt_mode.class		= 0;
+	tt_mode.x.basic		= 0;
+	tt_mode.x.y.length	= 0;
+	tt_mode.extended	= 0;
+    }
+    return(tt_mode);
+}
+
+/*
+ * Get the current window size in Rows and Columns.
+ */
+    int
+mch_get_shellsize(void)
+{
+    TT_MODE	tmode;
+
+    tmode = get_tty();			/* get size from VMS	*/
+    Columns = tmode.width;
+    Rows = tmode.x.y.length;
+    return OK;
+}
+
+/*
+ * Try to set the window size to Rows and new_Columns.
+ */
+    void
+mch_set_shellsize(void)
+{
+    set_tty(Rows, Columns);
+    switch (Columns)
+    {
+	case 132:	OUT_STR_NF((char_u *)"\033[?3h\033>");	break;
+	case 80:	OUT_STR_NF((char_u *)"\033[?3l\033>");	break;
+	default:	break;
+    }
+    out_flush();
+    screen_start();
+}
+
+    char_u *
+mch_getenv(char_u *lognam)
+{
+    DESC		d_file_dev, d_lognam  ;
+    static char		buffer[LNM$C_NAMLENGTH+1];
+    char_u		*cp = NULL;
+    unsigned long	attrib;
+    int			lengte = 0, dum = 0, idx = 0;
+    ITMLST2		itmlst;
+    char		*sbuf = NULL;
+
+    vul_desc(&d_lognam, (char *)lognam);
+    vul_desc(&d_file_dev, "LNM$FILE_DEV");
+    attrib = LNM$M_CASE_BLIND;
+    vul_item(&itmlst.index, sizeof(int), LNM$_INDEX, (char *)&idx, &dum);
+    vul_item(&itmlst.string, LNM$C_NAMLENGTH, LNM$_STRING, buffer, &lengte);
+    itmlst.nul	= 0;
+    if (sys$trnlnm(&attrib, &d_file_dev, &d_lognam, NULL,&itmlst) == SS$_NORMAL)
+    {
+	buffer[lengte] = '\0';
+	if (cp = (char_u *)alloc((unsigned)(lengte+1)))
+	    strcpy((char *)cp, buffer);
+	return(cp);
+    }
+    else if ((sbuf = getenv((char *)lognam)))
+    {
+	lengte = strlen(sbuf) + 1;
+	cp = (char_u *)malloc((size_t)lengte);
+	if (cp)
+	    strcpy((char *)cp, sbuf);
+	return cp;
+    }
+    else
+	return(NULL);
+}
+
+/*
+ *	mch_setenv	VMS version of setenv()
+ */
+    int
+mch_setenv(char *var, char *value, int x)
+{
+    int		res, dum;
+    long	attrib = 0L;
+    char	acmode = PSL$C_SUPER;	/* needs SYSNAM privilege */
+    DESC	tabnam, lognam;
+    ITMLST1	itmlst;
+
+    vul_desc(&tabnam, "LNM$JOB");
+    vul_desc(&lognam, var);
+    vul_item(&itmlst.equ, value ? strlen(value) : 0, value ? LNM$_STRING : 0,
+	    value, &dum);
+    itmlst.nul	= 0;
+    res = sys$crelnm(&attrib, &tabnam, &lognam, &acmode, &itmlst);
+    return((res == 1) ? 0 : -1);
+}
+
+    int
+vms_sys(char *cmd, char *out, char *inp)
+{
+    DESC	cdsc, odsc, idsc;
+    long	status;
+
+    if (cmd)
+	vul_desc(&cdsc, cmd);
+    if (out)
+	vul_desc(&odsc, out);
+    if (inp)
+	vul_desc(&idsc, inp);
+
+    lib$spawn(cmd ? &cdsc : NULL,		/* command string */
+	      inp ? &idsc : NULL,		/* input file */
+	      out ? &odsc : NULL,		/* output file */
+	      0, 0, 0, &status, 0, 0, 0, 0, 0, 0);
+    return status;
+}
+
+/*
+ * Convert VMS system() or lib$spawn() return code to Unix-like exit value.
+ */
+    int
+vms_sys_status(int status)
+{
+    if (status != SS$_NORMAL && (status & STS$M_SUCCESS) == 0)
+	return status;		/* Command failed. */
+    return 0;
+}
+
+/*
+ * vms_read()
+ * function for low level char input
+ *
+ * Returns: input length
+ */
+    int
+vms_read(char *inbuf, size_t nbytes)
+{
+    int		status, function, len;
+    TT_MODE	tt_mode;
+    ITEM	itmlst[2];
+    static long trm_mask[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+    /* whatever happened earlier we need an iochan here */
+    if (!iochan)
+	tt_mode = get_tty();
+
+    vul_item(&itmlst[0], 0, TRM$_MODIFIERS,
+	    (char *)(TRM$M_TM_ESCAPE | TRM$M_TM_NOECHO | TRM$M_TM_NOEDIT |
+	       TRM$M_TM_NOFILTR | TRM$M_TM_NORECALL | TRM$M_TM_TRMNOECHO), 0);
+    vul_item(&itmlst[1], sizeof(trm_mask), TRM$_TERM, (char *)&trm_mask, 0);
+
+    function = (IO$_READLBLK | IO$M_EXTEND);
+    memset(inbuf, 0, nbytes);
+
+    while (1)
+    {
+	status = sys$qiow(0, iochan, function, &iosb, 0, 0, inbuf, nbytes - 1,
+					       0, 0, &itmlst, sizeof(itmlst));
+	len = strlen(inbuf);
+	if (len > 0)
+	    break;
+    }
+    return len;
+}
+
+/*
+ * vms_wproc() is called for each matching filename by decc$to_vms().
+ * We want to save each match for later retrieval.
+ *
+ * Returns:  1 - continue finding matches
+ *	     0 - stop trying to find any further mathces
+ */
+    static int
+vms_wproc(char *name, int val)
+{
+    int i;
+    int nlen;
+    static int vms_match_alloced = 0;
+
+    if (val != DECC$K_FILE) /* Directories and foreing non VMS files are not counting  */
+	return 1;
+
+    if (vms_match_num == 0) {
+	/* first time through, setup some things */
+	if (NULL == vms_fmatch) {
+	    vms_fmatch = (char_u **)alloc(EXPL_ALLOC_INC * sizeof(char *));
+	    if (!vms_fmatch)
+		return 0;
+	    vms_match_alloced = EXPL_ALLOC_INC;
+	    vms_match_free = EXPL_ALLOC_INC;
+	}
+	else {
+	    /* re-use existing space */
+	    vms_match_free = vms_match_alloced;
+	}
+    }
+
+    vms_remove_version(name);
+
+    /* convert filename to lowercase */
+    nlen = strlen(name);
+    for (i = 0; i < nlen; i++)
+	name[i] = TOLOWER_ASC(name[i]);
+
+    /* if name already exists, don't add it */
+    for (i = 0; i<vms_match_num; i++) {
+	if (0 == STRCMP((char_u *)name,vms_fmatch[i]))
+	    return 1;
+    }
+    if (--vms_match_free == 0) {
+	/* add more space to store matches */
+	vms_match_alloced += EXPL_ALLOC_INC;
+	vms_fmatch = (char_u **)realloc(vms_fmatch,
+		sizeof(char **) * vms_match_alloced);
+	if (!vms_fmatch)
+	    return 0;
+	vms_match_free = EXPL_ALLOC_INC;
+    }
+    vms_fmatch[vms_match_num] = vim_strsave((char_u *)name);
+
+    ++vms_match_num;
+    return 1;
+}
+
+/*
+ *	mch_expand_wildcards	this code does wild-card pattern
+ *				matching NOT using the shell
+ *
+ *	return OK for success, FAIL for error (you may loose some
+ *	memory) and put an error message in *file.
+ *
+ *	num_pat	   number of input patterns
+ *	pat	   array of pointers to input patterns
+ *	num_file   pointer to number of matched file names
+ *	file	   pointer to array of pointers to matched file names
+ *
+ */
+    int
+mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags)
+{
+    int		i, j = 0, cnt = 0;
+    char	*cp;
+    char_u	buf[MAXPATHL];
+    int		dir;
+    int files_alloced, files_free;
+
+    *num_file = 0;			/* default: no files found	*/
+    files_alloced = EXPL_ALLOC_INC;
+    files_free = EXPL_ALLOC_INC;
+    *file = (char_u **) alloc(sizeof(char_u **) * files_alloced);
+    if (*file == NULL)
+    {
+	*num_file = 0;
+	return FAIL;
+    }
+    for (i = 0; i < num_pat; i++)
+    {
+	/* expand environment var or home dir */
+	if (vim_strchr(pat[i],'$') || vim_strchr(pat[i],'~'))
+	    expand_env(pat[i],buf,MAXPATHL);
+	else
+	    STRCPY(buf,pat[i]);
+
+	vms_match_num = 0; /* reset collection counter */
+	cnt = decc$to_vms(decc$translate_vms(vms_fixfilename(buf)), vms_wproc, 1, 0);
+						      /* allow wild, no dir */
+	if (cnt > 0)
+	    cnt = vms_match_num;
+
+	if (cnt < 1)
+	    continue;
+
+	for (i = 0; i < cnt; i++)
+	{
+	    /* files should exist if expanding interactively */
+	    if (!(flags & EW_NOTFOUND) && mch_getperm(vms_fmatch[i]) < 0)
+		continue;
+	    /* do not include directories */
+	    dir = (mch_isdir(vms_fmatch[i]));
+	    if (( dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE)))
+		continue;
+	    /* allocate memory for pointers */
+	    if (--files_free < 1)
+	    {
+		files_alloced += EXPL_ALLOC_INC;
+		*file = (char_u **)realloc(*file,
+		    sizeof(char_u **) * files_alloced);
+		if (*file == NULL)
+		{
+		    *file = (char_u **)"";
+		    *num_file = 0;
+		    return(FAIL);
+		}
+		files_free = EXPL_ALLOC_INC;
+	    }
+
+	    (*file)[*num_file++] = vms_fmatch[i];
+	}
+    }
+    return OK;
+}
+
+    int
+mch_expandpath(garray_T *gap, char_u *path, int flags)
+{
+    int		i,cnt = 0;
+    char	*cp;
+    vms_match_num = 0;
+
+    cnt = decc$to_vms(decc$translate_vms(vms_fixfilename(path)), vms_wproc, 1, 0);
+						      /* allow wild, no dir */
+    if (cnt > 0)
+	cnt = vms_match_num;
+    for (i = 0; i < cnt; i++)
+    {
+	if (mch_getperm(vms_fmatch[i]) >= 0) /* add existing file */
+	    addfile(gap, vms_fmatch[i], flags);
+    }
+    return cnt;
+}
+
+/*
+ * attempt to translate a mixed unix-vms file specification to pure vms
+ */
+    static void
+vms_unix_mixed_filespec(char *in, char *out)
+{
+    char *lastcolon;
+    char *end_of_dir;
+    char ch;
+    int len;
+
+    /* copy vms filename portion up to last colon
+     * (node and/or disk)
+     */
+    lastcolon = strrchr(in, ':');   /* find last colon */
+    if (lastcolon != NULL) {
+	len = lastcolon - in + 1;
+	strncpy(out, in, len);
+	out += len;
+	in += len;
+    }
+
+    end_of_dir = NULL;	/* default: no directory */
+
+    /* start of directory portion */
+    ch = *in;
+    if ((ch == '[') || (ch == '/') || (ch == '<') ) {	/* start of directory(s) ? */
+	ch = '[';
+	SKIP_FOLLOWING_SLASHES(in);
+    } else if (EQN(in, "../", 3)) { /* Unix parent directory? */
+	*out++ = '[';
+	*out++ = '-';
+	end_of_dir = out;
+	ch = '.';
+	in += 2;
+	SKIP_FOLLOWING_SLASHES(in);
+    } else {		    /* not a special character */
+	while (EQN(in, "./", 2)) {	/* Ignore Unix "current dir" */
+	    in += 2;
+	    SKIP_FOLLOWING_SLASHES(in);
+    }
+    if (strchr(in, '/') == NULL) {  /* any more Unix directories ? */
+	strcpy(out, in);	/* No - get rest of the spec */
+	return;
+    } else {
+	*out++ = '[';	    /* Yes, denote a Vms subdirectory */
+	ch = '.';
+	--in;
+	}
+    }
+
+    /* if we get here, there is a directory part of the filename */
+
+    /* initialize output file spec */
+    *out++ = ch;
+    ++in;
+
+    while (*in != '\0') {
+	ch = *in;
+	if ((ch == ']') || (ch == '/') || (ch == '>') ) {	/* end of (sub)directory ? */
+	    end_of_dir = out;
+	    ch = '.';
+	    SKIP_FOLLOWING_SLASHES(in);
+	    }
+	else if (EQN(in, "../", 3)) {	/* Unix parent directory? */
+	    *out++ = '-';
+	    end_of_dir = out;
+	    ch = '.';
+	    in += 2;
+	    SKIP_FOLLOWING_SLASHES(in);
+	    }
+	else {
+	    while (EQN(in, "./", 2)) {  /* Ignore Unix "current dir" */
+	    end_of_dir = out;
+	    in += 2;
+	    SKIP_FOLLOWING_SLASHES(in);
+	    ch = *in;
+	    }
+	}
+
+    /* Place next character into output file spec */
+	*out++ = ch;
+	++in;
+    }
+
+    *out = '\0';    /* Terminate output file spec */
+
+    if (end_of_dir != NULL) /* Terminate directory portion */
+	*end_of_dir = ']';
+}
+
+
+/*
+ * for decc$to_vms in vms_fixfilename
+ */
+    static int
+vms_fspec_proc(char *fil, int val)
+{
+    strcpy(Fspec_Rms,fil);
+    return(1);
+}
+
+/*
+ * change unix and mixed filenames to VMS
+ */
+    void *
+vms_fixfilename(void *instring)
+{
+    static char		*buf = NULL;
+    static size_t	buflen = 0;
+    size_t		len;
+
+    /* get a big-enough buffer */
+    len = strlen(instring) + 1;
+    if (len > buflen)
+    {
+	buflen = len + 128;
+	if (buf)
+	    buf = (char *)realloc(buf, buflen);
+	else
+	    buf = (char *)calloc(buflen, sizeof(char));
+    }
+
+#ifdef DEBUG
+     char		 *tmpbuf = NULL;
+     tmpbuf = (char *)calloc(buflen, sizeof(char));
+     strcpy(tmpbuf, instring);
+#endif
+
+    Fspec_Rms = buf;				/* for decc$to_vms */
+
+    if ( strchr(instring,'/') == NULL )
+	/* It is already a VMS file spec */
+	strcpy(buf, instring);
+    else if ( strchr(instring,'"') == NULL ){     /* password in the path ?   */
+	/* Seems it is a regular file, let guess that it is pure Unix fspec */
+	if ( decc$to_vms(instring, vms_fspec_proc, 0, 0) <= 0 )
+	    /* No... it must be mixed */
+	    vms_unix_mixed_filespec(instring, buf);
+    }
+    else
+	/* we have a password in the path   */
+	/* decc$ functions can not handle   */
+	/* this is our only hope to resolv  */
+	vms_unix_mixed_filespec(instring, buf);
+
+    return buf;
+}
+/*
+ * Remove version number from file name
+ * we need it in some special cases as:
+ * creating swap file name and writing new file
+ */
+    void
+vms_remove_version(void * fname)
+{
+    char_u	*cp;
+    char_u	*fp;
+
+    if ((cp = vim_strchr( fname, ';')) != NULL) /* remove version */
+	*cp = '\0';
+    else if ((cp = vim_strrchr( fname, '.')) != NULL )
+    {
+	if      ((fp = vim_strrchr( fname, ']')) != NULL ) {;}
+	else if ((fp = vim_strrchr( fname, '>')) != NULL ) {;}
+	else fp = fname;
+
+	while ( *fp != '\0' && fp < cp )
+	    if ( *fp++ == '.' )
+		*cp = '\0';
+    }
+    return ;
+}