Skip to content

Instantly share code, notes, and snippets.

@colesnicov
Last active September 22, 2019 16:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save colesnicov/1ea2cead3cb7b10e45c0143c5e2926dc to your computer and use it in GitHub Desktop.
Save colesnicov/1ea2cead3cb7b10e45c0143c5e2926dc to your computer and use it in GitHub Desktop.
TemplateEngine is a system for implementing dynamic templates in real time. Fields of application: Web (processing HTML, CSS and JS file) or any other application where it is necessary to replace the keys in the text or insert into it other blocks of text. Constructions are supported text replacement (replace) or inserting another (include).
/**
* file.cpp
* This file is a part of The TemplateEngine system.
*
* Author: Denis Colesnicov <eugustus@gmail.com>
* Copyright: 09.2019
* Licence: MIT
*
*/
#include "file.hpp"
#include <cstdlib>
#include <cstring>
/**
* Slouzi k lepsi citelnosti v konzoli.
*/
#define TAG (char*)"FILE.CPP"
/**
* Znak konce radku.
*/
#define EOL '\n'
FILE* openFile(char* _path)
{
FILE *file = fopen(_path, "r");
if (file == NULL)
{
printf("E:%s: Could not open file: '%s'!\n", TAG, _path);
return file;
}
return file;
}
void closeFile(FILE* _file)
{
if (_file != NULL)
{
fclose(_file);
}
}
char* readNext(FILE* _file, char* _temp, int &_len, bool _skip_eol)
{
int ch = 0;
char *ret = (char*) calloc(3, sizeof(char*));
_len = 0;
int iter = 0;
if (strlen(_temp) == 2)
{
ret[0] = _temp[1];
iter = 1;
} else if (strlen(_temp) == 1)
{
ret[0] = _temp[0];
iter = 1;
}
do
{
if (iter == 2)
// Dosazen potrebny pocet znaku.
// Konec smycky.
{
break;
}
ch = fgetc(_file);
if (ch == EOF)
// konec souboru
{
break;
}
if (ch == EOL && _skip_eol)
// @todo Pozor, nici strukturu dokumentu, odstranuje znacku skoku na novy radek!
// konec radku, prestup na dalsi/ nacteni dalsiho radku.
{
continue;
}
ret[iter] = (char) ch;
iter++;
} while (true);
_len = iter;
return ret;
}
/**
* file.hpp
* This file is a part of The TemplateEngine system.
*
* Author: Denis Colesnicov <eugustus@gmail.com>
* Copyright: 09.2019
* Licence: MIT
* Version: 1
*/
#pragma once
#include <cstdio>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Pokusi se otevrit soubor ke cteni.
* @param _path Cesta k souboru.
* @return Ukazatel na deskriptor souboru, nebo NULL pokud se nepovedlo.
*/
FILE* openFile(char* _path);
/**
* Uzavre soubor.
* @param _file Ukazatel na deskriptor souboru.
*/
void closeFile(FILE* _file);
/**
* Nacte nekolik znaku ze souboru.
* @param _file Ukazatel na otevreny soubor.
* @param _temp Retezec k zapsani.
* @param _len Skutecny pocet zapsanych znaku.
* @param _count Pocet znaku k zapsani (vychozi 2).
* @param _skip_eol Preskocit znak noveho radku?
*
* @return char[3] Retezec o 3 znacich.
*/
char* readNext(FILE* _file, char* _temp, int& _len, bool _skip_eol = false);
#ifdef __cplusplus
}
#endif
<!doctype HTML>
<html>
<head>
<title>{{name}}</title>
</head>
<body>
Version: {{version}}<br>
State: {{other}}
{@footer.tpl}}
</body>
</html>
/**
* main.cpp
* This file is an example for The TemplateEngine system.
*
* Author: Denis Colesnicov <eugustus@gmail.com>
* Copyright: 09.2019
* Licence: MIT
* Version: 1
*
*/
#include <cstring>
#include "TemplateEngine.hpp"
/**
* Funkce pro ziskani hodnoty podle klice.
* @param _key Klic k ziskani hodnoty.
* @return Ziskana hodnota.
*/
char* processKey(char* _key)
{
char *value = (char*) "";
if (strncmp(_key, "name", strlen(_key)) == 0)
{
value = (char*) "The TemplateEngine System";
} else if (strncmp(_key, "url", strlen(_key)) == 0)
{
value = (char*) "example.com";
} else if (strncmp(_key, "version", strlen(_key)) == 0)
{
value = (char*) "1";
} else
{
printf("\nUNKNOWN_KEY '%s'\n", _key);
value = (char*) "Ooops...";
}
return value;
}
/**
* Funkce pro dalsi praci s obsahem souboru.
* @param _buf Data. Pokud == NULL, jedna se o posledni volani teto funkce.
* @note Pokud funkce obdrzi NULL, mela by uvolnit svoje zdroje a ukoncit se.
*/
void flush(char* _buf)
{
if (_buf == NULL)
{
printf("\n--*END*--");
} else if (strlen(_buf) > 0)
{
printf("%s", _buf);
}
}
extern "C" int main(int argc, char **argv)
{
/**
* Cesta k ROOT.
*/
char *pathName = (char*) "/home/denis/Dropbox/Projects/esp8266/pp/";
/**
* Nazev souboru v ROOT.
*/
char *fileName = (char*) "index.tpl";
/**
* Vytvarim instanci, nastavuji a spoustim.
*/
TemplateEngine engine(pathName);
engine.setFlushCB(flush);
engine.setKeyCB(processKey);
return engine.process(fileName);
}
/**
* TemplateEngine.cpp
* This file is a part of The TemplateEngine system.
*
* Author: Denis Colesnicov <eugustus@gmail.com>
* Copyright: 09.2019
* Licence: MIT
*
*/
#include "TemplateEngine.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "file.hpp"
#define TAG (char*)"TEMPLATE_ENGINE"
#define BRACKET_NONE 0
#define BRACKET_VAR 1
#define BRACKET_BLOCK 2
TemplateEngine::TemplateEngine(char* _path) :
my_var_cb(nullptr), my_flush_cb(nullptr)
{
my_path = (char*) calloc(strlen(_path) + 1, sizeof(char));
strncpy(my_path, _path, strlen(_path));
}
TemplateEngine::~TemplateEngine()
{
if (my_path != NULL)
{
free(my_path);
my_path = NULL;
}
}
int TemplateEngine::process(char* _path, bool _endFlush, bool _skip_eol)
{
FILE *file;
{
char path[155] = { '\0' };
strncpy(path, my_path, strlen(my_path));
strncat(path, _path, strlen(_path));
if (!(file = openFile(path)))
{
return 1;
}
}
/**
* Zasobnik pro data.
*/
char buffer[BUFFER_SIZE] = { '\0' };
/**
* Nachazimse ve funkcnim bloku?
*/
uint8_t inside_bracket = BRACKET_NONE;
/**
* Pocet znaku ziskanych ze souboru.
*/
int temp_length = 0;
/**
* Zasobnik FILO pro 2 znaky ziskavane ze souboru.
*/
char temp[3] = { '\0' };
while (true)
{
{
/**
* Ziskavam nove 2 znaky, kopiruji je do interniho zasobniku a uvolnuji pamet.
*/
char *tr = readNext(file, temp, temp_length, _skip_eol);
strncpy(temp, tr, strlen(tr));
free(tr);
if (temp_length == 0)
/**
* Pokud neziskal ani jeden znak ze souboru, ukoncuji smycku.
*/
{
break;
}
}
if (isVar(temp, inside_bracket))
{
/**
* Zacatek funkcniho bloku VAR!
* Nastavit potrebny stav na opracovani promenne,
* provest flush. Vynulovat hlavni a pomocne zasobniky.
*/
inside_bracket = BRACKET_VAR;
my_flush_cb(buffer);
memset(temp, '\0', sizeof(temp) / sizeof(temp[0]));
memset(buffer, '\0', sizeof(buffer) / sizeof(buffer[0]));
} else if (isBlock(temp, inside_bracket))
{
/**
* Zacatek funkcniho bloku BLOCK!
* Nastavit potrebny stav na opracovani bloku,
* provest flush. Vynulovat hlavni a pomocne zasobniky.
*/
inside_bracket = BRACKET_BLOCK;
my_flush_cb(buffer);
memset(temp, '\0', sizeof(temp) / sizeof(temp[0]));
memset(buffer, '\0', sizeof(buffer) / sizeof(buffer[0]));
} else if (isEnd(buffer, temp, inside_bracket))
{
/**
* Konec funkcniho bloku VAR nebo BLOCK!
* Podle soucasneho stavu, bud opracovat VAR nebo BLOCK...
*/
if (inside_bracket == BRACKET_VAR)
{
/** Vkladam VAR vyraz */
my_flush_cb(my_var_cb(buffer));
} else if (inside_bracket == BRACKET_BLOCK)
{
/**
* Vkladam novy soubor.
* @todo Tento postup je nutny kvuli moznym omezenim na EMBEDED DEVICES.
*
* Provest flush bufferu zavolanim 'my_flush_cb'.
* Je treba si zaznamenat soucasnou pozici v souboru,
* zavrit soucasny soubor. Vytvorit novou instanci TemplateEngine,
* predat ji svoje 'nastaveni' a spustit.
* Po ukonceni prace noveho TemplateEngine znovu otevrit soubor,
* pretocit na zaznamenanou pozici v nem a pokracovat v processu.
*
* @note Mozna by bylo dobre provest uvolneni zdroju (buffer a dalsi)
*/
my_position = ftell(file);
closeFile(file);
{
TemplateEngine engine(my_path);
engine.setFlushCB(my_flush_cb);
engine.setKeyCB(my_var_cb);
if (engine.process(buffer, false) != 0)
/**
* Pri zpracovani zanoreneho bloku doslo k problemum!
*/
{
return 2;
}
}
/**
* Navrat do predchoziho stavu...
*/
if (!(file = openFile(_path)))
{
printf("E:%s: Could not reopen file: '%s'!\n", TAG, _path);
return 1;
}
//
fseek(file, my_position, SEEK_SET);
}
inside_bracket = BRACKET_NONE;
memset(buffer, '\0', sizeof(buffer) / sizeof(buffer[0]));
memset(temp, '\0', sizeof(temp) / sizeof(temp[0]));
} else
{
/**
* Pripoji nove ziskane 2 znaky do zasobniku
* Podle soucasneho stavu, bud match_key nebo buffer.
*/
if (inside_bracket == BRACKET_VAR)
{
strncat(buffer, temp, temp_length - 1);
} else if (inside_bracket == BRACKET_BLOCK)
{
strncat(buffer, temp, temp_length - 1);
} else
{
strncat(buffer, temp, 1);
}
}
if (strlen(buffer) + 1 >= BUFFER_SIZE)
// Umyslne overuji + 1 znaky aby nedoslo k overflow!
{
my_flush_cb(buffer);
memset(buffer, '\0', sizeof(buffer) / sizeof(buffer[0]));
}
if (temp_length < 2)
{
break;
}
}
if (strlen(buffer) > 0)
/**
* Pokud neco zustalo v zasobniku, 'vyplivnout' to..
*/
{
my_flush_cb(buffer);
memset(buffer, '\0', sizeof(buffer) / sizeof(buffer[0]));
}
if (_endFlush)
{
my_flush_cb(NULL);
}
closeFile(file);
return 0;
}
bool TemplateEngine::isVar(char* _tag, uint8_t _inside)
{
if (strncmp(VAR_BRACKET, _tag, 2) == 0)
{
if (_inside != BRACKET_NONE)
{
printf("E:%s: '%s'\n", TAG,
"Chyba 1, Oteviraci znacka ve funkcnim bloku.");
return false;
}
return true;
} else
{
return false;
}
}
bool TemplateEngine::isBlock(char* _tag, uint8_t _inside)
{
if (strncmp(BLOCK_BRACKET, _tag, 2) == 0)
{
if (_inside != BRACKET_NONE)
{
printf("E:%s: '%s'\n", TAG,
"Chyba 2, Oteviraci znacka ve funkcnim bloku.");
return false;
}
return true;
} else
{
return false;
}
}
bool TemplateEngine::isEnd(char* _buf, char* _tag, uint8_t _inside)
{
if (strncmp(LAST_BRACKET, _tag, 2) == 0)
{
if (_inside == BRACKET_NONE)
{
printf("E:%s: '%s'\n", TAG,
"Chyba 3, Uzaviraci znacka bez oteviraci.");
return false;
} else if (strlen(_buf) == 0)
{
// Zadny obsah uvnitr!
printf("E:%s: '%s'\n", TAG, "Chyba 4, Zadny obsah uvnitr!");
return false;
}
return true;
} else
{
return false;
}
}
void TemplateEngine::setKeyCB(keyCb _cb)
{
my_var_cb = _cb;
}
void TemplateEngine::setFlushCB(flushCb _cb)
{
my_flush_cb = _cb;
}
/**
* TemplateEngine.hpp
* This file is a part of The TemplateEngine system.
*
* Author: Denis Colesnicov <eugustus@gmail.com>
* Copyright: 09.2019
* Licence: MIT
* Version: 1
*
*/
#pragma once
#include <cstddef>
#include <cstdio>
#include <cstdint>
/**
* Prototyp funkce pro ziskavani hodnot podle klicu.
* @param _key Klic, podle ktereho se ma hledat.
* @return Nalezena hodnota
*/
typedef char* (*keyCb)(char* _key);
/**
* @note Pokud tato funkce bude zavolana s parametrem NULL,
* znamena to ze proces cteni ze souboru je ukoncen a k
* dalsimu volani teto funkce by jiz nemelo dojit!
* @param _str Retezec k 'vyplivnuti'.
*/
typedef void (*flushCb)(char* _str);
/**
* Mazimalni delka nazvu souboru i s cestou!
*/
#define MAX_NAME 155
/**
* Maximalni velikost zasobnika ve znacich.
*/
#define BUFFER_SIZE 100
/** Toto jsou znacky pro pouziti v souborech sablon. */
/**
* Pocatecni znacka por VAR/VYRAZ.
*/
#define VAR_BRACKET (char*)"{{"
/**
* Pocatecni znacka pro BLOCK
*/
#define BLOCK_BRACKET (char*)"{@"
/**
* Ukoncujici znacka pro VAR/VYRAZ i BLOCK
*/
#define LAST_BRACKET (char*)"}}"
/**
* Trida pro zpracovani souboru sablon.
* Umoznuje jednoduchou nahradu vyrazu podle key=>value
* a vkladani dalsich souboru sablon.
*
* Podporovana syntaxe:
* {{expression}} - VAR/VYRAZ k nahrazeni jinym retezcem @see keyCB
* {@expression}} - Na toto misto vlozi dalsi soubor sablon. @see TemplateEngine::process(...)
* @note Znacky se daji pouzit i jine. @see LAST_BRACKET @see BLOCK_BRACKET @see VAR_BRACKET
*/
class TemplateEngine
{
public:
/**
* Konstruktor
* @param _path Cela cesta k adresari se souborami sablon.
*/
TemplateEngine(char* _path);
/**
* Destruktor
*/
virtual ~TemplateEngine();
/**
* Nastavi funkci pro hledani hodnoty podle klice.
* @param _cb Ukazatel na funkci.
*/
void setKeyCB(keyCb _cb);
/**
* Nastavi funkci pro dalsi praci s daty
* @param _cb Ukazatel na funkci.
*/
void setFlushCB(flushCb _cb);
/**
* Zpracuje soubor sablony.
* @param _path nazev souboru.
* @param _endFlush Zavolat funkci flush s NULL? Toto ukonci zpracovani dat.
* @param _skip_eol Odstranit z dat znaky noveho radku?
* @return Kod chyby nebo 0 pokud vse OK
*/
int process(char *_path, bool _endFlush = true, bool _skip_eol = false);
private:
/**
* Funkce pro ziskani retezce podle predaneho klice.
*/
keyCb my_var_cb;
/**
* funkce pro 'vyplivnuti'/odeslani dat.
*/
flushCb my_flush_cb;
/**
* Cesta ke slozce se souborami sablon.
*/
char *my_path = NULL;
/**
* Pozice v souboru.
*/
size_t my_position = 0;
/**
* Overi jestli se jedna o zacatek funkcniho bloku 'VAR/VYRAZ'
* @param _tag Zasobnik se dvema znaky.
* @param _inside Soucasny stav.
* @return TRUE pokud ano, jinak FALSE
*/
bool isVar(char* _tag, uint8_t _inside);
/**
* Overi jestli se jedna o zacatek funkcniho bloku 'BLOCK'
* @param _tag Zasobnik se dvema znaky.
* @param _inside Soucasny stav.
* @return TRUE pokud ano, jinak FALSE
*/
bool isBlock(char* _tag, uint8_t _inside);
/**
* Overi jestli se jedna o ukonceni funkcniho bloku
* @param _buf Zasobnik se vsemi znaky.
* @param _tag Zasobnik se dvema znaky.
* @param _inside Soucasny stav.
* @return TRUE pokud ano, jinak FALSE
*/
bool isEnd(char* _buf, char* _tag, uint8_t _inside);
};
@colesnicov
Copy link
Author

TemplateEngine je system pro implementaci dynamickych sablon v realnem case. Oblast pouziti:
Web (zpracovani HTML, CSS a JS souboru) nebo jakakoliv jina aplikace kde je potreba nahrazovat klice v textu nebo vkladata do nej dalsi bloky textu.

Podporovane konstrukce jsou nahrazovani textu (replace) nebo vkladani dalsiho (include).

@colesnicov
Copy link
Author

ISSUES ARE WELCOME

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment