| /* 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 *)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 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; |
| int nlen; |
| static int vms_match_alloced = 0; |
| |
| if (val != DECC$K_FILE) /* Directories and foreign 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 **)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]; |
| 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; |
| |
| /* Skip files that are not executable if we check for that. */ |
| if (!dir && (flags & EW_EXEC) && !mch_can_exe(vms_fmatch[i])) |
| 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; |
| 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 *)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 ; |
| } |