Coverage Report

Created: 2025-10-26 06:49

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