Mercurial > vim
view src/os_vms.c @ 15621:bfbdef46aa7d v8.1.0818
patch 8.1.0818: MS-Windows: cannot send large data with ch_sendraw()
commit https://github.com/vim/vim/commit/240583869ae477202494dd01ef1e8e2bac650f10
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Jan 24 23:11:49 2019 +0100
patch 8.1.0818: MS-Windows: cannot send large data with ch_sendraw()
Problem: MS-Windows: cannot send large data with ch_sendraw().
Solution: Split write into several WriteFile() calls. (Yasuhiro Matsumoto,
closes #3823)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 24 Jan 2019 23:15:05 +0100 |
parents | 0d199e59a988 |
children | ef00b6bc186b |
line wrap: on
line source
/* vi:set ts=8 sts=4 sw=4 noet: * * 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" /* define _generic_64 for use in time functions */ #if !defined(VAX) && !defined(PROTO) # include <gen64def.h> #else /* based on Alpha's gen64def.h; the file is absent on VAX */ typedef struct _generic_64 { # pragma __nomember_alignment __union { /* You can treat me as... */ /* long long is not available on VAXen */ /* unsigned __int64 gen64$q_quadword; ...a single 64-bit value, or */ unsigned int gen64$l_longword [2]; /* ...two 32-bit values, or */ unsigned short int gen64$w_word [4]; /* ...four 16-bit values */ } gen64$r_quad_overlay; } GENERIC_64; #endif 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(void); static void set_tty(int row, int col); #define EXPL_ALLOC_INC 64 #define EQN(S1,S2,LN) (strncmp(S1,S2,LN) == 0) #define SKIP_FOLLOWING_SLASHES(Str) do { while (Str[1] == '/') ++Str; } while (0) /* * 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 *)alloc((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 string to lowercase - most often filename */ char * vms_tolower( char *name ) { int i,nlen = strlen(name); for (i = 0; i < nlen; i++) name[i] = TOLOWER_ASC(name[i]); return name; } /* * 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]; /* terminates on everything */ 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(); /* important: clean the inbuf */ memset(inbuf, 0, nbytes); /* set up the itemlist for the first read */ vul_item(&itmlst[0], 0, TRM$_MODIFIERS, (char *)( TRM$M_TM_NOECHO | TRM$M_TM_NOEDIT | TRM$M_TM_NOFILTR | TRM$M_TM_TRMNOECHO | TRM$M_TM_NORECALL) , 0); vul_item(&itmlst[1], sizeof(trm_mask), TRM$_TERM, (char *)&trm_mask, 0); /* wait forever for a char */ function = (IO$_READLBLK | IO$M_EXTEND); status = sys$qiow(0, iochan, function, &iosb, 0, 0, inbuf, nbytes-1, 0, 0, &itmlst, sizeof(itmlst)); len = strlen(inbuf); /* how many chars we got? */ /* read immediately the rest in the IO queue */ function = (IO$_READLBLK | IO$M_TIMED | IO$M_ESCAPE | IO$M_NOECHO | IO$M_NOFILTR); status = sys$qiow(0, iochan, function, &iosb, 0, 0, inbuf+len, nbytes-1-len, 0, 0, 0, 0); len = strlen(inbuf); /* return the total length */ 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 matches */ static int vms_wproc(char *name, int val) { int i; static int vms_match_alloced = 0; if (val == DECC$K_FOREIGN ) /* foreign non VMS files are not counting */ return 1; /* accept all DECC$K_FILE and DECC$K_DIRECTORY */ 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; } } /* make matches look uniform */ vms_remove_version(name); name=vms_tolower(name); /* 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 **)vim_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 lose 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, cnt = 0; char_u buf[MAXPATHL]; char *result; 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 */ result = decc$translate_vms(vms_fixfilename(buf)); if ( (int) result == 0 || (int) result == -1 ) { cnt = 0; } else { cnt = decc$to_vms(result, vms_wproc, 1 /*allow wild*/ , (flags & EW_DIR ? 0:1 ) /*allow directory*/) ; } 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; /* Skip files that are not executable if we check for that. */ if (!dir && (flags & EW_EXEC) && !mch_can_exe(vms_fmatch[i], NULL, !(flags & EW_SHELLCMD))) continue; /* allocate memory for pointers */ if (--files_free < 1) { files_alloced += EXPL_ALLOC_INC; *file = (char_u **)vim_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 *result; vms_match_num = 0; /* the result from the decc$translate_vms needs to be handled */ /* otherwise it might create ACCVIO error in decc$to_vms */ result = decc$translate_vms(vms_fixfilename(path)); if ( (int) result == 0 || (int) result == -1 ) { cnt = 0; } else { cnt = decc$to_vms(result, vms_wproc, 1 /*allow_wild*/, (flags & EW_DIR ? 0:1 ) /*allow directory*/); } 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; char *out_str=out; /* 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 *)vim_realloc(buf, buflen); else buf = (char *)alloc(buflen * sizeof(char)); } #ifdef DEBUG char *tmpbuf = NULL; tmpbuf = (char *)alloc(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 ; } struct typeahead_st { unsigned short numchars; unsigned char firstchar; unsigned char reserved0; unsigned long reserved1; } typeahead; /* * Wait "msec" msec until a character is available from file descriptor "fd". * "msec" == 0 will check for characters once. * "msec" == -1 will block until a character is available. */ int RealWaitForChar( int fd UNUSED, /* always read from iochan */ long msec, int *check_for_gpm UNUSED, int *interrupted) { int status; struct _generic_64 time_curr; struct _generic_64 time_diff; struct _generic_64 time_out; unsigned int convert_operation = LIB$K_DELTA_SECONDS_F; float sec =(float) msec/1000; /* make sure the iochan is set */ if (!iochan) get_tty(); if (sec > 0) { /* time-out specified; convert it to absolute time */ /* sec>0 requirement of lib$cvtf_to_internal_time()*/ /* get current time (number of 100ns ticks since the VMS Epoch) */ status = sys$gettim(&time_curr); if (status != SS$_NORMAL) return 0; /* error */ /* construct the delta time */ #if __G_FLOAT==0 # ifndef VAX /* IEEE is default on IA64, but can be used on Alpha too - but not on VAX */ status = lib$cvts_to_internal_time( &convert_operation, &sec, &time_diff); # endif #else /* default on Alpha and VAX */ status = lib$cvtf_to_internal_time( &convert_operation, &sec, &time_diff); #endif if (status != LIB$_NORMAL) return 0; /* error */ /* add them up */ status = lib$add_times( &time_curr, &time_diff, &time_out); if (status != LIB$_NORMAL) return 0; /* error */ } while (TRUE) { /* select() */ status = sys$qiow(0, iochan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, iosb, 0, 0, &typeahead, 8, 0, 0, 0, 0); if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL) return 0; /* error */ if (typeahead.numchars) return 1; /* ready to read */ /* there's nothing to read; what now? */ if (msec == 0) { /* immediate time-out; return impatiently */ return 0; } else if (msec < 0) { /* no time-out; wait on indefinitely */ return 1; /* fakeout to force a wait in vms_read() */ } else { /* time-out needs to be checked */ status = sys$gettim(&time_curr); if (status != SS$_NORMAL) return 0; /* error */ status = lib$sub_times( &time_out, &time_curr, &time_diff); if (status != LIB$_NORMAL) return 0; /* error, incl. time_diff < 0 (i.e. time-out) */ /* otherwise wait some more */ } } }