comparison gcc/go/gofrontend/import-archive.cc @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 1830386684a0
comparison
equal deleted inserted replaced
68:561a7518be6b 111:04ced10e8804
1 // import-archive.cc -- Go frontend read import data from an archive file.
2
3 // Copyright 2009 The Go Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
6
7 #include "go-system.h"
8
9 #include "go-diagnostics.h"
10 #include "import.h"
11
12 #ifndef O_BINARY
13 #define O_BINARY 0
14 #endif
15
16 // Archive magic numbers.
17
18 static const char armag[] =
19 {
20 '!', '<', 'a', 'r', 'c', 'h', '>', '\n'
21 };
22
23 static const char armagt[] =
24 {
25 '!', '<', 't', 'h', 'i', 'n', '>', '\n'
26 };
27
28 static const char armagb[] =
29 {
30 '<', 'b', 'i', 'g', 'a', 'f', '>', '\n'
31 };
32
33 static const char arfmag[2] = { '`', '\n' };
34
35 // Archive fixed length header for AIX big format.
36
37 struct Archive_fl_header
38 {
39 // Archive magic string.
40 char fl_magic[8];
41 // Offset to member table.
42 char fl_memoff[20];
43 // Offset to global symbol table.
44 char fl_gstoff[20];
45 // Offset to global symbol table for 64-bit objects.
46 char fl_gst64off[20];
47 // Offset to first archive member.
48 char fl_fstmoff[20];
49 // Offset to last archive member.
50 char fl_lstmoff[20];
51 // Offset to first member on free list.
52 char fl_freeoff[20];
53 };
54
55 // The header of an entry in an archive. This is all readable text,
56 // padded with spaces where necesary.
57
58 struct Archive_header
59 {
60 // The entry name.
61 char ar_name[16];
62 // The file modification time.
63 char ar_date[12];
64 // The user's UID in decimal.
65 char ar_uid[6];
66 // The user's GID in decimal.
67 char ar_gid[6];
68 // The file mode in octal.
69 char ar_mode[8];
70 // The file size in decimal.
71 char ar_size[10];
72 // The final magic code.
73 char ar_fmag[2];
74 };
75
76 // The header of an entry in an AIX big archive.
77 // This is followed by ar_namlen bytes + 2 bytes for arfmag.
78
79 struct Archive_big_header
80 {
81 // The file size in decimal.
82 char ar_size[20];
83 // The next member offset in decimal.
84 char ar_nxtmem[20];
85 // The previous member offset in decimal.
86 char ar_prvmem[20];
87 // The file modification time in decimal.
88 char ar_date[12];
89 // The user's UID in decimal.
90 char ar_uid[12];
91 // The user's GID in decimal.
92 char ar_gid[12];
93 // The file mode in octal.
94 char ar_mode[12];
95 // The file name length in decimal.
96 char ar_namlen[4];
97 };
98
99 // The functions in this file extract Go export data from an archive.
100
101 const int Import::archive_magic_len;
102
103 // Return true if BYTES, which are from the start of the file, are an
104 // archive magic number.
105
106 bool
107 Import::is_archive_magic(const char* bytes)
108 {
109 return (memcmp(bytes, armag, Import::archive_magic_len) == 0
110 || memcmp(bytes, armagt, Import::archive_magic_len) == 0
111 || memcmp(bytes, armagb, Import::archive_magic_len) == 0);
112 }
113
114 // An object used to read an archive file.
115
116 class Archive_file
117 {
118 public:
119 Archive_file(const std::string& filename, int fd, Location location)
120 : filename_(filename), fd_(fd), filesize_(-1), first_member_offset_(0),
121 extended_names_(), is_thin_archive_(false), is_big_archive_(false),
122 location_(location), nested_archives_()
123 { }
124
125 // Initialize.
126 bool
127 initialize();
128
129 // Return the file name.
130 const std::string&
131 filename() const
132 { return this->filename_; }
133
134 // Get the file size.
135 off_t
136 filesize() const
137 { return this->filesize_; }
138
139 // Return the offset of the first member.
140 off_t
141 first_member_offset() const
142 { return this->first_member_offset_; }
143
144 // Return whether this is a thin archive.
145 bool
146 is_thin_archive() const
147 { return this->is_thin_archive_; }
148
149 // Return whether this is a big archive.
150 bool
151 is_big_archive() const
152 { return this->is_big_archive_; }
153
154 // Return the location of the import statement.
155 Location
156 location() const
157 { return this->location_; }
158
159 // Read bytes.
160 bool
161 read(off_t offset, off_t size, char*);
162
163 // Parse a decimal in readable text.
164 bool
165 parse_decimal(const char* str, off_t size, long* res) const;
166
167 // Read the archive header at OFF, setting *PNAME, *SIZE,
168 // *NESTED_OFF and *NEXT_OFF.
169 bool
170 read_header(off_t off, std::string* pname, off_t* size, off_t* nested_off,
171 off_t* next_off);
172
173 // Interpret the header of HDR, the header of the archive member at
174 // file offset OFF. Return whether it succeeded. Set *SIZE to the
175 // size of the member. Set *PNAME to the name of the member. Set
176 // *NESTED_OFF to the offset in a nested archive.
177 bool
178 interpret_header(const Archive_header* hdr, off_t off,
179 std::string* pname, off_t* size, off_t* nested_off) const;
180
181 // Get the file and offset for an archive member.
182 bool
183 get_file_and_offset(off_t off, const std::string& hdrname,
184 off_t nested_off, int* memfd, off_t* memoff,
185 std::string* memname);
186
187 private:
188 // Initialize a big archive (AIX)
189 bool
190 initialize_big_archive();
191
192 // Initialize a normal archive
193 bool
194 initialize_archive();
195
196 // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
197 bool
198 read_big_archive_header(off_t off, std::string* pname,
199 off_t* size, off_t* next_off);
200
201 // Read the normal archive header at OFF, setting *PNAME, *SIZE,
202 // *NESTED_OFF and *NEXT_OFF.
203 bool
204 read_archive_header(off_t off, std::string* pname, off_t* size,
205 off_t* nested_off, off_t* next_off);
206
207 // For keeping track of open nested archives in a thin archive file.
208 typedef std::map<std::string, Archive_file*> Nested_archive_table;
209
210 // The name of the file.
211 std::string filename_;
212 // The file descriptor.
213 int fd_;
214 // The file size;
215 off_t filesize_;
216 // The first member offset;
217 off_t first_member_offset_;
218 // The extended name table.
219 std::string extended_names_;
220 // Whether this is a thin archive.
221 bool is_thin_archive_;
222 // Whether this is a big archive.
223 bool is_big_archive_;
224 // The location of the import statements.
225 Location location_;
226 // Table of nested archives.
227 Nested_archive_table nested_archives_;
228 };
229
230 bool
231 Archive_file::initialize()
232 {
233 struct stat st;
234 if (fstat(this->fd_, &st) < 0)
235 {
236 go_error_at(this->location_, "%s: %m", this->filename_.c_str());
237 return false;
238 }
239 this->filesize_ = st.st_size;
240
241 char buf[sizeof(armagt)];
242 if (::lseek(this->fd_, 0, SEEK_SET) < 0
243 || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt))
244 {
245 go_error_at(this->location_, "%s: %m", this->filename_.c_str());
246 return false;
247 }
248 if (memcmp(buf, armagt, sizeof(armagt)) == 0)
249 this->is_thin_archive_ = true;
250 else if (memcmp(buf, armagb, sizeof(armagb)) == 0)
251 this->is_big_archive_ = true;
252
253 if (this->is_big_archive_)
254 return this->initialize_big_archive();
255 else
256 return this->initialize_archive();
257 }
258
259 // Initialize a big archive (AIX).
260
261 bool
262 Archive_file::initialize_big_archive()
263 {
264 Archive_fl_header flhdr;
265
266 // Read the fixed length header.
267 if (::lseek(this->fd_, 0, SEEK_SET) < 0
268 || ::read(this->fd_, &flhdr, sizeof(flhdr)) != sizeof(flhdr))
269 {
270 go_error_at(this->location_, "%s: could not read archive header",
271 this->filename_.c_str());
272 return false;
273 }
274
275 // Parse offset of the first member.
276 long off;
277 if (!this->parse_decimal(flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff), &off))
278 {
279 char* buf = new char[sizeof(flhdr.fl_fstmoff) + 1];
280 memcpy(buf, flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff));
281 go_error_at(this->location_,
282 ("%s: malformed first member offset in archive header"
283 " (expected decimal, got %s)"),
284 this->filename_.c_str(), buf);
285 delete[] buf;
286 return false;
287 }
288 if (off == 0) // Empty archive.
289 this->first_member_offset_ = this->filesize_;
290 else
291 this->first_member_offset_ = off;
292 return true;
293 }
294
295 // Initialize a normal archive.
296
297 bool
298 Archive_file::initialize_archive()
299 {
300 this->first_member_offset_ = sizeof(armag);
301 if (this->first_member_offset_ == this->filesize_)
302 {
303 // Empty archive.
304 return true;
305 }
306
307 // Look for the extended name table.
308 std::string filename;
309 off_t size;
310 off_t next_off;
311 if (!this->read_header(this->first_member_offset_, &filename,
312 &size, NULL, &next_off))
313 return false;
314 if (filename.empty())
315 {
316 // We found the symbol table.
317 if (!this->read_header(next_off, &filename, &size, NULL, NULL))
318 filename.clear();
319 }
320 if (filename == "/")
321 {
322 char* rdbuf = new char[size];
323 if (::read(this->fd_, rdbuf, size) != size)
324 {
325 go_error_at(this->location_, "%s: could not read extended names",
326 filename.c_str());
327 delete[] rdbuf;
328 return false;
329 }
330 this->extended_names_.assign(rdbuf, size);
331 delete[] rdbuf;
332 }
333
334 return true;
335 }
336
337 // Read bytes from the file.
338
339 bool
340 Archive_file::read(off_t offset, off_t size, char* buf)
341 {
342 if (::lseek(this->fd_, offset, SEEK_SET) < 0
343 || ::read(this->fd_, buf, size) != size)
344 {
345 go_error_at(this->location_, "%s: %m", this->filename_.c_str());
346 return false;
347 }
348 return true;
349 }
350
351 // Parse a decimal in readable text.
352
353 bool
354 Archive_file::parse_decimal(const char* str, off_t size, long* res) const
355 {
356 char* buf = new char[size + 1];
357 memcpy(buf, str, size);
358 char* ps = buf + size;
359 while (ps > buf && ps[-1] == ' ')
360 --ps;
361 *ps = '\0';
362
363 errno = 0;
364 char* end;
365 *res = strtol(buf, &end, 10);
366 if (*end != '\0'
367 || *res < 0
368 || (*res == LONG_MAX && errno == ERANGE))
369 {
370 delete[] buf;
371 return false;
372 }
373 delete[] buf;
374 return true;
375 }
376
377 // Read the header at OFF. Set *PNAME to the name, *SIZE to the size,
378 // *NESTED_OFF to the nested offset, and *NEXT_OFF to the next member offset.
379
380 bool
381 Archive_file::read_header(off_t off, std::string* pname, off_t* size,
382 off_t* nested_off, off_t* next_off)
383 {
384 if (::lseek(this->fd_, off, SEEK_SET) < 0)
385 {
386 go_error_at(this->location_, "%s: %m", this->filename_.c_str());
387 return false;
388 }
389 if (this->is_big_archive_)
390 return this->read_big_archive_header(off, pname, size, next_off);
391 else
392 return this->read_archive_header(off, pname, size, nested_off, next_off);
393 }
394
395 // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
396
397 bool
398 Archive_file::read_big_archive_header(off_t off, std::string* pname,
399 off_t* size, off_t* next_off)
400 {
401 Archive_big_header hdr;
402 ssize_t got;
403
404 got = ::read(this->fd_, &hdr, sizeof hdr);
405 if (got != sizeof hdr)
406 {
407 if (got < 0)
408 go_error_at(this->location_, "%s: %m", this->filename_.c_str());
409 else if (got > 0)
410 go_error_at(this->location_, "%s: short entry header at %ld",
411 this->filename_.c_str(), static_cast<long>(off));
412 else
413 go_error_at(this->location_, "%s: unexpected EOF at %ld",
414 this->filename_.c_str(), static_cast<long>(off));
415 }
416
417 long local_size;
418 if (!this->parse_decimal(hdr.ar_size, sizeof(hdr.ar_size), &local_size))
419 {
420 char* buf = new char[sizeof(hdr.ar_size) + 1];
421 memcpy(buf, hdr.ar_size, sizeof(hdr.ar_size));
422 go_error_at(this->location_,
423 ("%s: malformed ar_size in entry header at %ld"
424 " (expected decimal, got %s)"),
425 this->filename_.c_str(), static_cast<long>(off), buf);
426 delete[] buf;
427 return false;
428 }
429 *size = local_size;
430
431 long namlen;
432 if (!this->parse_decimal(hdr.ar_namlen, sizeof(hdr.ar_namlen), &namlen))
433 {
434 char* buf = new char[sizeof(hdr.ar_namlen) + 1];
435 memcpy(buf, hdr.ar_namlen, sizeof(hdr.ar_namlen));
436 go_error_at(this->location_,
437 ("%s: malformed ar_namlen in entry header at %ld"
438 " (expected decimal, got %s)"),
439 this->filename_.c_str(), static_cast<long>(off), buf);
440 delete[] buf;
441 return false;
442 }
443 // Read member name following member header.
444 char* rdbuf = new char[namlen];
445 got = ::read(this->fd_, rdbuf, namlen);
446 if (got != namlen)
447 {
448 go_error_at(this->location_,
449 "%s: malformed member name in entry header at %ld",
450 this->filename_.c_str(), static_cast<long>(off));
451 delete[] rdbuf;
452 return false;
453 }
454 pname->assign(rdbuf, namlen);
455 delete[] rdbuf;
456
457 long local_next_off;
458 if (!this->parse_decimal(hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem), &local_next_off))
459 {
460 char* buf = new char[sizeof(hdr.ar_nxtmem) + 1];
461 memcpy(buf, hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem));
462 go_error_at(this->location_,
463 ("%s: malformed ar_nxtmem in entry header at %ld"
464 " (expected decimal, got %s)"),
465 this->filename_.c_str(), static_cast<long>(off), buf);
466 delete[] buf;
467 return false;
468 }
469 if (next_off != NULL)
470 {
471 if (local_next_off == 0) // Last member.
472 *next_off = this->filesize_;
473 else
474 *next_off = local_next_off;
475 }
476 return true;
477 }
478
479 // Read the normal archive header at OFF, setting *PNAME, *SIZE,
480 // *NESTED_OFF and *NEXT_OFF.
481
482 bool
483 Archive_file::read_archive_header(off_t off, std::string* pname, off_t* size,
484 off_t* nested_off, off_t* next_off)
485 {
486 Archive_header hdr;
487 ssize_t got = ::read(this->fd_, &hdr, sizeof hdr);
488 if (got != sizeof hdr)
489 {
490 if (got < 0)
491 go_error_at(this->location_, "%s: %m", this->filename_.c_str());
492 else if (got > 0)
493 go_error_at(this->location_, "%s: short archive header at %ld",
494 this->filename_.c_str(), static_cast<long>(off));
495 else
496 go_error_at(this->location_, "%s: unexpected EOF at %ld",
497 this->filename_.c_str(), static_cast<long>(off));
498 }
499 off_t local_nested_off;
500 if (!this->interpret_header(&hdr, off, pname, size, &local_nested_off))
501 return false;
502 if (nested_off != NULL)
503 *nested_off = local_nested_off;
504
505 off_t local_next_off;
506 local_next_off = off + sizeof(Archive_header);
507 if (!this->is_thin_archive_ || pname->empty() || *pname == "/")
508 local_next_off += *size;
509 if ((local_next_off & 1) != 0)
510 ++local_next_off;
511 if (local_next_off > this->filesize_) // Last member.
512 local_next_off = this->filesize_;
513 if (next_off != NULL)
514 *next_off = local_next_off;
515 return true;
516 }
517
518 // Interpret the header of HDR, the header of the archive member at
519 // file offset OFF.
520
521 bool
522 Archive_file::interpret_header(const Archive_header* hdr, off_t off,
523 std::string* pname, off_t* size,
524 off_t* nested_off) const
525 {
526 if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0)
527 {
528 go_error_at(this->location_, "%s: malformed archive header at %lu",
529 this->filename_.c_str(), static_cast<unsigned long>(off));
530 return false;
531 }
532
533 long local_size;
534 if (!this->parse_decimal(hdr->ar_size, sizeof hdr->ar_size, &local_size))
535 {
536 go_error_at(this->location_, "%s: malformed archive header size at %lu",
537 this->filename_.c_str(), static_cast<unsigned long>(off));
538 return false;
539 }
540 *size = local_size;
541
542 *nested_off = 0;
543 if (hdr->ar_name[0] != '/')
544 {
545 const char* name_end = strchr(hdr->ar_name, '/');
546 if (name_end == NULL
547 || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name))
548 {
549 go_error_at(this->location_,
550 "%s: malformed archive header name at %lu",
551 this->filename_.c_str(), static_cast<unsigned long>(off));
552 return false;
553 }
554 pname->assign(hdr->ar_name, name_end - hdr->ar_name);
555 }
556 else if (hdr->ar_name[1] == ' ')
557 {
558 // This is the symbol table.
559 pname->clear();
560 }
561 else if (hdr->ar_name[1] == 'S' && hdr->ar_name[2] == 'Y'
562 && hdr->ar_name[3] == 'M' && hdr->ar_name[4] == '6'
563 && hdr->ar_name[5] == '4' && hdr->ar_name[6] == '/'
564 && hdr->ar_name[7] == ' '
565 )
566 {
567 // 64-bit symbol table.
568 pname->clear();
569 }
570 else if (hdr->ar_name[1] == '/')
571 {
572 // This is the extended name table.
573 pname->assign(1, '/');
574 }
575 else
576 {
577 char* end;
578 errno = 0;
579 long x = strtol(hdr->ar_name + 1, &end, 10);
580 long y = 0;
581 if (*end == ':')
582 y = strtol(end + 1, &end, 10);
583 if (*end != ' '
584 || x < 0
585 || (x == LONG_MAX && errno == ERANGE)
586 || static_cast<size_t>(x) >= this->extended_names_.size())
587 {
588 go_error_at(this->location_, "%s: bad extended name index at %lu",
589 this->filename_.c_str(), static_cast<unsigned long>(off));
590 return false;
591 }
592
593 const char* name = this->extended_names_.data() + x;
594 const char* name_end = strchr(name, '\n');
595 if (static_cast<size_t>(name_end - name) > this->extended_names_.size()
596 || name_end[-1] != '/')
597 {
598 go_error_at(this->location_,
599 "%s: bad extended name entry at header %lu",
600 this->filename_.c_str(), static_cast<unsigned long>(off));
601 return false;
602 }
603 pname->assign(name, name_end - 1 - name);
604 *nested_off = y;
605 }
606
607 return true;
608 }
609
610 // Get the file and offset for an archive member.
611
612 bool
613 Archive_file::get_file_and_offset(off_t off, const std::string& hdrname,
614 off_t nested_off, int* memfd, off_t* memoff,
615 std::string* memname)
616 {
617 if (this->is_big_archive_)
618 {
619 *memfd = this->fd_;
620 *memoff = (off + sizeof(Archive_big_header) + hdrname.length()
621 + sizeof(arfmag));
622 if ((*memoff & 1) != 0)
623 ++*memoff;
624 *memname = this->filename_ + '(' + hdrname + ')';
625 return true;
626 }
627 else if (!this->is_thin_archive_)
628 {
629 *memfd = this->fd_;
630 *memoff = off + sizeof(Archive_header);
631 *memname = this->filename_ + '(' + hdrname + ')';
632 return true;
633 }
634
635 std::string filename = hdrname;
636 if (!IS_ABSOLUTE_PATH(filename.c_str()))
637 {
638 const char* archive_path = this->filename_.c_str();
639 const char* basename = lbasename(archive_path);
640 if (basename > archive_path)
641 filename.replace(0, 0,
642 this->filename_.substr(0, basename - archive_path));
643 }
644
645 if (nested_off > 0)
646 {
647 // This is a member of a nested archive.
648 Archive_file* nfile;
649 Nested_archive_table::const_iterator p =
650 this->nested_archives_.find(filename);
651 if (p != this->nested_archives_.end())
652 nfile = p->second;
653 else
654 {
655 int nfd = open(filename.c_str(), O_RDONLY | O_BINARY);
656 if (nfd < 0)
657 {
658 go_error_at(this->location_, "%s: can't open nested archive %s",
659 this->filename_.c_str(), filename.c_str());
660 return false;
661 }
662 nfile = new Archive_file(filename, nfd, this->location_);
663 if (!nfile->initialize())
664 {
665 delete nfile;
666 return false;
667 }
668 this->nested_archives_[filename] = nfile;
669 }
670
671 std::string nname;
672 off_t nsize;
673 off_t nnested_off;
674 if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off, NULL))
675 return false;
676 return nfile->get_file_and_offset(nested_off, nname, nnested_off,
677 memfd, memoff, memname);
678 }
679
680 // An external member of a thin archive.
681 *memfd = open(filename.c_str(), O_RDONLY | O_BINARY);
682 if (*memfd < 0)
683 {
684 go_error_at(this->location_, "%s: %m", filename.c_str());
685 return false;
686 }
687 *memoff = 0;
688 *memname = filename;
689 return true;
690 }
691
692 // An archive member iterator. This is more-or-less copied from gold.
693
694 class Archive_iterator
695 {
696 public:
697 // The header of an archive member. This is what this iterator
698 // points to.
699 struct Header
700 {
701 // The name of the member.
702 std::string name;
703 // The file offset of the member.
704 off_t off;
705 // The file offset of a nested archive member.
706 off_t nested_off;
707 // The size of the member.
708 off_t size;
709 };
710
711 Archive_iterator(Archive_file* afile, off_t off)
712 : afile_(afile), off_(off)
713 { this->read_next_header(); }
714
715 const Header&
716 operator*() const
717 { return this->header_; }
718
719 const Header*
720 operator->() const
721 { return &this->header_; }
722
723 Archive_iterator&
724 operator++()
725 {
726 if (this->off_ == this->afile_->filesize())
727 return *this;
728 this->off_ = this->next_off_;
729 this->read_next_header();
730 return *this;
731 }
732
733 Archive_iterator
734 operator++(int)
735 {
736 Archive_iterator ret = *this;
737 ++*this;
738 return ret;
739 }
740
741 bool
742 operator==(const Archive_iterator& p) const
743 { return this->off_ == p->off; }
744
745 bool
746 operator!=(const Archive_iterator& p) const
747 { return this->off_ != p->off; }
748
749 private:
750 void
751 read_next_header();
752
753 // The underlying archive file.
754 Archive_file* afile_;
755 // The current offset in the file.
756 off_t off_;
757 // The offset of the next member.
758 off_t next_off_;
759 // The current archive header.
760 Header header_;
761 };
762
763 // Read the next archive header.
764
765 void
766 Archive_iterator::read_next_header()
767 {
768 off_t filesize = this->afile_->filesize();
769 while (true)
770 {
771 if (this->off_ == filesize)
772 {
773 this->header_.off = filesize;
774 return;
775 }
776
777 if (!this->afile_->read_header(this->off_, &this->header_.name,
778 &this->header_.size,
779 &this->header_.nested_off,
780 &this->next_off_))
781 {
782 this->header_.off = filesize;
783 return;
784 }
785 this->header_.off = this->off_;
786
787 // Skip special members.
788 if (!this->header_.name.empty() && this->header_.name != "/")
789 return;
790
791 this->off_ = this->next_off_;
792 }
793 }
794
795 // Initial iterator.
796
797 Archive_iterator
798 archive_begin(Archive_file* afile)
799 {
800 return Archive_iterator(afile, afile->first_member_offset());
801 }
802
803 // Final iterator.
804
805 Archive_iterator
806 archive_end(Archive_file* afile)
807 {
808 return Archive_iterator(afile, afile->filesize());
809 }
810
811 // A type of Import_stream which concatenates other Import_streams
812 // together.
813
814 class Stream_concatenate : public Import::Stream
815 {
816 public:
817 Stream_concatenate()
818 : inputs_()
819 { }
820
821 // Add a new stream.
822 void
823 add(Import::Stream* is)
824 { this->inputs_.push_back(is); }
825
826 protected:
827 bool
828 do_peek(size_t, const char**);
829
830 void
831 do_advance(size_t);
832
833 private:
834 std::list<Import::Stream*> inputs_;
835 };
836
837 // Peek ahead.
838
839 bool
840 Stream_concatenate::do_peek(size_t length, const char** bytes)
841 {
842 while (true)
843 {
844 if (this->inputs_.empty())
845 return false;
846 if (this->inputs_.front()->peek(length, bytes))
847 return true;
848 delete this->inputs_.front();
849 this->inputs_.pop_front();
850 }
851 }
852
853 // Advance.
854
855 void
856 Stream_concatenate::do_advance(size_t skip)
857 {
858 while (true)
859 {
860 if (this->inputs_.empty())
861 return;
862 if (!this->inputs_.front()->at_eof())
863 {
864 // We just assume that this will do the right thing. It
865 // should be OK since we should never want to skip past
866 // multiple streams.
867 this->inputs_.front()->advance(skip);
868 return;
869 }
870 delete this->inputs_.front();
871 this->inputs_.pop_front();
872 }
873 }
874
875 // Import data from an archive. We walk through the archive and
876 // import data from each member.
877
878 Import::Stream*
879 Import::find_archive_export_data(const std::string& filename, int fd,
880 Location location)
881 {
882 Archive_file afile(filename, fd, location);
883 if (!afile.initialize())
884 return NULL;
885
886 Stream_concatenate* ret = new Stream_concatenate;
887
888 bool any_data = false;
889 bool any_members = false;
890 Archive_iterator pend = archive_end(&afile);
891 for (Archive_iterator p = archive_begin(&afile); p != pend; p++)
892 {
893 any_members = true;
894 int member_fd;
895 off_t member_off;
896 std::string member_name;
897 if (!afile.get_file_and_offset(p->off, p->name, p->nested_off,
898 &member_fd, &member_off, &member_name))
899 return NULL;
900
901 Import::Stream* is = Import::find_object_export_data(member_name,
902 member_fd,
903 member_off,
904 location);
905 if (is != NULL)
906 {
907 ret->add(is);
908 any_data = true;
909 }
910 }
911
912 if (!any_members)
913 {
914 // It's normal to have an empty archive file when using gobuild.
915 return new Stream_from_string("");
916 }
917
918 if (!any_data)
919 {
920 delete ret;
921 return NULL;
922 }
923
924 return ret;
925 }