HaikuDepot: notify work status from main window
[haiku.git] / src / kits / support / Archivable.cpp
blobd819d88d56efc15a8a8e217e6b714a79660c4e32
1 /*
2 * Copyright 2001-2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Rene Gollent (rene@gollent.com)
7 * Erik Jaesler (erik@cgsoftware.com)
8 * Alex Wilson (yourpalal2@gmail.com)
9 */
11 /*! BArchivable mix-in class defines the archiving protocol.
12 Also some global archiving functions.
16 #include <ctype.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string>
21 #include <syslog.h>
22 #include <typeinfo>
23 #include <vector>
25 #include <AppFileInfo.h>
26 #include <Archivable.h>
27 #include <Entry.h>
28 #include <List.h>
29 #include <OS.h>
30 #include <Path.h>
31 #include <Roster.h>
32 #include <String.h>
34 #include <binary_compatibility/Support.h>
36 #include "ArchivingManagers.h"
39 using std::string;
40 using std::vector;
42 using namespace BPrivate::Archiving;
44 const char* B_CLASS_FIELD = "class";
45 const char* B_ADD_ON_FIELD = "add_on";
46 const int32 FUNC_NAME_LEN = 1024;
48 // TODO: consider moving these to a separate module, and making them more
49 // full-featured (e.g., taking NS::ClassName::Function(Param p) instead
50 // of just NS::ClassName)
53 static status_t
54 demangle_class_name(const char* name, BString& out)
56 // TODO: add support for template classes
57 // _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
59 out = "";
61 #if __GNUC__ >= 4
62 if (name[0] == 'N')
63 name++;
64 int nameLen;
65 bool first = true;
66 while ((nameLen = strtoul(name, (char**)&name, 10))) {
67 if (!first)
68 out += "::";
69 else
70 first = false;
71 out.Append(name, nameLen);
72 name += nameLen;
74 if (first)
75 return B_BAD_VALUE;
77 #else
78 if (name[0] == 'Q') {
79 // The name is in a namespace
80 int namespaceCount = 0;
81 name++;
82 if (name[0] == '_') {
83 // more than 10 namespaces deep
84 if (!isdigit(*++name))
85 return B_BAD_VALUE;
87 namespaceCount = strtoul(name, (char**)&name, 10);
88 if (name[0] != '_')
89 return B_BAD_VALUE;
90 } else
91 namespaceCount = name[0] - '0';
93 name++;
95 for (int i = 0; i < namespaceCount - 1; i++) {
96 if (!isdigit(name[0]))
97 return B_BAD_VALUE;
99 int nameLength = strtoul(name, (char**)&name, 10);
100 out.Append(name, nameLength);
101 out += "::";
102 name += nameLength;
106 int nameLength = strtoul(name, (char**)&name, 10);
107 out.Append(name, nameLength);
108 #endif
110 return B_OK;
114 static void
115 mangle_class_name(const char* name, BString& out)
117 // TODO: add support for template classes
118 // _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
120 // Chop this:
121 // testthree::testfour::Testthree::Testfour
122 // up into little bite-sized pieces
123 int count = 0;
124 string origName(name);
125 vector<string> spacenames;
127 string::size_type pos = 0;
128 string::size_type oldpos = 0;
129 while (pos != string::npos) {
130 pos = origName.find_first_of("::", oldpos);
131 spacenames.push_back(string(origName, oldpos, pos - oldpos));
132 pos = origName.find_first_not_of("::", pos);
133 oldpos = pos;
134 ++count;
137 // Now mangle it into this:
138 // 9testthree8testfour9Testthree8Testfour
139 // (for __GNUC__ > 2)
140 // this isn't always the proper mangled class name, it should
141 // actually have an 'N' prefix and 'E' suffix if the name is
142 // in > 0 namespaces, but these would have to be removed in
143 // build_function_name() (the only place this function is called)
144 // so we don't add them.
145 // or this:
146 // Q49testthree8testfour9Testthree8Testfour
147 // (for __GNUC__ == 2)
149 out = "";
150 #if __GNUC__ == 2
151 if (count > 1) {
152 out += 'Q';
153 if (count > 10)
154 out += '_';
155 out << count;
156 if (count > 10)
157 out += '_';
159 #endif
161 for (unsigned int i = 0; i < spacenames.size(); ++i) {
162 out << (int)spacenames[i].length();
163 out += spacenames[i].c_str();
168 static void
169 build_function_name(const BString& className, BString& funcName)
171 funcName = "";
173 // This is what we're after:
174 // Instantiate__Q28OpenBeOS11BArchivableP8BMessage
175 mangle_class_name(className.String(), funcName);
176 #if __GNUC__ >= 4
177 funcName.Prepend("_ZN");
178 funcName.Append("11InstantiateE");
179 #else
180 funcName.Prepend("Instantiate__");
181 #endif
182 funcName.Append("P8BMessage");
186 static bool
187 add_private_namespace(BString& name)
189 if (name.Compare("_", 1) != 0)
190 return false;
192 name.Prepend("BPrivate::");
193 return true;
197 static instantiation_func
198 find_function_in_image(BString& funcName, image_id id, status_t& err)
200 instantiation_func instantiationFunc = NULL;
201 err = get_image_symbol(id, funcName.String(), B_SYMBOL_TYPE_TEXT,
202 (void**)&instantiationFunc);
203 if (err != B_OK)
204 return NULL;
206 return instantiationFunc;
210 static status_t
211 check_signature(const char* signature, image_info& info)
213 if (signature == NULL) {
214 // If it wasn't specified, anything "matches"
215 return B_OK;
218 // Get image signature
219 BFile file(info.name, B_READ_ONLY);
220 status_t err = file.InitCheck();
221 if (err != B_OK)
222 return err;
224 char imageSignature[B_MIME_TYPE_LENGTH];
225 BAppFileInfo appFileInfo(&file);
226 err = appFileInfo.GetSignature(imageSignature);
227 if (err != B_OK) {
228 syslog(LOG_ERR, "instantiate_object - couldn't get mime sig for %s",
229 info.name);
230 return err;
233 if (strcmp(signature, imageSignature) != 0)
234 return B_MISMATCHED_VALUES;
236 return B_OK;
240 namespace BPrivate {
242 instantiation_func
243 find_instantiation_func(const char* className, const char* signature,
244 image_id* id)
246 if (className == NULL) {
247 errno = B_BAD_VALUE;
248 return NULL;
251 thread_info threadInfo;
252 status_t err = get_thread_info(find_thread(NULL), &threadInfo);
253 if (err != B_OK) {
254 errno = err;
255 return NULL;
258 instantiation_func instantiationFunc = NULL;
259 image_info imageInfo;
261 BString name = className;
262 for (int32 pass = 0; pass < 2; pass++) {
263 BString funcName;
264 build_function_name(name, funcName);
266 // for each image_id in team_id
267 int32 cookie = 0;
268 while (instantiationFunc == NULL
269 && get_next_image_info(threadInfo.team, &cookie, &imageInfo)
270 == B_OK) {
271 instantiationFunc = find_function_in_image(funcName, imageInfo.id,
272 err);
274 if (instantiationFunc != NULL) {
275 // if requested, save the image id in
276 // which the function was found
277 if (id != NULL)
278 *id = imageInfo.id;
279 break;
282 // Check if we have a private class, and add the BPrivate namespace
283 // (for backwards compatibility)
284 if (!add_private_namespace(name))
285 break;
288 if (instantiationFunc != NULL
289 && check_signature(signature, imageInfo) != B_OK)
290 return NULL;
292 return instantiationFunc;
295 } // namespace BPrivate
298 // #pragma mark - BArchivable
301 BArchivable::BArchivable()
303 fArchivingToken(NULL_TOKEN)
308 BArchivable::BArchivable(BMessage* from)
310 fArchivingToken(NULL_TOKEN)
312 if (BUnarchiver::IsArchiveManaged(from)) {
313 BUnarchiver::PrepareArchive(from);
314 BUnarchiver(from).RegisterArchivable(this);
319 BArchivable::~BArchivable()
324 status_t
325 BArchivable::Archive(BMessage* into, bool deep) const
327 if (!into) {
328 // TODO: logging/other error reporting?
329 return B_BAD_VALUE;
332 if (BManagerBase::ArchiveManager(into))
333 BArchiver(into).RegisterArchivable(this);
335 BString name;
336 status_t status = demangle_class_name(typeid(*this).name(), name);
337 if (status != B_OK)
338 return status;
340 return into->AddString(B_CLASS_FIELD, name);
344 BArchivable*
345 BArchivable::Instantiate(BMessage* from)
347 debugger("Can't create a plain BArchivable object");
348 return NULL;
352 status_t
353 BArchivable::Perform(perform_code d, void* arg)
355 switch (d) {
356 case PERFORM_CODE_ALL_UNARCHIVED:
358 perform_data_all_unarchived* data =
359 (perform_data_all_unarchived*)arg;
361 data->return_value = BArchivable::AllUnarchived(data->archive);
362 return B_OK;
365 case PERFORM_CODE_ALL_ARCHIVED:
367 perform_data_all_archived* data =
368 (perform_data_all_archived*)arg;
370 data->return_value = BArchivable::AllArchived(data->archive);
371 return B_OK;
375 return B_NAME_NOT_FOUND;
379 status_t
380 BArchivable::AllUnarchived(const BMessage* archive)
382 return B_OK;
386 status_t
387 BArchivable::AllArchived(BMessage* archive) const
389 return B_OK;
393 // #pragma mark - BArchiver
396 BArchiver::BArchiver(BMessage* archive)
398 fManager(BManagerBase::ArchiveManager(archive)),
399 fArchive(archive),
400 fFinished(false)
402 if (fManager == NULL)
403 fManager = new BArchiveManager(this);
407 BArchiver::~BArchiver()
409 if (!fFinished)
410 fManager->ArchiverLeaving(this, B_OK);
414 status_t
415 BArchiver::AddArchivable(const char* name, BArchivable* archivable, bool deep)
417 int32 token;
418 status_t err = GetTokenForArchivable(archivable, deep, token);
420 if (err != B_OK)
421 return err;
423 return fArchive->AddInt32(name, token);
427 status_t
428 BArchiver::GetTokenForArchivable(BArchivable* archivable,
429 bool deep, int32& _token)
431 return fManager->ArchiveObject(archivable, deep, _token);
435 bool
436 BArchiver::IsArchived(BArchivable* archivable)
438 return fManager->IsArchived(archivable);
442 status_t
443 BArchiver::Finish(status_t err)
445 if (fFinished)
446 debugger("Finish() called multiple times on same BArchiver.");
448 fFinished = true;
450 return fManager->ArchiverLeaving(this, err);
454 BMessage*
455 BArchiver::ArchiveMessage() const
457 return fArchive;
461 void
462 BArchiver::RegisterArchivable(const BArchivable* archivable)
464 fManager->RegisterArchivable(archivable);
468 // #pragma mark - BUnarchiver
471 BUnarchiver::BUnarchiver(const BMessage* archive)
473 fManager(BManagerBase::UnarchiveManager(archive)),
474 fArchive(archive),
475 fFinished(false)
480 BUnarchiver::~BUnarchiver()
482 if (!fFinished && fManager)
483 fManager->UnarchiverLeaving(this, B_OK);
487 template<>
488 status_t
489 BUnarchiver::GetObject<BArchivable>(int32 token,
490 ownership_policy owning, BArchivable*& object)
492 _CallDebuggerIfManagerNull();
493 return fManager->GetArchivableForToken(token, owning, object);
497 template<>
498 status_t
499 BUnarchiver::FindObject<BArchivable>(const char* name,
500 int32 index, ownership_policy owning, BArchivable*& archivable)
502 archivable = NULL;
503 int32 token;
504 status_t err = fArchive->FindInt32(name, index, &token);
505 if (err != B_OK)
506 return err;
508 return GetObject(token, owning, archivable);
512 bool
513 BUnarchiver::IsInstantiated(int32 token)
515 _CallDebuggerIfManagerNull();
516 return fManager->IsInstantiated(token);
520 bool
521 BUnarchiver::IsInstantiated(const char* field, int32 index)
523 int32 token;
524 if (fArchive->FindInt32(field, index, &token) == B_OK)
525 return IsInstantiated(token);
527 return false;
531 status_t
532 BUnarchiver::Finish(status_t err)
534 if (fFinished)
535 debugger("Finish() called multiple times on same BArchiver.");
537 fFinished = true;
538 if (fManager)
539 return fManager->UnarchiverLeaving(this, err);
540 else
541 return B_OK;
545 const BMessage*
546 BUnarchiver::ArchiveMessage() const
548 return fArchive;
552 void
553 BUnarchiver::AssumeOwnership(BArchivable* archivable)
555 _CallDebuggerIfManagerNull();
556 fManager->AssumeOwnership(archivable);
560 void
561 BUnarchiver::RelinquishOwnership(BArchivable* archivable)
563 _CallDebuggerIfManagerNull();
564 fManager->RelinquishOwnership(archivable);
568 bool
569 BUnarchiver::IsArchiveManaged(const BMessage* archive)
571 // managed child archives will return here
572 if (BManagerBase::ManagerPointer(archive))
573 return true;
575 if (archive == NULL)
576 return false;
578 // managed top level archives return here
579 bool dummy;
580 if (archive->FindBool(kManagedField, &dummy) == B_OK)
581 return true;
583 return false;
587 template<>
588 status_t
589 BUnarchiver::InstantiateObject<BArchivable>(BMessage* from,
590 BArchivable* &object)
592 BUnarchiver unarchiver(BUnarchiver::PrepareArchive(from));
593 object = instantiate_object(from);
594 return unarchiver.Finish();
598 BMessage*
599 BUnarchiver::PrepareArchive(BMessage* &archive)
601 // this check allows PrepareArchive to be
602 // called on new or old-style archives
603 if (BUnarchiver::IsArchiveManaged(archive)) {
604 BUnarchiveManager* manager = BManagerBase::UnarchiveManager(archive);
605 if (!manager)
606 manager = new BUnarchiveManager(archive);
608 manager->Acquire();
611 return archive;
615 void
616 BUnarchiver::RegisterArchivable(BArchivable* archivable)
618 _CallDebuggerIfManagerNull();
619 fManager->RegisterArchivable(archivable);
623 void
624 BUnarchiver::_CallDebuggerIfManagerNull()
626 if (!fManager)
627 debugger("BUnarchiver used with legacy or unprepared archive.");
631 // #pragma mark -
634 BArchivable*
635 instantiate_object(BMessage* archive, image_id* _id)
637 status_t statusBuffer;
638 status_t* status = &statusBuffer;
639 if (_id != NULL)
640 status = _id;
642 // Check our params
643 if (archive == NULL) {
644 syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument");
645 *status = B_BAD_VALUE;
646 return NULL;
649 // Get class name from archive
650 const char* className = NULL;
651 status_t err = archive->FindString(B_CLASS_FIELD, &className);
652 if (err) {
653 syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry "
654 "defining the class name (%s).", strerror(err));
655 *status = B_BAD_VALUE;
656 return NULL;
659 // Get sig from archive
660 const char* signature = NULL;
661 bool hasSignature = archive->FindString(B_ADD_ON_FIELD, &signature) == B_OK;
663 instantiation_func instantiationFunc = BPrivate::find_instantiation_func(
664 className, signature, _id);
666 // if find_instantiation_func() can't locate Class::Instantiate()
667 // and a signature was specified
668 if (!instantiationFunc && hasSignature) {
669 // use BRoster::FindApp() to locate an app or add-on with the symbol
670 BRoster Roster;
671 entry_ref ref;
672 err = Roster.FindApp(signature, &ref);
674 // if an entry_ref is obtained
675 BEntry entry;
676 if (err == B_OK)
677 err = entry.SetTo(&ref);
679 BPath path;
680 if (err == B_OK)
681 err = entry.GetPath(&path);
683 if (err != B_OK) {
684 syslog(LOG_ERR, "instantiate_object failed: Error finding app "
685 "with signature \"%s\" (%s)", signature, strerror(err));
686 *status = err;
687 return NULL;
690 // load the app/add-on
691 image_id addOn = load_add_on(path.Path());
692 if (addOn < B_OK) {
693 syslog(LOG_ERR, "instantiate_object failed: Could not load "
694 "add-on %s: %s.", path.Path(), strerror(addOn));
695 *status = addOn;
696 return NULL;
699 // Save the image_id
700 if (_id != NULL)
701 *_id = addOn;
703 BString name = className;
704 for (int32 pass = 0; pass < 2; pass++) {
705 BString funcName;
706 build_function_name(name, funcName);
708 instantiationFunc = find_function_in_image(funcName, addOn, err);
709 if (instantiationFunc != NULL)
710 break;
712 // Check if we have a private class, and add the BPrivate namespace
713 // (for backwards compatibility)
714 if (!add_private_namespace(name))
715 break;
718 if (instantiationFunc == NULL) {
719 syslog(LOG_ERR, "instantiate_object failed: Failed to find exported "
720 "Instantiate static function for class %s.", className);
721 *status = B_NAME_NOT_FOUND;
722 return NULL;
724 } else if (instantiationFunc == NULL) {
725 syslog(LOG_ERR, "instantiate_object failed: No signature specified "
726 "in archive, looking for class \"%s\".", className);
727 *status = B_NAME_NOT_FOUND;
728 return NULL;
731 // if Class::Instantiate(BMessage*) was found
732 if (instantiationFunc != NULL) {
733 // use to create and return an object instance
734 return instantiationFunc(archive);
737 return NULL;
741 BArchivable*
742 instantiate_object(BMessage* from)
744 return instantiate_object(from, NULL);
748 // #pragma mark - support_globals
751 bool
752 validate_instantiation(BMessage* from, const char* className)
754 // Make sure our params are kosher -- original skimped here =P
755 if (!from) {
756 errno = B_BAD_VALUE;
757 return false;
760 BString name = className;
761 for (int32 pass = 0; pass < 2; pass++) {
762 const char* archiveClassName;
763 for (int32 index = 0; from->FindString(B_CLASS_FIELD, index,
764 &archiveClassName) == B_OK; ++index) {
765 if (name == archiveClassName)
766 return true;
769 if (!add_private_namespace(name))
770 break;
773 errno = B_MISMATCHED_VALUES;
774 syslog(LOG_ERR, "validate_instantiation failed on class %s.", className);
776 return false;
780 instantiation_func
781 find_instantiation_func(const char* className, const char* signature)
783 return BPrivate::find_instantiation_func(className, signature, NULL);
787 instantiation_func
788 find_instantiation_func(const char* className)
790 return find_instantiation_func(className, NULL);
794 instantiation_func
795 find_instantiation_func(BMessage* archive)
797 if (archive == NULL) {
798 errno = B_BAD_VALUE;
799 return NULL;
802 const char* name = NULL;
803 const char* signature = NULL;
804 if (archive->FindString(B_CLASS_FIELD, &name) != B_OK
805 || archive->FindString(B_ADD_ON_FIELD, &signature)) {
806 errno = B_BAD_VALUE;
807 return NULL;
810 return find_instantiation_func(name, signature);
814 // #pragma mark - BArchivable binary compatibility
817 #if __GNUC__ == 2
819 extern "C" status_t
820 _ReservedArchivable1__11BArchivable(BArchivable* archivable,
821 const BMessage* archive)
823 // AllUnarchived
824 perform_data_all_unarchived performData;
825 performData.archive = archive;
827 archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData);
828 return performData.return_value;
832 extern "C" status_t
833 _ReservedArchivable2__11BArchivable(BArchivable* archivable,
834 BMessage* archive)
836 // AllArchived
837 perform_data_all_archived performData;
838 performData.archive = archive;
840 archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData);
841 return performData.return_value;
845 #elif __GNUC__ > 2
847 extern "C" status_t
848 _ZN11BArchivable20_ReservedArchivable1Ev(BArchivable* archivable,
849 const BMessage* archive)
851 // AllUnarchived
852 perform_data_all_unarchived performData;
853 performData.archive = archive;
855 archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData);
856 return performData.return_value;
860 extern "C" status_t
861 _ZN11BArchivable20_ReservedArchivable2Ev(BArchivable* archivable,
862 BMessage* archive)
864 // AllArchived
865 perform_data_all_archived performData;
866 performData.archive = archive;
868 archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData);
869 return performData.return_value;
872 #endif // _GNUC__ > 2
875 void BArchivable::_ReservedArchivable3() {}