ARTS  2.0.49
docserver.cc
Go to the documentation of this file.
1 /* Copyright (C) 2010 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 
44 #define DOCSERVER_NAME "ARTS built-in documentation server"
45 
46 #define DS_ERROR_404 "Page not found"
47 
48 
49 static int ahc_echo(void *cls, struct MHD_Connection *connection,
50  const char *url, const char *method,
51  const char *version, const char *upload_data _U_,
52  size_t *upload_data_size, void **ptr);
53 
54 
56 
61 void Docserver::limit_line_length(ostringstream& curline,
62  ostringstream& token,
63  const String& indent,
64  size_t linelen)
65 {
66  if (indent.length() + curline.str().length() + token.str().length() > linelen)
67  {
68  get_os() << curline.str() << endl << indent;
69  curline.str("");
70  }
71  curline << token.str();
72  token.str("");
73 }
74 
75 
77 
85 string Docserver::new_page(const string &url)
86 {
87  string surl = url;
88 
89  if (surl.find(get_baseurl()) == 0)
90  {
91  surl.erase(0, get_baseurl().size());
92  }
93 
94  split_tokens (surl);
95 
96  while (tokens.size() && tokens[tokens.size()-1] == "")
97  tokens.erase(tokens.end());
98 
99  while (tokens.size() && tokens[0] == "")
100  tokens.erase(tokens.begin());
101 
102  string content_type = "text/html; charset=utf-8";
103  if (tokens.size() && tokens[tokens.size()-1] == "styles.css")
104  {
105  insert_stylesheet();
106  content_type = "text/css; charset=utf-8";
107  }
108  else if (tokens.size() && tokens[tokens.size()-1] == "doccheck")
109  {
110  insert_broken_doc_links();
111  }
112  else
113  {
114  switch (tokens.size())
115  {
116  case 0:
117  case 1:
118  insert_index();
119  break;
120  case 2:
121  insert_doc();
122  break;
123  default:
124  insert_error(DS_ERROR_404);
125  }
126  }
127 
128  return content_type;
129 }
130 
131 
133 
141 void Docserver::split_tokens(const string &s)
142 {
143  tokens.clear();
144 
145  stringstream ss(s);
146  string item;
147  while (getline(ss, item, '/'))
148  {
149  tokens.push_back(item);
150  }
151 }
152 
153 
155 
165 string Docserver::html_escape_char(const char ch)
166 {
167  string ret;
168 
169  switch (ch)
170  {
171  case '<': ret.append("&lt;"); break;
172  case '>': ret.append("&gt;"); break;
173  default: ret.append(1, ch);
174  }
175 
176  return ret;
177 }
178 
179 
181 
190 string Docserver::html_escape_string(const string& s)
191 {
192  string ret;
193 
194  for (string::const_iterator it = s.begin(); it != s.end(); it++)
195  {
196  ret.append(html_escape_char(*it));
197  }
198 
199  return ret;
200 }
201 
202 
204 
209 void Docserver::begin_content()
210 {
211  get_os() << "<div class=\"content\">" << endl;
212 }
213 
214 
216 
221 void Docserver::end_content()
222 {
223  get_os() << "</div>" << endl;
224 }
225 
226 
228 
235 void Docserver::begin_page (string title)
236 {
237  if (title.length())
238  title += " - ";
239 
240  title += DOCSERVER_NAME;
241 
242  get_os()
243  << "<!DOCTYPE html>" << endl
244  << "<html lang=\"en\">" << endl
245  << "<head>" << endl
246  << "<title>" << title << "</title>" << endl
247  << "<meta charset=\"utf-8\">" << endl
248  << "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />" << endl
249  << "<link rel=\"stylesheet\" href=\"" << mbaseurl << "/styles.css\">" << endl
250  << "</head>" << endl
251  << "<body>" << endl;
252 }
253 
254 
256 
261 void Docserver::end_page ()
262 {
263  get_os() << "<div class=\"footer\">Page generated by " << ARTS_FULL_VERSION
264  << " - <a href=\"" << mbaseurl << "/doccheck\">Check docs</a></div>\n" << endl;
265 
266  get_os() << "</body>" << endl << "</html>";
267 }
268 
269 
271 
280 String Docserver::insert_agenda_link (const String &aname)
281 {
282  ostringstream ret;
283  ret << "<a href=\"" << mbaseurl << "/agendas/" << aname << "\">" << aname << "</a>";
284  return ret.str();
285 }
286 
287 
289 
298 String Docserver::insert_group_link (const String &gname)
299 {
300  ostringstream ret;
301  ret << "<a href=\"" << mbaseurl << "/groups/" << gname << "\">" << gname << "</a>";
302  return ret.str();
303 }
304 
305 
307 
316 String Docserver::insert_wsm_link (const String &mname)
317 {
318  ostringstream ret;
319  ret << "<a href=\"" << mbaseurl << "/methods/" << mname << "\">" << mname << "</a>";
320  return ret.str();
321 }
322 
323 
325 
334 String Docserver::insert_wsv_link (const String &vname)
335 {
336  ostringstream ret;
337 
338  // Find wsv id:
339  map<String, Index>::const_iterator it = Workspace::WsvMap.find(vname);
340  if ( it != Workspace::WsvMap.end() )
341  {
342  if (Workspace::wsv_data[it->second].Group() == get_wsv_group_id("Agenda"))
343  ret << "<a href=\"" << mbaseurl << "/agendas/" << vname << "\">" << vname << "</a>";
344  else
345  ret << "<a href=\"" << mbaseurl << "/variables/" << vname << "\">" << vname << "</a>";
346  }
347 
348  return ret.str();
349 }
350 
351 
353 
358 void Docserver::list_agendas ()
359 {
360  Index i;
361 
362  get_os() << "<h2>Agendas</h2>" << endl;
363 
364  get_os() << "<div class=\"firstcol\">" << endl << "<ul>" << endl;
365  Index hitcount = 0;
366  for ( i=0; i<Workspace::wsv_data.nelem(); ++i )
367  {
368  if (Workspace::wsv_data[i].Group() == get_wsv_group_id("Agenda"))
369  hitcount++;
370  }
371 
372  Index hitcount2 = 0;
373  for ( i=0; i<Workspace::wsv_data.nelem(); ++i )
374  {
375  if (Workspace::wsv_data[i].Group() == get_wsv_group_id("Agenda"))
376  {
377  get_os() << "<li>" << insert_agenda_link(Workspace::wsv_data[i].Name()) << "</li>" << endl;
378  hitcount2++;
379 
380  if (hitcount2 == hitcount/2)
381  get_os() << "</ul>" << endl << "</div>" << endl
382  << "<div class=\"secondcol\">" << endl << "<ul>" << endl;
383  }
384  }
385 
386  get_os() << "</ul>" << endl << "</div>" << endl;
387 }
388 
389 
391 
396 void Docserver::list_groups ()
397 {
398  extern const ArrayOfString wsv_group_names;
399  Index i;
400 
401  get_os() << "<h2>Workspace Groups</h2>" << endl;
402 
403  get_os() << "<div class=\"firstcol\">" << endl << "<ul>" << endl;
404  for ( i=0; i<wsv_group_names.nelem(); ++i )
405  {
406  get_os() << "<li>" << insert_group_link(wsv_group_names[i]) << "</li>" << endl;
407 
408  if (i+1 == wsv_group_names.nelem()/2)
409  get_os() << "</ul>" << endl << "</div>" << endl
410  << "<div class=\"secondcol\">" << endl << "<ul>" << endl;
411  }
412 
413  get_os() << "</ul>" << endl << "</div>" << endl;
414 }
415 
416 
418 
423 void Docserver::list_methods ()
424 {
425  extern const Array<MdRecord> md_data_raw;
426  Index i;
427 
428  get_os() << "<h2>Workspace Methods</h2>" << endl;
429 
430  get_os() << "<div class=\"firstcol\">" << endl << "<ul>" << endl;
431  for ( i=0; i<md_data_raw.nelem(); ++i )
432  {
433  get_os() << "<li>" << insert_wsm_link(md_data_raw[i].Name()) << "</li>" << endl;
434 
435  if (i+1 == md_data_raw.nelem()/2)
436  get_os() << "</ul>" << endl << "</div>" << endl
437  << "<div class=\"secondcol\">" << endl << "<ul>" << endl;
438  }
439 
440  get_os() << "</ul>" << endl << "</div>" << endl;
441 }
442 
443 
445 
450 void Docserver::list_variables ()
451 {
452  Index i;
453 
454  get_os() << "<h2>Workspace Variables</h2>" << endl;
455 
456  get_os() << "<div class=\"firstcol\">" << endl << "<ul>" << endl;
457  Index hitcount = 0;
458  for ( i=0; i<Workspace::wsv_data.nelem(); ++i )
459  {
460  if (Workspace::wsv_data[i].Group() != get_wsv_group_id("Agenda"))
461  hitcount++;
462  }
463 
464  Index hitcount2 = 0;
465  for ( i=0; i<Workspace::wsv_data.nelem(); ++i )
466  {
467  if (Workspace::wsv_data[i].Group() != get_wsv_group_id("Agenda"))
468  {
469  get_os() << "<li>" << insert_wsv_link(Workspace::wsv_data[i].Name()) << "</li>" << endl;
470  hitcount2++;
471 
472  if (hitcount2 == hitcount/2)
473  get_os() << "</ul>" << endl << "</div>" << endl
474  << "<div class=\"secondcol\">" << endl << "<ul>" << endl;
475  }
476  }
477 
478  get_os() << "</ul>" << endl << "</div>" << endl;
479 }
480 
481 
483 
493 String Docserver::description_add_links (const String& desc, const String& mname)
494 {
495  string ret;
496  string link;
497  bool inside_link = false;
498  string::const_iterator it = desc.begin();
499 
500  extern const map<String, Index> MdRawMap;
501  extern const map<String, Index> AgendaMap;
502 
503  while (it != desc.end()) {
504  if (!inside_link)
505  {
506  if (*it == '*')
507  inside_link = true;
508  else
509  ret += html_escape_char(*it);
510  }
511  else
512  {
513  if (*it == '*')
514  {
515  inside_link = false;
516  if (MdRawMap.find(link) != MdRawMap.end())
517  ret += insert_wsm_link(link);
518  else if (AgendaMap.find(link) != AgendaMap.end())
519  ret += insert_agenda_link(link);
520  else if (Workspace::WsvMap.find(link) != Workspace::WsvMap.end())
521  ret += insert_wsv_link(link);
522  else if (get_wsv_group_id (link) != -1)
523  ret += insert_group_link(link);
524  else if (mname != "")
525  {
526  extern const map<String, Index> MdRawMap;
527  bool found = false;
528 
529  // Find method id:
530  map<String, Index>::const_iterator mit = MdRawMap.find(mname);
531  if ( mit != MdRawMap.end() )
532  {
533  extern const Array<MdRecord> md_data_raw;
534  const MdRecord& mdr = md_data_raw[mit->second];
535 
536  for (ArrayOfString::const_iterator sit = mdr.GIn().begin();
537  !found && sit != mdr.GIn().end();
538  sit++)
539  {
540  if ((*sit) == link)
541  {
542  ret += "*" + link + "*";
543  found = true;
544  }
545  }
546 
547  for (ArrayOfString::const_iterator sit = mdr.GOut().begin();
548  !found && sit != mdr.GOut().end();
549  sit++)
550  {
551  if ((*sit) == link)
552  {
553  ret += "*" + link + "*";
554  found = true;
555  }
556  }
557  }
558 
559  if (!found)
560  ret += "<span class=\"brokendoclink\">*" + link + "*</span>";
561  }
562  else
563  ret += "<span class=\"brokendoclink\">*" + link + "*</span>";
564 
565  link = "";
566  }
567  else
568  {
569  if (!isalnum(*it) && *it != '_')
570  {
571  inside_link = false;
572  ret += "*" + link + *it;
573  link = "";
574  }
575  else
576  link += html_escape_char(*it);
577  }
578  }
579 
580  it++;
581  }
582 
583  if (inside_link) ret += "*" + link;
584 
585  return ret;
586 }
587 
588 
590 
598 void Docserver::doc_method (const string& mname)
599 {
600  // Make global data visible:
601  extern const Array<MdRecord> md_data_raw;
602  extern const map<String, Index> MdRawMap;
603  extern const ArrayOfString wsv_group_names;
604 
605  // Let's first assume it is a method that the user wants to have
606  // described.
607 
608  // Find method id:
609  map<String, Index>::const_iterator it = MdRawMap.find(mname);
610  if ( it != MdRawMap.end() )
611  {
612  // If we are here, then the given name matches a method.
613  const MdRecord& mdr = md_data_raw[it->second];
614  String indent = "";
615 
616  get_os() << "<h3>Description</h3>" << endl;
617 
618  get_os() << "<pre>" << endl;
619  get_os() << description_add_links(mdr.Description(), mname);
620  get_os() << endl << "</pre>" << endl << endl;
621 
622  bool is_first_author = true;
623  for (Index i = 0; i < mdr.Authors().nelem(); i++)
624  {
625  if (is_first_author)
626  {
627  get_os() << "<p><b>Authors: </b>";
628  is_first_author = false;
629  }
630  else
631  get_os() << ", ";
632 
633  get_os() << mdr.Authors()[i];
634  }
635  get_os() << "\n";
636 
637  // Print the method's synopsis
638  while (indent.length() < mdr.Name().length() + 2) indent += ' ';
639 
640  get_os() << "<h3>Synopsis</h3>" << endl;
641 
642  ostringstream buf;
643  ostringstream param;
644  const size_t linelen = 2048;
645 
646  buf << "<p><table><tr><td>" << mdr.Name() << "(&nbsp;</td><td>";
647  bool first = true;
648  for ( Index i=0; i<mdr.Out().nelem(); ++i )
649  {
650  if (first) first=false; else buf << ", ";
651  param << insert_wsv_link(Workspace::wsv_data[mdr.Out()[i]].Name());
652 
653  limit_line_length( buf, param, indent, linelen );
654  }
655 
656  for ( Index i=0; i<mdr.GOutType().nelem(); ++i )
657  {
658  if (first) first=false; else buf << ", ";
659  if (mdr.GOut()[i].length())
660  param << mdr.GOut()[i];
661  else
662  param << "gout" << i;
663 
664  limit_line_length( buf, param, indent, linelen );
665  }
666 
667  const ArrayOfIndex &inonly = mdr.InOnly();
668  for ( Index i=0; i<inonly.nelem(); ++i )
669  {
670  if (first) first=false; else buf << ", ";
671  param << insert_wsv_link(Workspace::wsv_data[inonly[i]].Name());
672 
673  limit_line_length( buf, param, indent, linelen );
674  }
675 
676  for ( Index i=0; i<mdr.GInType().nelem(); ++i )
677  {
678  if (first) first=false; else buf << ", ";
679  if (mdr.GIn()[i].length())
680  {
681  param << mdr.GIn()[i];
682  }
683  else
684  {
685  param << "gin" << i;
686  }
687 
688  limit_line_length( buf, param, indent, linelen );
689  }
690  if (buf.str().length()) get_os() << buf.str();
691 
692  get_os() << " )</td></tr></table>" << endl;
693 
694  get_os() << "<h3>Variables</h3>" << endl;
695 
696  // Out:
697  indent = "";
698  String desc;
699  get_os() << "<table>" << endl;
700  for ( Index i=0; i<mdr.Out().nelem(); ++i )
701  {
702  buf.str("");
703  buf << "<tr>";
704  buf << "<td>OUT</td>";
705 
706  {
707  const String& vname = Workspace::wsv_data[mdr.Out()[i]].Name();
708  buf << "<td class=\"right\">" << insert_wsv_link(vname) << "</td><td>(";
709  buf << insert_group_link(wsv_group_names[Workspace::wsv_data[mdr.Out()[i]].Group()]);
710  buf << ")</td><td>";
711  }
712 
713  get_short_wsv_description(desc, Workspace::wsv_data[mdr.Out()[i]].Description());
714 
715  if (buf.str().length() + desc.length() > linelen)
716  {
717  format_paragraph (desc, indent, linelen);
718  buf << endl << indent << description_add_links(desc);
719  }
720  else
721  {
722  buf << description_add_links(desc);
723  }
724 
725  get_os() << buf.str() << "</td></tr>" << endl;
726  }
727 
728  size_t lastlen;
729  bool fit;
730  for ( Index i=0; i<mdr.GOut().nelem(); ++i )
731  {
732  buf.str("");
733  buf << "<tr>";
734  buf << "<td>GOUT</td><td class=\"right\">" << mdr.GOut()[i] << "</td><td>(";
735  if (mdr.GOutType()[i] == get_wsv_group_id("Any")
736  && mdr.GOutSpecType()[i].nelem())
737  {
738  bool firstarg = true;
739  for (Index j = 0; j < mdr.GOutSpecType()[i].nelem(); j++)
740  {
741  if (!firstarg) buf << ", "; else firstarg = false;
742  buf << insert_group_link(wsv_group_names[mdr.GOutSpecType()[i][j]]);
743  }
744  }
745  else
746  {
747  buf << insert_group_link(wsv_group_names[mdr.GOutType()[i]]);
748  }
749 
750  buf << ")</td><td>";
751  desc = buf.str();
752  lastlen = desc.length();
753  fit = format_paragraph (desc, indent, linelen);
754  buf.str("");
755  get_os() << desc;
756 
757  desc = mdr.GOutDescription()[i];
758  if (!fit)
759  {
760  format_paragraph (desc, indent, linelen);
761  buf << endl << indent << description_add_links(desc);
762  }
763  else if (lastlen + desc.length() > linelen)
764  {
765  format_paragraph (desc, indent, linelen, lastlen);
766  buf << endl << description_add_links(desc);
767  }
768  else
769  {
770  buf << description_add_links(desc);
771  }
772 
773  get_os() << buf.str() << "</td></tr>" << endl;
774  }
775 
776  for ( Index i=0; i<mdr.In().nelem(); ++i )
777  {
778  buf.str("");
779  buf << "<tr>";
780  buf << "<td>IN</td>";
781 
782  const String& vname = Workspace::wsv_data[mdr.In()[i]].Name();
783  buf << "<td class=\"right\">" << insert_wsv_link(vname);
784  buf << "</td><td>(";
785  buf << insert_group_link(wsv_group_names[Workspace::wsv_data[mdr.In()[i]].Group()]);
786  buf << ")</td><td>";
787 
788  get_short_wsv_description(desc, Workspace::wsv_data[mdr.In()[i]].Description());
789 
790  if (buf.str().length() + desc.length() > linelen)
791  {
792  format_paragraph (desc, indent, linelen, indent.length());
793  buf << endl << indent << description_add_links(desc);
794  }
795  else
796  {
797  buf << description_add_links(desc);
798  }
799 
800  get_os() << buf.str() << "</td></tr>" << endl;
801  }
802 
803  for ( Index i=0; i<mdr.GIn().nelem(); ++i )
804  {
805  buf.str("");
806  buf << "<tr>";
807  buf << "<td>GIN</td><td class=\"right\">" << mdr.GIn()[i] << "</td><td>(";
808  if (mdr.GInType()[i] == get_wsv_group_id("Any")
809  && mdr.GInSpecType()[i].nelem())
810  {
811  bool firstarg = true;
812  for (Index j = 0; j < mdr.GInSpecType()[i].nelem(); j++)
813  {
814  if (!firstarg) buf << ", "; else firstarg = false;
815  buf << insert_group_link(wsv_group_names[mdr.GInSpecType()[i][j]]);
816  }
817  }
818  else
819  {
820  buf << insert_group_link(wsv_group_names[mdr.GInType()[i]]);
821  }
822 
823  if (mdr.GInDefault()[i] != NODEF)
824  {
825  buf << ", Default: ";
826  if (mdr.GInType()[i] == get_wsv_group_id ("String"))
827  {
828  buf << "\"" << mdr.GInDefault()[i] << "\"";
829  }
830  else
831  {
832  buf << mdr.GInDefault()[i];
833  }
834 
835  }
836 
837  buf << ")</td><td>";
838  desc = buf.str();
839  lastlen = desc.length();
840  fit = format_paragraph(desc, indent, linelen);
841  buf.str("");
842  get_os() << desc;
843 
844  desc = mdr.GInDescription()[i];
845  if (!fit)
846  {
847  format_paragraph(desc, indent, linelen);
848  buf << indent << description_add_links(desc);
849  }
850  else if (lastlen + desc.length() > linelen)
851  {
852  format_paragraph(desc, indent, linelen, indent.length());
853  buf << indent << description_add_links(desc);
854  }
855  else
856  {
857  buf << description_add_links(desc);
858  }
859 
860  get_os() << buf.str() << "</td></tr>" << endl;
861  }
862  get_os() << "</table>" << endl;
863  }
864  else
865  {
866  insert_error_message("There is no method by this name.");
867  }
868 }
869 
870 
872 
880 void Docserver::doc_variable_methods(const string& vname)
881 {
882  // Check if the user gave the name of a specific variable.
883  map<String, Index>::const_iterator mi = Workspace::WsvMap.find(vname);
884  extern const Array<MdRecord> md_data_raw;
885  if ( mi != Workspace::WsvMap.end() )
886  {
887  // If we are here, then the given name matches a variable.
888  Index wsv_key = mi->second;
889  Index hitcount = 0;
890 
891  // List specific methods:
892  hitcount = 0;
893  get_os()
894  << "<h3>Specific methods that can generate " << vname << "</h3>" << endl
895  << "<ul>" << endl;
896  for ( Index i=0; i<md_data_raw.nelem(); ++i )
897  {
898  // Get handle on method record:
899  const MdRecord& mdd = md_data_raw[i];
900 
901  // This if statement checks whether Output, the list
902  // of output variables contains the workspace
903  // variable key.
904  if ( count( mdd.Out().begin(),
905  mdd.Out().end(),
906  wsv_key ) )
907  {
908  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << "\n";
909  ++hitcount;
910  }
911  }
912  if ( 0==hitcount ) get_os() << "<li>none\n";
913 
914  get_os() << endl << "</ul>" << endl;
915 
916  // List generic methods:
917  get_os() << "<h3>Generic and supergeneric methods that can generate " << vname << "</h3>" << endl;
918  get_os() << "<ul>" << endl;
919  for ( Index i=0; i<md_data_raw.nelem(); ++i )
920  {
921  // Get handle on method record:
922  const MdRecord& mdd = md_data_raw[i];
923 
924  // This if statement checks whether GOutType, the list
925  // of output variable types contains the group of the
926  // requested variable.
927  // The else clause picks up methods with supergeneric input.
928  if ( count( mdd.GOutType().begin(),
929  mdd.GOutType().end(),
930  Workspace::wsv_data[wsv_key].Group() ) )
931  {
932  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
933  ++hitcount;
934  }
935  else if ( count( mdd.GOutType().begin(),
936  mdd.GOutType().end(),
937  get_wsv_group_id("Any") ) )
938  {
939  for (Index j = 0; j < mdd.GOutType().nelem(); j++)
940  {
941  if (mdd.GOutType()[j] == get_wsv_group_id("Any"))
942  {
943  if (mdd.GOutSpecType()[j].nelem())
944  {
945  if (count( mdd.GOutSpecType()[j].begin(),
946  mdd.GOutSpecType()[j].end(),
947  Workspace::wsv_data[wsv_key].Group() ) )
948  {
949  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
950  ++hitcount;
951  }
952  }
953  else
954  {
955  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
956  ++hitcount;
957  }
958  }
959  }
960  }
961  }
962  if ( 0==hitcount ) get_os() << "<li>none" << endl;
963 
964  get_os() << endl << "</ul>" << endl;
965 
966  // List specific methods:
967  hitcount = 0;
968  get_os()
969  << "<h3>Specific methods that require " << vname << "</h3>" << endl
970  << "<ul>" << endl;
971  for ( Index i=0; i<md_data_raw.nelem(); ++i )
972  {
973  // Get handle on method record:
974  const MdRecord& mdd = md_data_raw[i];
975 
976  // This if statement checks whether Output, the list
977  // of output variables contains the workspace
978  // variable key.
979  if ( count( mdd.In().begin(),
980  mdd.In().end(),
981  wsv_key ) )
982  {
983  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << "\n";
984  ++hitcount;
985  }
986  }
987  if ( 0==hitcount ) get_os() << "<li>none\n";
988 
989  get_os() << endl << "</ul>" << endl;
990 
991  // List generic methods:
992  hitcount = 0;
993  get_os() << "<h3>Generic and supergeneric methods that can use " << vname << "</h3>" << endl;
994  get_os() << "<ul>" << endl;
995  for ( Index i=0; i<md_data_raw.nelem(); ++i )
996  {
997  // Get handle on method record:
998  const MdRecord& mdd = md_data_raw[i];
999 
1000  // This if statement checks whether GOutType, the list
1001  // of output variable types contains the group of the
1002  // requested variable.
1003  // The else clause picks up methods with supergeneric input.
1004  if ( count( mdd.GInType().begin(),
1005  mdd.GInType().end(),
1006  Workspace::wsv_data[wsv_key].Group() ) )
1007  {
1008  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1009  ++hitcount;
1010  }
1011  else if ( count( mdd.GInType().begin(),
1012  mdd.GInType().end(),
1013  get_wsv_group_id("Any") ) )
1014  {
1015  for (Index j = 0; j < mdd.GInType().nelem(); j++)
1016  {
1017  if (mdd.GInType()[j] == get_wsv_group_id("Any"))
1018  {
1019  if (mdd.GInSpecType()[j].nelem())
1020  {
1021  if (count( mdd.GInSpecType()[j].begin(),
1022  mdd.GInSpecType()[j].end(),
1023  Workspace::wsv_data[wsv_key].Group() ) )
1024  {
1025  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1026  ++hitcount;
1027  }
1028  }
1029  else
1030  {
1031  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1032  ++hitcount;
1033  }
1034  }
1035  }
1036  }
1037  }
1038  if ( 0==hitcount ) get_os() << "<li>none" << endl;
1039 
1040  get_os() << endl << "</ul>" << endl;
1041 
1042  // List agendas with this variable as output:
1044  hitcount = 0;
1045  get_os()
1046  << "<h3>Agendas that can generate " << vname << "</h3>" << endl
1047  << "<ul>" << endl;
1048  for ( Index i=0; i<agenda_data.nelem(); ++i )
1049  {
1050  // Get handle on method record:
1051  const AgRecord& ar = agenda_data[i];
1052 
1053  // This if statement checks whether Output, the list
1054  // of output variables contains the workspace
1055  // variable key.
1056  if ( count( ar.Out().begin(),
1057  ar.Out().end(),
1058  wsv_key ) )
1059  {
1060  get_os() << "<li>" << insert_agenda_link(ar.Name()) << "\n";
1061  ++hitcount;
1062  }
1063  }
1064  if ( 0==hitcount ) get_os() << "<li>none\n";
1065 
1066  get_os() << endl << "</ul>" << endl;
1067 
1068  // List agendas with this variable as input:
1069  hitcount = 0;
1070  get_os() << "<h3>Agendas that require " << vname << "</h3>" << endl
1071  << "<ul>" << endl;
1072  for ( Index i=0; i<agenda_data.nelem(); ++i )
1073  {
1074  // Get handle on method record:
1075  const AgRecord& ar = agenda_data[i];
1076 
1077  // This if statement checks whether Output, the list
1078  // of output variables contains the workspace
1079  // variable key.
1080  if ( count( ar.In().begin(),
1081  ar.In().end(),
1082  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 {
1107  extern const ArrayOfString wsv_group_names;
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()]) << endl;
1121 
1122  doc_variable_methods(vname);
1123  }
1124  else
1125  {
1126  insert_error_message("There is no variable by this name.");
1127  }
1128 }
1129 
1130 
1132 
1140 void Docserver::doc_agenda (const string& aname)
1141 {
1142  extern const ArrayOfString wsv_group_names;
1143 
1144  // Find wsv id:
1145  map<String, Index>::const_iterator it = Workspace::WsvMap.find(aname);
1147  extern const map<String, Index> AgendaMap;
1148  map<String, Index>::const_iterator ait = AgendaMap.find(aname);
1149 
1150  if ( it != Workspace::WsvMap.end() && ait != AgendaMap.end() )
1151  {
1152  // If we are here, then the given name matches a workspace
1153  // variable.
1154  get_os() << "<pre>" << endl;
1155  get_os() << description_add_links(agenda_data[ait->second].Description());
1156  get_os() << endl << "</pre>" << endl << endl;
1157 
1158  get_os() << "<p><b>Group: </b>"
1159  << insert_group_link(wsv_group_names[Workspace::wsv_data[it->second].Group()]) << endl;
1160 
1161  get_os() << "<h3>Variables</h3>" << endl;
1162 
1163  // Out:
1164  if ( ait != AgendaMap.end() )
1165  {
1166  // If we are here, then the given name matches a method.
1167  const AgRecord& agr = agenda_data[ait->second];
1168  String indent = "";
1169  String desc;
1170  ostringstream buf;
1171  size_t linelen = 80;
1172  get_os() << "<table>" << endl;
1173  for ( Index i=0; i<agr.Out().nelem(); ++i )
1174  {
1175  buf.str("");
1176  buf << "<tr>";
1177  buf << "<td>OUT</td>";
1178 
1179  {
1180  const String& vname = Workspace::wsv_data[agr.Out()[i]].Name();
1181  buf << "<td class=\"right\">" << insert_wsv_link(vname) << "</td><td>(";
1182  buf << insert_group_link(wsv_group_names[Workspace::wsv_data[agr.Out()[i]].Group()]);
1183  buf << ")</td><td>";
1184  }
1185 
1186  get_short_wsv_description(desc, Workspace::wsv_data[agr.Out()[i]].Description());
1187 
1188  if (buf.str().length() + desc.length() > linelen)
1189  {
1190  format_paragraph (desc, indent, linelen);
1191  buf << endl << indent << description_add_links(desc);
1192  }
1193  else
1194  {
1195  buf << description_add_links(desc);
1196  }
1197 
1198  get_os() << buf.str() << "</td></tr>" << endl;
1199 
1200  }
1201 
1202  for ( Index i=0; i<agr.In().nelem(); ++i )
1203  {
1204  buf.str("");
1205  buf << "<tr>";
1206  buf << "<td>IN</td>";
1207 
1208  const String& vname = Workspace::wsv_data[agr.In()[i]].Name();
1209  buf << "<td class=\"right\">" << insert_wsv_link(vname);
1210  buf << "</td><td>(";
1211  buf << insert_group_link(wsv_group_names[Workspace::wsv_data[agr.In()[i]].Group()]);
1212  buf << ")</td><td>";
1213 
1214  get_short_wsv_description(desc, Workspace::wsv_data[agr.In()[i]].Description());
1215 
1216  if (buf.str().length() + desc.length() > linelen)
1217  {
1218  format_paragraph (desc, indent, linelen, indent.length());
1219  buf << endl << indent << description_add_links(desc);
1220  }
1221  else
1222  {
1223  buf << description_add_links(desc);
1224  }
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);
1254  extern const Array<MdRecord> md_data_raw;
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>" << endl;
1299  get_os() << "<ul>" << endl;
1300  for ( Index i=0; i<md_data_raw.nelem(); ++i )
1301  {
1302  // Get handle on method record:
1303  const MdRecord& mdd = md_data_raw[i];
1304 
1305  // This if statement checks whether GOutType, the list
1306  // of output variable types contains the group of the
1307  // requested variable.
1308  // The else clause picks up methods with supergeneric input.
1309  if ( count( mdd.GOutType().begin(), mdd.GOutType().end(), gid ) )
1310  {
1311  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1312  ++hitcount;
1313  }
1314  else if ( count( mdd.GOutType().begin(),
1315  mdd.GOutType().end(),
1316  get_wsv_group_id("Any") ) )
1317  {
1318  for (Index j = 0; j < mdd.GOutType().nelem(); j++)
1319  {
1320  if (mdd.GOutType()[j] == get_wsv_group_id("Any"))
1321  {
1322  if (mdd.GOutSpecType()[j].nelem())
1323  {
1324  if (count( mdd.GOutSpecType()[j].begin(),
1325  mdd.GOutSpecType()[j].end(),
1326  gid ) )
1327  {
1328  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1329  ++hitcount;
1330  }
1331  }
1332  else
1333  {
1334  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1335  ++hitcount;
1336  }
1337  }
1338  }
1339  }
1340  }
1341  if ( 0==hitcount ) get_os() << "<li>none" << endl;
1342 
1343  get_os() << endl << "</ul>" << endl;
1344 
1345  if (gname != "Any")
1346  {
1347  hitcount = 0;
1348  get_os() << "<h3>Specific methods that require variables of group " << gname << "</h3>" << 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(),
1399  mdd.GInType().end(),
1400  get_wsv_group_id("Any") ) )
1401  {
1402  for (Index j = 0; j < mdd.GInType().nelem(); j++)
1403  {
1404  if (mdd.GInType()[j] == get_wsv_group_id("Any"))
1405  {
1406  if (mdd.GInSpecType()[j].nelem())
1407  {
1408  if (count( mdd.GInSpecType()[j].begin(),
1409  mdd.GInSpecType()[j].end(),
1410  gid ) )
1411  {
1412  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1413  ++hitcount;
1414  }
1415  }
1416  else
1417  {
1418  get_os() << "<li>" << insert_wsm_link(mdd.Name()) << endl;
1419  ++hitcount;
1420  }
1421  }
1422  }
1423  }
1424  }
1425  if ( 0==hitcount ) get_os() << "<li>none" << endl;
1426 
1427  get_os() << endl << "</ul>" << endl;
1428 
1429  if (gname != "Any")
1430  {
1431  Index i;
1432 
1433  get_os() << "<h3>Workspace Variables of group " << gname << "</h3>" << endl
1434  << "<ul>" << endl;
1435 
1436  hitcount = 0;
1437  for ( i=0; i<Workspace::wsv_data.nelem(); ++i )
1438  {
1439  if (Workspace::wsv_data[i].Group() == get_wsv_group_id(gname))
1440  {
1441  get_os() << "<li>" << insert_wsv_link(Workspace::wsv_data[i].Name()) << endl;
1442  hitcount++;
1443  }
1444  }
1445  if ( 0==hitcount ) get_os() << "<li>none" << endl;
1446 
1447  get_os() << "</ul>" << endl;
1448  }
1449  }
1450  else
1451  {
1452  insert_error_message ("There is no group by this name.");
1453  }
1454 }
1455 
1456 
1458 
1465 void Docserver::find_token_type ()
1466 {
1467  if (tokens.size() < 1 || tokens[0] != "all") return;
1468 
1469  // Make global data visible:
1470  extern const map<String, Index> MdRawMap;
1471  extern const map<String, Index> AgendaMap;
1472 
1473  // Find method id:
1474  if (MdRawMap.find(tokens[1]) != MdRawMap.end())
1475  tokens[0] = "methods";
1476  else if (AgendaMap.find(tokens[1]) != AgendaMap.end())
1477  tokens[0] = "agendas";
1478  else if (Workspace::WsvMap.find(tokens[1]) != Workspace::WsvMap.end())
1479  tokens[0] = "variables";
1480  else if (get_wsv_group_id (tokens[1]) != -1)
1481  tokens[0] = "groups";
1482 }
1483 
1484 
1486 
1494 void Docserver::insert_breadcrumb_token (size_t token_id)
1495 {
1496  if (token_id != tokens.size())
1497  {
1498  get_os() << "<a href=\"" << mbaseurl << "/";
1499  for (size_t t = 0; t < token_id; t++)
1500  {
1501  if (t) get_os() << "/";
1502  get_os() << tokens[t];
1503  }
1504  get_os() << "\">";
1505  }
1506 
1507  if (!token_id) get_os() << "Home";
1508  else if (tokens[token_id-1] == "methods") get_os() << "Methods";
1509  else if (tokens[token_id-1] == "variables") get_os() << "Variables";
1510  else if (tokens[token_id-1] == "agendas") get_os() << "Agendas";
1511  else if (tokens[token_id-1] == "groups") get_os() << "Groups";
1512  else if (tokens[token_id-1] == "all") get_os() << "All";
1513  else if (tokens[token_id-1] == "doccheck") get_os() << "Doc Check";
1514  else get_os() << tokens[token_id-1];
1515 
1516  if (token_id != tokens.size()) get_os() << "</a>";
1517 }
1518 
1519 
1521 
1527 void Docserver::insert_breadcrumbs ()
1528 {
1529  get_os() << "<div id=\"navbar\"><div class=\"breadcrumbs\">";
1530  for (size_t t = 0; t <= tokens.size(); t++)
1531  {
1532  if (t) get_os() << "&nbsp;>>&nbsp;";
1533  insert_breadcrumb_token(t);
1534  }
1535  get_os() << "</div>" << endl;
1536 
1537  get_os()
1538  << "<div class=\"goto\">Go to: "
1539  << "<a href=\"" << mbaseurl << "/groups/\">Groups</a>&nbsp;-&nbsp;"
1540  << "<a href=\"" << mbaseurl << "/variables/\">Variables</a>&nbsp;-&nbsp;"
1541  << "<a href=\"" << mbaseurl << "/methods/\">Methods</a>&nbsp;-&nbsp;"
1542  << "<a href=\"" << mbaseurl << "/agendas/\">Agendas</a>"
1543  << "</div></div>" << endl;
1544 }
1545 
1546 
1548 
1555 void Docserver::insert_error(const string& error)
1556 {
1557  begin_page("");
1558  insert_breadcrumbs();
1559  begin_content();
1560  insert_error_message(error);
1561  end_content();
1562  end_page();
1563 }
1564 
1565 
1567 
1574 void Docserver::insert_error_message(const string& error)
1575 {
1576  if (error.length())
1577  get_os() << "<p class=\"error\">" << error << "</p>" << endl;
1578 }
1579 
1580 
1582 
1589 void Docserver::insert_title (const string& title)
1590 {
1591  get_os() << "<h1>"DOCSERVER_NAME;
1592  if (title.length())
1593  get_os() << " &mdash; " << title;
1594  get_os() << "</h1>" << endl;
1595 }
1596 
1597 
1599 
1605 void Docserver::insert_index ()
1606 {
1607  if (tokens.size() == 0 || tokens[0] == "all")
1608  {
1609  begin_page("");
1610  insert_breadcrumbs();
1611  begin_content();
1612  insert_title("Index");
1613 
1614  list_groups();
1615  list_variables();
1616  list_methods();
1617  list_agendas();
1618  end_content();
1619  end_page();
1620  }
1621  else
1622  {
1623  if (tokens[0] == "methods")
1624  {
1625  begin_page("Method Index");
1626  insert_breadcrumbs();
1627  begin_content();
1628  insert_title("Method Index");
1629  get_os() << "<table class=\"list\">" << endl;
1630  list_methods();
1631  get_os() << "</table>" << endl;
1632  end_content();
1633  end_page();
1634  }
1635  else if (tokens[0] == "variables")
1636  {
1637  begin_page("Variable Index");
1638  insert_breadcrumbs();
1639  begin_content();
1640  insert_title ("Variable Index");
1641  get_os() << "<table class=\"list\">" << endl;
1642  list_variables();
1643  get_os() << "</table>" << endl;
1644  end_content();
1645  end_page ();
1646  }
1647  else if (tokens[0] == "groups")
1648  {
1649  begin_page("Group Index");
1650  insert_breadcrumbs();
1651  begin_content();
1652  insert_title ("Group Index");
1653  get_os() << "<table class=\"list\">" << endl;
1654  list_groups();
1655  get_os() << "</table>" << endl;
1656  end_content();
1657  end_page ();
1658  }
1659  else if (tokens[0] == "agendas")
1660  {
1661  begin_page("Agenda Index");
1662  insert_breadcrumbs();
1663  begin_content();
1664  insert_title ("Agenda Index");
1665  get_os() << "<table class=\"list\">" << endl;
1666  list_agendas();
1667  get_os() << "</table>" << endl;
1668  end_content();
1669  end_page ();
1670  }
1671  else
1672  {
1673  insert_error(DS_ERROR_404);
1674  }
1675  }
1676 }
1677 
1678 
1680 
1686 void Docserver::insert_doc ()
1687 {
1688  find_token_type ();
1689 
1690  if (tokens[0] == "methods")
1691  {
1692  begin_page(tokens[1]);
1693  insert_breadcrumbs();
1694  begin_content();
1695  insert_title ();
1696  get_os() << "<h2>" << "Workspace Method " + tokens[1] << "</h2>" << endl;
1697  doc_method(tokens[1]);
1698  end_content();
1699  end_page ();
1700  }
1701  else if (tokens[0] == "variables")
1702  {
1703  begin_page(tokens[1]);
1704  insert_breadcrumbs();
1705  begin_content();
1706  insert_title ();
1707  get_os() << "<h2>" << "Workspace Variable " + tokens[1] << "</h2>" << endl;
1708  doc_variable(tokens[1]);
1709  end_content();
1710  end_page ();
1711  }
1712  else if (tokens[0] == "groups")
1713  {
1714  begin_page(tokens[1]);
1715  insert_breadcrumbs();
1716  begin_content();
1717  insert_title ();
1718  get_os() << "<h2>" << "Workspace Group " + tokens[1] << "</h2>" << endl;
1719  doc_group(tokens[1]);
1720  end_content();
1721  end_page ();
1722  }
1723  else if (tokens[0] == "agendas")
1724  {
1725  begin_page(tokens[1]);
1726  insert_breadcrumbs();
1727  begin_content();
1728  insert_title ();
1729  get_os() << "<h2>" << "Agenda " + tokens[1] << "</h2>" << endl;
1730  doc_agenda(tokens[1]);
1731  end_content();
1732  end_page ();
1733  }
1734  else
1735  {
1736  insert_error(DS_ERROR_404);
1737  }
1738 }
1739 
1740 
1742 
1747 void Docserver::insert_stylesheet ()
1748 {
1749  get_os()
1750  << "body { font-family: monospace; }" << endl
1751  << "a:link { color: #3465a4; text-decoration: none; }" << endl
1752  << "a:visited { color: #729fcf; text-decoration: none; }" << endl
1753  << "a:active { color: #ce5c00; text-decoration: none; background-color: #eeeeec}" << endl
1754  << "a:hover { color: #f57900; text-decoration: none; }" << endl
1755 
1756  << "table.list {" << endl
1757  << "width: 90%;" << endl
1758  << "margin-left: 5%;" << endl
1759  << "margin-right: 5%;" << endl
1760  << "}" << endl
1761 
1762  << "h1 {" << endl
1763  << "font-size: 1.5em;" << endl
1764  << "}" << endl
1765 
1766  << "h2 {" << endl
1767  << "font-size: 1.25em;" << endl
1768  << "}" << endl
1769 
1770  << "h3 {" << endl
1771  << "font-size: 1em;" << endl
1772  << "}" << endl
1773 
1774  << "li {" << endl
1775  << "font-size: 1em;" << endl
1776  << "}" << endl
1777 
1778  << "#navbar {" << endl
1779  << "position: fixed;" << endl
1780  << "top: 0px;" << endl
1781  << "left: 10px;" << endl
1782  << "right: 10px;" << endl
1783  << "background-color: #fff;" << endl
1784  << "border-bottom: solid 1px #ddd;" << endl
1785  << "border-left: solid 1px #ddd;" << endl
1786  << "border-right: solid 1px #ddd;" << endl
1787  << "padding: 2px;" << endl
1788  << "}" << endl
1789 
1790  << ".firstcol {" << endl
1791  << "float: left;" << endl
1792  << "clear: left;" << endl
1793  << "width: 50%;" << endl
1794  << "white-space: nowrap;" << endl
1795  << "}" << endl
1796 
1797  << ".firstcol ul {" << endl
1798  << "float: left;" << endl
1799  << "clear: both;" << endl
1800  << "padding-top: 0;" << endl
1801  << "}" << endl
1802 
1803  << ".secondcol ul {" << endl
1804  << "float: left;" << endl
1805  << "clear: both;" << endl
1806  << "padding-top: 0;" << endl
1807  << "}" << endl
1808 
1809  << ".secondcol {" << endl
1810  << "float: left;" << endl
1811  << "clear: right;" << endl
1812  << "width: 50%;" << endl
1813  << "white-space: nowrap;" << endl
1814  << "}" << endl
1815 
1816  << ".firstcol ul li {" << endl
1817  << "margin-left: 0;" << endl
1818  << "}" << endl
1819 
1820  << ".brokendoclink {" << endl
1821  << "color: #f00;" << endl
1822  << "}" << endl
1823 
1824  << ".goto {" << endl
1825  << "font-size: small;" << endl
1826  << "float: right;" << endl
1827  << "}" << endl
1828 
1829  << ".breadcrumbs {" << endl
1830  << "font-size: small;" << endl
1831  << "float: left;" << endl
1832  << "}" << endl
1833 
1834  << "@media only screen and (max-device-width: 480px) {" << endl
1835  << "#navbar { position: static; border: none; }" << endl
1836  << ".goto { position: static; float: none; }" << endl
1837  << ".breadcrumbs { position: static; float: none; }" << endl
1838  << ".firstcol { float: left; clear: left; width: 100%; }" << endl
1839  << ".secondcol { float: left; clear: both; width: 100%; }" << endl
1840  << ".firstcol ul { margin-top: 0; margin-bottom: 0; }" << endl
1841  << ".secondcol ul { margin-top: 0; }" << endl
1842  << "ul { padding-left: 1em; }" << endl
1843  << "}" << endl
1844 
1845  << "table {" << endl
1846  << "border-width: 0px;" << endl
1847  << "}" << endl
1848 
1849  << "table td {" << endl
1850  << "vertical-align: top;" << endl
1851  << "}" << endl
1852 
1853  << "table td.right {" << endl
1854  << "text-align: right;" << endl
1855  << "}" << endl
1856 
1857  << ".content {" << endl
1858  << "padding-top: 1em;" << endl
1859  << "clear: both;" << endl
1860  << "width: 100%;" << endl
1861  << "}" << endl
1862 
1863  << ".error {" << endl
1864  << "color: #ff0000;" << endl
1865  << "font-weight: bold;" << endl
1866  << "font-size: 1.2em;" << endl
1867  << "}" << endl
1868 
1869  << "div.footer {" << endl
1870  << "float: left;" << endl
1871  << "text-align: right;" << endl
1872  << "color: #aaaaa8;" << endl
1873  << "font-size: small;" << endl
1874  << "clear: left;" << endl
1875  << "margin-top: 2em;" << endl
1876  << "width: 100%;" << endl
1877  << "}" << endl
1878 
1879  << endl;
1880 }
1881 
1882 
1884 
1890 void Docserver::insert_broken_doc_links()
1891 {
1892  begin_page(tokens[1]);
1893  insert_breadcrumbs();
1894  begin_content();
1895  insert_title ("Broken links");
1896 
1897  // Broken links in WSV descriptions
1898  bool first = true;
1899  for (Index i=0; i<Workspace::wsv_data.nelem(); ++i)
1900  {
1901  vector<string> broken_links;
1902  broken_links = find_broken_description_links(Workspace::wsv_data[i].Description());
1903 
1904  if (broken_links.size())
1905  {
1906  if (first)
1907  {
1908  first = false;
1909  get_os() << "<h2>Variable descriptions</h2>" << endl;
1910  }
1911  get_os() << "<p>" << insert_wsv_link(Workspace::wsv_data[i].Name()) << ": ";
1912  for (vector<string>::iterator it = broken_links.begin();
1913  it != broken_links.end(); it++)
1914  {
1915  if (it != broken_links.begin()) get_os() << ", ";
1916  get_os() << *it;
1917  }
1918  get_os() << "</p>" << endl;
1919  }
1920  }
1921 
1922  // Broken links in agenda descriptions
1924  first = true;
1925  for (Array<AgRecord>::const_iterator ait = agenda_data.begin();
1926  ait != agenda_data.end();
1927  ait++)
1928  {
1929  vector<string> broken_links;
1930  broken_links = find_broken_description_links(ait->Description());
1931 
1932  if (broken_links.size())
1933  {
1934  if (first)
1935  {
1936  first = false;
1937  get_os() << "<h2>Agenda descriptions</h2>" << endl;
1938  }
1939  get_os() << "<p>" << insert_agenda_link(ait->Name()) << ": ";
1940  for (vector<string>::iterator it = broken_links.begin();
1941  it != broken_links.end(); it++)
1942  {
1943  if (it != broken_links.begin()) get_os() << ", ";
1944  get_os() << *it;
1945  }
1946  get_os() << "</p>" << endl;
1947  }
1948  }
1949 
1950  // Broken links in method descriptions
1951  extern const Array<MdRecord> md_data_raw;
1952  first = true;
1953  for (Array<MdRecord>::const_iterator mit = md_data_raw.begin();
1954  mit != md_data_raw.end();
1955  mit++)
1956  {
1957  vector<string> broken_links;
1958  broken_links = find_broken_description_links(mit->Description(), mit->Name());
1959 
1960  if (broken_links.size())
1961  {
1962  if (first)
1963  {
1964  first = false;
1965  get_os() << "<h2>Method descriptions</h2>" << endl;
1966  }
1967  get_os() << "<p>" << insert_wsm_link(mit->Name()) << ": ";
1968  for (vector<string>::iterator it = broken_links.begin();
1969  it != broken_links.end(); it++)
1970  {
1971  if (it != broken_links.begin()) get_os() << ", ";
1972  get_os() << *it;
1973  }
1974  get_os() << "</p>" << endl;
1975  }
1976  }
1977 
1978  end_content();
1979  end_page ();
1980 }
1981 
1982 
1984 
1994 vector<string> Docserver::find_broken_description_links (const String& desc, const String& mname)
1995 {
1996  vector<string> broken_links;
1997  string ret;
1998  string link;
1999  bool inside_link = false;
2000  string::const_iterator it = desc.begin();
2001 
2002  extern const map<String, Index> MdRawMap;
2003  extern const map<String, Index> AgendaMap;
2004 
2005  while (it != desc.end()) {
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  extern const map<String, Index> MdRawMap;
2027 
2028  // Find method id:
2029  map<String, Index>::const_iterator mit = MdRawMap.find(mname);
2030  if ( mit != MdRawMap.end() )
2031  {
2032  extern const Array<MdRecord> md_data_raw;
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)
2121  {
2122  pause();
2123  }
2124  else
2125  {
2126  (void) getc (stdin);
2127  cout << "Stopping docserver.\n";
2128  MHD_stop_daemon (d);
2129  cout << "Goodbye.\n";
2130  }
2131  return 0;
2132 }
2133 
2134 
2136 
2144 Docserver::Docserver(const Index port, const string& baseurl) : mos(NULL)
2145 {
2146  mbaseurl = baseurl;
2147  if (port == -1) mport=9000; else mport=port;
2148 }
2149 
2150 
2152 
2169 static int ahc_echo (void *cls,
2170  struct MHD_Connection *connection,
2171  const char *url,
2172  const char *method,
2173  const char *version _U_,
2174  const char *upload_data _U_,
2175  size_t *upload_data_size _U_,
2176  void **ptr)
2177 {
2178  static int aptr;
2179  string surl(url);
2180  struct MHD_Response *response;
2181  int ret;
2182 
2183  if (!cls)
2184  {
2185  cerr << "Docserver error: Docserver object reference is NULL.\n";
2186  return MHD_NO; /* unexpected method */
2187  }
2188 
2189  // Make a local copy of the docserver object for thread-safety
2190  Docserver docserver = *((Docserver *)cls);
2191 
2192  if (0 != strcmp (method, "GET"))
2193  {
2194  cerr << "Docserver error: Unexpected method " << method << ".\n";
2195  return MHD_NO; /* unexpected method */
2196  }
2197  if (&aptr != *ptr)
2198  {
2199  /* do never respond on first call */
2200  *ptr = &aptr;
2201  return MHD_YES;
2202  }
2203  *ptr = NULL; /* reset when done */
2204  MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "q");
2205 
2206  string content_type;
2207  ostringstream hout;
2208  docserver.set_ostream(hout);
2209  content_type = docserver.new_page(surl);
2210  docserver.clear_ostream();
2211 
2212  response = MHD_create_response_from_data (hout.str().length(),
2213  (void *)hout.str().c_str(),
2214  MHD_NO, MHD_YES);
2215 
2216  if (response == NULL) {
2217  cerr << "Docserver error: response = 0\n";
2218  return MHD_NO;
2219  }
2220 
2221  MHD_add_response_header (response, "Content-type", content_type.c_str());
2222  ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
2223  MHD_destroy_response (response);
2224 
2225  return ret;
2226 }
2227 
2228 
2229 #endif /* ENABLE_DOCSERVER */
2230 
agenda_record.h
Declarations for AgRecord, storing lookup information for one agenda.
docserver.h
Declarations for the arts documentation server.
MdRecord::GOut
const ArrayOfString & GOut() const
Definition: methods.h:93
limit_line_length
void limit_line_length(ostream &os, ostringstream &curline, ostringstream &token, const String &indent, size_t linelen)
Limit length of output.
Definition: methods_aux.cc:624
Workspace::wsv_data
static Array< WsvRecord > wsv_data
Definition: workspace_ng.h:59
MdRecord::GOutSpecType
const ArrayOfArrayOfIndex & GOutSpecType() const
Definition: methods.h:95
MdRecord::GOutType
const ArrayOfIndex & GOutType() const
Definition: methods.h:94
md_data_raw
Array< MdRecord > md_data_raw
Lookup information for workspace methods.
Definition: globals_2.cc:54
MdRecord::GInType
const ArrayOfIndex & GInType() const
Definition: methods.h:99
AgendaMap
map< String, Index > AgendaMap
The map assiciated with agenda_data.
Definition: globals_2.cc:71
ARTS_FULL_VERSION
#define ARTS_FULL_VERSION
Definition: auto_version.h:1
Array
This can be used to make arrays out of anything.
Definition: array.h:103
agenda_data
Array< AgRecord > agenda_data
Definition: agendas.cc:40
messages.h
Declarations having to do with the four output streams.
AgRecord
Lookup information for one agenda.
Definition: agenda_record.h:44
_U_
#define _U_
Definition: config.h:158
my_basic_string
The implementation for String, the ARTS string class.
Definition: mystring.h:62
NODEF
#define NODEF
Definition: methods.h:36
MdRecord::GInDescription
const Array< String > & GInDescription() const
Definition: methods.h:102
AgRecord::Name
const String & Name() const
Definition: agenda_record.h:63
wsv_group_names
ArrayOfString wsv_group_names
Definition: groups.cc:40
get_short_wsv_description
void get_short_wsv_description(String &s, const String &desc)
Definition: methods_aux.cc:533
MdRawMap
map< String, Index > MdRawMap
The map associated with md_data_raw.
Definition: globals_2.cc:68
MdRecord::Name
const String & Name() const
Definition: methods.h:89
MdRecord::In
const ArrayOfIndex & In() const
Definition: methods.h:97
MdRecord::InOnly
const ArrayOfIndex & InOnly() const
Definition: methods.h:103
MdRecord::GOutDescription
const Array< String > & GOutDescription() const
Definition: methods.h:96
MdRecord::GInDefault
const Array< String > & GInDefault() const
Definition: methods.h:101
AgRecord::In
const ArrayOfIndex & In() const
Definition: agenda_record.h:66
auto_version.h
MdRecord::GIn
const ArrayOfString & GIn() const
Definition: methods.h:98
workspace_ng.h
This file contains the declaration and partly the implementation of the workspace class.
MdRecord
All information for one workspace method.
Definition: methods.h:42
Workspace::WsvMap
static map< String, Index > WsvMap
Definition: workspace_ng.h:62
format_paragraph
bool format_paragraph(String &s, const String &indent, const size_t linelen, const size_t offset=0)
Definition: methods_aux.cc:494
MdRecord::GInSpecType
const ArrayOfArrayOfIndex & GInSpecType() const
Definition: methods.h:100
Index
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
MdRecord::Out
const ArrayOfIndex & Out() const
Definition: methods.h:92
get_wsv_group_id
Index get_wsv_group_id(const String &name)
Definition: groups.cc:192
AgRecord::Out
const ArrayOfIndex & Out() const
Definition: agenda_record.h:65
MdRecord::Description
const String & Description() const
Definition: methods.h:90
Array::nelem
Index nelem() const
Number of elements.
Definition: array.h:172
methods.h
Declaration of the class MdRecord.
MdRecord::Authors
const ArrayOfString & Authors() const
Definition: methods.h:91