cvsserver: define a tag name character escape mechanism

CVS tags are officially only allowed to use [-_0-9A-Za-f]. Git refs commonly uses other characters, especially [./]. Such characters need to be escaped from CVS in order to be referenced. This just defines functions to escape/unescape names. The functions are not used yet. Signed-off-by: Matthew Ogilvie <mmogilvi_git@miniinfo.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Matthew Ogilvie committed Oct 13, 2012 at 23:42 UTC 51a7e6dbc9b871ddfd73752e1a1b254e38e4336b
1 file changed +91
git-cvsserver.perl
+91
@@ -3807,6 +3807,97 @@ sub gethistorydense
3807 return $result;
3808 }
3809
3810 +=head2 escapeRefName
3811 +
3812 +Apply an escape mechanism to compensate for characters that
3813 +git ref names can have that CVS tags can not.
3814 +
3815 +=cut
3816 +sub escapeRefName
3817 +{
3818 + my($self,$refName)=@_;
3819 +
3820 + # CVS officially only allows [-_A-Za-z0-9] in tag names (or in
3821 + # many contexts it can also be a CVS revision number).
3822 + #
3823 + # Git tags commonly use '/' and '.' as well, but also handle
3824 + # anything else just in case:
3825 + #
3826 + # = "_-s-" For '/'.
3827 + # = "_-p-" For '.'.
3828 + # = "_-u-" For underscore, in case someone wants a literal "_-" in
3829 + # a tag name.
3830 + # = "_-xx-" Where "xx" is the hexadecimal representation of the
3831 + # desired ASCII character byte. (for anything else)
3832 +
3833 + if(! $refName=~/^[1-9][0-9]*(\.[1-9][0-9]*)*$/)
3834 + {
3835 + $refName=~s/_-/_-u--/g;
3836 + $refName=~s/\./_-p-/g;
3837 + $refName=~s%/%_-s-%g;
3838 + $refName=~s/[^-_a-zA-Z0-9]/sprintf("_-%02x-",$1)/eg;
3839 + }
3840 +}
3841 +
3842 +=head2 unescapeRefName
3843 +
3844 +Undo an escape mechanism to compensate for characters that
3845 +git ref names can have that CVS tags can not.
3846 +
3847 +=cut
3848 +sub unescapeRefName
3849 +{
3850 + my($self,$refName)=@_;
3851 +
3852 + # see escapeRefName() for description of escape mechanism.
3853 +
3854 + $refName=~s/_-([spu]|[0-9a-f][0-9a-f])-/unescapeRefNameChar($1)/eg;
3855 +
3856 + # allowed tag names
3857 + # TODO: Perhaps use git check-ref-format, with an in-process cache of
3858 + # validated names?
3859 + if( !( $refName=~m%^[^-][-a-zA-Z0-9_/.]*$% ) ||
3860 + ( $refName=~m%[/.]$% ) ||
3861 + ( $refName=~/\.lock$/ ) ||
3862 + ( $refName=~m%\.\.|/\.|[[\\:?*~]|\@\{% ) ) # matching }
3863 + {
3864 + # Error:
3865 + $log->warn("illegal refName: $refName");
3866 + $refName=undef;
3867 + }
3868 + return $refName;
3869 +}
3870 +
3871 +sub unescapeRefNameChar
3872 +{
3873 + my($char)=@_;
3874 +
3875 + if($char eq "s")
3876 + {
3877 + $char="/";
3878 + }
3879 + elsif($char eq "p")
3880 + {
3881 + $char=".";
3882 + }
3883 + elsif($char eq "u")
3884 + {
3885 + $char="_";
3886 + }
3887 + elsif($char=~/^[0-9a-f][0-9a-f]$/)
3888 + {
3889 + $char=chr(hex($char));
3890 + }
3891 + else
3892 + {
3893 + # Error case: Maybe it has come straight from user, and
3894 + # wasn't supposed to be escaped? Restore it the way we got it:
3895 + $char="_-$char-";
3896 + }
3897 +
3898 + return $char;
3899 +}
3900 +
3901 =head2 in_array()
3902
3903 from Array::PAT - mimics the in_array() function