mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 17:50:22 +00:00
Replace GPL licensed C/C++ samples
This commit is contained in:
1178
samples/C++/PackageInfoParser.cpp
Normal file
1178
samples/C++/PackageInfoParser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
123
samples/C++/crypter.cpp
Normal file
123
samples/C++/crypter.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (c) 2009-2012 The Bitcoin Developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
// Source - https://github.com/Bradfrogger/Marvelous/blob/master/src/crypter.cpp
|
||||||
|
|
||||||
|
#include <openssl/aes.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "crypter.h"
|
||||||
|
|
||||||
|
bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
|
||||||
|
{
|
||||||
|
if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
if (nDerivationMethod == 0)
|
||||||
|
i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
|
||||||
|
(unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);
|
||||||
|
|
||||||
|
if (i != (int)WALLET_CRYPTO_KEY_SIZE)
|
||||||
|
{
|
||||||
|
OPENSSL_cleanse(chKey, sizeof(chKey));
|
||||||
|
OPENSSL_cleanse(chIV, sizeof(chIV));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fKeySet = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV)
|
||||||
|
{
|
||||||
|
if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
|
||||||
|
memcpy(&chIV[0], &chNewIV[0], sizeof chIV);
|
||||||
|
|
||||||
|
fKeySet = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext)
|
||||||
|
{
|
||||||
|
if (!fKeySet)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// max ciphertext len for a n bytes of plaintext is
|
||||||
|
// n + AES_BLOCK_SIZE - 1 bytes
|
||||||
|
int nLen = vchPlaintext.size();
|
||||||
|
int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
|
||||||
|
vchCiphertext = std::vector<unsigned char> (nCLen);
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX ctx;
|
||||||
|
|
||||||
|
bool fOk = true;
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX_init(&ctx);
|
||||||
|
if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV);
|
||||||
|
if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen);
|
||||||
|
if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0])+nCLen, &nFLen);
|
||||||
|
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||||
|
|
||||||
|
if (!fOk) return false;
|
||||||
|
|
||||||
|
vchCiphertext.resize(nCLen + nFLen);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext)
|
||||||
|
{
|
||||||
|
if (!fKeySet)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// plaintext will always be equal to or lesser than length of ciphertext
|
||||||
|
int nLen = vchCiphertext.size();
|
||||||
|
int nPLen = nLen, nFLen = 0;
|
||||||
|
|
||||||
|
vchPlaintext = CKeyingMaterial(nPLen);
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX ctx;
|
||||||
|
|
||||||
|
bool fOk = true;
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX_init(&ctx);
|
||||||
|
if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV);
|
||||||
|
if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen);
|
||||||
|
if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0])+nPLen, &nFLen);
|
||||||
|
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||||
|
|
||||||
|
if (!fOk) return false;
|
||||||
|
|
||||||
|
vchPlaintext.resize(nPLen + nFLen);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
|
||||||
|
{
|
||||||
|
CCrypter cKeyCrypter;
|
||||||
|
std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
|
||||||
|
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
|
||||||
|
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
|
||||||
|
return false;
|
||||||
|
return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
|
||||||
|
{
|
||||||
|
CCrypter cKeyCrypter;
|
||||||
|
std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
|
||||||
|
memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
|
||||||
|
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
|
||||||
|
return false;
|
||||||
|
return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
|
||||||
|
}
|
||||||
109
samples/C++/graphics.cpp
Normal file
109
samples/C++/graphics.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// License - https://github.com/TurtleP/Flask/blob/master/LICENSE
|
||||||
|
|
||||||
|
#include <shared.h>
|
||||||
|
|
||||||
|
int currentR = 0xFF;
|
||||||
|
int currentG = 0xFF;
|
||||||
|
int currentB = 0xFF;
|
||||||
|
int currentA = 0xFF;
|
||||||
|
|
||||||
|
int currentScreen = GFX_BOTTOM;
|
||||||
|
|
||||||
|
float transX = 0;
|
||||||
|
float transY = 0;
|
||||||
|
bool isPushed = false;
|
||||||
|
|
||||||
|
u32 getCurrentColor()
|
||||||
|
{
|
||||||
|
return RGBA8(currentR, currentG, currentB, currentA);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setColor(int r, int g, int b)
|
||||||
|
{
|
||||||
|
currentR = r;
|
||||||
|
currentG = g;
|
||||||
|
currentB = b;
|
||||||
|
currentA = currentA;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setColor(int r, int g, int b, int a)
|
||||||
|
{
|
||||||
|
currentR = r;
|
||||||
|
currentG = g;
|
||||||
|
currentB = b;
|
||||||
|
currentA = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setScreen(int screen)
|
||||||
|
{
|
||||||
|
currentScreen = screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getCurrentScreen()
|
||||||
|
{
|
||||||
|
return currentScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void screenShot() //for showing stuff being done
|
||||||
|
{
|
||||||
|
FILE * topScreen = fopen("sdmc:/framebuffer_top.rgb", "w+");
|
||||||
|
|
||||||
|
fwrite(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL), 288000, 1, topScreen);
|
||||||
|
|
||||||
|
fclose(topScreen);
|
||||||
|
|
||||||
|
FILE * bottomScreen = fopen("sdmc:/framebuffer_bottom.rgb", "w+");;
|
||||||
|
|
||||||
|
fwrite(gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL), 230400, 1, bottomScreen);
|
||||||
|
|
||||||
|
fclose(bottomScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void translateCoords(float * x, float * y) {
|
||||||
|
if (isPushed)
|
||||||
|
{
|
||||||
|
*x += transX;
|
||||||
|
*y += transY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void translate(float dx, float dy)
|
||||||
|
{
|
||||||
|
if (sf2d_get_current_screen() == getCurrentScreen())
|
||||||
|
{
|
||||||
|
transX = transX + dx;
|
||||||
|
transY = transY + dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void push()
|
||||||
|
{
|
||||||
|
if (sf2d_get_current_screen() == getCurrentScreen())
|
||||||
|
{
|
||||||
|
isPushed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop()
|
||||||
|
{
|
||||||
|
if (sf2d_get_current_screen() == getCurrentScreen())
|
||||||
|
{
|
||||||
|
transX = 0;
|
||||||
|
transY = 0;
|
||||||
|
isPushed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setScissor(u32 x, u32 y, u32 width, u32 height)
|
||||||
|
{
|
||||||
|
if (sf2d_get_current_screen() == getCurrentScreen())
|
||||||
|
{
|
||||||
|
GPU_SCISSORMODE mode = GPU_SCISSOR_NORMAL;
|
||||||
|
|
||||||
|
if (!x && !y && !width && !height) {
|
||||||
|
mode = GPU_SCISSOR_DISABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sf2d_set_scissor_test(mode, x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
920
samples/C++/json_reader.cpp
Normal file
920
samples/C++/json_reader.cpp
Normal file
@@ -0,0 +1,920 @@
|
|||||||
|
// Copyright 2007-2010 Baptiste Lepilleur
|
||||||
|
// Distributed under MIT license, or public domain if desired and
|
||||||
|
// recognized in your jurisdiction.
|
||||||
|
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
|
||||||
|
|
||||||
|
/*
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Source - https://github.com/Ij888/ApacheCordovaRecipes/blob/6e8a2c1d9de7302f74bc3dbac54a021f0499bbb3/jqmsandbox/plugins/cordova-plugin-globalization/src/blackberry10/native/public/json_reader.cpp
|
||||||
|
|
||||||
|
#include <json/reader.h>
|
||||||
|
#include <json/value.h>
|
||||||
|
#include <utility>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#if _MSC_VER >= 1400 // VC++ 8.0
|
||||||
|
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Json {
|
||||||
|
|
||||||
|
// QNX is strict about declaring C symbols in the std namespace.
|
||||||
|
#ifdef __QNXNTO__
|
||||||
|
using std::memcpy;
|
||||||
|
using std::sprintf;
|
||||||
|
using std::sscanf;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Implementation of class Features
|
||||||
|
// ////////////////////////////////
|
||||||
|
|
||||||
|
Features::Features()
|
||||||
|
: allowComments_( true )
|
||||||
|
, strictRoot_( false )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
Features::all()
|
||||||
|
{
|
||||||
|
return Features();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
Features::strictMode()
|
||||||
|
{
|
||||||
|
Features features;
|
||||||
|
features.allowComments_ = false;
|
||||||
|
features.strictRoot_ = true;
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of class Reader
|
||||||
|
// ////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
|
||||||
|
{
|
||||||
|
return c == c1 || c == c2 || c == c3 || c == c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
|
||||||
|
{
|
||||||
|
return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
containsNewLine( Reader::Location begin,
|
||||||
|
Reader::Location end )
|
||||||
|
{
|
||||||
|
for ( ;begin < end; ++begin )
|
||||||
|
if ( *begin == '\n' || *begin == '\r' )
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string codePointToUTF8(unsigned int cp)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
// based on description from http://en.wikipedia.org/wiki/UTF-8
|
||||||
|
|
||||||
|
if (cp <= 0x7f)
|
||||||
|
{
|
||||||
|
result.resize(1);
|
||||||
|
result[0] = static_cast<char>(cp);
|
||||||
|
}
|
||||||
|
else if (cp <= 0x7FF)
|
||||||
|
{
|
||||||
|
result.resize(2);
|
||||||
|
result[1] = static_cast<char>(0x80 | (0x3f & cp));
|
||||||
|
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
|
||||||
|
}
|
||||||
|
else if (cp <= 0xFFFF)
|
||||||
|
{
|
||||||
|
result.resize(3);
|
||||||
|
result[2] = static_cast<char>(0x80 | (0x3f & cp));
|
||||||
|
result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
|
||||||
|
result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
|
||||||
|
}
|
||||||
|
else if (cp <= 0x10FFFF)
|
||||||
|
{
|
||||||
|
result.resize(4);
|
||||||
|
result[3] = static_cast<char>(0x80 | (0x3f & cp));
|
||||||
|
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
|
||||||
|
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
|
||||||
|
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Class Reader
|
||||||
|
// //////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Reader::Reader()
|
||||||
|
: features_( Features::all() )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reader::Reader( const Features &features )
|
||||||
|
: features_( features )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::parse( const std::string &document,
|
||||||
|
Value &root,
|
||||||
|
bool collectComments )
|
||||||
|
{
|
||||||
|
document_ = document;
|
||||||
|
const char *begin = document_.c_str();
|
||||||
|
const char *end = begin + document_.length();
|
||||||
|
return parse( begin, end, root, collectComments );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::parse( std::istream& sin,
|
||||||
|
Value &root,
|
||||||
|
bool collectComments )
|
||||||
|
{
|
||||||
|
//std::istream_iterator<char> begin(sin);
|
||||||
|
//std::istream_iterator<char> end;
|
||||||
|
// Those would allow streamed input from a file, if parse() were a
|
||||||
|
// template function.
|
||||||
|
|
||||||
|
// Since std::string is reference-counted, this at least does not
|
||||||
|
// create an extra copy.
|
||||||
|
std::string doc;
|
||||||
|
std::getline(sin, doc, (char)EOF);
|
||||||
|
return parse( doc, root, collectComments );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::parse( const char *beginDoc, const char *endDoc,
|
||||||
|
Value &root,
|
||||||
|
bool collectComments )
|
||||||
|
{
|
||||||
|
if ( !features_.allowComments_ )
|
||||||
|
{
|
||||||
|
collectComments = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
begin_ = beginDoc;
|
||||||
|
end_ = endDoc;
|
||||||
|
collectComments_ = collectComments;
|
||||||
|
current_ = begin_;
|
||||||
|
lastValueEnd_ = 0;
|
||||||
|
lastValue_ = 0;
|
||||||
|
commentsBefore_ = "";
|
||||||
|
errors_.clear();
|
||||||
|
while ( !nodes_.empty() )
|
||||||
|
nodes_.pop();
|
||||||
|
nodes_.push( &root );
|
||||||
|
|
||||||
|
bool successful = readValue();
|
||||||
|
Token token;
|
||||||
|
skipCommentTokens( token );
|
||||||
|
if ( collectComments_ && !commentsBefore_.empty() )
|
||||||
|
root.setComment( commentsBefore_, commentAfter );
|
||||||
|
if ( features_.strictRoot_ )
|
||||||
|
{
|
||||||
|
if ( !root.isArray() && !root.isObject() )
|
||||||
|
{
|
||||||
|
// Set error location to start of doc, ideally should be first token found in doc
|
||||||
|
token.type_ = tokenError;
|
||||||
|
token.start_ = beginDoc;
|
||||||
|
token.end_ = endDoc;
|
||||||
|
addError( "A valid JSON document must be either an array or an object value.",
|
||||||
|
token );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return successful;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::readValue()
|
||||||
|
{
|
||||||
|
Token token;
|
||||||
|
skipCommentTokens( token );
|
||||||
|
bool successful = true;
|
||||||
|
|
||||||
|
if ( collectComments_ && !commentsBefore_.empty() )
|
||||||
|
{
|
||||||
|
currentValue().setComment( commentsBefore_, commentBefore );
|
||||||
|
commentsBefore_ = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch ( token.type_ )
|
||||||
|
{
|
||||||
|
case tokenObjectBegin:
|
||||||
|
successful = readObject( token );
|
||||||
|
break;
|
||||||
|
case tokenArrayBegin:
|
||||||
|
successful = readArray( token );
|
||||||
|
break;
|
||||||
|
case tokenNumber:
|
||||||
|
successful = decodeNumber( token );
|
||||||
|
break;
|
||||||
|
case tokenString:
|
||||||
|
successful = decodeString( token );
|
||||||
|
break;
|
||||||
|
case tokenTrue:
|
||||||
|
currentValue() = true;
|
||||||
|
break;
|
||||||
|
case tokenFalse:
|
||||||
|
currentValue() = false;
|
||||||
|
break;
|
||||||
|
case tokenNull:
|
||||||
|
currentValue() = Value();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return addError( "Syntax error: value, object or array expected.", token );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( collectComments_ )
|
||||||
|
{
|
||||||
|
lastValueEnd_ = current_;
|
||||||
|
lastValue_ = ¤tValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return successful;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Reader::skipCommentTokens( Token &token )
|
||||||
|
{
|
||||||
|
if ( features_.allowComments_ )
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
readToken( token );
|
||||||
|
}
|
||||||
|
while ( token.type_ == tokenComment );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
readToken( token );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::expectToken( TokenType type, Token &token, const char *message )
|
||||||
|
{
|
||||||
|
readToken( token );
|
||||||
|
if ( token.type_ != type )
|
||||||
|
return addError( message, token );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::readToken( Token &token )
|
||||||
|
{
|
||||||
|
skipSpaces();
|
||||||
|
token.start_ = current_;
|
||||||
|
Char c = getNextChar();
|
||||||
|
bool ok = true;
|
||||||
|
switch ( c )
|
||||||
|
{
|
||||||
|
case '{':
|
||||||
|
token.type_ = tokenObjectBegin;
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
token.type_ = tokenObjectEnd;
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
token.type_ = tokenArrayBegin;
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
token.type_ = tokenArrayEnd;
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
token.type_ = tokenString;
|
||||||
|
ok = readString();
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
token.type_ = tokenComment;
|
||||||
|
ok = readComment();
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
case '-':
|
||||||
|
token.type_ = tokenNumber;
|
||||||
|
readNumber();
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
token.type_ = tokenTrue;
|
||||||
|
ok = match( "rue", 3 );
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
token.type_ = tokenFalse;
|
||||||
|
ok = match( "alse", 4 );
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
token.type_ = tokenNull;
|
||||||
|
ok = match( "ull", 3 );
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
token.type_ = tokenArraySeparator;
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
token.type_ = tokenMemberSeparator;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
token.type_ = tokenEndOfStream;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( !ok )
|
||||||
|
token.type_ = tokenError;
|
||||||
|
token.end_ = current_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Reader::skipSpaces()
|
||||||
|
{
|
||||||
|
while ( current_ != end_ )
|
||||||
|
{
|
||||||
|
Char c = *current_;
|
||||||
|
if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' )
|
||||||
|
++current_;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::match( Location pattern,
|
||||||
|
int patternLength )
|
||||||
|
{
|
||||||
|
if ( end_ - current_ < patternLength )
|
||||||
|
return false;
|
||||||
|
int index = patternLength;
|
||||||
|
while ( index-- )
|
||||||
|
if ( current_[index] != pattern[index] )
|
||||||
|
return false;
|
||||||
|
current_ += patternLength;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::readComment()
|
||||||
|
{
|
||||||
|
Location commentBegin = current_ - 1;
|
||||||
|
Char c = getNextChar();
|
||||||
|
bool successful = false;
|
||||||
|
if ( c == '*' )
|
||||||
|
successful = readCStyleComment();
|
||||||
|
else if ( c == '/' )
|
||||||
|
successful = readCppStyleComment();
|
||||||
|
if ( !successful )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( collectComments_ )
|
||||||
|
{
|
||||||
|
CommentPlacement placement = commentBefore;
|
||||||
|
if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) )
|
||||||
|
{
|
||||||
|
if ( c != '*' || !containsNewLine( commentBegin, current_ ) )
|
||||||
|
placement = commentAfterOnSameLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
addComment( commentBegin, current_, placement );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Reader::addComment( Location begin,
|
||||||
|
Location end,
|
||||||
|
CommentPlacement placement )
|
||||||
|
{
|
||||||
|
assert( collectComments_ );
|
||||||
|
if ( placement == commentAfterOnSameLine )
|
||||||
|
{
|
||||||
|
assert( lastValue_ != 0 );
|
||||||
|
lastValue_->setComment( std::string( begin, end ), placement );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( !commentsBefore_.empty() )
|
||||||
|
commentsBefore_ += "\n";
|
||||||
|
commentsBefore_ += std::string( begin, end );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::readCStyleComment()
|
||||||
|
{
|
||||||
|
while ( current_ != end_ )
|
||||||
|
{
|
||||||
|
Char c = getNextChar();
|
||||||
|
if ( c == '*' && *current_ == '/' )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return getNextChar() == '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::readCppStyleComment()
|
||||||
|
{
|
||||||
|
while ( current_ != end_ )
|
||||||
|
{
|
||||||
|
Char c = getNextChar();
|
||||||
|
if ( c == '\r' || c == '\n' )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Reader::readNumber()
|
||||||
|
{
|
||||||
|
while ( current_ != end_ )
|
||||||
|
{
|
||||||
|
if ( !(*current_ >= '0' && *current_ <= '9') &&
|
||||||
|
!in( *current_, '.', 'e', 'E', '+', '-' ) )
|
||||||
|
break;
|
||||||
|
++current_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::readString()
|
||||||
|
{
|
||||||
|
Char c = 0;
|
||||||
|
while ( current_ != end_ )
|
||||||
|
{
|
||||||
|
c = getNextChar();
|
||||||
|
if ( c == '\\' )
|
||||||
|
getNextChar();
|
||||||
|
else if ( c == '"' )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return c == '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::readObject( Token &tokenStart )
|
||||||
|
{
|
||||||
|
Token tokenName;
|
||||||
|
std::string name;
|
||||||
|
currentValue() = Value( objectValue );
|
||||||
|
while ( readToken( tokenName ) )
|
||||||
|
{
|
||||||
|
bool initialTokenOk = true;
|
||||||
|
while ( tokenName.type_ == tokenComment && initialTokenOk )
|
||||||
|
initialTokenOk = readToken( tokenName );
|
||||||
|
if ( !initialTokenOk )
|
||||||
|
break;
|
||||||
|
if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object
|
||||||
|
return true;
|
||||||
|
if ( tokenName.type_ != tokenString )
|
||||||
|
break;
|
||||||
|
|
||||||
|
name = "";
|
||||||
|
if ( !decodeString( tokenName, name ) )
|
||||||
|
return recoverFromError( tokenObjectEnd );
|
||||||
|
|
||||||
|
Token colon;
|
||||||
|
if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator )
|
||||||
|
{
|
||||||
|
return addErrorAndRecover( "Missing ':' after object member name",
|
||||||
|
colon,
|
||||||
|
tokenObjectEnd );
|
||||||
|
}
|
||||||
|
Value &value = currentValue()[ name ];
|
||||||
|
nodes_.push( &value );
|
||||||
|
bool ok = readValue();
|
||||||
|
nodes_.pop();
|
||||||
|
if ( !ok ) // error already set
|
||||||
|
return recoverFromError( tokenObjectEnd );
|
||||||
|
|
||||||
|
Token comma;
|
||||||
|
if ( !readToken( comma )
|
||||||
|
|| ( comma.type_ != tokenObjectEnd &&
|
||||||
|
comma.type_ != tokenArraySeparator &&
|
||||||
|
comma.type_ != tokenComment ) )
|
||||||
|
{
|
||||||
|
return addErrorAndRecover( "Missing ',' or '}' in object declaration",
|
||||||
|
comma,
|
||||||
|
tokenObjectEnd );
|
||||||
|
}
|
||||||
|
bool finalizeTokenOk = true;
|
||||||
|
while ( comma.type_ == tokenComment &&
|
||||||
|
finalizeTokenOk )
|
||||||
|
finalizeTokenOk = readToken( comma );
|
||||||
|
if ( comma.type_ == tokenObjectEnd )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return addErrorAndRecover( "Missing '}' or object member name",
|
||||||
|
tokenName,
|
||||||
|
tokenObjectEnd );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::readArray( Token &tokenStart )
|
||||||
|
{
|
||||||
|
currentValue() = Value( arrayValue );
|
||||||
|
skipSpaces();
|
||||||
|
if ( *current_ == ']' ) // empty array
|
||||||
|
{
|
||||||
|
Token endArray;
|
||||||
|
readToken( endArray );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int index = 0;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
Value &value = currentValue()[ index++ ];
|
||||||
|
nodes_.push( &value );
|
||||||
|
bool ok = readValue();
|
||||||
|
nodes_.pop();
|
||||||
|
if ( !ok ) // error already set
|
||||||
|
return recoverFromError( tokenArrayEnd );
|
||||||
|
|
||||||
|
Token token;
|
||||||
|
// Accept Comment after last item in the array.
|
||||||
|
ok = readToken( token );
|
||||||
|
while ( token.type_ == tokenComment && ok )
|
||||||
|
{
|
||||||
|
ok = readToken( token );
|
||||||
|
}
|
||||||
|
bool badTokenType = ( token.type_ == tokenArraySeparator &&
|
||||||
|
token.type_ == tokenArrayEnd );
|
||||||
|
if ( !ok || badTokenType )
|
||||||
|
{
|
||||||
|
return addErrorAndRecover( "Missing ',' or ']' in array declaration",
|
||||||
|
token,
|
||||||
|
tokenArrayEnd );
|
||||||
|
}
|
||||||
|
if ( token.type_ == tokenArrayEnd )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::decodeNumber( Token &token )
|
||||||
|
{
|
||||||
|
bool isDouble = false;
|
||||||
|
for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
|
||||||
|
{
|
||||||
|
isDouble = isDouble
|
||||||
|
|| in( *inspect, '.', 'e', 'E', '+' )
|
||||||
|
|| ( *inspect == '-' && inspect != token.start_ );
|
||||||
|
}
|
||||||
|
if ( isDouble )
|
||||||
|
return decodeDouble( token );
|
||||||
|
Location current = token.start_;
|
||||||
|
bool isNegative = *current == '-';
|
||||||
|
if ( isNegative )
|
||||||
|
++current;
|
||||||
|
Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt)
|
||||||
|
: Value::maxUInt) / 10;
|
||||||
|
Value::UInt value = 0;
|
||||||
|
while ( current < token.end_ )
|
||||||
|
{
|
||||||
|
Char c = *current++;
|
||||||
|
if ( c < '0' || c > '9' )
|
||||||
|
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
|
||||||
|
if ( value >= threshold )
|
||||||
|
return decodeDouble( token );
|
||||||
|
value = value * 10 + Value::UInt(c - '0');
|
||||||
|
}
|
||||||
|
if ( isNegative )
|
||||||
|
currentValue() = -Value::Int( value );
|
||||||
|
else if ( value <= Value::UInt(Value::maxInt) )
|
||||||
|
currentValue() = Value::Int( value );
|
||||||
|
else
|
||||||
|
currentValue() = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::decodeDouble( Token &token )
|
||||||
|
{
|
||||||
|
double value = 0;
|
||||||
|
const int bufferSize = 32;
|
||||||
|
int count;
|
||||||
|
int length = int(token.end_ - token.start_);
|
||||||
|
if ( length <= bufferSize )
|
||||||
|
{
|
||||||
|
Char buffer[bufferSize];
|
||||||
|
memcpy( buffer, token.start_, length );
|
||||||
|
buffer[length] = 0;
|
||||||
|
count = sscanf( buffer, "%lf", &value );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string buffer( token.start_, token.end_ );
|
||||||
|
count = sscanf( buffer.c_str(), "%lf", &value );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( count != 1 )
|
||||||
|
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
|
||||||
|
currentValue() = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::decodeString( Token &token )
|
||||||
|
{
|
||||||
|
std::string decoded;
|
||||||
|
if ( !decodeString( token, decoded ) )
|
||||||
|
return false;
|
||||||
|
currentValue() = decoded;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::decodeString( Token &token, std::string &decoded )
|
||||||
|
{
|
||||||
|
decoded.reserve( token.end_ - token.start_ - 2 );
|
||||||
|
Location current = token.start_ + 1; // skip '"'
|
||||||
|
Location end = token.end_ - 1; // do not include '"'
|
||||||
|
while ( current != end )
|
||||||
|
{
|
||||||
|
Char c = *current++;
|
||||||
|
if ( c == '"' )
|
||||||
|
break;
|
||||||
|
else if ( c == '\\' )
|
||||||
|
{
|
||||||
|
if ( current == end )
|
||||||
|
return addError( "Empty escape sequence in string", token, current );
|
||||||
|
Char escape = *current++;
|
||||||
|
switch ( escape )
|
||||||
|
{
|
||||||
|
case '"': decoded += '"'; break;
|
||||||
|
case '/': decoded += '/'; break;
|
||||||
|
case '\\': decoded += '\\'; break;
|
||||||
|
case 'b': decoded += '\b'; break;
|
||||||
|
case 'f': decoded += '\f'; break;
|
||||||
|
case 'n': decoded += '\n'; break;
|
||||||
|
case 'r': decoded += '\r'; break;
|
||||||
|
case 't': decoded += '\t'; break;
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
unsigned int unicode;
|
||||||
|
if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
|
||||||
|
return false;
|
||||||
|
decoded += codePointToUTF8(unicode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return addError( "Bad escape sequence in string", token, current );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
decoded += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::decodeUnicodeCodePoint( Token &token,
|
||||||
|
Location ¤t,
|
||||||
|
Location end,
|
||||||
|
unsigned int &unicode )
|
||||||
|
{
|
||||||
|
|
||||||
|
if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
|
||||||
|
return false;
|
||||||
|
if (unicode >= 0xD800 && unicode <= 0xDBFF)
|
||||||
|
{
|
||||||
|
// surrogate pairs
|
||||||
|
if (end - current < 6)
|
||||||
|
return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
|
||||||
|
unsigned int surrogatePair;
|
||||||
|
if (*(current++) == '\\' && *(current++)== 'u')
|
||||||
|
{
|
||||||
|
if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
|
||||||
|
{
|
||||||
|
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::decodeUnicodeEscapeSequence( Token &token,
|
||||||
|
Location ¤t,
|
||||||
|
Location end,
|
||||||
|
unsigned int &unicode )
|
||||||
|
{
|
||||||
|
if ( end - current < 4 )
|
||||||
|
return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
|
||||||
|
unicode = 0;
|
||||||
|
for ( int index =0; index < 4; ++index )
|
||||||
|
{
|
||||||
|
Char c = *current++;
|
||||||
|
unicode *= 16;
|
||||||
|
if ( c >= '0' && c <= '9' )
|
||||||
|
unicode += c - '0';
|
||||||
|
else if ( c >= 'a' && c <= 'f' )
|
||||||
|
unicode += c - 'a' + 10;
|
||||||
|
else if ( c >= 'A' && c <= 'F' )
|
||||||
|
unicode += c - 'A' + 10;
|
||||||
|
else
|
||||||
|
return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::addError( const std::string &message,
|
||||||
|
Token &token,
|
||||||
|
Location extra )
|
||||||
|
{
|
||||||
|
ErrorInfo info;
|
||||||
|
info.token_ = token;
|
||||||
|
info.message_ = message;
|
||||||
|
info.extra_ = extra;
|
||||||
|
errors_.push_back( info );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::recoverFromError( TokenType skipUntilToken )
|
||||||
|
{
|
||||||
|
int errorCount = int(errors_.size());
|
||||||
|
Token skip;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
if ( !readToken(skip) )
|
||||||
|
errors_.resize( errorCount ); // discard errors caused by recovery
|
||||||
|
if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
errors_.resize( errorCount );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reader::addErrorAndRecover( const std::string &message,
|
||||||
|
Token &token,
|
||||||
|
TokenType skipUntilToken )
|
||||||
|
{
|
||||||
|
addError( message, token );
|
||||||
|
return recoverFromError( skipUntilToken );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Value &
|
||||||
|
Reader::currentValue()
|
||||||
|
{
|
||||||
|
return *(nodes_.top());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reader::Char
|
||||||
|
Reader::getNextChar()
|
||||||
|
{
|
||||||
|
if ( current_ == end_ )
|
||||||
|
return 0;
|
||||||
|
return *current_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Reader::getLocationLineAndColumn( Location location,
|
||||||
|
int &line,
|
||||||
|
int &column ) const
|
||||||
|
{
|
||||||
|
Location current = begin_;
|
||||||
|
Location lastLineStart = current;
|
||||||
|
line = 0;
|
||||||
|
while ( current < location && current != end_ )
|
||||||
|
{
|
||||||
|
Char c = *current++;
|
||||||
|
if ( c == '\r' )
|
||||||
|
{
|
||||||
|
if ( *current == '\n' )
|
||||||
|
++current;
|
||||||
|
lastLineStart = current;
|
||||||
|
++line;
|
||||||
|
}
|
||||||
|
else if ( c == '\n' )
|
||||||
|
{
|
||||||
|
lastLineStart = current;
|
||||||
|
++line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// column & line start at 1
|
||||||
|
column = int(location - lastLineStart) + 1;
|
||||||
|
++line;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Reader::getLocationLineAndColumn( Location location ) const
|
||||||
|
{
|
||||||
|
int line, column;
|
||||||
|
getLocationLineAndColumn( location, line, column );
|
||||||
|
char buffer[18+16+16+1];
|
||||||
|
sprintf( buffer, "Line %d, Column %d", line, column );
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Reader::getFormatedErrorMessages() const
|
||||||
|
{
|
||||||
|
std::string formattedMessage;
|
||||||
|
for ( Errors::const_iterator itError = errors_.begin();
|
||||||
|
itError != errors_.end();
|
||||||
|
++itError )
|
||||||
|
{
|
||||||
|
const ErrorInfo &error = *itError;
|
||||||
|
formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
|
||||||
|
formattedMessage += " " + error.message_ + "\n";
|
||||||
|
if ( error.extra_ )
|
||||||
|
formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
|
||||||
|
}
|
||||||
|
return formattedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::istream& operator>>( std::istream &sin, Value &root )
|
||||||
|
{
|
||||||
|
Json::Reader reader;
|
||||||
|
bool ok = reader.parse(sin, root, true);
|
||||||
|
//JSON_ASSERT( ok );
|
||||||
|
if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages());
|
||||||
|
return sin;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Json
|
||||||
857
samples/C++/json_writer.cpp
Normal file
857
samples/C++/json_writer.cpp
Normal file
@@ -0,0 +1,857 @@
|
|||||||
|
// Copyright 2007-2010 Baptiste Lepilleur
|
||||||
|
// Distributed under MIT license, or public domain if desired and
|
||||||
|
// recognized in your jurisdiction.
|
||||||
|
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
|
||||||
|
|
||||||
|
/*
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Source - https://github.com/Ij888/ApacheCordovaRecipes/blob/6e8a2c1d9de7302f74bc3dbac54a021f0499bbb3/jqmsandbox/plugins/cordova-plugin-globalization/src/blackberry10/native/public/json_writer.cpp
|
||||||
|
|
||||||
|
#include <json/writer.h>
|
||||||
|
#include <utility>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#if _MSC_VER >= 1400 // VC++ 8.0
|
||||||
|
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Json {
|
||||||
|
|
||||||
|
static bool isControlCharacter(char ch)
|
||||||
|
{
|
||||||
|
return ch > 0 && ch <= 0x1F;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool containsControlCharacter( const char* str )
|
||||||
|
{
|
||||||
|
while ( *str )
|
||||||
|
{
|
||||||
|
if ( isControlCharacter( *(str++) ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static void uintToString( unsigned int value,
|
||||||
|
char *¤t )
|
||||||
|
{
|
||||||
|
*--current = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
*--current = (value % 10) + '0';
|
||||||
|
value /= 10;
|
||||||
|
}
|
||||||
|
while ( value != 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string valueToString( Int value )
|
||||||
|
{
|
||||||
|
char buffer[32];
|
||||||
|
char *current = buffer + sizeof(buffer);
|
||||||
|
bool isNegative = value < 0;
|
||||||
|
if ( isNegative )
|
||||||
|
value = -value;
|
||||||
|
uintToString( UInt(value), current );
|
||||||
|
if ( isNegative )
|
||||||
|
*--current = '-';
|
||||||
|
assert( current >= buffer );
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string valueToString( UInt value )
|
||||||
|
{
|
||||||
|
char buffer[32];
|
||||||
|
char *current = buffer + sizeof(buffer);
|
||||||
|
uintToString( value, current );
|
||||||
|
assert( current >= buffer );
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string valueToString( double value )
|
||||||
|
{
|
||||||
|
char buffer[32];
|
||||||
|
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
|
||||||
|
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
|
||||||
|
#else
|
||||||
|
sprintf(buffer, "%#.16g", value);
|
||||||
|
#endif
|
||||||
|
char* ch = buffer + strlen(buffer) - 1;
|
||||||
|
if (*ch != '0') return buffer; // nothing to truncate, so save time
|
||||||
|
while(ch > buffer && *ch == '0'){
|
||||||
|
--ch;
|
||||||
|
}
|
||||||
|
char* last_nonzero = ch;
|
||||||
|
while(ch >= buffer){
|
||||||
|
switch(*ch){
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
--ch;
|
||||||
|
continue;
|
||||||
|
case '.':
|
||||||
|
// Truncate zeroes to save bytes in output, but keep one.
|
||||||
|
*(last_nonzero+2) = '\0';
|
||||||
|
return buffer;
|
||||||
|
default:
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string valueToString( bool value )
|
||||||
|
{
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string valueToQuotedString( const char *value )
|
||||||
|
{
|
||||||
|
// Not sure how to handle unicode...
|
||||||
|
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
|
||||||
|
return std::string("\"") + value + "\"";
|
||||||
|
// We have to walk value and escape any special characters.
|
||||||
|
// Appending to std::string is not efficient, but this should be rare.
|
||||||
|
// (Note: forward slashes are *not* rare, but I am not escaping them.)
|
||||||
|
unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
|
||||||
|
std::string result;
|
||||||
|
result.reserve(maxsize); // to avoid lots of mallocs
|
||||||
|
result += "\"";
|
||||||
|
for (const char* c=value; *c != 0; ++c)
|
||||||
|
{
|
||||||
|
switch(*c)
|
||||||
|
{
|
||||||
|
case '\"':
|
||||||
|
result += "\\\"";
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
result += "\\\\";
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
result += "\\b";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
result += "\\f";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
result += "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
result += "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
result += "\\t";
|
||||||
|
break;
|
||||||
|
//case '/':
|
||||||
|
// Even though \/ is considered a legal escape in JSON, a bare
|
||||||
|
// slash is also legal, so I see no reason to escape it.
|
||||||
|
// (I hope I am not misunderstanding something.
|
||||||
|
// blep notes: actually escaping \/ may be useful in javascript to avoid </
|
||||||
|
// sequence.
|
||||||
|
// Should add a flag to allow this compatibility mode and prevent this
|
||||||
|
// sequence from occurring.
|
||||||
|
default:
|
||||||
|
if ( isControlCharacter( *c ) )
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
|
||||||
|
result += oss.str();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result += *c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "\"";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class Writer
|
||||||
|
// //////////////////////////////////////////////////////////////////
|
||||||
|
Writer::~Writer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Class FastWriter
|
||||||
|
// //////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
FastWriter::FastWriter()
|
||||||
|
: yamlCompatiblityEnabled_( false )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
FastWriter::enableYAMLCompatibility()
|
||||||
|
{
|
||||||
|
yamlCompatiblityEnabled_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
FastWriter::write( const Value &root )
|
||||||
|
{
|
||||||
|
document_ = "";
|
||||||
|
writeValue( root );
|
||||||
|
document_ += "\n";
|
||||||
|
return document_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
FastWriter::writeValue( const Value &value )
|
||||||
|
{
|
||||||
|
switch ( value.type() )
|
||||||
|
{
|
||||||
|
case nullValue:
|
||||||
|
document_ += "null";
|
||||||
|
break;
|
||||||
|
case intValue:
|
||||||
|
document_ += valueToString( value.asInt() );
|
||||||
|
break;
|
||||||
|
case uintValue:
|
||||||
|
document_ += valueToString( value.asUInt() );
|
||||||
|
break;
|
||||||
|
case realValue:
|
||||||
|
document_ += valueToString( value.asDouble() );
|
||||||
|
break;
|
||||||
|
case stringValue:
|
||||||
|
document_ += valueToQuotedString( value.asCString() );
|
||||||
|
break;
|
||||||
|
case booleanValue:
|
||||||
|
document_ += valueToString( value.asBool() );
|
||||||
|
break;
|
||||||
|
case arrayValue:
|
||||||
|
{
|
||||||
|
document_ += "[";
|
||||||
|
int size = value.size();
|
||||||
|
for ( int index =0; index < size; ++index )
|
||||||
|
{
|
||||||
|
if ( index > 0 )
|
||||||
|
document_ += ",";
|
||||||
|
writeValue( value[index] );
|
||||||
|
}
|
||||||
|
document_ += "]";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case objectValue:
|
||||||
|
{
|
||||||
|
Value::Members members( value.getMemberNames() );
|
||||||
|
document_ += "{";
|
||||||
|
for ( Value::Members::iterator it = members.begin();
|
||||||
|
it != members.end();
|
||||||
|
++it )
|
||||||
|
{
|
||||||
|
const std::string &name = *it;
|
||||||
|
if ( it != members.begin() )
|
||||||
|
document_ += ",";
|
||||||
|
document_ += valueToQuotedString( name.c_str() );
|
||||||
|
document_ += yamlCompatiblityEnabled_ ? ": "
|
||||||
|
: ":";
|
||||||
|
writeValue( value[name] );
|
||||||
|
}
|
||||||
|
document_ += "}";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Class StyledWriter
|
||||||
|
// //////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
StyledWriter::StyledWriter()
|
||||||
|
: rightMargin_( 74 )
|
||||||
|
, indentSize_( 3 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
StyledWriter::write( const Value &root )
|
||||||
|
{
|
||||||
|
document_ = "";
|
||||||
|
addChildValues_ = false;
|
||||||
|
indentString_ = "";
|
||||||
|
writeCommentBeforeValue( root );
|
||||||
|
writeValue( root );
|
||||||
|
writeCommentAfterValueOnSameLine( root );
|
||||||
|
document_ += "\n";
|
||||||
|
return document_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::writeValue( const Value &value )
|
||||||
|
{
|
||||||
|
switch ( value.type() )
|
||||||
|
{
|
||||||
|
case nullValue:
|
||||||
|
pushValue( "null" );
|
||||||
|
break;
|
||||||
|
case intValue:
|
||||||
|
pushValue( valueToString( value.asInt() ) );
|
||||||
|
break;
|
||||||
|
case uintValue:
|
||||||
|
pushValue( valueToString( value.asUInt() ) );
|
||||||
|
break;
|
||||||
|
case realValue:
|
||||||
|
pushValue( valueToString( value.asDouble() ) );
|
||||||
|
break;
|
||||||
|
case stringValue:
|
||||||
|
pushValue( valueToQuotedString( value.asCString() ) );
|
||||||
|
break;
|
||||||
|
case booleanValue:
|
||||||
|
pushValue( valueToString( value.asBool() ) );
|
||||||
|
break;
|
||||||
|
case arrayValue:
|
||||||
|
writeArrayValue( value);
|
||||||
|
break;
|
||||||
|
case objectValue:
|
||||||
|
{
|
||||||
|
Value::Members members( value.getMemberNames() );
|
||||||
|
if ( members.empty() )
|
||||||
|
pushValue( "{}" );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeWithIndent( "{" );
|
||||||
|
indent();
|
||||||
|
Value::Members::iterator it = members.begin();
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
const std::string &name = *it;
|
||||||
|
const Value &childValue = value[name];
|
||||||
|
writeCommentBeforeValue( childValue );
|
||||||
|
writeWithIndent( valueToQuotedString( name.c_str() ) );
|
||||||
|
document_ += " : ";
|
||||||
|
writeValue( childValue );
|
||||||
|
if ( ++it == members.end() )
|
||||||
|
{
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
document_ += ",";
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
}
|
||||||
|
unindent();
|
||||||
|
writeWithIndent( "}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::writeArrayValue( const Value &value )
|
||||||
|
{
|
||||||
|
unsigned size = value.size();
|
||||||
|
if ( size == 0 )
|
||||||
|
pushValue( "[]" );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool isArrayMultiLine = isMultineArray( value );
|
||||||
|
if ( isArrayMultiLine )
|
||||||
|
{
|
||||||
|
writeWithIndent( "[" );
|
||||||
|
indent();
|
||||||
|
bool hasChildValue = !childValues_.empty();
|
||||||
|
unsigned index =0;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
const Value &childValue = value[index];
|
||||||
|
writeCommentBeforeValue( childValue );
|
||||||
|
if ( hasChildValue )
|
||||||
|
writeWithIndent( childValues_[index] );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeIndent();
|
||||||
|
writeValue( childValue );
|
||||||
|
}
|
||||||
|
if ( ++index == size )
|
||||||
|
{
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
document_ += ",";
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
}
|
||||||
|
unindent();
|
||||||
|
writeWithIndent( "]" );
|
||||||
|
}
|
||||||
|
else // output on a single line
|
||||||
|
{
|
||||||
|
assert( childValues_.size() == size );
|
||||||
|
document_ += "[ ";
|
||||||
|
for ( unsigned index =0; index < size; ++index )
|
||||||
|
{
|
||||||
|
if ( index > 0 )
|
||||||
|
document_ += ", ";
|
||||||
|
document_ += childValues_[index];
|
||||||
|
}
|
||||||
|
document_ += " ]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
StyledWriter::isMultineArray( const Value &value )
|
||||||
|
{
|
||||||
|
int size = value.size();
|
||||||
|
bool isMultiLine = size*3 >= rightMargin_ ;
|
||||||
|
childValues_.clear();
|
||||||
|
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||||
|
{
|
||||||
|
const Value &childValue = value[index];
|
||||||
|
isMultiLine = isMultiLine ||
|
||||||
|
( (childValue.isArray() || childValue.isObject()) &&
|
||||||
|
childValue.size() > 0 );
|
||||||
|
}
|
||||||
|
if ( !isMultiLine ) // check if line length > max line length
|
||||||
|
{
|
||||||
|
childValues_.reserve( size );
|
||||||
|
addChildValues_ = true;
|
||||||
|
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
|
||||||
|
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||||
|
{
|
||||||
|
writeValue( value[index] );
|
||||||
|
lineLength += int( childValues_[index].length() );
|
||||||
|
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
|
||||||
|
}
|
||||||
|
addChildValues_ = false;
|
||||||
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||||
|
}
|
||||||
|
return isMultiLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::pushValue( const std::string &value )
|
||||||
|
{
|
||||||
|
if ( addChildValues_ )
|
||||||
|
childValues_.push_back( value );
|
||||||
|
else
|
||||||
|
document_ += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::writeIndent()
|
||||||
|
{
|
||||||
|
if ( !document_.empty() )
|
||||||
|
{
|
||||||
|
char last = document_[document_.length()-1];
|
||||||
|
if ( last == ' ' ) // already indented
|
||||||
|
return;
|
||||||
|
if ( last != '\n' ) // Comments may add new-line
|
||||||
|
document_ += '\n';
|
||||||
|
}
|
||||||
|
document_ += indentString_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::writeWithIndent( const std::string &value )
|
||||||
|
{
|
||||||
|
writeIndent();
|
||||||
|
document_ += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::indent()
|
||||||
|
{
|
||||||
|
indentString_ += std::string( indentSize_, ' ' );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::unindent()
|
||||||
|
{
|
||||||
|
assert( int(indentString_.size()) >= indentSize_ );
|
||||||
|
indentString_.resize( indentString_.size() - indentSize_ );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::writeCommentBeforeValue( const Value &root )
|
||||||
|
{
|
||||||
|
if ( !root.hasComment( commentBefore ) )
|
||||||
|
return;
|
||||||
|
document_ += normalizeEOL( root.getComment( commentBefore ) );
|
||||||
|
document_ += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
|
||||||
|
{
|
||||||
|
if ( root.hasComment( commentAfterOnSameLine ) )
|
||||||
|
document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
|
||||||
|
|
||||||
|
if ( root.hasComment( commentAfter ) )
|
||||||
|
{
|
||||||
|
document_ += "\n";
|
||||||
|
document_ += normalizeEOL( root.getComment( commentAfter ) );
|
||||||
|
document_ += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
StyledWriter::hasCommentForValue( const Value &value )
|
||||||
|
{
|
||||||
|
return value.hasComment( commentBefore )
|
||||||
|
|| value.hasComment( commentAfterOnSameLine )
|
||||||
|
|| value.hasComment( commentAfter );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
StyledWriter::normalizeEOL( const std::string &text )
|
||||||
|
{
|
||||||
|
std::string normalized;
|
||||||
|
normalized.reserve( text.length() );
|
||||||
|
const char *begin = text.c_str();
|
||||||
|
const char *end = begin + text.length();
|
||||||
|
const char *current = begin;
|
||||||
|
while ( current != end )
|
||||||
|
{
|
||||||
|
char c = *current++;
|
||||||
|
if ( c == '\r' ) // mac or dos EOL
|
||||||
|
{
|
||||||
|
if ( *current == '\n' ) // convert dos EOL
|
||||||
|
++current;
|
||||||
|
normalized += '\n';
|
||||||
|
}
|
||||||
|
else // handle unix EOL & other char
|
||||||
|
normalized += c;
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Class StyledStreamWriter
|
||||||
|
// //////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
StyledStreamWriter::StyledStreamWriter( std::string indentation )
|
||||||
|
: document_(NULL)
|
||||||
|
, rightMargin_( 74 )
|
||||||
|
, indentation_( indentation )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::write( std::ostream &out, const Value &root )
|
||||||
|
{
|
||||||
|
document_ = &out;
|
||||||
|
addChildValues_ = false;
|
||||||
|
indentString_ = "";
|
||||||
|
writeCommentBeforeValue( root );
|
||||||
|
writeValue( root );
|
||||||
|
writeCommentAfterValueOnSameLine( root );
|
||||||
|
*document_ << "\n";
|
||||||
|
document_ = NULL; // Forget the stream, for safety.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeValue( const Value &value )
|
||||||
|
{
|
||||||
|
switch ( value.type() )
|
||||||
|
{
|
||||||
|
case nullValue:
|
||||||
|
pushValue( "null" );
|
||||||
|
break;
|
||||||
|
case intValue:
|
||||||
|
pushValue( valueToString( value.asInt() ) );
|
||||||
|
break;
|
||||||
|
case uintValue:
|
||||||
|
pushValue( valueToString( value.asUInt() ) );
|
||||||
|
break;
|
||||||
|
case realValue:
|
||||||
|
pushValue( valueToString( value.asDouble() ) );
|
||||||
|
break;
|
||||||
|
case stringValue:
|
||||||
|
pushValue( valueToQuotedString( value.asCString() ) );
|
||||||
|
break;
|
||||||
|
case booleanValue:
|
||||||
|
pushValue( valueToString( value.asBool() ) );
|
||||||
|
break;
|
||||||
|
case arrayValue:
|
||||||
|
writeArrayValue( value);
|
||||||
|
break;
|
||||||
|
case objectValue:
|
||||||
|
{
|
||||||
|
Value::Members members( value.getMemberNames() );
|
||||||
|
if ( members.empty() )
|
||||||
|
pushValue( "{}" );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeWithIndent( "{" );
|
||||||
|
indent();
|
||||||
|
Value::Members::iterator it = members.begin();
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
const std::string &name = *it;
|
||||||
|
const Value &childValue = value[name];
|
||||||
|
writeCommentBeforeValue( childValue );
|
||||||
|
writeWithIndent( valueToQuotedString( name.c_str() ) );
|
||||||
|
*document_ << " : ";
|
||||||
|
writeValue( childValue );
|
||||||
|
if ( ++it == members.end() )
|
||||||
|
{
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*document_ << ",";
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
}
|
||||||
|
unindent();
|
||||||
|
writeWithIndent( "}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeArrayValue( const Value &value )
|
||||||
|
{
|
||||||
|
unsigned size = value.size();
|
||||||
|
if ( size == 0 )
|
||||||
|
pushValue( "[]" );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool isArrayMultiLine = isMultineArray( value );
|
||||||
|
if ( isArrayMultiLine )
|
||||||
|
{
|
||||||
|
writeWithIndent( "[" );
|
||||||
|
indent();
|
||||||
|
bool hasChildValue = !childValues_.empty();
|
||||||
|
unsigned index =0;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
const Value &childValue = value[index];
|
||||||
|
writeCommentBeforeValue( childValue );
|
||||||
|
if ( hasChildValue )
|
||||||
|
writeWithIndent( childValues_[index] );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeIndent();
|
||||||
|
writeValue( childValue );
|
||||||
|
}
|
||||||
|
if ( ++index == size )
|
||||||
|
{
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*document_ << ",";
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
}
|
||||||
|
unindent();
|
||||||
|
writeWithIndent( "]" );
|
||||||
|
}
|
||||||
|
else // output on a single line
|
||||||
|
{
|
||||||
|
assert( childValues_.size() == size );
|
||||||
|
*document_ << "[ ";
|
||||||
|
for ( unsigned index =0; index < size; ++index )
|
||||||
|
{
|
||||||
|
if ( index > 0 )
|
||||||
|
*document_ << ", ";
|
||||||
|
*document_ << childValues_[index];
|
||||||
|
}
|
||||||
|
*document_ << " ]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
StyledStreamWriter::isMultineArray( const Value &value )
|
||||||
|
{
|
||||||
|
int size = value.size();
|
||||||
|
bool isMultiLine = size*3 >= rightMargin_ ;
|
||||||
|
childValues_.clear();
|
||||||
|
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||||
|
{
|
||||||
|
const Value &childValue = value[index];
|
||||||
|
isMultiLine = isMultiLine ||
|
||||||
|
( (childValue.isArray() || childValue.isObject()) &&
|
||||||
|
childValue.size() > 0 );
|
||||||
|
}
|
||||||
|
if ( !isMultiLine ) // check if line length > max line length
|
||||||
|
{
|
||||||
|
childValues_.reserve( size );
|
||||||
|
addChildValues_ = true;
|
||||||
|
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
|
||||||
|
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||||
|
{
|
||||||
|
writeValue( value[index] );
|
||||||
|
lineLength += int( childValues_[index].length() );
|
||||||
|
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
|
||||||
|
}
|
||||||
|
addChildValues_ = false;
|
||||||
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||||
|
}
|
||||||
|
return isMultiLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::pushValue( const std::string &value )
|
||||||
|
{
|
||||||
|
if ( addChildValues_ )
|
||||||
|
childValues_.push_back( value );
|
||||||
|
else
|
||||||
|
*document_ << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeIndent()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Some comments in this method would have been nice. ;-)
|
||||||
|
|
||||||
|
if ( !document_.empty() )
|
||||||
|
{
|
||||||
|
char last = document_[document_.length()-1];
|
||||||
|
if ( last == ' ' ) // already indented
|
||||||
|
return;
|
||||||
|
if ( last != '\n' ) // Comments may add new-line
|
||||||
|
*document_ << '\n';
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
*document_ << '\n' << indentString_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeWithIndent( const std::string &value )
|
||||||
|
{
|
||||||
|
writeIndent();
|
||||||
|
*document_ << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::indent()
|
||||||
|
{
|
||||||
|
indentString_ += indentation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::unindent()
|
||||||
|
{
|
||||||
|
assert( indentString_.size() >= indentation_.size() );
|
||||||
|
indentString_.resize( indentString_.size() - indentation_.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeCommentBeforeValue( const Value &root )
|
||||||
|
{
|
||||||
|
if ( !root.hasComment( commentBefore ) )
|
||||||
|
return;
|
||||||
|
*document_ << normalizeEOL( root.getComment( commentBefore ) );
|
||||||
|
*document_ << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
|
||||||
|
{
|
||||||
|
if ( root.hasComment( commentAfterOnSameLine ) )
|
||||||
|
*document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
|
||||||
|
|
||||||
|
if ( root.hasComment( commentAfter ) )
|
||||||
|
{
|
||||||
|
*document_ << "\n";
|
||||||
|
*document_ << normalizeEOL( root.getComment( commentAfter ) );
|
||||||
|
*document_ << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
StyledStreamWriter::hasCommentForValue( const Value &value )
|
||||||
|
{
|
||||||
|
return value.hasComment( commentBefore )
|
||||||
|
|| value.hasComment( commentAfterOnSameLine )
|
||||||
|
|| value.hasComment( commentAfter );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
StyledStreamWriter::normalizeEOL( const std::string &text )
|
||||||
|
{
|
||||||
|
std::string normalized;
|
||||||
|
normalized.reserve( text.length() );
|
||||||
|
const char *begin = text.c_str();
|
||||||
|
const char *end = begin + text.length();
|
||||||
|
const char *current = begin;
|
||||||
|
while ( current != end )
|
||||||
|
{
|
||||||
|
char c = *current++;
|
||||||
|
if ( c == '\r' ) // mac or dos EOL
|
||||||
|
{
|
||||||
|
if ( *current == '\n' ) // convert dos EOL
|
||||||
|
++current;
|
||||||
|
normalized += '\n';
|
||||||
|
}
|
||||||
|
else // handle unix EOL & other char
|
||||||
|
normalized += c;
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream& operator<<( std::ostream &sout, const Value &root )
|
||||||
|
{
|
||||||
|
Json::StyledStreamWriter writer;
|
||||||
|
writer.write(sout, root);
|
||||||
|
return sout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Json
|
||||||
@@ -1,415 +0,0 @@
|
|||||||
// This defines the interface to the QsciCommand class.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2011 Riverbank Computing Limited <info@riverbankcomputing.com>
|
|
||||||
//
|
|
||||||
// This file is part of QScintilla.
|
|
||||||
//
|
|
||||||
// This file may be used under the terms of the GNU General Public
|
|
||||||
// License versions 2.0 or 3.0 as published by the Free Software
|
|
||||||
// Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
|
|
||||||
// included in the packaging of this file. Alternatively you may (at
|
|
||||||
// your option) use any later version of the GNU General Public
|
|
||||||
// License if such license has been publicly approved by Riverbank
|
|
||||||
// Computing Limited (or its successors, if any) and the KDE Free Qt
|
|
||||||
// Foundation. In addition, as a special exception, Riverbank gives you
|
|
||||||
// certain additional rights. These rights are described in the Riverbank
|
|
||||||
// GPL Exception version 1.1, which can be found in the file
|
|
||||||
// GPL_EXCEPTION.txt in this package.
|
|
||||||
//
|
|
||||||
// If you are unsure which license is appropriate for your use, please
|
|
||||||
// contact the sales department at sales@riverbankcomputing.com.
|
|
||||||
//
|
|
||||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
||||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef QSCICOMMAND_H
|
|
||||||
#define QSCICOMMAND_H
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
extern "C++" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <qstring.h>
|
|
||||||
|
|
||||||
#include <Qsci/qsciglobal.h>
|
|
||||||
#include <Qsci/qsciscintillabase.h>
|
|
||||||
|
|
||||||
|
|
||||||
class QsciScintilla;
|
|
||||||
|
|
||||||
|
|
||||||
//! \brief The QsciCommand class represents an internal editor command that may
|
|
||||||
//! have one or two keys bound to it.
|
|
||||||
//!
|
|
||||||
//! Methods are provided to change the keys bound to the command and to remove
|
|
||||||
//! a key binding. Each command has a user friendly description of the command
|
|
||||||
//! for use in key mapping dialogs.
|
|
||||||
class QSCINTILLA_EXPORT QsciCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//! This enum defines the different commands that can be assigned to a key.
|
|
||||||
enum Command {
|
|
||||||
//! Move down one line.
|
|
||||||
LineDown = QsciScintillaBase::SCI_LINEDOWN,
|
|
||||||
|
|
||||||
//! Extend the selection down one line.
|
|
||||||
LineDownExtend = QsciScintillaBase::SCI_LINEDOWNEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection down one line.
|
|
||||||
LineDownRectExtend = QsciScintillaBase::SCI_LINEDOWNRECTEXTEND,
|
|
||||||
|
|
||||||
//! Scroll the view down one line.
|
|
||||||
LineScrollDown = QsciScintillaBase::SCI_LINESCROLLDOWN,
|
|
||||||
|
|
||||||
//! Move up one line.
|
|
||||||
LineUp = QsciScintillaBase::SCI_LINEUP,
|
|
||||||
|
|
||||||
//! Extend the selection up one line.
|
|
||||||
LineUpExtend = QsciScintillaBase::SCI_LINEUPEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection up one line.
|
|
||||||
LineUpRectExtend = QsciScintillaBase::SCI_LINEUPRECTEXTEND,
|
|
||||||
|
|
||||||
//! Scroll the view up one line.
|
|
||||||
LineScrollUp = QsciScintillaBase::SCI_LINESCROLLUP,
|
|
||||||
|
|
||||||
//! Scroll to the start of the document.
|
|
||||||
ScrollToStart = QsciScintillaBase::SCI_SCROLLTOSTART,
|
|
||||||
|
|
||||||
//! Scroll to the end of the document.
|
|
||||||
ScrollToEnd = QsciScintillaBase::SCI_SCROLLTOEND,
|
|
||||||
|
|
||||||
//! Scroll vertically to centre the current line.
|
|
||||||
VerticalCentreCaret = QsciScintillaBase::SCI_VERTICALCENTRECARET,
|
|
||||||
|
|
||||||
//! Move down one paragraph.
|
|
||||||
ParaDown = QsciScintillaBase::SCI_PARADOWN,
|
|
||||||
|
|
||||||
//! Extend the selection down one paragraph.
|
|
||||||
ParaDownExtend = QsciScintillaBase::SCI_PARADOWNEXTEND,
|
|
||||||
|
|
||||||
//! Move up one paragraph.
|
|
||||||
ParaUp = QsciScintillaBase::SCI_PARAUP,
|
|
||||||
|
|
||||||
//! Extend the selection up one paragraph.
|
|
||||||
ParaUpExtend = QsciScintillaBase::SCI_PARAUPEXTEND,
|
|
||||||
|
|
||||||
//! Move left one character.
|
|
||||||
CharLeft = QsciScintillaBase::SCI_CHARLEFT,
|
|
||||||
|
|
||||||
//! Extend the selection left one character.
|
|
||||||
CharLeftExtend = QsciScintillaBase::SCI_CHARLEFTEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection left one character.
|
|
||||||
CharLeftRectExtend = QsciScintillaBase::SCI_CHARLEFTRECTEXTEND,
|
|
||||||
|
|
||||||
//! Move right one character.
|
|
||||||
CharRight = QsciScintillaBase::SCI_CHARRIGHT,
|
|
||||||
|
|
||||||
//! Extend the selection right one character.
|
|
||||||
CharRightExtend = QsciScintillaBase::SCI_CHARRIGHTEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection right one character.
|
|
||||||
CharRightRectExtend = QsciScintillaBase::SCI_CHARRIGHTRECTEXTEND,
|
|
||||||
|
|
||||||
//! Move left one word.
|
|
||||||
WordLeft = QsciScintillaBase::SCI_WORDLEFT,
|
|
||||||
|
|
||||||
//! Extend the selection left one word.
|
|
||||||
WordLeftExtend = QsciScintillaBase::SCI_WORDLEFTEXTEND,
|
|
||||||
|
|
||||||
//! Move right one word.
|
|
||||||
WordRight = QsciScintillaBase::SCI_WORDRIGHT,
|
|
||||||
|
|
||||||
//! Extend the selection right one word.
|
|
||||||
WordRightExtend = QsciScintillaBase::SCI_WORDRIGHTEXTEND,
|
|
||||||
|
|
||||||
//! Move to the end of the previous word.
|
|
||||||
WordLeftEnd = QsciScintillaBase::SCI_WORDLEFTEND,
|
|
||||||
|
|
||||||
//! Extend the selection to the end of the previous word.
|
|
||||||
WordLeftEndExtend = QsciScintillaBase::SCI_WORDLEFTENDEXTEND,
|
|
||||||
|
|
||||||
//! Move to the end of the next word.
|
|
||||||
WordRightEnd = QsciScintillaBase::SCI_WORDRIGHTEND,
|
|
||||||
|
|
||||||
//! Extend the selection to the end of the next word.
|
|
||||||
WordRightEndExtend = QsciScintillaBase::SCI_WORDRIGHTENDEXTEND,
|
|
||||||
|
|
||||||
//! Move left one word part.
|
|
||||||
WordPartLeft = QsciScintillaBase::SCI_WORDPARTLEFT,
|
|
||||||
|
|
||||||
//! Extend the selection left one word part.
|
|
||||||
WordPartLeftExtend = QsciScintillaBase::SCI_WORDPARTLEFTEXTEND,
|
|
||||||
|
|
||||||
//! Move right one word part.
|
|
||||||
WordPartRight = QsciScintillaBase::SCI_WORDPARTRIGHT,
|
|
||||||
|
|
||||||
//! Extend the selection right one word part.
|
|
||||||
WordPartRightExtend = QsciScintillaBase::SCI_WORDPARTRIGHTEXTEND,
|
|
||||||
|
|
||||||
//! Move to the start of the document line.
|
|
||||||
Home = QsciScintillaBase::SCI_HOME,
|
|
||||||
|
|
||||||
//! Extend the selection to the start of the document line.
|
|
||||||
HomeExtend = QsciScintillaBase::SCI_HOMEEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection to the start of the document line.
|
|
||||||
HomeRectExtend = QsciScintillaBase::SCI_HOMERECTEXTEND,
|
|
||||||
|
|
||||||
//! Move to the start of the displayed line.
|
|
||||||
HomeDisplay = QsciScintillaBase::SCI_HOMEDISPLAY,
|
|
||||||
|
|
||||||
//! Extend the selection to the start of the displayed line.
|
|
||||||
HomeDisplayExtend = QsciScintillaBase::SCI_HOMEDISPLAYEXTEND,
|
|
||||||
|
|
||||||
//! Move to the start of the displayed or document line.
|
|
||||||
HomeWrap = QsciScintillaBase::SCI_HOMEWRAP,
|
|
||||||
|
|
||||||
//! Extend the selection to the start of the displayed or document
|
|
||||||
//! line.
|
|
||||||
HomeWrapExtend = QsciScintillaBase::SCI_HOMEWRAPEXTEND,
|
|
||||||
|
|
||||||
//! Move to the first visible character in the document line.
|
|
||||||
VCHome = QsciScintillaBase::SCI_VCHOME,
|
|
||||||
|
|
||||||
//! Extend the selection to the first visible character in the document
|
|
||||||
//! line.
|
|
||||||
VCHomeExtend = QsciScintillaBase::SCI_VCHOMEEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection to the first visible character in
|
|
||||||
//! the document line.
|
|
||||||
VCHomeRectExtend = QsciScintillaBase::SCI_VCHOMERECTEXTEND,
|
|
||||||
|
|
||||||
//! Move to the first visible character of the displayed or document
|
|
||||||
//! line.
|
|
||||||
VCHomeWrap = QsciScintillaBase::SCI_VCHOMEWRAP,
|
|
||||||
|
|
||||||
//! Extend the selection to the first visible character of the
|
|
||||||
//! displayed or document line.
|
|
||||||
VCHomeWrapExtend = QsciScintillaBase::SCI_VCHOMEWRAPEXTEND,
|
|
||||||
|
|
||||||
//! Move to the end of the document line.
|
|
||||||
LineEnd = QsciScintillaBase::SCI_LINEEND,
|
|
||||||
|
|
||||||
//! Extend the selection to the end of the document line.
|
|
||||||
LineEndExtend = QsciScintillaBase::SCI_LINEENDEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection to the end of the document line.
|
|
||||||
LineEndRectExtend = QsciScintillaBase::SCI_LINEENDRECTEXTEND,
|
|
||||||
|
|
||||||
//! Move to the end of the displayed line.
|
|
||||||
LineEndDisplay = QsciScintillaBase::SCI_LINEENDDISPLAY,
|
|
||||||
|
|
||||||
//! Extend the selection to the end of the displayed line.
|
|
||||||
LineEndDisplayExtend = QsciScintillaBase::SCI_LINEENDDISPLAYEXTEND,
|
|
||||||
|
|
||||||
//! Move to the end of the displayed or document line.
|
|
||||||
LineEndWrap = QsciScintillaBase::SCI_LINEENDWRAP,
|
|
||||||
|
|
||||||
//! Extend the selection to the end of the displayed or document line.
|
|
||||||
LineEndWrapExtend = QsciScintillaBase::SCI_LINEENDWRAPEXTEND,
|
|
||||||
|
|
||||||
//! Move to the start of the document.
|
|
||||||
DocumentStart = QsciScintillaBase::SCI_DOCUMENTSTART,
|
|
||||||
|
|
||||||
//! Extend the selection to the start of the document.
|
|
||||||
DocumentStartExtend = QsciScintillaBase::SCI_DOCUMENTSTARTEXTEND,
|
|
||||||
|
|
||||||
//! Move to the end of the document.
|
|
||||||
DocumentEnd = QsciScintillaBase::SCI_DOCUMENTEND,
|
|
||||||
|
|
||||||
//! Extend the selection to the end of the document.
|
|
||||||
DocumentEndExtend = QsciScintillaBase::SCI_DOCUMENTENDEXTEND,
|
|
||||||
|
|
||||||
//! Move up one page.
|
|
||||||
PageUp = QsciScintillaBase::SCI_PAGEUP,
|
|
||||||
|
|
||||||
//! Extend the selection up one page.
|
|
||||||
PageUpExtend = QsciScintillaBase::SCI_PAGEUPEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection up one page.
|
|
||||||
PageUpRectExtend = QsciScintillaBase::SCI_PAGEUPRECTEXTEND,
|
|
||||||
|
|
||||||
//! Move down one page.
|
|
||||||
PageDown = QsciScintillaBase::SCI_PAGEDOWN,
|
|
||||||
|
|
||||||
//! Extend the selection down one page.
|
|
||||||
PageDownExtend = QsciScintillaBase::SCI_PAGEDOWNEXTEND,
|
|
||||||
|
|
||||||
//! Extend the rectangular selection down one page.
|
|
||||||
PageDownRectExtend = QsciScintillaBase::SCI_PAGEDOWNRECTEXTEND,
|
|
||||||
|
|
||||||
//! Stuttered move up one page.
|
|
||||||
StutteredPageUp = QsciScintillaBase::SCI_STUTTEREDPAGEUP,
|
|
||||||
|
|
||||||
//! Stuttered extend the selection up one page.
|
|
||||||
StutteredPageUpExtend = QsciScintillaBase::SCI_STUTTEREDPAGEUPEXTEND,
|
|
||||||
|
|
||||||
//! Stuttered move down one page.
|
|
||||||
StutteredPageDown = QsciScintillaBase::SCI_STUTTEREDPAGEDOWN,
|
|
||||||
|
|
||||||
//! Stuttered extend the selection down one page.
|
|
||||||
StutteredPageDownExtend = QsciScintillaBase::SCI_STUTTEREDPAGEDOWNEXTEND,
|
|
||||||
|
|
||||||
//! Delete the current character.
|
|
||||||
Delete = QsciScintillaBase::SCI_CLEAR,
|
|
||||||
|
|
||||||
//! Delete the previous character.
|
|
||||||
DeleteBack = QsciScintillaBase::SCI_DELETEBACK,
|
|
||||||
|
|
||||||
//! Delete the previous character if not at start of line.
|
|
||||||
DeleteBackNotLine = QsciScintillaBase::SCI_DELETEBACKNOTLINE,
|
|
||||||
|
|
||||||
//! Delete the word to the left.
|
|
||||||
DeleteWordLeft = QsciScintillaBase::SCI_DELWORDLEFT,
|
|
||||||
|
|
||||||
//! Delete the word to the right.
|
|
||||||
DeleteWordRight = QsciScintillaBase::SCI_DELWORDRIGHT,
|
|
||||||
|
|
||||||
//! Delete right to the end of the next word.
|
|
||||||
DeleteWordRightEnd = QsciScintillaBase::SCI_DELWORDRIGHTEND,
|
|
||||||
|
|
||||||
//! Delete the line to the left.
|
|
||||||
DeleteLineLeft = QsciScintillaBase::SCI_DELLINELEFT,
|
|
||||||
|
|
||||||
//! Delete the line to the right.
|
|
||||||
DeleteLineRight = QsciScintillaBase::SCI_DELLINERIGHT,
|
|
||||||
|
|
||||||
//! Delete the current line.
|
|
||||||
LineDelete = QsciScintillaBase::SCI_LINEDELETE,
|
|
||||||
|
|
||||||
//! Cut the current line to the clipboard.
|
|
||||||
LineCut = QsciScintillaBase::SCI_LINECUT,
|
|
||||||
|
|
||||||
//! Copy the current line to the clipboard.
|
|
||||||
LineCopy = QsciScintillaBase::SCI_LINECOPY,
|
|
||||||
|
|
||||||
//! Transpose the current and previous lines.
|
|
||||||
LineTranspose = QsciScintillaBase::SCI_LINETRANSPOSE,
|
|
||||||
|
|
||||||
//! Duplicate the current line.
|
|
||||||
LineDuplicate = QsciScintillaBase::SCI_LINEDUPLICATE,
|
|
||||||
|
|
||||||
//! Select the whole document.
|
|
||||||
SelectAll = QsciScintillaBase::SCI_SELECTALL,
|
|
||||||
|
|
||||||
//! Move the selected lines up one line.
|
|
||||||
MoveSelectedLinesUp = QsciScintillaBase::SCI_MOVESELECTEDLINESUP,
|
|
||||||
|
|
||||||
//! Move the selected lines down one line.
|
|
||||||
MoveSelectedLinesDown = QsciScintillaBase::SCI_MOVESELECTEDLINESDOWN,
|
|
||||||
|
|
||||||
//! Duplicate the selection.
|
|
||||||
SelectionDuplicate = QsciScintillaBase::SCI_SELECTIONDUPLICATE,
|
|
||||||
|
|
||||||
//! Convert the selection to lower case.
|
|
||||||
SelectionLowerCase = QsciScintillaBase::SCI_LOWERCASE,
|
|
||||||
|
|
||||||
//! Convert the selection to upper case.
|
|
||||||
SelectionUpperCase = QsciScintillaBase::SCI_UPPERCASE,
|
|
||||||
|
|
||||||
//! Cut the selection to the clipboard.
|
|
||||||
SelectionCut = QsciScintillaBase::SCI_CUT,
|
|
||||||
|
|
||||||
//! Copy the selection to the clipboard.
|
|
||||||
SelectionCopy = QsciScintillaBase::SCI_COPY,
|
|
||||||
|
|
||||||
//! Paste from the clipboard.
|
|
||||||
Paste = QsciScintillaBase::SCI_PASTE,
|
|
||||||
|
|
||||||
//! Toggle insert/overtype.
|
|
||||||
EditToggleOvertype = QsciScintillaBase::SCI_EDITTOGGLEOVERTYPE,
|
|
||||||
|
|
||||||
//! Insert a platform dependent newline.
|
|
||||||
Newline = QsciScintillaBase::SCI_NEWLINE,
|
|
||||||
|
|
||||||
//! Insert a formfeed.
|
|
||||||
Formfeed = QsciScintillaBase::SCI_FORMFEED,
|
|
||||||
|
|
||||||
//! Indent one level.
|
|
||||||
Tab = QsciScintillaBase::SCI_TAB,
|
|
||||||
|
|
||||||
//! De-indent one level.
|
|
||||||
Backtab = QsciScintillaBase::SCI_BACKTAB,
|
|
||||||
|
|
||||||
//! Cancel any current operation.
|
|
||||||
Cancel = QsciScintillaBase::SCI_CANCEL,
|
|
||||||
|
|
||||||
//! Undo the last command.
|
|
||||||
Undo = QsciScintillaBase::SCI_UNDO,
|
|
||||||
|
|
||||||
//! Redo the last command.
|
|
||||||
Redo = QsciScintillaBase::SCI_REDO,
|
|
||||||
|
|
||||||
//! Zoom in.
|
|
||||||
ZoomIn = QsciScintillaBase::SCI_ZOOMIN,
|
|
||||||
|
|
||||||
//! Zoom out.
|
|
||||||
ZoomOut = QsciScintillaBase::SCI_ZOOMOUT,
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Return the command that will be executed by this instance.
|
|
||||||
Command command() const {return scicmd;}
|
|
||||||
|
|
||||||
//! Execute the command.
|
|
||||||
void execute();
|
|
||||||
|
|
||||||
//! Binds the key \a key to the command. If \a key is 0 then the key
|
|
||||||
//! binding is removed. If \a key is invalid then the key binding is
|
|
||||||
//! unchanged. Valid keys are any visible or control character or any
|
|
||||||
//! of \c Key_Down, \c Key_Up, \c Key_Left, \c Key_Right, \c Key_Home,
|
|
||||||
//! \c Key_End, \c Key_PageUp, \c Key_PageDown, \c Key_Delete,
|
|
||||||
//! \c Key_Insert, \c Key_Escape, \c Key_Backspace, \c Key_Tab and
|
|
||||||
//! \c Key_Return. Keys may be modified with any combination of \c SHIFT,
|
|
||||||
//! \c CTRL, \c ALT and \c META.
|
|
||||||
//!
|
|
||||||
//! \sa key(), setAlternateKey(), validKey()
|
|
||||||
void setKey(int key);
|
|
||||||
|
|
||||||
//! Binds the alternate key \a altkey to the command. If \a key is 0
|
|
||||||
//! then the alternate key binding is removed.
|
|
||||||
//!
|
|
||||||
//! \sa alternateKey(), setKey(), validKey()
|
|
||||||
void setAlternateKey(int altkey);
|
|
||||||
|
|
||||||
//! The key that is currently bound to the command is returned.
|
|
||||||
//!
|
|
||||||
//! \sa setKey(), alternateKey()
|
|
||||||
int key() const {return qkey;}
|
|
||||||
|
|
||||||
//! The alternate key that is currently bound to the command is
|
|
||||||
//! returned.
|
|
||||||
//!
|
|
||||||
//! \sa setAlternateKey(), key()
|
|
||||||
int alternateKey() const {return qaltkey;}
|
|
||||||
|
|
||||||
//! If the key \a key is valid then true is returned.
|
|
||||||
static bool validKey(int key);
|
|
||||||
|
|
||||||
//! The user friendly description of the command is returned.
|
|
||||||
QString description() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class QsciCommandSet;
|
|
||||||
|
|
||||||
QsciCommand(QsciScintilla *qs, Command cmd, int key, int altkey,
|
|
||||||
const char *desc);
|
|
||||||
|
|
||||||
void bindKey(int key,int &qk,int &scik);
|
|
||||||
|
|
||||||
QsciScintilla *qsCmd;
|
|
||||||
Command scicmd;
|
|
||||||
int qkey, scikey, qaltkey, scialtkey;
|
|
||||||
const char *descCmd;
|
|
||||||
|
|
||||||
QsciCommand(const QsciCommand &);
|
|
||||||
QsciCommand &operator=(const QsciCommand &);
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
// This module defines interface to the QsciPrinter class.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2011 Riverbank Computing Limited <info@riverbankcomputing.com>
|
|
||||||
//
|
|
||||||
// This file is part of QScintilla.
|
|
||||||
//
|
|
||||||
// This file may be used under the terms of the GNU General Public
|
|
||||||
// License versions 2.0 or 3.0 as published by the Free Software
|
|
||||||
// Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
|
|
||||||
// included in the packaging of this file. Alternatively you may (at
|
|
||||||
// your option) use any later version of the GNU General Public
|
|
||||||
// License if such license has been publicly approved by Riverbank
|
|
||||||
// Computing Limited (or its successors, if any) and the KDE Free Qt
|
|
||||||
// Foundation. In addition, as a special exception, Riverbank gives you
|
|
||||||
// certain additional rights. These rights are described in the Riverbank
|
|
||||||
// GPL Exception version 1.1, which can be found in the file
|
|
||||||
// GPL_EXCEPTION.txt in this package.
|
|
||||||
//
|
|
||||||
// If you are unsure which license is appropriate for your use, please
|
|
||||||
// contact the sales department at sales@riverbankcomputing.com.
|
|
||||||
//
|
|
||||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
||||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef QSCIPRINTER_H
|
|
||||||
#define QSCIPRINTER_H
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
extern "C++" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <qprinter.h>
|
|
||||||
|
|
||||||
#include <Qsci/qsciglobal.h>
|
|
||||||
#include <Qsci/qsciscintilla.h>
|
|
||||||
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QRect;
|
|
||||||
class QPainter;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
class QsciScintillaBase;
|
|
||||||
|
|
||||||
|
|
||||||
//! \brief The QsciPrinter class is a sub-class of the Qt QPrinter class that
|
|
||||||
//! is able to print the text of a Scintilla document.
|
|
||||||
//!
|
|
||||||
//! The class can be further sub-classed to alter to layout of the text, adding
|
|
||||||
//! headers and footers for example.
|
|
||||||
class QSCINTILLA_EXPORT QsciPrinter : public QPrinter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//! Constructs a printer paint device with mode \a mode.
|
|
||||||
QsciPrinter(PrinterMode mode = ScreenResolution);
|
|
||||||
|
|
||||||
//! Destroys the QsciPrinter instance.
|
|
||||||
virtual ~QsciPrinter();
|
|
||||||
|
|
||||||
//! Format a page, by adding headers and footers for example, before the
|
|
||||||
//! document text is drawn on it. \a painter is the painter to be used to
|
|
||||||
//! add customised text and graphics. \a drawing is true if the page is
|
|
||||||
//! actually being drawn rather than being sized. \a painter drawing
|
|
||||||
//! methods must only be called when \a drawing is true. \a area is the
|
|
||||||
//! area of the page that will be used to draw the text. This should be
|
|
||||||
//! modified if it is necessary to reserve space for any customised text or
|
|
||||||
//! graphics. By default the area is relative to the printable area of the
|
|
||||||
//! page. Use QPrinter::setFullPage() because calling printRange() if you
|
|
||||||
//! want to try and print over the whole page. \a pagenr is the number of
|
|
||||||
//! the page. The first page is numbered 1.
|
|
||||||
virtual void formatPage(QPainter &painter, bool drawing, QRect &area,
|
|
||||||
int pagenr);
|
|
||||||
|
|
||||||
//! Return the number of points to add to each font when printing.
|
|
||||||
//!
|
|
||||||
//! \sa setMagnification()
|
|
||||||
int magnification() const {return mag;}
|
|
||||||
|
|
||||||
//! Sets the number of points to add to each font when printing to \a
|
|
||||||
//! magnification.
|
|
||||||
//!
|
|
||||||
//! \sa magnification()
|
|
||||||
virtual void setMagnification(int magnification);
|
|
||||||
|
|
||||||
//! Print a range of lines from the Scintilla instance \a qsb. \a from is
|
|
||||||
//! the first line to print and a negative value signifies the first line
|
|
||||||
//! of text. \a to is the last line to print and a negative value
|
|
||||||
//! signifies the last line of text. true is returned if there was no
|
|
||||||
//! error.
|
|
||||||
virtual int printRange(QsciScintillaBase *qsb, int from = -1, int to = -1);
|
|
||||||
|
|
||||||
//! Return the line wrap mode used when printing. The default is
|
|
||||||
//! QsciScintilla::WrapWord.
|
|
||||||
//!
|
|
||||||
//! \sa setWrapMode()
|
|
||||||
QsciScintilla::WrapMode wrapMode() const {return wrap;}
|
|
||||||
|
|
||||||
//! Sets the line wrap mode used when printing to \a wmode.
|
|
||||||
//!
|
|
||||||
//! \sa wrapMode()
|
|
||||||
virtual void setWrapMode(QsciScintilla::WrapMode wmode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int mag;
|
|
||||||
QsciScintilla::WrapMode wrap;
|
|
||||||
|
|
||||||
QsciPrinter(const QsciPrinter &);
|
|
||||||
QsciPrinter &operator=(const QsciPrinter &);
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
// This module defines interface to the QsciPrinter class.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2011 Riverbank Computing Limited <info@riverbankcomputing.com>
|
|
||||||
//
|
|
||||||
// This file is part of QScintilla.
|
|
||||||
//
|
|
||||||
// This file may be used under the terms of the GNU General Public
|
|
||||||
// License versions 2.0 or 3.0 as published by the Free Software
|
|
||||||
// Foundation and appearing in the files LICENSE.GPL2 and LICENSE.GPL3
|
|
||||||
// included in the packaging of this file. Alternatively you may (at
|
|
||||||
// your option) use any later version of the GNU General Public
|
|
||||||
// License if such license has been publicly approved by Riverbank
|
|
||||||
// Computing Limited (or its successors, if any) and the KDE Free Qt
|
|
||||||
// Foundation. In addition, as a special exception, Riverbank gives you
|
|
||||||
// certain additional rights. These rights are described in the Riverbank
|
|
||||||
// GPL Exception version 1.1, which can be found in the file
|
|
||||||
// GPL_EXCEPTION.txt in this package.
|
|
||||||
//
|
|
||||||
// If you are unsure which license is appropriate for your use, please
|
|
||||||
// contact the sales department at sales@riverbankcomputing.com.
|
|
||||||
//
|
|
||||||
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
||||||
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef QSCIPRINTER_H
|
|
||||||
#define QSCIPRINTER_H
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
extern "C++" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <qprinter.h>
|
|
||||||
|
|
||||||
#include <Qsci/qsciglobal.h>
|
|
||||||
#include <Qsci/qsciscintilla.h>
|
|
||||||
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QRect;
|
|
||||||
class QPainter;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
class QsciScintillaBase;
|
|
||||||
|
|
||||||
|
|
||||||
//! \brief The QsciPrinter class is a sub-class of the Qt QPrinter class that
|
|
||||||
//! is able to print the text of a Scintilla document.
|
|
||||||
//!
|
|
||||||
//! The class can be further sub-classed to alter to layout of the text, adding
|
|
||||||
//! headers and footers for example.
|
|
||||||
class QSCINTILLA_EXPORT QsciPrinter : public QPrinter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//! Constructs a printer paint device with mode \a mode.
|
|
||||||
QsciPrinter(PrinterMode mode = ScreenResolution);
|
|
||||||
|
|
||||||
//! Destroys the QsciPrinter instance.
|
|
||||||
virtual ~QsciPrinter();
|
|
||||||
|
|
||||||
//! Format a page, by adding headers and footers for example, before the
|
|
||||||
//! document text is drawn on it. \a painter is the painter to be used to
|
|
||||||
//! add customised text and graphics. \a drawing is true if the page is
|
|
||||||
//! actually being drawn rather than being sized. \a painter drawing
|
|
||||||
//! methods must only be called when \a drawing is true. \a area is the
|
|
||||||
//! area of the page that will be used to draw the text. This should be
|
|
||||||
//! modified if it is necessary to reserve space for any customised text or
|
|
||||||
//! graphics. By default the area is relative to the printable area of the
|
|
||||||
//! page. Use QPrinter::setFullPage() because calling printRange() if you
|
|
||||||
//! want to try and print over the whole page. \a pagenr is the number of
|
|
||||||
//! the page. The first page is numbered 1.
|
|
||||||
virtual void formatPage(QPainter &painter, bool drawing, QRect &area,
|
|
||||||
int pagenr);
|
|
||||||
|
|
||||||
//! Return the number of points to add to each font when printing.
|
|
||||||
//!
|
|
||||||
//! \sa setMagnification()
|
|
||||||
int magnification() const {return mag;}
|
|
||||||
|
|
||||||
//! Sets the number of points to add to each font when printing to \a
|
|
||||||
//! magnification.
|
|
||||||
//!
|
|
||||||
//! \sa magnification()
|
|
||||||
virtual void setMagnification(int magnification);
|
|
||||||
|
|
||||||
//! Print a range of lines from the Scintilla instance \a qsb. \a from is
|
|
||||||
//! the first line to print and a negative value signifies the first line
|
|
||||||
//! of text. \a to is the last line to print and a negative value
|
|
||||||
//! signifies the last line of text. true is returned if there was no
|
|
||||||
//! error.
|
|
||||||
virtual int printRange(QsciScintillaBase *qsb, int from = -1, int to = -1);
|
|
||||||
|
|
||||||
//! Return the line wrap mode used when printing. The default is
|
|
||||||
//! QsciScintilla::WrapWord.
|
|
||||||
//!
|
|
||||||
//! \sa setWrapMode()
|
|
||||||
QsciScintilla::WrapMode wrapMode() const {return wrap;}
|
|
||||||
|
|
||||||
//! Sets the line wrap mode used when printing to \a wmode.
|
|
||||||
//!
|
|
||||||
//! \sa wrapMode()
|
|
||||||
virtual void setWrapMode(QsciScintilla::WrapMode wmode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int mag;
|
|
||||||
QsciScintilla::WrapMode wrap;
|
|
||||||
|
|
||||||
QsciPrinter(const QsciPrinter &);
|
|
||||||
QsciPrinter &operator=(const QsciPrinter &);
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
557
samples/C++/srs_app_ingest.cpp
Normal file
557
samples/C++/srs_app_ingest.cpp
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2015 SRS(ossrs)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Source - https://github.com/REN-I/srs/blob/3aae7854702a37955bce706fcd8122b02ac07f53/trunk/src/app/srs_app_ingest.cpp
|
||||||
|
|
||||||
|
#include <srs_app_ingest.hpp>
|
||||||
|
|
||||||
|
#ifdef SRS_AUTO_INGEST
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <srs_kernel_error.hpp>
|
||||||
|
#include <srs_app_config.hpp>
|
||||||
|
#include <srs_kernel_log.hpp>
|
||||||
|
#include <srs_app_ffmpeg.hpp>
|
||||||
|
#include <srs_app_pithy_print.hpp>
|
||||||
|
#include <srs_kernel_utility.hpp>
|
||||||
|
#include <srs_app_utility.hpp>
|
||||||
|
|
||||||
|
// when error, ingester sleep for a while and retry.
|
||||||
|
// ingest never sleep a long time, for we must start the stream ASAP.
|
||||||
|
#define SRS_AUTO_INGESTER_SLEEP_US (int64_t)(3*1000*1000LL)
|
||||||
|
|
||||||
|
SrsIngesterFFMPEG::SrsIngesterFFMPEG()
|
||||||
|
{
|
||||||
|
ffmpeg = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsIngesterFFMPEG::~SrsIngesterFFMPEG()
|
||||||
|
{
|
||||||
|
srs_freep(ffmpeg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngesterFFMPEG::initialize(SrsFFMPEG* ff, string v, string i)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
ffmpeg = ff;
|
||||||
|
vhost = v;
|
||||||
|
id = i;
|
||||||
|
starttime = srs_get_system_time_ms();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
string SrsIngesterFFMPEG::uri()
|
||||||
|
{
|
||||||
|
return vhost + "/" + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngesterFFMPEG::alive()
|
||||||
|
{
|
||||||
|
return (int)(srs_get_system_time_ms() - starttime);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsIngesterFFMPEG::equals(string v)
|
||||||
|
{
|
||||||
|
return vhost == v;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SrsIngesterFFMPEG::equals(string v, string i)
|
||||||
|
{
|
||||||
|
return vhost == v && id == i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngesterFFMPEG::start()
|
||||||
|
{
|
||||||
|
return ffmpeg->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsIngesterFFMPEG::stop()
|
||||||
|
{
|
||||||
|
ffmpeg->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngesterFFMPEG::cycle()
|
||||||
|
{
|
||||||
|
return ffmpeg->cycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsIngesterFFMPEG::fast_stop()
|
||||||
|
{
|
||||||
|
ffmpeg->fast_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsIngester::SrsIngester()
|
||||||
|
{
|
||||||
|
_srs_config->subscribe(this);
|
||||||
|
|
||||||
|
pthread = new SrsReusableThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US);
|
||||||
|
pprint = SrsPithyPrint::create_ingester();
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsIngester::~SrsIngester()
|
||||||
|
{
|
||||||
|
_srs_config->unsubscribe(this);
|
||||||
|
|
||||||
|
srs_freep(pthread);
|
||||||
|
clear_engines();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::start()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if ((ret = parse()) != ERROR_SUCCESS) {
|
||||||
|
clear_engines();
|
||||||
|
ret = ERROR_SUCCESS;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// even no ingesters, we must also start it,
|
||||||
|
// for the reload may add more ingesters.
|
||||||
|
|
||||||
|
// start thread to run all encoding engines.
|
||||||
|
if ((ret = pthread->start()) != ERROR_SUCCESS) {
|
||||||
|
srs_error("st_thread_create failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
srs_trace("ingest thread cid=%d, current_cid=%d", pthread->cid(), _srs_context->get_id());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::parse_ingesters(SrsConfDirective* vhost)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
std::vector<SrsConfDirective*> ingesters = _srs_config->get_ingesters(vhost->arg0());
|
||||||
|
|
||||||
|
// create engine
|
||||||
|
for (int i = 0; i < (int)ingesters.size(); i++) {
|
||||||
|
SrsConfDirective* ingest = ingesters[i];
|
||||||
|
if ((ret = parse_engines(vhost, ingest)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::parse_engines(SrsConfDirective* vhost, SrsConfDirective* ingest)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (!_srs_config->get_ingest_enabled(ingest)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ffmpeg_bin = _srs_config->get_ingest_ffmpeg(ingest);
|
||||||
|
if (ffmpeg_bin.empty()) {
|
||||||
|
ret = ERROR_ENCODER_PARSE;
|
||||||
|
srs_trace("empty ffmpeg ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all engines.
|
||||||
|
std::vector<SrsConfDirective*> engines = _srs_config->get_transcode_engines(ingest);
|
||||||
|
|
||||||
|
// create ingesters without engines.
|
||||||
|
if (engines.empty()) {
|
||||||
|
SrsFFMPEG* ffmpeg = new SrsFFMPEG(ffmpeg_bin);
|
||||||
|
if ((ret = initialize_ffmpeg(ffmpeg, vhost, ingest, NULL)) != ERROR_SUCCESS) {
|
||||||
|
srs_freep(ffmpeg);
|
||||||
|
if (ret != ERROR_ENCODER_LOOP) {
|
||||||
|
srs_error("invalid ingest engine. ret=%d", ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsIngesterFFMPEG* ingester = new SrsIngesterFFMPEG();
|
||||||
|
if ((ret = ingester->initialize(ffmpeg, vhost->arg0(), ingest->arg0())) != ERROR_SUCCESS) {
|
||||||
|
srs_freep(ingester);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ingesters.push_back(ingester);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create ingesters with engine
|
||||||
|
for (int i = 0; i < (int)engines.size(); i++) {
|
||||||
|
SrsConfDirective* engine = engines[i];
|
||||||
|
SrsFFMPEG* ffmpeg = new SrsFFMPEG(ffmpeg_bin);
|
||||||
|
if ((ret = initialize_ffmpeg(ffmpeg, vhost, ingest, engine)) != ERROR_SUCCESS) {
|
||||||
|
srs_freep(ffmpeg);
|
||||||
|
if (ret != ERROR_ENCODER_LOOP) {
|
||||||
|
srs_error("invalid ingest engine: %s %s, ret=%d",
|
||||||
|
ingest->arg0().c_str(), engine->arg0().c_str(), ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsIngesterFFMPEG* ingester = new SrsIngesterFFMPEG();
|
||||||
|
if ((ret = ingester->initialize(ffmpeg, vhost->arg0(), ingest->arg0())) != ERROR_SUCCESS) {
|
||||||
|
srs_freep(ingester);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ingesters.push_back(ingester);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsIngester::dispose()
|
||||||
|
{
|
||||||
|
// first, use fast stop to notice all FFMPEG to quit gracefully.
|
||||||
|
std::vector<SrsIngesterFFMPEG*>::iterator it;
|
||||||
|
for (it = ingesters.begin(); it != ingesters.end(); ++it) {
|
||||||
|
SrsIngesterFFMPEG* ingester = *it;
|
||||||
|
ingester->fast_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ingesters.empty()) {
|
||||||
|
srs_trace("fast stop all ingesters ok.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// then, use stop to wait FFMPEG quit one by one and send SIGKILL if needed.
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsIngester::stop()
|
||||||
|
{
|
||||||
|
pthread->stop();
|
||||||
|
clear_engines();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::cycle()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
std::vector<SrsIngesterFFMPEG*>::iterator it;
|
||||||
|
for (it = ingesters.begin(); it != ingesters.end(); ++it) {
|
||||||
|
SrsIngesterFFMPEG* ingester = *it;
|
||||||
|
|
||||||
|
// start all ffmpegs.
|
||||||
|
if ((ret = ingester->start()) != ERROR_SUCCESS) {
|
||||||
|
srs_error("ingest ffmpeg start failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check ffmpeg status.
|
||||||
|
if ((ret = ingester->cycle()) != ERROR_SUCCESS) {
|
||||||
|
srs_error("ingest ffmpeg cycle failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pithy print
|
||||||
|
show_ingest_log_message();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsIngester::on_thread_stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsIngester::clear_engines()
|
||||||
|
{
|
||||||
|
std::vector<SrsIngesterFFMPEG*>::iterator it;
|
||||||
|
|
||||||
|
for (it = ingesters.begin(); it != ingesters.end(); ++it) {
|
||||||
|
SrsIngesterFFMPEG* ingester = *it;
|
||||||
|
srs_freep(ingester);
|
||||||
|
}
|
||||||
|
|
||||||
|
ingesters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::parse()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// parse ingesters
|
||||||
|
std::vector<SrsConfDirective*> vhosts;
|
||||||
|
_srs_config->get_vhosts(vhosts);
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)vhosts.size(); i++) {
|
||||||
|
SrsConfDirective* vhost = vhosts[i];
|
||||||
|
if ((ret = parse_ingesters(vhost)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::initialize_ffmpeg(SrsFFMPEG* ffmpeg, SrsConfDirective* vhost, SrsConfDirective* ingest, SrsConfDirective* engine)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
std::string port;
|
||||||
|
if (true) {
|
||||||
|
std::vector<std::string> ip_ports = _srs_config->get_listens();
|
||||||
|
srs_assert(ip_ports.size() > 0);
|
||||||
|
|
||||||
|
std::string ep = ip_ports[0];
|
||||||
|
std::string ip;
|
||||||
|
srs_parse_endpoint(ep, ip, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string output = _srs_config->get_engine_output(engine);
|
||||||
|
// output stream, to other/self server
|
||||||
|
// ie. rtmp://localhost:1935/live/livestream_sd
|
||||||
|
output = srs_string_replace(output, "[vhost]", vhost->arg0());
|
||||||
|
output = srs_string_replace(output, "[port]", port);
|
||||||
|
if (output.empty()) {
|
||||||
|
ret = ERROR_ENCODER_NO_OUTPUT;
|
||||||
|
srs_trace("empty output url, ingest=%s. ret=%d", ingest->arg0().c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the app and stream in rtmp url
|
||||||
|
std::string url = output;
|
||||||
|
std::string app, stream;
|
||||||
|
size_t pos = std::string::npos;
|
||||||
|
if ((pos = url.rfind("/")) != std::string::npos) {
|
||||||
|
stream = url.substr(pos + 1);
|
||||||
|
url = url.substr(0, pos);
|
||||||
|
}
|
||||||
|
if ((pos = url.rfind("/")) != std::string::npos) {
|
||||||
|
app = url.substr(pos + 1);
|
||||||
|
url = url.substr(0, pos);
|
||||||
|
}
|
||||||
|
if ((pos = app.rfind("?")) != std::string::npos) {
|
||||||
|
app = app.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string log_file = SRS_CONSTS_NULL_FILE; // disabled
|
||||||
|
// write ffmpeg info to log file.
|
||||||
|
if (_srs_config->get_ffmpeg_log_enabled()) {
|
||||||
|
log_file = _srs_config->get_ffmpeg_log_dir();
|
||||||
|
log_file += "/";
|
||||||
|
log_file += "ffmpeg-ingest";
|
||||||
|
log_file += "-";
|
||||||
|
log_file += vhost->arg0();
|
||||||
|
log_file += "-";
|
||||||
|
log_file += app;
|
||||||
|
log_file += "-";
|
||||||
|
log_file += stream;
|
||||||
|
log_file += ".log";
|
||||||
|
}
|
||||||
|
|
||||||
|
// input
|
||||||
|
std::string input_type = _srs_config->get_ingest_input_type(ingest);
|
||||||
|
if (input_type.empty()) {
|
||||||
|
ret = ERROR_ENCODER_NO_INPUT;
|
||||||
|
srs_trace("empty intput type, ingest=%s. ret=%d", ingest->arg0().c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srs_config_ingest_is_file(input_type)) {
|
||||||
|
std::string input_url = _srs_config->get_ingest_input_url(ingest);
|
||||||
|
if (input_url.empty()) {
|
||||||
|
ret = ERROR_ENCODER_NO_INPUT;
|
||||||
|
srs_trace("empty intput url, ingest=%s. ret=%d", ingest->arg0().c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for file, set re.
|
||||||
|
ffmpeg->set_iparams("-re");
|
||||||
|
|
||||||
|
if ((ret = ffmpeg->initialize(input_url, output, log_file)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else if (srs_config_ingest_is_stream(input_type)) {
|
||||||
|
std::string input_url = _srs_config->get_ingest_input_url(ingest);
|
||||||
|
if (input_url.empty()) {
|
||||||
|
ret = ERROR_ENCODER_NO_INPUT;
|
||||||
|
srs_trace("empty intput url, ingest=%s. ret=%d", ingest->arg0().c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for stream, no re.
|
||||||
|
ffmpeg->set_iparams("");
|
||||||
|
|
||||||
|
if ((ret = ffmpeg->initialize(input_url, output, log_file)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = ERROR_ENCODER_INPUT_TYPE;
|
||||||
|
srs_error("invalid ingest=%s type=%s, ret=%d",
|
||||||
|
ingest->arg0().c_str(), input_type.c_str(), ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set output format to flv for RTMP
|
||||||
|
ffmpeg->set_oformat("flv");
|
||||||
|
|
||||||
|
std::string vcodec = _srs_config->get_engine_vcodec(engine);
|
||||||
|
std::string acodec = _srs_config->get_engine_acodec(engine);
|
||||||
|
// whatever the engine config, use copy as default.
|
||||||
|
bool engine_disabled = !engine || !_srs_config->get_engine_enabled(engine);
|
||||||
|
if (engine_disabled || vcodec.empty() || acodec.empty()) {
|
||||||
|
if ((ret = ffmpeg->initialize_copy()) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((ret = ffmpeg->initialize_transcode(engine)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("parse success, ingest=%s, vhost=%s",
|
||||||
|
ingest->arg0().c_str(), vhost->arg0().c_str());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsIngester::show_ingest_log_message()
|
||||||
|
{
|
||||||
|
pprint->elapse();
|
||||||
|
|
||||||
|
if ((int)ingesters.size() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// random choose one ingester to report.
|
||||||
|
int index = rand() % (int)ingesters.size();
|
||||||
|
SrsIngesterFFMPEG* ingester = ingesters.at(index);
|
||||||
|
|
||||||
|
// reportable
|
||||||
|
if (pprint->can_print()) {
|
||||||
|
srs_trace("-> "SRS_CONSTS_LOG_INGESTER" time=%"PRId64", ingesters=%d, #%d(alive=%ds, %s)",
|
||||||
|
pprint->age(), (int)ingesters.size(), index, ingester->alive() / 1000, ingester->uri().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::on_reload_vhost_added(string vhost)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
SrsConfDirective* _vhost = _srs_config->get_vhost(vhost);
|
||||||
|
if ((ret = parse_ingesters(_vhost)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("reload add vhost ingesters, vhost=%s", vhost.c_str());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::on_reload_vhost_removed(string vhost)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
std::vector<SrsIngesterFFMPEG*>::iterator it;
|
||||||
|
|
||||||
|
for (it = ingesters.begin(); it != ingesters.end();) {
|
||||||
|
SrsIngesterFFMPEG* ingester = *it;
|
||||||
|
|
||||||
|
if (!ingester->equals(vhost)) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop the ffmpeg and free it.
|
||||||
|
ingester->stop();
|
||||||
|
|
||||||
|
srs_trace("reload stop ingester, vhost=%s, id=%s", vhost.c_str(), ingester->uri().c_str());
|
||||||
|
|
||||||
|
srs_freep(ingester);
|
||||||
|
|
||||||
|
// remove the item from ingesters.
|
||||||
|
it = ingesters.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::on_reload_ingest_removed(string vhost, string ingest_id)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
std::vector<SrsIngesterFFMPEG*>::iterator it;
|
||||||
|
|
||||||
|
for (it = ingesters.begin(); it != ingesters.end();) {
|
||||||
|
SrsIngesterFFMPEG* ingester = *it;
|
||||||
|
|
||||||
|
if (!ingester->equals(vhost, ingest_id)) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop the ffmpeg and free it.
|
||||||
|
ingester->stop();
|
||||||
|
|
||||||
|
srs_trace("reload stop ingester, vhost=%s, id=%s", vhost.c_str(), ingester->uri().c_str());
|
||||||
|
|
||||||
|
srs_freep(ingester);
|
||||||
|
|
||||||
|
// remove the item from ingesters.
|
||||||
|
it = ingesters.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::on_reload_ingest_added(string vhost, string ingest_id)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
SrsConfDirective* _vhost = _srs_config->get_vhost(vhost);
|
||||||
|
SrsConfDirective* _ingester = _srs_config->get_ingest_by_id(vhost, ingest_id);
|
||||||
|
|
||||||
|
if ((ret = parse_engines(_vhost, _ingester)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("reload add ingester, "
|
||||||
|
"vhost=%s, id=%s", vhost.c_str(), ingest_id.c_str());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsIngester::on_reload_ingest_updated(string vhost, string ingest_id)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if ((ret = on_reload_ingest_removed(vhost, ingest_id)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = on_reload_ingest_added(vhost, ingest_id)) != ERROR_SUCCESS) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("reload updated ingester, "
|
||||||
|
"vhost=%s, id=%s", vhost.c_str(), ingest_id.c_str());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Reference in New Issue
Block a user