00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../stdafx.h"
00013 #include "../string_func.h"
00014 #include "../strings_type.h"
00015 #include "../misc/getoptdata.h"
00016 #include "../ini_type.h"
00017 #include "../core/smallvec_type.hpp"
00018
00019 #include <stdarg.h>
00020
00021 #if (!defined(WIN32) && !defined(WIN64)) || defined(__CYGWIN__)
00022 #include <unistd.h>
00023 #include <sys/stat.h>
00024 #endif
00025
00026 #ifdef __MORPHOS__
00027 #ifdef stderr
00028 #undef stderr
00029 #endif
00030 #define stderr stdout
00031 #endif
00032
00038 void NORETURN CDECL error(const char *s, ...)
00039 {
00040 char buf[1024];
00041 va_list va;
00042 va_start(va, s);
00043 vsnprintf(buf, lengthof(buf), s, va);
00044 va_end(va);
00045 fprintf(stderr, "FATAL: %s\n", buf);
00046 exit(1);
00047 }
00048
00049 static const int OUTPUT_BLOCK_SIZE = 16000;
00050
00052 class OutputBuffer {
00053 public:
00055 void Clear()
00056 {
00057 this->size = 0;
00058 }
00059
00066 int Add(const char *text, int length)
00067 {
00068 int store_size = min(length, OUTPUT_BLOCK_SIZE - this->size);
00069 assert(store_size >= 0);
00070 assert(store_size <= OUTPUT_BLOCK_SIZE);
00071 MemCpyT(this->data + this->size, text, store_size);
00072 this->size += store_size;
00073 return store_size;
00074 }
00075
00080 void Write(FILE *out_fp) const
00081 {
00082 if (fwrite(this->data, 1, this->size, out_fp) != (size_t)this->size) {
00083 fprintf(stderr, "Error: Cannot write output\n");
00084 }
00085 }
00086
00091 bool HasRoom() const
00092 {
00093 return this->size < OUTPUT_BLOCK_SIZE;
00094 }
00095
00096 int size;
00097 char data[OUTPUT_BLOCK_SIZE];
00098 };
00099
00101 class OutputStore {
00102 public:
00103 OutputStore()
00104 {
00105 this->Clear();
00106 }
00107
00109 void Clear()
00110 {
00111 this->output_buffer.Clear();
00112 }
00113
00119 void Add(const char *text, int length = 0)
00120 {
00121 if (length == 0) length = strlen(text);
00122
00123 if (length > 0 && this->BufferHasRoom()) {
00124 int stored_size = this->output_buffer[this->output_buffer.Length() - 1].Add(text, length);
00125 length -= stored_size;
00126 text += stored_size;
00127 }
00128 while (length > 0) {
00129 OutputBuffer *block = this->output_buffer.Append();
00130 block->Clear();
00131 int stored_size = block->Add(text, length);
00132 length -= stored_size;
00133 text += stored_size;
00134 }
00135 }
00136
00141 void Write(FILE *out_fp) const
00142 {
00143 for (const OutputBuffer *out_data = this->output_buffer.Begin(); out_data != this->output_buffer.End(); out_data++) {
00144 out_data->Write(out_fp);
00145 }
00146 }
00147
00148 private:
00153 bool BufferHasRoom() const
00154 {
00155 uint num_blocks = this->output_buffer.Length();
00156 return num_blocks > 0 && this->output_buffer[num_blocks - 1].HasRoom();
00157 }
00158
00159 typedef SmallVector<OutputBuffer, 2> OutputBufferVector;
00160 OutputBufferVector output_buffer;
00161 };
00162
00163
00165 struct SettingsIniFile : IniLoadFile {
00171 SettingsIniFile(const char * const *list_group_names = NULL, const char * const *seq_group_names = NULL) :
00172 IniLoadFile(list_group_names, seq_group_names)
00173 {
00174 }
00175
00176 virtual FILE *OpenFile(const char *filename, Subdirectory subdir, size_t *size)
00177 {
00178
00179
00180 FILE *in = fopen(filename, "rb");
00181 if (in == NULL) return NULL;
00182
00183 fseek(in, 0L, SEEK_END);
00184 *size = ftell(in);
00185
00186 fseek(in, 0L, SEEK_SET);
00187 return in;
00188 }
00189
00190 virtual void ReportFileError(const char * const pre, const char * const buffer, const char * const post)
00191 {
00192 error("%s%s%s", pre, buffer, post);
00193 }
00194 };
00195
00196 OutputStore _stored_output;
00197
00198 static const char *PREAMBLE_GROUP_NAME = "pre-amble";
00199 static const char *POSTAMBLE_GROUP_NAME = "post-amble";
00200 static const char *TEMPLATES_GROUP_NAME = "templates";
00201 static const char *DEFAULTS_GROUP_NAME = "defaults";
00202
00209 static IniLoadFile *LoadIniFile(const char *filename)
00210 {
00211 static const char * const seq_groups[] = {PREAMBLE_GROUP_NAME, POSTAMBLE_GROUP_NAME, NULL};
00212
00213 IniLoadFile *ini = new SettingsIniFile(NULL, seq_groups);
00214 ini->LoadFromDisk(filename, NO_DIRECTORY);
00215 return ini;
00216 }
00217
00223 static void DumpGroup(IniLoadFile *ifile, const char * const group_name)
00224 {
00225 IniGroup *grp = ifile->GetGroup(group_name, 0, false);
00226 if (grp != NULL && grp->type == IGT_SEQUENCE) {
00227 for (IniItem *item = grp->item; item != NULL; item = item->next) {
00228 if (item->name) {
00229 _stored_output.Add(item->name);
00230 _stored_output.Add("\n", 1);
00231 }
00232 }
00233 }
00234 }
00235
00243 static const char *FindItemValue(const char *name, IniGroup *grp, IniGroup *defaults)
00244 {
00245 IniItem *item = grp->GetItem(name, false);
00246 if (item == NULL && defaults != NULL) item = defaults->GetItem(name, false);
00247 if (item == NULL || item->value == NULL) return NULL;
00248 return item->value;
00249 }
00250
00255 static void DumpSections(IniLoadFile *ifile)
00256 {
00257 static const int MAX_VAR_LENGTH = 64;
00258 static const char * const special_group_names[] = {PREAMBLE_GROUP_NAME, POSTAMBLE_GROUP_NAME, DEFAULTS_GROUP_NAME, TEMPLATES_GROUP_NAME, NULL};
00259
00260 IniGroup *default_grp = ifile->GetGroup(DEFAULTS_GROUP_NAME, 0, false);
00261 IniGroup *templates_grp = ifile->GetGroup(TEMPLATES_GROUP_NAME, 0, false);
00262 if (templates_grp == NULL) return;
00263
00264
00265 for (IniGroup *grp = ifile->group; grp != NULL; grp = grp->next) {
00266 const char * const *sgn;
00267 for (sgn = special_group_names; *sgn != NULL; sgn++) if (strcmp(grp->name, *sgn) == 0) break;
00268 if (*sgn != NULL) continue;
00269
00270 IniItem *template_item = templates_grp->GetItem(grp->name, false);
00271 if (template_item == NULL || template_item->value == NULL) {
00272 fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name);
00273 continue;
00274 }
00275
00276
00277 static const char * const pp_lines[] = {"if", "ifdef", "ifndef", NULL};
00278 int count = 0;
00279 for (const char * const *name = pp_lines; *name != NULL; name++) {
00280 const char *condition = FindItemValue(*name, grp, default_grp);
00281 if (condition != NULL) {
00282 _stored_output.Add("#", 1);
00283 _stored_output.Add(*name);
00284 _stored_output.Add(" ", 1);
00285 _stored_output.Add(condition);
00286 _stored_output.Add("\n", 1);
00287 count++;
00288 }
00289 }
00290
00291
00292 const char *txt = template_item->value;
00293 while (*txt != '\0') {
00294 if (*txt != '$') {
00295 _stored_output.Add(txt, 1);
00296 txt++;
00297 continue;
00298 }
00299 txt++;
00300 if (*txt == '$') {
00301 _stored_output.Add(txt, 1);
00302 txt++;
00303 continue;
00304 }
00305
00306
00307 char variable[MAX_VAR_LENGTH];
00308 int i = 0;
00309 while (i < MAX_VAR_LENGTH - 1) {
00310 if (!(txt[i] == '_' || (txt[i] >= 'a' && txt[i] <= 'z') || (txt[i] >= '0' && txt[i] <= '9'))) break;
00311 variable[i] = txt[i];
00312 i++;
00313 }
00314 variable[i] = '\0';
00315 txt += i;
00316
00317 if (i > 0) {
00318
00319 const char *valitem = FindItemValue(variable, grp, default_grp);
00320 if (valitem != NULL) _stored_output.Add(valitem);
00321 } else {
00322 _stored_output.Add("$", 1);
00323 }
00324 }
00325 _stored_output.Add("\n", 1);
00326 while (count > 0) {
00327 _stored_output.Add("#endif\n");
00328 count--;
00329 }
00330 }
00331 }
00332
00338 static void CopyFile(const char *fname, FILE *out_fp)
00339 {
00340 if (fname == NULL) return;
00341
00342 FILE *in_fp = fopen(fname, "r");
00343 if (in_fp == NULL) {
00344 fprintf(stderr, "settingsgen: Warning: Cannot open file %s for copying\n", fname);
00345 return;
00346 }
00347
00348 char buffer[4096];
00349 size_t length;
00350 do {
00351 length = fread(buffer, 1, lengthof(buffer), in_fp);
00352 if (fwrite(buffer, 1, length, out_fp) != length) {
00353 fprintf(stderr, "Error: Cannot copy file\n");
00354 break;
00355 }
00356 } while (length == lengthof(buffer));
00357
00358 fclose(in_fp);
00359 }
00360
00367 static bool CompareFiles(const char *n1, const char *n2)
00368 {
00369 FILE *f2 = fopen(n2, "rb");
00370 if (f2 == NULL) return false;
00371
00372 FILE *f1 = fopen(n1, "rb");
00373 if (f1 == NULL) error("can't open %s", n1);
00374
00375 size_t l1, l2;
00376 do {
00377 char b1[4096];
00378 char b2[4096];
00379 l1 = fread(b1, 1, sizeof(b1), f1);
00380 l2 = fread(b2, 1, sizeof(b2), f2);
00381
00382 if (l1 != l2 || memcmp(b1, b2, l1) != 0) {
00383 fclose(f2);
00384 fclose(f1);
00385 return false;
00386 }
00387 } while (l1 != 0);
00388
00389 fclose(f2);
00390 fclose(f1);
00391 return true;
00392 }
00393
00395 static const OptionData _opts[] = {
00396 GETOPT_NOVAL( 'v', "--version"),
00397 GETOPT_NOVAL( 'h', "--help"),
00398 GETOPT_GENERAL('h', '?', NULL, ODF_NO_VALUE),
00399 GETOPT_VALUE( 'o', "--output"),
00400 GETOPT_VALUE( 'b', "--before"),
00401 GETOPT_VALUE( 'a', "--after"),
00402 GETOPT_END(),
00403 };
00404
00425 static void ProcessIniFile(const char *fname)
00426 {
00427 IniLoadFile *ini_data = LoadIniFile(fname);
00428 DumpGroup(ini_data, PREAMBLE_GROUP_NAME);
00429 DumpSections(ini_data);
00430 DumpGroup(ini_data, POSTAMBLE_GROUP_NAME);
00431 delete ini_data;
00432 }
00433
00439 int CDECL main(int argc, char *argv[])
00440 {
00441 const char *output_file = NULL;
00442 const char *before_file = NULL;
00443 const char *after_file = NULL;
00444
00445 GetOptData mgo(argc - 1, argv + 1, _opts);
00446 for (;;) {
00447 int i = mgo.GetOpt();
00448 if (i == -1) break;
00449
00450 switch (i) {
00451 case 'v':
00452 puts("$Revision$");
00453 return 0;
00454
00455 case 'h':
00456 puts("settingsgen - $Revision$\n"
00457 "Usage: settingsgen [options] ini-file...\n"
00458 "with options:\n"
00459 " -v, --version Print version information and exit\n"
00460 " -h, -?, --help Print this help message and exit\n"
00461 " -b FILE, --before FILE Copy FILE before all settings\n"
00462 " -a FILE, --after FILE Copy FILE after all settings\n"
00463 " -o FILE, --output FILE Write output to FILE\n");
00464 return 0;
00465
00466 case 'o':
00467 output_file = mgo.opt;
00468 break;
00469
00470 case 'a':
00471 after_file = mgo.opt;
00472 break;
00473
00474 case 'b':
00475 before_file = mgo.opt;
00476 break;
00477
00478 case -2:
00479 fprintf(stderr, "Invalid arguments\n");
00480 return 1;
00481 }
00482 }
00483
00484 _stored_output.Clear();
00485
00486 for (int i = 0; i < mgo.numleft; i++) ProcessIniFile(mgo.argv[i]);
00487
00488
00489 if (output_file == NULL) {
00490 CopyFile(before_file, stdout);
00491 _stored_output.Write(stdout);
00492 CopyFile(after_file, stdout);
00493 } else {
00494 static const char * const tmp_output = "tmp2.xxx";
00495
00496 FILE *fp = fopen(tmp_output, "w");
00497 if (fp == NULL) {
00498 fprintf(stderr, "settingsgen: Warning: Cannot open file %s\n", tmp_output);
00499 return 1;
00500 }
00501 CopyFile(before_file, fp);
00502 _stored_output.Write(fp);
00503 CopyFile(after_file, fp);
00504 fclose(fp);
00505
00506 if (CompareFiles(tmp_output, output_file)) {
00507
00508 unlink(tmp_output);
00509 } else {
00510
00511 #if defined(WIN32) || defined(WIN64)
00512 unlink(output_file);
00513 #endif
00514 if (rename(tmp_output, output_file) == -1) error("rename() failed");
00515 }
00516 }
00517 return 0;
00518 }