libhtmlpp 1.0.0
Loading...
Searching...
No Matches
html.cpp
Go to the documentation of this file.
1
9/*******************************************************************************
10 * Copyright (c) 2021, Jan Koester jan.koester@gmx.net
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * Neither the name of the <organization> nor the
21 * names of its contributors may be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *******************************************************************************/
35
36#include <iostream>
37#include <cstdarg>
38#include <compare>
39#include <string_view>
40#include <array>
41#include <algorithm>
42#include <fstream>
43
44#include "utils.h"
45#include "html.h"
46#include "config.h"
47#include "encode.h"
48#include <assert.h>
49
50
51#define HTMLTAG_OPEN '<'
52#define HTMLTAG_TERMINATE '/'
53#define HTMLTAG_CLOSE '>'
54#define HTMLTAG_COMMENT '!'
60namespace libhtmlpp {
61
62 const std::array<std::string_view,100> ContainerTypes{{
63 "a",
64 "abbr",
65 "address",
66 "article",
67 "aside",
68 "audio",
69 "b",
70 "bdi",
71 "bdo",
72 "blockquote",
73 "body",
74 "button",
75 "canvas",
76 "caption",
77 "cite",
78 "code",
79 "colgroup",
80 "data",
81 "datalist",
82 "dd",
83 "del",
84 "details",
85 "dfn",
86 "dialog",
87 "div",
88 "dl",
89 "dt",
90 "em",
91 "fieldset",
92 "figcaption",
93 "figure",
94 "footer",
95 "form",
96 "frame",
97 "frameset",
98 "h1",
99 "h2",
100 "h3",
101 "h4",
102 "h5",
103 "h6",
104 "head",
105 "header",
106 "hgroup",
107 "html",
108 "i",
109 "iframe",
110 "ins",
111 "kbd",
112 "label",
113 "legend",
114 "li",
115 "main",
116 "map",
117 "mark",
118 "menu",
119 "meter",
120 "nav",
121 "noscript",
122 "object",
123 "ol",
124 "optgroup",
125 "option",
126 "output",
127 "p",
128 "picture",
129 "pre",
130 "progress",
131 "q",
132 "rp",
133 "rt",
134 "ruby",
135 "s",
136 "samp",
137 "search",
138 "section",
139 "select",
140 "small",
141 "span",
142 "strong",
143 "style",
144 "sub",
145 "summary",
146 "sup",
147 "svg",
148 "table",
149 "tbody",
150 "td",
151 "template",
152 "textarea",
153 "tfoot",
154 "th",
155 "thead",
156 "time",
157 "title",
158 "tr",
159 "u",
160 "ul",
161 "var",
162 "video"
163 }};
164
166 public:
167 std::unique_ptr<Element> element;
169 std::unique_ptr<DocElements> nextel;
171
173 nextel = nullptr;
174 prevel = nullptr;
175 element = nullptr;
176 terminator = false;
177 }
178
180 auto cur = std::move(nextel);
181 while (cur) {
182 cur = std::move(cur->nextel);
183 }
184 }
185 };
186};
187
189 _Data.push_back(str);
190}
191
193 if(str.empty())
194 return;
195
196 if (!_Data.empty() && _Data.back() == '\0') {
197 _Data.pop_back();
198 }
199
200 for(auto i = str.begin(); i!=str.end(); ++i){
201 if(*i=='\0')
202 break;
203 _Data.push_back(*i);
204 }
205
206 if (_Data.empty() || _Data.back() != '\0') {
207 _Data.push_back('\0');
208 }
209}
210
213
215 std::copy(str._Data.begin(),str._Data.end(),std::insert_iterator<std::vector<char>>(_Data,_Data.begin()));
216}
217
219 if(!src)
220 return;
221
222 if (!_Data.empty() && _Data.back() == '\0') {
223 _Data.pop_back();
224 }
225 _Data.push_back(src);
226
227 _Data.push_back('\0');
228}
229
230void libhtmlpp::HtmlString::append(const std::string& src) {
231 if(src.empty())
232 return;
233
234 if (!_Data.empty() && _Data.back() == '\0') {
235 _Data.pop_back();
236 }
237
238 for(auto i = src.begin(); i!=src.end(); ++i){
239 if(*i=='\0')
240 break;
241 _Data.push_back(*i);
242 }
243
244 if (_Data.empty() || _Data.back() != '\0') {
245 _Data.push_back('\0');
246 }
247}
248
250 std::copy(hstring._Data.begin(),hstring._Data.end(),std::back_inserter(_Data));
251}
252
253void libhtmlpp::HtmlString::insert(size_t pos, char src){
254 _Data.at(pos)=src;
255}
256
258 _rootEl.reset();
259 _Data.clear();
260}
261
263 return _Data.empty();
264}
265
266
268 append(src);
269 return *this;
270}
271
273 append(hstring);
274 return *this;
275}
276
278 clear();
279 append(src);
280 return *this;
281}
282
284 clear();
285 std::copy(src._Data.begin(),src._Data.end(),std::insert_iterator<std::vector<char>>(_Data,_Data.begin()));
286 return *this;
287}
288
290 return _Data.at(pos);
291}
292
294 append(src);
295 return *this;
296}
297
299 append(src);
300 return *this;
301}
302
304 if(src._Data.data())
305 std::copy(src._Data.begin(),src._Data.end(),std::back_inserter(_Data));
306 return *this;
307}
308
310 char buf[255];
311 snprintf(buf, 255, "%d", src);
312 append(buf);
313 return *this;
314}
315
317 char buf[255];
318 snprintf(buf, 255, "%zu", src);
319 append(buf);
320 return *this;
321}
322
324 push_back(src);
325 return *this;
326}
327
329 return _Data.data();
330}
331
333 return _Data.empty() ? 0 : (_Data.size() - 1);
334}
335
337 // append()/push_back()/etc. maintain a trailing '\0' sentinel in _Data so
338 // c_str() stays valid between calls (pop it, write new content, push it
339 // back) — that byte is a private bookkeeping detail, not real content,
340 // so it must not be counted here. Without this, every consumer that
341 // trusts size()/str() to describe the actual string (e.g. building a
342 // larger buffer by concatenating multiple HtmlString results) ends up
343 // with a genuine embedded NUL baked into otherwise-clean text, which
344 // then silently truncates any later strlen()-based use of that buffer.
345 if (!_Data.empty() && _Data.back() == '\0')
346 return _Data.size() - 1;
347 return _Data.size();
348}
349
350const std::string libhtmlpp::HtmlString::str() const{
351 if(_Data.data())
352 return std::string(_Data.begin(), _Data.begin() + static_cast<long>(size()));
353 return "";
354}
355
357 return _Data.data();
358}
359
360const std::vector<char>& libhtmlpp::HtmlString::data() const{
361 return _Data;
362}
363
364
366 HTMLException excp;
367 _buildTree();
368 return *_rootEl;
369}
370
371void libhtmlpp::HtmlString::_buildtreenode(
372 DocElements *firstel,
374 std::unique_ptr<Element> &html)
375{
376 if (!firstel) {
377 HTMLException excp;
378 excp[HTMLException::Error] << "No start Element!";
379 throw excp;
380 }
381
382 struct Frame {
383 DocElements *open; // Opener-DocElement (Start-Tag)
384 DocElements *close; // passender Terminator-DocElement (End-Tag)
385 const DocElements *outer_end; // Grenze der aktuellen Ebene
386 Element *outer_prev_el; // letztes bereits eingebautes Geschwister der äußeren Ebene
387 };
388 std::stack<Frame> stack;
389
390 DocElements *start = firstel;
391 const DocElements *end = lastel; // nullptr bedeutet: bis Ketten-Ende
392
393 Element *prev_el_in_tree = nullptr; // zuletzt in den Baum eingebautes Element
394
395 auto checkContainer = [&](const std::string &tag) {
396 for (size_t i = 0; i < ContainerTypes.size(); ++i) {
397 if (tag == ContainerTypes[i]) return true;
398 }
399 return false;
400 };
401
402 // Leere DocElements überspringen (z. B. Kommentare/Text, die nicht als element abgebildet sind)
403 auto skip_empty = [](DocElements *cur, const DocElements *stop) -> DocElements* {
404 while (cur && cur != stop && (!cur->element)) {
405 cur = cur->nextel.get();
406 }
407 return cur;
408 };
409
410 // Finde zum gegebenen Start-Tag dessen passenden Terminator in [open->nextel, bound).
411 auto find_terminator = [&skip_empty, checkContainer](DocElements *open, const DocElements *bound) -> DocElements* {
412 if (!open || !open->element || open->terminator ||
413 open->element->getType() != HtmlEl) return nullptr;
414
415 const std::string &tag = static_cast<HtmlElement*>(open->element.get())->getTagname();
416 int nest = 0;
417 DocElements *cur = open->nextel.get();
418
419 while (cur && cur != bound) {
420 cur = skip_empty(cur, bound);
421 if (!cur || cur == bound) break;
422
423 if (cur->element && cur->element->getType() == HtmlEl) {
424 const std::string &curtag = static_cast<HtmlElement*>(cur->element.get())->getTagname();
425 if (curtag == tag) {
426 if (cur->terminator) {
427 if (nest == 0) return cur; // passendes End-Tag gefunden
428 --nest;
429 } else {
430 ++nest;
431 }
432 }
433 }
434 cur = cur->nextel.get();
435 }
436
437 // Wenn ein Container nicht geschlossen wurde: Warnung ignorieren oder als einfaches Tag behandeln
438 if (checkContainer(tag)) {
439 // Exception entfernt für nachsichtigeres Parsing
440 // return nullptr;
441 }
442 return nullptr;
443 };
444
445 for (;;) {
446 // bis zum nächsten sinnvollen DocElement laufen
447 start = skip_empty(start, end);
448
449 // Terminator-Knoten als eigenständige Nodes überspringen
450 if (start && start != end && start->terminator) {
451 start = start->nextel.get();
452 continue;
453 }
454
455 // Ende der aktuellen Ebene erreicht?
456 if (!start || start == end) {
457 if (stack.empty()) {
458 // Ganz oben: Root setzen (falls vorhanden)
459 if (firstel->element) {
460 html = std::move(firstel->element);
461 }
462 return;
463 }
464
465 // Frame schließen: Kinderbereich [open->nextel, close) einsammeln
466 Frame fr = stack.top(); stack.pop();
467 HtmlElement *opener_el = static_cast<HtmlElement*>(fr.open->element.get());
468
469 // Alle Kinder zwischen open und close verketten
470 Element* last_child_in_chain = nullptr;
471 {
472 DocElements* cur = fr.open->nextel.get();
473 // bis zum ersten brauchbaren Kind
474 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
475 cur = cur->nextel.get();
476 }
477
478 // alle nicht-leeren, nicht-Terminierer bis vor close anbinden
479 while (cur && cur != fr.close) {
480 if (cur->element && !cur->terminator) {
481 if (!opener_el->_childElement) {
482 opener_el->_childElement = std::move(cur->element);
483 last_child_in_chain = opener_el->_childElement.get();
484 } else {
485 last_child_in_chain->_nextElement = std::move(cur->element);
486 // _prev (falls genutzt) setzen
487 last_child_in_chain->_nextElement->_prevElement = last_child_in_chain;
488 last_child_in_chain = last_child_in_chain->_nextElement.get();
489 }
490 }
491 cur = cur->nextel.get();
492 // leere/terminator Knoten überspringen
493 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
494 cur = cur->nextel.get();
495 }
496 }
497 }
498
499 // dieses Container-Element ist nun das "aktuelle" in der äußeren Ebene
500 prev_el_in_tree = opener_el;
501
502 // Wenn es bereits ein vorheriges Geschwister in der äußeren Ebene gibt: verketten
503 if (fr.outer_prev_el) {
504 prev_el_in_tree->_prevElement = fr.outer_prev_el;
505 fr.outer_prev_el->_nextElement = std::move(fr.open->element);
506 prev_el_in_tree = fr.outer_prev_el->_nextElement.get();
507 }
508
509 // für die äußere Ebene fortsetzen
510 prev_el_in_tree = opener_el;
511 start = (fr.close ? fr.close->nextel.get() : nullptr);
512 end = fr.outer_end;
513
514 continue;
515 }
516
517 // Start-Tag eines HTML-Elements: passenden Terminator suchen → in den Stack tauchen
518 if (start->element && !start->terminator && start->element->getType() == HtmlEl) {
519 if (DocElements *close = find_terminator(start, end)) {
520 // neuen Rahmen für diesen Container aufmachen
521 stack.push(Frame{start, close, end, prev_el_in_tree});
522
523 // wir wechseln in die innere Ebene: prev zurücksetzen
524 prev_el_in_tree = nullptr;
525 start = start->nextel.get();
526 end = close;
527 continue;
528 }
529 }
530
531 // "normales" Element (kein Container mit eigenem Terminatorbereich):
532 if (start->element && !start->terminator) {
533 Element *current_el = start->element.get();
534
535 if (prev_el_in_tree) {
536 current_el->_prevElement = prev_el_in_tree;
537 prev_el_in_tree->_nextElement = std::move(start->element);
538 prev_el_in_tree = prev_el_in_tree->_nextElement.get();
539 } else {
540 prev_el_in_tree = current_el;
541 }
542 }
543
544 // weiter zum nächsten DocElement
545 start = start->nextel.get();
546 }
547}
548
554void libhtmlpp::HtmlString::_buildTree() {
555 DocElements* lastEl = nullptr;
556 std::unique_ptr<DocElements> firstEl = nullptr;
557
558 auto is_ws = [](unsigned char ch) -> bool {
559 // space, \t, \n, \r
560 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
561 };
562
563 auto ascii_tolower = [](unsigned char c) -> unsigned char {
564 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
565 };
566
567 auto starts_with_ci = [&](const char* s, const char* e, const char* k) -> bool {
568 const size_t klen = std::char_traits<char>::length(k);
569 if (static_cast<size_t>(e - s) < klen) return false;
570 for (size_t i = 0; i < klen; ++i) {
571 unsigned char a = static_cast<unsigned char>(s[i]);
572 unsigned char b = static_cast<unsigned char>(k[i]);
573 if (ascii_tolower(a) != ascii_tolower(b)) return false;
574 }
575 return true;
576 };
577
578 auto add_element_node = [&](DocElements** last) {
579 if (!firstEl) {
580 firstEl = std::make_unique<DocElements>();
581 *last = firstEl.get();
582 } else {
583 (*last)->nextel = std::make_unique<DocElements>();
584 (*last)->nextel->prevel = (*last);
585 *last = (*last)->nextel.get();
586 }
587 };
588
589 const char* const base = _Data.data();
590 const size_t n = _Data.size();
591 const char* const end = base + n;
592
593 const char* p = base;
594
595 while (p < end) {
596 while (p < end && is_ws(static_cast<unsigned char>(*p))) ++p;
597 if (p >= end) break;
598
599 if (*p == HTMLTAG_OPEN) { // '<'
600 const char* const remain_end = end;
601 const size_t remain = static_cast<size_t>(remain_end - p);
602
603 if (remain >= 2) {
604 const unsigned char c1 = ascii_tolower(static_cast<unsigned char>(p[1]));
605
606 if (p[1] == '!') {
607 // <!-- ... -->
608 if (starts_with_ci(p, end, "<!--")) {
609 add_element_node(&lastEl);
610 size_t i = static_cast<size_t>(p - base);
611 i = CommentElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
612 p = base + i;
613 continue;
614 }
615 if (starts_with_ci(p, end, "<!doctype")) {
616 const char* close_tag = p;
617 while (close_tag < end && *close_tag != '>') {
618 ++close_tag;
619 }
620 if (close_tag < end) {
621 p = close_tag + 1;
622 } else {
623 p = end;
624 }
625 continue;
626 }
627 } else if (c1 == 's') {
628 if (starts_with_ci(p, end, "<script")) {
629 add_element_node(&lastEl);
630 size_t i = static_cast<size_t>(p - base);
631 i = ScriptElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
632 p = base + i;
633 continue;
634 }
635 if (starts_with_ci(p, end, "<svg")) {
636 add_element_node(&lastEl);
637 size_t i = static_cast<size_t>(p - base);
638 i = SvgElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
639 p = base + i;
640 continue;
641 }
642 } else if (c1 == 't') {
643 if (starts_with_ci(p, end, "<textarea")) {
644 add_element_node(&lastEl);
645 size_t i = static_cast<size_t>(p - base);
646 i = TextArea::parseElement(_Data, lastEl->element, i, lastEl->terminator);
647 p = base + i;
648 continue;
649 }
650 }
651 }
652
653 {
654 add_element_node(&lastEl);
655 size_t i = static_cast<size_t>(p - base);
656 i = HtmlElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
657 p = base + i;
658 }
659 } else {
660 add_element_node(&lastEl);
661 size_t i = static_cast<size_t>(p - base);
662 i = TextElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
663 p = base + i;
664 }
665 }
666
667 _buildtreenode(firstEl.get(), nullptr, _rootEl);
668 // _buildtreenode's own linking logic is intricate enough (see its own
669 // comments) that threading parent-assignment through it directly risks
670 // a subtle new bug for marginal benefit -- this single pass over the
671 // now-correctly-linked tree is simple to get right by inspection
672 // instead.
673 if(_rootEl) HtmlElement::_assignParentPointers(_rootEl.get(), nullptr);
674}
675
683std::ostream& operator<<(std::ostream& os, const libhtmlpp::HtmlString& p) {
684 os << p.str();
685 return os;
686}
687
688void libhtmlpp::HtmlEncode(const std::string &input, std::string &output){
689 size_t ilen=input.length();
690 for(size_t i=0; i<ilen; ++i){
691 size_t ii=0;
692 bool changed=false;
693 while(HtmlSigns[ii][0]){
694 if(input[i]==HtmlSigns[ii][0][0]){
695 output+=HtmlSigns[ii][1];
696 changed=true;
697 }
698 ++ii;
699 }
700 if(!changed)
701 output.push_back(input[i]);
702 }
703}
704
705void libhtmlpp::HtmlDecode(const std::string &input,std::string &output){
706 size_t ilen=input.length();
707 for(size_t i=0; i<ilen; ++i){
708 size_t ii=0;
709 bool changed=false;
710 while(HtmlSigns[ii][0]){
711 if(input.compare(i,strlen(HtmlSigns[ii][1]),HtmlSigns[ii][1]) == 0){
712 output += HtmlSigns[ii][0];
713 changed=true;
714 }
715 ++ii;
716 }
717 if(!changed)
718 output += input[i];
719 }
720}
721
722void libhtmlpp::HtmlDecode(const std::string &input,HtmlString &output){
723 std::string tmp;
724 HtmlDecode(input,tmp);
725 output << tmp;
726 output.parse();
727}
728
729libhtmlpp::HtmlElement::HtmlElement(const std::string &tagname) : HtmlElement(){
730 _TagName.clear();
731 std::copy(tagname.begin(),tagname.end(),std::back_inserter(_TagName));
732}
733
735 _childElement=nullptr;
736 _firstAttr=nullptr;
737 _lastAttr=nullptr;
738}
739
743
747
750
752 return -1;
753}
754
755void libhtmlpp::HtmlElement::setTagname(const std::string &name){
756 _TagName.clear();
757 std::copy(name.begin(),name.begin()+name.length(),std::back_inserter(_TagName));
758}
759
760const std::string libhtmlpp::HtmlElement::getTagname() const{
761 if(_TagName.empty())
762 return "";
763 return std::string(_TagName.begin(),_TagName.end());
764}
765
766void libhtmlpp::HtmlElement::_assignParentPointers(Element* node, Element* parent){
767 while(node){
768 node->_parentElement = parent;
769 // Only plain HtmlEl nodes use this (base) _childElement for real
770 // children via insertChild/appendChild -- ScriptElement/SvgElement/
771 // TextArea each shadow their own separate _childElement instead
772 // (and don't support insertChild/appendChild at all, both deleted),
773 // so descending through the base pointer for those types would
774 // read the wrong field entirely.
775 if(node->getType()==HtmlEl){
776 HtmlElement *hel = static_cast<HtmlElement*>(node);
777 if(hel->_childElement)
778 _assignParentPointers(hel->_childElement.get(), node);
779 }
780 node = node->_nextElement.get();
781 }
782}
783
785 if(_childElement){
786 remove(_childElement.get());
787 }
788
789 switch(el->getType()){
790 case HtmlEl:
791 _childElement=std::make_unique<HtmlElement>();
792 break;
793 case TextEl:
794 _childElement=std::make_unique<TextElement>();
795 break;
796 case CommentEl:
797 _childElement=std::make_unique<CommentElement>();
798 break;
799 case ScriptEL:
800 _childElement=std::make_unique<ScriptElement>();
801 break;
802 case SvgEL:
803 _childElement=std::make_unique<SvgElement>();
804 break;
805 default:
806 HTMLException ex;
807 ex[HTMLException::Critical] << "appendChild: Unknown html element found: "<< el->getType() << " !";
808 throw ex;
809 }
810 _copy(_childElement.get(),el);
811 // _copy re-parents the copied subtree's own descendants (relative to
812 // _childElement.get()) but deliberately never touches _childElement's
813 // OWN parent -- that's this call site's job.
814 _childElement->_parentElement = this;
815}
816
818 insertChild(&el);
819}
820
822 if(!el)
823 return;
824 if(_childElement){
825 Element *prev=nullptr;
826
827 for(Element *curel=_childElement.get(); curel; curel=curel->nextElement()){
828 prev=curel;
829 }
830
831 switch(el->getType()){
832 case HtmlEl:
833 prev->_nextElement=std::make_unique<HtmlElement>();
834 break;
835 case TextEl:
836 prev->_nextElement=std::make_unique<TextElement>();
837 break;
838 case CommentEl:
839 prev->_nextElement=std::make_unique<CommentElement>();
840 break;
841 case ScriptEL:
842 prev->_nextElement=std::make_unique<ScriptElement>();
843 break;
844 case SvgEL:
845 prev->_nextElement=std::make_unique<SvgElement>();
846 break;
847 default:
848 HTMLException ex;
849 ex[HTMLException::Critical] << "appendChild: Unknown html element found: "<< el->getType() << " !";
850 throw ex;
851 }
852
853 _copy(prev->_nextElement.get(),el);
854
855 if(prev->_nextElement){
856 prev->_nextElement->_prevElement=prev;
857 // Siblings share the same parent -- this, not prev.
858 prev->_nextElement->_parentElement=this;
859 }
860 }else{
861 insertChild(el);
862 }
863}
864
866 appendChild(&el);
867}
868
869
871 if(!hel)
872 return false;
873 if( _TagName.size() != hel->_TagName.size())
874 return false;
875 if(std::equal(_TagName.begin(),_TagName.end(),hel->_TagName.begin()))
876 return true;
877 return false;
878}
879
881 if(_TagName.size() != hel._TagName.size())
882 return false;
883 if(std::equal(_TagName.begin(),_TagName.end(),hel._TagName.begin()))
884 return true;
885 return false;
886}
887
889 _copy(this,&hel);
890 return *this;
891}
892
897
899 // Previously a DFS over the whole subtree via an explicit stack/goto,
900 // searching for whoever owns `el` -- with _parentElement, that lookup
901 // is O(1) instead, and the simpler logic below replaces several bugs
902 // that walk found in that DFS: a null-pointer dereference right after
903 // finding `el` via ordinary sibling advancement (the old code set
904 // `cur=nullptr` on a match and then unconditionally dereferenced it on
905 // the very next line); `el` being silently walked past, never unlinked,
906 // when reached by diving into a `_childElement` (the old code never
907 // re-checked the dived-into node against `el`); and removing a parent's
908 // first child never repointing `parent->_childElement`, which quietly
909 // discarded the rest of that sibling chain.
910 if(!el) return;
911
912 Element *parent = el->_parentElement;
913 Element *prevSibling = el->_prevElement;
914 // Whoever currently owns `el` via a unique_ptr (parent->_childElement,
915 // or a previous sibling's _nextElement) is about to be reassigned away
916 // from `el` below, which destroys `el` immediately as part of that
917 // assignment (reassigning a unique_ptr destroys whatever it used to
918 // own) -- so everything needed from `el` must be captured first.
919 // release() (not move/get()) detaches el's own next-sibling chain
920 // without destroying anything, unlike move-assigning it while it's
921 // still reachable through `el`.
922 std::unique_ptr<Element> elsNext(el->_nextElement.release());
923
924 if(parent && parent->getType()==HtmlEl &&
925 static_cast<HtmlElement*>(parent)->_childElement.get()==el){
926 if(elsNext) elsNext->_prevElement = nullptr;
927 static_cast<HtmlElement*>(parent)->_childElement = std::move(elsNext); // destroys el
928 } else if(prevSibling){
929 if(elsNext) elsNext->_prevElement = prevSibling;
930 prevSibling->_nextElement = std::move(elsNext); // destroys el
931 }
932 // else: el has neither a parent nor a previous sibling (e.g. a
933 // rootless standalone node) -- nothing owns it via a reassignable
934 // unique_ptr, so it's left unlinked-from but not destroyed.
935
936 // el must not be touched below this point in the two branches above --
937 // it no longer exists.
938}
939
947void libhtmlpp::HtmlElement::_serialelize(const std::vector<char>& in, bool preserveAttrCase) {
948 _TagName.clear();
949
950 auto is_space = [](unsigned char c) -> bool {
951 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
952 };
953 auto tolower_ascii = [](unsigned char c) -> unsigned char {
954 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
955 };
956
957 size_t i = 0, n = in.size();
958
959 if (i < n && in[i] == '<') ++i;
960 while (i < n && is_space(static_cast<unsigned char>(in[i]))) ++i;
961
962 bool end_tag = false;
963 if (i < n && in[i] == '/') {
964 end_tag = true;
965 ++i;
966 while (i < n && is_space(static_cast<unsigned char>(in[i]))) ++i;
967 }
968
969 size_t r = n;
970 while (r > i && is_space(static_cast<unsigned char>(in[r - 1]))) --r;
971 if (r > i && in[r - 1] == '>') --r; // ignore '>' if present
972 while (r > i && is_space(static_cast<unsigned char>(in[r - 1]))) --r;
973
974 if (i >= r) {
975 HTMLException excp;
976 throw excp[HTMLException::Critical] << "no tag in element found!";
977 }
978
979 const size_t name_start = i;
980 while (i < r) {
981 unsigned char c = static_cast<unsigned char>(in[i]);
982 if (is_space(c) || c == '/' || c == '>') break;
983 ++i;
984 }
985 const size_t name_end = i;
986 if (name_start == name_end) {
987 HTMLException excp;
988 throw excp[HTMLException::Critical] << "no tag in element found!";
989 }
990
991 _TagName.assign(in.begin() + name_start, in.begin() + name_end);
992 for (char& ch : _TagName) ch = static_cast<char>(tolower_ascii(static_cast<unsigned char>(ch)));
993
994 if (end_tag) {
995 return;
996 }
997
998 while (i < r) {
999 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
1000 if (i >= r) break;
1001
1002 if (in[i] == '/') {
1003 ++i;
1004 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
1005 break;
1006 }
1007
1008 const size_t kstart = i;
1009 while (i < r) {
1010 unsigned char c = static_cast<unsigned char>(in[i]);
1011 if (is_space(c) || c == '=' || c == '/' || c == '>') break;
1012 ++i;
1013 }
1014 const size_t kend = i;
1015 if (kstart == kend) {
1016 ++i;
1017 continue;
1018 }
1019
1020 std::string key(in.begin() + kstart, in.begin() + kend);
1021 if (!preserveAttrCase) {
1022 for (char& ch : key) ch = static_cast<char>(tolower_ascii(static_cast<unsigned char>(ch)));
1023 }
1024
1025 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
1026
1027 std::string val;
1028
1029 if (i < r && in[i] == '=') {
1030 ++i;
1031 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
1032
1033 if (i < r && (in[i] == '"' || in[i] == '\'')) {
1034 char quote = in[i++];
1035 const size_t vstart = i;
1036 while (i < r && in[i] != quote) ++i;
1037 const size_t vend = i;
1038 val.assign(in.begin() + vstart, in.begin() + vend);
1039 if (i < r && in[i] == quote) ++i;
1040 } else {
1041 const size_t vstart = i;
1042 while (i < r) {
1043 unsigned char c = static_cast<unsigned char>(in[i]);
1044 if (is_space(c) || c == '/' || c == '>') break;
1045 ++i;
1046 }
1047 const size_t vend = i;
1048 val.assign(in.begin() + vstart, in.begin() + vend);
1049 }
1050 } else {
1051 val.clear();
1052 }
1053
1054 setAttribute(key, val);
1055 }
1056}
1057
1058
1060 const std::vector<char>& in,
1061 std::unique_ptr<libhtmlpp::Element>& el,
1062 size_t start,
1063 bool& termination
1064){
1065 el = std::make_unique<HtmlElement>();
1066 termination = false;
1067
1068 size_t i = start;
1069 if (i >= in.size() || in[i] != HTMLTAG_OPEN) return i;
1070
1071 ++i;
1072
1073 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) ++i;
1074
1075
1076
1077 size_t close = i;
1078 while (close < in.size() && in[close] != HTMLTAG_CLOSE) { // '>'
1079 ++close;
1080 }
1081
1082 size_t k = i;
1083 while (k < close && std::isspace(static_cast<unsigned char>(in[k]))) ++k;
1084
1085 std::vector<char> tel;
1086
1087 if (k < close && in[k] == HTMLTAG_TERMINATE) { // '/'
1088 termination = true;
1089 ++k;
1090 while (k < close && std::isspace(static_cast<unsigned char>(in[k]))) ++k;
1091 }
1092
1093 tel.insert(tel.end(), in.begin() + k, in.begin() + close);
1094
1095 reinterpret_cast<HtmlElement*>(el.get())->_serialelize(tel);
1096
1097 return ++close;
1098}
1099
1100namespace libhtmlpp {
1101
1103 if(!dest || !src)
1104 return;
1105
1106 // `dest` itself is reassigned throughout the copy loop below --
1107 // captured here so the parent-pointer fixup at the end can still
1108 // find the original top-level destination.
1109 libhtmlpp::Element* const originalDest = dest;
1110
1111 const libhtmlpp::Element* prev=nullptr;
1112
1113 struct cpyel {
1114 cpyel(){
1115 destin = nullptr;
1116 source = nullptr;
1117 };
1118
1119 cpyel(const cpyel &src){
1120 destin=src.destin;
1121 source=src.source;
1122 };
1123
1124 ~cpyel(){
1125 }
1126
1127 libhtmlpp::Element *destin;
1128 libhtmlpp::Element *source;
1129 };
1130
1131 std::stack<cpyel> cpylist;
1132
1133 NEWEL:
1134
1135 if(src->getType()==HtmlEl && dest->getType()==HtmlEl){
1136 ((libhtmlpp::HtmlElement*)dest)->_TagName=(((libhtmlpp::HtmlElement*)src)->_TagName);
1137 for(libhtmlpp::HtmlElement::Attributes *cattr=((libhtmlpp::HtmlElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1138 ((libhtmlpp::HtmlElement*)dest)->setAttribute(
1139 std::string(
1140 cattr->_Key.begin(),
1141 cattr->_Key.end()
1142 ),std::string(
1143 cattr->_Value.begin(),
1144 cattr->_Value.end()
1145 )
1146 );
1147 }
1148
1149 if(((libhtmlpp::HtmlElement*)src)->_childElement){
1150 switch(((libhtmlpp::HtmlElement*)src)->_childElement->getType()){
1151 case HtmlEl:
1152 ((libhtmlpp::HtmlElement*)dest)->_childElement=std::make_unique<HtmlElement>();
1153 break;
1154 case TextEl:
1155 ((libhtmlpp::HtmlElement*)dest)->_childElement =std::make_unique<TextElement>();
1156 break;
1157 case CommentEl:
1158 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<CommentElement>();
1159 break;
1160 case ScriptEL:
1161 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<ScriptElement>();
1162 break;
1163 case SvgEL:
1164 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<SvgElement>();
1165 break;
1166 case TextAreaEL:
1167 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<TextArea>();
1168 break;
1169 default:
1170 HTMLException ex;
1171 ex[HTMLException::Critical] << "_copy: Unknown html element found !";
1172 throw ex;
1173 }
1174 cpyel childel;
1175 childel.destin=((libhtmlpp::HtmlElement*)dest)->_childElement.get();
1176 childel.source=((libhtmlpp::HtmlElement*)src)->_childElement.get();
1177 cpylist.push(childel);
1178 }
1179 }else if(src->getType()==libhtmlpp::ScriptEL && dest->getType()== libhtmlpp::ScriptEL){
1180 ((libhtmlpp::ScriptElement*)dest)->_TagName=(((libhtmlpp::ScriptElement*)src)->_TagName);
1181 for(libhtmlpp::ScriptElement::Attributes *cattr=((libhtmlpp::ScriptElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1182 if(!cattr->_Value.empty()){
1183 ((libhtmlpp::ScriptElement*)dest)->setAttribute(
1184 std::string(
1185 cattr->_Key.begin(),
1186 cattr->_Key.end()
1187 ),std::string(
1188 cattr->_Value.begin(),
1189 cattr->_Value.end()
1190 )
1191 );
1192 }else{
1193 ((libhtmlpp::ScriptElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1194 }
1195 }
1196 ((ScriptElement*)dest)->_Script=(((ScriptElement*)src)->_Script);
1197 }else if(src->getType()==libhtmlpp::SvgEL && dest->getType()== libhtmlpp::SvgEL){
1198 ((libhtmlpp::SvgElement*)dest)->_TagName=(((libhtmlpp::SvgElement*)src)->_TagName);
1199 for(libhtmlpp::SvgElement::Attributes *cattr=((libhtmlpp::SvgElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1200 if(!cattr->_Value.empty()){
1201 ((libhtmlpp::SvgElement*)dest)->setAttribute(
1202 std::string(
1203 cattr->_Key.begin(),
1204 cattr->_Key.end()
1205 ),std::string(
1206 cattr->_Value.begin(),
1207 cattr->_Value.end()
1208 )
1209 );
1210 }else{
1211 ((libhtmlpp::SvgElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1212 }
1213 }
1214 ((SvgElement*)dest)->_Svg=(((SvgElement*)src)->_Svg);
1215 }else if(src->getType()==libhtmlpp::TextAreaEL&& dest->getType()== libhtmlpp::TextAreaEL){
1216 ((libhtmlpp::TextArea*)dest)->_TagName=(((libhtmlpp::TextArea*)src)->_TagName);
1217 for(libhtmlpp::TextArea::Attributes *cattr=((libhtmlpp::TextArea*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1218 if(!cattr->_Value.empty()){
1219 ((libhtmlpp::TextArea*)dest)->setAttribute(
1220 std::string(
1221 cattr->_Key.begin(),
1222 cattr->_Key.end()
1223 ),std::string(
1224 cattr->_Value.begin(),
1225 cattr->_Value.end()
1226 )
1227 );
1228 }else{
1229 ((libhtmlpp::TextArea*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1230 }
1231 }
1232 ((TextArea*)dest)->_Text=(((TextArea*)src)->_Text);
1233 }else if(src->getType()==libhtmlpp::TextEl && dest->getType()== libhtmlpp::TextEl){
1234 ((TextElement*)dest)->_Text=(((TextElement*)src)->_Text);
1235 }else if(src->getType()==libhtmlpp::CommentEl && dest->getType()== libhtmlpp::CommentEl){
1236 ((CommentElement*)dest)->_Comment=(((CommentElement*)src)->_Comment);
1237 }
1238
1239 if(prev)
1240 dest->_prevElement=(Element*)prev;
1241
1242 Element* next=src->nextElement();
1243
1244 if(next){
1245 switch(next->getType()){
1246 case HtmlEl:
1247 dest->_nextElement= std::make_unique<HtmlElement>();
1248 break;
1249 case TextEl:
1250 dest->_nextElement= std::make_unique<TextElement>();
1251 break;
1252 case CommentEl:
1253 dest->_nextElement= std::make_unique<CommentElement>();
1254 break;
1255 case ScriptEL:
1256 dest->_nextElement= std::make_unique<ScriptElement>();
1257 break;
1258 case SvgEL:
1259 dest->_nextElement= std::make_unique<SvgElement>();
1260 break;
1261 case TextAreaEL:
1262 dest->_nextElement= std::make_unique<TextArea>();
1263 break;
1264 default:
1265 HTMLException ex;
1266 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1267 throw ex;
1268 }
1269 prev=dest;
1270 src=next;
1271 dest=dest->_nextElement.get();
1272 goto NEWEL;
1273 }
1274
1275 if(!cpylist.empty()){
1276 cpyel childel(cpylist.top());
1277 prev=nullptr;
1278 dest=childel.destin;
1279 src=childel.source;
1280 cpylist.pop();
1281 goto NEWEL;
1282 }
1283
1284 // Re-parent the whole copied subtree relative to originalDest --
1285 // never derived from src's original ancestry, since this copy may
1286 // be landing somewhere entirely different (e.g. a fresh standalone
1287 // object, or as a new child elsewhere). originalDest's OWN parent
1288 // is deliberately left untouched: that's always the caller's
1289 // responsibility (insertChild/appendChild set it explicitly right
1290 // after calling _copy; operator=/copy-ctors leave it as whatever
1291 // it already was, since copying INTO an object doesn't change
1292 // where that object itself lives).
1293 if(originalDest->getType()==HtmlEl){
1294 libhtmlpp::HtmlElement *destHel = static_cast<libhtmlpp::HtmlElement*>(originalDest);
1295 if(destHel->_childElement)
1296 libhtmlpp::HtmlElement::_assignParentPointers(destHel->_childElement.get(), originalDest);
1297 }
1298 return;
1299 }
1300};
1301
1303 std::unique_ptr<Element> nel;
1304 switch(el->getType()){
1305 case HtmlEl:
1306 nel->_nextElement= std::make_unique<HtmlElement>();
1307 break;
1308 case TextEl:
1309 nel->_nextElement= std::make_unique<TextElement>();
1310 break;
1311 case CommentEl:
1312 nel->_nextElement= std::make_unique<CommentElement>();
1313 break;
1314 case ScriptEL:
1315 nel->_nextElement= std::make_unique<ScriptElement>();
1316 break;
1317 case SvgEL:
1318 nel->_nextElement= std::make_unique<SvgElement>();
1319 break;
1320 case TextAreaEL:
1321 nel->_nextElement= std::make_unique<TextArea>();
1322 break;
1323 default:
1324 HTMLException ex;
1325 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1326 throw ex;
1327 }
1328 _copy(nel.get(),el);
1329 std::unique_ptr<Element> prev=std::move(_prevElement->_nextElement);
1330 _prevElement->_nextElement=std::move(nel);
1331 nel->_nextElement=std::move(prev);
1332}
1333
1335 Element *nexel=nullptr,*prev=nullptr;
1336
1337 switch(el->getType()){
1338 case HtmlEl:
1339 _nextElement= std::make_unique<HtmlElement>();
1340 break;
1341 case TextEl:
1342 _nextElement= std::make_unique<TextElement>();
1343 break;
1344 case CommentEl:
1345 _nextElement= std::make_unique<CommentElement>();
1346 break;
1347 case ScriptEL:
1348 _nextElement= std::make_unique<ScriptElement>();
1349 break;
1350 case SvgEL:
1351 _nextElement= std::make_unique<SvgElement>();
1352 break;
1353 case TextAreaEL:
1354 _nextElement= std::make_unique<TextArea>();
1355 break;
1356 default:
1357 HTMLException ex;
1358 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1359 throw ex;
1360 }
1361
1362 _copy(_nextElement.get(),el);
1363
1364 nexel=_nextElement.get();
1365
1366 while(nexel){
1367 prev=nexel;
1368 nexel=nexel->nextElement();
1369 }
1370
1371 nexel=prev;
1372
1373}
1374
1376 _copy(this,&hel);
1377 return *this;
1378}
1379
1381 _copy(this,hel);
1382 return *this;
1383}
1384
1386 return _nextElement.get();
1387}
1388
1390 return _prevElement;
1391}
1392
1394 return _parentElement;
1395}
1396
1398 _prevElement=nullptr;
1399 _nextElement=nullptr;
1400}
1401
1403 _prevElement=nullptr;
1404 _nextElement=nullptr;
1405 _copy(this,&el);
1406}
1407
1409 auto cur = std::move(_nextElement);
1410 while (cur) {
1411 cur = std::move(cur->_nextElement);
1412 }
1413};
1414
1416 Element *curel=this;
1417
1418 while(curel){
1419 Element *next=curel->_nextElement.get();
1420
1421 if(curel==el){
1422 // Whoever owns curel via a unique_ptr (curel->_prevElement's
1423 // own _nextElement) is about to be reassigned away from curel
1424 // below, which destroys curel immediately as part of that
1425 // assignment -- so everything needed from curel (its previous
1426 // sibling, and its own next-sibling chain, detached via
1427 // release() rather than a move that's still reachable through
1428 // curel) must be captured first, and curel must not be
1429 // touched afterward.
1430 Element *prev = curel->_prevElement;
1431 std::unique_ptr<Element> curelsNext(curel->_nextElement.release());
1432 if(next) next->_prevElement=prev;
1433 if(prev)
1434 prev->_nextElement=std::move(curelsNext); // destroys curel
1435 // else: curel has no previous sibling (e.g. `this` itself was
1436 // passed as `el`) -- the base class has no notion of a
1437 // parent's own child-pointer to repoint, so curel is left
1438 // unlinked-from but not destroyed.
1439 return;
1440 }
1441
1442 curel=next;
1443 }
1444}
1445
1446
1449
1451 setText(txt);
1452}
1453
1455 _copy(this,&texel);
1456}
1457
1460
1461
1463 _copy(this,&hel);
1464 return *this;
1465}
1466
1468 _copy(this,hel);
1469 return *this;
1470}
1471
1472void libhtmlpp::TextElement::setText(const std::string& txt){
1473 std::copy(txt.begin(),txt.end(),std::back_inserter(_Text));
1474}
1475
1477 return std::string(_Text.begin(),_Text.end());
1478}
1479
1483
1485 const std::vector<char>& in,
1486 std::unique_ptr<libhtmlpp::Element>& el,
1487 size_t start,
1488 bool &termination
1489){
1490 termination = false;
1491
1492 std::vector<char> buf;
1493 buf.reserve(64);
1494 bool seen_nonws = false;
1495 bool last_was_space = false;
1496
1497 size_t i = start;
1498 while (i < in.size()) {
1499 char c = in[i];
1500 if (c == HTMLTAG_OPEN) {
1501 break;
1502 }
1503
1504 if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
1505 if (seen_nonws) {
1506 if (!last_was_space) {
1507 buf.push_back(' ');
1508 last_was_space = true;
1509 }
1510 }
1511 ++i;
1512 continue;
1513 }
1514
1515 buf.push_back(c);
1516 seen_nonws = true;
1517 last_was_space = false;
1518 ++i;
1519 }
1520
1521 if (!buf.empty()) {
1522 auto text = std::make_unique<TextElement>();
1523 (static_cast<TextElement*>(text.get()))->_Text.insert(
1524 (static_cast<TextElement*>(text.get()))->_Text.end(),
1525 buf.begin(), buf.end()
1526 );
1527 el = std::move(text);
1528 }
1529
1530 return i;
1531}
1532
1533
1536
1538 _copy(this,&comel);
1539}
1540
1543
1544
1546 _copy(this,&hel);
1547 return *this;
1548}
1549
1551 _copy(this,hel);
1552 return *this;
1553}
1554
1555void libhtmlpp::CommentElement::setComment(const std::string& txt){
1556 std::copy(txt.begin(),txt.end(),
1557 std::insert_iterator<std::vector<char>>(_Comment,_Comment.begin()));
1558}
1559
1561 return std::string(_Comment.begin(),_Comment.end());
1562}
1563
1567
1569 const std::vector<char>& in,
1570 std::unique_ptr<Element>& el,
1571 size_t start,
1572 bool& termination
1573){
1574 termination = false;
1575
1576 size_t i = start;
1577 if (i + 3 >= in.size()) return i;
1578
1579 if (!(in[i] == '<' && in[i+1] == '!' && in[i+2] == '-' && in[i+3] == '-')) {
1580 return i;
1581 }
1582
1583 el = std::make_unique<CommentElement>();
1584
1585 i += 4;
1586 const size_t content_begin = i;
1587
1588 while (i + 2 < in.size()) {
1589 if (in[i] == '-' && in[i+1] == '-' && in[i+2] == '>') {
1590 break;
1591 }
1592 ++i;
1593 }
1594
1595 auto* self = static_cast<CommentElement*>(el.get());
1596 if (i > content_begin) {
1597 self->_Comment.insert(self->_Comment.end(),
1598 in.begin() + content_begin,
1599 in.begin() + i);
1600 }
1601
1602 if (i + 2 < in.size()) {
1603 i += 3;
1604 } else {
1605 i = in.size();
1606 }
1607 return i;
1608}
1609
1610
1613
1615 _copy(this,&scriptsrc);
1616}
1617
1620
1621
1623 _copy(this,&hel);
1624 return *this;
1625}
1626
1628 _copy(this,hel);
1629 return *this;
1630}
1631
1632void libhtmlpp::ScriptElement::setScript(const std::string& script){
1633 _Script.assign(script.begin(),script.end());
1634}
1635
1637 return std::string(_Script.begin(),_Script.end());
1638}
1639
1643
1645 const std::vector<char>& in,
1646 std::unique_ptr<Element>& el,
1647 size_t start,
1648 bool& termination
1649){
1650termination = false;
1651 el = std::make_unique<ScriptElement>();
1652 auto* self = static_cast<ScriptElement*>(el.get());
1653
1654 size_t i = start;
1655 if (i >= in.size() || in[i] != '<') {
1656 // If it doesn't start with '<', we can't parse a tag here.
1657 return start;
1658 }
1659
1660 // Helper to perform case-insensitive comparison
1661 auto iequals = [](char a, char b) {
1662 return std::tolower(static_cast<unsigned char>(a)) ==
1663 std::tolower(static_cast<unsigned char>(b));
1664 };
1665
1666 // Helper to perform case-insensitive match for a keyword starting at 'pos'
1667 auto match_ci = [&](size_t pos, const char* k) -> bool {
1668 for (size_t j = 0; k[j]; ++j) {
1669 if (pos + j >= in.size() || !iequals(in[pos + j], k[j])) {
1670 return false;
1671 }
1672 }
1673 return true;
1674 };
1675
1676 // --- 1. Validate Opening Tag Name (<script) ---
1677 ++i; // Consume '<'
1678
1679 // Skip leading whitespace after '<'
1680 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) {
1681 ++i;
1682 }
1683
1684 const char* tag_keyword = "script";
1685 size_t keyword_len = std::char_traits<char>::length(tag_keyword);
1686
1687 if (i + keyword_len >= in.size() || !match_ci(i, tag_keyword)) {
1688 // Tag name doesn't match "script"
1689 // Skip till next '>' and return the position after it.
1690 while (i < in.size() && in[i] != '>') {
1691 ++i;
1692 }
1693 if (i < in.size()) {
1694 ++i; // Consume '>'
1695 }
1696 return i;
1697 }
1698 i += keyword_len; // Consume "script"
1699
1700 // --- 2. Extract Opening Tag and Attributes ---
1701 // Find the closing '>' of the opening tag.
1702 size_t tag_end = i;
1703 while (i < in.size() && in[i] != '>') {
1704 ++i;
1705 }
1706
1707 // Capture the raw opening tag data (including '<script' and attributes) for serialization.
1708 if (i > start && in[i] == '>') {
1709 // Copy data from '<' (start) up to and including '>' (i)
1710 std::vector<char> raw_tag_data(in.begin() + start, in.begin() + i + 1);
1711 self->_serialelize(raw_tag_data);
1712 }
1713
1714 if (i >= in.size() || in[i] != '>') {
1715 // The tag was never closed (e.g., '<script src="..." EOF')
1716 return i;
1717 }
1718
1719 ++i; // Consume '>' and move to content start
1720 size_t content_begin = i;
1721
1722 // --- 3. Extract Script Content (CDATA-like section) ---
1723 for (; i < in.size(); ++i) {
1724 // Look for the start of the closing tag sequence: </script
1725 if (in[i] == '<' && match_ci(i, "</script")) {
1726 size_t content_end = i;
1727
1728 // 3a. Extract content preceding the closing tag
1729 if (content_end > content_begin) {
1730 // FIX: Use std::vector::insert instead of non-existent append
1731 self->_Script.insert(self->_Script.end(),
1732 in.begin() + content_begin,
1733 in.begin() + content_end);
1734 }
1735
1736 // 3b. Find the end of the closing tag: </script>
1737 size_t closing_tag_end_pos = i + keyword_len + 2; // +2 for '</'
1738
1739 // Skip any characters/whitespace between </script and the final '>'
1740 while (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] != '>') {
1741 ++closing_tag_end_pos;
1742 }
1743
1744 if (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] == '>') {
1745 i = closing_tag_end_pos + 1; // Position after '>'
1746 return i;
1747 }
1748
1749 // If we found "</script" but not the final ">", return the last processed position.
1750 return closing_tag_end_pos;
1751 }
1752 }
1753
1754 // --- 4. End of Input Reached ---
1755 // If the input ends without a closing </script> tag, capture the remaining content.
1756 if (in.size() > content_begin) {
1757 // FIX: Use std::vector::insert instead of non-existent append
1758 self->_Script.insert(self->_Script.end(),
1759 in.begin() + content_begin,
1760 in.end());
1761 }
1762
1763 return i;
1764}
1765
1766
1767
1770
1772 _copy(this,&svgsrc);
1773}
1774
1777
1778
1780 _copy(this,&hel);
1781 return *this;
1782}
1783
1785 _copy(this,hel);
1786 return *this;
1787}
1788
1789void libhtmlpp::SvgElement::setSvg(const std::string& script){
1790 std::copy(script.begin(),script.end(),
1791 std::insert_iterator<std::vector<char>>(_Svg,_Svg.begin()));
1792}
1793
1794const std::vector<char>libhtmlpp::SvgElement::getSvg(){
1795 return _Svg;
1796}
1797
1801
1802size_t libhtmlpp::SvgElement::parseElement(const std::vector<char>& in,
1803 std::unique_ptr<libhtmlpp::Element>& el,
1804 size_t start,
1805 bool& termination)
1806{
1807 const size_t startel = start;
1808 termination = false;
1809
1810 const auto begin = in.begin();
1811 if (start >= in.size()) {
1812 HTMLException excp;
1813 throw excp[HTMLException::Error] << "Parsing error: start offset beyond buffer.";
1814 }
1815 auto it_close_angle = std::find(begin + start, in.end(), HTMLTAG_CLOSE);
1816 if (it_close_angle == in.end()) {
1817 HTMLException excp;
1818 throw excp[HTMLException::Error] << "Parsing error: Missing '>' for <svg> open tag.";
1819 }
1820
1821 el = std::make_unique<SvgElement>();
1822 auto* svgEl = static_cast<SvgElement*>(el.get());
1823
1824 {
1825 std::vector<char> tel;
1826 tel.assign(begin + startel, it_close_angle);
1827 svgEl->_serialelize(tel, /*preserveAttrCase=*/true);
1828 }
1829
1830 auto it_content_begin = it_close_angle;
1831 if (it_content_begin != in.end()) ++it_content_begin; // safe increment
1832
1833 static constexpr char kEndTag[] = "</svg>";
1834 auto ci_eq = [](char a, char b) {
1835 auto lower = [](unsigned char c) -> unsigned char {
1836 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
1837 };
1838 return lower(static_cast<unsigned char>(a)) == lower(static_cast<unsigned char>(b));
1839 };
1840 auto it_end = std::search(it_content_begin, in.end(),
1841 std::begin(kEndTag), std::end(kEndTag) - 1 /* no '\0' */, ci_eq);
1842 if (it_end == in.end()) {
1843 HTMLException excp;
1844 throw excp[HTMLException::Error] << "Parsing error: Missing </svg> closing tag.";
1845 }
1846
1847 svgEl->_Svg.insert(svgEl->_Svg.end(), it_content_begin, it_end);
1848
1849 const size_t consumed = static_cast<size_t>((it_end - begin) + (std::size(kEndTag) - 1));
1850 return consumed;
1851}
1852
1853
1854
1857
1859 _copy(this,&textsrc);
1860}
1861
1864
1865
1867 _copy(this,&hel);
1868 return *this;
1869}
1870
1872 _copy(this,hel);
1873 return *this;
1874}
1875
1876void libhtmlpp::TextArea::setText(const std::string& text){
1877 std::copy(text.begin(),text.end(),
1878 std::insert_iterator<std::vector<char>>(_Text,_Text.begin()));
1879}
1880
1881const std::vector<char>libhtmlpp::TextArea::getText(){
1882 return _Text;
1883}
1884
1888
1889size_t libhtmlpp::TextArea::parseElement(const std::vector<char>& in,
1890 std::unique_ptr<libhtmlpp::Element>& el,
1891 size_t start,
1892 bool& termination)
1893{
1894 termination = false;
1895
1896 const auto begin = in.begin();
1897 const auto end = in.end();
1898
1899 if (start >= in.size()) {
1900 HTMLException excp;
1901 throw excp[HTMLException::Error] << "Parsing error: start offset beyond buffer.";
1902 }
1903
1904 // ---- helpers -----------------------------------------------------------
1905 auto is_ws = [](unsigned char c) {
1906 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
1907 };
1908 auto tolower_ascii = [](unsigned char c) -> unsigned char {
1909 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
1910 };
1911 auto ieq_prefix = [&](std::vector<char>::const_iterator it,
1912 std::vector<char>::const_iterator it_end,
1913 const char* lit) -> bool
1914 {
1915 for (; *lit; ++lit, ++it) {
1916 if (it == it_end) return false;
1917 if (tolower_ascii(static_cast<unsigned char>(*it)) !=
1918 tolower_ascii(static_cast<unsigned char>(*lit))) {
1919 return false;
1920 }
1921 }
1922 return true;
1923 };
1924
1925 auto it_gt = std::find(begin + start, end, HTMLTAG_CLOSE);
1926 if (it_gt == end) {
1927 HTMLException excp;
1928 throw excp[HTMLException::Error] << "Parsing error: Unclosed <textarea> tag.";
1929 }
1930
1931 el = std::make_unique<TextArea>();
1932 auto* ta = static_cast<TextArea*>(el.get());
1933 {
1934 std::vector<char> tel;
1935 tel.assign(begin + start, it_gt); // opening tag without '>'
1936 ta->_serialelize(tel);
1937 }
1938
1939 auto it = it_gt;
1940 if (it != end) ++it;
1941 const auto content_begin = it;
1942
1943 for (;;) {
1944 auto lt = std::find(it, end, '<');
1945 if (lt == end) {
1946 HTMLException excp;
1947 throw excp[HTMLException::Error] << "Parsing error: Missing </textarea> closing tag.";
1948 }
1949
1950 if (ieq_prefix(lt, end, "</textarea")) {
1951 auto after_head = lt + std::strlen("</textarea");
1952 while (after_head != end && is_ws(static_cast<unsigned char>(*after_head))) {
1953 ++after_head;
1954 }
1955 if (after_head == end) {
1956 HTMLException excp;
1957 throw excp[HTMLException::Error] << "Parsing error: Unclosed </textarea> end tag.";
1958 }
1959 if (*after_head == '>') {
1960 ta->_Text.insert(ta->_Text.end(), content_begin, lt);
1961 const size_t consumed = static_cast<size_t>((after_head - begin) + 1);
1962 return consumed;
1963 }
1964
1965 it = lt + 1;
1966 continue;
1967 }
1968
1969 it = lt + 1;
1970 }
1971}
1972
1973
1974
1978
1988void libhtmlpp::HtmlPage::loadFile(libhtmlpp::HtmlElement &html,const std::string& path){
1989 std::string data;
1990 std::ifstream fs(path);
1991
1992 if(!fs.is_open()){
1993 HTMLException excp;
1994 throw excp[HTMLException::Critical] << "Can't open file: " << path;
1995 }
1996
1997 fs.seekg(std::ios::end);
1998
1999 data.reserve(fs.tellg());
2000
2001 fs.seekg(std::ios::beg);
2002
2003 data.assign((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>());
2004
2005 fs.close();
2006
2007 _CheckHeader(data);
2008 loadString(html,data);
2009}
2018 HtmlString buf=src;
2019 Element &el=buf.parse();
2020 _copy(&html,&el);
2021}
2022
2024 HtmlString buf=node;
2025 Element &el=buf.parse();
2026 _copy(&html,&el);
2027}
2028
2030 if(!node){
2032 throw excp[libhtmlpp::HTMLException::Critical] << "loadstring: node can't be null !";
2033 }
2034 libhtmlpp::HtmlString buf=*node;
2035 html=(libhtmlpp::HtmlElement&)buf.parse();
2036}
2044void libhtmlpp::HtmlPage::saveFile(libhtmlpp::HtmlElement &html,const std::string& path){
2045 HtmlString data;
2046 std::ofstream fs;
2047
2048 print(html,data);
2049
2050 try{
2051 fs.open(path);
2052 }catch(std::exception &e){
2053 HTMLException excp;
2054 throw excp[HTMLException::Critical] << e.what();
2055 }
2056
2057 fs << data.str();
2058
2059 fs.close();
2060
2061}
2062
2064 return _Html5;
2065}
2066
2067
2068void libhtmlpp::HtmlPage::_CheckHeader(const HtmlString &page){
2069 const char type[] = { '!','D','O','C','T','Y','P','E' };
2070
2071 int i = 0;
2072
2073 bool start=false;
2074
2075 do{
2076 ++i;
2077 if(page[i]== '<'){
2078 if(start==true){
2079 HTMLException excp;
2080 excp[HTMLException::Critical] << "Wrong Header arborting";
2081 throw excp;
2082 }
2083 start=true;
2084 }
2085 } while ( page[i]== '<' || page[i]== '!' || page[i] == ' ');
2086
2087 if (page.size() < 8) {
2088 HTMLException excp;
2089 excp[HTMLException::Critical] << "No Doctype found arborting";
2090 throw excp;
2091 }
2092
2093 while (i < 8) {
2094 if (page[i+1] != type[i]) {
2095 HTMLException excp;
2096 excp[HTMLException::Critical] << "No Doctype found arborting";
2097 throw excp;
2098 }
2099 ++i;
2100 }
2101
2102 do{
2103 ++i;
2104 }while (page[i] == ' ');
2105
2106 const char doctype[] = { 'H','T','M','L' };
2107 size_t tpvl = 4;
2108
2109 const char typevalue4[] = {'P','U','B','L','I','C'};
2110 size_t tpvl4 = 6;
2111
2112 if ((i + tpvl) > page.size()) {
2113 HTMLException excp;
2114 excp[HTMLException::Critical] << "Document to short broken !";
2115 throw excp;
2116 }
2117
2118 size_t ii=0;
2119
2120 while ( ii < tpvl) {
2121 if (tolower(page[i++]) != tolower(doctype[ii++])) {
2122 HTMLException excp;
2123 excp[HTMLException::Critical] << "Doctype header broken or wrong type";
2124 throw excp;
2125 }
2126 }
2127
2128 ii=0;
2129
2130 do{
2131 ++i;
2132 } while (page[i] == ' ');
2133
2134 bool html4=true;
2135
2136 if(i +tpvl4 <page.size()){
2137 while(ii < tpvl4){
2138 if (tolower(page[i++]) != tolower(typevalue4[ii++])) {
2139 html4=false;
2140 }
2141 }
2142 }
2143
2144 _Html5=!html4;
2145
2146}
2154void libhtmlpp::print(const Element &element, HtmlString &output,bool formated) {
2155
2156 const Element *el=&element;
2157
2158 // Emit <!DOCTYPE html> when the root element is <html>
2159 if (el->getType() == HtmlEl) {
2160 const std::string &tag = static_cast<const HtmlElement*>(el)->getTagname();
2161 if (tag == "html") {
2162 output.append("<!DOCTYPE html>");
2163 if (formated)
2164 output.append("\n");
2165 }
2166 }
2167
2168 auto isContainer = [](const std::string &tagname) {
2169 for(size_t i=0; i<ContainerTypes.size(); ++i){
2170 if(tagname==ContainerTypes[i])
2171 return true;
2172 }
2173 return false;
2174 };
2175
2176 std::stack<const libhtmlpp::Element*> cpylist;
2177
2178 int lvl=0;
2179
2180 bool virgin=true;
2181
2182 PRINTNEXTEL:
2183
2184 if(formated){
2185 for(int i=0; i<lvl; ++i){
2186 output.append(" ");
2187 }
2188 }
2189
2190 switch(el->getType()){
2191 case HtmlEl:{
2192 output.append("<");
2193 output.append(static_cast<const HtmlElement*>(el)->getTagname());
2194 for (HtmlElement::Attributes* curattr = static_cast<const HtmlElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2195 output.append(" ");
2196 std::copy(
2197 curattr->_Key.begin(),
2198 curattr->_Key.end(),
2199 std::back_inserter(output)
2200 );
2201 if(!curattr->_Value.empty()){
2202 output.append("=\"");
2203 std::copy(
2204 curattr->_Value.begin(),
2205 curattr->_Value.end(),
2206 std::back_inserter(output)
2207 );
2208 output.append("\"");
2209 }
2210 }
2211
2212 output.append(">");
2213
2214 if( !formated && virgin ){
2215 output.append("\r\n ");
2216 virgin=false;
2217 }
2218
2219 if (static_cast<const HtmlElement*>(el)->_childElement) {
2220 if(formated)
2221 output.append("\r\n");
2222 cpylist.push(el);
2223 el=static_cast<const HtmlElement*>(el)->_childElement.get();
2224 ++lvl;
2225 goto PRINTNEXTEL;
2226 }
2227
2228 //Container must be always terminated fuck html5
2229 if(isContainer(static_cast<const HtmlElement*>(el)->getTagname())){
2230 output.append("</");
2231 std::copy(
2232 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2233 static_cast<const HtmlElement*>(el)->_TagName.end(),
2234 std::back_inserter(output)
2235 );
2236 output.append(">");
2237 }
2238
2239 if(formated)
2240 output.append("\r\n");
2241
2242 if (el->_nextElement) {
2243 el=el->_nextElement.get();
2244 goto PRINTNEXTEL;
2245 }
2246 }break;
2247
2248 case TextEl :{
2249 std::copy(
2250 static_cast<const TextElement*>(el)->_Text.begin(),
2251 static_cast<const TextElement*>(el)->_Text.end(),
2252 std::back_inserter(output)
2253 );
2254 if(formated)
2255 output.append("\r\n");
2256
2257 if (el->_nextElement) {
2258 el=el->_nextElement.get();
2259 goto PRINTNEXTEL;
2260 }
2261 }break;
2262 case CommentEl: {
2263 output.append("<!--");
2264 std::copy(
2265 static_cast<const CommentElement*>(el)->_Comment.begin(),
2266 static_cast<const CommentElement*>(el)->_Comment.end(),
2267 std::back_inserter(output)
2268 );
2269 output.append("-->");
2270 if(formated)
2271 output.append("\r\n");
2272
2273 if (el->_nextElement) {
2274 el=el->_nextElement.get();
2275 goto PRINTNEXTEL;
2276 }
2277 }break;
2278 case ScriptEL:{
2279 output.append("<");
2280 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2281 for (ScriptElement::Attributes* curattr = static_cast<const ScriptElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2282 output.append(" ");
2283 std::copy(
2284 curattr->_Key.begin(),
2285 curattr->_Key.end(),
2286 std::back_inserter(output)
2287 );
2288 if(!curattr->_Value.empty()){
2289 output.append("=\"");
2290 std::copy(
2291 curattr->_Value.begin(),
2292 curattr->_Value.end(),
2293 std::back_inserter(output)
2294 );
2295 output.append("\"");
2296 }
2297 }
2298
2299 output.append(">");
2300 if(formated){
2301 output.append("\r\n");
2302 for(int i=0; i<lvl+1; ++i){
2303 output.append(" ");
2304 }
2305 }
2306 std::copy(
2307 static_cast<const ScriptElement*>(el)->_Script.begin(),
2308 static_cast<const ScriptElement*>(el)->_Script.end(),
2309 std::back_inserter(output)
2310 );
2311 if(formated){
2312 output.append("\r\n");
2313 for(int i=0; i<lvl; ++i){
2314 output.append(" ");
2315 }
2316 }
2317 output.append("</");
2318 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2319 output.append(">");
2320 if(formated)
2321 output.append("\r\n");
2322
2323 if (el->_nextElement) {
2324 el=el->_nextElement.get();
2325 goto PRINTNEXTEL;
2326 }
2327 }break;
2328 case SvgEL:{
2329 output.append("<");
2330 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2331 for (SvgElement::Attributes* curattr = static_cast<const SvgElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2332 output.append(" ");
2333 std::copy(
2334 curattr->_Key.begin(),
2335 curattr->_Key.end(),
2336 std::back_inserter(output)
2337 );
2338 if(!curattr->_Value.empty()){
2339 output.append("=\"");
2340 std::copy(
2341 curattr->_Value.begin(),
2342 curattr->_Value.end(),
2343 std::back_inserter(output)
2344 );
2345 output.append("\"");
2346 }
2347 }
2348 output.append(">");
2349
2350 if(formated){
2351 output.append("\r\n");
2352 for(int i=0; i<lvl+1; ++i){
2353 output.append(" ");
2354 }
2355 }
2356 std::copy(
2357 static_cast<const SvgElement*>(el)->_Svg.begin(),
2358 static_cast<const SvgElement*>(el)->_Svg.end(),
2359 std::back_inserter(output)
2360 );
2361 if(formated){
2362 output.append("\r\n");
2363 for(int i=0; i<lvl; ++i){
2364 output.append(" ");
2365 }
2366 }
2367 output.append("</");
2368 output.append(static_cast<const SvgElement*>(el)->getTagname());
2369 output.append(">");
2370 if(formated)
2371 output.append("\r\n");
2372
2373 if (el->_nextElement) {
2374 el=el->_nextElement.get();
2375 goto PRINTNEXTEL;
2376 }
2377 }break;
2378 case TextAreaEL:{
2379 output.append("<");
2380 output.append(static_cast<const TextArea*>(el)->getTagname());
2381 for (TextArea::Attributes* curattr = static_cast<const TextArea*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2382 output.append(" ");
2383 std::copy(
2384 curattr->_Key.begin(),
2385 curattr->_Key.end(),
2386 std::back_inserter(output)
2387 );
2388 if(!curattr->_Value.empty()){
2389 output.append("=\"");
2390 std::copy(
2391 curattr->_Value.begin(),
2392 curattr->_Value.end(),
2393 std::back_inserter(output)
2394 );
2395 output.append("\"");
2396 }
2397 }
2398 output.append(">");
2399 std::copy(
2400 static_cast<const TextArea*>(el)->_Text.begin(),
2401 static_cast<const TextArea*>(el)->_Text.end(),
2402 std::back_inserter(output)
2403 );
2404 output.append("</");
2405 output.append(static_cast<const TextArea*>(el)->getTagname());
2406 output.append(">");
2407 if(formated)
2408 output.append("\r\n");
2409
2410 if (el->_nextElement) {
2411 el=el->_nextElement.get();
2412 goto PRINTNEXTEL;
2413 }
2414 }break;
2415 default:
2416 HTMLException excp;
2417 excp[HTMLException::Error] << "Unkown Elementtype";
2418 throw excp;
2419 break;
2420 }
2421
2422 while(!cpylist.empty()){
2423 el=cpylist.top();
2424
2425 --lvl;
2426
2427 if(formated){
2428 for(int i=0; i<lvl; ++i){
2429 output.append(" ");
2430 }
2431 }
2432
2433 output.append("</");
2434 std::copy(
2435 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2436 static_cast<const HtmlElement*>(el)->_TagName.end(),
2437 std::back_inserter(output)
2438 );
2439 output.append(">");
2440
2441 if(formated)
2442 output.append("\r\n");
2443
2444 cpylist.pop();
2445 if (el->_nextElement) {
2446 el=el->_nextElement.get();
2447 goto PRINTNEXTEL;
2448 }
2449 }
2450}
2451
2453 std::stack <Element*> childs;
2454 const Element *curel=this;
2455 SEARCHBYID:
2456 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2457 if(((HtmlElement*)curel)->_childElement){
2458 childs.push(((HtmlElement*)curel)->_childElement.get());
2459 }
2460 std::string idname=((HtmlElement*)curel)->getAtributte("id");
2461 if(idname.length()==id.length() && std::equal(id.begin(),id.end(),idname.begin()) ){
2462 return (HtmlElement*)curel;
2463 }
2464 }
2465
2466 if(curel->nextElement()){
2467 curel=curel->nextElement();
2468 goto SEARCHBYID;
2469 }
2470
2471 if(!childs.empty()){
2472 curel=childs.top();
2473 childs.pop();
2474 goto SEARCHBYID;
2475 }
2476 return nullptr;
2477}
2478
2480 std::stack <Element*> childs;
2481 const Element *curel=this;
2482 SEARCHBYTAG:
2483 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2484 if(((HtmlElement*)curel)->_childElement){
2485 childs.push(((HtmlElement*)curel)->_childElement.get());
2486 }
2487 const std::string tname=((HtmlElement*)curel)->getTagname();
2488 if(!tname.empty() && std::equal(tag.begin(),tag.end(),tname.begin())){
2489 return (HtmlElement*)curel;
2490 }
2491 }
2492
2493 if(curel->nextElement()){
2494 curel=curel->nextElement();
2495 goto SEARCHBYTAG;
2496 }
2497
2498 if(!childs.empty()){
2499 curel=childs.top();
2500 childs.pop();
2501 goto SEARCHBYTAG;
2502 }
2503 return nullptr;
2504}
2505
2507 return _childElement.get();
2508}
2509
2511 return std::string(_Key.begin(), _Key.end());
2512}
2513
2515 return std::string(_Value.begin(), _Value.end());
2516}
2517
2521
2523 return _firstAttr.get();
2524}
2525
2526void libhtmlpp::HtmlElement::setAttribute(const std::string &name, const std::string &value) {
2527 Attributes* cattr = nullptr;
2528
2529 const char forbidden[] = {'\"'};
2530
2531 auto checkForbidden = [forbidden](const std::string &input){
2532 for(size_t i = 0; i<input.length(); ++i){
2533 for(size_t ii=0; ii<sizeof(forbidden[ii]); ii++){
2534 if(input[i]==forbidden[ii]){
2535 return true;
2536 }
2537 }
2538 }
2539 return false;
2540 };
2541
2542 if(checkForbidden(name) || checkForbidden(value)){
2543 HTMLException e;
2544 e[HTMLException::Error] << "setAttribute " << name.c_str() << "forbidden sign is used !";
2545 throw e;
2546 }
2547
2548 for (cattr= _firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()) {
2549 if(name.size() == cattr->_Key.size() && std::equal(name.begin(),name.end(),cattr->_Key.begin())){
2550 cattr->_Value.clear();
2551 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2552 return;
2553 }
2554 }
2555 if (_lastAttr){
2556 _lastAttr->_nextAttr = std::make_unique<Attributes>();
2557 _lastAttr = _lastAttr->_nextAttr.get();
2558 }else {
2559 _firstAttr = std::make_unique<Attributes>();
2560 _lastAttr = _firstAttr.get();
2561 }
2562
2563 cattr = _lastAttr;
2564 std::copy(name.begin(),name.end(),std::back_inserter(cattr->_Key) );
2565 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2566}
2567
2568void libhtmlpp::HtmlElement::setIntAttribute(const std::string& name, int value) {
2569 char buf[255];
2570 snprintf(buf,255,"%d",value);
2571 setAttribute(name,buf);
2572}
2573
2574const std::string libhtmlpp::HtmlElement::getAtributte(const std::string& name) const {
2575 for (Attributes* curattr = _firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2576
2577 if (curattr->_Key.size() != name.length() ) {
2578 continue;
2579 }
2580
2581 if (std::equal(name.begin(), name.end(), curattr->_Key.begin())) {
2582 return std::string(curattr->_Value.begin(), curattr->_Value.end());
2583 }
2584 }
2585 return "";
2586}
2587
2588int libhtmlpp::HtmlElement::getIntAtributte(const std::string& name) const
2589{
2590 return atoi(getAtributte(name).c_str());
2591}
2592
2594 _nextAttr=nullptr;
2595}
2596
2598 auto cur = std::move(_nextAttr);
2599 while (cur) {
2600 cur = std::move(cur->_nextAttr);
2601 }
2602}
2603
2607
2608
2610 _firstRow=nullptr;
2611 _lastRow=nullptr;
2612 _count = 0;
2613}
2614
2617
2619 std::unique_ptr<Row> newRow = std::make_unique<Row>(row);
2620
2621 if (!_firstRow) {
2622 _firstRow = std::move(newRow);
2623 _lastRow = _firstRow.get();
2624 } else {
2625 _lastRow->_nextRow = std::move(newRow);
2626 _lastRow = _lastRow->_nextRow.get();
2627 }
2628
2629 ++_count;
2630 return *_lastRow;
2631}
2632
2634 if(!_firstRow || _count<pos){
2636 exp[HTMLException::Error] << "HtmlTable: Row at this position won't exists !";
2637 throw exp;
2638 }
2639 size_t cpos=0;
2640 Row *curel=nullptr;
2641 for(curel=_firstRow.get(); curel; curel=curel->_nextRow.get()){
2642 if(cpos==pos)
2643 return *curel;
2644 ++cpos;
2645 }
2646 return *curel;
2647}
2648
2650 element->setTagname("table");
2651 for(Row *crow=_firstRow.get(); crow; crow=crow->_nextRow.get()){
2652 HtmlElement hrow("tr");
2653 for(Column *ccol=crow->_firstColumn.get(); ccol; ccol=ccol->_nextColumn.get() ){
2654 HtmlElement hcol("td");
2655 TextElement cellContent(ccol->Data.c_str());
2656 hcol.appendChild(cellContent);
2657 hrow.appendChild(hcol);
2658 }
2659 element->appendChild(&hrow);
2660 }
2661}
2662
2663
2666
2668 va_list args;
2669 va_start(args,count);
2670
2671 for (int i = 0; i < count; ++i) {
2672 _header << va_arg(args, const char*);
2673 }
2674
2675}
2676
2678 if (pos >= _count)
2679 return;
2680 if (pos == 0) {
2681 _firstRow = std::move(_firstRow->_nextRow);
2682 _lastRow = (pos == (_count - 1)) ? nullptr : _firstRow.get();
2683 } else {
2684 Row *prev = &(*this)[pos - 1];
2685 if (prev->_nextRow) {
2686 prev->_nextRow = std::move(prev->_nextRow->_nextRow);
2687
2688 if (prev->_nextRow.get() == nullptr) {
2689 _lastRow = prev;
2690 }
2691 }
2692 }
2693
2694 --_count;
2695 if (_count == 0) {
2696 _firstRow = nullptr;
2697 _lastRow = nullptr;
2698 }
2699}
2700
2702 _nextColumn=nullptr;
2703}
2704
2706 _nextColumn=nullptr;
2707 Data = col.Data;
2708}
2709
2711 _nextColumn=nullptr;
2712 Data=data;
2713}
2714
2716 _nextColumn=nullptr;
2717 Data=data.str();
2718}
2719
2722
2724 _nextColumn = std::move(col._nextColumn);
2725 Data = std::move(col.Data);
2726}
2727
2729 _firstColumn=nullptr;
2730 _lastColumn=nullptr;
2731 _nextRow=nullptr;
2732 _count=0;
2733}
2734
2737
2739 _firstColumn=nullptr;
2740 _lastColumn=nullptr;
2741 _nextRow=nullptr;
2742 _count=0;
2743
2744 for(Column *curel=row._firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2745 *this << HtmlString(curel->Data);
2746 }
2747}
2748
2750 std::unique_ptr<Column> ptr = std::make_unique<Column>(std::move(col));
2751
2752 if(_firstColumn){
2753 _lastColumn->_nextColumn=std::move(ptr);
2754 _lastColumn=_lastColumn->_nextColumn.get();
2755 }else{
2756 _firstColumn= std::move(ptr);
2757 _lastColumn=_firstColumn.get();
2758 }
2759 ++_count;
2760 return *this;
2761}
2762
2764 std::unique_ptr<Column> ptr = std::make_unique<Column>(col);
2765
2766 if(_firstColumn){
2767 _lastColumn->_nextColumn=std::move(ptr);
2768 _lastColumn=_lastColumn->_nextColumn.get();
2769 }else{
2770 _firstColumn= std::move(ptr);
2771 _lastColumn=_firstColumn.get();
2772 }
2773 ++_count;
2774 return *this;
2775}
2776
2778 Column col(value);
2779 *this << col;
2780 return *this;
2781}
2782
2784 Column col(value);
2785 *this << col;
2786 return *this;
2787}
2788
2789
2791 HtmlString buf;
2792 buf << value;
2793 *this << buf;
2794 return *this;
2795}
2796
2798 char buf[255];
2799 snprintf(buf,255,"%d",value);
2800 return *this << buf;
2801}
2802
2804 if(!_firstColumn || _count<pos){
2806 exp[HTMLException::Error] << "HtmlTable: Column at this position won't exists !";
2807 throw exp;
2808 }
2809 size_t cpos=0;
2810 Column *curel=nullptr;
2811 for(curel=_firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2812 if(cpos==pos)
2813 return *curel;
2814 ++cpos;
2815 }
2816 return *curel;
2817}
2818
2820 Column *dcol=&(*this)[pos];
2821 try{
2822 Column *prev=&(*this)[pos-1];
2823 prev->_nextColumn=std::move(dcol->_nextColumn);
2824 }catch(...){}
2825 --_count;
2826}
2827
2829 _firstColumn.reset();
2830 _lastColumn = nullptr;
2831 _count = 0;
2832}
Leaf node representing an HTML comment ().
Definition html.h:230
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1568
CommentElement & operator=(const Element &hel)
Definition html.cpp:1545
std::vector< char > _Comment
Definition html.h:246
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1102
void setComment(const std::string &txt)
Definition html.cpp:1555
const std::string getComment()
Definition html.cpp:1560
class DocElements * prevel
Definition html.cpp:170
std::unique_ptr< Element > element
Definition html.cpp:167
std::unique_ptr< DocElements > nextel
Definition html.cpp:169
Abstract base class for all nodes in the HTML tree.
Definition html.h:76
virtual void remove(Element *el)
Definition html.cpp:1415
Element * _prevElement
Definition html.h:100
void insertAfter(Element *el)
Definition html.cpp:1334
virtual ~Element()
Definition html.cpp:1408
Element * _parentElement
Definition html.h:101
void insertBefore(Element *el)
Definition html.cpp:1302
Element & operator=(const Element &hel)
Definition html.cpp:1375
std::unique_ptr< Element > _nextElement
Definition html.h:99
virtual int getType() const =0
Definition html.cpp:751
Element * prevElement() const
Definition html.cpp:1389
Element * parentElement() const
Definition html.cpp:1393
Element * nextElement() const
Definition html.cpp:1385
const char * what() const noexcept override
Definition exception.cpp:51
int getType() const
Definition html.cpp:2604
const Attributes * firstAttribute() const
Definition html.cpp:2522
HtmlElement * getElementbyTag(const std::string &tag) const
Definition html.cpp:2479
const std::string getAtributte(const std::string &name) const
Definition html.cpp:2574
void _serialelize(const std::vector< char > &in, bool preserveAttrCase=false)
Extracts tag name and attributes from a token vector into an HtmlElement.
Definition html.cpp:947
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1059
void appendChild(const Element *el)
Definition html.cpp:821
int getIntAtributte(const std::string &name) const
Definition html.cpp:2588
void setTagname(const std::string &name)
Definition html.cpp:755
void setAttribute(const std::string &name, const std::string &value)
Definition html.cpp:2526
void remove(Element *el)
Definition html.cpp:898
bool operator==(const HtmlElement *hel)
Definition html.cpp:870
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1102
void insertChild(const Element *el)
Definition html.cpp:784
Element * firstChild() const
Definition html.cpp:2506
const std::string getTagname() const
Definition html.cpp:760
HtmlElement * getElementbyID(const std::string &id) const
Definition html.cpp:2452
HtmlElement & operator=(const HtmlElement &hel)
Definition html.cpp:888
std::unique_ptr< Element > _childElement
Definition html.h:171
void setIntAttribute(const std::string &name, int value)
Definition html.cpp:2568
void loadString(libhtmlpp::HtmlElement &html, const std::string &src)
Parses an HTML source string and copies the result into html.
Definition html.cpp:2017
void saveFile(libhtmlpp::HtmlElement &html, const std::string &path)
Serializes an HtmlElement subtree and writes it to a file.
Definition html.cpp:2044
void loadFile(libhtmlpp::HtmlElement &html, const std::string &path)
Loads an HTML file from disk into a given HtmlElement root.
Definition html.cpp:1988
const std::vector< char > & data() const
Definition html.cpp:360
void append(const std::string &src)
Definition html.cpp:230
HtmlString & operator<<(const char *src)
Definition html.cpp:293
HtmlString & operator+=(const std::string &src)
Definition html.cpp:267
size_t size() const
Definition html.cpp:336
char operator[](size_t pos) const
Definition html.cpp:289
void push_back(const char src)
Definition html.cpp:218
void insert(size_t pos, char src)
Definition html.cpp:253
const char * c_str() const
Definition html.cpp:356
size_t length() const
Definition html.cpp:332
libhtmlpp::Element & parse()
Parses the current buffer into a DOM-like tree and returns the root element.
Definition html.cpp:365
const char * operator*()
Definition html.cpp:328
HtmlString & operator=(const std::string &src)
Definition html.cpp:277
const std::string str() const
Definition html.cpp:350
Row & operator<<(Column &&col)
Definition html.cpp:2749
Column & operator[](size_t pos)
Definition html.cpp:2803
void delColumn(size_t pos)
Definition html.cpp:2819
Row & operator[](size_t pos)
Definition html.cpp:2633
Row & operator<<(const Row &row)
Definition html.cpp:2618
void deleteRow(size_t pos)
Definition html.cpp:2677
void parse(HtmlElement *element)
Definition html.cpp:2664
void insert(HtmlElement *element)
Definition html.cpp:2649
void setHeader(int count,...)
Definition html.cpp:2667
Element representing a <script> tag and its text content.
Definition html.h:255
void setScript(const std::string &txt)
Definition html.cpp:1632
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1644
ScriptElement & operator=(const Element &hel)
Definition html.cpp:1622
std::vector< char > _Script
Definition html.h:279
const std::string getScript()
Definition html.cpp:1636
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1102
Element representing an embedded <svg> tag and its attributes/content.
Definition html.h:288
int getType() const
Definition html.cpp:1798
SvgElement & operator=(const Element &hel)
Definition html.cpp:1779
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1802
const std::vector< char > getSvg()
Definition html.cpp:1794
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1102
std::vector< char > _Svg
Definition html.h:312
void setSvg(const std::string &svg)
Definition html.cpp:1789
Element representing an embedded <textarea> tag and its attributes/content.
Definition html.h:322
std::vector< char > _Text
Definition html.h:346
TextArea & operator=(const Element &hel)
Definition html.cpp:1866
const std::vector< char > getText()
Definition html.cpp:1881
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1102
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1889
int getType() const
Definition html.cpp:1885
void setText(const std::string &text)
Definition html.cpp:1876
Leaf node representing plain text content of an HTML document.
Definition html.h:203
TextElement & operator=(const Element &hel)
Definition html.cpp:1462
std::vector< char > _Text
Definition html.h:221
void setText(const std::string &txt)
Definition html.cpp:1472
int getType() const
Definition html.cpp:1480
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1102
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1484
const std::string getText()
Definition html.cpp:1476
#define HTMLTAG_TERMINATE
Definition html.cpp:52
#define HTMLTAG_CLOSE
Definition html.cpp:53
#define HTMLTAG_OPEN
Definition html.cpp:51
std::ostream & operator<<(std::ostream &os, const libhtmlpp::HtmlString &p)
Streams an HtmlString to an output stream using its underlying string.
Definition html.cpp:683
void loadString(libhtmlpp::HtmlElement &html, const libhtmlpp::HtmlString *node)
Definition html.cpp:2029
Public declarations for libhtmlpp HTML element types and utilities.
Core namespace for the libhtmlpp HTML parsing and printing library.
Definition css.h:36
@ TextEl
Definition html.h:65
@ ScriptEL
Definition html.h:68
@ HtmlEl
Definition html.h:66
@ TextAreaEL
Definition html.h:70
@ SvgEL
Definition html.h:69
@ CommentEl
Definition html.h:67
void print(const Element &element, HtmlString &output, bool formated=false)
Serializes an element (and its subtree) into an HtmlString.
Definition html.cpp:2154
void HtmlEncode(const std::string &input, std::string &output)
Encodes special HTML characters in a string and writes into std::string.
Definition html.cpp:688
const std::array< std::string_view, 100 > ContainerTypes
Definition html.cpp:62
void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1102
const char * HtmlSigns[][2]
Definition encode.h:31
void HtmlDecode(const std::string &input, HtmlString &output)
Decodes special HTML characters in a string and appends to an HtmlString.
Definition html.cpp:722
const std::string getValue() const
Definition html.cpp:2514
Attributes * nextAttribute() const
Definition html.cpp:2518
const std::string getKey() const
Definition html.cpp:2510