1 /* 2 * Copyright 2011, 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 14 #include <Message.h> 15 #include <String.h> 16 17 18 enum { 19 FLAG_MANDATORY_FIELD = 0x01, 20 FLAG_LIST_ATTRIBUTE = 0x02, 21 FLAG_DONT_QUOTE = 0x04, 22 }; 23 24 25 extern const char* __progname; 26 const char* kCommandName = __progname; 27 28 29 static const char* kUsage = 30 "Usage: %s [ <options> ] <optional package description> " 31 "[ <package info> ]\n" 32 "Converts an .OptionalPackageDescription to a .PackageInfo. If " 33 "<package info>\n" 34 "is not specified, the output is printed to stdout.\n" 35 "Note that the generated .PackageInfo will not be complete. For several\n" 36 "fields an empty string will be used, unless specified via an option.\n" 37 "The \"provides\" and \"requires\" lists will always be empty, though\n" 38 "\n" 39 "Options:\n" 40 " -a <arch> - Use the given architecture string. Default is to " 41 "guess from the file name.\n" 42 " -d <description> - Use the given descripton string. Default is to use\n" 43 " the summary.\n" 44 " -h, --help - Print this usage info.\n" 45 " -p <packager> - Use the given packager string. Default is an empty " 46 "string.\n" 47 " -s <summary> - Use the given summary string. Default is an empty " 48 "string.\n" 49 " -v <version> - Use the given version string. Overrides the version\n" 50 " from the input file.\n" 51 " -V <vendor> - Use the given vendor string. Default is an empty " 52 "string.\n" 53 ; 54 55 56 static void 57 print_usage_and_exit(bool error) 58 { 59 fprintf(error ? stderr : stdout, kUsage, kCommandName); 60 exit(error ? 1 : 0); 61 } 62 63 64 static const char* 65 guess_architecture(const char* name) 66 { 67 if (strstr(name, "x86") != NULL) { 68 if (strstr(name, "gcc4") != NULL) 69 return "x86"; 70 71 return "x86_gcc2"; 72 } 73 74 return NULL; 75 } 76 77 78 struct OuputWriter { 79 OuputWriter(FILE* output, const BMessage& package) 80 : 81 fOutput(output), 82 fPackage(package) 83 { 84 } 85 86 void WriteAttribute(const char* attributeName, const char* fieldName, 87 const char* defaultValue, uint32 flags) 88 { 89 if (fieldName != NULL) { 90 int32 count; 91 type_code type; 92 if (fPackage.GetInfo(fieldName, &type, &count) != B_OK) { 93 if ((flags & FLAG_MANDATORY_FIELD) != 0) { 94 fprintf(stderr, "Error: Missing mandatory field \"%s\" in " 95 "input file.\n", fieldName); 96 exit(1); 97 } 98 count = 0; 99 } 100 101 if (count > 0) { 102 if (count == 1) { 103 const char* value; 104 fPackage.FindString(fieldName, &value); 105 _WriteSingleElementAttribute(attributeName, value, flags); 106 } else { 107 fprintf(fOutput, "\n%s {\n", attributeName); 108 109 for (int32 i = 0; i < count; i++) { 110 fprintf(fOutput, "\t"); 111 const char* value; 112 fPackage.FindString(fieldName, i, &value); 113 _WriteValue(value, flags); 114 fputc('\n', fOutput); 115 } 116 117 fputs("}\n", fOutput); 118 } 119 120 return; 121 } 122 } 123 124 // write the default value 125 if (defaultValue != NULL) 126 _WriteSingleElementAttribute(attributeName, defaultValue, flags); 127 } 128 129 private: 130 void _WriteSingleElementAttribute(const char* attributeName, 131 const char* value, uint32 flags) 132 { 133 fputs(attributeName, fOutput); 134 135 int32 indentation = 16 - (int32)strlen(attributeName); 136 if (indentation > 0) 137 indentation = (indentation + 3) / 4; 138 else 139 indentation = 1; 140 141 for (int32 i = 0; i < indentation; i++) 142 fputc('\t', fOutput); 143 144 _WriteValue(value, flags); 145 fputc('\n', fOutput); 146 } 147 148 void _WriteValue(const char* value, uint32 flags) 149 { 150 BString escapedValue(value); 151 152 if ((flags & FLAG_DONT_QUOTE) != 0) { 153 escapedValue.CharacterEscape("\\\"' \t", '\\'); 154 fputs(escapedValue.String(), fOutput); 155 } else { 156 escapedValue.CharacterEscape("\\\"", '\\'); 157 fprintf(fOutput, "\"%s\"", escapedValue.String()); 158 } 159 } 160 161 private: 162 FILE* fOutput; 163 const BMessage& fPackage; 164 }; 165 166 167 int 168 main(int argc, const char* const* argv) 169 { 170 const char* architecture = NULL; 171 const char* version = NULL; 172 const char* summary = ""; 173 const char* description = ""; 174 const char* packager = ""; 175 const char* vendor = ""; 176 177 while (true) { 178 static const struct option kLongOptions[] = { 179 { "help", no_argument, 0, 'h' }, 180 { 0, 0, 0, 0 } 181 }; 182 183 opterr = 0; // don't print errors 184 int c = getopt_long(argc, (char**)argv, "+ha:d:p:s:v:V:", kLongOptions, 185 NULL); 186 if (c == -1) 187 break; 188 189 switch (c) { 190 case 'a': 191 architecture = optarg; 192 break; 193 194 case 'd': 195 description = optarg; 196 break; 197 198 case 'h': 199 print_usage_and_exit(false); 200 break; 201 202 case 'p': 203 packager = optarg; 204 break; 205 206 case 's': 207 summary = optarg; 208 break; 209 210 case 'v': 211 version = optarg; 212 break; 213 214 case 'V': 215 vendor = optarg; 216 break; 217 218 default: 219 print_usage_and_exit(true); 220 break; 221 } 222 } 223 224 // One or two argument should remain -- the input file and optionally the 225 // output file. 226 if (optind + 1 != argc && optind + 2 != argc) 227 print_usage_and_exit(true); 228 229 const char* opdName = argv[optind++]; 230 const char* packageInfoName = optind < argc ? argv[optind++] : NULL; 231 232 // guess architecture from the input file name, if not given 233 if (architecture == NULL) { 234 const char* fileName = strrchr(opdName, '/'); 235 if (fileName == NULL) 236 fileName = opdName; 237 else 238 fileName++; 239 240 // Try to guess from the file name. 241 architecture = guess_architecture(fileName); 242 243 // If we've got nothing yet, try to guess from the file name. 244 if (architecture == NULL && fileName != opdName) 245 architecture = guess_architecture(opdName); 246 247 // fallback is "any" 248 if (architecture == NULL) 249 architecture = "any"; 250 } 251 252 // open the input 253 FILE* input = fopen(opdName, "r"); 254 if (input == NULL) { 255 fprintf(stderr, "Failed to open input file \"%s\": %s\n", opdName, 256 strerror(errno)); 257 exit(1); 258 } 259 260 // open the output 261 FILE* output = packageInfoName != NULL 262 ? fopen(packageInfoName, "w+") : stdout; 263 if (output == NULL) { 264 fprintf(stderr, "Failed to open output file \"%s\": %s\n", 265 packageInfoName, strerror(errno)); 266 exit(1); 267 } 268 269 // read and parse the input file 270 BMessage package; 271 BString fieldName; 272 BString fieldValue; 273 char lineBuffer[LINE_MAX]; 274 bool seenPackageAttribute = false; 275 276 while (char* line = fgets(lineBuffer, sizeof(lineBuffer), input)) { 277 // chop off line break 278 size_t lineLen = strlen(line); 279 if (lineLen > 0 && line[lineLen - 1] == '\n') 280 line[--lineLen] = '\0'; 281 282 // flush previous field, if a new field begins, otherwise append 283 if (lineLen == 0 || !isspace(line[0])) { 284 // new field -- flush the previous one 285 if (fieldName.Length() > 0) { 286 fieldValue.Trim(); 287 package.AddString(fieldName.String(), fieldValue); 288 fieldName = ""; 289 } 290 } else if (fieldName.Length() > 0) { 291 // append to current field 292 fieldValue += line; 293 continue; 294 } else { 295 // bogus line -- ignore 296 continue; 297 } 298 299 if (lineLen == 0) 300 continue; 301 302 // parse new field 303 char* colon = strchr(line, ':'); 304 if (colon == NULL) { 305 // bogus line -- ignore 306 continue; 307 } 308 309 fieldName.SetTo(line, colon - line); 310 fieldName.Trim(); 311 if (fieldName.Length() == 0) { 312 // invalid field name 313 continue; 314 } 315 316 fieldValue = colon + 1; 317 318 if (fieldName == "Package") { 319 if (seenPackageAttribute) { 320 fprintf(stderr, "Duplicate \"Package\" attribute!\n"); 321 exit(1); 322 } 323 324 seenPackageAttribute = true; 325 } 326 } 327 328 // write the output 329 OuputWriter writer(output, package); 330 331 // name 332 writer.WriteAttribute("name", "Package", NULL, 333 FLAG_MANDATORY_FIELD | FLAG_DONT_QUOTE); 334 335 // version 336 writer.WriteAttribute("version", "Version", version, FLAG_DONT_QUOTE); 337 338 // architecture 339 fprintf(output, "architecture\t%s\n", architecture); 340 341 // summary 342 fprintf(output, "summary\t\t\t\"%s\"\n", summary); 343 344 // description 345 if (description != NULL) 346 fprintf(output, "description\t\t\"%s\"\n", description); 347 else 348 fprintf(output, "description\t\t\"%s\"\n", summary); 349 350 // packager 351 fprintf(output, "packager\t\t\"%s\"\n", packager); 352 353 // vendor 354 fprintf(output, "vendor\t\t\t\"%s\"\n", vendor); 355 356 // copyrights 357 writer.WriteAttribute("copyrights", "Copyright", NULL, 358 FLAG_MANDATORY_FIELD | FLAG_LIST_ATTRIBUTE); 359 360 // licenses 361 writer.WriteAttribute("licenses", "License", NULL, FLAG_LIST_ATTRIBUTE); 362 363 // empty provides 364 fprintf(output, "\nprovides {\n}\n"); 365 366 // empty requires 367 fprintf(output, "\nrequires {\n}\n"); 368 369 // URLs 370 writer.WriteAttribute("urls", "URL", NULL, FLAG_LIST_ATTRIBUTE); 371 372 // source URLs 373 writer.WriteAttribute("source-urls", "SourceURL", NULL, 374 FLAG_LIST_ATTRIBUTE); 375 376 return 0; 377 } 378