@@ -801,6 +801,56 @@ namecheck(const char *name)
801801 return componentcheck (name , component , cp );
802802}
803803
804+ /*
805+ * Create symlink contents suitable for symlinking FROM to TO, as a
806+ * freshly allocated string. FROM should be a relative file name, and
807+ * is relative to the global variable DIRECTORY. TO can be either
808+ * relative or absolute.
809+ */
810+ #ifdef HAVE_SYMLINK
811+ static char *
812+ relname (char const * from , char const * to )
813+ {
814+ size_t i ,
815+ taillen ,
816+ dotdotetcsize ;
817+ size_t dir_len = 0 ,
818+ dotdots = 0 ,
819+ linksize = SIZE_MAX ;
820+ char const * f = from ;
821+ char * result = NULL ;
822+
823+ if (* to == '/' )
824+ {
825+ /* Make F absolute too. */
826+ size_t len = strlen (directory );
827+ bool needslash = len && directory [len - 1 ] != '/' ;
828+
829+ linksize = len + needslash + strlen (from ) + 1 ;
830+ f = result = emalloc (linksize );
831+ strcpy (result , directory );
832+ result [len ] = '/' ;
833+ strcpy (result + len + needslash , from );
834+ }
835+ for (i = 0 ; f [i ] && f [i ] == to [i ]; i ++ )
836+ if (f [i ] == '/' )
837+ dir_len = i + 1 ;
838+ for (; f [i ]; i ++ )
839+ dotdots += f [i ] == '/' && f [i - 1 ] != '/' ;
840+ taillen = i - dir_len ;
841+ dotdotetcsize = 3 * dotdots + taillen + 1 ;
842+ if (dotdotetcsize <= linksize )
843+ {
844+ if (!result )
845+ result = emalloc (dotdotetcsize );
846+ for (i = 0 ; i < dotdots ; i ++ )
847+ memcpy (result + 3 * i , "../" , 3 );
848+ memmove (result + 3 * dotdots , f + dir_len , taillen + 1 );
849+ }
850+ return result ;
851+ }
852+ #endif /* HAVE_SYMLINK */
853+
804854static void
805855dolink (char const * fromfield , char const * tofield , bool staysymlink )
806856{
@@ -844,31 +894,17 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink)
844894 if (link_errno != 0 )
845895 {
846896#ifdef HAVE_SYMLINK
847- const char * s = fromfield ;
848- const char * t ;
849- char * p ;
850- size_t dotdots = 0 ;
851- char * symlinkcontents ;
852- int symlink_errno ;
897+ bool absolute = * fromfield == '/' ;
898+ char * linkalloc = absolute ? NULL : relname (fromfield , tofield );
899+ char const * contents = absolute ? fromfield : linkalloc ;
900+ int symlink_errno = symlink (contents , tofield ) == 0 ? 0 : errno ;
853901
854- do
855- t = s ;
856- while ((s = strchr (s , '/' ))
857- && strncmp (fromfield , tofield , ++ s - fromfield ) == 0 );
858-
859- for (s = tofield + (t - fromfield ); * s ; s ++ )
860- dotdots += * s == '/' ;
861- symlinkcontents = emalloc (3 * dotdots + strlen (t ) + 1 );
862- for (p = symlinkcontents ; dotdots -- != 0 ; p += 3 )
863- memcpy (p , "../" , 3 );
864- strcpy (p , t );
865- symlink_errno = symlink (symlinkcontents , tofield ) == 0 ? 0 : errno ;
866902 if (symlink_errno == ENOENT && !todirs_made )
867903 {
868904 mkdirs (tofield , true);
869- symlink_errno = symlink (symlinkcontents , tofield ) == 0 ? 0 : errno ;
905+ symlink_errno = symlink (contents , tofield ) == 0 ? 0 : errno ;
870906 }
871- free (symlinkcontents );
907+ free (linkalloc );
872908 if (symlink_errno == 0 )
873909 {
874910 if (link_errno != ENOTSUP )
0 commit comments