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