Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

1818 lines
44KB

  1. /* xen support procedures */
  2. #include "mus-config.h"
  3. #include <ctype.h>
  4. #include <string.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <sys/types.h>
  8. #include <math.h>
  9. #include <time.h>
  10. #ifdef _MSC_VER
  11. #include <io.h>
  12. #include <process.h>
  13. #pragma warning(disable: 4244)
  14. #endif
  15. #include "xen.h"
  16. #define S_gc_off "gc-off"
  17. #define S_gc_on "gc-on"
  18. char *xen_strdup(const char *str)
  19. {
  20. char *newstr = NULL;
  21. if ((!str) || (!(*str))) return(NULL);
  22. newstr = (char *)malloc(strlen(str) + 1);
  23. if (newstr) strcpy(newstr, str);
  24. return(newstr);
  25. }
  26. /* ------------------------------ RUBY ------------------------------ */
  27. #if HAVE_RUBY
  28. #define HAVE_RB_PROC_NEW 1
  29. /* As the README says, only versions of ruby 1.8 or later will work */
  30. #if USE_SND
  31. void snd_rb_raise(Xen type, Xen info); /* XEN_ERROR */
  32. #endif
  33. #define S_add_help "add_help"
  34. #define S_get_help "get_help"
  35. Xen rb_documentation(Xen name)
  36. {
  37. Xen_check_type((Xen_is_string(name) || Xen_is_symbol(name)), name, 1, S_get_help, "a char* or symbol");
  38. if (Xen_is_symbol(name))
  39. return(rb_property(XEN_SYMBOL_TO_STRING(name), Xen_documentation_symbol));
  40. else
  41. return(rb_property(name, Xen_documentation_symbol));
  42. }
  43. Xen rb_set_documentation(Xen name, Xen help)
  44. {
  45. Xen_check_type((Xen_is_string(name) || Xen_is_symbol(name)), name, 1, S_add_help, "a char* or symbol");
  46. Xen_check_type(Xen_is_string(help), help, 2, S_add_help, "a char*");
  47. if (Xen_is_symbol(name))
  48. rb_set_property(XEN_SYMBOL_TO_STRING(name), Xen_documentation_symbol, help);
  49. else
  50. rb_set_property(name, Xen_documentation_symbol, help);
  51. return(name);
  52. }
  53. static Xen g_add_help(Xen name, Xen help)
  54. {
  55. #define H_add_help S_add_help "(name, help) add help to topic or function name (String or Symbol)"
  56. return(rb_set_documentation(name, help));
  57. }
  58. static Xen g_get_help(Xen name)
  59. {
  60. #define H_get_help S_get_help "([name=:" S_get_help "]) \
  61. return help associated with name (String or Symbol) or false"
  62. if (!Xen_is_bound(name))
  63. return(C_string_to_Xen_string(H_get_help));
  64. else
  65. return(rb_documentation(name));
  66. }
  67. void xen_initialize(void)
  68. {
  69. #ifdef RUBY_INIT_STACK
  70. RUBY_INIT_STACK;
  71. #endif
  72. ruby_init();
  73. ruby_init_loadpath();
  74. ruby_script("xen"); /* necessary in ruby 1.9 (else segfault in rb_raise -- is this the rb GC bug (see snd-xen.c)?) */
  75. Init_Hook();
  76. }
  77. void xen_gc_mark(Xen val)
  78. {
  79. rb_gc_mark(val);
  80. }
  81. Xen xen_rb_cdr(Xen val)
  82. {
  83. if (Xen_is_cons(val))
  84. {
  85. Xen new_list;
  86. new_list = Xen_copy_arg(val);
  87. rb_ary_delete_at(new_list, 0);
  88. return(new_list);
  89. }
  90. return(val);
  91. }
  92. Xen xen_rb_cons(Xen arg1, Xen arg2)
  93. {
  94. if (Xen_is_null(arg2))
  95. return(rb_ary_new3(1, arg1));
  96. if (!(Xen_is_cons(arg2)))
  97. return(rb_ary_new3(2, arg1, arg2));
  98. return(rb_ary_unshift(arg2, arg1)); /* arg2 assumed to be array here in Ruby */
  99. }
  100. Xen xen_rb_cons2(Xen arg1, Xen arg2, Xen arg3)
  101. {
  102. return(rb_ary_unshift(xen_rb_cons(arg2, arg3), arg1));
  103. }
  104. Xen xen_rb_ary_new_with_initial_element(long num, Xen element)
  105. {
  106. Xen arr;
  107. int i;
  108. arr = rb_ary_new2(num);
  109. for (i = 0; i < num; i++)
  110. rb_ary_store(arr, i, element);
  111. return(arr);
  112. }
  113. Xen xen_set_assoc(Xen key, Xen val, Xen alist)
  114. {
  115. /* assoc key val in alist so later rb_ary_assoc will find val given key in alist */
  116. /*
  117. if array?(alist)
  118. if array?(item = alist.assoc(key))
  119. item[1] = val
  120. else
  121. alist.push([key, val])
  122. end
  123. else
  124. [[key, val]]
  125. end
  126. */
  127. if (Xen_is_cons(alist))
  128. {
  129. Xen pair;
  130. pair = rb_ary_assoc(alist, key);
  131. if (Xen_is_cons(pair))
  132. rb_ary_store(pair, 1, val);
  133. else rb_ary_push(alist, rb_assoc_new(key, val));
  134. return(alist);
  135. }
  136. return(rb_ary_new3(1, rb_assoc_new(key, val)));
  137. }
  138. Xen xen_assoc(Xen key, Xen alist)
  139. {
  140. if (Xen_is_cons(alist))
  141. {
  142. Xen val;
  143. val = rb_ary_assoc(alist, key);
  144. if (val != Qnil)
  145. return(rb_ary_entry(val, 1));
  146. }
  147. return(Qfalse);
  148. }
  149. static char *scheme_to_ruby(const char *name)
  150. {
  151. /* replace any non-alphanumeric except "?" with "_". "?" -> "_p". '->" -> "2" drop "!" */
  152. char *new_name = NULL;
  153. int len;
  154. len = strlen(name);
  155. if (len > 0)
  156. {
  157. int i, j;
  158. new_name = (char *)calloc(len + 3, sizeof(char)); /* +1 for possible _p, +1 for possible $ */
  159. for (i = 0, j = 0; i < len; i++)
  160. {
  161. if (isalnum(name[i]))
  162. new_name[j++] = name[i];
  163. else
  164. {
  165. if (name[i] != '!')
  166. {
  167. if ((name[i] == '-') &&
  168. (name[i + 1] == '>'))
  169. {
  170. new_name[j++] = '2';
  171. i++;
  172. }
  173. else
  174. {
  175. new_name[j++] = '_';
  176. if (name[i] == '?')
  177. new_name[j++] = 'p';
  178. }
  179. }
  180. }
  181. }
  182. }
  183. return(new_name);
  184. }
  185. char *xen_scheme_constant_to_ruby(const char *name)
  186. {
  187. /* upcase first char */
  188. char *new_name;
  189. new_name = scheme_to_ruby(name);
  190. new_name[0] = toupper(new_name[0]);
  191. return(new_name);
  192. }
  193. char *xen_scheme_procedure_to_ruby(const char *name)
  194. {
  195. char *new_name = NULL;
  196. int len;
  197. len = name ? strlen(name) : 0;
  198. if (len > 0)
  199. {
  200. int i, j;
  201. new_name = (char *)calloc(len + 1, sizeof(char));
  202. for (i = 0, j = 0; i < len; i++)
  203. {
  204. if ((isalnum(name[i])) || (name[i] == '!') || (name[i] == '?'))
  205. new_name[j++] = name[i];
  206. else
  207. {
  208. if ((name[i] == '-') &&
  209. (name[i + 1] == '>'))
  210. {
  211. new_name[j++] = '2';
  212. i++;
  213. }
  214. else new_name[j++] = '_';
  215. }
  216. }
  217. }
  218. return(new_name);
  219. }
  220. char *xen_scheme_global_variable_to_ruby(const char *name)
  221. {
  222. /* prepend $ */
  223. char *new_name;
  224. new_name = scheme_to_ruby(name);
  225. if (new_name[0] == '_')
  226. new_name[0] = '$';
  227. else
  228. {
  229. int i, len;
  230. len = strlen(new_name);
  231. for (i = len; i > 0; i--)
  232. new_name[i] = new_name[i - 1];
  233. new_name[0] = '$';
  234. }
  235. return(new_name);
  236. }
  237. /* looks for global variables and constants (functions too?) */
  238. bool xen_rb_defined_p(const char *name)
  239. {
  240. char *var_name = scheme_to_ruby(name);
  241. char buf[128];
  242. if (var_name[0] == '$')
  243. sprintf(buf, "defined? %s", var_name);
  244. else sprintf(buf, "defined? $%s", var_name);
  245. if (Xen_eval_C_string(buf) != Qnil)
  246. {
  247. free(var_name);
  248. return(true);
  249. }
  250. else
  251. {
  252. bool val;
  253. var_name[0] = toupper(var_name[0]);
  254. val = rb_const_defined(rb_cObject, rb_intern(var_name));
  255. free(var_name);
  256. return(val);
  257. }
  258. }
  259. Xen xen_rb_gv_get(const char *name)
  260. {
  261. char *temp;
  262. Xen val;
  263. temp = xen_scheme_global_variable_to_ruby(name);
  264. val = rb_gv_get(temp);
  265. if (temp) free(temp);
  266. return(val);
  267. }
  268. Xen xen_rb_gv_set(const char *name, Xen new_val)
  269. {
  270. char *temp;
  271. Xen val;
  272. temp = xen_scheme_global_variable_to_ruby(name);
  273. val = rb_gv_set(temp, new_val);
  274. if (temp) free(temp);
  275. return(val);
  276. }
  277. Xen xen_rb_intern(const char *name)
  278. {
  279. char *temp;
  280. Xen val;
  281. temp = xen_scheme_constant_to_ruby(name);
  282. val = rb_intern(temp);
  283. if (temp) free(temp);
  284. return(val);
  285. }
  286. Xen xen_rb_make_keyword(const char *name)
  287. {
  288. char *temp;
  289. Xen val;
  290. temp = xen_scheme_procedure_to_ruby(name);
  291. val = C_string_to_Xen_symbol(temp);
  292. if (temp) free(temp);
  293. return(val);
  294. }
  295. void xen_rb_define(const char *name, Xen value)
  296. {
  297. char *temp;
  298. temp = xen_scheme_constant_to_ruby(name);
  299. rb_define_global_const(temp, value);
  300. if (temp) free(temp);
  301. }
  302. Xen xen_rb_define_class(const char *name)
  303. {
  304. char *temp;
  305. Xen val;
  306. temp = xen_scheme_constant_to_ruby(name);
  307. val = rb_define_class(temp, rb_cObject);
  308. if (temp) free(temp);
  309. return(val);
  310. }
  311. #ifndef RARRAY_PTR
  312. #define RB_ARRAY_PTR(Ary) RARRAY(Ary)->ptr
  313. #define RB_ARRAY_LEN(Ary) RARRAY(Ary)->len
  314. #else
  315. #define RB_ARRAY_PTR(Ary) RARRAY_PTR(Ary)
  316. #define RB_ARRAY_LEN(Ary) RARRAY_LEN(Ary)
  317. #endif
  318. int xen_rb_list_length(Xen obj)
  319. {
  320. if (Xen_is_vector(obj))
  321. return((int)RB_ARRAY_LEN(obj));
  322. if (obj == Xen_empty_list)
  323. return(0);
  324. return(-1);
  325. }
  326. Xen xen_rb_list_ref(Xen obj, int index)
  327. {
  328. if (Xen_is_vector(obj))
  329. return(rb_ary_entry(obj, (long)index));
  330. return(Xen_empty_list);
  331. }
  332. Xen xen_rb_list_set(Xen obj, int index, Xen value)
  333. {
  334. if (Xen_is_vector(obj))
  335. rb_ary_store(obj, (long)index, value);
  336. return(value);
  337. }
  338. char *xen_version(void)
  339. {
  340. /* there is no macro we can depend on for the version number (its name changes unpredictably),
  341. * and ruby/version.h tries to be funny about how unreliable their semi-functional access is.
  342. * Maybe use <ruby/version.h> and ruby_version here (a const char*).
  343. * No, even that doesn't work because there's no way to tell whether version.h exists.
  344. * Humph!
  345. */
  346. char *buf;
  347. buf = (char *)calloc(128, sizeof(char));
  348. snprintf(buf, 128, "%s", "Ruby");
  349. return(buf);
  350. }
  351. static Xen xen_rb_report_error(Xen nada, Xen err_info)
  352. {
  353. /* backtrace info: */
  354. /* return rb_funcall(err_info, rb_intern("backtrace"), 0); */
  355. /* which can be an array of strings */
  356. fprintf(stderr,"error: %s\n", Xen_object_to_C_string(err_info));
  357. return(Xen_false);
  358. }
  359. static char *rb_prompt = NULL;
  360. static Xen xen_rb_rep(Xen ig)
  361. {
  362. Xen val;
  363. char *str;
  364. size_t size = 512;
  365. char **buffer = NULL;
  366. buffer = (char **)calloc(1, sizeof(char *));
  367. buffer[0] = (char *)calloc(size, sizeof(char));
  368. fprintf(stdout, "%s", rb_prompt);
  369. fgets(buffer[0], size, stdin);
  370. val = xen_rb_eval_string_with_error(buffer[0]);
  371. str = Xen_object_to_C_string(val);
  372. fprintf(stdout, "%s\n", (str) ? str : "nil");
  373. free(buffer[0]);
  374. free(buffer);
  375. return(ig);
  376. }
  377. void xen_rb_repl_set_prompt(const char *prompt)
  378. {
  379. if (rb_prompt) free(rb_prompt);
  380. rb_prompt = xen_strdup(prompt);
  381. }
  382. static Xen xen_rb_rescue(Xen val)
  383. {
  384. if (!rb_prompt) rb_prompt = xen_strdup(">");
  385. return(rb_rescue(Xen_procedure_cast xen_rb_rep,
  386. Xen_false,
  387. Xen_procedure_cast xen_rb_report_error,
  388. Xen_false));
  389. }
  390. void xen_repl(int argc, char **argv)
  391. {
  392. while (true)
  393. {
  394. int status = 0;
  395. rb_protect(XEN_VALUE_ARG_PROCEDURE_CAST xen_rb_rescue,
  396. Xen_false,
  397. &status);
  398. if (status != 0)
  399. {
  400. fprintf(stderr, "%s\n", Xen_object_to_C_string(rb_gv_get("$!")));
  401. status = 0;
  402. }
  403. }
  404. }
  405. Xen xen_rb_eval_string_with_error(const char *str)
  406. {
  407. int status = 0;
  408. Xen res;
  409. res = rb_eval_string_protect(str, &status);
  410. if (status != 0)
  411. return(xen_rb_obj_as_string(rb_gv_get("$!")));
  412. return(res);
  413. }
  414. Xen xen_rb_load_file_with_error(Xen file)
  415. {
  416. int status = 0;
  417. rb_load_protect(file, 0, &status);
  418. if (status != 0)
  419. return(xen_rb_obj_as_string(rb_gv_get("$!")));
  420. return(Xen_true);
  421. }
  422. Xen xen_rb_add_to_load_path(char *path)
  423. {
  424. Xen rpath, load_path;
  425. rpath = rb_str_new2(path);
  426. load_path = rb_gv_get("$:");
  427. if (Xen_is_false(rb_ary_includes(load_path, rpath)))
  428. rb_ary_unshift(load_path, rpath);
  429. return(Xen_false);
  430. }
  431. static char *lstbuf = NULL;
  432. static char *xen_rb_list_to_s(Xen lst)
  433. {
  434. int i, len;
  435. if (lstbuf == NULL)
  436. lstbuf = (char *)calloc(512, sizeof(char));
  437. else lstbuf[0] = '\0';
  438. len = Xen_list_length(lst);
  439. for (i = 0; i < len; i++)
  440. {
  441. strcat(lstbuf, Xen_object_to_C_string(Xen_list_ref(lst, i)));
  442. strcat(lstbuf, " ");
  443. }
  444. return(lstbuf);
  445. }
  446. void xen_rb_raise(Xen type, Xen info)
  447. {
  448. rb_raise(rb_eStandardError, "%s: %s\n",
  449. rb_id2name(type),
  450. xen_rb_list_to_s(info));
  451. }
  452. int xen_rb_required_args(Xen val)
  453. {
  454. int args;
  455. args = Xen_integer_to_C_int(val);
  456. if (args == -1) return(1);
  457. if (args < 0) return(abs(args + 1));
  458. return(args);
  459. }
  460. Xen xen_rb_obj_as_string(Xen obj)
  461. {
  462. int status = 0;
  463. Xen result;
  464. result = rb_protect(XEN_VALUE_ARG_PROCEDURE_CAST rb_obj_as_string,
  465. obj,
  466. &status);
  467. if (status != 0)
  468. return(C_string_to_Xen_string("<invalid object>"));
  469. return(result);
  470. }
  471. #if HAVE_RB_PROC_NEW
  472. static Xen xen_rb_apply_1(Xen args)
  473. {
  474. return(rb_apply(Xen_car(args), rb_intern("call"), Xen_cadr(args)));
  475. }
  476. #else
  477. static Xen xen_rb_apply_1(Xen args)
  478. {
  479. if (Xen_is_procedure(Xen_car(args)))
  480. return(rb_apply(Xen_car(args), rb_intern("call"), Xen_cadr(args)));
  481. return(rb_apply(rb_mKernel, Xen_car(args), Xen_cadr(args)));
  482. }
  483. #endif
  484. Xen xen_rb_apply(Xen func, Xen args)
  485. {
  486. Xen val;
  487. int status = 0;
  488. val = rb_protect(XEN_VALUE_ARG_PROCEDURE_CAST xen_rb_apply_1,
  489. Xen_list_2(func, args),
  490. &status);
  491. if (status != 0)
  492. return(xen_rb_obj_as_string(rb_gv_get("$!")));
  493. return(val);
  494. }
  495. static Xen xen_rb_funcall_0_inner(Xen args)
  496. {
  497. return(rb_funcall(args, rb_intern("call"), 0));
  498. }
  499. Xen xen_rb_funcall_0(Xen func)
  500. {
  501. Xen val;
  502. int status = 0;
  503. val = rb_protect(XEN_VALUE_ARG_PROCEDURE_CAST xen_rb_funcall_0_inner,
  504. func,
  505. &status);
  506. if (status != 0)
  507. return(xen_rb_obj_as_string(rb_gv_get("$!")));
  508. return(val);
  509. }
  510. Xen xen_rb_copy_list(Xen val)
  511. {
  512. if ((val == Xen_empty_list) || (!Xen_is_cons(val)))
  513. return Xen_empty_list;
  514. return rb_ary_dup(val);
  515. }
  516. Xen xen_rb_str_new2(char *arg)
  517. {
  518. return(rb_str_new2((arg) ? arg : ""));
  519. }
  520. /* class Hook */
  521. static Xen xen_rb_cHook;
  522. static Xen hook_alloc(Xen klass)
  523. {
  524. return(Data_Wrap_Struct(klass, 0, 0, 0));
  525. }
  526. #define Xen_is_class_hook(Arg) rb_obj_is_kind_of(Arg, xen_rb_cHook)
  527. bool xen_rb_hook_p(Xen obj)
  528. {
  529. return(Xen_is_class_hook(obj));
  530. }
  531. bool xen_rb_hook_empty_p(Xen obj)
  532. {
  533. if (Xen_is_class_hook(obj))
  534. return(RB_ARRAY_LEN(rb_iv_get(obj, "@procs")) == 0);
  535. return(true);
  536. }
  537. /*
  538. * @name = "$name_of_hook"
  539. * @arity = arity of procedure(s), default 0
  540. * @procs = [["named proc1", proc1], ...]
  541. */
  542. static Xen xen_rb_hook_initialize(int argc, Xen *argv, Xen hook)
  543. {
  544. Xen name, arity, help;
  545. rb_scan_args(argc, argv, "12", &name, &arity, &help);
  546. Xen_check_type(Xen_is_string(name) || Xen_is_symbol(name), name, 1, __func__, "a char* or symbol");
  547. if (Xen_is_symbol(name))
  548. name = XEN_SYMBOL_TO_STRING(name);
  549. if (arity != Qnil)
  550. {
  551. Xen_check_type(Xen_is_integer(arity), arity, 2, __func__, "an integer");
  552. }
  553. else arity = INT2NUM(0);
  554. if (help != Qnil)
  555. {
  556. Xen_check_type(Xen_is_string(help), help, 3, __func__, "a char*");
  557. XEN_SET_OBJECT_HELP(name, help);
  558. }
  559. rb_iv_set(hook, "@name", name);
  560. rb_iv_set(hook, "@arity", arity);
  561. rb_iv_set(hook, "@procs", rb_ary_new());
  562. return(hook);
  563. }
  564. /*
  565. * To create a simple hook in C, see xen.h, XEN_DEFINE_SIMPLE_HOOK.
  566. * To create a global hook variables, see xen_rb_create_hook() below.
  567. */
  568. Xen xen_rb_hook_c_new(char *name, int arity, char *help)
  569. {
  570. Xen args[3];
  571. args[0] = C_string_to_Xen_string(name);
  572. args[1] = C_int_to_Xen_integer(arity);
  573. args[2] = C_string_to_Xen_string(help);
  574. return(xen_rb_hook_initialize(3, args, hook_alloc(xen_rb_cHook)));
  575. }
  576. /*
  577. RUBY_RELEASE_DATE < "2004-03-18" ? old : new
  578. lambda do end.arity -1 0 !!!
  579. lambda do || end.arity 0 0
  580. lambda do |a| end.arity -1 1 !!!
  581. lambda do |*a| end.arity -1 -1
  582. lambda do |a, b| end.arity 2 2
  583. lambda do |a, *b| end.arity -2 -2
  584. etc.
  585. */
  586. #ifdef RUBY_VERSION
  587. #define XEN_RUBY_RELEASE_DATE RUBY_RELEASE_DATE
  588. #else
  589. #define XEN_RUBY_RELEASE_DATE Xen_string_to_C_string(Xen_eval_C_string("RUBY_RELEASE_DATE"))
  590. #endif
  591. #define RUBY_NEW_ARITY_DATE "2004-03-18"
  592. #define OLD_RUBY_ARITY() (strcmp(XEN_RUBY_RELEASE_DATE, RUBY_NEW_ARITY_DATE) < 0)
  593. /* #define NEW_RUBY_ARITY() (strcmp(XEN_RUBY_RELEASE_DATE, RUBY_NEW_ARITY_DATE) >= 0) */
  594. bool xen_rb_arity_ok(int rargs, int args)
  595. {
  596. if (OLD_RUBY_ARITY())
  597. {
  598. if ((rargs >= 2) || (rargs == 0))
  599. return(rargs == args);
  600. else if (rargs <= -2)
  601. return(abs(rargs) <= args);
  602. else /* rargs -1 remains (no 1 exists) */
  603. return((args == 1) || (args == 0) || (args == -1));
  604. }
  605. else /* NEW_RUBY_ARITY */
  606. return((rargs >= 0) ? (rargs == args) : (abs(rargs) <= args));
  607. }
  608. static Xen xen_rb_hook_add_hook(int argc, Xen *argv, Xen hook)
  609. {
  610. Xen name, func;
  611. int args;
  612. args = Xen_integer_to_C_int(rb_iv_get(hook, "@arity"));
  613. rb_scan_args(argc, argv, "1&", &name, &func);
  614. Xen_check_type(Xen_is_string(name), name, 1, __func__, "a char*");
  615. Xen_check_type(Xen_is_procedure(func) && xen_rb_arity_ok(Xen_integer_to_C_int(Xen_arity(func)), args),
  616. func, 2, __func__, "a procedure");
  617. rb_ary_push(rb_iv_get(hook, "@procs"), rb_ary_new3(2, name, func));
  618. return(hook);
  619. }
  620. #if HAVE_RB_PROC_NEW
  621. static Xen xen_proc_call(Xen args, Xen id)
  622. {
  623. return(rb_apply(rb_mKernel, (ID)id, Xen_is_cons(args) ? args : Xen_list_1(args)));
  624. }
  625. #if 0
  626. VALUE rb_proc_new((VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE));
  627. #endif
  628. static Xen xen_rb_proc_new(const char *name, Xen (*func)(), int arity, const char* doc)
  629. {
  630. rb_define_module_function(rb_mKernel, name, Xen_procedure_cast func, arity);
  631. if (doc) C_SET_OBJECT_HELP(name, doc);
  632. return(rb_proc_new(Xen_procedure_cast xen_proc_call, rb_intern(name)));
  633. }
  634. static Xen xen_rb_hook_arity(Xen hook);
  635. Xen xen_rb_add_hook(Xen hook, VALUE (*func)(), const char *name, const char* doc)
  636. {
  637. /* called from C, not Ruby, to add a function to a Ruby-side hook */
  638. char *temp;
  639. temp = xen_scheme_procedure_to_ruby(name);
  640. 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)));
  641. if (temp) free(temp);
  642. return(hook);
  643. }
  644. #else
  645. Xen xen_rb_add_hook(Xen hook, VALUE (*func)(), const char *name, const char* doc)
  646. {
  647. /* called from C, not Ruby, to add a function to a Ruby-side hook
  648. * this doesn't work in g++ because it thinks the funcs are invalid:
  649. * "error: invalid conversion from 'VALUE (*)(VALUE, VALUE)' to 'VALUE (*)(...)'" (snd-file.c etc)
  650. */
  651. Xen var, avar;
  652. char *temp;
  653. temp = xen_scheme_procedure_to_ruby(name);
  654. avar = rb_iv_get(hook, "@arity");
  655. rb_define_module_function(rb_mKernel, temp, Xen_procedure_cast func, (Xen_is_integer(avar)) ? Xen_integer_to_C_int(avar) : 0);
  656. if (doc) C_SET_OBJECT_HELP(temp, doc);
  657. var = rb_intern(temp);
  658. rb_ary_push(rb_iv_get(hook, "@procs"), rb_ary_new3(2, C_string_to_Xen_string(temp), var));
  659. if (temp) free(temp);
  660. return(hook);
  661. }
  662. #endif
  663. static Xen xen_rb_hook_remove_hook(Xen hook, Xen name)
  664. {
  665. Xen ary;
  666. ary = rb_iv_get(hook, "@procs");
  667. return(rb_ary_delete(ary, rb_ary_assoc(ary, name)));
  668. }
  669. Xen xen_rb_hook_reset_hook(Xen hook)
  670. {
  671. if (Xen_is_class_hook(hook))
  672. rb_ary_clear(rb_iv_get(hook, "@procs"));
  673. return(hook);
  674. }
  675. static Xen xen_rb_hook_names(Xen hook)
  676. {
  677. Xen ary, ret = Qnil;
  678. long len;
  679. ary = rb_iv_get(hook, "@procs");
  680. len = RB_ARRAY_LEN(ary);
  681. if (len > 0)
  682. {
  683. long i;
  684. ret = rb_ary_new2(len);
  685. for (i = 0; i < len; i++)
  686. rb_ary_store(ret, i, Xen_vector_ref(Xen_vector_ref(ary, i), 0));
  687. }
  688. return(ret);
  689. }
  690. Xen xen_rb_hook_to_a(Xen hook)
  691. {
  692. Xen ret = Qnil;
  693. if (Xen_is_class_hook(hook))
  694. {
  695. Xen ary;
  696. long len;
  697. ary = rb_iv_get(hook, "@procs");
  698. len = Xen_list_length(ary);
  699. if (len > 0)
  700. {
  701. long i;
  702. ret = rb_ary_new2(len);
  703. for (i = 0; i < len; i++)
  704. rb_ary_store(ret, i, Xen_vector_ref(Xen_vector_ref(ary, i), 1));
  705. }
  706. }
  707. return(ret);
  708. }
  709. static Xen xen_rb_hook_run_hook(Xen hook)
  710. {
  711. if (RB_ARRAY_LEN(rb_iv_get(hook, "@procs")))
  712. rb_ary_each(xen_rb_hook_to_a(hook));
  713. return(hook);
  714. }
  715. /*
  716. * Calls all hook-procedures but returns only the last result; use
  717. * $var_hook.run_hook { |prc| ret << prc.call(*args) } for collecting
  718. * results.
  719. */
  720. static Xen xen_rb_hook_call(int argc, Xen *argv, Xen hook)
  721. {
  722. Xen result = Qnil, rest, procs;
  723. rb_scan_args(argc, argv, "*", &rest);
  724. procs = xen_rb_hook_to_a(hook);
  725. if (procs != Qnil)
  726. {
  727. long i;
  728. for (i = 0; i < RB_ARRAY_LEN(procs); i++)
  729. result = xen_rb_apply(rb_ary_entry(procs, i), rest);
  730. }
  731. return(result);
  732. }
  733. static Xen xen_rb_hook_is_empty_p(Xen hook)
  734. {
  735. return(C_bool_to_Xen_boolean(RB_ARRAY_LEN(rb_iv_get(hook, "@procs")) == 0));
  736. }
  737. static Xen xen_rb_hook_length(Xen hook)
  738. {
  739. return(C_int_to_Xen_integer(RB_ARRAY_LEN(rb_iv_get(hook, "@procs"))));
  740. }
  741. static Xen xen_rb_hook_name(Xen hook)
  742. {
  743. return(rb_iv_get(hook, "@name"));
  744. }
  745. static Xen xen_rb_hook_describe(Xen hook)
  746. {
  747. return(Xen_documentation(xen_rb_hook_name(hook)));
  748. }
  749. static Xen xen_rb_hook_arity(Xen hook)
  750. {
  751. return(rb_iv_get(hook, "@arity"));
  752. }
  753. static Xen xen_rb_hook_inspect(Xen hook)
  754. {
  755. Xen str = rb_str_new2("#<Hook name: ");
  756. rb_str_append(str, rb_inspect(rb_iv_get(hook, "@name")));
  757. rb_str_cat2(str, ", arity: ");
  758. rb_str_append(str, rb_inspect(rb_iv_get(hook, "@arity")));
  759. rb_str_cat2(str, ", procs[");
  760. rb_str_append(str, rb_inspect(xen_rb_hook_length(hook)));
  761. rb_str_cat2(str, "]: ");
  762. rb_str_append(str, rb_inspect(xen_rb_hook_names(hook)));
  763. rb_str_cat2(str, ">");
  764. return(str);
  765. }
  766. /* bil -- added xen_rb_create_hook for Xen_define_hook in xen.h, 13-Jun-05 --
  767. * seems to work, but I'm guessing, especially the rb_gv_set line.
  768. * I can't use rb_define_variable here, as in the old version, because it takes a pointer
  769. * to the new variable, which in this case is a local variable => segfault.
  770. */
  771. Xen xen_rb_create_hook(char *name, int arity, char *help)
  772. {
  773. Xen var, hook_name;
  774. char *temp;
  775. var = xen_rb_hook_c_new(temp = xen_scheme_global_variable_to_ruby(name), arity, help);
  776. hook_name = xen_rb_hook_name(var);
  777. rb_gv_set(Xen_string_to_C_string(hook_name), var);
  778. if (temp) free(temp);
  779. return(var);
  780. }
  781. static int simple_hook_number = 0;
  782. Xen xen_rb_create_simple_hook(int arity)
  783. {
  784. char *name;
  785. Xen hook;
  786. name = (char *)calloc(20, sizeof(char));
  787. snprintf(name, 20, "simple_%02d_hook", simple_hook_number++);
  788. hook = xen_rb_create_hook(name, arity, NULL);
  789. free(name);
  790. return(hook);
  791. }
  792. /*
  793. * make_hook(name, arity = 0, help = "", hook_name = nil, &func)
  794. *
  795. * make_hook("var_hook")
  796. * == $var_hook = Hook.new("var_hook")
  797. * make_hook("var_hook", 1)
  798. * == $var_hook = Hook.new("var_hook", 1)
  799. * make_hook("var_hook", 1, "help $var_hook")
  800. * == $var_hook = Hook.new("var_hook", 1, "help $var_hook")
  801. *
  802. * make_hook("var_hook", 1, "help $var_hook", "1st proc") do |a| ... end
  803. * == $var_hook = Hook.new("var_hook", 1, "help $var_hook")
  804. * $var_hook.add_hook!("1st proc") do |a| ... end
  805. */
  806. #ifndef RSTRING_LEN
  807. #define RB_STR_LEN(str) RSTRING(str)->len
  808. #else
  809. #define RB_STR_LEN(str) RSTRING_LEN(str)
  810. #endif
  811. static Xen xen_rb_make_hook(int argc, Xen *argv, Xen klass)
  812. {
  813. Xen hook = Xen_false, name;
  814. if (argc > 0 && argc < 4)
  815. {
  816. hook = xen_rb_hook_initialize(argc, argv, hook_alloc(xen_rb_cHook));
  817. if (rb_block_given_p())
  818. {
  819. argv[0] = rb_str_new2("");
  820. xen_rb_hook_add_hook(1, argv, hook);
  821. }
  822. }
  823. else if (argc == 4 && rb_block_given_p())
  824. {
  825. hook = xen_rb_hook_initialize(3, argv, hook_alloc(xen_rb_cHook));
  826. argv[0] = argv[3];
  827. xen_rb_hook_add_hook(1, argv, hook);
  828. }
  829. else Xen_error(Xen_make_error_type("wrong-number-of-args"),
  830. Xen_list_1(C_string_to_Xen_string("make_hook(name, arity=0, help=\"\", hook_name=\"\", &func)")));
  831. name = xen_rb_hook_name(hook);
  832. if (Xen_char_to_C_char(name) != '$')
  833. {
  834. char *temp;
  835. temp = xen_scheme_global_variable_to_ruby(Xen_string_to_C_string(name));
  836. name = C_string_to_Xen_string(temp);
  837. if (temp) free(temp);
  838. }
  839. Xen_check_type(RB_STR_LEN(name) >= 2, name, 1, __func__, "a char*, len >= 2");
  840. return(rb_gv_set(Xen_string_to_C_string(name), hook));
  841. }
  842. static Xen xen_rb_is_hook_p(Xen klass, Xen obj)
  843. {
  844. return(C_bool_to_Xen_boolean(Xen_is_class_hook(obj)));
  845. }
  846. /*
  847. * Hook.new(name, arity = 0, help = "")
  848. *
  849. * $my_hook = Hook.new("my_hook", 2, "info of my_hook")
  850. * $my_hook.add_hook!("1st proc") do |a, b| ... end
  851. * or make_hook("my_hook", 2, "info of my_hook", "1st proc") do |a, b| ... end
  852. *
  853. * $my_hook.add_hook!("2nd proc") do |a, b| ... end
  854. * $my_hook.inspect --> #<Hook name: "$my_hook", arity: 2, procs[2]: ["1st proc", "2nd proc"]>
  855. *
  856. * ret = 0
  857. * $my_hook.run_hook do |prc| ret = prc.call(ret, 2) end
  858. *
  859. * $my_hook.help --> info of my_hook
  860. * $my_hook.remove_hook!("1st proc")
  861. * $my_hook.inspect --> #<Hook name: "$my_hook", arity: 2, procs[1]: ["2nd proc"]>
  862. *
  863. * $my_hook.remove_hook!("2nd proc")
  864. * $my_hook.inspect --> #<Hook name: "$my_hook", arity: 2, procs[0]: nil>
  865. */
  866. #if (!HAVE_RB_DEFINE_ALLOC_FUNC)
  867. static Xen xen_rb_new(int argc, Xen *argv, Xen klass)
  868. {
  869. Xen hook = hook_alloc(klass);
  870. rb_obj_call_init(hook, argc, argv);
  871. return(hook);
  872. }
  873. #endif
  874. static Xen rb_object_properties = Xen_false;
  875. #define S_property "property"
  876. #define S_set_property "set_property"
  877. #define S_properties "properties"
  878. Xen rb_property(Xen obj, Xen key)
  879. {
  880. #define H_property S_property "(obj, key) \
  881. if key exists, return obj's value (maybe nil) associated with key otherwise false"
  882. Xen props = Xen_false;
  883. if (Xen_is_false(rb_object_properties))
  884. return(Xen_false);
  885. props = rb_hash_aref(rb_object_properties, obj);
  886. if (Xen_is_false(props) || props == Qnil)
  887. return(Xen_false);
  888. else
  889. return(rb_hash_aref(props, key));
  890. }
  891. Xen rb_set_property(Xen obj, Xen key, Xen value)
  892. {
  893. #define H_set_property S_set_property "(obj, key, value) \
  894. set key-value pair for obj and return value"
  895. Xen props = Xen_false;
  896. if (Xen_is_false(rb_object_properties))
  897. {
  898. rb_object_properties = rb_hash_new();
  899. Xen_GC_protect(rb_object_properties);
  900. }
  901. else
  902. props = rb_hash_aref(rb_object_properties, obj);
  903. if (Xen_is_false(props) || props == Qnil)
  904. props = rb_hash_new();
  905. rb_hash_aset(props, key, value);
  906. rb_hash_aset(rb_object_properties, obj, props);
  907. return(value);
  908. }
  909. Xen rb_properties(void)
  910. {
  911. #define H_properties S_properties "() return all properties of rb_object_properties (a hash)"
  912. return(rb_object_properties);
  913. }
  914. static Xen g_gc_off(void)
  915. {
  916. #define H_gc_off "(" S_gc_off ") turns off garbage collection"
  917. rb_gc_disable();
  918. return(Xen_false);
  919. }
  920. static Xen g_gc_on(void)
  921. {
  922. #define H_gc_on "(" S_gc_on ") turns on garbage collection"
  923. rb_gc_enable();
  924. return(Xen_false);
  925. }
  926. Xen_wrap_1_optional_arg(g_get_help_w, g_get_help);
  927. Xen_wrap_2_args(g_add_help_w, g_add_help);
  928. Xen_wrap_3_args(g_set_property_w, rb_set_property);
  929. Xen_wrap_2_args(g_property_w, rb_property);
  930. Xen_wrap_no_args(g_properties_w, rb_properties);
  931. Xen_wrap_no_args(g_gc_off_w, g_gc_off)
  932. Xen_wrap_no_args(g_gc_on_w, g_gc_on)
  933. static bool hook_inited = false;
  934. void Init_Hook(void)
  935. {
  936. if (hook_inited) return;
  937. hook_inited = true;
  938. xen_rb_cHook = rb_define_class("Hook", rb_cObject);
  939. rb_include_module(xen_rb_cHook, rb_mEnumerable);
  940. #if HAVE_RB_DEFINE_ALLOC_FUNC
  941. rb_define_alloc_func(xen_rb_cHook, hook_alloc);
  942. #else
  943. rb_define_singleton_method(xen_rb_cHook, "new", Xen_procedure_cast xen_rb_new, -1);
  944. #endif
  945. rb_define_method(xen_rb_cHook, "initialize", Xen_procedure_cast xen_rb_hook_initialize, -1);
  946. rb_define_method(xen_rb_cHook, "add_hook!", Xen_procedure_cast xen_rb_hook_add_hook, -1);
  947. rb_define_method(xen_rb_cHook, "remove_hook!", Xen_procedure_cast xen_rb_hook_remove_hook, 1);
  948. rb_define_method(xen_rb_cHook, "reset_hook!", Xen_procedure_cast xen_rb_hook_reset_hook, 0);
  949. rb_define_alias(xen_rb_cHook, "clear", "reset_hook!");
  950. rb_define_method(xen_rb_cHook, "to_a", Xen_procedure_cast xen_rb_hook_to_a, 0);
  951. rb_define_method(xen_rb_cHook, "run_hook", Xen_procedure_cast xen_rb_hook_run_hook, 0);
  952. rb_define_alias(xen_rb_cHook, "each", "run_hook");
  953. rb_define_method(xen_rb_cHook, "call", Xen_procedure_cast xen_rb_hook_call, -1);
  954. rb_define_method(xen_rb_cHook, "length", Xen_procedure_cast xen_rb_hook_length, 0);
  955. rb_define_alias(xen_rb_cHook, "size", "length");
  956. rb_define_method(xen_rb_cHook, "empty?", Xen_procedure_cast xen_rb_hook_is_empty_p, 0);
  957. rb_define_method(xen_rb_cHook, "name", Xen_procedure_cast xen_rb_hook_name, 0);
  958. rb_define_method(xen_rb_cHook, "arity", Xen_procedure_cast xen_rb_hook_arity, 0);
  959. rb_define_method(xen_rb_cHook, "describe", Xen_procedure_cast xen_rb_hook_describe, 0);
  960. rb_define_alias(xen_rb_cHook, "help", "describe");
  961. rb_define_alias(xen_rb_cHook, "documentation", "describe");
  962. rb_define_method(xen_rb_cHook, "inspect", Xen_procedure_cast xen_rb_hook_inspect, 0);
  963. rb_define_global_function("make_hook", Xen_procedure_cast xen_rb_make_hook, -1);
  964. rb_define_global_function("hook?", Xen_procedure_cast xen_rb_is_hook_p, 1);
  965. Xen_define_procedure(S_get_help, g_get_help_w, 0, 1, 0, H_get_help);
  966. Xen_define_procedure(S_add_help, g_add_help_w, 2, 0, 0, H_add_help);
  967. Xen_define_procedure(S_set_property, g_set_property_w, 3, 0, 0, H_set_property);
  968. Xen_define_procedure(S_property, g_property_w, 2, 0, 0, H_property);
  969. Xen_define_procedure(S_properties, g_properties_w, 0, 0, 0, H_properties);
  970. Xen_define_procedure(S_gc_off, g_gc_off_w, 0, 0, 0, H_gc_off);
  971. Xen_define_procedure(S_gc_on, g_gc_on_w, 0, 0, 0, H_gc_on);
  972. }
  973. /* end of class Hook */
  974. #endif
  975. /* ------------------------------ FORTH ------------------------------ */
  976. #if HAVE_FORTH
  977. char *xen_version(void)
  978. {
  979. return(fth_format("Fth: %s, Xen: " XEN_VERSION, FTH_VERSION));
  980. }
  981. void xen_gc_mark(Xen val)
  982. {
  983. fth_gc_mark(val);
  984. }
  985. /*
  986. * A simple interpreter:
  987. *
  988. * #include <xen.h>
  989. *
  990. * int main(int argc, char **argv)
  991. * {
  992. * xen_repl(argc, argv);
  993. * return(0);
  994. * }
  995. *
  996. * linking requires xen.o and -lfth -lm
  997. */
  998. void xen_repl(int argc, char **argv)
  999. {
  1000. fth_repl(argc, argv);
  1001. }
  1002. static ficlWord *snd_exit_xt;
  1003. static void fth_snd_exit(int n)
  1004. {
  1005. if (!snd_exit_xt)
  1006. snd_exit_xt = ficlSystemLookup(FTH_FICL_SYSTEM(), (char *)"snd-exit");
  1007. ficlStackPushInteger(FTH_FICL_STACK(), n);
  1008. ficlVmExecuteXT(FTH_FICL_VM(), snd_exit_xt);
  1009. ficlStackDrop(FTH_FICL_STACK(), 1);
  1010. }
  1011. static Xen g_gc_off(void)
  1012. {
  1013. #define H_gc_off "(" S_gc_off ") turns off garbage collection"
  1014. fth_gc_on();
  1015. return(Xen_false);
  1016. }
  1017. static Xen g_gc_on(void)
  1018. {
  1019. #define H_gc_on "(" S_gc_on ") turns on garbage collection"
  1020. fth_gc_on();
  1021. return(Xen_false);
  1022. }
  1023. void xen_initialize(void)
  1024. {
  1025. fth_init();
  1026. fth_exit_hook = fth_snd_exit;
  1027. Xen_define_procedure(S_gc_off, g_gc_off, 0, 0, 0, H_gc_off);
  1028. Xen_define_procedure(S_gc_on, g_gc_on, 0, 0, 0, H_gc_on);
  1029. }
  1030. #endif /* HAVE_FORTH */
  1031. /* ------------------------------ S7 ------------------------------ */
  1032. #if HAVE_SCHEME
  1033. #include "s7.h"
  1034. #if ENABLE_WEBSERVER
  1035. #include "s7webserver/s7webserver.h"
  1036. #endif
  1037. s7_scheme *s7;
  1038. Xen xen_false, xen_true, xen_nil, xen_undefined, xen_zero;
  1039. char *xen_version(void)
  1040. {
  1041. char *buf;
  1042. buf = (char *)calloc(64, sizeof(char));
  1043. #if HAVE_SNPRINTF
  1044. snprintf(buf, 64, "s7: %s (%s), Xen: %s", S7_VERSION, S7_DATE, XEN_VERSION);
  1045. #else
  1046. sprintf(buf, "s7: %s (%s), Xen: %s", S7_VERSION, S7_DATE, XEN_VERSION);
  1047. #endif
  1048. return(buf);
  1049. }
  1050. static char *xen_s7_repl_prompt = NULL;
  1051. void xen_s7_set_repl_prompt(const char *new_prompt)
  1052. {
  1053. if (xen_s7_repl_prompt) free(xen_s7_repl_prompt);
  1054. xen_s7_repl_prompt = xen_strdup(new_prompt);
  1055. }
  1056. #if USE_SND
  1057. char *stdin_check_for_full_expression(const char *newstr);
  1058. void stdin_free_str(void);
  1059. #endif
  1060. void xen_repl(int argc, char **argv)
  1061. {
  1062. int size = 512;
  1063. bool expr_ok = true;
  1064. char *buffer = NULL;
  1065. buffer = (char *)calloc(size, sizeof(char));
  1066. while (true)
  1067. {
  1068. if (expr_ok)
  1069. {
  1070. fprintf(stdout, "\n%s", xen_s7_repl_prompt);
  1071. expr_ok = false; /* don't get into an infinite loop if running in the background! */
  1072. }
  1073. if (fgets(buffer, size, stdin) != NULL)
  1074. {
  1075. /* also, it's possible to get a string of spaces or nulls (? -- not sure what is coming in) if stdin is /dev/null */
  1076. /* 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 */
  1077. int i, len;
  1078. expr_ok = false;
  1079. len = strlen(buffer);
  1080. for (i = 0; i < len; i++)
  1081. {
  1082. if (buffer[i] == 0)
  1083. break;
  1084. if (!isspace((int)buffer[i]))
  1085. {
  1086. expr_ok = true;
  1087. break;
  1088. }
  1089. }
  1090. if (expr_ok)
  1091. {
  1092. char *str, *temp;
  1093. #if USE_SND
  1094. str = stdin_check_for_full_expression(buffer); /* "str" here is actually stdin_str, so we need to clear it explicitly */
  1095. if (!str) {expr_ok = false; continue;}
  1096. len = strlen(str) + 16;
  1097. temp = (char *)malloc(len * sizeof(char));
  1098. snprintf(temp, len, "(write %s)", str);
  1099. Xen_eval_C_string(temp);
  1100. free(temp);
  1101. stdin_free_str();
  1102. #else
  1103. temp = (char *)malloc(len + 16);
  1104. snprintf(temp, len + 16, "(write %s)", buffer); /* use write, not display so that strings are in double quotes */
  1105. Xen_eval_C_string(temp);
  1106. free(temp);
  1107. #endif
  1108. }
  1109. }
  1110. }
  1111. free(buffer);
  1112. }
  1113. void xen_gc_mark(Xen val)
  1114. {
  1115. s7_mark_object(val);
  1116. }
  1117. Xen xen_set_assoc(s7_scheme *sc, s7_pointer key, s7_pointer val, s7_pointer alist)
  1118. {
  1119. /* fixup alist, return it (caller has to make sure it is reflected in its object) */
  1120. /*
  1121. (let ((old-val (assoc key alist)))
  1122. (if old-val
  1123. (progn
  1124. (set-cdr! old-val new-val)
  1125. alist)
  1126. (cons (cons key new-val) alist)))
  1127. */
  1128. Xen old_val;
  1129. old_val = s7_assoc(sc, key, alist); /* returns #f if nothing found */
  1130. if (old_val == s7_f(sc))
  1131. return(s7_cons(sc, s7_cons(sc, key, val), alist));
  1132. s7_set_cdr(old_val, val);
  1133. return(alist);
  1134. }
  1135. Xen xen_assoc(s7_scheme *sc, Xen key, Xen alist)
  1136. {
  1137. Xen val;
  1138. val = s7_assoc(sc, key, alist);
  1139. if (val != s7_f(sc))
  1140. return(s7_cdr(val));
  1141. return(s7_f(sc));
  1142. }
  1143. /* add various file functions that everyone else implements */
  1144. #ifndef _MSC_VER
  1145. #include <unistd.h>
  1146. #include <sys/time.h>
  1147. #endif
  1148. #include <sys/stat.h>
  1149. #include <fcntl.h>
  1150. static Xen g_getpid(void)
  1151. {
  1152. #define H_getpid "(getpid) returns the current job's process id"
  1153. return(C_int_to_Xen_integer((int)getpid()));
  1154. }
  1155. #if (!WITH_SYSTEM_EXTRAS)
  1156. static bool file_probe(const char *arg)
  1157. {
  1158. #ifndef _MSC_VER
  1159. return(access(arg, F_OK) == 0);
  1160. #else
  1161. int fd;
  1162. #ifdef O_NONBLOCK
  1163. fd = open(arg, O_RDONLY, O_NONBLOCK);
  1164. #else
  1165. fd = open(arg, O_RDONLY, 0);
  1166. #endif
  1167. if (fd == -1) return(false);
  1168. close(fd);
  1169. return(true);
  1170. #endif
  1171. }
  1172. static Xen g_file_exists_p(Xen name)
  1173. {
  1174. #define H_file_exists_p "(file-exists? filename): #t if the file exists"
  1175. Xen_check_type(Xen_is_string(name), name, 1, "file-exists?", "a string");
  1176. return(C_bool_to_Xen_boolean(file_probe(Xen_string_to_C_string(name))));
  1177. }
  1178. static bool is_directory(const char *filename)
  1179. {
  1180. #if (defined(_MSC_VER) || __CYGWIN__)
  1181. return(false);
  1182. #else
  1183. #ifdef S_ISDIR
  1184. struct stat statbuf;
  1185. return((stat(filename, &statbuf) >= 0) &&
  1186. (S_ISDIR(statbuf.st_mode)));
  1187. return(false);
  1188. #endif
  1189. #endif
  1190. }
  1191. static Xen g_is_directory(Xen name)
  1192. {
  1193. #define H_is_directory "(directory? filename): #t if filename names a directory"
  1194. Xen_check_type(Xen_is_string(name), name, 1, "directory?", "a string");
  1195. return(C_bool_to_Xen_boolean(is_directory(Xen_string_to_C_string(name)))); /* snd-file.c l 84 */
  1196. }
  1197. static Xen g_delete_file(Xen name)
  1198. {
  1199. #define H_delete_file "(delete-file filename): deletes the file"
  1200. Xen_check_type(Xen_is_string(name), name, 1, "delete-file", "a string");
  1201. return(C_bool_to_Xen_boolean(unlink(Xen_string_to_C_string(name))));
  1202. }
  1203. static Xen g_system(Xen command)
  1204. {
  1205. #define H_system "(system command): execute command"
  1206. Xen_check_type(Xen_is_string(command), command, 1, "system", "a string");
  1207. return(C_int_to_Xen_integer(system(Xen_string_to_C_string(command))));
  1208. }
  1209. static Xen g_s7_getenv(Xen var) /* "g_getenv" is in use in glib! */
  1210. {
  1211. #define H_getenv "(getenv var): return value of environment variable var"
  1212. Xen_check_type(Xen_is_string(var), var, 1, "getenv", "a string");
  1213. return(C_string_to_Xen_string(getenv(Xen_string_to_C_string(var))));
  1214. }
  1215. #endif
  1216. #ifdef _MSC_VER
  1217. #include <direct.h>
  1218. #endif
  1219. static Xen g_getcwd(void)
  1220. {
  1221. #define H_getcwd "(getcwd) returns the name of the current working directory"
  1222. char *buf;
  1223. Xen result = Xen_false;
  1224. buf = (char *)calloc(1024, sizeof(char));
  1225. #ifdef _MSC_VER
  1226. if (_getcwd(buf, 1024) != NULL)
  1227. #else
  1228. if (getcwd(buf, 1024) != NULL)
  1229. #endif
  1230. result = C_string_to_Xen_string(buf);
  1231. free(buf);
  1232. return(result);
  1233. }
  1234. static Xen g_strftime(Xen format, Xen tm)
  1235. {
  1236. #define H_strftime "(strftime format time) returns a string describing the time: (strftime \"%d-%b %H:%M %Z\" (localtime (current-time)))"
  1237. char *buf;
  1238. Xen result;
  1239. const struct tm *p;
  1240. Xen_check_type(Xen_is_string(format), format, 1, "strftime", "a string");
  1241. Xen_check_type(Xen_is_wrapped_c_pointer(tm), tm, 2, "strftime", "a localtime struct");
  1242. p = (const struct tm *)Xen_unwrap_C_pointer(tm);
  1243. Xen_check_type(p != NULL, tm, 2, "strftime", "a localtime struct");
  1244. buf = (char *)calloc(1024, sizeof(char));
  1245. strftime(buf, 1024, Xen_string_to_C_string(format), p);
  1246. result = C_string_to_Xen_string(buf);
  1247. free(buf);
  1248. return(result);
  1249. }
  1250. /* (format #f ";~A~%" (strftime "%d-%b %H:%M %Z" (localtime (current-time)))) */
  1251. /* these two need to be compatible with g_file_write_date in snd-file.c */
  1252. static Xen g_localtime(Xen tm)
  1253. {
  1254. #define H_localtime "(localtime tm) breaks up tm into something suitable for strftime"
  1255. time_t rtime;
  1256. rtime = (time_t)Xen_ulong_to_C_ulong(tm);
  1257. return(Xen_wrap_C_pointer(localtime((time_t *)(&rtime))));
  1258. }
  1259. static Xen g_current_time(void)
  1260. {
  1261. time_t curtime;
  1262. #define H_current_time "(current-time) returns the current time (for localtime and strftime)"
  1263. curtime = time(NULL);
  1264. return(C_ulong_to_Xen_ulong(curtime));
  1265. }
  1266. static Xen g_tmpnam(void)
  1267. {
  1268. #define H_tmpnam "(tmpnam) returns a new (hopefully unused) temporary file name"
  1269. #define BUFFER_SIZE 512
  1270. static int file_ctr = 0;
  1271. char *str, *tmpdir = NULL;
  1272. Xen result;
  1273. str = (char *)calloc(BUFFER_SIZE, sizeof(char));
  1274. tmpdir = xen_strdup(getenv("TMPDIR"));
  1275. #ifdef P_tmpdir
  1276. if (tmpdir == NULL)
  1277. tmpdir = xen_strdup(P_tmpdir); /* /usr/include/stdio.h */
  1278. if (tmpdir)
  1279. {
  1280. int len;
  1281. len = strlen(tmpdir);
  1282. if (len > 0)
  1283. {
  1284. if (tmpdir[len - 1] == '/')
  1285. tmpdir[len - 1] = 0;
  1286. }
  1287. else
  1288. {
  1289. free(tmpdir);
  1290. tmpdir = xen_strdup(".");
  1291. }
  1292. }
  1293. #else
  1294. if (tmpdir == NULL) tmpdir = xen_strdup("/tmp");
  1295. #endif
  1296. snprintf(str, BUFFER_SIZE, "%s/xen_%d_%d", tmpdir, (int)getpid(), file_ctr++);
  1297. if (tmpdir) free(tmpdir);
  1298. result = C_string_to_Xen_string(str);
  1299. free(str);
  1300. return(result);
  1301. }
  1302. static Xen g_ftell(Xen fd)
  1303. {
  1304. return(C_int_to_Xen_integer(lseek(Xen_integer_to_C_int(fd), 0, SEEK_CUR)));
  1305. }
  1306. static Xen g_gc_off(void)
  1307. {
  1308. #define H_gc_off "(" S_gc_off ") turns off garbage collection"
  1309. s7_gc_on(s7, false);
  1310. return(Xen_false);
  1311. }
  1312. static Xen g_gc_on(void)
  1313. {
  1314. #define H_gc_on "(" S_gc_on ") turns on garbage collection"
  1315. s7_gc_on(s7, true);
  1316. return(Xen_false);
  1317. }
  1318. Xen_wrap_no_args(g_getpid_w, g_getpid)
  1319. #if (!WITH_SYSTEM_EXTRAS)
  1320. Xen_wrap_1_arg(g_file_exists_p_w, g_file_exists_p)
  1321. Xen_wrap_1_arg(g_is_directory_w, g_is_directory)
  1322. Xen_wrap_1_arg(g_delete_file_w, g_delete_file)
  1323. Xen_wrap_1_arg(g_s7_getenv_w, g_s7_getenv)
  1324. Xen_wrap_1_arg(g_system_w, g_system)
  1325. #endif
  1326. Xen_wrap_no_args(g_getcwd_w, g_getcwd)
  1327. Xen_wrap_2_args(g_strftime_w, g_strftime)
  1328. Xen_wrap_1_arg(g_localtime_w, g_localtime)
  1329. Xen_wrap_no_args(g_current_time_w, g_current_time)
  1330. Xen_wrap_no_args(g_tmpnam_w, g_tmpnam)
  1331. Xen_wrap_1_arg(g_ftell_w, g_ftell)
  1332. Xen_wrap_no_args(g_gc_off_w, g_gc_off)
  1333. Xen_wrap_no_args(g_gc_on_w, g_gc_on)
  1334. #if ENABLE_WEBSERVER
  1335. #if USE_MOTIF
  1336. #include "snd.h"
  1337. static idle_func_t called_periodically(any_pointer_t pet)
  1338. {
  1339. s7webserver_call_very_often();
  1340. return(BACKGROUND_CONTINUE);
  1341. }
  1342. #endif
  1343. #endif
  1344. s7_scheme *s7_xen_initialize(s7_scheme *sc)
  1345. {
  1346. s7_pointer i, b, p, s;
  1347. xen_s7_repl_prompt = xen_strdup("> ");
  1348. if (!sc)
  1349. {
  1350. s7 = s7_init();
  1351. if (!s7)
  1352. {
  1353. fprintf(stderr, "Can't initialize s7!\n");
  1354. return(NULL);
  1355. }
  1356. #if ENABLE_WEBSERVER
  1357. {
  1358. s7webserver_t *s7webserver;
  1359. s7webserver = s7webserver_create(s7, 6080, true);
  1360. if (!s7webserver)
  1361. fprintf(stderr, "Unable to start web server. Port 6080 may be in use\n");
  1362. else fprintf(stdout, "Started s7 webserver at port %d\n", s7webserver_get_portnumber(s7webserver));
  1363. #if USE_MOTIF
  1364. BACKGROUND_ADD(called_periodically, NULL);
  1365. #endif
  1366. }
  1367. #endif
  1368. }
  1369. else s7 = sc;
  1370. i = s7_make_symbol(s7, "integer?");
  1371. b = s7_make_symbol(s7, "boolean?");
  1372. p = s7_make_symbol(s7, "pair?");
  1373. s = s7_make_symbol(s7, "string?");
  1374. xen_false = s7_f(s7);
  1375. xen_true = s7_t(s7);
  1376. xen_nil = s7_nil(s7);
  1377. xen_undefined = s7_undefined(s7);
  1378. xen_zero = s7_make_integer(s7, 0);
  1379. s7_gc_protect(s7, xen_zero);
  1380. Xen_define_typed_procedure("getpid", g_getpid_w, 0, 0, 0, H_getpid, s7_make_signature(s7, 1, i));
  1381. #if (!WITH_SYSTEM_EXTRAS)
  1382. 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));
  1383. Xen_define_typed_procedure("directory?", g_is_directory_w, 1, 0, 0, H_is_directory, s7_make_signature(s7, 2, b, s));
  1384. Xen_define_typed_procedure("delete-file", g_delete_file_w, 1, 0, 0, H_delete_file, s7_make_signature(s7, 2, b, s));
  1385. Xen_define_typed_procedure("getenv", g_s7_getenv_w, 1, 0, 0, H_getenv, s7_make_signature(s7, 2, s, s));
  1386. Xen_define_typed_procedure("system", g_system_w, 1, 0, 0, H_system, s7_make_signature(s7, 2, i, s));
  1387. #endif
  1388. Xen_define_typed_procedure("getcwd", g_getcwd_w, 0, 0, 0, H_getcwd, s7_make_signature(s7, 1, s));
  1389. Xen_define_typed_procedure("strftime", g_strftime_w, 2, 0, 0, H_strftime, s7_make_signature(s7, 3, s, s, p));
  1390. Xen_define_typed_procedure("tmpnam", g_tmpnam_w, 0, 0, 0, H_tmpnam, s7_make_signature(s7, 1, s));
  1391. Xen_define_typed_procedure("localtime", g_localtime_w, 1, 0, 0, H_localtime, s7_make_signature(s7, 2, p, i));
  1392. Xen_define_typed_procedure("current-time", g_current_time_w, 0, 0, 0, H_current_time, s7_make_signature(s7, 1, i));
  1393. Xen_define_typed_procedure("ftell", g_ftell_w, 1, 0, 0, "(ftell fd): lseek", s7_make_signature(s7, 2, i, i));
  1394. Xen_define_typed_procedure(S_gc_off, g_gc_off_w, 0, 0, 0, H_gc_off, s7_make_signature(s7, 1, b));
  1395. Xen_define_typed_procedure(S_gc_on, g_gc_on_w, 0, 0, 0, H_gc_on, s7_make_signature(s7, 1, b));
  1396. Xen_eval_C_string("(define (hook-push hook func) \n\
  1397. \"(hook-push hook func) adds func to hook's function list\" \n\
  1398. (if (not (member func (hook-functions hook) eq?)) (set! (hook-functions hook) (cons func (hook-functions hook)))))");
  1399. Xen_eval_C_string("(define (hook-append hook func) \n\
  1400. \"(hook-append hook func) adds func to the end of hook's function list\" \n\
  1401. (set! (hook-functions hook) (append (hook-functions hook) (list func))))");
  1402. Xen_eval_C_string("(define (hook-clear hook) (set! (hook-functions hook) ()))");
  1403. Xen_eval_C_string("(define (hook-remove hook func) \n\
  1404. (set! (hook-functions hook)\n\
  1405. (let loop ((l (hook-functions hook))\n\
  1406. (result ()))\n\
  1407. (cond ((null? l) (reverse! result))\n\
  1408. ((eq? func (car l)) (loop (cdr l) result))\n\
  1409. (else (loop (cdr l) (cons (car l) result)))))))");
  1410. #if (!DISABLE_DEPRECATED)
  1411. Xen_eval_C_string("(define load-from-path load)");
  1412. Xen_eval_C_string("(define (1+ x) \"add 1 to arg\" (+ x 1))");
  1413. Xen_eval_C_string("(define (1- x) \"subtract 1 from arg\" (- x 1))");
  1414. #endif
  1415. Xen_eval_C_string("(define-macro (while whether . body) `(do () ((not ,whether)) ,@body))");
  1416. Xen_eval_C_string("(define (identity x) \"return arg\" x)");
  1417. return(s7);
  1418. }
  1419. void xen_initialize(void)
  1420. {
  1421. s7_xen_initialize(NULL);
  1422. }
  1423. #endif
  1424. /* ------------------------------ NONE OF THE ABOVE ------------------------------ */
  1425. #if (!HAVE_EXTENSION_LANGUAGE)
  1426. char *xen_version(void)
  1427. {
  1428. char *buf;
  1429. buf = (char *)calloc(64, sizeof(char));
  1430. #if HAVE_SNPRINTF
  1431. snprintf(buf, 64, "no extension language");
  1432. #else
  1433. sprintf(buf, "no extension language");
  1434. #endif
  1435. return(buf);
  1436. }
  1437. void xen_repl(int argc, char **argv)
  1438. {
  1439. }
  1440. void xen_initialize(void)
  1441. {
  1442. }
  1443. void xen_gc_mark(Xen val)
  1444. {
  1445. }
  1446. void xen_no_ext_lang_check_args(const char *name, int args, int req_args, int opt_args, int rst_args)
  1447. {
  1448. if (args > 0) /* nargify -- all are required */
  1449. {
  1450. if (req_args != args)
  1451. fprintf(stderr, "%s: %d required args, but req: %d (opt: %d, rst: %d)\n", name, args, req_args, opt_args, rst_args);
  1452. if (opt_args != 0)
  1453. fprintf(stderr, "%s: all args required, but opt: %d (rst: %d)\n", name, opt_args, rst_args);
  1454. if (rst_args != 0)
  1455. fprintf(stderr, "%s: all args required, but rst: %d\n", name, rst_args);
  1456. }
  1457. else
  1458. {
  1459. if (args != -100) /* vargify -- any ok */
  1460. {
  1461. args = -args;
  1462. if (rst_args == 0)
  1463. {
  1464. if (req_args + opt_args != args)
  1465. fprintf(stderr, "%s: total args: %d, but req: %d and opt: %d\n", name, args, req_args, opt_args);
  1466. }
  1467. else
  1468. {
  1469. if (req_args + opt_args > args)
  1470. fprintf(stderr, "%s: has :rest, but req: %d and opt: %d , whereas total: %d\n", name, req_args, opt_args, args);
  1471. }
  1472. }
  1473. }
  1474. }
  1475. #endif