/* xen support procedures */ #include "mus-config.h" #include #include #include #include #include #include #include #ifdef _MSC_VER #include #include #pragma warning(disable: 4244) #endif #include "xen.h" #define S_gc_off "gc-off" #define S_gc_on "gc-on" char *xen_strdup(const char *str) { char *newstr = NULL; if ((!str) || (!(*str))) return(NULL); newstr = (char *)malloc(strlen(str) + 1); if (newstr) strcpy(newstr, str); return(newstr); } /* ------------------------------ RUBY ------------------------------ */ #if HAVE_RUBY #define HAVE_RB_PROC_NEW 1 /* As the README says, only versions of ruby 1.8 or later will work */ #if USE_SND void snd_rb_raise(Xen type, Xen info); /* XEN_ERROR */ #endif #define S_add_help "add_help" #define S_get_help "get_help" Xen rb_documentation(Xen name) { Xen_check_type((Xen_is_string(name) || Xen_is_symbol(name)), name, 1, S_get_help, "a char* or symbol"); if (Xen_is_symbol(name)) return(rb_property(XEN_SYMBOL_TO_STRING(name), Xen_documentation_symbol)); else return(rb_property(name, Xen_documentation_symbol)); } Xen rb_set_documentation(Xen name, Xen help) { Xen_check_type((Xen_is_string(name) || Xen_is_symbol(name)), name, 1, S_add_help, "a char* or symbol"); Xen_check_type(Xen_is_string(help), help, 2, S_add_help, "a char*"); if (Xen_is_symbol(name)) rb_set_property(XEN_SYMBOL_TO_STRING(name), Xen_documentation_symbol, help); else rb_set_property(name, Xen_documentation_symbol, help); return(name); } static Xen g_add_help(Xen name, Xen help) { #define H_add_help S_add_help "(name, help) add help to topic or function name (String or Symbol)" return(rb_set_documentation(name, help)); } static Xen g_get_help(Xen name) { #define H_get_help S_get_help "([name=:" S_get_help "]) \ return help associated with name (String or Symbol) or false" if (!Xen_is_bound(name)) return(C_string_to_Xen_string(H_get_help)); else return(rb_documentation(name)); } void xen_initialize(void) { #ifdef RUBY_INIT_STACK RUBY_INIT_STACK; #endif ruby_init(); ruby_init_loadpath(); ruby_script("xen"); /* necessary in ruby 1.9 (else segfault in rb_raise -- is this the rb GC bug (see snd-xen.c)?) */ Init_Hook(); } void xen_gc_mark(Xen val) { rb_gc_mark(val); } Xen xen_rb_cdr(Xen val) { if (Xen_is_cons(val)) { Xen new_list; new_list = Xen_copy_arg(val); rb_ary_delete_at(new_list, 0); return(new_list); } return(val); } Xen xen_rb_cons(Xen arg1, Xen arg2) { if (Xen_is_null(arg2)) return(rb_ary_new3(1, arg1)); if (!(Xen_is_cons(arg2))) return(rb_ary_new3(2, arg1, arg2)); return(rb_ary_unshift(arg2, arg1)); /* arg2 assumed to be array here in Ruby */ } Xen xen_rb_cons2(Xen arg1, Xen arg2, Xen arg3) { return(rb_ary_unshift(xen_rb_cons(arg2, arg3), arg1)); } Xen xen_rb_ary_new_with_initial_element(long num, Xen element) { Xen arr; int i; arr = rb_ary_new2(num); for (i = 0; i < num; i++) rb_ary_store(arr, i, element); return(arr); } Xen xen_set_assoc(Xen key, Xen val, Xen alist) { /* assoc key val in alist so later rb_ary_assoc will find val given key in alist */ /* if array?(alist) if array?(item = alist.assoc(key)) item[1] = val else alist.push([key, val]) end else [[key, val]] end */ if (Xen_is_cons(alist)) { Xen pair; pair = rb_ary_assoc(alist, key); if (Xen_is_cons(pair)) rb_ary_store(pair, 1, val); else rb_ary_push(alist, rb_assoc_new(key, val)); return(alist); } return(rb_ary_new3(1, rb_assoc_new(key, val))); } Xen xen_assoc(Xen key, Xen alist) { if (Xen_is_cons(alist)) { Xen val; val = rb_ary_assoc(alist, key); if (val != Qnil) return(rb_ary_entry(val, 1)); } return(Qfalse); } static char *scheme_to_ruby(const char *name) { /* replace any non-alphanumeric except "?" with "_". "?" -> "_p". '->" -> "2" drop "!" */ char *new_name = NULL; int len; len = strlen(name); if (len > 0) { int i, j; new_name = (char *)calloc(len + 3, sizeof(char)); /* +1 for possible _p, +1 for possible $ */ for (i = 0, j = 0; i < len; i++) { if (isalnum(name[i])) new_name[j++] = name[i]; else { if (name[i] != '!') { if ((name[i] == '-') && (name[i + 1] == '>')) { new_name[j++] = '2'; i++; } else { new_name[j++] = '_'; if (name[i] == '?') new_name[j++] = 'p'; } } } } } return(new_name); } char *xen_scheme_constant_to_ruby(const char *name) { /* upcase first char */ char *new_name; new_name = scheme_to_ruby(name); new_name[0] = toupper(new_name[0]); return(new_name); } char *xen_scheme_procedure_to_ruby(const char *name) { char *new_name = NULL; int len; len = name ? strlen(name) : 0; if (len > 0) { int i, j; new_name = (char *)calloc(len + 1, sizeof(char)); for (i = 0, j = 0; i < len; i++) { if ((isalnum(name[i])) || (name[i] == '!') || (name[i] == '?')) new_name[j++] = name[i]; else { if ((name[i] == '-') && (name[i + 1] == '>')) { new_name[j++] = '2'; i++; } else new_name[j++] = '_'; } } } return(new_name); } char *xen_scheme_global_variable_to_ruby(const char *name) { /* prepend $ */ char *new_name; new_name = scheme_to_ruby(name); if (new_name[0] == '_') new_name[0] = '$'; else { int i, len; len = strlen(new_name); for (i = len; i > 0; i--) new_name[i] = new_name[i - 1]; new_name[0] = '$'; } return(new_name); } /* looks for global variables and constants (functions too?) */ bool xen_rb_defined_p(const char *name) { char *var_name = scheme_to_ruby(name); char buf[128]; if (var_name[0] == '$') sprintf(buf, "defined? %s", var_name); else sprintf(buf, "defined? $%s", var_name); if (Xen_eval_C_string(buf) != Qnil) { free(var_name); return(true); } else { bool val; var_name[0] = toupper(var_name[0]); val = rb_const_defined(rb_cObject, rb_intern(var_name)); free(var_name); return(val); } } Xen xen_rb_gv_get(const char *name) { char *temp; Xen val; temp = xen_scheme_global_variable_to_ruby(name); val = rb_gv_get(temp); if (temp) free(temp); return(val); } Xen xen_rb_gv_set(const char *name, Xen new_val) { char *temp; Xen val; temp = xen_scheme_global_variable_to_ruby(name); val = rb_gv_set(temp, new_val); if (temp) free(temp); return(val); } Xen xen_rb_intern(const char *name) { char *temp; Xen val; temp = xen_scheme_constant_to_ruby(name); val = rb_intern(temp); if (temp) free(temp); return(val); } Xen xen_rb_make_keyword(const char *name) { char *temp; Xen val; temp = xen_scheme_procedure_to_ruby(name); val = C_string_to_Xen_symbol(temp); if (temp) free(temp); return(val); } void xen_rb_define(const char *name, Xen value) { char *temp; temp = xen_scheme_constant_to_ruby(name); rb_define_global_const(temp, value); if (temp) free(temp); } Xen xen_rb_define_class(const char *name) { char *temp; Xen val; temp = xen_scheme_constant_to_ruby(name); val = rb_define_class(temp, rb_cObject); if (temp) free(temp); return(val); } #ifndef RARRAY_PTR #define RB_ARRAY_PTR(Ary) RARRAY(Ary)->ptr #define RB_ARRAY_LEN(Ary) RARRAY(Ary)->len #else #define RB_ARRAY_PTR(Ary) RARRAY_PTR(Ary) #define RB_ARRAY_LEN(Ary) RARRAY_LEN(Ary) #endif int xen_rb_list_length(Xen obj) { if (Xen_is_vector(obj)) return((int)RB_ARRAY_LEN(obj)); if (obj == Xen_empty_list) return(0); return(-1); } Xen xen_rb_list_ref(Xen obj, int index) { if (Xen_is_vector(obj)) return(rb_ary_entry(obj, (long)index)); return(Xen_empty_list); } Xen xen_rb_list_set(Xen obj, int index, Xen value) { if (Xen_is_vector(obj)) rb_ary_store(obj, (long)index, value); return(value); } char *xen_version(void) { /* there is no macro we can depend on for the version number (its name changes unpredictably), * and ruby/version.h tries to be funny about how unreliable their semi-functional access is. * Maybe use and ruby_version here (a const char*). * No, even that doesn't work because there's no way to tell whether version.h exists. * Humph! */ char *buf; buf = (char *)calloc(128, sizeof(char)); snprintf(buf, 128, "%s", "Ruby"); return(buf); } static Xen xen_rb_report_error(Xen nada, Xen err_info) { /* backtrace info: */ /* return rb_funcall(err_info, rb_intern("backtrace"), 0); */ /* which can be an array of strings */ fprintf(stderr,"error: %s\n", Xen_object_to_C_string(err_info)); return(Xen_false); } static char *rb_prompt = NULL; static Xen xen_rb_rep(Xen ig) { Xen val; char *str; size_t size = 512; char **buffer = NULL; buffer = (char **)calloc(1, sizeof(char *)); buffer[0] = (char *)calloc(size, sizeof(char)); fprintf(stdout, "%s", rb_prompt); fgets(buffer[0], size, stdin); val = xen_rb_eval_string_with_error(buffer[0]); str = Xen_object_to_C_string(val); fprintf(stdout, "%s\n", (str) ? str : "nil"); free(buffer[0]); free(buffer); return(ig); } void xen_rb_repl_set_prompt(const char *prompt) { if (rb_prompt) free(rb_prompt); rb_prompt = xen_strdup(prompt); } static Xen xen_rb_rescue(Xen val) { if (!rb_prompt) rb_prompt = xen_strdup(">"); return(rb_rescue(Xen_procedure_cast xen_rb_rep, Xen_false, Xen_procedure_cast xen_rb_report_error, Xen_false)); } void xen_repl(int argc, char **argv) { while (true) { int status = 0; rb_protect(XEN_VALUE_ARG_PROCEDURE_CAST xen_rb_rescue, Xen_false, &status); if (status != 0) { fprintf(stderr, "%s\n", Xen_object_to_C_string(rb_gv_get("$!"))); status = 0; } } } Xen xen_rb_eval_string_with_error(const char *str) { int status = 0; Xen res; res = rb_eval_string_protect(str, &status); if (status != 0) return(xen_rb_obj_as_string(rb_gv_get("$!"))); return(res); } Xen xen_rb_load_file_with_error(Xen file) { int status = 0; rb_load_protect(file, 0, &status); if (status != 0) return(xen_rb_obj_as_string(rb_gv_get("$!"))); return(Xen_true); } Xen xen_rb_add_to_load_path(char *path) { Xen rpath, load_path; rpath = rb_str_new2(path); load_path = rb_gv_get("$:"); if (Xen_is_false(rb_ary_includes(load_path, rpath))) rb_ary_unshift(load_path, rpath); return(Xen_false); } static char *lstbuf = NULL; static char *xen_rb_list_to_s(Xen lst) { int i, len; if (lstbuf == NULL) lstbuf = (char *)calloc(512, sizeof(char)); else lstbuf[0] = '\0'; len = Xen_list_length(lst); for (i = 0; i < len; i++) { strcat(lstbuf, Xen_object_to_C_string(Xen_list_ref(lst, i))); strcat(lstbuf, " "); } return(lstbuf); } void xen_rb_raise(Xen type, Xen info) { rb_raise(rb_eStandardError, "%s: %s\n", rb_id2name(type), xen_rb_list_to_s(info)); } int xen_rb_required_args(Xen val) { int args; args = Xen_integer_to_C_int(val); if (args == -1) return(1); if (args < 0) return(abs(args + 1)); return(args); } Xen xen_rb_obj_as_string(Xen obj) { int status = 0; Xen result; result = rb_protect(XEN_VALUE_ARG_PROCEDURE_CAST rb_obj_as_string, obj, &status); if (status != 0) return(C_string_to_Xen_string("")); return(result); } #if HAVE_RB_PROC_NEW static Xen xen_rb_apply_1(Xen args) { return(rb_apply(Xen_car(args), rb_intern("call"), Xen_cadr(args))); } #else static Xen xen_rb_apply_1(Xen args) { if (Xen_is_procedure(Xen_car(args))) return(rb_apply(Xen_car(args), rb_intern("call"), Xen_cadr(args))); return(rb_apply(rb_mKernel, Xen_car(args), Xen_cadr(args))); } #endif Xen xen_rb_apply(Xen func, Xen args) { Xen val; int status = 0; val = rb_protect(XEN_VALUE_ARG_PROCEDURE_CAST xen_rb_apply_1, Xen_list_2(func, args), &status); if (status != 0) return(xen_rb_obj_as_string(rb_gv_get("$!"))); return(val); } static Xen xen_rb_funcall_0_inner(Xen args) { return(rb_funcall(args, rb_intern("call"), 0)); } Xen xen_rb_funcall_0(Xen func) { Xen val; int status = 0; val = rb_protect(XEN_VALUE_ARG_PROCEDURE_CAST xen_rb_funcall_0_inner, func, &status); if (status != 0) return(xen_rb_obj_as_string(rb_gv_get("$!"))); return(val); } Xen xen_rb_copy_list(Xen val) { if ((val == Xen_empty_list) || (!Xen_is_cons(val))) return Xen_empty_list; return rb_ary_dup(val); } Xen xen_rb_str_new2(char *arg) { return(rb_str_new2((arg) ? arg : "")); } /* class Hook */ static Xen xen_rb_cHook; static Xen hook_alloc(Xen klass) { return(Data_Wrap_Struct(klass, 0, 0, 0)); } #define Xen_is_class_hook(Arg) rb_obj_is_kind_of(Arg, xen_rb_cHook) bool xen_rb_hook_p(Xen obj) { return(Xen_is_class_hook(obj)); } bool xen_rb_hook_empty_p(Xen obj) { if (Xen_is_class_hook(obj)) return(RB_ARRAY_LEN(rb_iv_get(obj, "@procs")) == 0); return(true); } /* * @name = "$name_of_hook" * @arity = arity of procedure(s), default 0 * @procs = [["named proc1", proc1], ...] */ static Xen xen_rb_hook_initialize(int argc, Xen *argv, Xen hook) { Xen name, arity, help; rb_scan_args(argc, argv, "12", &name, &arity, &help); Xen_check_type(Xen_is_string(name) || Xen_is_symbol(name), name, 1, __func__, "a char* or symbol"); if (Xen_is_symbol(name)) name = XEN_SYMBOL_TO_STRING(name); if (arity != Qnil) { Xen_check_type(Xen_is_integer(arity), arity, 2, __func__, "an integer"); } else arity = INT2NUM(0); if (help != Qnil) { Xen_check_type(Xen_is_string(help), help, 3, __func__, "a char*"); XEN_SET_OBJECT_HELP(name, help); } rb_iv_set(hook, "@name", name); rb_iv_set(hook, "@arity", arity); rb_iv_set(hook, "@procs", rb_ary_new()); return(hook); } /* * To create a simple hook in C, see xen.h, XEN_DEFINE_SIMPLE_HOOK. * To create a global hook variables, see xen_rb_create_hook() below. */ Xen xen_rb_hook_c_new(char *name, int arity, char *help) { Xen args[3]; args[0] = C_string_to_Xen_string(name); args[1] = C_int_to_Xen_integer(arity); args[2] = C_string_to_Xen_string(help); return(xen_rb_hook_initialize(3, args, hook_alloc(xen_rb_cHook))); } /* RUBY_RELEASE_DATE < "2004-03-18" ? old : new lambda do end.arity -1 0 !!! lambda do || end.arity 0 0 lambda do |a| end.arity -1 1 !!! lambda do |*a| end.arity -1 -1 lambda do |a, b| end.arity 2 2 lambda do |a, *b| end.arity -2 -2 etc. */ #ifdef RUBY_VERSION #define XEN_RUBY_RELEASE_DATE RUBY_RELEASE_DATE #else #define XEN_RUBY_RELEASE_DATE Xen_string_to_C_string(Xen_eval_C_string("RUBY_RELEASE_DATE")) #endif #define RUBY_NEW_ARITY_DATE "2004-03-18" #define OLD_RUBY_ARITY() (strcmp(XEN_RUBY_RELEASE_DATE, RUBY_NEW_ARITY_DATE) < 0) /* #define NEW_RUBY_ARITY() (strcmp(XEN_RUBY_RELEASE_DATE, RUBY_NEW_ARITY_DATE) >= 0) */ bool xen_rb_arity_ok(int rargs, int args) { if (OLD_RUBY_ARITY()) { if ((rargs >= 2) || (rargs == 0)) return(rargs == args); else if (rargs <= -2) return(abs(rargs) <= args); else /* rargs -1 remains (no 1 exists) */ return((args == 1) || (args == 0) || (args == -1)); } else /* NEW_RUBY_ARITY */ return((rargs >= 0) ? (rargs == args) : (abs(rargs) <= args)); } static Xen xen_rb_hook_add_hook(int argc, Xen *argv, Xen hook) { Xen name, func; int args; args = Xen_integer_to_C_int(rb_iv_get(hook, "@arity")); rb_scan_args(argc, argv, "1&", &name, &func); Xen_check_type(Xen_is_string(name), name, 1, __func__, "a char*"); Xen_check_type(Xen_is_procedure(func) && xen_rb_arity_ok(Xen_integer_to_C_int(Xen_arity(func)), args), func, 2, __func__, "a procedure"); rb_ary_push(rb_iv_get(hook, "@procs"), rb_ary_new3(2, name, func)); return(hook); } #if HAVE_RB_PROC_NEW static Xen xen_proc_call(Xen args, Xen id) { return(rb_apply(rb_mKernel, (ID)id, Xen_is_cons(args) ? args : Xen_list_1(args))); } #if 0 VALUE rb_proc_new((VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE)); #endif static Xen xen_rb_proc_new(const char *name, Xen (*func)(), int arity, const char* doc) { rb_define_module_function(rb_mKernel, name, Xen_procedure_cast func, arity); if (doc) C_SET_OBJECT_HELP(name, doc); return(rb_proc_new(Xen_procedure_cast xen_proc_call, rb_intern(name))); } static Xen xen_rb_hook_arity(Xen hook); Xen xen_rb_add_hook(Xen hook, VALUE (*func)(), const char *name, const char* doc) { /* called from C, not Ruby, to add a function to a Ruby-side hook */ char *temp; temp = xen_scheme_procedure_to_ruby(name); rb_ary_push(rb_iv_get(hook, "@procs"), rb_ary_new3(2, C_string_to_Xen_string(temp), xen_rb_proc_new(temp, func, Xen_integer_to_C_int(xen_rb_hook_arity(hook)), doc))); if (temp) free(temp); return(hook); } #else Xen xen_rb_add_hook(Xen hook, VALUE (*func)(), const char *name, const char* doc) { /* called from C, not Ruby, to add a function to a Ruby-side hook * this doesn't work in g++ because it thinks the funcs are invalid: * "error: invalid conversion from 'VALUE (*)(VALUE, VALUE)' to 'VALUE (*)(...)'" (snd-file.c etc) */ Xen var, avar; char *temp; temp = xen_scheme_procedure_to_ruby(name); avar = rb_iv_get(hook, "@arity"); rb_define_module_function(rb_mKernel, temp, Xen_procedure_cast func, (Xen_is_integer(avar)) ? Xen_integer_to_C_int(avar) : 0); if (doc) C_SET_OBJECT_HELP(temp, doc); var = rb_intern(temp); rb_ary_push(rb_iv_get(hook, "@procs"), rb_ary_new3(2, C_string_to_Xen_string(temp), var)); if (temp) free(temp); return(hook); } #endif static Xen xen_rb_hook_remove_hook(Xen hook, Xen name) { Xen ary; ary = rb_iv_get(hook, "@procs"); return(rb_ary_delete(ary, rb_ary_assoc(ary, name))); } Xen xen_rb_hook_reset_hook(Xen hook) { if (Xen_is_class_hook(hook)) rb_ary_clear(rb_iv_get(hook, "@procs")); return(hook); } static Xen xen_rb_hook_names(Xen hook) { Xen ary, ret = Qnil; long len; ary = rb_iv_get(hook, "@procs"); len = RB_ARRAY_LEN(ary); if (len > 0) { long i; ret = rb_ary_new2(len); for (i = 0; i < len; i++) rb_ary_store(ret, i, Xen_vector_ref(Xen_vector_ref(ary, i), 0)); } return(ret); } Xen xen_rb_hook_to_a(Xen hook) { Xen ret = Qnil; if (Xen_is_class_hook(hook)) { Xen ary; long len; ary = rb_iv_get(hook, "@procs"); len = Xen_list_length(ary); if (len > 0) { long i; ret = rb_ary_new2(len); for (i = 0; i < len; i++) rb_ary_store(ret, i, Xen_vector_ref(Xen_vector_ref(ary, i), 1)); } } return(ret); } static Xen xen_rb_hook_run_hook(Xen hook) { if (RB_ARRAY_LEN(rb_iv_get(hook, "@procs"))) rb_ary_each(xen_rb_hook_to_a(hook)); return(hook); } /* * Calls all hook-procedures but returns only the last result; use * $var_hook.run_hook { |prc| ret << prc.call(*args) } for collecting * results. */ static Xen xen_rb_hook_call(int argc, Xen *argv, Xen hook) { Xen result = Qnil, rest, procs; rb_scan_args(argc, argv, "*", &rest); procs = xen_rb_hook_to_a(hook); if (procs != Qnil) { long i; for (i = 0; i < RB_ARRAY_LEN(procs); i++) result = xen_rb_apply(rb_ary_entry(procs, i), rest); } return(result); } static Xen xen_rb_hook_is_empty_p(Xen hook) { return(C_bool_to_Xen_boolean(RB_ARRAY_LEN(rb_iv_get(hook, "@procs")) == 0)); } static Xen xen_rb_hook_length(Xen hook) { return(C_int_to_Xen_integer(RB_ARRAY_LEN(rb_iv_get(hook, "@procs")))); } static Xen xen_rb_hook_name(Xen hook) { return(rb_iv_get(hook, "@name")); } static Xen xen_rb_hook_describe(Xen hook) { return(Xen_documentation(xen_rb_hook_name(hook))); } static Xen xen_rb_hook_arity(Xen hook) { return(rb_iv_get(hook, "@arity")); } static Xen xen_rb_hook_inspect(Xen hook) { Xen str = rb_str_new2("#"); return(str); } /* bil -- added xen_rb_create_hook for Xen_define_hook in xen.h, 13-Jun-05 -- * seems to work, but I'm guessing, especially the rb_gv_set line. * I can't use rb_define_variable here, as in the old version, because it takes a pointer * to the new variable, which in this case is a local variable => segfault. */ Xen xen_rb_create_hook(char *name, int arity, char *help) { Xen var, hook_name; char *temp; var = xen_rb_hook_c_new(temp = xen_scheme_global_variable_to_ruby(name), arity, help); hook_name = xen_rb_hook_name(var); rb_gv_set(Xen_string_to_C_string(hook_name), var); if (temp) free(temp); return(var); } static int simple_hook_number = 0; Xen xen_rb_create_simple_hook(int arity) { char *name; Xen hook; name = (char *)calloc(20, sizeof(char)); snprintf(name, 20, "simple_%02d_hook", simple_hook_number++); hook = xen_rb_create_hook(name, arity, NULL); free(name); return(hook); } /* * make_hook(name, arity = 0, help = "", hook_name = nil, &func) * * make_hook("var_hook") * == $var_hook = Hook.new("var_hook") * make_hook("var_hook", 1) * == $var_hook = Hook.new("var_hook", 1) * make_hook("var_hook", 1, "help $var_hook") * == $var_hook = Hook.new("var_hook", 1, "help $var_hook") * * make_hook("var_hook", 1, "help $var_hook", "1st proc") do |a| ... end * == $var_hook = Hook.new("var_hook", 1, "help $var_hook") * $var_hook.add_hook!("1st proc") do |a| ... end */ #ifndef RSTRING_LEN #define RB_STR_LEN(str) RSTRING(str)->len #else #define RB_STR_LEN(str) RSTRING_LEN(str) #endif static Xen xen_rb_make_hook(int argc, Xen *argv, Xen klass) { Xen hook = Xen_false, name; if (argc > 0 && argc < 4) { hook = xen_rb_hook_initialize(argc, argv, hook_alloc(xen_rb_cHook)); if (rb_block_given_p()) { argv[0] = rb_str_new2(""); xen_rb_hook_add_hook(1, argv, hook); } } else if (argc == 4 && rb_block_given_p()) { hook = xen_rb_hook_initialize(3, argv, hook_alloc(xen_rb_cHook)); argv[0] = argv[3]; xen_rb_hook_add_hook(1, argv, hook); } else Xen_error(Xen_make_error_type("wrong-number-of-args"), Xen_list_1(C_string_to_Xen_string("make_hook(name, arity=0, help=\"\", hook_name=\"\", &func)"))); name = xen_rb_hook_name(hook); if (Xen_char_to_C_char(name) != '$') { char *temp; temp = xen_scheme_global_variable_to_ruby(Xen_string_to_C_string(name)); name = C_string_to_Xen_string(temp); if (temp) free(temp); } Xen_check_type(RB_STR_LEN(name) >= 2, name, 1, __func__, "a char*, len >= 2"); return(rb_gv_set(Xen_string_to_C_string(name), hook)); } static Xen xen_rb_is_hook_p(Xen klass, Xen obj) { return(C_bool_to_Xen_boolean(Xen_is_class_hook(obj))); } /* * Hook.new(name, arity = 0, help = "") * * $my_hook = Hook.new("my_hook", 2, "info of my_hook") * $my_hook.add_hook!("1st proc") do |a, b| ... end * or make_hook("my_hook", 2, "info of my_hook", "1st proc") do |a, b| ... end * * $my_hook.add_hook!("2nd proc") do |a, b| ... end * $my_hook.inspect --> # * * ret = 0 * $my_hook.run_hook do |prc| ret = prc.call(ret, 2) end * * $my_hook.help --> info of my_hook * $my_hook.remove_hook!("1st proc") * $my_hook.inspect --> # * * $my_hook.remove_hook!("2nd proc") * $my_hook.inspect --> # */ #if (!HAVE_RB_DEFINE_ALLOC_FUNC) static Xen xen_rb_new(int argc, Xen *argv, Xen klass) { Xen hook = hook_alloc(klass); rb_obj_call_init(hook, argc, argv); return(hook); } #endif static Xen rb_object_properties = Xen_false; #define S_property "property" #define S_set_property "set_property" #define S_properties "properties" Xen rb_property(Xen obj, Xen key) { #define H_property S_property "(obj, key) \ if key exists, return obj's value (maybe nil) associated with key otherwise false" Xen props = Xen_false; if (Xen_is_false(rb_object_properties)) return(Xen_false); props = rb_hash_aref(rb_object_properties, obj); if (Xen_is_false(props) || props == Qnil) return(Xen_false); else return(rb_hash_aref(props, key)); } Xen rb_set_property(Xen obj, Xen key, Xen value) { #define H_set_property S_set_property "(obj, key, value) \ set key-value pair for obj and return value" Xen props = Xen_false; if (Xen_is_false(rb_object_properties)) { rb_object_properties = rb_hash_new(); Xen_GC_protect(rb_object_properties); } else props = rb_hash_aref(rb_object_properties, obj); if (Xen_is_false(props) || props == Qnil) props = rb_hash_new(); rb_hash_aset(props, key, value); rb_hash_aset(rb_object_properties, obj, props); return(value); } Xen rb_properties(void) { #define H_properties S_properties "() return all properties of rb_object_properties (a hash)" return(rb_object_properties); } static Xen g_gc_off(void) { #define H_gc_off "(" S_gc_off ") turns off garbage collection" rb_gc_disable(); return(Xen_false); } static Xen g_gc_on(void) { #define H_gc_on "(" S_gc_on ") turns on garbage collection" rb_gc_enable(); return(Xen_false); } Xen_wrap_1_optional_arg(g_get_help_w, g_get_help); Xen_wrap_2_args(g_add_help_w, g_add_help); Xen_wrap_3_args(g_set_property_w, rb_set_property); Xen_wrap_2_args(g_property_w, rb_property); Xen_wrap_no_args(g_properties_w, rb_properties); Xen_wrap_no_args(g_gc_off_w, g_gc_off) Xen_wrap_no_args(g_gc_on_w, g_gc_on) static bool hook_inited = false; void Init_Hook(void) { if (hook_inited) return; hook_inited = true; xen_rb_cHook = rb_define_class("Hook", rb_cObject); rb_include_module(xen_rb_cHook, rb_mEnumerable); #if HAVE_RB_DEFINE_ALLOC_FUNC rb_define_alloc_func(xen_rb_cHook, hook_alloc); #else rb_define_singleton_method(xen_rb_cHook, "new", Xen_procedure_cast xen_rb_new, -1); #endif rb_define_method(xen_rb_cHook, "initialize", Xen_procedure_cast xen_rb_hook_initialize, -1); rb_define_method(xen_rb_cHook, "add_hook!", Xen_procedure_cast xen_rb_hook_add_hook, -1); rb_define_method(xen_rb_cHook, "remove_hook!", Xen_procedure_cast xen_rb_hook_remove_hook, 1); rb_define_method(xen_rb_cHook, "reset_hook!", Xen_procedure_cast xen_rb_hook_reset_hook, 0); rb_define_alias(xen_rb_cHook, "clear", "reset_hook!"); rb_define_method(xen_rb_cHook, "to_a", Xen_procedure_cast xen_rb_hook_to_a, 0); rb_define_method(xen_rb_cHook, "run_hook", Xen_procedure_cast xen_rb_hook_run_hook, 0); rb_define_alias(xen_rb_cHook, "each", "run_hook"); rb_define_method(xen_rb_cHook, "call", Xen_procedure_cast xen_rb_hook_call, -1); rb_define_method(xen_rb_cHook, "length", Xen_procedure_cast xen_rb_hook_length, 0); rb_define_alias(xen_rb_cHook, "size", "length"); rb_define_method(xen_rb_cHook, "empty?", Xen_procedure_cast xen_rb_hook_is_empty_p, 0); rb_define_method(xen_rb_cHook, "name", Xen_procedure_cast xen_rb_hook_name, 0); rb_define_method(xen_rb_cHook, "arity", Xen_procedure_cast xen_rb_hook_arity, 0); rb_define_method(xen_rb_cHook, "describe", Xen_procedure_cast xen_rb_hook_describe, 0); rb_define_alias(xen_rb_cHook, "help", "describe"); rb_define_alias(xen_rb_cHook, "documentation", "describe"); rb_define_method(xen_rb_cHook, "inspect", Xen_procedure_cast xen_rb_hook_inspect, 0); rb_define_global_function("make_hook", Xen_procedure_cast xen_rb_make_hook, -1); rb_define_global_function("hook?", Xen_procedure_cast xen_rb_is_hook_p, 1); Xen_define_procedure(S_get_help, g_get_help_w, 0, 1, 0, H_get_help); Xen_define_procedure(S_add_help, g_add_help_w, 2, 0, 0, H_add_help); Xen_define_procedure(S_set_property, g_set_property_w, 3, 0, 0, H_set_property); Xen_define_procedure(S_property, g_property_w, 2, 0, 0, H_property); Xen_define_procedure(S_properties, g_properties_w, 0, 0, 0, H_properties); Xen_define_procedure(S_gc_off, g_gc_off_w, 0, 0, 0, H_gc_off); Xen_define_procedure(S_gc_on, g_gc_on_w, 0, 0, 0, H_gc_on); } /* end of class Hook */ #endif /* ------------------------------ FORTH ------------------------------ */ #if HAVE_FORTH char *xen_version(void) { return(fth_format("Fth: %s, Xen: " XEN_VERSION, FTH_VERSION)); } void xen_gc_mark(Xen val) { fth_gc_mark(val); } /* * A simple interpreter: * * #include * * int main(int argc, char **argv) * { * xen_repl(argc, argv); * return(0); * } * * linking requires xen.o and -lfth -lm */ void xen_repl(int argc, char **argv) { fth_repl(argc, argv); } static ficlWord *snd_exit_xt; static void fth_snd_exit(int n) { if (!snd_exit_xt) snd_exit_xt = ficlSystemLookup(FTH_FICL_SYSTEM(), (char *)"snd-exit"); ficlStackPushInteger(FTH_FICL_STACK(), n); ficlVmExecuteXT(FTH_FICL_VM(), snd_exit_xt); ficlStackDrop(FTH_FICL_STACK(), 1); } static Xen g_gc_off(void) { #define H_gc_off "(" S_gc_off ") turns off garbage collection" fth_gc_on(); return(Xen_false); } static Xen g_gc_on(void) { #define H_gc_on "(" S_gc_on ") turns on garbage collection" fth_gc_on(); return(Xen_false); } void xen_initialize(void) { fth_init(); fth_exit_hook = fth_snd_exit; Xen_define_procedure(S_gc_off, g_gc_off, 0, 0, 0, H_gc_off); Xen_define_procedure(S_gc_on, g_gc_on, 0, 0, 0, H_gc_on); } #endif /* HAVE_FORTH */ /* ------------------------------ S7 ------------------------------ */ #if HAVE_SCHEME #include "s7.h" #if ENABLE_WEBSERVER #include "s7webserver/s7webserver.h" #endif s7_scheme *s7; Xen xen_false, xen_true, xen_nil, xen_undefined, xen_zero; char *xen_version(void) { char *buf; buf = (char *)calloc(64, sizeof(char)); #if HAVE_SNPRINTF snprintf(buf, 64, "s7: %s (%s), Xen: %s", S7_VERSION, S7_DATE, XEN_VERSION); #else sprintf(buf, "s7: %s (%s), Xen: %s", S7_VERSION, S7_DATE, XEN_VERSION); #endif return(buf); } static char *xen_s7_repl_prompt = NULL; void xen_s7_set_repl_prompt(const char *new_prompt) { if (xen_s7_repl_prompt) free(xen_s7_repl_prompt); xen_s7_repl_prompt = xen_strdup(new_prompt); } #if USE_SND char *stdin_check_for_full_expression(const char *newstr); void stdin_free_str(void); #endif void xen_repl(int argc, char **argv) { int size = 512; bool expr_ok = true; char *buffer = NULL; buffer = (char *)calloc(size, sizeof(char)); while (true) { if (expr_ok) { fprintf(stdout, "\n%s", xen_s7_repl_prompt); expr_ok = false; /* don't get into an infinite loop if running in the background! */ } if (fgets(buffer, size, stdin) != NULL) { /* also, it's possible to get a string of spaces or nulls (? -- not sure what is coming in) if stdin is /dev/null */ /* then if (as in condor) stdout is being saved in a file, we get in an infinite loop storing "snd>" until the disk fills up */ int i, len; expr_ok = false; len = strlen(buffer); for (i = 0; i < len; i++) { if (buffer[i] == 0) break; if (!isspace((int)buffer[i])) { expr_ok = true; break; } } if (expr_ok) { char *str, *temp; #if USE_SND str = stdin_check_for_full_expression(buffer); /* "str" here is actually stdin_str, so we need to clear it explicitly */ if (!str) {expr_ok = false; continue;} len = strlen(str) + 16; temp = (char *)malloc(len * sizeof(char)); snprintf(temp, len, "(write %s)", str); Xen_eval_C_string(temp); free(temp); stdin_free_str(); #else temp = (char *)malloc(len + 16); snprintf(temp, len + 16, "(write %s)", buffer); /* use write, not display so that strings are in double quotes */ Xen_eval_C_string(temp); free(temp); #endif } } } free(buffer); } void xen_gc_mark(Xen val) { s7_mark_object(val); } Xen xen_set_assoc(s7_scheme *sc, s7_pointer key, s7_pointer val, s7_pointer alist) { /* fixup alist, return it (caller has to make sure it is reflected in its object) */ /* (let ((old-val (assoc key alist))) (if old-val (progn (set-cdr! old-val new-val) alist) (cons (cons key new-val) alist))) */ Xen old_val; old_val = s7_assoc(sc, key, alist); /* returns #f if nothing found */ if (old_val == s7_f(sc)) return(s7_cons(sc, s7_cons(sc, key, val), alist)); s7_set_cdr(old_val, val); return(alist); } Xen xen_assoc(s7_scheme *sc, Xen key, Xen alist) { Xen val; val = s7_assoc(sc, key, alist); if (val != s7_f(sc)) return(s7_cdr(val)); return(s7_f(sc)); } /* add various file functions that everyone else implements */ #ifndef _MSC_VER #include #include #endif #include #include static Xen g_getpid(void) { #define H_getpid "(getpid) returns the current job's process id" return(C_int_to_Xen_integer((int)getpid())); } #if (!WITH_SYSTEM_EXTRAS) static bool file_probe(const char *arg) { #ifndef _MSC_VER return(access(arg, F_OK) == 0); #else int fd; #ifdef O_NONBLOCK fd = open(arg, O_RDONLY, O_NONBLOCK); #else fd = open(arg, O_RDONLY, 0); #endif if (fd == -1) return(false); close(fd); return(true); #endif } static Xen g_file_exists_p(Xen name) { #define H_file_exists_p "(file-exists? filename): #t if the file exists" Xen_check_type(Xen_is_string(name), name, 1, "file-exists?", "a string"); return(C_bool_to_Xen_boolean(file_probe(Xen_string_to_C_string(name)))); } static bool is_directory(const char *filename) { #if (defined(_MSC_VER) || __CYGWIN__) return(false); #else #ifdef S_ISDIR struct stat statbuf; return((stat(filename, &statbuf) >= 0) && (S_ISDIR(statbuf.st_mode))); return(false); #endif #endif } static Xen g_is_directory(Xen name) { #define H_is_directory "(directory? filename): #t if filename names a directory" Xen_check_type(Xen_is_string(name), name, 1, "directory?", "a string"); return(C_bool_to_Xen_boolean(is_directory(Xen_string_to_C_string(name)))); /* snd-file.c l 84 */ } static Xen g_delete_file(Xen name) { #define H_delete_file "(delete-file filename): deletes the file" Xen_check_type(Xen_is_string(name), name, 1, "delete-file", "a string"); return(C_bool_to_Xen_boolean(unlink(Xen_string_to_C_string(name)))); } static Xen g_system(Xen command) { #define H_system "(system command): execute command" Xen_check_type(Xen_is_string(command), command, 1, "system", "a string"); return(C_int_to_Xen_integer(system(Xen_string_to_C_string(command)))); } static Xen g_s7_getenv(Xen var) /* "g_getenv" is in use in glib! */ { #define H_getenv "(getenv var): return value of environment variable var" Xen_check_type(Xen_is_string(var), var, 1, "getenv", "a string"); return(C_string_to_Xen_string(getenv(Xen_string_to_C_string(var)))); } #endif #ifdef _MSC_VER #include #endif static Xen g_getcwd(void) { #define H_getcwd "(getcwd) returns the name of the current working directory" char *buf; Xen result = Xen_false; buf = (char *)calloc(1024, sizeof(char)); #ifdef _MSC_VER if (_getcwd(buf, 1024) != NULL) #else if (getcwd(buf, 1024) != NULL) #endif result = C_string_to_Xen_string(buf); free(buf); return(result); } static Xen g_strftime(Xen format, Xen tm) { #define H_strftime "(strftime format time) returns a string describing the time: (strftime \"%d-%b %H:%M %Z\" (localtime (current-time)))" char *buf; Xen result; const struct tm *p; Xen_check_type(Xen_is_string(format), format, 1, "strftime", "a string"); Xen_check_type(Xen_is_wrapped_c_pointer(tm), tm, 2, "strftime", "a localtime struct"); p = (const struct tm *)Xen_unwrap_C_pointer(tm); Xen_check_type(p != NULL, tm, 2, "strftime", "a localtime struct"); buf = (char *)calloc(1024, sizeof(char)); strftime(buf, 1024, Xen_string_to_C_string(format), p); result = C_string_to_Xen_string(buf); free(buf); return(result); } /* (format #f ";~A~%" (strftime "%d-%b %H:%M %Z" (localtime (current-time)))) */ /* these two need to be compatible with g_file_write_date in snd-file.c */ static Xen g_localtime(Xen tm) { #define H_localtime "(localtime tm) breaks up tm into something suitable for strftime" time_t rtime; rtime = (time_t)Xen_ulong_to_C_ulong(tm); return(Xen_wrap_C_pointer(localtime((time_t *)(&rtime)))); } static Xen g_current_time(void) { time_t curtime; #define H_current_time "(current-time) returns the current time (for localtime and strftime)" curtime = time(NULL); return(C_ulong_to_Xen_ulong(curtime)); } static Xen g_tmpnam(void) { #define H_tmpnam "(tmpnam) returns a new (hopefully unused) temporary file name" #define BUFFER_SIZE 512 static int file_ctr = 0; char *str, *tmpdir = NULL; Xen result; str = (char *)calloc(BUFFER_SIZE, sizeof(char)); tmpdir = xen_strdup(getenv("TMPDIR")); #ifdef P_tmpdir if (tmpdir == NULL) tmpdir = xen_strdup(P_tmpdir); /* /usr/include/stdio.h */ if (tmpdir) { int len; len = strlen(tmpdir); if (len > 0) { if (tmpdir[len - 1] == '/') tmpdir[len - 1] = 0; } else { free(tmpdir); tmpdir = xen_strdup("."); } } #else if (tmpdir == NULL) tmpdir = xen_strdup("/tmp"); #endif snprintf(str, BUFFER_SIZE, "%s/xen_%d_%d", tmpdir, (int)getpid(), file_ctr++); if (tmpdir) free(tmpdir); result = C_string_to_Xen_string(str); free(str); return(result); } static Xen g_ftell(Xen fd) { return(C_int_to_Xen_integer(lseek(Xen_integer_to_C_int(fd), 0, SEEK_CUR))); } static Xen g_gc_off(void) { #define H_gc_off "(" S_gc_off ") turns off garbage collection" s7_gc_on(s7, false); return(Xen_false); } static Xen g_gc_on(void) { #define H_gc_on "(" S_gc_on ") turns on garbage collection" s7_gc_on(s7, true); return(Xen_false); } Xen_wrap_no_args(g_getpid_w, g_getpid) #if (!WITH_SYSTEM_EXTRAS) Xen_wrap_1_arg(g_file_exists_p_w, g_file_exists_p) Xen_wrap_1_arg(g_is_directory_w, g_is_directory) Xen_wrap_1_arg(g_delete_file_w, g_delete_file) Xen_wrap_1_arg(g_s7_getenv_w, g_s7_getenv) Xen_wrap_1_arg(g_system_w, g_system) #endif Xen_wrap_no_args(g_getcwd_w, g_getcwd) Xen_wrap_2_args(g_strftime_w, g_strftime) Xen_wrap_1_arg(g_localtime_w, g_localtime) Xen_wrap_no_args(g_current_time_w, g_current_time) Xen_wrap_no_args(g_tmpnam_w, g_tmpnam) Xen_wrap_1_arg(g_ftell_w, g_ftell) Xen_wrap_no_args(g_gc_off_w, g_gc_off) Xen_wrap_no_args(g_gc_on_w, g_gc_on) #if ENABLE_WEBSERVER #if USE_MOTIF #include "snd.h" static idle_func_t called_periodically(any_pointer_t pet) { s7webserver_call_very_often(); return(BACKGROUND_CONTINUE); } #endif #endif s7_scheme *s7_xen_initialize(s7_scheme *sc) { s7_pointer i, b, p, s; xen_s7_repl_prompt = xen_strdup("> "); if (!sc) { s7 = s7_init(); if (!s7) { fprintf(stderr, "Can't initialize s7!\n"); return(NULL); } #if ENABLE_WEBSERVER { s7webserver_t *s7webserver; s7webserver = s7webserver_create(s7, 6080, true); if (!s7webserver) fprintf(stderr, "Unable to start web server. Port 6080 may be in use\n"); else fprintf(stdout, "Started s7 webserver at port %d\n", s7webserver_get_portnumber(s7webserver)); #if USE_MOTIF BACKGROUND_ADD(called_periodically, NULL); #endif } #endif } else s7 = sc; i = s7_make_symbol(s7, "integer?"); b = s7_make_symbol(s7, "boolean?"); p = s7_make_symbol(s7, "pair?"); s = s7_make_symbol(s7, "string?"); xen_false = s7_f(s7); xen_true = s7_t(s7); xen_nil = s7_nil(s7); xen_undefined = s7_undefined(s7); xen_zero = s7_make_integer(s7, 0); s7_gc_protect(s7, xen_zero); Xen_define_typed_procedure("getpid", g_getpid_w, 0, 0, 0, H_getpid, s7_make_signature(s7, 1, i)); #if (!WITH_SYSTEM_EXTRAS) Xen_define_typed_procedure("file-exists?", g_file_exists_p_w, 1, 0, 0, H_file_exists_p, s7_make_signature(s7, 2, b, s)); Xen_define_typed_procedure("directory?", g_is_directory_w, 1, 0, 0, H_is_directory, s7_make_signature(s7, 2, b, s)); Xen_define_typed_procedure("delete-file", g_delete_file_w, 1, 0, 0, H_delete_file, s7_make_signature(s7, 2, b, s)); Xen_define_typed_procedure("getenv", g_s7_getenv_w, 1, 0, 0, H_getenv, s7_make_signature(s7, 2, s, s)); Xen_define_typed_procedure("system", g_system_w, 1, 0, 0, H_system, s7_make_signature(s7, 2, i, s)); #endif Xen_define_typed_procedure("getcwd", g_getcwd_w, 0, 0, 0, H_getcwd, s7_make_signature(s7, 1, s)); Xen_define_typed_procedure("strftime", g_strftime_w, 2, 0, 0, H_strftime, s7_make_signature(s7, 3, s, s, p)); Xen_define_typed_procedure("tmpnam", g_tmpnam_w, 0, 0, 0, H_tmpnam, s7_make_signature(s7, 1, s)); Xen_define_typed_procedure("localtime", g_localtime_w, 1, 0, 0, H_localtime, s7_make_signature(s7, 2, p, i)); Xen_define_typed_procedure("current-time", g_current_time_w, 0, 0, 0, H_current_time, s7_make_signature(s7, 1, i)); Xen_define_typed_procedure("ftell", g_ftell_w, 1, 0, 0, "(ftell fd): lseek", s7_make_signature(s7, 2, i, i)); Xen_define_typed_procedure(S_gc_off, g_gc_off_w, 0, 0, 0, H_gc_off, s7_make_signature(s7, 1, b)); Xen_define_typed_procedure(S_gc_on, g_gc_on_w, 0, 0, 0, H_gc_on, s7_make_signature(s7, 1, b)); Xen_eval_C_string("(define (hook-push hook func) \n\ \"(hook-push hook func) adds func to hook's function list\" \n\ (if (not (member func (hook-functions hook) eq?)) (set! (hook-functions hook) (cons func (hook-functions hook)))))"); Xen_eval_C_string("(define (hook-append hook func) \n\ \"(hook-append hook func) adds func to the end of hook's function list\" \n\ (set! (hook-functions hook) (append (hook-functions hook) (list func))))"); Xen_eval_C_string("(define (hook-clear hook) (set! (hook-functions hook) ()))"); Xen_eval_C_string("(define (hook-remove hook func) \n\ (set! (hook-functions hook)\n\ (let loop ((l (hook-functions hook))\n\ (result ()))\n\ (cond ((null? l) (reverse! result))\n\ ((eq? func (car l)) (loop (cdr l) result))\n\ (else (loop (cdr l) (cons (car l) result)))))))"); #if (!DISABLE_DEPRECATED) Xen_eval_C_string("(define load-from-path load)"); Xen_eval_C_string("(define (1+ x) \"add 1 to arg\" (+ x 1))"); Xen_eval_C_string("(define (1- x) \"subtract 1 from arg\" (- x 1))"); #endif Xen_eval_C_string("(define-macro (while whether . body) `(do () ((not ,whether)) ,@body))"); Xen_eval_C_string("(define (identity x) \"return arg\" x)"); return(s7); } void xen_initialize(void) { s7_xen_initialize(NULL); } #endif /* ------------------------------ NONE OF THE ABOVE ------------------------------ */ #if (!HAVE_EXTENSION_LANGUAGE) char *xen_version(void) { char *buf; buf = (char *)calloc(64, sizeof(char)); #if HAVE_SNPRINTF snprintf(buf, 64, "no extension language"); #else sprintf(buf, "no extension language"); #endif return(buf); } void xen_repl(int argc, char **argv) { } void xen_initialize(void) { } void xen_gc_mark(Xen val) { } void xen_no_ext_lang_check_args(const char *name, int args, int req_args, int opt_args, int rst_args) { if (args > 0) /* nargify -- all are required */ { if (req_args != args) fprintf(stderr, "%s: %d required args, but req: %d (opt: %d, rst: %d)\n", name, args, req_args, opt_args, rst_args); if (opt_args != 0) fprintf(stderr, "%s: all args required, but opt: %d (rst: %d)\n", name, opt_args, rst_args); if (rst_args != 0) fprintf(stderr, "%s: all args required, but rst: %d\n", name, rst_args); } else { if (args != -100) /* vargify -- any ok */ { args = -args; if (rst_args == 0) { if (req_args + opt_args != args) fprintf(stderr, "%s: total args: %d, but req: %d and opt: %d\n", name, args, req_args, opt_args); } else { if (req_args + opt_args > args) fprintf(stderr, "%s: has :rest, but req: %d and opt: %d , whereas total: %d\n", name, req_args, opt_args, args); } } } } #endif