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, 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
00208 static IniLoadFile *LoadIniFile(const char *filename)
00209 {
00210 static const char * const seq_groups[] = {PREAMBLE_GROUP_NAME, POSTAMBLE_GROUP_NAME, NULL};
00211
00212 IniLoadFile *ini = new SettingsIniFile(NULL, seq_groups);
00213 ini->LoadFromDisk(filename);
00214 return ini;
00215 }
00216
00222 static void DumpGroup(IniLoadFile *ifile, const char * const group_name)
00223 {
00224 IniGroup *grp = ifile->GetGroup(group_name, 0, false);
00225 if (grp != NULL && grp->type == IGT_SEQUENCE) {
00226 for (IniItem *item = grp->item; item != NULL; item = item->next) {
00227 if (item->name) {
00228 _stored_output.Add(item->name);
00229 _stored_output.Add("\n", 1);
00230 }
00231 }
00232 }
00233 }
00234
00242 static const char *FindItemValue(const char *name, IniGroup *grp, IniGroup *defaults)
00243 {
00244 IniItem *item = grp->GetItem(name, false);
00245 if (item == NULL && defaults != NULL) item = defaults->GetItem(name, false);
00246 if (item == NULL || item->value == NULL) return NULL;
00247 return item->value;
00248 }
00249
00254 static void DumpSections(IniLoadFile *ifile)
00255 {
00256 static const int MAX_VAR_LENGTH = 64;
00257 static const char * const special_group_names[] = {PREAMBLE_GROUP_NAME, POSTAMBLE_GROUP_NAME, DEFAULTS_GROUP_NAME, TEMPLATES_GROUP_NAME, NULL};
00258
00259 IniGroup *default_grp = ifile->GetGroup(DEFAULTS_GROUP_NAME, 0, false);
00260 IniGroup *templates_grp = ifile->GetGroup(TEMPLATES_GROUP_NAME, 0, false);
00261 if (templates_grp == NULL) return;
00262
00263
00264 for (IniGroup *grp = ifile->group; grp != NULL; grp = grp->next) {
00265 const char * const *sgn;
00266 for (sgn = special_group_names; *sgn != NULL; sgn++) if (strcmp(grp->name, *sgn) == 0) break;
00267 if (*sgn != NULL) continue;
00268
00269 IniItem *template_item = templates_grp->GetItem(grp->name, false);
00270 if (template_item == NULL || template_item->value == NULL) {
00271 fprintf(stderr, "settingsgen: Warning: Cannot find template %s\n", grp->name);
00272 continue;
00273 }
00274
00275
00276 static const char * const pp_lines[] = {"if", "ifdef", "ifndef", NULL};
00277 int count = 0;
00278 for (const char * const *name = pp_lines; *name != NULL; name++) {
00279 const char *condition = FindItemValue(*name, grp, default_grp);
00280 if (condition != NULL) {
00281 _stored_output.Add("#", 1);
00282 _stored_output.Add(*name);
00283 _stored_output.Add(" ", 1);
00284 _stored_output.Add(condition);
00285 _stored_output.Add("\n", 1);
00286 count++;
00287 }
00288 }
00289
00290
00291 const char *txt = template_item->value;
00292 while (*txt != '\0') {
00293 if (*txt != '$') {
00294 _stored_output.Add(txt, 1);
00295 txt++;
00296 continue;
00297 }
00298 txt++;
00299 if (*txt == '$') {
00300 _stored_output.Add(txt, 1);
00301 txt++;
00302 continue;
00303 }
00304
00305
00306 char variable[MAX_VAR_LENGTH];
00307 int i = 0;
00308 while (i < MAX_VAR_LENGTH - 1) {
00309 if (!(txt[i] == '_' || (txt[i] >= 'a' && txt[i] <= 'z') || (txt[i] >= '0' && txt[i] <= '9'))) break;
00310 variable[i] = txt[i];
00311 i++;
00312 }
00313 variable[i] = '\0';
00314 txt += i;
00315
00316 if (i > 0) {
00317
00318 const char *valitem = FindItemValue(variable, grp, default_grp);
00319 if (valitem != NULL) _stored_output.Add(valitem);
00320 } else {
00321 _stored_output.Add("$", 1);
00322 }
00323 }
00324 _stored_output.Add("\n", 1);
00325 while (count > 0) {
00326 _stored_output.Add("#endif\n");
00327 count--;
00328 }
00329 }
00330 }
00331
00337 static void CopyFile(const char *fname, FILE *out_fp)
00338 {
00339 if (fname == NULL) return;
00340
00341 FILE *in_fp = fopen(fname, "r");
00342 if (in_fp == NULL) {
00343 fprintf(stderr, "settingsgen: Warning: Cannot open file %s for copying\n", fname);
00344 return;
00345 }
00346
00347 char buffer[4096];
00348 size_t length;
00349 do {
00350 length = fread(buffer, 1, lengthof(buffer), in_fp);
00351 if (fwrite(buffer, 1, length, out_fp) != length) {
00352 fprintf(stderr, "Error: Cannot copy file\n");
00353 break;
00354 }
00355 } while (length == lengthof(buffer));
00356
00357 fclose(in_fp);
00358 }
00359
00366 static bool CompareFiles(const char *n1, const char *n2)
00367 {
00368 FILE *f2 = fopen(n2, "rb");
00369 if (f2 == NULL) return false;
00370
00371 FILE *f1 = fopen(n1, "rb");
00372 if (f1 == NULL) error("can't open %s", n1);
00373
00374 size_t l1, l2;
00375 do {
00376 char b1[4096];
00377 char b2[4096];
00378 l1 = fread(b1, 1, sizeof(b1), f1);
00379 l2 = fread(b2, 1, sizeof(b2), f2);
00380
00381 if (l1 != l2 || memcmp(b1, b2, l1) != 0) {
00382 fclose(f2);
00383 fclose(f1);
00384 return false;
00385 }
00386 } while (l1 != 0);
00387
00388 fclose(f2);
00389 fclose(f1);
00390 return true;
00391 }
00392
00394 static const OptionData _opts[] = {
00395 GETOPT_NOVAL( 'v', "--version"),
00396 GETOPT_NOVAL( 'h', "--help"),
00397 GETOPT_GENERAL('h', '?', NULL, ODF_NO_VALUE),
00398 GETOPT_VALUE( 'o', "--output"),
00399 GETOPT_VALUE( 'b', "--before"),
00400 GETOPT_VALUE( 'a', "--after"),
00401 GETOPT_END(),
00402 };
00403
00424 static void ProcessIniFile(const char *fname)
00425 {
00426 IniLoadFile *ini_data = LoadIniFile(fname);
00427 DumpGroup(ini_data, PREAMBLE_GROUP_NAME);
00428 DumpSections(ini_data);
00429 DumpGroup(ini_data, POSTAMBLE_GROUP_NAME);
00430 delete ini_data;
00431 }
00432
00438 int CDECL main(int argc, char *argv[])
00439 {
00440 const char *output_file = NULL;
00441 const char *before_file = NULL;
00442 const char *after_file = NULL;
00443
00444 GetOptData mgo(argc - 1, argv + 1, _opts);
00445 for (;;) {
00446 int i = mgo.GetOpt();
00447 if (i == -1) break;
00448
00449 switch (i) {
00450 case 'v':
00451 puts("$Revision$");
00452 return 0;
00453
00454 case 'h':
00455 puts("settingsgen - $Revision$\n"
00456 "Usage: settingsgen [options] ini-file...\n"
00457 "with options:\n"
00458 " -v, --version Print version information and exit\n"
00459 " -h, -?, --help Print this help message and exit\n"
00460 " -b FILE, --before FILE Copy FILE before all settings\n"
00461 " -a FILE, --after FILE Copy FILE after all settings\n"
00462 " -o FILE, --output FILE Write output to FILE\n");
00463 return 0;
00464
00465 case 'o':
00466 output_file = mgo.opt;
00467 break;
00468
00469 case 'a':
00470 after_file = mgo.opt;
00471 break;
00472
00473 case 'b':
00474 before_file = mgo.opt;
00475 break;
00476
00477 case -2:
00478 fprintf(stderr, "Invalid arguments\n");
00479 return 1;
00480 }
00481 }
00482
00483 _stored_output.Clear();
00484
00485 for (int i = 0; i < mgo.numleft; i++) ProcessIniFile(mgo.argv[i]);
00486
00487
00488 if (output_file == NULL) {
00489 CopyFile(before_file, stdout);
00490 _stored_output.Write(stdout);
00491 CopyFile(after_file, stdout);
00492 } else {
00493 static const char * const tmp_output = "tmp2.xxx";
00494
00495 FILE *fp = fopen(tmp_output, "w");
00496 if (fp == NULL) {
00497 fprintf(stderr, "settingsgen: Warning: Cannot open file %s\n", tmp_output);
00498 return 1;
00499 }
00500 CopyFile(before_file, fp);
00501 _stored_output.Write(fp);
00502 CopyFile(after_file, fp);
00503 fclose(fp);
00504
00505 if (CompareFiles(tmp_output, output_file)) {
00506
00507 unlink(tmp_output);
00508 } else {
00509
00510 #if defined(WIN32) || defined(WIN64)
00511 unlink(output_file);
00512 #endif
00513 if (rename(tmp_output, output_file) == -1) error("rename() failed");
00514 }
00515 }
00516 return 0;
00517 }