1 /* -*- mode: C; mode: fold; -*- */
2 /* Copyright (c) 1992, 1998 John E. Davis
3  * This file is part of JED editor library source.
4  *
5  * You may distribute this file under the terms the GNU General Public
6  * License.  See the file COPYING for more information.
7  */
8 
9 /*  This file is the interface to system specific files */
10 #include "config.h"
11 #include "jed-feat.h"
12 /*{{{ Include Files */
13 
14 #include <stdio.h>
15 #include <string.h>
16 
17 #ifdef HAVE_STDLIB_H
18 # include <stdlib.h>
19 #endif
20 
21 #ifdef HAVE_UNISTD_H
22 # include <unistd.h>
23 #endif
24 
25 #include "jdmacros.h"
26 
27 #ifdef POSIX
28 # ifndef _POSIX_SOURCE
29 #  define _POSIX_SOURCE
30 # endif
31 #endif
32 
33 #ifdef __WIN32__
34 # ifdef VOID
35 #  undef VOID
36 # endif
37 # include <windows.h>
38 # undef _POSIX_SOURCE
39 # include <sys/stat.h>
40 #endif
41 
42 #include <slang.h>
43 
44 #include "buffer.h"
45 #include "sysdep.h"
46 #include "display.h"
47 #include "file.h"
48 #include "screen.h"
49 #include "misc.h"
50 #include "hooks.h"
51 #include "kanji.h"
52 /*}}}*/
53 
54 /* These are hooks for porting to other systems */
55 
56 int (*X_Read_Hook) (void);
57 int (*X_Input_Pending_Hook) (void);
58 int (*X_Init_Term_Hook) (void);
59 void (*X_Reset_Term_Hook) (void);
60 
61 int Ignore_User_Abort = 1;	       /* Abort char triggers S-Lang error */
62 
63 unsigned char KeyBoard_Xlate[256] = /*{{{*/
64 {
65    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
66      21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
67      40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
68      59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
69      78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
70      97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
71      113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
72      128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
73      143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
74      158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
75      173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
76      188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
77      203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
78      218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
79      233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
80      248, 249, 250, 251, 252, 253, 254, 255
81 };
82 
83 /*}}}*/
84 
85 #if defined(__WIN32__)
86 # define SLASH_CHAR '\\'
87 # include "win32.c"
88 #else
89 # if defined (__MSDOS__) || defined(MSWINDOWS)
90 #  define SLASH_CHAR '\\'
91 #  if defined (__WATCOMC__) || defined (__GO32__)
92 #   include	"i386.c"
93 #  else
94 #   if defined(MSWINDOWS)
95 #    include   "win16.c"
96 #   else
97 #    include	"ibmpc.c"
98 #   endif
99 #  endif
100 # else
101 #  if defined (__os2__)
102 #   define SLASH_CHAR '\\'
103 #   include "os2.c"
104 #  else
105 #   if defined (VMS)
106 #    define SLASH_CHAR ']'
107 #    include "vms.c"
108 #   else
109 #    define SLASH_CHAR '/'
110 #    include "unix.c"
111 #   endif
112 #  endif
113 # endif
114 #endif
115 
116 int Input_Buffer_Len = 0, Kanji_Code_Conved = 0;
117 unsigned char Input_Buffer[MAX_INPUT_BUFFER_LEN];
118 
map_character(int * fromp,int * top)119 void map_character(int *fromp, int *top) /*{{{*/
120 {
121    int from = *fromp, to = *top;
122    if ((from > 255) || (to > 255) || (from < 0) || (to < 0)) return;
123    KeyBoard_Xlate[from] = to;
124 }
125 
126 /*}}}*/
127 
128 
129 /* if input char arrives with hi bit set, it is replaced by 2 characters:
130  *   Meta_Char + char with hi bit off.  If Meta_Char is -1, then return
131  * full 8 bits which self inserts */
132 
133 /* By default, 8 bit chars self insert. */
134 int Meta_Char = -1;
135 #if 0
136 int DEC_8Bit_Hack = 64;
137 #else
138 int DEC_8Bit_Hack = 0;
139 #endif
140 
my_getkey()141 int my_getkey() /*{{{*/
142 {
143    char buf[10];
144    int i, imax;
145    unsigned char ch;
146 
147    if (Batch)
148      {
149 	fgets(buf, 9 ,stdin);
150 	return (int) *buf;
151      }
152 
153    if (!Input_Buffer_Len)
154      {
155 	/* if (Batch) ch = (unsigned char) getc(stdin); else ch = sys_getkey(); */
156 	ch = (unsigned char) KeyBoard_Xlate[sys_getkey()];
157         if(!is_kanji_jedcode())
158 	  {
159 	     i = (ch & 0x7F);
160 	     if ((i < ' ') && DEC_8Bit_Hack)
161 	       {
162 		  i += DEC_8Bit_Hack;
163 		  ungetkey((int *) &i);
164 		  ch = 27;
165 	       }
166 	     else if (Meta_Char != -1)
167 	       {
168 		  ungetkey((int *) &i);
169 		  ch = Meta_Char;		       /* escape char */
170 	       }
171 	  }
172 	return((int) ch);
173      }
174 
175    ch = Input_Buffer[0];
176    if ((!is_kanji_jedcode() && !Kanji_Code_Conved) && (ch & 0x80) && ((Meta_Char != -1) || ((ch < 160) && DEC_8Bit_Hack)))
177      {
178 	ch = (ch & 0x7F);
179 	if ((ch < ' ') && DEC_8Bit_Hack)
180 	  {
181 	     ch += DEC_8Bit_Hack;
182 	     i = 27;
183 	  }
184 	else i = Meta_Char;
185 
186 	Input_Buffer[0] = ch;
187 	return ((int) (unsigned int) i);
188      }
189 
190 #ifdef USING_INPUT_BUFFER
191    USING_INPUT_BUFFER
192 #endif
193 
194      Input_Buffer_Len--;
195    imax = Input_Buffer_Len;
196 
197    SLMEMCPY ((char *) Input_Buffer, (char *) (Input_Buffer + 1), imax);
198 
199 #ifdef DONE_WITH_INPUT_BUFFER
200    DONE_WITH_INPUT_BUFFER
201 #endif
202 
203      return((int) ch);
204 }
205 
206 /*}}}*/
207 
208 
ungetkey_string(char * s,int n)209 void ungetkey_string(char *s, int n) /*{{{*/
210 {
211    /* int i; */
212    register unsigned char *bmax, *b, *b1;
213 
214    /* FIXME!! This should affect the keyboard macro buffer. */
215    if (Executing_Keyboard_Macro) return;
216 
217    if (n + Input_Buffer_Len > MAX_INPUT_BUFFER_LEN - 3) return;
218 
219 #ifdef USING_INPUT_BUFFER
220    USING_INPUT_BUFFER
221 #endif
222 
223      b = Input_Buffer;
224    bmax = b + (Input_Buffer_Len - 1);
225    b1 = bmax + n;
226    while (bmax >= b) *b1-- = *bmax--;
227    bmax = b + n;
228    while (b < bmax) *b++ = (unsigned char) *s++;
229    Input_Buffer_Len += n;
230 
231 #ifdef DONE_WITH_INPUT_BUFFER
232    DONE_WITH_INPUT_BUFFER
233 #endif
234 }
235 
236 /*}}}*/
237 
buffer_keystring(char * s,int n)238 void buffer_keystring(char *s, int n) /*{{{*/
239 {
240    unsigned char *d;
241 
242    d = kSLCodeConv (s, &n, kSLcode, kSLinput_code, SKanaToDKana);
243    if (n + Input_Buffer_Len > MAX_INPUT_BUFFER_LEN - 3) return;
244 
245 #ifdef USING_INPUT_BUFFER
246    USING_INPUT_BUFFER
247 # if 0
248      ;
249 # endif
250 #endif
251    SLMEMCPY ((char *) Input_Buffer + Input_Buffer_Len, d, n);
252    Input_Buffer_Len += n;
253 #ifdef DONE_WITH_INPUT_BUFFER
254    DONE_WITH_INPUT_BUFFER
255 #endif
256    if(s != d) SLfree (d);
257 }
258 
259 /*}}}*/
260 
ungetkey(int * ci)261 void ungetkey(int *ci) /*{{{*/
262 {
263    char ch;
264    ch = (char) *ci;
265    ungetkey_string(&ch, 1);
266    if(kSLinput_code && is_kanji_jedcode())	Kanji_Code_Conved++;
267 }
268 
269 /*}}}*/
270 
input_pending(int * tsecs)271 int input_pending (int *tsecs) /*{{{*/
272 {
273    int n;
274    char c;
275 
276    /* FIXME!!  This should affect the macro buffer */
277    if (Executing_Keyboard_Macro) return 1;
278 
279    if (Input_Buffer_Len) return Input_Buffer_Len;
280 
281    n = sys_input_pending (tsecs, 0);
282    if (n < 0)
283      {
284 	if (SLKeyBoard_Quit)
285 	  n = 1;
286 	else
287 	  n = 0;
288      }
289 
290    if (n && (Input_Buffer_Len == 0))
291      {
292 	c = (char)my_getkey ();
293 	ungetkey_string (&c, 1);
294      }
295    return n;
296 }
297 
298 /*}}}*/
299 
300 #if JED_HAS_SUBPROCESSES
get_process_input(int * t)301 void get_process_input (int *t) /*{{{*/
302 {
303    (void) sys_input_pending (t, -1);
304 }
305 
306 /*}}}*/
307 
308 #endif
309 
flush_input()310 void flush_input () /*{{{*/
311 {
312    int quit = SLKeyBoard_Quit;
313    Input_Buffer_Len = 0;
314    SLKeyBoard_Quit = 0;
315    if (Executing_Keyboard_Macro == 0)
316      {
317 #ifdef __MSDOS__
318 	while (input_pending(&Number_Zero)) if (!jed_kanji_getkey()) jed_kanji_getkey();
319 #else
320 # ifdef __os2__
321 	sys_flush_input();
322 # endif
323 	while (input_pending(&Number_Zero)) jed_kanji_getkey();
324 #endif
325 #ifdef HAS_MOUSE
326 	jed_flush_mouse_queue ();
327 #endif
328      }
329    SLKeyBoard_Quit = quit;
330 }
331 
332 /*}}}*/
333 
334 #include <time.h>
335 
sys_time(void)336 unsigned long sys_time(void) /*{{{*/
337 {
338    return((unsigned long) time((time_t *) 0));
339 }
340 
341 /*}}}*/
342 
343 
slash2slash(char * dir)344 char *slash2slash(char *dir) /*{{{*/
345 {
346 #ifndef VMS
347    register char *p = dir, ch;
348 
349    while ((ch = *p) != 0)
350      {
351 	if ((ch == '/') || (ch == '\\')) *p = SLASH_CHAR;
352       	p++;
353      }
354 #endif
355    return(dir);
356 }
357 
358 /*}}}*/
359 
360 /* given a canonical filename, return pointer to its name.
361  * Note: If the file ends in a slash as in a/b/c/, then a pointer to
362  * the END of the string is returned.
363  */
extract_file(char * file)364 char *extract_file(char *file) /*{{{*/
365 {
366    char *f;
367 
368    f = file + strlen(file);
369    while (f > file)
370      {
371 	f--;
372 	if (*f == SLASH_CHAR) return f + 1;
373      }
374    return (file);
375 }
376 
377 /*}}}*/
378 
379 
380 #ifndef VMS
381 /* this routine returns a Static pointer which is considered volatile */
expand_filename(char * file)382 char *expand_filename (char *file) /*{{{*/
383 {
384    register char *p, ch;
385    char  *last, *p1;
386 # if defined (IBMPC_SYSTEM)
387    static char work[JED_MAX_PATH_LEN];
388 # else
389    static char work[2 * JED_MAX_PATH_LEN];
390 # endif
391    char filebuf [JED_MAX_PATH_LEN];
392 
393    /* Do not mess with the file passed in as an argument */
394    safe_strcpy (filebuf, file, sizeof (filebuf));
395    file = filebuf;
396 
397    if (file[0] == '~' && file[1] == 0)	/* patch from toyofuku@juice.or.jp */
398      {
399 	file[1] = '/';
400 	file[2] = 0;
401      }
402 
403      *work = 0;
404    /* the following combinations indicate non-relative path names:
405     *	"//"	path from the root dir
406     *	"~/"	path from the $HOME dir
407     * for dos, os2 only
408     *   "x:/"	path from "x:/" dir
409     *   "x:"	same as "x:/"
410     */
411 
412    p = slash2slash(file) + strlen(file);
413 
414    while (p > file)
415      {
416 	ch = *p--;
417 	if (ch == SLASH_CHAR )
418 	  {
419 	     if ( *p == SLASH_CHAR ) /* "//" combination */
420 	       {
421 # ifdef MSWINDOWS
422  		  if (file == p)       /* With WFW network '\\host\filename' can be used */
423  		    safe_strcpy(work, p, sizeof (work));
424  		  else
425 # endif
426 		    /* QNX pathnames look like //<node number>/rest_of_path
427 		     *  -- we need to leave on the <node number>
428 		     */
429 # ifndef __QNX__
430  		    safe_strcpy(work, (p + 1), sizeof (work));
431  		  file = work;
432 #endif
433 		  break;
434 	       }
435 	     else if ((*p == '~')  /* "~/" combination */
436 		      && (((p > file) && (*(p - 1) == SLASH_CHAR))
437 			  || (p == file)))
438 	       {
439 		  if ( (p1 = getenv("HOME")) == NULL) p1 = "/";
440 		  safe_strcpy( work, p1, sizeof (work) );
441 		  /* remove trailing slash if any */
442 		  p1 = slash2slash(work) + (strlen(work) - 1);
443 		  if ( *p1 == SLASH_CHAR ) *p1 = '\0';
444 		  safe_strcat (work, (p + 1), sizeof (work) );
445 		  file = work;
446 		  while (*file && (*file != SLASH_CHAR)) file++;
447 		  break;
448 	       }
449 	  }
450 # if defined (IBMPC_SYSTEM)		       /* DOS, OS/2 stuff */
451 	else if (ch == ':' ) 	       /* "c:" or "c:/" combination */
452 	  {
453 	     safe_strcpy( work, p, sizeof (work) );
454 	     file = (work+2);	       /* start file past the drive spec */
455 	     p += 2;
456 	     if ( *p != SLASH_CHAR ) 	       /* "c:" combination */
457 	       {
458 		  strcpy( file, "\\");
459 		  safe_strcat (work, p, sizeof (work));
460 	       }
461 	     break;
462 	  }
463 # endif /* IBMPC_SYSTEM */
464      }
465 
466    if ( *work == '\0' )	       /* no special combinations */
467      {
468 	if ( *file != SLASH_CHAR )
469 	  {
470 	     strcpy(work, get_cwd());      /* assume relative dir */
471 	     slash2slash(work);
472 	  }
473 	safe_strcat(work, file, sizeof (work));
474 	file = work;
475      }
476 
477    /* remove ../ and ./ stuff.  These combinations are only relevant when
478     * immediately preceed by a / character.  For simplicity in the algorithm
479     * several passes will be made.  In most cases, only one pass will be
480     * required.
481     *
482     * last is a pointer to the character following last / character where
483     * ../ would backup to.  (This can also be at the beginning).
484     */
485 
486    p = last = file;
487 
488    while ((ch = *p++) != 0)
489      {
490 	if (ch != SLASH_CHAR)
491 	  continue;
492 
493 	p1 = last;
494 	last = p;
495 
496 	/* A / seen.  Look for ./ or ../ */
497 	if ((ch = *p) != '.')
498 	  continue;
499 
500 	p++;
501 	ch = *p;
502 
503 	if (ch != SLASH_CHAR)
504 	  {
505 	     if (ch != '.')
506 	       continue;
507 
508 	     p++;
509 	     ch = *p;
510 	     if (ch != SLASH_CHAR)
511 	       continue;
512 
513 	     /* We have a /../ combination.  p1 points to position that
514 	      * following characters should be copied to.
515 	      */
516 	     last = p1;
517 	  }
518 	/* else we have /./ combination.  Following characters should be
519 	 * copied to last position.
520 	 */
521 
522 	do
523 	  {
524 	     p++;
525 	     ch = *p;
526 	     *last++ = ch;
527 	  }
528 	while (ch != 0);
529 
530 	last = p = file;	       /* rescan */
531      }
532 
533    return work;
534 }
535 
536 /*}}}*/
537 #endif /* ! VMS */
538 
539 #ifdef sequent
my_strstr(char * a,char * b)540 char *my_strstr(char *a, char *b) /*{{{*/
541 {
542    register char *bb, *aa, *amax;
543 
544    if (*b == 0) return(a);
545 
546    bb = b; while (*bb) bb++;
547    aa = a; while (*aa++);
548 
549    amax = aa - (bb - b);
550 
551    while (a < amax)
552      {
553 	bb = b;
554 	while ((a < amax) && (*a != *bb)) a++;
555 	if (a == amax) return((char *) NULL);
556 
557 	aa = a;
558 	while (*aa && (*aa == *bb)) aa++, bb++;
559 	if (! *bb) return(a);
560 
561 	a++;
562      }
563    return((char *) NULL);
564 }
565 
566 /*}}}*/
567 #endif
568 
569 
deslash(char * dir)570 void deslash(char *dir) /*{{{*/
571 {
572 #ifndef VMS
573    int n;
574 
575    if ((n = strlen(dir)) > 1)
576      {
577 	n--;
578 # if defined (IBMPC_SYSTEM)
579 	if ( (dir[n] == '\\' || dir[n] == '/') && dir[n - 1] != ':' )
580 	  dir[n] = '\0';
581 # else
582 	if ( dir[n] == '/' )
583 	  dir[n] = '\0';
584 # endif
585      }
586 #endif /* !VMS */
587 }
588 
589 /*}}}*/
590 
591 /* add trailing slash to dir */
fixup_dir(char * dir)592 void fixup_dir(char *dir) /*{{{*/
593 {
594 #ifndef VMS
595    int n;
596 
597    if ((n = strlen(dir)) > 1)
598      {
599 	n--;
600 # if defined(IBMPC_SYSTEM)
601 	if ( dir[n] != '/' && dir[n] != '\\' )
602 	  strcat(dir, "\\" );
603 # else
604 	if ( dir[n] != '/' )
605 	  strcat(dir, "/" );
606 # endif
607      }
608 #endif /* !VMS */
609 }
610 
611 /*}}}*/
612 
613  /* ch_dir routine added during OS/2 port in order to
614     simplify script writing. */
615 
ch_dir(char * path)616 int ch_dir(char *path) /*{{{*/
617 {
618 #if defined(IBMPC_SYSTEM) || defined(__os2__)
619    char work[JED_MAX_PATH_LEN];
620 
621    safe_strcpy(work, path, sizeof (work));
622    deslash(work);
623    return chdir(work);
624 #else
625    return chdir(path);
626 #endif
627 }
628 
629 /*}}}*/
630 
631 /* generate a random number */
make_random_number(int * seed,int * max)632 int make_random_number (int *seed, int *max) /*{{{*/
633 {
634    static unsigned long s;
635    int mmax;
636 
637    if (*seed == -1)		       /* generate seed */
638      s = (unsigned long) (time(0) + getpid());
639    else if (*seed != 0)
640      s = *seed;
641 
642    if ((mmax = *max) < 2) mmax = 2;
643 
644    s = s * 69069UL + 1013904243UL;
645 
646    s = s & 0xFFFFFFFFUL;
647    return (int) (mmax * (double)s/4294967296.0);
648 }
649 
650 /*}}}*/
651 
652 #ifndef __GO32__
653 # ifdef __unix__
654 /* if non-zero, Flow control is enabled */
enable_flow_control(int * mode)655 void enable_flow_control(int *mode) /*{{{*/
656 {
657    /* This kills X windows.  For the time being, work around it as follows */
658    if (X_Init_Term_Hook != NULL) return;
659    Flow_Control = *mode;
660    reset_tty();
661    init_tty();
662 }
663 
664 /*}}}*/
665 
666 # endif
667 #endif
668 
669 #if defined(IBMPC_SYSTEM)
670 /* This routine converts  C:\  --> C:\ and C:\subdir\  -> C:\subdir */
msdos_pinhead_fix_dir(char * f)671 char *msdos_pinhead_fix_dir(char *f) /*{{{*/
672 {
673    static char file[JED_MAX_PATH_LEN];
674    register char ch;
675    int n;
676 
677    if (*f == 0) return f;
678    strncpy (file, f, JED_MAX_PATH_LEN); file[JED_MAX_PATH_LEN - 1] = 0;
679    f = file;
680    /* skip past colon */
681    while (((ch = *f) != 0) && (ch != ':')) f++;
682 
683    if (ch == 0)			       /* no colon */
684      {
685 	n = (int) (f - file);
686 	f = file;
687      }
688    else
689      {
690 	f++;
691 	n = strlen (f);
692      }
693    if (n == 0)
694      {
695 	*f++ = '\\'; *f = 0;
696 	return file;
697      }
698    if ((n == 1) && (*f == '\\')) return file;
699 
700    f += n - 1;
701    if (*f == '\\') *f = 0;
702    return file;
703 }
704 
705 /*}}}*/
706 
707 #endif
708 
709 #ifdef __WIN32__
jed_win32_rename(char * old_name,char * new_name)710 int jed_win32_rename (char *old_name, char *new_name)
711 {
712    /* Some versions of win95 have a bug in rename
713     * Nick Tatham discovered this and provided the patch:
714     *  C:\> echo try1 >temp.txt
715     *  C:\> ren temp.txt temp.txt~
716     *  C:\> echo try2 >temp.txt
717     */
718 
719    /* The work-around is to rename the file in two stages:
720     *   file.typ ==> file.~ ==> file.typ~
721     */
722 
723    char tmp_name[1024];
724    char *p;
725    unsigned int n, extlen;
726    unsigned int num_tries;
727 
728    p = extract_file (old_name);
729    while (*p && (*p != '.')) p++;      /* find extension */
730    /* create temporary filename */
731    /* everything up to potential dot */
732 
733    n = (unsigned int) (p - old_name);
734    extlen = strlen (p);
735 
736    if ((n + 10 + extlen) > sizeof (tmp_name))
737      {
738 #ifdef ENAMETOOLONG
739 	errno = ENAMETOOLONG;
740 #endif
741 	return -1;
742      }
743    strncpy (tmp_name, old_name, n);
744 
745    num_tries = 0xFFF;
746    while (num_tries != 0)
747      {
748 	struct stat st;
749 
750 	sprintf (tmp_name + n, "~~~~~~%03X%s", num_tries, p);
751 	if (0 == stat (tmp_name, &st))
752 	  {
753 	     num_tries--;
754 	     continue;
755 	  }
756 	if (-1 == rename (old_name, tmp_name))
757 	  return -1;
758 
759 	if (-1 == rename (tmp_name, new_name))
760 	  {
761 	     (void) rename (tmp_name, old_name);
762 	     return -1;
763 	  }
764 
765 	return 0;
766      }
767 #ifdef EFAULT
768    /* Any better idea? */
769    errno = EFAULT;
770 #endif
771    return -1;
772 }
773 
774 #endif
jed_pause(int * ms)775 void jed_pause (int *ms) /*{{{*/
776 {
777    if (*ms < 0)
778      return;
779 
780    sys_pause (*ms);
781 }
782 
783 /*}}}*/
784