xref: /vim-8.2.3635/src/os_vms.c (revision 2bf24176)
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 /* define _generic_64 for use in time functions */
15 #if !defined(VAX) && !defined(PROTO)
16 #   include <gen64def.h>
17 #else
18 /* based on Alpha's gen64def.h; the file is absent on VAX */
19 typedef struct _generic_64 {
20 #   pragma __nomember_alignment
21     __union  {                          /* You can treat me as...  */
22 	/* long long is not available on VAXen */
23 	/* unsigned __int64 gen64$q_quadword; ...a single 64-bit value, or */
24 
25 	unsigned int gen64$l_longword [2]; /* ...two 32-bit values, or */
26 	unsigned short int gen64$w_word [4]; /* ...four 16-bit values */
27     } gen64$r_quad_overlay;
28 } GENERIC_64;
29 #endif
30 
31 typedef struct
32 {
33     char	class;
34     char	type;
35     short	width;
36     union
37     {
38 	struct
39 	{
40 	    char	_basic[3];
41 	    char	length;
42 	}	y;
43 	int	basic;
44     }	x;
45     int		extended;
46 }	TT_MODE;
47 
48 typedef struct
49 {
50     short	buflen;
51     short	itemcode;
52     char	*bufadrs;
53     int		*retlen;
54 }	ITEM;
55 
56 typedef struct
57 {
58     ITEM	equ;
59     int		nul;
60 }	ITMLST1;
61 
62 typedef struct
63 {
64     ITEM	index;
65     ITEM	string;
66     int	nul;
67 }	ITMLST2;
68 
69 static TT_MODE	orgmode;
70 static short	iochan;			/* TTY I/O channel */
71 static short	iosb[4];		/* IO status block */
72 
73 static int vms_match_num = 0;
74 static int vms_match_free = 0;
75 static char_u **vms_fmatch = NULL;
76 static char *Fspec_Rms;		       /* rms file spec, passed implicitly between routines */
77 
78 
79 
80 static TT_MODE	get_tty __ARGS((void));
81 static void	set_tty __ARGS((int row, int col));
82 
83 #define EXPL_ALLOC_INC 64
84 
85 #define EQN(S1,S2,LN) (strncmp(S1,S2,LN) == 0)
86 #define SKIP_FOLLOWING_SLASHES(Str) while (Str[1] == '/') ++Str
87 
88 
89 /*
90  *	vul_desc	vult een descriptor met een string en de lengte
91  *			hier van.
92  */
93     static void
94 vul_desc(DESC *des, char *str)
95 {
96     des->dsc$b_dtype = DSC$K_DTYPE_T;
97     des->dsc$b_class = DSC$K_CLASS_S;
98     des->dsc$a_pointer = str;
99     des->dsc$w_length = str ? strlen(str) : 0;
100 }
101 
102 /*
103  *	vul_item	vult een item met een aantal waarden
104  */
105     static void
106 vul_item(ITEM *itm, short len, short cod, char *adr, int *ret)
107 {
108     itm->buflen   = len;
109     itm->itemcode = cod;
110     itm->bufadrs  = adr;
111     itm->retlen   = ret;
112 }
113 
114     void
115 mch_settmode(int tmode)
116 {
117     int	status;
118 
119     if ( tmode == TMODE_RAW )
120 	set_tty(0, 0);
121     else{
122 	switch (orgmode.width)
123 	{
124 	    case 132:	OUT_STR_NF((char_u *)"\033[?3h\033>");	break;
125 	    case 80:	OUT_STR_NF((char_u *)"\033[?3l\033>");	break;
126 	    default:	break;
127 	}
128 	out_flush();
129 	status = sys$qiow(0, iochan, IO$_SETMODE, iosb, 0, 0,
130 					  &orgmode, sizeof(TT_MODE), 0,0,0,0);
131 	if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
132 	    return;
133 	(void)sys$dassgn(iochan);
134 	iochan = 0;
135     }
136 }
137 
138     static void
139 set_tty(int row, int col)
140 {
141     int		    status;
142     TT_MODE	    newmode;		/* New TTY mode bits		*/
143     static short    first_time = TRUE;
144 
145     if (first_time)
146     {
147 	orgmode = get_tty();
148 	first_time = FALSE;
149     }
150     newmode = get_tty();
151     if (col)
152 	newmode.width		 = col;
153     if (row)
154 	newmode.x.y.length       = row;
155     newmode.x.basic		|= (TT$M_NOECHO | TT$M_HOSTSYNC);
156     newmode.x.basic		&= ~TT$M_TTSYNC;
157     newmode.extended		|= TT2$M_PASTHRU;
158     status = sys$qiow(0, iochan, IO$_SETMODE, iosb, 0, 0,
159 			  &newmode, sizeof(newmode), 0, 0, 0, 0);
160     if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
161 	return;
162 }
163 
164     static TT_MODE
165 get_tty(void)
166 {
167 
168     static $DESCRIPTOR(odsc,"SYS$OUTPUT");   /* output descriptor */
169 
170     int		status;
171     TT_MODE	tt_mode;
172 
173     if (!iochan)
174 	status = sys$assign(&odsc,&iochan,0,0);
175 
176     status = sys$qiow(0, iochan, IO$_SENSEMODE, iosb, 0, 0,
177 		      &tt_mode, sizeof(tt_mode), 0, 0, 0, 0);
178     if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL)
179     {
180 	tt_mode.width		= 0;
181 	tt_mode.type		= 0;
182 	tt_mode.class		= 0;
183 	tt_mode.x.basic		= 0;
184 	tt_mode.x.y.length	= 0;
185 	tt_mode.extended	= 0;
186     }
187     return(tt_mode);
188 }
189 
190 /*
191  * Get the current window size in Rows and Columns.
192  */
193     int
194 mch_get_shellsize(void)
195 {
196     TT_MODE	tmode;
197 
198     tmode = get_tty();			/* get size from VMS	*/
199     Columns = tmode.width;
200     Rows = tmode.x.y.length;
201     return OK;
202 }
203 
204 /*
205  * Try to set the window size to Rows and new_Columns.
206  */
207     void
208 mch_set_shellsize(void)
209 {
210     set_tty(Rows, Columns);
211     switch (Columns)
212     {
213 	case 132:	OUT_STR_NF((char_u *)"\033[?3h\033>");	break;
214 	case 80:	OUT_STR_NF((char_u *)"\033[?3l\033>");	break;
215 	default:	break;
216     }
217     out_flush();
218     screen_start();
219 }
220 
221     char_u *
222 mch_getenv(char_u *lognam)
223 {
224     DESC		d_file_dev, d_lognam  ;
225     static char		buffer[LNM$C_NAMLENGTH+1];
226     char_u		*cp = NULL;
227     unsigned long	attrib;
228     int			lengte = 0, dum = 0, idx = 0;
229     ITMLST2		itmlst;
230     char		*sbuf = NULL;
231 
232     vul_desc(&d_lognam, (char *)lognam);
233     vul_desc(&d_file_dev, "LNM$FILE_DEV");
234     attrib = LNM$M_CASE_BLIND;
235     vul_item(&itmlst.index, sizeof(int), LNM$_INDEX, (char *)&idx, &dum);
236     vul_item(&itmlst.string, LNM$C_NAMLENGTH, LNM$_STRING, buffer, &lengte);
237     itmlst.nul	= 0;
238     if (sys$trnlnm(&attrib, &d_file_dev, &d_lognam, NULL,&itmlst) == SS$_NORMAL)
239     {
240 	buffer[lengte] = '\0';
241 	if (cp = (char_u *)alloc((unsigned)(lengte+1)))
242 	    strcpy((char *)cp, buffer);
243 	return(cp);
244     }
245     else if ((sbuf = getenv((char *)lognam)))
246     {
247 	lengte = strlen(sbuf) + 1;
248 	cp = (char_u *)alloc((size_t)lengte);
249 	if (cp)
250 	    strcpy((char *)cp, sbuf);
251 	return cp;
252     }
253     else
254 	return(NULL);
255 }
256 
257 /*
258  *	mch_setenv	VMS version of setenv()
259  */
260     int
261 mch_setenv(char *var, char *value, int x)
262 {
263     int		res, dum;
264     long	attrib = 0L;
265     char	acmode = PSL$C_SUPER;	/* needs SYSNAM privilege */
266     DESC	tabnam, lognam;
267     ITMLST1	itmlst;
268 
269     vul_desc(&tabnam, "LNM$JOB");
270     vul_desc(&lognam, var);
271     vul_item(&itmlst.equ, value ? strlen(value) : 0, value ? LNM$_STRING : 0,
272 	    value, &dum);
273     itmlst.nul	= 0;
274     res = sys$crelnm(&attrib, &tabnam, &lognam, &acmode, &itmlst);
275     return((res == 1) ? 0 : -1);
276 }
277 
278     int
279 vms_sys(char *cmd, char *out, char *inp)
280 {
281     DESC	cdsc, odsc, idsc;
282     long	status;
283 
284     if (cmd)
285 	vul_desc(&cdsc, cmd);
286     if (out)
287 	vul_desc(&odsc, out);
288     if (inp)
289 	vul_desc(&idsc, inp);
290 
291     lib$spawn(cmd ? &cdsc : NULL,		/* command string */
292 	      inp ? &idsc : NULL,		/* input file */
293 	      out ? &odsc : NULL,		/* output file */
294 	      0, 0, 0, &status, 0, 0, 0, 0, 0, 0);
295     return status;
296 }
297 
298 /*
299  * Convert string to lowercase - most often filename
300  */
301     char *
302 vms_tolower( char *name )
303 {
304     int i,nlen = strlen(name);
305     for (i = 0; i < nlen; i++)
306 	name[i] = TOLOWER_ASC(name[i]);
307     return name;
308 }
309 
310 /*
311  * Convert VMS system() or lib$spawn() return code to Unix-like exit value.
312  */
313     int
314 vms_sys_status(int status)
315 {
316     if (status != SS$_NORMAL && (status & STS$M_SUCCESS) == 0)
317 	return status;		/* Command failed. */
318     return 0;
319 }
320 
321 /*
322  * vms_read()
323  * function for low level char input
324  *
325  * Returns: input length
326  */
327     int
328 vms_read(char *inbuf, size_t nbytes)
329 {
330     int		status, function, len;
331     TT_MODE	tt_mode;
332     ITEM	itmlst[2];     /* terminates on everything */
333     static long trm_mask[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
334 
335     /* whatever happened earlier we need an iochan here */
336     if (!iochan)
337 	tt_mode = get_tty();
338 
339     /* important: clean the inbuf */
340     memset(inbuf, 0, nbytes);
341 
342     /* set up the itemlist for the first read */
343     vul_item(&itmlst[0], 0, TRM$_MODIFIERS,
344 	 (char *)( TRM$M_TM_NOECHO  | TRM$M_TM_NOEDIT	 |
345 		   TRM$M_TM_NOFILTR | TRM$M_TM_TRMNOECHO |
346 		   TRM$M_TM_NORECALL) , 0);
347     vul_item(&itmlst[1], sizeof(trm_mask), TRM$_TERM, (char *)&trm_mask, 0);
348 
349     /* wait forever for a char */
350     function = (IO$_READLBLK | IO$M_EXTEND);
351     status = sys$qiow(0, iochan, function, &iosb, 0, 0,
352 			 inbuf, nbytes-1, 0, 0, &itmlst, sizeof(itmlst));
353     len = strlen(inbuf); /* how many chars we got? */
354 
355     /* read immediately the rest in the IO queue   */
356     function = (IO$_READLBLK | IO$M_TIMED | IO$M_ESCAPE | IO$M_NOECHO | IO$M_NOFILTR);
357     status = sys$qiow(0, iochan, function, &iosb, 0, 0,
358 			 inbuf+len, nbytes-1-len, 0, 0, 0, 0);
359 
360     len = strlen(inbuf); /* return the total length */
361 
362     return len;
363 }
364 
365 /*
366  * vms_wproc() is called for each matching filename by decc$to_vms().
367  * We want to save each match for later retrieval.
368  *
369  * Returns:  1 - continue finding matches
370  *	     0 - stop trying to find any further matches
371  */
372     static int
373 vms_wproc(char *name, int val)
374 {
375     int i;
376     static int vms_match_alloced = 0;
377 
378     if (val == DECC$K_FOREIGN ) /* foreign non VMS files are not counting */
379 	return 1;
380 
381     /* accept all DECC$K_FILE and DECC$K_DIRECTORY */
382     if (vms_match_num == 0) {
383 	/* first time through, setup some things */
384 	if (NULL == vms_fmatch) {
385 	    vms_fmatch = (char_u **)alloc(EXPL_ALLOC_INC * sizeof(char *));
386 	    if (!vms_fmatch)
387 		return 0;
388 	    vms_match_alloced = EXPL_ALLOC_INC;
389 	    vms_match_free = EXPL_ALLOC_INC;
390 	}
391 	else {
392 	    /* re-use existing space */
393 	    vms_match_free = vms_match_alloced;
394 	}
395     }
396 
397     /* make matches look uniform */
398     vms_remove_version(name);
399     name=vms_tolower(name);
400 
401     /* if name already exists, don't add it */
402     for (i = 0; i<vms_match_num; i++) {
403 	if (0 == STRCMP((char_u *)name,vms_fmatch[i]))
404 	    return 1;
405     }
406     if (--vms_match_free == 0) {
407 	/* add more space to store matches */
408 	vms_match_alloced += EXPL_ALLOC_INC;
409 	vms_fmatch = (char_u **)vim_realloc(vms_fmatch,
410 		sizeof(char **) * vms_match_alloced);
411 	if (!vms_fmatch)
412 	    return 0;
413 	vms_match_free = EXPL_ALLOC_INC;
414     }
415     vms_fmatch[vms_match_num] = vim_strsave((char_u *)name);
416 
417     ++vms_match_num;
418     return 1;
419 }
420 
421 /*
422  *	mch_expand_wildcards	this code does wild-card pattern
423  *				matching NOT using the shell
424  *
425  *	return OK for success, FAIL for error (you may lose some
426  *	memory) and put an error message in *file.
427  *
428  *	num_pat	   number of input patterns
429  *	pat	   array of pointers to input patterns
430  *	num_file   pointer to number of matched file names
431  *	file	   pointer to array of pointers to matched file names
432  *
433  */
434     int
435 mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags)
436 {
437     int		i, cnt = 0;
438     char_u	buf[MAXPATHL];
439     char       *result;
440     int		dir;
441     int files_alloced, files_free;
442 
443     *num_file = 0;			/* default: no files found	*/
444     files_alloced = EXPL_ALLOC_INC;
445     files_free = EXPL_ALLOC_INC;
446     *file = (char_u **) alloc(sizeof(char_u **) * files_alloced);
447     if (*file == NULL)
448     {
449 	*num_file = 0;
450 	return FAIL;
451     }
452     for (i = 0; i < num_pat; i++)
453     {
454 	/* expand environment var or home dir */
455 	if (vim_strchr(pat[i],'$') || vim_strchr(pat[i],'~'))
456 	    expand_env(pat[i],buf,MAXPATHL);
457 	else
458 	    STRCPY(buf,pat[i]);
459 
460 	vms_match_num = 0; /* reset collection counter */
461 	result = decc$translate_vms(vms_fixfilename(buf));
462 	if ( (int) result == 0 || (int) result == -1  ) {
463 	    cnt = 0;
464 	}
465         else {
466 	    cnt = decc$to_vms(result, vms_wproc, 1 /*allow wild*/ , (flags & EW_DIR ? 0:1 ) /*allow directory*/) ;
467 	}
468 	if (cnt > 0)
469 	    cnt = vms_match_num;
470 
471 	if (cnt < 1)
472 	    continue;
473 
474 	for (i = 0; i < cnt; i++)
475 	{
476 	    /* files should exist if expanding interactively */
477 	    if (!(flags & EW_NOTFOUND) && mch_getperm(vms_fmatch[i]) < 0)
478 		continue;
479 
480 	    /* do not include directories */
481 	    dir = (mch_isdir(vms_fmatch[i]));
482 	    if (( dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE)))
483 		continue;
484 
485 	    /* Skip files that are not executable if we check for that. */
486 	    if (!dir && (flags & EW_EXEC)
487 		 && !mch_can_exe(vms_fmatch[i], NULL, !(flags & EW_SHELLCMD)))
488 		continue;
489 
490 	    /* allocate memory for pointers */
491 	    if (--files_free < 1)
492 	    {
493 		files_alloced += EXPL_ALLOC_INC;
494 		*file = (char_u **)vim_realloc(*file,
495 		    sizeof(char_u **) * files_alloced);
496 		if (*file == NULL)
497 		{
498 		    *file = (char_u **)"";
499 		    *num_file = 0;
500 		    return(FAIL);
501 		}
502 		files_free = EXPL_ALLOC_INC;
503 	    }
504 
505 	    (*file)[*num_file++] = vms_fmatch[i];
506 	}
507     }
508     return OK;
509 }
510 
511     int
512 mch_expandpath(garray_T *gap, char_u *path, int flags)
513 {
514     int		i,cnt = 0;
515     char       *result;
516 
517     vms_match_num = 0;
518     /* the result from the decc$translate_vms needs to be handled */
519     /* otherwise it might create ACCVIO error in decc$to_vms      */
520     result = decc$translate_vms(vms_fixfilename(path));
521     if ( (int) result == 0 || (int) result == -1  ) {
522         cnt = 0;
523     }
524     else {
525         cnt = decc$to_vms(result, vms_wproc, 1 /*allow_wild*/, (flags & EW_DIR ? 0:1 ) /*allow directory*/);
526     }
527     if (cnt > 0)
528 	cnt = vms_match_num;
529     for (i = 0; i < cnt; i++)
530     {
531 	if (mch_getperm(vms_fmatch[i]) >= 0) /* add existing file */
532 	    addfile(gap, vms_fmatch[i], flags);
533     }
534     return cnt;
535 }
536 
537 /*
538  * attempt to translate a mixed unix-vms file specification to pure vms
539  */
540     static void
541 vms_unix_mixed_filespec(char *in, char *out)
542 {
543     char *lastcolon;
544     char *end_of_dir;
545     char ch;
546     int len;
547     char *out_str=out;
548 
549     /* copy vms filename portion up to last colon
550      * (node and/or disk)
551      */
552     lastcolon = strrchr(in, ':');   /* find last colon */
553     if (lastcolon != NULL) {
554 	len = lastcolon - in + 1;
555 	strncpy(out, in, len);
556 	out += len;
557 	in += len;
558     }
559 
560     end_of_dir = NULL;	/* default: no directory */
561 
562     /* start of directory portion */
563     ch = *in;
564     if ((ch == '[') || (ch == '/') || (ch == '<')) {	/* start of directory(s) ? */
565 	ch = '[';
566 	SKIP_FOLLOWING_SLASHES(in);
567     } else if (EQN(in, "../", 3)) { /* Unix parent directory? */
568 	*out++ = '[';
569 	*out++ = '-';
570 	end_of_dir = out;
571 	ch = '.';
572 	in += 2;
573 	SKIP_FOLLOWING_SLASHES(in);
574     } else {		    /* not a special character */
575 	while (EQN(in, "./", 2)) {	/* Ignore Unix "current dir" */
576 	    in += 2;
577 	    SKIP_FOLLOWING_SLASHES(in);
578     }
579     if (strchr(in, '/') == NULL) {  /* any more Unix directories ? */
580 	strcpy(out, in);	/* No - get rest of the spec */
581 	return;
582     } else {
583 	*out++ = '[';	    /* Yes, denote a Vms subdirectory */
584 	ch = '.';
585 	--in;
586 	}
587     }
588 
589     /* if we get here, there is a directory part of the filename */
590 
591     /* initialize output file spec */
592     *out++ = ch;
593     ++in;
594 
595     while (*in != '\0') {
596 	ch = *in;
597 	if ((ch == ']') || (ch == '/') || (ch == '>') ) {	/* end of (sub)directory ? */
598 	    end_of_dir = out;
599 	    ch = '.';
600 	    SKIP_FOLLOWING_SLASHES(in);
601 	    }
602 	else if (EQN(in, "../", 3)) {	/* Unix parent directory? */
603 	    *out++ = '-';
604 	    end_of_dir = out;
605 	    ch = '.';
606 	    in += 2;
607 	    SKIP_FOLLOWING_SLASHES(in);
608 	    }
609 	else {
610 	    while (EQN(in, "./", 2)) {  /* Ignore Unix "current dir" */
611 	    end_of_dir = out;
612 	    in += 2;
613 	    SKIP_FOLLOWING_SLASHES(in);
614 	    ch = *in;
615 	    }
616 	}
617 
618     /* Place next character into output file spec */
619 	*out++ = ch;
620 	++in;
621     }
622 
623     *out = '\0';    /* Terminate output file spec */
624 
625     if (end_of_dir != NULL) /* Terminate directory portion */
626 	*end_of_dir = ']';
627 }
628 
629 /*
630  * for decc$to_vms in vms_fixfilename
631  */
632     static int
633 vms_fspec_proc(char *fil, int val)
634 {
635     strcpy(Fspec_Rms,fil);
636     return(1);
637 }
638 
639 /*
640  * change unix and mixed filenames to VMS
641  */
642     void *
643 vms_fixfilename(void *instring)
644 {
645     static char		*buf = NULL;
646     static size_t	buflen = 0;
647     size_t		len;
648 
649     /* get a big-enough buffer */
650     len = strlen(instring) + 1;
651     if (len > buflen)
652     {
653 	buflen = len + 128;
654 	if (buf)
655 	    buf = (char *)vim_realloc(buf, buflen);
656 	else
657 	    buf = (char *)alloc(buflen * sizeof(char));
658     }
659 
660 #ifdef DEBUG
661      char		 *tmpbuf = NULL;
662      tmpbuf = (char *)alloc(buflen * sizeof(char));
663      strcpy(tmpbuf, instring);
664 #endif
665 
666     Fspec_Rms = buf;				/* for decc$to_vms */
667 
668     if (strchr(instring,'/') == NULL)
669 	/* It is already a VMS file spec */
670 	strcpy(buf, instring);
671     else if (strchr(instring,'"') == NULL)	/* password in the path? */
672     {
673 	/* Seems it is a regular file, let guess that it is pure Unix fspec */
674 	if (decc$to_vms(instring, vms_fspec_proc, 0, 0) <= 0)
675 	    /* No... it must be mixed */
676 	    vms_unix_mixed_filespec(instring, buf);
677     }
678     else
679 	/* we have a password in the path   */
680 	/* decc$ functions can not handle   */
681 	/* this is our only hope to resolv  */
682 	vms_unix_mixed_filespec(instring, buf);
683 
684     return buf;
685 }
686 
687 /*
688  * Remove version number from file name
689  * we need it in some special cases as:
690  * creating swap file name and writing new file
691  */
692     void
693 vms_remove_version(void * fname)
694 {
695     char_u	*cp;
696     char_u	*fp;
697 
698     if ((cp = vim_strchr( fname, ';')) != NULL) /* remove version */
699 	*cp = '\0';
700     else if ((cp = vim_strrchr( fname, '.')) != NULL )
701     {
702 	if      ((fp = vim_strrchr( fname, ']')) != NULL ) {;}
703 	else if ((fp = vim_strrchr( fname, '>')) != NULL ) {;}
704 	else fp = fname;
705 
706 	while ( *fp != '\0' && fp < cp )
707 	    if ( *fp++ == '.' )
708 		*cp = '\0';
709     }
710     return ;
711 }
712 
713 struct typeahead_st {
714     unsigned short numchars;
715     unsigned char  firstchar;
716     unsigned char  reserved0;
717     unsigned long  reserved1;
718 } typeahead;
719 
720 /*
721  * Wait "msec" msec until a character is available from file descriptor "fd".
722  * "msec" == 0 will check for characters once.
723  * "msec" == -1 will block until a character is available.
724  */
725     int
726 RealWaitForChar(fd, msec, check_for_gpm)
727     int		fd UNUSED; /* always read from iochan */
728     long	msec;
729     int		*check_for_gpm UNUSED;
730 {
731     int status;
732     struct _generic_64 time_curr;
733     struct _generic_64 time_diff;
734     struct _generic_64 time_out;
735     unsigned int convert_operation = LIB$K_DELTA_SECONDS_F;
736     float sec =(float) msec/1000;
737 
738     /* make sure the iochan is set */
739     if (!iochan)
740 	get_tty();
741 
742     if (sec > 0) {
743         /* time-out specified; convert it to absolute time */
744 	/* sec>0 requirement of lib$cvtf_to_internal_time()*/
745 
746         /* get current time (number of 100ns ticks since the VMS Epoch) */
747         status = sys$gettim(&time_curr);
748         if (status != SS$_NORMAL)
749             return 0; /* error */
750         /* construct the delta time */
751 #if __G_FLOAT==0
752 # ifndef VAX
753 	/* IEEE is default on IA64, but can be used on Alpha too - but not on VAX */
754         status = lib$cvts_to_internal_time(
755                 &convert_operation, &sec, &time_diff);
756 # endif
757 #else   /* default on Alpha and VAX  */
758         status = lib$cvtf_to_internal_time(
759 		&convert_operation, &sec, &time_diff);
760 #endif
761         if (status != LIB$_NORMAL)
762             return 0; /* error */
763         /* add them up */
764         status = lib$add_times(
765                 &time_curr,
766                 &time_diff,
767                 &time_out);
768         if (status != LIB$_NORMAL)
769             return 0; /* error */
770     }
771 
772     while (TRUE) {
773         /* select() */
774         status = sys$qiow(0, iochan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, iosb,
775                 0, 0, &typeahead, 8, 0, 0, 0, 0);
776 	if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL)
777             return 0; /* error */
778 
779         if (typeahead.numchars)
780             return 1; /* ready to read */
781 
782         /* there's nothing to read; what now? */
783         if (msec == 0) {
784             /* immediate time-out; return impatiently */
785             return 0;
786         }
787         else if (msec < 0) {
788             /* no time-out; wait on indefinitely */
789             continue;
790         }
791         else {
792             /* time-out needs to be checked */
793             status = sys$gettim(&time_curr);
794             if (status != SS$_NORMAL)
795                 return 0; /* error */
796 
797             status = lib$sub_times(
798                     &time_out,
799                     &time_curr,
800                     &time_diff);
801             if (status != LIB$_NORMAL)
802                 return 0; /* error, incl. time_diff < 0 (i.e. time-out) */
803 
804             /* otherwise wait some more */
805         }
806     }
807 }
808