diff --git a/uppsrc/CodeEditor/Highlight.cpp b/uppsrc/CodeEditor/Highlight.cpp index 3bb878a86..4e7d8d3ef 100644 --- a/uppsrc/CodeEditor/Highlight.cpp +++ b/uppsrc/CodeEditor/Highlight.cpp @@ -401,7 +401,7 @@ void CodeEditor::InitKeywords() "CLASSNAME", "THISBACK", "THISBACK1", "THISBACK2", "THISBACK3", "THISBACK4", "PTEBACK", "PTEBACK1", "PTEBACK2", "PTEBACK3", "PTEBACK4", "QUOTE", "XASSERT", "NEVER", "XNEVER", "CHECK", "XCHECK", "ASSERT", - "NAMESPACE_UPP", "END_UPP_NAMESPACE", "NEVER_", + "NAMESPACE_UPP", "END_UPP_NAMESPACE", "NEVER_", "SKYLARK", NULL }; static const char *upp_logs[] = { diff --git a/uppsrc/Core/Ini.cpp b/uppsrc/Core/Ini.cpp index 5047c1ecd..61efda82e 100644 --- a/uppsrc/Core/Ini.cpp +++ b/uppsrc/Core/Ini.cpp @@ -64,7 +64,14 @@ void SetIniFile(const char *name) { ReloadIniFile(); } +static +void sIniSet(int64& version) +{ + version = s_ini_version; +} + +static bool sIniChanged(int64& version) { if(version != s_ini_version) { @@ -119,6 +126,7 @@ String IniString::operator=(const String& s) { Mutex::Lock __(sMtx); (*ref_fn)() = s; + sIniSet(version); return s; } @@ -171,6 +179,7 @@ IniInt::operator int() { int IniInt::operator=(int b) { Mutex::Lock __(sMtx); + sIniSet(version); return value = b; } @@ -193,6 +202,7 @@ IniInt64::operator int64() int64 IniInt64::operator=(int64 b) { Mutex::Lock __(sMtx); + sIniSet(version); return value = b; } @@ -215,6 +225,7 @@ IniDouble::operator double() double IniDouble::operator=(double b) { Mutex::Lock __(sMtx); + sIniSet(version); return value = b; } @@ -237,6 +248,7 @@ IniBool::operator bool() { bool IniBool::operator=(bool b) { Mutex::Lock __(sMtx); + sIniSet(version); return value = b; } diff --git a/uppsrc/Core/JSON.cpp b/uppsrc/Core/JSON.cpp index b87eae11e..2d12f2758 100644 --- a/uppsrc/Core/JSON.cpp +++ b/uppsrc/Core/JSON.cpp @@ -111,7 +111,7 @@ String AsJSON(const Value& v, const String& sep, bool pretty) if(IsNumber(v)) return Format("%.16g", (double)v); if(IsString(v)) - return AsCString((String)v, INT_MAX, NULL, ASCSTRING_OCTALHI|ASCSTRING_JSON); + return AsCString((String)v, INT_MAX, NULL, ASCSTRING_JSON); if(IsNull(v)) return "null"; #ifdef SVO_VALUE diff --git a/uppsrc/Core/JSON.h b/uppsrc/Core/JSON.h index d972b3f84..0253367f2 100644 --- a/uppsrc/Core/JSON.h +++ b/uppsrc/Core/JSON.h @@ -4,9 +4,9 @@ Value ParseJSON(const char *s); inline String AsJSON(int i) { return IsNull(i) ? "null" : AsString(i); } inline String AsJSON(double n) { return IsNull(n) ? "null" : AsString(n); } inline String AsJSON(bool b) { return b ? "true" : "false"; } -inline String AsJSON(const String& s) { return AsCString(s, INT_MAX, NULL, ASCSTRING_OCTALHI|ASCSTRING_JSON); } -inline String AsJSON(const WString& s) { return AsCString(s.ToString(), INT_MAX, NULL, ASCSTRING_OCTALHI|ASCSTRING_JSON); } -inline String AsJSON(const char *s) { return AsCString(s, INT_MAX, NULL, ASCSTRING_OCTALHI|ASCSTRING_JSON); } +inline String AsJSON(const String& s) { return AsCString(s, INT_MAX, NULL, ASCSTRING_JSON); } +inline String AsJSON(const WString& s) { return AsCString(s.ToString(), INT_MAX, NULL, ASCSTRING_JSON); } +inline String AsJSON(const char *s) { return AsCString(s, INT_MAX, NULL, ASCSTRING_JSON); } String AsJSON(const Value& v, const String& indent, bool pretty); String AsJSON(const Value& v, bool pretty = false); diff --git a/uppsrc/CppBase/Parser.cpp b/uppsrc/CppBase/Parser.cpp index 56ce4c514..52018e145 100644 --- a/uppsrc/CppBase/Parser.cpp +++ b/uppsrc/CppBase/Parser.cpp @@ -668,6 +668,21 @@ Array Parser::Declaration0(bool l0, bool more) break; } Qualifier(); + if(l0 && lex == tk_SKYLARK && lex[1] == '(') { + ++lex; + ++lex; + Decl& a = r.Add(); + a.name = lex.GetId(); + a.function = true; + Decl& p = a.param.Add(); + p.name = "http"; + p.type = "Http"; + p.natural = "Http& http"; + Key(','); + lex.GetText(); + Key(')'); + return r; + } bool isdestructor = Key('~'); if(l0 && context.typenames.Find(lex) >= 0 && lex[1] == '(' && lex.IsId()) { Decl& a = r.Add(); diff --git a/uppsrc/CppBase/keyword.i b/uppsrc/CppBase/keyword.i index d90fb1a6f..fdb48aa40 100644 --- a/uppsrc/CppBase/keyword.i +++ b/uppsrc/CppBase/keyword.i @@ -79,3 +79,5 @@ CPPID(for) CPPID(try) CPPID(catch) CPPID(do) + +CPPID(SKYLARK) diff --git a/uppsrc/Skylark/App.cpp b/uppsrc/Skylark/App.cpp index ca87e77fd..821dda85a 100644 --- a/uppsrc/Skylark/App.cpp +++ b/uppsrc/Skylark/App.cpp @@ -1,296 +1,300 @@ -#include "Skylark.h" - -#ifdef PLATFORM_POSIX -#include -#endif - -#ifdef PLATFORM_WIN32 -#include -#endif - -namespace Upp { - -String GetThreadName() -{ - return FormatIntBase(Thread::GetCurrentId() % 33, 35); -} - -namespace Ini { - INI_BOOL(skylark_log, false, "Trace of Skylark"); -}; - -#ifdef PLATFORM_WIN32 -BOOL WINAPI SkylarkApp::CtrlCHandlerRoutine(__in DWORD dwCtrlType) -{ - LOG("Ctrl+C handler"); - TheApp().quit = true; - Cout() << "Ctrl + C\n"; - TcpSocket h; - h.Connect("127.0.0.1", TheApp().port); - h.Put("quit"); - return TRUE; -} -#endif - -void SkylarkApp::WorkThread() -{ - RunThread(); -} - -void SkylarkApp::ThreadRun() -{ - WorkThread(); -} - -void SkylarkApp::RunThread() -{ - if(SQL.IsOpen()) { - SQL.ClearError(); - SQLR.ClearError(); - SQL.GetSession().ThrowOnError(); - SQLR.GetSession().ThrowOnError(); - } - - for(;;) { - TcpSocket request; - accept_mutex.Enter(); - if(quit) { - accept_mutex.Leave(); - break; - } - SKYLARKLOG("Waiting for accept "); - bool b = request.Accept(server); - accept_mutex.Leave(); - if(quit) - break; - if(b) { - SKYLARKLOG("Accepted "); - #ifdef PLATFORM_POSIX - if(prefork) - alarm(timeout); - #endif - Http http(*this); - http.Dispatch(request); - #ifdef PLATFORM_POSIX - if(prefork) - alarm(0); - #endif - SKYLARKLOG("Finished "); - } - else { - SKYLARKLOG("Accept failed: " << request.GetErrorDesc()); - #ifdef _DEBUG - break; - #endif - } - } -} - -void SkylarkApp::Main() -{ - Buffer uwt(threads); - for(int i = 0; i < threads; i++) - Thread::Start(THISBACK(ThreadRun)); - - while(Thread::GetCount()) - Sleep(100); -} - -void SkylarkApp::Broadcast(int signal) -{ -#ifdef PLATFORM_POSIX - if(getpid() == main_pid) - for(int i = 0; i < child_pid.GetCount(); i++) - kill(child_pid[i], signal); -#endif -} - -void SkylarkApp::Signal(int signal) -{ -#ifdef PLATFORM_POSIX - switch(signal) { - case SIGTERM: - case SIGHUP: - quit = true; - Broadcast(signal); - break; - case SIGINT: - Broadcast(signal); - exit(0); - break; - case SIGALRM: - if(getpid() != TheApp().main_pid) { - // "Timeout - session stoped" - exit(0); - } - break; - } -#endif -} - -void SkylarkApp::SignalHandler(int signal) -{ - TheApp().Signal(signal); -} - -void DisableHUP() -{ -#ifdef PLATFORM_POSIX - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGHUP); - sigprocmask(SIG_BLOCK, &mask, NULL); -#endif -} - -void EnableHUP() -{ -#ifdef PLATFORM_POSIX - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGHUP); - sigprocmask(SIG_UNBLOCK, &mask, NULL); -#endif -} - -void SkylarkApp::Run() -{ -// DisableHUP(); - if(static_dir.GetCount() == 0) - static_dir = root + "/static"; - - SqlSession::PerThread(); - SqlId::UseQuotes(); - FinalizeViews(); - -#ifdef PLATFORM_WIN32 - SetConsoleCtrlHandler(CtrlCHandlerRoutine, true); -#endif - - main_pid = getpid(); - quit = false; - -#if defined(PLATFORM_POSIX) && defined(_DEBUG) - // Avoid the need to close running server in debug mode... - int qq = 0; - for(;;) { - if(server.Listen(port, 5)) - break; - Cout() << "Trying to start listening (other process using the same port?) " << ++qq << "\n"; - Sleep(1000); - } -#else - if(!server.Listen(port, 5)) { - SKYLARKLOG("Cannot open server socket!"); - Exit(1); - } -#endif - - SKYLARKLOG("Starting Skylark, current static path: " << path); - -#ifdef PLATFORM_POSIX - if(prefork) { - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SignalHandler; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGALRM, &sa, NULL); -// EnableHUP(); - while(!quit) { - while(child_pid.GetCount() < prefork && !quit) { - pid_t p = fork(); - if(p == 0) { - Main(); - return; - } - else - if(p > 0) - child_pid.Add(p); - else { - // "cant create new process" - Broadcast(SIGINT); - abort(); - } - } - int status = 0; - pid_t p = wait(&status); - if(p > 0) { - int q = FindIndex(child_pid, p); - if(q >= 0) - child_pid.Remove(q); - } - } - - Broadcast(SIGTERM); - int status = 0; - for(int i = 0; i < child_pid.GetCount(); i++) - waitpid(child_pid[i], &status, 0); - // "server stopped"; - } - else -#endif - Main(); - -#if defined(_DEBUG) && defined(POSIX) - FileDelete(pidf); -#endif - - SKYLARKLOG("ExitSkylark"); -} - - -void SkylarkApp::SqlError(Http& http) -{ -} - -void SkylarkApp::InternalError(Http& http) -{ -} - -void SkylarkApp::NotFound(Http& http) -{ -} - -void SkylarkApp::Unauthorized(Http& http) -{ -} - -SkylarkApp *SkylarkApp::app; - -SkylarkApp& SkylarkApp::TheApp() -{ - ASSERT(app); - return *app; -} - -const SkylarkConfig& SkylarkApp::Config() -{ - ASSERT(app); - return *app; -} - -SkylarkApp::SkylarkApp() -{ - ASSERT(!app); - path = GetEnv("UPP_ASSEMBLY__"); - app = this; - threads = 3 * CPU_Cores() + 1; - port = 8001; - use_caching = true; -#ifdef _DEBUG - prefork = 0; - timeout = 0; -#else - prefork = 1; - timeout = 300; -#endif -} - -SkylarkApp::~SkylarkApp() -{ - app = NULL; -} - +#include "Skylark.h" + +#ifdef PLATFORM_POSIX +#include +#endif + +#ifdef PLATFORM_WIN32 +#include +#endif + +namespace Upp { + +String GetThreadName() +{ + return FormatIntBase(Thread::GetCurrentId() % 33, 35); +} + +namespace Ini { + INI_BOOL(skylark_log, false, "Trace of Skylark"); +}; + +#ifdef PLATFORM_WIN32 +BOOL WINAPI SkylarkApp::CtrlCHandlerRoutine(__in DWORD dwCtrlType) +{ + LOG("Ctrl+C handler"); + TheApp().quit = true; + Cout() << "Ctrl + C\n"; + TcpSocket h; + h.Connect("127.0.0.1", TheApp().port); + h.Put("quit"); + return TRUE; +} +#endif + +void SkylarkApp::WorkThread() +{ + RunThread(); +} + +void SkylarkApp::ThreadRun() +{ + WorkThread(); +} + +void SkylarkApp::RunThread() +{ + if(SQL.IsOpen()) { + SQL.ClearError(); + SQLR.ClearError(); + SQL.GetSession().ThrowOnError(); + SQLR.GetSession().ThrowOnError(); + } + + for(;;) { + TcpSocket request; + accept_mutex.Enter(); + if(quit) { + accept_mutex.Leave(); + break; + } + SKYLARKLOG("Waiting for accept "); + bool b = request.Accept(server); + accept_mutex.Leave(); + if(quit) + break; + if(b) { + SKYLARKLOG("Accepted "); + #ifdef PLATFORM_POSIX + if(prefork) + alarm(timeout); + #endif + Http http(*this); + http.Dispatch(request); + #ifdef PLATFORM_POSIX + if(prefork) + alarm(0); + #endif + SKYLARKLOG("Finished "); + } + else { + SKYLARKLOG("Accept failed: " << request.GetErrorDesc()); + #ifdef _DEBUG + break; + #endif + } + } +} + +void SkylarkApp::Main() +{ + Buffer uwt(threads); + for(int i = 0; i < threads; i++) + Thread::Start(THISBACK(ThreadRun)); + + while(Thread::GetCount()) + Sleep(100); +} + +void SkylarkApp::Broadcast(int signal) +{ +#ifdef PLATFORM_POSIX + if(getpid() == main_pid) + for(int i = 0; i < child_pid.GetCount(); i++) + kill(child_pid[i], signal); +#endif +} + +void SkylarkApp::Signal(int signal) +{ +#ifdef PLATFORM_POSIX + switch(signal) { + case SIGTERM: + case SIGHUP: + quit = true; + Broadcast(signal); + break; + case SIGINT: + Broadcast(signal); + exit(0); + break; + case SIGALRM: + if(getpid() != TheApp().main_pid) { + // "Timeout - session stoped" + exit(0); + } + break; + } +#endif +} + +void SkylarkApp::SignalHandler(int signal) +{ + TheApp().Signal(signal); +} + +void DisableHUP() +{ +#ifdef PLATFORM_POSIX + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGHUP); + sigprocmask(SIG_BLOCK, &mask, NULL); +#endif +} + +void EnableHUP() +{ +#ifdef PLATFORM_POSIX + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGHUP); + sigprocmask(SIG_UNBLOCK, &mask, NULL); +#endif +} + +void SkylarkApp::Run() +{ +// DisableHUP(); + if(static_dir.GetCount() == 0) + static_dir = root + "/static"; + + SqlSession::PerThread(); + SqlId::UseQuotes(); + FinalizeViews(); + +#ifdef PLATFORM_WIN32 + SetConsoleCtrlHandler(CtrlCHandlerRoutine, true); +#endif + + main_pid = getpid(); + quit = false; + +#ifdef _DEBUG + // Avoid the need to close running server in debug mode... + int qq = 0; + for(;;) { + if(server.Listen(port, 5)) + break; + Cout() << "Trying to start listening (other process using the same port?) " << ++qq << "\n"; + Sleep(1000); + } +#else + if(!server.Listen(port, 5)) { + SKYLARKLOG("Cannot open server socket!"); + Exit(1); + } +#endif + + SKYLARKLOG("Starting Skylark, current static path: " << path); + +#ifdef PLATFORM_POSIX + if(prefork) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SignalHandler; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGALRM, &sa, NULL); +// EnableHUP(); + while(!quit) { + while(child_pid.GetCount() < prefork && !quit) { + pid_t p = fork(); + if(p == 0) { + Main(); + return; + } + else + if(p > 0) + child_pid.Add(p); + else { + // "cant create new process" + Broadcast(SIGINT); + abort(); + } + } + int status = 0; + pid_t p = wait(&status); + if(p > 0) { + int q = FindIndex(child_pid, p); + if(q >= 0) + child_pid.Remove(q); + } + } + + Broadcast(SIGTERM); + int status = 0; + for(int i = 0; i < child_pid.GetCount(); i++) + waitpid(child_pid[i], &status, 0); + // "server stopped"; + } + else +#endif + Main(); + +#if defined(_DEBUG) && defined(POSIX) + FileDelete(pidf); +#endif + + SKYLARKLOG("ExitSkylark"); +} + + +void SkylarkApp::SqlError(Http& http) +{ +} + +void SkylarkApp::InternalError(Http& http) +{ +} + +void SkylarkApp::NotFound(Http& http) +{ +} + +void SkylarkApp::Unauthorized(Http& http) +{ +} + +void SkylarkApp::BadRequest(Http& http) +{ +} + +SkylarkApp *SkylarkApp::app; + +SkylarkApp& SkylarkApp::TheApp() +{ + ASSERT(app); + return *app; +} + +const SkylarkConfig& SkylarkApp::Config() +{ + ASSERT(app); + return *app; +} + +SkylarkApp::SkylarkApp() +{ + ASSERT(!app); + path = GetEnv("UPP_ASSEMBLY__"); + app = this; + threads = 3 * CPU_Cores() + 1; + port = 8001; + use_caching = true; +#ifdef _DEBUG + prefork = 0; + timeout = 0; +#else + prefork = 1; + timeout = 300; +#endif +} + +SkylarkApp::~SkylarkApp() +{ + app = NULL; +} + }; \ No newline at end of file diff --git a/uppsrc/Skylark/Dispatch.cpp b/uppsrc/Skylark/Dispatch.cpp index 7f3303ec4..095ab0816 100644 --- a/uppsrc/Skylark/Dispatch.cpp +++ b/uppsrc/Skylark/Dispatch.cpp @@ -1,394 +1,402 @@ -#include "Skylark.h" - -#define LLOG(x) //DLOG(x) -#define LDUMP(x) //DDUMP(x) -#define LDUMPC(x) //DDUMPC(x) -#define LDUMPM(x) //DDUMPM(x) -#define LTIMING(x) //RTIMING(x) - -namespace Upp { - -enum { DISPATCH_VARARGS = -1 }; - -struct DispatchNode : Moveable { - VectorMap subnode; - void (*view)(Http&); - int argpos; - int method; - bool post_raw; - String id; - - enum { GET, POST }; - - DispatchNode() { view = NULL; argpos = Null; method = GET; post_raw = false; } -}; - -static Vector& sDispatchMap() -{ - static Vector x; - return x; -} - -static VectorMap >& sLinkMap() -{ - static VectorMap > x; - return x; -} - -static Index& sViewIndex() -{ - static Index x; - return x; -} - -void DumpDispatchMap() -{ - Vector& DispatchMap = sDispatchMap(); - for(int i = 0; i < DispatchMap.GetCount(); i++) { - LLOG("-------------"); - String sub; - for(int j = 0; j < DispatchMap[i].subnode.GetCount(); j++) - sub << DispatchMap[i].subnode.GetKey(j) << "->" << DispatchMap[i].subnode[j] << ", "; - LLOG(i << " " << (bool)DispatchMap[i].view << ": " << sub); - } -} - -Vector *GetUrlViewLinkParts(const String& id) -{ - int q = sLinkMap().Find(id); - if(q < 0) - return NULL; - return &sLinkMap()[q]; -} - -String MakeLink(void (*view)(Http&), const Vector& arg) -{ - int q = sViewIndex().Find((uintptr_t)view); - if(q < 0) - throw Exc("Invalid view"); - if(q < 0) - return String(); - StringBuffer out; - MakeLink(out, sLinkMap()[q], arg); - return out; -} - -void RegisterView0(void (*view)(Http&), const char *id, String path, bool primary) -{ - LLOG("RegisterView " << path); - Vector& linkpart = sLinkMap().GetAdd(id); - sViewIndex().FindAdd((uintptr_t)view); - Vector& DispatchMap = sDispatchMap(); - int method = DispatchNode::GET; - bool post_raw = false; - int q = path.Find(':'); - if(q >= 0) { - if(path.Mid(q + 1) == "POST") - method = DispatchNode::POST; - if(path.Mid(q + 1) == "POST_RAW") { - method = DispatchNode::POST; - post_raw = true; - } - path = path.Mid(0, q); - } - Vector h = Split(path, '/'); - if(DispatchMap.GetCount() == 0) - DispatchMap.Add(); - q = 0; - int linkargpos = 0; - for(int i = 0; i < h.GetCount(); i++) { - String s = h[i]; - LLOG(" Node " << h[i]); - DispatchNode& n = DispatchMap[q]; - if(*s == '*') { - int argpos = Null; - if(IsDigit(s[1])) - linkargpos = argpos = minmax(atoi(~s + 1), 0, 30); - else - if(s[1] == '*') - argpos = DISPATCH_VARARGS; - q = DispatchMap.GetCount(); - LLOG(" Adding arg " << argpos << ": " << q); - n.subnode.Add(Null, q); - DispatchMap.Add(); - DispatchMap[q].argpos = argpos; - if(primary) - linkpart.Add(String(linkargpos++, 1)); - } - else { - if(primary) - linkpart.Add(s); - q = n.subnode.Get(s, -1); - if(q < 0) { - q = DispatchMap.GetCount(); - LLOG(" Adding " << s << ": " << q); - n.subnode.Add(s, q); - DispatchMap.Add(); - } - } - } - ASSERT_(!DispatchMap[q].view, "duplicate view " + String(path)); - DispatchMap[q].view = view; - DispatchMap[q].method = method; - DispatchMap[q].id = id; - DispatchMap[q].post_raw = post_raw; -// DumpDispatchMap(); -} - -struct ViewData { - void (*view)(Http&); - String id; - String path; -}; - -static Array& sViewData() -{ - static Array x; - return x; -} - -void RegisterView(void (*view)(Http&), const char *id, const char *path) -{ - Array& v = sViewData(); - ViewData& w = v.Add(); - w.view = view; - w.id = id; - w.path = path; -} - -void SkylarkApp::FinalizeViews() -{ - Array& w = sViewData(); - for(int i = 0; i < w.GetCount(); i++) { - const ViewData& v = w[i]; - ASSERT_(sViewIndex().Find((uintptr_t)v.view) < 0, "duplicate view function registration " + String(v.id)); - Vector h = Split(ReplaceVars(root + '/' + v.path, view_var, '$'), ';'); - for(int i = 0; i < h.GetCount(); i++) - RegisterView0(v.view, v.id, h[i], i == 0); - } - w.Clear(); -} - -struct BestDispatch { - void (*view)(Http&); - int matched_parts; - int matched_params; - Vector& arg; - String id; - bool post_raw; - - BestDispatch(Vector& arg) : arg(arg) { matched_parts = -1; matched_params = 0; view = NULL; post_raw = false; } -}; - -void GetBestDispatch(int method, - const Vector& part, int ii, const DispatchNode& n, Vector& arg, - BestDispatch& bd, int matched_parts, int matched_params) -{ - Vector& DispatchMap = sDispatchMap(); - if(ii >= part.GetCount()) { - if(n.view && n.method == method && - (matched_parts > bd.matched_parts || - matched_parts == bd.matched_parts && matched_params > bd.matched_params)) { - LLOG("Matched " << n.id << " parts " << matched_parts << " params " << matched_params); - bd.arg <<= arg; - bd.view = n.view; - bd.matched_parts = matched_parts; - bd.id = n.id; - bd.post_raw = n.post_raw; - } - if(!bd.view) { - int q = n.subnode.Find(String()); - while(q >= 0) { - const DispatchNode& an = DispatchMap[n.subnode[q]]; - if(an.argpos == DISPATCH_VARARGS && an.view && an.method == method) { - bd.view = an.view; - bd.id = n.id; - bd.arg.Clear(); - break; - } - q = n.subnode.FindNext(q); - } - } - return; - } - int qq = n.subnode.Get(part[ii], -1); - if(qq >= 0) - GetBestDispatch(method, part, ii + 1, DispatchMap[qq], arg, bd, matched_parts + 1, matched_params); - int q = n.subnode.Find(String()); - while(q >= 0) { - int qq = n.subnode[q]; - int ac = arg.GetCount(); - const DispatchNode& an = DispatchMap[qq]; - int apos = an.argpos; - LLOG(" *" << qq << " apos: " << apos); - if(apos == DISPATCH_VARARGS) { - if(an.view && an.method == method && - (matched_parts > bd.matched_parts || matched_parts == bd.matched_parts && matched_params > bd.matched_params)) { - LLOG("Matched VARARGS " << an.id << " parts " << matched_parts << " params " << matched_params); - bd.arg <<= arg; - bd.arg.Append(part, ii, part.GetCount() - ii); - bd.view = an.view; - bd.matched_parts = matched_parts; - bd.id = an.id; - } - } - else { - String pv; - if(IsNull(apos)) - arg.Add(part[ii]); - else { - String& at = arg.At(apos); - pv = at; - at = part[ii]; - } - GetBestDispatch(method, part, ii + 1, an, arg, bd, matched_parts, matched_params + 1); - if(!IsNull(apos)) - arg[apos] = pv; - } - arg.SetCount(ac); - q = n.subnode.FindNext(q); - } -} - -void Http::Dispatch(TcpSocket& socket) -{ - const Vector& DispatchMap = sDispatchMap(); - if(hdr.Read(socket)) { - int len = GetLength(); - content = socket.GetAll(len); - LLOG("--------------------------------------------"); - SKYLARKLOG(hdr.GetMethod() << " " << hdr.GetURI()); - LDUMP(content); - String r; - var.Clear(); - arg.Clear(); - LTIMING("Request processing"); - request_content_type = GetHeader("content-type"); - String rc = ToLower(request_content_type); - bool post = hdr.GetMethod() == "POST"; - if(post) - if(rc.StartsWith("application/x-www-form-urlencoded")) - ParseRequest(content); - else - if(rc.StartsWith("multipart/")) - ReadMultiPart(content); - String uri = hdr.GetURI(); - int q = uri.Find('?'); - if(q >= 0) { - if(!post) - ParseRequest(~uri + q + 1); - uri.Trim(q); - } - for(int i = hdr.fields.Find("cookie"); i >= 0; i = hdr.fields.FindNext(i)) { - const String& h = hdr.fields[i]; - int q = 0; - for(;;) { - int qq = h.Find('=', q); - if(qq < 0) - break; - String id = ToLower(TrimBoth(h.Mid(q, qq - q))); - qq++; - q = h.Find(';', qq); - if(q < 0) { - var.Add('@' + id, UrlDecode(h.Mid(qq))); - break; - } - var.Add('@' + id, UrlDecode(h.Mid(qq, q - qq))); - q++; - } - } - var.GetAdd("static") = app.static_dir; - var.GetAdd(".__identity__"); // To make StdLib.icpp GetIndentity work without changing preset stack positions - LDUMPM(var); - Vector part = Split(uri, '/'); - for(int i = 0; i < part.GetCount(); i++) - part[i] = UrlDecode(part[i]); - LDUMPC(part); - Vector a; - BestDispatch bd(arg); - if(DispatchMap.GetCount()) - GetBestDispatch(post ? DispatchNode::POST : DispatchNode::GET, part, 0, DispatchMap[0], a, bd, 0, 0); - LDUMPC(arg); - response.Clear(); - if(bd.view) { - try { - if(SQL.IsOpen()) - SQL.Begin(); - LoadSession(); - session_dirty = false; - if(post && !bd.post_raw) { - String id = Nvl((*this)["__post_identity__"], (*this)["__js_identity__"]); - if(id != (*this)[".__identity__"]) - throw AuthExc("identity error"); - } - lang = Nvl(Int(".__lang__"), LNG_ENGLISH); - Upp::SetLanguage(lang); - var.GetAdd(".__lang__") = lang; - var.GetAdd(".language") = ToLower(LNGAsText(lang)); - viewid = bd.id; - LDUMP(viewid); - (*bd.view)(*this); - if(session_dirty) - SaveSession(); - if(SQL.IsOpen()) - SQL.Commit(); - } - catch(SqlExc e) { - if(SQL.IsOpen()) - SQL.Rollback(); - response << "Internal server error
" - << "SQL ERROR: " << e; - code = 500; - code_text = "Internal server error"; - app.SqlError(*this); - } - catch(AuthExc e) { - if(SQL.IsOpen()) - SQL.Rollback(); - response << e; - code = 403; - code_text = "Unauthorized"; - app.Unauthorized(*this); - } - catch(Exc e) { - if(SQL.IsOpen()) - SQL.Rollback(); - response << "Internal server error
" - << e; - code = 500; - code_text = "Internal server error"; - app.InternalError(*this); - } - } - else { - response << "Page not found"; - code = 404; - code_text = "Not found"; - app.NotFound(*this); - } - r.Clear(); - SKYLARKLOG("=== Response: " << code << ' ' << code_text); - if(redirect.GetCount()) { - SKYLARKLOG("Redirect to: " << redirect); - r << "HTTP/1.1 " << code << " Found\r\n"; - r << "Location: " << redirect << "\r\n"; - } - else { - r << - "HTTP/1.0 " << code << ' ' << code_text << "\r\n" - "Date: " << WwwFormat(GetUtcTime()) << "\r\n" - "Server: U++\r\n" - "Content-Length: " << response.GetCount() << "\r\n" - "Connection: close\r\n" - "Cache-Control: no-cache\r\n" - "Content-Type: " << content_type << "\r\n"; - for(int i = 0; i < cookies.GetCount(); i++) - r << cookies[i]; - r << "\r\n"; - } - socket.PutAll(r); - socket.PutAll(response); - } -} - +#include "Skylark.h" + +#define LLOG(x) //DLOG(x) +#define LDUMP(x) //DDUMP(x) +#define LDUMPC(x) //DDUMPC(x) +#define LDUMPM(x) //DDUMPM(x) +#define LTIMING(x) //RTIMING(x) + +namespace Upp { + +enum { DISPATCH_VARARGS = -1 }; + +struct DispatchNode : Moveable { + VectorMap subnode; + void (*view)(Http&); + int argpos; + int method; + bool post_raw; + String id; + + enum { GET, POST }; + + DispatchNode() { view = NULL; argpos = Null; method = GET; post_raw = false; } +}; + +static Vector& sDispatchMap() +{ + static Vector x; + return x; +} + +static VectorMap >& sLinkMap() +{ + static VectorMap > x; + return x; +} + +static Index& sViewIndex() +{ + static Index x; + return x; +} + +void DumpDispatchMap() +{ + Vector& DispatchMap = sDispatchMap(); + for(int i = 0; i < DispatchMap.GetCount(); i++) { + LLOG("-------------"); + String sub; + for(int j = 0; j < DispatchMap[i].subnode.GetCount(); j++) + sub << DispatchMap[i].subnode.GetKey(j) << "->" << DispatchMap[i].subnode[j] << ", "; + LLOG(i << " " << (bool)DispatchMap[i].view << ": " << sub); + } +} + +Vector *GetUrlViewLinkParts(const String& id) +{ + int q = sLinkMap().Find(id); + if(q < 0) + return NULL; + return &sLinkMap()[q]; +} + +String MakeLink(void (*view)(Http&), const Vector& arg) +{ + int q = sViewIndex().Find((uintptr_t)view); + if(q < 0) + throw Exc("Invalid view"); + if(q < 0) + return String(); + StringBuffer out; + MakeLink(out, sLinkMap()[q], arg); + return out; +} + +void RegisterView0(void (*view)(Http&), const char *id, String path, bool primary) +{ + LLOG("RegisterView " << path); + Vector& linkpart = sLinkMap().GetAdd(id); + sViewIndex().FindAdd((uintptr_t)view); + Vector& DispatchMap = sDispatchMap(); + int method = DispatchNode::GET; + bool post_raw = false; + int q = path.Find(':'); + if(q >= 0) { + if(path.Mid(q + 1) == "POST") + method = DispatchNode::POST; + if(path.Mid(q + 1) == "POST_RAW") { + method = DispatchNode::POST; + post_raw = true; + } + path = path.Mid(0, q); + } + Vector h = Split(path, '/'); + if(DispatchMap.GetCount() == 0) + DispatchMap.Add(); + q = 0; + int linkargpos = 0; + for(int i = 0; i < h.GetCount(); i++) { + String s = h[i]; + LLOG(" Node " << h[i]); + DispatchNode& n = DispatchMap[q]; + if(*s == '*') { + int argpos = Null; + if(IsDigit(s[1])) + linkargpos = argpos = minmax(atoi(~s + 1), 0, 30); + else + if(s[1] == '*') + argpos = DISPATCH_VARARGS; + q = DispatchMap.GetCount(); + LLOG(" Adding arg " << argpos << ": " << q); + n.subnode.Add(Null, q); + DispatchMap.Add(); + DispatchMap[q].argpos = argpos; + if(primary) + linkpart.Add(String(linkargpos++, 1)); + } + else { + if(primary) + linkpart.Add(s); + q = n.subnode.Get(s, -1); + if(q < 0) { + q = DispatchMap.GetCount(); + LLOG(" Adding " << s << ": " << q); + n.subnode.Add(s, q); + DispatchMap.Add(); + } + } + } + ASSERT_(!DispatchMap[q].view, "duplicate view " + String(path)); + DispatchMap[q].view = view; + DispatchMap[q].method = method; + DispatchMap[q].id = id; + DispatchMap[q].post_raw = post_raw; +// DumpDispatchMap(); +} + +struct ViewData { + void (*view)(Http&); + String id; + String path; +}; + +static Array& sViewData() +{ + static Array x; + return x; +} + +void RegisterHandler(void (*view)(Http&), const char *id, const char *path) +{ + Array& v = sViewData(); + ViewData& w = v.Add(); + w.view = view; + w.id = id; + w.path = path; +} + +void SkylarkApp::FinalizeViews() +{ + Array& w = sViewData(); + for(int i = 0; i < w.GetCount(); i++) { + const ViewData& v = w[i]; + ASSERT_(sViewIndex().Find((uintptr_t)v.view) < 0, "duplicate view function registration " + String(v.id)); + Vector h = Split(ReplaceVars(root + '/' + v.path, view_var, '$'), ';'); + for(int i = 0; i < h.GetCount(); i++) + RegisterView0(v.view, v.id, h[i], i == 0); + } + w.Clear(); +} + +struct BestDispatch { + void (*view)(Http&); + int matched_parts; + int matched_params; + Vector& arg; + String id; + bool post_raw; + + BestDispatch(Vector& arg) : arg(arg) { matched_parts = -1; matched_params = 0; view = NULL; post_raw = false; } +}; + +void GetBestDispatch(int method, + const Vector& part, int ii, const DispatchNode& n, Vector& arg, + BestDispatch& bd, int matched_parts, int matched_params) +{ + Vector& DispatchMap = sDispatchMap(); + if(ii >= part.GetCount()) { + if(n.view && n.method == method && + (matched_parts > bd.matched_parts || + matched_parts == bd.matched_parts && matched_params > bd.matched_params)) { + LLOG("Matched " << n.id << " parts " << matched_parts << " params " << matched_params); + bd.arg <<= arg; + bd.view = n.view; + bd.matched_parts = matched_parts; + bd.id = n.id; + bd.post_raw = n.post_raw; + } + if(!bd.view) { + int q = n.subnode.Find(String()); + while(q >= 0) { + const DispatchNode& an = DispatchMap[n.subnode[q]]; + if(an.argpos == DISPATCH_VARARGS && an.view && an.method == method) { + bd.view = an.view; + bd.id = n.id; + bd.arg.Clear(); + break; + } + q = n.subnode.FindNext(q); + } + } + return; + } + int qq = n.subnode.Get(part[ii], -1); + if(qq >= 0) + GetBestDispatch(method, part, ii + 1, DispatchMap[qq], arg, bd, matched_parts + 1, matched_params); + int q = n.subnode.Find(String()); + while(q >= 0) { + int qq = n.subnode[q]; + int ac = arg.GetCount(); + const DispatchNode& an = DispatchMap[qq]; + int apos = an.argpos; + LLOG(" *" << qq << " apos: " << apos); + if(apos == DISPATCH_VARARGS) { + if(an.view && an.method == method && + (matched_parts > bd.matched_parts || matched_parts == bd.matched_parts && matched_params > bd.matched_params)) { + LLOG("Matched VARARGS " << an.id << " parts " << matched_parts << " params " << matched_params); + bd.arg <<= arg; + bd.arg.Append(part, ii, part.GetCount() - ii); + bd.view = an.view; + bd.matched_parts = matched_parts; + bd.id = an.id; + } + } + else { + String pv; + if(IsNull(apos)) + arg.Add(part[ii]); + else { + String& at = arg.At(apos); + pv = at; + at = part[ii]; + } + GetBestDispatch(method, part, ii + 1, an, arg, bd, matched_parts, matched_params + 1); + if(!IsNull(apos)) + arg[apos] = pv; + } + arg.SetCount(ac); + q = n.subnode.FindNext(q); + } +} + +void Http::Dispatch(TcpSocket& socket) +{ + const Vector& DispatchMap = sDispatchMap(); + if(hdr.Read(socket)) { + int len = GetLength(); + content = socket.GetAll(len); + LLOG("--------------------------------------------"); + SKYLARKLOG(hdr.GetMethod() << " " << hdr.GetURI()); + LDUMP(content); + String r; + var.Clear(); + arg.Clear(); + LTIMING("Request processing"); + request_content_type = GetHeader("content-type"); + String rc = ToLower(request_content_type); + bool post = hdr.GetMethod() == "POST"; + if(post) + if(rc.StartsWith("application/x-www-form-urlencoded")) + ParseRequest(content); + else + if(rc.StartsWith("multipart/")) + ReadMultiPart(content); + String uri = hdr.GetURI(); + int q = uri.Find('?'); + if(q >= 0) { + if(!post) + ParseRequest(~uri + q + 1); + uri.Trim(q); + } + for(int i = hdr.fields.Find("cookie"); i >= 0; i = hdr.fields.FindNext(i)) { + const String& h = hdr.fields[i]; + int q = 0; + for(;;) { + int qq = h.Find('=', q); + if(qq < 0) + break; + String id = ToLower(TrimBoth(h.Mid(q, qq - q))); + qq++; + q = h.Find(';', qq); + if(q < 0) { + var.Add('@' + id, UrlDecode(h.Mid(qq))); + break; + } + var.Add('@' + id, UrlDecode(h.Mid(qq, q - qq))); + q++; + } + } + var.GetAdd("static") = app.static_dir; + var.GetAdd(".__identity__"); // To make StdLib.icpp GetIndentity work without changing preset stack positions + LDUMPM(var); + Vector part = Split(uri, '/'); + for(int i = 0; i < part.GetCount(); i++) + part[i] = UrlDecode(part[i]); + LDUMPC(part); + Vector a; + BestDispatch bd(arg); + if(DispatchMap.GetCount()) + GetBestDispatch(post ? DispatchNode::POST : DispatchNode::GET, part, 0, DispatchMap[0], a, bd, 0, 0); + LDUMPC(arg); + response.Clear(); + if(bd.view) { + try { + if(SQL.IsOpen()) + SQL.Begin(); + LoadSession(); + session_dirty = false; + if(post && !bd.post_raw) { + String id = Nvl((*this)["__post_identity__"], (*this)["__js_identity__"]); + if(id != (*this)[".__identity__"]) + throw AuthExc("identity error"); + } + lang = Nvl(Int(".__lang__"), LNG_ENGLISH); + Upp::SetLanguage(lang); + var.GetAdd(".__lang__") = lang; + var.GetAdd(".language") = ToLower(LNGAsText(lang)); + viewid = bd.id; + LDUMP(viewid); + (*bd.view)(*this); + if(session_dirty) + SaveSession(); + if(SQL.IsOpen()) + SQL.Commit(); + } + catch(SqlExc e) { + if(SQL.IsOpen()) + SQL.Rollback(); + response << "Internal server error
" + << "SQL ERROR: " << e; + code = 500; + code_text = "Internal server error"; + app.SqlError(*this); + } + catch(AuthExc e) { + if(SQL.IsOpen()) + SQL.Rollback(); + response << e; + code = 403; + code_text = "Unauthorized"; + app.Unauthorized(*this); + } + catch(BadRequestExc e) { + if(SQL.IsOpen()) + SQL.Rollback(); + response << "Bad request"; + code = 400; + code_text = "Bad request"; + app.Unauthorized(*this); + } + catch(Exc e) { + if(SQL.IsOpen()) + SQL.Rollback(); + response << "Internal server error
" + << e; + code = 500; + code_text = "Internal server error"; + app.InternalError(*this); + } + } + else { + response << "Page not found"; + code = 404; + code_text = "Not found"; + app.NotFound(*this); + } + r.Clear(); + SKYLARKLOG("=== Response: " << code << ' ' << code_text); + if(redirect.GetCount()) { + SKYLARKLOG("Redirect to: " << redirect); + r << "HTTP/1.1 " << code << " Found\r\n"; + r << "Location: " << redirect << "\r\n"; + } + else { + r << + "HTTP/1.0 " << code << ' ' << code_text << "\r\n" + "Date: " << WwwFormat(GetUtcTime()) << "\r\n" + "Server: U++\r\n" + "Content-Length: " << response.GetCount() << "\r\n" + "Connection: close\r\n" + "Cache-Control: no-cache\r\n" + "Content-Type: " << content_type << "\r\n"; + for(int i = 0; i < cookies.GetCount(); i++) + r << cookies[i]; + r << "\r\n"; + } + socket.PutAll(r); + socket.PutAll(response); + } +} + }; \ No newline at end of file diff --git a/uppsrc/Skylark/Http.h b/uppsrc/Skylark/Http.h index dc2106a8a..26f3b9d12 100644 --- a/uppsrc/Skylark/Http.h +++ b/uppsrc/Skylark/Http.h @@ -1,142 +1,144 @@ -void MakeLink(StringBuffer& out, const Vector& part, const Vector& arg); - -class Renderer { -protected: - VectorMap var; - int lang; - - Renderer& Link(const char *id, void (*view)(Http&), const Vector& arg); - const One& GetTemplate(const char *template_name); - -public: - Renderer& operator()(const char *id, const Value& v) { var.Add(id, v); return *this; } - Renderer& operator()(const ValueMap& map); - Renderer& operator()(const char *id, void (*view)(Http&)); - Renderer& operator()(const char *id, void (*view)(Http&), const Value& arg1); - Renderer& operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2); - - Renderer& operator()(const Sql& sql); - Renderer& operator()(Fields rec); - SqlUpdate Update(SqlId table); - SqlInsert Insert(SqlId table); - - Value operator[](const char *id) const { return var.Get(id, Null); } - - String RenderString(const String& template_name); - Value Render(const String& template_name) { return Raw(RenderString(template_name)); } - - Renderer& Render(const char *id, const String& template_name); - - Renderer() { lang = LNG_ENGLISH; } - virtual ~Renderer(); -}; - -class Http : public Renderer { - SkylarkApp& app; - HttpHeader hdr; - - String content; - String viewid; - - Vector arg; - String session_id; - VectorMap session_var; - bool session_dirty; - - String redirect; - int code; - String code_text; - String response; - String content_type; - String request_content_type; - - VectorMap cookies; - - void ParseRequest(const char *s); - void ReadMultiPart(const String& content); - - String SessionFile(const String& sid); - - void LoadSession(); - void SaveSession(); - -public: - Http& operator()(const char *id, const Value& v) { var.Add(id, v); return *this; } - Http& operator()(const ValueMap& map) { Renderer::operator()(map); return *this; } - Http& operator()(const char *id, void (*view)(Http&)) { Renderer::operator()(id, view); return *this; } - Http& operator()(const char *id, void (*view)(Http&), const Value& arg1) { Renderer::operator()(id, view, arg1); return *this; } - Http& operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2) { Renderer::operator()(id, view, arg1, arg2); return *this; } - - Http& operator()(const Sql& sql) { Renderer::operator()(sql); return *this; } - Http& operator()(Fields rec) { Renderer::operator()(rec); return *this; } - Http& Render(const char *id, const String& template_name) { Renderer::Render(id, template_name); return *this; } - Value Render(const String& template_name) { return Renderer::Render(template_name); } - - void Dispatch(TcpSocket& socket); - - String GetHeader(const char *s) const { return hdr[s]; } - int GetLength() const { return atoi(GetHeader("content-length")); } - - String GetViewId() const { return viewid; } - - Value operator[](const char *id) const { return Renderer::operator[](id); } - String operator[](int i) const { return i >= 0 && i < arg.GetCount() ? arg[i] : String(); } - - int Int(const char *id) const; - int Int(int i) const; - - int GetParamCount() const { return arg.GetCount(); } - - Http& ContentType(const char *s) { content_type = s; return *this; } - - Http& Content(const char *s, const Value& data); - Http& operator<<(const Value& s); - - Http& SetRawCookie(const char *id, const String& value, - Time expires = Null, const char *path = NULL, - const char *domain = NULL, bool secure = false, bool httponly = false); - Http& SetCookie(const char *id, const String& value, - Time expires = Null, const char *path = NULL, - const char *domain = NULL, bool secure = false, bool httponly = false); - - Http& ClearSession(); - Http& SessionSet(const char *id, const Value& value); - - Http& NewIdentity() { SessionSet("__identity__", Null); return *this; } - Http& NewSessionId(); - Http& SetLanguage(int lang); - - Http& Response(int code_, const String& ctext) { code = code_; code_text = ctext; return *this; } - - Http& RenderResult(const char *template_name); - Http& Redirect(const char *url, int code_ = 302) { code = code_; redirect = url; return *this; } - Http& Redirect(void (*view)(Http&), const Vector& arg); - Http& Redirect(void (*view)(Http&)); - Http& Redirect(void (*view)(Http&), const Value& v1); - Http& Redirect(void (*view)(Http&), const Value& v1, const Value& v2); - - Http& Ux(const char *id, const String& text); - Http& UxRender(const char *id, const char *template_name); - Http& UxSet(const char *id, const String& value); - Http& UxRun(const String& js_code); - - String GetResponse() const { return response; } - - const SkylarkApp& App() const { return app; } - - Http(SkylarkApp& app); -}; - -String HttpResponse(int code, const char *phrase, const String& data, const char *content_type = NULL); - -void RegisterView(void (*view)(Http&), const char *id, const char *path); - -#define SKYLARK(name, path) void name(Http& http); INITBLOCK { UPP::RegisterView(name, #name, path); } void name(Http& http) - -Vector *GetUrlViewLinkParts(const String& id); - -String MakeLink(void (*view)(Http&), const Vector& arg); - -enum { - SESSION_FORMAT_BINARY, SESSION_FORMAT_JSON, SESSION_FORMAT_XML -}; +void MakeLink(StringBuffer& out, const Vector& part, const Vector& arg); + +class Renderer { +protected: + VectorMap var; + int lang; + + Renderer& Link(const char *id, void (*view)(Http&), const Vector& arg); + const One& GetTemplate(const char *template_name); + +public: + Renderer& operator()(const char *id, const Value& v) { var.Add(id, v); return *this; } + Renderer& operator()(const ValueMap& map); + Renderer& operator()(const char *id, void (*view)(Http&)); + Renderer& operator()(const char *id, void (*view)(Http&), const Value& arg1); + Renderer& operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2); + + Renderer& operator()(const Sql& sql); + Renderer& operator()(Fields rec); + Renderer& operator()(const SqlSelect& row_sel); + Renderer& operator()(const char *id, const SqlSelect& sel); + SqlUpdate Update(SqlId table); + SqlInsert Insert(SqlId table); + + Value operator[](const char *id) const { return var.Get(id, Null); } + + String RenderString(const String& template_name); + Value Render(const String& template_name) { return Raw(RenderString(template_name)); } + + Renderer& Render(const char *id, const String& template_name); + + Renderer() { lang = LNG_ENGLISH; } + virtual ~Renderer(); +}; + +class Http : public Renderer { + SkylarkApp& app; + HttpHeader hdr; + + String content; + String viewid; + + Vector arg; + String session_id; + VectorMap session_var; + bool session_dirty; + + String redirect; + int code; + String code_text; + String response; + String content_type; + String request_content_type; + + VectorMap cookies; + + void ParseRequest(const char *s); + void ReadMultiPart(const String& content); + + String SessionFile(const String& sid); + + void LoadSession(); + void SaveSession(); + +public: + Http& operator()(const char *id, const Value& v) { var.Add(id, v); return *this; } + Http& operator()(const ValueMap& map) { Renderer::operator()(map); return *this; } + Http& operator()(const char *id, void (*view)(Http&)) { Renderer::operator()(id, view); return *this; } + Http& operator()(const char *id, void (*view)(Http&), const Value& arg1) { Renderer::operator()(id, view, arg1); return *this; } + Http& operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2) { Renderer::operator()(id, view, arg1, arg2); return *this; } + + Http& operator()(const Sql& sql) { Renderer::operator()(sql); return *this; } + Http& operator()(Fields rec) { Renderer::operator()(rec); return *this; } + Http& operator()(const SqlSelect& row_sel) { Renderer::operator()(row_sel); return *this; } + Http& operator()(const char *id, const SqlSelect& sel) { Renderer::operator()(id, sel); return *this; } + Http& Render(const char *id, const String& template_name) { Renderer::Render(id, template_name); return *this; } + Value Render(const String& template_name) { return Renderer::Render(template_name); } + + String GetHeader(const char *s) const { return hdr[s]; } + int GetLength() const { return atoi(GetHeader("content-length")); } + + String GetViewId() const { return viewid; } + + Value operator[](const char *id) const { return Renderer::operator[](id); } + int Int(const char *id) const; + + String operator[](int i) const { return i >= 0 && i < arg.GetCount() ? arg[i] : String(); } + int Int(int i) const; + + int GetParamCount() const { return arg.GetCount(); } + + Http& ContentType(const char *s) { content_type = s; return *this; } + + Http& Content(const char *s, const Value& data); + Http& operator<<(const Value& s); + + Http& SetRawCookie(const char *id, const String& value, + Time expires = Null, const char *path = NULL, + const char *domain = NULL, bool secure = false, bool httponly = false); + Http& SetCookie(const char *id, const String& value, + Time expires = Null, const char *path = NULL, + const char *domain = NULL, bool secure = false, bool httponly = false); + + Http& ClearSession(); + Http& SessionSet(const char *id, const Value& value); + + Http& NewIdentity() { SessionSet("__identity__", Null); return *this; } + Http& NewSessionId(); + Http& SetLanguage(int lang); + + Http& Response(int code_, const String& ctext) { code = code_; code_text = ctext; return *this; } + + Http& RenderResult(const char *template_name); + Http& Redirect(const char *url, int code_ = 302) { code = code_; redirect = url; return *this; } + Http& Redirect(void (*view)(Http&), const Vector& arg); + Http& Redirect(void (*view)(Http&)); + Http& Redirect(void (*view)(Http&), const Value& v1); + Http& Redirect(void (*view)(Http&), const Value& v1, const Value& v2); + + Http& Ux(const char *id, const String& text); + Http& UxRender(const char *id, const char *template_name); + Http& UxSet(const char *id, const String& value); + Http& UxRun(const String& js_code); + + String GetResponse() const { return response; } + + void Dispatch(TcpSocket& socket); + + const SkylarkApp& App() const { return app; } + + Http(SkylarkApp& app); +}; + +void RegisterHandler(void (*handler)(Http& http), const char *id, const char *path); + +#define SKYLARK(name, path) void name(Http& http); INITBLOCK { UPP::RegisterHandler(name, #name, path); } void name(Http& http) + +Vector *GetUrlViewLinkParts(const String& id); + +String MakeLink(void (*view)(Http&), const Vector& arg); + +enum { + SESSION_FORMAT_BINARY, SESSION_FORMAT_JSON, SESSION_FORMAT_XML +}; diff --git a/uppsrc/Skylark/Iml/Iml.h b/uppsrc/Skylark/Iml/Iml.h index 7e8b7af45..7f59443ba 100644 --- a/uppsrc/Skylark/Iml/Iml.h +++ b/uppsrc/Skylark/Iml/Iml.h @@ -1,10 +1,10 @@ -#ifndef _Iml_Iml_h -#define _Iml_Iml_h - -#include -#include -#include - - - -#endif +#ifndef _Iml_Iml_h +#define _Iml_Iml_h + +#include +#include +#include +#include + + +#endif diff --git a/uppsrc/Skylark/Iml/Iml.upp b/uppsrc/Skylark/Iml/Iml.upp index 8d8e9dc59..bb8b6890b 100644 --- a/uppsrc/Skylark/Iml/Iml.upp +++ b/uppsrc/Skylark/Iml/Iml.upp @@ -1,3 +1,10 @@ +description "\377128,0,0"; + +uses + Draw, + plugin\png, + plugin\jpg; + file Iml.h, Lib.icpp; diff --git a/uppsrc/Skylark/Iml/init b/uppsrc/Skylark/Iml/init index 82019c4b0..85b2f7095 100644 --- a/uppsrc/Skylark/Iml/init +++ b/uppsrc/Skylark/Iml/init @@ -1,6 +1,9 @@ #ifndef _Skylark_Iml_icpp_init_stub #define _Skylark_Iml_icpp_init_stub -#define BLITZ_INDEX__ F565e623033e15f6c077dba0bc03cb2ea +#include "Draw/init" +#include "plugin\png/init" +#include "plugin\jpg/init" +#define BLITZ_INDEX__ F2cfe659668367946f7f2baa1fbeb2b5e #include "Lib.icpp" #undef BLITZ_INDEX__ #endif diff --git a/uppsrc/Skylark/Skylark.h b/uppsrc/Skylark/Skylark.h index 186f8065a..e526f9022 100644 --- a/uppsrc/Skylark/Skylark.h +++ b/uppsrc/Skylark/Skylark.h @@ -1,96 +1,99 @@ -#ifndef _Wpp_Wpp_h -#define _Wpp_Wpp_h - -#include - -namespace Upp { - -namespace Ini { -extern IniBool skylark_log; -}; - -String GetThreadName(); - -#define SKYLARKLOG(x) LOG_(Ini::skylark_log, GetThreadName() << ' ' << x) - -class Renderer; -class Http; - -struct SessionConfig { - String cookie; - String dir; - int format; - SqlId table, id_column, data_column, lastwrite_column; - int expire; - - SessionConfig(); -}; - -struct AuthExc : Exc { - AuthExc(const String& s) : Exc(s) {} -}; - -struct SkylarkConfig { - String root; - VectorMap view_var; - String path; - String static_dir; - SessionConfig session; - int threads; - int port; - int prefork; - int timeout; - bool use_caching; -}; - -class SkylarkApp : protected SkylarkConfig { - TcpSocket server; - Mutex accept_mutex; - int main_pid; - Vector child_pid; - bool quit; - - - void ThreadRun(); - void Broadcast(int signal); - void Signal(int signal); - static void SignalHandler(int signal); - void Main(); - - void FinalizeViews(); - - static SkylarkApp *app; - -#ifdef PLATFORM_WIN32 - static BOOL WINAPI CtrlCHandlerRoutine(__in DWORD dwCtrlType); -#endif - - typedef SkylarkApp CLASSNAME; - - friend class Http; - -public: - virtual void SqlError(Http& http); - virtual void InternalError(Http& http); - virtual void NotFound(Http& http); - virtual void Unauthorized(Http& http); - - virtual void WorkThread(); - - void RunThread(); - - void Run(); - - static SkylarkApp& TheApp(); - static const SkylarkConfig& Config(); - - SkylarkApp(); - virtual ~SkylarkApp(); -}; - -#include "Witz.h" -#include "Http.h" - -}; - -#endif +#ifndef _Skylark_Skylark_h +#define _Skylark_Skylark_h + +#include + +namespace Upp { + +namespace Ini { +extern IniBool skylark_log; +}; + +String GetThreadName(); + +#define SKYLARKLOG(x) LOG_(Ini::skylark_log, GetThreadName() << ' ' << x) + +class Renderer; +class Http; + +struct SessionConfig { + String cookie; + String dir; + int format; + SqlId table, id_column, data_column, lastwrite_column; + int expire; + + SessionConfig(); +}; + +struct AuthExc : Exc { + AuthExc(const String& s) : Exc(s) {} +}; + +struct BadRequestExc : Exc {}; + +struct SkylarkConfig { + String root; + VectorMap view_var; + String path; + String static_dir; + SessionConfig session; + int threads; + int port; + int prefork; + int timeout; + bool use_caching; +}; + +class SkylarkApp : protected SkylarkConfig { + TcpSocket server; + Mutex accept_mutex; + int main_pid; + Vector child_pid; + bool quit; + + + void ThreadRun(); + void Broadcast(int signal); + void Signal(int signal); + static void SignalHandler(int signal); + void Main(); + + void FinalizeViews(); + + static SkylarkApp *app; + +#ifdef PLATFORM_WIN32 + static BOOL WINAPI CtrlCHandlerRoutine(__in DWORD dwCtrlType); +#endif + + typedef SkylarkApp CLASSNAME; + + friend class Http; + +public: + virtual void SqlError(Http& http); + virtual void InternalError(Http& http); + virtual void NotFound(Http& http); + virtual void Unauthorized(Http& http); + virtual void BadRequest(Http& http); + + virtual void WorkThread(); + + void RunThread(); + + void Run(); + + static SkylarkApp& TheApp(); + static const SkylarkConfig& Config(); + + SkylarkApp(); + virtual ~SkylarkApp(); +}; + +#include "Witz.h" +#include "Http.h" + +}; + +#endif diff --git a/uppsrc/Skylark/Sql.cpp b/uppsrc/Skylark/Sql.cpp index ec4097298..b55181ed4 100644 --- a/uppsrc/Skylark/Sql.cpp +++ b/uppsrc/Skylark/Sql.cpp @@ -1,60 +1,80 @@ -#include "Skylark.h" - -namespace Upp { - -struct sFieldsToRenderer : public FieldOperator { - Renderer& http; - - void Field(const char *name, Ref f) { - http(name, f); - } - - sFieldsToRenderer(Renderer& http) : http(http) {} -}; - -Renderer& Renderer::operator()(Fields rec) -{ - sFieldsToRenderer x(*this); - rec(x); - return *this; -} - -Renderer& Renderer::operator()(const Sql& sql) -{ - int n = sql.GetColumns(); - for(int i = 0; i < n; i++) - (*this)(sql.GetColumnInfo(i).name, sql[i]); - return *this; -} - -SqlUpdate Renderer::Update(SqlId table) -{ - Vector col = GetSchColumns(~table); - SqlUpdate u(table); - for(int i = 0; i < col.GetCount(); i++) { - String c = col[i]; - int q = var.Find(c); - if(q < 0) - q = var.Find(ToLower(c)); - if(q >= 0) - u(c, var[q]); - } - return u; -} - -SqlInsert Renderer::Insert(SqlId table) -{ - Vector col = GetSchColumns(~table); - SqlInsert y(table); - for(int i = 0; i < col.GetCount(); i++) { - String c = col[i]; - int q = var.Find(c); - if(q < 0) - q = var.Find(ToLower(c)); - if(q >= 0) - y(c, var[q]); - } - return y; -} - +#include "Skylark.h" + +namespace Upp { + +struct sFieldsToRenderer : public FieldOperator { + Renderer& http; + + void Field(const char *name, Ref f) { + http(name, f); + } + + sFieldsToRenderer(Renderer& http) : http(http) {} +}; + +Renderer& Renderer::operator()(Fields rec) +{ + sFieldsToRenderer x(*this); + rec(x); + return *this; +} + +Renderer& Renderer::operator()(const Sql& sql) +{ + int n = sql.GetColumns(); + for(int i = 0; i < n; i++) + (*this)(sql.GetColumnInfo(i).name, sql[i]); + return *this; +} + +Renderer& Renderer::operator()(const SqlSelect& sel) +{ + SqlR sql; + sql * sel; + if(!sql.Fetch()) + throw BadRequestExc(); + return operator()(sql); +} + +Renderer& Renderer::operator()(const char *id, const SqlSelect& sel) +{ + ValueArray list; + ValueMap vm; + SqlR sql; + sql * sel; + while(sql.Fetch(vm)) + list.Add(vm); + return operator()(id, list); +} + +SqlUpdate Renderer::Update(SqlId table) +{ + Vector col = GetSchColumns(~table); + SqlUpdate u(table); + for(int i = 0; i < col.GetCount(); i++) { + String c = col[i]; + int q = var.Find(c); + if(q < 0) + q = var.Find(ToLower(c)); + if(q >= 0) + u(c, var[q]); + } + return u; +} + +SqlInsert Renderer::Insert(SqlId table) +{ + Vector col = GetSchColumns(~table); + SqlInsert y(table); + for(int i = 0; i < col.GetCount(); i++) { + String c = col[i]; + int q = var.Find(c); + if(q < 0) + q = var.Find(ToLower(c)); + if(q >= 0) + y(c, var[q]); + } + return y; +} + }; \ No newline at end of file diff --git a/uppsrc/Skylark/srcdoc.tpp/Tutorial$en-us.tpp b/uppsrc/Skylark/srcdoc.tpp/Tutorial$en-us.tpp index 8d6624f15..b0e8a8242 100644 --- a/uppsrc/Skylark/srcdoc.tpp/Tutorial$en-us.tpp +++ b/uppsrc/Skylark/srcdoc.tpp/Tutorial$en-us.tpp @@ -590,4 +590,347 @@ the value attribute of matching element. Not shown here, [*@5 UxRender] is similar to Ux, but renders the text from witz template and finally [*@5 UxRun] can be used to run any JavaScript code in the client.&] -[s5; ]] \ No newline at end of file +[s5; &] +[s3; 9. Connecting SQL database&] +[s5; Skylark naturally uses U`+`+ SQL support when dealing with persistent +storage, using `"session per`-thread`" mode. Because of this, +database session needs to be connected when starting each thread +`- this is done by overloading [@5 WorkThread] virtual method of +SkylarkApp:&] +[s7; &] +[s7; &] +[s7; #include &] +[s7; #include &] +[s7; &] +[s7; using namespace Upp;&] +[s7; &] +[s7; #define MODEL &] +[s7; #define SCHEMADIALECT &] +[s7; #include &] +[s7; #include &] +[s7; #include &] +[s7; &] +[s7; SKYLARK(HomePage, `"`")&] +[s7; `{&] +[s7; -|Sql sql;&] +[s7; -|sql `* Select(ID, NAME, LASTNAME)&] +[s7; -| .From(PERSON)&] +[s7; -| .OrderBy(LASTNAME, NAME);&] +[s7; -|ValueArray person;&] +[s7; -|ValueMap vm;&] +[s7; -|while(sql.Fetch(vm))&] +[s7; -|-|person.Add(vm);&] +[s7; -|http(`"PERSON`", person)&] +[s7; -| .RenderResult(`"Skylark09/index`");&] +[s7; `}&] +[s7; &] +[s7; struct MyApp : SkylarkApp `{&] +[s7; -|[* virtual void WorkThread();]&] +[s7; &] +[s7; -|MyApp() `{&] +[s7; -|-|root `= `"myapp`";&] +[s7; -|-|threads `= 1; // Sqlite3 does not like threads...&] +[s7; -|#ifdef `_DEBUG&] +[s7; -|-|prefork `= 0;&] +[s7; -|-|use`_caching `= false;&] +[s7; -|#endif&] +[s7; -|`}&] +[s7; `};&] +[s7; &] +[s7; void InitModel()&] +[s7; `{&] +[s7; #ifdef `_DEBUG&] +[s7; -|SqlSchema sch(SQLITE3);&] +[s7; -|All`_Tables(sch);&] +[s7; -|SqlPerformScript(sch.Upgrade());&] +[s7; -|SqlPerformScript(sch.Attributes());&] +[s7; -|sch.SaveNormal();&] +[s7; #endif&] +[s7; `}&] +[s7; &] +[s7; void OpenSQL(Sqlite3Session`& sqlite3)&] +[s7; `{&] +[s7; -|if(!sqlite3.Open(ConfigFile(`"db`"))) `{&] +[s7; -|-|LOG(`"Can`'t create or open database file`\n`");&] +[s7; -|-|Exit(1);&] +[s7; -|`}&] +[s7; #ifdef `_DEBUG&] +[s7; -|sqlite3.LogErrors();&] +[s7; -|sqlite3.SetTrace();&] +[s7; #endif&] +[s7; -|SQL `= sqlite3;&] +[s7; `}&] +[s7; &] +[s7; [* void MyApp`::WorkThread()]&] +[s7; [* `{]&] +[s7; [* -|Sqlite3Session sqlite3;]&] +[s7; [* -|OpenSQL(sqlite3);]&] +[s7; [* -|RunThread();]&] +[s7; [* `}]&] +[s7; &] +[s7; void InitDB()&] +[s7; `{&] +[s7; -|Sqlite3Session sqlsession;&] +[s7; -|OpenSQL(sqlsession);&] +[s7; -|SqlSchema sch(SQLITE3);&] +[s7; -|All`_Tables(sch);&] +[s7; -|SqlPerformScript(sch.Upgrade());&] +[s7; -|SqlPerformScript(sch.Attributes());&] +[s7; &] +[s7; -|SQL `* Insert(PERSON)(NAME,`"Joe`")(LASTNAME,`"Smith`");&] +[s7; -|SQL `* Insert(PERSON)(NAME,`"Mike`")(LASTNAME,`"Carpenter`");&] +[s7; -|SQL `* Insert(PERSON)(NAME,`"Jon`")(LASTNAME,`"Goober`");&] +[s7; `}&] +[s7; &] +[s7; CONSOLE`_APP`_MAIN&] +[s7; `{&] +[s7; #ifdef `_DEBUG&] +[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&] +[s7; -|Ini`::skylark`_log `= true;&] +[s7; #endif&] +[s7; &] +[s7; -|DeleteFile(ConfigFile(`"db`")); // for this example, always +create a new DB&] +[s7; -|InitDB();&] +[s7; &] +[s7; -|MyApp().Run();-|&] +[s7; `}&] +[s7; &] +[s7; &] +[s5; [* Skylark09/index.witz:]&] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; `$for(i in PERSON)&] +[s7; -|&] +[s7; -| &] +[s7; -| &] +[s7; -| &] +[s7; -|-|&] +[s7; `$/&] +[s7;
No.First NameLast Name
`$i.`_index.`$i.NAME`$i.LASTNAME
&] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s5; [* Skylark09/myapp.sch:]&] +[s7; TABLE`_(PERSON)&] +[s7; -|INT`_ (ID) PRIMARY`_KEY AUTO`_INCREMENT&] +[s7; -|STRING`_ (NAME, 200)&] +[s7; -|STRING`_ (LASTNAME, 200)&] +[s7; END`_TABLE&] +[s7; &] +[s7; &] +[s3; 10. Advanced SQL&] +[s5; Http class contains some advanced convenience support for dealing +with SQL databases:&] +[s5;i150;O0; It is possible to fill result set of SqlSelect into +witz variable as demonstrated by [@5 HomePage ]handler&] +[s5;i150;O0; Http`::Insert inserts a row to database, reading values +of columns based on known (from .sch file) names of columns from +equally named variables of shared variable space (which come +from POST or GET request), as demonstrated by [@5 SubmitNew].&] +[s5;i150;O0; Http`::Update updates a row in database, again based +on names of columns and shared variable space, as demonstrated +by [@5 SubmitEdit].&] +[s5;i150;O0; It is also possible to add shared variables based on +single row select, names being then same as names of columns, +as demonstrated by [@5 Edit].&] +[s5; &] +[s7; #include &] +[s7; #include &] +[s7; &] +[s7; using namespace Upp;&] +[s7; &] +[s7; #define MODEL &] +[s7; #define SCHEMADIALECT &] +[s7; #include &] +[s7; #include &] +[s7; #include &] +[s7; &] +[s7; SKYLARK(HomePage, `"`")&] +[s7; `{&] +[s7; /`*&] +[s7; -|Sql sql;&] +[s7; -|sql `* Select(ID, NAME, LASTNAME)&] +[s7; -| .From(PERSON)&] +[s7; -| .OrderBy(LASTNAME, NAME);&] +[s7; -|ValueArray person;&] +[s7; -|ValueMap vm;&] +[s7; -|while(sql.Fetch(vm))&] +[s7; -|-|person.Add(vm);&] +[s7; `*/&] +[s7; -|[* http(`"PERSON`", Select(SqlAll()).From(PERSON).OrderBy(LASTNAME, +NAME))]&] +[s7; -|.RenderResult(`"Skylark10/index`");&] +[s7; `}&] +[s7; &] +[s7; SKYLARK(SubmitNew, `"create/submit:POST`")&] +[s7; `{&] +[s7; /`*&] +[s7; -|SQL `* Insert(PERSON)(NAME, http`[`"name`"`])(LASTNAME, http`[`"lastname`"`]);&] +[s7; `*/&] +[s7; -|[* SQL `* http.Insert(PERSON);]&] +[s7; -|http.Redirect(HomePage);&] +[s7; `}&] +[s7; &] +[s7; SKYLARK(New, `"create`")&] +[s7; `{&] +[s7; -|http(`"ACTION`", SubmitNew)&] +[s7; -|.RenderResult(`"Skylark10/Dialog`");&] +[s7; `}&] +[s7; &] +[s7; SKYLARK(SubmitEdit, `"edit/submit/`*:POST`")&] +[s7; `{&] +[s7; /`*&] +[s7; -|SQL `* Update(PERSON)&] +[s7; -|-|-|(NAME, http`[`"name`"`])&] +[s7; -|-|-|(LASTNAME, http`[`"lastname`"`])&] +[s7; -|-|-|.Where(ID `=`= http.Int(0));&] +[s7; -|;&] +[s7; `*/&] +[s7; -|[* SQL `* http.Update(PERSON).Where(ID `=`= http.Int(0));]&] +[s7; -|http.Redirect(HomePage);&] +[s7; `}&] +[s7; &] +[s7; SKYLARK(Edit, `"edit/`*`")&] +[s7; `{&] +[s7; -|int id `= http.Int(0);&] +[s7; /`*&] +[s7; -|Sql sql;&] +[s7; -|sql `* Select(NAME, LASTNAME).From(PERSON).Where(ID `=`= id);&] +[s7; -|if(!sql.Fetch()) `{&] +[s7; -|-|http.Redirect(HomePage);&] +[s7; -|-|return;&] +[s7; -|`}&] +[s7; -|http(`"NAME`", sql`[NAME`])(`"LASTNAME`", sql`[LASTNAME`]);&] +[s7; `*/&] +[s7; -|[* http]&] +[s7; [* -|-|(Select(SqlAll()).From(PERSON).Where(ID `=`= id))]&] +[s7; -|-|(`"ID`", id)&] +[s7; -|-|(`"ACTION`", SubmitEdit, id)&] +[s7; -|.RenderResult(`"Skylark10/Dialog`");&] +[s7; `}&] +[s7; &] +[s7; struct MyApp : SkylarkApp `{&] +[s7; -|virtual void WorkThread();&] +[s7; &] +[s7; -|MyApp() `{&] +[s7; -|-|root `= `"myapp`";&] +[s7; -|-|threads `= 1; // Sqlite3 does not like threads...&] +[s7; -|#ifdef `_DEBUG&] +[s7; -|-|prefork `= 0;&] +[s7; -|-|use`_caching `= false;&] +[s7; -|#endif&] +[s7; -|`}&] +[s7; `};&] +[s7; &] +[s7; void InitModel()&] +[s7; `{&] +[s7; #ifdef `_DEBUG&] +[s7; -|SqlSchema sch(SQLITE3);&] +[s7; -|All`_Tables(sch);&] +[s7; -|SqlPerformScript(sch.Upgrade());&] +[s7; -|SqlPerformScript(sch.Attributes());&] +[s7; -|sch.SaveNormal();&] +[s7; #endif&] +[s7; `}&] +[s7; &] +[s7; void OpenSQL(Sqlite3Session`& sqlite3)&] +[s7; `{&] +[s7; -|if(!sqlite3.Open(ConfigFile(`"db`"))) `{&] +[s7; -|-|LOG(`"Can`'t create or open database file`\n`");&] +[s7; -|-|Exit(1);&] +[s7; -|`}&] +[s7; #ifdef `_DEBUG&] +[s7; -|sqlite3.LogErrors();&] +[s7; -|sqlite3.SetTrace();&] +[s7; #endif&] +[s7; -|SQL `= sqlite3;&] +[s7; `}&] +[s7; &] +[s7; void MyApp`::WorkThread()&] +[s7; `{&] +[s7; -|Sqlite3Session sqlite3;&] +[s7; -|OpenSQL(sqlite3);&] +[s7; -|RunThread();&] +[s7; `}&] +[s7; &] +[s7; void InitDB()&] +[s7; `{&] +[s7; -|Sqlite3Session sqlsession;&] +[s7; -|OpenSQL(sqlsession);&] +[s7; -|SqlSchema sch(SQLITE3);&] +[s7; -|All`_Tables(sch);&] +[s7; -|SqlPerformScript(sch.Upgrade());&] +[s7; -|SqlPerformScript(sch.Attributes());&] +[s7; `}&] +[s7; &] +[s7; CONSOLE`_APP`_MAIN&] +[s7; `{&] +[s7; #ifdef `_DEBUG&] +[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&] +[s7; -|Ini`::skylark`_log `= true;&] +[s7; #endif&] +[s7; &] +[s7; -|InitDB();&] +[s7; &] +[s7; -|MyApp().Run();-|&] +[s7; `}&] +[s7; &] +[s7; &] +[s5; [* Skylark10/index.witz:]&] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; `$for(i in PERSON)&] +[s7; -|&] +[s7; -| &] +[s7; -| &] +[s7; -| &] +[s7; -| &] +[s7; -|-|&] +[s7; `$/&] +[s7;
No.First NameLast Name
`$(i.`_index `+ 1).`$i.NAME`$i.LASTNAMEEdit
&] +[s7; Create new&] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s5; [* Skylark10/dialog.witz:]&] +[s7; &] +[s7; &] +[s7;
&] +[s7; `$post`_identity()&] +[s7;

&] +[s7; First name:
&] +[s7; Last name:
&] +[s7;
&] +[s7;

&] +[s7;
&] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s7; &] +[s5; [* Skylark10/myapp.sch:]&] +[s7; TABLE`_(PERSON)&] +[s7; -|INT`_ (ID) PRIMARY`_KEY AUTO`_INCREMENT&] +[s7; -|STRING`_ (NAME, 200)&] +[s7; -|STRING`_ (LASTNAME, 200)&] +[s7; END`_TABLE&] +[s7; &] +[s7; &] +[s7; ]] \ No newline at end of file diff --git a/uppsrc/ide/Common/ComDlg.cpp b/uppsrc/ide/Common/ComDlg.cpp index b0d3bea7b..b8d865f11 100644 --- a/uppsrc/ide/Common/ComDlg.cpp +++ b/uppsrc/ide/Common/ComDlg.cpp @@ -17,6 +17,14 @@ void IdeFileIcon0(bool dir, const String& filename, Image& img) break; } } + if(ext == ".html") + img = IdeCommonImg::html(); + if(ext == ".css") + img = IdeCommonImg::css(); + if(ext == ".witz") + img = IdeCommonImg::witz(); + if(ext == ".js") + img = IdeCommonImg::js(); if(ext == ".usc") img = IdeCommonImg::Script(); if(ext == ".lng" || ext == ".lngj" || ext == ".t" || ext == ".jt") @@ -89,12 +97,13 @@ void IdeFs(FileSel& fs) { } void SourceFs(FileSel& fs) { - String mask = "*.cpp *.h *.hpp *.c *.C *.cc *.cxx *.icpp *.lay *.iml *.lng *.sch *.usc *.rc *.brc *.upt"; fs.Type("C++ files (*.cpp *.h *.hpp *.c *.C *.cc *.cxx *.icpp)", "*.cpp *.h *.hpp *.c *.C *.cc *.cxx *.icpp"); fs.Type("Layout files (*.lay)", "*.lay"); fs.Type("Image files (*.iml)", "*.iml"); fs.Type("Language files (*.lng)", "*.lng"); + fs.Type("Web development files (*.html *.js *.css *.witz)", "*.html *.js *.css *.witz"); fs.Type("Other special files (*.sch *.usc *.rc *.brc *.upt)", "*.sch *.usc *.rc *.brc *.upt"); + String mask = "*.cpp *.h *.hpp *.c *.C *.cc *.cxx *.icpp *.lay *.iml *.lng *.sch *.usc *.rc *.brc *.upt *.html *.js *.css *.witz"; fs.Type("All source files (" + mask + ")", mask); IdeFs(fs); } diff --git a/uppsrc/ide/Common/common.iml b/uppsrc/ide/Common/common.iml index 6fe688f49..ecb881b67 100644 --- a/uppsrc/ide/Common/common.iml +++ b/uppsrc/ide/Common/common.iml @@ -13,62 +13,74 @@ IMAGE_ID(FFff) IMAGE_ID(Source) IMAGE_ID(Fast) IMAGE_ID(ISource) +IMAGE_ID(js) +IMAGE_ID(html) +IMAGE_ID(css) +IMAGE_ID(witz) IMAGE_ID(Script) IMAGE_ID(Images) IMAGE_ID(Language) IMAGE_ID(IncludePath) IMAGE_BEGIN_DATA -IMAGE_DATA(120,156,237,154,223,107,28,85,20,199,239,63,32,20,124,40,136,224,67,20,132,130,32,216,167,72,138,15,34,98,173,136) -IMAGE_DATA(79,22,181,47,21,5,3,190,136,52,193,226,136,81,108,108,218,162,162,226,163,248,226,75,69,16,95,252,149,146,64,90) -IMAGE_DATA(232,214,180,89,109,146,182,70,177,173,63,98,91,220,180,82,237,143,175,51,179,123,103,238,220,57,231,254,218,217,100,183) -IMAGE_DATA(221,19,14,187,51,115,62,231,156,251,99,206,220,59,89,177,78,172,19,29,16,88,212,202,115,226,232,67,26,35,210,124) -IMAGE_DATA(201,107,22,31,70,222,161,45,44,79,248,234,243,22,158,82,87,158,85,23,222,34,38,222,67,187,71,58,84,12,164,132) -IMAGE_DATA(118,66,106,115,125,239,155,172,26,252,24,57,198,15,153,179,99,252,18,207,216,128,242,109,226,251,249,247,100,254,89,27) -IMAGE_DATA(2,231,47,217,23,132,86,47,221,92,12,110,199,65,86,13,126,140,28,227,135,204,217,49,126,137,103,108,64,249,54,241) -IMAGE_DATA(253,252,123,50,255,172,13,129,243,151,236,11,66,171,151,164,24,160,51,174,219,148,36,171,8,252,113,183,242,234,106,52) -IMAGE_DATA(2,127,204,73,4,55,237,84,254,237,242,55,123,251,171,226,215,64,154,43,3,118,112,168,74,228,83,157,168,189,149,109) -IMAGE_DATA(191,85,85,124,53,78,65,181,243,174,188,237,124,149,241,41,59,142,215,125,176,156,163,15,155,255,204,200,145,23,145,40) -IMAGE_DATA(239,249,213,118,26,248,140,141,207,20,62,9,31,44,159,48,186,154,120,42,119,143,248,5,113,136,111,20,135,248,206,62) -IMAGE_DATA(84,37,13,59,188,77,8,20,89,156,66,214,44,212,251,162,155,41,62,37,99,224,213,101,82,141,49,47,208,50,31,193) -IMAGE_DATA(124,238,199,206,171,121,235,199,33,241,139,237,240,139,95,206,163,179,241,237,90,129,172,82,49,160,158,78,94,246,147,147) -IMAGE_DATA(147,169,194,205,151,214,217,34,99,37,159,72,114,206,204,55,43,177,80,239,108,121,156,84,232,40,242,227,225,196,83,237) -IMAGE_DATA(203,89,176,79,251,140,151,125,37,85,242,66,225,245,235,54,62,255,67,27,60,156,120,82,51,150,93,245,24,251,63,103) -IMAGE_DATA(225,52,126,73,94,201,245,220,166,201,202,124,139,215,202,124,89,161,183,215,245,94,224,250,164,122,9,40,6,214,27,209) -IMAGE_DATA(166,119,224,99,12,224,51,118,32,175,95,219,140,127,47,15,161,241,247,70,252,241,251,125,248,121,105,8,11,243,155,241) -IMAGE_DATA(67,253,153,212,126,3,106,184,7,11,44,255,207,165,123,113,113,101,16,231,207,109,194,153,211,15,226,212,201,199,98,246) -IMAGE_DATA(105,204,126,63,156,218,111,196,37,12,94,187,202,242,231,207,109,192,95,203,131,248,237,236,67,113,236,39,48,63,191,13) -IMAGE_DATA(71,103,135,113,112,102,52,181,223,186,2,60,123,129,93,234,227,236,153,187,113,250,215,7,176,180,244,56,22,22,182,97) -IMAGE_DATA(110,110,24,135,15,143,96,122,250,245,212,126,215,47,192,251,39,121,254,167,83,67,113,206,143,198,109,126,42,102,95,64) -IMAGE_DATA(173,182,3,51,51,111,224,192,129,137,212,254,243,250,50,190,172,159,96,249,19,139,143,224,248,143,91,49,119,236,121,28) -IMAGE_DATA(169,189,140,67,135,198,48,61,181,15,223,126,243,81,106,127,164,254,65,220,31,175,177,252,252,241,39,113,236,232,115,113) -IMAGE_DATA(206,47,197,109,142,48,53,181,59,158,196,31,198,252,39,169,253,114,125,19,86,234,119,145,133,200,69,49,123,43,80,27) -IMAGE_DATA(96,138,222,146,162,23,136,167,104,108,243,221,118,224,171,209,150,61,208,104,52,20,222,44,169,205,254,47,128,79,191,38) -IMAGE_DATA(121,167,252,223,153,0,118,141,114,55,40,222,123,119,15,246,237,125,59,213,137,137,113,140,143,191,133,157,59,95,193,200) -IMAGE_DATA(200,142,38,255,226,253,192,246,1,157,115,238,195,255,182,172,199,229,45,119,134,22,6,254,254,189,81,139,1,21,215,167) -IMAGE_DATA(24,36,159,80,52,57,246,41,6,66,225,164,250,20,3,42,190,79,49,160,226,251,20,3,149,239,23,131,142,23,3,175) -IMAGE_DATA(62,36,138,129,79,65,168,180,24,152,146,246,177,5,234,185,10,75,135,80,219,4,133,43,241,250,4,208,183,9,182,248) -IMAGE_DATA(45,18,220,54,193,45,62,191,77,208,120,123,95,245,183,9,253,109,66,216,205,239,46,171,240,59,3,249,233,221,1,202) -IMAGE_DATA(141,35,60,223,25,144,188,90,12,18,49,191,51,40,250,214,139,129,253,157,65,49,63,189,24,152,222,25,20,10,134,146) -IMAGE_DATA(135,107,49,208,223,92,103,215,237,197,160,148,99,137,53,223,204,108,238,62,197,160,217,55,90,187,220,139,1,211,118,231) -IMAGE_DATA(98,80,102,253,138,65,105,238,48,237,101,11,153,225,92,79,23,131,170,36,168,35,228,96,41,75,4,189,152,24,89,157) -IMAGE_DATA(211,181,229,159,205,89,101,229,228,81,121,67,14,5,182,53,129,178,188,34,145,253,123,136,231,221,114,228,115,39,98,135) -IMAGE_DATA(240,84,254,137,88,242,39,125,164,5,189,60,142,78,124,192,248,9,33,139,154,166,194,30,155,244,19,192,173,174,164,63) -IMAGE_DATA(58,122,117,173,179,232,77,9,188,209,186,69,218,125,218,208,79,96,79,190,215,227,7,112,25,31,24,183,115,210,189,191) -IMAGE_DATA(64,44,72,37,43,3,145,28,231,75,59,231,149,65,198,105,234,178,50,80,109,35,221,135,101,101,32,237,66,86,6,142) -IMAGE_DATA(57,178,185,83,177,67,120,42,127,215,149,129,238,67,174,12,28,250,175,196,7,140,159,16,50,71,122,220,110,208,149,65) -IMAGE_DATA(247,166,151,8,245,4,115,237,88,24,255,108,147,169,108,235,19,223,39,150,91,238,190,19,208,191,205,171,227,163,221,190) -IMAGE_DATA(176,245,59,165,161,227,238,159,123,59,243,117,109,69,45,6,88,127,11,18,85,175,255,249,48,144,168,60,198,202,109,72) -IMAGE_DATA(148,112,5,27,111,98,69,185,195,210,239,45,54,227,73,182,177,191,196,229,219,125,145,125,138,242,96,52,217,156,135,137) -IMAGE_DATA(87,252,20,89,133,87,99,25,248,34,71,196,103,57,42,103,229,251,226,226,98,170,186,175,194,121,7,158,83,91,254,210) -IMAGE_DATA(238,74,124,227,168,170,241,108,255,73,54,249,46,89,249,157,106,23,199,83,90,106,191,37,190,170,108,124,166,159,29,218) -IMAGE_DATA(207,9,57,14,204,252,181,250,241,225,254,7,104,184,9,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) -IMAGE_END_DATA(1408, 16) +IMAGE_DATA(120,156,237,155,207,107,29,85,20,199,231,79,40,184,112,35,184,136,130,80,16,4,187,138,164,184,16,23,214,138,184,40) +IMAGE_DATA(22,109,55,21,5,3,110,68,154,96,113,196,40,54,26,91,84,172,184,20,55,66,81,4,113,227,175,148,4,210,66,95) +IMAGE_DATA(77,155,167,77,108,107,20,219,106,137,105,49,177,82,197,230,235,204,36,119,222,157,59,231,220,123,238,157,121,201,123,237) +IMAGE_DATA(59,225,48,153,55,231,115,206,185,103,230,158,185,51,121,137,54,69,155,162,54,8,28,234,228,57,17,250,80,198,136,13) +IMAGE_DATA(95,234,152,195,135,149,23,140,133,229,9,95,61,222,193,83,42,229,89,149,240,14,177,241,30,218,57,210,166,102,160,36) +IMAGE_DATA(180,8,153,205,202,129,215,88,181,248,177,114,140,31,50,103,97,252,18,207,216,128,242,109,227,123,249,119,101,254,249,24) +IMAGE_DATA(2,175,95,178,22,132,214,47,157,220,12,110,195,81,86,45,126,172,28,227,135,204,89,24,191,196,51,54,160,124,219,248) +IMAGE_DATA(94,254,93,153,127,62,134,192,235,151,172,5,161,245,75,218,12,208,30,215,21,37,205,42,6,191,223,169,188,190,26,141) +IMAGE_DATA(193,239,115,18,67,166,237,202,191,42,127,179,143,191,46,126,3,100,117,101,192,158,28,170,19,249,116,39,234,217,202,245) +IMAGE_DATA(188,85,87,124,61,78,65,141,207,165,188,235,243,58,227,83,118,28,111,250,96,57,161,15,151,255,220,72,200,71,113,84) +IMAGE_DATA(126,230,215,199,105,225,115,54,249,164,176,37,124,176,124,202,152,106,227,169,220,61,226,23,68,16,223,42,130,248,98,31) +IMAGE_DATA(186,146,134,109,126,76,8,20,213,156,66,214,44,212,251,162,155,41,62,37,35,224,85,114,81,141,48,47,208,114,31,193) +IMAGE_DATA(124,203,143,155,215,243,54,247,67,226,23,199,225,23,191,156,71,123,227,187,181,6,89,167,102,64,221,157,188,236,199,199) +IMAGE_DATA(199,51,133,204,151,81,236,40,103,21,159,74,250,153,157,95,237,196,145,62,179,213,126,218,161,227,216,143,135,136,167,198) +IMAGE_DATA(215,98,193,222,237,115,94,213,74,169,226,35,141,55,143,187,248,214,15,42,240,16,241,164,230,44,187,234,177,214,191,197) +IMAGE_DATA(66,116,254,210,188,210,227,45,155,85,86,229,91,60,86,230,203,10,115,188,210,185,192,213,164,126,9,104,6,206,137,232) +IMAGE_DATA(210,219,241,33,250,240,41,123,34,87,174,111,195,63,215,6,176,244,231,22,92,250,253,94,252,60,63,128,185,217,109,248) +IMAGE_DATA(190,185,43,179,223,140,6,238,198,28,203,255,125,245,30,252,181,220,143,203,139,91,113,225,252,3,56,119,246,145,132,125) +IMAGE_DATA(18,211,223,13,102,246,91,112,21,253,215,255,99,249,203,139,155,241,199,66,63,126,187,248,96,18,251,49,204,206,238,198) +IMAGE_DATA(201,233,65,28,157,26,206,236,119,46,3,79,93,97,151,250,184,120,225,46,156,255,245,126,204,207,63,138,185,185,221,152) +IMAGE_DATA(153,25,196,241,227,67,152,156,124,37,179,223,255,11,240,222,89,158,255,233,220,64,146,243,195,201,152,159,72,216,103,209) +IMAGE_DATA(104,236,197,212,212,171,56,114,100,44,179,255,172,185,128,47,154,103,88,254,204,143,15,225,244,15,59,49,115,234,25,156) +IMAGE_DATA(104,188,128,99,199,70,48,57,113,16,223,124,253,65,102,127,162,121,40,169,199,203,44,63,123,250,113,156,58,249,116,146) +IMAGE_DATA(243,243,201,152,99,76,76,188,153,92,196,239,39,252,71,153,253,66,115,43,150,155,119,146,141,72,162,152,190,5,104,244) +IMAGE_DATA(49,77,111,94,211,43,196,93,52,177,249,118,15,240,229,240,154,61,176,180,180,164,241,118,201,108,62,249,28,248,248,43) +IMAGE_DATA(146,23,229,255,246,24,176,127,152,155,160,120,247,157,183,112,240,192,27,153,142,141,141,98,116,244,117,236,219,247,34,134) +IMAGE_DATA(134,246,174,242,207,221,7,236,233,51,57,113,13,255,221,126,43,174,109,191,35,180,49,240,243,247,70,109,6,84,92,159) +IMAGE_DATA(102,144,110,161,105,186,239,211,12,34,141,83,234,211,12,168,248,62,205,128,138,239,211,12,116,190,215,12,218,222,12,188) +IMAGE_DATA(106,72,52,3,159,134,80,107,51,176,37,237,99,11,52,91,26,57,10,66,61,38,104,92,137,55,47,0,243,49,193,21) +IMAGE_DATA(127,141,4,247,152,32,139,207,63,38,24,188,187,86,189,199,132,222,99,66,216,228,151,203,58,124,207,64,109,189,11,160) +IMAGE_DATA(77,156,200,243,157,1,201,235,205,32,21,251,59,131,162,111,179,25,184,223,25,20,243,51,155,129,237,157,65,161,97,104) +IMAGE_DATA(121,72,155,129,249,230,58,63,238,110,6,165,28,75,172,125,50,179,185,251,52,131,213,218,24,227,146,55,3,102,236,226) +IMAGE_DATA(102,80,102,253,154,65,233,218,97,198,203,54,50,203,103,93,221,12,234,146,160,66,168,147,165,45,17,204,102,98,101,77) +IMAGE_DATA(206,212,53,255,108,206,58,171,46,30,157,183,228,80,96,215,46,160,60,175,56,202,255,60,196,243,178,28,249,220,137,216) +IMAGE_DATA(33,60,149,127,42,142,252,73,31,89,67,47,159,71,17,31,112,254,162,72,53,53,67,35,119,108,210,79,0,183,190,146) +IMAGE_DATA(125,233,232,165,141,206,162,59,37,112,162,117,138,84,189,219,208,119,96,79,190,219,227,7,112,57,31,24,183,125,210,185) +IMAGE_DATA(223,64,44,72,45,43,131,40,221,111,45,237,196,43,131,156,51,84,178,50,208,109,99,211,135,99,101,160,236,66,86,6) +IMAGE_DATA(194,28,217,220,169,216,33,60,149,191,116,101,96,250,80,43,3,65,253,74,124,192,249,139,34,149,35,125,222,110,204,149) +IMAGE_DATA(129,135,84,25,8,176,18,92,140,140,85,234,233,163,192,122,250,32,89,161,143,146,173,193,217,124,144,177,76,63,76,30) +IMAGE_DATA(108,174,212,239,82,31,130,241,144,99,224,242,118,140,159,244,33,172,157,179,150,85,175,33,79,182,224,35,144,205,125,84) +IMAGE_DATA(96,221,178,65,255,194,236,236,200,54,17,248,112,242,154,159,42,241,249,137,32,20,27,159,126,151,221,220,42,149,242,101) +IMAGE_DATA(251,214,239,190,241,125,249,58,198,127,248,240,37,50,15,41,191,99,199,98,37,222,220,250,212,191,202,248,61,181,22,249) +IMAGE_DATA(31,114,29,133,141,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_END_DATA(1376, 16) IMAGE_BEGIN_DATA -IMAGE_DATA(120,156,237,148,97,10,128,48,8,133,61,194,238,213,133,186,189,181,31,110,42,110,234,42,40,232,193,35,130,190,167,115) -IMAGE_DATA(18,20,40,128,8,36,12,122,36,60,179,112,219,177,63,1,132,61,190,50,220,90,79,215,87,223,101,103,33,250,236,103) -IMAGE_DATA(144,61,145,77,158,213,183,24,254,110,241,220,154,213,25,222,44,84,157,133,89,244,58,214,93,58,247,218,122,149,179,140) -IMAGE_DATA(243,220,179,157,138,236,149,224,213,78,101,246,218,98,163,252,136,141,240,33,215,159,193,75,53,59,156,203,210,18,173,178) -IMAGE_DATA(228,100,134,96,239,200,72,178,34,99,145,109,25,23,216,95,31,212,1,40,156,179,185,0,0,0,0,0,0,0,0,0) -IMAGE_END_DATA(160, 2) +IMAGE_DATA(120,156,237,152,75,78,196,48,12,134,115,1,164,217,33,113,35,196,133,88,246,68,28,168,71,96,9,75,22,30,50,144) +IMAGE_DATA(214,118,253,76,59,165,149,240,232,87,211,105,62,219,113,83,79,53,229,82,46,229,14,6,1,153,188,101,1,31,46,143) +IMAGE_DATA(252,172,137,175,249,48,121,193,151,202,151,193,30,91,188,199,238,201,175,205,191,135,39,243,58,242,143,152,198,39,117,12) +IMAGE_DATA(251,163,102,224,242,78,241,187,155,73,187,230,248,136,196,239,110,38,206,70,154,248,82,6,133,27,118,225,233,92,122,60) +IMAGE_DATA(11,175,177,25,94,51,139,79,232,56,86,155,1,28,43,37,110,171,222,48,204,143,211,16,132,185,153,248,153,88,177,220) +IMAGE_DATA(115,62,122,214,188,143,143,181,181,240,234,46,169,247,190,231,115,63,207,195,207,13,55,3,120,124,128,42,124,253,253,25) +IMAGE_DATA(160,170,157,195,231,19,84,9,174,192,227,45,182,44,11,118,27,255,178,19,47,178,31,111,11,14,119,111,212,197,57,255) +IMAGE_DATA(195,206,60,88,60,251,53,152,89,196,227,88,6,79,57,33,190,202,73,57,163,241,56,142,55,113,95,228,251,0,175,201) +IMAGE_DATA(203,191,205,251,250,126,112,176,24,175,214,175,177,117,220,216,54,150,214,165,241,146,22,235,119,226,99,169,241,149,58,7) +IMAGE_DATA(214,175,153,120,31,148,253,235,250,73,113,236,205,192,93,172,227,184,110,91,120,121,133,249,88,10,145,199,87,6,43,241) +IMAGE_DATA(106,182,73,124,54,47,91,11,146,231,188,6,154,83,147,200,163,248,18,131,207,37,30,139,179,220,135,87,11,22,167,163) +IMAGE_DATA(22,115,28,233,94,58,247,117,202,149,214,50,206,99,89,123,42,178,175,8,207,246,84,102,95,75,108,148,215,216,8,31) +IMAGE_DATA(210,157,254,51,216,194,50,93,112,193,2,184,5,50,217,166,164,15,194,110,225,35,201,18,31,157,236,228,99,5,251,111) +IMAGE_DATA(39,180,43,210,170,244,249,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) +IMAGE_END_DATA(448, 6)