PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/verbs.c

https://gitlab.com/mudmoo/gammamoo
C | 693 lines | 505 code | 71 blank | 117 comment | 145 complexity | 3304810a30f158cacaa0d6ccde80031d MD5 | raw file
  1. /******************************************************************************
  2. Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
  3. Portions of this code were written by Stephen White, aka ghond.
  4. Use and copying of this software and preparation of derivative works based
  5. upon this software are permitted. Any distribution of this software or
  6. derivative works must comply with all applicable United States export
  7. control laws. This software is made available AS IS, and Xerox Corporation
  8. makes no warranty about the software, its performance or its conformity to
  9. any specification. Any person obtaining a copy of this software is requested
  10. to send their name and post office or electronic mail address to:
  11. Pavel Curtis
  12. Xerox PARC
  13. 3333 Coyote Hill Rd.
  14. Palo Alto, CA 94304
  15. Pavel@Xerox.Com
  16. *****************************************************************************/
  17. #include "my-string.h"
  18. #include "config.h"
  19. #include "db.h"
  20. #include "exceptions.h"
  21. #include "execute.h"
  22. #include "functions.h"
  23. #include "list.h"
  24. #include "log.h"
  25. #include "match.h"
  26. #include "parse_cmd.h"
  27. #include "parser.h"
  28. #include "server.h"
  29. #include "storage.h"
  30. #include "unparse.h"
  31. #include "utils.h"
  32. #include "verbs.h"
  33. struct verb_data {
  34. Var r;
  35. int i;
  36. };
  37. static int
  38. add_to_list(void *data, const char *verb_name)
  39. {
  40. struct verb_data *d = data;
  41. d->i++;
  42. d->r.v.list[d->i].type = TYPE_STR;
  43. d->r.v.list[d->i].v.str = str_ref(verb_name);
  44. return 0;
  45. }
  46. static package
  47. bf_verbs(Var arglist, Byte next, void *vdata, Objid progr)
  48. { /* (object) */
  49. Objid oid = arglist.v.list[1].v.obj;
  50. free_var(arglist);
  51. if (!valid(oid))
  52. return make_error_pack(E_INVARG);
  53. else if (!db_object_allows(oid, progr, FLAG_READ))
  54. return make_error_pack(E_PERM);
  55. else {
  56. struct verb_data d;
  57. d.r = new_list(db_count_verbs(oid));
  58. d.i = 0;
  59. db_for_all_verbs(oid, add_to_list, &d);
  60. return make_var_pack(d.r);
  61. }
  62. }
  63. static enum error
  64. validate_verb_info(Var v, Objid * owner, unsigned *flags, const char **names)
  65. {
  66. const char *s;
  67. if (!(v.type == TYPE_LIST
  68. && v.v.list[0].v.num == 3
  69. && v.v.list[1].type == TYPE_OBJ
  70. && v.v.list[2].type == TYPE_STR
  71. && v.v.list[3].type == TYPE_STR))
  72. return E_TYPE;
  73. *owner = v.v.list[1].v.obj;
  74. if (!valid(*owner))
  75. return E_INVARG;
  76. for (*flags = 0, s = v.v.list[2].v.str; *s; s++) {
  77. switch (*s) {
  78. case 'r':
  79. case 'R':
  80. *flags |= VF_READ;
  81. break;
  82. case 'w':
  83. case 'W':
  84. *flags |= VF_WRITE;
  85. break;
  86. case 'x':
  87. case 'X':
  88. *flags |= VF_EXEC;
  89. break;
  90. case 'd':
  91. case 'D':
  92. *flags |= VF_DEBUG;
  93. break;
  94. default:
  95. return E_INVARG;
  96. }
  97. }
  98. *names = v.v.list[3].v.str;
  99. while (**names == ' ')
  100. (*names)++;
  101. if (**names == '\0')
  102. return E_INVARG;
  103. *names = str_dup(*names);
  104. return E_NONE;
  105. }
  106. static int
  107. match_arg_spec(const char *s, db_arg_spec * spec)
  108. {
  109. if (!mystrcasecmp(s, "none")) {
  110. *spec = ASPEC_NONE;
  111. return 1;
  112. } else if (!mystrcasecmp(s, "any")) {
  113. *spec = ASPEC_ANY;
  114. return 1;
  115. } else if (!mystrcasecmp(s, "this")) {
  116. *spec = ASPEC_THIS;
  117. return 1;
  118. } else
  119. return 0;
  120. }
  121. static int
  122. match_prep_spec(const char *s, db_prep_spec * spec)
  123. {
  124. if (!mystrcasecmp(s, "none")) {
  125. *spec = PREP_NONE;
  126. return 1;
  127. } else if (!mystrcasecmp(s, "any")) {
  128. *spec = PREP_ANY;
  129. return 1;
  130. } else
  131. return (*spec = db_match_prep(s)) != PREP_NONE;
  132. }
  133. static enum error
  134. validate_verb_args(Var v, db_arg_spec * dobj, db_prep_spec * prep,
  135. db_arg_spec * iobj)
  136. {
  137. if (!(v.type == TYPE_LIST
  138. && v.v.list[0].v.num == 3
  139. && v.v.list[1].type == TYPE_STR
  140. && v.v.list[2].type == TYPE_STR
  141. && v.v.list[3].type == TYPE_STR))
  142. return E_TYPE;
  143. if (!match_arg_spec(v.v.list[1].v.str, dobj)
  144. || !match_prep_spec(v.v.list[2].v.str, prep)
  145. || !match_arg_spec(v.v.list[3].v.str, iobj))
  146. return E_INVARG;
  147. return E_NONE;
  148. }
  149. static package
  150. bf_add_verb(Var arglist, Byte next, void *vdata, Objid progr)
  151. { /* (object, info, args) */
  152. Objid oid = arglist.v.list[1].v.obj;
  153. Var info = arglist.v.list[2];
  154. Var args = arglist.v.list[3];
  155. Var result;
  156. Objid owner;
  157. unsigned flags;
  158. const char *names;
  159. db_arg_spec dobj, iobj;
  160. db_prep_spec prep;
  161. enum error e;
  162. if ((e = validate_verb_info(info, &owner, &flags, &names)) != E_NONE)
  163. /* Already failed */ ;
  164. else if ((e = validate_verb_args(args, &dobj, &prep, &iobj)) != E_NONE)
  165. free_str(names);
  166. else if (!valid(oid)) {
  167. free_str(names);
  168. e = E_INVARG;
  169. } else if (!db_object_allows(oid, progr, FLAG_WRITE)
  170. || (progr != owner && !is_wizard(progr))) {
  171. free_str(names);
  172. e = E_PERM;
  173. } else {
  174. result.type = TYPE_INT;
  175. result.v.num = db_add_verb(oid, names, owner, flags, dobj, prep, iobj);
  176. }
  177. free_var(arglist);
  178. if (e == E_NONE)
  179. return make_var_pack(result);
  180. else
  181. return make_error_pack(e);
  182. }
  183. enum error
  184. validate_verb_descriptor(Var desc)
  185. {
  186. if (desc.type == TYPE_STR)
  187. return E_NONE;
  188. else if (desc.type != TYPE_INT)
  189. return E_TYPE;
  190. else if (desc.v.num <= 0)
  191. return E_INVARG;
  192. else
  193. return E_NONE;
  194. }
  195. db_verb_handle
  196. find_described_verb(Objid oid, Var desc)
  197. {
  198. if (desc.type == TYPE_INT)
  199. return db_find_indexed_verb(oid, desc.v.num);
  200. else {
  201. int flag = server_flag_option("support_numeric_verbname_strings", 0);
  202. return db_find_defined_verb(oid, desc.v.str, flag);
  203. }
  204. }
  205. static package
  206. bf_delete_verb(Var arglist, Byte next, void *vdata, Objid progr)
  207. { /* (object, verb-desc) */
  208. Objid oid = arglist.v.list[1].v.obj;
  209. Var desc = arglist.v.list[2];
  210. db_verb_handle h;
  211. enum error e;
  212. if ((e = validate_verb_descriptor(desc)) != E_NONE); /* Do nothing; e is already set. */
  213. else if (!valid(oid))
  214. e = E_INVARG;
  215. else if (!db_object_allows(oid, progr, FLAG_WRITE))
  216. e = E_PERM;
  217. else {
  218. h = find_described_verb(oid, desc);
  219. if (h.ptr)
  220. db_delete_verb(h);
  221. else
  222. e = E_VERBNF;
  223. }
  224. free_var(arglist);
  225. if (e == E_NONE)
  226. return no_var_pack();
  227. else
  228. return make_error_pack(e);
  229. }
  230. static package
  231. bf_verb_info(Var arglist, Byte next, void *vdata, Objid progr)
  232. { /* (object, verb-desc) */
  233. Objid oid = arglist.v.list[1].v.obj;
  234. Var desc = arglist.v.list[2];
  235. db_verb_handle h;
  236. Var r;
  237. unsigned flags;
  238. char perms[5], *s;
  239. enum error e;
  240. if ((e = validate_verb_descriptor(desc)) != E_NONE
  241. || (e = E_INVARG, !valid(oid))) {
  242. free_var(arglist);
  243. return make_error_pack(e);
  244. }
  245. h = find_described_verb(oid, desc);
  246. free_var(arglist);
  247. if (!h.ptr)
  248. return make_error_pack(E_VERBNF);
  249. else if (!db_verb_allows(h, progr, VF_READ))
  250. return make_error_pack(E_PERM);
  251. h = db_dup_verb_handle(h);
  252. r = new_list(3);
  253. r.v.list[1].type = TYPE_OBJ;
  254. r.v.list[1].v.obj = db_verb_owner(h);
  255. r.v.list[2].type = TYPE_STR;
  256. s = perms;
  257. flags = db_verb_flags(h);
  258. if (flags & VF_READ)
  259. *s++ = 'r';
  260. if (flags & VF_WRITE)
  261. *s++ = 'w';
  262. if (flags & VF_EXEC)
  263. *s++ = 'x';
  264. if (flags & VF_DEBUG)
  265. *s++ = 'd';
  266. *s = '\0';
  267. r.v.list[2].v.str = str_dup(perms);
  268. r.v.list[3].type = TYPE_STR;
  269. r.v.list[3].v.str = str_ref(db_verb_names(h));
  270. db_free_verb_handle(h);
  271. return make_var_pack(r);
  272. }
  273. static package
  274. bf_set_verb_info(Var arglist, Byte next, void *vdata, Objid progr)
  275. { /* (object, verb-desc, {owner, flags, names}) */
  276. Objid oid = arglist.v.list[1].v.obj;
  277. Var desc = arglist.v.list[2];
  278. Var info = arglist.v.list[3];
  279. Objid new_owner;
  280. unsigned new_flags;
  281. const char *new_names;
  282. enum error e;
  283. db_verb_handle h;
  284. if ((e = validate_verb_descriptor(desc)) != E_NONE); /* Do nothing; e is already set. */
  285. else if (!valid(oid))
  286. e = E_INVARG;
  287. else
  288. e = validate_verb_info(info, &new_owner, &new_flags, &new_names);
  289. if (e != E_NONE) {
  290. free_var(arglist);
  291. return make_error_pack(e);
  292. }
  293. h = find_described_verb(oid, desc);
  294. free_var(arglist);
  295. h = db_dup_verb_handle(h);
  296. if (!h.ptr) {
  297. free_str(new_names);
  298. return make_error_pack(E_VERBNF);
  299. } else if (!db_verb_allows(h, progr, VF_WRITE)
  300. || (!is_wizard(progr) && db_verb_owner(h) != new_owner)) {
  301. db_free_verb_handle(h);
  302. free_str(new_names);
  303. return make_error_pack(E_PERM);
  304. }
  305. db_set_verb_owner(h, new_owner);
  306. db_set_verb_flags(h, new_flags);
  307. db_set_verb_names(h, new_names);
  308. db_free_verb_handle(h);
  309. return no_var_pack();
  310. }
  311. static const char *
  312. unparse_arg_spec(db_arg_spec spec)
  313. {
  314. switch (spec) {
  315. case ASPEC_NONE:
  316. return str_dup("none");
  317. case ASPEC_ANY:
  318. return str_dup("any");
  319. case ASPEC_THIS:
  320. return str_dup("this");
  321. default:
  322. panic("UNPARSE_ARG_SPEC: Unknown db_arg_spec!");
  323. return "";
  324. }
  325. }
  326. static package
  327. bf_verb_args(Var arglist, Byte next, void *vdata, Objid progr)
  328. { /* (object, verb-desc) */
  329. Objid oid = arglist.v.list[1].v.obj;
  330. Var desc = arglist.v.list[2];
  331. db_verb_handle h;
  332. db_arg_spec dobj, iobj;
  333. db_prep_spec prep;
  334. Var r;
  335. enum error e;
  336. if ((e = validate_verb_descriptor(desc)) != E_NONE
  337. || (e = E_INVARG, !valid(oid))) {
  338. free_var(arglist);
  339. return make_error_pack(e);
  340. }
  341. h = find_described_verb(oid, desc);
  342. free_var(arglist);
  343. if (!h.ptr)
  344. return make_error_pack(E_VERBNF);
  345. else if (!db_verb_allows(h, progr, VF_READ))
  346. return make_error_pack(E_PERM);
  347. db_verb_arg_specs(h, &dobj, &prep, &iobj);
  348. r = new_list(3);
  349. r.v.list[1].type = TYPE_STR;
  350. r.v.list[1].v.str = unparse_arg_spec(dobj);
  351. r.v.list[2].type = TYPE_STR;
  352. r.v.list[2].v.str = str_dup(db_unparse_prep(prep));
  353. r.v.list[3].type = TYPE_STR;
  354. r.v.list[3].v.str = unparse_arg_spec(iobj);
  355. return make_var_pack(r);
  356. }
  357. static package
  358. bf_set_verb_args(Var arglist, Byte next, void *vdata, Objid progr)
  359. { /* (object, verb-desc, {dobj, prep, iobj}) */
  360. Objid oid = arglist.v.list[1].v.obj;
  361. Var desc = arglist.v.list[2];
  362. Var args = arglist.v.list[3];
  363. enum error e;
  364. db_verb_handle h;
  365. db_arg_spec dobj, iobj;
  366. db_prep_spec prep;
  367. if ((e = validate_verb_descriptor(desc)) != E_NONE); /* Do nothing; e is already set. */
  368. else if (!valid(oid))
  369. e = E_INVARG;
  370. else
  371. e = validate_verb_args(args, &dobj, &prep, &iobj);
  372. if (e != E_NONE) {
  373. free_var(arglist);
  374. return make_error_pack(e);
  375. }
  376. h = find_described_verb(oid, desc);
  377. free_var(arglist);
  378. if (!h.ptr)
  379. return make_error_pack(E_VERBNF);
  380. else if (!db_verb_allows(h, progr, VF_WRITE))
  381. return make_error_pack(E_PERM);
  382. db_set_verb_arg_specs(h, dobj, prep, iobj);
  383. return no_var_pack();
  384. }
  385. static void
  386. lister(void *data, const char *line)
  387. {
  388. Var *r = (Var *) data;
  389. Var v;
  390. v.type = TYPE_STR;
  391. v.v.str = str_dup(line);
  392. *r = listappend(*r, v);
  393. }
  394. static package
  395. bf_verb_code(Var arglist, Byte next, void *vdata, Objid progr)
  396. { /* (object, verb-desc [, fully-paren [, indent]]) */
  397. int nargs = arglist.v.list[0].v.num;
  398. Objid oid = arglist.v.list[1].v.obj;
  399. Var desc = arglist.v.list[2];
  400. int parens = nargs >= 3 && is_true(arglist.v.list[3]);
  401. int indent = nargs < 4 || is_true(arglist.v.list[4]);
  402. db_verb_handle h;
  403. Var code;
  404. enum error e;
  405. if ((e = validate_verb_descriptor(desc)) != E_NONE
  406. || (e = E_INVARG, !valid(oid))) {
  407. free_var(arglist);
  408. return make_error_pack(e);
  409. }
  410. h = find_described_verb(oid, desc);
  411. free_var(arglist);
  412. if (!h.ptr)
  413. return make_error_pack(E_VERBNF);
  414. else if (!db_verb_allows(h, progr, VF_READ))
  415. return make_error_pack(E_PERM);
  416. code = new_list(0);
  417. unparse_program(db_verb_program(h), lister, &code, parens, indent,
  418. MAIN_VECTOR);
  419. return make_var_pack(code);
  420. }
  421. static package
  422. bf_set_verb_code(Var arglist, Byte next, void *vdata, Objid progr)
  423. { /* (object, verb-desc, code) */
  424. Objid oid = arglist.v.list[1].v.obj;
  425. Var desc = arglist.v.list[2];
  426. Var code = arglist.v.list[3];
  427. int i;
  428. Program *program;
  429. db_verb_handle h;
  430. Var errors;
  431. enum error e;
  432. for (i = 1; i <= code.v.list[0].v.num; i++)
  433. if (code.v.list[i].type != TYPE_STR) {
  434. free_var(arglist);
  435. return make_error_pack(E_TYPE);
  436. }
  437. if ((e = validate_verb_descriptor(desc)) != E_NONE
  438. || (e = E_INVARG, !valid(oid))) {
  439. free_var(arglist);
  440. return make_error_pack(e);
  441. }
  442. h = find_described_verb(oid, desc);
  443. h = db_dup_verb_handle(h);
  444. if (!h.ptr) {
  445. free_var(arglist);
  446. return make_error_pack(E_VERBNF);
  447. } else if (!is_programmer(progr) || !db_verb_allows(h, progr, VF_WRITE)) {
  448. db_free_verb_handle(h);
  449. free_var(arglist);
  450. return make_error_pack(E_PERM);
  451. }
  452. program = parse_list_as_program(code, &errors);
  453. if (program) {
  454. if (task_timed_out)
  455. free_program(program);
  456. else
  457. db_set_verb_program(h, program);
  458. }
  459. db_free_verb_handle(h);
  460. free_var(arglist);
  461. return make_var_pack(errors);
  462. }
  463. static package
  464. bf_eval(Var arglist, Byte next, void *data, Objid progr)
  465. {
  466. package p;
  467. if (next == 1) {
  468. if (!is_programmer(progr)) {
  469. free_var(arglist);
  470. p = make_error_pack(E_PERM);
  471. } else {
  472. Var errors;
  473. Program *program = parse_list_as_program(arglist, &errors);
  474. free_var(arglist);
  475. if (program) {
  476. free_var(errors);
  477. if (setup_activ_for_eval(program))
  478. p = make_call_pack(2, 0);
  479. else {
  480. free_program(program);
  481. p = make_error_pack(E_MAXREC);
  482. }
  483. } else {
  484. Var r;
  485. r = new_list(2);
  486. r.v.list[1].type = TYPE_INT;
  487. r.v.list[1].v.num = 0;
  488. r.v.list[2] = errors;
  489. p = make_var_pack(r);
  490. }
  491. }
  492. } else { /* next == 2 */
  493. Var r;
  494. r = new_list(2);
  495. r.v.list[1].type = TYPE_INT;
  496. r.v.list[1].v.num = 1;
  497. r.v.list[2] = arglist;
  498. p = make_var_pack(r);
  499. }
  500. return p;
  501. }
  502. void
  503. register_verbs(void)
  504. {
  505. register_function("verbs", 1, 1, bf_verbs, TYPE_OBJ);
  506. register_function("verb_info", 2, 2, bf_verb_info, TYPE_OBJ, TYPE_ANY);
  507. register_function("set_verb_info", 3, 3, bf_set_verb_info,
  508. TYPE_OBJ, TYPE_ANY, TYPE_LIST);
  509. register_function("verb_args", 2, 2, bf_verb_args, TYPE_OBJ, TYPE_ANY);
  510. register_function("set_verb_args", 3, 3, bf_set_verb_args,
  511. TYPE_OBJ, TYPE_ANY, TYPE_LIST);
  512. register_function("add_verb", 3, 3, bf_add_verb,
  513. TYPE_OBJ, TYPE_LIST, TYPE_LIST);
  514. register_function("delete_verb", 2, 2, bf_delete_verb, TYPE_OBJ, TYPE_ANY);
  515. register_function("verb_code", 2, 4, bf_verb_code,
  516. TYPE_OBJ, TYPE_ANY, TYPE_ANY, TYPE_ANY);
  517. register_function("set_verb_code", 3, 3, bf_set_verb_code,
  518. TYPE_OBJ, TYPE_ANY, TYPE_LIST);
  519. register_function("eval", 1, 1, bf_eval, TYPE_STR);
  520. }
  521. char rcsid_verbs[] = "$Id$";
  522. /*
  523. * $Log$
  524. * Revision 1.4 2001/01/29 08:38:44 bjj
  525. * Fix Sourceforge Bug #127620: add_verb() should return verbindex
  526. * And now it does. Old servers always returned 0, new servers will always
  527. * return a positive integer.
  528. *
  529. * Revision 1.3 1998/12/14 13:19:16 nop
  530. * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
  531. *
  532. * Revision 1.2 1997/03/03 04:19:37 nop
  533. * GNU Indent normalization
  534. *
  535. * Revision 1.1.1.1 1997/03/03 03:45:01 nop
  536. * LambdaMOO 1.8.0p5
  537. *
  538. * Revision 2.8 1996/05/12 21:29:46 pavel
  539. * Fixed memory leak for verb names string in bf_add_verb. Release 1.8.0p5.
  540. *
  541. * Revision 2.7 1996/03/19 07:19:13 pavel
  542. * Fixed off-by-one bug in type-checking in set_verb_code(). Release 1.8.0p2.
  543. *
  544. * Revision 2.6 1996/02/08 06:39:44 pavel
  545. * Renamed TYPE_NUM to TYPE_INT. Updated copyright notice for 1996.
  546. * Release 1.8.0beta1.
  547. *
  548. * Revision 2.5 1996/01/16 07:29:04 pavel
  549. * Fixed `parens' and `indent' arguments to verb_code() to allow values of any
  550. * type. Release 1.8.0alpha6.
  551. *
  552. * Revision 2.4 1996/01/11 07:51:42 pavel
  553. * Fixed bad free() in bf_add_verb(). Release 1.8.0alpha5.
  554. *
  555. * Revision 2.3 1995/12/31 03:28:27 pavel
  556. * Fixed C syntax botch in bf_delete_verb(). Release 1.8.0alpha4.
  557. *
  558. * Revision 2.2 1995/12/28 00:25:40 pavel
  559. * Added support for using numbers to designate defined verbs in built-in
  560. * functions. Release 1.8.0alpha3.
  561. *
  562. * Revision 2.1 1995/12/11 07:53:59 pavel
  563. * Accounted for verb programs never being NULL any more.
  564. *
  565. * Release 1.8.0alpha2.
  566. *
  567. * Revision 2.0 1995/11/30 04:43:36 pavel
  568. * New baseline version, corresponding to release 1.8.0alpha1.
  569. *
  570. * Revision 1.16 1992/10/23 23:03:47 pavel
  571. * Added copyright notice.
  572. *
  573. * Revision 1.15 1992/10/23 22:23:43 pavel
  574. * Eliminated all uses of the useless macro NULL.
  575. *
  576. * Revision 1.14 1992/10/21 03:02:35 pavel
  577. * Converted to use new automatic configuration system.
  578. *
  579. * Revision 1.13 1992/10/17 20:58:25 pavel
  580. * Global rename of strdup->str_dup, strref->str_ref, vardup->var_dup, and
  581. * varref->var_ref.
  582. *
  583. * Revision 1.12 1992/10/06 18:25:57 pavel
  584. * Changed name of global Parser_Client to avoid a name clash.
  585. *
  586. * Revision 1.11 1992/09/24 16:44:38 pavel
  587. * Made the parsing done by eval() and set_verb_code() abort if the task runs
  588. * out of seconds.
  589. *
  590. * Revision 1.10 1992/09/21 17:38:05 pavel
  591. * Restored RCS log information.
  592. *
  593. * Revision 1.9 1992/09/14 18:41:15 pjames
  594. * Moved rcsid to bottom.
  595. * Changed bf_eval to avoid a compiler warning when compiled un-optimized.
  596. *
  597. * Revision 1.8 1992/09/14 17:31:23 pjames
  598. * Moved db_modification code to db modules.
  599. *
  600. * Revision 1.7 1992/09/08 22:00:18 pjames
  601. * Renambed bf_verb.c to verbs.c
  602. *
  603. * Revision 1.6 1992/08/31 22:29:24 pjames
  604. * Changed some `char *'s to `const char *'
  605. *
  606. * Revision 1.5 1992/08/28 16:16:14 pjames
  607. * Changed myfree(*, M_STRING) to free_str(*).
  608. * Changed some strref's to strdup.
  609. *
  610. * Revision 1.4 1992/08/21 00:42:59 pavel
  611. * Renamed include file "parse_command.h" to "parse_cmd.h".
  612. *
  613. * Revision 1.3 1992/08/10 17:21:32 pjames
  614. * Updated #includes. Updated to use new registration format. Built in
  615. * functions now only receive programmer, instead of entire Parse_Info.
  616. *
  617. * Revision 1.2 1992/07/20 23:55:24 pavel
  618. * Added rcsid_<filename-root> declaration to hold the RCS ident. string.
  619. *
  620. * Revision 1.1 1992/07/20 23:23:12 pavel
  621. * Initial RCS-controlled version.
  622. */