BibTeXq
query a bibtex file
bibtexq.c
Go to the documentation of this file.
00001 /*
00002  * This work is licensed under a Creative Commons 
00003  * Attribution-Noncommercial-Share Alike 3.0 United States License.
00004  * 
00005  *    http://creativecommons.org/licenses/by-nc-sa/3.0/us/
00006  * 
00007  * You are free:
00008  * 
00009  *    to Share - to copy, distribute, display, and perform the work
00010  *    to Remix - to make derivative works
00011  * 
00012  * Under the following conditions:
00013  * 
00014  *    Attribution. You must attribute the work in the manner specified by the
00015  *    author or licensor (but not in any way that suggests that they endorse you
00016  *    or your use of the work).
00017  * 
00018  *    Noncommercial. You may not use this work for commercial purposes.
00019  * 
00020  *    Share Alike. If you alter, transform, or build upon this work, you may
00021  *    distribute the resulting work only under the same or similar license to
00022  *    this one.
00023  * 
00024  * For any reuse or distribution, you must make clear to others the license
00025  * terms of this work. The best way to do this is by including this header.
00026  * 
00027  * Any of the above conditions can be waived if you get permission from the
00028  * copyright holder.
00029  * 
00030  * Apart from the remix rights granted under this license, nothing in this
00031  * license impairs or restricts the author's moral rights.
00032  */
00033 
00034 
00072 #include <config.h>
00073 
00074 #include <stdlib.h>
00075 #include <stdio.h>
00076 #include <assert.h>
00077 #include <errno.h>
00078 #include <string.h>
00079 #include <strings.h>
00080 #include <regex.h>
00081 
00082 #ifdef BIBTEXQ_HAVE_DMALLOC
00083 #include <dmalloc.h>
00084 #endif
00085 
00086 #include <jwsc/base/option.h>
00087 #include <jwsc/base/error.h>
00088 
00089 #include "bibtex_entry.h"
00090 
00091 
00092 #define  NUM_OPTS_NO_ARG    2
00093 #define  NUM_OPTS_WITH_ARG  8
00094 
00095 
00096 extern FILE* yyin;
00097 extern int yyparse();
00098 
00099 
00101 Bibtex_entry* entry = NULL;
00102 
00103 
00105 static Option_no_arg opts_no_arg[NUM_OPTS_NO_ARG];
00106 
00108 static Option_with_arg opts_with_arg[NUM_OPTS_WITH_ARG];
00109 
00111 static const char* output_fname;
00112 
00114 static FILE* output_fp;
00115 
00117 static uint8_t query_type;
00118 
00120 static uint8_t query_key;
00121 
00123 static uint8_t query_author;
00124 
00126 static uint8_t query_title;
00127 
00129 static uint8_t query_project;
00130 
00132 static uint8_t query_venue;
00133 
00135 static uint8_t query_keyword;
00136 
00138 static regex_t type_preg;
00139 
00141 static regex_t key_preg;
00142 
00144 static regex_t author_preg;
00145 
00147 static regex_t title_preg;
00148 
00150 static regex_t project_preg;
00151 
00153 static regex_t venue_preg;
00154 
00156 static regex_t keyword_preg;
00157 
00158 
00159 static Error* init_regex(const char* value, regex_t* preg)
00160 {
00161     int  errcode;
00162     char errbuf[256] = {0};
00163 
00164     if ((errcode = regcomp(preg, value, REG_EXTENDED | REG_ICASE | REG_NOSUB)))
00165     {
00166         regerror(errcode, preg, errbuf, 255);
00167         return JWSC_EARG(errbuf);
00168     }
00169     return NULL;
00170 }
00171 
00173 static void print_usage(void)
00174 {
00175     fprintf(stderr, "usage: bibtexq OPTIONS <file.bib>\n");
00176     print_options(stderr, 25, NUM_OPTS_NO_ARG, opts_no_arg, NUM_OPTS_WITH_ARG,
00177             opts_with_arg);
00178 }
00179 
00181 static Error* process_help_opt(void)
00182 {
00183     print_usage();
00184     exit(EXIT_SUCCESS);
00185     return NULL;
00186 }
00187 
00189 static Error* process_version_opt(void)
00190 {
00191     fprintf(stderr, "%s\n", BIBTEXQ_PACKAGE_STRING);
00192     exit(EXIT_SUCCESS);
00193     return NULL;
00194 }
00195 
00197 static Error* process_output_opt(Option_arg arg)
00198 {
00199     if (arg == NULL)
00200     {
00201         return JWSC_EARG("Option 'output' requires an argument");
00202     }
00203     output_fname = arg;
00204     return NULL;
00205 }
00206 
00208 static Error* process_type_opt(Option_arg arg)
00209 {
00210     Error* e;
00211 
00212     if (arg == NULL)
00213     {
00214         return JWSC_EARG("Option 'type' requires an argument");
00215     }
00216     if ((e = init_regex(arg, &type_preg)))
00217     {
00218         return e;
00219     }
00220     query_type = 1;
00221     return NULL;
00222 }
00223 
00225 static Error* process_key_opt(Option_arg arg)
00226 {
00227     Error* e;
00228 
00229     if (arg == NULL)
00230     {
00231         return JWSC_EARG("Option 'key' requires an argument");
00232     }
00233     if ((e = init_regex(arg, &key_preg)))
00234     {
00235         return e;
00236     }
00237     query_key = 1;
00238     return NULL;
00239 }
00240 
00242 static Error* process_author_opt(Option_arg arg)
00243 {
00244     Error* e;
00245 
00246     if (arg == NULL)
00247     {
00248         return JWSC_EARG("Option 'author' requires an argument");
00249     }
00250     if ((e = init_regex(arg, &author_preg)))
00251     {
00252         return e;
00253     }
00254     query_author = 1;
00255     return NULL;
00256 }
00257 
00259 static Error* process_title_opt(Option_arg arg)
00260 {
00261     Error* e;
00262 
00263     if (arg == NULL)
00264     {
00265         return JWSC_EARG("Option 'title' requires an argument");
00266     }
00267     if ((e = init_regex(arg, &title_preg)))
00268     {
00269         return e;
00270     }
00271     query_title = 1;
00272     return NULL;
00273 }
00274 
00276 static Error* process_project_opt(Option_arg arg)
00277 {
00278     Error* e;
00279 
00280     if (arg == NULL)
00281     {
00282         return JWSC_EARG("Option 'project' requires an argument");
00283     }
00284     if ((e = init_regex(arg, &project_preg)))
00285     {
00286         return e;
00287     }
00288     query_project = 1;
00289     return NULL;
00290 }
00291 
00293 static Error* process_venue_opt(Option_arg arg)
00294 {
00295     Error* e;
00296 
00297     if (arg == NULL)
00298     {
00299         return JWSC_EARG("Option 'venue' requires an argument");
00300     }
00301     if ((e = init_regex(arg, &venue_preg)))
00302     {
00303         return e;
00304     }
00305     query_venue = 1;
00306     return NULL;
00307 }
00308 
00310 static Error* process_keyword_opt(Option_arg arg)
00311 {
00312     Error* e;
00313 
00314     if (arg == NULL)
00315     {
00316         return JWSC_EARG("Option 'keyword' requires an argument");
00317     }
00318     if ((e = init_regex(arg, &keyword_preg)))
00319     {
00320         return e;
00321     }
00322     query_keyword = 1;
00323     return NULL;
00324 }
00325 
00327 static void init_options(void)
00328 {
00329     uint32_t i;
00330 
00331     i = 0;
00332     init_option_no_arg(&(opts_no_arg[i++]), "help", 'h', 
00333             "Prints program usage.", process_help_opt);
00334     init_option_no_arg(&(opts_no_arg[i++]), "version", 'v', 
00335             "Prints program version.", process_version_opt);
00336 
00337     i = 0;
00338     init_option_with_arg(&(opts_with_arg[i++]), "output", 'o',
00339             "File to write the queried entries to. Default is to use stdout.", 
00340             process_output_opt);
00341     init_option_with_arg(&(opts_with_arg[i++]), "type", '\0',
00342             "Entry type regular expression.", process_type_opt);
00343     init_option_with_arg(&(opts_with_arg[i++]), "key", '\0',
00344             "Unique entry key regular expression.", process_key_opt);
00345     init_option_with_arg(&(opts_with_arg[i++]), "author", '\0',
00346             "Author field regular expression.", process_author_opt);
00347     init_option_with_arg(&(opts_with_arg[i++]), "title", '\0',
00348             "Title field regular expression.", process_title_opt);
00349     init_option_with_arg(&(opts_with_arg[i++]), "project", '\0',
00350             "Project field regular expression.", process_project_opt);
00351     init_option_with_arg(&(opts_with_arg[i++]), "venue", '\0',
00352             "Venue field regular expression.", process_venue_opt);
00353     init_option_with_arg(&(opts_with_arg[i++]), "keyword", '\0',
00354             "Keyword field regular expression.", process_keyword_opt);
00355 }
00356 
00357 
00358 
00367 void query_bibtex_entry(Bibtex_entry* entry)
00368 {
00369     uint32_t      i;
00370     uint8_t       match;
00371     uint8_t       queried_author;
00372     uint8_t       queried_title;
00373     uint8_t       queried_project;
00374     uint8_t       queried_venue;
00375     uint8_t       queried_keyword;
00376     Bibtex_field* field;
00377 
00378     match = 1;
00379     queried_author  = 0;
00380     queried_title   = 0;
00381     queried_project = 0;
00382     queried_venue   = 0;
00383     queried_keyword = 0;
00384 
00385     if (match && query_type)
00386     { 
00387         if (regexec(&type_preg, entry->type, 0, 0, 0) 
00388                 == REG_NOMATCH)
00389         {
00390             match = 0;
00391         }
00392     }
00393 
00394     if (match && query_key)
00395     { 
00396         if (regexec(&key_preg, entry->key, 0, 0, 0) 
00397                 == REG_NOMATCH)
00398         {
00399             match = 0;
00400         }
00401     }
00402 
00403     for (i = 0; match && i < entry->num_fields; i++)
00404     {
00405         field = entry->fields[ i ];
00406 
00407         if (match && query_author && strcasecmp(field->name, "author") == 0)
00408         {
00409             queried_author = 1;
00410             if (regexec(&author_preg, (const char*)field->value, 
00411                         0, 0, 0) == REG_NOMATCH)
00412             {
00413                 match = 0;
00414             }
00415         }
00416 
00417         if (match && query_title && strcasecmp(field->name, "title") == 0)
00418         {
00419             queried_title = 1;
00420             if (regexec(&title_preg, (const char*)field->value, 
00421                         0, 0, 0) == REG_NOMATCH)
00422             {
00423                 match = 0;
00424             }
00425         }
00426 
00427         if (match && query_project && strcasecmp(field->name, "projects") == 0)
00428         {
00429             queried_project = 1;
00430             if (regexec(&project_preg, (const char*)field->value, 
00431                         0, 0, 0) == REG_NOMATCH)
00432             {
00433                 match = 0;
00434             }
00435         }
00436 
00437         if (match && query_venue && strcasecmp(field->name, "venue") == 0)
00438         {
00439             queried_venue = 1;
00440             if (regexec(&venue_preg, (const char*)field->value, 
00441                         0, 0, 0) == REG_NOMATCH)
00442             {
00443                 match = 0;
00444             }
00445         }
00446 
00447         if (match && query_keyword && strcasecmp(field->name, "keywords") == 0)
00448         {
00449             queried_keyword = 1;
00450             if (regexec(&keyword_preg, (const char*)field->value, 
00451                         0, 0, 0) == REG_NOMATCH)
00452             {
00453                 match = 0;
00454             }
00455         }
00456     }
00457 
00458     if ((query_author   &&  !queried_author ) ||
00459         (query_title    &&  !queried_title  ) ||
00460         (query_project  &&  !queried_project) ||
00461         (query_venue    &&  !queried_venue  ) ||
00462         (query_keyword  &&  !queried_keyword))
00463     {
00464         match = 0;
00465     }
00466 
00467     if (match)
00468     {
00469         write_bibtex_entry(entry, output_fp);
00470     }
00471 }
00472 
00473 
00474 
00476 int main(int argc, const char** argv)
00477 {
00478     int         argi;
00479     const char* bibtex_fname;
00480     Error*      e;
00481 
00482     init_options();
00483 
00484     output_fp     = stdout;
00485     query_type    = 0;
00486     query_key     = 0;
00487     query_author  = 0;
00488     query_title   = 0;
00489     query_project = 0;
00490     query_venue   = 0;
00491     query_keyword = 0;
00492 
00493     if ((e = process_options(argc, argv, &argi, NUM_OPTS_NO_ARG, opts_no_arg,
00494                     NUM_OPTS_WITH_ARG, opts_with_arg)) != NULL)
00495     {
00496         print_error_msg_exit("bibtexq", e->msg);
00497     }
00498 
00499     if (output_fname && (output_fp = fopen(output_fname, "w")) == NULL)
00500     {
00501         print_error_msg("bibtexq", NULL);
00502         print_error_msg_exit(output_fname, strerror(errno));
00503     }
00504 
00505     if ((argc - argi) < 1)
00506     {
00507         print_error_msg_exit("bibtexq", "No BIBTEX file to query");
00508     }
00509     bibtex_fname = argv[ argi ];
00510 
00511     if ((yyin = fopen(bibtex_fname, "r")) == NULL)
00512     {
00513         print_error_msg("bibtexq", NULL);
00514         print_errno_exit(bibtex_fname);
00515     }
00516 
00517     yyparse();
00518 
00519     if (output_fp != stdout)
00520     {
00521         if (fclose(output_fp) != 0)
00522         {
00523             print_error_msg("bibtexq", NULL);
00524             print_errno_exit("output file");
00525         }
00526     }
00527 
00528     fclose(yyin);
00529 
00530     free_bibtex_entry(entry);
00531 
00532     return EXIT_SUCCESS;
00533 }