From d9a7bac9f806e9199e8448886ed5fa9aa5a2e370 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Mon, 4 May 2020 08:40:05 +0000 Subject: [PATCH] added path canonicalization functions --- mio/lib/Makefile.am | 3 + mio/lib/Makefile.in | 43 ++- mio/lib/chr.c | 2 +- mio/lib/ecs-imp.h | 2 +- mio/lib/ecs.c | 2 +- mio/lib/htb.c | 2 +- mio/lib/htrd.c | 1 + mio/lib/mio-chr.h | 2 +- mio/lib/mio-ecs.h | 2 +- mio/lib/mio-htb.h | 2 +- mio/lib/mio-path.h | 96 +++++++ mio/lib/path.c | 641 ++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 782 insertions(+), 16 deletions(-) create mode 100644 mio/lib/mio-path.h create mode 100644 mio/lib/path.c diff --git a/mio/lib/Makefile.am b/mio/lib/Makefile.am index b315b32..c7f7090 100644 --- a/mio/lib/Makefile.am +++ b/mio/lib/Makefile.am @@ -32,6 +32,7 @@ include_HEADERS = \ mio-http.h \ mio-nwif.h \ mio-pac1.h \ + mio-path.h \ mio-pro.h \ mio-sck.h \ mio-skad.h \ @@ -50,11 +51,13 @@ libmio_la_SOURCES = \ fmt.c \ fmt-imp.h \ htb.c \ + htrd.c \ htre.c \ http.c \ mio-prv.h \ mio.c \ nwif.c \ + path.c \ pro.c \ sck.c \ skad.c \ diff --git a/mio/lib/Makefile.in b/mio/lib/Makefile.in index a4d79a3..650cff4 100644 --- a/mio/lib/Makefile.in +++ b/mio/lib/Makefile.in @@ -140,12 +140,13 @@ libmio_la_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am_libmio_la_OBJECTS = libmio_la-chr.lo libmio_la-dns.lo \ libmio_la-dns-cli.lo libmio_la-ecs.lo libmio_la-err.lo \ - libmio_la-fmt.lo libmio_la-htb.lo libmio_la-htre.lo \ - libmio_la-http.lo libmio_la-mio.lo libmio_la-nwif.lo \ - libmio_la-pro.lo libmio_la-sck.lo libmio_la-skad.lo \ - libmio_la-sys.lo libmio_la-sys-ass.lo libmio_la-sys-err.lo \ - libmio_la-sys-log.lo libmio_la-sys-mux.lo libmio_la-sys-tim.lo \ - libmio_la-tmr.lo libmio_la-utf8.lo libmio_la-utl.lo + libmio_la-fmt.lo libmio_la-htb.lo libmio_la-htrd.lo \ + libmio_la-htre.lo libmio_la-http.lo libmio_la-mio.lo \ + libmio_la-nwif.lo libmio_la-path.lo libmio_la-pro.lo \ + libmio_la-sck.lo libmio_la-skad.lo libmio_la-sys.lo \ + libmio_la-sys-ass.lo libmio_la-sys-err.lo libmio_la-sys-log.lo \ + libmio_la-sys-mux.lo libmio_la-sys-tim.lo libmio_la-tmr.lo \ + libmio_la-utf8.lo libmio_la-utl.lo libmio_la_OBJECTS = $(am_libmio_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -173,9 +174,10 @@ am__depfiles_remade = ./$(DEPDIR)/libmio_la-chr.Plo \ ./$(DEPDIR)/libmio_la-dns-cli.Plo \ ./$(DEPDIR)/libmio_la-dns.Plo ./$(DEPDIR)/libmio_la-ecs.Plo \ ./$(DEPDIR)/libmio_la-err.Plo ./$(DEPDIR)/libmio_la-fmt.Plo \ - ./$(DEPDIR)/libmio_la-htb.Plo ./$(DEPDIR)/libmio_la-htre.Plo \ - ./$(DEPDIR)/libmio_la-http.Plo ./$(DEPDIR)/libmio_la-mio.Plo \ - ./$(DEPDIR)/libmio_la-nwif.Plo ./$(DEPDIR)/libmio_la-pro.Plo \ + ./$(DEPDIR)/libmio_la-htb.Plo ./$(DEPDIR)/libmio_la-htrd.Plo \ + ./$(DEPDIR)/libmio_la-htre.Plo ./$(DEPDIR)/libmio_la-http.Plo \ + ./$(DEPDIR)/libmio_la-mio.Plo ./$(DEPDIR)/libmio_la-nwif.Plo \ + ./$(DEPDIR)/libmio_la-path.Plo ./$(DEPDIR)/libmio_la-pro.Plo \ ./$(DEPDIR)/libmio_la-sck.Plo ./$(DEPDIR)/libmio_la-skad.Plo \ ./$(DEPDIR)/libmio_la-sys-ass.Plo \ ./$(DEPDIR)/libmio_la-sys-err.Plo \ @@ -412,6 +414,7 @@ include_HEADERS = \ mio-http.h \ mio-nwif.h \ mio-pac1.h \ + mio-path.h \ mio-pro.h \ mio-sck.h \ mio-skad.h \ @@ -430,11 +433,13 @@ libmio_la_SOURCES = \ fmt.c \ fmt-imp.h \ htb.c \ + htrd.c \ htre.c \ http.c \ mio-prv.h \ mio.c \ nwif.c \ + path.c \ pro.c \ sck.c \ skad.c \ @@ -553,10 +558,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-err.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-fmt.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-htb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-htrd.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-htre.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-http.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-mio.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-nwif.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-path.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-pro.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-sck.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmio_la-skad.Plo@am__quote@ # am--include-marker @@ -649,6 +656,13 @@ libmio_la-htb.lo: htb.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmio_la-htb.lo `test -f 'htb.c' || echo '$(srcdir)/'`htb.c +libmio_la-htrd.lo: htrd.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmio_la-htrd.lo -MD -MP -MF $(DEPDIR)/libmio_la-htrd.Tpo -c -o libmio_la-htrd.lo `test -f 'htrd.c' || echo '$(srcdir)/'`htrd.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmio_la-htrd.Tpo $(DEPDIR)/libmio_la-htrd.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='htrd.c' object='libmio_la-htrd.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmio_la-htrd.lo `test -f 'htrd.c' || echo '$(srcdir)/'`htrd.c + libmio_la-htre.lo: htre.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmio_la-htre.lo -MD -MP -MF $(DEPDIR)/libmio_la-htre.Tpo -c -o libmio_la-htre.lo `test -f 'htre.c' || echo '$(srcdir)/'`htre.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmio_la-htre.Tpo $(DEPDIR)/libmio_la-htre.Plo @@ -677,6 +691,13 @@ libmio_la-nwif.lo: nwif.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmio_la-nwif.lo `test -f 'nwif.c' || echo '$(srcdir)/'`nwif.c +libmio_la-path.lo: path.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmio_la-path.lo -MD -MP -MF $(DEPDIR)/libmio_la-path.Tpo -c -o libmio_la-path.lo `test -f 'path.c' || echo '$(srcdir)/'`path.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmio_la-path.Tpo $(DEPDIR)/libmio_la-path.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='path.c' object='libmio_la-path.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmio_la-path.lo `test -f 'path.c' || echo '$(srcdir)/'`path.c + libmio_la-pro.lo: pro.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmio_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmio_la-pro.lo -MD -MP -MF $(DEPDIR)/libmio_la-pro.Tpo -c -o libmio_la-pro.lo `test -f 'pro.c' || echo '$(srcdir)/'`pro.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmio_la-pro.Tpo $(DEPDIR)/libmio_la-pro.Plo @@ -923,10 +944,12 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/libmio_la-err.Plo -rm -f ./$(DEPDIR)/libmio_la-fmt.Plo -rm -f ./$(DEPDIR)/libmio_la-htb.Plo + -rm -f ./$(DEPDIR)/libmio_la-htrd.Plo -rm -f ./$(DEPDIR)/libmio_la-htre.Plo -rm -f ./$(DEPDIR)/libmio_la-http.Plo -rm -f ./$(DEPDIR)/libmio_la-mio.Plo -rm -f ./$(DEPDIR)/libmio_la-nwif.Plo + -rm -f ./$(DEPDIR)/libmio_la-path.Plo -rm -f ./$(DEPDIR)/libmio_la-pro.Plo -rm -f ./$(DEPDIR)/libmio_la-sck.Plo -rm -f ./$(DEPDIR)/libmio_la-skad.Plo @@ -992,10 +1015,12 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/libmio_la-err.Plo -rm -f ./$(DEPDIR)/libmio_la-fmt.Plo -rm -f ./$(DEPDIR)/libmio_la-htb.Plo + -rm -f ./$(DEPDIR)/libmio_la-htrd.Plo -rm -f ./$(DEPDIR)/libmio_la-htre.Plo -rm -f ./$(DEPDIR)/libmio_la-http.Plo -rm -f ./$(DEPDIR)/libmio_la-mio.Plo -rm -f ./$(DEPDIR)/libmio_la-nwif.Plo + -rm -f ./$(DEPDIR)/libmio_la-path.Plo -rm -f ./$(DEPDIR)/libmio_la-pro.Plo -rm -f ./$(DEPDIR)/libmio_la-sck.Plo -rm -f ./$(DEPDIR)/libmio_la-skad.Plo diff --git a/mio/lib/chr.c b/mio/lib/chr.c index 18c2db1..8007615 100644 --- a/mio/lib/chr.c +++ b/mio/lib/chr.c @@ -1,7 +1,7 @@ /* * $Id$ * - Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved. + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/mio/lib/ecs-imp.h b/mio/lib/ecs-imp.h index 4844bdf..3f557ac 100644 --- a/mio/lib/ecs-imp.h +++ b/mio/lib/ecs-imp.h @@ -1,7 +1,7 @@ /* * $Id$ * - Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved. + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/mio/lib/ecs.c b/mio/lib/ecs.c index 38edf17..e841176 100644 --- a/mio/lib/ecs.c +++ b/mio/lib/ecs.c @@ -1,7 +1,7 @@ /* * $Id$ * - Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved. + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/mio/lib/htb.c b/mio/lib/htb.c index 0515679..c929692 100644 --- a/mio/lib/htb.c +++ b/mio/lib/htb.c @@ -1,7 +1,7 @@ /* * $Id$ * - Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved. + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/mio/lib/htrd.c b/mio/lib/htrd.c index 63295f6..b1f5675 100644 --- a/mio/lib/htrd.c +++ b/mio/lib/htrd.c @@ -24,6 +24,7 @@ #include "mio-htrd.h" #include "mio-chr.h" +#include "mio-path.h" #include "mio-prv.h" static const mio_bch_t NUL = '\0'; diff --git a/mio/lib/mio-chr.h b/mio/lib/mio-chr.h index 43cce22..dc2cf13 100644 --- a/mio/lib/mio-chr.h +++ b/mio/lib/mio-chr.h @@ -1,7 +1,7 @@ /* * $Id$ * - Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved. + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/mio/lib/mio-ecs.h b/mio/lib/mio-ecs.h index 3c8b570..121cc50 100644 --- a/mio/lib/mio-ecs.h +++ b/mio/lib/mio-ecs.h @@ -1,7 +1,7 @@ /* * $Id$ * - Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved. + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/mio/lib/mio-htb.h b/mio/lib/mio-htb.h index 78f84e1..57a4ede 100644 --- a/mio/lib/mio-htb.h +++ b/mio/lib/mio-htb.h @@ -1,7 +1,7 @@ /* * $Id$ * - Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved. + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/mio/lib/mio-path.h b/mio/lib/mio-path.h new file mode 100644 index 0000000..8df186f --- /dev/null +++ b/mio/lib/mio-path.h @@ -0,0 +1,96 @@ +/* + * $Id$ + * + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MIO_PATH_H_ +#define _MIO_PATH_H_ + +#include +#include + +enum mio_canon_path_flag_t +{ + /** if the final output is . logically, return an empty path */ + MIO_CANON_PATH_EMPTY_SINGLE_DOT = (1 << 0), + + /** keep the .. segment in the path name */ + MIO_CANON_PATH_KEEP_DOUBLE_DOTS = (1 << 1), + + /** drop a trailing separator even if the source contains one */ + MIO_CANON_PATH_DROP_TRAILING_SEP = (1 << 2) +}; + +typedef enum mio_canon_path_flag_t mio_canon_path_flag_t; + +#define MIO_CANON_OOCSTR_PATH_EMPTY_SINGLE_DOT MIO_CANON_PATH_EMPTY_SINGLE_DOT +#define MIO_CANON_OOCSTR_PATH_KEEP_DOUBLE_DOTS MIO_CANON_PATH_KEEP_DOUBLE_DOTS +#define MIO_CANON_OOCSTR_PATH_DROP_TRAILING_SEP MIO_CANON_PATH_DROP_TRAILING_SEP + +#define MIO_CANON_UCSTR_PATH_EMPTY_SINGLE_DOT MIO_CANON_PATH_EMPTY_SINGLE_DOT +#define MIO_CANON_UCSTR_PATH_KEEP_DOUBLE_DOTS MIO_CANON_PATH_KEEP_DOUBLE_DOTS +#define MIO_CANON_UCSTR_PATH_DROP_TRAILING_SEP MIO_CANON_PATH_DROP_TRAILING_SEP + +#define MIO_CANON_BCSTR_PATH_EMPTY_SINGLE_DOT MIO_CANON_PATH_EMPTY_SINGLE_DOT +#define MIO_CANON_BCSTR_PATH_KEEP_DOUBLE_DOTS MIO_CANON_PATH_KEEP_DOUBLE_DOTS +#define MIO_CANON_BCSTR_PATH_DROP_TRAILING_SEP MIO_CANON_PATH_DROP_TRAILING_SEP + + +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + +# define MIO_IS_PATH_SEP(c) ((c) == '/' || (c) == '\\') + +#else +# define MIO_IS_PATH_SEP(c) ((c) == '/') +#endif + +#define MIO_IS_PATH_DRIVE(s) \ + (((s[0] >= 'A' && s[0] <= 'Z') || \ + (s[0] >= 'a' && s[0] <= 'z')) && \ + s[1] == ':') + +#define MIO_IS_PATH_SEP_OR_NIL(c) (MIO_IS_PATH_SEP(c) || (c) == '\0') + + +#if defined(__cplusplus) +extern "C" { +#endif + +MIO_EXPORT mio_oow_t mio_canon_ucstr_path ( + const mio_uch_t* path, + mio_uch_t* canon, + int flags +); + +MIO_EXPORT mio_oow_t mio_canon_bcstr_path ( + const mio_bch_t* path, + mio_bch_t* canon, + int flags +); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/mio/lib/path.c b/mio/lib/path.c new file mode 100644 index 0000000..6796185 --- /dev/null +++ b/mio/lib/path.c @@ -0,0 +1,641 @@ +/* + * $Id + * + Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "mio-path.h" +#include "mio-prv.h" + +/* TODO: support the \\?\ prefix and the \\.\ prefix on windows + * support \\?\UNC\server\path which is equivalent to \\server\path. + * */ +/* ------------------------------------------------------------------ */ +/* WCS IMPLEMENTATION */ +/* ------------------------------------------------------------------ */ + +#if 0 +int mio_is_ucstr_path_absolute (const mio_uch_t* path) +{ + if (MIO_IS_PATH_SEP(path[0])) return 1; +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + /* a drive like c:tmp is absolute in positioning the drive. + * but the path within the drive is kind of relative */ + if (MIO_IS_PATH_DRIVE(path)) return 1; +#endif + return 0; +} + +int mio_is_ucstr_path_drive (const mio_uch_t* path) +{ +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (MIO_IS_PATH_DRIVE(path)) return 1; +#endif + return 0; +} + +int mio_is_ucstr_path_drive_absolute (const mio_uch_t* path) +{ +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (MIO_IS_PATH_DRIVE(path) && MIO_IS_PATH_SEP(path[2])) return 1; +#endif + return 0; +} + +int mio_is_ucstr_path_drive_current (const mio_uch_t* path) +{ +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (MIO_IS_PATH_DRIVE(path) && path[2] == '\0') return 1; +#endif + return 0; +} +#endif + +mio_oow_t mio_canon_ucstr_path (const mio_uch_t* path, mio_uch_t* canon, int flags) +{ + const mio_uch_t* ptr; + mio_uch_t* dst; + mio_uch_t* non_root_start; + int has_root = 0; +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + int is_drive = 0; +#endif + mio_oow_t canon_len; + + if (path[0] == '\0') + { + /* if the source is empty, no translation is needed */ + canon[0] = '\0'; + return 0; + } + + ptr = path; + dst = canon; + +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (MIO_IS_PATH_DRIVE(ptr)) + { + /* handle drive letter */ + *dst++ = *ptr++; /* drive letter */ + *dst++ = *ptr++; /* colon */ + + is_drive = 1; + if (MIO_IS_PATH_SEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + } + } + else if (MIO_IS_PATH_SEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + + #if defined(_WIN32) + /* handle UNC path for Windows */ + if (MIO_IS_PATH_SEP(*ptr)) + { + *dst++ = *ptr++; + + if (MIO_IS_PATH_SEP_OR_NIL(*ptr)) + { + /* if there is another separator after \\, + * it's not an UNC path. */ + dst--; + } + else + { + /* if it starts with \\, process host name */ + do { *dst++ = *ptr++; } while (!MIO_IS_PATH_SEP_OR_NIL(*ptr)); + if (MIO_IS_PATH_SEP(*ptr)) *dst++ = *ptr++; + } + } + #endif + } +#else + if (MIO_IS_PATH_SEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + } +#endif + + /* non_root_start points to the beginning of the canonicalized + * path excluding the root directory part. */ + non_root_start = dst; + + do + { + const mio_uch_t* seg; + mio_oow_t seglen; + + /* skip duplicate separators */ + while (MIO_IS_PATH_SEP(*ptr)) ptr++; + + /* end of path reached */ + if (*ptr == '\0') break; + + /* find the next segment */ + seg = ptr; + while (!MIO_IS_PATH_SEP_OR_NIL(*ptr)) ptr++; + seglen = ptr - seg; + + /* handle the segment */ + if (seglen == 1 && seg[0] == '.') + { + /* eat up . */ + } + else if (!(flags & MIO_CANON_UCSTR_PATH_KEEP_DOUBLE_DOTS) && + seglen == 2 && seg[0] == '.' && seg[1] == '.') + { + /* eat up the previous segment */ + mio_uch_t* tmp; + + tmp = dst; + if (tmp > non_root_start) + { + /* there is a previous segment. */ + + tmp--; /* skip the separator just before .. */ + + /* find the beginning of the previous segment */ + while (tmp > non_root_start) + { + tmp--; + if (MIO_IS_PATH_SEP(*tmp)) + { + tmp++; /* position it next to the separator */ + break; + } + } + } + + if (has_root) + { + /* + * Eat up the previous segment if it exists. + * + * If it doesn't exist, tmp == dst so dst = tmp + * keeps dst unchanged. If it exists, + * tmp != dst. so dst = tmp changes dst. + * + * path /abc/def/.. + * ^ ^ + * seg ptr + * + * canon /abc/def/ + * ^ ^ + * tmp dst + */ + dst = tmp; + } + else + { + mio_oow_t prevlen; + + prevlen = dst - tmp; + + if (/*tmp == non_root_start &&*/ prevlen == 0) + { + /* there is no previous segment */ + goto normal; + } + + if (prevlen == 3 && tmp[0] == '.' && tmp[1] == '.') + { + /* nothing to eat away because the previous segment is ../ + * + * path ../../ + * ^ ^ + * seg ptr + * + * canon ../ + * ^ ^ + * tmp dst + */ + goto normal; + } + + dst = tmp; + } + } + else + { + normal: + while (seg < ptr) *dst++ = *seg++; + if (MIO_IS_PATH_SEP(*ptr)) + { + /* this segment ended with a separator */ + *dst++ = *seg++; /* copy the separator */ + ptr++; /* move forward the pointer */ + } + } + } + while (1); + + if (dst > non_root_start && MIO_IS_PATH_SEP(dst[-1]) && + ((flags & MIO_CANON_BCSTR_PATH_DROP_TRAILING_SEP) || !MIO_IS_PATH_SEP(ptr[-1]))) + { + /* if the canoncal path composed so far ends with a separator + * and the original path didn't end with the separator, delete + * the ending separator. + * also delete it if MIO_CANON_BCSTR_PATH_DROP_TRAILING_SEP is set. + * + * dst > non_root_start: + * there is at least 1 character after the root directory + * part. + * MIO_IS_PATH_SEP(dst[-1]): + * the canonical path ends with a separator. + * MIO_IS_PATH_SEP(ptr[-1]): + * the origial path ends with a separator. + */ + dst[-1] = '\0'; + canon_len = dst - canon - 1; + } + else + { + /* just null-terminate the canonical path normally */ + dst[0] = '\0'; + canon_len = dst - canon; + } + + if (canon_len <= 0) + { + if (!(flags & MIO_CANON_UCSTR_PATH_EMPTY_SINGLE_DOT)) + { + /* when resolving to a single dot, a trailing separator is not + * retained though the orignal path name contains it. */ + canon[0] = '.'; + canon[1] = '\0'; + canon_len = 1; + } + } + else + { + /* drop a traling separator if the last segment is + * double slashes */ + + int adj_base_len = 3; + +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (is_drive && !has_root) + { + /* A path like A:..\\\ need some adjustment for + * finalization below. */ + adj_base_len += 2; + } +#endif + + if (canon_len == adj_base_len) + { + /* i don't have to retain a trailing separator + * if the last segment is double slashes because + * the double slahses indicate a directory obviously */ + if (canon[canon_len-3] == '.' && + canon[canon_len-2] == '.' && + MIO_IS_PATH_SEP(canon[canon_len-1])) + { + canon[--canon_len] = '\0'; + } + } + else if (canon_len > adj_base_len) + { + if (MIO_IS_PATH_SEP(canon[canon_len-4]) && + canon[canon_len-3] == '.' && + canon[canon_len-2] == '.' && + MIO_IS_PATH_SEP(canon[canon_len-1])) + { + canon[--canon_len] = '\0'; + } + } + } + + return canon_len; +} + + +/* ------------------------------------------------------------------ */ +/* MBS IMPLEMENTATION */ +/* ------------------------------------------------------------------ */ + +#if 0 +int mio_is_bcstr_path_absolute (const mio_bch_t* path) +{ + if (MIO_IS_PATH_SEP(path[0])) return 1; +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + /* a drive like c:tmp is absolute in positioning the drive. + * but the path within the drive is kind of relative */ + if (MIO_IS_PATH_DRIVE(path)) return 1; +#endif + return 0; +} + +int mio_is_bcstr_path_drive (const mio_bch_t* path) +{ +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (MIO_IS_PATH_DRIVE(path)) return 1; +#endif + return 0; +} + +int mio_is_bcstr_path_drive_absolute (const mio_bch_t* path) +{ +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (MIO_IS_PATH_DRIVE(path) && MIO_IS_PATH_SEP(path[2])) return 1; +#endif + return 0; +} + +int mio_is_bcstr_path_drive_current (const mio_bch_t* path) +{ +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (MIO_IS_PATH_DRIVE(path) && path[2] == '\0') return 1; +#endif + return 0; +} + +#endif + +mio_oow_t mio_canon_bcstr_path (const mio_bch_t* path, mio_bch_t* canon, int flags) +{ + const mio_bch_t* ptr; + mio_bch_t* dst; + mio_bch_t* non_root_start; + int has_root = 0; +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + int is_drive = 0; +#endif + mio_oow_t canon_len; + + if (path[0] == '\0') + { + /* if the source is empty, no translation is needed */ + canon[0] = '\0'; + return 0; + } + + ptr = path; + dst = canon; + +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (MIO_IS_PATH_DRIVE(ptr)) + { + /* handle drive letter */ + *dst++ = *ptr++; /* drive letter */ + *dst++ = *ptr++; /* colon */ + + is_drive = 1; + if (MIO_IS_PATH_SEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + } + } + else if (MIO_IS_PATH_SEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + + #if defined(_WIN32) + /* handle UNC path for Windows */ + if (MIO_IS_PATH_SEP(*ptr)) + { + *dst++ = *ptr++; + + if (MIO_IS_PATH_SEP_OR_NIL(*ptr)) + { + /* if there is another separator after \\, + * it's not an UNC path. */ + dst--; + } + else + { + /* if it starts with \\, process host name */ + do { *dst++ = *ptr++; } while (!MIO_IS_PATH_SEP_OR_NIL(*ptr)); + if (MIO_IS_PATH_SEP(*ptr)) *dst++ = *ptr++; + } + } + #endif + } +#else + if (MIO_IS_PATH_SEP(*ptr)) + { + *dst++ = *ptr++; /* root directory */ + has_root = 1; + } +#endif + + /* non_root_start points to the beginning of the canonicalized + * path excluding the root directory part. */ + non_root_start = dst; + + do + { + const mio_bch_t* seg; + mio_oow_t seglen; + + /* skip duplicate separators */ + while (MIO_IS_PATH_SEP(*ptr)) ptr++; + + /* end of path reached */ + if (*ptr == '\0') break; + + /* find the next segment */ + seg = ptr; + while (!MIO_IS_PATH_SEP_OR_NIL(*ptr)) ptr++; + seglen = ptr - seg; + + /* handle the segment */ + if (seglen == 1 && seg[0] == '.') + { + /* eat up . */ + } + else if (!(flags & MIO_CANON_BCSTR_PATH_KEEP_DOUBLE_DOTS) && + seglen == 2 && seg[0] == '.' && seg[1] == '.') + { + /* eat up the previous segment */ + mio_bch_t* tmp; + + tmp = dst; + if (tmp > non_root_start) + { + /* there is a previous segment. */ + + tmp--; /* skip the separator just before .. */ + + /* find the beginning of the previous segment */ + while (tmp > non_root_start) + { + tmp--; + if (MIO_IS_PATH_SEP(*tmp)) + { + tmp++; /* position it next to the separator */ + break; + } + } + } + + if (has_root) + { + /* + * Eat up the previous segment if it exists. + * + * If it doesn't exist, tmp == dst so dst = tmp + * keeps dst unchanged. If it exists, + * tmp != dst. so dst = tmp changes dst. + * + * path /abc/def/.. + * ^ ^ + * seg ptr + * + * canon /abc/def/ + * ^ ^ + * tmp dst + */ + dst = tmp; + } + else + { + mio_oow_t prevlen; + + prevlen = dst - tmp; + + if (/*tmp == non_root_start &&*/ prevlen == 0) + { + /* there is no previous segment */ + goto normal; + } + + if (prevlen == 3 && tmp[0] == '.' && tmp[1] == '.') + { + /* nothing to eat away because the previous segment is ../ + * + * path ../../ + * ^ ^ + * seg ptr + * + * canon ../ + * ^ ^ + * tmp dst + */ + goto normal; + } + + dst = tmp; + } + } + else + { + normal: + while (seg < ptr) *dst++ = *seg++; + if (MIO_IS_PATH_SEP(*ptr)) + { + /* this segment ended with a separator */ + *dst++ = *seg++; /* copy the separator */ + ptr++; /* move forward the pointer */ + } + } + } + while (1); + + if (dst > non_root_start && MIO_IS_PATH_SEP(dst[-1]) && + ((flags & MIO_CANON_BCSTR_PATH_DROP_TRAILING_SEP) || !MIO_IS_PATH_SEP(ptr[-1]))) + { + /* if the canoncal path composed so far ends with a separator + * and the original path didn't end with the separator, delete + * the ending separator. + * also delete it if MIO_CANON_BCSTR_PATH_DROP_TRAILING_SEP is set. + * + * dst > non_root_start: + * there is at least 1 character after the root directory + * part. + * MIO_IS_PATH_SEP(dst[-1]): + * the canonical path ends with a separator. + * MIO_IS_PATH_SEP(ptr[-1]): + * the origial path ends with a separator. + */ + dst[-1] = '\0'; + canon_len = dst - canon - 1; + } + else + { + /* just null-terminate the canonical path normally */ + dst[0] = '\0'; + canon_len = dst - canon; + } + + if (canon_len <= 0) + { + if (!(flags & MIO_CANON_BCSTR_PATH_EMPTY_SINGLE_DOT)) + { + /* when resolving to a single dot, a trailing separator is not + * retained though the orignal path name contains it. */ + canon[0] = '.'; + canon[1] = '\0'; + canon_len = 1; + } + } + else + { + /* drop a traling separator if the last segment is + * double slashes */ + + int adj_base_len = 3; + +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + if (is_drive && !has_root) + { + /* A path like A:..\\\ need some adjustment for + * finalization below. */ + adj_base_len += 2; + } +#endif + + if (canon_len == adj_base_len) + { + /* i don't have to retain a trailing separator + * if the last segment is double slashes because + * the double slahses indicate a directory obviously */ + if (canon[canon_len-3] == '.' && + canon[canon_len-2] == '.' && + MIO_IS_PATH_SEP(canon[canon_len-1])) + { + canon[--canon_len] = '\0'; + } + } + else if (canon_len > adj_base_len) + { + if (MIO_IS_PATH_SEP(canon[canon_len-4]) && + canon[canon_len-3] == '.' && + canon[canon_len-2] == '.' && + MIO_IS_PATH_SEP(canon[canon_len-1])) + { + canon[--canon_len] = '\0'; + } + } + } + + return canon_len; +} + +