Coverage Report

Created: 2022-11-18 06:58

/src/netcdf-c/libdispatch/dpathmgr.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2018, University Corporation for Atmospheric Research
3
 * See netcdf/COPYRIGHT file for copying and redistribution conditions.
4
 */
5
6
#include "config.h"
7
#include <stdlib.h>
8
#include <stdio.h>
9
#include <string.h>
10
#include <assert.h>
11
#include <errno.h>
12
#ifdef HAVE_FCNTL_H
13
#include <fcntl.h>
14
#endif
15
#ifdef HAVE_SYS_STAT_H
16
#include <sys/stat.h>
17
#endif
18
#ifdef HAVE_UNISTD_H
19
#include <unistd.h>
20
#endif
21
#ifdef HAVE_DIRENT_H
22
#include <dirent.h>
23
#endif
24
#ifdef _WIN32
25
#include <windows.h>
26
#include <io.h>
27
#include <wchar.h>
28
#include <direct.h>
29
#endif
30
#include <locale.h>
31
#include "netcdf.h"
32
#include "ncpathmgr.h"
33
#include "nclog.h"
34
#include "nclist.h"
35
#include "ncbytes.h"
36
#include "ncuri.h"
37
#include "ncutf8.h"
38
39
#undef DEBUGPATH
40
static int pathdebug = -1;
41
#undef DEBUG
42
43
#ifdef DEBUG
44
#define REPORT(e,msg) report((e),(msg),__LINE__)
45
#else
46
#define REPORT(e,msg)
47
#endif
48
49
#ifdef _WIN32
50
#define access _access
51
#define mkdir _mkdir
52
#define rmdir _rmdir
53
#define getcwd _getcwd
54
55
#if WINVERMAJOR > 10 || (WINVERMAJOR == 10 && WINVERBUILD >= 17134)
56
/* Should be possible to use UTF8 directly */
57
#define WINUTF8
58
#else
59
#undef WINUTF8
60
#endif
61
62
#endif
63
64
/*
65
Code to provide some path conversion code so that
66
paths in one format can be used on a platform that uses
67
a different format.
68
See the documentation in ncpathmgr.h for details.
69
*/
70
71
/* Define legal windows drive letters */
72
static const char* windrive = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/";
73
74
static const char netdrive = '/';
75
76
static const size_t cdlen = 10; /* strlen("/cygdrive/") */
77
78
static int pathinitialized = 0;
79
80
static const char* cygwinspecial[] =
81
    {"/bin/","/dev/","/etc/","/home/",
82
     "/lib/","/proc/","/sbin/","/tmp/",
83
     "/usr/","/var/",NULL};
84
85
static const struct Path {
86
    int kind;
87
    int drive;
88
    char* path;
89
} empty = {NCPD_UNKNOWN,0,NULL};
90
91
/* Keep the working directory kind and drive */
92
static char wdprefix[8192];
93
94
/* The current codepage */
95
#ifdef _WIN32
96
static int acp = -1;
97
#endif
98
99
/* Keep CYGWIN/MSYS2 mount point */
100
static struct MountPoint {
101
    int defined;
102
    char prefix[8192]; /*minus leading drive */
103
    char drive;
104
} mountpoint;
105
106
/* Pick the target kind for testing */
107
static int testkind = 0;
108
109
static int parsepath(const char* inpath, struct Path* path);
110
static int unparsepath(struct Path* p, char** pathp, int target);
111
static int getwdpath(void);
112
static void clearPath(struct Path* path);
113
static void pathinit(void);
114
static int iscygwinspecial(const char* path);
115
static int testurl(const char* path);
116
117
#ifdef WINPATH
118
static int ansi2utf8(const char* local, char** u8p);
119
static int ansi2wide(const char* local, wchar_t** u16p);
120
static int utf82wide(const char* utf8, wchar_t** u16p);
121
static int wide2utf8(const wchar_t* u16, char** u8p);
122
#endif
123
124
/*Forward*/
125
#ifdef DEBUG
126
static void report(int stat, const char* msg, int line);
127
#endif
128
static char* printPATH(struct Path* p);
129
130
EXTERNL
131
char* /* caller frees */
132
NCpathcvt(const char* inpath)
133
0
{
134
0
    int stat = NC_NOERR;
135
0
    char* tmp1 = NULL;
136
0
    char* result = NULL;
137
0
    struct Path inparsed = empty;
138
0
    int target = NCgetlocalpathkind();
139
140
0
    if(inpath == NULL) goto done; /* defensive driving */
141
142
0
    if(!pathinitialized) pathinit();
143
144
0
    if(testurl(inpath)) { /* Pass thru URLs */
145
0
  if((result = strdup(inpath))==NULL) stat = NC_ENOMEM;
146
0
  goto done;
147
0
    }
148
149
0
    if((stat = parsepath(inpath,&inparsed)))
150
0
  {REPORT(stat,"NCpathcvt: parsepath"); goto done;}
151
0
    if(pathdebug > 0)
152
0
        fprintf(stderr,">>> NCpathcvt: inparsed=%s\n",printPATH(&inparsed));
153
154
0
    if((stat = unparsepath(&inparsed,&result,target)))
155
0
        {REPORT(stat,"NCpathcvt: unparsepath"); goto done;}
156
157
0
done:
158
0
    if(pathdebug > 0) {
159
0
        fprintf(stderr,">>> inpath=|%s| result=|%s|\n",
160
0
            inpath?inpath:"NULL",result?result:"NULL");
161
0
        fflush(stderr);
162
0
    }
163
0
    if(stat) {
164
0
        nullfree(result); result = NULL;
165
0
  nclog(NCLOGERR,"NCpathcvt: stat=%d (%s)",
166
0
    stat,nc_strerror(stat));
167
0
    }
168
0
    nullfree(tmp1);
169
0
    clearPath(&inparsed);
170
0
    return result;
171
0
}
172
173
EXTERNL
174
int
175
NCpathcanonical(const char* srcpath, char** canonp)
176
1
{
177
1
    int stat = NC_NOERR;
178
1
    char* canon = NULL;
179
1
    struct Path path = empty;
180
181
1
    if(srcpath == NULL) goto done;
182
183
1
    if(!pathinitialized) pathinit();
184
185
    /* parse the src path */
186
1
    if((stat = parsepath(srcpath,&path))) {goto done;}
187
188
    /* Convert to cygwin form */
189
1
    if((stat = unparsepath(&path,&canon, NCPD_CYGWIN)))
190
0
        goto done;
191
192
1
    if(canonp) {*canonp = canon; canon = NULL;}
193
194
1
done:
195
1
    nullfree(canon);
196
1
    clearPath(&path);
197
1
    return stat;
198
1
}
199
200
EXTERNL
201
char* /* caller frees */
202
NCpathabsolute(const char* relpath)
203
0
{
204
0
    int stat = NC_NOERR;
205
0
    struct Path canon = empty;
206
0
    char* tmp1 = NULL;
207
0
    char* result = NULL;
208
0
    size_t len;
209
210
0
    if(relpath == NULL) goto done; /* defensive driving */
211
212
0
    if(!pathinitialized) pathinit();
213
214
    /* Decompose path */
215
0
    if((stat = parsepath(relpath,&canon)))
216
0
  {REPORT(stat,"pathabs: parsepath"); goto done;}
217
218
    /* See if relative */
219
0
    if(canon.kind == NCPD_REL) {
220
  /* prepend the wd path to the inpath, including drive letter, if any */
221
0
  len = strlen(wdprefix)+strlen(canon.path)+1+1;
222
0
  if((tmp1 = (char*)malloc(len))==NULL)
223
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
224
0
  tmp1[0] = '\0';
225
0
  strlcat(tmp1,wdprefix,len);
226
0
  strlcat(tmp1,"/",len);
227
0
  strlcat(tmp1,canon.path,len);
228
0
        nullfree(canon.path);
229
0
        canon.path = NULL;
230
  /* Reparse */
231
0
  result = NCpathabsolute(tmp1);
232
0
  goto done;
233
0
    }
234
    /* rebuild */
235
0
    if((stat=unparsepath(&canon,&result,NCgetlocalpathkind())))
236
0
  {REPORT(stat,"pathabs: unparsepath"); goto done;}
237
0
done:
238
0
    if(pathdebug > 0) {
239
0
        fprintf(stderr,">>> relpath=|%s| result=|%s|\n",
240
0
            relpath?relpath:"NULL",result?result:"NULL");
241
0
        fflush(stderr);
242
0
    }
243
0
    if(stat) {
244
0
        nullfree(tmp1); tmp1 = NULL;
245
0
  nclog(NCLOGERR,"NCpathcvt: stat=%d (%s)",
246
0
    stat,nc_strerror(stat));
247
0
    }
248
0
    clearPath(&canon);
249
0
    nullfree(tmp1);
250
0
    return result;
251
0
}
252
253
254
/* Testing support */
255
EXTERNL
256
char* /* caller frees */
257
NCpathcvt_test(const char* inpath, int ukind, int udrive)
258
0
{
259
0
    char* result = NULL;
260
0
    struct MountPoint old;
261
262
0
    if(!pathinitialized) pathinit();
263
264
0
    old = mountpoint;
265
0
    memset(&mountpoint,0,sizeof(mountpoint));
266
267
0
    mountpoint.drive = (char)udrive;
268
0
    mountpoint.defined = (mountpoint.drive || nulllen(mountpoint.prefix) > 0);
269
0
    testkind = ukind;
270
0
    result = NCpathcvt(inpath);
271
0
    mountpoint = old;
272
0
    return result;
273
0
}
274
275
static void
276
pathinit(void)
277
1
{
278
1
    if(pathinitialized) return;
279
1
    pathinitialized = 1; /* avoid recursion */
280
281
    /* Check for path debug env vars */
282
1
    if(pathdebug < 0) {
283
1
  const char* s = getenv("NCPATHDEBUG");
284
1
        pathdebug = (s == NULL ? 0 : 1);
285
1
    }
286
1
    (void)getwdpath();
287
288
#ifdef _WIN32
289
    /* Get the current code page */
290
    acp = GetACP();
291
#endif
292
293
1
    memset(&mountpoint,0,sizeof(mountpoint));
294
#ifdef REGEDIT
295
    { /* See if we can get the MSYS2 prefix from the registry */
296
  if(getmountpoint(mountpoint.prefix,sizeof(mountpoint.prefix)))
297
      goto next;
298
  mountpoint.defined = 1;
299
if(pathdebug > 0)
300
  fprintf(stderr,">>>> registry: mountprefix=|%s|\n",mountpoint.prefix);
301
    }
302
next:
303
#endif
304
1
    if(!mountpoint.defined) {
305
1
  mountpoint.prefix[0] = '\0';
306
        /* See if MSYS2_PREFIX is defined */
307
1
        if(getenv("MSYS2_PREFIX")) {
308
0
      const char* m2 = getenv("MSYS2_PREFIX");
309
0
      mountpoint.prefix[0] = '\0';
310
0
            strlcat(mountpoint.prefix,m2,sizeof(mountpoint.prefix));
311
0
  }
312
1
        if(pathdebug > 0) {
313
0
            fprintf(stderr,">>>> prefix: mountprefix=|%s|\n",mountpoint.prefix);
314
0
        }
315
1
    }
316
1
    if(mountpoint.defined) {
317
0
  char* p;
318
0
  size_t size = strlen(mountpoint.prefix);
319
0
        for(p=mountpoint.prefix;*p;p++) {if(*p == '\\') *p = '/';} /* forward slash*/
320
0
  if(mountpoint.prefix[size-1] == '/') {
321
0
      size--;
322
0
      mountpoint.prefix[size] = '\0'; /* no trailing slash */
323
0
  }
324
  /* Finally extract the drive letter, if any */
325
  /* assumes mount prefix is in windows form */
326
0
  mountpoint.drive = 0;
327
0
  if(strchr(windrive,mountpoint.prefix[0]) != NULL
328
0
           && mountpoint.prefix[1] == ':') {
329
0
      char* q = mountpoint.prefix;
330
0
      mountpoint.drive = mountpoint.prefix[0];
331
      /* Shift prefix left 2 chars */
332
0
            for(p=mountpoint.prefix+2;*p;p++) {*q++ = *p;}
333
0
      *q = '\0';
334
0
  }
335
0
    }
336
1
    pathinitialized = 1;
337
1
}
338
339
static void
340
clearPath(struct Path* path)
341
1
{
342
1
    nullfree(path->path);
343
1
    path->path = NULL;
344
1
}
345
346
/* Unfortunately, not all cygwin paths start with /cygdrive.
347
   So see if the path starts with one of the special paths.
348
*/
349
static int
350
iscygwinspecial(const char* path)
351
1
{
352
1
    const char** p;
353
1
    if(path == NULL) return 0;
354
11
    for(p=cygwinspecial;*p;p++) {
355
10
        if(strncmp(*p,path,strlen(*p))==0) return 1;
356
10
    }
357
1
    return 0;
358
1
}
359
360
/* return 1 if path looks like a url; 0 otherwise */
361
static int
362
testurl(const char* path)
363
0
{
364
0
    int isurl = 0;
365
0
    NCURI* tmpurl = NULL;
366
367
0
    if(path == NULL) return 0;
368
369
    /* Ok, try to parse as a url */
370
0
    ncuriparse(path,&tmpurl);
371
0
    isurl = (tmpurl == NULL?0:1);
372
0
    ncurifree(tmpurl);
373
0
    return isurl;
374
0
}
375
376
#ifdef WINPATH
377
378
/*
379
Provide wrappers for Path-related functions
380
*/
381
382
EXTERNL
383
FILE*
384
NCfopen(const char* path, const char* flags)
385
{
386
    int stat = NC_NOERR;
387
    FILE* f = NULL;
388
    char bflags[64];
389
    char* path8 = NULL; /* ACP -> UTF=8 */
390
    char* bflags8 = NULL;
391
    char* cvtpath = NULL;
392
    wchar_t* wpath = NULL;
393
    wchar_t* wflags = NULL;
394
    size_t flaglen = strlen(flags)+1+1;
395
396
    bflags[0] = '\0';
397
    strlcat(bflags, flags, sizeof(bflags));
398
#ifdef _WIN32
399
    strlcat(bflags,"b",sizeof(bflags));
400
#endif
401
402
    /* First, convert from current Code Page to utf8 */
403
    if((stat = ansi2utf8(path,&path8))) goto done;
404
    if((stat = ansi2utf8(bflags,&bflags8))) goto done;
405
406
    /* Localize */
407
    if((cvtpath = NCpathcvt(path8))==NULL) goto done;
408
409
#ifdef WINUTF8
410
    if(acp == CP_UTF8) {
411
        /* This should take utf8 directly */ 
412
        f = fopen(cvtpath,bflags8);
413
    } else
414
#endif
415
    {
416
        /* Convert from utf8 to wide */
417
        if((stat = utf82wide(cvtpath,&wpath))) goto done;
418
        if((stat = utf82wide(bflags8,&wflags))) goto done;
419
        f = _wfopen(wpath,wflags);
420
    }
421
done:
422
    nullfree(cvtpath);
423
    nullfree(path8);
424
    nullfree(wpath);
425
    nullfree(wflags);
426
    return f;
427
}
428
429
EXTERNL
430
int
431
NCopen3(const char* path, int flags, int mode)
432
{
433
    int stat = NC_NOERR;
434
    int fd = -1;
435
    char* cvtpath = NULL;
436
    char* path8 = NULL;
437
    wchar_t* wpath = NULL;
438
439
    /* First, convert from current Code Page to utf8 */
440
    if((stat = ansi2utf8(path,&path8))) goto done;
441
442
    if((cvtpath = NCpathcvt(path8))==NULL) goto done;
443
444
#ifdef _WIN32
445
    flags |= O_BINARY;
446
#endif
447
#ifdef WINUTF8
448
    if(acp == CP_UTF8) {
449
        /* This should take utf8 directly */ 
450
        fd = _open(cvtpath,flags,mode);
451
    } else
452
#endif
453
    {
454
        /* Convert from utf8 to wide */
455
        if((stat = utf82wide(cvtpath,&wpath))) goto done;
456
        fd = _wopen(wpath,flags,mode);
457
    }
458
459
done:
460
    nullfree(cvtpath);
461
    nullfree(path8);
462
    nullfree(wpath);
463
    return fd;
464
}
465
466
EXTERNL
467
int
468
NCopen2(const char *path, int flags)
469
{
470
    return NCopen3(path,flags,0);
471
}
472
473
#ifdef HAVE_DIRENT_H
474
EXTERNL
475
DIR*
476
NCopendir(const char* path)
477
{
478
    DIR* ent = NULL;
479
    char* cvtname = NCpathcvt(path);
480
    if(cvtname == NULL) return NULL;
481
    ent = opendir(cvtname);
482
    nullfree(cvtname);
483
    return ent;
484
}
485
486
EXTERNL
487
int
488
NCclosedir(DIR* ent)
489
{
490
    int stat = NC_NOERR;
491
    if(closedir(ent) < 0) stat = errno;
492
    return stat;
493
}
494
#endif
495
496
/*
497
Provide wrappers for other file system functions
498
*/
499
500
/* Return access applied to path+mode */
501
EXTERNL
502
int
503
NCaccess(const char* path, int mode)
504
{
505
    int stat = 0;
506
    char* cvtpath = NULL;
507
    char* path8 = NULL;
508
    wchar_t* wpath = NULL;
509
510
    /* First, convert from current Code Page to utf8 */
511
    if((stat = ansi2utf8(path,&path8))) goto done;
512
513
    if((cvtpath = NCpathcvt(path8)) == NULL) {stat = EINVAL; goto done;}
514
#ifdef WINUTF8
515
    if(acp == CP_UTF8) {
516
        /* This should take utf8 directly */ 
517
        if(_access(cvtpath,mode) < 0) {stat = errno; goto done;}
518
    } else
519
#endif
520
    {
521
        /* Convert from utf8 to wide */
522
        if((stat = utf82wide(cvtpath,&wpath))) goto done;
523
        if(_waccess(wpath,mode) < 0) {stat = errno; goto done;}
524
    }
525
526
done:
527
    nullfree(cvtpath);
528
    nullfree(path8);
529
    nullfree(wpath);
530
    errno = stat;
531
    return (errno?-1:0);
532
}
533
534
EXTERNL
535
int
536
NCremove(const char* path)
537
{
538
    int status = 0;
539
    char* path8 = NULL;
540
    char* cvtpath = NULL;
541
    wchar_t* wpath = NULL;
542
543
    /* First, convert from current Code Page to utf8 */
544
    if((status = ansi2utf8(path,&path8))) goto done;
545
546
    if((cvtpath = NCpathcvt(path8)) == NULL) {status=ENOMEM; goto done;}
547
#ifdef WINUTF8
548
    if(acp == CP_UTF8) {
549
        /* This should take utf8 directly */ 
550
        if(remove(cvtpath) < 0) {status = errno; goto done;}
551
    } else
552
#endif
553
    {
554
        if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
555
        if(_wremove(wpath) < 0) {status = errno; goto done;}
556
    }
557
558
done:
559
    nullfree(cvtpath);
560
    nullfree(path8);
561
    nullfree(wpath);
562
    errno = status;
563
    return (errno?-1:0);
564
}
565
566
EXTERNL
567
int
568
NCmkdir(const char* path, int mode)
569
{
570
    int status = 0;
571
    char* cvtpath = NULL;
572
    char* path8 = NULL;
573
    wchar_t* wpath = NULL;
574
575
    /* First, convert from current Code Page to utf8 */
576
    if((status = ansi2utf8(path,&path8))) goto done;
577
578
    if((cvtpath = NCpathcvt(path8)) == NULL) {status=ENOMEM; goto done;}
579
#ifdef WINUTF8
580
    if(acp == CP_UTF8) {
581
        /* This should take utf8 directly */ 
582
        if(_mkdir(cvtpath) < 0) {status = errno; goto done;}
583
    } else
584
#endif
585
    {
586
        if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
587
        if(_wmkdir(wpath) < 0) {status = errno; goto done;}
588
    }
589
590
done:
591
    nullfree(cvtpath);
592
    nullfree(path8);
593
    nullfree(wpath);
594
    errno = status;
595
    return (errno?-1:0);
596
}
597
598
EXTERNL
599
int
600
NCrmdir(const char* path)
601
{
602
    int status = 0;
603
    char* cvtname = NULL;
604
    char* path8 = NULL;
605
606
    /* First, convert from current Code Page to utf8 */
607
    if((status = ansi2utf8(path,&path8))) goto done;
608
  
609
    cvtname = NCpathcvt(path8);
610
    if(cvtname == NULL) {errno = ENOENT; status = -1;}
611
    status = rmdir(cvtname);
612
done:
613
    nullfree(cvtname);
614
    nullfree(path8);
615
    return status;
616
}
617
618
EXTERNL
619
char*
620
NCgetcwd(char* cwdbuf, size_t cwdlen)
621
{
622
    int status = NC_NOERR;
623
    char* path = NULL;
624
    size_t len;
625
    struct Path wd;
626
627
    errno = 0;
628
    if(cwdlen == 0) {status = ENAMETOOLONG; goto done;}
629
    if(!pathinitialized) pathinit();
630
    if((status = getwdpath())) {status = ENOENT; goto done;}
631
    if((status = parsepath(wdprefix,&wd))) {status = EINVAL; goto done;}
632
    if((status = unparsepath(&wd,&path,NCgetlocalpathkind()))) {status = EINVAL; goto done;}
633
    len = strlen(path);
634
    if(len >= cwdlen) {status = ENAMETOOLONG; goto done;}
635
    if(cwdbuf == NULL) {
636
  if((cwdbuf = malloc(cwdlen))==NULL)
637
      {status = NCTHROW(ENOMEM); goto done;}
638
    }
639
    memcpy(cwdbuf,path,len+1);
640
done:
641
    clearPath(&wd);
642
    nullfree(path);
643
    errno = status;
644
    return cwdbuf;
645
}
646
647
EXTERNL
648
int
649
NCmkstemp(char* base)
650
{
651
    int stat = 0;
652
    int fd, rno;
653
    char* tmp = NULL;
654
    size_t len;
655
    char* xp = NULL;
656
    char* cvtpath = NULL;
657
    int attempts;
658
659
    cvtpath = NCpathcvt(base);
660
    len = strlen(cvtpath);
661
    xp = cvtpath+(len-6);
662
    assert(memcmp(xp,"XXXXXX",6)==0);
663
    for(attempts=10;attempts>0;attempts--) {
664
        /* The Windows version of mkstemp does not work right;
665
           it only allows for 26 possible XXXXXX values */
666
        /* Need to simulate by using some kind of pseudo-random number */
667
        rno = rand();
668
        if(rno < 0) rno = -rno;
669
        snprintf(xp,7,"%06d",rno);
670
        fd=NCopen3(cvtpath,O_RDWR|O_BINARY|O_CREAT, _S_IREAD|_S_IWRITE);
671
        if(fd >= 0) break;
672
    }
673
    if(fd < 0) {
674
       nclog(NCLOGERR, "Could not create temp file: %s",tmp);
675
       stat = EACCES;
676
       goto done;
677
    }
678
done:
679
    nullfree(cvtpath);
680
    if(stat && fd >= 0) {close(fd);}
681
    return (stat?-1:fd);
682
}
683
684
#ifdef HAVE_SYS_STAT_H
685
EXTERNL
686
int
687
NCstat(const char* path, struct stat* buf)
688
{
689
    int status = 0;
690
    char* cvtpath = NULL;
691
    char* path8 = NULL;
692
    wchar_t* wpath = NULL;
693
694
    if((status = ansi2utf8(path,&path8))) goto done;
695
696
    if((cvtpath = NCpathcvt(path8)) == NULL) {status=ENOMEM; goto done;}
697
#ifdef WINUTF8
698
    if(acp == CP_UTF8) {
699
        if(_stat64(cvtpath,buf) < 0) {status = errno; goto done;}
700
    } else
701
#endif
702
    {
703
        if((status = utf82wide(cvtpath,&wpath))) {status = ENOENT; goto done;}
704
        if(_wstat64(wpath,buf) < 0) {status = errno; goto done;}
705
    }
706
707
done:
708
    nullfree(cvtpath);
709
    nullfree(path8);
710
    nullfree(wpath);
711
    errno = status;
712
    return (errno?-1:0);
713
}
714
#endif /*HAVE_SYS_STAT_H*/
715
716
int
717
NCpath2utf8(const char* s, char** u8p)
718
{
719
    return ansi2utf8(s,u8p);
720
}
721
722
#else /*!WINPATH*/
723
724
int
725
NCpath2utf8(const char* path, char** u8p)
726
0
{
727
0
    int stat = NC_NOERR;
728
0
    char* u8 = NULL;
729
0
    if(path != NULL) {
730
0
        u8 = strdup(path);
731
0
  if(u8 == NULL) {stat =  NC_ENOMEM; goto done;}
732
0
    }
733
0
    if(u8p) {*u8p = u8; u8 = NULL;}
734
0
done:
735
0
    return stat;
736
0
}
737
#endif /*!WINPATH*/
738
739
EXTERNL int
740
NChasdriveletter(const char* path)
741
0
{
742
0
    int stat = NC_NOERR;
743
0
    int hasdl = 0;
744
0
    struct Path canon = empty;
745
746
0
    if(!pathinitialized) pathinit();
747
748
0
    if((stat = parsepath(path,&canon))) goto done;
749
0
    hasdl = (canon.drive != 0);
750
0
done:
751
0
    clearPath(&canon);
752
0
    return hasdl;
753
0
}
754
755
EXTERNL int
756
NCisnetworkpath(const char* path)
757
0
{
758
0
    int stat = NC_NOERR;
759
0
    int isnp = 0;
760
0
    struct Path canon = empty;
761
762
0
    if(!pathinitialized) pathinit();
763
764
0
    if((stat = parsepath(path,&canon))) goto done;
765
0
    isnp = (canon.drive == netdrive);
766
0
done:
767
0
    clearPath(&canon);
768
0
    return isnp;
769
0
}
770
771
/**************************************************/
772
/* Utilities */
773
774
/* Parse a path */
775
static int
776
parsepath(const char* inpath, struct Path* path)
777
1
{
778
1
    int stat = NC_NOERR;
779
1
    char* tmp1 = NULL;
780
1
    size_t len;
781
1
    char* p;
782
783
1
    assert(path);
784
1
    memset(path,0,sizeof(struct Path));
785
786
1
    if(inpath == NULL) goto done; /* defensive driving */
787
1
    if(!pathinitialized) pathinit();
788
789
#if 0
790
    /* Convert to UTF8 */
791
    if((stat = NCpath2utf8(inpath,&tmp1))) goto done;
792
#else
793
1
    tmp1 = strdup(inpath);
794
1
#endif
795
    /* Convert to forward slash to simplify later code */
796
6
    for(p=tmp1;*p;p++) {if(*p == '\\') *p = '/';}
797
798
    /* parse all paths to 2 parts:
799
  1. drive letter (optional)
800
  2. path after drive letter
801
    */
802
803
1
    len = strlen(tmp1);
804
805
    /* 1. look for Windows network path //...; drive letter is faked using
806
          the character '/' */
807
1
    if(len >= 2 && (tmp1[0] == '/') && (tmp1[1] == '/')) {
808
0
  path->drive = netdrive;
809
  /* Remainder */
810
0
  if(tmp1[2] == '\0')
811
0
      path->path = NULL;
812
0
  else
813
0
      path->path = strdup(tmp1+1); /*keep first '/' */
814
0
  if(path == NULL)
815
0
      {stat = NC_ENOMEM; goto done;}
816
0
  path->kind = NCPD_WIN;
817
0
    }
818
    /* 2. Look for leading /cygdrive/D where D is a single-char drive letter */
819
1
    else if(len >= (cdlen+1)
820
1
  && memcmp(tmp1,"/cygdrive/",cdlen)==0
821
1
  && strchr(windrive,tmp1[cdlen]) != NULL
822
1
  && (tmp1[cdlen+1] == '/'
823
0
      || tmp1[cdlen+1] == '\0')) {
824
  /* Assume this is a cygwin path */
825
0
  path->drive = tmp1[cdlen];
826
  /* Remainder */
827
0
  if(tmp1[cdlen+1] == '\0')
828
0
      path->path = NULL;
829
0
  else
830
0
      path->path = strdup(tmp1+cdlen+1);
831
0
  if(path == NULL)
832
0
      {stat = NC_ENOMEM; goto done;}
833
0
  path->kind = NCPD_CYGWIN;
834
0
    }
835
    /* 4. Look for windows path:  D:/... where D is a single-char
836
          drive letter */
837
1
    else if(len >= 2
838
1
  && strchr(windrive,tmp1[0]) != NULL
839
1
  && tmp1[1] == ':'
840
1
  && (tmp1[2] == '\0' || tmp1[2] == '/')) {
841
  /* Assume this is a windows path */
842
0
  path->drive = tmp1[0];
843
  /* Remainder */
844
0
  if(tmp1[2] == '\0')
845
0
      path->path = NULL;
846
0
  else
847
0
      path->path = strdup(tmp1+2);
848
0
  if(path == NULL)
849
0
      {stat = NC_ENOMEM; goto done;}
850
0
  path->kind = NCPD_WIN; /* Might be MINGW */
851
0
    }
852
#if 0
853
    /* X. look for MSYS2 path /D/... */
854
    else if(len >= 2
855
  && (tmp1[0] == '/')
856
  && strchr(windrive,tmp1[1]) != NULL
857
  && (tmp1[2] == '/' || tmp1[2] == '\0')) {
858
  /* Assume this is an MSYS2 path */
859
  path->drive = tmp1[1];
860
  /* Remainder */
861
  if(tmp1[2] == '\0')
862
      path->path = NULL;
863
  else
864
      path->path = strdup(tmp1+2);
865
  if(path == NULL)
866
      {stat = NC_ENOMEM; goto done;}
867
  path->kind = NCPD_MSYS;
868
    }
869
#endif
870
    /* 5. look for *nix* path; note this includes MSYS2 paths as well */
871
1
    else if(len >= 1 && tmp1[0] == '/') {
872
  /* Assume this is a *nix path */
873
1
  path->drive = 0; /* no drive letter */
874
  /* Remainder */
875
1
  path->path = tmp1; tmp1 = NULL;
876
1
  path->kind = NCPD_NIX;
877
1
    } else {/* 6. Relative path of unknown type */
878
0
  path->kind = NCPD_REL;
879
0
  path->path = tmp1; tmp1 = NULL;
880
0
    }
881
882
1
done:
883
1
    nullfree(tmp1);
884
1
    if(stat) {clearPath(path);}
885
1
    return stat;
886
1
}
887
888
static int
889
unparsepath(struct Path* xp, char** pathp, int target)
890
1
{
891
1
    int stat = NC_NOERR;
892
1
    size_t len;
893
1
    char* path = NULL;
894
1
    char sdrive[4] = "\0\0\0\0";
895
1
    char* p = NULL;
896
1
    int cygspecial = 0;
897
1
    int drive = 0;
898
899
    /* Short circuit a relative path */
900
1
    if(xp->kind == NCPD_REL) {
901
  /* Pass thru relative paths, but with proper slashes */
902
0
  if((path = strdup(xp->path))==NULL) stat = NC_ENOMEM;
903
0
  if(target == NCPD_WIN || target == NCPD_MSYS) {
904
0
      char* p;
905
0
            for(p=path;*p;p++) {if(*p == '/') *p = '\\';} /* back slash*/
906
0
  }
907
0
  goto exit;
908
0
    }
909
910
    /* We need a two level switch with an arm
911
       for every pair of (xp->kind,target)
912
    */
913
914
1
#define CASE(k,t) case ((k)*10+(t))
915
916
1
    switch (xp->kind*10 + target) {
917
0
    CASE(NCPD_NIX,NCPD_NIX):
918
0
  assert(xp->drive == 0);
919
0
  len = nulllen(xp->path)+1;
920
0
  if((path = (char*)malloc(len))==NULL)
921
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
922
0
  path[0] = '\0';
923
0
  if(xp->path != NULL)
924
0
      strlcat(path,xp->path,len);
925
0
  break;
926
0
    CASE(NCPD_NIX,NCPD_MSYS):
927
0
    CASE(NCPD_NIX,NCPD_WIN):
928
0
  assert(xp->drive == 0);
929
0
  len = nulllen(xp->path)+1;
930
0
  if(!mountpoint.defined)
931
0
      {stat = NC_EINVAL; goto done;} /* drive required */
932
0
  len += (strlen(mountpoint.prefix) + 2);
933
0
  if((path = (char*)malloc(len))==NULL)
934
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
935
0
  path[0] = '\0';
936
0
  assert(mountpoint.drive != 0);
937
0
  sdrive[0] = mountpoint.drive;
938
0
  sdrive[1] = ':';
939
0
  sdrive[2] = '\0';
940
0
  strlcat(path,sdrive,len);
941
0
  strlcat(path,mountpoint.prefix,len);
942
0
  if(xp->path != NULL) strlcat(path,xp->path,len);
943
0
        for(p=path;*p;p++) {if(*p == '/') *p = '\\';} /* restore back slash */
944
0
  break;
945
1
    CASE(NCPD_NIX,NCPD_CYGWIN):
946
1
  assert(xp->drive == 0);
947
  /* Is this one of the special cygwin paths? */
948
1
  cygspecial = iscygwinspecial(xp->path);
949
1
  len = 0;
950
1
  if(!cygspecial && mountpoint.drive != 0) {
951
0
      len = cdlen + 1+1; /* /cygdrive/D */
952
0
      len += nulllen(mountpoint.prefix);
953
0
  }
954
1
  if(xp->path)
955
1
      len += strlen(xp->path);
956
1
  len++; /* nul term */
957
1
        if((path = (char*)malloc(len))==NULL)
958
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
959
1
  path[0] = '\0';
960
1
  if(!cygspecial && mountpoint.drive != 0) {
961
0
            strlcat(path,"/cygdrive/",len);
962
0
      sdrive[0] = mountpoint.drive;
963
0
      sdrive[1] = '\0';
964
0
            strlcat(path,sdrive,len);
965
0
            strlcat(path,mountpoint.prefix,len);
966
0
  }
967
1
    if(xp->path)
968
1
      strlcat(path,xp->path,len);
969
1
  break;
970
971
0
    CASE(NCPD_CYGWIN,NCPD_NIX):
972
0
        len = nulllen(xp->path);
973
0
        if(xp->drive != 0)
974
0
      len += (cdlen + 2); /* /cygdrive/D */
975
0
  len++;
976
0
        if((path = (char*)malloc(len))==NULL)
977
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
978
0
  path[0] = '\0';
979
0
  if(xp->drive != 0) {
980
0
            strlcat(path,"/cygdrive/",len);
981
0
      sdrive[0] = xp->drive; sdrive[1] = '\0';
982
0
            strlcat(path,sdrive,len);
983
0
  }
984
0
    if(xp->path)
985
0
      strlcat(path,xp->path,len);
986
0
  break;
987
988
0
    CASE(NCPD_CYGWIN,NCPD_WIN):
989
0
    CASE(NCPD_CYGWIN,NCPD_MSYS):
990
0
  len = nulllen(xp->path)+1;
991
0
  if(xp->drive == 0 && !mountpoint.defined)
992
0
      {stat = NC_EINVAL; goto done;} /* drive required */
993
0
        if (xp->drive == 0)
994
0
            len += (strlen(mountpoint.prefix) + 2);
995
0
        else
996
0
            len += sizeof(sdrive);
997
0
        len++;
998
0
  if((path = (char*)malloc(len))==NULL)
999
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
1000
0
  path[0] = '\0';
1001
0
  if(xp->drive != 0)
1002
0
      drive = xp->drive;
1003
0
  else
1004
0
      drive = mountpoint.drive;
1005
0
  sdrive[0] = drive; sdrive[1] = ':'; sdrive[2] = '\0';
1006
0
        strlcat(path,sdrive,len);
1007
0
  if(xp->path != NULL)
1008
0
            strlcat(path,xp->path,len);
1009
0
        for(p=path;*p;p++) {if(*p == '/') *p = '\\';} /* restore back slash */
1010
0
  break;
1011
1012
0
    CASE(NCPD_CYGWIN,NCPD_CYGWIN):
1013
0
  len = nulllen(xp->path)+1;
1014
0
  if(xp->drive != 0)
1015
0
      len += (cdlen + 2);
1016
0
  len++;
1017
0
  if((path = (char*)malloc(len))==NULL)
1018
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
1019
0
  path[0] = '\0';
1020
0
  if(xp->drive != 0) {
1021
0
      sdrive[0] = xp->drive; sdrive[1] = '\0';
1022
0
      strlcat(path,"/cygdrive/",len);
1023
0
      strlcat(path,sdrive,len);
1024
0
  }
1025
0
  if(xp->path != NULL)
1026
0
      strlcat(path,xp->path,len);
1027
0
  break;
1028
1029
0
    CASE(NCPD_WIN, NCPD_WIN) :
1030
0
    CASE(NCPD_MSYS, NCPD_MSYS) :
1031
0
    CASE(NCPD_WIN, NCPD_MSYS) :
1032
0
    CASE(NCPD_MSYS, NCPD_WIN) :
1033
0
  if(xp->drive == 0 && !mountpoint.defined)
1034
0
      {stat = NC_EINVAL; goto done;} /* drive required */
1035
0
        len = nulllen(xp->path) + 1 + sizeof(sdrive);
1036
0
  if(xp->drive == 0)
1037
0
      len += strlen(mountpoint.prefix);
1038
0
  len++;
1039
0
  if((path = (char*)malloc(len))==NULL)
1040
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
1041
0
  path[0] = '\0';
1042
0
  if(xp->drive != 0)
1043
0
      drive = xp->drive;
1044
0
  else
1045
0
      drive = mountpoint.drive;
1046
0
  sdrive[0] = drive;
1047
0
  sdrive[1] = (drive == netdrive ? '\0' : ':');
1048
0
  sdrive[2] = '\0';
1049
0
        strlcat(path,sdrive,len);
1050
0
  if(xp->path != NULL)
1051
0
            strlcat(path,xp->path,len);
1052
0
        for(p=path;*p;p++) {if(*p == '/') *p = '\\';} /* restore back slash */
1053
0
  break;
1054
1055
0
    CASE(NCPD_WIN,NCPD_NIX):
1056
0
    CASE(NCPD_MSYS,NCPD_NIX):
1057
0
  assert(xp->drive != 0);
1058
0
  len = nulllen(xp->path)+1;
1059
0
  if(xp->drive != 0)
1060
0
            len += sizeof(sdrive);
1061
0
  len++;
1062
0
  if((path = (char*)malloc(len))==NULL)
1063
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
1064
0
  path[0] = '\0';
1065
0
  if(xp->drive != 0) {
1066
0
      sdrive[0] = '/'; sdrive[1] = xp->drive; sdrive[2] = '\0';
1067
0
            strlcat(path,sdrive,len);
1068
0
  }
1069
0
  if(xp->path != NULL) strlcat(path,xp->path,len);
1070
0
  break;
1071
1072
0
    CASE(NCPD_MSYS,NCPD_CYGWIN):
1073
0
    CASE(NCPD_WIN,NCPD_CYGWIN):
1074
0
  assert(xp->drive != 0);
1075
0
  len = nulllen(xp->path)+1;
1076
0
        len += (cdlen + 2);
1077
0
  len++;
1078
0
  if((path = (char*)malloc(len))==NULL)
1079
0
      {stat = NCTHROW(NC_ENOMEM); goto done;}
1080
0
  path[0] = '\0';
1081
0
        sdrive[0] = xp->drive; sdrive[1] = '\0';
1082
0
  strlcat(path,"/cygdrive/",len);
1083
0
        strlcat(path,sdrive,len);
1084
0
  if(xp->path != NULL) strlcat(path,xp->path,len);
1085
0
  break;
1086
1087
0
    default: stat = NC_EINTERNAL; goto done;
1088
1
    }
1089
1090
1
    if(pathdebug > 0)
1091
0
  fprintf(stderr,">>> unparse: target=%s xp=%s path=|%s|\n",NCgetkindname(target),printPATH(xp),path);
1092
1093
1
exit:
1094
1
    if(pathp) {*pathp = path; path = NULL;}
1095
1
done:
1096
1
    nullfree(path);
1097
1
    return stat;
1098
1
}
1099
1100
static int
1101
getwdpath(void)
1102
1
{
1103
1
    int stat = NC_NOERR;
1104
1
    char* path = NULL;
1105
1106
1
    wdprefix[0] = '\0';
1107
#ifdef _WIN32
1108
    {
1109
        wchar_t* wcwd = NULL;
1110
        wchar_t* wpath = NULL;
1111
        wcwd = (wchar_t*)calloc(8192, sizeof(wchar_t));
1112
        wpath = _wgetcwd(wcwd, 8192);
1113
        path = NULL;
1114
        stat = wide2utf8(wpath, &path);
1115
        nullfree(wcwd);
1116
        if (stat) return stat;
1117
  strlcat(wdprefix,path,sizeof(wdprefix));
1118
    }
1119
#else
1120
1
    {
1121
1
        getcwd(wdprefix, sizeof(wdprefix));
1122
1
    }
1123
1
#endif
1124
1
    nullfree(path); path = NULL;
1125
1
    return stat;
1126
1
}
1127
1128
int
1129
NCgetinputpathkind(const char* inpath)
1130
0
{
1131
0
    struct Path p;
1132
0
    int result = NCPD_UNKNOWN;
1133
1134
0
    memset(&p,0,sizeof(p));
1135
0
    if(inpath == NULL) goto done; /* defensive driving */
1136
0
    if(testurl(inpath)) goto done;
1137
0
    if(!pathinitialized) pathinit();
1138
0
    if(parsepath(inpath,&p)) goto done;
1139
1140
0
done:
1141
0
    result = p.kind;
1142
0
    clearPath(&p);
1143
0
    return result;
1144
0
}
1145
1146
int
1147
NCgetlocalpathkind(void)
1148
0
{
1149
0
    int kind = NCPD_UNKNOWN;
1150
0
    if(testkind) return testkind;
1151
#ifdef __CYGWIN__
1152
  kind = NCPD_CYGWIN;
1153
#elif defined _MSC_VER /* not _WIN32 */
1154
  kind = NCPD_WIN;
1155
#elif defined __MSYS__
1156
  kind = NCPD_MSYS;
1157
#elif defined __MINGW32__
1158
  kind = NCPD_WIN; /* alias */
1159
#else
1160
0
  kind = NCPD_NIX;
1161
0
#endif
1162
0
    return kind;
1163
0
}
1164
1165
const char*
1166
NCgetkindname(int kind)
1167
0
{
1168
0
    switch (kind) {
1169
0
    case NCPD_UNKNOWN: return "NCPD_UNKNOWN";
1170
0
    case NCPD_NIX: return "NCPD_NIX";
1171
0
    case NCPD_MSYS: return "NCPD_MSYS";
1172
0
    case NCPD_CYGWIN: return "NCPD_CYGWIN";
1173
0
    case NCPD_WIN: return "NCPD_WIN";
1174
    /* same as WIN case NCPD_MINGW: return "NCPD_MINGW";*/
1175
0
    case NCPD_REL: return "NCPD_REL";
1176
0
    default: break;
1177
0
    }
1178
0
    return "NCPD_UNDEF";
1179
0
}
1180
1181
#ifdef WINPATH
1182
/**
1183
 * Converts the file path from current character set (presumably some
1184
 * ANSI character set like ISO-Latin-1 or UTF-8 to UTF-8
1185
 * @param local Pointer to a nul-terminated string in current char set.
1186
 * @param u8p Pointer for returning the output utf8 string
1187
 *
1188
 * @return NC_NOERR return converted filename
1189
 * @return NC_EINVAL if conversion fails
1190
 * @return NC_ENOMEM if no memory available
1191
 *
1192
 */
1193
static int
1194
ansi2utf8(const char* path, char** u8p)
1195
{
1196
    int stat=NC_NOERR;
1197
    char* u8 = NULL;
1198
    int n;
1199
    wchar_t* u16 = NULL;
1200
1201
    if(path == NULL) goto done;
1202
1203
    if(!pathinitialized) pathinit();
1204
1205
#ifdef WINUTF8
1206
    if(acp == CP_UTF8) { /* Current code page is UTF8 and Windows supports */
1207
        u8 = strdup(path);
1208
  if(u8 == NULL) stat = NC_ENOMEM;
1209
    } else
1210
#endif
1211
    /* Use wide character conversion plus current code page*/
1212
    {
1213
        /* Get length of the converted string */
1214
        n = MultiByteToWideChar(acp, 0, path, -1, NULL, 0);
1215
        if (!n) {stat = NC_EINVAL; goto done;}
1216
        if((u16 = malloc(sizeof(wchar_t) * (n)))==NULL)
1217
      {stat = NCTHROW(NC_ENOMEM); goto done;}
1218
        /* do the conversion */
1219
        if (!MultiByteToWideChar(CP_ACP, 0, path, -1, u16, n))
1220
            {stat = NC_EINVAL; goto done;}
1221
        /* Now reverse the process to produce utf8 */
1222
        n = WideCharToMultiByte(CP_UTF8, 0, u16, -1, NULL, 0, NULL, NULL);
1223
        if (!n) {stat = NC_EINVAL; goto done;}
1224
        if((u8 = malloc(sizeof(char) * n))==NULL)
1225
      {stat = NCTHROW(NC_ENOMEM); goto done;}
1226
        if (!WideCharToMultiByte(CP_UTF8, 0, u16, -1, u8, n, NULL, NULL))
1227
            {stat = NC_EINVAL; goto done;}
1228
    }
1229
done:
1230
    if(u8p) {*u8p = u8; u8 = NULL;}
1231
    nullfree(u8);
1232
    nullfree(u16);
1233
    return stat;
1234
}
1235
1236
static int
1237
ansi2wide(const char* local, wchar_t** u16p)
1238
{
1239
    int stat=NC_NOERR;
1240
    wchar_t* u16 = NULL;
1241
    int n;
1242
1243
    if(!pathinitialized) pathinit();
1244
1245
    /* Get length of the converted string */
1246
    n = MultiByteToWideChar(CP_ACP, 0,  local, -1, NULL, 0);
1247
    if (!n) {stat = NC_EINVAL; goto done;}
1248
    if((u16 = malloc(sizeof(wchar_t) * n))==NULL)
1249
  {stat = NCTHROW(NC_ENOMEM); goto done;}
1250
    /* do the conversion */
1251
    if (!MultiByteToWideChar(CP_ACP, 0, local, -1, u16, n))
1252
        {stat = NC_EINVAL; goto done;}
1253
    if(u16p) {*u16p = u16; u16 = NULL;}
1254
done:
1255
    nullfree(u16);
1256
    return stat;
1257
}
1258
1259
static int
1260
utf82wide(const char* utf8, wchar_t** u16p)
1261
{
1262
    int stat=NC_NOERR;
1263
    wchar_t* u16 = NULL;
1264
    int n;
1265
1266
    if(!pathinitialized) pathinit();
1267
1268
    /* Get length of the converted string */
1269
    n = MultiByteToWideChar(CP_UTF8, 0,  utf8, -1, NULL, 0);
1270
    if (!n) {stat = NC_EINVAL; goto done;}
1271
    if((u16 = malloc(sizeof(wchar_t) * n))==NULL)
1272
  {stat = NCTHROW(NC_ENOMEM); goto done;}
1273
    /* do the conversion */
1274
    if (!MultiByteToWideChar(CP_UTF8, 0, utf8, -1, u16, n))
1275
        {stat = NC_EINVAL; goto done;}
1276
    if(u16p) {*u16p = u16; u16 = NULL;}
1277
done:
1278
    nullfree(u16);
1279
    return stat;
1280
}
1281
1282
static int
1283
wide2utf8(const wchar_t* u16, char** u8p)
1284
{
1285
    int stat=NC_NOERR;
1286
    char* u8 = NULL;
1287
    int n;
1288
1289
    if(!pathinitialized) pathinit();
1290
1291
    /* Get length of the converted string */
1292
    n = WideCharToMultiByte(CP_UTF8, 0,  u16, -1, NULL, 0, NULL, NULL);
1293
    if (!n) {stat = NC_EINVAL; goto done;}
1294
    if((u8 = malloc(sizeof(char) * n))==NULL)
1295
  {stat = NCTHROW(NC_ENOMEM); goto done;}
1296
    /* do the conversion */
1297
    if (!WideCharToMultiByte(CP_UTF8, 0, u16, -1, u8, n, NULL, NULL))
1298
        {stat = NC_EINVAL; goto done;}
1299
    if(u8p) {*u8p = u8; u8 = NULL;}
1300
done:
1301
    nullfree(u8);
1302
    return stat;
1303
}
1304
1305
#endif /*WINPATH*/
1306
1307
static char*
1308
printPATH(struct Path* p)
1309
0
{
1310
0
    static char buf[4096];
1311
0
    buf[0] = '\0';
1312
0
    snprintf(buf,sizeof(buf),"Path{kind=%d drive='%c' path=|%s|}",
1313
0
  p->kind,(p->drive > 0?p->drive:'0'),p->path);
1314
0
    return buf;
1315
0
}
1316
1317
#if 0
1318
static int
1319
hexfor(int c)
1320
{
1321
    if(c >= '0' && c <= '9') return c - '0';
1322
    if(c >= 'a' && c <= 'f') return (c - 'a')+10;
1323
    if(c >= 'A' && c <= 'F') return (c - 'A')+10;
1324
    return -1;
1325
}
1326
#endif
1327
1328
static char hexdigit[] = "0123456789abcdef";
1329
1330
EXTERNL void
1331
printutf8hex(const char* s, char* sx)
1332
0
{
1333
0
    const char* p;
1334
0
    char* q;
1335
0
    for(q=sx,p=s;*p;p++) {
1336
0
  unsigned int c = (unsigned char)*p;
1337
0
  if(c >= ' ' && c <= 127)
1338
0
      *q++ = (char)c;
1339
0
  else {
1340
0
      *q++ = '\\';
1341
0
      *q++ = 'x';
1342
0
      *q++ = hexdigit[(c>>4)&0xf];
1343
0
      *q++ = hexdigit[(c)&0xf];
1344
0
  }
1345
0
    }
1346
0
    *q = '\0';
1347
0
}
1348
1349
#ifdef DEBUG
1350
static void
1351
report(int stat, const char* msg, int line)
1352
{
1353
    if(stat) {
1354
  nclog(NCLOGERR,"NCpathcvt(%d): %s: stat=%d (%s)",
1355
    line,msg,stat,nc_strerror(stat));
1356
    }
1357
}
1358
#endif