1 /*
2  * The Doomsday Engine Project -- libcore
3  *
4  * Copyright © 2011-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/c_wrapper.h"
21 #include "de/Error"
22 #include "de/App"
23 #include "de/CommandLine"
24 #include "de/Loop"
25 #include "de/Address"
26 #include "de/ByteRefArray"
27 #include "de/Block"
28 #include "de/LogBuffer"
29 #include "de/ByteOrder"
30 #include "de/Info"
31 #include "de/UnixInfo"
32 #include <QFile>
33 #include <cstring>
34 #include <stdarg.h>
35 
36 #define DENG2_COMMANDLINE()     DENG2_APP->commandLine()
37 
checkLogEntryMetadata(unsigned int & metadata)38 static bool checkLogEntryMetadata(unsigned int &metadata)
39 {
40     // Automatically apply the generic domain if not specified.
41     if (!(metadata & de::LogEntry::DomainMask))
42     {
43         metadata |= de::LogEntry::Generic;
44     }
45 
46     // Validate the level.
47     de::LogEntry::Level logLevel = de::LogEntry::Level(metadata & de::LogEntry::LevelMask);
48     if (logLevel < de::LogEntry::XVerbose || logLevel > de::LogEntry::Critical)
49     {
50         metadata &= ~de::LogEntry::LevelMask;
51         metadata |= de::LogEntry::Message;
52     }
53 
54     // If this level is not enabled, just ignore.
55     return de::LogBuffer::get().isEnabled(metadata);
56 }
57 
logFragmentPrinter(duint32 metadata,char const * fragment)58 static void logFragmentPrinter(duint32 metadata, char const *fragment)
59 {
60     static std::string currentLogLine;
61 
62     currentLogLine += fragment;
63 
64     std::string::size_type pos;
65     while ((pos = currentLogLine.find('\n')) != std::string::npos)
66     {
67         LOG().enter(metadata, currentLogLine.substr(0, pos).c_str());
68         currentLogLine.erase(0, pos + 1);
69     }
70 }
71 
App_Log(unsigned int metadata,char const * format,...)72 void App_Log(unsigned int metadata, char const *format, ...)
73 {
74     if (!checkLogEntryMetadata(metadata)) return;
75 
76     char buffer[0x2000];
77     va_list args;
78     va_start(args, format);
79     size_t nc = vsprintf(buffer, format, args); /// @todo unsafe
80     va_end(args);
81     DENG2_ASSERT(nc < sizeof(buffer) - 2);
82     if (!nc) return;
83 
84     LOG().enter(metadata, buffer);
85 
86     // Make sure there's a newline in the end.
87     /*if (buffer[nc - 1] != '\n')
88     {
89         buffer[nc++] = '\n';
90         buffer[nc] = 0;
91     }
92     logFragmentPrinter(metadata, buffer);*/
93 }
94 
App_Timer(unsigned int milliseconds,void (* callback)(void))95 void App_Timer(unsigned int milliseconds, void (*callback)(void))
96 {
97     de::Loop::timer(de::TimeSpan::fromMilliSeconds(milliseconds), callback);
98 }
99 
App_FatalError(char const * msgFormat,...)100 void App_FatalError(char const *msgFormat, ...)
101 {
102     char buffer[4096];
103     de::zap(buffer);
104 
105     va_list args;
106     va_start(args, msgFormat);
107     qvsnprintf(buffer, sizeof(buffer) - 1, msgFormat, args);
108     va_end(args);
109 
110     DENG2_APP->handleUncaughtException(buffer);
111 
112     // Let's make sure this is the end.
113     exit(-1);
114 }
115 
CommandLine_Alias(char const * longname,char const * shortname)116 void CommandLine_Alias(char const *longname, char const *shortname)
117 {
118     DENG2_COMMANDLINE().alias(longname, shortname);
119 }
120 
CommandLine_Count(void)121 int CommandLine_Count(void)
122 {
123     return DENG2_COMMANDLINE().count();
124 }
125 
CommandLine_At(int i)126 char const *CommandLine_At(int i)
127 {
128     DENG2_ASSERT(i >= 0);
129     DENG2_ASSERT(i < DENG2_COMMANDLINE().count());
130     return *(DENG2_COMMANDLINE().argv() + i);
131 }
132 
CommandLine_PathAt(int i)133 char const *CommandLine_PathAt(int i)
134 {
135     DENG2_COMMANDLINE().makeAbsolutePath(i);
136     return CommandLine_At(i);
137 }
138 
139 static int argLastMatch = 0; // used only in ArgCheck/ArgNext (not thread-safe)
140 
CommandLine_Next(void)141 char const *CommandLine_Next(void)
142 {
143     if (!argLastMatch || argLastMatch >= CommandLine_Count() - 1)
144     {
145         // No more arguments following the last match.
146         return 0;
147     }
148     return CommandLine_At(++argLastMatch);
149 }
150 
CommandLine_NextAsPath(void)151 char const *CommandLine_NextAsPath(void)
152 {
153     if (!argLastMatch || argLastMatch >= CommandLine_Count() - 1)
154     {
155         // No more arguments following the last match.
156         return 0;
157     }
158     DENG2_COMMANDLINE().makeAbsolutePath(argLastMatch + 1);
159     return CommandLine_Next();
160 }
161 
CommandLine_Check(char const * check)162 int CommandLine_Check(char const *check)
163 {
164     return argLastMatch = DENG2_COMMANDLINE().check(check);
165 }
166 
CommandLine_CheckWith(char const * check,int num)167 int CommandLine_CheckWith(char const *check, int num)
168 {
169     return argLastMatch = DENG2_COMMANDLINE().check(check, num);
170 }
171 
CommandLine_Exists(char const * check)172 int CommandLine_Exists(char const *check)
173 {
174     return DENG2_COMMANDLINE().has(check);
175 }
176 
CommandLine_IsOption(int i)177 int CommandLine_IsOption(int i)
178 {
179     return DENG2_COMMANDLINE().isOption(i);
180 }
181 
CommandLine_IsMatchingAlias(char const * original,char const * originalOrAlias)182 int CommandLine_IsMatchingAlias(char const *original, char const *originalOrAlias)
183 {
184     return DENG2_COMMANDLINE().matches(original, originalOrAlias);
185 }
186 
LogBuffer_Flush(void)187 void LogBuffer_Flush(void)
188 {
189     if (de::LogBuffer::appBufferExists())
190     {
191         de::LogBuffer::get().flush();
192     }
193 }
194 
LogBuffer_Clear(void)195 void LogBuffer_Clear(void)
196 {
197     de::LogBuffer::get().clear();
198 }
199 
LogBuffer_EnableStandardOutput(int enable)200 void LogBuffer_EnableStandardOutput(int enable)
201 {
202     de::LogBuffer::get().enableStandardOutput(enable != 0);
203 }
204 
LogBuffer_Printf(unsigned int metadata,char const * format,...)205 void LogBuffer_Printf(unsigned int metadata, char const *format, ...)
206 {
207     if (!checkLogEntryMetadata(metadata)) return;
208 
209     char buffer[0x2000];
210     va_list args;
211     va_start(args, format);
212     size_t nc = vsprintf(buffer, format, args); /// @todo unsafe
213     va_end(args);
214     DENG2_ASSERT(nc < sizeof(buffer) - 1);
215     DENG2_UNUSED(nc);
216 
217     logFragmentPrinter(metadata, buffer);
218 }
219 
Info_NewFromString(char const * utf8text)220 de_Info *Info_NewFromString(char const *utf8text)
221 {
222     try
223     {
224         return reinterpret_cast<de_Info *>(new de::Info(QString::fromUtf8(utf8text)));
225     }
226     catch (de::Error const &er)
227     {
228         LOG_WARNING(er.asText());
229         return 0;
230     }
231 }
232 
Info_NewFromFile(char const * nativePath)233 de_Info *Info_NewFromFile(char const *nativePath)
234 {
235     try
236     {
237         QScopedPointer<de::Info> info(new de::Info);
238         info->parseNativeFile(nativePath);
239         return reinterpret_cast<de_Info *>(info.take());
240     }
241     catch (de::Error const &er)
242     {
243         LOG_WARNING(er.asText());
244         return 0;
245     }
246 }
247 
Info_Delete(de_Info * info)248 void Info_Delete(de_Info *info)
249 {
250     if (info)
251     {
252         DENG2_SELF(Info, info);
253         delete self;
254     }
255 }
256 
Info_FindValue(de_Info * info,char const * path,char * buffer,size_t bufSize)257 int Info_FindValue(de_Info *info, char const *path, char *buffer, size_t bufSize)
258 {
259     if (!info) return false;
260 
261     DENG2_SELF(Info, info);
262     de::Info::Element const *element = self->findByPath(path);
263     if (!element || !element->isKey()) return false;
264     QString value = static_cast<de::Info::KeyElement const *>(element)->value();
265     if (buffer)
266     {
267         qstrncpy(buffer, value.toUtf8().constData(), uint(bufSize));
268         return true;
269     }
270     else
271     {
272         // Just return the size of the value.
273         return value.size();
274     }
275 }
276 
UnixInfo_GetConfigValue(char const * configFile,char const * key)277 char *UnixInfo_GetConfigValue(char const *configFile, char const *key)
278 {
279     de::UnixInfo &info = de::App::unixInfo();
280 
281     // "paths" is the only config file currently being used.
282     if (!qstrcmp(configFile, "paths"))
283     {
284         de::NativePath foundValue;
285         if (info.path(key, foundValue))
286         {
287             return qstrdup(foundValue.toString().toUtf8().constData());
288         }
289     }
290     else if (!qstrcmp(configFile, "defaults"))
291     {
292         de::String foundValue;
293         if (info.defaults(key, foundValue))
294         {
295             return qstrdup(foundValue.toUtf8().constData());
296         }
297     }
298     return nullptr;
299 }
300 
LittleEndianByteOrder_ToForeignInt16(dint16 value)301 dint16 LittleEndianByteOrder_ToForeignInt16(dint16 value)
302 {
303     DENG2_ASSERT(sizeof(dint16) == sizeof(de::dint16));
304     return de::littleEndianByteOrder.toNetwork(de::dint16(value));
305 }
306 
LittleEndianByteOrder_ToForeignInt32(dint32 value)307 dint32 LittleEndianByteOrder_ToForeignInt32(dint32 value)
308 {
309     DENG2_ASSERT(sizeof(dint32) == sizeof(de::dint32));
310     return de::littleEndianByteOrder.toNetwork(de::dint32(value));
311 }
312 
LittleEndianByteOrder_ToForeignInt64(dint64 value)313 dint64 LittleEndianByteOrder_ToForeignInt64(dint64 value)
314 {
315     DENG2_ASSERT(sizeof(dint64) == sizeof(de::dint64));
316     return de::littleEndianByteOrder.toNetwork(de::dint64(value));
317 }
318 
LittleEndianByteOrder_ToForeignUInt16(duint16 value)319 duint16 LittleEndianByteOrder_ToForeignUInt16(duint16 value)
320 {
321     DENG2_ASSERT(sizeof(duint16) == sizeof(de::duint16));
322     return de::littleEndianByteOrder.toNetwork(de::duint16(value));
323 }
324 
LittleEndianByteOrder_ToForeignUInt32(duint32 value)325 duint32 LittleEndianByteOrder_ToForeignUInt32(duint32 value)
326 {
327     DENG2_ASSERT(sizeof(duint32) == sizeof(de::duint32));
328     return de::littleEndianByteOrder.toNetwork(de::duint32(value));
329 }
330 
LittleEndianByteOrder_ToForeignUInt64(duint64 value)331 duint64 LittleEndianByteOrder_ToForeignUInt64(duint64 value)
332 {
333     DENG2_ASSERT(sizeof(duint64) == sizeof(de::duint64));
334     return de::littleEndianByteOrder.toNetwork(de::duint64(value));
335 }
336 
LittleEndianByteOrder_ToForeignFloat(dfloat value)337 dfloat LittleEndianByteOrder_ToForeignFloat(dfloat value)
338 {
339     DENG2_ASSERT(sizeof(dfloat) == sizeof(de::dfloat));
340     return de::littleEndianByteOrder.toNetwork(de::dfloat(value));
341 }
342 
LittleEndianByteOrder_ToForeignDouble(ddouble value)343 ddouble LittleEndianByteOrder_ToForeignDouble(ddouble value)
344 {
345     DENG2_ASSERT(sizeof(ddouble) == sizeof(de::ddouble));
346     return de::littleEndianByteOrder.toNetwork(de::ddouble(value));
347 }
348 
LittleEndianByteOrder_ToNativeInt16(dint16 value)349 dint16 LittleEndianByteOrder_ToNativeInt16(dint16 value)
350 {
351     DENG2_ASSERT(sizeof(dint16) == sizeof(de::dint16));
352     return de::littleEndianByteOrder.toHost(de::dint16(value));
353 }
354 
LittleEndianByteOrder_ToNativeInt32(dint32 value)355 dint32 LittleEndianByteOrder_ToNativeInt32(dint32 value)
356 {
357     DENG2_ASSERT(sizeof(dint32) == sizeof(de::dint32));
358     return de::littleEndianByteOrder.toHost(de::dint32(value));
359 }
360 
LittleEndianByteOrder_ToNativeInt64(dint64 value)361 dint64 LittleEndianByteOrder_ToNativeInt64(dint64 value)
362 {
363     DENG2_ASSERT(sizeof(dint64) == sizeof(de::dint64));
364     return de::littleEndianByteOrder.toHost(de::dint64(value));
365 }
366 
LittleEndianByteOrder_ToNativeUInt16(duint16 value)367 duint16 LittleEndianByteOrder_ToNativeUInt16(duint16 value)
368 {
369     DENG2_ASSERT(sizeof(duint16) == sizeof(de::duint16));
370     return de::littleEndianByteOrder.toHost(de::duint16(value));
371 }
372 
LittleEndianByteOrder_ToNativeUInt32(duint32 value)373 duint32 LittleEndianByteOrder_ToNativeUInt32(duint32 value)
374 {
375     DENG2_ASSERT(sizeof(duint32) == sizeof(de::duint32));
376     return de::littleEndianByteOrder.toHost(de::duint32(value));
377 }
378 
LittleEndianByteOrder_ToNativeUInt64(duint64 value)379 duint64 LittleEndianByteOrder_ToNativeUInt64(duint64 value)
380 {
381     DENG2_ASSERT(sizeof(duint64) == sizeof(de::duint64));
382     return de::littleEndianByteOrder.toHost(de::duint64(value));
383 }
384 
LittleEndianByteOrder_ToNativeFloat(dfloat value)385 dfloat LittleEndianByteOrder_ToNativeFloat(dfloat value)
386 {
387     DENG2_ASSERT(sizeof(dfloat) == sizeof(de::dfloat));
388     return de::littleEndianByteOrder.toHost(de::dfloat(value));
389 }
390 
LittleEndianByteOrder_ToNativeDouble(ddouble value)391 ddouble LittleEndianByteOrder_ToNativeDouble(ddouble value)
392 {
393     DENG2_ASSERT(sizeof(ddouble) == sizeof(de::ddouble));
394     return de::littleEndianByteOrder.toHost(de::ddouble(value));
395 }
396