1 /*
2 * The Doomsday Engine Project -- libcore
3 *
4 * Copyright © 2009-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5 *
6 * @par License
7 * LGPL: http://www.gnu.org/licenses/lgpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
15 * General Public License for more details. You should have received a copy of
16 * the GNU Lesser General Public License along with this program; if not, see:
17 * http://www.gnu.org/licenses</small>
18 */
19
20 #include "de/LibraryFile"
21 #include "de/Library"
22 #include "de/NativeFile"
23 #include "de/NativePath"
24 #include "de/LogBuffer"
25
26 #include <QLibrary>
27
28 using namespace de;
29
DENG2_PIMPL_NOREF(LibraryFile)30 DENG2_PIMPL_NOREF(LibraryFile)
31 {
32 Library *library = nullptr;
33 NativePath nativePath;
34 };
35
LibraryFile(File * source)36 LibraryFile::LibraryFile(File *source)
37 : File(source->name())
38 , d(new Impl)
39 {
40 DENG2_ASSERT(source != 0);
41 setSource(source); // takes ownership
42 }
43
LibraryFile(NativePath const & nativePath)44 LibraryFile::LibraryFile(NativePath const &nativePath)
45 : File(nativePath.fileName())
46 , d(new Impl)
47 {
48 d->nativePath = nativePath;
49 }
50
~LibraryFile()51 LibraryFile::~LibraryFile()
52 {
53 DENG2_FOR_AUDIENCE2(Deletion, i) i->fileBeingDeleted(*this);
54 audienceForDeletion().clear();
55
56 deindex();
57 delete d->library;
58 }
59
describe() const60 String LibraryFile::describe() const
61 {
62 String desc = "shared library";
63 if (loaded()) desc += " [" + library().type() + "]";
64 return desc;
65 }
66
loaded() const67 bool LibraryFile::loaded() const
68 {
69 return d->library != 0;
70 }
71
library()72 Library &LibraryFile::library()
73 {
74 if (d->library)
75 {
76 return *d->library;
77 }
78
79 if (!d->nativePath.isEmpty())
80 {
81 d->library = new Library(d->nativePath);
82 }
83 else
84 {
85 /// @todo A mechanism to make a NativeFile out of any File via caching?
86
87 NativeFile *native = maybeAs<NativeFile>(source());
88 if (!native)
89 {
90 /// @throw UnsupportedSourceError Currently shared libraries are only loaded directly
91 /// from native files. Other kinds of files would require a temporary native file.
92 throw UnsupportedSourceError("LibraryFile::library", source()->description() +
93 ": can only load from NativeFile");
94 }
95 d->library = new Library(native->nativePath());
96 }
97 return *d->library;
98 }
99
library() const100 Library const &LibraryFile::library() const
101 {
102 if (d->library) return *d->library;
103
104 /// @throw NotLoadedError Library is presently not loaded.
105 throw NotLoadedError("LibraryFile::library", "Library is not loaded: " + description());
106 }
107
clear()108 void LibraryFile::clear()
109 {
110 if (d->library)
111 {
112 delete d->library;
113 d->library = nullptr;
114 }
115 }
116
hasUnderscoreName(String const & nameAfterUnderscore) const117 bool LibraryFile::hasUnderscoreName(String const &nameAfterUnderscore) const
118 {
119 return name().contains("_" + nameAfterUnderscore + ".") ||
120 name().endsWith("_" + nameAfterUnderscore);
121 }
122
recognize(File const & file)123 bool LibraryFile::recognize(File const &file)
124 {
125 #if defined (DENG_APPLE)
126 {
127 // On macOS/iOS, plugins are in the .bundle format. The LibraryFile will point
128 // to the actual binary inside the bundle. Libraries must be loaded from
129 // native files.
130 if (NativeFile const *native = maybeAs<NativeFile>(file))
131 {
132 // Check if this in the executable folder with a matching bundle name.
133 if (native->nativePath().fileNamePath().toString()
134 .endsWith(file.name() + ".bundle/Contents/MacOS"))
135 {
136 return true;
137 }
138 }
139 }
140 #else // not Apple
141 {
142 // Check the extension first.
143 if (QLibrary::isLibrary(file.name()))
144 {
145 #if defined(UNIX)
146 {
147 // Only actual .so files should be considered.
148 if (!file.name().endsWith(".so")) // just checks the file name
149 {
150 return false;
151 }
152 }
153 #endif
154
155 // Looks like a library.
156 return true;
157 }
158 }
159 #endif
160
161 return false;
162 }
163
interpretFile(File * sourceData) const164 File *LibraryFile::Interpreter::interpretFile(File *sourceData) const
165 {
166 if (recognize(*sourceData))
167 {
168 LOG_RES_XVERBOSE("Interpreted %s as a shared library", sourceData->description());
169
170 // It is a shared library intended for Doomsday.
171 return new LibraryFile(sourceData);
172 }
173 return nullptr;
174 }
175