| Commit | Line | Data |
|---|---|---|
| 92d0a6a6 | 1 | // -*- C++ -*- |
| 4d3e9548 | 2 | /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2004, 2006, 2009 |
| 92d0a6a6 JR |
3 | Free Software Foundation, Inc. |
| 4 | Written by James Clark (jjc@jclark.com) | |
| 5 | ||
| 6 | This file is part of groff. | |
| 7 | ||
| 8 | groff is free software; you can redistribute it and/or modify it under | |
| 9 | the terms of the GNU General Public License as published by the Free | |
| 4d3e9548 JL |
10 | Software Foundation, either version 3 of the License, or |
| 11 | (at your option) any later version. | |
| 92d0a6a6 JR |
12 | |
| 13 | groff is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 16 | for more details. | |
| 17 | ||
| 4d3e9548 JL |
18 | You should have received a copy of the GNU General Public License |
| 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| 92d0a6a6 JR |
20 | |
| 21 | #include "refer.h" | |
| 22 | #include "refid.h" | |
| 23 | #include "search.h" | |
| 24 | #include "command.h" | |
| 25 | ||
| 26 | cset cs_field_name = csalpha; | |
| 27 | ||
| 28 | class input_item { | |
| 29 | input_item *next; | |
| 30 | char *filename; | |
| 31 | int first_lineno; | |
| 32 | string buffer; | |
| 33 | const char *ptr; | |
| 34 | const char *end; | |
| 35 | public: | |
| 36 | input_item(string &, const char *, int = 1); | |
| 37 | ~input_item(); | |
| 38 | int get_char(); | |
| 39 | int peek_char(); | |
| 40 | void skip_char(); | |
| 41 | int get_location(const char **, int *); | |
| 42 | ||
| 43 | friend class input_stack; | |
| 44 | }; | |
| 45 | ||
| 46 | input_item::input_item(string &s, const char *fn, int ln) | |
| 47 | : filename(strsave(fn)), first_lineno(ln) | |
| 48 | { | |
| 49 | buffer.move(s); | |
| 50 | ptr = buffer.contents(); | |
| 51 | end = ptr + buffer.length(); | |
| 52 | } | |
| 53 | ||
| 54 | input_item::~input_item() | |
| 55 | { | |
| 56 | a_delete filename; | |
| 57 | } | |
| 58 | ||
| 59 | inline int input_item::peek_char() | |
| 60 | { | |
| 61 | if (ptr >= end) | |
| 62 | return EOF; | |
| 63 | else | |
| 64 | return (unsigned char)*ptr; | |
| 65 | } | |
| 66 | ||
| 67 | inline int input_item::get_char() | |
| 68 | { | |
| 69 | if (ptr >= end) | |
| 70 | return EOF; | |
| 71 | else | |
| 72 | return (unsigned char)*ptr++; | |
| 73 | } | |
| 74 | ||
| 75 | inline void input_item::skip_char() | |
| 76 | { | |
| 77 | ptr++; | |
| 78 | } | |
| 79 | ||
| 80 | int input_item::get_location(const char **filenamep, int *linenop) | |
| 81 | { | |
| 82 | *filenamep = filename; | |
| 83 | if (ptr == buffer.contents()) | |
| 84 | *linenop = first_lineno; | |
| 85 | else { | |
| 86 | int ln = first_lineno; | |
| 87 | const char *e = ptr - 1; | |
| 88 | for (const char *p = buffer.contents(); p < e; p++) | |
| 89 | if (*p == '\n') | |
| 90 | ln++; | |
| 91 | *linenop = ln; | |
| 92 | } | |
| 93 | return 1; | |
| 94 | } | |
| 95 | ||
| 96 | class input_stack { | |
| 97 | static input_item *top; | |
| 98 | public: | |
| 99 | static void init(); | |
| 100 | static int get_char(); | |
| 101 | static int peek_char(); | |
| 102 | static void skip_char() { top->skip_char(); } | |
| 103 | static void push_file(const char *); | |
| 104 | static void push_string(string &, const char *, int); | |
| 105 | static void error(const char *format, | |
| 106 | const errarg &arg1 = empty_errarg, | |
| 107 | const errarg &arg2 = empty_errarg, | |
| 108 | const errarg &arg3 = empty_errarg); | |
| 109 | }; | |
| 110 | ||
| 111 | input_item *input_stack::top = 0; | |
| 112 | ||
| 113 | void input_stack::init() | |
| 114 | { | |
| 115 | while (top) { | |
| 116 | input_item *tem = top; | |
| 117 | top = top->next; | |
| 118 | delete tem; | |
| 119 | } | |
| 120 | } | |
| 121 | ||
| 122 | int input_stack::get_char() | |
| 123 | { | |
| 124 | while (top) { | |
| 125 | int c = top->get_char(); | |
| 126 | if (c >= 0) | |
| 127 | return c; | |
| 128 | input_item *tem = top; | |
| 129 | top = top->next; | |
| 130 | delete tem; | |
| 131 | } | |
| 132 | return -1; | |
| 133 | } | |
| 134 | ||
| 135 | int input_stack::peek_char() | |
| 136 | { | |
| 137 | while (top) { | |
| 138 | int c = top->peek_char(); | |
| 139 | if (c >= 0) | |
| 140 | return c; | |
| 141 | input_item *tem = top; | |
| 142 | top = top->next; | |
| 143 | delete tem; | |
| 144 | } | |
| 145 | return -1; | |
| 146 | } | |
| 147 | ||
| 148 | void input_stack::push_file(const char *fn) | |
| 149 | { | |
| 150 | FILE *fp; | |
| 151 | if (strcmp(fn, "-") == 0) { | |
| 152 | fp = stdin; | |
| 153 | fn = "<standard input>"; | |
| 154 | } | |
| 155 | else { | |
| 156 | errno = 0; | |
| 157 | fp = fopen(fn, "r"); | |
| 158 | if (fp == 0) { | |
| 159 | error("can't open `%1': %2", fn, strerror(errno)); | |
| 160 | return; | |
| 161 | } | |
| 162 | } | |
| 163 | string buf; | |
| 164 | int bol = 1; | |
| 165 | int lineno = 1; | |
| 166 | for (;;) { | |
| 167 | int c = getc(fp); | |
| 168 | if (bol && c == '.') { | |
| 169 | // replace lines beginning with .R1 or .R2 with a blank line | |
| 170 | c = getc(fp); | |
| 171 | if (c == 'R') { | |
| 172 | c = getc(fp); | |
| 173 | if (c == '1' || c == '2') { | |
| 174 | int cc = c; | |
| 175 | c = getc(fp); | |
| 176 | if (compatible_flag || c == ' ' || c == '\n' || c == EOF) { | |
| 177 | while (c != '\n' && c != EOF) | |
| 178 | c = getc(fp); | |
| 179 | } | |
| 180 | else { | |
| 181 | buf += '.'; | |
| 182 | buf += 'R'; | |
| 183 | buf += cc; | |
| 184 | } | |
| 185 | } | |
| 186 | else { | |
| 187 | buf += '.'; | |
| 188 | buf += 'R'; | |
| 189 | } | |
| 190 | } | |
| 191 | else | |
| 192 | buf += '.'; | |
| 193 | } | |
| 194 | if (c == EOF) | |
| 195 | break; | |
| 196 | if (invalid_input_char(c)) | |
| 197 | error_with_file_and_line(fn, lineno, | |
| 198 | "invalid input character code %1", int(c)); | |
| 199 | else { | |
| 200 | buf += c; | |
| 201 | if (c == '\n') { | |
| 202 | bol = 1; | |
| 203 | lineno++; | |
| 204 | } | |
| 205 | else | |
| 206 | bol = 0; | |
| 207 | } | |
| 208 | } | |
| 209 | if (fp != stdin) | |
| 210 | fclose(fp); | |
| 211 | if (buf.length() > 0 && buf[buf.length() - 1] != '\n') | |
| 212 | buf += '\n'; | |
| 213 | input_item *it = new input_item(buf, fn); | |
| 214 | it->next = top; | |
| 215 | top = it; | |
| 216 | } | |
| 217 | ||
| 218 | void input_stack::push_string(string &s, const char *filename, int lineno) | |
| 219 | { | |
| 220 | input_item *it = new input_item(s, filename, lineno); | |
| 221 | it->next = top; | |
| 222 | top = it; | |
| 223 | } | |
| 224 | ||
| 225 | void input_stack::error(const char *format, const errarg &arg1, | |
| 226 | const errarg &arg2, const errarg &arg3) | |
| 227 | { | |
| 228 | const char *filename; | |
| 229 | int lineno; | |
| 230 | for (input_item *it = top; it; it = it->next) | |
| 231 | if (it->get_location(&filename, &lineno)) { | |
| 232 | error_with_file_and_line(filename, lineno, format, arg1, arg2, arg3); | |
| 233 | return; | |
| 234 | } | |
| 235 | ::error(format, arg1, arg2, arg3); | |
| 236 | } | |
| 237 | ||
| 238 | void command_error(const char *format, const errarg &arg1, | |
| 239 | const errarg &arg2, const errarg &arg3) | |
| 240 | { | |
| 241 | input_stack::error(format, arg1, arg2, arg3); | |
| 242 | } | |
| 243 | ||
| 244 | // # not recognized in "" | |
| 245 | // \<newline> is recognized in "" | |
| 246 | // # does not conceal newline | |
| 247 | // if missing closing quote, word extends to end of line | |
| 248 | // no special treatment of \ other than before newline | |
| 249 | // \<newline> not recognized after # | |
| 250 | // ; allowed as alternative to newline | |
| 251 | // ; not recognized in "" | |
| 252 | // don't clear word_buffer; just append on | |
| 253 | // return -1 for EOF, 0 for newline, 1 for word | |
| 254 | ||
| 255 | int get_word(string &word_buffer) | |
| 256 | { | |
| 257 | int c = input_stack::get_char(); | |
| 258 | for (;;) { | |
| 259 | if (c == '#') { | |
| 260 | do { | |
| 261 | c = input_stack::get_char(); | |
| 262 | } while (c != '\n' && c != EOF); | |
| 263 | break; | |
| 264 | } | |
| 265 | if (c == '\\' && input_stack::peek_char() == '\n') | |
| 266 | input_stack::skip_char(); | |
| 267 | else if (c != ' ' && c != '\t') | |
| 268 | break; | |
| 269 | c = input_stack::get_char(); | |
| 270 | } | |
| 271 | if (c == EOF) | |
| 272 | return -1; | |
| 273 | if (c == '\n' || c == ';') | |
| 274 | return 0; | |
| 275 | if (c == '"') { | |
| 276 | for (;;) { | |
| 277 | c = input_stack::peek_char(); | |
| 278 | if (c == EOF || c == '\n') | |
| 279 | break; | |
| 280 | input_stack::skip_char(); | |
| 281 | if (c == '"') { | |
| 282 | int d = input_stack::peek_char(); | |
| 283 | if (d == '"') | |
| 284 | input_stack::skip_char(); | |
| 285 | else | |
| 286 | break; | |
| 287 | } | |
| 288 | else if (c == '\\') { | |
| 289 | int d = input_stack::peek_char(); | |
| 290 | if (d == '\n') | |
| 291 | input_stack::skip_char(); | |
| 292 | else | |
| 293 | word_buffer += '\\'; | |
| 294 | } | |
| 295 | else | |
| 296 | word_buffer += c; | |
| 297 | } | |
| 298 | return 1; | |
| 299 | } | |
| 300 | word_buffer += c; | |
| 301 | for (;;) { | |
| 302 | c = input_stack::peek_char(); | |
| 303 | if (c == ' ' || c == '\t' || c == '\n' || c == '#' || c == ';') | |
| 304 | break; | |
| 305 | input_stack::skip_char(); | |
| 306 | if (c == '\\') { | |
| 307 | int d = input_stack::peek_char(); | |
| 308 | if (d == '\n') | |
| 309 | input_stack::skip_char(); | |
| 310 | else | |
| 311 | word_buffer += '\\'; | |
| 312 | } | |
| 313 | else | |
| 314 | word_buffer += c; | |
| 315 | } | |
| 316 | return 1; | |
| 317 | } | |
| 318 | ||
| 319 | union argument { | |
| 320 | const char *s; | |
| 321 | int n; | |
| 322 | }; | |
| 323 | ||
| 324 | // This is for debugging. | |
| 325 | ||
| 326 | static void echo_command(int argc, argument *argv) | |
| 327 | { | |
| 328 | for (int i = 0; i < argc; i++) | |
| 329 | fprintf(stderr, "%s\n", argv[i].s); | |
| 330 | } | |
| 331 | ||
| 332 | static void include_command(int argc, argument *argv) | |
| 333 | { | |
| 334 | assert(argc == 1); | |
| 335 | input_stack::push_file(argv[0].s); | |
| 336 | } | |
| 337 | ||
| 338 | static void capitalize_command(int argc, argument *argv) | |
| 339 | { | |
| 340 | if (argc > 0) | |
| 341 | capitalize_fields = argv[0].s; | |
| 342 | else | |
| 343 | capitalize_fields.clear(); | |
| 344 | } | |
| 345 | ||
| 346 | static void accumulate_command(int, argument *) | |
| 347 | { | |
| 348 | accumulate = 1; | |
| 349 | } | |
| 350 | ||
| 351 | static void no_accumulate_command(int, argument *) | |
| 352 | { | |
| 353 | accumulate = 0; | |
| 354 | } | |
| 355 | ||
| 356 | static void move_punctuation_command(int, argument *) | |
| 357 | { | |
| 358 | move_punctuation = 1; | |
| 359 | } | |
| 360 | ||
| 361 | static void no_move_punctuation_command(int, argument *) | |
| 362 | { | |
| 363 | move_punctuation = 0; | |
| 364 | } | |
| 365 | ||
| 366 | static void sort_command(int argc, argument *argv) | |
| 367 | { | |
| 368 | if (argc == 0) | |
| 369 | sort_fields = "AD"; | |
| 370 | else | |
| 371 | sort_fields = argv[0].s; | |
| 372 | accumulate = 1; | |
| 373 | } | |
| 374 | ||
| 375 | static void no_sort_command(int, argument *) | |
| 376 | { | |
| 377 | sort_fields.clear(); | |
| 378 | } | |
| 379 | ||
| 380 | static void articles_command(int argc, argument *argv) | |
| 381 | { | |
| 382 | articles.clear(); | |
| 383 | int i; | |
| 384 | for (i = 0; i < argc; i++) { | |
| 385 | articles += argv[i].s; | |
| 386 | articles += '\0'; | |
| 387 | } | |
| 388 | int len = articles.length(); | |
| 389 | for (i = 0; i < len; i++) | |
| 390 | articles[i] = cmlower(articles[i]); | |
| 391 | } | |
| 392 | ||
| 393 | static void database_command(int argc, argument *argv) | |
| 394 | { | |
| 395 | for (int i = 0; i < argc; i++) | |
| 396 | database_list.add_file(argv[i].s); | |
| 397 | } | |
| 398 | ||
| 399 | static void default_database_command(int, argument *) | |
| 400 | { | |
| 401 | search_default = 1; | |
| 402 | } | |
| 403 | ||
| 404 | static void no_default_database_command(int, argument *) | |
| 405 | { | |
| 406 | search_default = 0; | |
| 407 | } | |
| 408 | ||
| 409 | static void bibliography_command(int argc, argument *argv) | |
| 410 | { | |
| 4d3e9548 | 411 | have_bibliography = 1; |
| 92d0a6a6 JR |
412 | const char *saved_filename = current_filename; |
| 413 | int saved_lineno = current_lineno; | |
| 414 | int saved_label_in_text = label_in_text; | |
| 415 | label_in_text = 0; | |
| 416 | if (!accumulate) | |
| 417 | fputs(".]<\n", stdout); | |
| 418 | for (int i = 0; i < argc; i++) | |
| 419 | do_bib(argv[i].s); | |
| 420 | if (accumulate) | |
| 421 | output_references(); | |
| 422 | else | |
| 423 | fputs(".]>\n", stdout); | |
| 424 | current_filename = saved_filename; | |
| 425 | current_lineno = saved_lineno; | |
| 426 | label_in_text = saved_label_in_text; | |
| 427 | } | |
| 428 | ||
| 429 | static void annotate_command(int argc, argument *argv) | |
| 430 | { | |
| 431 | if (argc > 0) | |
| 432 | annotation_field = argv[0].s[0]; | |
| 433 | else | |
| 434 | annotation_field = 'X'; | |
| 435 | if (argc == 2) | |
| 436 | annotation_macro = argv[1].s; | |
| 437 | else | |
| 438 | annotation_macro = "AP"; | |
| 439 | } | |
| 440 | ||
| 441 | static void no_annotate_command(int, argument *) | |
| 442 | { | |
| 443 | annotation_macro.clear(); | |
| 444 | annotation_field = -1; | |
| 445 | } | |
| 446 | ||
| 447 | static void reverse_command(int, argument *argv) | |
| 448 | { | |
| 449 | reverse_fields = argv[0].s; | |
| 450 | } | |
| 451 | ||
| 452 | static void no_reverse_command(int, argument *) | |
| 453 | { | |
| 454 | reverse_fields.clear(); | |
| 455 | } | |
| 456 | ||
| 457 | static void abbreviate_command(int argc, argument *argv) | |
| 458 | { | |
| 459 | abbreviate_fields = argv[0].s; | |
| 460 | period_before_initial = argc > 1 ? argv[1].s : ". "; | |
| 461 | period_before_last_name = argc > 2 ? argv[2].s : ". "; | |
| 462 | period_before_other = argc > 3 ? argv[3].s : ". "; | |
| 463 | period_before_hyphen = argc > 4 ? argv[4].s : "."; | |
| 464 | } | |
| 465 | ||
| 466 | static void no_abbreviate_command(int, argument *) | |
| 467 | { | |
| 468 | abbreviate_fields.clear(); | |
| 469 | } | |
| 470 | ||
| 471 | string search_ignore_fields; | |
| 472 | ||
| 473 | static void search_ignore_command(int argc, argument *argv) | |
| 474 | { | |
| 475 | if (argc > 0) | |
| 476 | search_ignore_fields = argv[0].s; | |
| 477 | else | |
| 478 | search_ignore_fields = "XYZ"; | |
| 479 | search_ignore_fields += '\0'; | |
| 480 | linear_ignore_fields = search_ignore_fields.contents(); | |
| 481 | } | |
| 482 | ||
| 483 | static void no_search_ignore_command(int, argument *) | |
| 484 | { | |
| 485 | linear_ignore_fields = ""; | |
| 486 | } | |
| 487 | ||
| 488 | static void search_truncate_command(int argc, argument *argv) | |
| 489 | { | |
| 490 | if (argc > 0) | |
| 491 | linear_truncate_len = argv[0].n; | |
| 492 | else | |
| 493 | linear_truncate_len = 6; | |
| 494 | } | |
| 495 | ||
| 496 | static void no_search_truncate_command(int, argument *) | |
| 497 | { | |
| 498 | linear_truncate_len = -1; | |
| 499 | } | |
| 500 | ||
| 501 | static void discard_command(int argc, argument *argv) | |
| 502 | { | |
| 503 | if (argc == 0) | |
| 504 | discard_fields = "XYZ"; | |
| 505 | else | |
| 506 | discard_fields = argv[0].s; | |
| 507 | accumulate = 1; | |
| 508 | } | |
| 509 | ||
| 510 | static void no_discard_command(int, argument *) | |
| 511 | { | |
| 512 | discard_fields.clear(); | |
| 513 | } | |
| 514 | ||
| 515 | static void label_command(int, argument *argv) | |
| 516 | { | |
| 517 | set_label_spec(argv[0].s); | |
| 518 | } | |
| 519 | ||
| 520 | static void abbreviate_label_ranges_command(int argc, argument *argv) | |
| 521 | { | |
| 522 | abbreviate_label_ranges = 1; | |
| 523 | label_range_indicator = argc > 0 ? argv[0].s : "-"; | |
| 524 | } | |
| 525 | ||
| 526 | static void no_abbreviate_label_ranges_command(int, argument *) | |
| 527 | { | |
| 528 | abbreviate_label_ranges = 0; | |
| 529 | } | |
| 530 | ||
| 531 | static void label_in_reference_command(int, argument *) | |
| 532 | { | |
| 533 | label_in_reference = 1; | |
| 534 | } | |
| 535 | ||
| 536 | static void no_label_in_reference_command(int, argument *) | |
| 537 | { | |
| 538 | label_in_reference = 0; | |
| 539 | } | |
| 540 | ||
| 541 | static void label_in_text_command(int, argument *) | |
| 542 | { | |
| 543 | label_in_text = 1; | |
| 544 | } | |
| 545 | ||
| 546 | static void no_label_in_text_command(int, argument *) | |
| 547 | { | |
| 548 | label_in_text = 0; | |
| 549 | } | |
| 550 | ||
| 551 | static void sort_adjacent_labels_command(int, argument *) | |
| 552 | { | |
| 553 | sort_adjacent_labels = 1; | |
| 554 | } | |
| 555 | ||
| 556 | static void no_sort_adjacent_labels_command(int, argument *) | |
| 557 | { | |
| 558 | sort_adjacent_labels = 0; | |
| 559 | } | |
| 560 | ||
| 561 | static void date_as_label_command(int argc, argument *argv) | |
| 562 | { | |
| 563 | if (set_date_label_spec(argc > 0 ? argv[0].s : "D%a*")) | |
| 564 | date_as_label = 1; | |
| 565 | } | |
| 566 | ||
| 567 | static void no_date_as_label_command(int, argument *) | |
| 568 | { | |
| 569 | date_as_label = 0; | |
| 570 | } | |
| 571 | ||
| 572 | static void short_label_command(int, argument *argv) | |
| 573 | { | |
| 574 | if (set_short_label_spec(argv[0].s)) | |
| 575 | short_label_flag = 1; | |
| 576 | } | |
| 577 | ||
| 578 | static void no_short_label_command(int, argument *) | |
| 579 | { | |
| 580 | short_label_flag = 0; | |
| 581 | } | |
| 582 | ||
| 583 | static void compatible_command(int, argument *) | |
| 584 | { | |
| 585 | compatible_flag = 1; | |
| 586 | } | |
| 587 | ||
| 588 | static void no_compatible_command(int, argument *) | |
| 589 | { | |
| 590 | compatible_flag = 0; | |
| 591 | } | |
| 592 | ||
| 593 | static void join_authors_command(int argc, argument *argv) | |
| 594 | { | |
| 595 | join_authors_exactly_two = argv[0].s; | |
| 596 | join_authors_default = argc > 1 ? argv[1].s : argv[0].s; | |
| 597 | join_authors_last_two = argc == 3 ? argv[2].s : argv[0].s; | |
| 598 | } | |
| 599 | ||
| 600 | static void bracket_label_command(int, argument *argv) | |
| 601 | { | |
| 602 | pre_label = argv[0].s; | |
| 603 | post_label = argv[1].s; | |
| 604 | sep_label = argv[2].s; | |
| 605 | } | |
| 606 | ||
| 607 | static void separate_label_second_parts_command(int, argument *argv) | |
| 608 | { | |
| 609 | separate_label_second_parts = argv[0].s; | |
| 610 | } | |
| 611 | ||
| 612 | static void et_al_command(int argc, argument *argv) | |
| 613 | { | |
| 614 | et_al = argv[0].s; | |
| 615 | et_al_min_elide = argv[1].n; | |
| 616 | if (et_al_min_elide < 1) | |
| 617 | et_al_min_elide = 1; | |
| 618 | et_al_min_total = argc >= 3 ? argv[2].n : 0; | |
| 619 | } | |
| 620 | ||
| 621 | static void no_et_al_command(int, argument *) | |
| 622 | { | |
| 623 | et_al.clear(); | |
| 624 | et_al_min_elide = 0; | |
| 625 | } | |
| 626 | ||
| 627 | typedef void (*command_t)(int, argument *); | |
| 628 | ||
| 629 | /* arg_types is a string describing the numbers and types of arguments. | |
| 630 | s means a string, i means an integer, f is a list of fields, F is | |
| 631 | a single field, | |
| 632 | ? means that the previous argument is optional, * means that the | |
| 633 | previous argument can occur any number of times. */ | |
| 634 | ||
| 635 | struct S { | |
| 636 | const char *name; | |
| 637 | command_t func; | |
| 638 | const char *arg_types; | |
| 639 | } command_table[] = { | |
| 640 | { "include", include_command, "s" }, | |
| 641 | { "echo", echo_command, "s*" }, | |
| 642 | { "capitalize", capitalize_command, "f?" }, | |
| 643 | { "accumulate", accumulate_command, "" }, | |
| 644 | { "no-accumulate", no_accumulate_command, "" }, | |
| 645 | { "move-punctuation", move_punctuation_command, "" }, | |
| 646 | { "no-move-punctuation", no_move_punctuation_command, "" }, | |
| 647 | { "sort", sort_command, "s?" }, | |
| 648 | { "no-sort", no_sort_command, "" }, | |
| 649 | { "articles", articles_command, "s*" }, | |
| 650 | { "database", database_command, "ss*" }, | |
| 651 | { "default-database", default_database_command, "" }, | |
| 652 | { "no-default-database", no_default_database_command, "" }, | |
| 653 | { "bibliography", bibliography_command, "ss*" }, | |
| 654 | { "annotate", annotate_command, "F?s?" }, | |
| 655 | { "no-annotate", no_annotate_command, "" }, | |
| 656 | { "reverse", reverse_command, "s" }, | |
| 657 | { "no-reverse", no_reverse_command, "" }, | |
| 658 | { "abbreviate", abbreviate_command, "ss?s?s?s?" }, | |
| 659 | { "no-abbreviate", no_abbreviate_command, "" }, | |
| 660 | { "search-ignore", search_ignore_command, "f?" }, | |
| 661 | { "no-search-ignore", no_search_ignore_command, "" }, | |
| 662 | { "search-truncate", search_truncate_command, "i?" }, | |
| 663 | { "no-search-truncate", no_search_truncate_command, "" }, | |
| 664 | { "discard", discard_command, "f?" }, | |
| 665 | { "no-discard", no_discard_command, "" }, | |
| 666 | { "label", label_command, "s" }, | |
| 667 | { "abbreviate-label-ranges", abbreviate_label_ranges_command, "s?" }, | |
| 668 | { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command, "" }, | |
| 669 | { "label-in-reference", label_in_reference_command, "" }, | |
| 670 | { "no-label-in-reference", no_label_in_reference_command, "" }, | |
| 671 | { "label-in-text", label_in_text_command, "" }, | |
| 672 | { "no-label-in-text", no_label_in_text_command, "" }, | |
| 673 | { "sort-adjacent-labels", sort_adjacent_labels_command, "" }, | |
| 674 | { "no-sort-adjacent-labels", no_sort_adjacent_labels_command, "" }, | |
| 675 | { "date-as-label", date_as_label_command, "s?" }, | |
| 676 | { "no-date-as-label", no_date_as_label_command, "" }, | |
| 677 | { "short-label", short_label_command, "s" }, | |
| 678 | { "no-short-label", no_short_label_command, "" }, | |
| 679 | { "compatible", compatible_command, "" }, | |
| 680 | { "no-compatible", no_compatible_command, "" }, | |
| 681 | { "join-authors", join_authors_command, "sss?" }, | |
| 682 | { "bracket-label", bracket_label_command, "sss" }, | |
| 683 | { "separate-label-second-parts", separate_label_second_parts_command, "s" }, | |
| 684 | { "et-al", et_al_command, "sii?" }, | |
| 685 | { "no-et-al", no_et_al_command, "" }, | |
| 686 | }; | |
| 687 | ||
| 688 | static int check_args(const char *types, const char *name, | |
| 689 | int argc, argument *argv) | |
| 690 | { | |
| 691 | int argno = 0; | |
| 692 | while (*types) { | |
| 693 | if (argc == 0) { | |
| 694 | if (types[1] == '?') | |
| 695 | break; | |
| 696 | else if (types[1] == '*') { | |
| 697 | assert(types[2] == '\0'); | |
| 698 | break; | |
| 699 | } | |
| 700 | else { | |
| 701 | input_stack::error("missing argument for command `%1'", name); | |
| 702 | return 0; | |
| 703 | } | |
| 704 | } | |
| 705 | switch (*types) { | |
| 706 | case 's': | |
| 707 | break; | |
| 708 | case 'i': | |
| 709 | { | |
| 710 | char *ptr; | |
| 711 | long n = strtol(argv->s, &ptr, 10); | |
| 712 | if ((n == 0 && ptr == argv->s) | |
| 713 | || *ptr != '\0') { | |
| 714 | input_stack::error("argument %1 for command `%2' must be an integer", | |
| 715 | argno + 1, name); | |
| 716 | return 0; | |
| 717 | } | |
| 718 | argv->n = (int)n; | |
| 719 | break; | |
| 720 | } | |
| 721 | case 'f': | |
| 722 | { | |
| 723 | for (const char *ptr = argv->s; *ptr != '\0'; ptr++) | |
| 724 | if (!cs_field_name(*ptr)) { | |
| 725 | input_stack::error("argument %1 for command `%2' must be a list of fields", | |
| 726 | argno + 1, name); | |
| 727 | return 0; | |
| 728 | } | |
| 729 | break; | |
| 730 | } | |
| 731 | case 'F': | |
| 732 | if (argv->s[0] == '\0' || argv->s[1] != '\0' | |
| 733 | || !cs_field_name(argv->s[0])) { | |
| 734 | input_stack::error("argument %1 for command `%2' must be a field name", | |
| 735 | argno + 1, name); | |
| 736 | return 0; | |
| 737 | } | |
| 738 | break; | |
| 739 | default: | |
| 740 | assert(0); | |
| 741 | } | |
| 742 | if (types[1] == '?') | |
| 743 | types += 2; | |
| 744 | else if (types[1] != '*') | |
| 745 | types += 1; | |
| 746 | --argc; | |
| 747 | ++argv; | |
| 748 | ++argno; | |
| 749 | } | |
| 750 | if (argc > 0) { | |
| 751 | input_stack::error("too many arguments for command `%1'", name); | |
| 752 | return 0; | |
| 753 | } | |
| 754 | return 1; | |
| 755 | } | |
| 756 | ||
| 757 | static void execute_command(const char *name, int argc, argument *argv) | |
| 758 | { | |
| 759 | for (unsigned int i = 0; | |
| 760 | i < sizeof(command_table)/sizeof(command_table[0]); i++) | |
| 761 | if (strcmp(name, command_table[i].name) == 0) { | |
| 762 | if (check_args(command_table[i].arg_types, name, argc, argv)) | |
| 763 | (*command_table[i].func)(argc, argv); | |
| 764 | return; | |
| 765 | } | |
| 766 | input_stack::error("unknown command `%1'", name); | |
| 767 | } | |
| 768 | ||
| 769 | static void command_loop() | |
| 770 | { | |
| 771 | string command; | |
| 772 | for (;;) { | |
| 773 | command.clear(); | |
| 774 | int res = get_word(command); | |
| 775 | if (res != 1) { | |
| 776 | if (res == 0) | |
| 777 | continue; | |
| 778 | break; | |
| 779 | } | |
| 780 | int argc = 0; | |
| 781 | command += '\0'; | |
| 782 | while ((res = get_word(command)) == 1) { | |
| 783 | argc++; | |
| 784 | command += '\0'; | |
| 785 | } | |
| 786 | argument *argv = new argument[argc]; | |
| 787 | const char *ptr = command.contents(); | |
| 788 | for (int i = 0; i < argc; i++) | |
| 789 | argv[i].s = ptr = strchr(ptr, '\0') + 1; | |
| 790 | execute_command(command.contents(), argc, argv); | |
| 791 | a_delete argv; | |
| 792 | if (res == -1) | |
| 793 | break; | |
| 794 | } | |
| 795 | } | |
| 796 | ||
| 797 | void process_commands(const char *file) | |
| 798 | { | |
| 799 | input_stack::init(); | |
| 800 | input_stack::push_file(file); | |
| 801 | command_loop(); | |
| 802 | } | |
| 803 | ||
| 804 | void process_commands(string &s, const char *file, int lineno) | |
| 805 | { | |
| 806 | input_stack::init(); | |
| 807 | input_stack::push_string(s, file, lineno); | |
| 808 | command_loop(); | |
| 809 | } |