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