home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / MISC / GNU / LES177AS.ZIP / PROMPT.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-09-01  |  8.8 KB  |  430 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, "%u", 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.     default:    /* When in doubt, just add the char to the message */
  253.         *mp++ = c;
  254.     }
  255. }
  256.  
  257. /*
  258.  * Skip a false conditional.
  259.  * When a false condition is found (either a false IF or the ELSE part 
  260.  * of a true IF), this routine scans the prototype string to decide
  261.  * where to resume parsing the string.
  262.  * We must keep track of nested IFs and skip them properly.
  263.  */
  264.     static char *
  265. skipcond(p)
  266.     register char *p;
  267. {
  268.     register int iflevel;
  269.  
  270.     /*
  271.      * We came in here after processing a ? or :,
  272.      * so we start nested one level deep.
  273.      */
  274.     iflevel = 1;
  275.  
  276.     for (;;) switch (*++p)
  277.     {
  278.     case '?':
  279.         /*
  280.          * Start of a nested IF.
  281.          */
  282.         iflevel++;
  283.         break;
  284.     case ':':
  285.         /*
  286.          * Else.
  287.          * If this matches the IF we came in here with,
  288.          * then we're done.
  289.          */
  290.         if (iflevel == 1)
  291.             return (p);
  292.         break;
  293.     case '.':
  294.         /*
  295.          * Endif.
  296.          * If this matches the IF we came in here with,
  297.          * then we're done.
  298.          */
  299.         if (--iflevel == 0)
  300.             return (p);
  301.         break;
  302.     case '\\':
  303.         /*
  304.          * Backslash escapes the next character.
  305.          */
  306.         ++p;
  307.         break;
  308.     case '\0':
  309.         /*
  310.          * Whoops.  Hit end of string.
  311.          * This is a malformed conditional, but just treat it
  312.          * as if all active conditionals ends here.
  313.          */
  314.         return (p-1);
  315.     }
  316.     /*NOTREACHED*/
  317. }
  318.  
  319.     static char *
  320. wherechar(p, wp)
  321.     char *p;
  322.     int *wp;
  323. {
  324.     switch (*p)
  325.     {
  326.     case 'b': case 'l': case 'p':
  327.         switch (*++p)
  328.         {
  329.         case 't':   *wp = TOP;            break;
  330.         case 'm':   *wp = MIDDLE;        break;
  331.         case 'b':   *wp = BOTTOM;        break;
  332.         case 'B':   *wp = BOTTOM_PLUS_ONE;    break;
  333.         case 'j':   *wp = adjsline(jump_sline);    break;
  334.         default:    *wp = TOP;  p--;        break;
  335.         }
  336.     }
  337.     return (p);
  338. }
  339.  
  340. /*
  341.  * Construct a message based on a prototype string.
  342.  */
  343.     public char *
  344. pr_expand(proto, maxwidth)
  345.     char *proto;
  346.     int maxwidth;
  347. {
  348.     register char *p;
  349.     register int c;
  350.     int where;
  351.  
  352.     mp = message;
  353.  
  354.     if (*proto == '\0')
  355.         return ("");
  356.  
  357.     for (p = proto;  *p != '\0';  p++)
  358.     {
  359.         switch (*p)
  360.         {
  361.         default:    /* Just put the character in the message */
  362.             *mp++ = *p;
  363.             break;
  364.         case '\\':    /* Backslash escapes the next character */
  365.             p++;
  366.             *mp++ = *p;
  367.             break;
  368.         case '?':    /* Conditional (IF) */
  369.             if ((c = *++p) == '\0')
  370.                 --p;
  371.             else
  372.             {
  373.                 p = wherechar(p, &where);
  374.                 if (!cond(c, where))
  375.                     p = skipcond(p);
  376.             }
  377.             break;
  378.         case ':':    /* ELSE */
  379.             p = skipcond(p);
  380.             break;
  381.         case '.':    /* ENDIF */
  382.             break;
  383.         case '%':    /* Percent escape */
  384.             if ((c = *++p) == '\0')
  385.                 --p;
  386.             else
  387.             {
  388.                 p = wherechar(p, &where);
  389.                 protochar(c, where);
  390.             }
  391.             break;
  392.         }
  393.     }
  394.  
  395.     new_file = 0;
  396.     if (mp == message)
  397.         return (NULL);
  398.     *mp = '\0';
  399.     if (maxwidth > 0 && mp >= message + maxwidth)
  400.     {
  401.         /*
  402.          * Message is too long.
  403.          * Return just the final portion of it.
  404.          */
  405.         return (mp - maxwidth);
  406.     }
  407.     return (message);
  408. }
  409.  
  410. /*
  411.  * Return a message suitable for printing by the "=" command.
  412.  */
  413.     public char *
  414. eq_message()
  415. {
  416.     return (pr_expand(eqproto, 0));
  417. }
  418.  
  419. /*
  420.  * Return a prompt.
  421.  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
  422.  * If we can't come up with an appropriate prompt, return NULL
  423.  * and the caller will prompt with a colon.
  424.  */
  425.     public char *
  426. pr_string()
  427. {
  428.     return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2));
  429. }
  430.