Coverage Report

Created: 2023-05-28 06:42

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