text: /* userio.c -- handy user interface functions */
  /* Copyright 1989 Carnegie Mellon University */
  
  /*****************************************************************************
  *       Change Log
  *  Date | Change
  *-----------+-----------------------------------------------------------------
  * 21-May-86 | Created
  * 11-Aug-87 | F.H: Added clear_abort(), stop()
  *    May-88 | JCD : AMIGA VERSION 
  * 11-Jun-88 | RBD: disable printing of GDEBUG messages
  * 12-Oct-88 | JCD : EXCLUSIVE AMIGA VERSION
  * 13-Apr-89 | JCD : New portable version.
  *  5-Apr    | JDW : Further changes
  *  2-Mar-92 | GWL : Little changes to satisfy compiler
  * 19-Nov-92 | JDZ : Mach tty io threads
  * 28-Apr-03 |  DM : portability changes. true->TRUE, false->FALSE
  *****************************************************************************/
  
  /* Notes on ascii input:
  
  Input is complicated because different systems have varying input models,
  especially with regard to handling ^C.  The CMT model handles ^C and ^G as
  special characters, and these do not cause software interrupts.  Also, the
  lowest level of the CMT model does not support line editing: Every character
  is acted upon immediately.  This has two implications:
  (1) CMT must "read ahead" looking for ^C and ^G characters.  This is handled
  by the check_aborted() procedure, which reads characters into the 
  type_ahead[] array.
  (2) CMT must do its own line editing.  This is handled by the ggets() routine.
  
  A number of functions support ascii input, only some of which are visible
  to the application programmer.  Let's start at the top-level and work down;
  each of the following calls the routine below it:
  
  ggets() - gets a string with line editing support.  This function is fairly
  machine independent, except for some backspace-and-erase control character
  code sequences.
  
  ggetchar() - gets a raw character.  This function calls wait_ascii()
   and echoes it.  Note that it may return ABORT_CHAR or BREAK_CHAR.
  
  wait_ascii() - gets a raw character without echo and without character
  code translation.  wait_ascii() either polls get_ascii() or uses some
  kind of system-dependent event waiting.  Returns ABORT_CHAR or BREAK_CHAR
  immediately if abort_flag has been set, regardless of whether there is
  new ascii input.
  
  get_ascii() - checks to see if a character is available.  (Using
  check_aborted().)
  The only dependency here is on the Amiga, we restart input when buffer goes
  from full to non-full.
  
  check_aborted() - looks for input by calling ascii_input.  If found,
  put the input into the type_ahead[] buffer. Returns abort_flag.
  
  ascii_input() - lowest level of input; just gets a character if there is
  one.  Does conversion from RETURN (\r) to EOL (\n).  The Amiga handles
  this in-line directly in check_aborted().
  
  Here's a quick summary:
  
  ggets = ggetchar + line editing & string building
  ggetchar = wait_ascii + character echo
  wait_ascii = get_ascii + wait for character
  get_ascii = check_aborted + pull char from buffer
  check_aborted = ascii_input + test for ^C,^G + put in buffer
  ascii_input = poll for char + CR->EOL conversion
  
  */
  
  #include "switches.h"
  
  #include <stdio.h>
  #include <string.h>
  #if HAS_STDLIB_H
  #include <stdlib.h> /* normal case */
  #endif
  
  
  #ifdef MACINTOSH
  # include "StandardFile.h"
    /* added for ThinkC 7 */
  # ifdef THINK_C
  #  include <pascal.h>
  # endif
  #endif
  
  #ifdef AMIGA
  
  # ifdef AZTEC
  #  include "functions.h"
  # else /* LATTICE */
  #  include "amiga.h"
  #  include "stdarg.h"
  # endif
  
  # include "intuition/intuition.h"
  # include "devices/console.h"
  #endif
  
  #include "ctype.h"
  #include "stdio.h"
  #include "cext.h"
  #include "userio.h"
  
  #ifdef MICROSOFT
  #include "signal.h"
  #endif
  
  #ifdef UNIX_MACH
  #include <varargs.h>
  #include <midistruct.h>
  extern char a_in;
  extern int a_in_flag;
  extern int i_am_running;
  #ifdef RTMach
  extern itc_mutex_t a_mutex;
  extern itc_condition_t a_cond, a_in_cond;
  #define A_LOCK() itc_mutex_lock(&a_mutex)
  #define A_UNLOCK() itc_mutex_unlock(&a_mutex)
  #else /* RTMach */
  extern struct mutex a_mutex;
  extern struct condition a_cond, a_in_cond;
  #define A_LOCK() mutex_lock(&a_mutex)
  #define A_UNLOCK() mutex_unlock(&a_mutex)
  #endif /* RTMach */
  #endif
  
  #ifdef DOTS_FOR_ARGS
  #include <stdarg.h>
  #endif
  
  #ifdef UNIX
  #include <sys/param.h>
  #include <sys/resource.h>
  #include "cmtio.h"
  #ifdef _IBMR2
  #define NBBY 8
  #define OPEN_MAX 2000
  #endif
  #include <sys/select.h>
  #endif
  
  #ifdef linux
  #include <sys/time.h> /* for FD_ZERO / FD_SET */
  #endif
  
  extern int debug;
  
  #ifdef NYQUIST
  /* get definitions for stdputstr, etc. */
  #include "xlisp.h"
  #endif
  
  
  /****************************************************************************
  *
  *       routines private to this module
  *
  ****************************************************************************/
  
  int GetReadFileName();
  int GetWriteFileName();
  
  #ifdef MACINTOSH
  private void PtoC_StrCopy(char *p1, char *p2);
  #endif
  
  #ifdef AMIGA
          char ConGetChar();
          ConMayGetChar();
  private void ConRead();
  private void ConPutStr();
  private void ConPutChar();
  UBYTE   ascii_signal();
  UBYTE   KeybSig();
  #endif
  
  
  /****************************************************************************
  *
  * variables shared with other modules
  *
  ****************************************************************************/
  
  public int abort_flag;          /* control C or control G equivalent */
  public int redirect_flag;		/* check whether the I/O has been redirected--
                                      Added by Ning Hu	Apr.2001*/
  /* extern void musicterm(); */ /*DMH: from macmidi.c, to allow abort_check*/
  public boolean ascii_input();
  
  /****************************************************************************
  *
  * variables private to this module
  *
  ****************************************************************************/
  
  #ifdef AMIGA
  struct IntuitionBase *IntuitionBase;
  private struct IOStdReq *ConOutReq;
  private struct MsgPort *ConOutPort;
  private struct IOStdReq *ConInReq;
  private struct MsgPort *ConInPort;
  private char KeyBuff[16];
  private struct Window *Window;
  private struct NewWindow NewWindow = {
      0,11,640,189,
      0,1,
      NULL,
      SMART_REFRESH | ACTIVATE | WINDOWDRAG | WINDOWDEPTH |
          WINDOWSIZING,
      NULL,NULL,
      (STRPTR) "Carnegie Mellon University MIDI Toolkit for Commodore AMIGA",
      NULL,NULL,
      100,25,640,200,
      WBENCHSCREEN };
  #endif
  
  #ifdef MACINTOSH
  private OSType io_file_type = 0x3F3F3F3F; /* '????' */
  private OSType io_file_creator = 0x3F3F3F3F; /* '????' */
  #endif
  
  #define type_ahead_max 100
  char type_ahead[100];
  int type_ahead_head = 0;
  int type_ahead_tail = 0;
  int type_ahead_count = 0;
  
  
  #ifdef DOS
  #ifdef BORLAND
  int c_break(void)
  {
      gprintf(TRANS, " BREAK ");
      abort_flag = ABORT_LEVEL;
      return 1; /* non-zero means do not exit program */
  }
  #endif
  #ifdef MICROSOFT
  void c_break(int sig)
  {
      abort_flag = ABORT_LEVEL;
      /* The CTRL+C interrupt must be reset to our handler since
       * by default it is reset to the system handler.
       */
      signal(SIGINT, c_break); /* assume this succeeds */
  }
  #endif
  #endif
  
  #ifdef MACINTOSH
  #ifdef NYQUIST
  void FlushOutput (void);
  #endif
  #endif
  
  /* gflush -- flush output produced by gprintf, etc. */
  /**/
  void gflush(void)
  {
  #ifdef MACINTOSH
  #ifdef NYQUIST
      FlushOutput();
  #else
      fflush(stdout);    /* make sure any prompts or errors have been output */
      fflush(STDERR);
  #endif /* NYQUIST */
  #endif /* MACINTOSH */
  #ifdef UNIX
      fflush(stdout);    /* make sure any prompts or errors have been output */
      fflush(STDERR);
  #endif
  }
  
  
  /****************************************************************************
  *                                io_init
  *
  *  I added this init function for the AMIGA version. 
  *
  *  io_init : opens a window
  *  and exits if initialisation can not be done properly.
  *  registers cleanup calls to carefully deallocate resources.
  *
  *  io_init is not amiga specific : the simplest version 
  *  of io_init could be a clear screen statement for example, and a
  *  printf("Good bye !\n") on exit.
  *
  *  for the Mac, it seems that ascii_input doesn't work unless getchar() is
  *  called first.  I assume this is because getchar() initializes the ability
  *  of the window to process type-in, so there is probably a way to set this
  *  directly.  If you figure it out, let me know.  -RBD
  *
  *****************************************************************************/
  
  void
  io_init()
  {
  
  #ifdef AMIGA
      int error;
  
      /* Window and console initialisation  */
      IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",1L);
      if (IntuitionBase == NULL) EXIT(1);
      cu_register((cu_fn_type) CloseLibrary, IntuitionBase);
      
      ConOutPort = CreatePort("conoutport", 0L);
      if (ConOutPort == NULL) EXIT(1);
      cu_register((cu_fn_type) DeletePort, ConOutPort);
      
      ConOutReq = CreateStdIO(ConOutPort);
      if (ConOutReq == NULL) EXIT(1);
      cu_register((cu_fn_type) DeleteStdIO, ConOutReq);
      
      ConInPort = CreatePort("coninport", 0L);
      if (ConInPort == NULL) EXIT(1);
      cu_register((cu_fn_type) DeletePort, ConInPort);
      
      ConInReq = CreateStdIO(ConInPort);
      if (ConInReq == NULL) EXIT(1);
      cu_register((cu_fn_type) DeleteStdIO, ConInReq);
      
      Window = OpenWindow(&NewWindow);
      if (Window == NULL) EXIT(1);
      cu_register((cu_fn_type) CloseWindow, Window);
      
      ConOutReq->io_Data = (APTR)Window;
      ConOutReq->io_Length = sizeof(*Window);
      error = OpenDevice("console.device", 0L, (struct IORequest *) ConOutReq, 0L);
      ConInReq->io_Device = ConOutReq->io_Device;
      ConInReq->io_Unit = ConOutReq->io_Unit;
      if (error != NULL) EXIT(1);
      cu_register((cu_fn_type) CloseDevice, ConOutReq);
      
      ConInReq->io_Command = CMD_READ;
      ConInReq->io_Data = (APTR)KeyBuff;
      ConInReq->io_Length = 1;
      SendIO((struct IORequest *) ConInReq);
  #endif
  
  #ifdef UNIX
  #ifndef BUFFERED_SYNCHRONOUS_INPUT
      IOsetup(0 /* standard input */);
      cu_register((cu_fn_type) IOcleanup, NULL);
  #endif
  #endif
  
  #ifdef MACINTOSH
  #ifndef NYQUIST /* don't need this if we're in Nyquist */
      char s[100];
      printf("Type <return> to start: ");
      fgets(s, 100, stdin);
  #endif /* NYQUIST */
  #endif
  
  #ifdef DOS
  #ifdef MICROSOFT
      if (signal(SIGINT, c_break) == SIG_ERR) {
          gprintf(ERROR, "Couldn't set Ctrl C handler\n");
          EXIT(1);
      }
  #else
  #ifdef BORLAND
      ctrlbrk(c_break);
  #else
      ... we are in DOS, but neither MICROSOFT nor BORLAND,
      please set up a control C handler here...
  #endif
  #endif
  #endif
  }
  
  #ifdef MACINTOSH
  
  /****************************************************************************
  *                                       abort_check
  * Effect:
  *       exit nicely if the aborted flag is set
  ****************************************************************************/
  
  public void abort_check()
  {
      if (abort_flag) clean_exit();
  }
  
  
  /****************************************************************************
  *                                       clean_exit
  * Effect:
  *       clean up and exit
  ****************************************************************************/
  
  public void clean_exit()
  {
          gprintf(TRANS, "Exiting...\n");
          EXIT(1);
  }
  
  #ifdef MPW
  /****************************************************************************
  *                                       cleanup_abort_handler
  * Effect:
  *       shuts down abort watcher
  ****************************************************************************/
  
  public void cleanup_abort_handler()
  {
          (void) sigset(SIGINT, SIG_DFL); /* deactivate abort watcher */
  }
  
  
  /****************************************************************************
  *                                       init_abort_handler
  * Effect:
  *       starts abort watcher
  *       aborted flag is set to FALSE
  ****************************************************************************/
  
  public void init_abort_handler()
  {
          abort_flag = FALSE;
          (void) sigset(SIGINT, abort_watcher);   /* activate abort watcher */
  }
  #endif
  
  #endif
  
  
  /****************************************************************************
  *               askbool
  * Inputs:
  *    char *prompt: string to prompt for user input
  *    int deflt: TRUE or FALSE default
  * Returns:
  *    boolean: TRUE or FALSE as entered by user
  * Effect:
  *    prompts user for yes or no input, returns result
  ****************************************************************************/
  
  int askbool(prompt, deflt)
  char *prompt;
  int deflt;
  {
  #define undefined -1
      char defchar;    /* the default answer */
      char c;     /* user input */
      char in_string[100];
      int result = -1;    /* the result: -1 = undefined, 0 = FALSE, 1 = TRUE */
      if (deflt) defchar = 'y';
      else defchar = 'n';
      while (result == undefined) {
          gprintf(TRANS, "%s? [%c]: ", prompt, defchar);
          ggets(in_string);
          c = in_string[0];
          if (islower(c)) c = toupper(c);
          if (c == 'Y') result = TRUE;
          else if (c == 'N') result = FALSE;
          else if (c == EOS) result = deflt;
          else if (abort_flag) result = deflt;
          /* space before Please to separate from user's type-in: */
          else gprintf(TRANS, " Please type Y or N.\n");
      }
      if (abort_flag == BREAK_LEVEL) {
          abort_flag = 0;
          result = deflt;
          gprintf(TRANS, "\n");
      }
      return result;
  }
  
  
  /****************************************************************************
  *               fileopen
  * Inputs:
  *    char *deflt: the default file name (e.g. from command line)
  *    char *extension: default extension
  *    char *mode: read ("r") or write ("w")
  *    char *prompt: prompt for user
  * Returns:
  *    opened file pointer
  * Effect: 
  *    opens file, prompts for user input if necessary and warns about
  *    possible confusion.  If deflt is a null string or NULL, the user will
  *    be prompted for a name.     The routine loops until a file is opened.
  *    If the mode is "r", a check is made to see if the file exists
  *    with and without the extension.     If both exist a warning is given.
  *    For mode "w", a check is made to see if the file will be overwritten.
  *    The extension is automatically added if the default or user-typed
  *    file has no "."     At the bottom of the loop body, if no file has
  *    been opened, the user is prompted for another file name.
  ****************************************************************************/
  
  char fileopen_name[100];        /* name of the opened file */
  
  FILE *fileopen(deflt, extension, mode, prompt)
    char *deflt;
    char *extension;    /* default extension */
    char *mode;   /* read "r" or write "w" */
    char *prompt;    /* prompt for user */
  {
      char extname[100];          /* trial name with extension added */
      FILE *fp = NULL;            /* file corresponding to filename */
      FILE *fpext;                /* file corresponding to extname */
      char *problem = NULL;       /* tells user why he has to try again */
  
      if (!deflt) deflt = "";     /* treat NULL as the empty string */
      strcpy(fileopen_name, deflt);
      /* keep trying until a good file is found: */
      while (fp == NULL) {
          /* avoid null file names: */
          while (strlen(fileopen_name) == 0) {
  #ifndef MACINTOSH
              gprintf(TRANS, "%s : ", prompt);
              ggets(fileopen_name);
              if (abort_flag) {
                  if (abort_flag == BREAK_LEVEL) {
                      abort_flag = 0;
                      /* type return since user didn't... */
                      gprintf(TRANS, "\n");
                  }
                  return NULL;
              }
  #else /* use Macintosh file dialog */
              if (mode[0] == 'r') {
                   if (!GetReadFileName(fileopen_name)) return NULL;
               } else if (mode[0] == 'w') {
                   if (!(GetWriteFileName(fileopen_name, prompt))) return NULL;
               } else {
                   gprintf(ERROR, "(fileopen) internal error: bad mode\n");
               }
  #endif /* MACINTOSH */
          }
          if (mode[0] == 'r') {
              strcpy(extname, fileopen_name);
              strcat(extname, ".");
              strcat(extname, extension);
              fp = NULL;
              fpext = NULL;
              if (ok_to_open(fileopen_name, mode)) {
                  fp = fopen(fileopen_name, mode);
              }
              if (ok_to_open(extname, mode)) {
                  fpext = fopen(extname, mode);
              }
              if (fp != NULL && fpext != NULL) {
                  gprintf(TRANS,
                  "warning: both %s and %s exist.     %s will be used.\n",
                  fileopen_name, extname, fileopen_name);
                  fclose(fpext);
              } else if (fpext != NULL) {
                  fp = fpext;
                  strcpy(fileopen_name, extname);  /* remember what was opened */
              }
              if (fp == NULL) problem = "Couldn't find %s.\n";
          } else if (mode[0] == 'w') {
              boolean added_extension = FALSE;
              
              /* add the extension if there is no '.' in the file name */
              if (!strchr(fileopen_name, '.')) {
                  strcat(fileopen_name, ".");
                  strcat(fileopen_name, extension);
                  added_extension = TRUE;
              }
              if (TRUE
  #ifdef MACINTOSH
                  /* file open dialog already asked user to confirm unless we're
                   * adding an extension
                   */
                  && added_extension
  #endif
                  ) {
                  fp = NULL;
                  if (ok_to_open(fileopen_name, "r"))
                      fp = fopen(fileopen_name, "r");
                  if (fp != NULL) {
                      char question[100];
                      fclose(fp);
                      strcpy(question, "OK to overwrite ");
                      strcat(question, fileopen_name);
                      if (!askbool(question, FALSE)) {
                          fp = NULL;
                          problem = "\n";
                          goto tryagain;
                      }
                  }
              }
              fp = NULL;
              if (ok_to_open(fileopen_name, mode))
                  fp = fopen(fileopen_name, mode);
              if (fp == NULL) problem = "Couldn't create %s.\n";
          }
    tryagain:
          if (fp == NULL) {
              gprintf(TRANS, problem, fileopen_name);
              gprintf(TRANS,"Try again.\n");
              fileopen_name[0] = EOS;
          }
      }
      return fp;
  }
  
  #ifdef MACINTOSH
  
  static int GetReadFileName(name)
  char *name;
  {
      static Point p = {100,100};
      SFReply loadfile;
      SFTypeList mytypes;
      
      mytypes[0] = 0x54455854; /* 'TEXT' */
      mytypes[1] = 0x4D696469; /* 'Midi' */
      mytypes[2] = 0x3F3F3F3F; /* '????' */
  /* could put any filter here (i.e. giofilefileter) */
      SFGetFile(p, "\p", NULL, 3, mytypes, 0L, &loadfile); 
      if (loadfile.good) {
          SetVol(0L,loadfile.vRefNum); 
          PtoC_StrCopy((char *) &loadfile.fName, name);
          return(true);
      } else return(false);
  }
  
  
  static int GetWriteFileName(fn, str)
  char *fn, *str;
  {
          static Point SFPwhere = { 106, 104 };
          unsigned char Pstr[100], Pfn[100];
          SFReply reply;
   
          strcpy((char *)Pstr, str);
          CtoPstr((char *)Pstr);
          strcpy((char *)Pfn, fn);
          CtoPstr((char *)Pfn);
  
          SFPutFile(SFPwhere, (ConstStr255Param) Pstr, (ConstStr255Param) Pfn,
                    0L, &reply);
          if (reply.good) {
                  SetVol (0L,reply.vRefNum);
                  PtoC_StrCopy((char *) &reply.fName, fn); 
                  return(true);
          }
          else return(false);
  }
  
  
  void PtoC_StrCopy(p1, p2)
    register char *p1, *p2;
  /* copies a pascal string from p1 to p2 */
  {
          register int len;
          
          len = *p1++;
          while (--len>=0) *p2++=*p1++;
          *p2 = '\0';
  }
  
  
  boolean get_file_info(char *filename, OSType *file_type, OSType *file_creator)
  {
      short rc;           /* toolbox return code */
      FInfo fi;           /* toolbox file info */
      char fn[101];       /* temporary file name */
  
      strcpy(fn, filename);
      CtoPstr(fn);
      if (rc = GetFInfo((byte*)fn, 0, &fi)) {
          gprintf(ERROR, "rc from GetFInfo=%d\n", rc);
          gprintf(ERROR, "unable to get file type\n");
          *file_type = 0x3F3F3F3F; /* '????' */
          *file_creator = 0x3F3F3F3F; /* '????' */
          return FALSE;
      } else /* set file type & creator */ {
          if (debug) gprintf(TRANS, "File Type: '%.4s'  File Creator: '%.4s'\n",
                             &fi.fdType, &fi.fdCreator );
          *file_type = fi.fdType;
          *file_creator = fi.fdCreator;
      }
      return TRUE;
  }
  
  
  boolean put_file_info(char *filename, OSType file_type, OSType file_creator)
  {
      short rc;           /* toolbox return code */
      FInfo fi;           /* toolbox file info */
      char fn[101];       /* temporary file name */
  
      if (debug) gprintf(TRANS,"set file %s to become type '%.4s'\n", filename, &file_type);
      strcpy(fn, filename);
      CtoPstr(fn);
      if (rc = GetFInfo((byte*)fn, 0, &fi)) {
          gprintf(TRANS, "rc from GetFInfo=%d\n", rc);
          gprintf(TRANS, "unable to set file type\n");
      } else /* set file type & creator */ {
          if (debug) gprintf(TRANS, "File Type: '%.4s'  File Creator: '%.4s'\n",
                             &fi.fdType, &fi.fdCreator );
          fi.fdType = file_type;
          fi.fdCreator = file_creator;
          if (rc=SetFInfo((byte*)fn, 0, &fi)) {
              gprintf(TRANS, "rc from SetFInfo=%d\n", rc);
              gprintf(TRANS, "unable to set file type\n");
          } else if (rc=GetFInfo((byte*)fn, 0, &fi)) {
              gprintf(TRANS, "rc from GetFInfo=%d\n", rc);
              gprintf(TRANS, "unable to verify file type\n");
          } else {
              if (debug) gprintf(TRANS, "File Type: '%.4s'  File Creator: '%.4s'\n",
                                 &fi.fdType, &fi.fdCreator );
          }
      }
  }
  #endif /* MACINTOSH */
  
  
  
  #ifdef AMIGA
  /***************************************************************
  *                           ascii_signal
  *
  * Input : none
  * Ouput : none
  * Return: the signal that will be raised on ascii input
  * Effect: none
  ***************************************************************/
  
  UBYTE ascii_signal()
  {
      return ConInPort->mp_SigBit;
  }
  #endif
  
  /* check_aborted -- see if any characters are available, check for ctrl C */
  
  int check_aborted()
  {
          char in_c;
  #ifdef AMIGA 
      if (GetMsg(ConInPort)) {
          in_c = KeyBuff[0];
          if (in_c == '\r') in_c = '\n';
  #endif
  #ifndef AMIGA   /* DOS or MACINTOSH or UNIX */
      if (type_ahead_count < type_ahead_max && ascii_input(&in_c)) {
  #endif
          type_ahead[type_ahead_tail] = in_c;
          if (in_c == ABORT_CHAR) abort_flag = ABORT_LEVEL;
          else if (!abort_flag && in_c == BREAK_CHAR) 
              abort_flag = BREAK_LEVEL;
  
          /* go ahead and insert anything into buffer, including ^C, ^G: */
          type_ahead_count++;
          type_ahead_tail++;
          if (type_ahead_tail == type_ahead_max) type_ahead_tail = 0;
  
  #ifdef AMIGA
          if (type_ahead_count < type_ahead_max) ConRead();
  #endif
      }
      return abort_flag;
  }    
  
  
  /****************************************************************************
  *                   readln
  * Inputs:
  *    FILE * fp: File to read from
  * Effect: 
  *    Reads and discards characters until a newline is seen
  ****************************************************************************/
  
  void readln(fp)
    register FILE *fp;
  {
      register int c;
      while (((c = getc(fp)) != '\n') && (c != EOF)) ;
  }
  
  
  /****************************************************************************
  *                   gprintf
  * Inputs:
  *    int * handler: pointer to output handler (say, a window)
  *        or one of {TRANS, ERROR, FATAL, GDEBUG} from userio.h
  *    char * format: a null-terminated printf style format string
  *    int arg0 through arg14: a variable number of arguments for printf
  * Effect:
  *    formats and outputs the specified information to an output handler.
  *    this is a system-independent approach to output.  On
  *    a simple machine, it is like printf.  on a more complex machine,
  *    output is directed to the appropriate window.
  * Implementation
  *    Note that to handle the variable argument list, a number of different
  *    approaches are implemented.  The first part of the implementation selects
  *    one of 4 ways to build temp1, a formatted string.  The 4 ways arise from
  *    use or non-use of vsnprintf, and use or non-use of ... in the arg list.
  *    After building temp1, non-Amiga systems write to stdout or stderr, 
  *    whereas AMIGA writes to a special console.  Why? Because the Amiga
  *    needs a new console so we can set up a signal upon character typein.
  ****************************************************************************/
  
  #ifndef gprintf
  #define GPRINTF_MESSAGE_LEN 512
  #ifdef HAVE_VSNPRINTF
  #ifdef DOTS_FOR_ARGS
  
  /* define with ... in arg list and use vsnprintf to get temp1 */
  public void gprintf(long where, char *format, ...)
  {
      char temp1[GPRINTF_MESSAGE_LEN];
  #ifdef AMIGA
      char temp2[GPRINTF_MESSAGE_LEN];
  #endif
      va_list ap;
  
      va_start(ap, format);
      vsnprintf(temp1, GPRINTF_MESSAGE_LEN, format, ap);
      va_end(ap);
  
  #else /* !DOTS_FOR_ARGS */
  
  /* define with va_alist and use vsnprintf to get temp1 */
  public void gprintf(where, format, va_alist)
  long where;
  char *format;
  va_dcl
  {
      char temp1[GPRINTF_MESSAGE_LEN];
      va_list pvar;
  /* this is a syntax error - if you don't have to remove this, */
  /* then this whole section of code is unnecessary. */
      va_start(pvar);
      vsnprintf(temp1, GPRINTF_MESSAGE_LEN, format, pvar);
      va_end(pvar);
  
  #endif /* DOTS_FOR_ARGS */
  
  #else /* !HAVE_VSNPRINTF */
  #define MAX_GPRINTF_ARGS 10
  typedef struct gp_args_struct {
      long arg[MAX_GPRINTF_ARGS];
  } gp_args_node;
  
  #ifdef DOTS_FOR_ARGS
  /* use ... but not vsnprintf */
  public void gprintf(long where, char *format, ...)
  {
      char temp1[GPRINTF_MESSAGE_LEN];
  #ifdef AMIGA
      char temp2[GPRINTF_MESSAGE_LEN];
  #endif
      va_list ap;
      gp_args_node args;
      va_start(ap, format);
      args = va_arg(ap, gp_args_node);
      va_end(ap);
  #else /* !DOTS_FOR_ARGS */
  /* don't use ... and don't use vsnprintf */
  public void gprintf(where, format, args)
    long where;
    char *format;
    gp_args_node args;
  {
      char temp1[GPRINTF_MESSAGE_LEN];
  #ifdef AMIGA
      char temp2[GPRINTF_MESSAGE_LEN];
  #endif /* AMIGA*/
  #endif /* DOTS_FOR_ARGS */
  
      snprintf(temp1, GPRINTF_MESSAGE_LEN, format, args);
  
  #endif /* HAVE_VSNPRINTF */
  
  /*
   * Now we've got formatted output in temp1.  Write it out.
   */
  #ifdef NYQUIST
      switch ((long) where) {
        case TRANS:
            stdputstr(temp1);
            break;
        case ERROR:
          errputstr(temp1);
          break;
        case FATAL:
          errputstr("FATAL: ");
          errputstr(temp1);
          break;
        case GDEBUG:
          errputstr("DEBUG: ");
          errputstr(temp1);
          break;
        default:
          errputstr("UNKNOWN: ");
          errputstr(temp1);
          break;
      }
      gflush();
  #else /* not NYQUIST */
  #ifdef AMIGA
  
      switch((long) where) {
        case TRANS:
          strcpy(temp2, temp1);
          break;
        case ERROR:
          strcpy(temp2, temp1);
          break;
        case FATAL:
          strcpy(temp2, "FATAL: ");
          strcat(temp2, temp1);
          break;
        case GDEBUG:
          strcpy(temp2,"DEBUG: ");
          strcat(temp2, temp1);
          break;
        default:
          strcpy(temp2, "UNKNOWN: ");
          strcat(temp2, temp1);
          break;
      }
      ConOutReq->io_Command = CMD_WRITE;
      ConOutReq->io_Data = (APTR)temp2;
      ConOutReq->io_Length = -1;   /* NULL terminated string */
      DoIO((struct IORequest *) ConOutReq);            
  #else /* not NYQUIST or AMIGA */
      switch(where) {
        case TRANS:
          printf("%s", temp1);
          break;
        case ERROR:
          fprintf(STDERR, "%s", temp1);
          break;
        case GDEBUG:
          fprintf(STDERR, "DEBUG %s", temp1);
          break;
        case FATAL:
          fprintf(STDERR, "FATAL %s", temp1);
          break;
        default:
          fprintf(STDERR, "UNKNOWN %s", temp1);
          break;
      }
  #endif /* AMIGA */
  #endif /* NYQUIST */
  }
  
  #endif  /* ifndef gprintf */
  
  
  /**************************************************************************
  *                               gputchar
  * General putchar
  **************************************************************************/
  
  #ifndef gputchar
  
  #ifdef AMIGA
  public int gputchar(c)
  int c;
  {
      ConPutChar((char)c);
      return(c);
  }
  #else
  public int gputchar(c)
  int c;
  {
      putchar((char)c);
      return(c);
  }
  #endif
  
  #endif  /* ifndef gputchar */
  
  /**************************************************************************
  *                               ggetchar
  * General getchar
  **************************************************************************/
  
  public int ggetchar()
  {
  #ifdef BUFFERED_SYNCHRONOUS_INPUT
      return getchar();
  #else
      int key = wait_ascii();
      if (key != ABORT_CHAR && key != '\b') gputchar((char)key);
      return(key);
  #endif
  }
  
  
  /**************************************************************************
  *                                  ggets
  * General gets
  **************************************************************************/
  
  
  #ifndef ggets
  
  public char *ggets(str)
    char *str;
  {
      char *s = str;
      int c;
  
      do {
          c = ggetchar();
          if (c == '\b' /* backspace */) {
              if (s != str) {
                  gputchar('\b');
                  gputchar((int)' ');
                  gputchar('\b');
                  s--;
              } else {
  #ifdef AMIGA
                  gputchar((int)0x9b);
                  gputchar((int)0x43);
  #else
                  /* gputchar((int)' '); */
  #endif
                  gputchar((int)0x07);
              }
          } else *s++ = (char) c;
      } while (c != (int) '\n' && !abort_flag);
  
      *(s-1) = EOS;
      if (abort_flag) *str = EOS;
      return str;
  }
  
  #endif  /* ifndef ggets */
  
  
  /****************************************************************************
  *                 get_ascii
  * Returns:
  *    boolean: TRUE if a character was found
  *    int * c: pointer to int into which to store the character, if any
  * Effect:
  *    polls (doesn't wait) for an ascii character and says if it got one
  *    the character is returned in *c.
  ****************************************************************************/
  
  public boolean get_ascii(c)
    char *c;
  {
      check_aborted(); /* input buffer check */
      if (type_ahead_count == 0) return FALSE;
  #ifdef AMIGA
      /* if the buffer is full, then there is no outstanding read, restart it: */
      if (type_ahead_count == type_ahead_max) ConRead();
  #endif
      type_ahead_count--;
      *c = type_ahead[type_ahead_head++];
      if (type_ahead_head == type_ahead_max) type_ahead_head = 0;
      return TRUE;
  }
  
  #ifdef MACINTOSH  /** Macintosh direct ascii input**/
  public boolean ascii_input(c)
  char *c;
  {
      EventRecord theEvent;
  
      (void) GetNextEvent((keyDownMask | autoKeyMask), &theEvent);
      if ((theEvent.what == keyDown) || (theEvent.what == autoKey)) {
          *c = theEvent.message & charCodeMask;
          if (*c == '\r') *c = '\n';
          return(true);
      } 
      else {
          return(false);
      }
  }
  #endif
  
  #ifdef WINDOWS
  #include "conio.h"
  #define kbhit _kbhit
  #define getch _getch
  #endif
  
  #ifdef DOS
  public boolean ascii_input(c)
  char *c;
  {
      if (abort_flag == ABORT_LEVEL) {
              *c=ABORT_CHAR;
          return((boolean)TRUE);
      }
      if (kbhit()) {              /* If the keyboard was hit */
          *c = getch();           /* Don't echo it */
  //      printf("now break");
          if (*c == '\r') *c = '\n';
          return((boolean)TRUE);
      }
      return((boolean)FALSE);     /* Keeps Lattice compiler happy */
  }
  #endif
  
  #ifdef UNIX
  public boolean ascii_input(c)
  char *c;
  {
  #ifdef UNIX_MACH
          /*
           * we can't read from stdin directly, because the ascii
           * input thread is already doing so, so instead we'll
           * wait for that thread to read a character and then take
           * it
           */
          boolean ret = FALSE;
  
          A_LOCK();
                  if (a_in_flag) {
                          (*c) = a_in;
                          a_in_flag = 0;
                          ret = TRUE;
                  }
          A_UNLOCK();
          if (ret) {
  #ifdef RTMach
                  itc_condition_signal(&a_cond);
  #else /* RTMach */
                  condition_signal(&a_cond);
  #endif /* RTMach */
          }
          if ((*c) == '\r')
                  (*c) = '\n';
          return(ret);
  #else /* __APPLE__ */
  #ifndef BUFFERED_SYNCHRONOUS_INPUT
      int input = IOgetchar();
      if (input != IOnochar) {
          *c = input;
          if (*c == '\r') *c = '\n';
          return TRUE;
      }
  #endif /* BUFFERED_SYNCHRONOUS_INPUT */
      return FALSE;
  #endif /* __APPLE__ */
  }
  #endif
  
  #ifndef AMIGA /*DOS and MAC and UNIX */
  public void unget_ascii(char c)
  {
          if (type_ahead_head == 0) type_ahead_head = type_ahead_max;
          type_ahead_head--;
          type_ahead[type_ahead_head] = c;
          type_ahead_count++;
  }
  
  
  public boolean check_ascii()
  {
          char c;
          
          if(get_ascii(&c)) {
                  unget_ascii(c);
                  return TRUE;
          }
          else return FALSE;
  }
  #endif
  
  
  /****************************************************************************
  *                   wait_ascii
  * Returns:
  *    int: character for key pressed
  * Effect:
  *    waits for the user to type a key on the terminal keyboard
  *    (versus the synthesizer keyboard) and returns the key typed
  ****************************************************************************/
  
  #ifdef MACINTOSH
  public int wait_ascii()
  {
      char key ;    /* key typed */
  
      if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
      if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
      gflush();
      while (!get_ascii(&key)) ;
      return(key); 
  }
  #endif
  
  #ifdef DOS
  public int wait_ascii()
  {
      char key ;    /* key typed */
  
      if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
      if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
      if (!get_ascii(&key)) {
          key = _getch(); // block until we get an input
      }
      /* GWL - check for abort on previos line */
      return (int)key; 
  }
  #endif
  
  #ifndef MACINTOSH
  #ifndef DOS
  public int wait_ascii()
  {
  #ifdef UNIX /* was defined (UNIX) || defined(ITC) */
  #ifndef UNIX_MACH
          fd_set readfds;
  #endif /* !UNIX_MACH */
  #endif
      char c;
      struct rlimit file_limit;
  
      if (abort_flag == ABORT_LEVEL) return ABORT_CHAR;
      if (abort_flag == BREAK_LEVEL) return BREAK_CHAR;
      while (!get_ascii(&c)) {
  #ifdef AMIGA
          WaitPort(ConInPort);
  #endif
  #ifdef UNIX
          fflush(stdout);
  #ifdef UNIX_MACH
          /*
           * we can't select, since another thread is reading
           * from stdin, and we don't want to have an input war
           * so instead, the ascii input thread will signal
           * a_in_cond when it gets input, so we just wait
           * for that to happen
           */
          A_LOCK();
  #ifdef RTMach
                  itc_condition_wait(&a_in_cond, &a_mutex);
  #else /* RTMach */
                  condition_wait(&a_in_cond, &a_mutex);
  #endif /* RTMach */
          A_UNLOCK();
  #else /* UNIX_MACH */
          FD_ZERO(&readfds);
          FD_SET(IOinputfd, &readfds);
          gflush();
          getrlimit(RLIMIT_NOFILE, &file_limit);
          select(file_limit.rlim_max+1, &readfds, 0, 0, NULL);
  #endif /* !__APPLE__ */
  #endif /* ifdef UNIX */
      }
      return (int) c;
  }
  #endif
  #endif
  
  #ifdef AMIGA
  /******************************************************************
                                AMIGA 2000.
                           Console IO Functions
                               JCD 25-Apr-88
  *******************************************************************/
  
  UBYTE KeybSig()
  {
      return ConInPort->mp_SigBit;
  }
  
  private void ConPutChar(c)
  char c;
  {
      ConOutReq->io_Command = CMD_WRITE;
      ConOutReq->io_Data = (APTR)&c;
      ConOutReq->io_Length = 1;
      DoIO((struct IORequest *) ConOutReq);
  }
  
  private void ConPutStr(str)
  char *str;
  {
      ConOutReq->io_Command = CMD_WRITE;
      ConOutReq->io_Data = (APTR)str;
      ConOutReq->io_Length = -1;
      DoIO((struct IORequest *) ConOutReq);
  }
  
  private void ConRead()
  {
      ConInReq->io_Command = CMD_READ;
      ConInReq->io_Data = (APTR)KeyBuff;
      ConInReq->io_Length = 1;
      SendIO((struct IORequest *) ConInReq);
  }
  #endif
  