mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-18 17:10:05 +02:00
------------------------------------------------------------------------ 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.
999 lines
25 KiB
C
999 lines
25 KiB
C
/* xlread - xlisp expression input routine */
|
|
/* Copyright (c) 1985, by David Michael Betz
|
|
All Rights Reserved
|
|
Permission is granted for unrestricted non-commercial use */
|
|
/* CHANGE LOG
|
|
* --------------------------------------------------------------------
|
|
* 28Apr03 dm eliminate some compiler warnings
|
|
* replaced system-specific code with generic calls (see path.c)
|
|
*/
|
|
|
|
|
|
#include "stdlib.h"
|
|
#include "string.h"
|
|
#include "switches.h"
|
|
#include "xlisp.h"
|
|
#ifdef WINDOWS
|
|
#include "winfun.h"
|
|
#endif
|
|
#ifdef MACINTOSH
|
|
#include "macstuff.h"
|
|
#endif
|
|
|
|
#ifdef DEBUG_INPUT
|
|
extern FILE *debug_input_fp;
|
|
#endif
|
|
|
|
/* symbol parser modes */
|
|
#define DONE 0
|
|
#define NORMAL 1
|
|
#define ESCAPE 2
|
|
|
|
/* external variables */
|
|
extern LVAL s_stdout,s_true,s_dot;
|
|
extern LVAL s_quote,s_function,s_bquote,s_comma,s_comat;
|
|
extern LVAL s_rtable,k_wspace,k_const,k_nmacro,k_tmacro;
|
|
extern LVAL k_sescape,k_mescape;
|
|
extern char buf[];
|
|
|
|
/* external routines */
|
|
extern FILE *osaopen(const char *name, const char *mode);
|
|
/* on the NeXT, atof is a macro in stdlib.h */
|
|
/* Is this a mistake? atof is declared in stdlib.h, but it is never a macro:
|
|
#if !defined(atof) && !defined(_WIN32)
|
|
extern double atof(const char *);
|
|
#endif
|
|
*/
|
|
#ifndef __MWERKS__
|
|
#if !defined(ITYPE) && !defined(_WIN32)
|
|
extern ITYPE;
|
|
#endif
|
|
#endif
|
|
|
|
#define WSPACE "\t \f\r\n"
|
|
#define CONST1 "!$%&*+-./0123456789:<=>?@[]^_{}~"
|
|
#define CONST2 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
|
|
/* forward declarations */
|
|
FORWARD LVAL callmacro(LVAL fptr, int ch);
|
|
FORWARD LOCAL LVAL psymbol(LVAL fptr);
|
|
FORWARD LOCAL LVAL punintern(LVAL fptr);
|
|
FORWARD LOCAL LVAL pnumber(LVAL fptr, int radix);
|
|
FORWARD LOCAL LVAL pquote(LVAL fptr, LVAL sym);
|
|
FORWARD LOCAL LVAL plist(LVAL fptr);
|
|
FORWARD LOCAL LVAL pvector(LVAL fptr);
|
|
FORWARD LOCAL void upcase(char *str);
|
|
FORWARD LOCAL int pname(LVAL fptr,int *pescflag);
|
|
FORWARD LOCAL void pcomment(LVAL fptr);
|
|
FORWARD LOCAL int checkeof(LVAL fptr);
|
|
FORWARD LOCAL int nextch(LVAL fptr);
|
|
FORWARD LOCAL void badeof(LVAL fptr);
|
|
FORWARD LOCAL int storech(char *buf, int i, int ch);
|
|
|
|
#ifdef WINDOWS
|
|
static char save_file_name[STRMAX+1]; /* keeps files opened by prompt */
|
|
static int sfn_valid = FALSE;
|
|
#endif
|
|
|
|
#ifdef DEBUG_INPUT
|
|
extern FILE *read_by_xlisp;
|
|
#endif
|
|
|
|
|
|
/* xlload - load a file of xlisp expressions */
|
|
int xlload(const char *fname, int vflag, int pflag)
|
|
{
|
|
char fullname[STRMAX+1];
|
|
#ifdef WINDOWS
|
|
char *ptr;
|
|
#endif
|
|
LVAL fptr,expr;
|
|
XLCONTEXT cntxt;
|
|
FILE *fp;
|
|
int sts;
|
|
|
|
/* protect some pointers */
|
|
xlstkcheck(2);
|
|
xlsave(fptr);
|
|
xlsave(expr);
|
|
|
|
/* space for copy + extension? */
|
|
if (strlen(fname) > STRMAX - 4) {
|
|
expr = cvstring(fname);
|
|
goto toolong;
|
|
}
|
|
strcpy(fullname,fname);
|
|
#ifdef WINDOWS
|
|
#ifdef WINGUI
|
|
if (strcmp(fullname, "*") == 0) {
|
|
if (sfn_valid) {
|
|
strcpy(fullname, save_file_name);
|
|
} else {
|
|
strcpy(fullname, "*.*");
|
|
}
|
|
}
|
|
if (strcmp(fullname, "*.*") == 0) {
|
|
const char *name = getfilename(NULL, "lsp", "r", "Load file");
|
|
if (name) {
|
|
strcpy(fullname, name);
|
|
strcpy(save_file_name, name);
|
|
sfn_valid = TRUE;
|
|
} else {
|
|
xlpopn(2);
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
/* replace "/" with "\" so that (current-path) will work */
|
|
for (ptr = fullname; *ptr; ptr++) {
|
|
if (*ptr == '/') *ptr = '\\';
|
|
}
|
|
#endif
|
|
|
|
/* allocate a file node */
|
|
fptr = cvfile(NULL);
|
|
|
|
/* open the file */
|
|
fp = osaopen(fullname, "r");
|
|
if (fp == NULL) {
|
|
/* default the extension if there is room */
|
|
if (needsextension(fullname)) {
|
|
char fullname_plus[STRMAX+1];
|
|
strcpy(fullname_plus, fullname);
|
|
strcat(fullname_plus, ".lsp");
|
|
fp = osaopen(fullname_plus, "r");
|
|
if (fp) strcpy(fullname, fullname_plus);
|
|
}
|
|
}
|
|
if (fp == NULL) {
|
|
/* new cross-platform code by dmazzoni - new xlisp_path
|
|
implementation is in path.c */
|
|
const char *newname = find_in_xlisp_path(fullname);
|
|
if (newname && newname[0]) {
|
|
if (strlen(newname) > STRMAX) {
|
|
expr = cvstring(newname);
|
|
goto toolong;
|
|
}
|
|
strcpy(fullname, newname);
|
|
fp = osaopen(fullname, "r");
|
|
}
|
|
}
|
|
if (fp == NULL) {
|
|
/* the file STILL wasn't found */
|
|
#ifdef DEBUG_INPUT
|
|
if (read_by_xlisp) {
|
|
fprintf(read_by_xlisp, ";;;;xlload: failed to open %s\n", fullname);
|
|
}
|
|
#endif
|
|
xlpopn(2);
|
|
return (FALSE);
|
|
}
|
|
|
|
setfile(fptr,fp);
|
|
setvalue(s_loadingfiles, cons(fptr, getvalue(s_loadingfiles)));
|
|
setvalue(s_loadingfiles, cons(cvstring(fullname), getvalue(s_loadingfiles)));
|
|
|
|
/* print the information line */
|
|
if (vflag)
|
|
{ snprintf(buf, STRMAX, "; loading \"%s\"\n", fullname); stdputstr(buf); }
|
|
|
|
#ifdef DEBUG_INPUT
|
|
if (read_by_xlisp) {
|
|
fprintf(read_by_xlisp, ";;;;xlload: begin loading %s\n", fullname);
|
|
}
|
|
#endif
|
|
|
|
/* read, evaluate and possibly print each expression in the file */
|
|
xlbegin(&cntxt,CF_ERROR,s_true);
|
|
if (_setjmp(cntxt.c_jmpbuf))
|
|
sts = FALSE;
|
|
#ifdef DEBUG_INPUT
|
|
if (read_by_xlisp) {
|
|
fprintf(read_by_xlisp, ";;;;xlload: catch longjump, back to %s\n", fullname);
|
|
}
|
|
#endif
|
|
else {
|
|
#ifdef DEBUG_INPUT
|
|
if (read_by_xlisp) {
|
|
fprintf(read_by_xlisp, ";;;;xlload: about to read from %s (%x)\n", fullname, fptr);
|
|
}
|
|
#endif
|
|
/* a nested load that fails will cause all loading files to be closed,
|
|
* so check to make sure fptr is still valid each time through the loop */
|
|
while (getfile(fptr) && xlread(fptr,&expr,FALSE)) {
|
|
#ifdef DEBUG_INPUT
|
|
if (debug_input_fp) {
|
|
int c = getc(debug_input_fp);
|
|
ungetc(c, debug_input_fp);
|
|
}
|
|
#endif
|
|
|
|
expr = xleval(expr);
|
|
|
|
#ifdef DEBUG_INPUT
|
|
if (debug_input_fp) {
|
|
int c = getc(debug_input_fp);
|
|
ungetc(c, debug_input_fp);
|
|
}
|
|
#endif
|
|
|
|
if (pflag)
|
|
stdprint(expr);
|
|
|
|
#ifdef DEBUG_INPUT
|
|
if (debug_input_fp) {
|
|
int c = getc(debug_input_fp);
|
|
ungetc(c, debug_input_fp);
|
|
}
|
|
#endif
|
|
#ifdef DEBUG_INPUT
|
|
if (read_by_xlisp) {
|
|
fprintf(read_by_xlisp, ";;;;xlload: about to read from %s (%x)\n", fullname, fptr);
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef DEBUG_INPUT
|
|
if (read_by_xlisp) {
|
|
fprintf(read_by_xlisp, ";;;;xlload: xlread returned false for %s (%x)\n", fullname, fptr);
|
|
}
|
|
#endif
|
|
/* return success only if file did not disappear out from under us */
|
|
sts = (getfile(fptr) != NULL);
|
|
}
|
|
xlend(&cntxt);
|
|
|
|
/* close the file */
|
|
if (getfile(fptr)) { /* test added by RBD, see close_loadingfiles() */
|
|
osclose(getfile(fptr));
|
|
setfile(fptr,NULL);
|
|
}
|
|
if (consp(getvalue(s_loadingfiles)) &&
|
|
consp(cdr(getvalue(s_loadingfiles))) &&
|
|
car(cdr(getvalue(s_loadingfiles))) == fptr) {
|
|
setvalue(s_loadingfiles, cdr(cdr(getvalue(s_loadingfiles))));
|
|
}
|
|
|
|
/* restore the stack */
|
|
xlpopn(2);
|
|
|
|
#ifdef DEBUG_INPUT
|
|
if (read_by_xlisp) {
|
|
fprintf(read_by_xlisp, ";;;;xlload: finished loading %s\n", fullname);
|
|
}
|
|
#endif
|
|
|
|
/* return status */
|
|
return (sts);
|
|
|
|
toolong:
|
|
xlcerror("ignore file", "file name too long", expr);
|
|
xlpopn(2);
|
|
return FALSE;
|
|
}
|
|
|
|
/* xlread - read an xlisp expression */
|
|
int xlread(LVAL fptr, LVAL *pval, int rflag)
|
|
{
|
|
int sts;
|
|
|
|
/* read an expression */
|
|
while ((sts = readone(fptr,pval)) == FALSE)
|
|
#ifdef DEBUG_INPUT
|
|
if (debug_input_fp) {
|
|
int c = getc(debug_input_fp);
|
|
ungetc(c, debug_input_fp);
|
|
}
|
|
#endif
|
|
;
|
|
|
|
/* return status */
|
|
return (sts == EOF ? FALSE : TRUE);
|
|
}
|
|
|
|
/* readone - attempt to read a single expression */
|
|
int readone(LVAL fptr, LVAL *pval)
|
|
{
|
|
LVAL val,type;
|
|
int ch;
|
|
|
|
#ifdef DEBUG_INPUT
|
|
if (debug_input_fp) {
|
|
int c = getc(debug_input_fp);
|
|
ungetc(c, debug_input_fp);
|
|
}
|
|
#endif
|
|
/* get a character and check for EOF */
|
|
if ((ch = xlgetc(fptr)) == EOF)
|
|
return (EOF);
|
|
|
|
/* handle white space */
|
|
if ((type = tentry(ch)) == k_wspace)
|
|
return (FALSE);
|
|
|
|
/* handle symbol constituents */
|
|
else if (type == k_const) {
|
|
xlungetc(fptr,ch);
|
|
*pval = psymbol(fptr);
|
|
return (TRUE);
|
|
}
|
|
|
|
/* handle single and multiple escapes */
|
|
else if (type == k_sescape || type == k_mescape) {
|
|
xlungetc(fptr,ch);
|
|
*pval = psymbol(fptr);
|
|
return (TRUE);
|
|
}
|
|
|
|
/* handle read macros */
|
|
else if (consp(type)) {
|
|
if ((val = callmacro(fptr,ch)) && consp(val)) {
|
|
*pval = car(val);
|
|
return (TRUE);
|
|
}
|
|
else
|
|
return (FALSE);
|
|
}
|
|
|
|
/* handle illegal characters */
|
|
else {
|
|
xlerror("illegal character",cvfixnum((FIXTYPE)ch));
|
|
/* this point will never be reached because xlerror() does a
|
|
_longjmp(). The return is added to avoid false positive
|
|
error messages from static analyzers and compilers */
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
/* rmhash - read macro for '#' */
|
|
LVAL rmhash(void)
|
|
{
|
|
LVAL fptr,mch,val;
|
|
int escflag,ch;
|
|
|
|
/* protect some pointers */
|
|
xlsave1(val);
|
|
|
|
/* get the file and macro character */
|
|
fptr = xlgetfile();
|
|
mch = xlgachar();
|
|
xllastarg();
|
|
|
|
/* make the return value */
|
|
val = consa(NIL);
|
|
|
|
/* check the next character */
|
|
switch (ch = xlgetc(fptr)) {
|
|
case '\'':
|
|
rplaca(val,pquote(fptr,s_function));
|
|
break;
|
|
case '(':
|
|
rplaca(val,pvector(fptr));
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
rplaca(val,pnumber(fptr,2));
|
|
break;
|
|
case 'o':
|
|
case 'O':
|
|
rplaca(val,pnumber(fptr,8));
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
rplaca(val,pnumber(fptr,16));
|
|
break;
|
|
case '\\':
|
|
xlungetc(fptr,ch);
|
|
pname(fptr,&escflag);
|
|
ch = buf[0];
|
|
if (strlen(buf) > 1) {
|
|
upcase((char *) buf);
|
|
if (strcmp(buf,"NEWLINE") == 0)
|
|
ch = '\n';
|
|
else if (strcmp(buf,"SPACE") == 0)
|
|
ch = ' ';
|
|
else if (strcmp(buf,"TAB") == 0)
|
|
ch = '\t';
|
|
else
|
|
xlerror("unknown character name",cvstring(buf));
|
|
}
|
|
rplaca(val,cvchar(ch));
|
|
break;
|
|
case ':':
|
|
rplaca(val,punintern(fptr));
|
|
break;
|
|
case '|':
|
|
pcomment(fptr);
|
|
val = NIL;
|
|
break;
|
|
default:
|
|
xlerror("illegal character after #",cvfixnum((FIXTYPE)ch));
|
|
}
|
|
|
|
/* restore the stack */
|
|
xlpop();
|
|
|
|
/* return the value */
|
|
return (val);
|
|
}
|
|
|
|
/* rmquote - read macro for '\'' */
|
|
LVAL rmquote(void)
|
|
{
|
|
LVAL fptr,mch;
|
|
|
|
/* get the file and macro character */
|
|
fptr = xlgetfile();
|
|
mch = xlgachar();
|
|
xllastarg();
|
|
|
|
/* parse the quoted expression */
|
|
return (consa(pquote(fptr,s_quote)));
|
|
}
|
|
|
|
/* rmdquote - read macro for '"' */
|
|
LVAL rmdquote(void)
|
|
{
|
|
unsigned char buf[STRMAX+1],*p,*sptr;
|
|
LVAL fptr,str,newstr,mch;
|
|
int len,blen,ch,d2,d3;
|
|
|
|
/* protect some pointers */
|
|
xlsave1(str);
|
|
|
|
/* get the file and macro character */
|
|
fptr = xlgetfile();
|
|
mch = xlgachar();
|
|
xllastarg();
|
|
|
|
/* loop looking for a closing quote */
|
|
len = blen = 0; p = buf;
|
|
while ((ch = checkeof(fptr)) != '"') {
|
|
|
|
/* handle escaped characters */
|
|
switch (ch) {
|
|
case '\\':
|
|
switch (ch = checkeof(fptr)) {
|
|
case 't':
|
|
ch = '\011';
|
|
break;
|
|
case 'n':
|
|
ch = '\012';
|
|
break;
|
|
case 'f':
|
|
ch = '\014';
|
|
break;
|
|
case 'r':
|
|
ch = '\015';
|
|
break;
|
|
default:
|
|
if (ch >= '0' && ch <= '7') {
|
|
d2 = checkeof(fptr);
|
|
d3 = checkeof(fptr);
|
|
if (d2 < '0' || d2 > '7'
|
|
|| d3 < '0' || d3 > '7')
|
|
xlfail("invalid octal digit");
|
|
ch -= '0'; d2 -= '0'; d3 -= '0';
|
|
ch = (ch << 6) | (d2 << 3) | d3;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* check for buffer overflow */
|
|
if (blen >= STRMAX) {
|
|
newstr = new_string(len + STRMAX + 1);
|
|
sptr = getstring(newstr); *sptr = '\0';
|
|
if (str) strcat((char *) sptr, (char *) getstring(str));
|
|
*p = '\0'; strcat((char *) sptr, (char *) buf);
|
|
p = buf; blen = 0;
|
|
len += STRMAX;
|
|
str = newstr;
|
|
}
|
|
|
|
/* store the character */
|
|
*p++ = ch; ++blen;
|
|
}
|
|
|
|
/* append the last substring */
|
|
if (str == NIL || blen) {
|
|
newstr = new_string(len + blen + 1);
|
|
sptr = getstring(newstr); *sptr = '\0';
|
|
if (str) strcat((char *) sptr, (char *) getstring(str));
|
|
*p = '\0'; strcat((char *) sptr, (char *) buf);
|
|
str = newstr;
|
|
}
|
|
|
|
/* restore the stack */
|
|
xlpop();
|
|
|
|
/* return the new string */
|
|
return (consa(str));
|
|
}
|
|
|
|
/* rmbquote - read macro for '`' */
|
|
LVAL rmbquote(void)
|
|
{
|
|
LVAL fptr,mch;
|
|
|
|
/* get the file and macro character */
|
|
fptr = xlgetfile();
|
|
mch = xlgachar();
|
|
xllastarg();
|
|
|
|
/* parse the quoted expression */
|
|
return (consa(pquote(fptr,s_bquote)));
|
|
}
|
|
|
|
/* rmcomma - read macro for ',' */
|
|
LVAL rmcomma(void)
|
|
{
|
|
LVAL fptr,mch,sym;
|
|
int ch;
|
|
|
|
/* get the file and macro character */
|
|
fptr = xlgetfile();
|
|
mch = xlgachar();
|
|
xllastarg();
|
|
|
|
/* check the next character */
|
|
if ((ch = xlgetc(fptr)) == '@')
|
|
sym = s_comat;
|
|
else {
|
|
xlungetc(fptr,ch);
|
|
sym = s_comma;
|
|
}
|
|
|
|
/* make the return value */
|
|
return (consa(pquote(fptr,sym)));
|
|
}
|
|
|
|
/* rmlpar - read macro for '(' */
|
|
LVAL rmlpar(void)
|
|
{
|
|
LVAL fptr,mch;
|
|
|
|
/* get the file and macro character */
|
|
fptr = xlgetfile();
|
|
mch = xlgachar();
|
|
xllastarg();
|
|
|
|
/* make the return value */
|
|
return (consa(plist(fptr)));
|
|
}
|
|
|
|
/* 4035 is the "no return value" warning message */
|
|
/* rmrpar, pcomment, badeof, and upcase don't return anything */
|
|
/* #pragma warning(disable: 4035) */
|
|
|
|
/* rmrpar - read macro for ')' */
|
|
LVAL rmrpar(void)
|
|
{
|
|
xlfail("misplaced right paren");
|
|
return NULL; /* never used */
|
|
}
|
|
|
|
/* rmsemi - read macro for ';' */
|
|
LVAL rmsemi(void)
|
|
{
|
|
LVAL fptr,mch;
|
|
int ch;
|
|
|
|
/* get the file and macro character */
|
|
fptr = xlgetfile();
|
|
mch = xlgachar();
|
|
xllastarg();
|
|
|
|
/* skip to end of line */
|
|
while ((ch = xlgetc(fptr)) != EOF && ch != '\n')
|
|
;
|
|
|
|
/* return nil (nothing read) */
|
|
return (NIL);
|
|
}
|
|
|
|
/* pcomment - parse a comment delimited by #| and |# */
|
|
LOCAL void pcomment(LVAL fptr)
|
|
{
|
|
int lastch,ch,n;
|
|
|
|
/* look for the matching delimiter (and handle nesting) */
|
|
for (n = 1, lastch = -1; n > 0 && (ch = xlgetc(fptr)) != EOF; ) {
|
|
if (lastch == '|' && ch == '#')
|
|
{ --n; ch = -1; }
|
|
else if (lastch == '#' && ch == '|')
|
|
{ ++n; ch = -1; }
|
|
lastch = ch;
|
|
}
|
|
}
|
|
|
|
/* pnumber - parse a number */
|
|
LOCAL LVAL pnumber(LVAL fptr, int radix)
|
|
{
|
|
int digit,ch;
|
|
long num;
|
|
|
|
for (num = 0L; (ch = xlgetc(fptr)) != EOF; ) {
|
|
if (islower(ch)) ch = toupper(ch);
|
|
if (!('0' <= ch && ch <= '9') && !('A' <= ch && ch <= 'F'))
|
|
break;
|
|
if ((digit = (ch <= '9' ? ch - '0' : ch - 'A' + 10)) >= radix)
|
|
break;
|
|
num = num * (long)radix + (long)digit;
|
|
}
|
|
xlungetc(fptr,ch);
|
|
return (cvfixnum((FIXTYPE)num));
|
|
}
|
|
|
|
/* plist - parse a list */
|
|
LOCAL LVAL plist(LVAL fptr)
|
|
{
|
|
LVAL val,expr,lastnptr,nptr;
|
|
|
|
/* protect some pointers */
|
|
xlstkcheck(2);
|
|
xlsave(val);
|
|
xlsave(expr);
|
|
|
|
/* keep appending nodes until a closing paren is found */
|
|
for (lastnptr = NIL; nextch(fptr) != ')'; )
|
|
|
|
/* get the next expression */
|
|
switch (readone(fptr,&expr)) {
|
|
case EOF:
|
|
badeof(fptr);
|
|
case TRUE:
|
|
|
|
/* check for a dotted tail */
|
|
if (expr == s_dot) {
|
|
/* make sure there's a node */
|
|
if (lastnptr == NIL)
|
|
xlfail("invalid dotted pair");
|
|
|
|
/* parse the expression after the dot */
|
|
if (!xlread(fptr,&expr,TRUE))
|
|
badeof(fptr);
|
|
rplacd(lastnptr,expr);
|
|
|
|
/* make sure its followed by a close paren */
|
|
if (nextch(fptr) != ')')
|
|
xlfail("invalid dotted pair");
|
|
}
|
|
|
|
/* otherwise, handle a normal list element */
|
|
else {
|
|
nptr = consa(expr);
|
|
if (lastnptr == NIL)
|
|
val = nptr;
|
|
else
|
|
rplacd(lastnptr,nptr);
|
|
lastnptr = nptr;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* skip the closing paren */
|
|
xlgetc(fptr);
|
|
|
|
/* restore the stack */
|
|
xlpopn(2);
|
|
|
|
/* return successfully */
|
|
return (val);
|
|
}
|
|
|
|
/* pvector - parse a vector */
|
|
LOCAL LVAL pvector(LVAL fptr)
|
|
{
|
|
LVAL list,expr,val,lastnptr,nptr;
|
|
int len,ch,i;
|
|
|
|
/* protect some pointers */
|
|
xlstkcheck(2);
|
|
xlsave(list);
|
|
xlsave(expr);
|
|
|
|
/* keep appending nodes until a closing paren is found */
|
|
for (lastnptr = NIL, len = 0; (ch = nextch(fptr)) != ')'; ) {
|
|
|
|
/* check for end of file */
|
|
if (ch == EOF)
|
|
badeof(fptr);
|
|
|
|
/* get the next expression */
|
|
switch (readone(fptr,&expr)) {
|
|
case EOF:
|
|
badeof(fptr);
|
|
case TRUE:
|
|
nptr = consa(expr);
|
|
if (lastnptr == NIL)
|
|
list = nptr;
|
|
else
|
|
rplacd(lastnptr,nptr);
|
|
lastnptr = nptr;
|
|
len++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* skip the closing paren */
|
|
xlgetc(fptr);
|
|
|
|
/* make a vector of the appropriate length */
|
|
val = newvector(len);
|
|
|
|
/* copy the list into the vector */
|
|
for (i = 0; i < len; ++i, list = cdr(list))
|
|
setelement(val,i,car(list));
|
|
|
|
/* restore the stack */
|
|
xlpopn(2);
|
|
|
|
/* return successfully */
|
|
return (val);
|
|
}
|
|
|
|
/* pquote - parse a quoted expression */
|
|
LOCAL LVAL pquote(LVAL fptr, LVAL sym)
|
|
{
|
|
LVAL val,p;
|
|
|
|
/* protect some pointers */
|
|
xlsave1(val);
|
|
|
|
/* allocate two nodes */
|
|
val = consa(sym);
|
|
rplacd(val,consa(NIL));
|
|
|
|
/* initialize the second to point to the quoted expression */
|
|
if (!xlread(fptr,&p,TRUE))
|
|
badeof(fptr);
|
|
rplaca(cdr(val),p);
|
|
|
|
/* restore the stack */
|
|
xlpop();
|
|
|
|
/* return the quoted expression */
|
|
return (val);
|
|
}
|
|
|
|
/* psymbol - parse a symbol name */
|
|
LOCAL LVAL psymbol(LVAL fptr)
|
|
{
|
|
int escflag;
|
|
LVAL val;
|
|
pname(fptr,&escflag);
|
|
return (escflag || !xlisnumber(buf,&val) ? xlenter(buf) : val);
|
|
}
|
|
|
|
/* punintern - parse an uninterned symbol */
|
|
LOCAL LVAL punintern(LVAL fptr)
|
|
{
|
|
int escflag;
|
|
pname(fptr,&escflag);
|
|
return (xlmakesym(buf));
|
|
}
|
|
|
|
/* pname - parse a symbol/package name */
|
|
LOCAL int pname(LVAL fptr,int *pescflag)
|
|
{
|
|
int mode,ch=0,i;
|
|
LVAL type;
|
|
|
|
/* initialize */
|
|
*pescflag = FALSE;
|
|
mode = NORMAL;
|
|
i = 0;
|
|
|
|
/* accumulate the symbol name */
|
|
while (mode != DONE) {
|
|
|
|
/* handle normal mode */
|
|
while (mode == NORMAL)
|
|
if ((ch = xlgetc(fptr)) == EOF)
|
|
mode = DONE;
|
|
else if ((type = tentry(ch)) == k_sescape) {
|
|
i = storech(buf,i,checkeof(fptr));
|
|
*pescflag = TRUE;
|
|
}
|
|
else if (type == k_mescape) {
|
|
*pescflag = TRUE;
|
|
mode = ESCAPE;
|
|
}
|
|
else if (type == k_const
|
|
|| (consp(type) && car(type) == k_nmacro))
|
|
i = storech(buf,i,islower(ch) ? toupper(ch) : ch);
|
|
else
|
|
mode = DONE;
|
|
|
|
/* handle multiple escape mode */
|
|
while (mode == ESCAPE)
|
|
if ((ch = xlgetc(fptr)) == EOF)
|
|
badeof(fptr);
|
|
else if ((type = tentry(ch)) == k_sescape)
|
|
i = storech(buf,i,checkeof(fptr));
|
|
else if (type == k_mescape)
|
|
mode = NORMAL;
|
|
else
|
|
i = storech(buf,i,ch);
|
|
}
|
|
buf[i] = 0;
|
|
|
|
/* check for a zero length name */
|
|
if (i == 0)
|
|
xlerror("zero length name", s_unbound);
|
|
|
|
/* unget the last character and return it */
|
|
xlungetc(fptr,ch);
|
|
return (ch);
|
|
}
|
|
|
|
/* storech - store a character in the print name buffer */
|
|
LOCAL int storech(char *buf, int i, int ch)
|
|
{
|
|
if (i < STRMAX)
|
|
buf[i++] = ch;
|
|
return (i);
|
|
}
|
|
|
|
/* tentry - get a readtable entry */
|
|
LVAL tentry(int ch)
|
|
{
|
|
LVAL rtable;
|
|
rtable = getvalue(s_rtable);
|
|
if (!vectorp(rtable) || ch < 0 || ch >= getsize(rtable))
|
|
return (NIL);
|
|
return (getelement(rtable,ch));
|
|
}
|
|
|
|
/* nextch - look at the next non-blank character */
|
|
LOCAL int nextch(LVAL fptr)
|
|
{
|
|
int ch;
|
|
|
|
/* return and save the next non-blank character */
|
|
while ((ch = xlgetc(fptr)) != EOF && isspace(ch))
|
|
;
|
|
xlungetc(fptr,ch);
|
|
return (ch);
|
|
}
|
|
|
|
/* checkeof - get a character and check for end of file */
|
|
LOCAL int checkeof(LVAL fptr)
|
|
{
|
|
int ch;
|
|
|
|
if ((ch = xlgetc(fptr)) == EOF)
|
|
badeof(fptr);
|
|
return (ch);
|
|
}
|
|
|
|
/* badeof - unexpected eof */
|
|
LOCAL void badeof(LVAL fptr)
|
|
{
|
|
xlgetc(fptr);
|
|
xlfail("unexpected EOF");
|
|
}
|
|
|
|
/* xlisnumber - check if this string is a number */
|
|
int xlisnumber(char *str, LVAL *pval)
|
|
{
|
|
int dl,dr;
|
|
char *p;
|
|
|
|
/* initialize */
|
|
p = str; dl = dr = 0;
|
|
|
|
/* check for a sign */
|
|
if (*p == '+' || *p == '-')
|
|
p++;
|
|
|
|
/* check for a string of digits */
|
|
while (isdigit(*p)) {
|
|
p++;
|
|
dl++;
|
|
}
|
|
/* check for a decimal point */
|
|
if (*p == '.') {
|
|
p++;
|
|
while (isdigit(*p)) {
|
|
p++;
|
|
dr++;
|
|
}
|
|
}
|
|
|
|
/* check for an exponent */
|
|
if ((dl || dr) && *p == 'E') {
|
|
p++;
|
|
|
|
/* check for a sign */
|
|
if (*p == '+' || *p == '-')
|
|
p++;
|
|
|
|
/* check for a string of digits */
|
|
while (isdigit(*p)) {
|
|
p++;
|
|
dr++;
|
|
}
|
|
}
|
|
|
|
/* make sure there was at least one digit and this is the end */
|
|
if ((dl == 0 && dr == 0) || *p)
|
|
return (FALSE);
|
|
|
|
/* convert the string to an integer and return successfully */
|
|
if (pval) {
|
|
if (*str == '+') ++str;
|
|
if (str[strlen(str)-1] == '.') str[strlen(str)-1] = 0;
|
|
*pval = (dr ? cvflonum(atof(str)) : cvfixnum(ICNV(str)));
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
/* defmacro - define a read macro */
|
|
void defmacro(int ch, LVAL type, int offset)
|
|
{
|
|
extern FUNDEF funtab[];
|
|
LVAL subr;
|
|
subr = cvsubr(funtab[offset].fd_subr,funtab[offset].fd_type,offset);
|
|
setelement(getvalue(s_rtable),ch,cons(type,subr));
|
|
}
|
|
|
|
/* callmacro - call a read macro */
|
|
LVAL callmacro(LVAL fptr, int ch)
|
|
{
|
|
LVAL *newfp;
|
|
|
|
/* create the new call frame */
|
|
newfp = xlsp;
|
|
pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
|
|
pusharg(cdr(getelement(getvalue(s_rtable),ch)));
|
|
pusharg(cvfixnum((FIXTYPE)2));
|
|
pusharg(fptr);
|
|
pusharg(cvchar(ch));
|
|
xlfp = newfp;
|
|
return (xlapply(2));
|
|
}
|
|
|
|
/* upcase - translate a string to upper case */
|
|
LOCAL void upcase(char *str)
|
|
{
|
|
for (; *str != '\0'; ++str)
|
|
if (islower(*str))
|
|
*str = toupper(*str);
|
|
}
|
|
|
|
/* xlrinit - initialize the reader */
|
|
void xlrinit(void)
|
|
{
|
|
LVAL rtable;
|
|
char *p;
|
|
int ch;
|
|
|
|
/* create the read table */
|
|
rtable = newvector(256);
|
|
setvalue(s_rtable,rtable);
|
|
|
|
/* initialize the readtable */
|
|
for (p = WSPACE; (ch = *p++); )
|
|
setelement(rtable,ch,k_wspace);
|
|
for (p = CONST1; (ch = *p++); )
|
|
setelement(rtable,ch,k_const);
|
|
for (p = CONST2; (ch = *p++); )
|
|
setelement(rtable,ch,k_const);
|
|
|
|
/* setup the escape characters */
|
|
setelement(rtable,'\\',k_sescape);
|
|
setelement(rtable,'|', k_mescape);
|
|
|
|
/* install the read macros */
|
|
defmacro('#', k_nmacro,FT_RMHASH);
|
|
defmacro('\'',k_tmacro,FT_RMQUOTE);
|
|
defmacro('"', k_tmacro,FT_RMDQUOTE);
|
|
defmacro('`', k_tmacro,FT_RMBQUOTE);
|
|
defmacro(',', k_tmacro,FT_RMCOMMA);
|
|
defmacro('(', k_tmacro,FT_RMLPAR);
|
|
defmacro(')', k_tmacro,FT_RMRPAR);
|
|
defmacro(';', k_tmacro,FT_RMSEMI);
|
|
}
|
|
|