home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / LESS177.ZIP / src / prompt.c < prev    next >
C/C++ Source or Header  |  1992-07-18  |  9KB  |  428 lines

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