home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / less373.zip / prompt.c < prev    next >
C/C++ Source or Header  |  2002-01-14  |  11KB  |  546 lines

  1. /*
  2.  * Copyright (C) 1984-2000  Mark Nudelman
  3.  *
  4.  * You may distribute under the terms of either the GNU General Public
  5.  * License or the Less License, as specified in the README file.
  6.  *
  7.  * For more information about less, or for information on how to 
  8.  * contact the author, see the README file.
  9.  */
  10.  
  11.  
  12. /*
  13.  * Prompting and other messages.
  14.  * There are three flavors of prompts, SHORT, MEDIUM and LONG,
  15.  * selected by the -m/-M options.
  16.  * There is also the "equals message", printed by the = command.
  17.  * A prompt is a message composed of various pieces, such as the 
  18.  * name of the file being viewed, the percentage into the file, etc.
  19.  */
  20.  
  21. #include "less.h"
  22. #include "position.h"
  23.  
  24. extern int pr_type;
  25. extern int hit_eof;
  26. extern int new_file;
  27. extern int sc_width;
  28. extern int so_s_width, so_e_width;
  29. extern int linenums;
  30. extern int hshift;
  31. extern int sc_height;
  32. extern int jump_sline;
  33. extern IFILE curr_ifile;
  34. #if EDITOR
  35. extern char *editor;
  36. extern char *editproto;
  37. #endif
  38.  
  39. /*
  40.  * Prototypes for the three flavors of prompts.
  41.  * These strings are expanded by pr_expand().
  42.  */
  43. static constant char s_proto[] =
  44.   "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t";
  45. static constant char m_proto[] =
  46.   "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
  47. static constant char M_proto[] =
  48.   "?f%f .?n?m(%T %i of %m) ..?ltlines %lt-%lb?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
  49. static constant char e_proto[] =
  50.   "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
  51. static constant char h_proto[] =
  52.   "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done";
  53. static constant char w_proto[] =
  54.   "Waiting for data";
  55.  
  56. public char *prproto[3];
  57. public char constant *eqproto = e_proto;
  58. public char constant *hproto = h_proto;
  59. public char constant *wproto = w_proto;
  60.  
  61. static char message[PROMPT_SIZE];
  62. static char *mp;
  63.  
  64. /*
  65.  * Initialize the prompt prototype strings.
  66.  */
  67.     public void
  68. init_prompt()
  69. {
  70.     prproto[0] = save(s_proto);
  71.     prproto[1] = save(m_proto);
  72.     prproto[2] = save(M_proto);
  73.     eqproto = save(e_proto);
  74.     hproto = save(h_proto);
  75.     wproto = save(w_proto);
  76. }
  77.  
  78. /*
  79.  * Append a string to the end of the message.
  80.  */
  81.     static void
  82. ap_str(s)
  83.     char *s;
  84. {
  85.     int len;
  86.  
  87.     len = strlen(s);
  88.     if (mp + len >= message + PROMPT_SIZE)
  89.         len = message + PROMPT_SIZE - mp - 1;
  90.     strncpy(mp, s, len);
  91.     mp += len;
  92.     *mp = '\0';
  93. }
  94.  
  95. /*
  96.  * Append a character to the end of the message.
  97.  */
  98.     static void
  99. ap_char(c)
  100.     char c;
  101. {
  102.     char buf[2];
  103.  
  104.     buf[0] = c;
  105.     buf[1] = '\0';
  106.     ap_str(buf);
  107. }
  108.  
  109. /*
  110.  * Append a POSITION (as a decimal integer) to the end of the message.
  111.  */
  112.     static void
  113. ap_pos(pos)
  114.     POSITION pos;
  115. {
  116.     char buf[INT_STRLEN_BOUND(pos) + 1]; 
  117.     char *p = buf + sizeof(buf) - 1;
  118.     int neg = (pos < 0);
  119.  
  120.     if (neg)
  121.         pos = -pos;
  122.     *p = '\0';
  123.     do
  124.         *--p = '0' + (pos % 10);
  125.     while ((pos /= 10) != 0);
  126.     if (neg)
  127.         *--p = '-';
  128.     ap_str(p);
  129. }
  130.  
  131. /*
  132.  * Append an integer to the end of the message.
  133.  */
  134.     static void
  135. ap_int(n)
  136.     int n;
  137. {
  138.     char buf[INT_STRLEN_BOUND(n) + 1];
  139.  
  140.     sprintf(buf, "%d", n);
  141.     ap_str(buf);
  142. }
  143.  
  144. /*
  145.  * Append a question mark to the end of the message.
  146.  */
  147.     static void
  148. ap_quest()
  149. {
  150.     ap_str("?");
  151. }
  152.  
  153. /*
  154.  * Return the "current" byte offset in the file.
  155.  */
  156.     static POSITION
  157. curr_byte(where)
  158.     int where;
  159. {
  160.     POSITION pos;
  161.  
  162.     pos = position(where);
  163.     while (pos == NULL_POSITION && where >= 0 && where < sc_height)
  164.         pos = position(++where);
  165.     if (pos == NULL_POSITION)
  166.         pos = ch_length();
  167.     return (pos);
  168. }
  169.  
  170. /*
  171.  * Return the value of a prototype conditional.
  172.  * A prototype string may include conditionals which consist of a 
  173.  * question mark followed by a single letter.
  174.  * Here we decode that letter and return the appropriate boolean value.
  175.  */
  176.     static int
  177. cond(c, where)
  178.     char c;
  179.     int where;
  180. {
  181.     POSITION len;
  182.  
  183.     switch (c)
  184.     {
  185.     case 'a':    /* Anything in the message yet? */
  186.         return (mp > message);
  187.     case 'b':    /* Current byte offset known? */
  188.         return (curr_byte(where) != NULL_POSITION);
  189.     case 'c':
  190.         return (hshift != 0);
  191.     case 'e':    /* At end of file? */
  192.         return (hit_eof);
  193.     case 'f':    /* Filename known? */
  194.         return (strcmp(get_filename(curr_ifile), "-") != 0);
  195.     case 'l':    /* Line number known? */
  196.     case 'd':    /* Same as l */
  197.         return (linenums);
  198.     case 'L':    /* Final line number known? */
  199.     case 'D':    /* Same as L */
  200.         return (linenums && ch_length() != NULL_POSITION);
  201.     case 'm':    /* More than one file? */
  202.         return (ntags() ? (ntags() > 1) : (nifile() > 1));
  203.     case 'n':    /* First prompt in a new file? */
  204.         return (ntags() ? 1 : new_file);
  205.     case 'p':    /* Percent into file (bytes) known? */
  206.         return (curr_byte(where) != NULL_POSITION && 
  207.                 ch_length() > 0);
  208.     case 'P':    /* Percent into file (lines) known? */
  209.         return (currline(where) != 0 &&
  210.                 (len = ch_length()) > 0 &&
  211.                 find_linenum(len) != 0);
  212.     case 's':    /* Size of file known? */
  213.     case 'B':
  214.         return (ch_length() != NULL_POSITION);
  215.     case 'x':    /* Is there a "next" file? */
  216.         if (ntags())
  217.             return (0);
  218.         return (next_ifile(curr_ifile) != NULL_IFILE);
  219.     }
  220.     return (0);
  221. }
  222.  
  223. /*
  224.  * Decode a "percent" prototype character.
  225.  * A prototype string may include various "percent" escapes;
  226.  * that is, a percent sign followed by a single letter.
  227.  * Here we decode that letter and take the appropriate action,
  228.  * usually by appending something to the message being built.
  229.  */
  230.     static void
  231. protochar(c, where, iseditproto)
  232.     int c;
  233.     int where;
  234.     int iseditproto;
  235. {
  236.     POSITION pos;
  237.     POSITION len;
  238.     int n;
  239.     IFILE h;
  240.  
  241.     switch (c)
  242.     {
  243.     case 'b':    /* Current byte offset */
  244.         pos = curr_byte(where);
  245.         if (pos != NULL_POSITION)
  246.             ap_pos(pos);
  247.         else
  248.             ap_quest();
  249.         break;
  250.     case 'c':
  251.         ap_int(hshift);
  252.         break;
  253.     case 'd':    /* Current page number */
  254.         n = currline(where);
  255.         if (n > 0 && sc_height > 1)
  256.             ap_int(((n - 1) / (sc_height - 1)) + 1);
  257.         else
  258.             ap_quest();
  259.         break;
  260.     case 'D':    /* Last page number */
  261.         len = ch_length();
  262.         if (len == NULL_POSITION || len == ch_zero() ||
  263.             (n = find_linenum(len)) <= 0)
  264.             ap_quest();
  265.         else
  266.             ap_int(((n - 1) / (sc_height - 1)) + 1);
  267.         break;
  268. #if EDITOR
  269.     case 'E':    /* Editor name */
  270.         ap_str(editor);
  271.         break;
  272. #endif
  273.     case 'f':    /* File name */
  274.         ap_str(get_filename(curr_ifile));
  275.         break;
  276.     case 'i':    /* Index into list of files */
  277.         if (ntags())
  278.             ap_int(curr_tag());
  279.         else
  280.             ap_int(get_index(curr_ifile));
  281.         break;
  282.     case 'l':    /* Current line number */
  283.         n = currline(where);
  284.         if (n != 0)
  285.             ap_int(n);
  286.         else
  287.             ap_quest();
  288.         break;
  289.     case 'L':    /* Final line number */
  290.         len = ch_length();
  291.         if (len == NULL_POSITION || len == ch_zero() ||
  292.             (n = find_linenum(len)) <= 0)
  293.             ap_quest();
  294.         else
  295.             ap_int(n-1);
  296.         break;
  297.     case 'm':    /* Number of files */
  298.         n = ntags();
  299.         if (n)
  300.             ap_int(n);
  301.         else
  302.             ap_int(nifile());
  303.         break;
  304.     case 'p':    /* Percent into file (bytes) */
  305.         pos = curr_byte(where);
  306.         len = ch_length();
  307.         if (pos != NULL_POSITION && len > 0)
  308.             ap_int(percentage(pos,len));
  309.         else
  310.             ap_quest();
  311.         break;
  312.     case 'P':    /* Percent into file (lines) */
  313.         pos = (POSITION) currline(where);
  314.         if (pos == 0 ||
  315.             (len = ch_length()) == NULL_POSITION || len == ch_zero() ||
  316.             (n = find_linenum(len)) <= 0)
  317.             ap_quest();
  318.         else
  319.             ap_int(percentage(pos, (POSITION)n));
  320.         break;
  321.     case 's':    /* Size of file */
  322.     case 'B':
  323.         len = ch_length();
  324.         if (len != NULL_POSITION)
  325.             ap_pos(len);
  326.         else
  327.             ap_quest();
  328.         break;
  329.     case 't':    /* Truncate trailing spaces in the message */
  330.         while (mp > message && mp[-1] == ' ')
  331.             mp--;
  332.         break;
  333.     case 'T':    /* Type of list */
  334.         if (ntags())
  335.             ap_str("tag");
  336.         else
  337.             ap_str("file");
  338.         break;
  339.     case 'x':    /* Name of next file */
  340.         h = next_ifile(curr_ifile);
  341.         if (h != NULL_IFILE)
  342.             ap_str(get_filename(h));
  343.         else
  344.             ap_quest();
  345.         break;
  346.     }
  347. }
  348.  
  349. /*
  350.  * Skip a false conditional.
  351.  * When a false condition is found (either a false IF or the ELSE part 
  352.  * of a true IF), this routine scans the prototype string to decide
  353.  * where to resume parsing the string.
  354.  * We must keep track of nested IFs and skip them properly.
  355.  */
  356.     static char *
  357. skipcond(p)
  358.     register char *p;
  359. {
  360.     register int iflevel;
  361.  
  362.     /*
  363.      * We came in here after processing a ? or :,
  364.      * so we start nested one level deep.
  365.      */
  366.     iflevel = 1;
  367.  
  368.     for (;;) switch (*++p)
  369.     {
  370.     case '?':
  371.         /*
  372.          * Start of a nested IF.
  373.          */
  374.         iflevel++;
  375.         break;
  376.     case ':':
  377.         /*
  378.          * Else.
  379.          * If this matches the IF we came in here with,
  380.          * then we're done.
  381.          */
  382.         if (iflevel == 1)
  383.             return (p);
  384.         break;
  385.     case '.':
  386.         /*
  387.          * Endif.
  388.          * If this matches the IF we came in here with,
  389.          * then we're done.
  390.          */
  391.         if (--iflevel == 0)
  392.             return (p);
  393.         break;
  394.     case '\\':
  395.         /*
  396.          * Backslash escapes the next character.
  397.          */
  398.         ++p;
  399.         break;
  400.     case '\0':
  401.         /*
  402.          * Whoops.  Hit end of string.
  403.          * This is a malformed conditional, but just treat it
  404.          * as if all active conditionals ends here.
  405.          */
  406.         return (p-1);
  407.     }
  408.     /*NOTREACHED*/
  409. }
  410.  
  411. /*
  412.  * Decode a char that represents a position on the screen.
  413.  */
  414.     static char *
  415. wherechar(p, wp)
  416.     char *p;
  417.     int *wp;
  418. {
  419.     switch (*p)
  420.     {
  421.     case 'b': case 'd': case 'l': case 'p': case 'P':
  422.         switch (*++p)
  423.         {
  424.         case 't':   *wp = TOP;            break;
  425.         case 'm':   *wp = MIDDLE;        break;
  426.         case 'b':   *wp = BOTTOM;        break;
  427.         case 'B':   *wp = BOTTOM_PLUS_ONE;    break;
  428.         case 'j':   *wp = adjsline(jump_sline);    break;
  429.         default:    *wp = TOP;  p--;        break;
  430.         }
  431.     }
  432.     return (p);
  433. }
  434.  
  435. /*
  436.  * Construct a message based on a prototype string.
  437.  */
  438.     public char *
  439. pr_expand(proto, maxwidth)
  440.     char *proto;
  441.     int maxwidth;
  442. {
  443.     register char *p;
  444.     register int c;
  445.     int where;
  446.  
  447.     mp = message;
  448.  
  449.     if (*proto == '\0')
  450.         return ("");
  451.  
  452.     for (p = proto;  *p != '\0';  p++)
  453.     {
  454.         switch (*p)
  455.         {
  456.         default:    /* Just put the character in the message */
  457.             ap_char(*p);
  458.             break;
  459.         case '\\':    /* Backslash escapes the next character */
  460.             p++;
  461.             ap_char(*p);
  462.             break;
  463.         case '?':    /* Conditional (IF) */
  464.             if ((c = *++p) == '\0')
  465.                 --p;
  466.             else
  467.             {
  468.                 where = 0;
  469.                 p = wherechar(p, &where);
  470.                 if (!cond(c, where))
  471.                     p = skipcond(p);
  472.             }
  473.             break;
  474.         case ':':    /* ELSE */
  475.             p = skipcond(p);
  476.             break;
  477.         case '.':    /* ENDIF */
  478.             break;
  479.         case '%':    /* Percent escape */
  480.             if ((c = *++p) == '\0')
  481.                 --p;
  482.             else
  483.             {
  484.                 where = 0;
  485.                 p = wherechar(p, &where);
  486.                 protochar(c, where,
  487. #if EDITOR
  488.                     (proto == editproto));
  489. #else
  490.                     0);
  491. #endif
  492.  
  493.             }
  494.             break;
  495.         }
  496.     }
  497.  
  498.     if (mp == message)
  499.         return (NULL);
  500.     if (maxwidth > 0 && mp >= message + maxwidth)
  501.     {
  502.         /*
  503.          * Message is too long.
  504.          * Return just the final portion of it.
  505.          */
  506.         return (mp - maxwidth);
  507.     }
  508.     return (message);
  509. }
  510.  
  511. /*
  512.  * Return a message suitable for printing by the "=" command.
  513.  */
  514.     public char *
  515. eq_message()
  516. {
  517.     return (pr_expand(eqproto, 0));
  518. }
  519.  
  520. /*
  521.  * Return a prompt.
  522.  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
  523.  * If we can't come up with an appropriate prompt, return NULL
  524.  * and the caller will prompt with a colon.
  525.  */
  526.     public char *
  527. pr_string()
  528. {
  529.     char *prompt;
  530.  
  531.     prompt = pr_expand((ch_getflags() & CH_HELPFILE) ?
  532.                 hproto : prproto[pr_type],
  533.             sc_width-so_s_width-so_e_width-2);
  534.     new_file = 0;
  535.     return (prompt);
  536. }
  537.  
  538. /*
  539.  * Return a message suitable for printing while waiting in the F command.
  540.  */
  541.     public char *
  542. wait_message()
  543. {
  544.     return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2));
  545. }
  546.