@@ -789,6 +789,56 @@ namecheck(const char *name)
789789 return componentcheck (name , component , cp );
790790}
791791
792+ /*
793+ * Create symlink contents suitable for symlinking FROM to TO, as a
794+ * freshly allocated string. FROM should be a relative file name, and
795+ * is relative to the global variable DIRECTORY. TO can be either
796+ * relative or absolute.
797+ */
798+ #ifdef HAVE_SYMLINK
799+ static char *
800+ relname (char const * from , char const * to )
801+ {
802+ size_t i ,
803+ taillen ,
804+ dotdotetcsize ;
805+ size_t dir_len = 0 ,
806+ dotdots = 0 ,
807+ linksize = SIZE_MAX ;
808+ char const * f = from ;
809+ char * result = NULL ;
810+
811+ if (* to == '/' )
812+ {
813+ /* Make F absolute too. */
814+ size_t len = strlen (directory );
815+ bool needslash = len && directory [len - 1 ] != '/' ;
816+
817+ linksize = len + needslash + strlen (from ) + 1 ;
818+ f = result = emalloc (linksize );
819+ strcpy (result , directory );
820+ result [len ] = '/' ;
821+ strcpy (result + len + needslash , from );
822+ }
823+ for (i = 0 ; f [i ] && f [i ] == to [i ]; i ++ )
824+ if (f [i ] == '/' )
825+ dir_len = i + 1 ;
826+ for (; f [i ]; i ++ )
827+ dotdots += f [i ] == '/' && f [i - 1 ] != '/' ;
828+ taillen = i - dir_len ;
829+ dotdotetcsize = 3 * dotdots + taillen + 1 ;
830+ if (dotdotetcsize <= linksize )
831+ {
832+ if (!result )
833+ result = emalloc (dotdotetcsize );
834+ for (i = 0 ; i < dotdots ; i ++ )
835+ memcpy (result + 3 * i , "../" , 3 );
836+ memmove (result + 3 * dotdots , f + dir_len , taillen + 1 );
837+ }
838+ return result ;
839+ }
840+ #endif /* HAVE_SYMLINK */
841+
792842static void
793843dolink (char const * fromfield , char const * tofield , bool staysymlink )
794844{
@@ -832,31 +882,17 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink)
832882 if (link_errno != 0 )
833883 {
834884#ifdef HAVE_SYMLINK
835- const char * s = fromfield ;
836- const char * t ;
837- char * p ;
838- size_t dotdots = 0 ;
839- char * symlinkcontents ;
840- int symlink_errno ;
885+ bool absolute = * fromfield == '/' ;
886+ char * linkalloc = absolute ? NULL : relname (fromfield , tofield );
887+ char const * contents = absolute ? fromfield : linkalloc ;
888+ int symlink_errno = symlink (contents , tofield ) == 0 ? 0 : errno ;
841889
842- do
843- t = s ;
844- while ((s = strchr (s , '/' ))
845- && strncmp (fromfield , tofield , ++ s - fromfield ) == 0 );
846-
847- for (s = tofield + (t - fromfield ); * s ; s ++ )
848- dotdots += * s == '/' ;
849- symlinkcontents = emalloc (3 * dotdots + strlen (t ) + 1 );
850- for (p = symlinkcontents ; dotdots -- != 0 ; p += 3 )
851- memcpy (p , "../" , 3 );
852- strcpy (p , t );
853- symlink_errno = symlink (symlinkcontents , tofield ) == 0 ? 0 : errno ;
854890 if (symlink_errno == ENOENT && !todirs_made )
855891 {
856892 mkdirs (tofield , true);
857- symlink_errno = symlink (symlinkcontents , tofield ) == 0 ? 0 : errno ;
893+ symlink_errno = symlink (contents , tofield ) == 0 ? 0 : errno ;
858894 }
859- free (symlinkcontents );
895+ free (linkalloc );
860896 if (symlink_errno == 0 )
861897 {
862898 if (link_errno != ENOTSUP )
0 commit comments