1 /******************************************************************************
2   Copyright (c) 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 /*****************************************************************************
19  * Routines for use by non-DB modules with persistent state stored in the DB
20  *****************************************************************************/
21 
22 #include "my-ctype.h"
23 #include <float.h>
24 #include "my-stdarg.h"
25 #include "my-stdio.h"
26 #include "my-stdlib.h"
27 
28 #include "db_io.h"
29 #include "db_private.h"
30 #include "exceptions.h"
31 #include "list.h"
32 #include "log.h"
33 #include "numbers.h"
34 #include "parser.h"
35 #include "storage.h"
36 #include "streams.h"
37 #include "structures.h"
38 #include "str_intern.h"
39 #include "unparse.h"
40 #include "version.h"
41 
42 
43 /*********** Input ***********/
44 
45 static FILE *input;
46 
47 void
dbpriv_set_dbio_input(FILE * f)48 dbpriv_set_dbio_input(FILE * f)
49 {
50     input = f;
51 }
52 
53 void
dbio_read_line(char * s,int n)54 dbio_read_line(char *s, int n)
55 {
56     fgets(s, n, input);
57 }
58 
59 int
dbio_scanf(const char * format,...)60 dbio_scanf(const char *format,...)
61 {
62     va_list args;
63     int count;
64     const char *ptr;
65 
66     va_start(args, format);
67     /* The following line would be nice, but unfortunately those darlings on
68      * the ANSI C committee apparently didn't feel it worthwhile to include
69      * support for functions wrapping `scanf' *even though* they included
70      * symmetric such support for functions wrapping `printf'.  (*sigh*)
71      * Fortunately, we only use a small fraction of the full functionality of
72      * scanf in the server, so it's not unbearably unpleasant to have to
73      * reimplement it here.
74      */
75     /*  count = vfscanf(input, format, args);  */
76 
77     count = 0;
78     for (ptr = format; *ptr; ptr++) {
79 	int c, n, *ip;
80 	unsigned *up;
81 	char *cp;
82 
83 	if (isspace(*ptr)) {
84 	    do
85 		c = fgetc(input);
86 	    while (isspace(c));
87 	    ungetc(c, input);
88 	} else if (*ptr != '%') {
89 	    do
90 		c = fgetc(input);
91 	    while (isspace(c));
92 
93 	    if (c == EOF)
94 		return count ? count : EOF;
95 	    else if (c != *ptr) {
96 		ungetc(c, input);
97 		return count;
98 	    }
99 	} else
100 	    switch (*++ptr) {
101 	    case 'd':
102 		ip = va_arg(args, int *);
103 		n = fscanf(input, "%d", ip);
104 		goto finish;
105 	    case 'u':
106 		up = va_arg(args, unsigned *);
107 		n = fscanf(input, "%u", up);
108 		goto finish;
109 	    case 'c':
110 		cp = va_arg(args, char *);
111 		n = fscanf(input, "%c", cp);
112 	      finish:
113 		if (n == 1)
114 		    count++;
115 		else if (n == 0)
116 		    return count;
117 		else		/* n == EOF */
118 		    return count ? count : EOF;
119 		break;
120 	    default:
121 		panic("DBIO_SCANF: Unsupported directive!");
122 	    }
123     }
124 
125     va_end(args);
126 
127     return count;
128 }
129 
130 int
dbio_read_num(void)131 dbio_read_num(void)
132 {
133     char s[20];
134     char *p;
135     int i;
136 
137     fgets(s, 20, input);
138     i = strtol(s, &p, 10);
139     if (isspace(*s) || *p != '\n')
140 	errlog("DBIO_READ_NUM: Bad number: \"%s\" at file pos. %ld\n",
141 	       s, ftell(input));
142     return i;
143 }
144 
145 double
dbio_read_float(void)146 dbio_read_float(void)
147 {
148     char s[40];
149     char *p;
150     double d;
151 
152     fgets(s, 40, input);
153     d = strtod(s, &p);
154     if (isspace(*s) || *p != '\n')
155 	errlog("DBIO_READ_FLOAT: Bad number: \"%s\" at file pos. %ld\n",
156 	       s, ftell(input));
157     return d;
158 }
159 
160 Objid
dbio_read_objid(void)161 dbio_read_objid(void)
162 {
163     return dbio_read_num();
164 }
165 
166 const char *
dbio_read_string(void)167 dbio_read_string(void)
168 {
169     static Stream *str = 0;
170     static char buffer[1024];
171     int len, used_stream = 0;
172 
173     if (str == 0)
174 	str = new_stream(1024);
175 
176   try_again:
177     fgets(buffer, sizeof(buffer), input);
178     len = strlen(buffer);
179     if (len == sizeof(buffer) - 1 && buffer[len - 1] != '\n') {
180 	stream_add_string(str, buffer);
181 	used_stream = 1;
182 	goto try_again;
183     }
184     if (buffer[len - 1] == '\n')
185 	buffer[len - 1] = '\0';
186 
187     if (used_stream) {
188 	stream_add_string(str, buffer);
189 	return reset_stream(str);
190     } else
191 	return buffer;
192 }
193 
194 const char *
dbio_read_string_intern(void)195 dbio_read_string_intern(void)
196 {
197     const char *s, *r;
198 
199     s = dbio_read_string();
200     r = str_intern(s);
201 
202     /* puts(r); */
203 
204     return r;
205 }
206 
207 
208 Var
dbio_read_var(void)209 dbio_read_var(void)
210 {
211     Var r;
212     int i, l = dbio_read_num();
213 
214     if (l == (int) TYPE_ANY && dbio_input_version == DBV_Prehistory)
215 	l = TYPE_NONE;		/* Old encoding for VM's empty temp register
216 				 * and any as-yet unassigned variables.
217 				 */
218     r.type = (var_type) l;
219     switch (l) {
220     case TYPE_CLEAR:
221     case TYPE_NONE:
222 	break;
223     case _TYPE_STR:
224 	r.v.str = dbio_read_string_intern();
225 	r.type |= TYPE_COMPLEX_FLAG;
226 	break;
227     case TYPE_OBJ:
228     case TYPE_ERR:
229     case TYPE_INT:
230     case TYPE_CATCH:
231     case TYPE_FINALLY:
232 	r.v.num = dbio_read_num();
233 	break;
234     case _TYPE_FLOAT:
235 	r = new_float(dbio_read_float());
236 	break;
237     case _TYPE_LIST:
238 	l = dbio_read_num();
239 	r = new_list(l);
240 	for (i = 0; i < l; i++)
241 	    r.v.list[i + 1] = dbio_read_var();
242 	break;
243     default:
244 	errlog("DBIO_READ_VAR: Unknown type (%d) at DB file pos. %ld\n",
245 	       l, ftell(input));
246 	r = zero;
247 	break;
248     }
249     return r;
250 }
251 
252 struct state {
253     char prev_char;
254     const char *(*fmtr) (void *);
255     void *data;
256 };
257 
258 static const char *
program_name(struct state * s)259 program_name(struct state *s)
260 {
261     if (!s->fmtr)
262 	return s->data;
263     else
264 	return (*s->fmtr) (s->data);
265 }
266 
267 static void
my_error(void * data,const char * msg)268 my_error(void *data, const char *msg)
269 {
270     errlog("PARSER: Error in %s:\n", program_name(data));
271     errlog("           %s\n", msg);
272 }
273 
274 static void
my_warning(void * data,const char * msg)275 my_warning(void *data, const char *msg)
276 {
277     oklog("PARSER: Warning in %s:\n", program_name(data));
278     oklog("           %s\n", msg);
279 }
280 
281 static int
my_getc(void * data)282 my_getc(void *data)
283 {
284     struct state *s = data;
285     int c;
286 
287     c = fgetc(input);
288     if (c == '.' && s->prev_char == '\n') {
289 	/* end-of-verb marker in DB */
290 	c = fgetc(input);	/* skip next newline */
291 	return EOF;
292     }
293     if (c == EOF)
294 	my_error(data, "Unexpected EOF");
295     s->prev_char = c;
296     return c;
297 }
298 
299 static Parser_Client parser_client =
300 {my_error, my_warning, my_getc};
301 
302 Program *
dbio_read_program(DB_Version version,const char * (* fmtr)(void *),void * data)303 dbio_read_program(DB_Version version, const char *(*fmtr) (void *), void *data)
304 {
305     struct state s;
306 
307     s.prev_char = '\n';
308     s.fmtr = fmtr;
309     s.data = data;
310     return parse_program(version, parser_client, &s);
311 }
312 
313 
314 /*********** Output ***********/
315 
316 Exception dbpriv_dbio_failed;
317 
318 static FILE *output;
319 
320 void
dbpriv_set_dbio_output(FILE * f)321 dbpriv_set_dbio_output(FILE * f)
322 {
323     output = f;
324 }
325 
326 void
dbio_printf(const char * format,...)327 dbio_printf(const char *format,...)
328 {
329     va_list args;
330 
331     va_start(args, format);
332     if (vfprintf(output, format, args) < 0)
333 	RAISE(dbpriv_dbio_failed, 0);
334     va_end(args);
335 }
336 
337 void
dbio_write_num(int n)338 dbio_write_num(int n)
339 {
340     dbio_printf("%d\n", n);
341 }
342 
343 void
dbio_write_float(double d)344 dbio_write_float(double d)
345 {
346     static const char *fmt = 0;
347     static char buffer[10];
348 
349     if (!fmt) {
350 	sprintf(buffer, "%%.%dg\n", DBL_DIG + 4);
351 	fmt = buffer;
352     }
353     dbio_printf(fmt, d);
354 }
355 
356 void
dbio_write_objid(Objid oid)357 dbio_write_objid(Objid oid)
358 {
359     dbio_write_num(oid);
360 }
361 
362 void
dbio_write_string(const char * s)363 dbio_write_string(const char *s)
364 {
365     dbio_printf("%s\n", s ? s : "");
366 }
367 
368 void
dbio_write_var(Var v)369 dbio_write_var(Var v)
370 {
371     int i;
372 
373     dbio_write_num((int) v.type & TYPE_DB_MASK);
374     switch ((int) v.type) {
375     case TYPE_CLEAR:
376     case TYPE_NONE:
377 	break;
378     case TYPE_STR:
379 	dbio_write_string(v.v.str);
380 	break;
381     case TYPE_OBJ:
382     case TYPE_ERR:
383     case TYPE_INT:
384     case TYPE_CATCH:
385     case TYPE_FINALLY:
386 	dbio_write_num(v.v.num);
387 	break;
388     case TYPE_FLOAT:
389 	dbio_write_float(*v.v.fnum);
390 	break;
391     case TYPE_LIST:
392 	dbio_write_num(v.v.list[0].v.num);
393 	for (i = 0; i < v.v.list[0].v.num; i++)
394 	    dbio_write_var(v.v.list[i + 1]);
395 	break;
396     }
397 }
398 
399 static void
receiver(void * data,const char * line)400 receiver(void *data, const char *line)
401 {
402     dbio_printf("%s\n", line);
403 }
404 
405 void
dbio_write_program(Program * program)406 dbio_write_program(Program * program)
407 {
408     unparse_program(program, receiver, 0, 1, 0, MAIN_VECTOR);
409     dbio_printf(".\n");
410 }
411 
412 void
dbio_write_forked_program(Program * program,int f_index)413 dbio_write_forked_program(Program * program, int f_index)
414 {
415     unparse_program(program, receiver, 0, 1, 0, f_index);
416     dbio_printf(".\n");
417 }
418 
419 char rcsid_db_io[] = "$Id: db_io.c,v 1.5 1998/12/14 13:17:34 nop Exp $";
420 
421 /*
422  * $Log: db_io.c,v $
423  * Revision 1.5  1998/12/14 13:17:34  nop
424  * Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
425  *
426  * Revision 1.4  1998/02/19 07:36:16  nop
427  * Initial string interning during db load.
428  *
429  * Revision 1.3  1997/07/07 03:24:53  nop
430  * Merge UNSAFE_OPTS (r5) after extensive testing.
431  *
432  * Revision 1.2.2.1  1997/03/20 18:07:51  bjj
433  * Add a flag to the in-memory type identifier so that inlines can cheaply
434  * identify Vars that need actual work done to ref/free/dup them.  Add the
435  * appropriate inlines to utils.h and replace old functions in utils.c with
436  * complex_* functions which only handle the types with external storage.
437  *
438  * Revision 1.2  1997/03/03 04:18:27  nop
439  * GNU Indent normalization
440  *
441  * Revision 1.1.1.1  1997/03/03 03:44:59  nop
442  * LambdaMOO 1.8.0p5
443  *
444  * Revision 2.5  1996/03/19  07:16:12  pavel
445  * Increased precision of floating-point numbers printed in the DB file.
446  * Release 1.8.0p2.
447  *
448  * Revision 2.4  1996/03/10  01:04:16  pavel
449  * Increased the precision of printed floating-point numbers by two digits.
450  * Release 1.8.0.
451  *
452  * Revision 2.3  1996/02/08  07:19:15  pavel
453  * Renamed err/logf() to errlog/oklog() and TYPE_NUM to TYPE_INT.  Added
454  * dbio_read/write_float().  Updated copyright notice for 1996.
455  * Release 1.8.0beta1.
456  *
457  * Revision 2.2  1995/12/28  00:44:51  pavel
458  * Added support for receiving MOO-compilation warnings during loading and for
459  * printing useful error and warning messages in the log.
460  * Release 1.8.0alpha3.
461  *
462  * Revision 2.1  1995/12/11  07:59:50  pavel
463  * Fixed broken #includes.  Removed another silly use of `unsigned'.
464  *
465  * Release 1.8.0alpha2.
466  *
467  * Revision 2.0  1995/11/30  04:20:10  pavel
468  * New baseline version, corresponding to release 1.8.0alpha1.
469  *
470  * Revision 1.1  1995/11/30  04:19:56  pavel
471  * Initial revision
472  */
473