home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / less2912.zip / prompt.c < prev    next >
C/C++ Source or Header  |  1995-02-01  |  10KB  |  452 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Prompting and other messages.
  30.  * There are three flavors of prompts, SHORT, MEDIUM and LONG,
  31.  * selected by the -m/-M options.
  32.  * There is also the "equals message", printed by the = command.
  33.  * A prompt is a message composed of various pieces, such as the 
  34.  * name of the file being viewed, the percentage into the file, etc.
  35.  */
  36.  
  37. #include "less.h"
  38. #include "position.h"
  39.  
  40. extern int pr_type;
  41. extern int hit_eof;
  42. extern int new_file;
  43. extern int sc_width;
  44. extern int so_s_width, so_e_width;
  45. extern int linenums;
  46. extern int sc_height;
  47. extern int jump_sline;
  48. extern IFILE curr_ifile;
  49. #if EDITOR
  50. extern char *editor;
  51. #endif
  52.  
  53. /*
  54.  * Prototypes for the three flavors of prompts.
  55.  * These strings are expanded by pr_expand().
  56.  */
  57. static char s_proto[] =
  58.   "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
  59. static char m_proto[] =
  60.   "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
  61. static char M_proto[] =
  62.   "?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
  63. static char e_proto[] =
  64.   "?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
  65.  
  66. public char *prproto[3];
  67. public char *eqproto = e_proto;
  68.  
  69. static char message[250];
  70. static char *mp;
  71.  
  72. /*
  73.  * Initialize the prompt prototype strings.
  74.  */
  75.     public void
  76. init_prompt()
  77. {
  78.     prproto[0] = save(s_proto);
  79.     prproto[1] = save(m_proto);
  80.     prproto[2] = save(M_proto);
  81.     eqproto = save(e_proto);
  82. }
  83.  
  84. /*
  85.  * Set the message pointer to the end of the message string.
  86.  */
  87.     static void
  88. setmp()
  89. {
  90.     while (*mp != '\0')
  91.         mp++;
  92. }
  93.  
  94. /*
  95.  * Append a POSITION (as a decimal integer) to the end of the message.
  96.  */
  97.     static void
  98. ap_pos(pos)
  99.     POSITION pos;
  100. {
  101.     sprintf(mp, "%ld", (long)pos);
  102.     setmp();
  103. }
  104.  
  105. /*
  106.  * Append an integer to the end of the message.
  107.  */
  108.     static void
  109. ap_int(n)
  110.     int n;
  111. {
  112.     sprintf(mp, "%d", n);
  113.     setmp();
  114. }
  115.  
  116. /*
  117.  * Append a string to the end of the message.
  118.  */
  119.     static void
  120. ap_str(s)
  121.     char *s;
  122. {
  123.     strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp));
  124.     setmp();
  125. }
  126.  
  127. /*
  128.  * Append a question mark to the end of the message.
  129.  */
  130.     static void
  131. ap_quest()
  132. {
  133.     *mp++ = '?';
  134. }
  135.  
  136. /*
  137.  * Return the "current" byte offset in the file.
  138.  */
  139.     static POSITION
  140. curr_byte(where)
  141.     int where;
  142. {
  143.     POSITION pos;
  144.  
  145.     pos = position(where);
  146.     while (pos == NULL_POSITION && where >= 0 && where < sc_height)
  147.         pos = position(++where);
  148.     if (pos == NULL_POSITION)
  149.         pos = ch_length();
  150.     return (pos);
  151. }
  152.  
  153. /*
  154.  * Return the value of a prototype conditional.
  155.  * A prototype string may include conditionals which consist of a 
  156.  * question mark followed by a single letter.
  157.  * Here we decode that letter and return the appropriate boolean value.
  158.  */
  159.     static int
  160. cond(c, where)
  161.     char c;
  162.     int where;
  163. {
  164.     switch (c)
  165.     {
  166.     case 'a':    /* Anything in the message yet? */
  167.         return (mp > message);
  168.     case 'b':    /* Current byte offset known? */
  169.         return (curr_byte(where) != NULL_POSITION);
  170.     case 'e':    /* At end of file? */
  171.         return (hit_eof);
  172.     case 'f':    /* Filename known? */
  173.         return (strcmp(get_filename(curr_ifile), "-") != 0);
  174.     case 'l':    /* Line number known? */
  175.         return (linenums);
  176.     case 'L':    /* Final line number known? */
  177.         return (linenums && ch_length() != NULL_POSITION);
  178.     case 'm':    /* More than one file? */
  179.         return (nifile() > 1);
  180.     case 'n':    /* First prompt in a new file? */
  181.         return (new_file);
  182.     case 'p':    /* Percent into file known? */
  183.         return (curr_byte(where) != NULL_POSITION && 
  184.                 ch_length() > 0);
  185.     case 's':    /* Size of file known? */
  186.     case 'B':
  187.         return (ch_length() != NULL_POSITION);
  188.     case 'x':    /* Is there a "next" file? */
  189.         return (next_ifile(curr_ifile) != NULL_IFILE);
  190.     }
  191.     return (0);
  192. }
  193.  
  194. /*
  195.  * Decode a "percent" prototype character.
  196.  * A prototype string may include various "percent" escapes;
  197.  * that is, a percent sign followed by a single letter.
  198.  * Here we decode that letter and take the appropriate action,
  199.  * usually by appending something to the message being built.
  200.  */
  201.     static void
  202. protochar(c, where)
  203.     int c;
  204.     int where;
  205. {
  206.     POSITION pos;
  207.     POSITION len;
  208.     int n;
  209.     IFILE h;
  210.  
  211.     switch (c)
  212.     {
  213.     case 'b':    /* Current byte offset */
  214.         pos = curr_byte(where);
  215.         if (pos != NULL_POSITION)
  216.             ap_pos(pos);
  217.         else
  218.             ap_quest();
  219.         break;
  220. #if EDITOR
  221.     case 'E':    /* Editor name */
  222.         ap_str(editor);
  223.         break;
  224. #endif
  225.     case 'f':    /* File name */
  226.         ap_str(get_filename(curr_ifile));
  227.         break;
  228.     case 'i':    /* Index into list of files */
  229.         ap_int(get_index(curr_ifile));
  230.         break;
  231.     case 'l':    /* Current line number */
  232.         n = currline(where);
  233.         if (n != 0)
  234.             ap_int(n);
  235.         else
  236.             ap_quest();
  237.         break;
  238.     case 'L':    /* Final line number */
  239.         len = ch_length();
  240.         if (len == NULL_POSITION || len == ch_zero() ||
  241.             (n = find_linenum(len)) <= 0)
  242.             ap_quest();
  243.         else
  244.             ap_int(n-1);
  245.         break;
  246.     case 'm':    /* Number of files */
  247.         ap_int(nifile());
  248.         break;
  249.     case 'p':    /* Percent into file */
  250.         pos = curr_byte(where);
  251.         len = ch_length();
  252.         if (pos != NULL_POSITION && len > 0)
  253.             ap_int(percentage(pos,len));
  254.         else
  255.             ap_quest();
  256.         break;
  257.     case 's':    /* Size of file */
  258.     case 'B':
  259.         len = ch_length();
  260.         if (len != NULL_POSITION)
  261.             ap_pos(len);
  262.         else
  263.             ap_quest();
  264.         break;
  265.     case 't':    /* Truncate trailing spaces in the message */
  266.         while (mp > message && mp[-1] == ' ')
  267.             mp--;
  268.         break;
  269.     case 'x':    /* Name of next file */
  270.         h = next_ifile(curr_ifile);
  271.         if (h != NULL_IFILE)
  272.             ap_str(get_filename(h));
  273.         else
  274.             ap_quest();
  275.         break;
  276.     }
  277. }
  278.  
  279. /*
  280.  * Skip a false conditional.
  281.  * When a false condition is found (either a false IF or the ELSE part 
  282.  * of a true IF), this routine scans the prototype string to decide
  283.  * where to resume parsing the string.
  284.  * We must keep track of nested IFs and skip them properly.
  285.  */
  286.     static char *
  287. skipcond(p)
  288.     register char *p;
  289. {
  290.     register int iflevel;
  291.  
  292.     /*
  293.      * We came in here after processing a ? or :,
  294.      * so we start nested one level deep.
  295.      */
  296.     iflevel = 1;
  297.  
  298.     for (;;) switch (*++p)
  299.     {
  300.     case '?':
  301.         /*
  302.          * Start of a nested IF.
  303.          */
  304.         iflevel++;
  305.         break;
  306.     case ':':
  307.         /*
  308.          * Else.
  309.          * If this matches the IF we came in here with,
  310.          * then we're done.
  311.          */
  312.         if (iflevel == 1)
  313.             return (p);
  314.         break;
  315.     case '.':
  316.         /*
  317.          * Endif.
  318.          * If this matches the IF we came in here with,
  319.          * then we're done.
  320.          */
  321.         if (--iflevel == 0)
  322.             return (p);
  323.         break;
  324.     case '\\':
  325.         /*
  326.          * Backslash escapes the next character.
  327.          */
  328.         ++p;
  329.         break;
  330.     case '\0':
  331.         /*
  332.          * Whoops.  Hit end of string.
  333.          * This is a malformed conditional, but just treat it
  334.          * as if all active conditionals ends here.
  335.          */
  336.         return (p-1);
  337.     }
  338.     /*NOTREACHED*/
  339. }
  340.  
  341.     static char *
  342. wherechar(p, wp)
  343.     char *p;
  344.     int *wp;
  345. {
  346.     switch (*p)
  347.     {
  348.     case 'b': case 'l': case 'p':
  349.         switch (*++p)
  350.         {
  351.         case 't':   *wp = TOP;            break;
  352.         case 'm':   *wp = MIDDLE;        break;
  353.         case 'b':   *wp = BOTTOM;        break;
  354.         case 'B':   *wp = BOTTOM_PLUS_ONE;    break;
  355.         case 'j':   *wp = adjsline(jump_sline);    break;
  356.         default:    *wp = TOP;  p--;        break;
  357.         }
  358.     }
  359.     return (p);
  360. }
  361.  
  362. /*
  363.  * Construct a message based on a prototype string.
  364.  */
  365.     public char *
  366. pr_expand(proto, maxwidth)
  367.     char *proto;
  368.     int maxwidth;
  369. {
  370.     register char *p;
  371.     register int c;
  372.     int where;
  373.  
  374.     mp = message;
  375.  
  376.     if (*proto == '\0')
  377.         return ("");
  378.  
  379.     for (p = proto;  *p != '\0';  p++)
  380.     {
  381.         switch (*p)
  382.         {
  383.         default:    /* Just put the character in the message */
  384.             *mp++ = *p;
  385.             break;
  386.         case '\\':    /* Backslash escapes the next character */
  387.             p++;
  388.             *mp++ = *p;
  389.             break;
  390.         case '?':    /* Conditional (IF) */
  391.             if ((c = *++p) == '\0')
  392.                 --p;
  393.             else
  394.             {
  395.                 p = wherechar(p, &where);
  396.                 if (!cond(c, where))
  397.                     p = skipcond(p);
  398.             }
  399.             break;
  400.         case ':':    /* ELSE */
  401.             p = skipcond(p);
  402.             break;
  403.         case '.':    /* ENDIF */
  404.             break;
  405.         case '%':    /* Percent escape */
  406.             if ((c = *++p) == '\0')
  407.                 --p;
  408.             else
  409.             {
  410.                 p = wherechar(p, &where);
  411.                 protochar(c, where);
  412.             }
  413.             break;
  414.         }
  415.     }
  416.  
  417.     new_file = 0;
  418.     if (mp == message)
  419.         return (NULL);
  420.     *mp = '\0';
  421.     if (maxwidth > 0 && mp >= message + maxwidth)
  422.     {
  423.         /*
  424.          * Message is too long.
  425.          * Return just the final portion of it.
  426.          */
  427.         return (mp - maxwidth);
  428.     }
  429.     return (message);
  430. }
  431.  
  432. /*
  433.  * Return a message suitable for printing by the "=" command.
  434.  */
  435.     public char *
  436. eq_message()
  437. {
  438.     return (pr_expand(eqproto, 0));
  439. }
  440.  
  441. /*
  442.  * Return a prompt.
  443.  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
  444.  * If we can't come up with an appropriate prompt, return NULL
  445.  * and the caller will prompt with a colon.
  446.  */
  447.     public char *
  448. pr_string()
  449. {
  450.     return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2));
  451. }
  452.