home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / less3292.zip / prompt.c < prev    next >
C/C++ Source or Header  |  1996-08-22  |  10KB  |  466 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995,1996  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 constant char s_proto[] =
  58.   "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
  59. static constant 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 constant 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 constant 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. static constant char h_proto[] =
  66.   "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done";
  67.  
  68. public char *prproto[3];
  69. public char constant *eqproto = e_proto;
  70. public char constant *hproto = h_proto;
  71.  
  72. static char message[PROMPT_SIZE];
  73. static char *mp;
  74.  
  75. /*
  76.  * Initialize the prompt prototype strings.
  77.  */
  78.     public void
  79. init_prompt()
  80. {
  81.     prproto[0] = save(s_proto);
  82.     prproto[1] = save(m_proto);
  83.     prproto[2] = save(M_proto);
  84.     eqproto = save(e_proto);
  85.     hproto = save(h_proto);
  86. }
  87.  
  88. /*
  89.  * Set the message pointer to the end of the message string.
  90.  */
  91.     static void
  92. setmp()
  93. {
  94.     while (*mp != '\0')
  95.         mp++;
  96. }
  97.  
  98. /*
  99.  * Append a POSITION (as a decimal integer) to the end of the message.
  100.  */
  101.     static void
  102. ap_pos(pos)
  103.     POSITION pos;
  104. {
  105.     sprintf(mp, "%ld", (long)pos);
  106.     setmp();
  107. }
  108.  
  109. /*
  110.  * Append an integer to the end of the message.
  111.  */
  112.     static void
  113. ap_int(n)
  114.     int n;
  115. {
  116.     sprintf(mp, "%d", n);
  117.     setmp();
  118. }
  119.  
  120. /*
  121.  * Append a string to the end of the message.
  122.  */
  123.     static void
  124. ap_str(s)
  125.     char *s;
  126. {
  127.     strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp));
  128.     setmp();
  129. }
  130.  
  131. /*
  132.  * Append a question mark to the end of the message.
  133.  */
  134.     static void
  135. ap_quest()
  136. {
  137.     *mp++ = '?';
  138. }
  139.  
  140. /*
  141.  * Return the "current" byte offset in the file.
  142.  */
  143.     static POSITION
  144. curr_byte(where)
  145.     int where;
  146. {
  147.     POSITION pos;
  148.  
  149.     pos = position(where);
  150.     while (pos == NULL_POSITION && where >= 0 && where < sc_height)
  151.         pos = position(++where);
  152.     if (pos == NULL_POSITION)
  153.         pos = ch_length();
  154.     return (pos);
  155. }
  156.  
  157. /*
  158.  * Return the value of a prototype conditional.
  159.  * A prototype string may include conditionals which consist of a 
  160.  * question mark followed by a single letter.
  161.  * Here we decode that letter and return the appropriate boolean value.
  162.  */
  163.     static int
  164. cond(c, where)
  165.     char c;
  166.     int where;
  167. {
  168.     switch (c)
  169.     {
  170.     case 'a':    /* Anything in the message yet? */
  171.         return (mp > message);
  172.     case 'b':    /* Current byte offset known? */
  173.         return (curr_byte(where) != NULL_POSITION);
  174.     case 'e':    /* At end of file? */
  175.         return (hit_eof);
  176.     case 'f':    /* Filename known? */
  177.         return (strcmp(get_filename(curr_ifile), "-") != 0);
  178.     case 'l':    /* Line number known? */
  179.         return (linenums);
  180.     case 'L':    /* Final line number known? */
  181.         return (linenums && ch_length() != NULL_POSITION);
  182.     case 'm':    /* More than one file? */
  183.         return (nifile() > 1);
  184.     case 'n':    /* First prompt in a new file? */
  185.         return (new_file);
  186.     case 'p':    /* Percent into file known? */
  187.         return (curr_byte(where) != NULL_POSITION && 
  188.                 ch_length() > 0);
  189.     case 's':    /* Size of file known? */
  190.     case 'B':
  191.         return (ch_length() != NULL_POSITION);
  192.     case 'x':    /* Is there a "next" file? */
  193.         return (next_ifile(curr_ifile) != NULL_IFILE);
  194.     }
  195.     return (0);
  196. }
  197.  
  198. /*
  199.  * Decode a "percent" prototype character.
  200.  * A prototype string may include various "percent" escapes;
  201.  * that is, a percent sign followed by a single letter.
  202.  * Here we decode that letter and take the appropriate action,
  203.  * usually by appending something to the message being built.
  204.  */
  205.     static void
  206. protochar(c, where)
  207.     int c;
  208.     int where;
  209. {
  210.     POSITION pos;
  211.     POSITION len;
  212.     int n;
  213.     IFILE h;
  214.     char *s;
  215.  
  216.     switch (c)
  217.     {
  218.     case 'b':    /* Current byte offset */
  219.         pos = curr_byte(where);
  220.         if (pos != NULL_POSITION)
  221.             ap_pos(pos);
  222.         else
  223.             ap_quest();
  224.         break;
  225. #if EDITOR
  226.     case 'E':    /* Editor name */
  227.         ap_str(editor);
  228.         break;
  229. #endif
  230.     case 'f':    /* File name */
  231.         s = unquote_file(get_filename(curr_ifile));
  232.         ap_str(s);
  233.         free(s);
  234.         break;
  235.     case 'i':    /* Index into list of files */
  236.         ap_int(get_index(curr_ifile));
  237.         break;
  238.     case 'l':    /* Current line number */
  239.         n = currline(where);
  240.         if (n != 0)
  241.             ap_int(n);
  242.         else
  243.             ap_quest();
  244.         break;
  245.     case 'L':    /* Final line number */
  246.         len = ch_length();
  247.         if (len == NULL_POSITION || len == ch_zero() ||
  248.             (n = find_linenum(len)) <= 0)
  249.             ap_quest();
  250.         else
  251.             ap_int(n-1);
  252.         break;
  253.     case 'm':    /* Number of files */
  254.         ap_int(nifile());
  255.         break;
  256.     case 'p':    /* Percent into file */
  257.         pos = curr_byte(where);
  258.         len = ch_length();
  259.         if (pos != NULL_POSITION && len > 0)
  260.             ap_int(percentage(pos,len));
  261.         else
  262.             ap_quest();
  263.         break;
  264.     case 's':    /* Size of file */
  265.     case 'B':
  266.         len = ch_length();
  267.         if (len != NULL_POSITION)
  268.             ap_pos(len);
  269.         else
  270.             ap_quest();
  271.         break;
  272.     case 't':    /* Truncate trailing spaces in the message */
  273.         while (mp > message && mp[-1] == ' ')
  274.             mp--;
  275.         break;
  276.     case 'x':    /* Name of next file */
  277.         h = next_ifile(curr_ifile);
  278.         if (h != NULL_IFILE)
  279.         {
  280.             s = unquote_file(get_filename(h));
  281.             ap_str(s);
  282.             free(s);
  283.         } else
  284.             ap_quest();
  285.         break;
  286.     }
  287. }
  288.  
  289. /*
  290.  * Skip a false conditional.
  291.  * When a false condition is found (either a false IF or the ELSE part 
  292.  * of a true IF), this routine scans the prototype string to decide
  293.  * where to resume parsing the string.
  294.  * We must keep track of nested IFs and skip them properly.
  295.  */
  296.     static char *
  297. skipcond(p)
  298.     register char *p;
  299. {
  300.     register int iflevel;
  301.  
  302.     /*
  303.      * We came in here after processing a ? or :,
  304.      * so we start nested one level deep.
  305.      */
  306.     iflevel = 1;
  307.  
  308.     for (;;) switch (*++p)
  309.     {
  310.     case '?':
  311.         /*
  312.          * Start of a nested IF.
  313.          */
  314.         iflevel++;
  315.         break;
  316.     case ':':
  317.         /*
  318.          * Else.
  319.          * If this matches the IF we came in here with,
  320.          * then we're done.
  321.          */
  322.         if (iflevel == 1)
  323.             return (p);
  324.         break;
  325.     case '.':
  326.         /*
  327.          * Endif.
  328.          * If this matches the IF we came in here with,
  329.          * then we're done.
  330.          */
  331.         if (--iflevel == 0)
  332.             return (p);
  333.         break;
  334.     case '\\':
  335.         /*
  336.          * Backslash escapes the next character.
  337.          */
  338.         ++p;
  339.         break;
  340.     case '\0':
  341.         /*
  342.          * Whoops.  Hit end of string.
  343.          * This is a malformed conditional, but just treat it
  344.          * as if all active conditionals ends here.
  345.          */
  346.         return (p-1);
  347.     }
  348.     /*NOTREACHED*/
  349. }
  350.  
  351.     static char *
  352. wherechar(p, wp)
  353.     char *p;
  354.     int *wp;
  355. {
  356.     switch (*p)
  357.     {
  358.     case 'b': case 'l': case 'p':
  359.         switch (*++p)
  360.         {
  361.         case 't':   *wp = TOP;            break;
  362.         case 'm':   *wp = MIDDLE;        break;
  363.         case 'b':   *wp = BOTTOM;        break;
  364.         case 'B':   *wp = BOTTOM_PLUS_ONE;    break;
  365.         case 'j':   *wp = adjsline(jump_sline);    break;
  366.         default:    *wp = TOP;  p--;        break;
  367.         }
  368.     }
  369.     return (p);
  370. }
  371.  
  372. /*
  373.  * Construct a message based on a prototype string.
  374.  */
  375.     public char *
  376. pr_expand(proto, maxwidth)
  377.     char *proto;
  378.     int maxwidth;
  379. {
  380.     register char *p;
  381.     register int c;
  382.     int where;
  383.  
  384.     mp = message;
  385.  
  386.     if (*proto == '\0')
  387.         return ("");
  388.  
  389.     for (p = proto;  *p != '\0';  p++)
  390.     {
  391.         switch (*p)
  392.         {
  393.         default:    /* Just put the character in the message */
  394.             *mp++ = *p;
  395.             break;
  396.         case '\\':    /* Backslash escapes the next character */
  397.             p++;
  398.             *mp++ = *p;
  399.             break;
  400.         case '?':    /* Conditional (IF) */
  401.             if ((c = *++p) == '\0')
  402.                 --p;
  403.             else
  404.             {
  405.                 where = 0;
  406.                 p = wherechar(p, &where);
  407.                 if (!cond(c, where))
  408.                     p = skipcond(p);
  409.             }
  410.             break;
  411.         case ':':    /* ELSE */
  412.             p = skipcond(p);
  413.             break;
  414.         case '.':    /* ENDIF */
  415.             break;
  416.         case '%':    /* Percent escape */
  417.             if ((c = *++p) == '\0')
  418.                 --p;
  419.             else
  420.             {
  421.                 where = 0;
  422.                 p = wherechar(p, &where);
  423.                 protochar(c, where);
  424.             }
  425.             break;
  426.         }
  427.     }
  428.  
  429.     new_file = 0;
  430.     if (mp == message)
  431.         return (NULL);
  432.     *mp = '\0';
  433.     if (maxwidth > 0 && mp >= message + maxwidth)
  434.     {
  435.         /*
  436.          * Message is too long.
  437.          * Return just the final portion of it.
  438.          */
  439.         return (mp - maxwidth);
  440.     }
  441.     return (message);
  442. }
  443.  
  444. /*
  445.  * Return a message suitable for printing by the "=" command.
  446.  */
  447.     public char *
  448. eq_message()
  449. {
  450.     return (pr_expand(eqproto, 0));
  451. }
  452.  
  453. /*
  454.  * Return a prompt.
  455.  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
  456.  * If we can't come up with an appropriate prompt, return NULL
  457.  * and the caller will prompt with a colon.
  458.  */
  459.     public char *
  460. pr_string()
  461. {
  462.     if (ch_getflags() & CH_HELPFILE)
  463.         return (pr_expand(hproto, sc_width-so_s_width-so_e_width-2));
  464.     return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2));
  465. }
  466.