ddnet/docs/tool/Modules/NaturalDocs/Version.pm
2008-08-02 08:21:29 +00:00

385 lines
12 KiB
Perl

###############################################################################
#
# Package: NaturalDocs::Version
#
###############################################################################
#
# A package for handling version information. What? That's right. Although it should be easy and obvious, version numbers
# need to be dealt with in a variety of formats, plus there's compatibility with older releases which handled it differently. I
# wanted to centralize the code after it started getting complicated. So there ya go.
#
###############################################################################
# This file is part of Natural Docs, which is Copyright (C) 2003-2008 Greg Valure
# Natural Docs is licensed under the GPL
use strict;
use integer;
package NaturalDocs::Version;
###############################################################################
# Group: Functions
#
# Function: ToString
#
# Converts a <VersionInt> to a string.
#
sub ToString #(VersionInt version) => string
{
my ($self, $version) = @_;
my ($major, $minor, $month, $day, $year) = $self->ToValues($version);
if ($minor % 10 == 0)
{ $minor /= 10; };
if ($day)
{ return sprintf('Development Release %02d-%02d-%d (%d.%d base)', $month, $day, $year, $major, $minor); }
else
{ return $major . '.' . $minor; };
};
#
# Function: FromString
#
# Converts a version string to a <VersionInt>.
#
sub FromString #(string string) => VersionInt
{
my ($self, $string) = @_;
if ($string eq '1')
{
return $self->FromValues(0, 91, 0, 0, 0); # 0.91
}
else
{
my ($major, $minor, $month, $day, $year);
if ($string =~ /^(\d{1,2})\.(\d{1,2})$/)
{
($major, $minor) = ($1, $2);
($month, $day, $year) = (0, 0, 0);
}
elsif ($string =~ /^Development Release (\d{1,2})-(\d{1,2})-(\d\d\d\d) \((\d{1,2})\.(\d{1,2}) base\)$/)
{
($month, $day, $year, $major, $minor) = ($1, $2, $3, $4, $5);
# We have to do sanity checking because these can come from user-editable text files. The version numbers should
# already be constrained simply by being forced to have only two digits.
if ($month > 12 || $month < 1 || $day > 31 || $day < 1 || $year > 2255 || $year < 2000)
{ die 'The version string ' . $string . " doesn't have a valid date.\n"; };
}
else
{
die 'The version string ' . $string . " isn't in a recognized format.\n";
};
if (length $minor == 1)
{ $minor *= 10; };
return $self->FromValues($major, $minor, $month, $day, $year);
};
};
#
# Function: ToTextFile
#
# Writes a <VersionInt> to a text file.
#
# Parameters:
#
# fileHandle - The handle of the file to write it to. It should be at the correct location.
# version - The <VersionInt> to write.
#
sub ToTextFile #(handle fileHandle, VersionInt version)
{
my ($self, $fileHandle, $version) = @_;
print $fileHandle $self->ToString($version) . "\n";
};
#
# Function: FromTextFile
#
# Retrieves a <VersionInt> from a text file.
#
# Parameters:
#
# fileHandle - The handle of the file to read it from. It should be at the correct location.
#
# Returns:
#
# The <VersionInt>.
#
sub FromTextFile #(handle fileHandle) => VersionInt
{
my ($self, $fileHandle) = @_;
my $version = <$fileHandle>;
::XChomp(\$version);
return $self->FromString($version);
};
#
# Function: ToBinaryFile
#
# Writes a <VersionInt> to a binary file.
#
# Parameters:
#
# fileHandle - The handle of the file to write it to. It should be at the correct location.
# version - The <VersionInt> to write.
#
sub ToBinaryFile #(handle fileHandle, VersionInt version)
{
my ($self, $fileHandle, $version) = @_;
my ($major, $minor, $month, $day, $year) = $self->ToValues($version);
# 1.35 development releases are encoded as 1.36. Everything else is literal.
if ($day && $major == 1 && $minor == 35)
{ $minor = 36; };
print $fileHandle pack('CC', $major, $minor);
# Date fields didn't exist with 1.35 stable and earlier. 1.35 development releases are encoded as 1.36, so this works.
if ($major > 1 || ($major == 1 && $minor > 35))
{
if ($day)
{ $year -= 2000; };
print $fileHandle pack('CCC', $month, $day, $year);
};
};
#
# Function: FromBinaryFile
#
# Retrieves a <VersionInt> from a binary file.
#
# Parameters:
#
# fileHandle - The handle of the file to read it from. It should be at the correct location.
#
# Returns:
#
# The <VersionInt>.
#
sub FromBinaryFile #(handle fileHandle) => VersionInt
{
my ($self, $fileHandle) = @_;
my ($major, $minor, $month, $day, $year);
my $raw;
read($fileHandle, $raw, 2);
($major, $minor) = unpack('CC', $raw);
# 1.35 stable is the last release without the date fields. 1.35 development releases are encoded as 1.36, so this works.
if ($major > 1 || ($major == 1 && $minor > 35))
{
read($fileHandle, $raw, 3);
($month, $day, $year) = unpack('CCC', $raw);
if ($day)
{ $year += 2000; };
}
else
{ ($month, $day, $year) = (0, 0, 0); };
# Fix the 1.35 development release special encoding.
if ($major == 1 && $minor == 36)
{ $minor = 35; };
return $self->FromValues($major, $minor, $month, $day, $year);
};
#
# Function: ToValues
#
# Converts a <VersionInt> to the array ( major, minor, month, day, year ). The minor version will be in two digit form, so x.2
# will return 20. The date fields will be zero for stable releases.
#
sub ToValues #(VersionInt version) => ( int, int, int, int, int )
{
my ($self, $version) = @_;
my $major = ($version & 0x00003F80) >> 7;
my $minor = ($version & 0x0000007F);
my $month = ($version & 0x00780000) >> 19;
my $day = ($version & 0x0007C000) >> 14;
my $year = ($version & 0x7F800000) >> 23;
if ($year)
{ $year += 2000; };
return ( $major, $minor, $month, $day, $year );
};
#
# Function: FromValues
#
# Returns a <VersionInt> created from the passed values.
#
# Parameters:
#
# major - The major version number. For development releases, it should be the stable version it's based off of.
# minor - The minor version number. It should always be two digits, so x.2 should pass 20. For development
# releases, it should be the stable version it's based off of.
# month - The numeric month of the development release. For stable releases it should be zero.
# day - The day of the development release. For stable releases it should be zero.
# year - The year of the development release. For stable releases it should be zero.
#
# Returns:
#
# The <VersionInt>.
#
sub FromValues #(int major, int minor, int month, int day, int year) => VersionInt
{
my ($self, $major, $minor, $month, $day, $year) = @_;
if ($day)
{ $year -= 2000; };
return ($major << 7) + ($minor) + ($month << 19) + ($day << 14) + ($year << 23);
};
#
# Function: CheckFileFormat
#
# Checks if a file's format is compatible with the current release.
#
# - If the application is a development release or the file is from one, this only returns true if they are from the exact same
# development release.
# - If neither of them are development releases, this only returns true if the file is from a release between the minimum specified
# and the current version. If there's no minimum it just checks that it's below the current version.
#
# Parameters:
#
# fileVersion - The <VersionInt> of the file format.
# minimumVersion - The minimum <VersionInt> required of the file format. May be undef.
#
# Returns:
#
# Whether the file's format is compatible per the above rules.
#
sub CheckFileFormat #(VersionInt fileVersion, optional VersionInt minimumVersion) => bool
{
my ($self, $fileVersion, $minimumVersion) = @_;
my $appVersion = NaturalDocs::Settings->AppVersion();
if ($self->IsDevelopmentRelease($appVersion) || $self->IsDevelopmentRelease($fileVersion))
{ return ($appVersion == $fileVersion); }
elsif ($minimumVersion && $fileVersion < $minimumVersion)
{ return 0; }
else
{ return ($fileVersion <= $appVersion); };
};
#
# Function: IsDevelopmentRelease
#
# Returns whether the passed <VersionInt> is for a development release.
#
sub IsDevelopmentRelease #(VersionInt version) => bool
{
my ($self, $version) = @_;
# Return if any of the date fields are set.
return ($version & 0x7FFFC000);
};
###############################################################################
# Group: Implementation
#
# About: String Format
#
# Full Releases:
#
# Full releases are in the common major.minor format. Either part can be up to two digits. The minor version is interpreted
# as decimal places, so 1.3 > 1.22. There are no leading or trailing zeroes.
#
# Development Releases:
#
# Development releases are in the format "Development Release mm-dd-yyyy (vv.vv base)" where vv.vv is the version
# number of the full release it's based off of. The month and day will have leading zeroes where applicable. Example:
# "Development Release 07-09-2006 (1.35 base)".
#
# 0.91 and Earlier:
#
# Text files from releases prior to 0.95 had a separate file format version number that was used instead of the application
# version. These were never changed between 0.85 and 0.91, so they are simply "1". Text version numbers that are "1"
# instead of "1.0" will be interpreted as 0.91.
#
#
# About: Integer Format
#
# <VersionInts> are 32-bit values with the bit distribution below.
#
# > s yyyyyyyy mmmm ddddd vvvvvvv xxxxxxx
# > [syyy|yyyy] [ymmm|mddd] [ddvv|vvvv] [vxxx|xxxx]
#
# s - The sign bit. Always zero, so it's always interpreted as positive.
# y - The year bits if it's a development release, zero otherwise. 2000 is added to the value, so the range is from 2000 to 2255.
# m - The month bits if it's a development release, zero otherwise.
# d - The day bits if it's a development release, zero otherwise.
# v - The major version bits. For development releases, it's the last stable version it was based off of.
# x - The minor version bits. It's always stored as two decimals, so x.2 would store 20 here. For development releases, it's the
# last stable version it was based off of.
#
# It's stored with the development release date at a higher significance than the version because we want a stable release to
# always treat a development release as higher than itself, and thus not attempt to read any of the data files. I'm not tracking
# data file formats at the development release level.
#
#
# About: Binary File Format
#
# Current:
#
# Five 8-bit unsigned values, appearing major, minor, month, day, year. Minor is always stored with two digits, so x.2 would
# store 20. Year is stored minus 2000, so 2006 is stored 6. Stable releases store zero for all the date fields.
#
# 1.35 Development Releases:
#
# 1.35-based development releases are stored the same as current releases, but with 1.36 as the version number. This is
# done so previous versions of Natural Docs that didn't include the date fields would still know it's a higher version. There is
# no actual 1.36 release.
#
# 1.35 and Earlier:
#
# Two 8-bit unsigned values, appearing major then minor. Minor is always stored with two digits, so x.2 would store 20.
#
#
# About: Text File Format
#
# In text files, versions are the <String Format> followed by a native line break.
#
1;