xref: /vim-8.2.3635/src/os_amiga.c (revision 94688b8a)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * os_amiga.c
12  *
13  * Amiga system-dependent routines.
14  */
15 
16 #include "vim.h"
17 
18 #ifdef Window
19 # undef Window	/* Amiga has its own Window definition */
20 #endif
21 
22 #undef TRUE		/* will be redefined by exec/types.h */
23 #undef FALSE
24 
25 /* cproto fails on missing include files, skip them */
26 #ifndef PROTO
27 
28 #ifndef LATTICE
29 # include <exec/types.h>
30 # include <exec/exec.h>
31 # include <libraries/dos.h>
32 # include <intuition/intuition.h>
33 #endif
34 
35 /* XXX These are included from os_amiga.h
36 #include <proto/exec.h>
37 #include <proto/dos.h>
38 #include <proto/intuition.h>
39 */
40 
41 #include <exec/memory.h>
42 #include <libraries/dosextens.h>
43 
44 #include <dos/dostags.h>	    /* for 2.0 functions */
45 #include <dos/dosasl.h>
46 
47 /* From version 4 of AmigaOS, several system structures must be allocated
48  * and freed using system functions. "struct AnchorPath" is one.
49  */
50 #ifdef __amigaos4__
51 # include <dos/anchorpath.h>
52 # define	free_fib(x) FreeDosObject(DOS_FIB, x)
53 #else
54 # define	free_fib(x) vim_free(fib)
55 #endif
56 
57 #if defined(LATTICE) && !defined(SASC) && defined(FEAT_ARP)
58 # include <libraries/arp_pragmas.h>
59 #endif
60 
61 #endif /* PROTO */
62 
63 /*
64  * At this point TRUE and FALSE are defined as 1L and 0L, but we want 1 and 0.
65  */
66 #undef	TRUE
67 #define TRUE (1)
68 #undef	FALSE
69 #define FALSE (0)
70 
71 #ifdef __amigaos4__
72 # define	dos_packet(a, b, c)   DoPkt(a, b, c, 0, 0, 0, 0)
73 #elif !defined(AZTEC_C) && !defined(__AROS__)
74 static long dos_packet(struct MsgPort *, long, long);
75 #endif
76 static int lock2name(BPTR lock, char_u *buf, long   len);
77 static void out_num(long n);
78 static struct FileInfoBlock *get_fib(char_u *);
79 static int sortcmp(const void *a, const void *b);
80 
81 static BPTR		raw_in = (BPTR)NULL;
82 static BPTR		raw_out = (BPTR)NULL;
83 static int		close_win = FALSE;  /* set if Vim opened the window */
84 
85 #ifndef __amigaos4__	/* Use autoopen for AmigaOS4 */
86 struct IntuitionBase	*IntuitionBase = NULL;
87 #endif
88 #ifdef FEAT_ARP
89 struct ArpBase		*ArpBase = NULL;
90 #endif
91 
92 static struct Window	*wb_window;
93 static char_u		*oldwindowtitle = NULL;
94 
95 #ifdef FEAT_ARP
96 int			dos2 = FALSE;	    /* Amiga DOS 2.0x or higher */
97 #endif
98 int			size_set = FALSE;   /* set to TRUE if window size was set */
99 
100     void
101 win_resize_on(void)
102 {
103     OUT_STR_NF("\033[12{");
104 }
105 
106     void
107 win_resize_off(void)
108 {
109     OUT_STR_NF("\033[12}");
110 }
111 
112     void
113 mch_write(char_u *p, int len)
114 {
115     Write(raw_out, (char *)p, (long)len);
116 }
117 
118 /*
119  * mch_inchar(): low level input function.
120  * Get a characters from the keyboard.
121  * If time == 0 do not wait for characters.
122  * If time == n wait a short time for characters.
123  * If time == -1 wait forever for characters.
124  *
125  * Return number of characters read.
126  */
127     int
128 mch_inchar(
129     char_u  *buf,
130     int	    maxlen,
131     long    time,		/* milli seconds */
132     int	    tb_change_cnt)
133 {
134     int	    len;
135     long    utime;
136 
137     if (time >= 0)
138     {
139 	if (time == 0)
140 	    utime = 100L;	    /* time = 0 causes problems in DOS 1.2 */
141 	else
142 	    utime = time * 1000L;   /* convert from milli to micro secs */
143 	if (WaitForChar(raw_in, utime) == 0)	/* no character available */
144 	    return 0;
145     }
146     else    /* time == -1 */
147     {
148 	/*
149 	 * If there is no character available within 2 seconds (default)
150 	 * write the autoscript file to disk.  Or cause the CursorHold event
151 	 * to be triggered.
152 	 */
153 	if (WaitForChar(raw_in, p_ut * 1000L) == 0)
154 	{
155 	    if (trigger_cursorhold() && maxlen >= 3)
156 	    {
157 		buf[0] = K_SPECIAL;
158 		buf[1] = KS_EXTRA;
159 		buf[2] = (int)KE_CURSORHOLD;
160 		return 3;
161 	    }
162 	    before_blocking();
163 	}
164     }
165 
166     for (;;)	    /* repeat until we got a character */
167     {
168 	len = Read(raw_in, (char *)buf, (long)maxlen / input_conv.vc_factor);
169 	if (len > 0)
170 	{
171 	    /* Convert from 'termencoding' to 'encoding'. */
172 	    if (input_conv.vc_type != CONV_NONE)
173 		len = convert_input(buf, len, maxlen);
174 	    return len;
175 	}
176     }
177 }
178 
179 /*
180  * return non-zero if a character is available
181  */
182     int
183 mch_char_avail(void)
184 {
185     return (WaitForChar(raw_in, 100L) != 0);
186 }
187 
188 /*
189  * Return amount of memory still available in Kbyte.
190  */
191     long_u
192 mch_avail_mem(int special)
193 {
194 #ifdef __amigaos4__
195     return (long_u)AvailMem(MEMF_ANY) >> 10;
196 #else
197     return (long_u)(AvailMem(special ? (long)MEMF_CHIP : (long)MEMF_ANY)) >> 10;
198 #endif
199 }
200 
201 /*
202  * Waits a specified amount of time, or until input arrives if
203  * ignoreinput is FALSE.
204  */
205     void
206 mch_delay(long msec, int ignoreinput)
207 {
208 #ifndef LATTICE		/* SAS declares void Delay(ULONG) */
209     void	    Delay(long);
210 #endif
211 
212     if (msec > 0)
213     {
214 	if (ignoreinput)
215 	    Delay(msec / 20L);	    /* Delay works with 20 msec intervals */
216 	else
217 	    WaitForChar(raw_in, msec * 1000L);
218     }
219 }
220 
221 /*
222  * We have no job control, fake it by starting a new shell.
223  */
224     void
225 mch_suspend(void)
226 {
227     suspend_shell();
228 }
229 
230 #ifndef DOS_LIBRARY
231 # define DOS_LIBRARY	((UBYTE *)"dos.library")
232 #endif
233 
234     void
235 mch_init(void)
236 {
237     static char	    intlibname[] = "intuition.library";
238 
239 #ifdef AZTEC_C
240     Enable_Abort = 0;		/* disallow vim to be aborted */
241 #endif
242     Columns = 80;
243     Rows = 24;
244 
245     /*
246      * Set input and output channels, unless we have opened our own window
247      */
248     if (raw_in == (BPTR)NULL)
249     {
250 	raw_in = Input();
251 	raw_out = Output();
252 	/*
253 	 * If Input() is not interactive, then Output() will be (because of
254 	 * check in mch_check_win()).  Used for "Vim -".
255 	 * Also check the other way around, for "Vim -h | more".
256 	 */
257 	if (!IsInteractive(raw_in))
258 	    raw_in = raw_out;
259 	else if (!IsInteractive(raw_out))
260 	    raw_out = raw_in;
261     }
262 
263     out_flush();
264 
265     wb_window = NULL;
266 #ifndef __amigaos4__
267     if ((IntuitionBase = (struct IntuitionBase *)
268 				OpenLibrary((UBYTE *)intlibname, 0L)) == NULL)
269     {
270 	mch_errmsg(_("cannot open "));
271 	mch_errmsg(intlibname);
272 	mch_errmsg("!?\n");
273 	mch_exit(3);
274     }
275 #endif
276 }
277 
278 #ifndef PROTO
279 # include <workbench/startup.h>
280 #endif
281 
282 /*
283  * Check_win checks whether we have an interactive window.
284  * If not, a new window is opened with the newcli command.
285  * If we would open a window ourselves, the :sh and :! commands would not
286  * work properly (Why? probably because we are then running in a background
287  * CLI). This also is the best way to assure proper working in a next
288  * Workbench release.
289  *
290  * For the -f option (foreground mode) we open our own window and disable :sh.
291  * Otherwise the calling program would never know when editing is finished.
292  */
293 #define BUF2SIZE 320	    /* length of buffer for argument with complete path */
294 
295     int
296 mch_check_win(int argc, char **argv)
297 {
298     int		    i;
299     BPTR	    nilfh, fh;
300     char_u	    buf1[24];
301     char_u	    buf2[BUF2SIZE];
302     static char_u   *(constrings[3]) = {(char_u *)"con:0/0/662/210/",
303 					(char_u *)"con:0/0/640/200/",
304 					(char_u *)"con:0/0/320/200/"};
305     static char_u   *winerr = (char_u *)N_("VIM: Can't open window!\n");
306     struct WBArg    *argp;
307     int		    ac;
308     char	    *av;
309     char_u	    *device = NULL;
310     int		    exitval = 4;
311 #ifndef __amigaos4__
312     struct Library  *DosBase;
313 #endif
314     int		    usewin = FALSE;
315 
316 /*
317  * check if we are running under DOS 2.0x or higher
318  */
319 #ifndef __amigaos4__
320     DosBase = OpenLibrary(DOS_LIBRARY, 37L);
321     if (DosBase != NULL)
322     /* if (((struct Library *)DOSBase)->lib_Version >= 37) */
323     {
324 	CloseLibrary(DosBase);
325 # ifdef FEAT_ARP
326 	dos2 = TRUE;
327 # endif
328     }
329     else	    /* without arp functions we NEED 2.0 */
330     {
331 # ifndef FEAT_ARP
332 	mch_errmsg(_("Need Amigados version 2.04 or later\n"));
333 	exit(3);
334 # else
335 		    /* need arp functions for dos 1.x */
336 	if (!(ArpBase = (struct ArpBase *) OpenLibrary((UBYTE *)ArpName, ArpVersion)))
337 	{
338 	    fprintf(stderr, _("Need %s version %ld\n"), ArpName, ArpVersion);
339 	    exit(3);
340 	}
341 # endif
342     }
343 #endif	/* __amigaos4__ */
344 
345     /*
346      * scan argv[] for the "-f" and "-d" arguments
347      */
348     for (i = 1; i < argc; ++i)
349 	if (argv[i][0] == '-')
350 	{
351 	    switch (argv[i][1])
352 	    {
353 	    case 'f':
354 		usewin = TRUE;
355 		break;
356 
357 	    case 'd':
358 		if (i < argc - 1
359 #ifdef FEAT_DIFF
360 			/* require using "-dev", "-d" means diff mode */
361 			&& argv[i][2] == 'e' && argv[i][3] == 'v'
362 #endif
363 		   )
364 		    device = (char_u *)argv[i + 1];
365 		break;
366 	    }
367 	}
368 
369 /*
370  * If we were not started from workbench, do not have a "-d" or "-dev"
371  * argument and we have been started with an interactive window, use that
372  * window.
373  */
374     if (argc != 0
375 	    && device == NULL
376 	    && (IsInteractive(Input()) || IsInteractive(Output())))
377 	return OK;
378 
379 /*
380  * When given the "-f" argument, we open our own window. We can't use the
381  * newcli trick below, because the calling program (mail, rn, etc.) would not
382  * know when we are finished.
383  */
384     if (usewin)
385     {
386 	/*
387 	 * Try to open a window. First try the specified device.
388 	 * Then try a 24 line 80 column window.
389 	 * If that fails, try two smaller ones.
390 	 */
391 	for (i = -1; i < 3; ++i)
392 	{
393 	    if (i >= 0)
394 		device = constrings[i];
395 	    if (device != NULL && (raw_in = Open((UBYTE *)device,
396 					   (long)MODE_NEWFILE)) != (BPTR)NULL)
397 		break;
398 	}
399 	if (raw_in == (BPTR)NULL)	/* all three failed */
400 	{
401 	    mch_errmsg(_(winerr));
402 	    goto exit;
403 	}
404 	raw_out = raw_in;
405 	close_win = TRUE;
406 	return OK;
407     }
408 
409     if ((nilfh = Open((UBYTE *)"NIL:", (long)MODE_NEWFILE)) == (BPTR)NULL)
410     {
411 	mch_errmsg(_("Cannot open NIL:\n"));
412 	goto exit;
413     }
414 
415     /*
416      * Make a unique name for the temp file (which we will not delete!).
417      * Use a pointer on the stack (nobody else will be using it).
418      * Under AmigaOS4, this assumption might change in the future, so
419      * we use a pointer to the current task instead. This should be a
420      * shared structure and thus globally unique.
421      */
422 #ifdef __amigaos4__
423     sprintf((char *)buf1, "t:nc%p", FindTask(0));
424 #else
425     sprintf((char *)buf1, "t:nc%ld", (long)buf1);
426 #endif
427     if ((fh = Open((UBYTE *)buf1, (long)MODE_NEWFILE)) == (BPTR)NULL)
428     {
429 	mch_errmsg(_("Cannot create "));
430 	mch_errmsg((char *)buf1);
431 	mch_errmsg("\n");
432 	goto exit;
433     }
434     /*
435      * Write the command into the file, put quotes around the arguments that
436      * have a space in them.
437      */
438     if (argc == 0)	/* run from workbench */
439 	ac = ((struct WBStartup *)argv)->sm_NumArgs;
440     else
441 	ac = argc;
442     for (i = 0; i < ac; ++i)
443     {
444 	if (argc == 0)
445 	{
446 	    *buf2 = NUL;
447 	    argp = &(((struct WBStartup *)argv)->sm_ArgList[i]);
448 	    if (argp->wa_Lock)
449 		(void)lock2name(argp->wa_Lock, buf2, (long)(BUF2SIZE - 1));
450 #ifdef FEAT_ARP
451 	    if (dos2)	    /* use 2.0 function */
452 #endif
453 		AddPart((UBYTE *)buf2, (UBYTE *)argp->wa_Name, (long)(BUF2SIZE - 1));
454 #ifdef FEAT_ARP
455 	    else	    /* use arp function */
456 		TackOn((char *)buf2, argp->wa_Name);
457 #endif
458 	    av = (char *)buf2;
459 	}
460 	else
461 	    av = argv[i];
462 
463 	/* skip '-d' or "-dev" option */
464 	if (av[0] == '-' && av[1] == 'd'
465 #ifdef FEAT_DIFF
466 		&& av[2] == 'e' && av[3] == 'v'
467 #endif
468 		)
469 	{
470 	    ++i;
471 	    continue;
472 	}
473 	if (vim_strchr((char_u *)av, ' '))
474 	    Write(fh, "\"", 1L);
475 	Write(fh, av, (long)strlen(av));
476 	if (vim_strchr((char_u *)av, ' '))
477 	    Write(fh, "\"", 1L);
478 	Write(fh, " ", 1L);
479     }
480     Write(fh, "\nendcli\n", 8L);
481     Close(fh);
482 
483 /*
484  * Try to open a new cli in a window. If "-d" or "-dev" argument was given try
485  * to open the specified device. Then try a 24 line 80 column window.  If that
486  * fails, try two smaller ones.
487  */
488     for (i = -1; i < 3; ++i)
489     {
490 	if (i >= 0)
491 	    device = constrings[i];
492 	else if (device == NULL)
493 	    continue;
494 	sprintf((char *)buf2, "newcli <nil: >nil: %s from %s", (char *)device, (char *)buf1);
495 #ifdef FEAT_ARP
496 	if (dos2)
497 	{
498 #endif
499 	    if (!SystemTags((UBYTE *)buf2, SYS_UserShell, TRUE, TAG_DONE))
500 		break;
501 #ifdef FEAT_ARP
502 	}
503 	else
504 	{
505 	    if (Execute((UBYTE *)buf2, nilfh, nilfh))
506 		break;
507 	}
508 #endif
509     }
510     if (i == 3)	    /* all three failed */
511     {
512 	DeleteFile((UBYTE *)buf1);
513 	mch_errmsg(_(winerr));
514 	goto exit;
515     }
516     exitval = 0;    /* The Execute succeeded: exit this program */
517 
518 exit:
519 #ifdef FEAT_ARP
520     if (ArpBase)
521 	CloseLibrary((struct Library *) ArpBase);
522 #endif
523     exit(exitval);
524     /* NOTREACHED */
525     return FAIL;
526 }
527 
528 /*
529  * Return TRUE if the input comes from a terminal, FALSE otherwise.
530  * We fake there is a window, because we can always open one!
531  */
532     int
533 mch_input_isatty(void)
534 {
535     return TRUE;
536 }
537 
538 /*
539  * fname_case(): Set the case of the file name, if it already exists.
540  *		 This will cause the file name to remain exactly the same
541  *		 if the file system ignores, but preserves case.
542  */
543 /*ARGSUSED*/
544     void
545 fname_case(
546     char_u	*name,
547     int		len)		/* buffer size, ignored here */
548 {
549     struct FileInfoBlock    *fib;
550     size_t		    flen;
551 
552     fib = get_fib(name);
553     if (fib != NULL)
554     {
555 	flen = STRLEN(name);
556 	/* TODO: Check if this fix applies to AmigaOS < 4 too.*/
557 #ifdef __amigaos4__
558 	if (fib->fib_DirEntryType == ST_ROOT)
559 	    strcat(fib->fib_FileName, ":");
560 #endif
561 	if (flen == strlen(fib->fib_FileName))	/* safety check */
562 	    mch_memmove(name, fib->fib_FileName, flen);
563 	free_fib(fib);
564     }
565 }
566 
567 /*
568  * Get the FileInfoBlock for file "fname"
569  * The returned structure has to be free()d.
570  * Returns NULL on error.
571  */
572     static struct FileInfoBlock *
573 get_fib(char_u *fname)
574 {
575     BPTR		    flock;
576     struct FileInfoBlock    *fib;
577 
578     if (fname == NULL)	    /* safety check */
579 	return NULL;
580 #ifdef __amigaos4__
581     fib = AllocDosObject(DOS_FIB,0);
582 #else
583     fib = (struct FileInfoBlock *)alloc(sizeof(struct FileInfoBlock));
584 #endif
585     if (fib != NULL)
586     {
587 	flock = Lock((UBYTE *)fname, (long)ACCESS_READ);
588 	if (flock == (BPTR)NULL || !Examine(flock, fib))
589 	{
590 	    free_fib(fib);  /* in case of an error the memory is freed here */
591 	    fib = NULL;
592 	}
593 	if (flock)
594 	    UnLock(flock);
595     }
596     return fib;
597 }
598 
599 #ifdef FEAT_TITLE
600 /*
601  * set the title of our window
602  * icon name is not set
603  */
604     void
605 mch_settitle(char_u *title, char_u *icon)
606 {
607     if (wb_window != NULL && title != NULL)
608 	SetWindowTitles(wb_window, (UBYTE *)title, (UBYTE *)-1L);
609 }
610 
611 /*
612  * Restore the window/icon title.
613  * which is one of:
614  *  SAVE_RESTORE_TITLE  Just restore title
615  *  SAVE_RESTORE_ICON   Just restore icon (which we don't have)
616  *  SAVE_RESTORE_BOTH   Restore title and icon (which we don't have)
617  */
618     void
619 mch_restore_title(int which)
620 {
621     if (which & SAVE_RESTORE_TITLE)
622 	mch_settitle(oldwindowtitle, NULL);
623 }
624 
625     int
626 mch_can_restore_title(void)
627 {
628     return (wb_window != NULL);
629 }
630 
631     int
632 mch_can_restore_icon(void)
633 {
634     return FALSE;
635 }
636 #endif
637 
638 /*
639  * Insert user name in s[len].
640  */
641     int
642 mch_get_user_name(char_u *s, int len)
643 {
644     /* TODO: Implement this. */
645     *s = NUL;
646     return FAIL;
647 }
648 
649 /*
650  * Insert host name is s[len].
651  */
652     void
653 mch_get_host_name(char_u *s, int len)
654 {
655 #if defined(__amigaos4__) && defined(__CLIB2__)
656     gethostname(s, len);
657 #else
658     vim_strncpy(s, "Amiga", len - 1);
659 #endif
660 }
661 
662 /*
663  * return process ID
664  */
665     long
666 mch_get_pid(void)
667 {
668 #ifdef __amigaos4__
669     /* This is as close to a pid as we can come. We could use CLI numbers also,
670      * but then we would have two different types of process identifiers.
671      */
672     return((long)FindTask(0));
673 #else
674     return (long)0;
675 #endif
676 }
677 
678 /*
679  * Get name of current directory into buffer 'buf' of length 'len' bytes.
680  * Return OK for success, FAIL for failure.
681  */
682     int
683 mch_dirname(char_u *buf, int len)
684 {
685     return mch_FullName((char_u *)"", buf, len, FALSE);
686 }
687 
688 /*
689  * get absolute file name into buffer 'buf' of length 'len' bytes
690  *
691  * return FAIL for failure, OK otherwise
692  */
693     int
694 mch_FullName(
695     char_u	*fname,
696     char_u	*buf,
697     int		len,
698     int		force)
699 {
700     BPTR	l;
701     int		retval = FAIL;
702     int		i;
703 
704     /* Lock the file.  If it exists, we can get the exact name. */
705     if ((l = Lock((UBYTE *)fname, (long)ACCESS_READ)) != (BPTR)0)
706     {
707 	retval = lock2name(l, buf, (long)len - 1);
708 	UnLock(l);
709     }
710     else if (force || !mch_isFullName(fname))	    /* not a full path yet */
711     {
712 	/*
713 	 * If the file cannot be locked (doesn't exist), try to lock the
714 	 * current directory and concatenate the file name.
715 	 */
716 	if ((l = Lock((UBYTE *)"", (long)ACCESS_READ)) != (BPTR)NULL)
717 	{
718 	    retval = lock2name(l, buf, (long)len);
719 	    UnLock(l);
720 	    if (retval == OK)
721 	    {
722 		i = STRLEN(buf);
723 		/* Concatenate the fname to the directory.  Don't add a slash
724 		 * if fname is empty, but do change "" to "/". */
725 		if (i == 0 || *fname != NUL)
726 		{
727 		    if (i < len - 1 && (i == 0 || buf[i - 1] != ':'))
728 			buf[i++] = '/';
729 		    vim_strncpy(buf + i, fname, len - i - 1);
730 		}
731 	    }
732 	}
733     }
734     if (*buf == 0 || *buf == ':')
735 	retval = FAIL;	/* something failed; use the file name */
736     return retval;
737 }
738 
739 /*
740  * Return TRUE if "fname" does not depend on the current directory.
741  */
742     int
743 mch_isFullName(char_u *fname)
744 {
745     return (vim_strchr(fname, ':') != NULL && *fname != ':');
746 }
747 
748 /*
749  * Get the full file name from a lock. Use 2.0 function if possible, because
750  * the arp function has more restrictions on the path length.
751  *
752  * return FAIL for failure, OK otherwise
753  */
754     static int
755 lock2name(BPTR lock, char_u *buf, long len)
756 {
757 #ifdef FEAT_ARP
758     if (dos2)		    /* use 2.0 function */
759 #endif
760 	return ((int)NameFromLock(lock, (UBYTE *)buf, len) ? OK : FAIL);
761 #ifdef FEAT_ARP
762     else		/* use arp function */
763 	return ((int)PathName(lock, (char *)buf, (long)(len/32)) ? OK : FAIL);
764 #endif
765 }
766 
767 /*
768  * get file permissions for 'name'
769  * Returns -1 when it doesn't exist.
770  */
771     long
772 mch_getperm(char_u *name)
773 {
774     struct FileInfoBlock    *fib;
775     long		    retval = -1;
776 
777     fib = get_fib(name);
778     if (fib != NULL)
779     {
780 	retval = fib->fib_Protection;
781 	free_fib(fib);
782     }
783     return retval;
784 }
785 
786 /*
787  * set file permission for 'name' to 'perm'
788  *
789  * return FAIL for failure, OK otherwise
790  */
791     int
792 mch_setperm(char_u *name, long perm)
793 {
794     perm &= ~FIBF_ARCHIVE;		/* reset archived bit */
795     return (SetProtection((UBYTE *)name, (long)perm) ? OK : FAIL);
796 }
797 
798 /*
799  * Set hidden flag for "name".
800  */
801     void
802 mch_hide(char_u *name)
803 {
804     /* can't hide a file */
805 }
806 
807 /*
808  * return FALSE if "name" is not a directory
809  * return TRUE if "name" is a directory.
810  * return FALSE for error.
811  */
812     int
813 mch_isdir(char_u *name)
814 {
815     struct FileInfoBlock    *fib;
816     int			    retval = FALSE;
817 
818     fib = get_fib(name);
819     if (fib != NULL)
820     {
821 #ifdef __amigaos4__
822 	retval = (FIB_IS_DRAWER(fib)) ? TRUE : FALSE;
823 #else
824 	retval = ((fib->fib_DirEntryType >= 0) ? TRUE : FALSE);
825 #endif
826 	free_fib(fib);
827     }
828     return retval;
829 }
830 
831 /*
832  * Create directory "name".
833  */
834     int
835 mch_mkdir(char_u *name)
836 {
837     BPTR	lock;
838 
839     lock = CreateDir(name);
840     if (lock != NULL)
841     {
842 	UnLock(lock);
843 	return 0;
844     }
845     return -1;
846 }
847 
848 /*
849  * Return 1 if "name" can be executed, 0 if not.
850  * If "use_path" is FALSE only check if "name" is executable.
851  * Return -1 if unknown.
852  */
853     int
854 mch_can_exe(char_u *name, char_u **path, int use_path)
855 {
856     /* TODO */
857     return -1;
858 }
859 
860 /*
861  * Check what "name" is:
862  * NODE_NORMAL: file or directory (or doesn't exist)
863  * NODE_WRITABLE: writable device, socket, fifo, etc.
864  * NODE_OTHER: non-writable things
865  */
866     int
867 mch_nodetype(char_u *name)
868 {
869     /* TODO */
870     return NODE_NORMAL;
871 }
872 
873     void
874 mch_early_init(void)
875 {
876 }
877 
878 /*
879  * Careful: mch_exit() may be called before mch_init()!
880  */
881     void
882 mch_exit(int r)
883 {
884     exiting = TRUE;
885 
886     if (raw_in)			    /* put terminal in 'normal' mode */
887     {
888 	settmode(TMODE_COOK);
889 	stoptermcap();
890     }
891     out_char('\n');
892     if (raw_out)
893     {
894 	if (term_console)
895 	{
896 	    win_resize_off();	    /* window resize events de-activated */
897 	    if (size_set)
898 		OUT_STR("\233t\233u");	/* reset window size (CSI t CSI u) */
899 	}
900 	out_flush();
901     }
902 
903 #ifdef FEAT_TITLE
904     mch_restore_title(SAVE_RESTORE_BOTH);    /* restore window title */
905 #endif
906 
907     ml_close_all(TRUE);		    /* remove all memfiles */
908 
909 #ifdef FEAT_ARP
910     if (ArpBase)
911 	CloseLibrary((struct Library *) ArpBase);
912 #endif
913     if (close_win)
914 	Close(raw_in);
915     if (r)
916 	printf(_("Vim exiting with %d\n"), r); /* somehow this makes :cq work!? */
917     exit(r);
918 }
919 
920 /*
921  * This is a routine for setting a given stream to raw or cooked mode on the
922  * Amiga . This is useful when you are using Lattice C to produce programs
923  * that want to read single characters with the "getch()" or "fgetc" call.
924  *
925  * Written : 18-Jun-87 By Chuck McManis.
926  */
927 
928 #define MP(xx)	((struct MsgPort *)((struct FileHandle *) (BADDR(xx)))->fh_Type)
929 
930 /*
931  * Function mch_settmode() - Convert the specified file pointer to 'raw' or
932  * 'cooked' mode. This only works on TTY's.
933  *
934  * Raw: keeps DOS from translating keys for you, also (BIG WIN) it means
935  *	getch() will return immediately rather than wait for a return. You
936  *	lose editing features though.
937  *
938  * Cooked: This function returns the designate file pointer to its normal,
939  *	wait for a <CR> mode. This is exactly like raw() except that
940  *	it sends a 0 to the console to make it back into a CON: from a RAW:
941  */
942     void
943 mch_settmode(int tmode)
944 {
945 #if defined(__AROS__) || defined(__amigaos4__)
946     if (!SetMode(raw_in, tmode == TMODE_RAW ? 1 : 0))
947 #else
948     if (dos_packet(MP(raw_in), (long)ACTION_SCREEN_MODE,
949 					  tmode == TMODE_RAW ? -1L : 0L) == 0)
950 #endif
951 	mch_errmsg(_("cannot change console mode ?!\n"));
952 }
953 
954 /*
955  * set screen mode, always fails.
956  */
957     int
958 mch_screenmode(char_u *arg)
959 {
960     emsg(_(e_screenmode));
961     return FAIL;
962 }
963 
964 /*
965  * Code for this routine came from the following :
966  *
967  * ConPackets.c -  C. Scheppner, A. Finkel, P. Lindsay	CBM
968  *   DOS packet example
969  *   Requires 1.2
970  *
971  * Found on Fish Disk 56.
972  *
973  * Heavely modified by mool.
974  */
975 
976 #ifndef PROTO
977 # include <devices/conunit.h>
978 #endif
979 
980 /*
981  * try to get the real window size
982  * return FAIL for failure, OK otherwise
983  */
984     int
985 mch_get_shellsize(void)
986 {
987     struct ConUnit  *conUnit;
988 #ifndef __amigaos4__
989     char	    id_a[sizeof(struct InfoData) + 3];
990 #endif
991     struct InfoData *id=0;
992 
993     if (!term_console)	/* not an amiga window */
994 	goto out;
995 
996     /* insure longword alignment */
997 #ifdef __amigaos4__
998     if (!(id = AllocDosObject(DOS_INFODATA, 0)))
999 	goto out;
1000 #else
1001     id = (struct InfoData *)(((long)id_a + 3L) & ~3L);
1002 #endif
1003 
1004     /*
1005      * Should make console aware of real window size, not the one we set.
1006      * Unfortunately, under DOS 2.0x this redraws the window and it
1007      * is rarely needed, so we skip it now, unless we changed the size.
1008      */
1009     if (size_set)
1010 	OUT_STR("\233t\233u");	/* CSI t CSI u */
1011     out_flush();
1012 
1013 #ifdef __AROS__
1014     if (!Info(raw_out, id)
1015 		 || (wb_window = (struct Window *) id->id_VolumeNode) == NULL)
1016 #else
1017     if (dos_packet(MP(raw_out), (long)ACTION_DISK_INFO, ((ULONG) id) >> 2) == 0
1018 	    || (wb_window = (struct Window *)id->id_VolumeNode) == NULL)
1019 #endif
1020     {
1021 	/* it's not an amiga window, maybe aux device */
1022 	/* terminal type should be set */
1023 	term_console = FALSE;
1024 	goto out;
1025     }
1026     if (oldwindowtitle == NULL)
1027 	oldwindowtitle = (char_u *)wb_window->Title;
1028     if (id->id_InUse == (BPTR)NULL)
1029     {
1030 	mch_errmsg(_("mch_get_shellsize: not a console??\n"));
1031 	return FAIL;
1032     }
1033     conUnit = (struct ConUnit *) ((struct IOStdReq *) id->id_InUse)->io_Unit;
1034 
1035     /* get window size */
1036     Rows = conUnit->cu_YMax + 1;
1037     Columns = conUnit->cu_XMax + 1;
1038     if (Rows < 0 || Rows > 200)	    /* cannot be an amiga window */
1039     {
1040 	Columns = 80;
1041 	Rows = 24;
1042 	term_console = FALSE;
1043 	return FAIL;
1044     }
1045 
1046     return OK;
1047 out:
1048 #ifdef __amigaos4__
1049     FreeDosObject(DOS_INFODATA, id); /* Safe to pass NULL */
1050 #endif
1051 
1052     return FAIL;
1053 }
1054 
1055 /*
1056  * Try to set the real window size to Rows and Columns.
1057  */
1058     void
1059 mch_set_shellsize(void)
1060 {
1061     if (term_console)
1062     {
1063 	size_set = TRUE;
1064 	out_char(CSI);
1065 	out_num((long)Rows);
1066 	out_char('t');
1067 	out_char(CSI);
1068 	out_num((long)Columns);
1069 	out_char('u');
1070 	out_flush();
1071     }
1072 }
1073 
1074 /*
1075  * Rows and/or Columns has changed.
1076  */
1077     void
1078 mch_new_shellsize(void)
1079 {
1080     /* Nothing to do. */
1081 }
1082 
1083 /*
1084  * out_num - output a (big) number fast
1085  */
1086     static void
1087 out_num(long n)
1088 {
1089     OUT_STR_NF(tltoa((unsigned long)n));
1090 }
1091 
1092 #if !defined(AZTEC_C) && !defined(__AROS__) && !defined(__amigaos4__)
1093 /*
1094  * Sendpacket.c
1095  *
1096  * An invaluable addition to your Amiga.lib file. This code sends a packet to
1097  * the given message port. This makes working around DOS lots easier.
1098  *
1099  * Note, I didn't write this, those wonderful folks at CBM did. I do suggest
1100  * however that you may wish to add it to Amiga.Lib, to do so, compile it and
1101  * say 'oml lib:amiga.lib -r sendpacket.o'
1102  */
1103 
1104 #ifndef PROTO
1105 /* #include <proto/exec.h> */
1106 /* #include <proto/dos.h> */
1107 # include <exec/memory.h>
1108 #endif
1109 
1110 /*
1111  * Function - dos_packet written by Phil Lindsay, Carolyn Scheppner, and Andy
1112  * Finkel. This function will send a packet of the given type to the Message
1113  * Port supplied.
1114  */
1115 
1116     static long
1117 dos_packet(
1118     struct MsgPort *pid,    /* process identifier ... (handlers message port) */
1119     long	    action, /* packet type ... (what you want handler to do)   */
1120     long	    arg)    /* single argument */
1121 {
1122 # ifdef FEAT_ARP
1123     struct MsgPort	    *replyport;
1124     struct StandardPacket   *packet;
1125     long		    res1;
1126 
1127     if (dos2)
1128 # endif
1129 	return DoPkt(pid, action, arg, 0L, 0L, 0L, 0L);	/* use 2.0 function */
1130 # ifdef FEAT_ARP
1131 
1132     replyport = (struct MsgPort *) CreatePort(NULL, 0);	/* use arp function */
1133     if (!replyport)
1134 	return (0);
1135 
1136     /* Allocate space for a packet, make it public and clear it */
1137     packet = (struct StandardPacket *)
1138 	AllocMem((long) sizeof(struct StandardPacket), MEMF_PUBLIC | MEMF_CLEAR);
1139     if (!packet) {
1140 	DeletePort(replyport);
1141 	return (0);
1142     }
1143     packet->sp_Msg.mn_Node.ln_Name = (char *) &(packet->sp_Pkt);
1144     packet->sp_Pkt.dp_Link = &(packet->sp_Msg);
1145     packet->sp_Pkt.dp_Port = replyport;
1146     packet->sp_Pkt.dp_Type = action;
1147     packet->sp_Pkt.dp_Arg1 = arg;
1148 
1149     PutMsg(pid, (struct Message *)packet);	/* send packet */
1150 
1151     WaitPort(replyport);
1152     GetMsg(replyport);
1153 
1154     res1 = packet->sp_Pkt.dp_Res1;
1155 
1156     FreeMem(packet, (long) sizeof(struct StandardPacket));
1157     DeletePort(replyport);
1158 
1159     return (res1);
1160 # endif
1161 }
1162 #endif /* !defined(AZTEC_C) && !defined(__AROS__) */
1163 
1164 /*
1165  * Call shell.
1166  * Return error number for failure, 0 otherwise
1167  */
1168     int
1169 mch_call_shell(
1170     char_u	*cmd,
1171     int		options)	/* SHELL_*, see vim.h */
1172 {
1173     BPTR	mydir;
1174     int		x;
1175     int		tmode = cur_tmode;
1176 #ifdef AZTEC_C
1177     int		use_execute;
1178     char_u	*shellcmd = NULL;
1179     char_u	*shellarg;
1180 #endif
1181     int		retval = 0;
1182 
1183     if (close_win)
1184     {
1185 	/* if Vim opened a window: Executing a shell may cause crashes */
1186 	emsg(_("E360: Cannot execute shell with -f option"));
1187 	return -1;
1188     }
1189 
1190     if (term_console)
1191 	win_resize_off();	    /* window resize events de-activated */
1192     out_flush();
1193 
1194     if (options & SHELL_COOKED)
1195 	settmode(TMODE_COOK);	    /* set to normal mode */
1196     mydir = Lock((UBYTE *)"", (long)ACCESS_READ);   /* remember current dir */
1197 
1198 #if !defined(AZTEC_C)		    /* not tested very much */
1199     if (cmd == NULL)
1200     {
1201 # ifdef FEAT_ARP
1202 	if (dos2)
1203 # endif
1204 	    x = SystemTags(p_sh, SYS_UserShell, TRUE, TAG_DONE);
1205 # ifdef FEAT_ARP
1206 	else
1207 	    x = Execute(p_sh, raw_in, raw_out);
1208 # endif
1209     }
1210     else
1211     {
1212 # ifdef FEAT_ARP
1213 	if (dos2)
1214 # endif
1215 	    x = SystemTags((char *)cmd, SYS_UserShell, TRUE, TAG_DONE);
1216 # ifdef FEAT_ARP
1217 	else
1218 	    x = Execute((char *)cmd, 0L, raw_out);
1219 # endif
1220     }
1221 # ifdef FEAT_ARP
1222     if ((dos2 && x < 0) || (!dos2 && !x))
1223 # else
1224     if (x < 0)
1225 # endif
1226     {
1227 	msg_puts(_("Cannot execute "));
1228 	if (cmd == NULL)
1229 	{
1230 	    msg_puts(_("shell "));
1231 	    msg_outtrans(p_sh);
1232 	}
1233 	else
1234 	    msg_outtrans(cmd);
1235 	msg_putchar('\n');
1236 	retval = -1;
1237     }
1238 # ifdef FEAT_ARP
1239     else if (!dos2 || x)
1240 # else
1241     else if (x)
1242 # endif
1243     {
1244 	if ((x = IoErr()) != 0)
1245 	{
1246 	    if (!(options & SHELL_SILENT))
1247 	    {
1248 		msg_putchar('\n');
1249 		msg_outnum((long)x);
1250 		msg_puts(_(" returned\n"));
1251 	    }
1252 	    retval = x;
1253 	}
1254     }
1255 #else	/* else part is for AZTEC_C */
1256     if (p_st >= 4 || (p_st >= 2 && !(options & SHELL_FILTER)))
1257 	use_execute = 1;
1258     else
1259 	use_execute = 0;
1260     if (!use_execute)
1261     {
1262 	/*
1263 	 * separate shell name from argument
1264 	 */
1265 	shellcmd = vim_strsave(p_sh);
1266 	if (shellcmd == NULL)	    /* out of memory, use Execute */
1267 	    use_execute = 1;
1268 	else
1269 	{
1270 	    shellarg = skiptowhite(shellcmd);	/* find start of arguments */
1271 	    if (*shellarg != NUL)
1272 	    {
1273 		*shellarg++ = NUL;
1274 		shellarg = skipwhite(shellarg);
1275 	    }
1276 	}
1277     }
1278     if (cmd == NULL)
1279     {
1280 	if (use_execute)
1281 	{
1282 # ifdef FEAT_ARP
1283 	    if (dos2)
1284 # endif
1285 		x = SystemTags((UBYTE *)p_sh, SYS_UserShell, TRUE, TAG_DONE);
1286 # ifdef FEAT_ARP
1287 	    else
1288 		x = !Execute((UBYTE *)p_sh, raw_in, raw_out);
1289 # endif
1290 	}
1291 	else
1292 	    x = fexecl((char *)shellcmd, (char *)shellcmd, (char *)shellarg, NULL);
1293     }
1294     else if (use_execute)
1295     {
1296 # ifdef FEAT_ARP
1297 	if (dos2)
1298 # endif
1299 	    x = SystemTags((UBYTE *)cmd, SYS_UserShell, TRUE, TAG_DONE);
1300 # ifdef FEAT_ARP
1301 	else
1302 	    x = !Execute((UBYTE *)cmd, 0L, raw_out);
1303 # endif
1304     }
1305     else if (p_st & 1)
1306 	x = fexecl((char *)shellcmd, (char *)shellcmd, (char *)shellarg,
1307 							   (char *)cmd, NULL);
1308     else
1309 	x = fexecl((char *)shellcmd, (char *)shellcmd, (char *)shellarg,
1310 					   (char *)p_shcf, (char *)cmd, NULL);
1311 # ifdef FEAT_ARP
1312     if ((dos2 && x < 0) || (!dos2 && x))
1313 # else
1314     if (x < 0)
1315 # endif
1316     {
1317 	msg_puts(_("Cannot execute "));
1318 	if (use_execute)
1319 	{
1320 	    if (cmd == NULL)
1321 		msg_outtrans(p_sh);
1322 	    else
1323 		msg_outtrans(cmd);
1324 	}
1325 	else
1326 	{
1327 	    msg_puts(_("shell "));
1328 	    msg_outtrans(shellcmd);
1329 	}
1330 	msg_putchar('\n');
1331 	retval = -1;
1332     }
1333     else
1334     {
1335 	if (use_execute)
1336 	{
1337 # ifdef FEAT_ARP
1338 	    if (!dos2 || x)
1339 # else
1340 	    if (x)
1341 # endif
1342 		x = IoErr();
1343 	}
1344 	else
1345 	    x = wait();
1346 	if (x)
1347 	{
1348 	    if (!(options & SHELL_SILENT) && !emsg_silent)
1349 	    {
1350 		msg_putchar('\n');
1351 		msg_outnum((long)x);
1352 		msg_puts(_(" returned\n"));
1353 	    }
1354 	    retval = x;
1355 	}
1356     }
1357     vim_free(shellcmd);
1358 #endif	/* AZTEC_C */
1359 
1360     if ((mydir = CurrentDir(mydir)) != 0) /* make sure we stay in the same directory */
1361 	UnLock(mydir);
1362     if (tmode == TMODE_RAW)
1363 	settmode(TMODE_RAW);		/* set to raw mode */
1364 #ifdef FEAT_TITLE
1365     resettitle();
1366 #endif
1367     if (term_console)
1368 	win_resize_on();		/* window resize events activated */
1369     return retval;
1370 }
1371 
1372 /*
1373  * check for an "interrupt signal"
1374  * We only react to a CTRL-C, but also clear the other break signals to avoid
1375  * trouble with lattice-c programs.
1376  */
1377     void
1378 mch_breakcheck(int force)
1379 {
1380    if (SetSignal(0L, (long)(SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F)) & SIGBREAKF_CTRL_C)
1381 	got_int = TRUE;
1382 }
1383 
1384 /* this routine causes manx to use this Chk_Abort() rather than its own */
1385 /* otherwise it resets our ^C when doing any I/O (even when Enable_Abort */
1386 /* is zero).  Since we want to check for our own ^C's			 */
1387 
1388 #ifdef _DCC
1389 #define Chk_Abort chkabort
1390 #endif
1391 
1392 #ifdef LATTICE
1393 void __regargs __chkabort(void);
1394 
1395 void __regargs __chkabort(void)
1396 {}
1397 
1398 #else
1399     long
1400 Chk_Abort(void)
1401 {
1402     return(0L);
1403 }
1404 #endif
1405 
1406 /*
1407  * mch_expandpath() - this code does wild-card pattern matching using the arp
1408  *		      routines.
1409  *
1410  * "pat" has backslashes before chars that are not to be expanded.
1411  * Returns the number of matches found.
1412  *
1413  * This is based on WildDemo2.c (found in arp1.1 distribution).
1414  * That code's copyright follows:
1415  *	Copyright (c) 1987, Scott Ballantyne
1416  *	Use and abuse as you please.
1417  */
1418 
1419 #ifdef __amigaos4__
1420 # define	ANCHOR_BUF_SIZE	1024
1421 #else
1422 # define ANCHOR_BUF_SIZE (512)
1423 # define ANCHOR_SIZE (sizeof(struct AnchorPath) + ANCHOR_BUF_SIZE)
1424 #endif
1425 
1426     int
1427 mch_expandpath(
1428     garray_T	*gap,
1429     char_u	*pat,
1430     int		flags)		/* EW_* flags */
1431 {
1432     struct AnchorPath	*Anchor;
1433     LONG		Result;
1434     char_u		*starbuf, *sp, *dp;
1435     int			start_len;
1436     int			matches;
1437 #ifdef __amigaos4__
1438     struct TagItem	AnchorTags[] = {
1439 	{ADO_Strlen, ANCHOR_BUF_SIZE},
1440 	{ADO_Flags, APF_DODOT|APF_DOWILD|APF_MultiAssigns},
1441 	{TAG_DONE, 0L}
1442     };
1443 #endif
1444 
1445     start_len = gap->ga_len;
1446 
1447     /* Get our AnchorBase */
1448 #ifdef __amigaos4__
1449     Anchor = AllocDosObject(DOS_ANCHORPATH, AnchorTags);
1450 #else
1451     Anchor = (struct AnchorPath *)alloc_clear((unsigned)ANCHOR_SIZE);
1452 #endif
1453     if (Anchor == NULL)
1454 	return 0;
1455 
1456 #ifndef __amigaos4__
1457     Anchor->ap_Strlen = ANCHOR_BUF_SIZE;  /* ap_Length not supported anymore */
1458 # ifdef APF_DODOT
1459     Anchor->ap_Flags = APF_DODOT | APF_DOWILD;	/* allow '.' for current dir */
1460 # else
1461     Anchor->ap_Flags = APF_DoDot | APF_DoWild;	/* allow '.' for current dir */
1462 # endif
1463 #endif
1464 
1465 #ifdef FEAT_ARP
1466     if (dos2)
1467     {
1468 #endif
1469 	/* hack to replace '*' by '#?' */
1470 	starbuf = alloc((unsigned)(2 * STRLEN(pat) + 1));
1471 	if (starbuf == NULL)
1472 	    goto Return;
1473 	for (sp = pat, dp = starbuf; *sp; ++sp)
1474 	{
1475 	    if (*sp == '*')
1476 	    {
1477 		*dp++ = '#';
1478 		*dp++ = '?';
1479 	    }
1480 	    else
1481 		*dp++ = *sp;
1482 	}
1483 	*dp = NUL;
1484 	Result = MatchFirst((UBYTE *)starbuf, Anchor);
1485 	vim_free(starbuf);
1486 #ifdef FEAT_ARP
1487     }
1488     else
1489 	Result = FindFirst((char *)pat, Anchor);
1490 #endif
1491 
1492     /*
1493      * Loop to get all matches.
1494      */
1495     while (Result == 0)
1496     {
1497 #ifdef __amigaos4__
1498 	addfile(gap, (char_u *)Anchor->ap_Buffer, flags);
1499 #else
1500 	addfile(gap, (char_u *)Anchor->ap_Buf, flags);
1501 #endif
1502 #ifdef FEAT_ARP
1503 	if (dos2)
1504 #endif
1505 	    Result = MatchNext(Anchor);
1506 #ifdef FEAT_ARP
1507 	else
1508 	    Result = FindNext(Anchor);
1509 #endif
1510     }
1511     matches = gap->ga_len - start_len;
1512 
1513     if (Result == ERROR_BUFFER_OVERFLOW)
1514 	emsg(_("ANCHOR_BUF_SIZE too small."));
1515     else if (matches == 0 && Result != ERROR_OBJECT_NOT_FOUND
1516 			  && Result != ERROR_DEVICE_NOT_MOUNTED
1517 			  && Result != ERROR_NO_MORE_ENTRIES)
1518 	emsg(_("I/O ERROR"));
1519 
1520     /*
1521      * Sort the files for this pattern.
1522      */
1523     if (matches)
1524 	qsort((void *)(((char_u **)gap->ga_data) + start_len),
1525 				  (size_t)matches, sizeof(char_u *), sortcmp);
1526 
1527     /* Free the wildcard stuff */
1528 #ifdef FEAT_ARP
1529     if (dos2)
1530 #endif
1531 	MatchEnd(Anchor);
1532 #ifdef FEAT_ARP
1533     else
1534 	FreeAnchorChain(Anchor);
1535 #endif
1536 
1537 Return:
1538 #ifdef __amigaos4__
1539     FreeDosObject(DOS_ANCHORPATH, Anchor);
1540 #else
1541     vim_free(Anchor);
1542 #endif
1543 
1544     return matches;
1545 }
1546 
1547     static int
1548 sortcmp(const void *a, const void *b)
1549 {
1550     char *s = *(char **)a;
1551     char *t = *(char **)b;
1552 
1553     return pathcmp(s, t, -1);
1554 }
1555 
1556 /*
1557  * Return TRUE if "p" has wildcards that can be expanded by mch_expandpath().
1558  */
1559     int
1560 mch_has_exp_wildcard(char_u *p)
1561 {
1562     for ( ; *p; MB_PTR_ADV(p))
1563     {
1564 	if (*p == '\\' && p[1] != NUL)
1565 	    ++p;
1566 	else if (vim_strchr((char_u *)"*?[(#", *p) != NULL)
1567 	    return TRUE;
1568     }
1569     return FALSE;
1570 }
1571 
1572     int
1573 mch_has_wildcard(char_u *p)
1574 {
1575     for ( ; *p; MB_PTR_ADV(p))
1576     {
1577 	if (*p == '\\' && p[1] != NUL)
1578 	    ++p;
1579 	else
1580 	    if (vim_strchr((char_u *)
1581 #  ifdef VIM_BACKTICK
1582 				    "*?[(#$`"
1583 #  else
1584 				    "*?[(#$"
1585 #  endif
1586 						, *p) != NULL
1587 		    || (*p == '~' && p[1] != NUL))
1588 		return TRUE;
1589     }
1590     return FALSE;
1591 }
1592 
1593 /*
1594  * With AmigaDOS 2.0 support for reading local environment variables
1595  *
1596  * Two buffers are allocated:
1597  * - A big one to do the expansion into.  It is freed before returning.
1598  * - A small one to hold the return value.  It is kept until the next call.
1599  */
1600     char_u *
1601 mch_getenv(char_u *var)
1602 {
1603     int		    len;
1604     UBYTE	    *buf;		/* buffer to expand in */
1605     char_u	    *retval;		/* return value */
1606     static char_u   *alloced = NULL;	/* allocated memory */
1607 
1608 #ifdef FEAT_ARP
1609     if (!dos2)
1610 	retval = (char_u *)getenv((char *)var);
1611     else
1612 #endif
1613     {
1614 	VIM_CLEAR(alloced);
1615 	retval = NULL;
1616 
1617 	buf = alloc(IOSIZE);
1618 	if (buf == NULL)
1619 	    return NULL;
1620 
1621 	len = GetVar((UBYTE *)var, buf, (long)(IOSIZE - 1), (long)0);
1622 	if (len >= 0)
1623 	{
1624 	    retval = vim_strsave((char_u *)buf);
1625 	    alloced = retval;
1626 	}
1627 
1628 	vim_free(buf);
1629     }
1630 
1631     /* if $VIM is not defined, use "vim:" instead */
1632     if (retval == NULL && STRCMP(var, "VIM") == 0)
1633 	retval = (char_u *)"vim:";
1634 
1635     return retval;
1636 }
1637 
1638 /*
1639  * Amiga version of setenv() with AmigaDOS 2.0 support.
1640  */
1641 /* ARGSUSED */
1642     int
1643 mch_setenv(char *var, char *value, int x)
1644 {
1645 #ifdef FEAT_ARP
1646     if (!dos2)
1647 	return setenv(var, value);
1648 #endif
1649 
1650     if (SetVar((UBYTE *)var, (UBYTE *)value, (LONG)-1, (ULONG)GVF_LOCAL_ONLY))
1651 	return 0;   /* success */
1652     return -1;	    /* failure */
1653 }
1654