Compare commits

...

17 Commits

Author SHA1 Message Date
ef3af2ec45 UF CHANGED README UUUUFH UH UH AAAAAAAAA I- I am COOOOMMITTING YEEEAAH! YEAH ANOTHER README CHANGE THIS IS THE UF- UH -AH I'M DOING SO MUCH JOB 2024-08-15 13:39:42 +03:00
7a1bfd81f3 nytl: quick fix 2024-08-15 11:53:44 +03:00
0f84548f78 nytl: hot fix 2024-08-15 09:50:14 +03:00
da2cb4261c nytl: bug fix 2024-08-14 22:32:30 +03:00
ea7d50a183 nytl: merged files rendering.cpp and execute_expression.cpp 2024-08-14 21:24:27 +03:00
dd806a5633 New York Transit Line is finally usable at least a little. Integrated it with frontend 2024-08-14 21:18:00 +03:00
393703b0b2 Fixed one of those "Cs convenient ways to show error" (sigsegv caused by ivalid pointer). It were 2 hours of pure frustration 2024-08-14 21:18:00 +03:00
e03794410b FIXED SOME TABULATION BUGS, DO NOT LOOK AT MY PREVIOUS SYSTEM 2024-08-14 21:18:00 +03:00
076a941a6e Mhm, I wrote something, for some reason nothing works 2024-08-14 21:18:00 +03:00
83c17c22e6 Daily update 2024-08-14 21:18:00 +03:00
01176052d7 Лмао, Адель, харе carriage return ставить в конце каждой строки((( 2024-08-14 21:18:00 +03:00
91aaf51410 I hate my life so much 2024-08-14 21:18:00 +03:00
be37e4f5ab Very raw version of nytl, no parsing yet 2024-08-14 21:18:00 +03:00
5fab1131ca Daily update 2024-08-14 21:18:00 +03:00
c264e3802b Daily saving of progress 2024-08-14 21:18:00 +03:00
vladimir
3a8d1207b1 Upgrade of registration page 2024-08-06 15:11:27 +03:00
af537a7639 Merge pull request 'Connected fron t to back' (#4) from back into master
Reviewed-on: https://peppermintgingerbread.zip/collarbone-annihilation/iu9-ca-web-chat/pulls/4
2024-08-06 09:37:06 +00:00
33 changed files with 1853 additions and 186 deletions

View File

@ -1,6 +1,60 @@
# ИУ9-21Б Вэб-чат C.A
# IU9 C.A. WEB CHAT
Сделан на летней практике 5-ю первокурсниками ИУ9
C.A. stands for Collarbone Annihilation.
Сделан на летней практике 5-ю первокурсниками из ИУ9-21Б.
# Dependencies
iu9-ca-web-chat использует
- C++
- [regexis024-build-system](
https://peppermintgingerbread.zip/collarbone-annihilation/regexis024-build-system
)
- [libregexis024](
https://peppermintgingerbread.zip/kme-devline/libregexis024
)
- [libjsonincpp](
https://peppermintgingerbread.zip/collarbone-annihilation/libjsonincpp
)
- [sqlite3](
https://www.sqlite.org
)
- pkg-config и pkg-config файлики для всех этих зависимостей
Сервис так же использует библиотеки engine_engine_number_9 и new_york_transit_line,
размещённые прямо в репозитории.
Работает только на unix системах.
# Compilation
```sh
./building/build_build_sytem.sh
./building/main bi ./ "absolute/path/to/installation/root"
```
# Usage
Помимо самого бинарника нужен файл с настройками сервиса. Формат настроек: JSON.
Комментарии не поддерживаются. Пример такого файла находится в example/config.json.
Вместе с бинарным фалом так же распространяются ассеты, необъходимые для работы сайта.
Их можно найти в папке assets. В настроках (поле `["assets"]`) указывается путь до
папки с ассетами. Путь может быть как абсолютным, так и относительным к рабочей директории.
Поле настроек `["database"]` указывает как соединиться с базой данных.
Поддерживается только база данных sqlite. Поддерживается только хранение в файле.
Поле `["database"]["file"]` указывает путь где хранится sqlite база данных.
Перед тем как использовать сервис нужно его проинициализировать (а точнее проинициализировать
базу данных):
`ROOT_PW="<your desired root password>" iu9-ca-web-chat /path/to/config.json initialize`
Переменная окружения `ROOT_PW` читается для устаановки пароля root пользователю раз и навсегда.
Далее можно запускать сервис:
`iu9-ca-web-chat /path/to/config.json run`
Для остановки сервиса киньте ему SIGTERM или SIGINT.
# Список участников
@ -10,3 +64,13 @@
4. [Каримов Адель](https://gitflic.ru/user/ra1n)
5. [Яковлев Антон](https://gitflic.ru/user/yakovlevanton)
# Комментарии (для разработчиков)
Зачем писать комментарии в коде, если можно их вынести в отдельные пдф-ки?
- [API сервиса](
https://peppermintgingerbread.zip/collarbone-annihilation/iu9-ca-chat-api)
- [Доки New York Transit Line](
https://peppermintgingerbread.zip/collarbone-annihilation/new_york_transit_line_documentation_rus)
О том как работает всё остальное можно только догадываться.

View File

@ -1,31 +1,32 @@
{% ELDEF main JSON pres %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Список Чат-Комнат</title>
<title>{% WRITE pres.phr.decl.list-of-chat-rooms %}</title>
<link rel="stylesheet" href="/assets/css/list-rooms.css">
</head>
<body>
<div class="container">
<h1 style="color: white;">Выберите Чат-Комнату</h1>
<h1 style="color: white;">{% WRITE pres.phr.decl.select-chat-room %}</h1>
<ul class="room-list">
<!-- Здесь будет список комнат -->
</ul>
<button class="create-room-button" onclick="openCreateRoomModal()">Создать Комнату</button>
<button class="create-room-button" onclick="openCreateRoomModal()">{% WRITE pres.phr.act.create-room %}</button>
</div>
<div id="passwordModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<span class="close" onclick="closeModal()">&times;</span>
<h2>Введите Пароль</h2>
<h2>VVedite parol</h2> <!-- Nam ne nuzhen parol ot komnat -->
</div>
<div class="modal-body">
<input type="password" id="roomPassword" placeholder="Пароль">
</div>
<div class="modal-footer">
<button class="join-button" onclick="validatePassword()">Подтвердить</button>
<button class="join-button" onclick="validatePassword()">{% WRITE pres.phr.act.confirm %}</button>
</div>
</div>
</div>
@ -35,14 +36,14 @@
<div class="modal-content">
<div class="modal-header">
<span class="close" onclick="closeCreateRoomModal()">&times;</span>
<h2>Создать Комнату</h2>
<h2>{% WRITE pres.phr.decl.create-room %}</h2>
</div>
<div class="modal-body">
<input type="text" id="newRoomName" placeholder="Название комнаты">
<input type="password" id="newRoomPassword" placeholder="Пароль">
<input type="text" id="newRoomName" placeholder="{% WRITE pres.phr.decl.name-of-room %}">
<input type="password" id="newRoomPassword" placeholder="Пароль"> <!-- Fedya, nam ne nuzhen parol -->
</div>
<div class="modal-footer">
<button class="join-button" onclick="createRoom()">Создать</button>
<button class="join-button" onclick="createRoom()">{% WRITE pres.phr.act.create %}</button>
</div>
</div>
</div>
@ -50,3 +51,4 @@
<script src="/assets/js/list-rooms.js"></script>
</body>
</html>
{% ENDELDEF %}

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Страница Регистрации</title>
<link rel="stylesheet" href="assets/css/registration.css">
</head>
<body>
<div class="form-container">
<h1 class="hide-cursor no-select">Вход</h1>
<form action="assets/html/list-rooms.html" method="post">
<input type="text" name="username" placeholder="Имя пользователя" id="username"><br>
<input type="text" name="login" placeholder="Логин" id="login"><br>
<input type="password" name="password" placeholder="Пароль" id="password"><br>
<button type="submit" class="hide-cursor no-select">Зарегистрироваться</button>
<div id="error"></div>
</form>
</div>
<script src="assets/js/registration.js"></script>
</body>
</html>

View File

@ -47,8 +47,31 @@ button {
outline: none;
font-size: 16px;
font-weight: bold;
transition: background-color 0.3s;
}
button:hover {
button:hover,
button:focus-visible {
background-color: #007bb5;
}
.hide-cursor::placeholder {
color: #000;
}
.hide-cursor {
caret-color: transparent;
}
.no-select {
-webkit-user-select: none; /* Для Safari */
-moz-user-select: none; /* Для Firefox */
user-select: none; /* Для всех остальных браузеров */
}
div {
color: red;
font-size: 15px;
margin-top: 10px;
display: none;
}

View File

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Страница Регистрации</title>
<link rel="stylesheet" href="/assets/css/registration.css">
</head>
<body>
<div class="form-container">
<h1>Вход</h1>
<form action="/pathToChat" method="post">
<input type="text" name="username" placeholder="Имя пользователя" id="username"><br>
<input type="text" name="login" placeholder="Логин" id="login"><br>
<input type="password" name="password" placeholder="Пароль" id="password"><br>
<button type="submit">Зарегистрироваться</button>
</form>
</div>
<script src="/assets/js/registration.js"></script>
</body>
</html>

View File

@ -1,16 +1,50 @@
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form');
const submitButton = form.querySelector('button[type="submit"]');
const errorElement = document.getElementById('error');
form.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
const activeElement = document.activeElement;
if (activeElement.tagName === 'INPUT' && activeElement.type !== 'submit') {
event.preventDefault();
const formElements = Array.from(form.elements);
const currentIndex = formElements.indexOf(activeElement);
if (currentIndex !== -1 && formElements[currentIndex + 1]) {
if (activeElement.tagName === 'INPUT') {
if (event.key === 'Enter') {
event.preventDefault();
if (currentIndex === formElements.length - 1) {
submitButton.focus();
} else if (currentIndex !== -1 && formElements[currentIndex + 1]) {
formElements[currentIndex + 1].focus();
}
}
}
});
});
submitButton.addEventListener('focus', function() {
submitButton.classList.add('focus-visible');
});
submitButton.addEventListener('blur', function() {
submitButton.classList.remove('focus-visible');
});
form.addEventListener('submit', function(event) {
if (!validateForm()) {
event.preventDefault();
}
});
function validateForm() {
const username = document.getElementById('username').value.trim();
const login = document.getElementById('login').value.trim();
const password = document.getElementById('password').value.trim();
if (username === '' || login === '' || password === '') {
errorElement.textContent = 'Пожалуйста, заполните все поля';
errorElement.style.display = 'block';
return false;
}
return true;
}
});

View File

@ -34,6 +34,7 @@ struct CAWebChat {
BuildUnitsArray runlevel_2;
std::string build_type;
bool build_tests = false;
std::vector<std::string> warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic",
"-Wno-unused-but-set-variable", "-Wno-reorder"};
@ -57,8 +58,8 @@ struct CAWebChat {
return my_flag_collection;
}
CAWebChat(std::string _build_type, const NormalCBuildSystemCommandMeaning& cmd)
: build_type(std::move(_build_type))
CAWebChat(const std::string& _build_type, bool _build_tests, const NormalCBuildSystemCommandMeaning& cmd)
: build_type(_build_type), build_tests(_build_tests)
{
ASSERT(build_type == "release" || build_type == "debug", "Unknown build type");
@ -108,11 +109,43 @@ struct CAWebChat {
T.installation_dir = "";
my_targets.push_back(T);
}
{ CTarget T{"new_york_transit_line", "shared_library"};
T.additional_compilation_flags = getSomeRadFlags();
T.external_deps = {
CTargetDependenceOnExternalLibrary{"libjsonincpp", {true, true}},
};
T.units = {
"alotalot.cpp",
"html_case.cpp",
"parser.cpp",
"rendering.cpp",
"templater.cpp",
};
for (std::string& u: T.units)
u = "http_server/new_york_transit_line/" + u;
T.include_pr = "http_server";
T.exported_headers = {
"templater.h",
"html_case.h",
};
for (std::string& u: T.exported_headers)
u = "new_york_transit_line/" + u;
my_targets.push_back(T);
}
{ CTarget T{"iu9-ca-web-chat", "executable"};
T.additional_compilation_flags = getSomeRadFlags();
T.proj_deps = {CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"}};
T.external_deps = {CTargetDependenceOnExternalLibrary{"sqlite3"}};
T.units = {"main.cpp"};
T.proj_deps = {
CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"},
CTargetDependenceOnProjectsLibrary{"new_york_transit_line"},
};
T.external_deps = {
CTargetDependenceOnExternalLibrary{"sqlite3"}
};
T.units = {
"main.cpp",
"initialize.cpp",
"run.cpp",
};
for (std::string& u: T.units)
u = "web_chat/" + u;
T.include_pr = "web_chat";
@ -133,7 +166,9 @@ int main(int argc, char** argv) {
}
NormalCBuildSystemCommandMeaning cmd;
regular_bs_cli_cmd_interpret(args, cmd);
CAWebChat bs("debug", cmd);
const char* BS_SCRIPT_TYPE = getenv("BS_SCRIPT_TYPE");
const char* BS_SCRIPT_TESTS = getenv("BS_SCRIPT_TESTS");
CAWebChat bs(BS_SCRIPT_TYPE ? BS_SCRIPT_TYPE : "release", (bool)BS_SCRIPT_TESTS, cmd);
if (cmd.need_to_build)
complete_tasks_of_build_units(bs.runlevel_1);
umask(~0755);

View File

@ -1,3 +1,36 @@
{
"name": "Web chat"
"presentation": {
"instance-identity": {
"top-title": "Вэб чат от ИУ9"
},
"phr": {
"decl": {
"list-of-chat-rooms": "Список Чат-Комнат",
"select-chat-room": "Выберете чат комнату",
"name-of-room": "Название комнаты",
"create-room": "Создать комнату"
},
"act": {
"create-room": "Создать комнату",
"confirm": "Подтвердить",
"create": "Создать"
}
}
},
"assets": "./assets",
"database": {
"type": "sqlite",
"file": "./iu9-ca-web-chat.db"
},
"limits": {
"max-users": 100000,
"max-rooms": 100000,
"max-messages": 10000000000000,
"storage-size-limit": 100000000000
},
"server": {
"workers": 8,
"http-listen": ["127.0.0.1:1025"],
"command-listen": []
}
}

View File

@ -5,15 +5,9 @@
#include <string.h>
namespace een9 {
ServerError::ServerError(const std::string &err, const std::string &file, const std::string &func, int line): err(err),
FILE(file),
func(func),
LINE(line) {
char buf[4096];
snprintf(buf, 4096, "Error occured in function %s (line %d of %s)\n"
"Error: %s",
func.c_str(), LINE, FILE.c_str(), err.c_str());
WHAT = buf;
ServerError::ServerError(const std::string &err, const std::string &file, const std::string &func, int line){
WHAT = "Error occured in function " + func + " (line " + std::to_string(line) + " of " +
file + ")\nError: " + err;
}
const char * ServerError::what() const noexcept {

View File

@ -6,10 +6,6 @@
namespace een9 {
class ServerError : public std::exception{
std::string err;
std::string FILE;
std::string func;
int LINE;
std::string WHAT;
public:

View File

@ -23,9 +23,9 @@ namespace een9 {
ASSERT_on_iret(ret, "stat(\"" + cur + "\")");
if (S_ISDIR(info.st_mode)) {
DIR* D = opendir(path_to_cur_ent.c_str());
cur += "/";
ASSERT(D != NULL, prettyprint_errno("opendir(\"" + cur +"\")"));
struct Guard1{ DIR*& D; ~Guard1(){ closedir(D); } } g1{D};
ASSERT(D != NULL, prettyprint_errno("opendir(\"" + cur +"\")"));
cur += "/";
while (true) {
errno = 0;
struct dirent* Dent = readdir(D);

View File

@ -55,7 +55,7 @@ namespace een9 {
while ((ret = (int)read(fd, buf, 2048)) > 0) {
size_t oldN = result.size();
result.resize(oldN + ret);
memcpy(&result[oldN], buf, ret);
memcpy((void*)&result.c_str()[oldN], buf, ret);
}
ASSERT_on_iret(ret, "Reading from " + description);
}

View File

@ -82,10 +82,10 @@ namespace een9 {
struct WorkersEnv {
WorkersEnvCommon& wtec;
int id;
worker_id_t id;
ClientRequestParser_WorkerBuffers personal_parser_buffer;
explicit WorkersEnv(WorkersEnvCommon& wtec, int id): wtec(wtec), id(id), personal_parser_buffer(wtec.parser_programs){}
explicit WorkersEnv(WorkersEnvCommon& wtec, worker_id_t id): wtec(wtec), id(id), personal_parser_buffer(wtec.parser_programs){}
};
// todo: add timeout for multiple bytes, add more settings
@ -125,7 +125,7 @@ namespace een9 {
void process_connection(const SlaveTask& task, WorkersEnv& wte) {
ClientRequest client_request = process_connection_input(task.fd(), task.s_tips, wte);
std::string server_response = wte.wtec.guest_core(task, client_request);
std::string server_response = wte.wtec.guest_core(task, client_request, wte.id);
process_connection_output(task.fd(), server_response);
}
@ -149,6 +149,7 @@ namespace een9 {
} catch (const std::exception& e) {
printf("Client request procession failure in worker\n");
printf("%s\n", e.what());
/* Under mysterious some circumstances, in this place destructor of string in SystemError causes segfault. I can't fix that */
}
}
printf("Worker finished\n");
@ -166,7 +167,7 @@ namespace een9 {
std::vector<pthread_t> workers(params.slave_number);
std::vector<uptr<WorkersEnv>> wtes(params.slave_number);
for (size_t i = 0; i < params.slave_number; i++) {
wtes[i] = std::make_unique<WorkersEnv>(wtec, i);
wtes[i] = std::make_unique<WorkersEnv>(wtec, (worker_id_t)i);
}
for (size_t i = 0; i < params.slave_number; i++) {
pthread_create(&workers[i], NULL, worker_func, wtes[i].get());

View File

@ -29,8 +29,10 @@ namespace een9 {
EEN9_ServerTips s_tips;
};
typedef int worker_id_t;
/* guest_core function must not throw anything that is not derived from std::exception */
typedef std::function<std::string(const SlaveTask&, const ClientRequest&)> guest_core_t;
typedef std::function<std::string(const SlaveTask&, const ClientRequest&, worker_id_t worker_id)> guest_core_t;
struct ServersConfiguration {
size_t critical_load_1 = 90;

View File

@ -0,0 +1,95 @@
#include "alotalot.h"
#include <algorithm>
#include <errno.h>
#include <string.h>
#include <vector>
namespace nytl {
FUp::FUp(const std::string &err, const std::string &file, const std::string &func, int line){
WHAT = "Error occured in function " + func + " (line " + std::to_string(line) + " of " +
file + ")\nError: " + err;
}
const char * FUp::what() const noexcept {
return WHAT.c_str();
}
std::string prettyprint_errno(const std::string &pref) {
const char* d = strerrorname_np(errno);
return pref.empty() ? std::string(d) : std::string(pref) + ": " + d;
}
bool endsIn(const std::string &a, const std::string &b) {
if (b.size() > a.size())
return false;
return std::equal(a.end() - (ssize_t)b.size(), a.end(), b.begin());
}
std::string throwout_postfix(const std::string &a, size_t bsz) {
return a.substr(0, a.size() >= bsz ? a.size() - bsz : 0);
}
bool isALPHA(char ch) {
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z');
}
bool isNUM(char ch) {
return '0' <= ch && ch <= '9';
}
bool isUNCHAR(char ch) {
return isALPHA(ch) || isNUM(ch) || ch == '-' || ch == '_';
}
bool isUNCHARnonNUM(char ch) {
return isALPHA(ch) || ch == '-' || ch == '_';
}
bool isSPACE(char ch) {
return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n';
}
bool isUname(const std::string &str) {
if (str.empty() || str == "_")
return false;
if (isNUM(str[0]))
return false;
for (char ch: str)
if (!isUNCHAR(ch))
return false;
return true;
}
bool is_uname_dotted_sequence(const std::string& uinp) {
if (uinp.empty())
return false;
std::vector<std::string> r = {""};
for (char ch: uinp) {
if (ch == '.') {
r.emplace_back();
} else {
r.back() += ch;
}
}
for (const std::string& c: r)
if (!isUname(c))
return false;
return true;
}
std::string make_uppercase(const std::string &source) {
std::string result(source);
for (size_t i = 0; i < source.size(); i++) {
char ch = source[i];
if ('a' <= ch && ch <= 'z')
result[i] = (char)(ch - 'a' + 'A');
}
return result;
}
void rstrip(std::string &str) {
while (!str.empty() && isSPACE(str.back()))
str.resize(str.size() - 1);
}
}

View File

@ -0,0 +1,53 @@
#ifndef NEW_YORK_TRANSIT_LINE_ALOTALOT_H
#define NEW_YORK_TRANSIT_LINE_ALOTALOT_H
#include <stdexcept>
#include <memory>
/* A little of this, a little of that
* DO NOT EXPORT THIS FILE */
namespace nytl {
template<typename T>
using uptr = std::unique_ptr<T>;
template<typename Tp>
constexpr std::remove_reference_t<Tp>&&
mv(Tp&& t) noexcept
{ return static_cast<std::remove_reference_t<Tp>&&>(t); }
class FUp : public std::exception{
std::string WHAT;
public:
FUp(const std::string &err, const std::string &file, const std::string &func, int line);
const char *what() const noexcept override;
};
std::string prettyprint_errno(const std::string& pref);
#define THROW(err) throw FUp(err, __FILE__, __func__, __LINE__)
#define THROW_on_errno(err) THROW(prettyprint_errno(err))
#define ASSERT(cond, err) do { if (!(cond)) { THROW(err); } } while (0);
#define ASSERT_pl(cond) ASSERT(cond, "Failed assertion `" #cond "`")
#define ASSERT_on_iret(iret, err) ASSERT((iret) >= 0, prettyprint_errno(err));
bool endsIn(const std::string& a, const std::string& b);
std::string throwout_postfix(const std::string& a, size_t bsz);
bool isALPHA(char ch);
bool isNUM(char ch);
bool isUNCHAR(char ch);
bool isUNCHARnonNUM(char ch);
bool isSPACE(char ch);
bool isUname(const std::string& str);
bool is_uname_dotted_sequence(const std::string& uinp);
std::string make_uppercase(const std::string& source);
void rstrip(std::string& str);
}
#endif

View File

@ -0,0 +1,24 @@
#ifndef NEW_YORK_TRANSIT_LINE_CORE_H
#define NEW_YORK_TRANSIT_LINE_CORE_H
#include "templater.h"
#include <functional>
/* Do not export this header */
namespace nytl {
void debug_print_templater(const Templater& T);
/* ============== For parsing =============================*/
void parse_bare_file(const std::string& filename, const std::string& content,
global_elem_set_t& result);
void parse_special_file(const std::string& filename, const std::string& content,
global_elem_set_t& result, TemplaterSettings& syntax);
/* =================== For rendering ====================*/
std::string rendering_core(const std::string& entry_func, const std::vector<const json::JSON*>& entry_arguments,
const global_elem_set_t& elem_ns, const std::function<std::string(std::string)>& escape);
}
#endif

View File

@ -0,0 +1,49 @@
#include "templater.h"
#include "alotalot.h"
#include "jsonincpp/string_representation.h"
#include <assert.h>
namespace nytl {
void debug_print_templater(const Templater& T) {
printf("===== TEMPLATER INTERNAL RESOURCES =====\n");
for (auto& p: T.elements) {
printf("=== %s element =====\n", p.first.c_str());
const Element& el = p.second;
printf("%s, %s\n", el.base ? "BASE" : "NOT BASE", el.is_hidden ? "HIDDEN" : "NOT HIDDEN");
if (!el.is_hidden) {
std::string signature;
for (const json::JSON& arg_type: el.arguments) {
if (!signature.empty())
signature += " ";
signature += json::generate_str(arg_type, json::print_compact);
}
printf("Signature: %s\n", signature.c_str());
}
for (const ElementPart& part: el.parts) {
if (part.type == ElementPart::p_code) {
printf("code: <b><e><f><o><r><e><><l><f>\n%s\n<a><f><t><e><r><><l><f>\n", part.when_code.lines.c_str());
} else if (part.type == ElementPart::p_for_put) {
const ElementPart::when_for_put_S& P = part.when_for_put;
printf("dor cycle call:\ninternal_element: %s,\nref_over:%s,\nwhere_key_var: %ld, where_value_var: %ld, %s\n",
P.internal_element.c_str(), json::generate_str(P.ref_over, json::print_pretty).c_str(),
P.where_key_var, P.where_value_var, P.line_feed ? "LF" : "NOLF");
} else if (part.type == ElementPart::p_ref_put) {
const ElementPart::when_ref_put_S& P = part.when_ref_put;
printf("ref block call:\ninternal_element: %s\nref_over:%s\n",
P.internal_element.c_str(), json::generate_str(P.ref_over, json::print_pretty).c_str());
} else {
assert(part.type == ElementPart::p_put);
const ElementPart::when_put_S& P = part.when_put;
printf("PUT:\ncalled_element: %s\n",
json::generate_str(P.called_element, json::print_pretty).c_str());
for (size_t i = 0; i < P.passed_arguments.size(); i++) {
printf("passed_arguments[%lu] = %s\n", i,
json::generate_str(P.passed_arguments[i], json::print_pretty).c_str());
}
}
}
printf("=== That was element %s ====\n", p.first.c_str());
}
printf("===== DEBUG IS OVER =====\n");
}
}

View File

@ -0,0 +1,30 @@
#include "html_case.h"
namespace nytl {
std::string html_case_espace_string(const std::string &inp) {
std::string res;
res.reserve(inp.size());
for (char ch: inp) {
switch (ch) {
case '&':
res += "&amp";
break;
case '<':
res += "&lt";
break;
case '>':
res += "&gt";
break;
case '"':
res += "&quot";
break;
case '\'':
res += "&#39";
break;
default:
res += ch;
}
}
return res;
}
}

View File

@ -0,0 +1,10 @@
#ifndef NEW_YORK_TRANSIT_LINE_HTML_CASE_H
#define NEW_YORK_TRANSIT_LINE_HTML_CASE_H
#include <string>
namespace nytl {
std::string html_case_espace_string(const std::string &inp);
}
#endif

View File

@ -0,0 +1,554 @@
#include "core.h"
#include "alotalot.h"
#include <vector>
#include <assert.h>
namespace nytl {
size_t first_nw_char(const std::string& str) {
size_t i = 0;
for (; i < str.size(); i++)
if (!isSPACE(str[i]))
break;
return i;
}
bool is_space_only(const std::string& str) {
return first_nw_char(str) == str.size();
}
std::string clement_lstrip(const std::string& str) {
size_t gone = 0;
size_t n = str.size();
for (size_t i = 0; i < n; i++) {
if (str[i] == '\n') {
gone = i + 1;
} else if (!isSPACE(str[i])) {
break;
}
}
return str.substr(gone);
}
struct ParsingContext {
std::string text;
size_t pos = 0;
size_t column = 0;
size_t line = 0;
};
constexpr int EOFVAL = -999;
int peep(ParsingContext &ctx) {
if (ctx.text.size() <= ctx.pos)
return EOFVAL;
return ctx.text[ctx.pos];
}
char advance(ParsingContext& ctx) {
if (ctx.text[ctx.pos] == '\n') {
ctx.line++;
ctx.column = 0;
} else {
ctx.column++;
}
return ctx.text[ctx.pos++];
}
char skip(ParsingContext& ctx) {
ASSERT(ctx.pos < ctx.text.size(), "Unexpected EOF");
return advance(ctx);
}
void skip(ParsingContext& ctx, char ch) {
ASSERT(ctx.pos < ctx.text.size(), "Unexpected EOF");
ASSERT(ctx.text[ctx.pos] == ch, "Unexpected character");
advance(ctx);
}
void skipWhitespace(ParsingContext &ctx) {
while (peep(ctx) >= 0 && isSPACE((char)peep(ctx)))
skip(ctx);
}
void skipString(ParsingContext &ctx, const std::string &str) {
for (char ch: str)
skip(ctx, ch);
}
std::string readName(ParsingContext &ctx) {
std::string result;
int f = peep(ctx);
if (f >= 0 && isUNCHARnonNUM((char)f)) {
skip(ctx);
result += (char)f;
while (peep(ctx) >= 0 && isUNCHAR((char)peep(ctx)))
result += skip(ctx);
}
return result;
}
std::string readUint(ParsingContext &ctx) {
if (peep(ctx) == '0') {
skip(ctx);
return "0";
}
std::string result;
while (peep(ctx) >= 0 && isNUM((char)peep(ctx)))
result += skip(ctx);
return result;
}
std::vector<std::string> splitIntoLines(const std::string &str) {
std::vector<std::string> result = {""};
for (char ch: str) {
if (ch == '\n')
result.emplace_back();
else
result.back() += ch;
}
return result;
}
std::string concatenateLines(const std::vector<std::string>& lines) {
std::string result;
size_t n = lines.size();
for (size_t i = 0; i < n; i++) {
if (i)
result += '\n';
result += lines[i];
}
return result;
}
bool is_relevant_in_tab_cut(size_t PN, size_t I, size_t LN, size_t j, const std::string& line) {
if (j == 0 && I != 0)
return false;
if (!is_space_only(line))
return true;
return j + 1 == LN && I + 1 < PN;
}
void one_part_update_min_start_wsp_non_empty(const std::string& str, size_t I, size_t PN, size_t& min) {
std::vector<std::string> lines = splitIntoLines(str);
size_t LN = lines.size();
for (size_t j = 0; j < LN; j++) {
if (is_relevant_in_tab_cut(PN, I, LN, j, lines[j]))
min = std::min(min, first_nw_char(lines[j]));
}
}
std::string one_part_cut_excess_tab(const std::string& str, size_t I, size_t PN, size_t cut) {
std::vector<std::string> lines = splitIntoLines(str);
size_t LN = lines.size();
for (size_t j = 0; j < LN; j++) {
if (is_relevant_in_tab_cut(PN, I, LN, j, lines[j]))
lines[j] = lines[j].substr(cut);
}
return concatenateLines(lines);
}
void parse_bare_file(const std::string& filename, const std::string& content,
global_elem_set_t& result)
{
ASSERT(result.count(filename) == 0, "Repeated element " + filename);
std::string txt = clement_lstrip(content);
rstrip(txt);
size_t cut = 9999999999999;
one_part_update_min_start_wsp_non_empty(txt, 0, 1, cut);
txt = one_part_cut_excess_tab(txt, 0, 1, cut);
Element& el = result[filename];
el.parts = {ElementPart{}};
el.parts[0].when_code.lines = mv(txt);
}
/* Type parsing frame */
struct TPFrame {
json::JSON& result;
explicit TPFrame(json::JSON& result_): result(result_){}
uptr<TPFrame> toMe(bool returned, ParsingContext& ctx) {
if (!returned) {
std::string nm = readName(ctx);
ASSERT(!nm.empty(), "Type specification expected");
nm = make_uppercase(nm);
if (nm == "JSON") {
result = json::JSON(true);
return NULL;
}
ASSERT(nm == "EL", "Type of argument variable is either JSON or EL(...signature)")
skip(ctx, '(');
result = json::JSON(json::array);
assert(result.isArray());
}
skipWhitespace(ctx);
if (peep(ctx) == ')')
return NULL;
result.asArray().emplace_back();
return std::make_unique<TPFrame>(result.asArray().back());
}
};
json::JSON parse_type(ParsingContext& ctx) {
json::JSON result;
std::vector<uptr<TPFrame>> stack;
stack.push_back(mv(std::make_unique<TPFrame>(result)));
bool returned = false;
while (!stack.empty()) {
uptr<TPFrame> ret = stack.back()->toMe(returned, ctx);
returned = !(bool)ret;
if (ret)
stack.push_back(mv(ret));
else
stack.pop_back();
}
return result;
}
/* From arg name to arg ID */
typedef std::map<std::string, size_t> arg_name_list_t;
/* Expression parsing frame */
struct EPFrame {
json::JSON& result;
explicit EPFrame(json::JSON& result_): result(result_){}
uptr<EPFrame> toMe(bool returned, ParsingContext& ctx, const arg_name_list_t& local_var_names) {
if (!returned) {
std::string first = readName(ctx);
ASSERT(!first.empty(), "Expression should start with 'root' name of global package or local variable");
ASSERT(first != "_", "_ ??? ARE YOU KIDDING???");
if (local_var_names.count(first) == 1) {
result["V"] = json::JSON(json::Integer((int64_t)local_var_names.at(first)));
} else {
result["V"] = json::JSON(first);
}
result["C"] = json::JSON(json::array);
} else {
skipWhitespace(ctx);
skip(ctx, ']');
}
std::vector<json::JSON>& chain = result["C"].g().asArray();
while (true) {
if (peep(ctx) == '.') {
skip(ctx, '.');
chain.emplace_back();
std::string t;
t = readName(ctx);
if (!t.empty()) {
chain.back() = json::JSON(t);
continue;
}
t = readUint(ctx);
if (!t.empty()) {
size_t v = std::stoul(t);
ASSERT(v < INT64_MAX, "Index is too big");
chain.back() = json::JSON((int64_t)v);
continue;
}
THROW("Bad expression after . operator in expression");
} else if (peep(ctx) == '[') {
skip(ctx, '[');
skipWhitespace(ctx);
chain.emplace_back();
return std::make_unique<EPFrame>(chain.back());
} else
return NULL;
}
}
};
json::JSON parse_expression(ParsingContext& ctx, const arg_name_list_t& local_var_names) {
json::JSON result;
std::vector<uptr<EPFrame>> stack;
stack.push_back(mv(std::make_unique<EPFrame>(result)));
bool returned = false;
while (!stack.empty()) {
uptr<EPFrame> ret = stack.back()->toMe(returned, ctx, local_var_names);
returned = !(bool)ret;
if (ret)
stack.push_back(mv(ret));
else
stack.pop_back();
}
return result;
}
std::string read_code_up_to_mag_block_start(ParsingContext& ctx, const TemplaterSettings& syntax) {
size_t begin = ctx.pos;
while (peep(ctx) != EOFVAL && peep(ctx) != syntax.magic_block_start[0]) {
skip(ctx);
}
size_t end = ctx.pos;
return ctx.text.substr(begin, end - begin);
}
void skip_magic_block_start(ParsingContext& ctx, const TemplaterSettings& syntax) {
skipString(ctx, syntax.magic_block_start);
skipWhitespace(ctx);
}
void skip_magic_block_end(ParsingContext& ctx, const TemplaterSettings& syntax) {
skipWhitespace(ctx);
skipString(ctx, syntax.magic_block_end);
}
bool isIt_magic_block_end(ParsingContext& ctx, const TemplaterSettings& syntax) {
return peep(ctx) == syntax.magic_block_end[0];
}
/* Element content parsing frame */
struct ECPFrame {
enum block_type{
gone_for_nothing,
gone_for_for,
gone_for_ref,
};
std::string el_name;
block_type myself;
arg_name_list_t local_var_names;
int& ret_data_int; // Received from the top (and passed down to get for_put LF mode value)
Element& result;
block_type stopped_for = gone_for_nothing;
size_t free_hidden = 0;
ECPFrame(const std::string& el_name, block_type myself, const arg_name_list_t &local_var_names, int &ret_data_int,
Element& result)
: el_name(el_name),
myself(myself),
local_var_names(local_var_names),
ret_data_int(ret_data_int),
result(result) {
}
uptr<ECPFrame> toMe(bool returned, ParsingContext& ctx, const TemplaterSettings& syntax, global_elem_set_t& elem_ns) {
if (returned) {
if (stopped_for == gone_for_for) {
assert(result.parts.back().type == ElementPart::p_for_put);
if (ret_data_int == 1)
result.parts.back().when_for_put.line_feed = false;
else if (ret_data_int == 2)
result.parts.back().when_for_put.line_feed = true;
else
assert(false);
} else
assert(ret_data_int == 0);
}
ret_data_int = 0;
ya_e_ya_h_i_ya_g_d_o:
result.parts.emplace_back();
result.parts.back().when_code.lines = read_code_up_to_mag_block_start(ctx, syntax);
skip_magic_block_start(ctx, syntax);
if (isIt_magic_block_end(ctx, syntax)) {
skip_magic_block_end(ctx, syntax);
goto ya_e_ya_h_i_ya_g_d_o;
}
std::string op = make_uppercase(readName(ctx));
if (op == "FOR") {
result.parts.emplace_back();
result.parts.back().type = ElementPart::p_for_put;
ElementPart::when_for_put_S& P = result.parts.back().when_for_put;
skipWhitespace(ctx);
std::string V1 = readName(ctx);
ASSERT(!V1.empty(), "Expected variable name");
skipWhitespace(ctx);
bool have_colon_and_2 = false;
std::string V2;
if (peep(ctx) == ':') {
have_colon_and_2 = true;
skip(ctx, ':');
skipWhitespace(ctx);
V2 = readName(ctx);
skipWhitespace(ctx);
}
op = make_uppercase(readName(ctx));
ASSERT(op == "IN", "Expected IN");
skipWhitespace(ctx);
P.ref_over = parse_expression(ctx, local_var_names);
P.internal_element = el_name + ".~" + std::to_string(free_hidden++);
Element& newborn = elem_ns[P.internal_element];
newborn.is_hidden = true;
arg_name_list_t local_var_names_of_nxt = local_var_names;
if (V1 != "_") {
ASSERT(local_var_names_of_nxt.count(V1) == 0, "Repeated local variable");
size_t k = local_var_names_of_nxt.size();
local_var_names_of_nxt.emplace(V1, k);
(have_colon_and_2 ? P.where_key_var : P.where_value_var) = (ssize_t)k;
}
if (have_colon_and_2 && V2 != "_") {
ASSERT(local_var_names_of_nxt.count(V2) == 0, "Repeated local variable");
size_t k = local_var_names_of_nxt.size();
local_var_names_of_nxt.emplace(V2, k);
P.where_value_var = (ssize_t)k;
}
skip_magic_block_end(ctx, syntax);
/* Yep, I am passing this int random data reference, that was actually given to me, I CAN DO THAT TRUST ME */
stopped_for = gone_for_for;
return std::make_unique<ECPFrame>(P.internal_element, gone_for_for, local_var_names_of_nxt,
ret_data_int, newborn);
}
if (op == "REF") {
result.parts.emplace_back();
result.parts.back().type = ElementPart::p_ref_put;
ElementPart::when_ref_put_S& P = result.parts.back().when_ref_put;
skipWhitespace(ctx);
std::string Vn = readName(ctx);
ASSERT(!Vn.empty(), "Expected variable name");
ASSERT(Vn != "_", "Are you kidding???");
skipWhitespace(ctx);
op = make_uppercase(readName(ctx));
ASSERT(op == "AS", "Expected AS");
skipWhitespace(ctx);
P.ref_over = parse_expression(ctx, local_var_names);
P.internal_element = el_name + ".~" + std::to_string(free_hidden++);
Element& newborn = elem_ns[P.internal_element];
newborn.is_hidden = true;
arg_name_list_t local_var_names_of_nxt = local_var_names;
size_t k = local_var_names_of_nxt.size();
local_var_names_of_nxt.emplace(Vn, k);
skip_magic_block_end(ctx, syntax);
stopped_for = gone_for_ref;
return std::make_unique<ECPFrame>(P.internal_element, gone_for_ref, local_var_names_of_nxt,
ret_data_int, newborn);
}
if (op == "PUT") {
result.parts.emplace_back();
result.parts.back().type = ElementPart::p_put;
ElementPart::when_put_S& P = result.parts.back().when_put;
skipWhitespace(ctx);
P.called_element = parse_expression(ctx, local_var_names);
while (true) {
skipWhitespace(ctx);
if (isIt_magic_block_end(ctx, syntax)) {
skip_magic_block_end(ctx, syntax);
break;
}
P.passed_arguments.push_back(parse_expression(ctx, local_var_names));
}
goto ya_e_ya_h_i_ya_g_d_o;
}
auto mediocre_operator = [&](const std::string& base_el) -> void {
result.parts.emplace_back();
result.parts.back().type = ElementPart::p_put;
ElementPart::when_put_S& P = result.parts.back().when_put;
P.called_element["V"] = json::JSON(base_el);
P.called_element["C"] = json::JSON(json::array);
skipWhitespace(ctx);
P.passed_arguments = {parse_expression(ctx, local_var_names)};
skip_magic_block_end(ctx, syntax);
};
if (op == "WRITE") {
mediocre_operator("str2text");
goto ya_e_ya_h_i_ya_g_d_o;;
}
if (op == "ROUGHINSERT") {
mediocre_operator("str2code");
goto ya_e_ya_h_i_ya_g_d_o;;
}
auto prepare_to_depart_parts = [&]() {
assert(!result.parts.empty());
if (result.parts[0].type == ElementPart::p_code)
result.parts[0].when_code.lines = clement_lstrip(result.parts[0].when_code.lines);
if (result.parts.back().type == ElementPart::p_code)
rstrip(result.parts.back().when_code.lines);
size_t cut = 999999999999;
size_t N = result.parts.size();
for (size_t i = 0; i < N; i++) {
if (result.parts[i].type == ElementPart::p_code) {
one_part_update_min_start_wsp_non_empty(result.parts[i].when_code.lines, i, N, cut);
}
}
for (size_t i = 0; i < N; i++) {
if (result.parts[i].type == ElementPart::p_code) {
result.parts[i].when_code.lines = one_part_cut_excess_tab(result.parts[i].when_code.lines, i, N, cut);
}
}
};
if (op == "ENDELDEF") {
ASSERT(myself == gone_for_nothing, "Unexpected end of element");
skip_magic_block_end(ctx, syntax);
prepare_to_depart_parts();
return NULL;
}
if (op == "ENDFOR") {
ASSERT(myself == gone_for_for, "Unexpected end of for cycle");
skipWhitespace(ctx);
/* Here I am using ret_data_int to return info about NOLF(1)/LF(2) decision */
ret_data_int = 2; // Default is to do LF
if (!isIt_magic_block_end(ctx, syntax)) {
op = make_uppercase(readName(ctx));
if (op == "LF") {
ret_data_int = 2;
} else if (op == "NOLF") {
ret_data_int = 1;
} else
THROW("Expected LF, NOLF or end of magic block");
}
skip_magic_block_end(ctx, syntax);
prepare_to_depart_parts();
return NULL;
}
if (op == "ENDREF") {
assert(myself == gone_for_ref);
skip_magic_block_end(ctx, syntax);
prepare_to_depart_parts();
return NULL;
}
THROW("Unknown operator. Expected FOR, REF, PUT, WRITE, ROUGHINSERT, ENDELDEF, ENDFOR, ENDREF");
}
};
void parse_element_content(const std::string& el_name, ParsingContext& ctx, const TemplaterSettings& syntax,
const arg_name_list_t& local_var_names, Element& result, global_elem_set_t& elem_ns) {
int random_junk; // Used onlt for for_put blocks
std::vector<uptr<ECPFrame>> stack;
stack.push_back(mv(std::make_unique<ECPFrame>(el_name, ECPFrame::gone_for_nothing, local_var_names, random_junk, result)));
bool returned = false;
while (!stack.empty()) {
uptr<ECPFrame> ret = stack.back()->toMe(returned, ctx, syntax, elem_ns);
returned = !(bool)ret;
if (ret)
stack.push_back(mv(ret));
else
stack.pop_back();
}
}
void parse_special_file(const std::string& filename, const std::string& content,
global_elem_set_t& result, TemplaterSettings& syntax)
{
ParsingContext ctx{content};
while(true) {
skipWhitespace(ctx);
if (peep(ctx) == EOFVAL)
break;
skip_magic_block_start(ctx, syntax);
ASSERT(make_uppercase(readName(ctx)) == "ELDEF", "Expected ELDEF");
skipWhitespace(ctx);
std::string elname_postfix = readName(ctx);
ASSERT(elname_postfix != "_", "please don't");
std::string fullname = elname_postfix == "main" ? filename : filename + "." + elname_postfix;
ASSERT(result.count(fullname) == 0, "Element " + fullname + " has been already defined");
Element& newborn = result[fullname];
arg_name_list_t arglist;
while (true) {
skipWhitespace(ctx);
if (isIt_magic_block_end(ctx, syntax))
break;
newborn.arguments.push_back(parse_type(ctx));
skipWhitespace(ctx);
std::string argname = readName(ctx);
ASSERT(!argname.empty(), "Expected argument name");
if (argname != "_") {
ASSERT(arglist.count(argname) == 0, "Repeated argument (" + argname + ")");
size_t k = arglist.size();
arglist[argname] = k;
}
}
skip_magic_block_end(ctx, syntax);
parse_element_content(fullname, ctx, syntax, arglist, newborn, result);
}
}
}

View File

@ -0,0 +1,359 @@
#include "core.h"
#include "alotalot.h"
#include <string.h>
#include <jsonincpp/string_representation.h>
#include <assert.h>
namespace nytl {
struct LocalVarValue {
bool is_json = false;
std::string EL_name;
const json::JSON* JSON_subval = NULL;
};
/* Expression Execution Frame */
struct EEFrame {
const json::JSON& expr;
LocalVarValue& result;
LocalVarValue temp_ret;
size_t chain_el = 0;
EEFrame(const json::JSON &expr, LocalVarValue &result)
: expr(expr),
result(result) {
}
void descend(const json::JSON& what) {
if (result.is_json) {
const json::JSON& P = *result.JSON_subval;
if (P.isArray() && what.isInteger()) {
const std::vector<json::JSON>& arr_p = P.asArray();
int64_t ind_w = what.asInteger().get_int();
ASSERT(ind_w > 0 && ind_w < arr_p.size(), "Expression \"array[integer]\" caused out-of-bound situation");
result = LocalVarValue{true, "", &arr_p[ind_w]};
} else if (P.isDictionary() && what.isString()) {
const std::map<std::string, json::JSON>& dict_p = P.asDictionary();
const std::string& key_w = what.asString();
ASSERT(dict_p.count(key_w) == 1, "No such key exception");
result = LocalVarValue{true, "", &dict_p.at(key_w)};
} else
THROW("Incorrect type of \"json[json]\" expression. Unallowed signature of [] operator");
} else {
ASSERT(what.isString(), "Expression \"element[X]\" allowed only if X is string (json object)");
if (what.asString().empty())
return;
if (!is_uname_dotted_sequence(what.asString()))
THROW("Incorrect X in \"element[X]\"");
result.EL_name += ("." + what.asString());
}
}
uptr<EEFrame> toMe(bool returned, const global_elem_set_t& global_elems,
const std::vector<LocalVarValue>& local_vars) {
if (returned) {
ASSERT(temp_ret.is_json, "Expression \"X[ element ]\" is not allowed");
assert(temp_ret.JSON_subval);
descend(*(temp_ret.JSON_subval));
} else {
assert(expr.isDictionary());
const json::JSON& val = expr["V"].g();
if (val.isInteger()) {
size_t lv_ind = val.asInteger().get_int();
assert(lv_ind < local_vars.size());
result = local_vars[lv_ind];
} else if (val.isString()) {
std::string cur_el_name_str = expr["V"].g().asString();
result = LocalVarValue{false, cur_el_name_str, NULL};
} else
assert(false);
}
const std::vector<json::JSON>& chain = expr["C"].g().asArray();
while (true) {
if (chain_el >= chain.size())
return NULL;
const json::JSON& t = chain[chain_el++];
if (t.isDictionary())
return std::make_unique<EEFrame>(t, temp_ret);
descend(t);
}
}
};
/* No new JSON object will ever be created, I have N root json arguments,
* all the other json variables are subtrees of them. With one exception: Key iterators for arrays
* and dictionaries. They are stored in json in rendering stack */
LocalVarValue rendering_core_execute_expression(const global_elem_set_t& global_elems,
const std::vector<LocalVarValue>& local_vars, const json::JSON& expr) {
bool returned = false;
std::vector<uptr<EEFrame>> stack;
LocalVarValue result;
stack.push_back(std::make_unique<EEFrame>(expr, result));
while (!stack.empty()) {
EEFrame& cur = *stack.back();
uptr<EEFrame> todo = cur.toMe(returned, global_elems, local_vars);
returned = !(bool)todo;
if (todo)
stack.push_back(mv(todo));
else
stack.pop_back();
}
return result;
}
struct Ditch {
std::string result;
size_t cur_line_width = 0;
/* Fix idea: get rid of newlined_somewhere */
void append(const std::string& text, size_t wsp_before_newlines, bool& newlined_somewhere) {
size_t n = result.size();
size_t m = text.size();
result.reserve(n + m);
for (size_t i = 0; i < m; i++) {
result += text[i];
if (text[i] == '\n') {
// newlined_somewhere = true;
result.resize(result.size() + wsp_before_newlines, ' ');
cur_line_width = wsp_before_newlines;
} else {
// if (cur_line_width == 0 && newlined_somewhere) {
// result.resize(result.size() + wsp_before_newlines, ' ');
// cur_line_width = wsp_before_newlines;
// }
cur_line_width++;
}
}
}
};
#define RFrame_passed const global_elem_set_t& elem_ns, Ditch& result, const std::function<std::string(std::string)>& escape
/* Rendering Frame */
struct RFrame {
size_t wsp_before_newlines = 0;
bool newlined_somewhere = false;
void append(const std::string& text, Ditch& ditch) {
ditch.append(text, wsp_before_newlines, newlined_somewhere);
}
explicit RFrame(size_t multiline_put_start)
: wsp_before_newlines(multiline_put_start) {
}
virtual uptr<RFrame> toMe(bool returned, RFrame_passed) {assert(false);}
virtual ~RFrame() = default;
};
struct RFrame_OverParts : public RFrame{
std::string name;
std::vector<LocalVarValue> passed_args;
/* This parameter incapsulates `cur_line_width` at some point for multiline `put-parts` */
/* main iterator of this frame. Persistent across control returns */
size_t part_to_do = 0;
RFrame_OverParts(const std::string &name, const std::vector<LocalVarValue>& passed_args,
size_t multiline_put_start)
: RFrame(multiline_put_start), name(name),
passed_args(passed_args) {
}
uptr<RFrame> toMe(bool returned, RFrame_passed) override;
};
struct RFrame_OverJSON: public RFrame {
const ElementPart::when_for_put_S& part;
/* During the course of iteration execution, given arg list will expand and shrink back */
std::vector<LocalVarValue> saved_args_plus_iter;
RFrame_OverJSON(const ElementPart::when_for_put_S &part, size_t multiline_put_start,
const std::vector<LocalVarValue> &saved_args)
: RFrame(multiline_put_start), part(part),
saved_args_plus_iter(saved_args) {
if (part.where_key_var > -1)
this->saved_args_plus_iter.emplace_back();
if (part.where_value_var > -1)
this->saved_args_plus_iter.emplace_back();
}
};
struct RFrame_OverArray : public RFrame_OverJSON {
const std::vector<json::JSON>& arr;
size_t it = 0;
/* Crutch. I can't pass simple integer as nytl local variable, I need persistent json wrapper */
json::JSON additional_json_wrapper;
RFrame_OverArray(const ElementPart::when_for_put_S& part, size_t multiline_put_start, const std::vector<LocalVarValue> &saved_args,
const std::vector<json::JSON> &arr): RFrame_OverJSON(part, multiline_put_start, saved_args),
arr(arr) {
if (part.where_key_var >= 0)
additional_json_wrapper = json::JSON(json::Integer(0l));
}
uptr<RFrame> toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result,
const std::function<std::string(std::string)> &escape) override;
};
struct RFrame_OverDictionary: public RFrame_OverJSON {
const std::map<std::string, json::JSON>& dict;
std::map<std::string, json::JSON>::const_iterator it;
/* Crutch */
json::JSON addition_json_wrapper;
RFrame_OverDictionary(const ElementPart::when_for_put_S& part, size_t multiline_put_start, const std::vector<LocalVarValue> &saved_args_plus_iter,
const std::map<std::string, json::JSON> &dict): RFrame_OverJSON(part, multiline_put_start, saved_args_plus_iter),
dict(dict) {
it = dict.begin();
if (part.where_key_var >= 0)
addition_json_wrapper = json::JSON("");
}
uptr<RFrame> toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result,
const std::function<std::string(std::string)> &escape) override;
};
/* Rendering Frame */
uptr<RFrame> RFrame_OverParts::toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result,
const std::function<std::string(std::string)> &escape) {
if (!returned)
ASSERT(elem_ns.count(name) == 1, "No such element");
const Element& el = elem_ns.at(name);
if (!returned) {
/* Continue to do checks */
/* hidden elements (internal) do not need any check */
if (!el.is_hidden) {
size_t n = el.arguments.size();
ASSERT(n == passed_args.size(), "Argument count mismatch");
for (size_t i = 0; i < n; i++) {
if (el.arguments[i].type == json::true_symbol) {
ASSERT(passed_args[i].is_json, "Expected json element argument, got element");
} else {
// If not json is expected, element must be expected
assert(el.arguments[i].isArray());
ASSERT(!passed_args[i].is_json, "Expected element element arguemnt, got json");
ASSERT(elem_ns.count(passed_args[i].EL_name), "No such element, can't compare signatures of argument value");
const Element& arg_element = elem_ns.at(passed_args[i].EL_name);
// ASSERT(passed_args);
if(el.arguments[i].asArray() != arg_element.arguments)
THROW("Signature of argument " + std::to_string(i) + " does not match");
}
}
}
}
if (el.base) {
assert(!returned);
assert(passed_args.size() == 1);
const json::JSON* X = passed_args[0].JSON_subval;
assert(X);
if (name == "jesc") {
std::string escaped_json = escape(json::generate_str(*X, json::print_pretty));
rstrip(escaped_json);
append(escaped_json, result);
} else if (name == "jesccomp") {
append(escape(json::generate_str(*X, json::print_compact)), result);
} else if (name == "str2text") {
ASSERT(X->isString(), "str2text takes json string");
append(escape(X->asString()), result);
} else if (name == "str2code") {
ASSERT(X->isString(), "str2code takes json string");
append(X->asString(), result);
}
return NULL;
}
while (true) {
if (part_to_do == el.parts.size())
return NULL;
const ElementPart& cur_part = el.parts[part_to_do++];
if (cur_part.type == ElementPart::p_code) {
const ElementPart::when_code_S& pt = cur_part.when_code;
append(pt.lines, result);
} else if (cur_part.type == ElementPart::p_put) {
const ElementPart::when_put_S& pt = cur_part.when_put;
LocalVarValue called_element_expv = rendering_core_execute_expression(elem_ns, passed_args, pt.called_element);
ASSERT(!called_element_expv.is_json, "Can't PUT json variable");
size_t AN = pt.passed_arguments.size();
std::vector<LocalVarValue> passed_arguments_expv(AN);
for (size_t i = 0; i < AN; i++)
passed_arguments_expv[i] = rendering_core_execute_expression(elem_ns, passed_args, pt.passed_arguments[i]);
return std::make_unique<RFrame_OverParts>(called_element_expv.EL_name, passed_arguments_expv,
result.cur_line_width);
} else if (cur_part.type == ElementPart::p_for_put) {
const ElementPart::when_for_put_S& pt = cur_part.when_for_put;
LocalVarValue iting_over = rendering_core_execute_expression(elem_ns, passed_args, pt.ref_over);
ASSERT(iting_over.is_json, "Can't iterate over element");
const json::JSON& container = *iting_over.JSON_subval;
if (container.isArray()) {
return std::make_unique<RFrame_OverArray>(pt, result.cur_line_width, passed_args, container.asArray());
} else if (container.isDictionary()) {
return std::make_unique<RFrame_OverDictionary>(pt, result.cur_line_width, passed_args, container.asDictionary());
} else
THROW("Can't iterate over non-natalistic jsobject");
} else if (cur_part.type == ElementPart::p_ref_put) {
const ElementPart::when_ref_put_S& pt = cur_part.when_ref_put;
std::vector<LocalVarValue> more_variables(passed_args.size() + 1);
std::copy(passed_args.begin(), passed_args.end(), more_variables.begin());
more_variables.back() = rendering_core_execute_expression(elem_ns, passed_args, pt.ref_over);
return std::make_unique<RFrame_OverParts>(pt.internal_element, more_variables, result.cur_line_width);
}
}
}
uptr<RFrame> RFrame_OverArray::toMe(bool returned, RFrame_passed) {
if (it >= arr.size())
return NULL;
if (returned && part.line_feed)
append("\n", result);
if (part.where_key_var > -1) {
additional_json_wrapper.asInteger() = json::Integer((int64_t)it);
saved_args_plus_iter[part.where_key_var] = {true, "", &additional_json_wrapper};
}
if (part.where_value_var > -1) {
saved_args_plus_iter[part.where_value_var] = {true, "", &(arr[it])};
}
it++;
return std::make_unique<RFrame_OverParts>(part.internal_element, saved_args_plus_iter, wsp_before_newlines);
}
uptr<RFrame> RFrame_OverDictionary::toMe(bool returned, RFrame_passed) {
if (it == dict.end())
return NULL;
if (returned && part.line_feed)
append("\n", result);
if (part.where_key_var > -1) {
addition_json_wrapper.asString() = it->first;
saved_args_plus_iter[part.where_key_var] = {true, "", &addition_json_wrapper};
}
if (part.where_value_var > -1) {
saved_args_plus_iter[part.where_value_var] = {true, "", &it->second};
}
++it;
return std::make_unique<RFrame_OverParts>(part.internal_element, saved_args_plus_iter, wsp_before_newlines);
}
std::string rendering_core(const std::string& entry_func, const std::vector<const json::JSON*>& entry_arguments,
const global_elem_set_t& elem_ns, const std::function<std::string(std::string)>& escape)
{
Ditch result;
std::vector<uptr<RFrame>> stack;
{
size_t AN = entry_arguments.size();
std::vector<LocalVarValue> entry_arguments_conv(AN);
for (size_t i = 0; i < AN; i++)
entry_arguments_conv[i] = {true, "", entry_arguments[i]};
stack.push_back(std::make_unique<RFrame_OverParts>(entry_func, entry_arguments_conv, 0));
}
bool returned = false;
while (!stack.empty()) {
uptr<RFrame> ret = stack.back()->toMe(returned, elem_ns, result, escape);
returned = !(bool)ret;
if (ret)
stack.push_back(mv(ret));
else
stack.pop_back();
}
assert(returned);
return result.result;
}
}

View File

@ -0,0 +1,137 @@
#include "templater.h"
#include <sys/stat.h>
#include <dirent.h>
#include "alotalot.h"
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "core.h"
namespace nytl {
/* Throws std::runtime_exception on incorrect settings */
void check_settings(const TemplaterSettings& settings) {
#define lmao_serving_kid_with_identity_issues throw std::runtime_error("What was wrong with {% %} ????")
if (settings.magic_block_start.empty() || settings.magic_block_end.empty())
lmao_serving_kid_with_identity_issues;
char incode = settings.magic_block_start[0];
if (isSPACE(incode) || isALPHA(incode) || isNUM(incode))
lmao_serving_kid_with_identity_issues;
char ender = settings.magic_block_end[0];
if (isUNCHAR(ender) || ender == ':' || isSPACE(ender) || ender == '[' || ender == ']' || ender == '.')
lmao_serving_kid_with_identity_issues;
}
Templater::Templater(TemplaterSettings settings): settings(std::move(settings)) {
check_settings(this->settings);
}
struct InterestingFile {
std::string path;
std::string dot_name;
bool special_syntax_applied;
};
std::vector<InterestingFile> indexing_detour(const TemplaterDetourRules& rules) {
std::vector<InterestingFile> result;
int ret;
std::vector<std::string> todo;
todo.emplace_back();
while (!todo.empty()) {
std::string cur = mv(todo.back());
todo.pop_back();
std::string path_to_cur_dir = rules.root_dir_path + "/" + cur;
DIR* D = opendir(path_to_cur_dir.c_str());
struct Guard1{ DIR*& D; ~Guard1(){ closedir(D); } } g1{D};
ASSERT(D != NULL, prettyprint_errno("opendir(\"" + cur +"\")"));
while (true) {
errno = 0;
struct dirent* Dent = readdir(D);
if (Dent == NULL) {
if (errno == 0)
break;
THROW_on_errno("dirent in \"" + cur + "\"");
}
std::string child_entry = Dent->d_name;
if (child_entry == "." || child_entry == "..")
continue;
std::string path_to_cur_child = path_to_cur_dir + "/" + child_entry;
struct stat info;
ret = stat(path_to_cur_child.c_str(), &info);
ASSERT_on_iret(ret, "stat(" + path_to_cur_child + ")");
if (S_ISDIR(info.st_mode)) {
if (isUname(child_entry))
todo.push_back(cur.empty() ? child_entry : cur + "/" + child_entry);
} else if (S_ISREG(info.st_mode)) {
auto replace_sep = [](const std::string& slashed) -> std::string {
std::string dotted;
dotted.reserve(slashed.size());
for (char ch: slashed) {
if (ch == '/')
dotted += '.';
else
dotted += ch;
}
return dotted;
};
auto np_reg_categ_result = [&](const std::string& no_postfix, bool applied) {
if (isUname(no_postfix))
result.push_back({path_to_cur_child, replace_sep(cur.empty() ? no_postfix : cur + "/" + no_postfix), applied});
};
if (endsIn(child_entry, rules.postfix_rule_for_element_cont)) {
np_reg_categ_result(throwout_postfix(child_entry, rules.postfix_rule_for_element_cont.size()), true);
} else if (endsIn(child_entry, rules.postfix_rule_for_static_files)) {
np_reg_categ_result(throwout_postfix(child_entry, rules.postfix_rule_for_static_files.size()), false);
}
} else {
THROW("unknown fs entry type \"" + cur + "\"");
}
}
}
return result;
}
std::string readFile(const std::string& path) {
std::string result;
int ret;
int fd = open(path.c_str(), O_RDONLY);
ASSERT_on_iret(fd, "Opening \"" + path + "\"");
char buf[2048];
while ((ret = (int)read(fd, buf, 2048)) > 0) {
size_t oldN = result.size();
result.resize(oldN + ret);
memcpy((void*)&result.c_str()[oldN], buf, ret);
}
if (ret < 0) {
close(fd);
THROW("reading file");
}
return result;
}
void Templater::update() {
elements = {
{"jesc", Element{{json::JSON(true)}, true}},
{"jesccomp", Element{{json::JSON(true)}, true}},
/* str2text base element has a dedicated operator - WRITE */
{"str2text", Element{{json::JSON(true)}, true}},
/* str2code base element has a dedicated operator - ROUGHINSERT */
{"str2code", Element{{json::JSON(true)}, true}},
};
std::vector<InterestingFile> intersting_files = indexing_detour(settings.det);
for (const InterestingFile& file: intersting_files) {
std::string content = readFile(file.path);
if (file.special_syntax_applied) {
parse_special_file(file.dot_name, content, elements, settings);
} else {
parse_bare_file(file.dot_name, content, elements);
}
}
}
/* Still can throw some stuff derived from std::exception (like bad alloc) */
std::string Templater::render(const std::string& element, const std::vector<const json::JSON*> &arguments) const {
ASSERT(is_uname_dotted_sequence(element), "Incorrect entry element name");
return rendering_core(element, arguments, elements, settings.escape);
}
}

View File

@ -0,0 +1,81 @@
#ifndef NEW_YORK_TRANSIT_LINE_TEMPLATER_H
#define NEW_YORK_TRANSIT_LINE_TEMPLATER_H
#include <vector>
#include <string>
#include <jsonincpp/jsonobj.h>
#include <functional>
#include "html_case.h"
namespace nytl {
typedef json::JSON expression_t;
struct ElementPart {
/* Used with all types */
enum element_part_type_E {
p_code,
/* write statements really mean PUT str2text X */
p_put,
p_for_put,
p_ref_put
} type = p_code;
struct when_code_S {
std::string lines;
} when_code;
struct when_put_S {
expression_t called_element;
std::vector<expression_t> passed_arguments;
} when_put;
struct when_for_put_S {
expression_t ref_over;
ssize_t where_key_var = -1;
ssize_t where_value_var = -1;
std::string internal_element;
bool line_feed = true;
} when_for_put;
struct when_ref_put_S {
expression_t ref_over;
std::string internal_element;
} when_ref_put;
};
struct Element {
/* Stores signature of element */
std::vector<json::JSON> arguments;
/* `base` is true for builtin elements (jesc str2code str2text). Parts for such ' are empty */
bool base = false;
bool is_hidden = false;
std::vector<ElementPart> parts;
};
struct TemplaterDetourRules {
std::string root_dir_path;
std::string postfix_rule_for_element_cont = ".nytl.html";
std::string postfix_rule_for_static_files = ".html";
};
struct TemplaterSettings {
TemplaterDetourRules det;
std::string magic_block_start = "{%";
std::string magic_block_end = "%}";
std::function<std::string(std::string)> escape = html_case_espace_string;
};
typedef std::map<std::string, Element> global_elem_set_t;
struct Templater {
TemplaterSettings settings;
global_elem_set_t elements;
explicit Templater(TemplaterSettings settings);
/* Throws exception, derived from std::exception */
void update();
/* Throws exception, derived from std::exception */
std::string render(const std::string& element, const std::vector<const json::JSON*>& arguments) const;
};
}
#endif

View File

@ -0,0 +1,4 @@
AAA
{% FOR _:val IN cba %}
TUTUTUTUTUTUTUTUN {% PUT jesccomp val %}
{% ENDFOR %}

View File

@ -0,0 +1,8 @@
{% ELDEF main JSON cba %}
AAA
{% FOR val IN cba.arr %}
--> {% WRITE val %}
{% ENDFOR %}
AAA
{% ENDELDEF %}

View File

@ -0,0 +1,29 @@
#include <jsonincpp/string_representation.h>
#include <new_york_transit_line/templater.h>
#include <new_york_transit_line/core.h>
int main(int argc, char** argv) {
if (argc < 2) {
fprintf(stderr, "Usage: test assets_dir");
exit(1);
}
std::string dir_path = argv[1];
nytl::Templater templater(nytl::TemplaterSettings{nytl::TemplaterDetourRules{dir_path}});
templater.update();
nytl::debug_print_templater(templater);
json::JSON cba;
cba["boba"] = json::JSON("<>");
cba["arr"][0] = json::JSON("zero");
cba["arr"][1] = json::JSON("one");
cba["arr"][2] = json::JSON("two");
cba["k"] = json::JSON("arr");
cba["i"] = json::JSON(1l);
// printf("DEBUG WAS: %p\n", &cba["boba"].g());
// printf("%s\n", json::generate_str(cba["boba"].g(), json::print_compact).c_str());
// return 0;
std::string answer2 = templater.render("test", {&cba});
printf("%s\n<a><f><t><e><r><><l><f>\n", answer2.c_str());
return 0;
}

9
src/web_chat/actions.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef IU9_CA_WEB_CHAT_ACTIONS_H
#define IU9_CA_WEB_CHAT_ACTIONS_H
#include <jsonincpp/jsonobj.h>
void run_website(const json::JSON& config);
void initialize_website(const json::JSON& config, const std::string& root_pw);
#endif

View File

@ -0,0 +1,5 @@
#include "actions.h"
void initialize_website(const json::JSON& config, const std::string& root_pw) {
}

View File

@ -1,108 +1,39 @@
#include <engine_engine_number_9/baza_throw.h>
#include <engine_engine_number_9/running_mainloop.h>
#include <engine_engine_number_9/http_structures/response_gen.h>
#include <signal.h>
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
#include <engine_engine_number_9/os_utils.h>
#include <assert.h>
#include <sqlite3.h>
#include <libjsonincpp/string_representation.h>
#include <libregexis024vm/vm_opcodes.h>
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
bool termination = false;
void sigterm_action(int) {
termination = true;
}
#include <jsonincpp/string_representation.h>
#include "actions.h"
void usage(char** argv) {
printf("Usage: %s <file with settings> <assets folder>\n", argv[0]);
printf("Usage: %s <file with settings>\n", argv[0]);
exit(1);
}
std::string unsafe_client_request_stringification(const een9::ClientRequest& req) {
std::string text = "\n\nGot some cool stuff\n";
text += (req.method + " " + req.uri_path + " " + req.http_version + "\n");
for (auto& p: req.headers) {
text += p.first; text += ": "; text += p.second; text += "\n";
}
text += "Body\n"; text += req.body; text += "\n";
return text;
}
int main(int argc, char** argv){
printf("%s\n", regexis024::opcode_to_str(regexis024::opcode_t::DIE));
try {
een9_ASSERT_pl(argc > 0);
if (argc < 1 + 2)
if (argc != 1 + 2)
usage(argv);
if (!een9::isRegularFile(argv[1]) || !een9::endsIn(argv[1], ".json")) {
std::string config_file = argv[1];
if (!een9::isRegularFile(config_file) || !een9::endsIn(config_file, ".json")) {
printf("\"%s\" is not a json file\n", argv[1]);
usage(argv);
}
std::string config_file = argv[1];
if (!een9::isDirectory(argv[2])) {
printf("\"%s\" is not a directory\n", argv[2]);
usage(argv);
}
std::string assets_dir = argv[2];
std::string cmd = argv[2];
std::string config_text;
een9::readFile(config_file, config_text);
json::JSON config = json::parse_str_flawless(config_text);
een9_ASSERT(config.isDictionary(), "config root is not dictionary");
const json::JSON config = json::parse_str_flawless(config_text);
een9::StaticAssetManagerSlaveModule samI;
samI.update({
een9::StaticAssetManagerRule{assets_dir + "/html", "/assets/html", {{".html", "text/html"}} },
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/js"}} },
een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", {
{".jpg", "image/jpg"}, {".png", "image/png"}, {".svg", "image/svg+xml"}
} },
});
een9::MainloopParameters params;
params.guest_core = [&samI](const een9::SlaveTask& task, const een9::ClientRequest& req) -> std::string {
een9::StaticAsset sa;
int ret;
// printf("%s", unsafe_client_request_stringification(req).c_str());
if (req.uri_path == "/output") {
std::string text = unsafe_client_request_stringification(req);
return een9::form_http_server_response_200("text/plain", text);
}
auto rteee = [&](const std::string& asset_path) -> std::string {
ret = samI.get_asset(asset_path, sa);
een9_ASSERT_pl(ret == 0);
return een9::form_http_server_response_200(sa.type, sa.content);
};
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
return rteee("/assets/html/list-rooms.html");
}
if (req.uri_path == "/chat") {
return rteee("/assets/html/chat.html");
}
if (req.uri_path == "/profile") {
return rteee("/assets/html/profile.html");
}
if (req.uri_path == "/registration") {
return rteee("/assets/html/registration.html");
}
/* Trying to interpret request as asset lookup */
ret = samI.get_asset(req.uri_path, sa);
if (ret >= 0) {
return een9::form_http_server_response_200(sa.type, sa.content);
}
return een9::form_http_server_response_404("text/html", "<h1> Not found! </h1>");
};
params.ports_to_listen = {1025};
params.slave_number = 8;
params.open_admin_listener = false;
signal(SIGINT, sigterm_action);
signal(SIGTERM, sigterm_action);
een9::electric_boogaloo(params, termination);
if (cmd == "initialize") {
const char* ROOT_PW = getenv("ROOT_PW");
een9_ASSERT(ROOT_PW, "No root password specified."
"Assign desired root password value to environment variable ROOT_PW");
std::string root_pw = ROOT_PW;
initialize_website(config, root_pw);
} else if (cmd == "run") {
run_website(config);
} else
een9_THROW("unknown action (known are 'run', 'initialize')");
} catch (std::exception& e) {
printf("System failure\n%s\n", e.what());
}

103
src/web_chat/run.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "actions.h"
#include <engine_engine_number_9/baza_throw.h>
#include <engine_engine_number_9/running_mainloop.h>
#include <engine_engine_number_9/http_structures/response_gen.h>
#include <signal.h>
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
#include <assert.h>
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
#include <new_york_transit_line/templater.h>
#include <sqlite3.h>
bool termination = false;
void sigterm_action(int) {
termination = true;
}
std::string unsafe_client_request_stringification(const een9::ClientRequest& req) {
std::string text = "\n\nGot some cool stuff\n";
text += (req.method + " " + req.uri_path + " " + req.http_version + "\n");
for (auto& p: req.headers) {
text += p.first; text += ": "; text += p.second; text += "\n";
}
text += "Body\n"; text += req.body; text += "\n";
return text;
}
void run_website(const json::JSON& config) {
een9_ASSERT(config["assets"].g().isString(), "config[\"\assets\"] is not string");
std::string assets_dir = config["assets"].g().asString();
een9_ASSERT(een9::isDirectory(assets_dir), "\"" + assets_dir + "\" is not a directory");
een9::StaticAssetManagerSlaveModule samI;
samI.update({
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/js"}} },
een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", {
{".jpg", "image/jpg"}, {".png", "image/png"}, {".svg", "image/svg+xml"}
} },
});
const json::JSON& config_presentation = config["presentation"].g();
/* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
std::vector<std::unique_ptr<nytl::Templater>> templaters_copies(8);
for (int i = 0; i < 8; i++) {
templaters_copies[i] = std::make_unique<nytl::Templater>(
nytl::TemplaterSettings{nytl::TemplaterDetourRules{assets_dir + "/HypertextPages"}});
templaters_copies[i]->update();
}
// printf("%s\n", templaters_copies[0]->render("list-rooms", {&config_presentation}).c_str());
// return 0;
een9::MainloopParameters params;
params.guest_core = [&samI, &templaters_copies, config_presentation]
(const een9::SlaveTask& task, const een9::ClientRequest& req, een9::worker_id_t worker_id) -> std::string {
een9_ASSERT_pl(0 <= worker_id && worker_id < templaters_copies.size());
nytl::Templater& templater = *templaters_copies[worker_id];
een9::StaticAsset sa;
int ret;
// printf("%s", unsafe_client_request_stringification(req).c_str());
// if (req.uri_path == "/output") {
// std::string text = unsafe_client_request_stringification(req);
// return een9::form_http_server_response_200("text/plain", text);
// }
auto rteee = [&](const std::string& el_name, bool pass_phr) -> std::string {
std::string page = templater.render(el_name,
pass_phr ? std::vector<const json::JSON*>{&config_presentation} : std::vector<const json::JSON*>{});
return een9::form_http_server_response_200("text/html", page);
};
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
return rteee("list-rooms", true);
}
if (req.uri_path == "/chat") {
return rteee("chat", false);
}
if (req.uri_path == "/profile") {
return rteee("profile", false);
}
if (req.uri_path == "/registration") {
return rteee("registration", false);
}
/* Trying to interpret request as asset lookup */
ret = samI.get_asset(req.uri_path, sa);
if (ret >= 0) {
return een9::form_http_server_response_200(sa.type, sa.content);
}
return een9::form_http_server_response_404("text/html", "<h1> Not found! </h1>");
};
params.ports_to_listen = {1025};
params.slave_number = 8;
params.open_admin_listener = false;
signal(SIGINT, sigterm_action);
signal(SIGTERM, sigterm_action);
een9::electric_boogaloo(params, termination);
}