command.cpp revision 151497
1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2004 3114402Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22114402Sru#include "refer.h" 23114402Sru#include "refid.h" 24114402Sru#include "search.h" 25114402Sru#include "command.h" 26114402Sru 27114402Srucset cs_field_name = csalpha; 28114402Sru 29114402Sruclass input_item { 30114402Sru input_item *next; 31114402Sru char *filename; 32114402Sru int first_lineno; 33114402Sru string buffer; 34114402Sru const char *ptr; 35114402Sru const char *end; 36114402Srupublic: 37114402Sru input_item(string &, const char *, int = 1); 38114402Sru ~input_item(); 39114402Sru int get_char(); 40114402Sru int peek_char(); 41114402Sru void skip_char(); 42114402Sru int get_location(const char **, int *); 43114402Sru 44114402Sru friend class input_stack; 45114402Sru}; 46114402Sru 47114402Sruinput_item::input_item(string &s, const char *fn, int ln) 48114402Sru: filename(strsave(fn)), first_lineno(ln) 49114402Sru{ 50114402Sru buffer.move(s); 51114402Sru ptr = buffer.contents(); 52114402Sru end = ptr + buffer.length(); 53114402Sru} 54114402Sru 55114402Sruinput_item::~input_item() 56114402Sru{ 57114402Sru a_delete filename; 58114402Sru} 59114402Sru 60114402Sruinline int input_item::peek_char() 61114402Sru{ 62114402Sru if (ptr >= end) 63114402Sru return EOF; 64114402Sru else 65114402Sru return (unsigned char)*ptr; 66114402Sru} 67114402Sru 68114402Sruinline int input_item::get_char() 69114402Sru{ 70114402Sru if (ptr >= end) 71114402Sru return EOF; 72114402Sru else 73114402Sru return (unsigned char)*ptr++; 74114402Sru} 75114402Sru 76114402Sruinline void input_item::skip_char() 77114402Sru{ 78114402Sru ptr++; 79114402Sru} 80114402Sru 81114402Sruint input_item::get_location(const char **filenamep, int *linenop) 82114402Sru{ 83114402Sru *filenamep = filename; 84114402Sru if (ptr == buffer.contents()) 85114402Sru *linenop = first_lineno; 86114402Sru else { 87114402Sru int ln = first_lineno; 88114402Sru const char *e = ptr - 1; 89114402Sru for (const char *p = buffer.contents(); p < e; p++) 90114402Sru if (*p == '\n') 91114402Sru ln++; 92114402Sru *linenop = ln; 93114402Sru } 94114402Sru return 1; 95114402Sru} 96114402Sru 97114402Sruclass input_stack { 98114402Sru static input_item *top; 99114402Srupublic: 100114402Sru static void init(); 101114402Sru static int get_char(); 102114402Sru static int peek_char(); 103114402Sru static void skip_char() { top->skip_char(); } 104114402Sru static void push_file(const char *); 105114402Sru static void push_string(string &, const char *, int); 106114402Sru static void error(const char *format, 107114402Sru const errarg &arg1 = empty_errarg, 108114402Sru const errarg &arg2 = empty_errarg, 109114402Sru const errarg &arg3 = empty_errarg); 110114402Sru}; 111114402Sru 112114402Sruinput_item *input_stack::top = 0; 113114402Sru 114114402Sruvoid input_stack::init() 115114402Sru{ 116114402Sru while (top) { 117114402Sru input_item *tem = top; 118114402Sru top = top->next; 119114402Sru delete tem; 120114402Sru } 121114402Sru} 122114402Sru 123114402Sruint input_stack::get_char() 124114402Sru{ 125114402Sru while (top) { 126114402Sru int c = top->get_char(); 127114402Sru if (c >= 0) 128114402Sru return c; 129114402Sru input_item *tem = top; 130114402Sru top = top->next; 131114402Sru delete tem; 132114402Sru } 133114402Sru return -1; 134114402Sru} 135114402Sru 136114402Sruint input_stack::peek_char() 137114402Sru{ 138114402Sru while (top) { 139114402Sru int c = top->peek_char(); 140114402Sru if (c >= 0) 141114402Sru return c; 142114402Sru input_item *tem = top; 143114402Sru top = top->next; 144114402Sru delete tem; 145114402Sru } 146114402Sru return -1; 147114402Sru} 148114402Sru 149114402Sruvoid input_stack::push_file(const char *fn) 150114402Sru{ 151114402Sru FILE *fp; 152114402Sru if (strcmp(fn, "-") == 0) { 153114402Sru fp = stdin; 154114402Sru fn = "<standard input>"; 155114402Sru } 156114402Sru else { 157114402Sru errno = 0; 158114402Sru fp = fopen(fn, "r"); 159114402Sru if (fp == 0) { 160114402Sru error("can't open `%1': %2", fn, strerror(errno)); 161114402Sru return; 162114402Sru } 163114402Sru } 164114402Sru string buf; 165114402Sru int bol = 1; 166114402Sru int lineno = 1; 167114402Sru for (;;) { 168114402Sru int c = getc(fp); 169114402Sru if (bol && c == '.') { 170114402Sru // replace lines beginning with .R1 or .R2 with a blank line 171114402Sru c = getc(fp); 172114402Sru if (c == 'R') { 173114402Sru c = getc(fp); 174114402Sru if (c == '1' || c == '2') { 175114402Sru int cc = c; 176114402Sru c = getc(fp); 177114402Sru if (compatible_flag || c == ' ' || c == '\n' || c == EOF) { 178114402Sru while (c != '\n' && c != EOF) 179114402Sru c = getc(fp); 180114402Sru } 181114402Sru else { 182114402Sru buf += '.'; 183114402Sru buf += 'R'; 184114402Sru buf += cc; 185114402Sru } 186114402Sru } 187114402Sru else { 188114402Sru buf += '.'; 189114402Sru buf += 'R'; 190114402Sru } 191114402Sru } 192114402Sru else 193114402Sru buf += '.'; 194114402Sru } 195114402Sru if (c == EOF) 196114402Sru break; 197114402Sru if (invalid_input_char(c)) 198114402Sru error_with_file_and_line(fn, lineno, 199114402Sru "invalid input character code %1", int(c)); 200114402Sru else { 201114402Sru buf += c; 202114402Sru if (c == '\n') { 203114402Sru bol = 1; 204114402Sru lineno++; 205114402Sru } 206114402Sru else 207114402Sru bol = 0; 208114402Sru } 209114402Sru } 210114402Sru if (fp != stdin) 211114402Sru fclose(fp); 212114402Sru if (buf.length() > 0 && buf[buf.length() - 1] != '\n') 213114402Sru buf += '\n'; 214114402Sru input_item *it = new input_item(buf, fn); 215114402Sru it->next = top; 216114402Sru top = it; 217114402Sru} 218114402Sru 219114402Sruvoid input_stack::push_string(string &s, const char *filename, int lineno) 220114402Sru{ 221114402Sru input_item *it = new input_item(s, filename, lineno); 222114402Sru it->next = top; 223114402Sru top = it; 224114402Sru} 225114402Sru 226114402Sruvoid input_stack::error(const char *format, const errarg &arg1, 227114402Sru const errarg &arg2, const errarg &arg3) 228114402Sru{ 229114402Sru const char *filename; 230114402Sru int lineno; 231114402Sru for (input_item *it = top; it; it = it->next) 232114402Sru if (it->get_location(&filename, &lineno)) { 233114402Sru error_with_file_and_line(filename, lineno, format, arg1, arg2, arg3); 234114402Sru return; 235114402Sru } 236114402Sru ::error(format, arg1, arg2, arg3); 237114402Sru} 238114402Sru 239114402Sruvoid command_error(const char *format, const errarg &arg1, 240114402Sru const errarg &arg2, const errarg &arg3) 241114402Sru{ 242114402Sru input_stack::error(format, arg1, arg2, arg3); 243114402Sru} 244114402Sru 245114402Sru// # not recognized in "" 246114402Sru// \<newline> is recognized in "" 247114402Sru// # does not conceal newline 248114402Sru// if missing closing quote, word extends to end of line 249114402Sru// no special treatment of \ other than before newline 250114402Sru// \<newline> not recognized after # 251114402Sru// ; allowed as alternative to newline 252114402Sru// ; not recognized in "" 253114402Sru// don't clear word_buffer; just append on 254114402Sru// return -1 for EOF, 0 for newline, 1 for word 255114402Sru 256114402Sruint get_word(string &word_buffer) 257114402Sru{ 258114402Sru int c = input_stack::get_char(); 259114402Sru for (;;) { 260114402Sru if (c == '#') { 261114402Sru do { 262114402Sru c = input_stack::get_char(); 263114402Sru } while (c != '\n' && c != EOF); 264114402Sru break; 265114402Sru } 266114402Sru if (c == '\\' && input_stack::peek_char() == '\n') 267114402Sru input_stack::skip_char(); 268114402Sru else if (c != ' ' && c != '\t') 269114402Sru break; 270114402Sru c = input_stack::get_char(); 271114402Sru } 272114402Sru if (c == EOF) 273114402Sru return -1; 274114402Sru if (c == '\n' || c == ';') 275114402Sru return 0; 276114402Sru if (c == '"') { 277114402Sru for (;;) { 278114402Sru c = input_stack::peek_char(); 279114402Sru if (c == EOF || c == '\n') 280114402Sru break; 281114402Sru input_stack::skip_char(); 282114402Sru if (c == '"') { 283114402Sru int d = input_stack::peek_char(); 284114402Sru if (d == '"') 285114402Sru input_stack::skip_char(); 286114402Sru else 287114402Sru break; 288114402Sru } 289114402Sru else if (c == '\\') { 290114402Sru int d = input_stack::peek_char(); 291114402Sru if (d == '\n') 292114402Sru input_stack::skip_char(); 293114402Sru else 294114402Sru word_buffer += '\\'; 295114402Sru } 296114402Sru else 297114402Sru word_buffer += c; 298114402Sru } 299114402Sru return 1; 300114402Sru } 301114402Sru word_buffer += c; 302114402Sru for (;;) { 303114402Sru c = input_stack::peek_char(); 304114402Sru if (c == ' ' || c == '\t' || c == '\n' || c == '#' || c == ';') 305114402Sru break; 306114402Sru input_stack::skip_char(); 307114402Sru if (c == '\\') { 308114402Sru int d = input_stack::peek_char(); 309114402Sru if (d == '\n') 310114402Sru input_stack::skip_char(); 311114402Sru else 312114402Sru word_buffer += '\\'; 313114402Sru } 314114402Sru else 315114402Sru word_buffer += c; 316114402Sru } 317114402Sru return 1; 318114402Sru} 319114402Sru 320114402Sruunion argument { 321114402Sru const char *s; 322114402Sru int n; 323114402Sru}; 324114402Sru 325114402Sru// This is for debugging. 326114402Sru 327114402Srustatic void echo_command(int argc, argument *argv) 328114402Sru{ 329114402Sru for (int i = 0; i < argc; i++) 330114402Sru fprintf(stderr, "%s\n", argv[i].s); 331114402Sru} 332114402Sru 333114402Srustatic void include_command(int argc, argument *argv) 334114402Sru{ 335114402Sru assert(argc == 1); 336114402Sru input_stack::push_file(argv[0].s); 337114402Sru} 338114402Sru 339114402Srustatic void capitalize_command(int argc, argument *argv) 340114402Sru{ 341114402Sru if (argc > 0) 342114402Sru capitalize_fields = argv[0].s; 343114402Sru else 344114402Sru capitalize_fields.clear(); 345114402Sru} 346114402Sru 347114402Srustatic void accumulate_command(int, argument *) 348114402Sru{ 349114402Sru accumulate = 1; 350114402Sru} 351114402Sru 352114402Srustatic void no_accumulate_command(int, argument *) 353114402Sru{ 354114402Sru accumulate = 0; 355114402Sru} 356114402Sru 357114402Srustatic void move_punctuation_command(int, argument *) 358114402Sru{ 359114402Sru move_punctuation = 1; 360114402Sru} 361114402Sru 362114402Srustatic void no_move_punctuation_command(int, argument *) 363114402Sru{ 364114402Sru move_punctuation = 0; 365114402Sru} 366114402Sru 367114402Srustatic void sort_command(int argc, argument *argv) 368114402Sru{ 369114402Sru if (argc == 0) 370114402Sru sort_fields = "AD"; 371114402Sru else 372114402Sru sort_fields = argv[0].s; 373114402Sru accumulate = 1; 374114402Sru} 375114402Sru 376114402Srustatic void no_sort_command(int, argument *) 377114402Sru{ 378114402Sru sort_fields.clear(); 379114402Sru} 380114402Sru 381114402Srustatic void articles_command(int argc, argument *argv) 382114402Sru{ 383114402Sru articles.clear(); 384114402Sru int i; 385114402Sru for (i = 0; i < argc; i++) { 386114402Sru articles += argv[i].s; 387114402Sru articles += '\0'; 388114402Sru } 389114402Sru int len = articles.length(); 390114402Sru for (i = 0; i < len; i++) 391114402Sru articles[i] = cmlower(articles[i]); 392114402Sru} 393114402Sru 394114402Srustatic void database_command(int argc, argument *argv) 395114402Sru{ 396114402Sru for (int i = 0; i < argc; i++) 397114402Sru database_list.add_file(argv[i].s); 398114402Sru} 399114402Sru 400114402Srustatic void default_database_command(int, argument *) 401114402Sru{ 402114402Sru search_default = 1; 403114402Sru} 404114402Sru 405114402Srustatic void no_default_database_command(int, argument *) 406114402Sru{ 407114402Sru search_default = 0; 408114402Sru} 409114402Sru 410114402Srustatic void bibliography_command(int argc, argument *argv) 411114402Sru{ 412114402Sru const char *saved_filename = current_filename; 413114402Sru int saved_lineno = current_lineno; 414114402Sru int saved_label_in_text = label_in_text; 415114402Sru label_in_text = 0; 416114402Sru if (!accumulate) 417114402Sru fputs(".]<\n", stdout); 418114402Sru for (int i = 0; i < argc; i++) 419114402Sru do_bib(argv[i].s); 420114402Sru if (accumulate) 421114402Sru output_references(); 422114402Sru else 423114402Sru fputs(".]>\n", stdout); 424114402Sru current_filename = saved_filename; 425114402Sru current_lineno = saved_lineno; 426114402Sru label_in_text = saved_label_in_text; 427114402Sru} 428114402Sru 429114402Srustatic void annotate_command(int argc, argument *argv) 430114402Sru{ 431114402Sru if (argc > 0) 432114402Sru annotation_field = argv[0].s[0]; 433114402Sru else 434114402Sru annotation_field = 'X'; 435114402Sru if (argc == 2) 436114402Sru annotation_macro = argv[1].s; 437114402Sru else 438114402Sru annotation_macro = "AP"; 439114402Sru} 440114402Sru 441114402Srustatic void no_annotate_command(int, argument *) 442114402Sru{ 443114402Sru annotation_macro.clear(); 444114402Sru annotation_field = -1; 445114402Sru} 446114402Sru 447114402Srustatic void reverse_command(int, argument *argv) 448114402Sru{ 449114402Sru reverse_fields = argv[0].s; 450114402Sru} 451114402Sru 452114402Srustatic void no_reverse_command(int, argument *) 453114402Sru{ 454114402Sru reverse_fields.clear(); 455114402Sru} 456114402Sru 457114402Srustatic void abbreviate_command(int argc, argument *argv) 458114402Sru{ 459114402Sru abbreviate_fields = argv[0].s; 460114402Sru period_before_initial = argc > 1 ? argv[1].s : ". "; 461114402Sru period_before_last_name = argc > 2 ? argv[2].s : ". "; 462114402Sru period_before_other = argc > 3 ? argv[3].s : ". "; 463114402Sru period_before_hyphen = argc > 4 ? argv[4].s : "."; 464114402Sru} 465114402Sru 466114402Srustatic void no_abbreviate_command(int, argument *) 467114402Sru{ 468114402Sru abbreviate_fields.clear(); 469114402Sru} 470114402Sru 471114402Srustring search_ignore_fields; 472114402Sru 473114402Srustatic void search_ignore_command(int argc, argument *argv) 474114402Sru{ 475114402Sru if (argc > 0) 476114402Sru search_ignore_fields = argv[0].s; 477114402Sru else 478114402Sru search_ignore_fields = "XYZ"; 479114402Sru search_ignore_fields += '\0'; 480114402Sru linear_ignore_fields = search_ignore_fields.contents(); 481114402Sru} 482114402Sru 483114402Srustatic void no_search_ignore_command(int, argument *) 484114402Sru{ 485114402Sru linear_ignore_fields = ""; 486114402Sru} 487114402Sru 488114402Srustatic void search_truncate_command(int argc, argument *argv) 489114402Sru{ 490114402Sru if (argc > 0) 491114402Sru linear_truncate_len = argv[0].n; 492114402Sru else 493114402Sru linear_truncate_len = 6; 494114402Sru} 495114402Sru 496114402Srustatic void no_search_truncate_command(int, argument *) 497114402Sru{ 498114402Sru linear_truncate_len = -1; 499114402Sru} 500114402Sru 501114402Srustatic void discard_command(int argc, argument *argv) 502114402Sru{ 503114402Sru if (argc == 0) 504114402Sru discard_fields = "XYZ"; 505114402Sru else 506114402Sru discard_fields = argv[0].s; 507114402Sru accumulate = 1; 508114402Sru} 509114402Sru 510114402Srustatic void no_discard_command(int, argument *) 511114402Sru{ 512114402Sru discard_fields.clear(); 513114402Sru} 514114402Sru 515114402Srustatic void label_command(int, argument *argv) 516114402Sru{ 517114402Sru set_label_spec(argv[0].s); 518114402Sru} 519114402Sru 520114402Srustatic void abbreviate_label_ranges_command(int argc, argument *argv) 521114402Sru{ 522114402Sru abbreviate_label_ranges = 1; 523114402Sru label_range_indicator = argc > 0 ? argv[0].s : "-"; 524114402Sru} 525114402Sru 526114402Srustatic void no_abbreviate_label_ranges_command(int, argument *) 527114402Sru{ 528114402Sru abbreviate_label_ranges = 0; 529114402Sru} 530114402Sru 531114402Srustatic void label_in_reference_command(int, argument *) 532114402Sru{ 533114402Sru label_in_reference = 1; 534114402Sru} 535114402Sru 536114402Srustatic void no_label_in_reference_command(int, argument *) 537114402Sru{ 538114402Sru label_in_reference = 0; 539114402Sru} 540114402Sru 541114402Srustatic void label_in_text_command(int, argument *) 542114402Sru{ 543114402Sru label_in_text = 1; 544114402Sru} 545114402Sru 546114402Srustatic void no_label_in_text_command(int, argument *) 547114402Sru{ 548114402Sru label_in_text = 0; 549114402Sru} 550114402Sru 551114402Srustatic void sort_adjacent_labels_command(int, argument *) 552114402Sru{ 553114402Sru sort_adjacent_labels = 1; 554114402Sru} 555114402Sru 556114402Srustatic void no_sort_adjacent_labels_command(int, argument *) 557114402Sru{ 558114402Sru sort_adjacent_labels = 0; 559114402Sru} 560114402Sru 561114402Srustatic void date_as_label_command(int argc, argument *argv) 562114402Sru{ 563114402Sru if (set_date_label_spec(argc > 0 ? argv[0].s : "D%a*")) 564114402Sru date_as_label = 1; 565114402Sru} 566114402Sru 567114402Srustatic void no_date_as_label_command(int, argument *) 568114402Sru{ 569114402Sru date_as_label = 0; 570114402Sru} 571114402Sru 572114402Srustatic void short_label_command(int, argument *argv) 573114402Sru{ 574114402Sru if (set_short_label_spec(argv[0].s)) 575114402Sru short_label_flag = 1; 576114402Sru} 577114402Sru 578114402Srustatic void no_short_label_command(int, argument *) 579114402Sru{ 580114402Sru short_label_flag = 0; 581114402Sru} 582114402Sru 583114402Srustatic void compatible_command(int, argument *) 584114402Sru{ 585114402Sru compatible_flag = 1; 586114402Sru} 587114402Sru 588114402Srustatic void no_compatible_command(int, argument *) 589114402Sru{ 590114402Sru compatible_flag = 0; 591114402Sru} 592114402Sru 593114402Srustatic void join_authors_command(int argc, argument *argv) 594114402Sru{ 595114402Sru join_authors_exactly_two = argv[0].s; 596114402Sru join_authors_default = argc > 1 ? argv[1].s : argv[0].s; 597114402Sru join_authors_last_two = argc == 3 ? argv[2].s : argv[0].s; 598114402Sru} 599114402Sru 600114402Srustatic void bracket_label_command(int, argument *argv) 601114402Sru{ 602114402Sru pre_label = argv[0].s; 603114402Sru post_label = argv[1].s; 604114402Sru sep_label = argv[2].s; 605114402Sru} 606114402Sru 607114402Srustatic void separate_label_second_parts_command(int, argument *argv) 608114402Sru{ 609114402Sru separate_label_second_parts = argv[0].s; 610114402Sru} 611114402Sru 612114402Srustatic void et_al_command(int argc, argument *argv) 613114402Sru{ 614114402Sru et_al = argv[0].s; 615114402Sru et_al_min_elide = argv[1].n; 616114402Sru if (et_al_min_elide < 1) 617114402Sru et_al_min_elide = 1; 618114402Sru et_al_min_total = argc >= 3 ? argv[2].n : 0; 619114402Sru} 620114402Sru 621114402Srustatic void no_et_al_command(int, argument *) 622114402Sru{ 623114402Sru et_al.clear(); 624114402Sru et_al_min_elide = 0; 625114402Sru} 626114402Sru 627114402Srutypedef void (*command_t)(int, argument *); 628114402Sru 629114402Sru/* arg_types is a string describing the numbers and types of arguments. 630114402Srus means a string, i means an integer, f is a list of fields, F is 631114402Srua single field, 632114402Sru? means that the previous argument is optional, * means that the 633114402Sruprevious argument can occur any number of times. */ 634114402Sru 635151497Srustruct S { 636114402Sru const char *name; 637114402Sru command_t func; 638114402Sru const char *arg_types; 639114402Sru} command_table[] = { 640114402Sru { "include", include_command, "s" }, 641114402Sru { "echo", echo_command, "s*" }, 642114402Sru { "capitalize", capitalize_command, "f?" }, 643114402Sru { "accumulate", accumulate_command, "" }, 644114402Sru { "no-accumulate", no_accumulate_command, "" }, 645114402Sru { "move-punctuation", move_punctuation_command, "" }, 646114402Sru { "no-move-punctuation", no_move_punctuation_command, "" }, 647114402Sru { "sort", sort_command, "s?" }, 648114402Sru { "no-sort", no_sort_command, "" }, 649114402Sru { "articles", articles_command, "s*" }, 650114402Sru { "database", database_command, "ss*" }, 651114402Sru { "default-database", default_database_command, "" }, 652114402Sru { "no-default-database", no_default_database_command, "" }, 653114402Sru { "bibliography", bibliography_command, "ss*" }, 654114402Sru { "annotate", annotate_command, "F?s?" }, 655114402Sru { "no-annotate", no_annotate_command, "" }, 656114402Sru { "reverse", reverse_command, "s" }, 657114402Sru { "no-reverse", no_reverse_command, "" }, 658114402Sru { "abbreviate", abbreviate_command, "ss?s?s?s?" }, 659114402Sru { "no-abbreviate", no_abbreviate_command, "" }, 660114402Sru { "search-ignore", search_ignore_command, "f?" }, 661114402Sru { "no-search-ignore", no_search_ignore_command, "" }, 662114402Sru { "search-truncate", search_truncate_command, "i?" }, 663114402Sru { "no-search-truncate", no_search_truncate_command, "" }, 664114402Sru { "discard", discard_command, "f?" }, 665114402Sru { "no-discard", no_discard_command, "" }, 666114402Sru { "label", label_command, "s" }, 667114402Sru { "abbreviate-label-ranges", abbreviate_label_ranges_command, "s?" }, 668114402Sru { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command, "" }, 669114402Sru { "label-in-reference", label_in_reference_command, "" }, 670114402Sru { "no-label-in-reference", no_label_in_reference_command, "" }, 671114402Sru { "label-in-text", label_in_text_command, "" }, 672114402Sru { "no-label-in-text", no_label_in_text_command, "" }, 673114402Sru { "sort-adjacent-labels", sort_adjacent_labels_command, "" }, 674114402Sru { "no-sort-adjacent-labels", no_sort_adjacent_labels_command, "" }, 675114402Sru { "date-as-label", date_as_label_command, "s?" }, 676114402Sru { "no-date-as-label", no_date_as_label_command, "" }, 677114402Sru { "short-label", short_label_command, "s" }, 678114402Sru { "no-short-label", no_short_label_command, "" }, 679114402Sru { "compatible", compatible_command, "" }, 680114402Sru { "no-compatible", no_compatible_command, "" }, 681114402Sru { "join-authors", join_authors_command, "sss?" }, 682114402Sru { "bracket-label", bracket_label_command, "sss" }, 683114402Sru { "separate-label-second-parts", separate_label_second_parts_command, "s" }, 684114402Sru { "et-al", et_al_command, "sii?" }, 685114402Sru { "no-et-al", no_et_al_command, "" }, 686114402Sru}; 687114402Sru 688114402Srustatic int check_args(const char *types, const char *name, 689114402Sru int argc, argument *argv) 690114402Sru{ 691114402Sru int argno = 0; 692114402Sru while (*types) { 693114402Sru if (argc == 0) { 694114402Sru if (types[1] == '?') 695114402Sru break; 696114402Sru else if (types[1] == '*') { 697114402Sru assert(types[2] == '\0'); 698114402Sru break; 699114402Sru } 700114402Sru else { 701114402Sru input_stack::error("missing argument for command `%1'", name); 702114402Sru return 0; 703114402Sru } 704114402Sru } 705114402Sru switch (*types) { 706114402Sru case 's': 707114402Sru break; 708114402Sru case 'i': 709114402Sru { 710114402Sru char *ptr; 711114402Sru long n = strtol(argv->s, &ptr, 10); 712114402Sru if ((n == 0 && ptr == argv->s) 713114402Sru || *ptr != '\0') { 714114402Sru input_stack::error("argument %1 for command `%2' must be an integer", 715114402Sru argno + 1, name); 716114402Sru return 0; 717114402Sru } 718114402Sru argv->n = (int)n; 719114402Sru break; 720114402Sru } 721114402Sru case 'f': 722114402Sru { 723114402Sru for (const char *ptr = argv->s; *ptr != '\0'; ptr++) 724114402Sru if (!cs_field_name(*ptr)) { 725114402Sru input_stack::error("argument %1 for command `%2' must be a list of fields", 726114402Sru argno + 1, name); 727114402Sru return 0; 728114402Sru } 729114402Sru break; 730114402Sru } 731114402Sru case 'F': 732114402Sru if (argv->s[0] == '\0' || argv->s[1] != '\0' 733114402Sru || !cs_field_name(argv->s[0])) { 734114402Sru input_stack::error("argument %1 for command `%2' must be a field name", 735114402Sru argno + 1, name); 736114402Sru return 0; 737114402Sru } 738114402Sru break; 739114402Sru default: 740114402Sru assert(0); 741114402Sru } 742114402Sru if (types[1] == '?') 743114402Sru types += 2; 744114402Sru else if (types[1] != '*') 745114402Sru types += 1; 746114402Sru --argc; 747114402Sru ++argv; 748114402Sru ++argno; 749114402Sru } 750114402Sru if (argc > 0) { 751114402Sru input_stack::error("too many arguments for command `%1'", name); 752114402Sru return 0; 753114402Sru } 754114402Sru return 1; 755114402Sru} 756114402Sru 757114402Srustatic void execute_command(const char *name, int argc, argument *argv) 758114402Sru{ 759114402Sru for (unsigned int i = 0; 760114402Sru i < sizeof(command_table)/sizeof(command_table[0]); i++) 761114402Sru if (strcmp(name, command_table[i].name) == 0) { 762114402Sru if (check_args(command_table[i].arg_types, name, argc, argv)) 763114402Sru (*command_table[i].func)(argc, argv); 764114402Sru return; 765114402Sru } 766114402Sru input_stack::error("unknown command `%1'", name); 767114402Sru} 768114402Sru 769114402Srustatic void command_loop() 770114402Sru{ 771114402Sru string command; 772114402Sru for (;;) { 773114402Sru command.clear(); 774114402Sru int res = get_word(command); 775114402Sru if (res != 1) { 776114402Sru if (res == 0) 777114402Sru continue; 778114402Sru break; 779114402Sru } 780114402Sru int argc = 0; 781114402Sru command += '\0'; 782114402Sru while ((res = get_word(command)) == 1) { 783114402Sru argc++; 784114402Sru command += '\0'; 785114402Sru } 786114402Sru argument *argv = new argument[argc]; 787114402Sru const char *ptr = command.contents(); 788114402Sru for (int i = 0; i < argc; i++) 789114402Sru argv[i].s = ptr = strchr(ptr, '\0') + 1; 790114402Sru execute_command(command.contents(), argc, argv); 791114402Sru a_delete argv; 792114402Sru if (res == -1) 793114402Sru break; 794114402Sru } 795114402Sru} 796114402Sru 797114402Sruvoid process_commands(const char *file) 798114402Sru{ 799114402Sru input_stack::init(); 800114402Sru input_stack::push_file(file); 801114402Sru command_loop(); 802114402Sru} 803114402Sru 804114402Sruvoid process_commands(string &s, const char *file, int lineno) 805114402Sru{ 806114402Sru input_stack::init(); 807114402Sru input_stack::push_string(s, file, lineno); 808114402Sru command_loop(); 809114402Sru} 810