@@ -789,6 +789,56 @@ namecheck(const char *name)
789789return 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)
832882if (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 ;
854890if (symlink_errno == ENOENT && !todirs_made )
855891{
856892mkdirs (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 );
860896if (symlink_errno == 0 )
861897{
862898if (link_errno != ENOTSUP )