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