2 /* Copyright (C) 1989-2014 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
27 extern "C" const char *Version_string;
29 const char PRE_LABEL_MARKER = '\013';
30 const char POST_LABEL_MARKER = '\014';
31 const char LABEL_MARKER = '\015'; // label_type is added on
33 #define FORCE_LEFT_BRACKET 04
34 #define FORCE_RIGHT_BRACKET 010
36 static FILE *outfp = stdout;
38 string capitalize_fields;
39 string reverse_fields;
40 string abbreviate_fields;
41 string period_before_last_name = ". ";
42 string period_before_initial = ".";
43 string period_before_hyphen = "";
44 string period_before_other = ". ";
46 int annotation_field = -1;
47 string annotation_macro;
48 string discard_fields = "XYZ";
49 string pre_label = "\\*([.";
50 string post_label = "\\*(.]";
51 string sep_label = ", ";
52 int have_bibliography = 0;
54 int move_punctuation = 0;
55 int abbreviate_label_ranges = 0;
56 string label_range_indicator;
57 int label_in_text = 1;
58 int label_in_reference = 1;
59 int date_as_label = 0;
60 int sort_adjacent_labels = 0;
61 // Join exactly two authors with this.
62 string join_authors_exactly_two = " and ";
63 // When there are more than two authors join the last two with this.
64 string join_authors_last_two = ", and ";
65 // Otherwise join authors with this.
66 string join_authors_default = ", ";
67 string separate_label_second_parts = ", ";
68 // Use this string to represent that there are other authors.
69 string et_al = " et al";
70 // Use et al only if it can replace at least this many authors.
71 int et_al_min_elide = 2;
72 // Use et al only if the total number of authors is at least this.
73 int et_al_min_total = 3;
76 int compatible_flag = 0;
78 int short_label_flag = 0;
80 static int recognize_R1_R2 = 1;
82 search_list database_list;
83 int search_default = 1;
84 static int default_database_loaded = 0;
86 static reference **citation = 0;
87 static int ncitations = 0;
88 static int citation_max = 0;
90 static reference **reference_hash_table = 0;
91 static int hash_table_size;
92 static int nreferences = 0;
94 static int need_syncing = 0;
96 string pending_lf_lines;
98 static void output_pending_line();
99 static unsigned immediately_handle_reference(const string &);
100 static void immediately_output_references();
101 static unsigned store_reference(const string &);
102 static void divert_to_temporary_file();
103 static reference *make_reference(const string &, unsigned *);
104 static void usage(FILE *stream);
105 static void do_file(const char *);
106 static void split_punct(string &line, string &punct);
107 static void output_citation_group(reference **v, int n, label_type, FILE *fp);
108 static void possibly_load_default_database();
110 int main(int argc, char **argv)
112 program_name = argv[0];
113 static char stderr_buf[BUFSIZ];
114 setbuf(stderr, stderr_buf);
116 int finished_options = 0;
121 !finished_options && argc > 0 && argv[0][0] == '-'
122 && argv[0][1] != '\0';
124 const char *opt = argv[0] + 1;
125 while (opt != 0 && *opt != '\0') {
133 label_in_reference = 0;
137 annotation_field = 'X';
138 annotation_macro = "AP";
140 else if (csalnum(opt[0]) && opt[1] == '.' && opt[2] != '\0') {
141 annotation_field = opt[0];
142 annotation_macro = opt + 2;
147 move_punctuation = 1;
155 // Not a very useful spec.
156 set_label_spec("(A.n|Q)', '(D.y|D)");
170 if (*++opt == '\0') {
176 error("option `f' requires an argument");
186 for (ptr = num; *ptr; ptr++)
187 if (!csdigit(*ptr)) {
188 error("bad character `%1' in argument to -f option", *ptr);
196 set_label_spec(spec.contents());
203 label_in_reference = 0;
211 capitalize_fields = ++opt;
221 error("bad field name `%1'", *opt++);
235 for (ptr = ++opt; *ptr; ptr++)
236 if (!csdigit(*ptr)) {
237 error("argument to `a' option not a number");
241 reverse_fields = 'A';
242 reverse_fields += opt;
248 linear_ignore_fields = ++opt;
253 char buf[INT_DIGITS*2 + 11]; // A.n+2D.y-3%a
255 if (*++opt != '\0' && *opt != ',') {
257 long n = strtol(opt, &ptr, 10);
258 if (n == 0 && ptr == opt) {
259 error("bad integer `%1' in `l' option", opt);
266 sprintf(strchr(buf, '\0'), "+%ld", n);
273 long n = strtol(opt, &ptr, 10);
274 if (n == 0 && ptr == opt) {
275 error("bad integer `%1' in `l' option", opt);
281 sprintf(strchr(buf, '\0'), "-%ld", n);
284 error("argument to `l' option not of form `m,n'");
287 if (!set_label_spec(buf))
298 const char *filename = 0;
299 if (*++opt == '\0') {
305 error("option `p' requires an argument");
314 database_list.add_file(filename);
329 long n = strtol(opt, &ptr, 10);
330 if (n == 0 && ptr == opt) {
331 error("bad integer `%1' in `t' option", opt);
337 linear_truncate_len = int(n);
342 if (opt[1] == '\0') {
343 finished_options = 1;
347 if (strcmp(opt,"-version")==0) {
349 printf("GNU refer (groff) version %s\n", Version_string);
353 if (strcmp(opt,"-help")==0) {
360 error("unrecognized option `%1'", *opt);
368 set_label_spec("%1");
376 for (int i = 0; i < argc; i++) {
385 if (fflush(stdout) < 0)
386 fatal("output error");
390 static void usage(FILE *stream)
393 "usage: %s [-benvCPRS] [-aN] [-cXYZ] [-fN] [-iXYZ] [-kX] [-lM,N] [-p file]\n"
394 " [-sXYZ] [-tN] [-BL.M] [files ...]\n",
398 static void possibly_load_default_database()
400 if (search_default && !default_database_loaded) {
401 char *filename = getenv("REFER");
403 database_list.add_file(filename);
405 database_list.add_file(DEFAULT_INDEX, 1);
406 default_database_loaded = 1;
410 static int is_list(const string &str)
412 const char *start = str.contents();
413 const char *end = start + str.length();
414 while (end > start && csspace(end[-1]))
416 while (start < end && csspace(*start))
418 return end - start == 6 && memcmp(start, "$LIST$", 6) == 0;
421 static void do_file(const char *filename)
424 if (strcmp(filename, "-") == 0) {
429 fp = fopen(filename, "r");
431 error("can't open `%1': %2", filename, strerror(errno));
435 current_filename = filename;
436 fprintf(outfp, ".lf 1 %s\n", filename);
444 if (line.length() > 0)
448 if (invalid_input_char(c))
449 error("invalid input character code %1", c);
456 int len = line.length();
460 if (len >= 2 && line[0] == '.' && line[1] == '[') {
461 int start_lineno = current_lineno;
462 int start_of_line = 1;
465 string pre(line.contents() + 2, line.length() - 3);
469 error_with_file_and_line(current_filename, start_lineno,
470 "missing `.]' line");
475 if (start_of_line && c == '.') {
478 while ((d = getc(fp)) != '\n' && d != EOF) {
479 if (invalid_input_char(d))
480 error("invalid input character code %1", d);
489 if (invalid_input_char(c))
490 error("invalid input character code %1", c);
493 start_of_line = (c == '\n');
496 output_pending_line();
500 error("found `$LIST$' but not accumulating references");
503 unsigned flags = (accumulate
504 ? store_reference(str)
505 : immediately_handle_reference(str));
507 if (accumulate && outfp == stdout)
508 divert_to_temporary_file();
509 if (pending_line.length() == 0) {
510 warning("can't attach citation to previous line");
513 pending_line.set_length(pending_line.length() - 1);
515 if (move_punctuation)
516 split_punct(pending_line, punct);
517 int have_text = pre.length() > 0 || post.length() > 0;
518 label_type lt = label_type(flags & ~(FORCE_LEFT_BRACKET
519 |FORCE_RIGHT_BRACKET));
520 if ((flags & FORCE_LEFT_BRACKET) || !have_text)
521 pending_line += PRE_LABEL_MARKER;
523 char lm = LABEL_MARKER + (int)lt;
525 pending_line += post;
526 if ((flags & FORCE_RIGHT_BRACKET) || !have_text)
527 pending_line += POST_LABEL_MARKER;
528 pending_line += punct;
529 pending_line += '\n';
535 && line[0] == '.' && line[1] == 'l' && line[2] == 'f'
536 && (compatible_flag || line[3] == '\n' || line[3] == ' ')) {
537 pending_lf_lines += line;
539 if (interpret_lf_args(line.contents() + 3))
542 else if (recognize_R1_R2
544 && line[0] == '.' && line[1] == 'R' && line[2] == '1'
545 && (compatible_flag || line[3] == '\n' || line[3] == ' ')) {
547 int start_of_line = 1;
548 int start_lineno = current_lineno;
551 if (c != EOF && start_of_line)
553 if (start_of_line && c == '.') {
559 if (compatible_flag || c == ' ' || c == '\n' || c == EOF) {
560 while (c != EOF && c != '\n')
579 error_with_file_and_line(current_filename, start_lineno,
580 "missing `.R2' line");
583 if (invalid_input_char(c))
584 error("invalid input character code %1", int(c));
587 start_of_line = c == '\n';
590 output_pending_line();
595 process_commands(line, current_filename, start_lineno + 1);
599 output_pending_line();
604 output_pending_line();
609 class label_processing_state {
614 PENDING_LABEL_POST_PRE,
617 label_type type; // type of pending labels
618 int count; // number of pending labels
619 reference **rptr; // pointer to next reference
620 int rcount; // number of references left
622 int handle_pending(int c);
624 label_processing_state(reference **, int, FILE *);
625 ~label_processing_state();
629 static void output_pending_line()
631 if (label_in_text && !accumulate && ncitations > 0) {
632 label_processing_state state(citation, ncitations, outfp);
633 int len = pending_line.length();
634 for (int i = 0; i < len; i++)
635 state.process((unsigned char)(pending_line[i]));
638 put_string(pending_line, outfp);
639 pending_line.clear();
640 if (pending_lf_lines.length() > 0) {
641 put_string(pending_lf_lines, outfp);
642 pending_lf_lines.clear();
645 immediately_output_references();
647 fprintf(outfp, ".lf %d %s\n", current_lineno, current_filename);
652 static void split_punct(string &line, string &punct)
654 const char *start = line.contents();
655 const char *end = start + line.length();
656 const char *ptr = start;
657 const char *last_token_start = 0;
661 last_token_start = ptr;
662 if (*ptr == PRE_LABEL_MARKER || *ptr == POST_LABEL_MARKER
663 || (*ptr >= LABEL_MARKER && *ptr < LABEL_MARKER + N_LABEL_TYPES))
665 else if (!get_token(&ptr, end))
668 if (last_token_start) {
669 const token_info *ti = lookup_token(last_token_start, end);
670 if (ti->is_punct()) {
671 punct.append(last_token_start, end - last_token_start);
672 line.set_length(last_token_start - start);
677 static void divert_to_temporary_file()
682 static void store_citation(reference *ref)
684 if (ncitations >= citation_max) {
686 citation = new reference*[citation_max = 100];
688 reference **old_citation = citation;
690 citation = new reference *[citation_max];
691 memcpy(citation, old_citation, ncitations*sizeof(reference *));
692 a_delete old_citation;
695 citation[ncitations++] = ref;
698 static unsigned store_reference(const string &str)
700 if (reference_hash_table == 0) {
701 reference_hash_table = new reference *[17];
702 hash_table_size = 17;
703 for (int i = 0; i < hash_table_size; i++)
704 reference_hash_table[i] = 0;
707 reference *ref = make_reference(str, &flags);
708 ref->compute_hash_code();
709 unsigned h = ref->hash();
711 for (ptr = reference_hash_table + (h % hash_table_size);
713 ((ptr == reference_hash_table)
714 ? (ptr = reference_hash_table + hash_table_size - 1)
716 if (same_reference(**ptr, *ref))
719 if (ref->is_merged())
720 warning("fields ignored because reference already used");
726 ref->set_number(nreferences);
728 ref->pre_compute_label();
729 ref->compute_sort_key();
730 if (nreferences*2 >= hash_table_size) {
732 reference **old_table = reference_hash_table;
733 int old_size = hash_table_size;
734 hash_table_size = next_size(hash_table_size);
735 reference_hash_table = new reference*[hash_table_size];
737 for (i = 0; i < hash_table_size; i++)
738 reference_hash_table[i] = 0;
739 for (i = 0; i < old_size; i++)
742 for (p = (reference_hash_table
743 + (old_table[i]->hash() % hash_table_size));
745 ((p == reference_hash_table)
746 ? (p = reference_hash_table + hash_table_size - 1)
759 unsigned immediately_handle_reference(const string &str)
762 reference *ref = make_reference(str, &flags);
763 ref->set_number(nreferences);
764 if (label_in_text || label_in_reference) {
765 ref->pre_compute_label();
766 ref->immediate_compute_label();
773 static void immediately_output_references()
775 for (int i = 0; i < ncitations; i++) {
776 reference *ref = citation[i];
777 if (label_in_reference) {
778 fputs(".ds [F ", outfp);
779 const string &label = ref->get_label(NORMAL_LABEL);
780 if (label.length() > 0
781 && (label[0] == ' ' || label[0] == '\\' || label[0] == '"'))
783 put_string(label, outfp);
792 static void output_citation_group(reference **v, int n, label_type type,
795 if (sort_adjacent_labels) {
796 // Do an insertion sort. Usually n will be very small.
797 for (int i = 1; i < n; i++) {
798 int num = v[i]->get_number();
799 reference *temp = v[i];
801 for (j = i - 1; j >= 0 && v[j]->get_number() > num; j--)
806 // This messes up if !accumulate.
807 if (accumulate && n > 1) {
810 for (int i = 1; i < n; i++)
811 if (v[i]->get_label(type) != v[i - 1]->get_label(type))
816 for (int i = 0; i < n; i++) {
817 int nmerged = v[i]->merge_labels(v + i + 1, n - i - 1, type, merged_label);
819 put_string(merged_label, fp);
823 put_string(v[i]->get_label(type), fp);
825 put_string(sep_label, fp);
830 label_processing_state::label_processing_state(reference **p, int n, FILE *f)
831 : state(NORMAL), count(0), rptr(p), rcount(n), fp(f)
835 label_processing_state::~label_processing_state()
837 int handled = handle_pending(EOF);
842 int label_processing_state::handle_pending(int c)
848 if (c == POST_LABEL_MARKER) {
849 state = PENDING_LABEL_POST;
853 output_citation_group(rptr, count, type, fp);
859 case PENDING_LABEL_POST:
860 if (c == PRE_LABEL_MARKER) {
861 state = PENDING_LABEL_POST_PRE;
865 output_citation_group(rptr, count, type, fp);
868 put_string(post_label, fp);
872 case PENDING_LABEL_POST_PRE:
873 if (c >= LABEL_MARKER
874 && c < LABEL_MARKER + N_LABEL_TYPES
875 && c - LABEL_MARKER == type) {
877 state = PENDING_LABEL;
881 output_citation_group(rptr, count, type, fp);
884 put_string(sep_label, fp);
889 if (c == PRE_LABEL_MARKER) {
890 put_string(sep_label, fp);
895 put_string(post_label, fp);
903 void label_processing_state::process(int c)
905 if (handle_pending(c))
907 assert(state == NORMAL);
909 case PRE_LABEL_MARKER:
910 put_string(pre_label, fp);
913 case POST_LABEL_MARKER:
914 state = PENDING_POST;
917 case LABEL_MARKER + 1:
919 state = PENDING_LABEL;
920 type = label_type(c - LABEL_MARKER);
931 int rcompare(const void *p1, const void *p2)
933 return compare_reference(**(reference **)p1, **(reference **)p2);
938 void output_references()
941 if (!hash_table_size) {
942 if (have_bibliography)
943 error("nothing to reference (probably `bibliography' before `sort')");
948 if (nreferences > 0) {
951 for (i = 0; i < hash_table_size; i++)
952 if (reference_hash_table[i] != 0)
953 reference_hash_table[j++] = reference_hash_table[i];
954 assert(j == nreferences);
955 for (; j < hash_table_size; j++)
956 reference_hash_table[j] = 0;
957 qsort(reference_hash_table, nreferences, sizeof(reference*), rcompare);
958 for (i = 0; i < nreferences; i++)
959 reference_hash_table[i]->set_number(i);
960 compute_labels(reference_hash_table, nreferences);
962 if (outfp != stdout) {
965 label_processing_state state(citation, ncitations, stdout);
967 while ((c = getc(outfp)) != EOF)
974 if (nreferences > 0) {
975 fputs(".]<\n", outfp);
976 for (int i = 0; i < nreferences; i++) {
977 if (sort_fields.length() > 0)
978 reference_hash_table[i]->print_sort_key_comment(outfp);
979 if (label_in_reference) {
980 fputs(".ds [F ", outfp);
981 const string &label = reference_hash_table[i]->get_label(NORMAL_LABEL);
982 if (label.length() > 0
983 && (label[0] == ' ' || label[0] == '\\' || label[0] == '"'))
985 put_string(label, outfp);
988 reference_hash_table[i]->output(outfp);
989 delete reference_hash_table[i];
990 reference_hash_table[i] = 0;
992 fputs(".]>\n", outfp);
998 static reference *find_reference(const char *query, int query_len)
1000 // This is so that error messages look better.
1001 while (query_len > 0 && csspace(query[query_len - 1]))
1004 for (int i = 0; i < query_len; i++)
1005 str += query[i] == '\n' ? ' ' : query[i];
1007 possibly_load_default_database();
1008 search_list_iterator iter(&database_list, str.contents());
1012 if (!iter.next(&start, &len, &rid)) {
1013 error("no matches for `%1'", str.contents());
1016 const char *end = start + len;
1017 while (start < end) {
1020 while (start < end && *start++ != '\n')
1024 error("found a reference for `%1' but it didn't contain any fields",
1028 reference *result = new reference(start, end - start, &rid);
1029 if (iter.next(&start, &len, &rid))
1030 warning("multiple matches for `%1'", str.contents());
1034 static reference *make_reference(const string &str, unsigned *flagsp)
1036 const char *start = str.contents();
1037 const char *end = start + str.length();
1038 const char *ptr = start;
1042 while (ptr < end && *ptr++ != '\n')
1046 for (; start < ptr; start++) {
1048 *flagsp = (SHORT_LABEL | (*flagsp & (FORCE_RIGHT_BRACKET
1049 | FORCE_LEFT_BRACKET)));
1050 else if (*start == '[')
1051 *flagsp |= FORCE_LEFT_BRACKET;
1052 else if (*start == ']')
1053 *flagsp |= FORCE_RIGHT_BRACKET;
1054 else if (!csspace(*start))
1058 error("empty reference");
1059 return new reference;
1061 reference *database_ref = 0;
1063 database_ref = find_reference(start, ptr - start);
1064 reference *inline_ref = 0;
1066 inline_ref = new reference(ptr, end - ptr);
1069 database_ref->merge(*inline_ref);
1071 return database_ref;
1076 else if (database_ref)
1077 return database_ref;
1079 return new reference;
1082 static void do_ref(const string &str)
1085 (void)store_reference(str);
1087 (void)immediately_handle_reference(str);
1088 immediately_output_references();
1092 static void trim_blanks(string &str)
1094 const char *start = str.contents();
1095 const char *end = start + str.length();
1096 while (end > start && end[-1] != '\n' && csspace(end[-1]))
1098 str.set_length(end - start);
1101 void do_bib(const char *filename)
1104 if (strcmp(filename, "-") == 0)
1108 fp = fopen(filename, "r");
1110 error("can't open `%1': %2", filename, strerror(errno));
1113 current_filename = filename;
1116 START, MIDDLE, BODY, BODY_START, BODY_BLANK, BODY_DOT
1123 if (invalid_input_char(c)) {
1124 error("invalid input character code %1", c);
1152 else if (csspace(c)) {
1167 else if (csspace(c))
1182 state = c == '\n' ? BODY_START : BODY;
1211 // from the Dragon Book
1213 unsigned hash_string(const char *s, int len)
1215 const char *end = s + len;
1220 if ((g = h & 0xf0000000) != 0) {
1228 int next_size(int n)
1230 static const int table_sizes[] = {
1231 101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009,
1232 80021, 160001, 500009, 1000003, 2000003, 4000037, 8000009,
1233 16000057, 32000011, 64000031, 128000003, 0
1237 for (p = table_sizes; *p <= n && *p != 0; p++)