28 #ifdef ENABLE_DOCSERVER
39 #include "libmicrohttpd/microhttpd.h"
40 #include "libmicrohttpd/platform.h"
45 #define DOCSERVER_NAME "ARTS built-in documentation server"
47 #define DS_ERROR_404 "Page not found"
49 static int ahc_echo(
void* cls,
50 struct MHD_Connection* connection,
54 const char* upload_data,
55 size_t* upload_data_size,
68 if (indent.length() + curline.str().length() + token.str().length() >
70 get_os() << curline.str() << endl << indent;
73 curline << token.str();
86 string Docserver::new_page(
const string& url) {
89 if (surl.find(get_baseurl()) == 0) surl.erase(0, get_baseurl().size());
93 while (tokens.size() && tokens[tokens.size() - 1] ==
"") tokens.pop_back();
95 while (tokens.size() && tokens[0] ==
"") tokens.erase(tokens.begin());
97 string content_type =
"text/html; charset=utf-8";
98 if (tokens.size() && tokens[tokens.size() - 1] ==
"styles.css") {
100 content_type =
"text/css; charset=utf-8";
102 switch (tokens.size()) {
111 insert_error(DS_ERROR_404);
127 void Docserver::split_tokens(
const string& s) {
132 while (getline(ss, item,
'/')) tokens.push_back(item);
146 string Docserver::html_escape_char(
const char ch) {
173 string Docserver::html_escape_string(
const string& s) {
176 for (string::const_iterator it = s.begin(); it != s.end(); it++)
177 ret.append(html_escape_char(*it));
188 void Docserver::begin_content() {
189 get_os() <<
"<div class=\"content\">" << endl;
198 void Docserver::end_content() { get_os() <<
"</div>" << endl; }
208 void Docserver::begin_page(
string title) {
209 if (title.length()) title +=
" - ";
211 title += DOCSERVER_NAME;
214 <<
"<!DOCTYPE html>" << endl
215 <<
"<html lang=\"en\">" << endl
217 <<
"<title>" << title <<
"</title>" << endl
218 <<
"<meta charset=\"utf-8\">" << endl
219 <<
"<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />"
221 <<
"<link rel=\"stylesheet\" href=\"" << mbaseurl <<
"/styles.css\">"
233 void Docserver::end_page() {
238 get_os() <<
"</body>" << endl <<
"</html>";
251 String Docserver::insert_agenda_link(
const String& aname) {
253 ret <<
"<a href=\"" << mbaseurl <<
"/agendas/" << aname <<
"\">" << aname
268 String Docserver::insert_group_link(
const String& gname) {
270 ret <<
"<a href=\"" << mbaseurl <<
"/groups/" << gname <<
"\">" << gname
285 String Docserver::insert_wsm_link(
const String& mname) {
287 ret <<
"<a href=\"" << mbaseurl <<
"/methods/" << mname <<
"\">" << mname
302 String Docserver::insert_wsv_link(
const String& vname) {
309 ret <<
"<a href=\"" << mbaseurl <<
"/agendas/" << vname <<
"\">" << vname
312 ret <<
"<a href=\"" << mbaseurl <<
"/variables/" << vname <<
"\">"
325 void Docserver::list_agendas() {
328 get_os() <<
"<h2>Agendas</h2>" << endl;
330 get_os() <<
"<div class=\"firstcol\">" << endl <<
"<ul>" << endl;
343 if (hitcount2 == hitcount / 2)
344 get_os() <<
"</ul>" << endl
346 <<
"<div class=\"secondcol\">" << endl
351 get_os() <<
"</ul>" << endl <<
"</div>" << endl;
360 void Docserver::list_groups() {
364 get_os() <<
"<h2>Workspace Groups</h2>" << endl;
366 get_os() <<
"<div class=\"firstcol\">" << endl <<
"<ul>" << endl;
372 get_os() <<
"</ul>" << endl
374 <<
"<div class=\"secondcol\">" << endl
378 get_os() <<
"</ul>" << endl <<
"</div>" << endl;
387 void Docserver::list_methods() {
391 get_os() <<
"<h2>Workspace Methods</h2>" << endl;
393 get_os() <<
"<div class=\"firstcol\">" << endl <<
"<ul>" << endl;
395 get_os() <<
"<li>" << insert_wsm_link(
md_data_raw[i].Name()) <<
"</li>"
399 get_os() <<
"</ul>" << endl
401 <<
"<div class=\"secondcol\">" << endl
405 get_os() <<
"</ul>" << endl <<
"</div>" << endl;
414 void Docserver::list_variables() {
417 get_os() <<
"<h2>Workspace Variables</h2>" << endl;
419 get_os() <<
"<div class=\"firstcol\">" << endl <<
"<ul>" << endl;
432 if (hitcount2 == hitcount / 2)
433 get_os() <<
"</ul>" << endl
435 <<
"<div class=\"secondcol\">" << endl
440 get_os() <<
"</ul>" << endl <<
"</div>" << endl;
454 String Docserver::description_add_links(
const String& desc,
458 bool inside_link =
false;
459 string::const_iterator it = desc.begin();
464 while (it != desc.end()) {
469 ret += html_escape_char(*it);
474 ret += insert_wsm_link(link);
476 ret += insert_agenda_link(link);
478 ret += insert_wsv_link(link);
480 ret += insert_group_link(link);
481 else if (mname !=
"") {
486 map<String, Index>::const_iterator mit =
MdRawMap.find(mname);
491 for (ArrayOfString::const_iterator sit = mdr.
GIn().begin();
492 !found && sit != mdr.
GIn().end();
494 if ((*sit) == link) {
495 ret +=
"*" + link +
"*";
500 for (ArrayOfString::const_iterator sit = mdr.
GOut().begin();
501 !found && sit != mdr.
GOut().end();
503 if ((*sit) == link) {
504 ret +=
"*" + link +
"*";
511 ret +=
"<span class=\"brokendoclink\">*" + link +
"*</span>";
513 ret +=
"<span class=\"brokendoclink\">*" + link +
"*</span>";
517 if (!isalnum(*it) && *it !=
'_') {
519 ret +=
"*" + link + *it;
522 link += html_escape_char(*it);
529 if (inside_link) ret +=
"*" + link;
543 void Docserver::doc_method(
const string& mname) {
553 map<String, Index>::const_iterator it =
MdRawMap.find(mname);
559 get_os() <<
"<h3>Description</h3>" << endl;
561 get_os() <<
"<pre>" << endl;
562 get_os() << description_add_links(mdr.
Description(), mname);
563 get_os() << endl <<
"</pre>" << endl << endl;
565 bool is_first_author =
true;
567 if (is_first_author) {
568 get_os() <<
"<p><b>Authors: </b>";
569 is_first_author =
false;
578 while (indent.length() < mdr.
Name().length() + 2) indent +=
' ';
580 get_os() <<
"<h3>Synopsis</h3>" << endl;
584 const size_t linelen = 2048;
586 buf <<
"<p><table><tr><td>" << mdr.
Name() <<
"( </td><td>";
604 if (mdr.
GOut()[i].length())
605 param << mdr.
GOut()[i];
607 param <<
"gout" << i;
630 if (mdr.
GIn()[i].length())
631 param << mdr.
GIn()[i];
637 if (buf.str().length()) get_os() << buf.str();
639 get_os() <<
" )</td></tr></table>" << endl;
641 get_os() <<
"<h3>Variables</h3>" << endl;
646 get_os() <<
"<table>" << endl;
650 if (std::find(mdr.
In().begin(), mdr.
In().end(), mdr.
Out()[i]) ==
652 buf <<
"<td>OUT</td>";
654 buf <<
"<td>OUT+IN</td>";
658 buf <<
"<td class=\"right\">" << insert_wsv_link(vname) <<
"</td><td>(";
659 buf << insert_group_link(
667 if (buf.str().length() + desc.length() > linelen) {
669 buf << endl << indent << description_add_links(desc);
671 buf << description_add_links(desc);
674 get_os() << buf.str() <<
"</td></tr>" << endl;
682 buf <<
"<td>GOUT</td><td class=\"right\">" << mdr.
GOut()[i]
687 bool firstarg =
true;
702 lastlen = desc.length();
710 buf << endl << indent << description_add_links(desc);
712 if (lastlen + desc.length() > linelen) {
714 buf << endl << description_add_links(desc);
716 buf << description_add_links(desc);
719 get_os() << buf.str() <<
"</td></tr>" << endl;
723 if (std::find(mdr.
Out().begin(), mdr.
Out().end(), mdr.
In()[i]) !=
729 buf <<
"<td>IN</td>";
732 buf <<
"<td class=\"right\">" << insert_wsv_link(vname);
734 buf << insert_group_link(
741 if (buf.str().length() + desc.length() > linelen) {
743 buf << endl << indent << description_add_links(desc);
745 buf << description_add_links(desc);
747 get_os() << buf.str() <<
"</td></tr>" << endl;
753 buf <<
"<td>GIN</td><td class=\"right\">" << mdr.
GIn()[i] <<
"</td><td>(";
756 bool firstarg =
true;
769 buf <<
", Default: ";
779 lastlen = desc.length();
787 buf << indent << description_add_links(desc);
788 }
else if (lastlen + desc.length() > linelen) {
790 buf << indent << description_add_links(desc);
792 buf << description_add_links(desc);
795 get_os() << buf.str() <<
"</td></tr>" << endl;
797 get_os() <<
"</table>" << endl;
799 insert_error_message(
"There is no method by this name.");
812 void Docserver::doc_variable_methods(
const string& vname) {
818 Index wsv_key = mi->second;
823 get_os() <<
"<h3>Specific methods that can generate " << vname <<
"</h3>"
833 if (count(mdd.
Out().begin(), mdd.
Out().end(), wsv_key)) {
834 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) <<
"\n";
838 if (0 == hitcount) get_os() <<
"<li>none\n";
840 get_os() << endl <<
"</ul>" << endl;
843 get_os() <<
"<h3>Generic and supergeneric methods that can generate "
844 << vname <<
"</h3>" << endl;
845 get_os() <<
"<ul>" << endl;
857 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
859 }
else if (count(mdd.
GOutType().begin(),
868 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
872 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
879 if (0 == hitcount) get_os() <<
"<li>none" << endl;
881 get_os() << endl <<
"</ul>" << endl;
885 get_os() <<
"<h3>Specific methods that require " << vname <<
"</h3>" << endl
894 if (count(mdd.
In().begin(), mdd.
In().end(), wsv_key)) {
895 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) <<
"\n";
899 if (0 == hitcount) get_os() <<
"<li>none\n";
901 get_os() << endl <<
"</ul>" << endl;
905 get_os() <<
"<h3>Generic and supergeneric methods that can use " << vname
907 get_os() <<
"<ul>" << endl;
916 if (count(mdd.
GInType().begin(),
919 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
921 }
else if (count(mdd.
GInType().begin(),
930 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
934 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
941 if (0 == hitcount) get_os() <<
"<li>none" << endl;
943 get_os() << endl <<
"</ul>" << endl;
948 get_os() <<
"<h3>Agendas that can generate " << vname <<
"</h3>" << endl
957 if (count(ar.
Out().begin(), ar.
Out().end(), wsv_key)) {
958 get_os() <<
"<li>" << insert_agenda_link(ar.
Name()) <<
"\n";
962 if (0 == hitcount) get_os() <<
"<li>none\n";
964 get_os() << endl <<
"</ul>" << endl;
968 get_os() <<
"<h3>Agendas that require " << vname <<
"</h3>" << endl
977 if (count(ar.
In().begin(), ar.
In().end(), wsv_key)) {
978 get_os() <<
"<li>" << insert_agenda_link(ar.
Name()) <<
"\n";
983 if (0 == hitcount) get_os() <<
"<li>none\n";
985 get_os() << endl <<
"</ul>" << endl;
998 void Docserver::doc_variable(
const string& vname) {
1006 get_os() <<
"<pre>" << endl;
1007 get_os() << description_add_links(
1009 get_os() << endl <<
"</pre>" << endl << endl;
1011 get_os() <<
"<p><b>Group: </b>"
1012 << insert_group_link(
1016 doc_variable_methods(vname);
1018 insert_error_message(
"There is no variable by this name.");
1031 void Docserver::doc_agenda(
const string& aname) {
1038 map<String, Index>::const_iterator ait =
AgendaMap.find(aname);
1043 get_os() <<
"<pre>" << endl;
1044 get_os() << description_add_links(
agenda_data[ait->second].Description());
1045 get_os() << endl <<
"</pre>" << endl << endl;
1047 get_os() <<
"<p><b>Group: </b>"
1048 << insert_group_link(
1052 get_os() <<
"<h3>Variables</h3>" << endl;
1061 size_t linelen = 80;
1062 get_os() <<
"<table>" << endl;
1066 buf <<
"<td>OUT</td>";
1070 buf <<
"<td class=\"right\">" << insert_wsv_link(vname)
1072 buf << insert_group_link(
1074 buf <<
")</td><td>";
1080 if (buf.str().length() + desc.length() > linelen) {
1082 buf << endl << indent << description_add_links(desc);
1084 buf << description_add_links(desc);
1087 get_os() << buf.str() <<
"</td></tr>" << endl;
1093 buf <<
"<td>IN</td>";
1096 buf <<
"<td class=\"right\">" << insert_wsv_link(vname);
1097 buf <<
"</td><td>(";
1098 buf << insert_group_link(
1100 buf <<
")</td><td>";
1105 if (buf.str().length() + desc.length() > linelen) {
1107 buf << endl << indent << description_add_links(desc);
1109 buf << description_add_links(desc);
1111 get_os() << buf.str() <<
"</td></tr>" << endl;
1114 get_os() <<
"</table>" << endl;
1117 doc_variable_methods(aname);
1119 insert_error_message(
"There is no agenda by this name.");
1132 void Docserver::doc_group(
const string& gname) {
1140 if (gname !=
"Any") {
1143 get_os() <<
"<h3>Specific methods that can generate " << gname <<
"</h3>"
1145 get_os() <<
"<ul>" << endl;
1157 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) <<
" (";
1160 get_os() << insert_wsv_link(
1166 if (!first) get_os() <<
")" << endl;
1168 if (0 == hitcount) get_os() <<
"<li>none" << endl;
1170 get_os() << endl <<
"</ul>" << endl;
1174 get_os() <<
"<h3>Generic and supergeneric methods that can generate "
1175 << gname <<
"</h3>" << endl;
1176 get_os() <<
"<ul>" << endl;
1186 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
1188 }
else if (count(mdd.
GOutType().begin(),
1197 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
1201 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
1208 if (0 == hitcount) get_os() <<
"<li>none" << endl;
1210 get_os() << endl <<
"</ul>" << endl;
1212 if (gname !=
"Any") {
1214 get_os() <<
"<h3>Specific methods that require variables of group "
1215 << gname <<
"</h3>" << endl;
1216 get_os() <<
"<ul>" << endl;
1228 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) <<
" (";
1231 get_os() << insert_wsv_link(
1237 if (!first) get_os() <<
")" << endl;
1239 if (0 == hitcount) get_os() <<
"<li>none" << endl;
1241 get_os() << endl <<
"</ul>" << endl;
1245 get_os() <<
"<h3>Generic and supergeneric methods that can use " << gname
1247 get_os() <<
"<ul>" << endl;
1257 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
1259 }
else if (count(mdd.
GInType().begin(),
1268 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
1272 get_os() <<
"<li>" << insert_wsm_link(mdd.
Name()) << endl;
1279 if (0 == hitcount) get_os() <<
"<li>none" << endl;
1281 get_os() << endl <<
"</ul>" << endl;
1283 if (gname !=
"Any") {
1286 get_os() <<
"<h3>Workspace Variables of group " << gname <<
"</h3>"
1298 if (0 == hitcount) get_os() <<
"<li>none" << endl;
1300 get_os() <<
"</ul>" << endl;
1303 insert_error_message(
"There is no group by this name.");
1315 void Docserver::find_token_type() {
1316 if (tokens.size() < 1 || tokens[0] !=
"all")
return;
1324 tokens[0] =
"methods";
1326 tokens[0] =
"agendas";
1328 tokens[0] =
"variables";
1330 tokens[0] =
"groups";
1342 void Docserver::insert_breadcrumb_token(
size_t token_id) {
1343 if (token_id != tokens.size()) {
1344 get_os() <<
"<a href=\"" << mbaseurl <<
"/";
1345 for (
size_t t = 0; t < token_id; t++) {
1346 if (t) get_os() <<
"/";
1347 get_os() << tokens[t];
1354 else if (tokens[token_id - 1] ==
"methods")
1355 get_os() <<
"Methods";
1356 else if (tokens[token_id - 1] ==
"variables")
1357 get_os() <<
"Variables";
1358 else if (tokens[token_id - 1] ==
"agendas")
1359 get_os() <<
"Agendas";
1360 else if (tokens[token_id - 1] ==
"groups")
1361 get_os() <<
"Groups";
1362 else if (tokens[token_id - 1] ==
"all")
1364 else if (tokens[token_id - 1] ==
"doccheck")
1365 get_os() <<
"Doc Check";
1367 get_os() << tokens[token_id - 1];
1369 if (token_id != tokens.size()) get_os() <<
"</a>";
1379 void Docserver::insert_breadcrumbs() {
1380 get_os() <<
"<div id=\"navbar\"><div class=\"breadcrumbs\">";
1381 for (
size_t t = 0; t <= tokens.size(); t++) {
1382 if (t) get_os() <<
" >> ";
1383 insert_breadcrumb_token(t);
1385 get_os() <<
"</div>" << endl;
1387 get_os() <<
"<div class=\"goto\">Go to: "
1388 <<
"<a href=\"" << mbaseurl <<
"/groups/\">Groups</a> - "
1389 <<
"<a href=\"" << mbaseurl
1390 <<
"/variables/\">Variables</a> - "
1391 <<
"<a href=\"" << mbaseurl <<
"/methods/\">Methods</a> - "
1392 <<
"<a href=\"" << mbaseurl <<
"/agendas/\">Agendas</a>"
1393 <<
"</div></div>" << endl;
1404 void Docserver::insert_error(
const string& error) {
1406 insert_breadcrumbs();
1408 insert_error_message(error);
1421 void Docserver::insert_error_message(
const string& error) {
1423 get_os() <<
"<p class=\"error\">" << error <<
"</p>" << endl;
1434 void Docserver::insert_title(
const string& title) {
1435 get_os() <<
"<h1>" DOCSERVER_NAME;
1436 if (title.length()) get_os() <<
" — " << title;
1437 get_os() <<
"</h1>" << endl;
1447 void Docserver::insert_index() {
1448 if (tokens.size() == 0 || tokens[0] ==
"all") {
1450 insert_breadcrumbs();
1452 insert_title(
"Index");
1461 if (tokens[0] ==
"methods") {
1462 begin_page(
"Method Index");
1463 insert_breadcrumbs();
1465 insert_title(
"Method Index");
1466 get_os() <<
"<table class=\"list\">" << endl;
1468 get_os() <<
"</table>" << endl;
1471 }
else if (tokens[0] ==
"variables") {
1472 begin_page(
"Variable Index");
1473 insert_breadcrumbs();
1475 insert_title(
"Variable Index");
1476 get_os() <<
"<table class=\"list\">" << endl;
1478 get_os() <<
"</table>" << endl;
1481 }
else if (tokens[0] ==
"groups") {
1482 begin_page(
"Group Index");
1483 insert_breadcrumbs();
1485 insert_title(
"Group Index");
1486 get_os() <<
"<table class=\"list\">" << endl;
1488 get_os() <<
"</table>" << endl;
1491 }
else if (tokens[0] ==
"agendas") {
1492 begin_page(
"Agenda Index");
1493 insert_breadcrumbs();
1495 insert_title(
"Agenda Index");
1496 get_os() <<
"<table class=\"list\">" << endl;
1498 get_os() <<
"</table>" << endl;
1502 insert_error(DS_ERROR_404);
1514 void Docserver::insert_doc() {
1517 if (tokens[0] ==
"methods") {
1518 begin_page(tokens[1]);
1519 insert_breadcrumbs();
1523 <<
"Workspace Method " + tokens[1] <<
"</h2>" << endl;
1524 doc_method(tokens[1]);
1527 }
else if (tokens[0] ==
"variables") {
1528 begin_page(tokens[1]);
1529 insert_breadcrumbs();
1533 <<
"Workspace Variable " + tokens[1] <<
"</h2>" << endl;
1534 doc_variable(tokens[1]);
1537 }
else if (tokens[0] ==
"groups") {
1538 begin_page(tokens[1]);
1539 insert_breadcrumbs();
1543 <<
"Workspace Group " + tokens[1] <<
"</h2>" << endl;
1544 doc_group(tokens[1]);
1547 }
else if (tokens[0] ==
"agendas") {
1548 begin_page(tokens[1]);
1549 insert_breadcrumbs();
1553 <<
"Agenda " + tokens[1] <<
"</h2>" << endl;
1554 doc_agenda(tokens[1]);
1558 insert_error(DS_ERROR_404);
1568 void Docserver::insert_stylesheet() {
1570 <<
"body { font-family: 'Roboto Mono', 'Hack', 'Andale Mono', monospace; "
1571 <<
"font-size: 90%; }" << endl
1572 <<
"a:link { color: #3465a4; text-decoration: none; }" << endl
1573 <<
"a:visited { color: #729fcf; text-decoration: none; }" << endl
1574 <<
"a:active { color: #ce5c00; text-decoration: none; background-color: #eeeeec}"
1576 <<
"a:hover { color: #f57900; text-decoration: none; }" << endl
1578 <<
"@media (prefers-color-scheme: dark) {" << endl
1579 <<
" body { background-color: #121212; color: #b0bec5; }" << endl
1580 <<
" a:link { color: #90caf9; }" << endl
1581 <<
" a:visited { color: #bbdefb; }" << endl
1582 <<
" a:hover { color: #ffcc80; }" << endl
1583 <<
" a:active { color: #ffcc80; background-color: #121212; }"
1587 <<
"pre { font-family: 'Roboto Mono', 'Hack', 'Andale Mono', monospace; }" << endl
1589 <<
"table.list {" << endl
1590 <<
"width: 90%;" << endl
1591 <<
"margin-left: 5%;" << endl
1592 <<
"margin-right: 5%;" << endl
1596 <<
"font-size: 1.5em;" << endl
1600 <<
"font-size: 1.25em;" << endl
1604 <<
"font-size: 1em;" << endl
1608 <<
"font-size: 1em;" << endl
1611 <<
"#navbar {" << endl
1612 <<
"position: fixed;" << endl
1613 <<
"top: 0px;" << endl
1614 <<
"left: 10px;" << endl
1615 <<
"right: 10px;" << endl
1616 <<
"background-color: #fff;" << endl
1617 <<
"border-bottom: solid 1px #ddd;" << endl
1618 <<
"border-left: solid 1px #ddd;" << endl
1619 <<
"border-right: solid 1px #ddd;" << endl
1620 <<
"padding: 2px;" << endl
1623 <<
"@media (prefers-color-scheme: dark) {" << endl
1624 <<
" #navbar {" << endl
1625 <<
" background-color: #121212;" << endl
1626 <<
" color: #b0bec5;" << endl
1627 <<
" border-bottom-color: #b0bec5;" << endl
1628 <<
" border-left-color: #b0bec5;" << endl
1629 <<
" border-right-color: #b0bec5;" << endl
1633 <<
".firstcol {" << endl
1634 <<
"float: left;" << endl
1635 <<
"clear: left;" << endl
1636 <<
"width: 50%;" << endl
1637 <<
"white-space: nowrap;" << endl
1640 <<
".firstcol ul {" << endl
1641 <<
"float: left;" << endl
1642 <<
"clear: both;" << endl
1643 <<
"padding-top: 0;" << endl
1646 <<
".secondcol ul {" << endl
1647 <<
"float: left;" << endl
1648 <<
"clear: both;" << endl
1649 <<
"padding-top: 0;" << endl
1652 <<
".secondcol {" << endl
1653 <<
"float: left;" << endl
1654 <<
"clear: right;" << endl
1655 <<
"width: 50%;" << endl
1656 <<
"white-space: nowrap;" << endl
1659 <<
".firstcol ul li {" << endl
1660 <<
"margin-left: 0;" << endl
1663 <<
".brokendoclink {" << endl
1664 <<
" color: #f44336;" << endl
1667 <<
"@media (prefers-color-scheme: dark) {" << endl
1668 <<
" .brokendoclink {" << endl
1669 <<
" color: #ef9a9a;" << endl
1673 <<
".goto {" << endl
1674 <<
"float: right;" << endl
1677 <<
".breadcrumbs {" << endl
1678 <<
"float: left;" << endl
1681 <<
"@media only screen and (max-device-width: 480px) {" << endl
1682 <<
"#navbar { position: static; border: none; }" << endl
1683 <<
".goto { position: static; float: none; }" << endl
1684 <<
".breadcrumbs { position: static; float: none; }" << endl
1685 <<
".firstcol { float: left; clear: left; width: 100%; }" << endl
1686 <<
".secondcol { float: left; clear: both; width: 100%; }" << endl
1687 <<
".firstcol ul { margin-top: 0; margin-bottom: 0; }" << endl
1688 <<
".secondcol ul { margin-top: 0; }" << endl
1689 <<
"ul { padding-left: 1em; }" << endl
1692 <<
"table {" << endl
1693 <<
"border-width: 0px;" << endl
1696 <<
"table td {" << endl
1697 <<
"vertical-align: top;" << endl
1700 <<
"table td.right {" << endl
1701 <<
"text-align: right;" << endl
1704 <<
".content {" << endl
1705 <<
"padding-top: 1em;" << endl
1706 <<
"clear: both;" << endl
1707 <<
"width: 100%;" << endl
1710 <<
".error {" << endl
1711 <<
"color: #f44336;" << endl
1712 <<
"font-weight: bold;" << endl
1713 <<
"font-size: 1.2em;" << endl
1716 <<
"@media (prefers-color-scheme: dark) {" << endl
1717 <<
" .error {" << endl
1718 <<
" color: #ef9a9a;" << endl
1722 <<
"div.footer {" << endl
1723 <<
"float: left;" << endl
1724 <<
"text-align: right;" << endl
1725 <<
"color: #aaaaa8;" << endl
1726 <<
"font-size: small;" << endl
1727 <<
"clear: left;" << endl
1728 <<
"margin-top: 2em;" << endl
1729 <<
"width: 100%;" << endl
1742 std::tuple<size_t, std::vector<string> >
1743 Docserver::list_broken_description_links() {
1745 vector<string> all_broken_links;
1747 auto insert_broken = [](vector<string> brk_links) {
1749 bool first_broken =
true;
1750 for (
auto&& broken_link : brk_links) {
1754 first_broken =
false;
1763 vector<string> broken_links;
1766 counter += broken_links.size();
1768 if (broken_links.size()) {
1771 all_broken_links.push_back(
"");
1772 all_broken_links.push_back(
"WSV descriptions");
1773 all_broken_links.push_back(
"----------------");
1776 insert_broken(broken_links));
1784 vector<string> broken_links;
1785 broken_links = find_broken_description_links(ait.Description());
1786 counter += broken_links.size();
1788 if (broken_links.size()) {
1791 all_broken_links.push_back(
"");
1792 all_broken_links.push_back(
"Agenda descriptions");
1793 all_broken_links.push_back(
"-------------------");
1795 all_broken_links.push_back(ait.Name() +
": " +
1796 insert_broken(broken_links));
1805 vector<string> broken_links;
1806 broken_links = find_broken_description_links(mit.Description(), mit.Name());
1807 counter += broken_links.size();
1809 if (broken_links.size()) {
1812 all_broken_links.push_back(
"");
1813 all_broken_links.push_back(
"WSM descriptions");
1814 all_broken_links.push_back(
"----------------");
1816 all_broken_links.push_back(mit.Name() +
": " +
1817 insert_broken(broken_links));
1821 return std::make_tuple(counter, all_broken_links);
1835 vector<string> Docserver::find_broken_description_links(
const String& desc,
1837 vector<string> broken_links;
1840 bool inside_link =
false;
1841 string::const_iterator it = desc.begin();
1846 while (it != desc.end()) {
1854 inside_link =
false;
1861 else if (mname !=
"") {
1863 map<String, Index>::const_iterator mit =
MdRawMap.find(mname);
1868 for (ArrayOfString::const_iterator sit = mdr.
GIn().begin();
1869 !found && sit != mdr.
GIn().end();
1871 if ((*sit) == link) {
1872 ret +=
"*" + link +
"*";
1877 for (ArrayOfString::const_iterator sit = mdr.
GOut().begin();
1878 !found && sit != mdr.
GOut().end();
1880 if ((*sit) == link) {
1881 ret +=
"*" + link +
"*";
1889 broken_links.push_back(
"*" + link +
"*");
1894 if (!isalnum(*it) && *it !=
'_') {
1895 inside_link =
false;
1896 ret +=
"*" + link + *it;
1899 link += html_escape_char(*it);
1908 return broken_links;
1921 int Docserver::launch(
bool daemon) {
1922 struct MHD_Daemon* d;
1924 d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
1933 cerr <<
"Error: Cannot start server. Maybe port " << mport
1934 <<
" is already in use?\n";
1938 cerr <<
"ARTS docserver listening at http://localhost:" << mport <<
"\n";
1941 <<
"===========================================================\n"
1942 <<
"Now point your web browser to http://localhost:" << mport <<
"\n"
1943 <<
"===========================================================\n\n"
1944 <<
"Press enter to exit.\n";
1951 cout <<
"Stopping docserver.\n";
1953 cout <<
"Goodbye.\n";
1967 Docserver::Docserver(
const Index port,
const string& baseurl) : mos(NULL) {
1993 static int ahc_echo(
void* cls,
1994 struct MHD_Connection* connection,
1997 const char* version
_U_,
1998 const char* upload_data
_U_,
1999 size_t* upload_data_size
_U_,
2003 struct MHD_Response* response;
2007 cerr <<
"Docserver error: Docserver object reference is NULL.\n";
2012 Docserver docserver = *((Docserver*)cls);
2014 if (0 != strcmp(method,
"GET")) {
2015 cerr <<
"Docserver error: Unexpected method " << method <<
".\n";
2018 if (&aptr != *ptr) {
2024 MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND,
"q");
2026 string content_type;
2028 docserver.set_ostream(hout);
2029 content_type = docserver.new_page(surl);
2030 docserver.clear_ostream();
2032 response = MHD_create_response_from_data(
2033 hout.str().length(), (
void*)hout.str().c_str(), MHD_NO, MHD_YES);
2035 if (response == NULL) {
2036 cerr <<
"Docserver error: response = 0\n";
2040 MHD_add_response_header(response,
"Content-type", content_type.c_str());
2041 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
2042 MHD_destroy_response(response);