home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / gnu / groff_src.lha / groff-1.10src / refer / command.cc next >
C/C++ Source or Header  |  1995-06-22  |  18KB  |  808 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  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 2, or (at your option) any later
  10. version.
  11.  
  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
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  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 (illegal_input_char(c))
  197.       error_with_file_and_line(fn, lineno,
  198.                    "illegal 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. {
  411.   const char *saved_filename = current_filename;
  412.   int saved_lineno = current_lineno;
  413.   int saved_label_in_text = label_in_text;
  414.   label_in_text = 0;
  415.   if (!accumulate)
  416.     fputs(".]<\n", stdout);
  417.   for (int i = 0; i < argc; i++)
  418.     do_bib(argv[i].s);
  419.   if (accumulate)
  420.     output_references();
  421.   else
  422.     fputs(".]>\n", stdout);
  423.   current_filename = saved_filename;
  424.   current_lineno = saved_lineno;
  425.   label_in_text = saved_label_in_text;
  426. }
  427.  
  428. static void annotate_command(int argc, argument *argv)
  429. {
  430.   if (argc > 0)
  431.     annotation_field = argv[0].s[0];
  432.   else
  433.     annotation_field = 'X';
  434.   if (argc == 2)
  435.     annotation_macro = argv[1].s;
  436.   else
  437.     annotation_macro = "AP";
  438. }
  439.  
  440. static void no_annotate_command(int, argument *)
  441. {
  442.   annotation_macro.clear();
  443.   annotation_field = -1;
  444. }
  445.  
  446. static void reverse_command(int, argument *argv)
  447. {
  448.   reverse_fields = argv[0].s;
  449. }
  450.  
  451. static void no_reverse_command(int, argument *)
  452. {
  453.   reverse_fields.clear();
  454. }
  455.  
  456. static void abbreviate_command(int argc, argument *argv)
  457. {
  458.   abbreviate_fields = argv[0].s;
  459.   period_before_initial = argc > 1 ? argv[1].s : ". ";
  460.   period_before_last_name = argc > 2 ? argv[2].s : ". ";
  461.   period_before_other = argc > 3 ? argv[3].s : ". ";
  462.   period_before_hyphen = argc > 4 ? argv[4].s : ".";
  463. }
  464.  
  465. static void no_abbreviate_command(int, argument *)
  466. {
  467.   abbreviate_fields.clear();
  468. }
  469.  
  470. string search_ignore_fields;
  471.  
  472. static void search_ignore_command(int argc, argument *argv)
  473. {
  474.   if (argc > 0)
  475.     search_ignore_fields = argv[0].s;
  476.   else
  477.     search_ignore_fields = "XYZ";
  478.   search_ignore_fields += '\0';
  479.   linear_ignore_fields = search_ignore_fields.contents();
  480. }
  481.  
  482. static void no_search_ignore_command(int, argument *)
  483. {
  484.   linear_ignore_fields = "";
  485. }
  486.  
  487. static void search_truncate_command(int argc, argument *argv)
  488. {
  489.   if (argc > 0)
  490.     linear_truncate_len = argv[0].n;
  491.   else
  492.     linear_truncate_len = 6;
  493. }
  494.  
  495. static void no_search_truncate_command(int, argument *)
  496. {
  497.   linear_truncate_len = -1;
  498. }
  499.  
  500. static void discard_command(int argc, argument *argv)
  501. {
  502.   if (argc == 0)
  503.     discard_fields = "XYZ";
  504.   else
  505.     discard_fields = argv[0].s;
  506.   accumulate = 1;
  507. }
  508.  
  509. static void no_discard_command(int, argument *)
  510. {
  511.   discard_fields.clear();
  512. }
  513.  
  514. static void label_command(int, argument *argv)
  515. {
  516.   set_label_spec(argv[0].s);
  517. }
  518.  
  519. static void abbreviate_label_ranges_command(int argc, argument *argv)
  520. {
  521.   abbreviate_label_ranges = 1;
  522.   label_range_indicator = argc > 0 ? argv[0].s : "-";
  523. }
  524.  
  525. static void no_abbreviate_label_ranges_command(int, argument *)
  526. {
  527.   abbreviate_label_ranges = 0;
  528. }
  529.  
  530. static void label_in_reference_command(int, argument *)
  531. {
  532.   label_in_reference = 1;
  533. }
  534.  
  535. static void no_label_in_reference_command(int, argument *)
  536. {
  537.   label_in_reference = 0;
  538. }
  539.  
  540. static void label_in_text_command(int, argument *)
  541. {
  542.   label_in_text = 1;
  543. }
  544.  
  545. static void no_label_in_text_command(int, argument *)
  546. {
  547.   label_in_text = 0;
  548. }
  549.  
  550. static void sort_adjacent_labels_command(int, argument *)
  551. {
  552.   sort_adjacent_labels = 1;
  553. }
  554.  
  555. static void no_sort_adjacent_labels_command(int, argument *)
  556. {
  557.   sort_adjacent_labels = 0;
  558. }
  559.  
  560. static void date_as_label_command(int argc, argument *argv)
  561. {
  562.   if (set_date_label_spec(argc > 0 ? argv[0].s : "D%a*"))
  563.     date_as_label = 1;
  564. }
  565.  
  566. static void no_date_as_label_command(int, argument *)
  567. {
  568.   date_as_label = 0;
  569. }
  570.  
  571. static void short_label_command(int, argument *argv)
  572. {
  573.   if (set_short_label_spec(argv[0].s))
  574.     short_label_flag = 1;
  575. }
  576.  
  577. static void no_short_label_command(int, argument *)
  578. {
  579.   short_label_flag = 0;
  580. }
  581.  
  582. static void compatible_command(int, argument *)
  583. {
  584.   compatible_flag = 1;
  585. }
  586.  
  587. static void no_compatible_command(int, argument *)
  588. {
  589.   compatible_flag = 0;
  590. }
  591.  
  592. static void join_authors_command(int argc, argument *argv)
  593. {
  594.   join_authors_exactly_two = argv[0].s;
  595.   join_authors_default = argc > 1 ? argv[1].s : argv[0].s;
  596.   join_authors_last_two = argc == 3 ? argv[2].s : argv[0].s;
  597. }
  598.  
  599. static void bracket_label_command(int, argument *argv)
  600. {
  601.   pre_label = argv[0].s;
  602.   post_label = argv[1].s;
  603.   sep_label = argv[2].s;
  604. }
  605.  
  606. static void separate_label_second_parts_command(int, argument *argv)
  607. {
  608.   separate_label_second_parts = argv[0].s;
  609. }
  610.  
  611. static void et_al_command(int argc, argument *argv)
  612. {
  613.   et_al = argv[0].s;
  614.   et_al_min_elide = argv[1].n;
  615.   if (et_al_min_elide < 1)
  616.     et_al_min_elide = 1;
  617.   et_al_min_total = argc >= 3 ? argv[2].n : 0;
  618. }
  619.  
  620. static void no_et_al_command(int, argument *)
  621. {
  622.   et_al.clear();
  623.   et_al_min_elide = 0;
  624. }
  625.  
  626. typedef void (*command_t)(int, argument *);
  627.  
  628. /* arg_types is a string describing the numbers and types of arguments.
  629. s means a string, i means an integer, f is a list of fields, F is
  630. a single field,
  631. ? means that the previous argument is optional, * means that the
  632. previous argument can occur any number of times. */
  633.  
  634. struct {
  635.   const char *name;
  636.   command_t func;
  637.   const char *arg_types;
  638. } command_table[] = {
  639.   { "include", include_command, "s" },
  640.   { "echo", echo_command, "s*" },
  641.   { "capitalize", capitalize_command, "f?" },
  642.   { "accumulate", accumulate_command, "" },
  643.   { "no-accumulate", no_accumulate_command, "" },
  644.   { "move-punctuation", move_punctuation_command, "" },
  645.   { "no-move-punctuation", no_move_punctuation_command, "" },
  646.   { "sort", sort_command, "s?" },
  647.   { "no-sort", no_sort_command, "" },
  648.   { "articles", articles_command, "s*" },
  649.   { "database", database_command, "ss*" },
  650.   { "default-database", default_database_command, "" },
  651.   { "no-default-database", no_default_database_command, "" },
  652.   { "bibliography", bibliography_command, "ss*" },
  653.   { "annotate", annotate_command, "F?s?" },
  654.   { "no-annotate", no_annotate_command, "" },
  655.   { "reverse", reverse_command, "s" },
  656.   { "no-reverse", no_reverse_command, "" },
  657.   { "abbreviate", abbreviate_command, "ss?s?s?s?" },
  658.   { "no-abbreviate", no_abbreviate_command, "" },
  659.   { "search-ignore", search_ignore_command, "f?" },
  660.   { "no-search-ignore", no_search_ignore_command, "" },
  661.   { "search-truncate", search_truncate_command, "i?" },
  662.   { "no-search-truncate", no_search_truncate_command, "" },
  663.   { "discard", discard_command, "f?" },
  664.   { "no-discard", no_discard_command, "" },
  665.   { "label", label_command, "s" },
  666.   { "abbreviate-label-ranges", abbreviate_label_ranges_command, "s?" },
  667.   { "no-abbreviate-label-ranges", no_abbreviate_label_ranges_command, "" },
  668.   { "label-in-reference", label_in_reference_command, "" },
  669.   { "no-label-in-reference", no_label_in_reference_command, "" },
  670.   { "label-in-text", label_in_text_command, "" },
  671.   { "no-label-in-text", no_label_in_text_command, "" },
  672.   { "sort-adjacent-labels", sort_adjacent_labels_command, "" },
  673.   { "no-sort-adjacent-labels", no_sort_adjacent_labels_command, "" },
  674.   { "date-as-label", date_as_label_command, "s?" },
  675.   { "no-date-as-label", no_date_as_label_command, "" },
  676.   { "short-label", short_label_command, "s" },
  677.   { "no-short-label", no_short_label_command, "" },
  678.   { "compatible", compatible_command, "" },
  679.   { "no-compatible", no_compatible_command, "" },
  680.   { "join-authors", join_authors_command, "sss?" },
  681.   { "bracket-label", bracket_label_command, "sss" },
  682.   { "separate-label-second-parts", separate_label_second_parts_command, "s" },
  683.   { "et-al", et_al_command, "sii?" },
  684.   { "no-et-al", no_et_al_command, "" },
  685. };
  686.  
  687. static int check_args(const char *types, const char *name,
  688.               int argc, argument *argv)
  689. {
  690.   int argno = 0;
  691.   while (*types) {
  692.     if (argc == 0) {
  693.       if (types[1] == '?')
  694.     break;
  695.       else if (types[1] == '*') {
  696.     assert(types[2] == '\0');
  697.     break;
  698.       }
  699.       else {
  700.     input_stack::error("missing argument for command `%1'", name);
  701.     return 0;
  702.       }
  703.     }
  704.     switch (*types) {
  705.     case 's':
  706.       break;
  707.     case 'i':
  708.       {
  709.     char *ptr;
  710.     long n = strtol(argv->s, &ptr, 10);
  711.     if ((n == 0 && ptr == argv->s)
  712.         || *ptr != '\0') {
  713.       input_stack::error("argument %1 for command `%2' must be an integer",
  714.                  argno + 1, name);
  715.       return 0;
  716.     }
  717.     argv->n = (int)n;
  718.     break;
  719.       }
  720.     case 'f':
  721.       {
  722.     for (const char *ptr = argv->s; *ptr != '\0'; ptr++)
  723.       if (!cs_field_name(*ptr)) {
  724.         input_stack::error("argument %1 for command `%2' must be a list of fields",
  725.                  argno + 1, name);
  726.         return 0;
  727.       }
  728.     break;
  729.       }
  730.     case 'F':
  731.       if (argv->s[0] == '\0' || argv->s[1] != '\0'
  732.       || !cs_field_name(argv->s[0])) {
  733.     input_stack::error("argument %1 for command `%2' must be a field name",
  734.                argno + 1, name);
  735.     return 0;
  736.       }
  737.       break;
  738.     default:
  739.       assert(0);
  740.     }
  741.     if (types[1] == '?')
  742.       types += 2;
  743.     else if (types[1] != '*')
  744.       types += 1;
  745.     --argc;
  746.     ++argv;
  747.     ++argno;
  748.   }
  749.   if (argc > 0) {
  750.     input_stack::error("too many arguments for command `%1'", name);
  751.     return 0;
  752.   }
  753.   return 1;
  754. }
  755.  
  756. static void execute_command(const char *name, int argc, argument *argv)
  757. {
  758.   for (int i = 0; i < sizeof(command_table)/sizeof(command_table[0]); i++)
  759.     if (strcmp(name, command_table[i].name) == 0) {
  760.       if (check_args(command_table[i].arg_types, name, argc, argv))
  761.     (*command_table[i].func)(argc, argv);
  762.       return;
  763.     }
  764.   input_stack::error("unknown command `%1'", name);
  765. }
  766.  
  767. static void command_loop()
  768. {
  769.   string command;
  770.   for (;;) {
  771.     command.clear();
  772.     int res = get_word(command);
  773.     if (res != 1) {
  774.       if (res == 0)
  775.     continue;
  776.       break;
  777.     }
  778.     int argc = 0;
  779.     command += '\0';
  780.     while ((res = get_word(command)) == 1) {
  781.       argc++;
  782.       command += '\0';
  783.     }
  784.     argument *argv = new argument[argc];
  785.     const char *ptr = command.contents();
  786.     for (int i = 0; i < argc; i++)
  787.       argv[i].s = ptr = strchr(ptr, '\0') + 1;
  788.     execute_command(command.contents(), argc, argv);
  789.     a_delete argv;
  790.     if (res == -1)
  791.       break;
  792.   }
  793. }
  794.  
  795. void process_commands(const char *file)
  796. {
  797.   input_stack::init();
  798.   input_stack::push_file(file);
  799.   command_loop();
  800. }
  801.  
  802. void process_commands(string &s, const char *file, int lineno)
  803. {
  804.   input_stack::init();
  805.   input_stack::push_string(s, file, lineno);
  806.   command_loop();
  807. }
  808.