1 /* 2 * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT license. 4 */ 5 6 #include "Utilities.h" 7 8 #include <ctype.h> 9 #include <stdlib.h> 10 11 #if __GNUC__ < 4 12 #define va_copy __va_copy 13 #endif 14 15 #include <Catalog.h> 16 #include <Locale.h> 17 #include <Message.h> 18 19 20 #undef B_TRANSLATION_CONTEXT 21 #define B_TRANSLATION_CONTEXT "Utilities" 22 23 24 BString 25 trim_string(const char* string, size_t len) 26 { 27 BString trimmed; 28 size_t i = 0; 29 bool addSpace = false; 30 31 while (i < len) { 32 // skip space block 33 while (i < len && isspace(string[i])) 34 i++; 35 36 // write non-spaced (if any) 37 if (i < len && !isspace(string[i])) { 38 // pad with a single space, for all but the first block 39 if (addSpace) 40 trimmed << ' '; 41 else 42 addSpace = true; 43 44 // append chars 45 while (i < len && !isspace(string[i])) 46 trimmed << string[i++]; 47 } 48 } 49 50 return trimmed; 51 } 52 53 54 void 55 parse_named_url(const BString& namedURL, BString& name, BString& url) 56 { 57 int32 urlStart = namedURL.FindFirst('<'); 58 int32 urlEnd = namedURL.FindLast('>'); 59 if (urlStart < 0 || urlEnd < 0 || urlStart + 1 >= urlEnd) { 60 name = namedURL; 61 url = namedURL; 62 return; 63 } 64 65 url.SetTo(namedURL.String() + urlStart + 1, urlEnd - urlStart - 1); 66 67 if (urlStart > 0) 68 name = trim_string(namedURL, urlStart); 69 else 70 name = url; 71 } 72 73 74 // #pragma mark - StringVector 75 76 77 StringVector::StringVector() 78 : 79 fStrings(NULL), 80 fCount(0) 81 { 82 } 83 84 85 StringVector::StringVector(const char* string,...) 86 : 87 fStrings(NULL), 88 fCount(0) 89 { 90 if (string == NULL) 91 return; 92 93 va_list list; 94 va_start(list, string); 95 SetTo(string, list); 96 va_end(list); 97 } 98 99 100 StringVector::StringVector(const BMessage& strings, const char* fieldName) 101 : 102 fStrings(NULL), 103 fCount(0) 104 { 105 SetTo(strings, fieldName); 106 } 107 108 109 StringVector::StringVector(const StringVector& other) 110 : 111 fStrings(NULL), 112 fCount(0) 113 { 114 if (other.fCount == 0) 115 return; 116 117 fStrings = new BString[other.fCount]; 118 fCount = other.fCount; 119 120 for (int32 i = 0; i < fCount; i++) 121 fStrings[i] = other.fStrings[i]; 122 } 123 124 125 StringVector::~StringVector() 126 { 127 Unset(); 128 } 129 130 131 void 132 StringVector::SetTo(const char* string,...) 133 { 134 va_list list; 135 va_start(list, string); 136 SetTo(string, list); 137 va_end(list); 138 } 139 140 141 void 142 StringVector::SetTo(const char* string, va_list _list) 143 { 144 // free old strings 145 Unset(); 146 147 if (string == NULL) 148 return; 149 150 // count strings 151 va_list list; 152 va_copy(list, _list); 153 fCount = 1; 154 while (va_arg(list, const char*) != NULL) 155 fCount++; 156 va_end(list); 157 158 // create array and copy them 159 fStrings = new BString[fCount]; 160 fStrings[0] = string; 161 162 va_copy(list, _list); 163 for (int32 i = 1; i < fCount; i++) 164 fStrings[i] = va_arg(list, const char*); 165 va_end(list); 166 167 } 168 169 170 void 171 StringVector::SetTo(const BMessage& strings, const char* fieldName, 172 const char* prefix) 173 { 174 Unset(); 175 176 type_code type; 177 int32 count; 178 if (strings.GetInfo(fieldName, &type, &count) != B_OK 179 || type != B_STRING_TYPE) { 180 return; 181 } 182 183 fStrings = new BString[count]; 184 185 for (int32 i = 0; i < count; i++) { 186 if (strings.FindString(fieldName, i, &fStrings[i]) != B_OK) 187 return; 188 189 if (prefix != NULL) 190 fStrings[i].Prepend(prefix); 191 192 fCount++; 193 } 194 } 195 196 197 void 198 StringVector::Unset() 199 { 200 delete[] fStrings; 201 fStrings = NULL; 202 fCount = 0; 203 } 204 205 206 const char* 207 StringVector::StringAt(int32 index) const 208 { 209 return (index >= 0 && index < fCount ? fStrings[index].String() : NULL); 210 } 211 212 213 // #pragma mark - PackageCredit 214 215 216 PackageCredit::PackageCredit(const char* packageName) 217 : 218 fPackageName(packageName) 219 { 220 } 221 222 223 PackageCredit::PackageCredit(const BMessage& packageDescription) 224 { 225 const char* package; 226 const char* copyright; 227 const char* url; 228 229 // package and copyright are mandatory 230 if (packageDescription.FindString("Package", &package) != B_OK 231 || packageDescription.FindString("Copyright", ©right) != B_OK) { 232 return; 233 } 234 235 // URL is optional 236 if (packageDescription.FindString("URL", &url) != B_OK) 237 url = NULL; 238 239 fPackageName = package; 240 fCopyrights.SetTo(packageDescription, "Copyright", COPYRIGHT_STRING); 241 fLicenses.SetTo(packageDescription, "License"); 242 fSources.SetTo(packageDescription, "SourceURL"); 243 fURL = url; 244 } 245 246 247 PackageCredit::PackageCredit(const PackageCredit& other) 248 : 249 fPackageName(other.fPackageName), 250 fCopyrights(other.fCopyrights), 251 fLicenses(other.fLicenses), 252 fSources(other.fSources), 253 fURL(other.fURL) 254 { 255 } 256 257 258 PackageCredit::~PackageCredit() 259 { 260 } 261 262 263 bool 264 PackageCredit::IsValid() const 265 { 266 // should have at least a package name and a copyright 267 return fPackageName.Length() > 0 && !fCopyrights.IsEmpty(); 268 } 269 270 271 bool 272 PackageCredit::IsBetterThan(const PackageCredit& other) const 273 { 274 // We prefer credits with licenses. 275 if (CountLicenses() > 0 && other.CountLicenses() == 0) 276 return true; 277 278 // Scan the copyrights for year numbers and let the greater one win. 279 return _MaxCopyrightYear() > other._MaxCopyrightYear(); 280 } 281 282 283 PackageCredit& 284 PackageCredit::SetCopyrights(const char* copyright,...) 285 { 286 va_list list; 287 va_start(list, copyright); 288 fCopyrights.SetTo(copyright, list); 289 va_end(list); 290 291 return *this; 292 } 293 294 295 PackageCredit& 296 PackageCredit::SetCopyright(const char* copyright) 297 { 298 return SetCopyrights(copyright, NULL); 299 } 300 301 302 PackageCredit& 303 PackageCredit::SetLicenses(const char* license,...) 304 { 305 va_list list; 306 va_start(list, license); 307 fLicenses.SetTo(license, list); 308 va_end(list); 309 310 return *this; 311 } 312 313 314 PackageCredit& 315 PackageCredit::SetLicense(const char* license) 316 { 317 return SetLicenses(license, NULL); 318 } 319 320 321 PackageCredit& 322 PackageCredit::SetSources(const char* source,...) 323 { 324 va_list list; 325 va_start(list, source); 326 fSources.SetTo(source, list); 327 va_end(list); 328 329 return *this; 330 } 331 332 333 PackageCredit& 334 PackageCredit::SetSource(const char* source) 335 { 336 return SetSources(source, NULL); 337 } 338 339 340 PackageCredit& 341 PackageCredit::SetURL(const char* url) 342 { 343 fURL = url; 344 345 return *this; 346 } 347 348 349 const char* 350 PackageCredit::PackageName() const 351 { 352 return fPackageName.String(); 353 } 354 355 356 const StringVector& 357 PackageCredit::Copyrights() const 358 { 359 return fCopyrights; 360 } 361 362 363 int32 364 PackageCredit::CountCopyrights() const 365 { 366 return fCopyrights.CountStrings(); 367 } 368 369 370 const char* 371 PackageCredit::CopyrightAt(int32 index) const 372 { 373 return fCopyrights.StringAt(index); 374 } 375 376 377 const StringVector& 378 PackageCredit::Licenses() const 379 { 380 return fLicenses; 381 } 382 383 384 int32 385 PackageCredit::CountLicenses() const 386 { 387 return fLicenses.CountStrings(); 388 } 389 390 391 const char* 392 PackageCredit::LicenseAt(int32 index) const 393 { 394 return fLicenses.StringAt(index); 395 } 396 397 398 const StringVector& 399 PackageCredit::Sources() const 400 { 401 return fSources; 402 } 403 404 405 int32 406 PackageCredit::CountSources() const 407 { 408 return fSources.CountStrings(); 409 } 410 411 412 const char* 413 PackageCredit::SourceAt(int32 index) const 414 { 415 return fSources.StringAt(index); 416 } 417 418 419 const char* 420 PackageCredit::URL() const 421 { 422 return fURL.Length() > 0 ? fURL.String() : NULL; 423 } 424 425 426 /*static*/ bool 427 PackageCredit::NameLessInsensitive(const PackageCredit* a, 428 const PackageCredit* b) 429 { 430 return a->fPackageName.ICompare(b->fPackageName) < 0; 431 } 432 433 434 int 435 PackageCredit::_MaxCopyrightYear() const 436 { 437 int maxYear = 0; 438 439 for (int32 i = 0; const char* string = CopyrightAt(i); i++) { 440 // iterate through the numbers 441 int32 start = 0; 442 while (true) { 443 // find the next number start 444 while (string[start] != '\0' && !isdigit(string[start])) 445 start++; 446 447 if (string[start] == '\0') 448 break; 449 450 // find the end 451 int32 end = start + 1; 452 while (string[end] != '\0' && isdigit(string[end])) 453 end++; 454 455 if (end - start == 4) { 456 int year = atoi(string + start); 457 if (year > 1900 && year < 2200 && year > maxYear) 458 maxYear = year; 459 } 460 461 start = end; 462 } 463 } 464 465 return maxYear; 466 } 467