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 24BString 25trim_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 54void 55parse_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 77StringVector::StringVector() 78 : 79 fStrings(NULL), 80 fCount(0) 81{ 82} 83 84 85StringVector::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 100StringVector::StringVector(const BMessage& strings, const char* fieldName) 101 : 102 fStrings(NULL), 103 fCount(0) 104{ 105 SetTo(strings, fieldName); 106} 107 108 109StringVector::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 125StringVector::~StringVector() 126{ 127 Unset(); 128} 129 130 131void 132StringVector::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 141void 142StringVector::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 170void 171StringVector::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 197void 198StringVector::Unset() 199{ 200 delete[] fStrings; 201 fStrings = NULL; 202 fCount = 0; 203} 204 205 206const char* 207StringVector::StringAt(int32 index) const 208{ 209 return (index >= 0 && index < fCount ? fStrings[index].String() : NULL); 210} 211 212 213// #pragma mark - PackageCredit 214 215 216PackageCredit::PackageCredit(const char* packageName) 217 : 218 fPackageName(packageName) 219{ 220} 221 222 223PackageCredit::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 247PackageCredit::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 258PackageCredit::~PackageCredit() 259{ 260} 261 262 263bool 264PackageCredit::IsValid() const 265{ 266 // should have at least a package name and a copyright 267 return fPackageName.Length() > 0 && !fCopyrights.IsEmpty(); 268} 269 270 271bool 272PackageCredit::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 283PackageCredit& 284PackageCredit::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 295PackageCredit& 296PackageCredit::SetCopyright(const char* copyright) 297{ 298 return SetCopyrights(copyright, NULL); 299} 300 301 302PackageCredit& 303PackageCredit::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 314PackageCredit& 315PackageCredit::SetLicense(const char* license) 316{ 317 return SetLicenses(license, NULL); 318} 319 320 321PackageCredit& 322PackageCredit::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 333PackageCredit& 334PackageCredit::SetSource(const char* source) 335{ 336 return SetSources(source, NULL); 337} 338 339 340PackageCredit& 341PackageCredit::SetURL(const char* url) 342{ 343 fURL = url; 344 345 return *this; 346} 347 348 349const char* 350PackageCredit::PackageName() const 351{ 352 return fPackageName.String(); 353} 354 355 356const StringVector& 357PackageCredit::Copyrights() const 358{ 359 return fCopyrights; 360} 361 362 363int32 364PackageCredit::CountCopyrights() const 365{ 366 return fCopyrights.CountStrings(); 367} 368 369 370const char* 371PackageCredit::CopyrightAt(int32 index) const 372{ 373 return fCopyrights.StringAt(index); 374} 375 376 377const StringVector& 378PackageCredit::Licenses() const 379{ 380 return fLicenses; 381} 382 383 384int32 385PackageCredit::CountLicenses() const 386{ 387 return fLicenses.CountStrings(); 388} 389 390 391const char* 392PackageCredit::LicenseAt(int32 index) const 393{ 394 return fLicenses.StringAt(index); 395} 396 397 398const StringVector& 399PackageCredit::Sources() const 400{ 401 return fSources; 402} 403 404 405int32 406PackageCredit::CountSources() const 407{ 408 return fSources.CountStrings(); 409} 410 411 412const char* 413PackageCredit::SourceAt(int32 index) const 414{ 415 return fSources.StringAt(index); 416} 417 418 419const char* 420PackageCredit::URL() const 421{ 422 return fURL.Length() > 0 ? fURL.String() : NULL; 423} 424 425 426/*static*/ bool 427PackageCredit::NameLessInsensitive(const PackageCredit* a, 428 const PackageCredit* b) 429{ 430 return a->fPackageName.ICompare(b->fPackageName) < 0; 431} 432 433 434int 435PackageCredit::_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