/********************************************************************** nyx.c Nyx: A very simple external interface to Nyquist Dominic Mazzoni **********************************************************************/ /* system includes */ #include #include #include #include #include #ifndef WIN32 #include #else #include #include #endif /* nyx includes */ #include "nyx.h" /* xlisp includes */ #include "switches.h" #include "xlisp.h" #include "cext.h" /* nyquist includes */ #include "sound.h" #include "samples.h" #include "falloc.h" /* use full copy */ #define NYX_FULL_COPY 1 /* show memory stats */ // #define NYX_MEMORY_STATS 1 /* show details of obarray copy */ // #define NYX_DEBUG_COPY 1 /* macro to compute the size of a segment (taken from xldmem.h) */ #define segsize(n) (sizeof(SEGMENT)+((n)-1)*sizeof(struct node)) /* xldmem external variables */ extern long nnodes; extern long nfree; extern long total; extern int nsegs; extern SEGMENT *segs; extern SEGMENT *lastseg; extern LVAL fnodes; /* nyquist externs */ extern LVAL a_sound; extern snd_list_type zero_snd_list; /* globals */ LOCAL nyx_os_callback nyx_os_cb = NULL; LOCAL void *nyx_os_ud; LOCAL nyx_output_callback nyx_output_cb; LOCAL void *nyx_output_ud; LOCAL int nyx_expr_pos; LOCAL int nyx_expr_len; LOCAL const char *nyx_expr_string; LOCAL LVAL nyx_result; LOCAL nyx_rval nyx_result_type = nyx_error; LOCAL XLCONTEXT nyx_cntxt; LOCAL int nyx_first_time = 1; LOCAL LVAL nyx_obarray; LOCAL FLOTYPE nyx_warp_stretch; LOCAL long nyx_input_length = 0; /* Suspension node */ typedef struct nyx_susp_struct { snd_susp_node susp; // Must be first nyx_audio_callback callback; void *userdata; long len; int channel; } nyx_susp_node, *nyx_susp_type; #if defined(NYX_DEBUG_COPY) && NYX_DEBUG_COPY static const char *_types_[] = { "FREE_NODE", "SUBR", "FSUBR", "CONS", "SYMBOL", "FIXNUM", "FLONUM", "STRING", "OBJECT", "STREAM", "VECTOR", "CLOSURE", "CHAR", "USTREAM", "EXTERN" }; // Dump the contents of the obarray LOCAL void nyx_show_obarray() { LVAL array = getvalue(obarray); LVAL sym; int i; for (i = 0; i < HSIZE; i++) { for (sym = getelement(array, i); sym; sym = cdr(sym)) { LVAL syma = car(sym); printf("_sym_ = "); xlprint(getvalue(s_stdout), syma, TRUE); if (getvalue(syma)) { printf(" _type_ = %s _val_ = ", _types_[ntype(getvalue(syma))]); xlprint(getvalue(s_stdout), getvalue(syma), TRUE); } if (getfunction(syma)) { printf(" _type_ = %s _fun_ = ", _types_[ntype(getfunction(syma))]); xlprint(getvalue(s_stdout), getfunction(syma), TRUE); } printf("\n"); } } } #endif // // Free empty segments // LOCAL void freesegs() { SEGMENT *seg; SEGMENT *next; // Free up as many nodes as possible gc(); // Reset free node tracking fnodes = NIL; nfree = 0L; // Reset the last segment pointer lastseg = NULL; // Scan all segments for (seg = segs; seg != NULL; seg = next) { int n = seg->sg_size; int empty = TRUE; int i; LVAL p; // Check this segment for in-use nodes p = &seg->sg_nodes[0]; for (i = n; --i >= 0; ++p) { if (ntype(p) != FREE_NODE) { empty = FALSE; break; } } // Retain pointer to next segment next = seg->sg_next; // Was the current segment empty? if (empty) { // Free the segment; free((void *) seg); // Unlink it from the list. No need to worry about a NULL lastseg // pointer here since the fixnum and char segments will always exist // at the head of the list and they will always have nodes. So, lastseg // will have been set before we find any empty nodes. lastseg->sg_next = next; // Reduce the stats total -= (long) segsize(n); nsegs--; nnodes -= n; } else { // Not empty, so remember this node as the last segment lastseg = seg; // Add all of the free nodes in this segment to the free list p = &seg->sg_nodes[0]; for (i = n; --i >= 0; ++p) { if (ntype(p) == FREE_NODE) { rplaca(p, NIL); rplacd(p, fnodes); fnodes = p; nfree++; } } } } } #if defined(NYX_FULL_COPY) && NYX_FULL_COPY // Copy a node (recursively if appropriate) LOCAL LVAL nyx_dup_value(LVAL val) { LVAL nval = val; // Protect old and new values xlprot1(val); xlprot1(nval); // Copy the node if (val != NIL) { switch (ntype(val)) { case FIXNUM: nval = cvfixnum(getfixnum(val)); break; case FLONUM: nval = cvflonum(getflonum(val)); break; case CHAR: nval = cvchar(getchcode(val)); break; case STRING: nval = cvstring((char *) getstring(val)); break; case VECTOR: { int len = getsize(val); int i; nval = newvector(len); nval->n_type = ntype(val); for (i = 0; i < len; i++) { if (getelement(val, i) == val) { setelement(nval, i, val); } else { setelement(nval, i, nyx_dup_value(getelement(val, i))); } } } break; case CONS: nval = nyx_dup_value(cdr(val)); nval = cons(nyx_dup_value(car(val)), nval); break; case SUBR: case FSUBR: nval = cvsubr(getsubr(val), ntype(val), getoffset(val)); break; // Symbols should never be copied since their addresses are cached // all over the place. case SYMBOL: nval = val; break; // Streams are not copied (although USTREAM could be) and reference // the original value. case USTREAM: case STREAM: nval = val; break; // Externals aren't copied because I'm not entirely certain they can be. case EXTERN: nval = val; break; // For all other types, just allow them to reference the original // value. Probably not the right thing to do, but easier. case OBJECT: case CLOSURE: default: nval = val; break; } } xlpop(); xlpop(); return nval; } // Make a copy of the original obarray, leaving the original in place LOCAL void nyx_save_obarray() { LVAL newarray; int i; // This provide permanent protection for nyx_obarray as we do not want it // to be garbage-collected. xlprot1(nyx_obarray); nyx_obarray = getvalue(obarray); // Create and set the new vector. This allows us to use xlenter() to // properly add the new symbol. Probably slower than adding directly, // but guarantees proper hashing. newarray = newvector(HSIZE); setvalue(obarray, newarray); // Scan all obarray vectors for (i = 0; i < HSIZE; i++) { LVAL sym; // Scan all elements for (sym = getelement(nyx_obarray, i); sym; sym = cdr(sym)) { LVAL syma = car(sym); char *name = (char *) getstring(getpname(syma)); LVAL nsym = xlenter(name); // Ignore *OBARRAY* since there's no need to copy it if (strcmp(name, "*OBARRAY*") == 0) { continue; } // Ignore *SCRATCH* since it's allowed to be updated if (strcmp(name, "*SCRATCH*") == 0) { continue; } // Duplicate the symbol's values setvalue(nsym, nyx_dup_value(getvalue(syma))); setplist(nsym, nyx_dup_value(getplist(syma))); setfunction(nsym, nyx_dup_value(getfunction(syma))); } } // Swap the obarrays, so that the original is put back into service setvalue(obarray, nyx_obarray); nyx_obarray = newarray; } // Restore the symbol values to their original value and remove any added // symbols. LOCAL void nyx_restore_obarray() { LVAL obvec = getvalue(obarray); int i; // Scan all obarray vectors for (i = 0; i < HSIZE; i++) { LVAL last = NULL; LVAL dcon; // Scan all elements for (dcon = getelement(obvec, i); dcon; dcon = cdr(dcon)) { LVAL dsym = car(dcon); char *name = (char *)getstring(getpname(dsym)); LVAL scon; // Ignore *OBARRAY* since setting it causes the input array to be // truncated. if (strcmp(name, "*OBARRAY*") == 0) { continue; } // Ignore *SCRATCH* since it's allowed to be updated if (strcmp(name, "*SCRATCH*") == 0) { continue; } // Find the symbol in the original obarray. for (scon = getelement(nyx_obarray, hash(name, HSIZE)); scon; scon = cdr(scon)) { LVAL ssym = car(scon); // If found, then set the current symbols value to the original. if (strcmp(name, (char *)getstring(getpname(ssym))) == 0) { setvalue(dsym, nyx_dup_value(getvalue(ssym))); setplist(dsym, nyx_dup_value(getplist(ssym))); setfunction(dsym, nyx_dup_value(getfunction(ssym))); break; } } // If we didn't find the symbol in the original obarray, then it must've // been added and must be removed from the current obarray. if (scon == NULL) { if (last) { rplacd(last, cdr(dcon)); } else { setelement(obvec, i, cdr(dcon)); } } // Must track the last dcon for symbol removal last = dcon; } } } #else LOCAL LVAL copylist(LVAL from) { if (from == NULL) { return NULL; } return cons(car(from), copylist(cdr(from))); } /* Make a copy of the obarray so that we can erase any changes the user makes to global variables */ LOCAL void nyx_copy_obarray() { LVAL newarray; int i; // Create and set the new vector. newarray = newvector(HSIZE); setvalue(obarray, newarray); for (i = 0; i < HSIZE; i++) { LVAL from = getelement(nyx_obarray, i); if (from) { setelement(newarray, i, copylist(from)); } } } #endif void nyx_init() { if (nyx_first_time) { char *argv[1]; argv[0] = "nyquist"; xlisp_main_init(1, argv); nyx_os_cb = NULL; nyx_output_cb = NULL; nyx_first_time = 0; #if defined(NYX_FULL_COPY) && NYX_FULL_COPY // Save a copy of the original obarray's contents. nyx_save_obarray(); #else // Permanently protect the original obarray value. This is needed since // it would be unreferenced in the new obarray and would be garbage // collected. We want to keep it around so we can make copies of it to // refresh the execution state. xlprot1(nyx_obarray); nyx_obarray = getvalue(obarray); #endif } #if !defined(NYX_FULL_COPY) || !NYX_FULL_COPY // Create a copy of the original obarray nyx_copy_obarray(); #endif // Keep nyx_result from being garbage-collected xlprot1(nyx_result); #if defined(NYX_MEMORY_STATS) && NYX_MEMORY_STATS printf("\nnyx_init\n"); xmem(); #endif } void nyx_cleanup() { // Garbage-collect nyx_result xlpop(); #if defined(NYX_FULL_COPY) && NYX_FULL_COPY // Restore the original symbol values nyx_restore_obarray(); #else // Restore obarray to original state...but not the values setvalue(obarray, nyx_obarray); #endif // Make sure the sound nodes can be garbage-collected. Sounds are EXTERN // nodes whose value does not get copied during a full copy of the obarray. setvalue(xlenter("S"), NIL); // Free excess memory segments - does a gc() freesegs(); // Free unused memory pools falloc_gc(); // No longer need the callbacks nyx_output_cb = NULL; nyx_os_cb = NULL; // Reset vars nyx_input_length = 0; #if defined(NYX_MEMORY_STATS) && NYX_MEMORY_STATS printf("\nnyx_cleanup\n"); xmem(); #endif } void nyx_set_xlisp_path(const char *path) { set_xlisp_path(path); } LOCAL void nyx_susp_fetch(register nyx_susp_type susp, snd_list_type snd_list) { sample_block_type out; sample_block_values_type out_ptr; long n; int err; falloc_sample_block(out, "nyx_susp_fetch"); out_ptr = out->samples; snd_list->block = out; n = max_sample_block_len; if (susp->susp.current + n > susp->len) { n = susp->len - susp->susp.current; } err = susp->callback(out_ptr, susp->channel, susp->susp.current, n, 0, susp->userdata); if (err) { // The user canceled or some other error occurred, so we use // xlsignal() to jump back to our error handler. xlsignal(NULL, NULL); // never get here. } snd_list->block_len = (short)n; susp->susp.current += n; if (n == 0) { /* we didn't read anything, but can't return length zero, so convert snd_list to pointer to zero block */ snd_list_terminate(snd_list); } else if (n < max_sample_block_len) { /* should free susp */ snd_list_unref(snd_list->u.next); /* if something is in buffer, terminate by pointing to zero block */ snd_list->u.next = zero_snd_list; } } LOCAL void nyx_susp_free(nyx_susp_type susp) { ffree_generic(susp, sizeof(nyx_susp_node), "nyx_susp_free"); } LOCAL void nyx_susp_print_tree(nyx_susp_type susp, int n) { } void nyx_capture_output(nyx_output_callback callback, void *userdata) { nyx_output_cb = callback; nyx_output_ud = userdata; } void nyx_set_audio_params(double rate, long len) { LVAL flo; LVAL con; xlstkcheck(2); xlsave(flo); xlsave(con); /* Bind the sample rate to the "*sound-srate*" global */ flo = cvflonum(rate); setvalue(xlenter("*SOUND-SRATE*"), flo); /* Bind selection len to "len" global */ nyx_input_length = len; flo = cvflonum(len); setvalue(xlenter("LEN"), flo); /* Set the "*warp*" global based on the length of the audio */ con = cons(NULL, NULL); flo = cvflonum(len > 0 ? (double) len / rate : 1.0); con = cons(flo, con); flo = cvflonum(0); con = cons(flo, con); setvalue(xlenter("*WARP*"), con); xlpopn(2); } void nyx_set_input_audio(nyx_audio_callback callback, void *userdata, int num_channels, long len, double rate) { LVAL val; int ch; nyx_set_audio_params(rate, len); if (num_channels > 1) { val = newvector(num_channels); } xlprot1(val); for (ch = 0; ch < num_channels; ch++) { nyx_susp_type susp; sound_type snd; falloc_generic(susp, nyx_susp_node, "nyx_set_input_audio"); susp->callback = callback; susp->userdata = userdata; susp->len = len; susp->channel = ch; susp->susp.fetch = nyx_susp_fetch; susp->susp.keep_fetch = NULL; susp->susp.free = nyx_susp_free; susp->susp.mark = NULL; susp->susp.print_tree = nyx_susp_print_tree; susp->susp.name = "nyx"; susp->susp.toss_cnt = 0; susp->susp.current = 0; susp->susp.sr = rate; susp->susp.t0 = 0.0; susp->susp.log_stop_cnt = 0; snd = sound_create((snd_susp_type) susp, 0.0, rate, 1.0); if (num_channels > 1) { setelement(val, ch, cvsound(snd)); } else { val = cvsound(snd); } } setvalue(xlenter("S"), val); xlpop(); } LOCAL int nyx_is_labels(LVAL expr) { /* make sure that we have a list whose first element is a list of the form (time "label") */ LVAL label; LVAL first; LVAL second; LVAL third; if (expr == NULL) { return 0; } while (expr != NULL) { if (!consp(expr)) { return 0; } label = car(expr); if (!consp(label)) { return 0; } first = car(label); if (!(floatp(first) || fixp(first))) { return 0; } if (!consp(cdr(label))) { return 0; } second = car(cdr(label)); if (floatp(second) || fixp(second)) { if (!consp(cdr(cdr(label)))) { return 0; } third = car(cdr(cdr(label))); if (!(stringp(third))) { return 0; } } else { if (!(stringp(second))) { return 0; } } expr = cdr(expr); } return 1; } nyx_rval nyx_get_type(LVAL expr) { if (nyx_result_type != nyx_error) { return nyx_result_type; } nyx_result_type = nyx_error; if (expr == NULL) { return nyx_result_type; } switch (ntype(expr)) { case FIXNUM: nyx_result_type = nyx_int; break; case FLONUM: nyx_result_type = nyx_double; break; case STRING: nyx_result_type = nyx_string; break; case VECTOR: { /* make sure it's a vector of sounds */ int i; nyx_result_type = nyx_audio; for (i = 0; i < getsize(expr); i++) { if (!soundp(getelement(expr, i))) { nyx_result_type = nyx_error; break; } } } break; case CONS: { /* see if it's a list of time/string pairs representing a label track */ if (nyx_is_labels(expr)) { nyx_result_type = nyx_labels; } } break; case EXTERN: { if (soundp(expr)) { nyx_result_type = nyx_audio; } } break; } /* switch */ return nyx_result_type; } nyx_rval nyx_eval_expression(const char *expr_string) { LVAL expr = NULL; #if defined(NYX_MEMORY_STATS) && NYX_MEMORY_STATS printf("\nnyx_eval_expression before\n"); xmem(); #endif nyx_result = NULL; nyx_result_type = nyx_error; // Check argument if (!expr_string || !strlen(expr_string)) { return nyx_get_type(nyx_result); } nyx_expr_string = expr_string; nyx_expr_len = strlen(nyx_expr_string); nyx_expr_pos = 0; // Protect the expression from being garbage collected xlprot1(expr); // Setup a new context xlbegin(&nyx_cntxt, CF_TOPLEVEL|CF_CLEANUP|CF_BRKLEVEL|CF_ERROR, s_true); // Set the context jump destination if (setjmp(nyx_cntxt.c_jmpbuf)) { // If the script is cancelled or some other condition occurs that causes // the script to exit and return to this level, then we don't need to // restore the previous context. goto finish; } while (nyx_expr_pos < nyx_expr_len) { expr = NULL; // Read an expression if (!xlread(getvalue(s_stdin), &expr, FALSE)) { break; } #if 0 /* save the input expression (so the user can refer to it as +, ++, or +++) */ xlrdsave(expr); #endif // Evaluate the expression nyx_result = xleval(expr); } // This will unwind the xlisp context and restore internals to a point just // before we issued our xlbegin() above. This is important since the internal // xlisp stacks will contain pointers to invalid objects otherwise. // // Also note that execution will jump back up to the statement following the // setjmp() above. xljump(&nyx_cntxt, CF_TOPLEVEL, NIL); // Never reached finish: xlflush(); xlpop(); // unprotect expr setvalue(xlenter("S"), NIL); gc(); #if defined(NYX_MEMORY_STATS) && NYX_MEMORY_STATS printf("\nnyx_eval_expression after\n"); xmem(); #endif return nyx_get_type(nyx_result); } int nyx_get_audio_num_channels() { if (nyx_get_type(nyx_result) != nyx_audio) { return 0; } if (vectorp(nyx_result)) { return getsize(nyx_result); } return 1; } int nyx_get_audio(nyx_audio_callback callback, void *userdata) { float *buffer = NULL; sound_type *snds = NULL; long *totals = NULL; long *lens = NULL; sound_type snd; int result = 0; int num_channels; int ch; // Any variable whose value is set between the setjmp() and the "finish" label // and that is used after the "finish" label, must be marked volatile since // any routine outside of the current one that calls longjmp() will cause values // cached in registers to be lost. volatile int success = FALSE; if (nyx_get_type(nyx_result) != nyx_audio) { return FALSE; } #if defined(NYX_MEMORY_STATS) && NYX_MEMORY_STATS printf("\nnyx_get_audio before\n"); xmem(); #endif num_channels = nyx_get_audio_num_channels(); buffer = (sample_type *) malloc(max_sample_block_len * sizeof(sample_type)); if (buffer == NULL) { goto finish; } snds = (sound_type *) malloc(num_channels * sizeof(sound_type)); if (snds == NULL) { goto finish; } totals = (long *) malloc(num_channels * sizeof(long)); if (totals == NULL) { goto finish; } lens = (long *) malloc(num_channels * sizeof(long)); if (lens == NULL) { goto finish; } // Setup a new context xlbegin(&nyx_cntxt, CF_TOPLEVEL|CF_CLEANUP|CF_BRKLEVEL|CF_ERROR, s_true); // Set the context jump destination if (setjmp(nyx_cntxt.c_jmpbuf)) { // If the script is cancelled or some other condition occurs that causes // the script to exit and return to this level, then we don't need to // restore the previous context. goto finish; } if (nyx_input_length == 0) { LVAL val = getvalue(xlenter("LEN")); if (val != s_unbound) { if (ntype(val) == FLONUM) { nyx_input_length = (long) getflonum(val); } else if (ntype(val) == FIXNUM) { nyx_input_length = (long) getfixnum(val); } } } for (ch = 0; ch < num_channels; ch++) { if (num_channels == 1) { snd = getsound(nyx_result); } else { snd = getsound(getelement(nyx_result, ch)); } snds[ch] = snd; totals[ch] = 0; lens[ch] = nyx_input_length; } while (result == 0) { for (ch =0 ; ch < num_channels; ch++) { sample_block_type block; long cnt; int i; snd = snds[ch]; cnt = 0; block = sound_get_next(snd, &cnt); if (block == zero_block || cnt == 0) { success = TRUE; result = -1; break; } // Copy and scale the samples for (i = 0; i < cnt; i++) { buffer[i] = block->samples[i] * snd->scale; } result = callback((float *)buffer, ch, totals[ch], cnt, lens[ch] ? lens[ch] : cnt, userdata); if (result != 0) { result = -1; break; } totals[ch] += cnt; } } // This will unwind the xlisp context and restore internals to a point just // before we issued our xlbegin() above. This is important since the internal // xlisp stacks will contain pointers to invalid objects otherwise. // // Also note that execution will jump back up to the statement following the // setjmp() above. xljump(&nyx_cntxt, CF_TOPLEVEL, NIL); // Never reached finish: if (buffer) { free(buffer); } if (lens) { free(lens); } if (totals) { free(totals); } if (snds) { free(snds); } gc(); #if defined(NYX_MEMORY_STATS) && NYX_MEMORY_STATS printf("\nnyx_get_audio after\n"); xmem(); #endif return success; } int nyx_get_int() { if (nyx_get_type(nyx_result) != nyx_int) { return -1; } return getfixnum(nyx_result); } double nyx_get_double() { if (nyx_get_type(nyx_result) != nyx_double) { return -1.0; } return getflonum(nyx_result); } const char *nyx_get_string() { if (nyx_get_type(nyx_result) != nyx_string) { return NULL; } return (const char *)getstring(nyx_result); } unsigned int nyx_get_num_labels() { LVAL s; int count = 0; if (nyx_get_type(nyx_result) != nyx_labels) { return 0; } for (s = nyx_result; s; s = cdr(s)) { count++; } return count; } void nyx_get_label(unsigned int index, double *start_time, double *end_time, const char **label) { LVAL s = nyx_result; LVAL label_expr; LVAL t0_expr; LVAL t1_expr; LVAL str_expr; if (nyx_get_type(nyx_result) != nyx_labels) { return; } while (index) { index--; s = cdr(s); if (s == NULL) { // index was larger than number of labels return; } } /* We either have (t0 "label") or (t0 t1 "label") */ label_expr = car(s); t0_expr = car(label_expr); t1_expr = car(cdr(label_expr)); if (stringp(t1_expr)) { str_expr = t1_expr; t1_expr = t0_expr; } else { str_expr = car(cdr(cdr(label_expr))); } if (floatp(t0_expr)) { *start_time = getflonum(t0_expr); } else if (fixp(t0_expr)) { *start_time = (double)getfixnum(t0_expr); } if (floatp(t1_expr)) { *end_time = getflonum(t1_expr); } else if (fixp(t1_expr)) { *end_time = (double)getfixnum(t1_expr); } *label = (const char *)getstring(str_expr); } const char *nyx_get_error_str() { return NULL; } void nyx_set_os_callback(nyx_os_callback callback, void *userdata) { nyx_os_cb = callback; nyx_os_ud = userdata; } void nyx_stop() { xlflush(); xltoplevel(); } void nyx_break() { xlflush(); xlbreak("BREAK", s_unbound); } void nyx_continue() { xlflush(); xlcontinue(); } int ostgetc() { if (nyx_expr_pos < nyx_expr_len) { fflush(stdout); return (nyx_expr_string[nyx_expr_pos++]); } else if (nyx_expr_pos == nyx_expr_len) { /* Add whitespace at the end so that the parser knows that this is the end of the expression */ nyx_expr_pos++; return '\n'; } return EOF; } /* osinit - initialize */ void osinit(char *banner) { } /* osfinish - clean up before returning to the operating system */ void osfinish(void) { } /* oserror - print an error message */ void oserror(char *msg) { errputstr(msg); } long osrand(long n) { return (((int) rand()) % n); } /* cd .. open - open an ascii file */ FILE *osaopen(char *name, char *mode) { return fopen(name, mode); } /* osbopen - open a binary file */ FILE *osbopen(char *name, char *mode) { char bmode[10]; strncpy(bmode, mode, 8); strcat(bmode, "b"); return fopen(name,bmode); } /* osclose - close a file */ int osclose(FILE *fp) { return fclose(fp); } /* osagetc - get a character from an ascii file */ int osagetc(FILE *fp) { return getc(fp); } /* osaputc - put a character to an ascii file */ int osaputc(int ch, FILE *fp) { return putc(ch,fp); } /* osoutflush - flush output to a file */ void osoutflush(FILE *fp) { fflush(fp); } /* osbgetc - get a character from a binary file */ int osbgetc(FILE *fp) { return getc(fp); } /* osbputc - put a character to a binary file */ int osbputc(int ch, FILE *fp) { return putc(ch, fp); } /* ostputc - put a character to the terminal */ void ostputc(int ch) { oscheck(); /* check for control characters */ if (nyx_output_cb) { nyx_output_cb(ch, nyx_output_ud); } else { putchar((char) ch); } } /* ostoutflush - flush output buffer */ void ostoutflush() { if (!nyx_output_cb) { fflush(stdout); } } /* osflush - flush the terminal input buffer */ void osflush(void) { } /* oscheck - check for control characters during execution */ void oscheck(void) { if (nyx_os_cb) { nyx_os_cb(nyx_os_ud); } /* if they hit control-c: xflush(); xltoplevel(); return; */ } /* xsystem - execute a system command */ LVAL xsystem() { if (moreargs()) { unsigned char *cmd; cmd = (unsigned char *)getstring(xlgastring()); fprintf(stderr, "Will not execute system command: %s\n", cmd); } return s_true; } /* xsetdir -- set current directory of the process */ LVAL xsetdir() { char *dir = (char *)getstring(xlgastring()); int result; LVAL cwd = NULL; xllastarg(); result = chdir(dir); if (result) { perror("SETDIR"); } dir = getcwd(NULL, 1000); if (dir) { cwd = cvstring(dir); free(dir); } return cwd; } /* xgetkey - get a key from the keyboard */ LVAL xgetkey() { xllastarg(); return (cvfixnum((FIXTYPE)getchar())); } /* ossymbols - enter os specific symbols */ void ossymbols(void) { } /* xsetupconsole -- used to configure window in Win32 version */ LVAL xsetupconsole() { return NULL; } #if defined(WIN32) const char os_pathchar = '\\'; const char os_sepchar = ','; #else const char os_pathchar = '/'; const char os_sepchar = ':'; #endif /* control-C handling */ void ctcinit() { } /* xechoenabled -- set/clear echo_enabled flag (unix only) */ LVAL xechoenabled() { return NULL; } #if defined(WIN32) static WIN32_FIND_DATA FindFileData; static HANDLE hFind = INVALID_HANDLE_VALUE; #define OSDIR_LIST_READY 0 #define OSDIR_LIST_STARTED 1 #define OSDIR_LIST_DONE 2 static int osdir_list_status = OSDIR_LIST_READY; #define OSDIR_MAX_PATH 256 static char osdir_path[OSDIR_MAX_PATH]; // osdir_list_start -- prepare to list a directory int osdir_list_start(char *path) { if (strlen(path) >= OSDIR_MAX_PATH - 2) { xlcerror("LISTDIR path too big", "return nil", NULL); return FALSE; } strcpy(osdir_path, path); strcat(osdir_path, "/*"); // make a pattern to match all files if (osdir_list_status != OSDIR_LIST_READY) { osdir_list_finish(); // close previously interrupted listing } hFind = FindFirstFile(osdir_path, &FindFileData); // get the "." if (hFind == INVALID_HANDLE_VALUE) { return FALSE; } if (FindNextFile(hFind, &FindFileData) == 0) { return FALSE; // get the ".." } osdir_list_status = OSDIR_LIST_STARTED; return TRUE; } /* osdir_list_next -- read the next entry from a directory */ char *osdir_list_next() { if (FindNextFile(hFind, &FindFileData) == 0) { osdir_list_status = OSDIR_LIST_DONE; return NULL; } return FindFileData.cFileName; } /* osdir_list_finish -- close an open directory */ void osdir_list_finish() { if (osdir_list_status != OSDIR_LIST_READY) { FindClose(hFind); } osdir_list_status = OSDIR_LIST_READY; } #else #include #define OSDIR_LIST_READY 0 #define OSDIR_LIST_STARTED 1 #define OSDIR_LIST_DONE 2 static int osdir_list_status = OSDIR_LIST_READY; static DIR *osdir_dir; /* osdir_list_start -- open a directory listing */ int osdir_list_start(char *path) { if (osdir_list_status != OSDIR_LIST_READY) { osdir_list_finish(); /* close current listing */ } osdir_dir = opendir(path); if (!osdir_dir) { return FALSE; } osdir_list_status = OSDIR_LIST_STARTED; return TRUE; } /* osdir_list_next -- read the next entry from a directory */ char *osdir_list_next() { struct dirent *entry; if (osdir_list_status != OSDIR_LIST_STARTED) { return NULL; } entry = readdir(osdir_dir); if (!entry) { osdir_list_status = OSDIR_LIST_DONE; return NULL; } return entry->d_name; } /* osdir_list_finish -- close an open directory */ void osdir_list_finish() { if (osdir_list_status != OSDIR_LIST_READY) { closedir(osdir_dir); } osdir_list_status = OSDIR_LIST_READY; } #endif /* xget_temp_path -- get a path to create temp files */ LVAL xget_temp_path() { char *tmp; #if defined(WINDOWS) tmp = getenv("TEMP"); #else tmp = getenv("TMPDIR"); #endif if (!tmp || !*tmp) { tmp = getenv("TMP"); if (!tmp || !*tmp) { #if defined(WINDOWS) tmp = "/"; #else tmp = "/tmp/"; #endif } } return cvstring(tmp); } /* xget_user -- get a string identifying the user, for use in file names */ LVAL xget_user() { char *user = getenv("USER"); if (!user || !*user) { user = getenv("USERNAME"); if (!user || !*user) { errputstr("Warning: could not get user ID, using 'nyquist'\n"); user = "nyquist"; } } return cvstring(user); } #if defined(WINDOWS) /* get_xlisp_path -- return path to xlisp */ void get_xlisp_path(char *p, long p_max) { char *paths = getenv("XLISPPATH"); if (!paths || !*paths) { *p = 0; return; } strncpy(p, paths, p_max); p[p_max] = 0; } #endif