1.1 --- a/bookloupe/bookloupe.c Sun Sep 29 09:18:05 2013 +0100
1.2 +++ b/bookloupe/bookloupe.c Mon Sep 30 08:18:42 2013 +0100
1.3 @@ -132,37 +132,99 @@
1.4 gboolean pswit[SWITNO]; /* program switches */
1.5 gchar *opt_charset;
1.6
1.7 +gboolean typo_compat,paranoid_compat;
1.8 +
1.9 static GOptionEntry options[]={
1.10 { "dp", 'd', 0, G_OPTION_ARG_NONE, pswit+DP_SWITCH,
1.11 "Ignore DP-specific markup", NULL },
1.12 - { "noecho", 'e', 0, G_OPTION_ARG_NONE, pswit+ECHO_SWITCH,
1.13 + { "no-dp", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.14 + G_OPTION_ARG_NONE, pswit+DP_SWITCH,
1.15 + "Don't ignore DP-specific markup", NULL },
1.16 + { "echo", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, pswit+ECHO_SWITCH,
1.17 + "Echo queried line", NULL },
1.18 + { "no-echo", 'e', G_OPTION_FLAG_REVERSE,
1.19 + G_OPTION_ARG_NONE, pswit+ECHO_SWITCH,
1.20 "Don't echo queried line", NULL },
1.21 { "squote", 's', 0, G_OPTION_ARG_NONE, pswit+SQUOTE_SWITCH,
1.22 "Check single quotes", NULL },
1.23 - { "typo", 't', 0, G_OPTION_ARG_NONE, pswit+TYPO_SWITCH,
1.24 + { "no-squote", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.25 + G_OPTION_ARG_NONE, pswit+SQUOTE_SWITCH,
1.26 + "Don't check single quotes", NULL },
1.27 + { "typo", 0, 0, G_OPTION_ARG_NONE, pswit+TYPO_SWITCH,
1.28 "Check common typos", NULL },
1.29 + { "no-typo", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.30 + G_OPTION_ARG_NONE, pswit+TYPO_SWITCH,
1.31 + "Don't check common typos", NULL },
1.32 { "qpara", 'p', 0, G_OPTION_ARG_NONE, pswit+QPARA_SWITCH,
1.33 "Require closure of quotes on every paragraph", NULL },
1.34 - { "relaxed", 'x', 0, G_OPTION_ARG_NONE, pswit+PARANOID_SWITCH,
1.35 + { "no-qpara", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.36 + G_OPTION_ARG_NONE, pswit+QPARA_SWITCH,
1.37 + "Don't require closure of quotes on every paragraph", NULL },
1.38 + { "paranoid", 0, G_OPTION_FLAG_HIDDEN,
1.39 + G_OPTION_ARG_NONE, pswit+PARANOID_SWITCH,
1.40 + "Enable paranoid querying of everything", NULL },
1.41 + { "no-paranoid", 0, G_OPTION_FLAG_REVERSE,
1.42 + G_OPTION_ARG_NONE, pswit+PARANOID_SWITCH,
1.43 "Disable paranoid querying of everything", NULL },
1.44 - { "line-end", 'l', 0, G_OPTION_ARG_NONE, pswit+LINE_END_SWITCH,
1.45 - "Disable line end checking", NULL },
1.46 + { "line-end", 0, G_OPTION_FLAG_HIDDEN,
1.47 + G_OPTION_ARG_NONE, pswit+LINE_END_SWITCH,
1.48 + "Enable line end checking", NULL },
1.49 + { "no-line-end", 'l', G_OPTION_FLAG_REVERSE,
1.50 + G_OPTION_ARG_NONE, pswit+LINE_END_SWITCH,
1.51 + "Diable line end checking", NULL },
1.52 { "overview", 'o', 0, G_OPTION_ARG_NONE, pswit+OVERVIEW_SWITCH,
1.53 "Overview: just show counts", NULL },
1.54 + { "no-overview", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.55 + G_OPTION_ARG_NONE, pswit+OVERVIEW_SWITCH,
1.56 + "Show individual warnings", NULL },
1.57 { "stdout", 'y', 0, G_OPTION_ARG_NONE, pswit+STDOUT_SWITCH,
1.58 "Output errors to stdout instead of stderr", NULL },
1.59 + { "no-stdout", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.60 + G_OPTION_ARG_NONE, pswit+STDOUT_SWITCH,
1.61 + "Output errors to stderr instead of stdout", NULL },
1.62 { "header", 'h', 0, G_OPTION_ARG_NONE, pswit+HEADER_SWITCH,
1.63 "Echo header fields", NULL },
1.64 + { "no-header", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.65 + G_OPTION_ARG_NONE, pswit+HEADER_SWITCH,
1.66 + "Don't echo header fields", NULL },
1.67 { "markup", 'm', 0, G_OPTION_ARG_NONE, pswit+MARKUP_SWITCH,
1.68 "Ignore markup in < >", NULL },
1.69 + { "no-markup", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.70 + G_OPTION_ARG_NONE, pswit+MARKUP_SWITCH,
1.71 + "No special handling for markup in < >", NULL },
1.72 { "usertypo", 'u', 0, G_OPTION_ARG_NONE, pswit+USERTYPO_SWITCH,
1.73 "Use file of user-defined typos", NULL },
1.74 + { "no-usertypo", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.75 + G_OPTION_ARG_NONE, pswit+USERTYPO_SWITCH,
1.76 + "Ignore file of user-defined typos", NULL },
1.77 + { "verbose", 'v', 0, G_OPTION_ARG_NONE, pswit+VERBOSE_SWITCH,
1.78 + "Verbose - list everything", NULL },
1.79 + { "no-verbose", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_REVERSE,
1.80 + G_OPTION_ARG_NONE, pswit+VERBOSE_SWITCH,
1.81 + "Switch off verbose mode", NULL },
1.82 + { "charset", 0, 0, G_OPTION_ARG_STRING, &opt_charset,
1.83 + "Set of characters valid for this ebook", "NAME" },
1.84 + { NULL }
1.85 +};
1.86 +
1.87 +/*
1.88 + * Options relating to configuration which make no sense from inside
1.89 + * a configuration file.
1.90 + */
1.91 +
1.92 +static GOptionEntry config_options[]={
1.93 { "web", 'w', 0, G_OPTION_ARG_NONE, pswit+WEB_SWITCH,
1.94 "Defaults for use on www upload", NULL },
1.95 - { "verbose", 'v', 0, G_OPTION_ARG_NONE, pswit+VERBOSE_SWITCH,
1.96 - "Verbose - list everything", NULL },
1.97 - { "charset", 0, 0, G_OPTION_ARG_STRING, &opt_charset,
1.98 - "Set of characters valid for this ebook", "NAME" },
1.99 + { "dump-config", 0, 0, G_OPTION_ARG_NONE, pswit+DUMP_CONFIG_SWITCH,
1.100 + "Dump current config settings", NULL },
1.101 + { NULL }
1.102 +};
1.103 +
1.104 +static GOptionEntry compatibility_options[]={
1.105 + { "toggle-typo", 't', 0, G_OPTION_ARG_NONE, &typo_compat,
1.106 + "Toggle checking for common typos", NULL },
1.107 + { "toggle-relaxed", 'x', 0, G_OPTION_ARG_NONE, ¶noid_compat,
1.108 + "Toggle both paranoid mode and common typos", NULL },
1.109 { NULL }
1.110 };
1.111
1.112 @@ -249,31 +311,232 @@
1.113 return TRUE;
1.114 }
1.115
1.116 +GKeyFile *config;
1.117 +
1.118 +void config_file_update(GKeyFile *kf)
1.119 +{
1.120 + int i;
1.121 + const char *s;
1.122 + gboolean sw;
1.123 + for(i=0;options[i].long_name;i++)
1.124 + {
1.125 + if (g_str_has_prefix(options[i].long_name,"no-"))
1.126 + continue;
1.127 + if (options[i].arg==G_OPTION_ARG_NONE)
1.128 + {
1.129 + sw=*(gboolean *)options[i].arg_data;
1.130 + if (options[i].flags&G_OPTION_FLAG_REVERSE)
1.131 + sw=!sw;
1.132 + g_key_file_set_boolean(kf,"options",options[i].long_name,sw);
1.133 + }
1.134 + else if (options[i].arg==G_OPTION_ARG_STRING)
1.135 + {
1.136 + s=*(gchar **)options[i].arg_data;
1.137 + if (!s)
1.138 + s="auto";
1.139 + g_key_file_set_string(kf,"options",options[i].long_name,s);
1.140 + }
1.141 + else
1.142 + g_assert_not_reached();
1.143 + }
1.144 +}
1.145 +
1.146 +void config_file_add_comments(GKeyFile *kf)
1.147 +{
1.148 + int i;
1.149 + gchar *comment;
1.150 + g_key_file_set_comment(kf,NULL,NULL," Default configuration for bookloupe",
1.151 + NULL);
1.152 + for(i=0;options[i].long_name;i++)
1.153 + {
1.154 + if (g_str_has_prefix(options[i].long_name,"no-"))
1.155 + continue;
1.156 + comment=g_strconcat(" ",options[i].description,NULL);
1.157 + g_key_file_set_comment(kf,"options",options[i].long_name,comment,NULL);
1.158 + g_free(comment);
1.159 + }
1.160 +}
1.161 +
1.162 +void dump_config(void)
1.163 +{
1.164 + gchar *s;
1.165 + if (config)
1.166 + config_file_update(config);
1.167 + else
1.168 + {
1.169 + config=g_key_file_new();
1.170 + config_file_update(config);
1.171 + config_file_add_comments(config);
1.172 + }
1.173 + s=g_key_file_to_data(config,NULL,NULL);
1.174 + if (s)
1.175 + g_print("%s",s);
1.176 + g_free(s);
1.177 +}
1.178 +
1.179 +GKeyFile *read_config_file(gchar **full_path)
1.180 +{
1.181 + int i;
1.182 + GError *err=NULL;
1.183 + gchar **search_dirs;
1.184 + gchar *path;
1.185 + const char *search_path;
1.186 + GKeyFile *kf;
1.187 + kf=g_key_file_new();
1.188 + search_path=g_getenv("BOOKLOUPE_CONFIG_PATH");
1.189 + if (search_path)
1.190 + {
1.191 +#ifdef __WIN32__
1.192 + search_dirs=g_strsplit(search_path,";",0);
1.193 +#else
1.194 + search_dirs=g_strsplit(search_path,":",0);
1.195 +#endif
1.196 + }
1.197 + else
1.198 + {
1.199 + search_dirs=g_new(gchar *,4);
1.200 + search_dirs[0]=g_get_current_dir();
1.201 + search_dirs[1]=g_strdup(running_from);
1.202 + search_dirs[2]=g_strdup(g_get_user_config_dir());
1.203 + search_dirs[3]=NULL;
1.204 + }
1.205 + for(i=0;search_dirs[i];i++)
1.206 + {
1.207 + path=g_build_filename(search_dirs[i],"bookloupe.ini",NULL);
1.208 + if (g_key_file_load_from_file(kf,path,
1.209 + G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,&err))
1.210 + break;
1.211 + if (!g_error_matches(err,G_FILE_ERROR,G_FILE_ERROR_NOENT))
1.212 + {
1.213 + g_printerr("Bookloupe: Error reading %s\n",path);
1.214 + g_printerr("%s\n",err->message);
1.215 + exit(1);
1.216 + }
1.217 + g_clear_error(&err);
1.218 + g_free(path);
1.219 + path=NULL;
1.220 + }
1.221 + if (!search_dirs[i])
1.222 + {
1.223 + g_key_file_free(kf);
1.224 + kf=NULL;
1.225 + }
1.226 + g_strfreev(search_dirs);
1.227 + if (full_path && kf)
1.228 + *full_path=path;
1.229 + else
1.230 + g_free(path);
1.231 + return kf;
1.232 +}
1.233 +
1.234 +void parse_config_file(void)
1.235 +{
1.236 + int i,j;
1.237 + gchar *path,*s;
1.238 + gchar **keys;
1.239 + gboolean sw;
1.240 + GError *err=NULL;
1.241 + config=read_config_file(&path);
1.242 + if (config)
1.243 + keys=g_key_file_get_keys(config,"options",NULL,NULL);
1.244 + else
1.245 + keys=NULL;
1.246 + if (keys)
1.247 + {
1.248 + for(i=0;keys[i];i++)
1.249 + {
1.250 + for(j=0;options[j].long_name;j++)
1.251 + {
1.252 + if (g_str_has_prefix(options[j].long_name,"no-"))
1.253 + continue;
1.254 + else if (!strcmp(keys[i],options[j].long_name))
1.255 + {
1.256 + if (options[j].arg==G_OPTION_ARG_NONE)
1.257 + {
1.258 + sw=g_key_file_get_boolean(config,"options",keys[i],
1.259 + &err);
1.260 + if (err)
1.261 + {
1.262 + g_printerr("Bookloupe: %s: options.%s: %s\n",
1.263 + path,keys[i],err->message);
1.264 + g_clear_error(&err);
1.265 + }
1.266 + else
1.267 + {
1.268 + if (options[j].flags&G_OPTION_FLAG_REVERSE)
1.269 + sw=!sw;
1.270 + *(gboolean *)options[j].arg_data=sw;
1.271 + }
1.272 + break;
1.273 + }
1.274 + else if (options[j].arg==G_OPTION_ARG_STRING)
1.275 + {
1.276 + s=g_key_file_get_string(config,"options",keys[i],
1.277 + &err);
1.278 + if (err)
1.279 + {
1.280 + g_printerr("Bookloupe: %s: options.%s: %s\n",
1.281 + path,keys[i],err->message);
1.282 + g_clear_error(&err);
1.283 + }
1.284 + else
1.285 + {
1.286 + g_free(*(gchar **)options[j].arg_data);
1.287 + if (!g_strcmp0(s,"auto"))
1.288 + {
1.289 + *(gchar **)options[j].arg_data=NULL;
1.290 + g_free(s);
1.291 + }
1.292 + else
1.293 + *(gchar **)options[j].arg_data=s;
1.294 + }
1.295 + break;
1.296 + }
1.297 + else
1.298 + g_assert_not_reached();
1.299 + }
1.300 + }
1.301 + if (!options[j].long_name)
1.302 + g_printerr("Bookloupe: %s: Unknown option \"%s\" ignored\n",
1.303 + path,keys[i]);
1.304 + }
1.305 + g_strfreev(keys);
1.306 + }
1.307 + if (config)
1.308 + g_free(path);
1.309 +}
1.310 +
1.311 void parse_options(int *argc,char ***argv)
1.312 {
1.313 GError *err=NULL;
1.314 GOptionContext *context;
1.315 + GOptionGroup *compatibility;
1.316 context=g_option_context_new(
1.317 - "file - looks for errors in Project Gutenberg(TM) etexts");
1.318 + "file - look for errors in Project Gutenberg(TM) etexts");
1.319 g_option_context_add_main_entries(context,options,NULL);
1.320 + g_option_context_add_main_entries(context,config_options,NULL);
1.321 + compatibility=g_option_group_new("compatibility",
1.322 + "Options for Compatibility with Gutcheck:",
1.323 + "Show compatibility options",NULL,NULL);
1.324 + g_option_group_add_entries(compatibility,compatibility_options);
1.325 + g_option_context_add_group(context,compatibility);
1.326 + g_option_context_set_description(context,
1.327 + "For simplicity, only the switch options which reverse the\n"
1.328 + "default configuration are listed. In most cases, both vanilla\n"
1.329 + "and \"no-\" prefixed versions are available for use.");
1.330 if (!g_option_context_parse(context,argc,argv,&err))
1.331 {
1.332 g_printerr("Bookloupe: %s\n",err->message);
1.333 g_printerr("Use \"%s --help\" for help\n",(*argv)[0]);
1.334 exit(1);
1.335 }
1.336 - /* Paranoid checking is turned OFF, not on, by its switch */
1.337 - pswit[PARANOID_SWITCH]=!pswit[PARANOID_SWITCH];
1.338 - if (pswit[PARANOID_SWITCH])
1.339 - /* if running in paranoid mode, typo checks default to enabled */
1.340 + if (typo_compat)
1.341 pswit[TYPO_SWITCH]=!pswit[TYPO_SWITCH];
1.342 - /* Line-end checking is turned OFF, not on, by its switch */
1.343 - pswit[LINE_END_SWITCH]=!pswit[LINE_END_SWITCH];
1.344 - /* Echoing is turned OFF, not on, by its switch */
1.345 - pswit[ECHO_SWITCH]=!pswit[ECHO_SWITCH];
1.346 - if (pswit[OVERVIEW_SWITCH])
1.347 - /* just print summary; don't echo */
1.348 - pswit[ECHO_SWITCH]=FALSE;
1.349 + if (paranoid_compat)
1.350 + {
1.351 + pswit[PARANOID_SWITCH]=!pswit[PARANOID_SWITCH];
1.352 + pswit[TYPO_SWITCH]=!pswit[TYPO_SWITCH];
1.353 + }
1.354 /*
1.355 * Web uploads - for the moment, this is really just a placeholder
1.356 * until we decide what processing we really want to do on web uploads
1.357 @@ -300,8 +563,16 @@
1.358 g_printerr("%s\n",err->message);
1.359 exit(1);
1.360 }
1.361 + if (pswit[DUMP_CONFIG_SWITCH])
1.362 + {
1.363 + dump_config();
1.364 + exit(0);
1.365 + }
1.366 g_free(opt_charset);
1.367 opt_charset=NULL;
1.368 + if (pswit[OVERVIEW_SWITCH])
1.369 + /* just print summary; don't echo */
1.370 + pswit[ECHO_SWITCH]=FALSE;
1.371 if (*argc<2)
1.372 {
1.373 proghelp(context);
1.374 @@ -448,6 +719,15 @@
1.375 saved_cp=GetConsoleOutputCP();
1.376 #endif
1.377 running_from=g_path_get_dirname(argv[0]);
1.378 + /* Paranoid checking is turned OFF, not on, by its switch */
1.379 + pswit[PARANOID_SWITCH]=TRUE;
1.380 + /* if running in paranoid mode, typo checks default to enabled */
1.381 + pswit[TYPO_SWITCH]=TRUE;
1.382 + /* Line-end checking is turned OFF, not on, by its switch */
1.383 + pswit[LINE_END_SWITCH]=TRUE;
1.384 + /* Echoing is turned OFF, not on, by its switch */
1.385 + pswit[ECHO_SWITCH]=TRUE;
1.386 + parse_config_file();
1.387 parse_options(&argc,&argv);
1.388 if (pswit[USERTYPO_SWITCH])
1.389 read_user_scannos();
1.390 @@ -489,6 +769,8 @@
1.391 if (usertypo)
1.392 g_tree_unref(usertypo);
1.393 set_charset(NULL,NULL);
1.394 + if (config)
1.395 + g_key_file_free(config);
1.396 return 0;
1.397 }
1.398