SKVersionNumber.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. //
  2. // SKVersionNumber.h
  3. // Skim
  4. //
  5. // Created by Christiaan Hofman on 2/15/07.
  6. /*
  7. This software is Copyright (c) 2007-2018
  8. Christiaan Hofman. All rights reserved.
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions
  11. are met:
  12. - Redistributions of source code must retain the above copyright
  13. notice, this list of conditions and the following disclaimer.
  14. - Redistributions in binary form must reproduce the above copyright
  15. notice, this list of conditions and the following disclaimer in
  16. the documentation and/or other materials provided with the
  17. distribution.
  18. - Neither the name of Christiaan Hofman nor the names of any
  19. contributors may be used to endorse or promote products derived
  20. from this software without specific prior written permission.
  21. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. */
  33. #import "SKVersionNumber.h"
  34. #define VERSION_LONG @"version"
  35. #define VERSION_SHORT @"v"
  36. #define ALPHA_LONG @"alpha"
  37. #define ALPHA_SHORT @"a"
  38. #define BETA_LONG @"beta"
  39. #define BETA_SHORT @"b"
  40. #define DEVELOPMENT_LONG @"development"
  41. #define DEVELOPMENT_SHORT @"d"
  42. #define FINAL_LONG @"final"
  43. #define FINAL_SHORT @"f"
  44. #define RELEASE_CANDIDATE_LONG @"release candidate"
  45. #define RELEASE_CANDIDATE_SHORT @"rc"
  46. #define SEPARATOR @"."
  47. #define DASH @"-"
  48. #define EMPTY @""
  49. @implementation SKVersionNumber
  50. @synthesize originalVersionString, cleanVersionString, componentCount, releaseType;
  51. + (NSComparisonResult)compareVersionString:(NSString *)versionString toVersionString:(NSString *)otherVersionString;
  52. {
  53. SKVersionNumber *versionNumber = [[self alloc] initWithVersionString:versionString];
  54. SKVersionNumber *otherVersionNumber = [[self alloc] initWithVersionString:otherVersionString];
  55. NSComparisonResult result = [versionNumber compare:otherVersionNumber];
  56. // [versionNumber release];
  57. // [otherVersionNumber release];
  58. return result;
  59. }
  60. // Initializes the receiver from a string representation of a version number. The input string may have an optional leading 'v' or 'V' followed by a sequence of positive integers separated by '.'s. Any trailing component of the input string that doesn't match this pattern is ignored. If no portion of this string matches the pattern, nil is returned.
  61. - (id)initWithVersionString:(NSString *)versionString;
  62. {
  63. self = [super init];
  64. if (self) {
  65. // Input might be from a NSBundle info dictionary that could be misconfigured, so check at runtime too
  66. if (versionString == nil || [versionString isKindOfClass:[NSString class]] == NO) {
  67. // [self release];
  68. return nil;
  69. }
  70. originalVersionString = [versionString copy];
  71. releaseType = SKReleaseVersionType;
  72. NSMutableString *mutableVersionString = [[NSMutableString alloc] init];
  73. NSScanner *scanner = [[NSScanner alloc] initWithString:versionString];
  74. NSString *sep = EMPTY;
  75. [scanner setCharactersToBeSkipped:[NSCharacterSet whitespaceCharacterSet]];
  76. // ignore a leading "version" or "v", possibly followed by "-"
  77. if ([scanner scanString:VERSION_LONG intoString:NULL] || [scanner scanString:VERSION_SHORT intoString:NULL])
  78. [scanner scanString:DASH intoString:NULL];
  79. while ([scanner isAtEnd] == NO && sep != nil) {
  80. NSInteger component;
  81. if ([scanner scanInteger:&component] && component >= 0) {
  82. [mutableVersionString appendFormat:@"%@%ld", sep, (long)component];
  83. componentCount++;
  84. components = (NSInteger *)NSZoneRealloc(NSDefaultMallocZone(), components, sizeof(NSInteger) * componentCount);
  85. components[componentCount - 1] = component;
  86. if ([scanner isAtEnd] == NO) {
  87. sep = nil;
  88. if ([scanner scanString:SEPARATOR intoString:NULL] || [scanner scanString:DASH intoString:NULL] || [scanner scanString:VERSION_LONG intoString:NULL] || [scanner scanString:VERSION_SHORT intoString:NULL]) {
  89. sep = SEPARATOR;
  90. }
  91. if (releaseType == SKReleaseVersionType) {
  92. if ([scanner scanString:ALPHA_LONG intoString:NULL] || [scanner scanString:ALPHA_SHORT intoString:NULL]) {
  93. releaseType = SKAlphaVersionType;
  94. [mutableVersionString appendString:ALPHA_SHORT];
  95. } else if ([scanner scanString:BETA_LONG intoString:NULL] || [scanner scanString:BETA_SHORT intoString:NULL]) {
  96. releaseType = SKBetaVersionType;
  97. [mutableVersionString appendString:BETA_SHORT];
  98. } else if ([scanner scanString:DEVELOPMENT_LONG intoString:NULL] || [scanner scanString:DEVELOPMENT_SHORT intoString:NULL]) {
  99. releaseType = SKDevelopmentVersionType;
  100. [mutableVersionString appendString:DEVELOPMENT_SHORT];
  101. } else if ([scanner scanString:FINAL_LONG intoString:NULL] || [scanner scanString:FINAL_SHORT intoString:NULL]) {
  102. releaseType = SKReleaseCandidateVersionType;
  103. [mutableVersionString appendString:FINAL_SHORT];
  104. } else if ([scanner scanString:RELEASE_CANDIDATE_LONG intoString:NULL] || [scanner scanString:RELEASE_CANDIDATE_SHORT intoString:NULL]) {
  105. releaseType = SKReleaseCandidateVersionType;
  106. [mutableVersionString appendString:RELEASE_CANDIDATE_SHORT];
  107. }
  108. if (releaseType != SKReleaseVersionType) {
  109. // we scanned an "a", "b", "d", "f", or "rc"
  110. componentCount++;
  111. components = (NSInteger *)NSZoneRealloc(NSDefaultMallocZone(), components, sizeof(NSInteger) * componentCount);
  112. components[componentCount - 1] = releaseType;
  113. sep = EMPTY;
  114. // ignore a "." or "-"
  115. if ([scanner scanString:SEPARATOR intoString:NULL] == NO)
  116. [scanner scanString:DASH intoString:NULL];
  117. }
  118. }
  119. }
  120. } else
  121. sep = nil;
  122. }
  123. if ([mutableVersionString isEqualToString:originalVersionString])
  124. // cleanVersionString = [originalVersionString retain];
  125. cleanVersionString = originalVersionString;
  126. else
  127. cleanVersionString = [mutableVersionString copy];
  128. // [mutableVersionString release];
  129. // [scanner release];
  130. if (componentCount == 0) {
  131. // Failed to parse anything and we don't allow empty version strings. For now, we'll not assert on this, since people might want to use this to detect if a string begins with a valid version number.
  132. // [self release];
  133. return nil;
  134. }
  135. }
  136. return self;
  137. }
  138. - (void)dealloc;
  139. {
  140. // SKDESTROY(originalVersionString);
  141. // SKDESTROY(cleanVersionString);
  142. // SKZONEDESTROY(components);
  143. // [super dealloc];
  144. }
  145. - (NSString *)description {
  146. return [NSString stringWithFormat:@"<%@: %@>", [self class], [self originalVersionString]];
  147. }
  148. #pragma mark API
  149. - (NSInteger)componentAtIndex:(NSUInteger)componentIndex;
  150. {
  151. // This treats the version as a infinite sequence ending in "...0.0.0.0", making comparison easier
  152. if (componentIndex < componentCount)
  153. return components[componentIndex];
  154. return 0;
  155. }
  156. #pragma mark NSCopying
  157. //- (id)copyWithZone:(NSZone *)zone;
  158. //{
  159. // if (NSShouldRetainWithZone(self, zone))
  160. // return [self retain];
  161. // else
  162. // return [[[self class] allocWithZone:zone] initWithVersionString:originalVersionString];
  163. //}
  164. #pragma mark Comparison
  165. - (NSUInteger)hash;
  166. {
  167. return [cleanVersionString hash];
  168. }
  169. - (BOOL)isEqual:(id)otherObject;
  170. {
  171. if ([otherObject isMemberOfClass:[self class]] == NO)
  172. return NO;
  173. return [self compare:(SKVersionNumber *)otherObject] == NSOrderedSame;
  174. }
  175. - (NSComparisonResult)compare:(SKVersionNumber *)otherVersion;
  176. {
  177. if (otherVersion == nil)
  178. return NSOrderedAscending;
  179. NSUInteger idx = 0, otherIdx = 0, otherCount = [otherVersion componentCount];
  180. while (idx < componentCount || otherIdx < otherCount) {
  181. NSInteger component = [self componentAtIndex:idx];
  182. NSInteger otherComponent = [otherVersion componentAtIndex:otherIdx];
  183. // insert zeros before matching possible a/d/b/rc components, e.g. to get 1b1 > 1.0a1
  184. if (component < 0 && otherComponent >= 0 && otherIdx < otherCount) {
  185. component = 0;
  186. otherIdx++;
  187. } else if (component >= 0 && otherComponent < 0 && idx < componentCount) {
  188. otherComponent = 0;
  189. idx++;
  190. } else {
  191. idx++;
  192. otherIdx++;
  193. }
  194. if (component < otherComponent)
  195. return NSOrderedAscending;
  196. else if (component > otherComponent)
  197. return NSOrderedDescending;
  198. }
  199. return NSOrderedSame;
  200. }
  201. @end