1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-07 23:51:14 +02:00
Leland Lucius 15b9bb96cd Update nyquist to SVN r331 (r3.16+)
------------------------------------------------------------------------
   r331 | rbd | 2020-10-13 12:40:12 -0500 (Tue, 13 Oct 2020) | 2 lines

   Also forgot to install NyquistWords.txt

   ------------------------------------------------------------------------
   r330 | rbd | 2020-10-13 12:34:06 -0500 (Tue, 13 Oct 2020) | 2 lines

   Forgot to move nyquistman.pdf from docsrc/s2h to release

   ------------------------------------------------------------------------
   r329 | rbd | 2020-10-13 11:32:33 -0500 (Tue, 13 Oct 2020) | 2 lines

   Updated some version numbers for 3.16.

   ------------------------------------------------------------------------
   r328 | rbd | 2020-10-13 11:20:52 -0500 (Tue, 13 Oct 2020) | 2 lines

   Fixed NyquistIDE antialiasing for plot text, fix format of message.

   ------------------------------------------------------------------------
   r327 | rbd | 2020-10-12 21:01:53 -0500 (Mon, 12 Oct 2020) | 2 lines

   Fixed a couple of format problems in manual. This version of Nyquist has been tested wtih macOS, Linux, 32&64-bit Windows.

   ------------------------------------------------------------------------
   r326 | rbd | 2020-10-12 20:21:38 -0500 (Mon, 12 Oct 2020) | 1 line

   Modified WIN32 32-bit XLisp to use 64-bit FIXNUMs. This allows XLisp and Nyquist to handle big sounds even on 32-bit machines. Probably at some cost, but inner loops are mostly float and int32, and the Nyquist release is 64-bit anyway. Maybe we'll have to run some benchmarks on Audacity, which is still 32-bit on Windows.
   ------------------------------------------------------------------------
   r325 | rbd | 2020-10-12 13:16:57 -0500 (Mon, 12 Oct 2020) | 1 line

   Win64 passes bigfiletest.lsp now. This version should work on all 64-bit systems now. These changes untested on Linux and macOS.
   ------------------------------------------------------------------------
   r324 | rbd | 2020-10-11 21:31:53 -0500 (Sun, 11 Oct 2020) | 2 lines

   I couldn't free enough space on my linux box, so I adjusted the bigfiletest to write 8-bit ulaw. It's still >4GB and >4G samples. Works on Linux.

   ------------------------------------------------------------------------
   r323 | rbd | 2020-10-11 19:41:25 -0500 (Sun, 11 Oct 2020) | 2 lines

   Missing file from last commit.

   ------------------------------------------------------------------------
   r322 | rbd | 2020-10-11 19:36:08 -0500 (Sun, 11 Oct 2020) | 1 line

   Found another case where WIN64 needs int64_t instead of long for sample count.
   ------------------------------------------------------------------------
   r321 | rbd | 2020-10-11 19:33:25 -0500 (Sun, 11 Oct 2020) | 3 lines

   Fixed s-save to	handle optional	and keyword parameters (which should never have	been mixed in the first	place).	Documentation cleanup - should be final for this version.

   ------------------------------------------------------------------------
   r320 | rbd | 2020-10-11 14:44:37 -0500 (Sun, 11 Oct 2020) | 2 lines

   Fixes to handle IRCAM sound format and tests for big file io working on macOS.

   ------------------------------------------------------------------------
   r319 | rbd | 2020-10-10 21:31:58 -0500 (Sat, 10 Oct 2020) | 2 lines

   Changes for linux and to avoid compiler warnings on linux.

   ------------------------------------------------------------------------
   r318 | rbd | 2020-10-10 20:50:23 -0500 (Sat, 10 Oct 2020) | 1 line

   This is the test used for Win64 version.
   ------------------------------------------------------------------------
   r317 | rbd | 2020-10-10 20:34:34 -0500 (Sat, 10 Oct 2020) | 1 line

   This version works on Win64. Need to test changes on macOS and linux.
   ------------------------------------------------------------------------
   r316 | rbd | 2020-10-10 19:59:15 -0500 (Sat, 10 Oct 2020) | 2 lines

   PWL changes to avoid compiler warning.

   ------------------------------------------------------------------------
   r315 | rbd | 2020-10-10 19:34:04 -0500 (Sat, 10 Oct 2020) | 2 lines

   A few more changes for 64-bit sample counts on Win64

   ------------------------------------------------------------------------
   r314 | rbd | 2020-10-10 13:19:42 -0500 (Sat, 10 Oct 2020) | 2 lines

   Fixed int64_t declaration in gate.alg

   ------------------------------------------------------------------------
   r313 | rbd | 2020-10-10 12:07:40 -0500 (Sat, 10 Oct 2020) | 2 lines

   Fixes to gate for long sounds

   ------------------------------------------------------------------------
   r312 | rbd | 2020-10-10 11:47:29 -0500 (Sat, 10 Oct 2020) | 2 lines

   Fixed sound_save types for intgen

   ------------------------------------------------------------------------
   r311 | rbd | 2020-10-10 11:09:01 -0500 (Sat, 10 Oct 2020) | 2 lines

   Fixed a 64-bit sample count problem in siosc.alg

   ------------------------------------------------------------------------
   r310 | rbd | 2020-10-10 11:03:12 -0500 (Sat, 10 Oct 2020) | 2 lines

   Fixed sndmax to handle 64-bit sample counts.

   ------------------------------------------------------------------------
   r309 | rbd | 2020-10-10 10:57:04 -0500 (Sat, 10 Oct 2020) | 2 lines

   Forgot to re-translate all tran/*.alg files with fix for int64 cast to int32. This version compiles on macOS and ready for test on Win64.

   ------------------------------------------------------------------------
   r308 | rbd | 2020-10-10 10:16:05 -0500 (Sat, 10 Oct 2020) | 2 lines

   Everything seems to compile and run on macOS now. Moving changes to Windows for test.

   ------------------------------------------------------------------------
   r307 | rbd | 2020-10-10 09:23:45 -0500 (Sat, 10 Oct 2020) | 1 line

   Added casts to avoid compiler warnings and to review changes to support 64-bit sample counts on Windows. Still not complete, and waiting to regenerate and compile tran directory code after updates to translation code that will insert more casts.
   ------------------------------------------------------------------------
   r306 | rbd | 2020-10-09 21:55:15 -0500 (Fri, 09 Oct 2020) | 2 lines

   Rebuilt seqfnint.c from header files.

   ------------------------------------------------------------------------
   r305 | rbd | 2020-10-09 21:53:33 -0500 (Fri, 09 Oct 2020) | 1 line

   Changed some FIXNUMS to LONG to avoid compiler warnings in seqfnint.c
   ------------------------------------------------------------------------
   r304 | rbd | 2020-10-09 21:44:03 -0500 (Fri, 09 Oct 2020) | 2 lines

   I discovered forgotten regression-test.lsp and added test that requires 64-bit sample counts to pass. Fixed a few bugs revealed by running the type-checking regression tests.

   ------------------------------------------------------------------------
   r303 | rbd | 2020-10-09 12:28:58 -0500 (Fri, 09 Oct 2020) | 2 lines

   Changes for 64-bit sample counts broke mult-channel s-save. Fixed in the commit for macOS.

   ------------------------------------------------------------------------
   r302 | rbd | 2020-10-09 10:03:39 -0500 (Fri, 09 Oct 2020) | 2 lines

   Changed snd-play to return samples computed and used that to make a test for computing long sounds that would overflow 32-bit length counts.

   ------------------------------------------------------------------------
   r301 | rbd | 2020-10-09 09:11:26 -0500 (Fri, 09 Oct 2020) | 2 lines

   corrected mistake in delaycv.alg and re-translated

   ------------------------------------------------------------------------
   r300 | rbd | 2020-10-09 09:09:06 -0500 (Fri, 09 Oct 2020) | 2 lines

   Fix to delaycv.alg -- "s" changed to "input" to avoid matching "s" in "sample_type".

   ------------------------------------------------------------------------
   r299 | rbd | 2020-10-09 09:03:33 -0500 (Fri, 09 Oct 2020) | 4 lines

   To avoid compiler warnings, XLisp interfaces to C int and long are now
   specified as LONG rather than FIXNUM, and the stubs that call the C
   functions cast FIXNUMs from XLisp into longs before calling C functions.

   ------------------------------------------------------------------------
   r298 | rbd | 2020-10-08 22:20:26 -0500 (Thu, 08 Oct 2020) | 2 lines

   This commit has many more fixes to handle long (64-bit) sounds, including a lot of fixes for warnings by Visual Studio assigning int64_t to long (works on macOS, doesn't work on VS). This was compiled and tested on macOS, and even computed a 27.1-hour sound using OSC, LP, SUM and MULT (haven't tested I/O yet).

   ------------------------------------------------------------------------
   r297 | rbd | 2020-10-07 13:04:02 -0500 (Wed, 07 Oct 2020) | 2 lines

   This is a major cleanup. It started with the goal of changing long to int64_t for sample counts so that on 64-bit windows, where long is only 32-bits, the sample counts would nevertheless be 64-bit allowing long sounds, which was a limitation for long recordings in Audacity. Since I was using compiler warnings to track possible loss-of-precision conversions from 64-bit sample counts, and there were *many* warnings, I started cleaning up *all* the warnings and ended up with a very large set of changes, including "modernizing" C declarations that date back to XLisp and CMU MIDI Toolkit code and were never changed. This version runs all the examples.sal code on macOS, but will surely have problems on Windows and Linux given the number of changes.

   ------------------------------------------------------------------------
   r296 | rbd | 2020-10-06 13:34:20 -0500 (Tue, 06 Oct 2020) | 2 lines

   More changes from long to int64_t for sample counts.

   ------------------------------------------------------------------------
   r295 | rbd | 2020-10-06 11:53:49 -0500 (Tue, 06 Oct 2020) | 2 lines

   More work on using 64-bit sample counts. Changed MAX_STOP from 32-bit to 64-bit limit.

   ------------------------------------------------------------------------
   r294 | rbd | 2020-10-06 11:48:05 -0500 (Tue, 06 Oct 2020) | 2 lines

   Made some changes so that sample counts are int64_t (for windows) instead of long to support sample counts above 31 bits.

   ------------------------------------------------------------------------
   r293 | rbd | 2020-10-04 21:30:55 -0500 (Sun, 04 Oct 2020) | 2 lines

   Fixed a few minor things for Linux and tested on Linux.

   ------------------------------------------------------------------------
   r292 | rbd | 2020-10-04 21:00:28 -0500 (Sun, 04 Oct 2020) | 2 lines

   Update extensions: all are minor changes.

   ------------------------------------------------------------------------
   r291 | rbd | 2020-09-24 13:59:31 -0500 (Thu, 24 Sep 2020) | 2 lines

   New implementation of seq and seqrep, added get-real-time, documented get-real-time, fixed examples.sal and examples.lsp which are now in lib rather than extensions (so they are now back in the basic installation), other cleanup.

   ------------------------------------------------------------------------
   r290 | rbd | 2020-08-16 16:24:52 -0500 (Sun, 16 Aug 2020) | 2 lines

   Fixed bug in snd-gate, revised GATE and NOISE-GATE to handle multi-channel sound. RMS now handles multi-channel input. S-AVG added to take multichannel input (but not used, because RMS could not be written without making SND-SRATE convert multichannel sound to vector of floats. That seems to be going toward a fully vectorized model. Not going there for now.

   ------------------------------------------------------------------------
   r289 | rbd | 2020-07-09 16:27:45 -0500 (Thu, 09 Jul 2020) | 2 lines

   Added GET-REAL-TIME function to XLISP. May not work yet on Windows. Various fixes for compiler warnings. I noticed FLAC doesn't work (I guess it never did) and I cannot figure out how this even links because flac_min seems to be undefined. Something to look at later.
2021-01-27 23:45:25 -06:00

1294 lines
36 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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(void);
int GetWriteFileName(void);
#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(char *c);
/****************************************************************************
*
* 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, const 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(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(void)
{
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((int) (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