File indexing completed on 2024-04-28 04:49:23

0001 /*
0002     SPDX-FileCopyrightText: 1998-2007 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "k3bversion.h"
0007 
0008 #include <QDebug>
0009 #include <QRegExp>
0010 #include <QRegularExpression>
0011 #include <QSharedData>
0012 
0013 namespace {
0014     /**
0015      * splits the leading number from s and puts it in num
0016      * the dot is removed and the rest put in suffix
0017      * if s does not start with a digit or the first non-digit char is not a dot
0018      * suffix = s and num = -1 is returned
0019      */
0020     void splitVersionString( const QString& s, int& num, QString& suffix )
0021     {
0022         static const QRegularExpression rx("\\D");
0023         int pos = s.indexOf( rx );
0024         if( pos < 0 ) {
0025             num = s.toInt();
0026             suffix = "";
0027         }
0028         else if( pos == 0 ) {
0029             num = -1;
0030             suffix = s;
0031         }
0032         else {
0033             num = s.left( pos ).toInt();
0034             suffix = s.mid( pos );
0035         }
0036     }
0037 }
0038 
0039 
0040 class K3b::Version::Private : public QSharedData
0041 {
0042 public:
0043     Private()
0044         : m_majorVersion( -1 ),
0045           m_minorVersion( -1 ),
0046           m_patchLevel( -1 ) {
0047     }
0048 
0049     QString m_versionString;
0050     int m_majorVersion;
0051     int m_minorVersion;
0052     int m_patchLevel;
0053     QString m_suffix;
0054 };
0055 
0056 
0057 
0058 K3b::Version::Version()
0059     :d( new Private() )
0060 {
0061 }
0062 
0063 
0064 K3b::Version::Version( const K3b::Version& v )
0065 {
0066     d = v.d;
0067 }
0068 
0069 
0070 K3b::Version::Version( const QString& version )
0071     :d( new Private() )
0072 {
0073     setVersion( version );
0074 }
0075 
0076 
0077 K3b::Version::Version( int majorVersion,
0078                        int minorVersion,
0079                        int patchlevel,
0080                        const QString& suffix )
0081     :d( new Private() )
0082 {
0083     setVersion( majorVersion, minorVersion, patchlevel, suffix );
0084 }
0085 
0086 
0087 K3b::Version::~Version()
0088 {
0089 }
0090 
0091 
0092 K3b::Version& K3b::Version::operator=( const Version& other )
0093 {
0094     d = other.d;
0095     return *this;
0096 }
0097 
0098 
0099 QString K3b::Version::toString() const { return d->m_versionString; }
0100 QString K3b::Version::versionString() const { return d->m_versionString; }
0101 int K3b::Version::majorVersion() const { return d->m_majorVersion; }
0102 int K3b::Version::minorVersion() const { return d->m_minorVersion; }
0103 int K3b::Version::patchLevel() const { return d->m_patchLevel; }
0104 QString K3b::Version::suffix() const { return d->m_suffix; }
0105 
0106 
0107 void K3b::Version::setVersion( const QString& v )
0108 {
0109     QString suffix;
0110     splitVersionString( v.trimmed(), d->m_majorVersion, suffix );
0111     if( d->m_majorVersion >= 0 ) {
0112         if( suffix.startsWith('.') ) {
0113             suffix = suffix.mid( 1 );
0114             splitVersionString( suffix, d->m_minorVersion, suffix );
0115             if( d->m_minorVersion < 0 ) {
0116                 qDebug() << "(K3b::Version) suffix must not start with a dot!";
0117                 d->m_majorVersion = -1;
0118                 d->m_minorVersion = -1;
0119                 d->m_patchLevel = -1;
0120                 d->m_suffix = "";
0121             }
0122             else {
0123                 if( suffix.startsWith('.') ) {
0124                     suffix = suffix.mid( 1 );
0125                     splitVersionString( suffix, d->m_patchLevel, suffix );
0126                     if( d->m_patchLevel < 0 ) {
0127                         qDebug() << "(K3b::Version) suffix must not start with a dot!";
0128                         d->m_majorVersion = -1;
0129                         d->m_minorVersion = -1;
0130                         d->m_patchLevel = -1;
0131                         d->m_suffix = "";
0132                     }
0133                     else {
0134                         d->m_suffix = suffix;
0135                     }
0136                 }
0137                 else {
0138                     d->m_patchLevel = -1;
0139                     d->m_suffix = suffix;
0140                 }
0141             }
0142         }
0143         else {
0144             d->m_minorVersion = -1;
0145             d->m_patchLevel = -1;
0146             d->m_suffix = suffix;
0147         }
0148     }
0149 
0150     d->m_versionString = createVersionString( d->m_majorVersion, d->m_minorVersion, d->m_patchLevel, d->m_suffix );
0151 }
0152 
0153 
0154 bool K3b::Version::isValid() const
0155 {
0156     return (d->m_majorVersion >= 0);
0157 }
0158 
0159 
0160 void K3b::Version::setVersion( int majorVersion,
0161                                int minorVersion,
0162                                int patchlevel,
0163                                const QString& suffix )
0164 {
0165     d->m_majorVersion = majorVersion;
0166     d->m_minorVersion = minorVersion;
0167     d->m_patchLevel = patchlevel;
0168     d->m_suffix = suffix;
0169     d->m_versionString = createVersionString( majorVersion, minorVersion, patchlevel, suffix );
0170 }
0171 
0172 K3b::Version& K3b::Version::operator=( const QString& v )
0173 {
0174     setVersion( v );
0175     return *this;
0176 }
0177 
0178 K3b::Version K3b::Version::simplify() const
0179 {
0180     K3b::Version v( *this );
0181     v.d->m_suffix.truncate(0);
0182     return v;
0183 }
0184 
0185 QString K3b::Version::createVersionString( int majorVersion,
0186                                            int minorVersion,
0187                                            int patchlevel,
0188                                            const QString& suffix )
0189 {
0190     if( majorVersion >= 0 ) {
0191         QString s = QString::number(majorVersion);
0192 
0193         if( minorVersion > -1 ) {
0194             s.append( QString(".%1").arg(minorVersion) );
0195             if( patchlevel > -1 )
0196                 s.append( QString(".%1").arg(patchlevel) );
0197         }
0198 
0199         if( !suffix.isNull() )
0200             s.append( suffix );
0201 
0202         return s;
0203     }
0204     else
0205         return "";
0206 }
0207 
0208 
0209 int K3b::Version::compareSuffix( const QString& suffix1, const QString& suffix2 )
0210 {
0211     static QRegExp rcRx( "rc(\\d+)" );
0212     static QRegExp preRx( "pre(\\d+)" );
0213     static QRegExp betaRx( "beta(\\d+)" );
0214     static QRegExp alphaRx( "a(?:lpha)?(\\d+)" );
0215 
0216     // first we check if one of the suffixes (or both are empty) because that case if simple
0217     if( suffix1.isEmpty() ) {
0218         if( suffix2.isEmpty() )
0219             return 0;
0220         else
0221             return 1; // empty greater than the non-empty (should we treat something like 1.0a as greater than 1.0?)
0222     }
0223     else if( suffix2.isEmpty() )
0224         return -1;
0225 
0226     // now search for our special suffixes
0227     if( rcRx.exactMatch( suffix1 ) ) {
0228         int v1 = rcRx.cap(1).toInt();
0229 
0230         if( rcRx.exactMatch( suffix2 ) ) {
0231             int v2 = rcRx.cap(1).toInt();
0232             return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
0233         }
0234         else if( preRx.exactMatch( suffix2 ) ||
0235                  betaRx.exactMatch( suffix2 ) ||
0236                  alphaRx.exactMatch( suffix2 ) )
0237             return 1; // rc > than all the others
0238         else
0239             return QString::compare( suffix1, suffix2 );
0240     }
0241 
0242     else if( preRx.exactMatch( suffix1 ) ) {
0243         int v1 = preRx.cap(1).toInt();
0244 
0245         if( rcRx.exactMatch( suffix2 ) ) {
0246             return -1; // pre is less than rc
0247         }
0248         else if( preRx.exactMatch( suffix2 ) ) {
0249             int v2 = preRx.cap(1).toInt();
0250             return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
0251         }
0252         else if( betaRx.exactMatch( suffix2 ) ||
0253                  alphaRx.exactMatch( suffix2 ) )
0254             return 1; // pre is greater than beta or alpha
0255         else
0256             return QString::compare( suffix1, suffix2 );
0257     }
0258 
0259     else if( betaRx.exactMatch( suffix1 ) ) {
0260         int v1 = betaRx.cap(1).toInt();
0261 
0262         if( rcRx.exactMatch( suffix2 ) ||
0263             preRx.exactMatch( suffix2 ) )
0264             return -1; // beta is less than rc or pre
0265         else if( betaRx.exactMatch( suffix2 ) ) {
0266             int v2 = betaRx.cap(1).toInt();
0267             return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
0268         }
0269         else if( alphaRx.exactMatch( suffix2 ) )
0270             return 1; // beta is greater then alpha
0271         else
0272             return QString::compare( suffix1, suffix2 );
0273     }
0274 
0275     else if( alphaRx.exactMatch( suffix1 ) ) {
0276         int v1 = alphaRx.cap(1).toInt();
0277 
0278         if( rcRx.exactMatch( suffix2 ) ||
0279             preRx.exactMatch( suffix2 ) ||
0280             betaRx.exactMatch( suffix2 ) )
0281             return -1; // alpha is less than all the others
0282         else if( alphaRx.exactMatch( suffix2 ) ) {
0283             int v2 = alphaRx.cap(1).toInt();
0284             return ( v1 == v2 ? 0 : ( v1 < v2 ? -1 : 1 ) );
0285         }
0286         else
0287             return QString::compare( suffix1, suffix2 );
0288     }
0289 
0290     else
0291         return QString::compare( suffix1, suffix2 );
0292 }
0293 
0294 
0295 bool K3b::operator<( const K3b::Version& v1, const K3b::Version& v2 )
0296 {
0297     // both version objects need to be valid
0298 
0299     if( v1.majorVersion() == v2.majorVersion() ) {
0300 
0301         // 1 == 1.0
0302         if( ( v1.minorVersion() == v2.minorVersion() )
0303             ||
0304             ( v1.minorVersion() == -1 && v2.minorVersion() == 0 )
0305             ||
0306             ( v2.minorVersion() == -1 && v1.minorVersion() == 0 )
0307             )
0308         {
0309             // 1.0 == 1.0.0
0310             if( ( v1.patchLevel() == v2.patchLevel() )
0311                 ||
0312                 ( v1.patchLevel() == -1 && v2.patchLevel() == 0 )
0313                 ||
0314                 ( v2.patchLevel() == -1 && v1.patchLevel() == 0 )
0315                 )
0316             {
0317                 return K3b::Version::compareSuffix( v1.suffix(), v2.suffix() ) < 0;
0318             }
0319             else
0320                 return ( v1.patchLevel() < v2.patchLevel() );
0321         }
0322         else
0323             return ( v1.minorVersion() < v2.minorVersion() );
0324     }
0325     else
0326         return ( v1.majorVersion() < v2.majorVersion() );
0327 }
0328 
0329 
0330 bool K3b::operator>( const K3b::Version& v1, const K3b::Version& v2 )
0331 {
0332     return operator<( v2, v1 );
0333 }
0334 
0335 
0336 bool K3b::operator==( const K3b::Version& v1, const K3b::Version& v2 )
0337 {
0338     return ( v1.majorVersion() == v2.majorVersion() &&
0339              v1.minorVersion() == v2.minorVersion() &&
0340              v1.patchLevel() == v2.patchLevel() &&
0341              K3b::Version::compareSuffix( v1.suffix(), v2.suffix() ) == 0 );
0342 }
0343 
0344 
0345 bool K3b::operator<=( const K3b::Version& v1, const K3b::Version& v2 )
0346 {
0347     return ( operator<( v1, v2 ) || operator==( v1, v2 ) );
0348 }
0349 
0350 
0351 bool K3b::operator>=( const K3b::Version& v1, const K3b::Version& v2 )
0352 {
0353     return ( operator>( v1, v2 ) || operator==( v1, v2 ) );
0354 }