1 /* 2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <errno.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include <list> 13 #include <map> 14 15 #include <package/PackageInfo.h> 16 #include <package/PackageResolvable.h> 17 #include <package/PackageResolvableExpression.h> 18 #include <package/RepositoryCache.h> 19 20 21 using namespace BPackageKit; 22 23 24 typedef std::list<BPackageResolvable*> ProvidesList; 25 26 27 static const char* sProgramName = "update_package_requires"; 28 29 30 #define DIE(result, msg...) \ 31 do { \ 32 fprintf(stderr, "*** " msg); \ 33 fprintf(stderr, " : %s\n", strerror(result)); \ 34 exit(5); \ 35 } while(0) 36 37 38 static void 39 print_usage_and_exit(bool error) 40 { 41 fprintf(error ? stderr : stdout, 42 "Usage: %s <package info> <repository>\n" 43 "Updates the versions in the \"requires\" list of the given package\n" 44 "info file using the available package information from the given\n" 45 "repository cache file <repository>.\n", 46 sProgramName); 47 exit(error ? 1 : 0); 48 } 49 50 51 static void 52 update_requires_expression(BPackageResolvableExpression& expression, 53 const ProvidesList& providesList) 54 { 55 // find the best-matching provides 56 BPackageResolvable* bestProvides = NULL; 57 for (ProvidesList::const_iterator it = providesList.begin(); 58 it != providesList.end(); ++it) { 59 BPackageResolvable* provides = *it; 60 if (!expression.Matches(*provides)) 61 continue; 62 63 if (bestProvides == NULL || bestProvides->Version().InitCheck() != B_OK 64 || (provides->Version().InitCheck() == B_OK 65 && provides->Version().Compare(bestProvides->Version()) > 0)) { 66 bestProvides = provides; 67 } 68 } 69 70 if (bestProvides == NULL || bestProvides->Version().InitCheck() != B_OK) 71 return; 72 73 // Update the expression. Enforce the minimum found version, if the requires 74 // has no version requirement or also a minimum. Otherwise enforce the exact 75 // version found. 76 BPackageResolvableOperator newOperator = B_PACKAGE_RESOLVABLE_OP_EQUAL; 77 switch (expression.Operator()) { 78 case B_PACKAGE_RESOLVABLE_OP_LESS: 79 case B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL: 80 case B_PACKAGE_RESOLVABLE_OP_EQUAL: 81 case B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL: 82 break; 83 case B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL: 84 case B_PACKAGE_RESOLVABLE_OP_GREATER: 85 case B_PACKAGE_RESOLVABLE_OP_ENUM_COUNT: 86 newOperator = B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL; 87 break; 88 } 89 90 expression.SetTo(expression.Name(), newOperator, bestProvides->Version()); 91 } 92 93 94 int 95 main(int argc, const char* const* argv) 96 { 97 if (argc != 3) 98 print_usage_and_exit(true); 99 100 if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) 101 print_usage_and_exit(false); 102 103 const char* const packageInfoPath = argv[1]; 104 const char* const repositoryCachePath = argv[2]; 105 106 // read the repository cache 107 BRepositoryCache repositoryCache; 108 status_t error = repositoryCache.SetTo(repositoryCachePath); 109 if (error != B_OK) { 110 DIE(error, "failed to read repository cache file \"%s\"", 111 repositoryCachePath); 112 } 113 114 // create a map for all provides (name -> resolvable list) 115 typedef std::map<BString, ProvidesList> ProvidesMap; 116 117 ProvidesMap providesMap; 118 119 for (BRepositoryCache::Iterator it = repositoryCache.GetIterator(); 120 const BPackageInfo* info = it.Next();) { 121 const BObjectList<BPackageResolvable>& provides = info->ProvidesList(); 122 int32 count = provides.CountItems(); 123 for (int32 i = 0; i < count; i++) { 124 BPackageResolvable* resolvable = provides.ItemAt(i); 125 ProvidesList& providesList = providesMap[resolvable->Name()]; 126 providesList.push_back(resolvable); 127 } 128 } 129 130 // load the package info 131 BPackageInfo packageInfo; 132 error = packageInfo.ReadFromConfigFile(packageInfoPath); 133 if (error != B_OK) 134 DIE(error, "failed to read package info file \"%s\"", packageInfoPath); 135 136 // clone the package info's requires list 137 typedef std::list<BPackageResolvableExpression> RequiresList; 138 RequiresList requiresList; 139 int32 requiresCount = packageInfo.RequiresList().CountItems(); 140 for (int32 i = 0; i < requiresCount; i++) 141 requiresList.push_back(*packageInfo.RequiresList().ItemAt(i)); 142 143 // rebuild the requires list with updated versions 144 packageInfo.ClearRequiresList(); 145 for (RequiresList::iterator it = requiresList.begin(); 146 it != requiresList.end(); ++it) { 147 BPackageResolvableExpression expression = *it; 148 ProvidesMap::iterator foundIt = providesMap.find(expression.Name()); 149 if (foundIt != providesMap.end()) 150 update_requires_expression(expression, foundIt->second); 151 152 error = packageInfo.AddRequires(expression); 153 if (error != B_OK) 154 DIE(error, "failed to add requires item to package info"); 155 } 156 157 // write updated package info 158 BString configString; 159 error = packageInfo.GetConfigString(configString); 160 if (error != B_OK) 161 DIE(error, "failed to get updated package info string"); 162 163 FILE* file = fopen(packageInfoPath, "w"); 164 if (file == NULL) { 165 DIE(errno, "failed to open package info file \"%s\" for writing", 166 packageInfoPath); 167 } 168 169 if (fwrite(configString.String(), configString.Length(), 1, file) != 1) { 170 DIE(errno, "failed to write updated package info file \"%s\"", 171 packageInfoPath); 172 } 173 174 fclose(file); 175 176 return 0; 177 } 178