fork for IVI
[profile/ivi/vim.git] / src / os_vms.c
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
14 typedef 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
31 typedef struct
32 {
33     short       buflen;
34     short       itemcode;
35     char        *bufadrs;
36     int         *retlen;
37 }       ITEM;
38
39 typedef struct
40 {
41     ITEM        equ;
42     int         nul;
43 }       ITMLST1;
44
45 typedef struct
46 {
47     ITEM        index;
48     ITEM        string;
49     int nul;
50 }       ITMLST2;
51
52 static TT_MODE  orgmode;
53 static short    iochan;                 /* TTY I/O channel */
54 static short    iosb[4];                /* IO status block */
55
56 static int vms_match_num = 0;
57 static int vms_match_free = 0;
58 static char_u **vms_fmatch = NULL;
59 static char *Fspec_Rms;                /* rms file spec, passed implicitly between routines */
60
61
62
63 static TT_MODE  get_tty __ARGS((void));
64 static 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
77 vul_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
89 vul_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
98 mch_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
122 set_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
148 get_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
177 mch_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
191 mch_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 *
205 mch_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
244 mch_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
262 vms_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
285 vms_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
299 vms_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
344 vms_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
410 mch_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
480 mch_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
501 vms_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
593 vms_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 *
603 vms_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
653 vms_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 }