📄 refer.cc
字号:
// -*- C++ -*-/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. Written by James Clark (jjc@jclark.com)This file is part of groff.groff is free software; you can redistribute it and/or modify it underthe terms of the GNU General Public License as published by the FreeSoftware Foundation; either version 2, or (at your option) any laterversion.groff is distributed in the hope that it will be useful, but WITHOUT ANYWARRANTY; without even the implied warranty of MERCHANTABILITY orFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licensefor more details.You should have received a copy of the GNU General Public License alongwith groff; see the file COPYING. If not, write to the Free SoftwareFoundation, 675 Mass Ave, Cambridge, MA 02139, USA. */#include "refer.h"#include "refid.h"#include "ref.h"#include "token.h"#include "search.h"#include "command.h"const char PRE_LABEL_MARKER = '\013';const char POST_LABEL_MARKER = '\014';const char LABEL_MARKER = '\015'; // label_type is added on#define FORCE_LEFT_BRACKET 04#define FORCE_RIGHT_BRACKET 010static FILE *outfp = stdout;string capitalize_fields;string reverse_fields;string abbreviate_fields;string period_before_last_name = ". ";string period_before_initial = ".";string period_before_hyphen = "";string period_before_other = ". ";string sort_fields;int annotation_field = -1;string annotation_macro;string discard_fields = "XYZ";string pre_label = "\\*([.";string post_label = "\\*(.]";string sep_label = ", ";int accumulate = 0;int move_punctuation = 0;int abbreviate_label_ranges = 0;string label_range_indicator;int label_in_text = 1;int label_in_reference = 1;int date_as_label = 0;int sort_adjacent_labels = 0;// Join exactly two authors with this.string join_authors_exactly_two = " and ";// When there are more than two authors join the last two with this.string join_authors_last_two = ", and ";// Otherwise join authors with this.string join_authors_default = ", ";string separate_label_second_parts = ", ";// Use this string to represent that there are other authors.string et_al = " et al";// Use et al only if it can replace at least this many authors.int et_al_min_elide = 2;// Use et al only if the total number of authors is at least this.int et_al_min_total = 3;int compatible_flag = 0;int short_label_flag = 0;static int recognize_R1_R2 = 1;search_list database_list;int search_default = 1;static int default_database_loaded = 0;static reference **citation = 0;static int ncitations = 0;static int citation_max = 0;static reference **reference_hash_table = 0;static int hash_table_size;static int nreferences = 0;static int need_syncing = 0;string pending_line;string pending_lf_lines;static void output_pending_line();static unsigned immediately_handle_reference(const string &);static void immediately_output_references();static unsigned store_reference(const string &);static void divert_to_temporary_file();static reference *make_reference(const string &, unsigned *);static void usage();static void do_file(const char *);static void split_punct(string &line, string &punct);static void output_citation_group(reference **v, int n, label_type, FILE *fp);static void possibly_load_default_database();int main(int argc, char **argv){ program_name = argv[0]; static char stderr_buf[BUFSIZ]; setbuf(stderr, stderr_buf); outfp = stdout; int finished_options = 0; int bib_flag = 0; int done_spec = 0; for (--argc, ++argv; !finished_options && argc > 0 && argv[0][0] == '-' && argv[0][1] != '\0'; argv++, argc--) { const char *opt = argv[0] + 1; while (opt != 0 && *opt != '\0') { switch (*opt) { case 'C': compatible_flag = 1; opt++; break; case 'B': bib_flag = 1; label_in_reference = 0; label_in_text = 0; ++opt; if (*opt == '\0') { annotation_field = 'X'; annotation_macro = "AP"; } else if (csalnum(opt[0]) && opt[1] == '.' && opt[2] != '\0') { annotation_field = opt[0]; annotation_macro = opt + 2; } opt = 0; break; case 'P': move_punctuation = 1; opt++; break; case 'R': recognize_R1_R2 = 0; opt++; break; case 'S': // Not a very useful spec. set_label_spec("(A.n|Q)', '(D.y|D)"); done_spec = 1; pre_label = " ("; post_label = ")"; sep_label = "; "; opt++; break; case 'V': verify_flag = 1; opt++; break; case 'f': { const char *num = 0; if (*++opt == '\0') { if (argc > 1) { num = *++argv; --argc; } else { error("option `f' requires an argument"); usage(); } } else { num = opt; opt = 0; } for (const char *ptr = num; *ptr; ptr++) if (!csdigit(*ptr)) { error("bad character `%1' in argument to -f option", *ptr); break; } if (*ptr == '\0') { string spec; spec = '%'; spec += num; spec += '\0'; set_label_spec(spec.contents()); done_spec = 1; } break; } case 'b': label_in_text = 0; label_in_reference = 0; opt++; break; case 'e': accumulate = 1; opt++; break; case 'c': capitalize_fields = ++opt; opt = 0; break; case 'k': { char buf[5]; if (csalpha(*++opt)) buf[0] = *opt++; else { if (*opt != '\0') error("bad field name `%1'", *opt++); buf[0] = 'L'; } buf[1] = '~'; buf[2] = '%'; buf[3] = 'a'; buf[4] = '\0'; set_label_spec(buf); done_spec = 1; } break; case 'a': { for (const char *ptr = ++opt; *ptr; ptr++) if (!csdigit(*ptr)) { error("argument to `a' option not a number"); break; } if (*ptr == '\0') { reverse_fields = 'A'; reverse_fields += opt; } opt = 0; } break; case 'i': linear_ignore_fields = ++opt; opt = 0; break; case 'l': { char buf[INT_DIGITS*2 + 11]; // A.n+2D.y-3%a strcpy(buf, "A.n"); if (*++opt != '\0' && *opt != ',') { char *ptr; long n = strtol(opt, &ptr, 10); if (n == 0 && ptr == opt) { error("bad integer `%1' in `l' option", opt); opt = 0; break; } if (n < 0) n = 0; opt = ptr; sprintf(strchr(buf, '\0'), "+%d", n); } strcat(buf, "D.y"); if (*opt == ',') opt++; if (*opt != '\0') { char *ptr; long n = strtol(opt, &ptr, 10); if (n == 0 && ptr == opt) { error("bad integer `%1' in `l' option", opt); opt = 0; break; } if (n < 0) n = 0; sprintf(strchr(buf, '\0'), "-%d", n); opt = ptr; if (*opt != '\0') error("argument to `l' option not of form `m,n'"); } strcat(buf, "%a"); if (!set_label_spec(buf)) assert(0); done_spec = 1; } break; case 'n': search_default = 0; opt++; break; case 'p': { const char *filename = 0; if (*++opt == '\0') { if (argc > 1) { filename = *++argv; argc--; } else { error("option `p' requires an argument"); usage(); } } else { filename = opt; opt = 0; } database_list.add_file(filename); } break; case 's': if (*++opt == '\0') sort_fields = "AD"; else { sort_fields = opt; opt = 0; } accumulate = 1; break; case 't': { char *ptr; long n = strtol(opt, &ptr, 10); if (n == 0 && ptr == opt) { error("bad integer `%1' in `t' option", opt); opt = 0; break; } if (n < 1) n = 1; linear_truncate_len = int(n); opt = ptr; break; } case 'v': { extern const char *version_string; fprintf(stderr, "GNU refer version %s\n", version_string); fflush(stderr); opt++; break; } case '-': if (opt[1] == '\0') { finished_options = 1; opt++; break; } // fall through default: error("unrecognized option `%1'", *opt); usage(); break; } } } if (!done_spec) set_label_spec("%1"); if (argc <= 0) { if (bib_flag) do_bib("-"); else do_file("-"); } else { for (int i = 0; i < argc; i++) { if (bib_flag) do_bib(argv[i]); else do_file(argv[i]); } } if (accumulate) output_references(); if (fflush(stdout) < 0) fatal("output error"); exit(0);}static void usage(){ fprintf(stderr,"usage: %s [-benvCPRS] [-aN] [-cXYZ] [-fN] [-iXYZ] [-kX] [-lM,N] [-p file]\n"" [-sXYZ] [-tN] [-BL.M] [files ...]\n", program_name); exit(1);}static void possibly_load_default_database(){ if (search_default && !default_database_loaded) { char *filename = getenv("REFER"); if (filename) database_list.add_file(filename); else database_list.add_file(DEFAULT_INDEX, 1); default_database_loaded = 1; }}static int is_list(const string &str){ const char *start = str.contents(); const char *end = start + str.length(); while (end > start && csspace(end[-1])) end--; while (start < end && csspace(*start)) start++; return end - start == 6 && memcmp(start, "$LIST$", 6) == 0;}static void do_file(const char *filename){ FILE *fp; if (strcmp(filename, "-") == 0) { fp = stdin; } else { errno = 0; fp = fopen(filename, "r"); if (fp == 0) { error("can't open `%1': %2", filename, strerror(errno)); return; } current_filename = filename; } fprintf(outfp, ".lf 1 %s\n", filename); string line; current_lineno = 0; for (;;) { line.clear(); for (;;) { int c = getc(fp); if (c == EOF) { if (line.length() > 0) line += '\n'; break; } if (illegal_input_char(c)) error("illegal input character code %1", c); else { line += c; if (c == '\n') break; } } int len = line.length(); if (len == 0) break; current_lineno++; if (len >= 2 && line[0] == '.' && line[1] == '[') { int start_lineno = current_lineno; int start_of_line = 1; string str; string post; string pre(line.contents() + 2, line.length() - 3); for (;;) { int c = getc(fp); if (c == EOF) { error_with_file_and_line(current_filename, start_lineno, "missing `.]' line"); break; } if (start_of_line) current_lineno++; if (start_of_line && c == '.') { int d = getc(fp); if (d == ']') { while ((d = getc(fp)) != '\n' && d != EOF) { if (illegal_input_char(d)) error("illegal input character code %1", d); else post += d; } break; } if (d != EOF) ungetc(d, fp); } if (illegal_input_char(c)) error("illegal input character code %1", c); else str += c; start_of_line = (c == '\n'); } if (is_list(str)) { output_pending_line(); if (accumulate) output_references(); else error("found `$LIST$' but not accumulating references"); } else { unsigned flags = (accumulate ? store_reference(str) : immediately_handle_reference(str)); if (label_in_text) { if (accumulate && outfp == stdout) divert_to_temporary_file(); if (pending_line.length() == 0) { warning("can't attach citation to previous line"); } else pending_line.set_length(pending_line.length() - 1); string punct; if (move_punctuation) split_punct(pending_line, punct); int have_text = pre.length() > 0 || post.length() > 0; label_type lt = label_type(flags & ~(FORCE_LEFT_BRACKET |FORCE_RIGHT_BRACKET)); if ((flags & FORCE_LEFT_BRACKET) || !have_text) pending_line += PRE_LABEL_MARKER; pending_line += pre; pending_line += LABEL_MARKER + lt; pending_line += post; if ((flags & FORCE_RIGHT_BRACKET) || !have_text) pending_line += POST_LABEL_MARKER; pending_line += punct; pending_line += '\n'; } } need_syncing = 1; } else if (len >= 4 && line[0] == '.' && line[1] == 'l' && line[2] == 'f' && (compatible_flag || line[3] == '\n' || line[3] == ' ')) { pending_lf_lines += line; line += '\0'; if (interpret_lf_args(line.contents() + 3)) current_lineno--; } else if (recognize_R1_R2 && len >= 4 && line[0] == '.' && line[1] == 'R' && line[2] == '1' && (compatible_flag || line[3] == '\n' || line[3] == ' ')) { line.clear(); int start_of_line = 1; int start_lineno = current_lineno; for (;;) { int c = getc(fp); if (c != EOF && start_of_line) current_lineno++; if (start_of_line && c == '.') { c = getc(fp); if (c == 'R') { c = getc(fp); if (c == '2') { c = getc(fp); if (compatible_flag || c == ' ' || c == '\n' || c == EOF) { while (c != EOF && c != '\n') c = getc(fp); break; } else { line += '.'; line += 'R'; line += '2'; } } else { line += '.'; line += 'R'; } } else line += '.'; } if (c == EOF) { error_with_file_and_line(current_filename, start_lineno, "missing `.R2' line"); break; } if (illegal_input_char(c)) error("illegal input character code %1", int(c)); else { line += c; start_of_line = c == '\n'; } } output_pending_line(); if (accumulate) output_references(); else nreferences = 0; process_commands(line, current_filename, start_lineno + 1); need_syncing = 1; } else { output_pending_line(); pending_line = line; } } need_syncing = 0; output_pending_line(); if (fp != stdin) fclose(fp);}class label_processing_state { enum { NORMAL, PENDING_LABEL, PENDING_LABEL_POST, PENDING_LABEL_POST_PRE, PENDING_POST } state; label_type type; // type of pending labels int count; // number of pending labels reference **rptr; // pointer to next reference int rcount; // number of references left FILE *fp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -