1 /* 2 * Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <ctype.h> 8 #include <errno.h> 9 #include <getopt.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <time.h> 14 15 #include <Entry.h> 16 #include <package/hpkg/PackageContentHandler.h> 17 #include <package/hpkg/PackageEntry.h> 18 #include <package/hpkg/PackageEntryAttribute.h> 19 #include <package/hpkg/PackageInfoAttributeValue.h> 20 #include <package/hpkg/PackageReader.h> 21 #include <package/hpkg/StandardErrorOutput.h> 22 #include <package/hpkg/v1/PackageContentHandler.h> 23 #include <package/hpkg/v1/PackageEntry.h> 24 #include <package/hpkg/v1/PackageEntryAttribute.h> 25 #include <package/hpkg/v1/PackageReader.h> 26 27 #include <package/PackageInfo.h> 28 29 #include "package.h" 30 #include "PackageInfoPrinter.h" 31 32 33 using namespace BPackageKit; 34 using BPackageKit::BHPKG::BErrorOutput; 35 using BPackageKit::BHPKG::BPackageInfoAttributeValue; 36 using BPackageKit::BHPKG::BStandardErrorOutput; 37 38 39 struct VersionPolicyV1 { 40 typedef BPackageKit::BHPKG::V1::BPackageContentHandler 41 PackageContentHandler; 42 typedef BPackageKit::BHPKG::V1::BPackageEntry PackageEntry; 43 typedef BPackageKit::BHPKG::V1::BPackageEntryAttribute 44 PackageEntryAttribute; 45 typedef BPackageKit::BHPKG::V1::BPackageReader PackageReader; 46 47 static inline uint64 PackageDataSize( 48 const BPackageKit::BHPKG::V1::BPackageData& data) 49 { 50 return data.UncompressedSize(); 51 } 52 53 static inline status_t InitReader(PackageReader& packageReader, 54 const char* fileName) 55 { 56 return packageReader.Init(fileName); 57 } 58 }; 59 60 struct VersionPolicyV2 { 61 typedef BPackageKit::BHPKG::BPackageContentHandler PackageContentHandler; 62 typedef BPackageKit::BHPKG::BPackageEntry PackageEntry; 63 typedef BPackageKit::BHPKG::BPackageEntryAttribute PackageEntryAttribute; 64 typedef BPackageKit::BHPKG::BPackageReader PackageReader; 65 66 static inline uint64 PackageDataSize( 67 const BPackageKit::BHPKG::BPackageData& data) 68 { 69 return data.Size(); 70 } 71 72 static inline status_t InitReader(PackageReader& packageReader, 73 const char* fileName) 74 { 75 return packageReader.Init(fileName, 76 BPackageKit::BHPKG 77 ::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE); 78 } 79 }; 80 81 82 enum ListMode { 83 LIST_ALL, 84 LIST_PATHS_ONLY, 85 LIST_META_INFO_ONLY 86 }; 87 88 89 template<typename VersionPolicy> 90 struct PackageContentListHandler : VersionPolicy::PackageContentHandler { 91 PackageContentListHandler(bool listEntries, bool listAttributes) 92 : 93 fPrinter(), 94 fLevel(0), 95 fListEntries(listEntries), 96 fListAttribute(listEntries && listAttributes) 97 { 98 } 99 100 virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry) 101 { 102 if (!fListEntries) 103 return B_OK; 104 105 fLevel++; 106 107 int indentation = (fLevel - 1) * 2; 108 printf("%*s", indentation, ""); 109 110 // name and size 111 printf("%-*s", indentation < 32 ? 32 - indentation : 0, entry->Name()); 112 printf(" %8llu", 113 (unsigned long long)VersionPolicy::PackageDataSize(entry->Data())); 114 115 // time 116 struct tm* time = localtime(&entry->ModifiedTime().tv_sec); 117 printf(" %04d-%02d-%02d %02d:%02d:%02d", 118 1900 + time->tm_year, time->tm_mon + 1, time->tm_mday, 119 time->tm_hour, time->tm_min, time->tm_sec); 120 121 // file type 122 mode_t mode = entry->Mode(); 123 if (S_ISREG(mode)) 124 printf(" -"); 125 else if (S_ISDIR(mode)) 126 printf(" d"); 127 else if (S_ISLNK(mode)) 128 printf(" l"); 129 else 130 printf(" ?"); 131 132 // permissions 133 char buffer[4]; 134 printf("%s", _PermissionString(buffer, mode >> 6, 135 (mode & S_ISUID) != 0)); 136 printf("%s", _PermissionString(buffer, mode >> 3, 137 (mode & S_ISGID) != 0)); 138 printf("%s", _PermissionString(buffer, mode, false)); 139 140 // print the symlink path 141 if (S_ISLNK(mode)) 142 printf(" -> %s", entry->SymlinkPath()); 143 144 printf("\n"); 145 return B_OK; 146 } 147 148 virtual status_t HandleEntryAttribute( 149 typename VersionPolicy::PackageEntry* entry, 150 typename VersionPolicy::PackageEntryAttribute* attribute) 151 { 152 if (!fListAttribute) 153 return B_OK; 154 155 int indentation = fLevel * 2; 156 printf("%*s<", indentation, ""); 157 printf("%-*s %8llu", indentation < 31 ? 31 - indentation : 0, 158 attribute->Name(), 159 (unsigned long long)VersionPolicy::PackageDataSize( 160 attribute->Data())); 161 162 uint32 type = attribute->Type(); 163 if (isprint(type & 0xff) && isprint((type >> 8) & 0xff) 164 && isprint((type >> 16) & 0xff) && isprint(type >> 24)) { 165 printf(" '%c%c%c%c'", int(type >> 24), int((type >> 16) & 0xff), 166 int((type >> 8) & 0xff), int(type & 0xff)); 167 } else 168 printf(" %#" B_PRIx32, type); 169 170 printf(">\n"); 171 return B_OK; 172 } 173 174 virtual status_t HandleEntryDone( 175 typename VersionPolicy::PackageEntry* entry) 176 { 177 if (!fListEntries) 178 return B_OK; 179 180 fLevel--; 181 return B_OK; 182 } 183 184 virtual status_t HandlePackageAttribute( 185 const BPackageInfoAttributeValue& value) 186 { 187 if (value.attributeID == B_PACKAGE_INFO_NAME) 188 printf("package-attributes:\n"); 189 190 if (!fPrinter.PrintAttribute(value)) { 191 printf("*** Invalid package attribute section: unexpected " 192 "package attribute id %d encountered\n", value.attributeID); 193 return B_BAD_DATA; 194 } 195 196 return B_OK; 197 } 198 199 virtual void HandleErrorOccurred() 200 { 201 } 202 203 private: 204 static const char* _PermissionString(char* buffer, uint32 mode, bool sticky) 205 { 206 buffer[0] = (mode & 0x4) != 0 ? 'r' : '-'; 207 buffer[1] = (mode & 0x2) != 0 ? 'w' : '-'; 208 209 if ((mode & 0x1) != 0) 210 buffer[2] = sticky ? 's' : 'x'; 211 else 212 buffer[2] = '-'; 213 214 buffer[3] = '\0'; 215 return buffer; 216 } 217 218 static void _PrintPackageVersion(const BPackageVersionData& version) 219 { 220 printf("%s", BPackageVersion(version).ToString().String()); 221 } 222 223 private: 224 PackageInfoPrinter fPrinter; 225 int fLevel; 226 bool fListEntries; 227 bool fListAttribute; 228 }; 229 230 231 template<typename VersionPolicy> 232 struct PackageContentListPathsHandler : VersionPolicy::PackageContentHandler { 233 PackageContentListPathsHandler() 234 : 235 fPathComponents() 236 { 237 } 238 239 virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry) 240 { 241 fPathComponents.Add(entry->Name()); 242 printf("%s\n", fPathComponents.Join("/").String()); 243 return B_OK; 244 } 245 246 virtual status_t HandleEntryAttribute( 247 typename VersionPolicy::PackageEntry* entry, 248 typename VersionPolicy::PackageEntryAttribute* attribute) 249 { 250 return B_OK; 251 } 252 253 virtual status_t HandleEntryDone( 254 typename VersionPolicy::PackageEntry* entry) 255 { 256 fPathComponents.Remove(fPathComponents.CountStrings() - 1); 257 return B_OK; 258 } 259 260 virtual status_t HandlePackageAttribute( 261 const BPackageInfoAttributeValue& value) 262 { 263 return B_OK; 264 } 265 266 virtual void HandleErrorOccurred() 267 { 268 } 269 270 private: 271 BStringList fPathComponents; 272 }; 273 274 275 template<typename VersionPolicy> 276 static void 277 do_list(const char* packageFileName, bool listAttributes, ListMode listMode, 278 bool ignoreVersionError) 279 { 280 // open package 281 BStandardErrorOutput errorOutput; 282 typename VersionPolicy::PackageReader packageReader(&errorOutput); 283 status_t error = VersionPolicy::InitReader(packageReader, packageFileName); 284 if (error != B_OK) { 285 if (ignoreVersionError && error == B_MISMATCHED_VALUES) 286 return; 287 exit(1); 288 } 289 290 // list 291 switch (listMode) { 292 case LIST_PATHS_ONLY: 293 { 294 PackageContentListPathsHandler<VersionPolicy> handler; 295 error = packageReader.ParseContent(&handler); 296 break; 297 } 298 299 case LIST_ALL: 300 case LIST_META_INFO_ONLY: 301 { 302 PackageContentListHandler<VersionPolicy> handler( 303 listMode != LIST_META_INFO_ONLY, listAttributes); 304 error = packageReader.ParseContent(&handler); 305 } 306 } 307 308 if (error != B_OK) 309 exit(1); 310 311 exit(0); 312 } 313 314 315 int 316 command_list(int argc, const char* const* argv) 317 { 318 ListMode listMode = LIST_ALL; 319 bool listAttributes = false; 320 321 while (true) { 322 static struct option sLongOptions[] = { 323 { "help", no_argument, 0, 'h' }, 324 { 0, 0, 0, 0 } 325 }; 326 327 opterr = 0; // don't print errors 328 int c = getopt_long(argc, (char**)argv, "+ahip", sLongOptions, NULL); 329 if (c == -1) 330 break; 331 332 switch (c) { 333 case 'a': 334 listAttributes = true; 335 break; 336 337 case 'i': 338 listMode = LIST_META_INFO_ONLY; 339 break; 340 341 case 'h': 342 print_usage_and_exit(false); 343 break; 344 345 case 'p': 346 listMode = LIST_PATHS_ONLY; 347 break; 348 349 default: 350 print_usage_and_exit(true); 351 break; 352 } 353 } 354 355 // One argument should remain -- the package file name. 356 if (optind + 1 != argc) 357 print_usage_and_exit(true); 358 359 const char* packageFileName = argv[optind++]; 360 361 // If the file doesn't look like a package file, try to load it as a 362 // package info file. 363 if (!BString(packageFileName).EndsWith(".hpkg")) { 364 struct ErrorListener : BPackageInfo::ParseErrorListener { 365 virtual void OnError(const BString& msg, int line, int col) 366 { 367 fprintf(stderr, "%s:%d:%d: %s\n", fPath, line, col, 368 msg.String()); 369 } 370 371 const char* fPath; 372 } errorListener; 373 errorListener.fPath = packageFileName; 374 375 BPackageInfo info; 376 if (info.ReadFromConfigFile(BEntry(packageFileName), &errorListener) 377 != B_OK) { 378 return 1; 379 } 380 381 printf("package-attributes:\n"); 382 PackageInfoPrinter().PrintPackageInfo(info); 383 return 0; 384 } 385 386 BHPKG::BStandardErrorOutput errorOutput; 387 388 // current package file format version 389 do_list<VersionPolicyV2>(packageFileName, listAttributes, listMode, true); 390 do_list<VersionPolicyV1>(packageFileName, listAttributes, listMode, false); 391 392 return 0; 393 } 394