Logo Search packages:      
Sourcecode: tardy version File versions  Download package

help.cc

/*
 *    tardy - a tar post-processor
 *    Copyright (C) 1991-1995, 1998-2002 Peter Miller;
 *    All rights reserved.
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to provide consistent treatment of -Help options
 */

#include <ac/ctype.h>
#include <ac/stdio.h>
#include <ac/string.h>
#include <ac/stdlib.h>
#include <ac/unistd.h>
#include <ac/stdarg.h>

#include <arglex.h>
#include <error.h>
#include <help.h>
#include <mem.h>
#include <progname.h>
#include <trace.h>
#include <version_stmp.h>


#define PAIR(a, b) ((a) * 256 + (b))


static char *cr[] =
{
      "\\*(n) version \\*(v)",
      ".br",
      "Copyright (C) \\*(Y) Peter Miller;",
      "All rights reserved.",
      "",
      "The \\*(n) program comes with ABSOLUTELY NO WARRANTY;",
      "for details use the '\\*(n) -VERSion Warranty' command.",
      "The \\*(n) program is free software, and you are welcome to",
      "redistribute it under certain conditions;",
      "for details use the '\\*(n) -VERSion Redistribution' command.",
};

static char *au[] =
{
      ".nf",
      "Peter Miller   EMail: millerp@canb.auug.org.au",
      "/\\e/\\e*        WWW: http://www.canb.auug.org.au/~millerp/",
      ".fi",
};

char *so_o__rules[] =
{
#include <../man1/o__rules.h>
};

char *so_o_help[] =
{
#include <../man1/o_help.h>
};

char *so_z_cr[] =
{
      ".SH COPYRIGHT",
      ".so cr",
      ".SH AUTHOR",
      ".so au",
};

char *so_z_exit[] =
{
#include <../man1/z_exit.h>
};


typedef struct so_list_ty so_list_ty;
struct so_list_ty
{
      char  *name;
      char  **text;
      int   length;
};

static so_list_ty so_list[] =
{
      { "o__rules.so",  so_o__rules,      SIZEOF(so_o__rules)     },
      { "o_help.so",          so_o_help,  SIZEOF(so_o_help) },
      { "z_cr.so",            so_z_cr,    SIZEOF(so_z_cr)         },
      { "z_exit.so",          so_z_exit,  SIZEOF(so_z_exit) },
      { "z_name.so",          0,          0                 },
      { "../etc/version.so",  0,          0                 },
      { "cr",                 cr,         SIZEOF(cr),       },
      { "au",                 au,         SIZEOF(au),       },
};


static      int   ocol;
static      int   icol;
static      int   fill; /* true if currently filling */
static      int   in;   /* current indent */
static      int   in_base;    /* current paragraph indent */
static      int   ll;   /* line length */
static      long  roff_line;
static      char  *roff_file;
static      int   TP_line;


static void
emit(int c)
{
      switch (c)
      {
      case ' ':
            icol++;
            break;

      case '\t':
            icol = ((icol / 8) + 1) * 8;
            break;
      
      case '\n':
            putchar('\n');
            icol = 0;
            ocol = 0;
            break;

      default:
            if (!isprint(c))
                  break;
            while (((ocol / 8) + 1) * 8 <= icol && ocol + 1 < icol)
            {
                  putchar('\t');
                  ocol = ((ocol / 8) + 1) * 8;
            }
            while (ocol < icol)
            {
                  putchar(' ');
                  ++ocol;
            }
            putchar(c);
            ++icol;
            ++ocol;
            break;
      }
      if (ferror(stdout))
            nfatal("standard output");
}


static void
emit_word(char *buf, long len)
{
      if (len <= 0)
            return;

      /*
       * if this line is not yet indented, indent it
       */
      if (!ocol && !icol)
            icol = in;
      
      /*
       * if there is already something on this line 
       * and we are in "fill" mode
       * and this word would cause it to overflow
       * then wrap the line
       */
      if (ocol && fill && icol + len >= ll)
      {
            emit('\n');
            icol = in;
      }
      if (ocol)
            emit(' ');
      while (len-- > 0)
            emit(*buf++);
}


static void
br(void)
{
      if (ocol)
            emit('\n');
}


static void
sp(void)
{
      br();
      emit('\n');
}


static void
interpret_line_of_words(char *line)
{
      /*
       * if not filling,
       * pump the line out literrally.
       */
      if (!fill)
      {
            if (!ocol && !icol)
                  icol = in;
            while (*line)
                  emit(*line++);
            emit('\n');
            return;
      }

      /*
       * in fill mode, a blank line means
       * finish the paragraph and emit a blank line
       */
      if (!*line)
      {
            sp();
            return;
      }

      /*
       * break the line into space-separated words
       * and emit each individually
       */
      while (*line)
      {
            char  *start;

            while (isspace(*line))
                  ++line;
            if (!*line)
                  break;
            start = line;
            while (*line && !isspace(*line))
                  ++line;
            emit_word(start, line - start);

            /*
             * extra space at end of sentences
             */
            if
            (
                  (line[-1] == '.' || line[-1] == '?')
            &&
                  (
                        !line[0]
                  ||
                        (
                              line[0] == ' '
                        &&
                              (!line[1] || line[1] == ' ')
                        )
                  )
            )
                  emit(' ');
      }
}


static void
roff_error(char *s, ...)
{
      va_list     ap;
      va_start(ap, s);
      char buffer[1000];
      vsnprintf(buffer, sizeof(buffer), s, ap);
      va_end(ap);

#if 0
      br();
      if (roff_file)
            emit_word(roff_file, strlen(roff_file));
      if (roff_line)
      {
            char line[20];
            snprintf(line, sizeof(line), "%ld", roff_line);
            emit_word(line, strlen(line));
      }
      interpret_line_of_words(buffer);
      br();
#else
      fatal
      (
            "%s: %ld: %s",
            (roff_file ? roff_file : "(noname)"),
            roff_line,
            buffer
      );
#endif
}


static void
get_name(char **lp, char *name)
{
      char  *line;

      line = *lp;
      if (*line == '('/*)*/)
      {
            ++line;
            if (*line)
            {
                  name[0] = *line++;
                  if (*line)
                  {
                        name[1] = *line++;
                        name[2] = 0;
                  }
                  else
                        name[1] = 0;
            }
            else
                  name[0] = 0;
      }
      else if (*line)
      {
            name[0] = *line++;
            name[1] = 0;
      }
      else
            name[0] = 0;
      *lp = line;
}


typedef struct string_reg_ty string_reg_ty;
struct string_reg_ty
{
      char  *name;
      char  *value;
};


static      long        string_reg_count;
static      string_reg_ty     *string_reg;



static char *
string_find(char *name)
{
      long  j;

      for (j = 0; j < string_reg_count; ++j)
      {
            string_reg_ty     *srp;

            srp = &string_reg[j];
            if (!strcmp(name, srp->name))
                  return srp->value;
      }
      return 0;
}


static char *
numreg_find(char *)
{
      return 0;
}


static void
roff_prepro(char *buffer, char *line)
{
      char  *bp;
      char  *value;
      char  name[4];

      bp = buffer;
      while (*line)
      {
            int c = *line++;
            if (c != '\\')
            {
                  *bp++ = c;
                  continue;
            }
            c = *line++;
            if (!c)
            {
                  roff_error("can't do escaped end-of-line");
                  break;
            }
            switch (c)
            {
            default:
                  roff_error("unknown \\%c inline directive", c);
                  break;

            case '%':
                  /* word break info */
                  break;

            case '*':
                  /* inline string */
                  get_name(&line, name);
                  value = string_find(name);
                  if (value)
                  {
                        while (*value)
                              *bp++ = *value++;
                  }
                  break;

            case 'n':
                  /* inline number register */
                  get_name(&line, name);
                  value = numreg_find(name);
                  if (value)
                  {
                        while (*value)
                              *bp++ = *value++;
                  }
                  break;

            case 'e':
            case '\\':
                  *bp++ = '\\';
                  break;

            case '-':
                  *bp++ = '-';
                  break;

            case 'f':
                  /* ignore font directives */
                  get_name(&line, name);
                  break;

            case '&':
            case '|':
                  /* ignore weird space directives */
                  break;
            }
      }
      *bp = 0;
}


static void
interpret_text(char *line)
{
      char  buffer[1000];

      roff_prepro(buffer, line);
      interpret_line_of_words(buffer);
      if (TP_line)
      {
            if (icol >= 15)
                  br();
            else
                  icol = 15;
            TP_line = 0;
            in = in_base + 8;
      }
}


static void
roff_sub(char *buffer, int argc, char **argv)
{
      int   j;
      char  *bp;
      long  len;

      bp = buffer;
      for (j = 0; j < argc; ++j)
      {
            len = strlen(argv[j]);
            if (j)
                  *bp++ = ' ';
            memcpy(bp, argv[j], len);
            bp += len;
      }
      *bp = 0;
}


static void
interpret_text_args(int argc, char **argv)
{
      char  buffer[1000];

      roff_sub(buffer, argc, argv);
      interpret_text(buffer);
}


static void
concat_text_args(int argc, char **argv)
{
      int   j;
      char  *bp;
      size_t      len;
      char  buffer[1000];

      bp = buffer;
      for (j = 0; j < argc; ++j)
      {
            len = strlen(argv[j]);
            if ((bp - buffer) + len + 1 >= sizeof(buffer))
                  break;
            memcpy(bp, argv[j], len);
            bp += len;
      }
      *bp = 0;
      interpret_text(buffer);
}


static void interpret(char **, int); /* forward */


static void
so(int argc, char **argv)
{
      so_list_ty  *sop;

      if (argc != 1)
      {
            roff_error(".so requires one argument");
            return;
      }
      for (sop = so_list; sop < ENDOF(so_list); ++sop)
      {
            if (!strcmp(sop->name, argv[0]))
            {
                  interpret(sop->text, sop->length);
                  return;
            }
      }
      roff_error("\".so %s\" not known", argv[0]);
}


static void
lf(int argc, char **argv)
{
      if (roff_file)
            mem_free(roff_file);
      if (argc >= 1)
            roff_line = atol(argv[0]) - 1;
      else
            roff_line = 0;
      if (argc >= 2)
            roff_file = mem_copy_string(argv[1]);
      else
            roff_file = 0;
}


static void
ds_guts(const char *name, const char *value)
{
      long        j;
      string_reg_ty     *srp;

      for (j = 0; j < string_reg_count; ++j)
      {
            srp = &string_reg[j];
            if (!strcmp(name, srp->name))
            {
                  mem_free(srp->value);
                  srp->value = mem_copy_string(value);
                  return;
            }
      }

      if (string_reg_count)
      {
            string_reg =
                  (string_reg_ty *)
                  mem_change_size
                  (
                        string_reg,
                        (string_reg_count + 1) * sizeof(string_reg_ty)
                  );
      }
      else
            string_reg = (string_reg_ty *)mem_alloc(sizeof(string_reg_ty));
      srp = &string_reg[string_reg_count++];
      srp->name = mem_copy_string(name);
      srp->value = mem_copy_string(value);
}


static void
ds(int argc, char **argv)
{
      char  buf1[1000];
      char  buf2[1000];

      if (!argc)
            return;
      roff_sub(buf1, argc - 1, argv + 1);
      roff_prepro(buf2, buf1);
      ds_guts(argv[0], buf2);
}


static void
dot_in(int argc, char **argv)
{
      if (argc < 1)
            return;
      switch (argv[0][0])
      {
      case '-':
            in -= atoi(argv[0] + 1);
            break;

      case '+':
            in += atoi(argv[0] + 1);
            break;

      default:
            in = atoi(argv[0] + 1);
            break;
      }
      if (in < 0)
            in = 0;
}


static void interpret(char **, int); /* forward */


static void
interpret_control(char *line)
{
      int   c1, c2;
      int   argc;
      char  *argv[20];
      char  temp[1000];
      char  *cp;

      /*
       * find the directive name
       */
      line++;
      while (isspace(*line))
            ++line;
      if (*line)
            c1 = *line++;
      else
            c1 = ' ';
      if (*line)
            c2 = *line++;
      else
            c2 = ' ';

      /*
       * break the line into space-separated arguments
       */
      argc = 0;
      cp = temp;
      while (argc < (int)SIZEOF(argv))
      {
            int quoting;

            while (isspace(*line))
                  ++line;
            if (!*line)
                  break;
            argv[argc++] = cp;
            quoting = 0;
            while (*line)
            {
                  if (*line == '"')
                  {
                        quoting = !quoting;
                        ++line;
                        continue;
                  }
                  if (!quoting && isspace(*line))
                        break;
                  *cp++ = *line++;
            }
            *cp++ = 0;
            if (!*line)
                  break;
      }

      /*
       * now do something with it
       */
      switch (PAIR(c1, c2))
      {
      case PAIR('n', 'e'):
            /* ignore the space needed directive */
            break;

      case PAIR('i', 'n'):
            dot_in(argc, argv);
            break;

      case PAIR('I', ' '):
      case PAIR('I', 'R'):
      case PAIR('I', 'B'):
      case PAIR('R', ' '):
      case PAIR('R', 'I'):
      case PAIR('R', 'B'):
      case PAIR('B', ' '):
      case PAIR('B', 'I'):
      case PAIR('B', 'R'):
            concat_text_args(argc, argv);
            break;

      case PAIR('n', 'f'):
            br();
            fill = 0;
            break;

      case PAIR('f', 'i'):
            br();
            fill = 1;
            break;

      case PAIR('t', 'a'):
            /* ignore tab directive */
            break;

      case PAIR('b', 'r'):
            br();
            break;

      case PAIR('s', 'p'):
            sp();
            break;

      case PAIR('I', 'P'):
            in = in_base;
            sp();
            emit(' ');
            emit(' ');
            break;

      case PAIR('P', 'P'):
            in = in_base;
            sp();
            break;

      case PAIR('T', 'H'):
            break;

      case PAIR('T', 'P'):
            in = in_base;
            sp();
            TP_line = 1;
            break;

      case PAIR('S', 'H'):
            in = 0;
            sp();
            interpret_text_args(argc, argv);
            br();
            in_base = 8;
            in = 8;
            break;

      case PAIR('s', 'o'):
            so(argc, argv);
            break;

      case PAIR('l', 'f'):
            lf(argc, argv);
            break;

      case PAIR('R', 'S'):
            in_base = 16;
            in = 16;
            br();
            break;

      case PAIR('R', 'E'):
            in_base = 8;
            in = 8;
            br();
            break;

      case PAIR('d', 's'):
            ds(argc, argv);
            break;

      case PAIR('r', /*(*/')'):
            cp = string_find(/*(*/"R)");
            if (!cp)
                  cp = "";
            if (strcmp(cp, "no") != 0)
            {
                  static char *macro[] =
                  {
                        ".PP",
                        "See also",
                        ".IR \\*(n) (1)",
                        "for options common to all \\*(n) commands.",
                  };

                  interpret(macro, SIZEOF(macro));
            }
            break;

      default:
            roff_error("formatting directive \".%c%c\" unknown", c1, c2);
            break;
      }
}


static void
interpret(char **text, int text_len)
{
      int   j;
      long  hold_line;
      char  *hold_file;

      /*
       * save position
       */
      trace(("interpret()\n{\n"/*}*/));
      hold_line = roff_line;
      hold_file = roff_file ? mem_copy_string(roff_file) : (char *)0;

      /*
       * interpret the text
       */
      for (j = 0; j < text_len; ++j)
      {
            char *s;

            s = text[j];
            if (*s == '.' || *s == '\'')
                  interpret_control(s);
            else
                  interpret_text(s);
            ++roff_line;
            if (ferror(stdout))
                  nfatal("standard output");
      }

      /*
       * restore position
       */
      if (roff_file)
            mem_free(roff_file);
      roff_line = hold_line;
      roff_file = hold_file;
      trace((/*{*/"}\n"));
}


void
help(char **text, int text_len, void (*usage)(void))
{
      /*
       * collect the rest of thge command line,
       * if necessary
       */
      trace(("help(text = %08lX, text_len = %d, usage = %08lX)\n{\n"/*}*/,
            text, text_len, usage));
      if (usage)
      {
            arglex();
            while (arglex_token != arglex_token_eoln)
                  generic_argument(usage);
      }

      /*
       * initialize the state of the interpreter
       */
      ds_guts(/*(*/"n)", progname_get());
      ds_guts(/*(*/"v)", version_stamp());
      ds_guts(/*(*/"Y)", copyright_years());
      ll = 79;
      in = 0;
      in_base = 0;
      fill = 1;
      ocol = 0;
      icol = 0;
      lf(0, 0);
      TP_line = 0;

      /*
       * do what they asked
       */
      interpret(text, text_len);
      br();

      /*
       * close the paginator
       */
      if (fflush(stdout))
            nfatal("standard output");
      trace((/*{*/"}\n"));
}


void
generic_argument(void (*usage)(void))
{
      trace(("generic_argument()\n{\n"/*}*/));
      switch (arglex_token)
      {
      default:
            bad_argument(usage);
            /* NOTREACHED */

      case arglex_token_trace:
            if (arglex() != arglex_token_string)
                  usage();
            for (;;)
            {
                  trace_enable(arglex_value.alv_string);
                  if (arglex() != arglex_token_string)
                        break;
            }
#ifndef DEBUG
            error
            (
"Warning: the -TRace option is only effective when the %s program \
is compiled using the DEBUG define in the common/main.h include file.",
                  progname_get()
            );
#endif
            break;
      }
      trace((/*{*/"}\n"));
}


void
bad_argument(void (*usage)(void))
{
      trace(("bad_argument()\n{\n"/*}*/));
      switch (arglex_token)
      {
      case arglex_token_string:
            error("misplaced file name (\"%s\")", arglex_value.alv_string);
            break;

      case arglex_token_number:
            error("misplaced number (%s)", arglex_value.alv_string);
            break;

      case arglex_token_option:
            error("unknown \"%s\" option", arglex_value.alv_string);
            break;

      case arglex_token_eoln:
            error("command line too short");
            break;

      default:
            error("misplaced \"%s\" option", arglex_value.alv_string);
            break;
      }
      usage();
      trace((/*{*/"}\n"));
      quit(1);
      /* NOTREACHED */
}

Generated by  Doxygen 1.6.0   Back to index