/********************************************************************** * * browsetools.cpp -- * Copyright (C) 1999 The New Zealand Digital Library Project * * A component of the Greenstone digital library software * from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: browsetools.cpp 533 1999-09-07 04:57:01Z sjboddie $ * *********************************************************************/ /* $Log$ Revision 1.21 1999/09/07 04:56:52 sjboddie added GPL notice Revision 1.20 1999/08/25 04:46:58 sjboddie fixed bug Revision 1.19 1999/08/13 04:18:04 sjboddie fixed some typos Revision 1.18 1999/08/10 22:42:21 sjboddie added more format options to document tocs - there are now just two types of toc - standard (Hierarchical) and document (as in books) Revision 1.17 1999/08/09 02:12:07 sjboddie made it so dates may be only 4 digits (i.e. year only) Revision 1.16 1999/07/30 02:16:10 sjboddie -added ability to display nested classifications (expanded versions of nested classifications has yet to be done). -changed set_arrow_macros slightly to fit in with new showtoppage format option Revision 1.15 1999/07/21 05:01:56 sjboddie wrote handler for DateList classification Revision 1.14 1999/07/20 02:58:15 sjboddie got List and AZList classifications using format strings - tidied up a bit Revision 1.13 1999/07/07 05:44:25 sjboddie Made some changes to allow for new way classifiers work (i.e. you can now have classifiers containing other classifiers). At present there's only a special case for dealing with the hdl 'magazine' section. A bit of a redesign is needed to get it completely flexible Revision 1.12 1999/07/01 03:47:49 rjmcnab Fixed a small warning. Revision 1.11 1999/06/27 21:49:01 sjboddie fixed a couple of version conflicts - tidied up some small things Revision 1.10 1999/06/26 01:07:21 rjmcnab Fixed a small "bug" -- well I probably just covered another one... Revision 1.9 1999/06/24 05:12:15 sjboddie lots of small changes Revision 1.8 1999/06/17 03:06:53 sjboddie got detach button working properly - the close book icon is now disabled when page is detached as the javascript close() function I was using is too unreliable over different browsers note that in my last comment I meant the "cl" arg (not the "c" arg). Revision 1.7 1999/06/16 23:53:14 sjboddie tidied a few things up. documentaction::define_external_macros now resets the "c" arg if it's set to something stupid by the .xx suffixes Revision 1.6 1999/06/16 04:03:47 sjboddie Now sets "cl" arg to "search" when going to a document from a search results page. This allows the close book icon (in hierarchy toc) to take you back to the results page if that's where you came from. If you got to the document page somehow other than from a classification or a search (i.e. if "cl" isn't set) then the close book icon is disabled Revision 1.5 1999/06/16 03:11:25 sjboddie get_info() now takes a getParents argument Revision 1.4 1999/05/10 03:40:26 sjboddie lots of changes - slowly getting document action sorted out Revision 1.3 1999/04/30 01:59:39 sjboddie lots of stuff - getting documentaction working (documentaction replaces old browseaction) Revision 1.2 1999/03/29 02:14:29 sjboddie More changes to browseaction Revision 1.1 1999/03/25 03:10:15 sjboddie new library for browse stuff */ #include "browsetools.h" #include "OIDtools.h" // simply checks to see if formatstring begins with a tag static bool is_table_content (const text_t &formatstring) { text_t::const_iterator here = formatstring.begin(); text_t::const_iterator end = formatstring.end(); while (here != end) { if (*here != ' ') { if (*here == '<') { if ((*(here+1) == 't' || *(here+1) == 'T') && (*(here+2) == 'd' || *(here+2) == 'D') && (*(here+3) == '>' || *(here+3) == ' ')) return true; } else return false; } here ++; } return false; } static bool is_table_content (const format_t *formatlistptr) { if (formatlistptr == NULL) return false; if (formatlistptr->command == comText) return is_table_content (formatlistptr->text); return false; } // output_controls displays the detach, expand/contract contents, // expand/contract text and highlighting/no highlighting buttons static void output_controls (cgiargsclass &args, const text_tarray &ibuttons, recptproto */*collectproto*/, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &/*logout*/) { if (args["u"] != "1") { FilterResponse_t response; text_tarray metadata; text_tarray buttons; text_tarray::const_iterator here = ibuttons.begin(); text_tarray::const_iterator end = ibuttons.end(); while (here != end) { if (*here == "Detach") buttons.push_back ("_document:imagedetach_"); else if (*here == "Highlight") { if (args["hl"] == "1") buttons.push_back ("_document:imagenohighlight_"); else buttons.push_back ("_document:imagehighlight_"); } else if (*here == "Expand Contents") { if (args["gc"] == "1") buttons.push_back ("_document:imagecontracttoc_"); else buttons.push_back ("_document:imageexpandtoc_"); } else if (*here == "Expand Text") { if (args.getintarg("gt")) buttons.push_back ("_document:imagecontracttext_"); else buttons.push_back ("_document:imageexpandtext_"); } here ++; } here = buttons.begin(); end = buttons.end(); int count = 0; while (here != end) { if ((count != 0) && ((count % 3) == 0)) textout << "
\n"; textout << outconvert << disp << *here; count ++; here ++; } } } // at the moment this just writes out the html to display // the cover image (assuming it's called cover.jpg) // this whole thing should be done with a call to the collection // server which would send a link to the cover image if there // was one otherwise send title, author and stuff static void output_cover_image (cgiargsclass &args, recptproto */*collectproto*/, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &/*logout*/) { if (args["d"].empty()) return; textout << outconvert << disp << "
\n"; } /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Functions for generating a document type tables of contents. These aren't really tables of contents at all, just a title, some navigation buttons and arrows and maybe a page ? of ? type thing. These should only be called for document level tocs (i.e. when the "d" argument is set) as I don't think it makes sense to display top level classifications in this way. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/ void output_document_toc (cgiargsclass &args, const formatinfo_t &formatinfo, recptproto *collectproto, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { text_t &arg_d = args["d"]; if (arg_d.empty()) return; text_tarray metadata; FilterResponse_t response; bool getParents = false; ResultDocInfo_t docinfo; text_t &collection = args["c"]; int gt = args.getintarg("gt"); bool istop = is_top (arg_d); format_t *formatlistptr = new format_t(); parse_formatstring (formatinfo.DocumentHeading, formatlistptr, metadata, getParents); text_tarray OIDs; OIDs.push_back (arg_d); if (formatinfo.DocumentArrowsTop && !gt && !istop) OIDs.push_back (arg_d + ".pr.lc"); metadata.push_back ("Title"); int metasize = metadata.size(); if (get_info (OIDs, collection, metadata, getParents, collectproto, response, logout)) { text_t &thistitle = response.docInfo[0].metadata[metasize-1].values.back(); text_t last_sib_title; if (formatinfo.DocumentArrowsTop && !gt && !istop) { if (response.docInfo.size() == 1) last_sib_title = response.docInfo[0].metadata[metasize-1].values.back(); else last_sib_title = response.docInfo[1].metadata[metasize-1].values.back(); } textout << "\n\n\n"; textout << outconvert << disp << "

\n" << "\n" << "\n"; // heading textout << outconvert << "\n
\n"; // top arrows and page ? of ? title if (formatinfo.DocumentArrowsTop && !gt) { // previous arrow textout << outconvert << disp << "\n" << "\n"; // page ? of ? text textout << "\n\n
_document:prevarrow_\n"; if (!istop && is_number (thistitle) && is_number (last_sib_title)) textout << outconvert << disp << "_document:page_" << thistitle << "_document:of_" << last_sib_title; else textout << outconvert << thistitle; // next arrow textout << outconvert << disp << "_document:nextarrow_
\n"; } // goto line if (formatinfo.DocumentGoTo) textout << outconvert << disp << "_document:gotoform_"; // control buttons output_controls (args, formatinfo.DocumentButtons, collectproto, disp, outconvert, textout, logout); textout << "
\n" << get_formatted_string (response.docInfo[0], formatlistptr); textout << "
\n"; textout << "\n\n"; } } /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Functions for generating a "Hierarchy" type table of contents. These can be used either at top classification level or at document level (the difference being that a cover image and control buttons may be displayed at document level). -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/ // prototypes static void output_contracted_hierarchy_toc (const text_t &classifytype, text_t &formatstring, cgiargsclass &args, recptproto *collectproto, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout); static void output_expanded_hierarchy_toc (cgiargsclass &args, recptproto *collectproto, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout); static void output_parents_toc (cgiargsclass &args, const FilterResponse_t &parents, int &tabcount, displayclass &disp, format_t *formatlistptr, outconvertclass &outconvert, ostream &textout, ostream &logout); static void output_siblings_toc (const text_t &classifytype, cgiargsclass &args, const FilterResponse_t &siblings, int &tabcount, displayclass &disp, format_t *formatlistptr, outconvertclass &outconvert, ostream &textout, ostream &logout); static void output_stdlist (cgiargsclass &args, ResultDocInfo_tarray::const_iterator &here, ResultDocInfo_tarray::const_iterator &end, bool intable, displayclass &disp, format_t *formatlistptr, outconvertclass &outconvert, ostream &textout); static void output_datelist (cgiargsclass &args, ResultDocInfo_tarray::const_iterator &here, ResultDocInfo_tarray::const_iterator &end, bool intable, displayclass &disp, format_t *formatlistptr, outconvertclass &outconvert, ostream &textout); void output_standard_toc (const text_t &classifytype, const formatinfo_t &formatinfo, text_t &formatstring, cgiargsclass &args, recptproto *collectproto, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { bool havecontrols = false; textout << "\n\n\n"; // get the cover image (if there is one) and the control buttons // if we're inside a book if (!args["d"].empty()) { textout << "

\n"; textout << "
\n"; if (formatinfo.DocumentImages) output_cover_image (args, collectproto, disp, outconvert, textout, logout); output_controls (args, formatinfo.DocumentButtons, collectproto, disp, outconvert, textout, logout); textout << "\n"; havecontrols = true; } // get table of contents textout << "\n"; if (args.getintarg("gc")) output_expanded_hierarchy_toc(args, collectproto, disp, outconvert, textout, logout); else output_contracted_hierarchy_toc(classifytype, formatstring, args, collectproto, disp, outconvert, textout, logout); textout << "
\n"; if (havecontrols) textout << "
\n"; textout << "\n\n"; } void output_contracted_hierarchy_toc (const text_t &classifytype, text_t &formatstring, cgiargsclass &args, recptproto *collectproto, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { int tabcount = 0; text_tarray parents, metadata; FilterResponse_t fsiblings, fparents; bool getParents = false; text_t &arg_d = args["d"]; text_t OID = arg_d; if (OID.empty()) OID = args["cl"]; text_t &collection = args["c"]; // if format string is empty use the default if (formatstring.empty()) formatstring = "[link][icon][/link]{Or}{[Title],Untitled}"; format_t *formatlistptr = new format_t(); parse_formatstring (formatstring, formatlistptr, metadata, getParents); metadata.push_back ("Date"); metadata.push_back ("hastxt"); metadata.push_back ("haschildren"); metadata.push_back ("doctype"); metadata.push_back ("classifytype"); if (has_children (OID, collection, collectproto, logout)) { get_parents_array (OID + ".fc", parents); if (!get_children (OID, collection, metadata, getParents, collectproto, fsiblings, logout)) return; } else { get_parents_array (OID, parents); if (!get_children (OID + ".pr", collection, metadata, getParents, collectproto, fsiblings, logout)) return; } if (!get_info (parents, collection, metadata, false, collectproto, fparents, logout)) return; if (!fparents.docInfo.empty()) output_parents_toc(args, fparents, tabcount, disp, formatlistptr, outconvert, textout, logout); if (!fsiblings.docInfo.empty()) output_siblings_toc (classifytype, args, fsiblings, tabcount, disp, formatlistptr, outconvert, textout, logout); } void output_parents_toc (cgiargsclass &args, const FilterResponse_t &parents, int &tabcount, displayclass &disp, format_t *formatlistptr, outconvertclass &outconvert, ostream &textout, ostream &/*logout*/) { text_t tab, icon; text_t &arg_cl = args["cl"]; text_t &arg_d = args["d"]; int numcols = parents.docInfo.size() + 1; bool intable = is_table_content (formatlistptr); ResultDocInfo_tarray::const_iterator thisparent = parents.docInfo.begin(); ResultDocInfo_tarray::const_iterator end = parents.docInfo.end(); int len = (*thisparent).metadata.size(); const text_t &classifytype = (*thisparent).metadata[len-1].values[0]; // don't want top level of any classifications to be displayed if (arg_d.empty() && thisparent != end) {thisparent ++; numcols --;} // don't want second level of classifications using classification links if ((classifytype == "AZList" || classifytype == "DateList") && (thisparent != end)) {thisparent ++; numcols --;} // the tab line if (thisparent != end) { text_t coltabs; for (int i = 0; i < numcols; i ++) coltabs += "_document:tab_"; textout << outconvert << disp << "" << coltabs << "\n"; } while (thisparent != end) { const text_t &doctype = (*thisparent).metadata[len-2].values.back(); // set up icon and pointer icon = "_document:icon"; if ((doctype != "classify") && ((*thisparent).OID == arg_d)) icon += "arrow"; if (doctype == "classify") icon += "openbookshelf_"; else if (is_top((*thisparent).OID)) icon += "openbook_"; else icon += "openfolder_"; if (tabcount == 1) textout << "\n"; else if (tabcount > 1) textout << ""; // set up the link text_t link = ""; else if (is_top ((*thisparent).OID)) if (arg_cl.empty()) link = ""; else if (arg_cl == "search") link = ""; else link += "&cl=" + arg_cl + "\">"; else link += "&cl=" + arg_cl + "&d=" + (*thisparent).OID + ".pr\">"; if ((numcols-tabcount) == 1) textout << ""; else if ((numcols-tabcount) > 1) textout << ""; if (intable) textout << ""; textout << outconvert << disp << get_formatted_string (*thisparent, formatlistptr, link, icon); if (intable) textout << "
"; textout << "\n"; tabcount ++; thisparent ++; } } void output_siblings_toc (const text_t &classifytype, cgiargsclass &args, const FilterResponse_t &siblings, int &tabcount, displayclass &disp, format_t *formatlistptr, outconvertclass &outconvert, ostream &textout, ostream &/*logout*/) { bool intable = is_table_content (formatlistptr); ResultDocInfo_tarray::const_iterator thissibling = siblings.docInfo.begin(); ResultDocInfo_tarray::const_iterator sibend = siblings.docInfo.end(); if (thissibling == sibend) return; // tabbing if (tabcount) { if (tabcount == 1) textout << "\n"; else if (tabcount > 1) textout << ""; textout << "\n"; } if (!intable) textout << "
\n"; if (classifytype == "DateList") output_datelist (args, thissibling, sibend, intable, disp, formatlistptr, outconvert, textout); else output_stdlist (args, thissibling, sibend, intable, disp, formatlistptr, outconvert, textout); if (!intable) textout << "
\n"; if (tabcount) textout << "\n"; } void output_stdlist (cgiargsclass &args, ResultDocInfo_tarray::const_iterator &here, ResultDocInfo_tarray::const_iterator &end, bool intable, displayclass &disp, format_t *formatlistptr, outconvertclass &outconvert, ostream &textout) { text_t icon; int count = 1; int gt = args.getintarg("gt"); text_t &arg_cl = args["cl"]; text_t &arg_d = args["d"]; while (here != end) { int len = (*here).metadata.size(); const text_t &doctype = (*here).metadata[len-2].values.back(); const text_t &hastxt = (*here).metadata[len-4].values.back(); const text_t &haschildren = (*here).metadata[len-3].values.back(); // set up icon and pointer icon = "_document:icon"; if (doctype == "classify") { if (((*here).OID == arg_cl) && (hastxt == "1")) icon += "arrow"; } else if ((*here).OID == arg_d) icon += "arrow"; if (haschildren == "0") icon += "smalltext_"; else if (doctype == "classify") icon += "closedbookshelf_"; else if (is_top((*here).OID)) icon += "closedbook_"; else icon += "closedfolder_"; // set up link text_t link = "
"; else link += "&cl=" + arg_cl + "&d=" + (*here).OID + "\">"; if (gt) { link = ""; count ++; } if (intable) textout << ""; textout << outconvert << disp << get_formatted_string (*here, formatlistptr, link, icon) << "\n"; if (intable) textout << ""; here ++; } } void output_datelist (cgiargsclass &args, ResultDocInfo_tarray::const_iterator &here, ResultDocInfo_tarray::const_iterator &end, bool intable, displayclass &disp, format_t *formatlistptr, outconvertclass &outconvert, ostream &textout) { text_t lastyear = "0000"; text_t lastmonth = "00"; while (here != end) { int len = (*here).metadata.size(); const text_t &doctype = (*here).metadata[len-2].values.back(); const text_t &date = (*here).metadata[len-5].values.back(); // bail on this document if it has no date if (date.empty()) continue; text_t::const_iterator datebegin = date.begin(); int datesize = date.size(); if (datesize < 4) continue; text_t thisyear = substr (datebegin, datebegin+4); text_t thismonth = "00"; if (datesize >= 6) thismonth = substr (datebegin+4, datebegin+6); text_t link = ""; } else link += args["cl"] + "&d=" + (*here).OID + "\">"; textout << "\n"; if (thisyear != lastyear) { textout << outconvert << "" << thisyear << ""; lastyear = thisyear; } else textout << ""; if (thismonth != lastmonth) { textout << outconvert << disp << ("_textmonth" + thismonth + "_"); lastmonth = thismonth; } else textout << ""; if (!intable) textout << "\n"; textout << outconvert << disp << get_formatted_string (*here, formatlistptr, link, icon) << "\n"; if (!intable) textout << ""; textout << "\n"; here ++; } } void output_expanded_hierarchy_toc (cgiargsclass &args, recptproto *collectproto, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { text_t OID, topOID, classifytype, icon; FilterResponse_t response; int tabcols=0, totalcols=0, lasttabcols=0; int gt = args.getintarg("gt"); text_t doclink = " 0) { tab = " 1) tab += " colspan=" + text_t(tabcols); tab += ">" + icontabs + pointer + ""; } textout << outconvert << disp << "" << tab << ""; if ((classifytype == "Document") && (is_top((*thissection).OID)) && (args.getintarg("x"))) textout << outconvert << disp << icon << ""; else if (arg_cl.empty()) link.clear(); else if (arg_cl == "search") link = ""; else link = doclink + arg_cl + "\">"; else if (haschildren) if (classifytype == "classify") link = doclink + thisOID + ".pr\">"; else link = doclink + arg_cl + "&d=" + thisOID + ".pr\">"; else if (classifytype == "classify") link = doclink + thisOID + "\">"; else link = doclink + arg_cl + "&d=" + thisOID + "\">"; textout << outconvert << disp << link; } else { textout << ""; count ++; } textout << outconvert << disp << icon << " 1) textout << " colspan=" << colsremaining; textout << outconvert << disp << ">" << title << "\n"; thissection ++; } } /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/ // set_arrow_macros sets the _httpprevarrow_ and _httpnextarrow_ macros // it shouldn't be called if OID is the top level of a book // as the "hasprevious" and "hasnext" metadata won't be set // when the filter can't get OIDs parent info - one day I'll // fix this ;-) void set_arrow_macros (const text_t &OID, const text_t &classifytype, bool showtoppage, displayclass &disp, recptproto *collectproto, const text_t &collection, ostream &logout) { if (OID.empty()) return; text_tarray metadata; FilterResponse_t response; metadata.push_back ("haschildren"); metadata.push_back ("hasnext"); metadata.push_back ("hasprevious"); // get "haschildren", "hasnext" and "hasprevious" metadata for OID if (get_info (OID, collection, metadata, false, collectproto, response, logout)) { text_t haschildren = response.docInfo[0].metadata[0].values[0]; text_t hasnext = response.docInfo[0].metadata[1].values[0]; text_t hasprevious = response.docInfo[0].metadata[2].values[0]; if ((classifytype == "Hierarchy") || (classifytype == "Book")) { if (haschildren == "1") disp.setmacro ("httpnextarrow", "document", "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.fc,_httpdocument_&cl=_cgiargcl_.fc)"); else if (hasnext == "1") disp.setmacro ("httpnextarrow", "document", "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ns,_httpdocument_&cl=_cgiargcl_.ns)"); else { // see if parent has younger siblings if (get_info (OID + ".pr", collection, metadata, false, collectproto, response, logout)) { if (!response.docInfo[0].metadata.empty() && response.docInfo[0].metadata[1].values[0] == "1") disp.setmacro ("httpnextarrow", "document", "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.pr.ns,_httpdocument_&cl=_cgiargcl_.pr.ns)"); } } if (hasprevious == "1") { // see if OIDs older sibling has children if (get_info (OID + ".ps", collection, metadata, false, collectproto, response, logout)) { if (response.docInfo[0].metadata[0].values[0] == "1") disp.setmacro ("httpprevarrow", "document", "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ps.lc,_httpdocument_&cl=_cgiargcl_.ps.lc)"); else disp.setmacro ("httpprevarrow", "document", "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ps,_httpdocument_&cl=_cgiargcl_.ps)"); } } else if (showtoppage && !is_top (OID)) disp.setmacro ("httpprevarrow", "document", "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.pr,_httpdocument_&cl=_cgiargcl_.pr)"); } else { if (hasnext == "1") disp.setmacro ("httpnextarrow", "document", "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ns,_httpdocument_&cl=_cgiargcl_.ns)"); if (hasprevious == "1") disp.setmacro ("httpprevarrow", "document", "_If_(_cgiargd_,_httpdocument_&cl=_cgiargcl_&d=_cgiargd_.ps,_httpdocument_&cl=_cgiargcl_.ps)"); } } }