1 /*
2     Copyright (C) 2010 Andrew Caudwell (acaudwell@gmail.com)
3 
4     This program is free software; you can redistribute it and/or
5     modify it under the terms of the GNU General Public License
6     as published by the Free Software Foundation; either version
7     3 of the License, or (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #include "gource_settings.h"
19 #include "core/sdlapp.h"
20 
21 #include <boost/filesystem.hpp>
22 #include <boost/format.hpp>
23 #include <boost/algorithm/string.hpp>
24 
25 #include "core/utf8/utf8.h"
26 #include <time.h>
27 
28 #include "formats/hg.h"
29 #include "formats/git.h"
30 #include "formats/bzr.h"
31 #include "formats/cvs-exp.h"
32 #include "formats/cvs2cl.h"
33 #include "formats/svn.h"
34 
35 #ifndef GOURCE_FONT_FILE
36 #define GOURCE_FONT_FILE "FreeSans.ttf"
37 #endif
38 
39 GourceSettings gGourceSettings;
40 
41 //display help message
help(bool extended_help)42 void GourceSettings::help(bool extended_help) {
43 
44 #ifdef _WIN32
45     //resize window to fit help message
46     SDLApp::resizeConsole(1000);
47     SDLApp::showConsole(true);
48 #endif
49 
50     printf("Gource v%s\n", GOURCE_VERSION);
51 
52     printf("Usage: gource [options] [path]\n");
53     printf("\nOptions:\n");
54     printf("  -h, --help                       Help\n\n");
55     printf("  -WIDTHxHEIGHT, --viewport        Set viewport size\n");
56     printf("  -f, --fullscreen                 Fullscreen\n");
57     printf("      --screen SCREEN              Screen number\n");
58     printf("      --multi-sampling             Enable multi-sampling\n");
59     printf("      --no-vsync                   Disable vsync\n\n");
60 
61     printf("  --start-date 'YYYY-MM-DD hh:mm:ss +tz'  Start at a date and optional time\n");
62     printf("  --stop-date  'YYYY-MM-DD hh:mm:ss +tz'  Stop at a date and optional time\n\n");
63     printf("  -p, --start-position POSITION    Start at some position (0.0-1.0 or 'random')\n");
64     printf("      --stop-position  POSITION    Stop at some position\n");
65     printf("  -t, --stop-at-time SECONDS       Stop after a specified number of seconds\n");
66     printf("      --stop-at-end                Stop at end of the log\n");
67     printf("      --dont-stop                  Keep running after the end of the log\n");
68     printf("      --loop                       Loop at the end of the log\n\n");
69 
70     printf("  -a, --auto-skip-seconds SECONDS  Auto skip to next entry if nothing happens\n");
71     printf("                                   for a number of seconds (default: 3)\n");
72     printf("      --disable-auto-skip          Disable auto skip\n");
73     printf("  -s, --seconds-per-day SECONDS    Speed in seconds per day (default: 10)\n");
74     printf("      --realtime                   Realtime playback speed\n");
75     printf("      --no-time-travel             Use the time of the last commit if the\n");
76     printf("                                   time of a commit is in the past\n");
77     printf("  -c, --time-scale SCALE           Change simulation time scale (default: 1.0)\n");
78     printf("  -e, --elasticity FLOAT           Elasticity of nodes (default: 0.0)\n\n");
79 
80     printf("  --key                            Show file extension key\n\n");
81 
82     printf("  --user-image-dir DIRECTORY       Dir containing images to use as avatars\n");
83     printf("  --default-user-image IMAGE       Default user image file\n");
84     printf("  --colour-images                  Colourize user images\n\n");
85 
86     printf("  -i, --file-idle-time SECONDS     Time files remain idle (default: 0)\n\n");
87 
88     printf("  --max-files NUMBER      Max number of files or 0 for no limit\n");
89     printf("  --max-file-lag SECONDS  Max time files of a commit can take to appear\n\n");
90 
91     printf("  --log-command VCS       Show the VCS log command (git,svn,hg,bzr,cvs2cl)\n");
92     printf("  --log-format  VCS       Specify the log format (git,svn,hg,bzr,cvs2cl,custom)\n\n");
93 
94     printf("  --load-config CONF_FILE  Load a config file\n");
95     printf("  --save-config CONF_FILE  Save a config file with the current options\n\n");
96 
97     printf("  -o, --output-ppm-stream FILE    Output PPM stream to a file ('-' for STDOUT)\n");
98     printf("  -r, --output-framerate  FPS     Framerate of output (25,30,60)\n\n");
99 
100 if(extended_help) {
101     printf("Extended Options:\n\n");
102 
103     printf("  --window-position XxY    Initial window position\n");
104     printf("  --frameless              Frameless window\n\n");
105 
106     printf("  --output-custom-log FILE  Output a custom format log file ('-' for STDOUT).\n\n");
107 
108     printf("  -b, --background-colour  FFFFFF    Background colour in hex\n");
109     printf("      --background-image   IMAGE     Set a background image\n\n");
110 
111     printf("  --bloom-multiplier       Adjust the amount of bloom (default: 1.0)\n");
112     printf("  --bloom-intensity        Adjust the intensity of the bloom (default: 0.75)\n\n");
113 
114     printf("  --camera-mode MODE       Camera mode (overview,track)\n");
115     printf("  --crop AXIS              Crop view on an axis (vertical,horizontal)\n");
116     printf("  --padding FLOAT          Camera view padding (default: 1.1)\n\n");
117 
118     printf("  --disable-auto-rotate    Disable automatic camera rotation\n\n");
119 
120     printf("  --disable-input          Disable keyboard and mouse input\n\n");
121 
122     printf("  --date-format FORMAT     Specify display date string (strftime format)\n\n");
123 
124     printf("  --font-file FILE         Specify the font\n");
125     printf("  --font-scale SCALE       Scale the size of all fonts\n");
126     printf("  --font-size SIZE         Font size used by date and title\n");
127     printf("  --file-font-size SIZE    Font size for filenames\n");
128     printf("  --dir-font-size SIZE     Font size for directory names\n");
129     printf("  --user-font-size SIZE    Font size for user names\n");
130     printf("  --font-colour FFFFFF     Font colour used by date and title in hex\n\n");
131 
132     printf("  --file-extensions          Show filename extensions only\n");
133     printf("  --file-extension-fallback  Use filename as extension if the extension\n");
134     printf("                             is missing or empty\n\n");
135 
136     printf("  --git-branch             Get the git log of a particular branch\n\n");
137 
138     printf("  --hide DISPLAY_ELEMENT   bloom,date,dirnames,files,filenames,mouse,progress,\n");
139     printf("                           root,tree,users,usernames\n\n");
140 
141     printf("  --logo IMAGE             Logo to display in the foreground\n");
142     printf("  --logo-offset XxY        Offset position of the logo\n\n");
143 
144     printf("  --loop-delay-seconds SECONDS Seconds to delay before looping (default: 3)\n\n");
145 
146     printf("  --title TITLE            Set a title\n\n");
147 
148     printf("  --transparent            Make the background transparent\n\n");
149 
150     printf("  --user-filter REGEX      Ignore usernames matching this regex\n");
151     printf("  --user-show-filter REGEX Show only usernames matching this regex\n\n");
152     printf("  --file-filter REGEX      Ignore file paths matching this regex\n");
153     printf("  --file-show-filter REGEX Show only file paths matching this regex\n\n");
154 
155     printf("  --user-friction SECONDS  Change the rate users slow down (default: 0.67)\n");
156     printf("  --user-scale SCALE       Change scale of users (default: 1.0)\n");
157     printf("  --max-user-speed UNITS   Speed users can travel per second (default: 500)\n\n");
158 
159     printf("  --follow-user USER       Camera will automatically follow this user\n");
160     printf("  --highlight-dirs         Highlight the names of all directories\n");
161     printf("  --highlight-user USER    Highlight the names of a particular user\n");
162     printf("  --highlight-users        Highlight the names of all users\n\n");
163 
164     printf("  --highlight-colour       Font colour for highlighted users in hex.\n");
165     printf("  --selection-colour       Font colour for selected users and files.\n");
166     printf("  --filename-colour        Font colour for filenames.\n");
167     printf("  --dir-colour             Font colour for directories.\n\n");
168 
169     printf("  --dir-name-depth DEPTH    Draw names of directories down to a specific depth.\n");
170     printf("  --dir-name-position FLOAT Position along edge of the directory name\n");
171     printf("                            (between 0.0 and 1.0, default is 0.5).\n\n");
172 
173     printf("  --filename-time SECONDS  Duration to keep filenames on screen (default: 4.0)\n\n");
174 
175     printf("  --caption-file FILE         Caption file\n");
176     printf("  --caption-size SIZE         Caption font size\n");
177     printf("  --caption-colour FFFFFF     Caption colour in hex\n");
178     printf("  --caption-duration SECONDS  Caption duration (default: 10.0)\n");
179     printf("  --caption-offset X          Caption horizontal offset\n\n");
180 
181     printf("  --hash-seed SEED         Change the seed of hash function.\n\n");
182 
183     printf("  --path PATH\n\n");
184 }
185 
186     printf("PATH may be a supported version control directory, a log file, a gource config\n");
187     printf("file, or '-' to read STDIN. If omitted, gource will attempt to generate a log\n");
188     printf("from the current directory.\n\n");
189 
190     if(!extended_help) {
191         printf("To see the full command line options use '-H'\n\n");
192     }
193 
194 #ifdef _WIN32
195     if(!SDLApp::existing_console) {
196         printf("Press Enter\n");
197         getchar();
198     }
199 #endif
200 
201     exit(0);
202 }
203 
GourceSettings()204 GourceSettings::GourceSettings() {
205     repo_count = 0;
206     file_graphic = 0;
207     log_level = LOG_LEVEL_OFF;
208     shutdown = false;
209 
210     setGourceDefaults();
211 
212     default_section_name = "gource";
213 
214     //translate args
215     arg_aliases["p"] = "start-position";
216     arg_aliases["a"] = "auto-skip-seconds";
217     arg_aliases["s"] = "seconds-per-day";
218     arg_aliases["t"] = "stop-at-time";
219     arg_aliases["i"] = "file-idle-time";
220     arg_aliases["e"] = "elasticity";
221     arg_aliases["h"] = "help";
222     arg_aliases["?"] = "help";
223     arg_aliases["H"] = "extended-help";
224     arg_aliases["b"] = "background-colour";
225     arg_aliases["c"] = "time-scale";
226     arg_aliases["background"]          = "background-colour";
227     arg_aliases["disable-bloom"]       = "hide-bloom";
228     arg_aliases["disable-progress"]    = "hide-progress";
229     arg_aliases["highlight-all-users"] = "highlight-users";
230 
231     //command line only options
232     conf_sections["help"]            = "command-line";
233     conf_sections["extended-help"]   = "command-line";
234     conf_sections["log-command"]     = "command-line";
235     conf_sections["git-log-command"] = "command-line";
236     conf_sections["cvs-exp-command"] = "command-line";
237     conf_sections["cvs2cl-command"]  = "command-line";
238     conf_sections["hg-log-command"]  = "command-line";
239     conf_sections["bzr-log-command"] = "command-line";
240     conf_sections["svn-log-command"] = "command-line";
241     conf_sections["load-config"]     = "command-line";
242     conf_sections["save-config"]     = "command-line";
243     conf_sections["output-custom-log"] = "command-line";
244     conf_sections["log-level"]         = "command-line";
245 
246     //boolean args
247     arg_types["help"]                    = "bool";
248     arg_types["extended-help"]           = "bool";
249     arg_types["stop-on-idle"]            = "bool";
250     arg_types["stop-at-end"]             = "bool";
251     arg_types["dont-stop"]               = "bool";
252     arg_types["loop"]                    = "bool";
253     arg_types["realtime"]                = "bool";
254     arg_types["no-time-travel"]          = "bool";
255     arg_types["colour-images"]           = "bool";
256     arg_types["hide-date"]               = "bool";
257     arg_types["hide-files"]              = "bool";
258     arg_types["hide-users"]              = "bool";
259     arg_types["hide-tree"]               = "bool";
260     arg_types["hide-usernames"]          = "bool";
261     arg_types["hide-filenames"]          = "bool";
262     arg_types["hide-dirnames"]           = "bool";
263     arg_types["hide-progress"]           = "bool";
264     arg_types["hide-bloom"]              = "bool";
265     arg_types["hide-mouse"]              = "bool";
266     arg_types["hide-root"]               = "bool";
267     arg_types["highlight-users"]         = "bool";
268     arg_types["highlight-dirs"]          = "bool";
269     arg_types["file-extensions"]         = "bool";
270     arg_types["file-extension-fallback"] = "bool";
271     arg_types["key"]                     = "bool";
272     arg_types["ffp"]                     = "bool";
273 
274     arg_types["disable-auto-rotate"] = "bool";
275     arg_types["disable-auto-skip"]   = "bool";
276     arg_types["disable-input"]       = "bool";
277 
278     arg_types["git-log-command"]= "bool";
279     arg_types["cvs-exp-command"]= "bool";
280     arg_types["cvs2cl-command"] = "bool";
281     arg_types["svn-log-command"]= "bool";
282     arg_types["hg-log-command"] = "bool";
283     arg_types["bzr-log-command"]= "bool";
284 
285     arg_types["bloom-intensity"]   = "float";
286     arg_types["bloom-multiplier"]  = "float";
287     arg_types["elasticity"]        = "float";
288     arg_types["seconds-per-day"]   = "float";
289     arg_types["auto-skip-seconds"] = "float";
290     arg_types["stop-at-time"]      = "float";
291     arg_types["max-user-speed"]    = "float";
292     arg_types["user-friction"]     = "float";
293     arg_types["padding"]           = "float";
294     arg_types["time-scale"]        = "float";
295     arg_types["dir-name-position"] = "float";
296     arg_types["loop-delay-seconds"] = "float";
297 
298     arg_types["max-files"] = "int";
299     arg_types["font-size"] = "int";
300     arg_types["font-scale"] = "float";
301     arg_types["file-font-size"] = "int";
302     arg_types["dir-font-size"] = "int";
303     arg_types["user-font-size"] = "int";
304     arg_types["hash-seed"] = "int";
305 
306     arg_types["user-filter"]      = "multi-value";
307     arg_types["user-show-filter"] = "multi-value";
308     arg_types["file-filter"]      = "multi-value";
309     arg_types["file-show-filter"] = "multi-value";
310     arg_types["follow-user"]      = "multi-value";
311     arg_types["highlight-user"]   = "multi-value";
312 
313     arg_types["log-level"]          = "string";
314     arg_types["background-image"]   = "string";
315     arg_types["logo"]               = "string";
316     arg_types["logo-offset"]        = "string";
317     arg_types["log-command"]        = "string";
318     arg_types["load-config"]        = "string";
319     arg_types["save-config"]        = "string";
320     arg_types["output-custom-log"]  = "string";
321     arg_types["path"]               = "string";
322     arg_types["log-command"]        = "string";
323     arg_types["background-colour"]  = "string";
324     arg_types["file-idle-time"]     = "string";
325     arg_types["user-image-dir"]     = "string";
326     arg_types["default-user-image"] = "string";
327     arg_types["date-format"]        = "string";
328     arg_types["log-format"]         = "string";
329     arg_types["git-branch"]         = "string";
330     arg_types["start-position"]     = "string";
331     arg_types["start-date"]         = "string";
332     arg_types["stop-date"]          = "string";
333     arg_types["stop-position"]      = "string";
334     arg_types["crop"]               = "string";
335     arg_types["hide"]               = "string";
336     arg_types["max-file-lag"]       = "string";
337     arg_types["user-scale"]         = "string";
338     arg_types["camera-mode"]        = "string";
339     arg_types["title"]              = "string";
340     arg_types["font-file"]          = "string";
341     arg_types["font-colour"]        = "string";
342     arg_types["highlight-colour"]   = "string";
343     arg_types["selection-colour"]   = "string";
344     arg_types["dir-colour"]         = "string";
345 
346     arg_types["caption-file"]       = "string";
347     arg_types["caption-size"]       = "int";
348     arg_types["caption-duration"]   = "float";
349     arg_types["caption-colour"]     = "string";
350     arg_types["caption-offset"]     = "int";
351 
352     arg_types["filename-colour"]    = "string";
353     arg_types["filename-time"]      = "float";
354 
355     arg_types["dir-name-depth"]     = "int";
356 }
357 
setGourceDefaults()358 void GourceSettings::setGourceDefaults() {
359 
360     path = ".";
361     default_path = true;
362 
363     ffp = false;
364 
365     hide_date      = false;
366     hide_users     = false;
367     hide_tree      = false;
368     hide_files     = false;
369     hide_usernames = false;
370     hide_filenames = false;
371     hide_dirnames  = false;
372     hide_progress  = false;
373     hide_bloom     = false;
374     hide_mouse     = false;
375     hide_root      = false;
376 
377     start_timestamp = 0;
378     start_date = "";
379 
380     stop_timestamp = 0;
381     stop_date = "";
382 
383     start_position = 0.0f;
384     stop_position  = 0.0f;
385     stop_at_time   = -1.0f;
386     stop_on_idle   = false;
387     stop_at_end    = false;
388     dont_stop      = false;
389     no_time_travel = false;
390 
391     show_key = false;
392 
393     disable_auto_rotate = false;
394 
395     disable_input = false;
396 
397     auto_skip_seconds = 3.0f;
398     days_per_second   = 0.1f; // TODO: check this is right
399     file_idle_time    = 0.0f;
400     time_scale        = 1.0f;
401 
402     loop = false;
403     loop_delay_seconds = 3.0f;
404 
405     logo = "";
406     logo_offset = vec2(20.0f,20.0f);
407 
408     colour_user_images = false;
409     default_user_image = "";
410     user_image_dir     = "";
411     user_image_map.clear();
412 
413     camera_zoom_min     = 50.0f;
414     camera_zoom_default = 100.0f;
415     camera_zoom_max     = 10000.0f;
416 
417     camera_mode     = "overview";
418     padding         = 1.1f;
419 
420     crop_vertical   = false;
421     crop_horizontal = false;
422 
423     bloom_multiplier = 1.0f;
424     bloom_intensity  = 0.75f;
425 
426     background_colour = vec3(0.1f, 0.1f, 0.1f);
427     background_image  = "";
428 
429     title             = "";
430 
431     font_file = GOURCE_FONT_FILE;
432     font_size = 16;
433     filename_font_size = 14;
434     dirname_font_size = 14;
435     user_font_size = 14;
436     dir_colour       = vec3(1.0f);
437     font_colour      = vec3(1.0f);
438     highlight_colour = vec3(1.0f);
439     selection_colour = vec3(1.0, 1.0, 0.3f);
440 
441     dir_name_depth = 0;
442     dir_name_position = 0.5f;
443 
444     elasticity = 0.0f;
445 
446     git_branch = "";
447 
448     log_format  = "";
449     date_format = "%A, %d %B, %Y %X";
450 
451     max_files      = 0;
452     max_user_speed = 500.0f;
453     max_file_lag   = 5.0f;
454 
455     user_idle_time = 3.0f;
456     user_friction  = 1.0f;
457     user_scale     = 1.0f;
458 
459     follow_users.clear();
460     highlight_users.clear();
461     highlight_all_users = false;
462     highlight_dirs = false;
463 
464     caption_file     = "";
465     caption_duration = 10.0f;
466     caption_size     = 16;
467     caption_offset   = 0;
468     caption_colour   = vec3(1.0f, 1.0f, 1.0f);
469 
470     filename_colour  = vec3(1.0f, 1.0f, 1.0f);
471     filename_time = 4.0f;
472 
473     gStringHashSeed = 31;
474 
475     //delete file filters
476     for(std::vector<Regex*>::iterator it = file_filters.begin(); it != file_filters.end(); it++) {
477         delete (*it);
478     }
479     file_filters.clear();
480 
481     //delete file whitelists
482     for(std::vector<Regex*>::iterator it = file_show_filters.begin(); it != file_show_filters.end(); it++) {
483         delete (*it);
484     }
485     file_show_filters.clear();
486 
487     file_extensions = false;
488     file_extension_fallback = false;
489 
490     //delete user filters
491     for(std::vector<Regex*>::iterator it = user_filters.begin(); it != user_filters.end(); it++) {
492         delete (*it);
493     }
494     user_filters.clear();
495 
496     //delete user whitelist
497     for(std::vector<Regex*>::iterator it = user_show_filters.begin(); it != user_show_filters.end(); it++) {
498         delete (*it);
499     }
500     user_show_filters.clear();
501 }
502 
commandLineOption(const std::string & name,const std::string & value)503 void GourceSettings::commandLineOption(const std::string& name, const std::string& value) {
504 
505     if(name == "help") {
506         help();
507     }
508 
509     if(name == "extended-help") {
510         help(true);
511     }
512 
513     if(name == "load-config" && value.size() > 0) {
514         load_config = value;
515         return;
516     }
517 
518     if(name == "save-config" && value.size() > 0) {
519         save_config = value;
520         return;
521     }
522 
523     std::string log_command;
524 
525     if(name == "log-command") {
526         log_command = value;
527     }
528 
529     if(name == "git-log-command" || log_command == "git") {
530         SDLAppInfo(GitCommitLog::logCommand());
531     }
532 
533     if(name == "cvs-exp-command" || log_command == "cvs-exp") {
534         SDLAppInfo(CVSEXPCommitLog::logCommand());
535     }
536 
537     if(log_command == "cvs") {
538         throw ConfFileException("please use either 'cvs2cl' or 'cvs-exp'", "", 0);
539     }
540 
541     if(name == "cvs2cl-command" || log_command == "cvs2cl") {
542         SDLAppInfo(CVS2CLCommitLog::logCommand());
543     }
544 
545     if(name == "svn-log-command" || log_command == "svn") {
546         SDLAppInfo(SVNCommitLog::logCommand());
547     }
548 
549     if(name == "hg-log-command" || log_command == "hg") {
550         SDLAppInfo(MercurialLog::logCommand());
551     }
552 
553     if(name == "bzr-log-command" || log_command == "bzr") {
554         SDLAppInfo(BazaarLog::logCommand());
555     }
556 
557     if(name == "output-custom-log" && value.size() > 0) {
558         output_custom_filename = value;
559         return;
560     }
561 
562     if(name == "log-level") {
563         if(value == "warn") {
564             log_level = LOG_LEVEL_WARN;
565         } else if(value == "debug") {
566             log_level = LOG_LEVEL_DEBUG;
567         } else if(value == "info") {
568             log_level = LOG_LEVEL_INFO;
569         } else if(value == "error") {
570             log_level = LOG_LEVEL_ERROR;
571         } else if(value == "pedantic") {
572             log_level = LOG_LEVEL_PEDANTIC;
573         }
574         return;
575     }
576 
577     std::string invalid_error = std::string("invalid ") + name + std::string(" value");
578     throw ConfFileException(invalid_error, "", 0);
579 }
580 
581 
582 #ifdef __APPLE__
583 #include <CoreFoundation/CoreFoundation.h>
584 #endif
importGourceSettings(ConfFile & conffile,ConfSection * gource_settings)585 void GourceSettings::importGourceSettings(ConfFile& conffile, ConfSection* gource_settings) {
586 
587     setGourceDefaults();
588 
589     if(gource_settings == 0) gource_settings = conffile.getSection(default_section_name);
590 
591     if(gource_settings == 0) {
592         gource_settings = conffile.addSection("gource");
593     }
594 
595     ConfEntry* entry = 0;
596 
597     //hide flags
598 
599     std::vector<std::string> hide_fields;
600 
601     if((entry = gource_settings->getEntry("hide")) != 0) {
602 
603         if(!entry->hasValue()) conffile.missingValueException(entry);
604 
605         std::string hide_string = entry->getString();
606 
607         size_t sep;
608         while((sep = hide_string.find(",")) != std::string::npos) {
609 
610             if(sep == 0 && hide_string.size()==1) break;
611 
612             if(sep == 0) {
613                 hide_string = hide_string.substr(sep+1, hide_string.size()-1);
614                 continue;
615             }
616 
617             std::string hide_field  = hide_string.substr(0, sep);
618             hide_fields.push_back(hide_field);
619             hide_string = hide_string.substr(sep+1, hide_string.size()-1);
620         }
621 
622         if(hide_string.size() > 0 && hide_string != ",") hide_fields.push_back(hide_string);
623 
624         //validate field list
625 
626         for(std::vector<std::string>::iterator it = hide_fields.begin(); it != hide_fields.end(); it++) {
627             std::string hide_field = (*it);
628 
629             if(   hide_field != "date"
630                && hide_field != "users"
631                && hide_field != "tree"
632                && hide_field != "files"
633                && hide_field != "usernames"
634                && hide_field != "filenames"
635                && hide_field != "dirnames"
636                && hide_field != "bloom"
637                && hide_field != "progress"
638                && hide_field != "mouse"
639                && hide_field != "root") {
640                 std::string unknown_hide_option = std::string("unknown option hide ") + hide_field;
641                 conffile.entryException(entry, unknown_hide_option);
642             }
643         }
644     }
645 
646     //check hide booleans
647     for(std::map<std::string,std::string>::iterator it = arg_types.begin(); it != arg_types.end(); it++) {
648         if(it->first.find("hide-") == 0 && it->second == "bool") {
649 
650             if(gource_settings->getBool(it->first)) {
651                 std::string hide_field = it->first.substr(5, it->first.size()-5);
652                 hide_fields.push_back(hide_field);
653             }
654         }
655     }
656 
657     if(hide_fields.size()>0) {
658 
659         for(std::vector<std::string>::iterator it = hide_fields.begin(); it != hide_fields.end(); it++) {
660             std::string hidestr = (*it);
661 
662                 if(hidestr == "date")       hide_date      = true;
663             else if(hidestr == "users")     hide_users     = true;
664             else if(hidestr == "tree")      hide_tree      = true;
665             else if(hidestr == "files")     hide_files     = true;
666             else if(hidestr == "usernames") hide_usernames = true;
667             else if(hidestr == "filenames") hide_filenames = true;
668             else if(hidestr == "dirnames")  hide_dirnames  = true;
669             else if(hidestr == "bloom")     hide_bloom     = true;
670             else if(hidestr == "progress")  hide_progress  = true;
671             else if(hidestr == "root")      hide_root      = true;
672             else if(hidestr == "mouse")     {
673                 hide_mouse     = true;
674                 hide_progress  = true;
675             }
676         }
677     }
678 
679     if((entry = gource_settings->getEntry("date-format")) != 0) {
680 
681         if(!entry->hasValue()) conffile.missingValueException(entry);
682 
683         date_format = entry->getString();
684     }
685 
686     if(gource_settings->getBool("disable-auto-rotate")) {
687         disable_auto_rotate=true;
688     }
689 
690     if(gource_settings->getBool("disable-auto-skip")) {
691         auto_skip_seconds = -1.0;
692     }
693 
694     if(gource_settings->getBool("disable-input")) {
695         disable_input=true;
696     }
697 
698     if(gource_settings->getBool("loop")) {
699         loop = true;
700     }
701 
702     if((entry = gource_settings->getEntry("loop-delay-seconds")) != 0) {
703 
704         if(!entry->hasValue()) conffile.entryException(entry, "specify loop-delay-seconds (float)");
705 
706         loop_delay_seconds = entry->getFloat();
707 
708         if(loop_delay_seconds<=0.0f) {
709             conffile.invalidValueException(entry);
710         }
711     }
712 
713     if((entry = gource_settings->getEntry("git-branch")) != 0) {
714 
715         if(!entry->hasValue()) conffile.missingValueException(entry);
716 
717         Regex branch_regex("^(?!-)[/\\w.,;_=+{}\\[\\]-]+$");
718 
719         std::string branch = entry->getString();
720 
721         if(branch_regex.match(branch)) {
722             git_branch = branch;
723         } else {
724             conffile.invalidValueException(entry);
725         }
726     }
727 
728     if(gource_settings->getBool("colour-images")) {
729         colour_user_images = true;
730     }
731 
732     if((entry = gource_settings->getEntry("crop")) != 0) {
733 
734         if(!entry->hasValue()) conffile.entryException(entry, "specify crop (vertical,horizontal)");
735 
736         std::string crop = entry->getString();
737 
738         if(crop == "vertical") {
739             crop_vertical = true;
740         } else if (crop == "horizontal") {
741             crop_horizontal = true;
742         } else {
743             conffile.invalidValueException(entry);
744         }
745     }
746 
747     if((entry = gource_settings->getEntry("log-format")) != 0) {
748 
749         if(!entry->hasValue()) conffile.entryException(entry, "specify log-format (format)");
750 
751         log_format = entry->getString();
752 
753         if(log_format == "cvs") {
754             conffile.entryException(entry, "please use either 'cvs2cl' or 'cvs-exp'");
755         }
756 
757         if(   log_format != "git"
758            && log_format != "cvs-exp"
759            && log_format != "cvs2cl"
760            && log_format != "svn"
761            && log_format != "custom"
762            && log_format != "hg"
763            && log_format != "bzr"
764            && log_format != "apache") {
765 
766             conffile.invalidValueException(entry);
767         }
768     }
769 
770     if((entry = gource_settings->getEntry("default-user-image")) != 0) {
771 
772         if(!entry->hasValue()) conffile.entryException(entry, "specify default-user-image (image path)");
773 
774         default_user_image = entry->getString();
775     }
776 
777     if((entry = gource_settings->getEntry("user-image-dir")) != 0) {
778 
779         if(!entry->hasValue()) conffile.entryException(entry, "specify user-image-dir (directory)");
780 
781         user_image_dir = entry->getString();
782 
783         //append slash
784         if(user_image_dir[user_image_dir.size()-1] != '/') {
785             user_image_dir += std::string("/");
786         }
787 
788         user_image_map.clear();
789 
790         boost::filesystem::path image_dir_path(user_image_dir);
791 
792         if(!is_directory(image_dir_path)) {
793              conffile.entryException(entry, "specified user-image-dir is not a directory");
794         }
795 
796         std::vector<boost::filesystem::path> image_dir_files;
797 
798         try {
799             copy(boost::filesystem::directory_iterator(image_dir_path), boost::filesystem::directory_iterator(), back_inserter(image_dir_files));
800         } catch(const boost::filesystem::filesystem_error& exception) {
801              conffile.entryException(entry, "error reading specified user-image-dir");
802         }
803 
804         for(boost::filesystem::path& p : image_dir_files) {
805 
806             std::string dirfile;
807 
808 #ifdef _WIN32
809             std::wstring dirfile_16 = p.filename().wstring();
810             utf8::utf16to8(dirfile_16.begin(), dirfile_16.end(), back_inserter(dirfile));
811 #else
812             dirfile = p.filename().string();
813 #endif
814             std::string file_ext = extension(p);
815             boost::algorithm::to_lower(file_ext);
816 
817             if(file_ext != ".jpg" && file_ext != ".jpeg" && file_ext != ".png") continue;
818 
819             std::string image_path = gGourceSettings.user_image_dir + dirfile;
820             std::string name       = dirfile.substr(0,dirfile.size() - file_ext.size());
821 
822 #ifdef __APPLE__
823                 CFMutableStringRef help = CFStringCreateMutable(kCFAllocatorDefault, 0);
824                 CFStringAppendCString(help, name.c_str(), kCFStringEncodingUTF8);
825                 CFStringNormalize(help, kCFStringNormalizationFormC);
826                 char data[4096];
827                 CFStringGetCString(help,
828                                    data,
829                                    sizeof(data),
830                                    kCFStringEncodingUTF8);
831                 name = data;
832 #endif
833 
834             debugLog("%s => %s", name.c_str(), image_path.c_str());
835 
836             user_image_map[name] = image_path;
837         }
838     }
839 
840     if((entry = gource_settings->getEntry("caption-file")) != 0) {
841 
842         if(!entry->hasValue()) conffile.entryException(entry, "specify caption file (filename)");
843 
844         caption_file = entry->getString();
845 
846         if(!boost::filesystem::exists(caption_file)) {
847             conffile.entryException(entry, "caption file not found");
848         }
849     }
850 
851     if((entry = gource_settings->getEntry("caption-duration")) != 0) {
852 
853         if(!entry->hasValue()) conffile.entryException(entry, "specify caption duration (seconds)");
854 
855         caption_duration = entry->getFloat();
856 
857         if(caption_duration <= 0.0f) {
858             conffile.invalidValueException(entry);
859         }
860     }
861 
862     if((entry = gource_settings->getEntry("caption-size")) != 0) {
863 
864         if(!entry->hasValue()) conffile.entryException(entry, "specify caption size");
865 
866         caption_size = entry->getInt();
867 
868         if(caption_size<1 || caption_size>100) {
869             conffile.invalidValueException(entry);
870         }
871     }
872 
873     if((entry = gource_settings->getEntry("caption-offset")) != 0) {
874 
875         if(!entry->hasValue()) conffile.entryException(entry, "specify caption offset");
876 
877         caption_offset = entry->getInt();
878     }
879 
880     if((entry = gource_settings->getEntry("caption-colour")) != 0) {
881 
882         if(!entry->hasValue()) conffile.entryException(entry, "specify caption colour (FFFFFF)");
883 
884         int r,g,b;
885 
886         std::string colstring = entry->getString();
887 
888         if(entry->isVec3()) {
889             caption_colour = entry->getVec3();
890         } else if(colstring.size()==6 && sscanf(colstring.c_str(), "%02x%02x%02x", &r, &g, &b) == 3) {
891             caption_colour = vec3(r,g,b);
892             caption_colour /= 255.0f;
893         } else {
894             conffile.invalidValueException(entry);
895         }
896     }
897 
898     if((entry = gource_settings->getEntry("filename-colour")) != 0) {
899         if(!entry->hasValue()) conffile.entryException(entry, "specify filename colour (FFFFFF)");
900 
901 	int r,g,b;
902 
903 	std::string colstring = entry->getString();
904 
905 	if(entry->isVec3()) {
906 	    filename_colour = entry->getVec3();
907 	} else if(colstring.size()==6 && sscanf(colstring.c_str(), "%02x%02x%02x", &r, &g, &b) == 3) {
908             filename_colour = vec3(r,g,b);
909             filename_colour /= 255.0f;
910         } else {
911             conffile.invalidValueException(entry);
912         }
913     }
914 
915     if((entry = gource_settings->getEntry("filename-time")) != 0) {
916 
917         if(!entry->hasValue()) conffile.entryException(entry, "specify duration to keep files on screen (float)");
918 
919         filename_time = entry->getFloat();
920 
921         if(filename_time<2.0f) {
922             conffile.entryException(entry, "filename-time must be >= 2.0");
923         }
924     }
925 
926     if((entry = gource_settings->getEntry("bloom-intensity")) != 0) {
927 
928         if(!entry->hasValue()) conffile.entryException(entry, "specify bloom-intensity (float)");
929 
930         bloom_intensity = entry->getFloat();
931 
932         if(bloom_intensity<=0.0f) {
933             conffile.invalidValueException(entry);
934         }
935     }
936 
937     if((entry = gource_settings->getEntry("bloom-multiplier")) != 0) {
938 
939         if(!entry->hasValue()) conffile.entryException(entry, "specify bloom-multiplier (float)");
940 
941         bloom_multiplier = entry->getFloat();
942 
943         if(bloom_multiplier<=0.0f) {
944             conffile.invalidValueException(entry);
945         }
946     }
947 
948     if((entry = gource_settings->getEntry("elasticity")) != 0) {
949 
950         if(!entry->hasValue()) conffile.entryException(entry, "specify elasticity (float)");
951 
952         elasticity = entry->getFloat();
953 
954         if(elasticity<=0.0f) {
955             conffile.invalidValueException(entry);
956         }
957     }
958 
959     if((entry = gource_settings->getEntry("font-file")) != 0) {
960 
961         if(!entry->hasValue()) conffile.entryException(entry, "specify font file");
962 
963         font_file = entry->getString();
964 
965         boost::filesystem::path font_file_path(font_file);
966 
967         if(!boost::filesystem::exists(font_file_path)) {
968             conffile.invalidValueException(entry);
969         }
970 
971         font_file = boost::filesystem::canonical(font_file_path).string();
972 
973         if(font_file.empty()) {
974            conffile.invalidValueException(entry);
975         }
976     }
977 
978     if((entry = gource_settings->getEntry("font-size")) != 0) {
979 
980         if(!entry->hasValue()) conffile.entryException(entry, "specify font size");
981 
982         font_size = entry->getInt();
983 
984         if(font_size<1 || font_size>100) {
985             conffile.invalidValueException(entry);
986         }
987     }
988 
989     if((entry = gource_settings->getEntry("file-font-size")) != 0) {
990 
991         if(!entry->hasValue()) conffile.entryException(entry, "specify font size");
992 
993         filename_font_size = entry->getInt();
994 
995         if(filename_font_size<1 || filename_font_size>100) {
996             conffile.invalidValueException(entry);
997         }
998     }
999 
1000     if((entry = gource_settings->getEntry("dir-font-size")) != 0) {
1001 
1002         if(!entry->hasValue()) conffile.entryException(entry, "specify font size");
1003 
1004         dirname_font_size = entry->getInt();
1005 
1006         if(dirname_font_size<1 || dirname_font_size>100) {
1007             conffile.invalidValueException(entry);
1008         }
1009     }
1010 
1011     if((entry = gource_settings->getEntry("user-font-size")) != 0) {
1012 
1013         if(!entry->hasValue()) conffile.entryException(entry, "specify font size");
1014 
1015         user_font_size = entry->getInt();
1016 
1017         if(user_font_size<1 || user_font_size>100) {
1018             conffile.invalidValueException(entry);
1019         }
1020     }
1021 
1022     if((entry = gource_settings->getEntry("font-scale")) != 0) {
1023 
1024         if(!entry->hasValue()) conffile.entryException(entry, "specify font scale");
1025 
1026         float font_scale = entry->getFloat();
1027 
1028         if(font_scale<0.0f || font_scale>10.0f) {
1029             conffile.invalidValueException(entry);
1030         }
1031 
1032         font_size         = glm::clamp((int)(font_size * font_scale), 1, 100);
1033         user_font_size    = glm::clamp((int)(user_font_size * font_scale), 1, 100);
1034         dirname_font_size = glm::clamp((int)(dirname_font_size * font_scale), 1, 100);
1035     }
1036 
1037     if((entry = gource_settings->getEntry("hash-seed")) != 0) {
1038 
1039         if(!entry->hasValue()) conffile.entryException(entry, "specify hash seed (integer)");
1040 
1041         gStringHashSeed = entry->getInt();
1042     }
1043 
1044     if((entry = gource_settings->getEntry("font-colour")) != 0) {
1045 
1046         if(!entry->hasValue()) conffile.entryException(entry, "specify font colour (FFFFFF)");
1047 
1048         int r,g,b;
1049 
1050         std::string colstring = entry->getString();
1051 
1052         if(entry->isVec3()) {
1053             font_colour = entry->getVec3();
1054         } else if(colstring.size()==6 && sscanf(colstring.c_str(), "%02x%02x%02x", &r, &g, &b) == 3) {
1055             font_colour = vec3(r,g,b);
1056             font_colour /= 255.0f;
1057         } else {
1058             conffile.invalidValueException(entry);
1059         }
1060     }
1061 
1062     if((entry = gource_settings->getEntry("background-colour")) != 0) {
1063 
1064         if(!entry->hasValue()) conffile.entryException(entry, "specify background colour (FFFFFF)");
1065 
1066         int r,g,b;
1067 
1068         std::string colstring = entry->getString();
1069 
1070         if(entry->isVec3()) {
1071             background_colour = entry->getVec3();
1072         } else if(colstring.size()==6 && sscanf(colstring.c_str(), "%02x%02x%02x", &r, &g, &b) == 3) {
1073             background_colour = vec3(r,g,b);
1074             background_colour /= 255.0f;
1075         } else {
1076             conffile.invalidValueException(entry);
1077         }
1078     }
1079 
1080     if((entry = gource_settings->getEntry("highlight-colour")) != 0) {
1081 
1082         if(!entry->hasValue()) conffile.entryException(entry, "specify highlight colour (FFFFFF)");
1083 
1084         int r,g,b;
1085 
1086         std::string colstring = entry->getString();
1087 
1088         if(entry->isVec3()) {
1089             highlight_colour = entry->getVec3();
1090         } else if(colstring.size()==6 && sscanf(colstring.c_str(), "%02x%02x%02x", &r, &g, &b) == 3) {
1091             highlight_colour = vec3(r,g,b);
1092             highlight_colour /= 255.0f;
1093         } else {
1094             conffile.invalidValueException(entry);
1095         }
1096     }
1097 
1098     if((entry = gource_settings->getEntry("selection-colour")) != 0) {
1099 
1100         if(!entry->hasValue()) conffile.entryException(entry, "specify selection colour (FFFFFF)");
1101 
1102         int r,g,b;
1103 
1104         std::string colstring = entry->getString();
1105 
1106         if(entry->isVec3()) {
1107             selection_colour = entry->getVec3();
1108         } else if(colstring.size()==6 && sscanf(colstring.c_str(), "%02x%02x%02x", &r, &g, &b) == 3) {
1109             selection_colour = vec3(r,g,b);
1110             selection_colour /= 255.0f;
1111         } else {
1112             conffile.invalidValueException(entry);
1113         }
1114     }
1115 
1116     if((entry = gource_settings->getEntry("dir-colour")) != 0) {
1117 
1118         if(!entry->hasValue()) conffile.entryException(entry, "specify dir colour (FFFFFF)");
1119 
1120         int r,g,b;
1121 
1122         std::string colstring = entry->getString();
1123 
1124         if(entry->isVec3()) {
1125             dir_colour = entry->getVec3();
1126         } else if(colstring.size()==6 && sscanf(colstring.c_str(), "%02x%02x%02x", &r, &g, &b) == 3) {
1127             dir_colour = vec3(r,g,b);
1128             dir_colour /= 255.0f;
1129         } else {
1130             conffile.invalidValueException(entry);
1131         }
1132     }
1133 
1134     if((entry = gource_settings->getEntry("background-image")) != 0) {
1135 
1136         if(!entry->hasValue()) conffile.entryException(entry, "specify background image (image path)");
1137 
1138         background_image = entry->getString();
1139     }
1140 
1141     if((entry = gource_settings->getEntry("title")) != 0) {
1142 
1143         if(!entry->hasValue()) conffile.entryException(entry, "specify title");
1144 
1145         title = entry->getString();
1146     }
1147 
1148     if((entry = gource_settings->getEntry("logo")) != 0) {
1149 
1150         if(!entry->hasValue()) conffile.entryException(entry, "specify logo (image path)");
1151 
1152         logo = entry->getString();
1153     }
1154 
1155     if((entry = gource_settings->getEntry("logo-offset")) != 0) {
1156 
1157         if(!entry->hasValue()) conffile.entryException(entry, "specify logo-offset (XxY)");
1158 
1159         std::string logo_offset_str = entry->getString();
1160 
1161         int posx = 0;
1162         int posy = 0;
1163 
1164         if(parseRectangle(logo_offset_str, posx, posy)) {
1165             logo_offset = vec2(posx, posy);
1166         } else {
1167             conffile.invalidValueException(entry);
1168         }
1169 
1170     }
1171 
1172     if((entry = gource_settings->getEntry("seconds-per-day")) != 0) {
1173 
1174         if(!entry->hasValue()) conffile.entryException(entry, "specify seconds-per-day (seconds)");
1175 
1176         float seconds_per_day = entry->getFloat();
1177 
1178         if(seconds_per_day<=0.0f) {
1179             conffile.invalidValueException(entry);
1180         }
1181 
1182         // convert seconds-per-day to days-per-second
1183         days_per_second = 1.0 / seconds_per_day;
1184     }
1185 
1186     if((entry = gource_settings->getEntry("auto-skip-seconds")) != 0) {
1187 
1188         if(!entry->hasValue()) conffile.entryException(entry, "specify auto-skip-seconds (seconds)");
1189 
1190         auto_skip_seconds = entry->getFloat();
1191 
1192         if(auto_skip_seconds <= 0.0) {
1193             conffile.invalidValueException(entry);
1194         }
1195     }
1196 
1197     if((entry = gource_settings->getEntry("file-idle-time")) != 0) {
1198 
1199         if(!entry->hasValue()) conffile.entryException(entry, "specify file-idle-time (seconds)");
1200 
1201         std::string file_idle_str = entry->getString();
1202 
1203         file_idle_time = (float) atoi(file_idle_str.c_str());
1204 
1205         if(file_idle_time<0.0f || (file_idle_time == 0.0f && file_idle_str[0] != '0') ) {
1206             conffile.invalidValueException(entry);
1207         }
1208     }
1209 
1210     if((entry = gource_settings->getEntry("user-idle-time")) != 0) {
1211 
1212         if(!entry->hasValue()) conffile.entryException(entry, "specify user-idle-time (seconds)");
1213 
1214         user_idle_time = entry->getFloat();
1215 
1216         if(user_idle_time < 0.0f) {
1217             conffile.invalidValueException(entry);
1218         }
1219     }
1220 
1221     if((entry = gource_settings->getEntry("time-scale")) != 0) {
1222 
1223         if(!entry->hasValue())
1224             conffile.entryException(entry, "specify time-scale (scale)");
1225 
1226         time_scale = entry->getFloat();
1227 
1228         if(time_scale <= 0.0f || time_scale > 4.0f) {
1229             conffile.entryException(entry, "time-scale outside of range 0.0 - 4.0");
1230         }
1231     }
1232 
1233     if((entry = gource_settings->getEntry("start-date")) != 0) {
1234 
1235         if(!entry->hasValue()) conffile.entryException(entry, "specify start-date (YYYY-MM-DD hh:mm:ss)");
1236 
1237         std::string start_date_string = entry->getString();
1238 
1239         if(parseDateTime(start_date_string, start_timestamp)) {
1240 
1241             char datestr[256];
1242             strftime(datestr, 256, "%Y-%m-%d", localtime ( &start_timestamp ));
1243             start_date = datestr;
1244 
1245         } else {
1246             conffile.invalidValueException(entry);
1247         }
1248     }
1249 
1250     if((entry = gource_settings->getEntry("stop-date")) != 0) {
1251 
1252         if(!entry->hasValue()) conffile.entryException(entry, "specify stop-date (YYYY-MM-DD hh:mm:ss)");
1253 
1254         std::string end_date_string = entry->getString();
1255 
1256         if(parseDateTime(end_date_string, stop_timestamp)) {
1257 
1258             struct tm * timeinfo;
1259             timeinfo = localtime ( &stop_timestamp );
1260 
1261             time_t stop_timestamp_rounded = stop_timestamp;
1262 
1263             if(timeinfo->tm_hour > 0 || timeinfo->tm_min > 0 || timeinfo->tm_sec > 0) {
1264                 stop_timestamp_rounded += 60*60*24;
1265             }
1266 
1267             char datestr[256];
1268             strftime(datestr, 256, "%Y-%m-%d", localtime ( &stop_timestamp_rounded ));
1269             stop_date = datestr;
1270 
1271         } else {
1272             conffile.invalidValueException(entry);
1273         }
1274     }
1275 
1276     if((entry = gource_settings->getEntry("start-position")) != 0) {
1277 
1278         if(!entry->hasValue()) conffile.entryException(entry, "specify start-position (float,random)");
1279 
1280         if(entry->getString() == "random") {
1281             srand(time(0));
1282             start_position = (rand() % 1000) / 1000.0f;
1283         } else {
1284             start_position = entry->getFloat();
1285 
1286             if(start_position<=0.0 || start_position>=1.0) {
1287                 conffile.entryException(entry, "start-position outside of range 0.0 - 1.0 (non-inclusive)");
1288             }
1289         }
1290     }
1291 
1292     if((entry = gource_settings->getEntry("stop-position")) != 0) {
1293 
1294         if(!entry->hasValue()) conffile.entryException(entry, "specify stop-position (float)");
1295 
1296         stop_position = entry->getFloat();
1297 
1298         if(stop_position<=0.0 || stop_position>1.0) {
1299             conffile.entryException(entry, "stop-position outside of range 0.0 - 1.0 (inclusive)");
1300         }
1301     }
1302 
1303     if((entry = gource_settings->getEntry("stop-at-time")) != 0) {
1304 
1305         if(!entry->hasValue()) conffile.entryException(entry, "specify stop-at-time (seconds)");
1306 
1307         stop_at_time = entry->getFloat();
1308 
1309         if(stop_at_time <= 0.0) {
1310             conffile.invalidValueException(entry);
1311         }
1312     }
1313 
1314     if(gource_settings->getBool("key")) {
1315         show_key = true;
1316     }
1317 
1318     if(gource_settings->getBool("ffp")) {
1319         ffp = true;
1320     }
1321 
1322     if(gource_settings->getBool("realtime")) {
1323         days_per_second = 1.0 / 86400.0;
1324     }
1325 
1326     if(gource_settings->getBool("no-time-travel")) {
1327         no_time_travel = true;
1328     }
1329 
1330     if(gource_settings->getBool("dont-stop")) {
1331         dont_stop = true;
1332     }
1333 
1334     if(gource_settings->getBool("stop-at-end")) {
1335         stop_at_end = true;
1336     }
1337 
1338     //NOTE: this no longer does anything
1339     if(gource_settings->getBool("stop-on-idle")) {
1340         stop_on_idle = true;
1341     }
1342 
1343     if((entry = gource_settings->getEntry("max-files")) != 0) {
1344 
1345         if(!entry->hasValue()) conffile.entryException(entry, "specify max-files (number)");
1346 
1347         max_files = entry->getInt();
1348 
1349         if( max_files<0 || (max_files == 0 && entry->getString() != "0") ) {
1350             conffile.invalidValueException(entry);
1351         }
1352     }
1353 
1354     if((entry = gource_settings->getEntry("max-file-lag")) != 0) {
1355 
1356         if(!entry->hasValue()) conffile.entryException(entry, "specify max-file-lag (seconds)");
1357 
1358         max_file_lag = entry->getFloat();
1359 
1360         if(max_file_lag==0.0) {
1361             conffile.invalidValueException(entry);
1362         }
1363     }
1364 
1365     if((entry = gource_settings->getEntry("user-friction")) != 0) {
1366 
1367         if(!entry->hasValue()) conffile.entryException(entry, "specify user-friction (seconds)");
1368 
1369         user_friction = entry->getFloat();
1370 
1371         if(user_friction<=0.0) {
1372             conffile.invalidValueException(entry);
1373         }
1374 
1375         user_friction = 1.0 / user_friction;
1376     }
1377 
1378     if((entry = gource_settings->getEntry("user-scale")) != 0) {
1379 
1380         if(!entry->hasValue()) conffile.entryException(entry, "specify user-scale (scale)");
1381 
1382         user_scale = entry->getFloat();
1383 
1384         if(user_scale<=0.0 || user_scale>100.0) {
1385             conffile.invalidValueException(entry);
1386         }
1387     }
1388 
1389     if((entry = gource_settings->getEntry("max-user-speed")) != 0) {
1390 
1391         if(!entry->hasValue()) conffile.entryException(entry, "specify max-user-speed (units)");
1392 
1393         max_user_speed = entry->getFloat();
1394 
1395         if(max_user_speed<=0) {
1396             conffile.invalidValueException(entry);
1397         }
1398     }
1399 
1400     if(   gource_settings->getBool("highlight-users")
1401        || gource_settings->getBool("highlight-all-users")) {
1402         highlight_all_users = true;
1403     }
1404 
1405     if(gource_settings->getBool("highlight-dirs")) {
1406         highlight_dirs = true;
1407     }
1408 
1409     if((entry = gource_settings->getEntry("camera-mode")) != 0) {
1410 
1411         if(!entry->hasValue()) conffile.entryException(entry, "specify camera-mode (overview,track)");
1412 
1413         camera_mode = entry->getString();
1414 
1415         if(camera_mode != "overview" && camera_mode != "track") {
1416             conffile.invalidValueException(entry);
1417         }
1418     }
1419 
1420     if((entry = gource_settings->getEntry("padding")) != 0) {
1421 
1422         if(!entry->hasValue()) conffile.entryException(entry, "specify padding (float)");
1423 
1424         padding = entry->getFloat();
1425 
1426         if(padding <= 0.0f || padding >= 2.0f) {
1427             conffile.invalidValueException(entry);
1428         }
1429     }
1430 
1431     // multi-value entries
1432 
1433     if((entry = gource_settings->getEntry("highlight-user")) != 0) {
1434 
1435         ConfEntryList* highlight_user_entries = gource_settings->getEntries("highlight-user");
1436 
1437         for(ConfEntryList::iterator it = highlight_user_entries->begin(); it != highlight_user_entries->end(); it++) {
1438 
1439             entry = *it;
1440 
1441             if(!entry->hasValue()) conffile.entryException(entry, "specify highlight-user (user)");
1442 
1443             highlight_users.push_back(entry->getString());
1444         }
1445     }
1446 
1447     if((entry = gource_settings->getEntry("follow-user")) != 0) {
1448 
1449         ConfEntryList* follow_user_entries = gource_settings->getEntries("follow-user");
1450 
1451         for(ConfEntryList::iterator it = follow_user_entries->begin(); it != follow_user_entries->end(); it++) {
1452 
1453             entry = *it;
1454 
1455             if(!entry->hasValue()) conffile.entryException(entry, "specify follow-user (user)");
1456 
1457             follow_users.push_back(entry->getString());
1458         }
1459     }
1460 
1461     if(gource_settings->getBool("file-extensions")) {
1462         file_extensions=true;
1463     }
1464 
1465     if(gource_settings->getBool("file-extension-fallback")) {
1466         file_extension_fallback=true;
1467     }
1468 
1469     if((entry = gource_settings->getEntry("file-filter")) != 0) {
1470 
1471         ConfEntryList* filters = gource_settings->getEntries("file-filter");
1472 
1473         for(ConfEntryList::iterator it = filters->begin(); it != filters->end(); it++) {
1474 
1475             entry = *it;
1476 
1477             if(!entry->hasValue()) conffile.entryException(entry, "specify file-filter (regex)");
1478 
1479             std::string filter_string = entry->getString();
1480 
1481             Regex* r = new Regex(filter_string, 1);
1482 
1483             if(!r->isValid()) {
1484                 delete r;
1485                 conffile.entryException(entry, "invalid file-filter regular expression");
1486             }
1487 
1488             file_filters.push_back(r);
1489         }
1490     }
1491 
1492     if((entry = gource_settings->getEntry("file-show-filter")) != 0) {
1493 
1494         ConfEntryList* filters = gource_settings->getEntries("file-show-filter");
1495 
1496         for(ConfEntryList::iterator it = filters->begin(); it != filters->end(); it++) {
1497 
1498             entry = *it;
1499 
1500             if(!entry->hasValue()) conffile.entryException(entry, "specify file-show-filter (regex)");
1501 
1502             std::string filter_string = entry->getString();
1503 
1504             Regex* r = new Regex(filter_string, 1);
1505 
1506             if(!r->isValid()) {
1507                 delete r;
1508                 conffile.entryException(entry, "invalid file-show-filter regular expression");
1509             }
1510 
1511             file_show_filters.push_back(r);
1512         }
1513     }
1514 
1515     if((entry = gource_settings->getEntry("user-filter")) != 0) {
1516 
1517         ConfEntryList* filters = gource_settings->getEntries("user-filter");
1518 
1519         for(ConfEntryList::iterator it = filters->begin(); it != filters->end(); it++) {
1520 
1521             entry = *it;
1522 
1523             if(!entry->hasValue()) conffile.entryException(entry, "specify user-filter (regex)");
1524 
1525             std::string filter_string = entry->getString();
1526 
1527             Regex* r = new Regex(filter_string, 1);
1528 
1529             if(!r->isValid()) {
1530                 delete r;
1531                 conffile.entryException(entry, "invalid user-filter regular expression");
1532             }
1533 
1534             user_filters.push_back(r);
1535         }
1536     }
1537 
1538     if((entry = gource_settings->getEntry("user-show-filter")) != 0) {
1539 
1540         ConfEntryList* filters = gource_settings->getEntries("user-show-filter");
1541 
1542         for(ConfEntryList::iterator it = filters->begin(); it != filters->end(); it++) {
1543 
1544             entry = *it;
1545 
1546             if(!entry->hasValue()) conffile.entryException(entry, "specify user-show-filter (regex)");
1547 
1548             std::string filter_string = entry->getString();
1549 
1550             Regex* r = new Regex(filter_string, 1);
1551 
1552             if(!r->isValid()) {
1553                 delete r;
1554                 conffile.entryException(entry, "invalid user-show-filter regular expression");
1555             }
1556 
1557             user_show_filters.push_back(r);
1558         }
1559     }
1560 
1561     if((entry = gource_settings->getEntry("dir-name-depth")) != 0) {
1562 
1563         if(!entry->hasValue()) conffile.entryException(entry, "specify dir-name-depth (depth)");
1564 
1565         dir_name_depth = entry->getInt();
1566 
1567         if(dir_name_depth <= 0) {
1568             conffile.invalidValueException(entry);
1569         }
1570     }
1571 
1572     if((entry = gource_settings->getEntry("dir-name-position")) != 0) {
1573 
1574         if(!entry->hasValue()) conffile.entryException(entry, "specify dir-name-position (float)");
1575 
1576         dir_name_position = entry->getFloat();
1577 
1578         if(dir_name_position < 0.1f || dir_name_position > 1.0f) {
1579             conffile.entryException(entry, "dir-name-position outside of range 0.1 - 1.0 (inclusive)");
1580         }
1581     }
1582 
1583     //validate path
1584     if(gource_settings->hasValue("path")) {
1585         path = gource_settings->getString("path");
1586         default_path = false;
1587     }
1588 
1589     if(path == "-") {
1590 
1591         if(log_format.size() == 0) {
1592             throw ConfFileException("log-format required when reading from STDIN", "", 0);
1593         }
1594 
1595 #ifdef _WIN32
1596         DWORD available_bytes;
1597         HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1598 
1599         while(PeekNamedPipe(stdin_handle, 0, 0, 0,
1600             &available_bytes, 0) && available_bytes==0 && !std::cin.fail()) {
1601             SDL_Delay(100);
1602         }
1603 #else
1604         while(std::cin.peek() == EOF && !std::cin.fail()) SDL_Delay(100);
1605 #endif
1606 
1607         std::cin.clear();
1608 
1609     } else if(!path.empty() && path != ".") {
1610 
1611         //remove trailing slash
1612         if(path[path.size()-1] == '\\' || path[path.size()-1] == '/') {
1613             path.resize(path.size()-1);
1614         }
1615 
1616         // check path exists
1617         if(!boost::filesystem::exists(path)) {
1618             throw ConfFileException(str(boost::format("'%s' does not appear to be a valid file or directory") % path), "", 0);
1619         }
1620     }
1621 }
1622