aboutsummaryrefslogtreecommitdiff
path: root/vendor/voclient/libvoclient/vocLib.c
blob: 19b2a435fb18710eb43c00f551775514c39ac18c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
/***************************************************************************
 *
 *  VOCLient Library --  Client interface library to the VOClient daemon
 *  application.  This API allows non-Java programs to make use of the  DAL
 *  client interface by means of a remote procedure call between this
 *  interface and the VOClient daemon.  State is maintained in the server and
 *  values passed back are language-neutral integer 'handles' on the remote
 *  objects, or simple int/double/string values.
 *
 *  All tasks must initialize the VO Client and establish a connection to
 *  the VOClient daemon by calling voc_initVOClient() before making any service
 *  connections, thereafter new connections may be opened/closed at will.
 *  Convenience procedures allow for easy use of specific services, e.g. Cone
 *  or Siap.  Service-specific parameters may be added to a query using the
 *  voc_add<type>Param() calls.  No action is taken until an execute of the
 *  query is performed, applications may get back a handle on the result and
 *  interrogate attributes directly, the raw VOTable or a CSV representation
 *  of the result may also be returned.
 *
 *  High-Level Functions:
 *  ---------------------
 *
 *               voc_initVOClient (config_opts)
 *              voc_closeVOClient (shutdown_flag)
 *              voc_abortVOClient (errcode, errmsg)
 * 
 *        string = voc_coneCaller (url, ra, dec, sr, otype)
 *  status = voc_coneCallerToFile (url, ra, dec, sr, otype, file)
 *        string = voc_siapCaller (url, ra, dec, rsize, dsize, fmt, otype)
 *  status = voc_siapCallerToFile (url, ra, dec, rsize, dsize, fmt, otype, file)
 * 
 *
 *  Interface Utility Procedures:
 *  ------------------------------
 * 
 *         flag = voc_validateObj (hcode)
 *		   voc_debugLevel (level)
 *		  voc_freePointer (ptr)
 *
 * 
 *  Main DAL Interface Procedures:
 *  ------------------------------
 *
 *       dal = voc_openConnection (svc_url, type)
 *   dal = voc_openConeConnection (svc_url)		    # Utility aliases
 *   dal = voc_openSiapConnection (svc_url)
 *            voc_closeConnection (dal)
 * 
 *    count = voc_getServiceCount (dal)
 *              voc_addServiceURL (dal, svc_url)
 *        url = voc_getServiceURL (dal, index)
 * 
 *           query = voc_getQuery (dal, type)
 *       query = voc_getConeQuery (dal, ra, dec, sr)
 *       query = voc_getSiapQuery (dal, ra, dec, ra_size, dec_size, format)
 *
 *         stat = voc_addIntParam (query, pname, ival)
 *       stat = voc_addFloatParam (query, pname, dval)
 *      stat = voc_addStringParam (query, pname, str)
 * 
 *   url_str = voc_getQueryString (query, type, index)
 *
 *          qr = voc_executeQuery (query)
 *      stat = voc_executeQueryAs (query, fname, type)
 *       csv_str = voc_executeCSV (query)
 *   vot_str = voc_executeVOTable (query)
 *
 *     count = voc_getRecordCount (qr)
 *            rec = voc_getRecord (qr, recnum)
 *
 *        attr = voc_getAttribute (rec, char *attrname)
 *       count = voc_getAttrCount (rec)                   
 *     list_str = voc_getAttrList (rec)                   
 *
 *            ival = voc_intValue (attr)
 *          dval = voc_floatValue (attr)
 *          str = voc_stringValue (attr)
 *
 *                 voc_setIntAttr (rec, attrname, ival)   (Not Yet Implemented)
 *               voc_setFloatAttr (rec, attrname, dval)   ( "   "      "      )
 *              voc_setStringAttr (rec, attrname, str)    ( "   "      "      )
 *
 *          stat = voc_getDataset (rec, acref, fname) 
 *
 *
 *  Sesame Name Resolver Interface:
 *  -------------------------------
 *
 *          sr = voc_nameResolver (target)
 *      pos_str = voc_resolverPos (sr)
 *         radeg = voc_resolverRA (sr)
 *       decdeg = voc_resolverDEC (sr)
 * 
 *
 *	Client programs may be written in any language that can interface to
 *  C code.  Sample programs using the interface are provided as is a SWIG
 *  interface definition file.  This inferface is based closely on the DAL
 *  client code produced for the 2005 NVOSS, as that interface evolves 
 * 
 *
 *  Michael Fitzpatrick, NOAO, June 2006
 *
 **************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>


#define _VOCLIENT_LIB_
#include "VOClient.h"


VOClient *vo = (VOClient *) NULL;	/* Interface runtime struct	*/

#define SVR_MAXTRY      	5
#define DEF_VOCSERVER_PORT	6200


typedef struct {
    char  server[SZ_FNAME];		/* VOClient server daemon	*/
    char  proxy[SZ_FNAME];		/* VOClient server proxy	*/
    char  config[SZ_FNAME];		/* VOClient config file		*/
    char  path[SZ_FNAME];		/* VOClientd path		*/
    char  runid[SZ_FNAME];		/* RUNID logging string	        */

    int   port;				/* Server port			*/
    int   spawn;			/* spawn a local daemon?	*/
    int   console;			/* initiate a console?		*/
    int   onetrip;			/* force a single instance?	*/
    int   use_cache;			/* use object/registry cache?   */
    int   use_runid;			/* use RUNID parameter?		*/
    int   quiet;			/* suppress API output?		*/
} vocOpt;




/*************************************
 *  Private procedure declarations.
 *************************************/
static int  voc_openVOCServer (char *dev);
static int  voc_parseDev (char *dev, unsigned short *host_port,
                unsigned long *host_addr);

static vocOpt *voc_initOpts ();
static vocOpt *voc_parseOpts (char *opts);
static vocOpt *voc_readConfig (char *config);

static char   *voc_cacheCreate (char *home, char *cache, char *subdir);

vocOpt *vopt  = (vocOpt *) NULL;


void	voc_exitHandler();


/******************************************************************************
**  OPENVOCLIENT --  Open and initialize the VOClient interface.
*/
int
voc_initVOClient (char *opts)
{
    register int   i;
    char  *home, *s, config[128], *opt_str;

    char  *VOCServer = (char *)NULL;
    char  *VOCProxy = (char *)NULL;
    int   port    = DEF_VOCSERVER_PORT;
    int   spawn   = TRUE;
    int   console = FALSE;
    int   onetrip = FALSE;


    /* Initialize the options.
    */
    vopt  = (vocOpt *) voc_initOpts ();


    /*  Configuration order (should be):
    **
    **		- defaults				(default)
    **		- $home/.voclient/config file		(user pref default)
    **		- application settings			(app requirements)
    **		- environment settings			(app-specific req.)
    */

    /* Get a debug flag from the opt string or the environment if we can.
     */
    opt_str = ( opts ? opts : getenv ("VOC_OPTS"));
    if (opt_str) {
	if (! (vopt = voc_parseOpts (opt_str))) {
	    if (!vopt->quiet)
	        fprintf (stderr, "ERROR: Invalid opt string '%s'!\n", opt_str);
	    return ERR;

	} else {
	    /* Allow an opt string to specify a config file to be used.
	    */
	    if (vopt->config && access (vopt->config, R_OK) == 0)
	        vopt = voc_readConfig (vopt->config);
	}

    } else {
	/* Attempt to open the config file.  This may be specified using
	** either and environment variable or the default path.
	*/
	memset (config, 0, 128);
	if ((s = getenv ("VOC_CONFIG")))
	    strcpy (config, s);
	else if ((home = getenv ("HOME")))
            sprintf (config, "%s/.voclient/config", home);

	/* Use the config from the environment, then try the user's
	** config file before using the default.
	*/  
	if (access (config, R_OK) == 0)
	    vopt = voc_readConfig (config);
    }


    /* Use defaults if we didn't get any config options.
    */
    if (vopt == (vocOpt *)NULL) {
	vopt = (vocOpt *) calloc (1, sizeof (vocOpt));
        strcpy (vopt->server, DEF_SERVER);
        strcpy (vopt->proxy,  DEF_NET_SERVER);
        strcpy (vopt->runid,  DEF_RUNID);
        vopt->port      =  DEF_VOCSERVER_PORT;
        vopt->spawn     =  TRUE;
        vopt->quiet     =  TRUE;
        vopt->use_cache =  TRUE;
        vopt->use_runid =  TRUE;
        vopt->console   =  FALSE;
        vopt->onetrip   =  onetrip;
    }


    /* Allow an environment variable to override a config file setting or
    ** task option string.
    */
    VOCServer = ((s = getenv("VOC_SERVER")) ? s : vopt->server);
    VOCProxy  = ((s = getenv("VOC_PROXY")) ? s : vopt->proxy);
	    
    if (0) {
	fprintf (stderr, "Server: '%s' Proxy: '%s' ", VOCServer, VOCProxy);
	fprintf (stderr, "spawn=%d  console=%d  port=%d  onetrip=%d\n",
	    spawn, console, port, onetrip);
    }

    if (vo) {
	if (VOC_DEBUG)
	    fprintf (stderr, "Warning: VOClient already initialized!\n");
	return OK;

    } else {
        /* Allocate the struct.
	*/
        vo = calloc (1, sizeof (VOClient));

	/* Get a channel to a server.  If we can't start our own daemon, try
	** connecting to the proxy server.
	*/
	if (!vopt->spawn) {
	    if ((vo->io_chan = voc_openVOCServer(VOCProxy)) == (int) VOC_NULL) {
                if (!vopt->quiet)
	            fprintf (stderr,
			"Cannot connect to local or proxy server.\n");
	        return ERR;
	    }


	} else if ((vo->io_chan=voc_openVOCServer(VOCServer))==(int)VOC_NULL) {
	    char *cmd = calloc (1, 128);

	    if (VOC_DEBUG)
		fprintf (stderr, "Couldn't open server, starting private...\n");

	    /*  FIXME -- 
	    //system ("voclientd -private >& /dev/null &");
	    if (opts && strstr (opts, "console")) {
	        system ("voclientd -gui &");
	    } else {
	        system ("voclientd >& /dev/null &");
	    }
	     */
	    if (vopt->console)
	        sprintf (cmd, "%s -gui -port %d &", 
		    (vopt->path[0] ? vopt->path : "voclientd"), vopt->port);
	    else
	        sprintf (cmd, "%s -port %d > /dev/null 2>&1 &", 
		    (vopt->path[0] ? vopt->path : "voclientd"), vopt->port);

	    system (cmd);

	    /* We couldn't connect to the default server, so try to start
	     * a private version for this session.  Timeout after SVR_MAXTRY
	     * attempts.
	     */
	    for (i=SVR_MAXTRY; i && vo->io_chan == (int) VOC_NULL; i--) {
		sleep (1);
	        vo->io_chan = voc_openVOCServer ("6200");
	    }
	    if (i == 0) {
	        if ((vo->io_chan=voc_openVOCServer(VOCProxy))==(int)VOC_NULL) {
		    if (!vopt->quiet)
	              fprintf (stderr,
		        "ERROR: Cannot connect to or create server process\n");
	            return ERR;
	        }
	    }
	}
    }

    /* Get a debug flag from the environment if we can.
     */
    vo->debug = ((s = getenv("VOC_DEBUG")) != NULL);
    if (s && (i = atoi(s)))
        vo->debug = i;

    vo->quiet     = vopt->quiet;
    vo->use_cache = vopt->use_cache;
    vo->use_runid = vopt->use_runid;
    vo->onetrip   = vopt->onetrip;
    vo->runid     = vopt->runid;

    /* Post an exit handler so we clean up properly.
     */
    (void) atexit (voc_exitHandler);

    return OK;
}


/******************************************************************************
**  CLOSEVOCLIENT -- Close and free the VOClient interface.
*/
void
voc_closeVOClient (int shutdown)
{
    vocMsg_t *msg = (vocMsg_t *) msg_quitMsg ();

    
    if (vo == (VOClient *) NULL)		/* no-op on null pointer */
	return;

    if (vo->onetrip || shutdown) {
        vocMsg_t *msg = (vocMsg_t *) msg_shutdownMsg ();

	/* Send the shutdown request.  We don't expect a reply so send a
	 * raw message and assume it got there.  Use an asynchrnous write
	 * so we don't block.
	 */
	(void) msg_sendRawMsg (vo->io_chan, msg);

    } else {
        vocRes_t *result;

    	/* Send the quit request. */
	if (msg_resultStatus ((result = msg_sendMsg (vo->io_chan, msg))) == ERR)
	    if (vo->debug)
	    	fprintf (stderr, "ERROR quitting voclientd.\n");

        free ((vocRes_t *) result);
    }

    /* Close the VOClient connection.
     */
    if (vo->io_chan >= 0) {
        close (vo->io_chan);
	vo->io_chan = -1;
    }

    /* Free the structure.
     */
    if (msg) free ((vocMsg_t *) msg);
    if (vo)  free ((void *) vo);
    vo = (VOClient *) NULL;
}



/******************************************************************************
**  ABORTVOCLIENT -- Close the VOClient interface and abort the application.
*/
void
voc_abortVOClient (int code, char *msg)
{
    if (msg && !vo->quiet)
	fprintf (stderr, "ABORT: %s\n", msg);
    voc_closeVOClient (1);
    exit (code);
}



/******************************************************************************
**  EXITHANDLER -- Automatically disconnect clients that forget to do so.
*/
void
voc_exitHandler()
{
    if (vo)
        voc_closeVOClient (0);
}


/******************************************************************************
**  VALIDATEOBJECT -- Given the hashcode hand for an object, verify it is 
**  still valid in the daemon hasmap.  Returns 0 if not valid, 1 if valid.
*/
int
voc_validateObject (int hcode)
{
    vocRes_t *result = (vocRes_t *) NULL;
    vocMsg_t *msg = (vocMsg_t *) msg_newCallMsg (hcode, "validateObject", 0);
    int       flag = 0;

    /* Send message and read result.  */
    if (msg_resultStatus ((result = msg_sendMsg (vo->io_chan, msg))) != ERR)
        flag = msg_getIntResult (result, 0);

    if (msg)    free ((void *)msg);         /* free the pointers            */
    if (result) free ((void *)result);

    return (flag);
}


/******************************************************************************
**  VOCREADY -- Verify that the VO Client daemon is alive and well.
*/
int
voc_ready ()
{
    vocRes_t *result = (vocRes_t *) NULL;
    vocMsg_t *msg = (vocMsg_t *) msg_ackMsg ();
    int       flag = 0;

    /* Send message and read result.  */
    if (msg_resultStatus ((result = msg_sendMsg (vo->io_chan, msg))) != ERR)
        flag = msg_getIntResult (result, 0);

    if (msg)    free ((void *)msg);         /* free the pointers            */
    if (result) free ((void *)result);

    return (flag);
}



/******************************************************************************
**  FREEPOINTER -- Free a pointer allocated in the interface.  For Client
**  apps like the IRAF CL a pointer returned for a string cannot be freed
**  properly because of a proprietary memory i/o interface.  So, we'll pass
**  it back into the interface to free it.
*/
void
voc_freePointer (char *ptr)
{
    if (ptr) 
	free ((void *) ptr);
}


/******************************************************************************
** DEBUGLEVEL -- Set the package debugging output level.
*/
void
voc_debugLevel (int level) { vo->debug = level; }






/*****************************************************************************
 *****************************************************************************
 ***
 ***   PRIVATE PROCEDURES
 ***
 *****************************************************************************
 ****************************************************************************/


/******************************************************************************
**  VOC_OPENVOCSERVER -- Open a connection to the VOClient server.  The caller
**  may either specify a connection at device open time, or the procedure will
**  attempt to connect on a unix socket or local port if that fails.
**  The syntax for the imtdev argument is as follows:
**
**      	<domain> : <address>
**
**  where <domain> is one of "inet" (internet tcp/ip socket), "unix" (unix
**  domain socket) or "fifo" (named pipe).  The form of the address depends
**  upon the domain, as illustrated in the examples below.
**
**  inet:6200 (or '6200')       Server connection to port 6200 on the local
**                              host.  For a client, a connection to the
**                              given port on the local host.
**
**  inet:6200:foo.bar.edu       Client connection to port 6200 on internet
**                              host foo.bar.edu.  The dotted form of address
**                              may also be used.
*/

static int voc_openVOCServer (char *dev)
{
    int	 fd;
    unsigned short host_port;
    unsigned long  host_addr;
    struct   sockaddr_in sockaddr;


    if (dev == (char *) NULL)
	dev = DEF_SERVER;

    if (voc_parseDev (dev, &host_port, &host_addr) == ERR) {
	if (!vo->quiet)
            fprintf (stderr, "Cannot parse device: '%s'.\n", dev);
    	return ((int ) VOC_NULL);

    } else {
        /* Get a socket. 
	 */
        if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
            if (!vo->quiet)
                fprintf (stderr, "Cannot create inet socket on '%s'.\n", dev);
    	    return ((int ) VOC_NULL);
	}

        /* Compose a network address. 
	 */
        memset ((char *)&sockaddr, 0, sizeof(sockaddr));
        sockaddr.sin_family = AF_INET;
        sockaddr.sin_port = host_port;		/* htons() done above */
        bcopy ((char *)&host_addr,(char *)&sockaddr.sin_addr,sizeof(host_addr));

        /* Connect to the server. 
	 */
        if (connect(fd,(struct sockaddr *)&sockaddr,sizeof(sockaddr)) < 0) {
            close (fd);
            /*
            if (!vo->quiet)
                fprintf (stderr, "Cannot open server connection on '%s'.\n",
		    dev);
            */
    	    return ((int ) VOC_NULL);
	}
    }
    if (VOC_DEBUG) fprintf (stderr, "Connection established on '%s'\n", dev);

    return (fd);
}
	


/******************************************************************************
**  VOC_PARSEDEV -- Parse the VOClient Server device string to extract the host
**  and port number information.
*/

static int 
voc_parseDev (char *dev, unsigned short *hport, unsigned long *host_addr)
{
    char   *ip, osfn[128], buf[128], host_str[128];
    unsigned short port;
    register int i;
    struct hostent *hp;


    if (dev == NULL)
	return ERR;

    memset (osfn, 0, 128);
    memset (host_str, 0, 128);

    /* Expand any %d fields in the network address to the UID. */
    sprintf (osfn, (char *)dev, getuid(), getuid());
 
    /* Form is a port number with an optional node name.  First extract
     * the port number.
     */
    if (isdigit (osfn[0]))
        ip = osfn;
    else if (strncmp (osfn, "inet:", 5) == 0)
        ip = osfn + 5;

    /* Get the port number. */
    for (i=0; isdigit (*ip); i++, ip++)
        buf[i] = (char) *ip;

    port = atoi (buf);		/* set port number	*/
    *hport = htons (port);

    /* Get host address.  This may be specified either has a host
     * name or as an Internet address in dot notation.  If no host
     * name is specified default to the local host.
     */
    if (*ip++ == ':') {
	strcpy (host_str, ip);
    } else
        sprintf (host_str, "%s", "127.0.0.1");

    if (isdigit (host_str[0])) {
        *host_addr = inet_addr (host_str);
        if ((int)*host_addr == -1)
            return ERR;

    } else if ((hp = gethostbyname (host_str))) {
        bcopy (hp->h_addr, (char *)host_addr, sizeof(*host_addr));

    } else
        return ERR;

    if (VOC_DEBUG)
	fprintf (stderr, "parseDev:  '%s' --> port=%d  node='%s'\n",
	    dev, port, host_str);

    return OK;
}


/******************************************************************************
**  VOC_INITOPTS -- Initialize the VOClient configuration options.
*/
static vocOpt *
voc_initOpts ()
{
    /* Use defaults if we didn't get any config options.
    */
    if (vopt == (vocOpt *)NULL)
	vopt = (vocOpt *) calloc (1, sizeof (vocOpt));

    strcpy (vopt->server, DEF_SERVER);
    strcpy (vopt->proxy,  DEF_NET_SERVER);
    strcpy (vopt->runid,  DEF_RUNID);
    vopt->port      =  DEF_VOCSERVER_PORT;
    vopt->spawn     =  TRUE;
    vopt->quiet     =  TRUE;
    vopt->use_cache =  TRUE;
    vopt->use_runid =  TRUE;
    vopt->console   =  FALSE;
    vopt->onetrip   =  FALSE;

    return (vopt);
}



/******************************************************************************
**  VOC_PARSEOPTS -- Parse the VOClient Server option string.  Options are
**  encoded as a comma-delimited list of options that include:
**
**      spawn           If set, will attempt to spawn a local instance of the
**                      voclient daemon if not already running.
**
**      port            Default port to use for a local voclient daemon.
**
**      console         Create a GUI console window for the local daemon?
**
**      quiet           Client API shouldn't produce any error output, it
**       		will be handled by the application.
**
**      runid           Set the RUNID logging string
**
**      use_cache       Use the object/registry cache?
**
**      use_runid       Use the RUNID parameter?
**
**      server          VOClient daemon server address.  The address is of
**                      the form
**                                   <port> ':' <host>
**
**                      where <host> can be a local machine name, a fully-
**                      qualified domain name, or an IP address.  <port> is
**                      the socket number on which the 'voclientd' is running
**                      on that machine.  A server will be started if the
**                      'use_local' flag is set and <host> is 'localhost' or
**                      the current machine name, the <port> specified here
**                      will override the 'port' setting
**
**  Keyword names may be abbreviated to the smallest unique word.
** 
**  An example opt string would be
** 
** 		server=8081:localhost,spawn=yes,console=yes,runid=test
** 
**  to spawn a voclientd running on the local machine at port 8081 with a
**  GUI console panel.
*/

#define	SZ_OPT_BUF	64


static vocOpt *  
voc_parseOpts (char *opts)
{
    register int i;
    char *ip, keyw[SZ_OPT_BUF], val[SZ_OPT_BUF];
    char  lserver[SZ_OPT_BUF];
    char  lproxy[SZ_OPT_BUF];
    char  lconfig[SZ_OPT_BUF];
    char  lpath[SZ_OPT_BUF];
    char  lrunid[SZ_OPT_BUF];
    int   lport      = DEF_VOCSERVER_PORT;
    int   lspawn     = TRUE;
    int   lcons      = FALSE;
    int   lone       = FALSE;
    int   lquiet     = TRUE;
    int   luse_cache = TRUE;
    int   luse_runid = TRUE;
    int   len;

    vocOpt *vopt  = (vocOpt *) NULL;


    memset (lserver, 0, SZ_OPT_BUF);	strcpy (lserver, DEF_SERVER);
    memset (lproxy, 0, SZ_OPT_BUF);	strcpy (lproxy, DEF_NET_SERVER);
    memset (lconfig, 0, SZ_OPT_BUF);
    memset (lpath, 0, SZ_OPT_BUF);

    /* Loop through the option string, setting values.
    */
    ip = opts;
    while (*ip) {
	memset (keyw, 0, SZ_OPT_BUF);
	memset (val, 0, SZ_OPT_BUF);

	for (i=0; *ip && *ip != '=' && i < SZ_OPT_BUF; ) 
	    keyw[i++] = tolower (*ip++);

 	if (*ip != '=') {
	    /* The presence of a keyword implies it's turned on.
	    */
	    val[0] = '1';
	} else {
	    ip++;
	    for (i=0; *ip && *ip != ',' && i < SZ_OPT_BUF; ) 
	        val[i++] = tolower (*ip++);
	}
	len = strlen (val);


	/*  Ugly but effective parsing.
	*/
	if (strncmp ("config", keyw, 4) == 0)
	    strcpy (lconfig, val);

	else if (strncmp ("console", keyw, 4) == 0)
	    lcons = (val[0] == 'y' || val[0] == '1');

	else if (strncmp ("onetrip", keyw, 2) == 0)
	    lone = (val[0] == 'y' || val[0] == '1');

	else if (strncmp ("port", keyw, 2) == 0)
	    lport = atoi (val);

	else if (strncmp ("proxy", keyw, 2) == 0)
	    strcpy (lproxy, val);

	else if (strncmp ("runid", keyw, 3) == 0)
	    strcpy (lrunid, val);

	/*
	else if (strncmp ("voc_path", keyw, 8) == 0)
	    strcpy (lpath, val);
            if (val[len-1] != '/')
                strcat (lpath, "/");
	*/

	else if (strncmp ("spawn", keyw, 2) == 0)
	    lspawn = (val[0] == 'y' || val[0] == '1');

	else if (strncmp ("use_cache", keyw, 5) == 0)
	    luse_cache = (val[0] == 'y' || val[0] == '1');

	else if (strncmp ("use_runid", keyw, 5) == 0)
	    luse_runid = (val[0] == 'y' || val[0] == '1');

	else if (strncmp ("server", keyw, 2) == 0)
	    strcpy (lserver, val);

	else if (strncmp ("quiet", keyw, 2) == 0)
	    lquiet = atoi (val);

	else if (!lquiet)
	    fprintf (stderr, "parseOpt: invalid option '%s'='%s'\n", keyw, val);

	/*  Skip to next option or break.
	*/
	if (*ip && *ip == ',')
	    ip++;
    }


    /* Save the values and return.
    */
    vopt = calloc (1, sizeof (vocOpt));
    strcpy (vopt->server, lserver);
    strcpy (vopt->proxy, lproxy);
    strcpy (vopt->config, lconfig);
    strcpy (vopt->path, lpath);
    strcpy (vopt->runid, lrunid);
    vopt->onetrip   = lone;
    vopt->port      = lport;
    vopt->spawn     = lspawn;
    vopt->quiet     = lquiet;
    vopt->use_cache = luse_cache;
    vopt->use_runid = luse_runid;
    vopt->console   = lcons;

    return (vopt);
}


static vocOpt *
voc_readConfig (char *config)
{
    return ((vocOpt *)NULL);
}



/******************************************************************************
**  Cache Utility procedures.
*/

char *
voc_getCacheDir (char *subdir)
{
    char *home, cache[SZ_FNAME], fullpath[SZ_FNAME], *path;
    DIR  *dir;


    if ((home = getenv ("HOME")) ) {
        memset (cache, 0, SZ_FNAME);
        sprintf (cache, "%s/.voclient/cache", home);
        sprintf (fullpath, "%s/%s", cache, subdir);
    } else
	return ((char *) NULL);


    /* Create the cache dir if it doesn't already exist.
    */
    if (!(dir = opendir (fullpath)) )
        path = voc_cacheCreate (home, cache, subdir);
    else {
	closedir (dir);
	path = calloc (1, strlen (fullpath)+2);
	strcpy (path, fullpath);
    }

    if ((dir = opendir (path)) ) {
	closedir (dir);
        return (path);
    } else 
	return ((char *)NULL);
}


/*  Create any needed cache directories.
*/
static char *
voc_cacheCreate (char *home, char *cache, char *subdir)
{
    char  *path, fname[SZ_FNAME];
    DIR   *dir;


    /* Be sure we can open the HOME dir. */
    if ((dir = opendir (home)) ) {
        closedir (dir);
        sprintf (fname, "%s/.voclient/", home);

        /* Check for access to the $HOME/.voclient dir, if it doesn't exist
        ** then create it.
        */
        if (!(dir = opendir (fname)) ) {
            mkdir (fname, (mode_t) 0777);
            sprintf (fname, "%s/.voclient/cache", home);
            mkdir (fname, (mode_t) 0777);       /* create cache dir     */

        } else {
            /* The $HOME/.voclient exists, now check for a cache dir.
            */
            sprintf (fname, "%s/.voclient/cache", home);
            if (!(dir = opendir (fname)) )
                mkdir (fname, (mode_t) 0777);
        }
        if (dir)
            closedir (dir);

	/* We now have the cache dir, see what's to be done about a subdir.
	*/
	if (subdir) {
	    path = calloc (1, SZ_FNAME);

            sprintf (path, "%s/%s", cache, subdir);
            if (!(dir = opendir (path)) )
                mkdir (path, (mode_t) 0777);
	    else
                closedir (dir);
    	    return (path);

	} else
    	    return (strdup (cache));

    }

    return ((char *) NULL);
}


int voc_dbg (void) { static int count = 0; count++; return (count); }