1/*********************************************************************
2 *   Copyright 1993, UCAR/Unidata
3 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 *********************************************************************/
5
6#include "config.h"
7
8#ifdef HAVE_SYS_TIME_H
9#include <sys/time.h>
10#endif
11
12#include "oc.h"
13extern int oc_dumpnode(OClinkOCddsnode);
14
15#include "ncdap.h"
16#include "dapalign.h"
17
18#define LBRACKET '['
19#define RBRACKET ']'
20
21
22static char* repairname(const char* name, const char* badchars);
23
24/**************************************************/
25/**
26 * Provide a hidden interface to allow utilities
27 * to check if a given path name is really an ncdap3 url.
28 * If no, return null, else return basename of the url
29 * minus any extension.
30 */
31
32int
33nc__testurl(const char* path, char** basenamep)
34{
35    NCURIuri;
36    int ok = ncuriparse(path,&uri);
37    if(ok) {
38 char* slash = (uri->file == NULL ? NULL : strrchr(uri->file, '/'));
39 char* dot;
40 if(slash == NULLslash = (char*)path; else slash++;
41    slash = nulldup(slash);
42
43    if(slash == NULL)
44      dot = NULL;
45    else
46      dot = strrchr(slash, '.');
47
48    if(dot != NULL &&  dot != slash) *dot = '\0';
49
50 if(basenamep)
51      *basenamep=slash;
52    else  {
53      if(slash)
54        free(slash);
55    }
56    ncurifree(uri);
57    }
58    return ok;
59}
60
61/**************************************************/
62
63/*
64Given a legal dap name with arbitrary characters,
65convert to equivalent legal cdf name.
66Currently, the only change is to convert '/'
67names to %2f.
68*/
69
70char*
71cdflegalname(char* name)
72{
73    return repairname(name,"/");
74}
75
76/* Define the type conversion of the DAP variables
77   to the external netCDF variable type.
78   The proper way is to, for example, convert unsigned short
79   to an int to maintain the values.
80   Unfortuneately, libnc-dap does not do this:
81   it translates the types directly. For example
82   libnc-dap upgrades the DAP byte type, which is unsigned char,
83   to NC_BYTE, which signed char.
84   Oh well.
85   For netcdf-4, we can do proper type conversion.
86*/
87nc_type
88nctypeconvert(NCDAPCOMMONdrnonc_type nctype)
89{
90    nc_type upgrade = NC_NAT;
91    if(drno->controls.flags & NCF_NC3) {
92 /* libnc-dap mimic invariant is to maintain type size */
93 switch (nctype) {
94 case NC_CHAR:    upgrade = NC_CHAR; break;
95 case NC_BYTE:    upgrade = NC_BYTE; break;
96 case NC_UBYTE:   upgrade = NC_BYTE; break;
97 case NC_SHORT:   upgrade = NC_SHORT; break;
98 case NC_USHORT:  upgrade = NC_SHORT; break;
99 case NC_INT:     upgrade = NC_INT; break;
100 case NC_UINT:    upgrade = NC_INT; break;
101 case NC_INT64:   upgrade = NC_INT64; break;
102 case NC_UINT64:  upgrade = NC_UINT64; break;
103 case NC_FLOAT:   upgrade = NC_FLOAT; break;
104 case NC_DOUBLE:  upgrade = NC_DOUBLE; break;
105 case NC_URL:
106 case NC_STRING:  upgrade = NC_CHAR; break;
107 default: break;
108 }
109    } else if(drno->controls.flags & NCF_NC4) {
110 /* netcdf-4 conversion is more correct */
111 switch (nctype) {
112 case NC_CHAR:    upgrade = NC_CHAR; break;
113 case NC_BYTE:    upgrade = NC_BYTE; break;
114 case NC_UBYTE:   upgrade = NC_UBYTE; break;
115 case NC_SHORT:   upgrade = NC_SHORT; break;
116 case NC_USHORT:  upgrade = NC_USHORT; break;
117 case NC_INT:     upgrade = NC_INT; break;
118 case NC_UINT:    upgrade = NC_UINT; break;
119 case NC_INT64:   upgrade = NC_INT64; break;
120 case NC_UINT64:  upgrade = NC_UINT64; break;
121 case NC_FLOAT:   upgrade = NC_FLOAT; break;
122 case NC_DOUBLE:  upgrade = NC_DOUBLE; break;
123 case NC_URL:
124 case NC_STRING:  upgrade = NC_STRING; break;
125 default: break;
126 }
127    }
128    return upgrade;
129}
130
131nc_type
132octypetonc(OCtype etype)
133{
134    switch (etype) {
135    case OC_Char: return NC_CHAR;
136    case OC_Byte: return NC_UBYTE;
137    case OC_UByte: return NC_UBYTE;
138    case OC_Int16: return NC_SHORT;
139    case OC_UInt16: return NC_USHORT;
140    case OC_Int32: return NC_INT;
141    case OC_UInt32: return NC_UINT;
142    case OC_Int64: return NC_INT64;
143    case OC_UInt64: return NC_UINT64;
144    case OC_Float32: return NC_FLOAT;
145    case OC_Float64: return NC_DOUBLE;
146    case OC_String: return NC_STRING;
147    case OC_URL: return NC_STRING;
148    case OC_Dataset: return NC_Dataset;
149    case OC_Sequence: return NC_Sequence;
150    case OC_Structure: return NC_Structure;
151    case OC_Grid: return NC_Grid;
152    case OC_Dimension: return NC_Dimension;
153    case OC_Atomic: return NC_Atomic;
154    default: break;
155    }
156    return NC_NAT;
157}
158
159OCtype
160nctypetodap(nc_type nctype)
161{
162    switch (nctype) {
163    case NC_CHAR: return OC_Char;
164    case NC_BYTE: return OC_Byte;
165    case NC_UBYTE: return OC_UByte;
166    case NC_SHORT: return OC_Int16;
167    case NC_USHORT: return OC_UInt16;
168    case NC_INT: return OC_Int32;
169    case NC_UINT: return OC_UInt32;
170    case NC_INT64: return OC_Int64;
171    case NC_UINT64: return OC_UInt64;
172    case NC_FLOAT: return OC_Float32;
173    case NC_DOUBLE: return OC_Float64;
174    case NC_STRING: return OC_String;
175    default : break;
176    }
177    return OC_NAT;
178}
179
180size_t
181nctypesizeof(nc_type nctype)
182{
183    switch (nctype) {
184    case NC_CHAR: return sizeof(char);
185    case NC_BYTE: return sizeof(signed char);
186    case NC_UBYTE: return sizeof(unsigned char);
187    case NC_SHORT: return sizeof(short);
188    case NC_USHORT: return sizeof(unsigned short);
189    case NC_INT: return sizeof(int);
190    case NC_UINT: return sizeof(unsigned int);
191    case NC_INT64: return sizeof(long long);
192    case NC_UINT64: return sizeof(unsigned long long);
193    case NC_FLOAT: return sizeof(float);
194    case NC_DOUBLE: return sizeof(double);
195    case NC_STRING: return sizeof(char*);
196    default: PANIC("nctypesizeof");
197    }
198    return 0;
199}
200
201char*
202nctypetostring(nc_type nctype)
203{
204    switch (nctype) {
205    case NC_NAT: return "NC_NAT";
206    case NC_BYTE: return "NC_BYTE";
207    case NC_CHAR: return "NC_CHAR";
208    case NC_SHORT: return "NC_SHORT";
209    case NC_INT: return "NC_INT";
210    case NC_FLOAT: return "NC_FLOAT";
211    case NC_DOUBLE: return "NC_DOUBLE";
212    case NC_UBYTE: return "NC_UBYTE";
213    case NC_USHORT: return "NC_USHORT";
214    case NC_UINT: return "NC_UINT";
215    case NC_INT64: return "NC_INT64";
216    case NC_UINT64: return "NC_UINT64";
217    case NC_STRING: return "NC_STRING";
218    case NC_VLEN: return "NC_VLEN";
219    case NC_OPAQUE: return "NC_OPAQUE";
220    case NC_ENUM: return "NC_ENUM";
221    case NC_COMPOUND: return "NC_COMPOUND";
222    case NC_URL: return "NC_URL";
223    case NC_SET: return "NC_SET";
224    case NC_Dataset: return "NC_Dataset";
225    case NC_Sequence: return "NC_Sequence";
226    case NC_Structure: return "NC_Structure";
227    case NC_Grid: return "NC_Grid";
228    case NC_Dimension: return "NC_Dimension";
229    case NC_Atomic: return "NC_Atomic";
230    default: break;
231    }
232    return NULL;
233}
234
235/* Pad a buffer */
236int
237dapalignbuffer(NCbytesbuf, int alignment)
238{
239    int pad;
240    unsigned long len;
241    if(buf == NULL) return 0;
242    len = ncbyteslength(buf);
243    pad = nccpadding(len,alignment);
244
245#ifdef TEST
246    for(;pad > 0;pad--)
247        ncbytesappend(buf,0x3a); /* 0x3a was chosen at random */
248#else
249    ncbytessetlength(buf,len+pad);
250#endif
251    return 1;
252}
253
254size_t
255dapdimproduct(NClistdimensions)
256{
257    size_t size = 1;
258    unsigned int i;
259    if(dimensions == NULL) return size;
260    for(i=0;i<nclistlength(dimensions);i++) {
261 CDFnodedim = (CDFnode*)nclistget(dimensions,i);
262 size *= dim->dim.declsize;
263    }
264    return size;
265}
266
267
268/* Return value of param or NULL if not found */
269const char*
270dapparamvalue(NCDAPCOMMONnccomm, const char* key)
271{
272    const char* value;
273
274    if(nccomm == NULL || key == NULL) return 0;
275    if(!ncurilookup(nccomm->oc.url,key,&value))
276 return NULL;
277    return value;
278}
279
280static const char* checkseps = "+,:;";
281
282/* Search for substring in value of param. If substring == NULL; then just
283   check if param is defined.
284*/
285int
286dapparamcheck(NCDAPCOMMONnccomm, const char* key, const char* subkey)
287{
288    const char* value;
289    char* p;
290
291    if(nccomm == NULL || key == NULL) return 0;
292    if(!ncurilookup(nccomm->oc.url,key,&value))
293 return 0;
294    if(subkey == NULL) return 1;
295    p = strstr(value,subkey);
296    if(p == NULL) return 0;
297    p += strlen(subkey);
298    if(*p != '\0' && strchr(checkseps,*p) == NULL) return 0;
299    return 1;
300}
301
302
303/* This is NOT UNION */
304int
305nclistconcat(NClistl1NClistl2)
306{
307    unsigned int i;
308    for(i=0;i<nclistlength(l2);i++) nclistpush(l1,nclistget(l2,i));
309    return 1;
310}
311
312int
313nclistminus(NClistl1NClistl2)
314{
315    unsigned int i,len,found;
316    len = nclistlength(l2);
317    found = 0;
318    for(i=0;i<len;i++) {
319 if(nclistdeleteall(l1,nclistget(l2,i))) found = 1;
320    }
321    return found;
322}
323
324int
325nclistdeleteall(NClistl, void* elem)
326{
327    int i; /* do not make unsigned */
328    unsigned int len,found;
329    found = 0;
330    len = nclistlength(l);
331    for(i=len-1;i>=0;i--) {
332 void* test = nclistget(l,i);
333 if(test==elem) {
334     nclistremove(l,i);
335            found=1;
336        }
337    }
338    return found;
339}
340
341/* Collect the set of container nodes ending in "container"*/
342void
343collectnodepath(CDFnodenodeNClistpath, int withdataset)
344{
345    if(node == NULL) return;
346    nclistpush(path,(void*)node);
347    while(node->container != NULL) {
348 node = node->container;
349 if(!withdataset && node->nctype == NC_Dataset) break;
350 nclistinsert(path,0,(void*)node);
351    }
352}
353
354/* Like collectnodepath3, but in ocspace */
355void
356collectocpath(OClink connOCddsnode nodeNClistpath)
357{
358    OCddsnode container;
359    OCtype octype;
360    if(node == NULL) return;
361    oc_dds_class(conn,node,&octype);
362    if(octype != OC_Dataset) {
363        oc_dds_container(conn,node,&container);
364        if(container != NULL)
365            collectocpath(conn,container,path);
366    }
367    nclistpush(path,(void*)node);
368}
369
370char*
371makeocpathstring(OClink connOCddsnode node, const char* sep)
372{
373    int i,len,first;
374    char* result;
375    char* name;
376    OCtype octype;
377    NClistocpath = NULL;
378    NCbytespathname = NULL;
379
380    /* If we are asking for the dataset path only,
381       then nclude it, otherwise elide it
382    */
383    oc_dds_type(conn,node,&octype);
384    if(octype == OC_Dataset) {
385        oc_dds_name(conn,node,&name);
386 return nulldup(name);
387    }
388
389    ocpath = nclistnew();
390    collectocpath(conn,node,ocpath);
391    len = nclistlength(ocpath);
392    assert(len > 0); /* dataset at least */
393
394    pathname = ncbytesnew();
395    for(first=1,i=1;i<len;i++) { /* start at 1 to skip dataset name */
396 OCddsnode node = (OCddsnode)nclistget(ocpath,i);
397 char* name;
398        oc_dds_type(conn,node,&octype);
399        oc_dds_name(conn,node,&name);
400 if(!firstncbytescat(pathname,sep);
401 ncbytescat(pathname,name);
402 nullfree(name);
403 first = 0;
404    }
405    result = ncbytesextract(pathname);
406    ncbytesfree(pathname);
407    nclistfree(ocpath);
408    return result;
409}
410
411char*
412makepathstring(NClistpath, const char* separator, int flags)
413{
414    int i,len,first;
415    NCbytespathname = NULL;
416    char* result;
417    CDFnodenode;
418
419    len = nclistlength(path);
420    ASSERT(len > 0); /* dataset at least */
421
422    if(len == 1) {/* dataset only */
423        node = (CDFnode*)nclistget(path,0);
424 return nulldup(node->ncbasename);
425    }
426
427    pathname = ncbytesnew();
428    for(first=1,i=0;i<len;i++) {
429 CDFnodenode = (CDFnode*)nclistget(path,i);
430 char* name;
431 if(!node->elided || (flags & PATHELIDE)==0) {
432         if(node->nctype != NC_Dataset) {
433                name = node->ncbasename;
434 assert(name != NULL);
435         if(!firstncbytescat(pathname,separator);
436                ncbytescat(pathname,name);
437         first = 0;
438     }
439 }
440    }
441    result = ncbytesextract(pathname);
442    ncbytesfree(pathname);
443    return result;
444}
445
446/* convert path to string using the ncname field */
447char*
448makecdfpathstring(CDFnodevar, const char* separator)
449{
450    char* spath;
451    NClistpath = nclistnew();
452    collectnodepath(var,path,WITHDATASET); /* <= note */
453    spath = makepathstring(path,separator,PATHNC);
454    nclistfree(path);
455    return spath;
456}
457
458/* Collect the set names of container nodes ending in "container"*/
459void
460clonenodenamepath(CDFnodenodeNClistpath, int withdataset)
461{
462    if(node == NULL) return;
463    /* stop at the dataset container as well*/
464    if(node->nctype != NC_Dataset)
465        clonenodenamepath(node->container,path,withdataset);
466    if(node->nctype != NC_Dataset || withdataset)
467        nclistpush(path,(void*)nulldup(node->ncbasename));
468}
469
470char*
471simplepathstring(NClistnames,  char* separator)
472{
473    int i;
474    size_t len;
475    char* result;
476    if(nclistlength(names) == 0) return nulldup("");
477    for(len=0,i=0;i<nclistlength(names);i++) {
478 char* name = (char*)nclistget(names,i);
479 len += strlen(name);
480 len += strlen(separator);
481    }
482    len++; /* null terminator */
483    result = (char*)malloc(len);
484    result[0] = '\0';
485    for(i=0;i<nclistlength(names);i++) {
486 char* segment = (char*)nclistget(names,i);
487 if(i > 0) strcat(result,separator);
488 strcat(result,segment);
489    }
490    return result;
491}
492
493/* Define a number of location tests */
494
495/* Is node contained (transitively) in a sequence ? */
496BOOL
497dapinsequence(CDFnodenode)
498{
499    if(node == NULL || node->container == NULL) return TRUE;
500    for(node=node->container;node->nctype != NC_Dataset;node=node->container) {
501       if(node->nctype == NC_Sequence) return TRUE;
502    }
503    return FALSE;
504}
505
506/* Is node contained (transitively) in a structure array */
507BOOL
508dapinstructarray(CDFnodenode)
509{
510    if(node == NULL) return TRUE;
511    for(node=node->container;node->nctype != NC_Dataset;node=node->container) {
512       if(node->nctype == NC_Structure
513   && nclistlength(node->array.dimset0) > 0)
514     return TRUE;
515    }
516    return FALSE;
517}
518
519/* Is node a map field of a grid? */
520BOOL
521dapgridmap(CDFnodenode)
522{
523    if(node != NULL && node->container != NULL
524       && node->container->nctype == NC_Grid) {
525 CDFnodearray = (CDFnode*)nclistget(node->container->subnodes,0);
526 return (node != array);
527    }
528    return FALSE;
529}
530
531/* Is node an array field of a grid? */
532BOOL
533dapgridarray(CDFnodenode)
534{
535    if(node != NULL && node->container != NULL
536       && node->container->nctype == NC_Grid) {
537 CDFnodearray = (CDFnode*)nclistget(node->container->subnodes,0);
538 return (node == array);
539    }
540    return FALSE;
541}
542
543BOOL
544dapgridelement(CDFnodenode)
545{
546    return dapgridarray(node)
547           || dapgridmap(node);
548}
549
550/* Is node a top-level grid node? */
551BOOL
552daptopgrid(CDFnodegrid)
553{
554    if(grid == NULL || grid->nctype != NC_Grid) return FALSE;
555    return daptoplevel(grid);
556}
557
558/* Is node a top-level sequence node? */
559BOOL
560daptopseq(CDFnodeseq)
561{
562    if(seq == NULL || seq->nctype != NC_Sequence) return FALSE;
563    return daptoplevel(seq);
564}
565
566/* Is node a top-level node? */
567BOOL
568daptoplevel(CDFnodenode)
569{
570    if(node->container == NULL
571       || node->container->nctype != NC_Dataset) return FALSE;
572    return TRUE;
573}
574
575unsigned int
576modeldecode(int translation, const char* smodel,
577            const struct NCTMODELmodels,
578            unsigned int dfalt)
579{
580    for(;models->translation;models++) {
581 if(translation != models->translation) continue;
582 if(smodel == models->model
583    || (models->model != NULL && strcasecmp(smodel,models->model)==0)) {
584     /* We have a match */
585            return models->flags;
586 }
587    }
588    return dfalt;
589}
590
591unsigned long
592getlimitnumber(const char* limit)
593{
594    size_t slen;
595    unsigned long multiplier = 1;
596    unsigned long lu;
597
598    if(limit == NULL) return 0;
599    slen = strlen(limit);
600    if(slen == 0) return 0;
601    switch (limit[slen-1]) {
602    case 'G': case 'g': multiplier = GIGBYTE; break;
603    case 'M': case 'm': multiplier = MEGBYTE; break;
604    case 'K': case 'k': multiplier = KILOBYTE; break;
605    default: break;
606    }
607    sscanf(limit,"%lu",&lu);
608    return (lu*multiplier);
609}
610
611void
612dapexpandescapes(char *termstring)
613{
614    char *s, *t, *endp;
615
616    /* expand "\" escapes, e.g. "\t" to tab character;
617       will only shorten string length, never increase it
618    */
619    s = termstring;
620    t = termstring;
621    while(*t) {
622 if (*t == '\\') {
623     t++;
624     switch (*t) {
625       case 'a':
626 *s++ = '\007'; t++; /* will use '\a' when STDC */
627 break;
628       case 'b':
629 *s++ = '\b'; t++;
630 break;
631       case 'f':
632 *s++ = '\f'; t++;
633 break;
634       case 'n':
635 *s++ = '\n'; t++;
636 break;
637       case 'r':
638 *s++ = '\r'; t++;
639 break;
640       case 't':
641 *s++ = '\t'; t++;
642 break;
643       case 'v':
644 *s++ = '\v'; t++;
645 break;
646       case '\\':
647 *s++ = '\\'; t++;
648 break;
649       case '?':
650 *s++ = '\177'; t++;
651 break;
652       case 'x':
653 t++; /* now t points to one or more hex digits */
654 *s++ = (char) strtol(t, &endp, 16);
655 t = endp;
656 break;
657       case '0':
658       case '1':
659       case '2':
660       case '3':
661       case '4':
662       case '5':
663       case '6':
664       case '7': {
665 /* t should now point to 3 octal digits */
666 int c;
667 c = t[0];
668 if(c == 0 || c < '0' || c > '7') goto normal;
669 c = t[1];
670 if(c == 0 || c < '0' || c > '7') goto normal;
671 c = t[2];
672 if(c == 0 || c < '0' || c > '7') goto normal;
673 c = ((t[0]-'0')<<6)+((t[1]-'0')<<3)+(t[2]-'0');
674 *s++ = (char)c;
675 t += 3;
676 } break;
677       default:
678 if(*t == 0)
679     *s++ = '\\';
680 else
681     *s++ = *t++;
682 break;
683     }
684 } else {
685normal:     *s++ = *t++;
686 }
687    }
688    *s = '\0';
689    return;
690}
691
692#ifdef HAVE_GETTIMEOFDAY
693static struct timeval time0;
694static struct timeval time1;
695
696static double
697deltatime()
698{
699    double t0t1;
700    t0 = ((double)time0.tv_sec);
701    t0 += ((double)time0.tv_usec) / 1000000.0;
702    t1 = ((double)time1.tv_sec);
703    t1 += ((double)time1.tv_usec) / 1000000.0;
704    return (t1 - t0);
705}
706#endif
707
708/* Provide a wrapper for oc_fetch so we can log what it does */
709NCerror
710dap_fetch(NCDAPCOMMONnccommOClink conn, const char* ce,
711             OCdxd dxdOCddsnoderootp)
712{
713    NCerror ncstat = NC_NOERR;
714    OCerror ocstat = OC_NOERR;
715    char* ext = NULL;
716    OCflags flags = 0;
717    int httpcode = 0;
718
719    if(dxd == OCDDSext = ".dds";
720    else if(dxd == OCDASext = ".das";
721    else ext = ".dods";
722
723    if(ce != NULL && strlen(ce) == 0)
724 ce = NULL;
725
726    if(FLAGSET(nccomm->controls,NCF_UNCONSTRAINABLE)) {
727 ce = NULL;
728    }
729
730    if(FLAGSET(nccomm->controls,NCF_ONDISK)) {
731 flags |= OCONDISK;
732    }
733
734    if(SHOWFETCH) {
735 /* Build uri string minus the constraint and #tag */
736 char* baseurl = ncuribuild(nccomm->oc.url,NULL,ext,0);
737 if(ce == NULL)
738            LOG1(NCLOGNOTE,"fetch: %s",baseurl);
739 else
740            LOG2(NCLOGNOTE,"fetch: %s?%s",baseurl,ce);
741 nullfree(baseurl);
742#ifdef HAVE_GETTIMEOFDAY
743 gettimeofday(&time0,NULL);
744#endif
745    }
746    ocstat = oc_fetch(conn,ce,dxd,flags,rootp);
747    if(FLAGSET(nccomm->controls,NCF_SHOWFETCH)) {
748#ifdef HAVE_GETTIMEOFDAY
749        double secs;
750 gettimeofday(&time1,NULL);
751 secs = deltatime();
752 nclog(NCLOGNOTE,"fetch complete: %0.3f secs",secs);
753#else
754 nclog(NCLOGNOTE,"fetch complete.");
755#endif
756    }
757#ifdef DEBUG2
758fprintf(stderr,"fetch: dds:\n");
759oc_dumpnode(conn,*rootp);
760#endif
761
762    /* Look at the HTTP return code */
763    httpcode = oc_httpcode(conn);
764    if(httpcode < 400) {
765        ncstat = ocerrtoncerr(ocstat);
766    } else if(httpcode >= 500) {
767        ncstat = NC_EDAPSVC;
768    } else if(httpcode == 401) {
769 ncstat = NC_EAUTH;
770    } else if(httpcode == 404) {
771 ncstat = NC_ENOTFOUND;
772    } else {
773 ncstat = NC_EACCESS;
774    }
775    return ncstat;
776}
777
778/* Check a name to see if it contains illegal dap characters
779*/
780
781static char* baddapchars = "./";
782
783int
784dap_badname(char* name)
785{
786    char* p;
787    if(name == NULL) return 0;
788    for(p=baddapchars;*p;p++) {
789        if(strchr(name,*p) != NULL)
790     return 1;
791    }
792    return 0;
793}
794
795/* Repair a dap name */
796char*
797dap_repairname(char* name)
798{
799    /* assume that dap_badname was called on this name and returned 1 */
800    return repairname(name,baddapchars);
801}
802
803/* Check a name to see if it contains illegal dap characters
804   and repair them
805*/
806
807static const char* hexdigits = "0123456789abcdef";
808
809static char*
810repairname(const char* name, const char* badchars)
811{
812    char* newname;
813    const char *p;
814    char *q;
815    int c;
816
817    if(name == NULL) return NULL;
818    newname = (char*)malloc(1+(3*strlen(name))); /* max needed */
819    newname[0] = '\0'; /* so we can use strcat */
820    for(p=name,q=newname;(c=*p);p++) {
821        if(strchr(badchars,c) != NULL) {
822     int digit;
823            char newchar[4];
824     newchar[0] = '%';
825            digit = (c & 0xf0) >> 4;
826     newchar[1] = hexdigits[digit];
827            digit = (c & 0x0f);
828     newchar[2] = hexdigits[digit];
829     newchar[3] = '\0';
830            strcat(newname,newchar);
831            q += 3; /*strlen(newchar)*/
832        } else
833            *q++ = c;
834 *q = '\0'; /* so we can always do strcat */
835    }
836    *q = '\0'; /* ensure trailing null */
837    return newname;
838}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed