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