]> git.buserror.net Git - polintos/scott/priv.git/blob - idlcomp/input.cc
1ef953d2be2cc95f7461aa8dd739b4fc8399ff2c
[polintos/scott/priv.git] / idlcomp / input.cc
1 // Code to load symbols from a legacy filesystem
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // This software is provided 'as-is', without any express or implied warranty.
6 // In no event will the authors or contributors be held liable for any damages
7 // arising from the use of this software.
8 // 
9 // Permission is hereby granted to everyone, free of charge, to use, copy,
10 // modify, prepare derivative works of, publish, distribute, perform,
11 // sublicense, and/or sell copies of the Software, provided that the above
12 // copyright notice and disclaimer of warranty be included in all copies or
13 // substantial portions of this software.
14
15 #include <cstdio>
16 #include <vector>
17 #include <memory>
18 #include <cstring>
19
20 #include <errno.h>
21 #include <dirent.h>
22
23 #include <idlc.h>
24 #include <parser.h>
25
26 // To deal with namespaces on a case-insensitive filesystem, names
27 // are annotated with a hexadecimal prefix indicating the case of each
28 // letter.  Each bit in the number represents a letter, with 0 being
29 // lowercase (or a non-letter) and 1 being uppercase.  The
30 // least-significant bit is the first letter.  This is done on both
31 // case sensitive and case insensitive filesystems so that the output
32 // can be freely copied between them (it also simplifies the code).
33 // As such, hex digits should be in upper case so that case-sensitive
34 // lookups can be used.  This prefix is separated from the actual name
35 // by an underscore.
36 //
37 // When the time comes along that UTF-8 is supported, this applies to
38 // UTF-8 characters, not ASCII characters.
39 //
40 // This mangling can also be used (with a leading underscore or
41 // whatever) in case-insensitive languages, though ideally there
42 // should be aliases with the unannotated name if there is no
43 // conflict.
44 //
45 // I don't want to go the CORBA route of disallowing case conflicts,
46 // as I find using capitals for types and lowercase for instances to
47 // be a useful convention (and less ugly than appending _t, except
48 // when case sensitivity is missing), and would rather not be limited
49 // at such a fundamental level by broken languages.
50
51 static char tohex(int digit)
52 {
53         if (digit < 10)
54                 return digit + '0';
55         
56         return digit + 'A' - 10;
57 }
58
59 const String *NameSpace::mangle(const String *name)
60 {
61         String *mangled_string = new String;
62         
63         // The conversion is done manually so that I don't have to deal
64         // with bignum arithmetic.
65         
66         std::vector<bool> hex;
67         std::vector<bool>::size_type name_len = name->length();
68         
69         // Pad out the first digit with leading zeroes, so that
70         // we don't have to deal with that later.
71         
72         if (name_len & 3)
73                 for (int i = name_len & 3; i < 4; ++i)
74                         hex.push_back(0);
75
76         for (string::const_iterator i = name->begin(); i != name->end(); ++i)
77                 hex.push_back(isupper(*i));
78
79         assert((hex.size() & 3) == 0);
80
81         for (std::vector<bool>::size_type i = 0; i < hex.size(); i += 4) {
82                 int digit = hex[i]     * 8 +
83                             hex[i + 1] * 4 +
84                             hex[i + 2] * 2 +
85                             hex[i + 3];
86
87                 *mangled_string += tohex(digit);
88         }
89         
90         *mangled_string += '_';
91         *mangled_string += *name;
92         return mangled_string;
93 }
94
95 struct IOError
96 {
97         const char *msg;
98
99         IOError(FILE *f)
100         {
101                 if (ferror(f))
102                         msg = strerror(ferror(f));
103                 else
104                         msg = "Short File";
105         }
106 };
107
108 struct ImportContext {
109         const String *name;
110         const char *filename;
111         File f;
112         NameSpace *parent;
113         bool swap;
114         bool is_dir;
115         bool is_typedef;
116         CompiledDefHeader hdr;
117 };
118
119 static void import_and_validate_array(ImportContext &ctx,
120                                       CompiledArray &out,
121                                       CompiledArray &in)
122 {
123         out.bounds[0] = swap64(in.bounds[0], ctx.swap);
124         out.bounds[1] = swap64(in.bounds[1], ctx.swap);
125
126         if (out.bounds[0] < 0) {
127                 fprintf(stderr, "\"%s\" is not a valid object "
128                                 "(bad array lower bound %" PRId64 ").\n",
129                         ctx.filename, out.bounds[0]);
130                 throw UserError();
131         }
132         
133         if (out.bounds[1] < -1) {
134                 fprintf(stderr, "\"%s\" is not a valid object "
135                                 "(bad array upper bound %" PRId64 ").\n",
136                         ctx.filename, out.bounds[0]);
137                 throw UserError();
138         }
139         
140         if (out.bounds[1] > 0 &&
141             out.bounds[1] < out.bounds[0]) {
142                 fprintf(stderr, "\"%s\" is not a valid object "
143                                 "(array upper bound %" PRId64 " less than lower bound %" PRId64 ").\n",
144                         ctx.filename, out.bounds[1], out.bounds[0]);
145                 throw UserError();
146         }
147 }
148
149 static void import_and_validate_basictype(ImportContext &ctx,
150                                           CompiledBasicType &out, 
151                                           CompiledBasicType &in)
152 {
153         out.bits = swap32(in.bits, ctx.swap);
154         out.flags.raw = swap32(in.flags.raw, ctx.swap);
155
156         int num = 0;
157         if (out.flags.field.Unsigned)
158                 num++;
159         if (out.flags.field.Float)
160                 num++;
161         if (out.flags.field.Bool)
162                 num++;
163         if (num > 1) {
164                 fprintf(stderr, "\"%s\" is not a valid object "
165                                 "(bad flags 0x%08x).\n",
166                         ctx.filename, out.flags.raw);
167                 throw UserError();
168         }
169
170         if (out.flags.field.Bool) {
171                 if (out.bits != 0) {
172                         fprintf(stderr, "\"%s\" is not a valid object "
173                                         "(bad bits %d for bool type).\n",
174                                 ctx.filename, out.bits);
175                         throw UserError();
176                 }
177         } else if (out.flags.field.Float) {
178                 if (out.bits != 32 && out.bits != 64) {
179                         fprintf(stderr, "\"%s\" is not a valid object "
180                                         "(bad bits %d for floating point type).\n",
181                                 ctx.filename, out.bits);
182                         throw UserError();
183                 }
184         } else if (!dynamic_cast<BitField *>(ctx.parent) &&
185                    !dynamic_cast<Enum *>(ctx.parent)) {
186                 if (out.bits != 8 && out.bits != 16 &&
187                     out.bits != 32 && out.bits != 64) {
188                         fprintf(stderr, "\"%s\" is not a valid object "
189                                         "(bad bits %d for integer type).\n",
190                                 ctx.filename, out.bits);
191                         throw UserError();
192                 }
193                 
194                 if (out.bits == 8 && !out.flags.field.Unsigned) {
195                         fprintf(stderr, "\"%s\" is not a valid object "
196                                         "(octet type must be unsigned).\n",
197                                 ctx.filename);
198                         throw UserError();
199                 }
200         }
201         
202         // Bitfield entries can take anywhere from 1 to 64 bits, but
203         // will be verified by the parent after all entries have loaded,
204         // so it can verify that the sum does not exceed the size of
205         // the bitfield.
206
207         import_and_validate_array(ctx, out.array, in.array);
208 }
209
210 BasicType *BasicType::import(ImportContext &ctx)
211 {
212         if (ctx.is_dir) {
213                 fprintf(stderr, "\"%s\" is not a valid object "
214                                 "(a BasicType must be a file).\n",
215                         ctx.filename);
216                 throw UserError();
217         }
218
219         CompiledBasicType def;
220         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
221                 throw IOError(ctx.f);
222
223         BasicType *bt = new BasicType(ctx.name);
224         ctx.parent->add_import(bt, ctx.filename);
225         
226         import_and_validate_basictype(ctx, bt->def, def);
227
228         bt->complete = true;
229         return bt;
230 }
231
232 static String *read_string_raw(ImportContext &ctx, int32_t length)
233 {
234         if (length < 0 || length > 4096) {
235                 fprintf(stderr, "\"%s\" is not a valid object "
236                                 "(unreasonable string length %d).\n",
237                         ctx.filename, length);
238                 throw UserError();
239         }
240
241         // Is there a way to reserve space in a C++ string, and pass
242         // the buffer into fread?
243         
244         char *buf = new char[length + 1];
245         
246         if (fread(buf, length + 1, 1, ctx.f) != 1)
247                 throw IOError(ctx.f);
248         
249         String *s = new String(buf);
250         delete[] buf;
251         return s;
252 }
253
254 static const String *read_string(ImportContext &ctx)
255 {
256         off_t pos = ftello(ctx.f);
257         int pad = ((pos + 3) & ~3) - pos;
258
259         if (pad != 0 && fseeko(ctx.f, pad, SEEK_CUR) != 0)
260                 throw IOError(ctx.f);
261
262         int32_t length;
263         if (fread(&length, sizeof(length), 1, ctx.f) != 1)
264                 throw IOError(ctx.f);
265         
266         return read_string_raw(ctx, swap32(length, ctx.swap));
267 }
268
269 StrList::StrList(const String *input, char delimeter)
270 {
271         const char *cur = input->c_str();
272         
273         do {
274                 char *next = strchr(cur, delimeter);
275                 String *s = new String();
276                 s->token = TOK_IDENT;
277                 
278                 if (next == cur || *cur == 0)
279                         throw InvalidArgument();
280                 
281                 if (next) {
282                         s->append(cur, next - cur);
283                         cur = next + 1;
284                 } else {
285                         s->append(cur);
286                         cur = NULL;
287                 }
288                 
289                 push_back(s);
290         } while (cur);
291 }
292
293 // FIXME: Handle illegal recursion.
294 static Symbol *lookup_sym_noyyerror(ImportContext &ctx, const String *name)
295 {
296         StrList *sl;
297
298         try {
299                 sl = new StrList(name);
300         }
301
302         catch (InvalidArgument) {
303                 fprintf(stderr, "\"%s\" is not a valid object "
304                                 "(\"%s\" is not a valid identifier).\n",
305                         ctx.filename, name->c_str());
306                 throw UserError();
307         }
308
309         try {
310                 return lookup_sym(toplevel, sl, toplevel);
311         }
312         
313         catch (UserError) {
314                 fprintf(stderr, "\"%s\" is not a valid object "
315                                 "(\"%s\" could not be loaded).\n",
316                         ctx.filename, name->c_str());
317
318                 throw UserError();
319         }
320 }
321
322 // FIXME: Handle illegal recursion.
323 static Symbol *lookup_sym_in_ns_noyyerror(ImportContext &ctx,
324                                           NameSpace *ns,
325                                           const String *name)
326 {
327         Symbol *sym = ns->lookup_noex(name);
328         if (!sym) {
329                 fprintf(stderr, "\"%s\" is not a valid object "
330                                 "(\"%s\" is not found).\n",
331                         ctx.filename, name->c_str());
332
333                 throw UserError();
334         }
335         
336         return sym;
337 }
338
339 Datum *Datum::import(ImportContext &ctx)
340 {
341         if (ctx.is_dir) {
342                 fprintf(stderr, "\"%s\" is not a valid object "
343                                 "(a Datum must be a file).\n",
344                         ctx.filename);
345                 throw UserError();
346         }
347
348         CompiledDatum def;
349         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
350                 throw IOError(ctx.f);
351
352         Datum *d = new Datum(ctx.name);
353         d->def.flags.raw = swap32(def.flags.raw, ctx.swap);
354
355         if (d->def.flags.field.Invalid) {
356                 fprintf(stderr, "\"%s\" is not a valid object "
357                                 "(bad flags 0x%08x).\n",
358                         ctx.filename, d->def.flags.raw);
359                 throw UserError();
360         }
361
362         ctx.parent->add_import(d, ctx.filename);
363
364         d->def.type.length = swap32(def.type.length, ctx.swap);
365
366         if (d->def.type.length != 0) {
367                 d->type_fq_name = read_string_raw(ctx, d->def.type.length);
368                 Symbol *sym = lookup_sym_noyyerror(ctx, d->type_fq_name);
369                 d->type = dynamic_cast<Type *>(sym);
370                 
371                 if (!d->type) {
372                         fprintf(stderr, "\"%s\" is not a valid object "
373                                         "(\"%s\" is not a type).\n",
374                                 ctx.filename, d->type_fq_name->c_str());
375                         throw UserError();
376                 }
377                 
378                 import_and_validate_array(ctx, d->def.basictype.array,
379                                           def.basictype.array);
380         
381                 BasicType *bt = dynamic_cast<BasicType *>(*d->type);
382                 if (bt)
383                         d->cbt = &bt->def;
384                 else
385                         d->cbt = NULL;
386         } else {
387                 d->basic = true;
388                 import_and_validate_basictype(ctx, d->def.basictype,
389                                               def.basictype);
390                 d->cbt = &d->def.basictype;
391         }
392
393         d->def.ucon = swap64(def.ucon, ctx.swap);
394
395         if (d->def.flags.field.Const) {
396                 if (!d->cbt) {
397                         fprintf(stderr, "\"%s\" is not a valid object "
398                                         "(constant, but non-basic type).\n",
399                                 ctx.filename);
400                         throw UserError();
401                 }
402         
403                 if (d->cbt->flags.field.Float)
404                         d->con_type = TOK_FCON;
405                 else if (!d->cbt->flags.field.Unsigned)
406                         d->con_type = TOK_ICON;
407                 else
408                         d->con_type = TOK_UCON;
409         
410                 if (!d->verify_const()) {
411                         fprintf(stderr, "\"%s\" is not a valid object "
412                                         "(bad constant).\n",
413                                 ctx.filename);
414                         throw UserError();
415                 }
416         } else {
417                 d->con_type = TOK_NONE;
418         }
419
420         return d;
421 }
422
423 // OPT: Importing methods and supers isn't necessary for idlc to
424 // work; it could be made optional to speed up large compilations.
425
426 Interface *Interface::import(ImportContext &ctx)
427 {
428         if (!ctx.is_dir) {
429                 fprintf(stderr, "\"%s\" is not a valid object "
430                                 "(an Interface must be a directory).\n",
431                         ctx.filename);
432                 throw UserError();
433         }
434
435         CompiledInterface def;
436         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
437                 throw IOError(ctx.f);
438
439         Interface *iface = new Interface(ctx.name);
440         iface->path = new String(ctx.filename);
441         ctx.parent->add_import(iface, ctx.filename);
442         
443         iface->def.guid[0] = def.guid[0];
444         iface->def.guid[1] = def.guid[1];
445
446         int32_t num_methods = swap32(def.num_methods, ctx.swap);
447         int32_t num_supers = swap32(def.num_supers, ctx.swap);
448         
449         if (num_methods < 0 || num_methods > 4096) {
450                 fprintf(stderr, "\"%s\" is not a valid object "
451                                 "(unreasonable num_methods %d).\n",
452                         ctx.filename, num_methods);
453                 throw UserError();
454         }
455
456         if (num_supers < 0 || num_supers > 4096) {
457                 fprintf(stderr, "\"%s\" is not a valid object "
458                                 "(unreasonable num_supers %d).\n",
459                         ctx.filename, num_supers);
460                 throw UserError();
461         }
462         
463         for (int32_t i = 0; i < num_methods; i++) {
464                 const String *str = read_string(ctx);
465                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, iface, str);
466                 Method *m = dynamic_cast<Method *>(sym);
467                 
468                 if (!m) {
469                         fprintf(stderr, "\"%s\" is not a valid object "
470                                         "(\"%s\" is not a method).\n",
471                                 ctx.filename, str->c_str());
472                         throw UserError();
473                 }
474                 
475                 iface->add_elem(m);
476         }
477
478         // FIXME: Check for bad recursion again
479
480         for (int32_t i = 0; i < num_supers; i++) {
481                 const String *str = read_string(ctx);
482                 Symbol *sym = lookup_sym_noyyerror(ctx, str);
483                 Interface *super = dynamic_cast<Interface *>(sym);
484         
485                 if (!super) {
486                         fprintf(stderr, "\"%s\" is not a valid object "
487                                         "(\"%s\" is not an interface).\n",
488                                 ctx.filename, str->c_str());
489                         throw UserError();
490                 }
491         
492                 iface->add_super(super);
493         }
494         
495         assert(num_methods == iface->def.num_methods);
496         assert(num_supers == iface->def.num_supers);
497
498         iface->sort_chains();
499         return iface;
500 }
501
502 Method *Method::import(ImportContext &ctx)
503 {
504         if (!ctx.is_dir) {
505                 fprintf(stderr, "\"%s\" is not a valid object "
506                                 "(a Method must be a directory).\n",
507                         ctx.filename);
508                 throw UserError();
509         }
510
511         CompiledMethod def;
512         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
513                 throw IOError(ctx.f);
514
515         Method *m = new Method(ctx.name);
516         m->path = new String(ctx.filename);
517         ctx.parent->add_import(m, ctx.filename);
518
519         int32_t num_entries = swap32(def.num_entries, ctx.swap);
520
521         m->def.flags.raw = swap32(def.flags.raw, ctx.swap);
522         
523         if (num_entries < 0 || num_entries > 4096) {
524                 fprintf(stderr, "\"%s\" is not a valid object "
525                                 "(unreasonable num_entries %d).\n",
526                         ctx.filename, num_entries);
527                 throw UserError();
528         }
529
530         for (int32_t i = 0; i < num_entries; i++) {
531                 const String *str = read_string(ctx);
532                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, m, str);
533                 Param *p = dynamic_cast<Param *>(sym);
534                 
535                 if (!p) {
536                         fprintf(stderr, "\"%s\" is not a valid object "
537                                         "(\"%s\" is not a parameter).\n",
538                                 ctx.filename, str->c_str());
539                         throw UserError();
540                 }
541                 
542                 m->add_elem(p);
543         }
544
545         assert(num_entries == m->def.num_entries);
546         return m;
547 }
548
549 Param *Param::import(ImportContext &ctx)
550 {
551         if (ctx.is_dir) {
552                 fprintf(stderr, "\"%s\" is not a valid object "
553                                 "(a Parameter must be a file).\n",
554                         ctx.filename);
555                 throw UserError();
556         }
557
558         CompiledParam def;
559         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
560                 throw IOError(ctx.f);
561
562         Param *p = new Param(ctx.name);
563         ctx.parent->add_import(p, ctx.filename);
564
565         p->def.type.length = swap32(def.type.length, ctx.swap);
566
567         if (p->def.type.length != 0) {
568                 p->type_fq_name = read_string_raw(ctx, p->def.type.length);
569                 Symbol *sym = lookup_sym_noyyerror(ctx, p->type_fq_name);
570                 p->type = dynamic_cast<Type *>(sym);
571                 
572                 if (!p->type) {
573                         fprintf(stderr, "\"%s\" is not a valid object "
574                                         "(\"%s\" is not a type).\n",
575                                 ctx.filename, p->type_fq_name->c_str());
576                         throw UserError();
577                 }
578                 
579                 import_and_validate_array(ctx, p->def.basictype.array,
580                                           def.basictype.array);
581         } else {
582                 import_and_validate_basictype(ctx, p->def.basictype,
583                                               def.basictype);
584         }
585
586         p->def.flags.raw = swap32(def.flags.raw, ctx.swap);
587         return p;
588 }
589
590 // OPT: Importing data and super isn't necessary for idlc to
591 // work; it could be made optional to speed up large compilations.
592
593 Struct *Struct::import(ImportContext &ctx)
594 {
595         if (!ctx.is_dir) {
596                 fprintf(stderr, "\"%s\" is not a valid object "
597                                 "(a Struct must be a directory).\n",
598                         ctx.filename);
599                 throw UserError();
600         }
601
602         CompiledStruct def;
603         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
604                 throw IOError(ctx.f);
605
606         Struct *s = new Struct(ctx.name);
607         s->path = new String(ctx.filename);
608         ctx.parent->add_import(s, ctx.filename);
609         
610         s->def.guid[0] = def.guid[0];
611         s->def.guid[1] = def.guid[1];
612         
613         int32_t num_entries = swap32(def.num_entries, ctx.swap);
614
615         s->def.flags.raw = swap32(def.flags.raw, ctx.swap);
616         
617         if (num_entries < 0 || num_entries > 4096) {
618                 fprintf(stderr, "\"%s\" is not a valid object "
619                                 "(unreasonable num_entries %d).\n",
620                         ctx.filename, num_entries);
621                 throw UserError();
622         }
623
624         for (int32_t i = 0; i < num_entries; i++) {
625                 const String *str = read_string(ctx);
626                 
627                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, s, str);
628                 Datum *d = dynamic_cast<Datum *>(sym);
629                 
630                 if (!d) {
631                         fprintf(stderr, "\"%s\" is not a valid object "
632                                         "(\"%s\" is not a datum).\n",
633                                 ctx.filename, str->c_str());
634                         throw UserError();
635                 }
636                 
637                 s->add_elem(d);
638         }
639         
640         if (s->def.flags.field.Super) {
641                 const String *str = read_string(ctx);
642                 Symbol *sym = lookup_sym_noyyerror(ctx, str);
643                 Struct *super = dynamic_cast<Struct *>(sym);
644                 
645                 if (!super) {
646                         fprintf(stderr, "\"%s\" is not a valid object "
647                                         "(\"%s\" is not a struct).\n",
648                                 ctx.filename, str->c_str());
649                         throw UserError();
650                 }
651                 
652                 s->super = super;
653                 
654                 if (super->is_virtual() && !s->is_virtual()) {
655                         fprintf(stderr, "\"%s\" is not a valid object "
656                                         "(not virtual but parent is).\n",
657                                 ctx.filename);
658                         throw UserError();
659                 }
660         }
661
662         assert(num_entries == s->def.num_entries);
663         return s;
664 }
665
666 // OPT: Importing elements isn't necessary for idlc to work (at
667 // least, not unless inheritance is implemented); it could be made
668 // optional to speed up large compilations.
669
670 BitField *BitField::import(ImportContext &ctx)
671 {
672         if (!ctx.is_dir) {
673                 fprintf(stderr, "\"%s\" is not a valid object "
674                                 "(a BitField must be a directory).\n",
675                         ctx.filename);
676                 throw UserError();
677         }
678
679         CompiledBitField def;
680         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
681                 throw IOError(ctx.f);
682
683         BitField *bf = new BitField(ctx.name);
684         bf->path = new String(ctx.filename);
685         ctx.parent->add_import(bf, ctx.filename);
686
687         int32_t num_entries = swap32(def.num_entries, ctx.swap);
688         bf->def.bits = swap32(def.bits, ctx.swap);
689
690         if (num_entries < 0 || num_entries > 4096) {
691                 fprintf(stderr, "\"%s\" is not a valid object "
692                                 "(unreasonable num_entries %d).\n",
693                         ctx.filename, num_entries);
694                 throw UserError();
695         }
696         
697         // FIXME: bits can only be 16, 32, or 64 when not in another bitfield.
698
699         if (bf->def.bits < 1 || bf->def.bits > 64) {
700                 fprintf(stderr, "\"%s\" is not a valid object "
701                                 "(unreasonable bits %d).\n",
702                         ctx.filename, num_entries);
703                 throw UserError();
704         }
705
706         for (int32_t i = 0; i < num_entries; i++) {
707                 const String *str = read_string(ctx);
708                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, bf, str);
709                 Datum *d = dynamic_cast<Datum *>(sym);
710                 
711                 if (!d) {
712                         fprintf(stderr, "\"%s\" is not a valid object "
713                                         "(\"%s\" is not a datum).\n",
714                                 ctx.filename, str->c_str());
715                         throw UserError();
716                 }
717                 
718                 bf->add_elem(d);
719         }
720         
721         assert(num_entries == bf->def.num_entries);
722         return bf;
723 }
724
725 // OPT: Importing elements isn't necessary for idlc to work (at
726 // least, not unless inheritance is implemented); it could be made
727 // optional to speed up large compilations.
728
729 Enum *Enum::import(ImportContext &ctx)
730 {
731         if (!ctx.is_dir) {
732                 fprintf(stderr, "\"%s\" is not a valid object "
733                                 "(a Enum must be a directory).\n",
734                         ctx.filename);
735                 throw UserError();
736         }
737
738         CompiledEnum def;
739         if (fread(&def, sizeof(def), 1, ctx.f) != 1)
740                 throw IOError(ctx.f);
741
742         Enum *e = new Enum(ctx.name);
743         e->path = new String(ctx.filename);
744         ctx.parent->add_import(e, ctx.filename);
745
746         int32_t num_entries = swap32(def.num_entries, ctx.swap);
747         e->def.bits = swap32(def.bits, ctx.swap);
748
749         if (num_entries < 0 || num_entries > 4096) {
750                 fprintf(stderr, "\"%s\" is not a valid object "
751                                 "(unreasonable num_entries %d).\n",
752                         ctx.filename, num_entries);
753                 throw UserError();
754         }
755         
756         // FIXME: bits can only be 16, 32, or 64 when not in another bitfield.
757
758         if (e->def.bits < 1 || e->def.bits > 64) {
759                 fprintf(stderr, "\"%s\" is not a valid object "
760                                 "(unreasonable bits %d).\n",
761                         ctx.filename, num_entries);
762                 throw UserError();
763         }
764
765         for (int32_t i = 0; i < num_entries; i++) {
766                 const String *str = read_string(ctx);
767                 Symbol *sym = lookup_sym_in_ns_noyyerror(ctx, e, str);
768                 Datum *d = dynamic_cast<Datum *>(sym);
769                 
770                 if (!d) {
771                         fprintf(stderr, "\"%s\" is not a valid object "
772                                         "(\"%s\" is not a datum).\n",
773                                 ctx.filename, str->c_str());
774                         throw UserError();
775                 }
776                 
777                 e->add_elem(d);
778         }
779         
780         assert(num_entries == e->def.num_entries);
781         return e;
782 }
783
784 Alias *Alias::import(ImportContext &ctx)
785 {
786         if (ctx.is_dir) {
787                 fprintf(stderr, "\"%s\" is not a valid object "
788                                 "(an Alias must be a file).\n",
789                         ctx.filename);
790                 throw UserError();
791         }
792
793         Alias *a = new Alias(ctx.name);
794         ctx.parent->add_import(a, ctx.filename);
795
796         const String *str = read_string(ctx);
797         Symbol *sym = lookup_sym_noyyerror(ctx, str);
798         
799         Alias *aptr = dynamic_cast<Alias *>(sym);
800         if (aptr) {
801                 fprintf(stderr, "\"%s\" is not a valid object "
802                                 "(points to \"%s\", which is an alias).\n",
803                         ctx.filename, str->c_str());
804                 throw UserError();
805         }
806
807         a->real_sym = sym;
808         a->sym_fq_name = str;
809         a->def.length = str->length();
810
811         return a;
812 }
813
814 TypeDef *TypeDef::import(ImportContext &ctx)
815 {
816         if (ctx.is_dir) {
817                 fprintf(stderr, "\"%s\" is not a valid object "
818                                 "(a TypeDef must be a file).\n",
819                         ctx.filename);
820                 throw UserError();
821         }
822
823         TypeDef *td = new TypeDef(ctx.name);
824         ctx.parent->add_import(td, ctx.filename);
825
826         const String *str = read_string(ctx);
827         Symbol *sym = lookup_sym_noyyerror(ctx, str);
828         
829         Alias *aptr = dynamic_cast<Alias *>(sym);
830         if (aptr) {
831                 fprintf(stderr, "\"%s\" is not a valid object "
832                                 "(points to \"%s\", which is an alias).\n",
833                         ctx.filename, str->c_str());
834                 throw UserError();
835         }
836
837         td->real_sym = sym;
838         td->sym_fq_name = str;
839         td->def.length = str->length();
840
841         return td;
842 }
843
844 UserNameSpace *UserNameSpace::import(ImportContext &ctx)
845 {
846         if (!ctx.is_dir) {
847                 fprintf(stderr, "\"%s\" is not a valid object "
848                                 "(a NameSpace must be a directory).\n",
849                         ctx.filename);
850                 throw UserError();
851         }
852
853         UserNameSpace *uns = new UserNameSpace();
854         uns->path = new String(ctx.filename);
855         
856         // FIXME: sanity check the mount point
857
858         uns->name = ctx.name;   
859         ctx.parent->add_import(uns, ctx.filename);
860         return uns;
861 }
862
863 Symbol *do_load(ImportContext &ctx)
864 {
865         int type = swap32(ctx.hdr.type, ctx.swap);
866         
867         switch (type) {
868                 case CompiledDefHeader::NameSpace:
869                         return UserNameSpace::import(ctx);
870                         break;
871                 
872                 case CompiledDefHeader::BasicType:
873                         return BasicType::import(ctx);
874                         break;
875
876                 case CompiledDefHeader::Datum:
877                         return Datum::import(ctx);
878                         break;
879
880                 case CompiledDefHeader::Interface:
881                         return Interface::import(ctx);
882                         break;
883
884                 case CompiledDefHeader::Method:
885                         return Method::import(ctx);
886                         break;
887
888                 case CompiledDefHeader::Param:
889                         return Param::import(ctx);
890                         break;
891
892                 case CompiledDefHeader::Struct:
893                         return Struct::import(ctx);
894                         break;
895
896                 case CompiledDefHeader::Enum:
897                         return Enum::import(ctx);
898                         break;
899
900                 case CompiledDefHeader::BitField:
901                         return BitField::import(ctx);
902                         break;
903
904                 case CompiledDefHeader::Alias:
905                         ctx.is_typedef = false;
906                         return Alias::import(ctx);
907                         break;
908
909                 case CompiledDefHeader::TypeDef:
910                         ctx.is_typedef = true;
911                         return TypeDef::import(ctx);
912                         break;
913
914                 default:
915                         fprintf(stderr, "\"%s\" is not a valid object "
916                                         "(bad type %08x).\n",
917                                 ctx.filename, type);
918                         
919                         throw UserError();
920         }
921 }
922
923 Symbol *NameSpace::load(const String *symname)
924 {
925         ImportContext ctx;
926         const String *mangled = mangle(symname);
927
928         ctx.name = new String(*symname);
929         ctx.is_dir = true;
930         ctx.parent = this;
931
932         string filename_no_self(path);
933         filename_no_self += '/';
934         filename_no_self += *mangled;
935         ctx.filename = filename_no_self.c_str();
936
937         string filename(filename_no_self);
938         filename.append("/.self");
939
940         ctx.f = fopen(filename.c_str(), "rb");
941         if (!ctx.f) {
942                 ctx.is_dir = false;
943                 ctx.f = fopen(ctx.filename, "rb");
944                 
945                 if (!ctx.f)
946                         return NULL;
947                 
948                 filename = filename_no_self;
949         }
950         
951         Symbol *ret;
952         try {
953                 if (fread(&ctx.hdr, sizeof(ctx.hdr), 1, ctx.f) != 1)
954                         throw IOError(ctx.f);
955
956                 if (ctx.hdr.magic == CompiledDefHeader::magic_normal)
957                         ctx.swap = false;
958                 else if (ctx.hdr.magic == CompiledDefHeader::magic_reversed)
959                         ctx.swap = true;
960                 else {
961                         fprintf(stderr, "\"%s\" is not a valid object "
962                                         "(bad magic 0x%08x).\n",
963                                 ctx.filename, ctx.hdr.magic);
964         
965                         throw UserError();
966                 }
967                 
968                 ret = do_load(ctx);
969         }
970         
971         catch (IOError &e) {
972                 fprintf(stderr, "Cannot read from file \"%s\": %s.\n",
973                         ctx.filename, e.msg);
974         
975                 throw UserError();
976         }
977         
978         return ret;
979 }
980
981 NameSpace *check_for_imports(NameSpace *ns)
982 {
983         while (ns) {
984                 if (ns->get_path())
985                         return ns;
986
987                 ns = ns->get_ns();
988         }
989         
990         return NULL;
991 }
992
993 void UserNameSpace::declare_import(const char *path)
994 {
995         ImportContext ctx;
996         UserNameSpace *ns = new UserNameSpace();
997         string filename(path);
998         filename.append("/.self");
999
1000         ctx.parent = NULL;
1001         ctx.filename = path;
1002         
1003         ctx.f = fopen(filename.c_str(), "rb");
1004         if (!ctx.f) {
1005                 fprintf(stderr, "Cannot import \"%s\": %s.\n",
1006                         path, strerror(errno));
1007         
1008                 throw UserError();
1009         }
1010         
1011         const String *mount;
1012         try {
1013                 if (fread(&ctx.hdr, sizeof(ctx.hdr), 1, ctx.f) != 1)
1014                         throw IOError(ctx.f);
1015         
1016                 if (ctx.hdr.magic == CompiledDefHeader::magic_normal)
1017                         ctx.swap = false;
1018                 else if (ctx.hdr.magic == CompiledDefHeader::magic_reversed)
1019                         ctx.swap = true;
1020                 else {
1021                         fprintf(stderr, "\"%s\" is not a valid object "
1022                                         "(bad magic 0x%08x).\n",
1023                                 ctx.filename, ctx.hdr.magic);
1024         
1025                         throw UserError();
1026                 }
1027                 
1028                 mount = read_string(ctx);
1029         }
1030         
1031         catch (IOError &e) {
1032                 fprintf(stderr, "Cannot read from file \"%s\": %s.\n",
1033                         ctx.filename, e.msg);
1034         
1035                 throw UserError();
1036         }
1037
1038         StrList *strl = new StrList(mount);
1039         if (!strl) {
1040                 fprintf(stderr, "\"%s\" is not a valid object "
1041                                 "(mount point \"%s\" is not valid).\n",
1042                         ctx.filename, mount->c_str());
1043
1044                 throw UserError();
1045         }
1046
1047         ns->name = strl->back();
1048         strl->pop_back();
1049         
1050         if (strl->size() != 0) {
1051                 ctx.parent = add_nspace(strl, false);
1052
1053                 if (!ctx.parent)
1054                         BUG();
1055                 
1056                 NameSpace *conflict = check_for_imports(ctx.parent);
1057                 if (conflict) {         
1058                         fprintf(stderr, "Import \"%s\" conflicts"
1059                                         " with \"%s\" at \"%s\".\n",
1060                                 path, conflict->get_fq_name()->flatten()->c_str(),
1061                                 conflict->get_path()->c_str());
1062
1063                         throw UserError();
1064                 }
1065         } else {
1066                 ctx.parent = toplevel;
1067         }
1068         
1069         ns->path = new String(path);
1070         ctx.parent->add_import(ns, ctx.filename);
1071 }
1072
1073 void NameSpace::import_all()
1074 {
1075         if (!path)
1076                 return;
1077         
1078         DIR *dir = opendir(path->c_str());
1079         if (!dir) {
1080                 fprintf(stderr, "Cannot open directory \"%s\".\n", path->c_str());
1081                 throw UserError();
1082         }
1083         
1084         struct dirent ent, *entp;
1085         while (true) {
1086                 // readdir_r is buggy on osx 10.2, and will fail if errno is
1087                 // non-zero.
1088                 errno = 0;
1089                 
1090                 if (readdir_r(dir, &ent, &entp)) {
1091                         fprintf(stderr, "1 Cannot readdir on \"%s\": %s.\n",
1092                                 path->c_str(), strerror(errno));
1093
1094                         closedir(dir);
1095                         throw UserError();
1096                 }
1097                 
1098                 if (!entp)
1099                         break;
1100                 
1101                 // Ignore ".", "..", and ".self".
1102                 if (ent.d_name[0] == '.')
1103                         continue;
1104                 
1105                 try {
1106                         char *under = strchr(ent.d_name, '_');
1107                         if (!under) {
1108                                 fprintf(stderr, "\"%s\" is not a valid namespace "
1109                                                 "(bad member \"%s\").\n",
1110                                         path->c_str(), ent.d_name);
1111
1112                                 throw UserError();
1113                         }
1114                         
1115                         String *s = new String(under + 1);
1116                         lookup(s);
1117                 }
1118                 
1119                 catch (SymbolNotFound) {
1120                         fprintf(stderr, "\"%s\" is not a valid namespace "
1121                                         "(member \"%s\" disappeared).\n",
1122                                 path->c_str(), ent.d_name);
1123
1124                         closedir(dir);
1125                         throw UserError();
1126                 }
1127         }
1128         
1129         closedir(dir);
1130 }
1131
1132 void NameSpace::import_all_recursive()
1133 {
1134         import_all();
1135         
1136         for (const_iterator i = begin(); i != end(); ++i) {
1137                 Symbol *sym = (*i).second;
1138                 NameSpace *ns = dynamic_cast<NameSpace *>(sym);
1139                 if (ns)
1140                         ns->import_all_recursive();
1141         }
1142 }