ARTS  2.2.66
docserver.cc
Go to the documentation of this file.
1 /* Copyright (C) 2012 Oliver Lemke <olemke@core-dump.info>
2 
3  This program is free software; you can redistribute it and/or modify it
4  under the terms of the GNU General Public License as published by the
5  Free Software Foundation; either version 2, or (at your option) any
6  later version.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16  USA. */
17 
26 #include "docserver.h"
27 
28 #ifdef ENABLE_DOCSERVER
29 
30 #include <stdint.h>
31 #include <string>
32 #include <vector>
33 #include <sstream>
34 #include <algorithm>
35 #include <map>
36 #include "libmicrohttpd/platform.h"
37 #include "libmicrohttpd/microhttpd.h"
38 #include "messages.h"
39 #include "methods.h"
40 #include "workspace_ng.h"
41 #include "agenda_record.h"
42 #include "auto_version.h"
43 #include "global_data.h"
44 
45 #define DOCSERVER_NAME "ARTS built-in documentation server"
46 
47 #define DS_ERROR_404 "Page not found"
48 
49 
50 static int ahc_echo(void* cls,
51  struct MHD_Connection* connection,
52  const char* url,
53  const char* method,
54  const char* version,
55  const char* upload_data,
56  size_t* upload_data_size,
57  void** ptr);
58 
59 
61 
66 void Docserver::limit_line_length(ostringstream& curline,
67  ostringstream& token,
68  const String& indent,
69  size_t linelen)
70 {
71  if (indent.length() + curline.str().length() + token.str().length() > linelen)
72  {
73  get_os() << curline.str() << endl << indent;
74  curline.str("");
75  }
76  curline << token.str();
77  token.str("");
78 }
79 
80 
82 
90 string Docserver::new_page(const string& url)
91 {
92  string surl = url;
93 
94  if (surl.find(get_baseurl()) == 0)
95  surl.erase(0, get_baseurl().size());
96 
97  split_tokens(surl);
98 
99  while (tokens.size() && tokens[tokens.size()-1] == "")
100  tokens.pop_back();
101 
102  while (tokens.size() && tokens[0] == "")
103  tokens.erase(tokens.begin());
104 
105  string content_type = "text/html; charset=utf-8";
106  if (tokens.size() && tokens[tokens.size()-1] == "styles.css")
107  {
108  insert_stylesheet();
109  content_type = "text/css; charset=utf-8";
110  }
111  else if (tokens.size() && tokens[tokens.size()-1] == "doccheck")
112  {
113  insert_broken_doc_links();
114  }
115  else
116  {
117  switch (tokens.size())
118  {
119  case 0:
120  case 1:
121  insert_index();
122  break;
123  case 2:
124  insert_doc();
125  break;
126  default:
127  insert_error(DS_ERROR_404);
128  }
129  }
130 
131  return content_type;
132 }
133 
134 
136 
144 void Docserver::split_tokens(const string& s)
145 {
146  tokens.clear();
147 
148  stringstream ss(s);
149  string item;
150  while (getline(ss, item, '/'))
151  tokens.push_back(item);
152 }
153 
154 
156 
166 string Docserver::html_escape_char(const char ch)
167 {
168  string ret;
169 
170  switch (ch)
171  {
172  case '<': ret.append("&lt;"); break;
173  case '>': ret.append("&gt;"); break;
174  default: ret.append(1, ch);
175  }
176 
177  return ret;
178 }
179 
180 
182 
191 string Docserver::html_escape_string(const string& s)
192 {
193  string ret;
194 
195  for (string::const_iterator it = s.begin(); it != s.end(); it++)
196  ret.append(html_escape_char(*it));
197 
198  return ret;
199 }
200 
201 
203 
208 void Docserver::begin_content()
209 {
210  get_os() << "<div class=\"content\">" << endl;
211 }
212 
213 
215 
220 void Docserver::end_content()
221 {
222  get_os() << "</div>" << endl;
223 }
224 
225 
227 
234 void Docserver::begin_page(string title)
235 {
236  if (title.length()) title += " - ";
237 
238  title += DOCSERVER_NAME;
239 
240  get_os()
241  << "<!DOCTYPE html>" << endl
242  << "<html lang=\"en\">" << endl
243  << "<head>" << endl
244  << "<title>" << title << "</title>" << endl
245  << "<meta charset=\"utf-8\">" << endl
246  << "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />" << endl
247  << "<link rel=\"stylesheet\" href=\"" << mbaseurl << "/styles.css\">" << endl
248  << "</head>" << endl
249  << "<body>" << endl;
250 }
251 
252 
254 
259 void Docserver::end_page()
260 {
261  get_os() << "<div class=\"footer\">Page generated by " << ARTS_FULL_VERSION
262  << " - <a href=\"" << mbaseurl << "/doccheck\">Check docs</a></div>\n" << endl;
263 
264  get_os() << "</body>" << endl << "</html>";
265 }
266 
267 
269 
278 String Docserver::insert_agenda_link(const String& aname)
279 {
280  ostringstream ret;
281  ret << "<a href=\"" << mbaseurl << "/agendas/" << aname << "\">" << aname << "</a>";
282  return ret.str();
283 }
284 
285 
287 
296 String Docserver::insert_group_link(const String& gname)
297 {
298  ostringstream ret;
299  ret << "<a href=\"" << mbaseurl << "/groups/" << gname << "\">" << gname << "</a>";
300  return ret.str();
301 }
302 
303 
305 
314 String Docserver::insert_wsm_link(const String& mname)
315 {
316  ostringstream ret;
317  ret << "<a href=\"" << mbaseurl << "/methods/" << mname << "\">" << mname << "</a>";
318  return ret.str();
319 }
320 
321 
323 
332 String Docserver::insert_wsv_link(const String& vname)
333 {
334  ostringstream ret;
335 
336  // Find wsv id:
337  map<String, Index>::const_iterator it = Workspace::WsvMap.find(vname);
338  if (it != Workspace::WsvMap.end())
339  {
340  if (Workspace::wsv_data[it->second].Group() == get_wsv_group_id("Agenda"))
341  ret << "<a href=\"" << mbaseurl << "/agendas/" << vname << "\">" << vname << "</a>";
342  else
343  ret << "<a href=\"" << mbaseurl << "/variables/" << vname << "\">" << vname << "</a>";
344  }
345 
346  return ret.str();
347 }
348 
349 
351 
356 void Docserver::list_agendas()
357 {
358  Index i;
359 
360  get_os() << "<h2>Agendas</h2>" << endl;
361 
362  get_os() << "<div class=\"firstcol\">" << endl << "<ul>" << endl;
363 
364  Index hitcount = 0;
365  for (i = 0; i < Workspace::wsv_data.nelem(); ++i)
366  if (Workspace::wsv_data[i].Group() == get_wsv_group_id("Agenda"))
367  hitcount++;
368 
369  Index hitcount2 = 0;
370  for (i = 0; i < Workspace::wsv_data.nelem(); ++i)
371  {
372  if (Workspace::wsv_data[i].Group() == get_wsv_group_id("Agenda"))
373  {
374  get_os() << "<li>" << insert_agenda_link(Workspace::wsv_data[i].Name()) << "</li>" << endl;
375  hitcount2++;
376 
377  if (hitcount2 == hitcount/2)
378  get_os() << "</ul>" << endl << "</div>" << endl
379  << "<div class=\"secondcol\">" << endl << "<ul>" << endl;
380  }
381  }
382 
383  get_os() << "</ul>" << endl << "</div>" << endl;
384 }
385 
386 
388 
393 void Docserver::list_groups()
394 {
396  Index i;
397 
398  get_os() << "<h2>Workspace Groups</h2>" << endl;
399 
400  get_os() << "<div class=\"firstcol\">" << endl << "<ul>" << endl;
401  for (i = 0; i < wsv_group_names.nelem(); ++i)
402  {
403  get_os() << "<li>" << insert_group_link(wsv_group_names[i]) << "</li>" << endl;
404 
405  if (i+1 == wsv_group_names.nelem()/2)
406  get_os() << "</ul>" << endl << "</div>" << endl
407  << "<div class=\"secondcol\">" << endl << "<ul>" << endl;
408  }
409 
410  get_os() << "</ul>" << endl << "</div>" << endl;
411 }
412 
413 
415 
420 void Docserver::list_methods()
421 {
423  Index i;
424 
425  get_os() << "<h2>Workspace Methods</h2>" << endl;
426 
427  get_os() << "<div class=\"firstcol\">" << endl << "<ul>" << endl;
428  for (i = 0; i < md_data_raw.nelem(); ++i)
429  {
430  get_os() << "<li>" << insert_wsm_link(md_data_raw[i].Name()) << "</li>" << endl;
431 
432  if (i+1 == md_data_raw.nelem()/2)
433  get_os() << "</ul>" << endl << "</div>" << endl
434  << "<div class=\"secondcol\">" << endl << "<ul>" << endl;
435  }
436 
437  get_os() << "</ul>" << endl << "</div>" << endl;
438 }
439 
440 
442 
447 void Docserver::list_variables()
448 {
449  Index i;
450 
451  get_os() << "<h2>Workspace Variables</h2>" << endl;
452 
453  get_os() << "<div class=\"firstcol\">" << endl << "<ul>" << endl;
454  Index hitcount = 0;
455  for (i = 0; i < Workspace::wsv_data.nelem(); ++i)
456  {
457  if (Workspace::wsv_data[i].Group() != get_wsv_group_id("Agenda"))
458  hitcount++;
459  }
460 
461  Index hitcount2 = 0;
462  for (i = 0; i < Workspace::wsv_data.nelem(); ++i)
463  {
464  if (Workspace::wsv_data[i].Group() != get_wsv_group_id("Agenda"))
465  {
466  get_os() << "<li>" << insert_wsv_link(Workspace::wsv_data[i].Name()) << "</li>" << endl;
467  hitcount2++;
468 
469  if (hitcount2 == hitcount/2)
470  get_os() << "</ul>" << endl << "</div>" << endl
471  << "<div class=\"secondcol\">" << endl << "<ul>" << endl;
472  }
473  }
474 
475  get_os() << "</ul>" << endl << "</div>" << endl;
476 }
477 
478 
480 
490 String Docserver::description_add_links(const String& desc, const String& mname)
491 {
492  string ret;
493  string link;
494  bool inside_link = false;
495  string::const_iterator it = desc.begin();
496 
497  using global_data::MdRawMap;
499 
500  while (it != desc.end())
501  {
502  if (!inside_link)
503  {
504  if (*it == '*')
505  inside_link = true;
506  else
507  ret += html_escape_char(*it);
508  }
509  else
510  {
511  if (*it == '*')
512  {
513  inside_link = false;
514  if (MdRawMap.find(link) != MdRawMap.end())
515  ret += insert_wsm_link(link);
516  else if (AgendaMap.find(link) != AgendaMap.end())
517  ret += insert_agenda_link(link);
518  else if (Workspace::WsvMap.find(link) != Workspace::WsvMap.end())
519  ret += insert_wsv_link(link);
520  else if (get_wsv_group_id(link) != -1)
521  ret += insert_group_link(link);
522  else if (mname != "")
523  {
524  using global_data::MdRawMap;
525  bool found = false;
526 
527  // Find method id:
528  map<String, Index>::const_iterator mit = MdRawMap.find(mname);
529  if (mit != MdRawMap.end())
530  {
532  const MdRecord& mdr = md_data_raw[mit->second];
533 
534  for (ArrayOfString::const_iterator sit = mdr.GIn().begin();
535  !found && sit != mdr.GIn().end();
536  sit++)
537  {
538  if ((*sit) == link)
539  {
540  ret += "*" + link + "*";
541  found = true;
542  }
543  }
544 
545  for (ArrayOfString::const_iterator sit = mdr.GOut().begin();
546  !found && sit != mdr.GOut().end();
547  sit++)
548  {
549  if ((*sit) == link)
550  {
551  ret += "*" + link + "*";
552  found = true;
553  }
554  }
555  }
556 
557  if (!found)
558  ret += "<span class=\"brokendoclink\">*" + link + "*</span>";
559  }
560  else
561  ret += "<span class=\"brokendoclink\">*" + link + "*</span>";
562 
563  link = "";
564  }
565  else
566  {
567  if (!isalnum(*it) && *it != '_')
568  {
569  inside_link = false;
570  ret += "*" + link + *it;
571  link = "";
572  }
573  else link += html_escape_char(*it);
574  }
575  }
576 
577  it++;
578  }
579 
580  if (inside_link) ret += "*" + link;
581 
582  return ret;
583 }
584 
585 
587 
595 void Docserver::doc_method(const string& mname)
596 {
597  // Make global data visible:
599  using global_data::MdRawMap;
601 
602  // Let's first assume it is a method that the user wants to have
603  // described.
604 
605  // Find method id:
606  map<String, Index>::const_iterator it = MdRawMap.find(mname);
607  if (it != MdRawMap.end())
608  {
609  // If we are here, then the given name matches a method.
610  const MdRecord& mdr = md_data_raw[it->second];
611  String indent = "";
612 
613  get_os() << "<h3>Description</h3>" << endl;
614 
615  get_os() << "<pre>" << endl;
616  get_os() << description_add_links(mdr.Description(), mname);
617  get_os() << endl << "</pre>" << endl << endl;
618 
619  bool is_first_author = true;
620  for (Index i = 0; i < mdr.Authors().nelem(); i++)
621  {
622  if (is_first_author)
623  {
624  get_os() << "<p><b>Authors: </b>";
625  is_first_author = false;
626  }
627  else
628  get_os() << ", ";
629 
630  get_os() << mdr.Authors()[i];
631  }
632  get_os() << "\n";
633 
634  // Print the method's synopsis
635  while (indent.length() < mdr.Name().length() + 2)
636  indent += ' ';
637 
638  get_os() << "<h3>Synopsis</h3>" << endl;
639 
640  ostringstream buf;
641  ostringstream param;
642  const size_t linelen = 2048;
643 
644  buf << "<p><table><tr><td>" << mdr.Name() << "(&nbsp;</td><td>";
645  bool first = true;
646  for (Index i = 0; i < mdr.Out().nelem(); ++i)
647  {
648  if (first) first = false;
649  else buf << ", ";
650  param << insert_wsv_link(Workspace::wsv_data[mdr.Out()[i]].Name());
651 
652  limit_line_length(buf, param, indent, linelen);
653  }
654 
655  for (Index i = 0; i < mdr.GOutType().nelem(); ++i)
656  {
657  if (first) first = false;
658  else buf << ", ";
659 
660  if (mdr.GOut()[i].length())
661  param << mdr.GOut()[i];
662  else
663  param << "gout" << i;
664 
665  limit_line_length(buf, param, indent, linelen);
666  }
667 
668  const ArrayOfIndex& inonly = mdr.InOnly();
669  for (Index i = 0; i < inonly.nelem(); ++i)
670  {
671  if (first) first = false;
672  else buf << ", ";
673 
674  param << insert_wsv_link(Workspace::wsv_data[inonly[i]].Name());
675 
676  limit_line_length(buf, param, indent, linelen);
677  }
678 
679  for (Index i = 0; i < mdr.GInType().nelem(); ++i)
680  {
681  if (first) first = false;
682  else buf << ", ";
683 
684  if (mdr.GIn()[i].length()) param << mdr.GIn()[i];
685  else param << "gin" << i;
686 
687  limit_line_length(buf, param, indent, linelen);
688  }
689  if (buf.str().length()) get_os() << buf.str();
690 
691  get_os() << " )</td></tr></table>" << endl;
692 
693  get_os() << "<h3>Variables</h3>" << endl;
694 
695  // Out:
696  indent = "";
697  String desc;
698  get_os() << "<table>" << endl;
699  for (Index i = 0; i < mdr.Out().nelem(); ++i)
700  {
701  buf.str("");
702  buf << "<tr>";
703  if (std::find(mdr.In().begin(), mdr.In().end(), mdr.Out()[i]) == mdr.In().end())
704  buf << "<td>OUT</td>";
705  else
706  buf << "<td>OUT+IN</td>";
707 
708  {
709  const String& vname = Workspace::wsv_data[mdr.Out()[i]].Name();
710  buf << "<td class=\"right\">" << insert_wsv_link(vname) << "</td><td>(";
711  buf << insert_group_link(wsv_group_names[Workspace::wsv_data[mdr.Out()[i]].Group()]);
712  buf << ")</td><td>";
713  }
714 
715  get_short_wsv_description(desc, Workspace::wsv_data[mdr.Out()[i]].Description());
716 
717  if (buf.str().length() + desc.length() > linelen)
718  {
719  format_paragraph(desc, indent, linelen);
720  buf << endl << indent << description_add_links(desc);
721  }
722  else
723  {
724  buf << description_add_links(desc);
725  }
726 
727  get_os() << buf.str() << "</td></tr>" << endl;
728  }
729 
730  size_t lastlen;
731  bool fit;
732  for (Index i = 0; i < mdr.GOut().nelem(); ++i)
733  {
734  buf.str("");
735  buf << "<tr>";
736  buf << "<td>GOUT</td><td class=\"right\">" << mdr.GOut()[i] << "</td><td>(";
737 
738  if (mdr.GOutType()[i] == get_wsv_group_id("Any") && mdr.GOutSpecType()[i].nelem())
739  {
740  bool firstarg = true;
741  for (Index j = 0; j < mdr.GOutSpecType()[i].nelem(); j++)
742  {
743  if (!firstarg) buf << ", ";
744  else firstarg = false;
745 
746  buf << insert_group_link(wsv_group_names[mdr.GOutSpecType()[i][j]]);
747  }
748  }
749  else
750  {
751  buf << insert_group_link(wsv_group_names[mdr.GOutType()[i]]);
752  }
753 
754  buf << ")</td><td>";
755  desc = buf.str();
756  lastlen = desc.length();
757  fit = format_paragraph(desc, indent, linelen);
758  buf.str("");
759  get_os() << desc;
760 
761  desc = mdr.GOutDescription()[i];
762  if (!fit)
763  {
764  format_paragraph(desc, indent, linelen);
765  buf << endl << indent << description_add_links(desc);
766  }
767  else
768  {
769  if (lastlen + desc.length() > linelen)
770  {
771  format_paragraph(desc, indent, linelen, lastlen);
772  buf << endl << description_add_links(desc);
773  }
774  else buf << description_add_links(desc);
775  }
776 
777  get_os() << buf.str() << "</td></tr>" << endl;
778  }
779 
780  for (Index i = 0; i < mdr.In().nelem(); ++i)
781  {
782  if (std::find(mdr.Out().begin(), mdr.Out().end(), mdr.In()[i]) != mdr.Out().end())
783  continue;
784 
785  buf.str("");
786  buf << "<tr>";
787  buf << "<td>IN</td>";
788 
789  const String& vname = Workspace::wsv_data[mdr.In()[i]].Name();
790  buf << "<td class=\"right\">" << insert_wsv_link(vname);
791  buf << "</td><td>(";
792  buf << insert_group_link(wsv_group_names[Workspace::wsv_data[mdr.In()[i]].Group()]);
793  buf << ")</td><td>";
794 
795  get_short_wsv_description(desc, Workspace::wsv_data[mdr.In()[i]].Description());
796 
797  if (buf.str().length() + desc.length() > linelen)
798  {
799  format_paragraph(desc, indent, linelen, indent.length());
800  buf << endl << indent << description_add_links(desc);
801  }
802  else buf << description_add_links(desc);
803 
804  get_os() << buf.str() << "</td></tr>" << endl;
805  }
806 
807  for (Index i = 0; i < mdr.GIn().nelem(); ++i)
808  {
809  buf.str("");
810  buf << "<tr>";
811  buf << "<td>GIN</td><td class=\"right\">" << mdr.GIn()[i] << "</td><td>(";
812  if (mdr.GInType()[i] == get_wsv_group_id("Any")
813  && mdr.GInSpecType()[i].nelem())
814  {
815  bool firstarg = true;
816  for (Index j = 0; j < mdr.GInSpecType()[i].nelem(); j++)
817  {
818  if (!firstarg) buf << ", ";
819  else firstarg = false;
820  buf << insert_group_link(wsv_group_names[mdr.GInSpecType()[i][j]]);
821  }
822  }
823  else
824  {
825  buf << insert_group_link(wsv_group_names[mdr.GInType()[i]]);
826  }
827 
828  if (mdr.GInDefault()[i] != NODEF)
829  {
830  buf << ", Default: ";
831  if (mdr.GInType()[i] == get_wsv_group_id("String"))
832  {
833  buf << "\"" << mdr.GInDefault()[i] << "\"";
834  }
835  else
836  {
837  buf << mdr.GInDefault()[i];
838  }
839 
840  }
841 
842  buf << ")</td><td>";
843  desc = buf.str();
844  lastlen = desc.length();
845  fit = format_paragraph(desc, indent, linelen);
846  buf.str("");
847  get_os() << desc;
848 
849  desc = mdr.GInDescription()[i];
850  if (!fit)
851  {
852  format_paragraph(desc, indent, linelen);
853  buf << indent << description_add_links(desc);
854  }
855  else if (lastlen + desc.length() > linelen)
856  {
857  format_paragraph(desc, indent, linelen, indent.length());
858  buf << indent << description_add_links(desc);
859  }
860  else
861  {
862  buf << description_add_links(desc);
863  }
864 
865  get_os() << buf.str() << "</td></tr>" << endl;
866  }
867  get_os() << "</table>" << endl;
868  }
869  else
870  {
871  insert_error_message("There is no method by this name.");
872  }
873 }
874 
875 
877 
885 void Docserver::doc_variable_methods(const string& vname)
886 {
887  // Check if the user gave the name of a specific variable.
888  map<String, Index>::const_iterator mi = Workspace::WsvMap.find(vname);
890  if (mi != Workspace::WsvMap.end())
891  {
892  // If we are here, then the given name matches a variable.
893  Index wsv_key = mi->second;
894  Index hitcount = 0;
895 
896  // List specific methods:
897  hitcount = 0;
898  get_os()
899  << "<h3>Specific methods that can generate " << vname << "</h3>" << endl
900  << "<ul>" << endl;
901  for (Index i = 0; i < md_data_raw.nelem(); ++i)
902  {
903  // Get handle on method record:
904  const MdRecord& mdd = md_data_raw[i];
905 
906  // This if statement checks whether Output, the list
907  // of output variables contains the workspace
908  // variable key.
909  if (count(mdd.Out().begin(),
910  mdd.Out().end(),
911  wsv_key))
912  {
913  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << "\n";
914  ++hitcount;
915  }
916  }
917  if (0 == hitcount) get_os() << "<li>none\n";
918 
919  get_os() << endl << "</ul>" << endl;
920 
921  // List generic methods:
922  get_os() << "<h3>Generic and supergeneric methods that can generate " << vname << "</h3>"
923  << endl;
924  get_os() << "<ul>" << endl;
925  for (Index i = 0; i < md_data_raw.nelem(); ++i)
926  {
927  // Get handle on method record:
928  const MdRecord& mdd = md_data_raw[i];
929 
930  // This if statement checks whether GOutType, the list
931  // of output variable types contains the group of the
932  // requested variable.
933  // The else clause picks up methods with supergeneric input.
934  if (count(mdd.GOutType().begin(),
935  mdd.GOutType().end(),
936  Workspace::wsv_data[wsv_key].Group()))
937  {
938  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
939  ++hitcount;
940  }
941  else if (count(mdd.GOutType().begin(),
942  mdd.GOutType().end(),
943  get_wsv_group_id("Any")))
944  {
945  for (Index j = 0; j < mdd.GOutType().nelem(); j++)
946  {
947  if (mdd.GOutType()[j] == get_wsv_group_id("Any"))
948  {
949  if (mdd.GOutSpecType()[j].nelem())
950  {
951  if (count(mdd.GOutSpecType()[j].begin(),
952  mdd.GOutSpecType()[j].end(),
953  Workspace::wsv_data[wsv_key].Group()))
954  {
955  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
956  ++hitcount;
957  }
958  }
959  else
960  {
961  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
962  ++hitcount;
963  }
964  }
965  }
966  }
967  }
968  if (0 == hitcount) get_os() << "<li>none" << endl;
969 
970  get_os() << endl << "</ul>" << endl;
971 
972  // List specific methods:
973  hitcount = 0;
974  get_os()
975  << "<h3>Specific methods that require " << vname << "</h3>" << endl
976  << "<ul>" << endl;
977  for (Index i = 0; i < md_data_raw.nelem(); ++i)
978  {
979  // Get handle on method record:
980  const MdRecord& mdd = md_data_raw[i];
981 
982  // This if statement checks whether Output, the list
983  // of output variables contains the workspace
984  // variable key.
985  if (count(mdd.In().begin(), mdd.In().end(), wsv_key))
986  {
987  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << "\n";
988  ++hitcount;
989  }
990  }
991  if (0 == hitcount) get_os() << "<li>none\n";
992 
993  get_os() << endl << "</ul>" << endl;
994 
995  // List generic methods:
996  hitcount = 0;
997  get_os() << "<h3>Generic and supergeneric methods that can use " << vname << "</h3>"
998  << endl;
999  get_os() << "<ul>" << endl;
1000  for (Index i = 0; i < md_data_raw.nelem(); ++i)
1001  {
1002  // Get handle on method record:
1003  const MdRecord& mdd = md_data_raw[i];
1004 
1005  // This if statement checks whether GOutType, the list
1006  // of output variable types contains the group of the
1007  // requested variable.
1008  // The else clause picks up methods with supergeneric input.
1009  if (count(mdd.GInType().begin(),
1010  mdd.GInType().end(), Workspace::wsv_data[wsv_key].Group()))
1011  {
1012  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1013  ++hitcount;
1014  }
1015  else if (count(mdd.GInType().begin(),
1016  mdd.GInType().end(),
1017  get_wsv_group_id("Any")))
1018  {
1019  for (Index j = 0; j < mdd.GInType().nelem(); j++)
1020  {
1021  if (mdd.GInType()[j] == get_wsv_group_id("Any"))
1022  {
1023  if (mdd.GInSpecType()[j].nelem())
1024  {
1025  if (count(mdd.GInSpecType()[j].begin(),
1026  mdd.GInSpecType()[j].end(),
1027  Workspace::wsv_data[wsv_key].Group()))
1028  {
1029  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1030  ++hitcount;
1031  }
1032  }
1033  else
1034  {
1035  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1036  ++hitcount;
1037  }
1038  }
1039  }
1040  }
1041  }
1042  if (0 == hitcount) get_os() << "<li>none" << endl;
1043 
1044  get_os() << endl << "</ul>" << endl;
1045 
1046  // List agendas with this variable as output:
1048  hitcount = 0;
1049  get_os()
1050  << "<h3>Agendas that can generate " << vname << "</h3>" << endl
1051  << "<ul>" << endl;
1052  for (Index i = 0; i < agenda_data.nelem(); ++i)
1053  {
1054  // Get handle on method record:
1055  const AgRecord& ar = agenda_data[i];
1056 
1057  // This if statement checks whether Output, the list
1058  // of output variables contains the workspace
1059  // variable key.
1060  if (count(ar.Out().begin(), ar.Out().end(), wsv_key))
1061  {
1062  get_os() << "<li>" << insert_agenda_link(ar.Name()) << "\n";
1063  ++hitcount;
1064  }
1065  }
1066  if (0 == hitcount) get_os() << "<li>none\n";
1067 
1068  get_os() << endl << "</ul>" << endl;
1069 
1070  // List agendas with this variable as input:
1071  hitcount = 0;
1072  get_os() << "<h3>Agendas that require " << vname << "</h3>" << endl
1073  << "<ul>" << endl;
1074  for (Index i = 0; i < agenda_data.nelem(); ++i)
1075  {
1076  // Get handle on method record:
1077  const AgRecord& ar = agenda_data[i];
1078 
1079  // This if statement checks whether Output, the list
1080  // of output variables contains the workspace
1081  // variable key.
1082  if (count(ar.In().begin(), ar.In().end(), wsv_key))
1083  {
1084  get_os() << "<li>" << insert_agenda_link(ar.Name()) << "\n";
1085  ++hitcount;
1086  }
1087  }
1088 
1089  if (0 == hitcount) get_os() << "<li>none\n";
1090 
1091  get_os() << endl << "</ul>" << endl;
1092  }
1093 }
1094 
1095 
1097 
1105 void Docserver::doc_variable(const string& vname)
1106 {
1108 
1109  // Find wsv id:
1110  map<String, Index>::const_iterator it = Workspace::WsvMap.find(vname);
1111  if (it != Workspace::WsvMap.end())
1112  {
1113  // If we are here, then the given name matches a workspace
1114  // variable.
1115  get_os() << "<pre>" << endl;
1116  get_os() << description_add_links(Workspace::wsv_data[it->second].Description());
1117  get_os() << endl << "</pre>" << endl << endl;
1118 
1119  get_os() << "<p><b>Group: </b>"
1120  << insert_group_link(wsv_group_names[Workspace::wsv_data[it->second].Group()])
1121  << endl;
1122 
1123  doc_variable_methods(vname);
1124  }
1125  else
1126  {
1127  insert_error_message("There is no variable by this name.");
1128  }
1129 }
1130 
1131 
1133 
1141 void Docserver::doc_agenda(const string& aname)
1142 {
1144 
1145  // Find wsv id:
1146  map<String, Index>::const_iterator it = Workspace::WsvMap.find(aname);
1148  using global_data::AgendaMap;
1149  map<String, Index>::const_iterator ait = AgendaMap.find(aname);
1150 
1151  if (it != Workspace::WsvMap.end() && ait != AgendaMap.end())
1152  {
1153  // If we are here, then the given name matches a workspace
1154  // variable.
1155  get_os() << "<pre>" << endl;
1156  get_os() << description_add_links(agenda_data[ait->second].Description());
1157  get_os() << endl << "</pre>" << endl << endl;
1158 
1159  get_os() << "<p><b>Group: </b>"
1160  << insert_group_link(wsv_group_names[Workspace::wsv_data[it->second].Group()])
1161  << endl;
1162 
1163  get_os() << "<h3>Variables</h3>" << endl;
1164 
1165  // Out:
1166  if (ait != AgendaMap.end())
1167  {
1168  // If we are here, then the given name matches a method.
1169  const AgRecord& agr = agenda_data[ait->second];
1170  String indent = "";
1171  String desc;
1172  ostringstream buf;
1173  size_t linelen = 80;
1174  get_os() << "<table>" << endl;
1175  for (Index i = 0; i < agr.Out().nelem(); ++i)
1176  {
1177  buf.str("");
1178  buf << "<tr>";
1179  buf << "<td>OUT</td>";
1180 
1181  {
1182  const String& vname = Workspace::wsv_data[agr.Out()[i]].Name();
1183  buf << "<td class=\"right\">" << insert_wsv_link(vname) << "</td><td>(";
1184  buf << insert_group_link(wsv_group_names[Workspace::wsv_data[agr.Out()[i]].Group()]);
1185  buf << ")</td><td>";
1186  }
1187 
1188  get_short_wsv_description(desc, Workspace::wsv_data[agr.Out()[i]].Description());
1189 
1190  if (buf.str().length() + desc.length() > linelen)
1191  {
1192  format_paragraph(desc, indent, linelen);
1193  buf << endl << indent << description_add_links(desc);
1194  }
1195  else
1196  {
1197  buf << description_add_links(desc);
1198  }
1199 
1200  get_os() << buf.str() << "</td></tr>" << endl;
1201 
1202  }
1203 
1204  for (Index i = 0; i < agr.In().nelem(); ++i)
1205  {
1206  buf.str("");
1207  buf << "<tr>";
1208  buf << "<td>IN</td>";
1209 
1210  const String& vname = Workspace::wsv_data[agr.In()[i]].Name();
1211  buf << "<td class=\"right\">" << insert_wsv_link(vname);
1212  buf << "</td><td>(";
1213  buf << insert_group_link(wsv_group_names[Workspace::wsv_data[agr.In()[i]].Group()]);
1214  buf << ")</td><td>";
1215 
1216  get_short_wsv_description(desc, Workspace::wsv_data[agr.In()[i]].Description());
1217 
1218  if (buf.str().length() + desc.length() > linelen)
1219  {
1220  format_paragraph(desc, indent, linelen, indent.length());
1221  buf << endl << indent << description_add_links(desc);
1222  }
1223  else
1224  buf << description_add_links(desc);
1225 
1226  get_os() << buf.str() << "</td></tr>" << endl;
1227  }
1228 
1229  get_os() << "</table>" << endl;
1230  }
1231 
1232  doc_variable_methods(aname);
1233  }
1234  else
1235  {
1236  insert_error_message("There is no agenda by this name.");
1237  }
1238 }
1239 
1240 
1242 
1250 void Docserver::doc_group(const string& gname)
1251 {
1252  // Check if the user gave the name of a specific variable.
1253  Index gid = get_wsv_group_id(gname);
1255  if (gid != -1)
1256  {
1257  // If we are here, then the given name matches a group.
1258  Index hitcount = 0;
1259 
1260  if (gname != "Any")
1261  {
1262  // List specific methods:
1263  hitcount = 0;
1264  get_os() << "<h3>Specific methods that can generate " << gname << "</h3>" << endl;
1265  get_os() << "<ul>" << endl;
1266  for (Index i = 0; i < md_data_raw.nelem(); ++i)
1267  {
1268  // Get handle on method record:
1269  const MdRecord& mdd = md_data_raw[i];
1270 
1271  bool first = true;
1272  for (Index j = 0; j < mdd.Out().nelem(); j++)
1273  {
1274  // This if statement checks whether the type of this output variable
1275  // matches this group.
1276  if (Workspace::wsv_data[mdd.Out()[j]].Group() == gid)
1277  {
1278  if (first)
1279  {
1280  first = false;
1281  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << " (";
1282  }
1283  else
1284  get_os() << ", ";
1285  get_os() << insert_wsv_link(Workspace::wsv_data[mdd.Out()[j]].Name());
1286 
1287  ++hitcount;
1288  }
1289  }
1290  if (!first) get_os() << ")" << endl;
1291  }
1292  if (0 == hitcount) get_os() << "<li>none" << endl;
1293 
1294  get_os() << endl << "</ul>" << endl;
1295  }
1296 
1297  // List generic methods:
1298  get_os() << "<h3>Generic and supergeneric methods that can generate " << gname << "</h3>"
1299  << endl;
1300  get_os() << "<ul>" << endl;
1301  for (Index i = 0; i < md_data_raw.nelem(); ++i)
1302  {
1303  // Get handle on method record:
1304  const MdRecord& mdd = md_data_raw[i];
1305 
1306  // This if statement checks whether GOutType, the list
1307  // of output variable types contains the group of the
1308  // requested variable.
1309  // The else clause picks up methods with supergeneric input.
1310  if (count(mdd.GOutType().begin(), mdd.GOutType().end(), gid))
1311  {
1312  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1313  ++hitcount;
1314  }
1315  else if (count(mdd.GOutType().begin(), mdd.GOutType().end(), get_wsv_group_id("Any")))
1316  {
1317  for (Index j = 0; j < mdd.GOutType().nelem(); j++)
1318  {
1319  if (mdd.GOutType()[j] == get_wsv_group_id("Any"))
1320  {
1321  if (mdd.GOutSpecType()[j].nelem())
1322  {
1323  if (count(mdd.GOutSpecType()[j].begin(),
1324  mdd.GOutSpecType()[j].end(),
1325  gid))
1326  {
1327  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1328  ++hitcount;
1329  }
1330  }
1331  else
1332  {
1333  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1334  ++hitcount;
1335  }
1336  }
1337  }
1338  }
1339  }
1340  if (0 == hitcount) get_os() << "<li>none" << endl;
1341 
1342  get_os() << endl << "</ul>" << endl;
1343 
1344  if (gname != "Any")
1345  {
1346  hitcount = 0;
1347  get_os() << "<h3>Specific methods that require variables of group " << gname << "</h3>"
1348  << endl;
1349  get_os() << "<ul>" << endl;
1350  for (Index i = 0; i < md_data_raw.nelem(); ++i)
1351  {
1352  // Get handle on method record:
1353  const MdRecord& mdd = md_data_raw[i];
1354 
1355  bool first = true;
1356  for (Index j = 0; j < mdd.In().nelem(); j++)
1357  {
1358  // This if statement checks whether the type of this output variable
1359  // matches this group.
1360  if (Workspace::wsv_data[mdd.In()[j]].Group() == gid)
1361  {
1362  if (first)
1363  {
1364  first = false;
1365  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << " (";
1366  }
1367  else
1368  get_os() << ", ";
1369  get_os() << insert_wsv_link(Workspace::wsv_data[mdd.In()[j]].Name());
1370 
1371  ++hitcount;
1372  }
1373  }
1374  if (!first) get_os() << ")" << endl;
1375  }
1376  if (0 == hitcount) get_os() << "<li>none" << endl;
1377 
1378  get_os() << endl << "</ul>" << endl;
1379  }
1380 
1381  hitcount = 0;
1382  get_os() << "<h3>Generic and supergeneric methods that can use " << gname << "</h3>" << endl;
1383  get_os() << "<ul>" << endl;
1384  for (Index i = 0; i < md_data_raw.nelem(); ++i)
1385  {
1386  // Get handle on method record:
1387  const MdRecord& mdd = md_data_raw[i];
1388 
1389  // This if statement checks whether GOutType, the list
1390  // of output variable types contains the group of the
1391  // requested variable.
1392  // The else clause picks up methods with supergeneric input.
1393  if (count(mdd.GInType().begin(), mdd.GInType().end(), gid))
1394  {
1395  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1396  ++hitcount;
1397  }
1398  else if (count(mdd.GInType().begin(), mdd.GInType().end(), get_wsv_group_id("Any")))
1399  {
1400  for (Index j = 0; j < mdd.GInType().nelem(); j++)
1401  {
1402  if (mdd.GInType()[j] == get_wsv_group_id("Any"))
1403  {
1404  if (mdd.GInSpecType()[j].nelem())
1405  {
1406  if (count(mdd.GInSpecType()[j].begin(),
1407  mdd.GInSpecType()[j].end(),
1408  gid))
1409  {
1410  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1411  ++hitcount;
1412  }
1413  }
1414  else
1415  {
1416  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1417  ++hitcount;
1418  }
1419  }
1420  }
1421  }
1422  }
1423  if (0 == hitcount) get_os() << "<li>none" << endl;
1424 
1425  get_os() << endl << "</ul>" << endl;
1426 
1427  if (gname != "Any")
1428  {
1429  Index i;
1430 
1431  get_os() << "<h3>Workspace Variables of group " << gname << "</h3>" << endl
1432  << "<ul>" << endl;
1433 
1434  hitcount = 0;
1435  for (i = 0; i < Workspace::wsv_data.nelem(); ++i)
1436  {
1437  if (Workspace::wsv_data[i].Group() == get_wsv_group_id(gname))
1438  {
1439  get_os() << "<li>" << insert_wsv_link(Workspace::wsv_data[i].Name()) << endl;
1440  hitcount++;
1441  }
1442  }
1443  if (0 == hitcount) get_os() << "<li>none" << endl;
1444 
1445  get_os() << "</ul>" << endl;
1446  }
1447  }
1448  else
1449  {
1450  insert_error_message("There is no group by this name.");
1451  }
1452 }
1453 
1454 
1456 
1463 void Docserver::find_token_type()
1464 {
1465  if (tokens.size() < 1 || tokens[0] != "all") return;
1466 
1467  // Make global data visible:
1468  using global_data::MdRawMap;
1469  using global_data::AgendaMap;
1470 
1471  // Find method id:
1472  if (MdRawMap.find(tokens[1]) != MdRawMap.end())
1473  tokens[0] = "methods";
1474  else if (AgendaMap.find(tokens[1]) != AgendaMap.end())
1475  tokens[0] = "agendas";
1476  else if (Workspace::WsvMap.find(tokens[1]) != Workspace::WsvMap.end())
1477  tokens[0] = "variables";
1478  else if (get_wsv_group_id(tokens[1]) != -1)
1479  tokens[0] = "groups";
1480 }
1481 
1482 
1484 
1492 void Docserver::insert_breadcrumb_token(size_t token_id)
1493 {
1494  if (token_id != tokens.size())
1495  {
1496  get_os() << "<a href=\"" << mbaseurl << "/";
1497  for (size_t t = 0; t < token_id; t++)
1498  {
1499  if (t) get_os() << "/";
1500  get_os() << tokens[t];
1501  }
1502  get_os() << "\">";
1503  }
1504 
1505  if (!token_id) get_os() << "Home";
1506  else if (tokens[token_id-1] == "methods") get_os() << "Methods";
1507  else if (tokens[token_id-1] == "variables") get_os() << "Variables";
1508  else if (tokens[token_id-1] == "agendas") get_os() << "Agendas";
1509  else if (tokens[token_id-1] == "groups") get_os() << "Groups";
1510  else if (tokens[token_id-1] == "all") get_os() << "All";
1511  else if (tokens[token_id-1] == "doccheck") get_os() << "Doc Check";
1512  else get_os() << tokens[token_id-1];
1513 
1514  if (token_id != tokens.size()) get_os() << "</a>";
1515 }
1516 
1517 
1519 
1525 void Docserver::insert_breadcrumbs()
1526 {
1527  get_os() << "<div id=\"navbar\"><div class=\"breadcrumbs\">";
1528  for (size_t t = 0; t <= tokens.size(); t++)
1529  {
1530  if (t) get_os() << "&nbsp;>>&nbsp;";
1531  insert_breadcrumb_token(t);
1532  }
1533  get_os() << "</div>" << endl;
1534 
1535  get_os()
1536  << "<div class=\"goto\">Go to: "
1537  << "<a href=\"" << mbaseurl << "/groups/\">Groups</a>&nbsp;-&nbsp;"
1538  << "<a href=\"" << mbaseurl << "/variables/\">Variables</a>&nbsp;-&nbsp;"
1539  << "<a href=\"" << mbaseurl << "/methods/\">Methods</a>&nbsp;-&nbsp;"
1540  << "<a href=\"" << mbaseurl << "/agendas/\">Agendas</a>"
1541  << "</div></div>" << endl;
1542 }
1543 
1544 
1546 
1553 void Docserver::insert_error(const string& error)
1554 {
1555  begin_page("");
1556  insert_breadcrumbs();
1557  begin_content();
1558  insert_error_message(error);
1559  end_content();
1560  end_page();
1561 }
1562 
1563 
1565 
1572 void Docserver::insert_error_message(const string& error)
1573 {
1574  if (error.length())
1575  get_os() << "<p class=\"error\">" << error << "</p>" << endl;
1576 }
1577 
1578 
1580 
1587 void Docserver::insert_title(const string& title)
1588 {
1589  get_os() << "<h1>" DOCSERVER_NAME;
1590  if (title.length())
1591  get_os() << " &mdash; " << title;
1592  get_os() << "</h1>" << endl;
1593 }
1594 
1595 
1597 
1603 void Docserver::insert_index()
1604 {
1605  if (tokens.size() == 0 || tokens[0] == "all")
1606  {
1607  begin_page("");
1608  insert_breadcrumbs();
1609  begin_content();
1610  insert_title("Index");
1611 
1612  list_groups();
1613  list_variables();
1614  list_methods();
1615  list_agendas();
1616  end_content();
1617  end_page();
1618  }
1619  else
1620  {
1621  if (tokens[0] == "methods")
1622  {
1623  begin_page("Method Index");
1624  insert_breadcrumbs();
1625  begin_content();
1626  insert_title("Method Index");
1627  get_os() << "<table class=\"list\">" << endl;
1628  list_methods();
1629  get_os() << "</table>" << endl;
1630  end_content();
1631  end_page();
1632  }
1633  else if (tokens[0] == "variables")
1634  {
1635  begin_page("Variable Index");
1636  insert_breadcrumbs();
1637  begin_content();
1638  insert_title("Variable Index");
1639  get_os() << "<table class=\"list\">" << endl;
1640  list_variables();
1641  get_os() << "</table>" << endl;
1642  end_content();
1643  end_page();
1644  }
1645  else if (tokens[0] == "groups")
1646  {
1647  begin_page("Group Index");
1648  insert_breadcrumbs();
1649  begin_content();
1650  insert_title("Group Index");
1651  get_os() << "<table class=\"list\">" << endl;
1652  list_groups();
1653  get_os() << "</table>" << endl;
1654  end_content();
1655  end_page();
1656  }
1657  else if (tokens[0] == "agendas")
1658  {
1659  begin_page("Agenda Index");
1660  insert_breadcrumbs();
1661  begin_content();
1662  insert_title("Agenda Index");
1663  get_os() << "<table class=\"list\">" << endl;
1664  list_agendas();
1665  get_os() << "</table>" << endl;
1666  end_content();
1667  end_page();
1668  }
1669  else
1670  {
1671  insert_error(DS_ERROR_404);
1672  }
1673  }
1674 }
1675 
1676 
1678 
1684 void Docserver::insert_doc()
1685 {
1686  find_token_type();
1687 
1688  if (tokens[0] == "methods")
1689  {
1690  begin_page(tokens[1]);
1691  insert_breadcrumbs();
1692  begin_content();
1693  insert_title();
1694  get_os() << "<h2>" << "Workspace Method " + tokens[1] << "</h2>" << endl;
1695  doc_method(tokens[1]);
1696  end_content();
1697  end_page();
1698  }
1699  else if (tokens[0] == "variables")
1700  {
1701  begin_page(tokens[1]);
1702  insert_breadcrumbs();
1703  begin_content();
1704  insert_title();
1705  get_os() << "<h2>" << "Workspace Variable " + tokens[1] << "</h2>" << endl;
1706  doc_variable(tokens[1]);
1707  end_content();
1708  end_page();
1709  }
1710  else if (tokens[0] == "groups")
1711  {
1712  begin_page(tokens[1]);
1713  insert_breadcrumbs();
1714  begin_content();
1715  insert_title();
1716  get_os() << "<h2>" << "Workspace Group " + tokens[1] << "</h2>" << endl;
1717  doc_group(tokens[1]);
1718  end_content();
1719  end_page();
1720  }
1721  else if (tokens[0] == "agendas")
1722  {
1723  begin_page(tokens[1]);
1724  insert_breadcrumbs();
1725  begin_content();
1726  insert_title();
1727  get_os() << "<h2>" << "Agenda " + tokens[1] << "</h2>" << endl;
1728  doc_agenda(tokens[1]);
1729  end_content();
1730  end_page();
1731  }
1732  else
1733  {
1734  insert_error(DS_ERROR_404);
1735  }
1736 }
1737 
1738 
1740 
1745 void Docserver::insert_stylesheet()
1746 {
1747  get_os()
1748  << "body { font-family: monospace; }" << endl
1749  << "a:link { color: #3465a4; text-decoration: none; }" << endl
1750  << "a:visited { color: #729fcf; text-decoration: none; }" << endl
1751  << "a:active { color: #ce5c00; text-decoration: none; background-color: #eeeeec}" << endl
1752  << "a:hover { color: #f57900; text-decoration: none; }" << endl
1753 
1754  << "table.list {" << endl
1755  << "width: 90%;" << endl
1756  << "margin-left: 5%;" << endl
1757  << "margin-right: 5%;" << endl
1758  << "}" << endl
1759 
1760  << "h1 {" << endl
1761  << "font-size: 1.5em;" << endl
1762  << "}" << endl
1763 
1764  << "h2 {" << endl
1765  << "font-size: 1.25em;" << endl
1766  << "}" << endl
1767 
1768  << "h3 {" << endl
1769  << "font-size: 1em;" << endl
1770  << "}" << endl
1771 
1772  << "li {" << endl
1773  << "font-size: 1em;" << endl
1774  << "}" << endl
1775 
1776  << "#navbar {" << endl
1777  << "position: fixed;" << endl
1778  << "top: 0px;" << endl
1779  << "left: 10px;" << endl
1780  << "right: 10px;" << endl
1781  << "background-color: #fff;" << endl
1782  << "border-bottom: solid 1px #ddd;" << endl
1783  << "border-left: solid 1px #ddd;" << endl
1784  << "border-right: solid 1px #ddd;" << endl
1785  << "padding: 2px;" << endl
1786  << "}" << endl
1787 
1788  << ".firstcol {" << endl
1789  << "float: left;" << endl
1790  << "clear: left;" << endl
1791  << "width: 50%;" << endl
1792  << "white-space: nowrap;" << endl
1793  << "}" << endl
1794 
1795  << ".firstcol ul {" << endl
1796  << "float: left;" << endl
1797  << "clear: both;" << endl
1798  << "padding-top: 0;" << endl
1799  << "}" << endl
1800 
1801  << ".secondcol ul {" << endl
1802  << "float: left;" << endl
1803  << "clear: both;" << endl
1804  << "padding-top: 0;" << endl
1805  << "}" << endl
1806 
1807  << ".secondcol {" << endl
1808  << "float: left;" << endl
1809  << "clear: right;" << endl
1810  << "width: 50%;" << endl
1811  << "white-space: nowrap;" << endl
1812  << "}" << endl
1813 
1814  << ".firstcol ul li {" << endl
1815  << "margin-left: 0;" << endl
1816  << "}" << endl
1817 
1818  << ".brokendoclink {" << endl
1819  << "color: #f00;" << endl
1820  << "}" << endl
1821 
1822  << ".goto {" << endl
1823  << "font-size: small;" << endl
1824  << "float: right;" << endl
1825  << "}" << endl
1826 
1827  << ".breadcrumbs {" << endl
1828  << "font-size: small;" << endl
1829  << "float: left;" << endl
1830  << "}" << endl
1831 
1832  << "@media only screen and (max-device-width: 480px) {" << endl
1833  << "#navbar { position: static; border: none; }" << endl
1834  << ".goto { position: static; float: none; }" << endl
1835  << ".breadcrumbs { position: static; float: none; }" << endl
1836  << ".firstcol { float: left; clear: left; width: 100%; }" << endl
1837  << ".secondcol { float: left; clear: both; width: 100%; }" << endl
1838  << ".firstcol ul { margin-top: 0; margin-bottom: 0; }" << endl
1839  << ".secondcol ul { margin-top: 0; }" << endl
1840  << "ul { padding-left: 1em; }" << endl
1841  << "}" << endl
1842 
1843  << "table {" << endl
1844  << "border-width: 0px;" << endl
1845  << "}" << endl
1846 
1847  << "table td {" << endl
1848  << "vertical-align: top;" << endl
1849  << "}" << endl
1850 
1851  << "table td.right {" << endl
1852  << "text-align: right;" << endl
1853  << "}" << endl
1854 
1855  << ".content {" << endl
1856  << "padding-top: 1em;" << endl
1857  << "clear: both;" << endl
1858  << "width: 100%;" << endl
1859  << "}" << endl
1860 
1861  << ".error {" << endl
1862  << "color: #ff0000;" << endl
1863  << "font-weight: bold;" << endl
1864  << "font-size: 1.2em;" << endl
1865  << "}" << endl
1866 
1867  << "div.footer {" << endl
1868  << "float: left;" << endl
1869  << "text-align: right;" << endl
1870  << "color: #aaaaa8;" << endl
1871  << "font-size: small;" << endl
1872  << "clear: left;" << endl
1873  << "margin-top: 2em;" << endl
1874  << "width: 100%;" << endl
1875  << "}" << endl
1876 
1877  << endl;
1878 }
1879 
1880 
1882 
1888 void Docserver::insert_broken_doc_links()
1889 {
1890  begin_page(tokens[1]);
1891  insert_breadcrumbs();
1892  begin_content();
1893  insert_title("Broken links");
1894 
1895  // Broken links in WSV descriptions
1896  bool first = true;
1897  for (Index i = 0; i < Workspace::wsv_data.nelem(); ++i)
1898  {
1899  vector<string> broken_links;
1900  broken_links = find_broken_description_links(Workspace::wsv_data[i].Description());
1901 
1902  if (broken_links.size())
1903  {
1904  if (first)
1905  {
1906  first = false;
1907  get_os() << "<h2>Variable descriptions</h2>" << endl;
1908  }
1909  get_os() << "<p>" << insert_wsv_link(Workspace::wsv_data[i].Name()) << ": ";
1910  for (vector<string>::iterator it = broken_links.begin();
1911  it != broken_links.end(); it++)
1912  {
1913  if (it != broken_links.begin()) get_os() << ", ";
1914  get_os() << *it;
1915  }
1916  get_os() << "</p>" << endl;
1917  }
1918  }
1919 
1920  // Broken links in agenda descriptions
1922  first = true;
1923  for (Array<AgRecord>::const_iterator ait = agenda_data.begin();
1924  ait != agenda_data.end();
1925  ait++)
1926  {
1927  vector<string> broken_links;
1928  broken_links = find_broken_description_links(ait->Description());
1929 
1930  if (broken_links.size())
1931  {
1932  if (first)
1933  {
1934  first = false;
1935  get_os() << "<h2>Agenda descriptions</h2>" << endl;
1936  }
1937  get_os() << "<p>" << insert_agenda_link(ait->Name()) << ": ";
1938  for (vector<string>::iterator it = broken_links.begin();
1939  it != broken_links.end(); it++)
1940  {
1941  if (it != broken_links.begin()) get_os() << ", ";
1942  get_os() << *it;
1943  }
1944  get_os() << "</p>" << endl;
1945  }
1946  }
1947 
1948  // Broken links in method descriptions
1950 
1951  first = true;
1952  for (Array<MdRecord>::const_iterator mit = md_data_raw.begin();
1953  mit != md_data_raw.end();
1954  mit++)
1955  {
1956  vector<string> broken_links;
1957  broken_links = find_broken_description_links(mit->Description(), mit->Name());
1958 
1959  if (broken_links.size())
1960  {
1961  if (first)
1962  {
1963  first = false;
1964  get_os() << "<h2>Method descriptions</h2>" << endl;
1965  }
1966  get_os() << "<p>" << insert_wsm_link(mit->Name()) << ": ";
1967  for (vector<string>::iterator it = broken_links.begin();
1968  it != broken_links.end(); it++)
1969  {
1970  if (it != broken_links.begin()) get_os() << ", ";
1971  get_os() << *it;
1972  }
1973  get_os() << "</p>" << endl;
1974  }
1975  }
1976 
1977  end_content();
1978  end_page();
1979 }
1980 
1981 
1983 
1993 vector<string> Docserver::find_broken_description_links(const String& desc, const String& mname)
1994 {
1995  vector<string> broken_links;
1996  string ret;
1997  string link;
1998  bool inside_link = false;
1999  string::const_iterator it = desc.begin();
2000 
2001  using global_data::MdRawMap;
2002  using global_data::AgendaMap;
2003 
2004  while (it != desc.end())
2005  {
2006  if (!inside_link)
2007  {
2008  if (*it == '*')
2009  inside_link = true;
2010  else
2011  ret += html_escape_char(*it);
2012  }
2013  else
2014  {
2015  if (*it == '*')
2016  {
2017  inside_link = false;
2018  bool found = false;
2019  if (MdRawMap.find(link) != MdRawMap.end()
2020  || AgendaMap.find(link) != AgendaMap.end()
2021  || Workspace::WsvMap.find(link) != Workspace::WsvMap.end()
2022  || get_wsv_group_id(link) != -1)
2023  found = true;
2024  else if (mname != "")
2025  {
2026  using global_data::MdRawMap;
2027 
2028  // Find method id:
2029  map<String, Index>::const_iterator mit = MdRawMap.find(mname);
2030  if (mit != MdRawMap.end())
2031  {
2033  const MdRecord& mdr = md_data_raw[mit->second];
2034 
2035  for (ArrayOfString::const_iterator sit = mdr.GIn().begin();
2036  !found && sit != mdr.GIn().end();
2037  sit++)
2038  {
2039  if ((*sit) == link)
2040  {
2041  ret += "*" + link + "*";
2042  found = true;
2043  }
2044  }
2045 
2046  for (ArrayOfString::const_iterator sit = mdr.GOut().begin();
2047  !found && sit != mdr.GOut().end();
2048  sit++)
2049  {
2050  if ((*sit) == link)
2051  {
2052  ret += "*" + link + "*";
2053  found = true;
2054  }
2055  }
2056  }
2057  }
2058 
2059  if (!found)
2060  broken_links.push_back("<span class=\"brokendoclink\">*" + link + "*</span>");
2061 
2062  link = "";
2063  }
2064  else
2065  {
2066  if (!isalnum(*it) && *it != '_')
2067  {
2068  inside_link = false;
2069  ret += "*" + link + *it;
2070  link = "";
2071  }
2072  else
2073  link += html_escape_char(*it);
2074  }
2075  }
2076 
2077  it++;
2078  }
2079 
2080  // if (inside_link) ret += "*" + link;
2081 
2082  return broken_links;
2083 }
2084 
2085 
2087 
2096 int Docserver::launch(bool daemon)
2097 {
2098  struct MHD_Daemon* d;
2099 
2100  d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
2101  (uint16_t)mport, NULL, NULL, &ahc_echo, (void*)this, MHD_OPTION_END);
2102 
2103  if (d == NULL)
2104  {
2105  cerr << "Error: Cannot start server. Maybe port " << mport << " is already in use?\n";
2106  return 1;
2107  }
2108  else
2109  {
2110  if (daemon)
2111  cerr << "ARTS docserver listening at http://localhost:" << mport << "\n";
2112  else
2113  cerr << "\n"
2114  << "===========================================================\n"
2115  << "Now point your web browser to http://localhost:" << mport << "\n"
2116  << "===========================================================\n\n"
2117  << "Press enter to exit.\n";
2118  }
2119 
2120  if (daemon) pause();
2121  else
2122  {
2123  (void) getc(stdin);
2124  cout << "Stopping docserver.\n";
2125  MHD_stop_daemon(d);
2126  cout << "Goodbye.\n";
2127  }
2128  return 0;
2129 }
2130 
2131 
2133 
2141 Docserver::Docserver(const Index port, const string& baseurl) : mos(NULL)
2142 {
2143  mbaseurl = baseurl;
2144  if (port == -1) mport = 9000;
2145  else mport = port;
2146 }
2147 
2148 
2150 
2167 static int ahc_echo(void* cls,
2168  struct MHD_Connection* connection,
2169  const char* url,
2170  const char* method,
2171  const char* version _U_,
2172  const char* upload_data _U_,
2173  size_t* upload_data_size _U_,
2174  void** ptr)
2175 {
2176  static int aptr;
2177  string surl(url);
2178  struct MHD_Response* response;
2179  int ret;
2180 
2181  if (!cls)
2182  {
2183  cerr << "Docserver error: Docserver object reference is NULL.\n";
2184  return MHD_NO; /* unexpected method */
2185  }
2186 
2187  // Make a local copy of the docserver object for thread-safety
2188  Docserver docserver = *((Docserver*)cls);
2189 
2190  if (0 != strcmp(method, "GET"))
2191  {
2192  cerr << "Docserver error: Unexpected method " << method << ".\n";
2193  return MHD_NO; /* unexpected method */
2194  }
2195  if (&aptr != *ptr)
2196  {
2197  /* do never respond on first call */
2198  *ptr = &aptr;
2199  return MHD_YES;
2200  }
2201  *ptr = NULL; /* reset when done */
2202  MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "q");
2203 
2204  string content_type;
2205  ostringstream hout;
2206  docserver.set_ostream(hout);
2207  content_type = docserver.new_page(surl);
2208  docserver.clear_ostream();
2209 
2210  response = MHD_create_response_from_data(hout.str().length(),
2211  (void*)hout.str().c_str(),
2212  MHD_NO, MHD_YES);
2213 
2214  if (response == NULL)
2215  {
2216  cerr << "Docserver error: response = 0\n";
2217  return MHD_NO;
2218  }
2219 
2220  MHD_add_response_header(response, "Content-type", content_type.c_str());
2221  ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
2222  MHD_destroy_response(response);
2223 
2224  return ret;
2225 }
2226 
2227 
2228 #endif /* ENABLE_DOCSERVER */
2229 
Index get_wsv_group_id(const String &name)
Returns the id of the given group.
Definition: groups.cc:205
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:35
static Array< WsvRecord > wsv_data
Definition: workspace_ng.h:64
Lookup information for one agenda.
Definition: agenda_record.h:44
Index nelem() const
Number of elements.
Definition: array.h:176
#define ARTS_FULL_VERSION
Definition: auto_version.h:1
Declarations having to do with the four output streams.
Declarations for the arts documentation server.
map< String, Index > AgendaMap
The map associated with agenda_data.
const ArrayOfIndex & Out() const
Definition: methods.h:92
const Array< String > & GInDescription() const
Definition: methods.h:102
const ArrayOfIndex & Out() const
Definition: agenda_record.h:64
const map< String, Index > MdRawMap
The map associated with md_data_raw.
Definition: methods_aux.cc:41
All information for one workspace method.
Definition: methods.h:42
const String & Name() const
Definition: methods.h:89
const ArrayOfString & GOut() const
Definition: methods.h:93
This file contains the declaration and partly the implementation of the workspace class...
The implementation for String, the ARTS string class.
Definition: mystring.h:63
const Array< String > & GInDefault() const
Definition: methods.h:101
#define NODEF
Definition: methods.h:36
const ArrayOfIndex & In() const
Definition: methods.h:97
const ArrayOfArrayOfIndex & GInSpecType() const
Definition: methods.h:100
const ArrayOfString & Authors() const
Definition: methods.h:91
bool format_paragraph(String &s, const String &indent, const size_t linelen, const size_t offset=0)
Definition: methods_aux.cc:526
const Array< AgRecord > agenda_data
The lookup information for the agendas.
Definition: agendas.cc:41
const ArrayOfIndex & In() const
Definition: agenda_record.h:65
This can be used to make arrays out of anything.
Definition: array.h:40
const ArrayOfIndex & InOnly() const
Definition: methods.h:103
const ArrayOfIndex & GOutType() const
Definition: methods.h:94
const ArrayOfString wsv_group_names
The names associated with Wsv groups as Strings.
Definition: global_data.h:94
void get_short_wsv_description(String &s, const String &desc)
Definition: methods_aux.cc:565
static map< String, Index > WsvMap
Definition: workspace_ng.h:67
const Array< String > & GOutDescription() const
Definition: methods.h:96
#define _U_
Definition: config.h:167
const ArrayOfArrayOfIndex & GOutSpecType() const
Definition: methods.h:95
const String & Description() const
Definition: methods.h:90
const ArrayOfIndex & GInType() const
Definition: methods.h:99
const String & Name() const
Definition: agenda_record.h:62
void limit_line_length(ostream &os, ostringstream &curline, ostringstream &token, const String &indent, size_t linelen)
Limit length of output.
Definition: methods_aux.cc:656
Declaration of the class MdRecord.
const ArrayOfString & GIn() const
Definition: methods.h:98
Declarations for AgRecord, storing lookup information for one agenda.
const Array< MdRecord > md_data_raw
Lookup information for workspace methods.
Definition: methods.cc:39