Index: bin/ln/Makefile =================================================================== --- bin/ln/Makefile +++ bin/ln/Makefile @@ -8,4 +8,5 @@ LINKS= ${BINDIR}/ln ${BINDIR}/link MLINKS= ln.1 link.1 +LIBADD+= util .include Index: bin/ln/ln.1 =================================================================== --- bin/ln/ln.1 +++ bin/ln/ln.1 @@ -42,12 +42,14 @@ .Sh SYNOPSIS .Nm .Op Fl L | Fl P | Fl s Op Fl F +.Op Fl a | Fl r .Op Fl f | iw .Op Fl hnv .Ar source_file .Op Ar target_file .Nm .Op Fl L | Fl P | Fl s Op Fl F +.Op Fl a | Fl r .Op Fl f | iw .Op Fl hnv .Ar source_file ... @@ -108,6 +110,10 @@ This option cancels the .Fl L option. +.It Fl a +If creating a symbolic link, +ensure that the link is an absolute link, +even if specified in a ralative form. .It Fl f If the target file already exists, then unlink it so that the link may occur. @@ -148,8 +154,23 @@ for compatibility with other .Nm implementations. +.It Fl r +If creating a symbolic link, +ensure that the link is a relative link, +even if specified in an absolute form. +The +.Fl a +and the +.Fl r +options override each other; +the last one specified detirmines the method used. .It Fl s Create a symbolic link. +If neither the +.Fl a +nor the +.Fl r +option is specified the link is created exactly as specified without checking that the target exists. .It Fl v Cause .Nm @@ -278,20 +299,22 @@ The first argument needs to exist, the second one is created. .Sh COMPATIBILITY The +.Fl a , .Fl h , .Fl i , .Fl n , +.Fl r , .Fl v and .Fl w -options are non-standard and their use in scripts is not recommended. +options are non-standard and their use in portable scripts is not recommended. They are provided solely for compatibility with other .Nm implementations. .Pp The -.Fl F -option is a +.Fl F , +options is a .Fx extension and should not be used in portable scripts. .Sh SEE ALSO Index: bin/ln/ln.c =================================================================== --- bin/ln/ln.c +++ bin/ln/ln.c @@ -53,6 +53,7 @@ #include #include #include +#include static int fflag; /* Unlink existing files. */ static int Fflag; /* Remove empty directories also. */ @@ -63,6 +64,9 @@ static int vflag; /* Verbose output. */ static int wflag; /* Warn if symlink target does not * exist, and -f is not enabled. */ +static int aflag; /* Absolute symbolic link. */ +static int rflag; /* Relative symbolic link. */ + static char linkch; static int linkit(const char *, const char *, int); @@ -94,7 +98,7 @@ exit(linkit(argv[0], argv[1], 0)); } - while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1) + while ((ch = getopt(argc, argv, "FLPafhinrsvw")) != -1) switch (ch) { case 'F': Fflag = 1; @@ -105,6 +109,11 @@ case 'P': Pflag = 1; break; + case 'a': + aflag = 1; + rflag = 0; + break; + case 'f': fflag = 1; iflag = 0; @@ -118,6 +127,10 @@ iflag = 1; fflag = 0; break; + case 'r': + rflag = 1; + aflag = 0; + break; case 's': sflag = 1; break; @@ -219,6 +232,10 @@ return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino; } + +/* + * do a single link. Arg isdir refers to the target. + */ static int linkit(const char *source, const char *target, int isdir) { @@ -228,6 +245,9 @@ char path[PATH_MAX]; char wbuf[PATH_MAX]; char bbuf[PATH_MAX]; + char base[PATH_MAX]; + char abssource[PATH_MAX]; + char relsource[PATH_MAX]; if (!sflag) { /* If source doesn't exist, quit now. */ @@ -243,6 +263,39 @@ } } + if (!isdir && (exists = !stat(target, &sb)) && S_ISDIR(sb.st_mode)) + isdir = 1; + if (sflag && (aflag || rflag)) { + /* convert source directory into absolute name */ + if (*source != '/') { + if (getcwd(base, PATH_MAX) == NULL) + err(1, "couldn't get current directory."); + if (rel2abs(source, base, abssource, PATH_MAX) == NULL) + err(1, "couldn't convert path"); + source = abssource; + } + /* convert source directory into relative name */ + if (rflag) { + if (!isdir) { + if ((p = strrchr(target, '/')) != NULL) { + int col = p - target + 1; + strncpy(path, target, col); + path[col] = 0; + } else { + path[0] = '.'; + path[1] = 0; + } + p = path; + } else + p = target; + if (realpath(p, base) == NULL) + err(1, "%s", p); + if (abs2rel(source, base, relsource, PATH_MAX) == NULL) + err(1, "couldn't convert path"); + source = relsource; + } + } + /* * If the target is a directory (and not a symlink if hflag), * append the source's name. @@ -351,8 +404,8 @@ usage(void) { (void)fprintf(stderr, "%s\n%s\n%s\n", - "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]", - " ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir", + "usage: ln [-s [a|r] [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]", + " ln [-s [a|r] [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir", " link source_file target_file"); exit(1); } Index: lib/libutil/Makefile =================================================================== --- lib/libutil/Makefile +++ lib/libutil/Makefile @@ -7,16 +7,16 @@ .include LIB= util -SHLIB_MAJOR= 9 +SHLIB_MAJOR= 10 -SRCS= _secure_path.c auth.c expand_number.c flopen.c fparseln.c gr_util.c \ +SRCS= _secure_path.c abs2rel.c auth.c expand_number.c flopen.c fparseln.c gr_util.c \ hexdump.c humanize_number.c kinfo_getfile.c \ kinfo_getallproc.c kinfo_getproc.c kinfo_getvmmap.c \ kinfo_getvmobject.c kld.c \ login_auth.c login_cap.c \ login_class.c login_crypt.c login_ok.c login_times.c login_tty.c \ pidfile.c property.c pty.c pw_util.c quotafile.c realhostname.c \ - stub.c trimdomain.c uucplock.c + rel2abs.c stub.c trimdomain.c uucplock.c INCS= libutil.h login_cap.h CFLAGS+= -DLIBC_SCCS @@ -27,13 +27,13 @@ CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../libc/gen/ -MAN+= expand_number.3 flopen.3 fparseln.3 hexdump.3 \ +MAN+= abs2rel.3 expand_number.3 flopen.3 fparseln.3 hexdump.3 \ humanize_number.3 kinfo_getallproc.3 kinfo_getfile.3 \ kinfo_getproc.3 kinfo_getvmmap.3 kinfo_getvmobject.3 kld.3 \ login_auth.3 login_cap.3 \ login_class.3 login_ok.3 login_times.3 login_tty.3 pidfile.3 \ property.3 pty.3 quotafile.3 realhostname.3 realhostname_sa.3 \ - _secure_path.3 trimdomain.3 uucplock.3 pw_util.3 + rel2abs.3 _secure_path.3 trimdomain.3 uucplock.3 pw_util.3 MAN+= login.conf.5 MLINKS+= kld.3 kld_isloaded.3 kld.3 kld_load.3 MLINKS+=login_auth.3 auth_cat.3 login_auth.3 auth_checknologin.3 Index: lib/libutil/abs2rel.3 =================================================================== --- /dev/null +++ lib/libutil/abs2rel.3 @@ -0,0 +1,136 @@ +.\" +.\" Copyright (c) 1997 Shigio Yamaguchi. All rights reserved. +.\" Copyright (c) 1999 Tama Communications Corporation. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd Dec 15, 1997" +.Dt ABS2REL 3 +.Os +.Sh NAME +.Nm abs2rel +.Nd make a relative path name from an absolute path name +.Sh SYNOPSIS +.Ft "char *" +.Fn abs2rel "const char *path" "const char *base" "char *result" "size_t size" +.Sh DESCRIPTION +The +.Fn abs2rel +function makes a relative path name from an absolute path name +.Fa path +based on a directory +.Fa base +and copies the resulting path name into the memory referenced by +.Fa result . +The +.Fa result +argument must refer to a buffer capable of storing at least +.Fa size +characters. + +The resulting path name may include symbolic links. +The +.Fn abs2rel +function doesn't check whether or not any path exists. +.Sh "RETURN VALUES" +The +.Fn abs2rel +function returns relative path name on success. +If an error occurs, +it returns +.Dv NULL . +.Sh ERRORS +The +.Fn abs2rel +function may fail and set the external variable +.Va errno +to indicate the error. +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa base +directory isn't an absolute path name or the +.Fa size +argument is zero. +.It Bq Er ERANGE +The +.Fa size +argument is greater than zero but smaller than the length of the pathname plus 1. +.Sh EXAMPLE + char result[MAXPATHLEN]; + char *path = abs2rel("/usr/src/sys", "/usr/local/lib", result, MAXPATHLEN); + +yields: + + path == "../../src/sys" + +Similarly, + + path1 = abs2rel("/usr/src/sys", "/usr", result, MAXPATHLEN); + path2 = abs2rel("/usr/src/sys", "/usr/src/sys", result, MAXPATHLEN); + +yields: + + path1 == "src/sys" + path2 == "." + +.Sh BUGS +If the +.Fa base +directory includes symbolic links, +the +.Fn abs2rel +function produces the wrong path. +For example, if '/sys' is a symbolic link to '/usr/src/sys', + + char *path = abs2rel("/usr/local/lib", "/sys", result, MAXPATHLEN); + +yields: + + path == "../usr/local/lib" /* It's wrong!! */ + +You should convert the base directory into a real path in advance. +.Pp + + path = abs2rel("/sys/kern", realpath("/sys", resolvedname), result, MAXPATHLEN); + +yields: + + path == "../../../sys/kern" /* It's correct but ... */ + +That is correct, but a little redundant. If you wish get the simple +answer 'kern', do the following. + + path = abs2rel(realpath("/sys/kern", r1), realpath("/sys", r2), + result, MAXPATHLEN); + +The +.Fn realpath +function assures correct result, but don't forget that +.Fn realpath +requires that all but the last component of the path exist. +.Sh "SEE ALSO" +.Xr rel2abs 3 +.Sh AUTHORS +Shigio Yamaguchi (shigio@tamacom.com) Index: lib/libutil/abs2rel.c =================================================================== --- /dev/null +++ lib/libutil/abs2rel.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1997 Shigio Yamaguchi. All rights reserved. + * Copyright (c) 1999 Tama Communications Corporation. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#include +#include +#include +#include /* prototypes */ +/* + * abs2rel: convert an absolute path name into relative. + * + * i) path absolute path + * i) base base directory (must be absolute path) + * o) result result buffer + * i) size size of result buffer + * r) != NULL: relative path + * == NULL: error + */ +char * +abs2rel(const char *path, const char *base, char *result, const size_t size) +{ + const char *pp, *bp, *branch; + /* + * endp points the last position which is safe in the result buffer. + */ + const char *endp = result + size - 1; + char *rp; + + if (*path != '/') { + if (strlen(path) >= size) + goto erange; + strcpy(result, path); + goto finish; + } else if (*base != '/' || !size) { + errno = EINVAL; + return (NULL); + } else if (size == 1) + goto erange; + /* + * seek to branched point. + */ + branch = path; + for (pp = path, bp = base; *pp && *bp && *pp == *bp; pp++, bp++) + if (*pp == '/') + branch = pp; + if ((*pp == 0 || (*pp == '/' && *(pp + 1) == 0)) && + (*bp == 0 || (*bp == '/' && *(bp + 1) == 0))) { + rp = result; + *rp++ = '.'; + if (*pp == '/' || *(pp - 1) == '/') + *rp++ = '/'; + if (rp > endp) + goto erange; + *rp = 0; + goto finish; + } + if ((*pp == 0 && *bp == '/') || (*pp == '/' && *bp == 0)) + branch = pp; + /* + * up to root. + */ + rp = result; + for (bp = base + (branch - path); *bp; bp++) + if (*bp == '/' && *(bp + 1) != 0) { + if (rp + 3 > endp) + goto erange; + *rp++ = '.'; + *rp++ = '.'; + *rp++ = '/'; + } + if (rp > endp) + goto erange; + *rp = 0; + /* + * down to leaf. + */ + if (*branch) { + if (rp + strlen(branch + 1) > endp) + goto erange; + strcpy(rp, branch + 1); + } else + *--rp = 0; +finish: + return result; +erange: + errno = ERANGE; + return (NULL); +} Index: lib/libutil/libutil.h =================================================================== --- lib/libutil/libutil.h +++ lib/libutil/libutil.h @@ -4,6 +4,9 @@ * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * + * pathconv elements Copyright (c) 1997 Shigio Yamaguchi. All rights reserved. + * pathconv elements Copyright (c) 1999 Tama Communications Corporation. All rights reserved. + * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 @@ -249,4 +252,9 @@ #define UU_LOCK_TRY_ERR (-6) #define UU_LOCK_OWNER_ERR (-7) -#endif /* !_LIBUTIL_H_ */ +/* from libpathconv by Shigio Yamaguchi. http://www.tamacom.com/pathconvert.html */ +char * rel2abs(const char *path, const char *base, char *result, const size_t size); +char * abs2rel(const char *path, const char *base, char *result, const size_t size); + +#endif /*_LIBUTIL_H_*/ + Index: lib/libutil/rel2abs.3 =================================================================== --- /dev/null +++ lib/libutil/rel2abs.3 @@ -0,0 +1,98 @@ +.\" +.\" Copyright (c) 1997 Shigio Yamaguchi. All rights reserved. +.\" Copyright (c) 1999 Tama Communications Corporation. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd Dec 3, 1997" +.Dt REL2ABS 3 +.Os +.Sh NAME +.Nm rel2abs +.Nd make an absolute path name from a relative path name +.Sh SYNOPSIS +.Ft "char *" +.Fn rel2abs "const char *path" "const char *base" "char *result" "size_t size" +.Sh DESCRIPTION +The +.Fn rel2abs +function makes an absolute path name from a relative path name +.Fa path +based on a directory +.Fa base +and copies the resulting path name into the memory referenced by +.Fa result . +The +.Fa result +argument must refer to a buffer capable of storing at least +.Fa size +character + +The resulting path name may include symbolic links. +.Fn abs2rel +doesn't check whether or not any path exists. +.Sh "RETURN VALUES" +The +.Fn rel2abs +function returns absolute path name on success. +If an error occurs, it returns +.Dv NULL . +.Sh ERRORS +The +.Fn rel2abs +function may fail and set the external variable +.Va errno +to indicate the error. +.Bl -tag -width Er +.It Bq Er EINVAL +The +.Fa base +directory isn't an absolute path name or the +.Fa size +argument is zero. +.It Bq Er ERANGE +The +.Fa size +argument is greater than zero but smaller than the length of the pathname plus 1 +.Sh EXAMPLE + char result[MAXPATHLEN]; + char *path = rel2abs("../../src/sys", "/usr/local/lib", result, MAXPATHLEN); + +yields: + + path == "/usr/src/sys" + +Similarly, + + path1 = rel2abs("src/sys", "/usr", result, MAXPATHLEN); + path2 = rel2abs(".", "/usr/src/sys", result, MAXPATHLEN); + +yields: + + path1 == "/usr/src/sys" + path2 == "/usr/src/sys" +.Sh "SEE ALSO" +.Xr abs2rel 3 +.Sh AUTHORS +Shigio Yamaguchi (shigio@tamacom.com) Index: lib/libutil/rel2abs.c =================================================================== --- /dev/null +++ lib/libutil/rel2abs.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 1997 Shigio Yamaguchi. All rights reserved. + * Copyright (c) 1999 Tama Communications Corporation. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#include +#include +#include +#include /* prototypes */ +/* + * rel2abs: convert an relative path name into absolute. + * + * i) path relative path + * i) base base directory (must be absolute path) + * o) result result buffer + * i) size size of result buffer + * r) != NULL: absolute path + * == NULL: error + */ +char * +rel2abs(const char *path, const char *base, char *result, const size_t size) +{ + const char *pp, *bp; + /* + * endp points the last position which is safe in the result buffer. + */ + const char *endp = result + size - 1; + char *rp; + size_t length; + + if (*path == '/') { + if (strlen(path) >= size) + goto erange; + strcpy(result, path); + goto finish; + } else if (*base != '/' || !size) { + errno = EINVAL; + return (NULL); + } else if (size == 1) + goto erange; + + length = strlen(base); + + if (!strcmp(path, ".") || !strcmp(path, "./")) { + if (length >= size) + goto erange; + strcpy(result, base); + /* + * rp points the last char. + */ + rp = result + length - 1; + /* + * remove the last '/'. + */ + if (*rp == '/') { + if (length > 1) + *rp = 0; + } else + rp++; + /* rp point NULL char */ + if (*++path == '/') { + /* + * Append '/' to the tail of path name. + */ + *rp++ = '/'; + if (rp > endp) + goto erange; + *rp = 0; + } + goto finish; + } + bp = base + length; + if (*(bp - 1) == '/') + --bp; + /* + * up to root. + */ + for (pp = path; *pp && *pp == '.'; ) { + if (!strncmp(pp, "../", 3)) { + pp += 3; + while (bp > base && *--bp != '/') + ; + } else if (!strncmp(pp, "./", 2)) { + pp += 2; + } else if (!strncmp(pp, "..\0", 3)) { + pp += 2; + while (bp > base && *--bp != '/') + ; + } else + break; + } + /* + * down to leaf. + */ + length = bp - base; + if (length >= size) + goto erange; + strncpy(result, base, length); + rp = result + length; + if (*pp || *(pp - 1) == '/' || length == 0) + *rp++ = '/'; + if (rp + strlen(pp) > endp) + goto erange; + strcpy(rp, pp); +finish: + return result; +erange: + errno = ERANGE; + return (NULL); +} Index: lib/libutil/tests/Makefile =================================================================== --- lib/libutil/tests/Makefile +++ lib/libutil/tests/Makefile @@ -3,6 +3,8 @@ TAP_TESTS_C+= flopen_test TAP_TESTS_C+= grp_test TAP_TESTS_C+= humanize_number_test +TAP_TESTS_C+= abs2rel +TAP_TESTS_C+= rel2abs .if ${MACHINE_CPUARCH} != "aarch64" # PR202304: pidfile_test hangs on arm64 TAP_TESTS_C+= pidfile_test .endif Index: lib/libutil/tests/abs2rel.c =================================================================== --- /dev/null +++ lib/libutil/tests/abs2rel.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1997 Shigio Yamaguchi. All rights reserved. + * Copyright (c) 1999 Tama Communications Corporation. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + char result[MAXPATHLEN]; + char cwd[MAXPATHLEN]; + + if (argc < 2) { + fprintf(stderr, "usage: abs2rel path [base]\n"); + exit(1); + } + if (argc == 2) { + if (!getcwd(cwd, MAXPATHLEN)) { + fprintf(stderr, "cannot get current directory.\n"); + exit(1); + } + } else + strcpy(cwd, argv[2]); + + if (abs2rel(argv[1], cwd, result, MAXPATHLEN)) { + printf("%s\n", result); + } else + printf("ERROR\n"); + exit(0); +} Index: lib/libutil/tests/pathconvtest.sh =================================================================== --- /dev/null +++ lib/libutil/tests/pathconvtest.sh @@ -0,0 +1,139 @@ +#!/bin/sh +# +# Copyright (c) 1997 Shigio Yamaguchi. All rights reserved. +# Copyright (c) 1999 Tama Communications Corporation. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +# +# $FreeBSD$ +# + +# +# Test script for abs2rel(3) and rel2abs(3). +# +# +# target base directory result +# -------------------------------------- + +ABS2REL=" . / . + a/b/c / a/b/c + a/b/c /a a/b/c + /a/b/c a ERROR +" + +REL2ABS=" . / / + ./ / / + /a/b/c / /a/b/c + /a/b/c /a /a/b/c + a/b/c a ERROR + .. /a / + ../ /a / + ../.. /a / + ../../ /a / + ../../.. /a / + ../../../ /a / + ../b /a /b + ../b/ /a /b/ + ../../b /a /b + ../../b/ /a /b/ + ../../../b /a /b + ../../../b/ /a /b/ + ../b/c /a /b/c + ../b/c/ /a /b/c/ + ../../b/c /a /b/c + ../../b/c/ /a /b/c/ + ../../../b/c /a /b/c + ../../../b/c/ /a /b/c/ +" +COMMON=" + /a/b/c /a/b/c . + /a/b/c /a/b/ c + /a/b/c /a/b c + /a/b/c /a/ b/c + /a/b/c /a b/c + /a/b/c / a/b/c + /a/b/c /a/b/c . + /a/b/c /a/b/c/ . + /a/b/c/ /a/b/c ./ + /a/b/ /a/b/c ../ + /a/b /a/b/c .. + /a/ /a/b/c ../../ + /a /a/b/c ../.. + / /a/b/c ../../../ + /a/b/c /a/b/z ../c + /a/b/c /a/y/z ../../b/c + /a/b/c /x/y/z ../../../a/b/c +" + +echo -n "TEST start " +LOGFILE="/tmp/pathconv.test" +date >$LOGFILE +CNT=0 + +log () { + echo " $(date): $* " >>$LOGFILE +} + +do_test_fwd () { + PROG=$1 + shift + while [ $# != 0 ] + do + RESULT=$( $PROG $1 $2 ) + if [ "$3" = "$RESULT" ] + then + echo -n '.' + else + echo -n 'X' + log "$PROG $1 $2 -> '$RESULT' (It should be '$3')" + CNT=$(( $CNT + 1 )) + fi + shift; shift; shift + done +} + +do_test_rev () { + PROG=$1 + shift + while [ $# != 0 ] + do + RESULT=$( $PROG $3 $2 ) + if [ "$1" = "$RESULT" ] + then + echo -n '.' + else + echo -n 'X' + log "$PROG $3 $2 -> '$RESULT' (It should be '$1')" + CNT=$(( $CNT + 1 )) + fi + shift; shift; shift + done +} + + +do_test_fwd ./abs2rel $ABS2REL +do_test_fwd ./abs2rel $COMMON +do_test_fwd ./rel2abs $REL2ABS +do_test_rev ./rel2abs $COMMON + +echo " $CNT errors detected."; +cat $LOGFILE Index: lib/libutil/tests/rel2abs.c =================================================================== --- /dev/null +++ lib/libutil/tests/rel2abs.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1997 Shigio Yamaguchi. All rights reserved. + * Copyright (c) 1999 Tama Communications Corporation. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ +#include +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + char result[MAXPATHLEN]; + char cwd[MAXPATHLEN]; + + if (argc < 2) { + fprintf(stderr, "usage: rel2abs path [base]\n"); + exit(1); + } + if (argc == 2) { + if (!getcwd(cwd, MAXPATHLEN)) { + fprintf(stderr, "cannot get current directory.\n"); + exit(1); + } + } else + strcpy(cwd, argv[2]); + + if (rel2abs(argv[1], cwd, result, MAXPATHLEN)) { + printf("%s\n", result); + } else + printf("ERROR\n"); + exit(0); +}