1 /* userio.c -- handy user interface functions */
2 /* Copyright 1989 Carnegie Mellon University */
3 
4 /*****************************************************************************
5 *       Change Log
6 *  Date | Change
7 *-----------+-----------------------------------------------------------------
8 * 21-May-86 | Created
9 * 11-Aug-87 | F.H: Added clear_abort(), stop()
10 *    May-88 | JCD : AMIGA VERSION
11 * 11-Jun-88 | RBD: disable printing of GDEBUG messages
12 * 12-Oct-88 | JCD : EXCLUSIVE AMIGA VERSION
13 * 13-Apr-89 | JCD : New portable version.
14 *  5-Apr    | JDW : Further changes
15 *  2-Mar-92 | GWL : Little changes to satisfy compiler
16 * 19-Nov-92 | JDZ : Mach tty io threads
17 * 28-Apr-03 |  DM : portability changes. true->TRUE, false->FALSE
18 *****************************************************************************/
19 
20 /* Notes on ascii input:
21 
22 Input is complicated because different systems have varying input models,
23 especially with regard to handling ^C.  The CMT model handles ^C and ^G as
24 special characters, and these do not cause software interrupts.  Also, the
25 lowest level of the CMT model does not support line editing: Every character
26 is acted upon immediately.  This has two implications:
27 (1) CMT must "read ahead" looking for ^C and ^G characters.  This is handled
28 by the check_aborted() procedure, which reads characters into the
29 type_ahead[] array.
30 (2) CMT must do its own line editing.  This is handled by the ggets() routine.
31 
32 A number of functions support ascii input, only some of which are visible
33 to the application programmer.  Let's start at the top-level and work down;
34 each of the following calls the routine below it:
35 
36 ggets() - gets a string with line editing support.  This function is fairly
37 machine independent, except for some backspace-and-erase control character
38 code sequences.
39 
40 ggetchar() - gets a raw character.  This function calls wait_ascii()
41  and echoes it.  Note that it may return ABORT_CHAR or BREAK_CHAR.
42 
43 wait_ascii() - gets a raw character without echo and without character
44 code translation.  wait_ascii() either polls get_ascii() or uses some
45 kind of system-dependent event waiting.  Returns ABORT_CHAR or BREAK_CHAR
46 immediately if abort_flag has been set, regardless of whether there is
47 new ascii input.
48 
49 get_ascii() - checks to see if a character is available.  (Using
50 check_aborted().)
51 The only dependency here is on the Amiga, we restart input when buffer goes
52 from full to non-full.
53 
54 check_aborted() - looks for input by calling ascii_input.  If found,
55 put the input into the type_ahead[] buffer. Returns abort_flag.
56 
57 ascii_input() - lowest level of input; just gets a character if there is
58 one.  Does conversion from RETURN (\r) to EOL (\n).  The Amiga handles
59 this in-line directly in check_aborted().
60 
61 Here's a quick summary:
62 
63 ggets = ggetchar + line editing & string building
64 ggetchar = wait_ascii + character echo
65 wait_ascii = get_ascii + wait for character
66 get_ascii = check_aborted + pull char from buffer
67 check_aborted = ascii_input + test for ^C,^G + put in buffer
68 ascii_input = poll for char + CR->EOL conversion
69 
70 */
71 
72 #include "switches.h"
73 
74 #include <stdio.h>
75 #include <string.h>
76 #if HAS_STDLIB_H
77 #include <stdlib.h> /* normal case */
78 #endif
79 
80 
81 #ifdef MACINTOSH
82 # include "StandardFile.h"
83   /* added for ThinkC 7 */
84 # ifdef THINK_C
85 #  include <pascal.h>
86 # endif
87 #endif
88 
89 #ifdef AMIGA
90 
91 # ifdef AZTEC
92 #  include "functions.h"
93 # else /* LATTICE */
94 #  include "amiga.h"
95 #  include "stdarg.h"
96 # endif
97 
98 # include "intuition/intuition.h"
99 # include "devices/console.h"
100 #endif
101 
102 #include "ctype.h"
103 #include "stdio.h"
104 #include "cext.h"
105 #include "userio.h"
106 
107 #ifdef MICROSOFT
108 #include "signal.h"
109 #endif
110 
111 #ifdef UNIX_MACH
112 #include <varargs.h>
113 #include <midistruct.h>
114 extern char a_in;
115 extern int a_in_flag;
116 extern int i_am_running;
117 #ifdef RTMach
118 extern itc_mutex_t a_mutex;
119 extern itc_condition_t a_cond, a_in_cond;
120 #define A_LOCK() itc_mutex_lock(&a_mutex)
121 #define A_UNLOCK() itc_mutex_unlock(&a_mutex)
122 #else /* RTMach */
123 extern struct mutex a_mutex;
124 extern struct condition a_cond, a_in_cond;
125 #define A_LOCK() mutex_lock(&a_mutex)
126 #define A_UNLOCK() mutex_unlock(&a_mutex)
127 #endif /* RTMach */
128 #endif
129 
130 #ifdef DOTS_FOR_ARGS
131 #include <stdarg.h>
132 #endif
133 
134 #ifdef UNIX
135 #include <sys/param.h>
136 #include <sys/resource.h>
137 #include "cmtio.h"
138 #ifdef _IBMR2
139 #define NBBY 8
140 #define OPEN_MAX 2000
141 #endif
142 #include <sys/select.h>
143 #endif
144 
145 #ifdef linux
146 #include <sys/time.h> /* for FD_ZERO / FD_SET */
147 #endif
148 
149 extern int debug;
150 
151 #ifdef NYQUIST
152 /* get definitions for stdputstr, etc. */
153 #include "xlisp.h"
154 #endif
155 
156 int     IOinputfd;      /* input file descriptor (usually 0) */
157 
158 int     IOnochar;       /* Value to be returned by IOgetchar()
159                            where there is no input to be had */
160 
161 /****************************************************************************
162 *
163 *       routines private to this module
164 *
165 ****************************************************************************/
166 
167 int GetReadFileName(void);
168 int GetWriteFileName(void);
169 
170 #ifdef MACINTOSH
171 private void PtoC_StrCopy(char *p1, char *p2);
172 #endif
173 
174 #ifdef AMIGA
175         char ConGetChar();
176         ConMayGetChar();
177 private void ConRead();
178 private void ConPutStr();
179 private void ConPutChar();
180 UBYTE   ascii_signal();
181 UBYTE   KeybSig();
182 #endif
183 
184 
185 /****************************************************************************
186 *
187 * variables shared with other modules
188 *
189 ****************************************************************************/
190 
191 public int abort_flag;          /* control C or control G equivalent */
192 public int redirect_flag;		/* check whether the I/O has been redirected--
193                                     Added by Ning Hu	Apr.2001*/
194 /* extern void musicterm(); */ /*DMH: from macmidi.c, to allow abort_check*/
195 public boolean ascii_input(char *c);
196 
197 /****************************************************************************
198 *
199 * variables private to this module
200 *
201 ****************************************************************************/
202 
203 #ifdef AMIGA
204 struct IntuitionBase *IntuitionBase;
205 private struct IOStdReq *ConOutReq;
206 private struct MsgPort *ConOutPort;
207 private struct IOStdReq *ConInReq;
208 private struct MsgPort *ConInPort;
209 private char KeyBuff[16];
210 private struct Window *Window;
211 private struct NewWindow NewWindow = {
212     0,11,640,189,
213     0,1,
214     NULL,
215     SMART_REFRESH | ACTIVATE | WINDOWDRAG | WINDOWDEPTH |
216         WINDOWSIZING,
217     NULL,NULL,
218     (STRPTR) "Carnegie Mellon University MIDI Toolkit for Commodore AMIGA",
219     NULL,NULL,
220     100,25,640,200,
221     WBENCHSCREEN };
222 #endif
223 
224 #ifdef MACINTOSH
225 private OSType io_file_type = 0x3F3F3F3F; /* '????' */
226 private OSType io_file_creator = 0x3F3F3F3F; /* '????' */
227 #endif
228 
229 #define type_ahead_max 100
230 char type_ahead[100];
231 int type_ahead_head = 0;
232 int type_ahead_tail = 0;
233 int type_ahead_count = 0;
234 
235 
236 #ifdef DOS
237 #ifdef BORLAND
c_break(void)238 int c_break(void)
239 {
240     gprintf(TRANS, " BREAK ");
241     abort_flag = ABORT_LEVEL;
242     return 1; /* non-zero means do not exit program */
243 }
244 #endif
245 #ifdef MICROSOFT
c_break(int sig)246 void c_break(int sig)
247 {
248     abort_flag = ABORT_LEVEL;
249     /* The CTRL+C interrupt must be reset to our handler since
250      * by default it is reset to the system handler.
251      */
252     signal(SIGINT, c_break); /* assume this succeeds */
253 }
254 #endif
255 #endif
256 
257 #ifdef MACINTOSH
258 #ifdef NYQUIST
259 void FlushOutput (void);
260 #endif
261 #endif
262 
263 /* gflush -- flush output produced by gprintf, etc. */
264 /**/
gflush(void)265 void gflush(void)
266 {
267 #ifdef MACINTOSH
268 #ifdef NYQUIST
269     FlushOutput();
270 #else
271     fflush(stdout);    /* make sure any prompts or errors have been output */
272     fflush(STDERR);
273 #endif /* NYQUIST */
274 #endif /* MACINTOSH */
275 #ifdef UNIX
276     fflush(stdout);    /* make sure any prompts or errors have been output */
277     fflush(STDERR);
278 #endif
279 }
280 
281 
282 /****************************************************************************
283 *                                io_init
284 *
285 *  I added this init function for the AMIGA version.
286 *
287 *  io_init : opens a window
288 *  and exits if initialisation can not be done properly.
289 *  registers cleanup calls to carefully deallocate resources.
290 *
291 *  io_init is not amiga specific : the simplest version
292 *  of io_init could be a clear screen statement for example, and a
293 *  printf("Good bye !\n") on exit.
294 *
295 *  for the Mac, it seems that ascii_input doesn't work unless getchar() is
296 *  called first.  I assume this is because getchar() initializes the ability
297 *  of the window to process type-in, so there is probably a way to set this
298 *  directly.  If you figure it out, let me know.  -RBD
299 *
300 *****************************************************************************/
301 
302 void
io_init()303 io_init()
304 {
305 
306 #ifdef AMIGA
307     int error;
308 
309     /* Window and console initialisation  */
310     IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",1L);
311     if (IntuitionBase == NULL) EXIT(1);
312     cu_register((cu_fn_type) CloseLibrary, IntuitionBase);
313 
314     ConOutPort = CreatePort("conoutport", 0L);
315     if (ConOutPort == NULL) EXIT(1);
316     cu_register((cu_fn_type) DeletePort, ConOutPort);
317 
318     ConOutReq = CreateStdIO(ConOutPort);
319     if (ConOutReq == NULL) EXIT(1);
320     cu_register((cu_fn_type) DeleteStdIO, ConOutReq);
321 
322     ConInPort = CreatePort("coninport", 0L);
323     if (ConInPort == NULL) EXIT(1);
324     cu_register((cu_fn_type) DeletePort, ConInPort);
325 
326     ConInReq = CreateStdIO(ConInPort);
327     if (ConInReq == NULL) EXIT(1);
328     cu_register((cu_fn_type) DeleteStdIO, ConInReq);
329 
330     Window = OpenWindow(&NewWindow);
331     if (Window == NULL) EXIT(1);
332     cu_register((cu_fn_type) CloseWindow, Window);
333 
334     ConOutReq->io_Data = (APTR)Window;
335     ConOutReq->io_Length = sizeof(*Window);
336     error = OpenDevice("console.device", 0L, (struct IORequest *) ConOutReq, 0L);
337     ConInReq->io_Device = ConOutReq->io_Device;
338     ConInReq->io_Unit = ConOutReq->io_Unit;
339     if (error != NULL) EXIT(1);
340     cu_register((cu_fn_type) CloseDevice, ConOutReq);
341 
342     ConInReq->io_Command = CMD_READ;
343     ConInReq->io_Data = (APTR)KeyBuff;
344     ConInReq->io_Length = 1;
345     SendIO((struct IORequest *) ConInReq);
346 #endif
347 
348 #ifdef UNIX
349 #ifndef BUFFERED_SYNCHRONOUS_INPUT
350     IOsetup(0 /* standard input */);
351     cu_register((cu_fn_type) IOcleanup, NULL);
352 #endif
353 #endif
354 
355 #ifdef MACINTOSH
356 #ifndef NYQUIST /* don't need this if we're in Nyquist */
357     char s[100];
358     printf("Type <return> to start: ");
359     fgets(s, 100, stdin);
360 #endif /* NYQUIST */
361 #endif
362 
363 #ifdef DOS
364 #ifdef MICROSOFT
365     if (signal(SIGINT, c_break) == SIG_ERR) {
366         gprintf(ERROR, "Couldn't set Ctrl C handler\n");
367         EXIT(1);
368     }
369 #else
370 #ifdef BORLAND
371     ctrlbrk(c_break);
372 #else
373     ... we are in DOS, but neither MICROSOFT nor BORLAND,
374     please set up a control C handler here...
375 #endif
376 #endif
377 #endif
378 }
379 
380 #ifdef MACINTOSH
381 
382 /****************************************************************************
383 *                                       abort_check
384 * Effect:
385 *       exit nicely if the aborted flag is set
386 ****************************************************************************/
387 
abort_check()388 public void abort_check()
389 {
390     if (abort_flag) clean_exit();
391 }
392 
393 
394 /****************************************************************************
395 *                                       clean_exit
396 * Effect:
397 *       clean up and exit
398 ****************************************************************************/
399 
clean_exit()400 public void clean_exit()
401 {
402         gprintf(TRANS, "Exiting...\n");
403         EXIT(1);
404 }
405 
406 #ifdef MPW
407 /****************************************************************************
408 *                                       cleanup_abort_handler
409 * Effect:
410 *       shuts down abort watcher
411 ****************************************************************************/
412 
cleanup_abort_handler()413 public void cleanup_abort_handler()
414 {
415         (void) sigset(SIGINT, SIG_DFL); /* deactivate abort watcher */
416 }
417 
418 
419 /****************************************************************************
420 *                                       init_abort_handler
421 * Effect:
422 *       starts abort watcher
423 *       aborted flag is set to FALSE
424 ****************************************************************************/
425 
init_abort_handler()426 public void init_abort_handler()
427 {
428         abort_flag = FALSE;
429         (void) sigset(SIGINT, abort_watcher);   /* activate abort watcher */
430 }
431 #endif
432 
433 #endif
434 
435 
436 /****************************************************************************
437 *               askbool
438 * Inputs:
439 *    char *prompt: string to prompt for user input
440 *    int deflt: TRUE or FALSE default
441 * Returns:
442 *    boolean: TRUE or FALSE as entered by user
443 * Effect:
444 *    prompts user for yes or no input, returns result
445 ****************************************************************************/
446 
askbool(prompt,deflt)447 int askbool(prompt, deflt)
448 char *prompt;
449 int deflt;
450 {
451 #define undefined -1
452     char defchar;    /* the default answer */
453     char c;     /* user input */
454     char in_string[100];
455     int result = -1;    /* the result: -1 = undefined, 0 = FALSE, 1 = TRUE */
456     if (deflt) defchar = 'y';
457     else defchar = 'n';
458     while (result == undefined) {
459         gprintf(TRANS, "%s? [%c]: ", prompt, defchar);
460         ggets(in_string);
461         c = in_string[0];
462         if (islower(c)) c = toupper(c);
463         if (c == 'Y') result = TRUE;
464         else if (c == 'N') result = FALSE;
465         else if (c == EOS) result = deflt;
466         else if (abort_flag) result = deflt;
467         /* space before Please to separate from user's type-in: */
468         else gprintf(TRANS, " Please type Y or N.\n");
469     }
470     if (abort_flag == BREAK_LEVEL) {
471         abort_flag = 0;
472         result = deflt;
473         gprintf(TRANS, "\n");
474     }
475     return result;
476 }
477 
478 
479 /****************************************************************************
480 *               fileopen
481 * Inputs:
482 *    char *deflt: the default file name (e.g. from command line)
483 *    char *extension: default extension
484 *    char *mode: read ("r") or write ("w")
485 *    char *prompt: prompt for user
486 * Returns:
487 *    opened file pointer
488 * Effect:
489 *    opens file, prompts for user input if necessary and warns about
490 *    possible confusion.  If deflt is a null string or NULL, the user will
491 *    be prompted for a name.     The routine loops until a file is opened.
492 *    If the mode is "r", a check is made to see if the file exists
493 *    with and without the extension.     If both exist a warning is given.
494 *    For mode "w", a check is made to see if the file will be overwritten.
495 *    The extension is automatically added if the default or user-typed
496 *    file has no "."     At the bottom of the loop body, if no file has
497 *    been opened, the user is prompted for another file name.
498 ****************************************************************************/
499 
500 char fileopen_name[100];        /* name of the opened file */
501 
fileopen(deflt,extension,mode,prompt)502 FILE *fileopen(deflt, extension, mode, prompt)
503   char *deflt;
504   char *extension;    /* default extension */
505   char *mode;   /* read "r" or write "w" */
506   char *prompt;    /* prompt for user */
507 {
508     char extname[100];          /* trial name with extension added */
509     FILE *fp = NULL;            /* file corresponding to filename */
510     FILE *fpext;                /* file corresponding to extname */
511     char *problem = NULL;       /* tells user why he has to try again */
512 
513     if (!deflt) deflt = "";     /* treat NULL as the empty string */
514     strcpy(fileopen_name, deflt);
515     /* keep trying until a good file is found: */
516     while (fp == NULL) {
517         /* avoid null file names: */
518         while (strlen(fileopen_name) == 0) {
519 #ifndef MACINTOSH
520             gprintf(TRANS, "%s : ", prompt);
521             ggets(fileopen_name);
522             if (abort_flag) {
523                 if (abort_flag == BREAK_LEVEL) {
524                     abort_flag = 0;
525                     /* type return since user didn't... */
526                     gprintf(TRANS, "\n");
527                 }
528                 return NULL;
529             }
530 #else /* use Macintosh file dialog */
531             if (mode[0] == 'r') {
532                  if (!GetReadFileName(fileopen_name)) return NULL;
533              } else if (mode[0] == 'w') {
534                  if (!(GetWriteFileName(fileopen_name, prompt))) return NULL;
535              } else {
536                  gprintf(ERROR, "(fileopen) internal error: bad mode\n");
537              }
538 #endif /* MACINTOSH */
539         }
540         if (mode[0] == 'r') {
541             strcpy(extname, fileopen_name);
542             strcat(extname, ".");
543             strcat(extname, extension);
544             fp = NULL;
545             fpext = NULL;
546             if (ok_to_open(fileopen_name, mode)) {
547                 fp = fopen(fileopen_name, mode);
548             }
549             if (ok_to_open(extname, mode)) {
550                 fpext = fopen(extname, mode);
551             }
552             if (fp != NULL && fpext != NULL) {
553                 gprintf(TRANS,
554                 "warning: both %s and %s exist.     %s will be used.\n",
555                 fileopen_name, extname, fileopen_name);
556                 fclose(fpext);
557             } else if (fpext != NULL) {
558                 fp = fpext;
559                 strcpy(fileopen_name, extname);  /* remember what was opened */
560             }
561             if (fp == NULL) problem = "Couldn't find %s.\n";
562         } else if (mode[0] == 'w') {
563             boolean added_extension = FALSE;
564 
565             /* add the extension if there is no '.' in the file name */
566             if (!strchr(fileopen_name, '.')) {
567                 strcat(fileopen_name, ".");
568                 strcat(fileopen_name, extension);
569                 added_extension = TRUE;
570             }
571             if (TRUE
572 #ifdef MACINTOSH
573                 /* file open dialog already asked user to confirm unless we're
574                  * adding an extension
575                  */
576                 && added_extension
577 #endif
578                 ) {
579                 fp = NULL;
580                 if (ok_to_open(fileopen_name, "r"))
581                     fp = fopen(fileopen_name, "r");
582                 if (fp != NULL) {
583                     char question[100];
584                     fclose(fp);
585                     strcpy(question, "OK to overwrite ");
586                     strcat(question, fileopen_name);
587                     if (!askbool(question, FALSE)) {
588                         fp = NULL;
589                         problem = "\n";
590                         goto tryagain;
591                     }
592                 }
593             }
594             fp = NULL;
595             if (ok_to_open(fileopen_name, mode))
596                 fp = fopen(fileopen_name, mode);
597             if (fp == NULL) problem = "Couldn't create %s.\n";
598         }
599   tryagain:
600         if (fp == NULL) {
601             gprintf(TRANS, problem, fileopen_name);
602             gprintf(TRANS,"Try again.\n");
603             fileopen_name[0] = EOS;
604         }
605     }
606     return fp;
607 }
608 
609 #ifdef MACINTOSH
610 
GetReadFileName(name)611 static int GetReadFileName(name)
612 char *name;
613 {
614     static Point p = {100,100};
615     SFReply loadfile;
616     SFTypeList mytypes;
617 
618     mytypes[0] = 0x54455854; /* 'TEXT' */
619     mytypes[1] = 0x4D696469; /* 'Midi' */
620     mytypes[2] = 0x3F3F3F3F; /* '????' */
621 /* could put any filter here (i.e. giofilefileter) */
622     SFGetFile(p, "\p", NULL, 3, mytypes, 0L, &loadfile);
623     if (loadfile.good) {
624         SetVol(0L,loadfile.vRefNum);
625         PtoC_StrCopy((char *) &loadfile.fName, name);
626         return(true);
627     } else return(false);
628 }
629 
630 
GetWriteFileName(fn,str)631 static int GetWriteFileName(fn, str)
632 char *fn, *str;
633 {
634         static Point SFPwhere = { 106, 104 };
635         unsigned char Pstr[100], Pfn[100];
636         SFReply reply;
637 
638         strcpy((char *)Pstr, str);
639         CtoPstr((char *)Pstr);
640         strcpy((char *)Pfn, fn);
641         CtoPstr((char *)Pfn);
642 
643         SFPutFile(SFPwhere, (ConstStr255Param) Pstr, (ConstStr255Param) Pfn,
644                   0L, &reply);
645         if (reply.good) {
646                 SetVol (0L,reply.vRefNum);
647                 PtoC_StrCopy((char *) &reply.fName, fn);
648                 return(true);
649         }
650         else return(false);
651 }
652 
653 
PtoC_StrCopy(p1,p2)654 void PtoC_StrCopy(p1, p2)
655   register char *p1, *p2;
656 /* copies a pascal string from p1 to p2 */
657 {
658         register int len;
659 
660         len = *p1++;
661         while (--len>=0) *p2++=*p1++;
662         *p2 = '\0';
663 }
664 
665 
get_file_info(char * filename,OSType * file_type,OSType * file_creator)666 boolean get_file_info(char *filename, OSType *file_type, OSType *file_creator)
667 {
668     short rc;           /* toolbox return code */
669     FInfo fi;           /* toolbox file info */
670     char fn[101];       /* temporary file name */
671 
672     strcpy(fn, filename);
673     CtoPstr(fn);
674     if (rc = GetFInfo((byte*)fn, 0, &fi)) {
675         gprintf(ERROR, "rc from GetFInfo=%d\n", rc);
676         gprintf(ERROR, "unable to get file type\n");
677         *file_type = 0x3F3F3F3F; /* '????' */
678         *file_creator = 0x3F3F3F3F; /* '????' */
679         return FALSE;
680     } else /* set file type & creator */ {
681         if (debug) gprintf(TRANS, "File Type: '%.4s'  File Creator: '%.4s'\n",
682                            &fi.fdType, &fi.fdCreator );
683         *file_type = fi.fdType;
684         *file_creator = fi.fdCreator;
685     }
686     return TRUE;
687 }
688 
689 
put_file_info(char * filename,OSType file_type,OSType file_creator)690 boolean put_file_info(char *filename, OSType file_type, OSType file_creator)
691 {
692     short rc;           /* toolbox return code */
693     FInfo fi;           /* toolbox file info */
694     char fn[101];       /* temporary file name */
695 
696     if (debug) gprintf(TRANS,"set file %s to become type '%.4s'\n", filename, &file_type);
697     strcpy(fn, filename);
698     CtoPstr(fn);
699     if (rc = GetFInfo((byte*)fn, 0, &fi)) {
700         gprintf(TRANS, "rc from GetFInfo=%d\n", rc);
701         gprintf(TRANS, "unable to set file type\n");
702     } else /* set file type & creator */ {
703         if (debug) gprintf(TRANS, "File Type: '%.4s'  File Creator: '%.4s'\n",
704                            &fi.fdType, &fi.fdCreator );
705         fi.fdType = file_type;
706         fi.fdCreator = file_creator;
707         if (rc=SetFInfo((byte*)fn, 0, &fi)) {
708             gprintf(TRANS, "rc from SetFInfo=%d\n", rc);
709             gprintf(TRANS, "unable to set file type\n");
710         } else if (rc=GetFInfo((byte*)fn, 0, &fi)) {
711             gprintf(TRANS, "rc from GetFInfo=%d\n", rc);
712             gprintf(TRANS, "unable to verify file type\n");
713         } else {
714             if (debug) gprintf(TRANS, "File Type: '%.4s'  File Creator: '%.4s'\n",
715                                &fi.fdType, &fi.fdCreator );
716         }
717     }
718 }
719 #endif /* MACINTOSH */
720 
721 
722 
723 #ifdef AMIGA
724 /***************************************************************
725 *                           ascii_signal
726 *
727 * Input : none
728 * Ouput : none
729 * Return: the signal that will be raised on ascii input
730 * Effect: none
731 ***************************************************************/
732 
ascii_signal()733 UBYTE ascii_signal()
734 {
735     return ConInPort->mp_SigBit;
736 }
737 #endif
738 
739 /* check_aborted -- see if any characters are available, check for ctrl C */
740 
check_aborted()741 int check_aborted()
742 {
743         char in_c;
744 #ifdef AMIGA
745     if (GetMsg(ConInPort)) {
746         in_c = KeyBuff[0];
747         if (in_c == '\r') in_c = '\n';
748 #endif
749 #ifndef AMIGA   /* DOS or MACINTOSH or UNIX */
750     if (type_ahead_count < type_ahead_max && ascii_input(&in_c)) {
751 #endif
752         type_ahead[type_ahead_tail] = in_c;
753         if (in_c == ABORT_CHAR) abort_flag = ABORT_LEVEL;
754         else if (!abort_flag && in_c == BREAK_CHAR)
755             abort_flag = BREAK_LEVEL;
756 
757         /* go ahead and insert anything into buffer, including ^C, ^G: */
758         type_ahead_count++;
759         type_ahead_tail++;
760         if (type_ahead_tail == type_ahead_max) type_ahead_tail = 0;
761 
762 #ifdef AMIGA
763         if (type_ahead_count < type_ahead_max) ConRead();
764 #endif
765     }
766     return abort_flag;
767 }
768 
769 
770 /****************************************************************************
771 *                   readln
772 * Inputs:
773 *    FILE * fp: File to read from
774 * Effect:
775 *    Reads and discards characters until a newline is seen
776 ****************************************************************************/
777 
778 void readln(fp)
779   register FILE *fp;
780 {
781     register int c;
782     while (((c = getc(fp)) != '\n') && (c != EOF)) ;
783 }
784 
785 
786 /****************************************************************************
787 *                   gprintf
788 * Inputs:
789 *    int * handler: pointer to output handler (say, a window)
790 *        or one of {TRANS, ERROR, FATAL, GDEBUG} from userio.h
791 *    char * format: a null-terminated printf style format string
792 *    int arg0 through arg14: a variable number of arguments for printf
793 * Effect:
794 *    formats and outputs the specified information to an output handler.
795 *    this is a system-independent approach to output.  On
796 *    a simple machine, it is like printf.  on a more complex machine,
797 *    output is directed to the appropriate window.
798 * Implementation
799 *    Note that to handle the variable argument list, a number of different
800 *    approaches are implemented.  The first part of the implementation selects
801 *    one of 4 ways to build temp1, a formatted string.  The 4 ways arise from
802 *    use or non-use of vsnprintf, and use or non-use of ... in the arg list.
803 *    After building temp1, non-Amiga systems write to stdout or stderr,
804 *    whereas AMIGA writes to a special console.  Why? Because the Amiga
805 *    needs a new console so we can set up a signal upon character typein.
806 ****************************************************************************/
807 
808 #ifndef gprintf
809 #define GPRINTF_MESSAGE_LEN 512
810 #ifdef HAVE_VSNPRINTF
811 #ifdef DOTS_FOR_ARGS
812 
813 /* define with ... in arg list and use vsnprintf to get temp1 */
814 public void gprintf(long where, const char *format, ...)
815 {
816     char temp1[GPRINTF_MESSAGE_LEN];
817 #ifdef AMIGA
818     char temp2[GPRINTF_MESSAGE_LEN];
819 #endif
820     va_list ap;
821 
822     va_start(ap, format);
823     vsnprintf(temp1, GPRINTF_MESSAGE_LEN, format, ap);
824     va_end(ap);
825 
826 #else /* !DOTS_FOR_ARGS */
827 
828 /* define with va_alist and use vsnprintf to get temp1 */
829 public void gprintf(where, format, va_alist)
830 long where;
831 char *format;
832 va_dcl
833 {
834     char temp1[GPRINTF_MESSAGE_LEN];
835     va_list pvar;
836 /* this is a syntax error - if you don't have to remove this, */
837 /* then this whole section of code is unnecessary. */
838     va_start(pvar);
839     vsnprintf(temp1, GPRINTF_MESSAGE_LEN, format, pvar);
840     va_end(pvar);
841 
842 #endif /* DOTS_FOR_ARGS */
843 
844 #else /* !HAVE_VSNPRINTF */
845 #define MAX_GPRINTF_ARGS 10
846 typedef struct gp_args_struct {
847     long arg[MAX_GPRINTF_ARGS];
848 } gp_args_node;
849 
850 #ifdef DOTS_FOR_ARGS
851 /* use ... but not vsnprintf */
852 public void gprintf(long where, char *format, ...)
853 {
854     char temp1[GPRINTF_MESSAGE_LEN];
855 #ifdef AMIGA
856     char temp2[GPRINTF_MESSAGE_LEN];
857 #endif
858     va_list ap;
859     gp_args_node args;
860     va_start(ap, format);
861     args = va_arg(ap, gp_args_node);
862     va_end(ap);
863 #else /* !DOTS_FOR_ARGS */
864 /* don't use ... and don't use vsnprintf */
865 public void gprintf(where, format, args)
866   long where;
867   char *format;
868   gp_args_node args;
869 {
870     char temp1[GPRINTF_MESSAGE_LEN];
871 #ifdef AMIGA
872     char temp2[GPRINTF_MESSAGE_LEN];
873 #endif /* AMIGA*/
874 #endif /* DOTS_FOR_ARGS */
875 
876     snprintf(temp1, GPRINTF_MESSAGE_LEN, format, args);
877 
878 #endif /* HAVE_VSNPRINTF */
879 
880 /*
881  * Now we've got formatted output in temp1.  Write it out.
882  */
883 #ifdef NYQUIST
884     switch ((long) where) {
885       case TRANS:
886           stdputstr(temp1);
887           break;
888       case ERROR:
889         errputstr(temp1);
890         break;
891       case FATAL:
892         errputstr("FATAL: ");
893         errputstr(temp1);
894         break;
895       case GDEBUG:
896         errputstr("DEBUG: ");
897         errputstr(temp1);
898         break;
899       default:
900         errputstr("UNKNOWN: ");
901         errputstr(temp1);
902         break;
903     }
904     gflush();
905 #else /* not NYQUIST */
906 #ifdef AMIGA
907 
908     switch((long) where) {
909       case TRANS:
910         strcpy(temp2, temp1);
911         break;
912       case ERROR:
913         strcpy(temp2, temp1);
914         break;
915       case FATAL:
916         strcpy(temp2, "FATAL: ");
917         strcat(temp2, temp1);
918         break;
919       case GDEBUG:
920         strcpy(temp2,"DEBUG: ");
921         strcat(temp2, temp1);
922         break;
923       default:
924         strcpy(temp2, "UNKNOWN: ");
925         strcat(temp2, temp1);
926         break;
927     }
928     ConOutReq->io_Command = CMD_WRITE;
929     ConOutReq->io_Data = (APTR)temp2;
930     ConOutReq->io_Length = -1;   /* NULL terminated string */
931     DoIO((struct IORequest *) ConOutReq);
932 #else /* not NYQUIST or AMIGA */
933     switch(where) {
934       case TRANS:
935         printf("%s", temp1);
936         break;
937       case ERROR:
938         fprintf(STDERR, "%s", temp1);
939         break;
940       case GDEBUG:
941         fprintf(STDERR, "DEBUG %s", temp1);
942         break;
943       case FATAL:
944         fprintf(STDERR, "FATAL %s", temp1);
945         break;
946       default:
947         fprintf(STDERR, "UNKNOWN %s", temp1);
948         break;
949     }
950 #endif /* AMIGA */
951 #endif /* NYQUIST */
952 }
953 
954 #endif  /* ifndef gprintf */
955 
956 
957 /**************************************************************************
958 *                               gputchar
959 * General putchar
960 **************************************************************************/
961 
962 #ifndef gputchar
963 
964 #ifdef AMIGA
965 public int gputchar(c)
966 int c;
967 {
968     ConPutChar((char)c);
969     return(c);
970 }
971 #else
972 public int gputchar(c)
973 int c;
974 {
975     putchar((char)c);
976     return(c);
977 }
978 #endif
979 
980 #endif  /* ifndef gputchar */
981 
982 /**************************************************************************
983 *                               ggetchar
984 * General getchar
985 **************************************************************************/
986 
987 public int ggetchar()
988 {
989 #ifdef BUFFERED_SYNCHRONOUS_INPUT
990     return getchar();
991 #else
992     int key = wait_ascii();
993     if (key != ABORT_CHAR && key != '\b') gputchar((char)key);
994     return(key);
995 #endif
996 }
997 
998 
999 /**************************************************************************
1000 *                                  ggets
1001 * General gets
1002 **************************************************************************/
1003 
1004 
1005 #ifndef ggets
1006 
1007 public char *ggets(str)
1008   char *str;
1009 {
1010     char *s = str;
1011     int c;
1012 
1013     do {
1014         c = ggetchar();
1015         if (c == '\b' /* backspace */) {
1016             if (s != str) {
1017                 gputchar('\b');
1018                 gputchar((int)' ');
1019                 gputchar('\b');
1020                 s--;
1021             } else {
1022 #ifdef AMIGA
1023                 gputchar((int)0x9b);
1024                 gputchar((int)0x43);
1025 #else
1026                 /* gputchar((int)' '); */
1027 #endif
1028                 gputchar((int)0x07);
1029             }
1030         } else *s++ = (char) c;
1031     } while (c != (int) '\n' && !abort_flag);
1032 
1033     *(s-1) = EOS;
1034     if (abort_flag) *str = EOS;
1035     return str;
1036 }
1037 
1038 #endif  /* ifndef ggets */
1039 
1040 
1041 /****************************************************************************
1042 *                 get_ascii
1043 * Returns:
1044 *    boolean: TRUE if a character was found
1045 *    int * c: pointer to int into which to store the character, if any
1046 * Effect:
1047 *    polls (doesn't wait) for an ascii character and says if it got one
1048 *    the character is returned in *c.
1049 ****************************************************************************/
1050 
1051 public boolean get_ascii(c)
1052   char *c;
1053 {
1054     check_aborted(); /* input buffer check */
1055     if (type_ahead_count == 0) return FALSE;
1056 #ifdef AMIGA
1057     /* if the buffer is full, then there is no outstanding read, restart it: */
1058     if (type_ahead_count == type_ahead_max) ConRead();
1059 #endif
1060     type_ahead_count--;
1061     *c = type_ahead[type_ahead_head++];
1062     if (type_ahead_head == type_ahead_max) type_ahead_head = 0;
1063     return TRUE;
1064 }
1065 
1066 #ifdef MACINTOSH  /** Macintosh direct ascii input**/
1067 public boolean ascii_input(c)
1068 char *c;
1069 {
1070     EventRecord theEvent;
1071 
1072     (void) GetNextEvent((keyDownMask | autoKeyMask), &theEvent);
1073     if ((theEvent.what == keyDown) || (theEvent.what == autoKey)) {
1074         *c = theEvent.message & charCodeMask;
1075         if (*c == '\r') *c = '\n';
1076         return(true);
1077     }
1078     else {
1079         return(false);
1080     }
1081 }
1082 #endif
1083 
1084 #ifdef WINDOWS
1085 #include "conio.h"
1086 #define kbhit _kbhit
1087 #define getch _getch
1088 #endif
1089 
1090 #ifdef DOS
1091 public boolean ascii_input(c)
1092 char *c;
1093 {
1094     if (abort_flag == ABORT_LEVEL) {
1095             *c=ABORT_CHAR;
1096         return((boolean)TRUE);
1097     }
1098     if (kbhit()) {              /* If the keyboard was hit */
1099         *c = getch();           /* Don't echo it */
1100 //      printf("now break");
1101         if (*c == '\r') *c = '\n';
1102         return((boolean)TRUE);
1103     }
1104     return((boolean)FALSE);     /* Keeps Lattice compiler happy */
1105 }
1106 #endif
1107 
1108 #ifdef UNIX
1109 public boolean ascii_input(char *c)
1110 {
1111 #ifdef UNIX_MACH
1112         /*
1113          * we can't read from stdin directly, because the ascii
1114          * input thread is already doing so, so instead we'll
1115          * wait for that thread to read a character and then take
1116          * it
1117          */
1118         boolean ret = FALSE;
1119 
1120         A_LOCK();
1121                 if (a_in_flag) {
1122                         (*c) = a_in;
1123                         a_in_flag = 0;
1124                         ret = TRUE;
1125                 }
1126         A_UNLOCK();
1127         if (ret) {
1128 #ifdef RTMach
1129                 itc_condition_signal(&a_cond);
1130 #else /* RTMach */
1131                 condition_signal(&a_cond);
1132 #endif /* RTMach */
1133         }
1134         if ((*c) == '\r')
1135                 (*c) = '\n';
1136         return(ret);
1137 #else /* __APPLE__ */
1138 #ifndef BUFFERED_SYNCHRONOUS_INPUT
1139     int input = IOgetchar();
1140     if (input != IOnochar) {
1141         *c = input;
1142         if (*c == '\r') *c = '\n';
1143         return TRUE;
1144     }
1145 #endif /* BUFFERED_SYNCHRONOUS_INPUT */
1146     return FALSE;
1147 #endif /* __APPLE__ */
1148 }
1149 #endif
1150 
1151 #ifndef AMIGA /*DOS and MAC and UNIX */
1152 public void unget_ascii(char c)
1153 {
1154         if (type_ahead_head == 0) type_ahead_head = type_ahead_max;
1155         type_ahead_head--;
1156         type_ahead[type_ahead_head] = c;
1157         type_ahead_count++;
1158 }
1159 
1160 
1161 public boolean check_ascii(void)
1162 {
1163         char c;
1164 
1165         if(get_ascii(&c)) {
1166                 unget_ascii(c);
1167                 return TRUE;
1168         }
1169         else return FALSE;
1170 }
1171 #endif
1172 
1173 
1174 /****************************************************************************
1175 *                   wait_ascii
1176 * Returns:
1177 *    int: character for key pressed
1178 * Effect:
1179 *    waits for the user to type a key on the terminal keyboard
1180 *    (versus the synthesizer keyboard) and returns the key typed
1181 ****************************************************************************/
1182 
1183 #ifdef MACINTOSH
1184 public int wait_ascii()
1185 {
1186     char key ;    /* key typed */
1187 
1188     if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
1189     if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
1190     gflush();
1191     while (!get_ascii(&key)) ;
1192     return(key);
1193 }
1194 #endif
1195 
1196 #ifdef DOS
1197 public int wait_ascii()
1198 {
1199     char key ;    /* key typed */
1200 
1201     if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
1202     if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
1203     if (!get_ascii(&key)) {
1204         key = _getch(); // block until we get an input
1205     }
1206     /* GWL - check for abort on previos line */
1207     return (int)key;
1208 }
1209 #endif
1210 
1211 #ifndef MACINTOSH
1212 #ifndef DOS
1213 public int wait_ascii()
1214 {
1215 #ifdef UNIX /* was defined (UNIX) || defined(ITC) */
1216 #ifndef UNIX_MACH
1217         fd_set readfds;
1218 #endif /* !UNIX_MACH */
1219 #endif
1220     char c;
1221     struct rlimit file_limit;
1222 
1223     if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
1224     if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
1225     while (!get_ascii(&c)) {
1226 #ifdef AMIGA
1227         WaitPort(ConInPort);
1228 #endif
1229 #ifdef UNIX
1230         fflush(stdout);
1231 #ifdef UNIX_MACH
1232         /*
1233          * we can't select, since another thread is reading
1234          * from stdin, and we don't want to have an input war
1235          * so instead, the ascii input thread will signal
1236          * a_in_cond when it gets input, so we just wait
1237          * for that to happen
1238          */
1239         A_LOCK();
1240 #ifdef RTMach
1241                 itc_condition_wait(&a_in_cond, &a_mutex);
1242 #else /* RTMach */
1243                 condition_wait(&a_in_cond, &a_mutex);
1244 #endif /* RTMach */
1245         A_UNLOCK();
1246 #else /* UNIX_MACH */
1247         FD_ZERO(&readfds);
1248         FD_SET(IOinputfd, &readfds);
1249         gflush();
1250         getrlimit(RLIMIT_NOFILE, &file_limit);
1251         select((int) (file_limit.rlim_max+1), &readfds, 0, 0, NULL);
1252 #endif /* !__APPLE__ */
1253 #endif /* ifdef UNIX */
1254     }
1255     return (int) c;
1256 }
1257 #endif
1258 #endif
1259 
1260 #ifdef AMIGA
1261 /******************************************************************
1262                               AMIGA 2000.
1263                          Console IO Functions
1264                              JCD 25-Apr-88
1265 *******************************************************************/
1266 
1267 UBYTE KeybSig()
1268 {
1269     return ConInPort->mp_SigBit;
1270 }
1271 
1272 private void ConPutChar(c)
1273 char c;
1274 {
1275     ConOutReq->io_Command = CMD_WRITE;
1276     ConOutReq->io_Data = (APTR)&c;
1277     ConOutReq->io_Length = 1;
1278     DoIO((struct IORequest *) ConOutReq);
1279 }
1280 
1281 private void ConPutStr(str)
1282 char *str;
1283 {
1284     ConOutReq->io_Command = CMD_WRITE;
1285     ConOutReq->io_Data = (APTR)str;
1286     ConOutReq->io_Length = -1;
1287     DoIO((struct IORequest *) ConOutReq);
1288 }
1289 
1290 private void ConRead()
1291 {
1292     ConInReq->io_Command = CMD_READ;
1293     ConInReq->io_Data = (APTR)KeyBuff;
1294     ConInReq->io_Length = 1;
1295     SendIO((struct IORequest *) ConInReq);
1296 }
1297 #endif
1298