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