1/* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * VMS port by Henk Elbers 5 * VMS deport by Zoltan Arpadffy 6 * 7 * Do ":help uganda" in Vim to read copying and usage conditions. 8 * Do ":help credits" in Vim to see a list of people who contributed. 9 * See README.txt for an overview of the Vim source code. 10 */ 11 12#include "vim.h" 13 14typedef struct 15{ 16 char class; 17 char type; 18 short width; 19 union 20 { 21 struct 22 { 23 char _basic[3]; 24 char length; 25 } y; 26 int basic; 27 } x; 28 int extended; 29} TT_MODE; 30 31typedef struct 32{ 33 short buflen; 34 short itemcode; 35 char *bufadrs; 36 int *retlen; 37} ITEM; 38 39typedef struct 40{ 41 ITEM equ; 42 int nul; 43} ITMLST1; 44 45typedef struct 46{ 47 ITEM index; 48 ITEM string; 49 int nul; 50} ITMLST2; 51 52static TT_MODE orgmode; 53static short iochan; /* TTY I/O channel */ 54static short iosb[4]; /* IO status block */ 55 56static int vms_match_num = 0; 57static int vms_match_free = 0; 58static char_u **vms_fmatch = NULL; 59static char *Fspec_Rms; /* rms file spec, passed implicitly between routines */ 60 61 62 63static TT_MODE get_tty __ARGS((void)); 64static void set_tty __ARGS((int row, int col)); 65 66#define EXPL_ALLOC_INC 64 67 68#define EQN(S1,S2,LN) (strncmp(S1,S2,LN) == 0) 69#define SKIP_FOLLOWING_SLASHES(Str) while (Str[1] == '/') ++Str 70 71 72/* 73 * vul_desc vult een descriptor met een string en de lengte 74 * hier van. 75 */ 76 static void 77vul_desc(DESC *des, char *str) 78{ 79 des->dsc$b_dtype = DSC$K_DTYPE_T; 80 des->dsc$b_class = DSC$K_CLASS_S; 81 des->dsc$a_pointer = str; 82 des->dsc$w_length = str ? strlen(str) : 0; 83} 84 85/* 86 * vul_item vult een item met een aantal waarden 87 */ 88 static void 89vul_item(ITEM *itm, short len, short cod, char *adr, int *ret) 90{ 91 itm->buflen = len; 92 itm->itemcode = cod; 93 itm->bufadrs = adr; 94 itm->retlen = ret; 95} 96 97 void 98mch_settmode(int tmode) 99{ 100 int status; 101 102 if ( tmode == TMODE_RAW ) 103 set_tty(0, 0); 104 else{ 105 switch (orgmode.width) 106 { 107 case 132: OUT_STR_NF((char_u *)"\033[?3h\033>"); break; 108 case 80: OUT_STR_NF((char_u *)"\033[?3l\033>"); break; 109 default: break; 110 } 111 out_flush(); 112 status = sys$qiow(0, iochan, IO$_SETMODE, iosb, 0, 0, 113 &orgmode, sizeof(TT_MODE), 0,0,0,0); 114 if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL) 115 return; 116 (void)sys$dassgn(iochan); 117 iochan = 0; 118 } 119} 120 121 static void 122set_tty(int row, int col) 123{ 124 int status; 125 TT_MODE newmode; /* New TTY mode bits */ 126 static short first_time = TRUE; 127 128 if (first_time) 129 { 130 orgmode = get_tty(); 131 first_time = FALSE; 132 } 133 newmode = get_tty(); 134 if (col) 135 newmode.width = col; 136 if (row) 137 newmode.x.y.length = row; 138 newmode.x.basic |= (TT$M_NOECHO | TT$M_HOSTSYNC); 139 newmode.x.basic &= ~TT$M_TTSYNC; 140 newmode.extended |= TT2$M_PASTHRU; 141 status = sys$qiow(0, iochan, IO$_SETMODE, iosb, 0, 0, 142 &newmode, sizeof(newmode), 0, 0, 0, 0); 143 if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL) 144 return; 145} 146 147 static TT_MODE 148get_tty(void) 149{ 150 151 static $DESCRIPTOR(odsc,"SYS$OUTPUT"); /* output descriptor */ 152 153 int status; 154 TT_MODE tt_mode; 155 156 if (!iochan) 157 status = sys$assign(&odsc,&iochan,0,0); 158 159 status = sys$qiow(0, iochan, IO$_SENSEMODE, iosb, 0, 0, 160 &tt_mode, sizeof(tt_mode), 0, 0, 0, 0); 161 if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL) 162 { 163 tt_mode.width = 0; 164 tt_mode.type = 0; 165 tt_mode.class = 0; 166 tt_mode.x.basic = 0; 167 tt_mode.x.y.length = 0; 168 tt_mode.extended = 0; 169 } 170 return(tt_mode); 171} 172 173/* 174 * Get the current window size in Rows and Columns. 175 */ 176 int 177mch_get_shellsize(void) 178{ 179 TT_MODE tmode; 180 181 tmode = get_tty(); /* get size from VMS */ 182 Columns = tmode.width; 183 Rows = tmode.x.y.length; 184 return OK; 185} 186 187/* 188 * Try to set the window size to Rows and new_Columns. 189 */ 190 void 191mch_set_shellsize(void) 192{ 193 set_tty(Rows, Columns); 194 switch (Columns) 195 { 196 case 132: OUT_STR_NF((char_u *)"\033[?3h\033>"); break; 197 case 80: OUT_STR_NF((char_u *)"\033[?3l\033>"); break; 198 default: break; 199 } 200 out_flush(); 201 screen_start(); 202} 203 204 char_u * 205mch_getenv(char_u *lognam) 206{ 207 DESC d_file_dev, d_lognam ; 208 static char buffer[LNM$C_NAMLENGTH+1]; 209 char_u *cp = NULL; 210 unsigned long attrib; 211 int lengte = 0, dum = 0, idx = 0; 212 ITMLST2 itmlst; 213 char *sbuf = NULL; 214 215 vul_desc(&d_lognam, (char *)lognam); 216 vul_desc(&d_file_dev, "LNM$FILE_DEV"); 217 attrib = LNM$M_CASE_BLIND; 218 vul_item(&itmlst.index, sizeof(int), LNM$_INDEX, (char *)&idx, &dum); 219 vul_item(&itmlst.string, LNM$C_NAMLENGTH, LNM$_STRING, buffer, &lengte); 220 itmlst.nul = 0; 221 if (sys$trnlnm(&attrib, &d_file_dev, &d_lognam, NULL,&itmlst) == SS$_NORMAL) 222 { 223 buffer[lengte] = '\0'; 224 if (cp = (char_u *)alloc((unsigned)(lengte+1))) 225 strcpy((char *)cp, buffer); 226 return(cp); 227 } 228 else if ((sbuf = getenv((char *)lognam))) 229 { 230 lengte = strlen(sbuf) + 1; 231 cp = (char_u *)alloc((size_t)lengte); 232 if (cp) 233 strcpy((char *)cp, sbuf); 234 return cp; 235 } 236 else 237 return(NULL); 238} 239 240/* 241 * mch_setenv VMS version of setenv() 242 */ 243 int 244mch_setenv(char *var, char *value, int x) 245{ 246 int res, dum; 247 long attrib = 0L; 248 char acmode = PSL$C_SUPER; /* needs SYSNAM privilege */ 249 DESC tabnam, lognam; 250 ITMLST1 itmlst; 251 252 vul_desc(&tabnam, "LNM$JOB"); 253 vul_desc(&lognam, var); 254 vul_item(&itmlst.equ, value ? strlen(value) : 0, value ? LNM$_STRING : 0, 255 value, &dum); 256 itmlst.nul = 0; 257 res = sys$crelnm(&attrib, &tabnam, &lognam, &acmode, &itmlst); 258 return((res == 1) ? 0 : -1); 259} 260 261 int 262vms_sys(char *cmd, char *out, char *inp) 263{ 264 DESC cdsc, odsc, idsc; 265 long status; 266 267 if (cmd) 268 vul_desc(&cdsc, cmd); 269 if (out) 270 vul_desc(&odsc, out); 271 if (inp) 272 vul_desc(&idsc, inp); 273 274 lib$spawn(cmd ? &cdsc : NULL, /* command string */ 275 inp ? &idsc : NULL, /* input file */ 276 out ? &odsc : NULL, /* output file */ 277 0, 0, 0, &status, 0, 0, 0, 0, 0, 0); 278 return status; 279} 280 281/* 282 * Convert VMS system() or lib$spawn() return code to Unix-like exit value. 283 */ 284 int 285vms_sys_status(int status) 286{ 287 if (status != SS$_NORMAL && (status & STS$M_SUCCESS) == 0) 288 return status; /* Command failed. */ 289 return 0; 290} 291 292/* 293 * vms_read() 294 * function for low level char input 295 * 296 * Returns: input length 297 */ 298 int 299vms_read(char *inbuf, size_t nbytes) 300{ 301 int status, function, len; 302 TT_MODE tt_mode; 303 ITEM itmlst[2]; /* terminates on everything */ 304 static long trm_mask[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; 305 306 /* whatever happened earlier we need an iochan here */ 307 if (!iochan) 308 tt_mode = get_tty(); 309 310 /* important: clean the inbuf */ 311 memset(inbuf, 0, nbytes); 312 313 /* set up the itemlist for the first read */ 314 vul_item(&itmlst[0], 0, TRM$_MODIFIERS, 315 (char *)( TRM$M_TM_NOECHO | TRM$M_TM_NOEDIT | 316 TRM$M_TM_NOFILTR | TRM$M_TM_TRMNOECHO | 317 TRM$M_TM_NORECALL) , 0); 318 vul_item(&itmlst[1], sizeof(trm_mask), TRM$_TERM, (char *)&trm_mask, 0); 319 320 /* wait forever for a char */ 321 function = (IO$_READLBLK | IO$M_EXTEND); 322 status = sys$qiow(0, iochan, function, &iosb, 0, 0, 323 inbuf, nbytes-1, 0, 0, &itmlst, sizeof(itmlst)); 324 len = strlen(inbuf); /* how many chars we got? */ 325 326 /* read immediately the rest in the IO queue */ 327 function = (IO$_READLBLK | IO$M_TIMED | IO$M_ESCAPE | IO$M_NOECHO | IO$M_NOFILTR); 328 status = sys$qiow(0, iochan, function, &iosb, 0, 0, 329 inbuf+len, nbytes-1-len, 0, 0, 0, 0); 330 331 len = strlen(inbuf); /* return the total length */ 332 333 return len; 334} 335 336/* 337 * vms_wproc() is called for each matching filename by decc$to_vms(). 338 * We want to save each match for later retrieval. 339 * 340 * Returns: 1 - continue finding matches 341 * 0 - stop trying to find any further matches 342 */ 343 static int 344vms_wproc(char *name, int val) 345{ 346 int i; 347 int nlen; 348 static int vms_match_alloced = 0; 349 350 if (val != DECC$K_FILE) /* Directories and foreign non VMS files are not 351 counting */ 352 return 1; 353 354 if (vms_match_num == 0) { 355 /* first time through, setup some things */ 356 if (NULL == vms_fmatch) { 357 vms_fmatch = (char_u **)alloc(EXPL_ALLOC_INC * sizeof(char *)); 358 if (!vms_fmatch) 359 return 0; 360 vms_match_alloced = EXPL_ALLOC_INC; 361 vms_match_free = EXPL_ALLOC_INC; 362 } 363 else { 364 /* re-use existing space */ 365 vms_match_free = vms_match_alloced; 366 } 367 } 368 369 vms_remove_version(name); 370 371 /* convert filename to lowercase */ 372 nlen = strlen(name); 373 for (i = 0; i < nlen; i++) 374 name[i] = TOLOWER_ASC(name[i]); 375 376 /* if name already exists, don't add it */ 377 for (i = 0; i<vms_match_num; i++) { 378 if (0 == STRCMP((char_u *)name,vms_fmatch[i])) 379 return 1; 380 } 381 if (--vms_match_free == 0) { 382 /* add more space to store matches */ 383 vms_match_alloced += EXPL_ALLOC_INC; 384 vms_fmatch = (char_u **)vim_realloc(vms_fmatch, 385 sizeof(char **) * vms_match_alloced); 386 if (!vms_fmatch) 387 return 0; 388 vms_match_free = EXPL_ALLOC_INC; 389 } 390 vms_fmatch[vms_match_num] = vim_strsave((char_u *)name); 391 392 ++vms_match_num; 393 return 1; 394} 395 396/* 397 * mch_expand_wildcards this code does wild-card pattern 398 * matching NOT using the shell 399 * 400 * return OK for success, FAIL for error (you may lose some 401 * memory) and put an error message in *file. 402 * 403 * num_pat number of input patterns 404 * pat array of pointers to input patterns 405 * num_file pointer to number of matched file names 406 * file pointer to array of pointers to matched file names 407 * 408 */ 409 int 410mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags) 411{ 412 int i, cnt = 0; 413 char_u buf[MAXPATHL]; 414 int dir; 415 int files_alloced, files_free; 416 417 *num_file = 0; /* default: no files found */ 418 files_alloced = EXPL_ALLOC_INC; 419 files_free = EXPL_ALLOC_INC; 420 *file = (char_u **) alloc(sizeof(char_u **) * files_alloced); 421 if (*file == NULL) 422 { 423 *num_file = 0; 424 return FAIL; 425 } 426 for (i = 0; i < num_pat; i++) 427 { 428 /* expand environment var or home dir */ 429 if (vim_strchr(pat[i],'$') || vim_strchr(pat[i],'~')) 430 expand_env(pat[i],buf,MAXPATHL); 431 else 432 STRCPY(buf,pat[i]); 433 434 vms_match_num = 0; /* reset collection counter */ 435 cnt = decc$to_vms(decc$translate_vms(vms_fixfilename(buf)), vms_wproc, 1, 0); 436 /* allow wild, no dir */ 437 if (cnt > 0) 438 cnt = vms_match_num; 439 440 if (cnt < 1) 441 continue; 442 443 for (i = 0; i < cnt; i++) 444 { 445 /* files should exist if expanding interactively */ 446 if (!(flags & EW_NOTFOUND) && mch_getperm(vms_fmatch[i]) < 0) 447 continue; 448 449 /* do not include directories */ 450 dir = (mch_isdir(vms_fmatch[i])); 451 if (( dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) 452 continue; 453 454 /* Skip files that are not executable if we check for that. */ 455 if (!dir && (flags & EW_EXEC) && !mch_can_exe(vms_fmatch[i])) 456 continue; 457 458 /* allocate memory for pointers */ 459 if (--files_free < 1) 460 { 461 files_alloced += EXPL_ALLOC_INC; 462 *file = (char_u **)vim_realloc(*file, 463 sizeof(char_u **) * files_alloced); 464 if (*file == NULL) 465 { 466 *file = (char_u **)""; 467 *num_file = 0; 468 return(FAIL); 469 } 470 files_free = EXPL_ALLOC_INC; 471 } 472 473 (*file)[*num_file++] = vms_fmatch[i]; 474 } 475 } 476 return OK; 477} 478 479 int 480mch_expandpath(garray_T *gap, char_u *path, int flags) 481{ 482 int i,cnt = 0; 483 vms_match_num = 0; 484 485 cnt = decc$to_vms(decc$translate_vms(vms_fixfilename(path)), vms_wproc, 1, 0); 486 /* allow wild, no dir */ 487 if (cnt > 0) 488 cnt = vms_match_num; 489 for (i = 0; i < cnt; i++) 490 { 491 if (mch_getperm(vms_fmatch[i]) >= 0) /* add existing file */ 492 addfile(gap, vms_fmatch[i], flags); 493 } 494 return cnt; 495} 496 497/* 498 * attempt to translate a mixed unix-vms file specification to pure vms 499 */ 500 static void 501vms_unix_mixed_filespec(char *in, char *out) 502{ 503 char *lastcolon; 504 char *end_of_dir; 505 char ch; 506 int len; 507 508 /* copy vms filename portion up to last colon 509 * (node and/or disk) 510 */ 511 lastcolon = strrchr(in, ':'); /* find last colon */ 512 if (lastcolon != NULL) { 513 len = lastcolon - in + 1; 514 strncpy(out, in, len); 515 out += len; 516 in += len; 517 } 518 519 end_of_dir = NULL; /* default: no directory */ 520 521 /* start of directory portion */ 522 ch = *in; 523 if ((ch == '[') || (ch == '/') || (ch == '<')) { /* start of directory(s) ? */ 524 ch = '['; 525 SKIP_FOLLOWING_SLASHES(in); 526 } else if (EQN(in, "../", 3)) { /* Unix parent directory? */ 527 *out++ = '['; 528 *out++ = '-'; 529 end_of_dir = out; 530 ch = '.'; 531 in += 2; 532 SKIP_FOLLOWING_SLASHES(in); 533 } else { /* not a special character */ 534 while (EQN(in, "./", 2)) { /* Ignore Unix "current dir" */ 535 in += 2; 536 SKIP_FOLLOWING_SLASHES(in); 537 } 538 if (strchr(in, '/') == NULL) { /* any more Unix directories ? */ 539 strcpy(out, in); /* No - get rest of the spec */ 540 return; 541 } else { 542 *out++ = '['; /* Yes, denote a Vms subdirectory */ 543 ch = '.'; 544 --in; 545 } 546 } 547 548 /* if we get here, there is a directory part of the filename */ 549 550 /* initialize output file spec */ 551 *out++ = ch; 552 ++in; 553 554 while (*in != '\0') { 555 ch = *in; 556 if ((ch == ']') || (ch == '/') || (ch == '>') ) { /* end of (sub)directory ? */ 557 end_of_dir = out; 558 ch = '.'; 559 SKIP_FOLLOWING_SLASHES(in); 560 } 561 else if (EQN(in, "../", 3)) { /* Unix parent directory? */ 562 *out++ = '-'; 563 end_of_dir = out; 564 ch = '.'; 565 in += 2; 566 SKIP_FOLLOWING_SLASHES(in); 567 } 568 else { 569 while (EQN(in, "./", 2)) { /* Ignore Unix "current dir" */ 570 end_of_dir = out; 571 in += 2; 572 SKIP_FOLLOWING_SLASHES(in); 573 ch = *in; 574 } 575 } 576 577 /* Place next character into output file spec */ 578 *out++ = ch; 579 ++in; 580 } 581 582 *out = '\0'; /* Terminate output file spec */ 583 584 if (end_of_dir != NULL) /* Terminate directory portion */ 585 *end_of_dir = ']'; 586} 587 588 589/* 590 * for decc$to_vms in vms_fixfilename 591 */ 592 static int 593vms_fspec_proc(char *fil, int val) 594{ 595 strcpy(Fspec_Rms,fil); 596 return(1); 597} 598 599/* 600 * change unix and mixed filenames to VMS 601 */ 602 void * 603vms_fixfilename(void *instring) 604{ 605 static char *buf = NULL; 606 static size_t buflen = 0; 607 size_t len; 608 609 /* get a big-enough buffer */ 610 len = strlen(instring) + 1; 611 if (len > buflen) 612 { 613 buflen = len + 128; 614 if (buf) 615 buf = (char *)vim_realloc(buf, buflen); 616 else 617 buf = (char *)alloc(buflen * sizeof(char)); 618 } 619 620#ifdef DEBUG 621 char *tmpbuf = NULL; 622 tmpbuf = (char *)alloc(buflen * sizeof(char)); 623 strcpy(tmpbuf, instring); 624#endif 625 626 Fspec_Rms = buf; /* for decc$to_vms */ 627 628 if (strchr(instring,'/') == NULL) 629 /* It is already a VMS file spec */ 630 strcpy(buf, instring); 631 else if (strchr(instring,'"') == NULL) /* password in the path? */ 632 { 633 /* Seems it is a regular file, let guess that it is pure Unix fspec */ 634 if (decc$to_vms(instring, vms_fspec_proc, 0, 0) <= 0) 635 /* No... it must be mixed */ 636 vms_unix_mixed_filespec(instring, buf); 637 } 638 else 639 /* we have a password in the path */ 640 /* decc$ functions can not handle */ 641 /* this is our only hope to resolv */ 642 vms_unix_mixed_filespec(instring, buf); 643 644 return buf; 645} 646 647/* 648 * Remove version number from file name 649 * we need it in some special cases as: 650 * creating swap file name and writing new file 651 */ 652 void 653vms_remove_version(void * fname) 654{ 655 char_u *cp; 656 char_u *fp; 657 658 if ((cp = vim_strchr( fname, ';')) != NULL) /* remove version */ 659 *cp = '\0'; 660 else if ((cp = vim_strrchr( fname, '.')) != NULL ) 661 { 662 if ((fp = vim_strrchr( fname, ']')) != NULL ) {;} 663 else if ((fp = vim_strrchr( fname, '>')) != NULL ) {;} 664 else fp = fname; 665 666 while ( *fp != '\0' && fp < cp ) 667 if ( *fp++ == '.' ) 668 *cp = '\0'; 669 } 670 return ; 671} 672