1 |
/* |
2 |
* HT Editor |
3 |
* store.cc |
4 |
* |
5 |
* Copyright (C) 1999-2003 Sebastian Biallas (sb@biallas.net) |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License version 2 as |
9 |
* published by the Free Software Foundation. |
10 |
* |
11 |
* This program is distributed in the hope that it will be useful, |
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
* GNU General Public License for more details. |
15 |
* |
16 |
* You should have received a copy of the GNU General Public License |
17 |
* along with this program; if not, write to the Free Software |
18 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
19 |
*/ |
20 |
|
21 |
#include <cerrno> |
22 |
#include <cstring> |
23 |
|
24 |
#include "atom.h" |
25 |
#include "debug.h" |
26 |
#include "endianess.h" |
27 |
#include "snprintf.h" |
28 |
#include "store.h" |
29 |
#include "strtools.h" |
30 |
|
31 |
static char hexchars[]="0123456789abcdef"; |
32 |
|
33 |
char oidchar(ObjectID oID, int byte) |
34 |
{ |
35 |
unsigned char c = (oID >> (byte*8)) & 0xff; |
36 |
if ((c<32) || (c>0x7f)) c = '?'; |
37 |
return c; |
38 |
} |
39 |
|
40 |
ObjectNotRegisteredException::ObjectNotRegisteredException(ObjectID aID) |
41 |
: MsgfException("Object %x/%c%c%c-%x not registered.", aID, |
42 |
oidchar(aID, 3), oidchar(aID, 2), oidchar(aID, 1), aID & 0xff) |
43 |
{ |
44 |
} |
45 |
|
46 |
/* |
47 |
* CLASS ObjectStreamInter |
48 |
*/ |
49 |
|
50 |
ObjectStreamInter::ObjectStreamInter(Stream *s, bool own_s) |
51 |
: ObjectStream(s, own_s) |
52 |
{ |
53 |
} |
54 |
|
55 |
void ObjectStreamInter::getObject(Object *&object, const char *name, ObjectID id) |
56 |
{ |
57 |
if (id == OBJID_INVALID) { |
58 |
GET_INT32X(*this, id); |
59 |
if (!id) { |
60 |
object = NULL; |
61 |
return; |
62 |
} |
63 |
} |
64 |
object_builder build = (object_builder)getAtomValue(id); |
65 |
if (!build) throw ObjectNotRegisteredException(id); |
66 |
object = build(); |
67 |
object->load(*this); |
68 |
} |
69 |
|
70 |
void ObjectStreamInter::putObject(const Object *object, const char *name, ObjectID id) |
71 |
{ |
72 |
if (!object) { |
73 |
if (id == OBJID_INVALID) { |
74 |
PUTX_INT32X(*this, 0, "id"); |
75 |
return; |
76 |
} else { |
77 |
throw IllegalArgumentException(HERE); |
78 |
} |
79 |
} |
80 |
if (id == OBJID_INVALID) { |
81 |
id = object->getObjectID(); |
82 |
char buf[64]; |
83 |
char id_str[4]; |
84 |
id_str[3] = id&0xff; |
85 |
id_str[2] = (id>>8)&0xff; |
86 |
id_str[1] = (id>>16)&0xff; |
87 |
id_str[0] = (id>>24)&0xff; |
88 |
escape_special(buf, sizeof buf, id_str, 4); |
89 |
putComment(buf); |
90 |
PUTX_INT32X(*this, id, "id"); |
91 |
} |
92 |
object_builder build = (object_builder)getAtomValue(id); |
93 |
if (!build) { |
94 |
throw ObjectNotRegisteredException(id); |
95 |
} |
96 |
object->store(*this); |
97 |
} |
98 |
|
99 |
/* |
100 |
* CLASS ObjectStreamBin |
101 |
*/ |
102 |
|
103 |
ObjectStreamBin::ObjectStreamBin(Stream *s, bool own_s) |
104 |
: ObjectStreamInter(s, own_s) |
105 |
{ |
106 |
} |
107 |
|
108 |
void ObjectStreamBin::getBinary(void *buf, uint size, const char *desc) |
109 |
{ |
110 |
mStream->readx(buf, size); |
111 |
} |
112 |
|
113 |
bool ObjectStreamBin::getBool(const char *desc) |
114 |
{ |
115 |
bool b; |
116 |
mStream->readx(&b, 1); |
117 |
return b; |
118 |
} |
119 |
|
120 |
uint64 ObjectStreamBin::getInt(uint size, const char *desc) |
121 |
{ |
122 |
ASSERT(size <= 8); |
123 |
byte neta[8]; |
124 |
mStream->readx(&neta, size); |
125 |
return createHostInt64(neta, size, big_endian); |
126 |
} |
127 |
|
128 |
char *ObjectStreamBin::getString(const char *desc) |
129 |
{ |
130 |
return getstrz(mStream); |
131 |
} |
132 |
|
133 |
byte *ObjectStreamBin::getLenString(int &length, const char *desc) |
134 |
{ |
135 |
byte b; |
136 |
mStream->readx(&b, 1); |
137 |
switch (b) { |
138 |
case 0xfe: { |
139 |
byte neta[2]; |
140 |
mStream->readx(&neta, 2); |
141 |
length = createHostInt(neta, 2, big_endian); |
142 |
break; |
143 |
} |
144 |
case 0xff: { |
145 |
byte neta[4]; |
146 |
mStream->readx(&neta, 4); |
147 |
length = createHostInt(neta, 4, big_endian); |
148 |
break; |
149 |
} |
150 |
default: |
151 |
length = b; |
152 |
break; |
153 |
} |
154 |
if (length==0) return NULL; |
155 |
byte *p = (byte*)malloc(length); |
156 |
mStream->readx(p, length); |
157 |
return p; |
158 |
} |
159 |
|
160 |
void ObjectStreamBin::putBinary(const void *mem, uint size, const char *desc) |
161 |
{ |
162 |
mStream->writex(mem, size); |
163 |
} |
164 |
|
165 |
void ObjectStreamBin::putBool(bool b, const char *desc) |
166 |
{ |
167 |
b = (b) ? 1 : 0; |
168 |
mStream->writex(&b, 1); |
169 |
} |
170 |
|
171 |
void ObjectStreamBin::putCommentf(const char *comment_format, ...) |
172 |
{ |
173 |
// NOP |
174 |
} |
175 |
|
176 |
void ObjectStreamBin::putComment(const char *comment) |
177 |
{ |
178 |
// NOP |
179 |
} |
180 |
|
181 |
void ObjectStreamBin::putInt(uint64 i, uint size, const char *desc, uint int_fmt_hint) |
182 |
{ |
183 |
ASSERT(size <= 8); |
184 |
byte neta[8]; |
185 |
createForeignInt64(neta, i, size, big_endian); |
186 |
mStream->writex(neta, size); |
187 |
} |
188 |
|
189 |
void ObjectStreamBin::putSeparator() |
190 |
{ |
191 |
// NOP |
192 |
} |
193 |
|
194 |
void ObjectStreamBin::putString(const char *string, const char *desc) |
195 |
{ |
196 |
putstrz(this, string); |
197 |
} |
198 |
|
199 |
void ObjectStreamBin::putLenString(const byte *string, int len, const char *desc) |
200 |
{ |
201 |
byte bLen; |
202 |
if (len < 0xfe) { |
203 |
bLen = len; |
204 |
mStream->writex(&bLen, 1); |
205 |
} else if (len <= 0xffff) { |
206 |
byte neta[2]; |
207 |
createForeignInt(neta, len, 2, big_endian); |
208 |
bLen = 0xfe; |
209 |
mStream->writex(&bLen, 1); |
210 |
mStream->writex(neta, 2); |
211 |
} else { |
212 |
byte neta[4]; |
213 |
createForeignInt(neta, len, 4, big_endian); |
214 |
bLen = 0xff; |
215 |
mStream->writex(&bLen, 1); |
216 |
mStream->writex(neta, 4); |
217 |
} |
218 |
mStream->writex(string, len); |
219 |
} |
220 |
|
221 |
/* |
222 |
* CLASS ObjectStreamText |
223 |
*/ |
224 |
|
225 |
ObjectStreamText::ObjectStreamText(Stream *s, bool own_s) |
226 |
: ObjectStreamInter(s, own_s) |
227 |
{ |
228 |
indent = 0; |
229 |
cur = ' '; // must be initialized to a whitespace char |
230 |
line = 1; |
231 |
errorline = 0; |
232 |
} |
233 |
|
234 |
void ObjectStreamText::getBinary(void *buf, uint size, const char *desc) |
235 |
{ |
236 |
readDesc(desc); |
237 |
expect('='); |
238 |
expect('['); |
239 |
byte *pp=(byte *)buf; |
240 |
for (uint i=0; i<size; i++) { |
241 |
skipWhite(); |
242 |
|
243 |
int bb; |
244 |
if ((bb = hexdigit(cur))==-1) setSyntaxError(); |
245 |
int b = bb*16; |
246 |
|
247 |
readChar(); |
248 |
if ((bb = hexdigit(cur))==-1) setSyntaxError(); |
249 |
b += bb; |
250 |
|
251 |
*pp++=b; |
252 |
|
253 |
readChar(); |
254 |
} |
255 |
expect(']'); |
256 |
} |
257 |
|
258 |
bool ObjectStreamText::getBool(const char *desc) |
259 |
{ |
260 |
readDesc(desc); |
261 |
expect('='); |
262 |
skipWhite(); |
263 |
if (cur=='f') { |
264 |
readDesc("false"); |
265 |
return false; |
266 |
} else { |
267 |
readDesc("true"); |
268 |
return true; |
269 |
} |
270 |
} |
271 |
|
272 |
static char mapchar(char c) |
273 |
{ |
274 |
return 0; |
275 |
} |
276 |
|
277 |
uint64 ObjectStreamText::getInt(uint size, const char *desc) |
278 |
{ |
279 |
readDesc(desc); |
280 |
expect('='); |
281 |
skipWhite(); |
282 |
if (mapchar(cur)!='0') setSyntaxError(); |
283 |
char str[40]; |
284 |
char *s=str; |
285 |
do { |
286 |
*s++ = cur; |
287 |
if (s-str >= 39) setSyntaxError(); |
288 |
readChar(); |
289 |
} while (mapchar(cur)=='0' || mapchar(cur)=='A'); |
290 |
*s=0; s=str; |
291 |
uint64 a; |
292 |
const char *s2 = s; |
293 |
if (!parseIntStr(s2, a, 10)) setSyntaxError(); |
294 |
return a; |
295 |
} |
296 |
|
297 |
void ObjectStreamText::getObject(Object *&object, const char *name, ObjectID id) |
298 |
{ |
299 |
readDesc(name); |
300 |
expect('='); |
301 |
expect('{'); |
302 |
ObjectStreamInter::getObject(object, name, id); |
303 |
expect('}'); |
304 |
} |
305 |
|
306 |
char *ObjectStreamText::getString(const char *desc) |
307 |
{ |
308 |
readDesc(desc); |
309 |
expect('='); |
310 |
skipWhite(); |
311 |
if (cur=='"') { |
312 |
String s; |
313 |
do { |
314 |
readChar(); |
315 |
s += cur; |
316 |
if (cur=='\\') { |
317 |
readChar(); |
318 |
s += cur; |
319 |
cur = 0; // hackish |
320 |
} |
321 |
} while (cur != '"'); |
322 |
readChar(); |
323 |
int str2l = s.length(); |
324 |
char *str2 = (char *)malloc(str2l); |
325 |
unescape_special_str(str2, str2l, s); |
326 |
return str2; |
327 |
} else { |
328 |
readDesc("NULL"); |
329 |
return NULL; |
330 |
} |
331 |
} |
332 |
|
333 |
byte *ObjectStreamText::getLenString(int &len, const char *desc) |
334 |
{ |
335 |
readDesc(desc); |
336 |
expect('='); |
337 |
skipWhite(); |
338 |
if (cur=='"') { |
339 |
String s; |
340 |
do { |
341 |
readChar(); |
342 |
s += cur; |
343 |
if (cur=='\\') { |
344 |
readChar(); |
345 |
s += cur; |
346 |
cur = 0; // hackish |
347 |
} |
348 |
} while (cur != '"'); |
349 |
readChar(); |
350 |
len = s.length()-1; |
351 |
if (!len) return NULL; |
352 |
byte *str2 = (byte *)malloc(len); |
353 |
unescape_special(str2, len, s); |
354 |
return str2; |
355 |
} else { |
356 |
readDesc("NULL"); |
357 |
return NULL; |
358 |
} |
359 |
} |
360 |
|
361 |
void ObjectStreamText::putBinary(const void *mem, uint size, const char *desc) |
362 |
{ |
363 |
putDesc(desc); |
364 |
putChar('['); |
365 |
for (uint i=0; i<size; i++) { |
366 |
byte a = *((byte *)mem+i); |
367 |
putChar(hexchars[(a & 0xf0) >> 4]); |
368 |
putChar(hexchars[(a & 0x0f)]); |
369 |
if (i+1<size) putChar(' '); |
370 |
} |
371 |
putS("]\n"); |
372 |
} |
373 |
|
374 |
void ObjectStreamText::putBool(bool b, const char *desc) |
375 |
{ |
376 |
putDesc(desc); |
377 |
if (b) putS("true"); else putS("false"); |
378 |
putChar('\n'); |
379 |
} |
380 |
|
381 |
void ObjectStreamText::putComment(const char *comment) |
382 |
{ |
383 |
putIndent(); |
384 |
putS("# "); |
385 |
putS(comment); |
386 |
putChar('\n'); |
387 |
} |
388 |
|
389 |
void ObjectStreamText::putInt(uint64 i, uint size, const char *desc, uint int_fmt_hint) |
390 |
{ |
391 |
putDesc(desc); |
392 |
char number[40]; |
393 |
switch (int_fmt_hint) { |
394 |
case OS_FMT_DEC: |
395 |
ht_snprintf(number, sizeof number, "%qd\n", i); |
396 |
break; |
397 |
case OS_FMT_HEX: |
398 |
default: |
399 |
ht_snprintf(number, sizeof number, "0x%qx\n", i); |
400 |
break; |
401 |
} |
402 |
putS(number); |
403 |
} |
404 |
|
405 |
void ObjectStreamText::putObject(const Object *object, const char *name, ObjectID id) |
406 |
{ |
407 |
putDesc(name); |
408 |
putS("{\n"); |
409 |
indent++; |
410 |
ObjectStreamInter::putObject(object, name, id); |
411 |
indent--; |
412 |
putIndent(); |
413 |
putS("}\n"); |
414 |
} |
415 |
|
416 |
void ObjectStreamText::putSeparator() |
417 |
{ |
418 |
putIndent(); |
419 |
putS("# ------------------------ \n"); |
420 |
} |
421 |
|
422 |
void ObjectStreamText::putString(const char *string, const char *desc) |
423 |
{ |
424 |
putDesc(desc); |
425 |
if (string) { |
426 |
int strl=strlen(string)*4+1; |
427 |
char *str = (char*)malloc(strl); |
428 |
putChar('"'); |
429 |
escape_special_str(str, strl, string, "\""); |
430 |
putS(str); |
431 |
putChar('"'); |
432 |
free(str); |
433 |
} else { |
434 |
putS("NULL"); |
435 |
} |
436 |
putChar('\n'); |
437 |
} |
438 |
|
439 |
void ObjectStreamText::putLenString(const byte *string, int len, const char *desc) |
440 |
{ |
441 |
putDesc(desc); |
442 |
if (string) { |
443 |
int strl=len*4+1; |
444 |
char *str = (char*)malloc(strl); |
445 |
putChar('"'); |
446 |
escape_special(str, strl, string, len, "\""); |
447 |
putS(str); |
448 |
putChar('"'); |
449 |
free(str); |
450 |
} else { |
451 |
putS("NULL"); |
452 |
} |
453 |
putChar('\n'); |
454 |
} |
455 |
|
456 |
class TextSyntaxError: public MsgfException { |
457 |
public: |
458 |
TextSyntaxError::TextSyntaxError(uint line) |
459 |
: MsgfException("syntax error in line %d", line) |
460 |
{ |
461 |
} |
462 |
}; |
463 |
|
464 |
void ObjectStreamText::setSyntaxError() |
465 |
{ |
466 |
// FIXME: errorline still usable ? |
467 |
if (!errorline) { |
468 |
errorline = line; |
469 |
throw TextSyntaxError(line); |
470 |
} |
471 |
} |
472 |
|
473 |
int ObjectStreamText::getErrorLine() |
474 |
{ |
475 |
return errorline; |
476 |
} |
477 |
|
478 |
void ObjectStreamText::expect(char c) |
479 |
{ |
480 |
skipWhite(); |
481 |
if (cur!=c) setSyntaxError(); |
482 |
readChar(); |
483 |
} |
484 |
|
485 |
void ObjectStreamText::skipWhite() |
486 |
{ |
487 |
while (1) { |
488 |
switch (mapchar(cur)) { |
489 |
case '\n': |
490 |
line++; // fallthrough |
491 |
case ' ': |
492 |
readChar(); |
493 |
break; |
494 |
case '#': |
495 |
do { |
496 |
readChar(); |
497 |
} while (cur!='\n'); |
498 |
break; |
499 |
default: return; |
500 |
} |
501 |
} |
502 |
} |
503 |
|
504 |
char ObjectStreamText::readChar() |
505 |
{ |
506 |
mStream->readx(&cur, 1); |
507 |
return cur; |
508 |
} |
509 |
|
510 |
void ObjectStreamText::readDesc(const char *desc) |
511 |
{ |
512 |
skipWhite(); |
513 |
if (!desc) desc="data"; |
514 |
while (*desc) { |
515 |
if (*desc!=cur) setSyntaxError(); |
516 |
readChar(); |
517 |
desc++; |
518 |
} |
519 |
} |
520 |
|
521 |
void ObjectStreamText::putDesc(const char *desc) |
522 |
{ |
523 |
putIndent(); |
524 |
if (desc) putS(desc); else putS("data"); |
525 |
putChar('='); |
526 |
} |
527 |
|
528 |
void ObjectStreamText::putIndent() |
529 |
{ |
530 |
for(int i=0; i<indent; i++) putChar(' '); |
531 |
} |
532 |
|
533 |
void ObjectStreamText::putChar(char c) |
534 |
{ |
535 |
mStream->writex(&c, 1); |
536 |
} |
537 |
|
538 |
void ObjectStreamText::putS(const char *s) |
539 |
{ |
540 |
uint len=strlen(s); |
541 |
if (mStream->write(s, len) != len) setSyntaxError(); |
542 |
} |
543 |
|
544 |
/* |
545 |
* ObjectStreamNative View:set/getData() methods |
546 |
* (endian-dependend) |
547 |
*/ |
548 |
|
549 |
ObjectStreamNative::ObjectStreamNative(Stream *s, bool own_s, bool d) |
550 |
: ObjectStream(s, own_s), allocd(true) |
551 |
{ |
552 |
duplicate = d; |
553 |
} |
554 |
|
555 |
void *ObjectStreamNative::duppa(const void *p, int size) |
556 |
{ |
557 |
if (duplicate) { |
558 |
MemArea *m = new MemArea(p, size, true); |
559 |
allocd += m; |
560 |
return m->ptr; |
561 |
} else { |
562 |
// FIXME: un-const'ing p |
563 |
return (void*)p; |
564 |
} |
565 |
} |
566 |
|
567 |
void ObjectStreamNative::getBinary(void *buf, uint size, const char *desc) |
568 |
{ |
569 |
void *pp; |
570 |
mStream->readx(&pp, sizeof pp); |
571 |
memmove(buf, pp, size); |
572 |
} |
573 |
|
574 |
bool ObjectStreamNative::getBool(const char *desc) |
575 |
{ |
576 |
bool b; |
577 |
mStream->readx(&b, sizeof b); |
578 |
return b; |
579 |
} |
580 |
|
581 |
uint64 ObjectStreamNative::getInt(uint size, const char *desc) |
582 |
{ |
583 |
switch (size) { |
584 |
case 1:case 2:case 4: { |
585 |
uint i = 0; |
586 |
mStream->readx(&i, size); |
587 |
return i; |
588 |
} |
589 |
case 8: { |
590 |
uint64 i; |
591 |
mStream->readx(&i, size); |
592 |
return i; |
593 |
} |
594 |
} |
595 |
throw IllegalArgumentException(HERE); |
596 |
} |
597 |
|
598 |
void ObjectStreamNative::getObject(Object *&object, const char *name, ObjectID id) |
599 |
{ |
600 |
Object *pp; |
601 |
mStream->readx(&pp, sizeof pp); |
602 |
object = pp; |
603 |
} |
604 |
|
605 |
char *ObjectStreamNative::getString(const char *desc) |
606 |
{ |
607 |
char *pp; |
608 |
mStream->readx(&pp, sizeof pp); |
609 |
return pp; |
610 |
} |
611 |
|
612 |
byte *ObjectStreamNative::getLenString(int &len, const char *desc) |
613 |
{ |
614 |
byte *pp; |
615 |
mStream->readx(&pp, sizeof pp); |
616 |
// FIXME? |
617 |
if (pp) len = strlen((char*)pp); else len = 0; |
618 |
return pp; |
619 |
} |
620 |
|
621 |
void ObjectStreamNative::putBinary(const void *mem, uint size, const char *desc) |
622 |
{ |
623 |
void *pp = mem ? duppa(mem, size) : NULL; |
624 |
mStream->writex(&pp, sizeof pp); |
625 |
} |
626 |
|
627 |
void ObjectStreamNative::putBool(bool b, const char *desc) |
628 |
{ |
629 |
mStream->writex(&b, sizeof b); |
630 |
} |
631 |
|
632 |
void ObjectStreamNative::putComment(const char *comment) |
633 |
{ |
634 |
// NOP |
635 |
} |
636 |
|
637 |
void ObjectStreamNative::putInt(uint64 i, uint size, const char *desc, uint int_fmt_hint) |
638 |
{ |
639 |
switch (size) { |
640 |
case 1:case 2:case 4: { |
641 |
uint x = i; |
642 |
mStream->writex(&x, size); |
643 |
return; |
644 |
} |
645 |
case 8: { |
646 |
mStream->writex(&i, size); |
647 |
return; |
648 |
} |
649 |
} |
650 |
throw IllegalArgumentException(HERE); |
651 |
} |
652 |
|
653 |
void ObjectStreamNative::putObject(const Object *object, const char *name, ObjectID id) |
654 |
{ |
655 |
Object *d = duplicate ? d->clone() : d; |
656 |
mStream->write(&d, sizeof d); |
657 |
} |
658 |
|
659 |
void ObjectStreamNative::putSeparator() |
660 |
{ |
661 |
// NOP |
662 |
} |
663 |
|
664 |
void ObjectStreamNative::putString(const char *string, const char *desc) |
665 |
{ |
666 |
const char *pp = string ? (const char*)duppa(string, strlen(string)+1) : NULL; |
667 |
mStream->write(&pp, sizeof pp); |
668 |
} |
669 |
|
670 |
void ObjectStreamNative::putLenString(const byte *string, int len, const char *desc) |
671 |
{ |
672 |
const char *pp = string ? (const char*)duppa(string, len+1) : NULL; |
673 |
mStream->write(&pp, sizeof pp); |
674 |
} |
675 |
|