1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 
21 #include "sunversion.hxx"
22 #include <osl/thread.h>
23 #include <rtl/character.hxx>
24 #include <string.h>
25 #if OSL_DEBUG_LEVEL >= 2
26 #include <osl/diagnose.h>
27 #include "diagnostics.h"
28 #endif
29 namespace jfw_plugin  { //stoc_javadetect
30 
31 
32 #if OSL_DEBUG_LEVEL >= 2
33 class SelfTest
34 {
35 public:
36     SelfTest();
37 } test;
38 #endif
39 
SunVersion(std::u16string_view usVer)40 SunVersion::SunVersion(std::u16string_view usVer):
41     m_nUpdateSpecial(0), m_preRelease(Rel_NONE)
42 {
43     OString sVersion= OUStringToOString(usVer, osl_getThreadTextEncoding());
44     m_bValid = init(sVersion.getStr());
45 }
SunVersion(const char * szVer)46 SunVersion::SunVersion(const char * szVer):
47     m_nUpdateSpecial(0), m_preRelease(Rel_NONE)
48 {
49     m_bValid = init(szVer);
50 }
51 
52 
53 /**Format major.minor.maintenance_update
54  */
init(const char * szVersion)55 bool SunVersion::init(const char *szVersion)
56 {
57     if (!szVersion || szVersion[0] == '\0')
58         return false;
59 
60     //first get the major,minor,maintenance
61     const char * pLast = szVersion;
62     const char * pCur = szVersion;
63     //pEnd point to the position after the last character
64     const char * pEnd = szVersion + strlen(szVersion);
65     // 0 = major, 1 = minor, 2 = maintenance, 3 = update
66     int nPart = 0;
67     // position within part beginning with 0
68     int nPartPos = 0;
69     char buf[128];
70 
71     //char must me a number 0 - 999 and no leading
72     while (true)
73     {
74         if (pCur < pEnd && rtl::isAsciiDigit(static_cast<unsigned char>(*pCur)))
75         {
76             pCur ++;
77             nPartPos ++;
78         }
79         //if  correct separator then form integer
80         else if (
81             (nPartPos != 0) // prevents: ".4.1", "..1", part must start with digit
82             && (
83                 //separators after maintenance (1.4.1_01, 1.4.1-beta, or 1.4.1)
84                 (pCur == pEnd || *pCur == '_' || *pCur == '-')
85                 ||
86                 //separators between major-minor and minor-maintenance
87                 (nPart < 2 && *pCur == '.') )
88             && (
89                 //prevent 1.4.0. 1.4.0-
90                 pCur + 1 != pEnd
91                 || rtl::isAsciiDigit(static_cast<unsigned char>(*pCur))) )
92         {
93             bool afterMaint = pCur == pEnd || *pCur == '_' || *pCur == '-';
94 
95             int len = pCur - pLast;
96             if (len >= 127)
97                 return false;
98 
99             strncpy(buf, pLast, len);
100             buf[len] = 0;
101             pCur ++;
102             pLast = pCur;
103 
104             m_arVersionParts[nPart] = atoi(buf);
105 
106             if (afterMaint)
107                 nPart = 2;
108             nPart ++;
109             nPartPos = 0;
110             if (nPart == 3)
111                 break;
112 
113             //check next character
114             if (! ( (pCur < pEnd)
115                     && ( (nPart < 3)
116                          && rtl::isAsciiDigit(
117                              static_cast<unsigned char>(*pCur)))))
118                 return false;
119         }
120         else
121         {
122             return false;
123         }
124     }
125     if (pCur >= pEnd)
126         return true;
127     //We have now 1.4.1. This can be followed by _01, -beta, etc.
128     // _01 (update) According to docu must not be followed by any other
129     //characters, but on Solaris 9 we have a 1.4.1_01a!!
130     if (* (pCur - 1) == '_')
131     {// _01, _02
132         // update is the last part _01, _01a, part 0 is the digits parts and 1 the trailing alpha
133         while (true)
134         {
135             if (pCur <= pEnd)
136             {
137                 if ( ! rtl::isAsciiDigit(static_cast<unsigned char>(*pCur)))
138                 {
139                     //1.8.0_102-, 1.8.0_01a,
140                     size_t len = pCur - pLast;
141                     if (len > sizeof(buf) - 1)
142                         return false;
143                     //we've got the update: 01, 02 etc
144                     strncpy(buf, pLast, len);
145                     buf[len] = 0;
146                     m_arVersionParts[nPart] = atoi(buf);
147                     if (pCur == pEnd)
148                     {
149                         break;
150                     }
151                     if (*pCur == 'a' && (pCur + 1) == pEnd)
152                     {
153                         //check if it s followed by a simple "a" (not specified)
154                         m_nUpdateSpecial = *pCur;
155                         break;
156                     }
157                     else if (*pCur == '-' && pCur < pEnd)
158                     {
159                         //check 1.5.0_01-ea
160                         PreRelease pr = getPreRelease(++pCur);
161                         if (pr == Rel_NONE)
162                             return false;
163                         //just ignore -ea because its no official release
164                         break;
165                     }
166                     else
167                     {
168                         return false;
169                     }
170                 }
171                 if (pCur < pEnd)
172                     pCur ++;
173                 else
174                     break;
175             }
176         }
177     }
178     // 1.4.1-ea
179     else if (*(pCur - 1) == '-')
180     {
181         m_preRelease = getPreRelease(pCur);
182         if (m_preRelease == Rel_NONE)
183             return false;
184 #if defined(FREEBSD)
185       if (m_preRelease == Rel_FreeBSD)
186       {
187           pCur++; //eliminate 'p'
188           if (pCur < pEnd
189               && rtl::isAsciiDigit(static_cast<unsigned char>(*pCur)))
190               pCur ++;
191           int len = pCur - pLast -1; //eliminate 'p'
192           if (len >= 127)
193               return false;
194           strncpy(buf, (pLast+1), len); //eliminate 'p'
195           buf[len] = 0;
196           m_nUpdateSpecial = atoi(buf)+100; //hack for FBSD #i56953#
197           return true;
198       }
199 #endif
200     }
201     else
202     {
203         return false;
204     }
205     return true;
206 }
207 
getPreRelease(const char * szRelease)208 SunVersion::PreRelease SunVersion::getPreRelease(const char *szRelease)
209 {
210     if (szRelease == nullptr)
211         return Rel_NONE;
212     if( ! strcmp(szRelease,"internal"))
213         return  Rel_INTERNAL;
214     else if( ! strcmp(szRelease,"ea"))
215         return  Rel_EA;
216     else if( ! strcmp(szRelease,"ea1"))
217         return Rel_EA1;
218     else if( ! strcmp(szRelease,"ea2"))
219         return Rel_EA2;
220     else if( ! strcmp(szRelease,"ea3"))
221         return Rel_EA3;
222     else if ( ! strcmp(szRelease,"beta"))
223         return Rel_BETA;
224     else if ( ! strcmp(szRelease,"beta1"))
225         return Rel_BETA1;
226     else if ( ! strcmp(szRelease,"beta2"))
227         return Rel_BETA2;
228     else if ( ! strcmp(szRelease,"beta3"))
229         return Rel_BETA3;
230     else if (! strcmp(szRelease, "rc"))
231         return Rel_RC;
232     else if (! strcmp(szRelease, "rc1"))
233         return Rel_RC1;
234     else if (! strcmp(szRelease, "rc2"))
235         return Rel_RC2;
236     else if (! strcmp(szRelease, "rc3"))
237         return Rel_RC3;
238 #if defined (FREEBSD)
239     else if (! strncmp(szRelease, "p", 1))
240         return Rel_FreeBSD;
241 #endif
242     else
243         return Rel_NONE;
244 }
245 
~SunVersion()246 SunVersion::~SunVersion()
247 {
248 
249 }
250 
251 /* Examples:
252    a) 1.0 < 1.1
253    b) 1.0 < 1.0.0
254    c)  1.0 < 1.0_00
255 
256    returns false if both values are equal
257 */
operator >(const SunVersion & ver) const258 bool SunVersion::operator > (const SunVersion& ver) const
259 {
260     if( &ver == this)
261         return false;
262 
263     //compare major.minor.maintenance
264     for( int i= 0; i < 4; i ++)
265     {
266         // 1.4 > 1.3
267         if(m_arVersionParts[i] > ver.m_arVersionParts[i])
268         {
269             return true;
270         }
271         else if (m_arVersionParts[i] < ver.m_arVersionParts[i])
272         {
273             return false;
274         }
275     }
276     //major.minor.maintenance_update are equal. Test for a trailing char
277     if (m_nUpdateSpecial > ver.m_nUpdateSpecial)
278     {
279         return true;
280     }
281 
282     //Until here the versions are equal
283     //compare pre -release values
284     if ((m_preRelease == Rel_NONE && ver.m_preRelease == Rel_NONE)
285         ||
286         (m_preRelease != Rel_NONE && ver.m_preRelease == Rel_NONE))
287         return false;
288     else if (m_preRelease == Rel_NONE && ver.m_preRelease != Rel_NONE)
289         return true;
290     else if (m_preRelease > ver.m_preRelease)
291         return true;
292 
293     return false;
294 }
295 
operator <(const SunVersion & ver) const296 bool SunVersion::operator < (const SunVersion& ver) const
297 {
298     return (! operator > (ver)) && (! operator == (ver));
299 }
300 
operator ==(const SunVersion & ver) const301 bool SunVersion::operator == (const SunVersion& ver) const
302 {
303     bool bRet= true;
304     for(int i= 0; i < 4; i++)
305     {
306         if( m_arVersionParts[i] != ver.m_arVersionParts[i])
307         {
308             bRet= false;
309             break;
310         }
311     }
312     bRet = m_nUpdateSpecial == ver.m_nUpdateSpecial && bRet;
313     bRet = m_preRelease == ver.m_preRelease && bRet;
314     return bRet;
315 }
316 
317 
318 #if OSL_DEBUG_LEVEL >= 2
SelfTest()319 SelfTest::SelfTest()
320 {
321     bool bRet = true;
322 
323     static char const * versions[] = {"1.4.0", "1.4.1", "1.0.0", "10.0.0", "10.10.0",
324                          "10.2.2", "10.10.0", "10.10.10", "111.0.999",
325                          "1.4.1_01", "9.90.99_09", "1.4.1_99",
326                          "1.4.1_00a",
327                          "1.4.1-ea", "1.4.1-beta", "1.4.1-rc1",
328                          "1.5.0_01-ea", "1.5.0_01-rc2"};
329     static char const * badVersions[] = {".4.0", "..1", "", "10.0", "10.10.0.", "10.10.0-", "10.10.0.",
330                             "10.2-2", "10_10.0", "10..10","10.10", "a.0.999",
331                             "1.4b.1_01", "9.90.-99_09", "1.4.1_99-",
332                             "1.4.1_00a2", "1.4.0_z01z", "1.4.1__99A",
333                             "1.4.1-1ea", "1.5.0_010", "1.5.0._01-", "1.5.0_01-eac"};
334     static char const * orderedVer[] = { "1.3.1-ea", "1.3.1-beta", "1.3.1-rc1",
335                             "1.3.1", "1.3.1_00a", "1.3.1_01", "1.3.1_01a",
336                             "1.3.2", "1.4.0", "1.5.0_01-ea", "2.0.0"};
337 
338     int num = SAL_N_ELEMENTS (versions);
339     int numBad = SAL_N_ELEMENTS (badVersions);
340     int numOrdered = SAL_N_ELEMENTS (orderedVer);
341     //parsing test (positive)
342     for (int i = 0; i < num; i++)
343     {
344         SunVersion ver(versions[i]);
345         if ( ! ver)
346         {
347             bRet = false;
348             break;
349         }
350     }
351     OSL_ENSURE(bRet, "SunVersion selftest failed");
352     //Parsing test (negative)
353     for ( int i = 0; i < numBad; i++)
354     {
355         SunVersion ver(badVersions[i]);
356         if (ver)
357         {
358             bRet = false;
359             break;
360         }
361     }
362     OSL_ENSURE(bRet, "SunVersion selftest failed");
363 
364     // Ordering test
365     bRet = true;
366     int j = 0;
367     for (int i = 0; i < numOrdered; i ++)
368     {
369         SunVersion curVer(orderedVer[i]);
370         if ( ! curVer)
371         {
372             bRet = false;
373             break;
374         }
375         for (j = 0; j < numOrdered; j++)
376         {
377             SunVersion compVer(orderedVer[j]);
378             if (i < j)
379             {
380                 if ( !(curVer < compVer))
381                 {
382                     bRet = false;
383                     break;
384                 }
385             }
386             else if ( i == j)
387             {
388                 if (! (curVer == compVer
389                        && ! (curVer > compVer)
390                        && ! (curVer < compVer)))
391                 {
392                     bRet = false;
393                     break;
394                 }
395             }
396             else if (i > j)
397             {
398                 if ( !(curVer > compVer))
399                 {
400                     bRet = false;
401                     break;
402                 }
403             }
404         }
405         if ( ! bRet)
406             break;
407     }
408     if (bRet)
409         JFW_TRACE2("Testing class SunVersion succeeded.");
410     else
411         OSL_ENSURE(bRet, "[Java framework] sunjavaplugin: SunVersion self test failed.");
412 }
413 #endif
414 
415 }
416 
417 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
418