dtinfo subtree dtinfo
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / Managers / EnvMgr.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * $TOG: EnvMgr.C /main/18 1998/04/17 11:52:44 mgreess $
25  *
26  * Copyright (c) 1992 HaL Computer Systems, Inc.  All rights reserved.
27  * UNPUBLISHED -- rights reserved under the Copyright Laws of the United
28  * States.  Use of a copyright notice is precautionary only and does not
29  * imply publication or disclosure.
30  *
31  * This software contains confidential information and trade secrets of HaL
32  * Computer Systems, Inc.  Use, disclosure, or reproduction is prohibited
33  * without the prior express written permission of HaL Computer Systems, Inc.
34  *
35  *                         RESTRICTED RIGHTS LEGEND
36  * Use, duplication, or disclosure by the Government is subject to
37  * restrictions as set forth in subparagraph (c)(l)(ii) of the Rights in
38  * Technical Data and Computer Software clause at DFARS 252.227-7013.
39  *                        HaL Computer Systems, Inc.
40  *                  1315 Dell Avenue, Campbell, CA  95008
41  *
42  */
43
44 #include <string.h>
45 #include <iostream>
46 using namespace std;
47 #include <sys/stat.h>
48
49 #include <lib/DtSvc/DtUtil2/LocaleXlate.h>
50
51 // xList, NodeViewInfo, NodeWindowAgent & NodeMgr used only for
52 // getting sections from NodeMgr
53
54 #define C_NodeViewInfo
55 #define L_Basic
56
57 #define C_WindowSystem
58 #define L_Other
59
60 #define C_HelpAgent
61 #define C_NodeWindowAgent
62 #define L_Agents
63
64 #define C_xList
65 #define L_Support
66
67 #define C_EnvMgr
68 #define C_MessageMgr
69 #define C_NodeMgr
70 #ifdef UseSessionMgmt
71 #define C_SessionMgr
72 #endif
73 #define C_GraphicsMgr
74 #define L_Managers
75
76 #include "Managers/CatMgr.hh"
77 #include "Prelude.h"
78
79 LONG_LIVED_CC(EnvMgr, env);
80
81 // a legacy global moved from WindowSystemMotif.C
82 bool g_debug;
83
84 // infolib string-list separator
85 #define ILSEP   ':'
86 // section string-list separator
87 #define SLSEP   ','
88
89
90 EnvMgr::EnvMgr() : f_argv(NULL),
91                    f_argc(0),
92                    f_lang(NULL),
93                    f_secondary(False),
94                    f_verbose(False),
95                    f_debug(False)
96 {
97   const char* lang;
98   if ((lang = getenv("LC_ALL")) == NULL)
99     if ((lang = getenv("LC_CTYPE")) == NULL)
100       if ((lang = getenv("LANG")) == NULL)
101         lang = "C";
102
103   _DtXlateDb db = NULL;
104   char platform[_DtPLATFORM_MAX_LEN + 1];
105   int execver, compver;
106
107   if (_DtLcxOpenAllDbs(&db) == 0)
108   {
109     if (_DtXlateGetXlateEnv(db, platform, &execver, &compver) == 0) {
110
111       char* std_locale = NULL;
112
113       _DtLcxXlateOpToStd(db, platform, compver, DtLCX_OPER_SETLOCALE,
114                                         lang, &std_locale, NULL, NULL, NULL);
115       if (std_locale) {
116 #ifdef LCX_DEBUG
117         fprintf(stderr, "(DEBUG) standard locale=\"%s\"\n", std_locale);
118 #endif
119         f_lang = XtNewString(std_locale);
120         free(std_locale);
121       }
122     }
123
124     _DtLcxCloseDb(&db);
125     db = NULL;
126   }
127
128   // If OpToStd conversion failed, use non-std name
129   if (f_lang == NULL)
130     f_lang = XtNewString(lang);
131
132   // tell mmdb info_lib to load info_base only if it matches to f_lang
133   static char mmdb_lang[_POSIX_PATH_MAX];
134   sprintf(mmdb_lang, "MMDB_LANG=%s", f_lang);
135   putenv (mmdb_lang);
136
137   f_home = XtNewString( getenv("HOME") );
138
139   char dirname[256];
140   sprintf (dirname, "%s/.dt/dtinfo/%s", f_home, f_lang);
141   f_user_path = XtNewString(dirname);
142
143
144   // if $HOME/.dt/dtinfo/$LANG does not exist, create it,
145   // display auto help.
146   if(!check_user_path())
147   {
148     create_user_path();
149     help_agent().display_help ((char*)"doc_list_help");
150   }
151
152   UAS_Collection::request(
153         (UAS_Receiver<UAS_CollectionRetrievedMsg> *)this) ;
154   UAS_Common::request(
155         (UAS_Receiver<UAS_LibraryDestroyedMsg> *)this) ;
156 }
157
158 EnvMgr::~EnvMgr()
159 {
160     // free our generated/abbreviated arguments array
161     for (int i=0; i < f_argc; i++)  XtFree(f_argv[i]);
162     XtFree((char*)f_argv);
163 }
164
165
166 // init()
167 //
168 // initialize the environment class
169 //
170 // place as much as possible in the constructor for the class,
171 // reserving for here those things that have window system dependencies.
172 // parse_cmdline is here only because it might be re-written to use
173 // Xrm Options handling, and because there is a use of XtResolvePathname
174 // for one arg which needs the "display" value...
175 //
176 int
177 EnvMgr::init(int argc_i, char** argv_i)
178 {
179   if( parse_cmdline(argc_i, argv_i) >= 0)
180   {
181     // See if any infolibs entered on command line. If not,
182     // check environment for the default infolib.
183     //
184     if (f_infolibsStr.length() == 0)
185     {
186       char *tmp = infolibDefault();
187
188       if( !(tmp && *tmp) )
189       {
190          // finally, try back door, which works better for non-CDE users
191          // displaying dtinfo from a CDE system to a different desktop,
192          // since all the Dt searchpath stuff won't be much help...
193          const char *directpath = getenv ("DTINFO_INFOLIB_PATH");
194
195          if (directpath != NULL && isInfolib(directpath))
196          {
197            f_infolibsStr = directpath ; // UAS_String promotion
198          }
199          else
200          {
201             // invalid infolib--display error message
202             message_mgr().error_dialog (
203                  (char*)UAS_String(CATGETS(Set_AddLibraryAgent, 7,
204                  "No infolibs found or specified.")));
205             return -1;
206          }
207       }
208       else f_infolibsStr = tmp;
209
210       XtFree(tmp);
211     }
212   }
213   else
214   {
215     usage();
216     return -1;
217   }
218   {
219     // link up indirect paths to mmdb code... ;-)
220     static char buffer[256];
221     sprintf (buffer, "MMDB_PATH=%s", (char *)f_infolibsStr );
222     putenv (buffer);
223
224     char *where = getenv ("DTINFO_MARKSPECPATH");
225     if (where == NULL)
226     {
227        static char markref[256];
228        sprintf (markref, "DTINFO_MARKSPECPATH=%s", "/usr/dt/infolib/etc" );
229        putenv (markref);
230     }
231   }
232   if (f_sectionsArg.length() == 0)
233   {
234     const char *locator = getenv ("DTINFO_FIRSTDOC");
235     if (locator != NULL)
236     {
237        f_sectionsArg = UAS_String(locator) ;    // UAS_String promotion
238     }
239   }
240
241   if (f_verbose) echoEnvState();
242   return 1;
243 }
244
245
246 // infolibStringToList()
247 //
248 // parse a string of colon-separated infolib paths and return as a list.
249 //
250 UAS_List<UAS_String>
251 EnvMgr::infolibStringToList( const UAS_String & s_infolibs )
252 {
253   return s_infolibs.splitFields (ILSEP);
254 }
255
256
257 UAS_List<UAS_String>
258 EnvMgr::sectionStringToList( const UAS_String & s_sections )
259 {
260   // ... currently only handles a comma-separated list
261   // ... for section "range" specifiers, need to make second pass
262   //     on results of following, which will require some kind of
263   //     dive into the UAS to interpret what a "range" of sections
264   //     means, at the moment. This was to only apply with "-print",
265   //     though.
266   return s_sections.splitFields (SLSEP);
267
268 //   UAS_List initial_cut = s_sections.splitFields (SLSEP);
269 //   UAS_String  doc_locator, start_doc, end_doc ;
270 // 
271 //   for (int i = 0; i < docs.length(); i++)
272 //   {
273 //     (*(initial_cut[i])).split( '-', start_doc, end_doc ) ;
274 //     initial_cut.insert( start_doc.length() ? start_doc : end_doc ) ;
275 //     ... duely process rest of any range here to build "expanded_list" ...
276 //   }
277 //   return expanded_list;
278 }
279
280
281 // parse_cmdline()
282 //
283 // Extracts info from command line and sets up internal flags.
284 // -1 returned on error. Innocuous problems ignored, like if no arg
285 // provided where we might have a default or environment variable
286 // specified. Caller should do "usage()" on error return, as this
287 // method flags rather than performs it. 
288 //
289 int
290 EnvMgr::parse_cmdline( int     argc_i,
291                        char ** argv_i )
292 {
293     for (int i = 1; i < argc_i; i++)
294     {   
295       if(strcmp(argv_i[i], "-help") == 0 ||
296          strcmp(argv_i[i], "-h") == 0)
297       {
298         // Print a summary of the command's syntax.
299         return -1;
300       }
301       else if (strcmp(argv_i[i], "-l") == 0 ||
302                strcmp(argv_i[i], "-lib") == 0 )
303       {
304         // get infolibs from command line
305         i++;
306         if( ( i < argc_i ) && ( argv_i[i][0] != '-' ) )
307         {
308           // This arg gives an absolute file path to an information
309           // library, or the short name of the library (which will be
310           // used for substitution into the search path specified by
311           // DTINFOLIBSEARCHPATH in order to locate the infolib).
312           // If the -l option is not provided, the browser displays
313           // the default information library by path obtained from
314           // substituting DTINFOLIBDEFAULT into DTINFOLIBSEARCHPATH.
315           // [DTINFOLIBDEFAULT should be a single library short name.]
316           // The "-l" option may be specified more than once.
317
318           UAS_String pathname;
319
320           if( !strchr( argv_i[i], ':' ) )
321           {
322             char *tmp = infolibStringToPath(argv_i[i]);
323             if( tmp )
324             {
325               pathname = UAS_String(tmp);
326               if( tmp != argv_i[i] )  XtFree(tmp);      // clean-up case
327             }
328           }
329           else   // defer validation if arg contains colon separators
330             pathname = UAS_String(argv_i[i]);  
331
332           if( pathname.length() != 0 )
333             if( f_infolibsStr.length() != 0 )
334             {
335               // need to concatenate
336               f_infolibsStr = f_infolibsStr + ":" + pathname ;
337             }
338             else
339             {
340               // init string
341               f_infolibsStr = pathname ;
342             }
343           else
344           {
345             // invalid infolib--display error message, but not fatal
346             message_mgr().error_dialog (
347                   (char*)UAS_String(CATGETS(Set_AddLibraryAgent, 5,
348                   "Infolib specification format error.")));
349           }
350         }
351       //else ... ignore this one
352       }
353       else if (strcmp(argv_i[i], "-sect") == 0 ||
354                strcmp(argv_i[i], "-section") == 0 )
355       {
356         // get a list of sections from the command line
357         i++;
358         if( ( i < argc_i ) && ( argv_i[i][0] != '-' ) )
359         {
360           // This arg specifies the infolib section (or a comma-separated
361           // list of sections) via their unique locator IDs to either
362           // display or print. 
363           // Sections can be specified using the hyphen character to
364           // represent a range of sections, although it is up the print
365           // interface to resolve what such a range means. Note that a
366           // section "range" is only predictable within the same version
367           // of an infolib from which it was mapped.
368           // If the -print option is specified, the sections are printed;
369           // otherwise, they are displayed (if a range is specified without
370           // "-print", only the first ID in the range will be processed).
371           // If a specified location ID is not at the top of a section,
372           // the section containing the location is printed.
373
374           f_sectionsArg = UAS_String(argv_i[i]);
375           // (we actually build the sections "list" later, based
376           //  on document windows actually displayed)
377         }
378       //else ... ignore this one
379       }
380       else if (strcmp(argv_i[i], "-secondary") == 0)
381       {
382         // Run this dtinfo instance without tooltalk session participation.
383         // Secondary instances do not respond to ToolTalk messages. 
384         f_secondary = True;
385       }
386       else if (strcmp(argv_i[i], "-verbose") == 0 ||
387                strcmp(argv_i[i], "-v") == 0)
388       {
389         // make verbose a boolean flag
390         f_verbose = True;
391       }
392 #ifdef UseSessionMgmt
393       else if (strcmp(argv_i[i], "-session") == 0)
394       {
395         // Get special session save-state file name
396         // This option will usually result only from programmatic insertion
397         // for purpose of session restart.
398         i++;
399         if(argv_i[i][0] != '-')
400         {
401           session().file( argv_i[i] ) ;
402         }
403       //else, ignore any errors for this arg, which is usually transparent
404 #endif
405       }
406       else if (strcmp(argv_i[i], "-Debug") == 0 ||
407                strcmp(argv_i[i], "-D") == 0)
408       {
409         // Undocumented flag:  turn on debugging
410         g_debug = TRUE;
411         f_debug = TRUE;
412         cerr << "\nInternal debugging activated." << endl;
413       }
414       else
415       {
416         cerr << CATGETS(Set_EnvMgr, 2, "Invalid argument") << endl;
417         return -1;
418       }
419    }
420
421    // do any argument consistency/cross-validation here
422
423     // make sure that if print option was specified that some sections 
424     // to print were also specified.
425     if ( (window_system().videoShell()->print_only) && (f_sectionsArg == (UAS_String)NULL)) {
426         cerr << CATGETS(Set_EnvMgr, 3, "ERROR: The -sect option must be specified with the -print option.") << endl;
427         cerr << endl;
428         usage();
429         exit(1);
430     }
431    return 0;
432 }
433
434
435 void
436 EnvMgr::usage()
437 {
438     cerr <<  CATGETS(Set_EnvMgr, 4, "Usage: dtinfo") << endl;
439     cerr << "   [-help]" << endl;
440     cerr << "   [-l infolib1] [-l infolib2] [...]" << endl;
441     cerr << "   [-sect section[-section][,section[-section]]]" << endl;
442     cerr << "   [-secondary]" << endl;
443     cerr << "   [-verbose]" << endl;
444     cerr << "   [[-print] [[-copies number]" << endl;
445     cerr << "              [-hierarchy]" << endl;
446     cerr << "              [-outputFile]" << endl;
447     cerr << "              [-paperSize]" << endl;
448     cerr << "              [-printer x_print_server]]]" << endl;
449     cerr << endl;
450     cerr << CATGETS(Set_EnvMgr, 5, 
451                     "This application understands all standard X Toolkit command-line options.") << endl;
452     
453 }
454
455
456 void
457 EnvMgr::echoEnvState()
458 {
459   // need to display environment variables and
460   // other information that would be useful
461   cerr << endl;
462   cerr << "   -l = " << (char*)f_infolibsStr << endl;
463   cerr << "-sect = " << (char*)f_sectionsArg << endl;
464   cerr << endl;
465   if (getenv("DTINFOLIBSEARCHPATH") == NULL)
466     cerr << "DTINFOLIBSEARCHPATH = " << endl;
467   else
468     cerr << "DTINFOLIBSEARCHPATH = " << getenv("DTINFOLIBSEARCHPATH") << endl;
469
470   if (getenv("DTINFOLIBDEFAULT") == NULL)
471     cerr << "   DTINFOLIBDEFAULT = " << endl;
472   else
473     cerr << "   DTINFOLIBDEFAULT = " << getenv("DTINFOLIBDEFAULT") << endl;
474
475   if (getenv("DTHELPSEARCHPATH") == NULL)
476     cerr << "   DTHELPSEARCHPATH = " << endl;
477   else
478     cerr << "   DTHELPSEARCHPATH = " << getenv("DTHELPSEARCHPATH") << endl;
479
480   if (getenv("XUSERFILESEARCHPATH") == NULL)
481     cerr << "XUSERFILESEARCHPATH = " << endl;
482   else
483     cerr << "XUSERFILESEARCHPATH = " << getenv("XUSERFILESEARCHPATH") << endl;
484
485   if (getenv("LANG") == NULL)
486     cerr << "               LANG = " << endl;
487   else
488     cerr << "               LANG = " << getenv("LANG") << endl;
489   cerr << "          LANG(CDE) = " << lang() << endl;
490
491   if(debug())
492   {
493     cerr << endl;
494     if (getenv("XAPPLRESDIR") == NULL)
495       cerr << "        XAPPLRESDIR = " << endl;
496     else
497       cerr << "        XAPPLRESDIR = " << getenv("XAPPLRESDIR") << endl;
498
499     if (getenv("XFILESEARCHPATH") == NULL)
500       cerr << "    XFILESEARCHPATH = " << endl;
501     else
502       cerr << "    XFILESEARCHPATH = " << getenv("XFILESEARCHPATH") << endl;
503
504     if (getenv("NLSPATH") == NULL)
505       cerr << "            NLSPATH = " << endl;
506     else
507       cerr << "            NLSPATH = " << getenv("NLSPATH") << endl;
508   }
509 }
510
511
512 // isDir()
513 //
514 // Function to pass to the XtResolvePathname call in place of
515 // the default predicate, which does not allow for directories.
516 //
517 static bool
518 isDir(char* dirname)
519 {
520   struct stat status;
521
522   // just check to see if it's a directory
523   if(access(dirname, R_OK) == 0)
524   {
525     stat(dirname, &status);
526     return (S_ISDIR(status.st_mode));
527   }
528  else
529     return False;
530 }
531
532
533 // isInfolib()
534 //
535 // determine whether an [assumed] absolute path specification
536 // matches basic file layout criteria for an infolib installation.
537 // (ignores search engine and index files, which would be more
538 // appropriately tested by a separate function, according to the
539 // search engine type)
540 //
541 bool
542 EnvMgr::isInfolib( const char* path )
543 {
544     if (path == NULL || *path == '\0')
545         return False;
546
547     struct stat stat_buf;
548     if (stat(path, &stat_buf) < 0)
549         return False;
550
551     if ((stat_buf.st_mode & S_IFMT & S_IFDIR) == 0)
552         return False;
553
554     if (access((char*)path, R_OK) < 0)
555         return False;
556
557     UAS_String map = path;
558     map = map + "/" + "bookcase.map";
559
560     if (access((char*)map, R_OK) < 0)
561         return False;
562
563     return True;
564 }
565
566
567 // get the default infolib per the environemnt variables
568 // DTINFOLIBDEFAULT and DTINFOLIBSEARCHPATH.
569 // Neither are tested for validity, which is assumed.
570 //
571 // Caller must free any returned string with XtFree()
572 //
573 char*
574 EnvMgr::infolibDefault()
575 {
576    return infolibNameToPath( getenv("DTINFOLIBDEFAULT") );
577 }
578
579
580 // infolibNameToPath()
581 //
582 // Take the "short name" of an infolib (like "cde" in its dir name
583 // "cde.dti", and return the full path to library, if found in the
584 // default infolib search path set. Else, return NULL for no match.
585 //
586 // Caller must free any returned string with XtFree(),
587 // per XtResolvePathName
588 //
589 char*
590 EnvMgr::infolibNameToPath(char* path)
591 {
592   SubstitutionRec subs[1];
593   char* FileName;
594
595   subs[0].match = 'I';
596   subs[0].substitution = path;
597   FileName = XtResolvePathname(
598             window_system().display(),
599             NULL, NULL, NULL,
600             getenv("DTINFOLIBSEARCHPATH"),
601             subs, XtNumber(subs),
602             (XtFilePredicate)isDir);
603
604   return FileName;
605 }
606
607
608 // infolibPathIsLocale()
609 //
610 // Attempt to match the immediate parent dir to the current locale or
611 // cde-normalized locale.
612 // Return false if no match, but caller must realize that this alone is
613 // inconclusive evidence.
614 bool
615 EnvMgr::infolibPathIsLocale(char* ilpath)
616 {
617   // ... implement check of path head
618   return False ;
619 }
620
621
622 // infolibStringToPath()
623 //
624 // Takes string representing a single infolib and attempts to convert
625 // it to an absolute infolib file path.
626 //
627 // Uses function isInfolib() for testing whether a result actually
628 // meets infolib criteria.
629 //
630 // Caller should test for NULL since valid infolib may not be found.
631 // Caller should free the result with XtFree() if returned pointer is
632 // not null AND not equal to the pointer supplied.
633 //
634 char*
635 EnvMgr::infolibStringToPath(char* path)
636 {
637   char* filename;
638
639   if( *path == '/' && isDir(path) )
640   {
641     filename = path ;           // assume input already absolute path
642   }
643   else if ( strchr( path, '/' ) )
644   {
645     // relative path... could get curr dir and build an absolute path
646     filename = NULL ;
647   }
648   else          // assume a name and do path look-up
649   {
650     filename = infolibNameToPath(path) ;
651   }
652
653   if( !isInfolib( filename ) ) filename = NULL ;
654
655   return filename;
656 }
657
658
659 // UAS_DocumentRetrievedMsg message handler
660 // ...
661 // void
662 // EnvMgr::receive (UAS_DocumentRetrievedMsg &msg,
663 //               void * client_data)
664 // {
665   // manipulate msg.fDoc ==> UAS_Pointer<UAS_Common> type
666 // }
667
668
669 // UAS_CollectionRetrievedMsg message handler
670 void
671 EnvMgr::receive (UAS_CollectionRetrievedMsg &msg,
672                  void * client_data)
673 {
674   infolibAdd( fqlToFilePath(msg.fCol->root()->locator()) );
675 }
676
677
678 // UAS_LibraryDestroyedMsg message handler
679 void
680 EnvMgr::receive (UAS_LibraryDestroyedMsg &msg,
681               void *    client_data)
682 {
683   infolibRemove( fqlToFilePath(msg.fLib->locator()) );
684 }
685
686
687 // infolibAdd()
688 //
689 // Maintains state of browser in terms of infolibs actually loaded.
690 // The intent is for this function to be called only if the infolib
691 // has been successfully loaded (and thus appears in the booklist)
692 //
693 void
694 EnvMgr::infolibAdd( UAS_String pathname )
695 {
696   if ( f_infolibsStr.length() != 0 )
697   {
698     // this is the init case, where the first infolib has actually been
699     // loaded successfully by the browser. The init str is now superfluous.
700     f_infolibsStr = "" ;
701   }
702   f_infolibs.insert_item(new UAS_String(pathname));
703 }
704
705
706 // infolibRemove()
707 //
708 // Maintains state of browser in terms of infolibs actually loaded.
709 // The intent is for this function to be called only if the infolib
710 // has been removed, once previously and successfully loaded
711 //
712 void
713 EnvMgr::infolibRemove( UAS_String pathname )
714 {
715   if ( f_infolibsStr.length() != 0 )
716   {
717     // this entry case should never occur, so the remove is ignored
718     cerr << "Invalid argument" << endl;
719   }
720   f_infolibs.remove_item(new UAS_String(pathname));
721 }
722
723
724 // infolibs()
725 //
726 // Return a list of infolibs known to browser.
727 // Any call to this function prior to the first infolib actually being
728 // loaded will get the list of infolibs initially requested at startup.
729 // Subsequent calls will be returned a list of the infolibs loaded at
730 // that time.
731 //
732 // The list entries are in the form of absolute path (as resolved and
733 // validated) to the infolib, and will be used for any session restart
734 // states saved with XSetCommand, as "-l" options to dtinfo.
735 //
736 // Prerequisite: command line args must have been processed (which
737 // will always have been the case for any call, since EnvMgr is "LongLived").
738 // 
739 UAS_List<UAS_String>
740 EnvMgr::infolibs()
741 {
742   // after initialization, there is always at least one infolib loaded
743   if( infolibCount() == 0 )
744     return infolibStringToList( f_infolibsStr ) ;
745   else
746     return f_infolibs;
747 }
748
749
750 // get current infolib list as arg value string
751 //
752 UAS_String
753 EnvMgr::infolibsArg()
754 {
755   if( ( infolibCount() == 0 ) && ( f_infolibsStr.length() != 0 ) )
756   {
757     return f_infolibsStr ;
758   }
759   else          // convert current list of sections to an arg
760   {
761     return cvtListToString( f_infolibs, ":", True ) ;
762   }
763 }
764
765
766 // sections()
767 //
768 // Return a list of sections/nodes currently on display by the browser.
769 // Any call to this function prior to the first section actually displayed
770 // will get the list of sections initially requested at startup.
771 // Subsequent calls will be returned a list of the sections loaded at
772 // that time.
773 //
774 // The list entries are in the form of vaildated unique ids, and
775 // will be used for any session restart states saved with XSetCommand,
776 // as the argument to the "-sect" option to dtinfo.
777 //
778 // Prerequisite: command line args must have been processed (which
779 // will always have been the case for any call, since Env is "LongLived").
780 // 
781 UAS_List<UAS_String>
782 EnvMgr::sections()
783 {
784   // check infolibCount to determine if in post-init phase...
785   if( infolibCount() == 0 )
786     return sectionStringToList( f_sectionsArg ) ;
787   else                                  // post-init phase
788   {
789     return getCurrentSections() ;
790   }
791 }
792
793
794 // get current section list as arg string
795 //
796 UAS_String
797 EnvMgr::sectionsArg()
798 {
799   if( infolibCount() == 0 )
800     return f_sectionsArg;
801   else                  // convert current list of sections to an arg
802     return cvtListToString( getCurrentSections(), ",", True ) ;
803 }
804
805
806 // getCurrentSections()
807 //
808 // get the sections actually on display [right now] in all known
809 // node view windows, in their unique id format (i.e., not fully-qualified)
810 //
811 UAS_List<UAS_String>
812 EnvMgr::getCurrentSections()
813 {
814   UAS_List<UAS_String> f_sections ;
815
816   // get current section IDs from each node view visible
817   xList<NodeWindowAgent *> &nwl = node_mgr().windows() ;
818
819   List_Iterator<NodeWindowAgent *> nwli (nwl) ;
820   for ( ; nwli; nwli ++)
821   {
822     f_sections.insert_item(
823          new UAS_String(nwli.item()->node_view_info()->node_ptr()->id()) ) ;
824   }
825   return f_sections ;
826 }
827
828
829 // fqlToFilePath()
830 //
831 // convert a fully qualified locator to the infolib's absolute path,
832 // as resolved during the load phase. This function is expecting only
833 // fully qualified forms which include the "INFOLIB=<path>" part, such
834 // as are returned by the UAS_Common method "locator()", and serves to
835 // extract the "path".
836 //
837 // Caution: An fql does not have to have the "INFOLIB=" part...
838 //          Could add insurance check for that here to be more robust.
839 //
840 UAS_String
841 EnvMgr::fqlToFilePath( UAS_String fulloc )
842 {
843   UAS_String discard;
844
845   fulloc.split ('=', discard, fulloc);
846   fulloc.split ('&', fulloc, discard);
847   return fulloc ;
848 }
849
850
851 // arglist()
852 //
853 // construct and return an arglist which could restart the browser
854 // in its current state.
855 //
856 const char**
857 EnvMgr::arglist()
858 {
859   char  *tmp ;
860   int p_argc = 5 ;      // always have an arg0, a "-l <path>", and a
861                         // "-sect <list>" (even though list may be empty)
862
863   if( !f_argv )
864   {
865     // augment count for any other necessary options here
866     if( secondary() )  ++p_argc ;
867
868     f_argv = (char **)XtMalloc( p_argc*sizeof(char *) ) ;
869
870     int ia = 0 ;
871     f_argv[ia++] = XtNewString( "dtinfo" ) ;
872     if( secondary() )
873     {
874       f_argv[ia++] = XtNewString( "-secondary" ) ;
875     }
876
877     f_argv[ia++] = XtNewString( "-l" ) ;
878     tmp = (char *)(infolibsArg()) ;
879     f_argv[ia++] = XtNewString( tmp ) ;
880
881     // always insert the -sect key and its arg, even if the arg
882     // is zero length. This saves us from any re-malloc and copy.
883     f_argv[ia++] = XtNewString( "-sect" ) ;
884     tmp = (char *)(sectionsArg()) ;     // can be empty here
885     f_argv[ia++] = XtNewString( tmp ) ;
886
887     f_argc = p_argc ;
888   }
889   else
890   {
891     int il=0, is=0 ;
892
893     // free then reset the infolibs/sections arg values per current state
894     for( int i=1; i < f_argc && (il == 0 || is == 0) ; i++ )
895     {
896       if( strcmp(f_argv[i], "-l") == 0 )
897       {
898         XtFree( f_argv[++i] ) ;
899         tmp = (char *)(infolibsArg()) ;
900         f_argv[i] = XtNewString( tmp ) ;
901         il = i ;
902       }
903       else if( strcmp(f_argv[i], "-sect") == 0 )
904       {
905         XtFree( f_argv[++i] ) ;
906         tmp = (char *)(sectionsArg()) ;
907         f_argv[i] = XtNewString( tmp ) ;
908         is = i ;
909       }
910     }
911   }
912
913   return (const char **)f_argv ;
914 }
915
916
917 const char*
918 EnvMgr::home()
919 {
920   return f_home;
921 }
922
923 const char*
924 EnvMgr::lang()
925 {
926   return f_lang;
927 }
928
929 bool
930 EnvMgr::mkdirs(char *pathname)
931 {
932   char buffer[256];
933   char *c;
934   strcpy(buffer, pathname);
935
936   if(mkdir(buffer, 0777) == -1)
937   {
938     c = strrchr(buffer, '/');
939     if (c != NULL)
940       *c = 0;
941     (void)mkdirs(buffer);
942     if (c != NULL)
943       *c = '/';
944     if(mkdir(buffer, 0777) == -1)
945       return False;
946   }
947   return True;
948 }
949
950 bool
951 EnvMgr::check_user_path()
952 {
953   struct stat file_info;
954
955   int status = stat (f_user_path, &file_info);
956   if (status == -1)
957     return False; 
958
959   if (!S_ISDIR (file_info.st_mode))
960     throw (CASTEXCEPT Exception());
961
962   return True;
963 }
964
965 void
966 EnvMgr::create_user_path()
967 {
968   if (mkdirs (f_user_path) == False)
969     throw (CASTEXCEPT Exception());
970 }
971
972 const char*
973 EnvMgr::user_path()
974 {
975   if(!check_user_path())
976     create_user_path();
977   return f_user_path;
978 }
979
980
981 // each element of theList is concatenated into a single string using
982 // the "delimiter" character for separation. Delimiter is treated as
983 // a prefix if "infix" is False, in which case it initiates the string.
984 //
985 UAS_String
986 EnvMgr::cvtListToString( UAS_List<UAS_String> theList,
987                          UAS_String           delimiter,
988                          bool                 infix )
989 {
990   UAS_String  aggregate ;
991
992   int len = theList.length();
993   if( len != 0 )
994   {
995     if( infix ) aggregate = *(theList[0]);
996     else        aggregate = delimiter + *(theList[0]);  // prefix
997     for (int i = 1; i < len; i++)
998     {
999       // concatenate each additional item using the separator
1000       aggregate = aggregate + delimiter + *(theList[i]) ;
1001     }
1002   }
1003   return aggregate ;
1004 }
1005