1 // resattr.cpp 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 7 #include <Entry.h> 8 #include <File.h> 9 #include <fs_attr.h> 10 #include <Resources.h> 11 12 // usage 13 static const char *kUsage = 14 "Usage: %s [ <options> ] -o <outFile> [ <inFile> ... ]\n" 15 "\n" 16 "Reads resources from zero or more input files and adds them as attributes\n" 17 "to the specified output file, or (in reverse mode) reads attributes from\n" 18 "zero or more input files and adds them as resources to the specified output\n" 19 "file. If not existent the output file is created as an empty file.\n" 20 "\n" 21 "Options:\n" 22 " -h, --help - Print this text.\n" 23 " -o <outfile> - Specifies the output file.\n" 24 " -O, --overwrite - Overwrite existing attributes. regardless of whether\n" 25 " an attribute does already exist, it is always written\n" 26 " when a respective resource is encountered in an input\n" 27 " file. The last input file specifying the attribute\n" 28 " wins. If the options is not given, already existing\n" 29 " attributes remain untouched. Otherwise the first input\n" 30 " file specifying an attribute wins.\n" 31 " -r, --reverse - Reverse mode: Reads attributes from input files and\n" 32 " writes resources. A unique resource ID is assigned to\n" 33 " each attribute, so there will be no conflicts if two\n" 34 " input files feature the same attribute.\n" 35 ; 36 37 // command line args 38 static int sArgc; 39 static const char *const *sArgv; 40 41 // print_usage 42 void 43 print_usage(bool error) 44 { 45 // get nice program name 46 const char *programName = (sArgc > 0 ? sArgv[0] : "resattr"); 47 if (const char *lastSlash = strrchr(programName, '/')) 48 programName = lastSlash + 1; 49 50 // print usage 51 fprintf((error ? stderr : stdout), kUsage, programName); 52 } 53 54 // print_usage_and_exit 55 static 56 void 57 print_usage_and_exit(bool error) 58 { 59 print_usage(error); 60 exit(error ? 1 : 0); 61 } 62 63 // next_arg 64 static 65 const char* 66 next_arg(int argc, const char* const* argv, int& argi, bool dontFail = false) 67 { 68 if (argi + 1 >= argc) { 69 if (dontFail) 70 return NULL; 71 print_usage_and_exit(true); 72 } 73 74 return argv[++argi]; 75 } 76 77 // write_attributes 78 static 79 void 80 write_attributes(BNode &out, const char *inFileName, BResources &resources, 81 bool overwrite) 82 { 83 // iterate through the resources 84 type_code type; 85 int32 id; 86 const char *name; 87 size_t size; 88 for (int resIndex = 0; 89 resources.GetResourceInfo(resIndex, &type, &id, &name, &size); 90 resIndex++) { 91 // if we shall not overwrite attributes, we skip the attribute, if it 92 // already exists 93 attr_info attrInfo; 94 if (!overwrite && out.GetAttrInfo(name, &attrInfo) == B_OK) 95 continue; 96 97 // get the resource 98 const void *data = resources.LoadResource(type, id, &size); 99 if (!data && size > 0) { 100 // should not happen 101 fprintf(stderr, "Failed to get resource `%s', type: %" B_PRIx32 102 ", id: %" B_PRId32 " from input file `%s'\n", name, type, id, 103 inFileName); 104 exit(1); 105 } 106 107 // construct a name, if the resource doesn't have one 108 char nameBuffer[32]; 109 if (!name) { 110 sprintf(nameBuffer, "unnamed_%d\n", resIndex); 111 name = nameBuffer; 112 } 113 114 // write the attribute 115 ssize_t bytesWritten = out.WriteAttr(name, type, 0LL, data, size); 116 if (bytesWritten < 0) { 117 fprintf(stderr, "Failed to write attribute `%s' to output file: " 118 "%s\n", name, strerror(bytesWritten)); 119 } 120 } 121 } 122 123 // write_resources 124 static 125 void 126 write_resources(BResources &resources, const char *inFileName, BNode &in, 127 int32 &resID) 128 { 129 // iterate through the attributes 130 char name[B_ATTR_NAME_LENGTH]; 131 while (in.GetNextAttrName(name) == B_OK) { 132 // get attribute info 133 attr_info attrInfo; 134 status_t error = in.GetAttrInfo(name, &attrInfo); 135 if (error != B_OK) { 136 fprintf(stderr, "Failed to get info for attribute `%s' of input " 137 "file `%s': %s\n", name, inFileName, strerror(error)); 138 exit(1); 139 } 140 141 // read attribute 142 char *data = new char[attrInfo.size]; 143 ssize_t bytesRead = in.ReadAttr(name, attrInfo.type, 0LL, data, 144 attrInfo.size); 145 if (bytesRead < 0) { 146 fprintf(stderr, "Failed to read attribute `%s' of input " 147 "file `%s': %s\n", name, inFileName, strerror(bytesRead)); 148 delete[] data; 149 exit(1); 150 } 151 152 // find unique ID 153 const char *existingName; 154 size_t existingSize; 155 while (resources.GetResourceInfo(attrInfo.type, resID, &existingName, 156 &existingSize)) { 157 resID++; 158 } 159 160 // write resource 161 error = resources.AddResource(attrInfo.type, resID++, data, 162 attrInfo.size, name); 163 if (error != B_OK) { 164 fprintf(stderr, "Failed to write resource `%s' to output " 165 "file: %s\n", name, strerror(error)); 166 } 167 delete[] data; 168 } 169 } 170 171 // resources_to_attributes 172 static 173 void 174 resources_to_attributes(const char *outputFile, const char **inputFiles, 175 int inputFileCount, bool overwrite) 176 { 177 // create output file, if it doesn't exist 178 if (!BEntry(outputFile).Exists()) { 179 BFile createdOut; 180 status_t error = createdOut.SetTo(outputFile, 181 B_READ_WRITE | B_CREATE_FILE); 182 if (error != B_OK) { 183 fprintf(stderr, "Failed to create output file `%s': %s\n", 184 outputFile, strerror(error)); 185 exit(1); 186 } 187 } 188 189 // open output file 190 BNode out; 191 status_t error = out.SetTo(outputFile); 192 if (error != B_OK) { 193 fprintf(stderr, "Failed to open output file `%s': %s\n", outputFile, 194 strerror(error)); 195 exit(1); 196 } 197 198 // iterate through the input files 199 for (int i = 0; i < inputFileCount; i++) { 200 // open input file 201 BFile in; 202 error = in.SetTo(inputFiles[i], B_READ_ONLY); 203 if (error != B_OK) { 204 fprintf(stderr, "Failed to open input file `%s': %s\n", 205 inputFiles[i], strerror(error)); 206 exit(1); 207 } 208 209 // open resources 210 BResources resources; 211 error = resources.SetTo(&in, false); 212 if (error != B_OK) { 213 fprintf(stderr, "Failed to read resources of input file `%s': %s\n", 214 inputFiles[i], strerror(error)); 215 exit(1); 216 } 217 218 // add the attributes 219 write_attributes(out, inputFiles[i], resources, overwrite); 220 } 221 } 222 223 // resources_to_attributes 224 static 225 void 226 attributes_to_resources(const char *outputFile, const char **inputFiles, 227 int inputFileCount) 228 { 229 // open output file 230 BFile out; 231 status_t error = out.SetTo(outputFile, B_READ_WRITE | B_CREATE_FILE); 232 if (error != B_OK) { 233 fprintf(stderr, "Failed to open output file `%s': %s\n", outputFile, 234 strerror(error)); 235 exit(1); 236 } 237 238 // init output resources 239 BResources resources; 240 error = resources.SetTo(&out, false); 241 if (error != B_OK) { 242 fprintf(stderr, "Failed to init resources of output file `%s': %s\n", 243 outputFile, strerror(error)); 244 exit(1); 245 } 246 247 int32 resID = 0; 248 249 // iterate through the input files 250 for (int i = 0; i < inputFileCount; i++) { 251 // open input file 252 BNode in; 253 error = in.SetTo(inputFiles[i]); 254 if (error != B_OK) { 255 fprintf(stderr, "Failed to open input file `%s': %s\n", 256 inputFiles[i], strerror(error)); 257 exit(1); 258 } 259 260 // add the resources 261 262 write_resources(resources, inputFiles[i], in, resID); 263 } 264 } 265 266 // main 267 int 268 main(int argc, const char *const *argv) 269 { 270 sArgc = argc; 271 sArgv = argv; 272 273 // parameters 274 const char *outputFile = NULL; 275 bool overwrite = false; 276 bool reverse = false; 277 const char **inputFiles = new const char*[argc]; 278 int inputFileCount = 0; 279 280 // parse arguments 281 for (int argi = 1; argi < argc; argi++) { 282 const char *arg = argv[argi]; 283 if (arg[0] == '-') { 284 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { 285 print_usage_and_exit(false); 286 } else if (strcmp(arg, "-o") == 0) { 287 outputFile = next_arg(argc, argv, argi); 288 } else if (strcmp(arg, "-O") == 0) { 289 overwrite = true; 290 } else if (strcmp(arg, "-r") == 0 291 || strcmp(arg, "--reverse") == 0) { 292 reverse = true; 293 } else { 294 print_usage_and_exit(true); 295 } 296 } else { 297 inputFiles[inputFileCount++] = arg; 298 } 299 } 300 301 // check parameters 302 if (!outputFile) 303 print_usage_and_exit(true); 304 305 if (reverse) { 306 attributes_to_resources(outputFile, inputFiles, inputFileCount); 307 } else { 308 resources_to_attributes(outputFile, inputFiles, inputFileCount, 309 overwrite); 310 } 311 312 return 0; 313 } 314