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
18 #include "my-ctype.h"
19 #include "my-stdio.h"
20 #include "my-string.h"
21
22 #include "config.h"
23 #include "db.h"
24 #include "db_io.h"
25 #include "exceptions.h"
26 #include "list.h"
27 #include "log.h"
28 #include "match.h"
29 #include "numbers.h"
30 #include "ref_count.h"
31 #include "server.h"
32 #include "storage.h"
33 #include "streams.h"
34 #include "structures.h"
35 #include "utils.h"
36
37 /*
38 * These versions of strcasecmp() and strncasecmp() depend on ASCII.
39 * We implement them here because neither one is in the ANSI standard.
40 */
41
42 static const char cmap[] =
43 "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
44 "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
45 "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
46 "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
47 "\100\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
48 "\160\161\162\163\164\165\166\167\170\171\172\133\134\135\136\137"
49 "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
50 "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
51 "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217"
52 "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237"
53 "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257"
54 "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
55 "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
56 "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
57 "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357"
58 "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377";
59
60 int
mystrcasecmp(const char * ss,const char * tt)61 mystrcasecmp(const char *ss, const char *tt)
62 {
63 const unsigned char *s = (const unsigned char *) ss;
64 const unsigned char *t = (const unsigned char *) tt;
65
66 if (ss == tt) {
67 return 0;
68 }
69 while (cmap[*s] == cmap[*t++]) {
70 if (!*s++)
71 return 0;
72 }
73 return (cmap[*s] - cmap[*--t]);
74 }
75
76 int
mystrncasecmp(const char * ss,const char * tt,int n)77 mystrncasecmp(const char *ss, const char *tt, int n)
78 {
79 const unsigned char *s = (const unsigned char *) ss;
80 const unsigned char *t = (const unsigned char *) tt;
81
82 if (!n || ss == tt)
83 return 0;
84 while (cmap[*s] == cmap[*t++]) {
85 if (!*s++ || !--n)
86 return 0;
87 }
88 return (cmap[*s] - cmap[*--t]);
89 }
90
91 int
verbcasecmp(const char * verb,const char * word)92 verbcasecmp(const char *verb, const char *word)
93 {
94 const unsigned char *w;
95 const unsigned char *v = (const unsigned char *) verb;
96 enum {
97 none, inner, end
98 } star;
99
100 if (verb == word) {
101 return 1;
102 }
103 while (*v) {
104 w = (const unsigned char *) word;
105 star = none;
106 while (1) {
107 while (*v == '*') {
108 v++;
109 star = (!*v || *v == ' ') ? end : inner;
110 }
111 if (!*v || *v == ' ' || !*w || cmap[*w] != cmap[*v])
112 break;
113 w++;
114 v++;
115 }
116 if (!*w ? (star != none || !*v || *v == ' ')
117 : (star == end))
118 return 1;
119 while (*v && *v != ' ')
120 v++;
121 while (*v == ' ')
122 v++;
123 }
124 return 0;
125 }
126
127 unsigned
str_hash(const char * s)128 str_hash(const char *s)
129 {
130 unsigned ans = 0;
131 int i, len = strlen(s), offset = 0;
132
133 for (i = 0; i < len; i++) {
134 ans = ans ^ (cmap[(unsigned char) s[i]] << offset++);
135 if (offset == 25)
136 offset = 0;
137 }
138 return ans;
139 }
140
141 void
complex_free_var(Var v)142 complex_free_var(Var v)
143 {
144 int i;
145
146 switch ((int) v.type) {
147 case TYPE_STR:
148 if (v.v.str)
149 free_str(v.v.str);
150 break;
151 case TYPE_LIST:
152 if (delref(v.v.list) == 0) {
153 Var *pv;
154
155 for (i = v.v.list[0].v.num, pv = v.v.list + 1; i > 0; i--, pv++)
156 free_var(*pv);
157 myfree(v.v.list, M_LIST);
158 }
159 break;
160 case TYPE_FLOAT:
161 if (delref(v.v.fnum) == 0)
162 myfree(v.v.fnum, M_FLOAT);
163 break;
164 }
165 }
166
167 Var
complex_var_ref(Var v)168 complex_var_ref(Var v)
169 {
170 switch ((int) v.type) {
171 case TYPE_STR:
172 addref(v.v.str);
173 break;
174 case TYPE_LIST:
175 addref(v.v.list);
176 break;
177 case TYPE_FLOAT:
178 addref(v.v.fnum);
179 break;
180 }
181 return v;
182 }
183
184 Var
complex_var_dup(Var v)185 complex_var_dup(Var v)
186 {
187 int i;
188 Var newlist;
189
190 switch ((int) v.type) {
191 case TYPE_STR:
192 v.v.str = str_dup(v.v.str);
193 break;
194 case TYPE_LIST:
195 newlist = new_list(v.v.list[0].v.num);
196 for (i = 1; i <= v.v.list[0].v.num; i++) {
197 newlist.v.list[i] = var_ref(v.v.list[i]);
198 }
199 v.v.list = newlist.v.list;
200 break;
201 case TYPE_FLOAT:
202 v = new_float(*v.v.fnum);
203 break;
204 }
205 return v;
206 }
207
208 /* could be inlined and use complex_etc like the others, but this should
209 * usually be called in a context where we already konw the type.
210 */
211 int
var_refcount(Var v)212 var_refcount(Var v)
213 {
214 switch ((int) v.type) {
215 case TYPE_STR:
216 return refcount(v.v.str);
217 break;
218 case TYPE_LIST:
219 return refcount(v.v.list);
220 break;
221 case TYPE_FLOAT:
222 return refcount(v.v.fnum);
223 break;
224 }
225 return 1;
226 }
227
228 int
is_true(Var v)229 is_true(Var v)
230 {
231 return ((v.type == TYPE_INT && v.v.num != 0)
232 || (v.type == TYPE_FLOAT && *v.v.fnum != 0.0)
233 || (v.type == TYPE_STR && v.v.str && *v.v.str != '\0')
234 || (v.type == TYPE_LIST && v.v.list[0].v.num != 0));
235 }
236
237 int
equality(Var lhs,Var rhs,int case_matters)238 equality(Var lhs, Var rhs, int case_matters)
239 {
240 if (lhs.type == TYPE_FLOAT || rhs.type == TYPE_FLOAT)
241 return do_equals(lhs, rhs);
242 else if (lhs.type != rhs.type)
243 return 0;
244 else {
245 switch (lhs.type) {
246 case TYPE_INT:
247 return lhs.v.num == rhs.v.num;
248 case TYPE_OBJ:
249 return lhs.v.obj == rhs.v.obj;
250 case TYPE_ERR:
251 return lhs.v.err == rhs.v.err;
252 case TYPE_STR:
253 if (case_matters)
254 return !strcmp(lhs.v.str, rhs.v.str);
255 else
256 return !mystrcasecmp(lhs.v.str, rhs.v.str);
257 case TYPE_LIST:
258 if (lhs.v.list[0].v.num != rhs.v.list[0].v.num)
259 return 0;
260 else {
261 int i;
262
263 if (lhs.v.list == rhs.v.list) {
264 return 1;
265 }
266 for (i = 1; i <= lhs.v.list[0].v.num; i++) {
267 if (!equality(lhs.v.list[i], rhs.v.list[i], case_matters))
268 return 0;
269 }
270 return 1;
271 }
272 default:
273 panic("EQUALITY: Unknown value type");
274 }
275 }
276 return 0;
277 }
278
279 char *
strsub(const char * source,const char * what,const char * with,int case_counts)280 strsub(const char *source, const char *what, const char *with, int case_counts)
281 {
282 static Stream *str = 0;
283 int lwhat = strlen(what);
284
285 if (str == 0)
286 str = new_stream(100);
287
288 while (*source) {
289 if (!(case_counts ? strncmp(source, what, lwhat)
290 : mystrncasecmp(source, what, lwhat))) {
291 stream_add_string(str, with);
292 source += lwhat;
293 } else
294 stream_add_char(str, *source++);
295 }
296
297 return reset_stream(str);
298 }
299
300 int
strindex(const char * source,const char * what,int case_counts)301 strindex(const char *source, const char *what, int case_counts)
302 {
303 const char *s, *e;
304 int lwhat = strlen(what);
305
306 for (s = source, e = source + strlen(source) - lwhat; s <= e; s++) {
307 if (!(case_counts ? strncmp(s, what, lwhat)
308 : mystrncasecmp(s, what, lwhat))) {
309 return s - source + 1;
310 }
311 }
312 return 0;
313 }
314
315 int
strrindex(const char * source,const char * what,int case_counts)316 strrindex(const char *source, const char *what, int case_counts)
317 {
318 const char *s;
319 int lwhat = strlen(what);
320
321 for (s = source + strlen(source) - lwhat; s >= source; s--) {
322 if (!(case_counts ? strncmp(s, what, lwhat)
323 : mystrncasecmp(s, what, lwhat))) {
324 return s - source + 1;
325 }
326 }
327 return 0;
328 }
329
330 Var
get_system_property(const char * name)331 get_system_property(const char *name)
332 {
333 Var value;
334 db_prop_handle h;
335
336 if (!valid(SYSTEM_OBJECT)) {
337 value.type = TYPE_ERR;
338 value.v.err = E_INVIND;
339 return value;
340 }
341 h = db_find_property(SYSTEM_OBJECT, name, &value);
342 if (!h.ptr) {
343 value.type = TYPE_ERR;
344 value.v.err = E_PROPNF;
345 } else if (!h.built_in) /* make two cases the same */
346 value = var_ref(value);
347 return value;
348 }
349
350 Objid
get_system_object(const char * name)351 get_system_object(const char *name)
352 {
353 Var value;
354
355 value = get_system_property(name);
356 if (value.type != TYPE_OBJ) {
357 free_var(value);
358 return NOTHING;
359 } else
360 return value.v.obj;
361 }
362
363 int
value_bytes(Var v)364 value_bytes(Var v)
365 {
366 int i, len, size = sizeof(Var);
367
368 switch (v.type) {
369 case TYPE_STR:
370 size += strlen(v.v.str) + 1;
371 break;
372 case TYPE_FLOAT:
373 size += sizeof(double);
374 break;
375 case TYPE_LIST:
376 len = v.v.list[0].v.num;
377 size += sizeof(Var); /* for the `length' element */
378 for (i = 1; i <= len; i++)
379 size += value_bytes(v.v.list[i]);
380 break;
381 default:
382 break;
383 }
384
385 return size;
386 }
387
388 const char *
raw_bytes_to_binary(const char * buffer,int buflen)389 raw_bytes_to_binary(const char *buffer, int buflen)
390 {
391 static Stream *s = 0;
392 int i;
393
394 if (!s)
395 s = new_stream(100);
396
397 for (i = 0; i < buflen; i++) {
398 unsigned char c = buffer[i];
399
400 if (c != '~' && (isgraph(c) || c == ' '))
401 stream_add_char(s, c);
402 else
403 stream_printf(s, "~%02x", (int) c);
404 }
405
406 return reset_stream(s);
407 }
408
409 const char *
binary_to_raw_bytes(const char * binary,int * buflen)410 binary_to_raw_bytes(const char *binary, int *buflen)
411 {
412 static Stream *s = 0;
413 const char *ptr = binary;
414
415 if (!s)
416 s = new_stream(100);
417 else
418 reset_stream(s);
419
420 while (*ptr) {
421 unsigned char c = *ptr++;
422
423 if (c != '~')
424 stream_add_char(s, c);
425 else {
426 int i;
427 char cc = 0;
428
429 for (i = 1; i <= 2; i++) {
430 c = toupper(*ptr++);
431 if (('0' <= c && c <= '9') || ('A' <= c && c <= 'F'))
432 cc = cc * 16 + (c <= '9' ? c - '0' : c - 'A' + 10);
433 else
434 return 0;
435 }
436
437 stream_add_char(s, cc);
438 }
439 }
440
441 *buflen = stream_length(s);
442 return reset_stream(s);
443 }
444
445 char rcsid_utils[] = "$Id: utils.c,v 1.5 1999/08/09 02:36:33 nop Exp $";
446
447 /*
448 * $Log: utils.c,v $
449 * Revision 1.5 1999/08/09 02:36:33 nop
450 * Shortcut various equality tests if we have pointer equality.
451 *
452 * Revision 1.4 1998/12/14 13:19:14 nop
453 * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
454 *
455 * Revision 1.3 1997/07/07 03:24:55 nop
456 * Merge UNSAFE_OPTS (r5) after extensive testing.
457 *
458 * Revision 1.2.2.3 1997/03/21 15:11:22 bjj
459 * add var_refcount interface
460 *
461 * Revision 1.2.2.2 1997/03/21 14:29:03 bjj
462 * Some code bumming in complex_free_var (3rd most expensive function!)
463 *
464 * Revision 1.2.2.1 1997/03/20 18:07:48 bjj
465 * Add a flag to the in-memory type identifier so that inlines can cheaply
466 * identify Vars that need actual work done to ref/free/dup them. Add the
467 * appropriate inlines to utils.h and replace old functions in utils.c with
468 * complex_* functions which only handle the types with external storage.
469 *
470 * Revision 1.2 1997/03/03 04:19:36 nop
471 * GNU Indent normalization
472 *
473 * Revision 1.1.1.1 1997/03/03 03:45:01 nop
474 * LambdaMOO 1.8.0p5
475 *
476 * Revision 2.8 1996/04/08 00:43:09 pavel
477 * Changed definition of `value_bytes()' to add in `sizeof(Var)'.
478 * Release 1.8.0p3.
479 *
480 * Revision 2.7 1996/03/11 23:34:41 pavel
481 * Changed %X to %x in a stream_printf call, since I don't want to support
482 * both upper- and lower-case. Release 1.8.0p1.
483 *
484 * Revision 2.6 1996/03/10 01:14:16 pavel
485 * Change the format of binary strings to use hex instead of octal.
486 * Release 1.8.0.
487 *
488 * Revision 2.5 1996/02/08 06:41:04 pavel
489 * Added support for floating-point. Moved compare_ints() to numbers.c.
490 * Renamed err/logf() to errlog/oklog() and TYPE_NUM to TYPE_INT. Updated
491 * copyright notice for 1996. Release 1.8.0beta1.
492 *
493 * Revision 2.4 1996/01/16 07:24:48 pavel
494 * Removed special format for `~' in binary strings. Release 1.8.0alpha6.
495 *
496 * Revision 2.3 1996/01/11 07:40:01 pavel
497 * Added raw_bytes_to_binary() and binary_to_raw_bytes(), in support of binary
498 * I/O facilities. Release 1.8.0alpha5.
499 *
500 * Revision 2.2 1995/12/28 00:38:54 pavel
501 * Neatened up implementation of case-folding string comparison functions.
502 * Release 1.8.0alpha3.
503 *
504 * Revision 2.1 1995/12/11 08:09:31 pavel
505 * Account for newly-clarified reference-counting behavior for built-in
506 * properties. Add `value_bytes()' from elsewhere. Release 1.8.0alpha2.
507 *
508 * Revision 2.0 1995/11/30 04:43:24 pavel
509 * New baseline version, corresponding to release 1.8.0alpha1.
510 *
511 * Revision 1.13 1992/10/23 23:03:47 pavel
512 * Added copyright notice.
513 *
514 * Revision 1.12 1992/10/23 22:23:18 pavel
515 * Eliminated all uses of the useless macro NULL.
516 *
517 * Revision 1.11 1992/10/17 20:57:08 pavel
518 * Global rename of strdup->str_dup, strref->str_ref, vardup->var_dup, and
519 * varref->var_ref.
520 * Removed useless #ifdef of STRCASE.
521 *
522 * Revision 1.10 1992/09/14 17:39:53 pjames
523 * Moved db_modification code to db modules.
524 *
525 * Revision 1.9 1992/09/08 21:55:47 pjames
526 * Updated #includes.
527 *
528 * Revision 1.8 1992/09/03 16:23:49 pjames
529 * Make cmap[] visible. Added TYPE_CLEAR handling.
530 *
531 * Revision 1.7 1992/08/28 16:23:41 pjames
532 * Changed vardup to varref.
533 * Changed myfree(*, M_STRING) to free_str(*).
534 * Added `varref()'.
535 * Changed `free_var()' to check `delref()' before freeing.
536 * Removed `copy_pi()'.
537 *
538 * Revision 1.6 1992/08/18 00:54:40 pavel
539 * Fixed typo.
540 *
541 * Revision 1.5 1992/08/18 00:41:14 pavel
542 * Fixed boundary-condition bugs in index() and rindex(), when the search string
543 * is empty.
544 *
545 * Revision 1.4 1992/08/11 17:26:56 pjames
546 * Removed read/write Parse_Info procedures.
547 *
548 * Revision 1.3 1992/08/10 16:40:20 pjames
549 * Added is_true (from execute.c) and *_activ_as_pi routines. Added a
550 * check for null parse_info in write_pi, because parse_infos are no
551 * longer stored in each activation.
552 *
553 * Revision 1.2 1992/07/21 00:07:48 pavel
554 * Added rcsid_<filename-root> declaration to hold the RCS ident. string.
555 *
556 * Revision 1.1 1992/07/20 23:23:12 pavel
557 * Initial RCS-controlled version.
558 */
559