prompt.c revision 161478
160814Sps/* $FreeBSD: head/contrib/less/prompt.c 161478 2006-08-20 15:50:51Z delphij $ */ 260786Sps/* 3161478Sdelphij * Copyright (C) 1984-2004 Mark Nudelman 460786Sps * 560786Sps * You may distribute under the terms of either the GNU General Public 660786Sps * License or the Less License, as specified in the README file. 760786Sps * 860786Sps * For more information about less, or for information on how to 960786Sps * contact the author, see the README file. 1060786Sps */ 1160786Sps 1260786Sps 1360786Sps/* 1460786Sps * Prompting and other messages. 1560786Sps * There are three flavors of prompts, SHORT, MEDIUM and LONG, 1660786Sps * selected by the -m/-M options. 1760786Sps * There is also the "equals message", printed by the = command. 1860786Sps * A prompt is a message composed of various pieces, such as the 1960786Sps * name of the file being viewed, the percentage into the file, etc. 2060786Sps */ 2160786Sps 2260786Sps#include "less.h" 2360786Sps#include "position.h" 2460786Sps 2560786Spsextern int pr_type; 2660786Spsextern int hit_eof; 2760786Spsextern int new_file; 2860786Spsextern int sc_width; 2960786Spsextern int so_s_width, so_e_width; 3060786Spsextern int linenums; 3160786Spsextern int hshift; 3260786Spsextern int sc_height; 3360786Spsextern int jump_sline; 3460786Spsextern IFILE curr_ifile; 3560786Sps#if EDITOR 3660786Spsextern char *editor; 3760786Spsextern char *editproto; 3860786Sps#endif 3960786Sps 4060786Sps/* 4160786Sps * Prototypes for the three flavors of prompts. 4260786Sps * These strings are expanded by pr_expand(). 4360786Sps */ 4460786Spsstatic constant char s_proto[] = 4589022Sps "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t"; 4660786Spsstatic constant char m_proto[] = 4789022Sps "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; 4860786Spsstatic constant char M_proto[] = 4989022Sps "?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"; 5060786Spsstatic constant char e_proto[] = 5189022Sps "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; 5260786Spsstatic constant char h_proto[] = 5360786Sps "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done"; 5489022Spsstatic constant char w_proto[] = 5589022Sps "Waiting for data"; 5660786Sps 5760786Spspublic char *prproto[3]; 5860786Spspublic char constant *eqproto = e_proto; 5960786Spspublic char constant *hproto = h_proto; 6089022Spspublic char constant *wproto = w_proto; 6160786Sps 6260786Spsstatic char message[PROMPT_SIZE]; 6360786Spsstatic char *mp; 6460786Sps 6560786Sps/* 6660786Sps * Initialize the prompt prototype strings. 6760786Sps */ 6860786Sps public void 6960786Spsinit_prompt() 7060786Sps{ 7160786Sps prproto[0] = save(s_proto); 7260786Sps prproto[1] = save(m_proto); 7360786Sps prproto[2] = save(M_proto); 7460786Sps eqproto = save(e_proto); 7560786Sps hproto = save(h_proto); 7689022Sps wproto = save(w_proto); 7760786Sps} 7860786Sps 7960786Sps/* 8060786Sps * Append a string to the end of the message. 8160786Sps */ 8260786Sps static void 8360786Spsap_str(s) 8460786Sps char *s; 8560786Sps{ 8660786Sps int len; 8760786Sps 8860786Sps len = strlen(s); 8960786Sps if (mp + len >= message + PROMPT_SIZE) 9060786Sps len = message + PROMPT_SIZE - mp - 1; 9160786Sps strncpy(mp, s, len); 9260786Sps mp += len; 9360786Sps *mp = '\0'; 9460786Sps} 9560786Sps 9660786Sps/* 9760786Sps * Append a character to the end of the message. 9860786Sps */ 9960786Sps static void 10060786Spsap_char(c) 10160786Sps char c; 10260786Sps{ 10360786Sps char buf[2]; 10460786Sps 10560786Sps buf[0] = c; 10660786Sps buf[1] = '\0'; 10760786Sps ap_str(buf); 10860786Sps} 10960786Sps 11060786Sps/* 11160786Sps * Append a POSITION (as a decimal integer) to the end of the message. 11260786Sps */ 11360786Sps static void 11460786Spsap_pos(pos) 11560786Sps POSITION pos; 11660786Sps{ 117128348Stjr char buf[INT_STRLEN_BOUND(pos) + 2]; 118128348Stjr 119128348Stjr postoa(pos, buf); 120128348Stjr ap_str(buf); 12160786Sps} 12260786Sps 12360786Sps/* 124128348Stjr * Append a line number to the end of the message. 125128348Stjr */ 126128348Stjr static void 127128348Stjrap_linenum(linenum) 128128348Stjr LINENUM linenum; 129128348Stjr{ 130128348Stjr char buf[INT_STRLEN_BOUND(linenum) + 2]; 131128348Stjr 132128348Stjr linenumtoa(linenum, buf); 133128348Stjr ap_str(buf); 134128348Stjr} 135128348Stjr 136128348Stjr/* 13760786Sps * Append an integer to the end of the message. 13860786Sps */ 13960786Sps static void 140128348Stjrap_int(num) 141128348Stjr int num; 14260786Sps{ 143128348Stjr char buf[INT_STRLEN_BOUND(num) + 2]; 14460786Sps 145128348Stjr inttoa(num, buf); 14660786Sps ap_str(buf); 14760786Sps} 14860786Sps 14960786Sps/* 15060786Sps * Append a question mark to the end of the message. 15160786Sps */ 15260786Sps static void 15360786Spsap_quest() 15460786Sps{ 15560786Sps ap_str("?"); 15660786Sps} 15760786Sps 15860786Sps/* 15960786Sps * Return the "current" byte offset in the file. 16060786Sps */ 16160786Sps static POSITION 16260786Spscurr_byte(where) 16360786Sps int where; 16460786Sps{ 16560786Sps POSITION pos; 16660786Sps 16760786Sps pos = position(where); 168161478Sdelphij while (pos == NULL_POSITION && where >= 0 && where < sc_height-1) 16960786Sps pos = position(++where); 17060786Sps if (pos == NULL_POSITION) 17160786Sps pos = ch_length(); 17260786Sps return (pos); 17360786Sps} 17460786Sps 17560786Sps/* 17660786Sps * Return the value of a prototype conditional. 17760786Sps * A prototype string may include conditionals which consist of a 17860786Sps * question mark followed by a single letter. 17960786Sps * Here we decode that letter and return the appropriate boolean value. 18060786Sps */ 18160786Sps static int 18260786Spscond(c, where) 18360786Sps char c; 18460786Sps int where; 18560786Sps{ 18660786Sps POSITION len; 18760786Sps 18860786Sps switch (c) 18960786Sps { 19060786Sps case 'a': /* Anything in the message yet? */ 19160786Sps return (mp > message); 19260786Sps case 'b': /* Current byte offset known? */ 19360786Sps return (curr_byte(where) != NULL_POSITION); 19460786Sps case 'c': 19560786Sps return (hshift != 0); 19660786Sps case 'e': /* At end of file? */ 19760786Sps return (hit_eof); 19860786Sps case 'f': /* Filename known? */ 19960786Sps return (strcmp(get_filename(curr_ifile), "-") != 0); 20060786Sps case 'l': /* Line number known? */ 20160786Sps case 'd': /* Same as l */ 20260786Sps return (linenums); 20360786Sps case 'L': /* Final line number known? */ 204161478Sdelphij case 'D': /* Final page number known? */ 20560786Sps return (linenums && ch_length() != NULL_POSITION); 20660786Sps case 'm': /* More than one file? */ 207128348Stjr#if TAGS 20889022Sps return (ntags() ? (ntags() > 1) : (nifile() > 1)); 209128348Stjr#else 210128348Stjr return (nifile() > 1); 211128348Stjr#endif 21260786Sps case 'n': /* First prompt in a new file? */ 213128348Stjr#if TAGS 21489022Sps return (ntags() ? 1 : new_file); 215128348Stjr#else 216128348Stjr return (new_file); 217128348Stjr#endif 21860786Sps case 'p': /* Percent into file (bytes) known? */ 21960786Sps return (curr_byte(where) != NULL_POSITION && 22060786Sps ch_length() > 0); 22160786Sps case 'P': /* Percent into file (lines) known? */ 22260786Sps return (currline(where) != 0 && 22360786Sps (len = ch_length()) > 0 && 22460786Sps find_linenum(len) != 0); 22560786Sps case 's': /* Size of file known? */ 22660786Sps case 'B': 22760786Sps return (ch_length() != NULL_POSITION); 22860786Sps case 'x': /* Is there a "next" file? */ 229128348Stjr#if TAGS 23089022Sps if (ntags()) 23189022Sps return (0); 232128348Stjr#endif 23360786Sps return (next_ifile(curr_ifile) != NULL_IFILE); 23460786Sps } 23560786Sps return (0); 23660786Sps} 23760786Sps 23860786Sps/* 23960786Sps * Decode a "percent" prototype character. 24060786Sps * A prototype string may include various "percent" escapes; 24160786Sps * that is, a percent sign followed by a single letter. 24260786Sps * Here we decode that letter and take the appropriate action, 24360786Sps * usually by appending something to the message being built. 24460786Sps */ 24560786Sps static void 24660786Spsprotochar(c, where, iseditproto) 24760786Sps int c; 24860786Sps int where; 24960786Sps int iseditproto; 25060786Sps{ 25160786Sps POSITION pos; 25260786Sps POSITION len; 25360786Sps int n; 254128348Stjr LINENUM linenum; 255128348Stjr LINENUM last_linenum; 25660786Sps IFILE h; 25760786Sps 258161478Sdelphij#undef PAGE_NUM 259161478Sdelphij#define PAGE_NUM(linenum) ((((linenum) - 1) / (sc_height - 1)) + 1) 260161478Sdelphij 26160786Sps switch (c) 26260786Sps { 26360786Sps case 'b': /* Current byte offset */ 26460786Sps pos = curr_byte(where); 26560786Sps if (pos != NULL_POSITION) 26660786Sps ap_pos(pos); 26760786Sps else 26860786Sps ap_quest(); 26960786Sps break; 27060786Sps case 'c': 27160786Sps ap_int(hshift); 27260786Sps break; 27360786Sps case 'd': /* Current page number */ 274128348Stjr linenum = currline(where); 275128348Stjr if (linenum > 0 && sc_height > 1) 276161478Sdelphij ap_linenum(PAGE_NUM(linenum)); 27760786Sps else 27860786Sps ap_quest(); 27960786Sps break; 280161478Sdelphij case 'D': /* Final page number */ 281161478Sdelphij /* Find the page number of the last byte in the file (len-1). */ 28260786Sps len = ch_length(); 283161478Sdelphij if (len == NULL_POSITION) 28460786Sps ap_quest(); 285161478Sdelphij else if (len == 0) 286161478Sdelphij /* An empty file has no pages. */ 287161478Sdelphij ap_linenum(0); 28860786Sps else 289161478Sdelphij { 290161478Sdelphij linenum = find_linenum(len - 1); 291161478Sdelphij if (linenum <= 0) 292161478Sdelphij ap_quest(); 293161478Sdelphij else 294161478Sdelphij ap_linenum(PAGE_NUM(linenum)); 295161478Sdelphij } 29660786Sps break; 29760786Sps#if EDITOR 29860786Sps case 'E': /* Editor name */ 29960786Sps ap_str(editor); 30060786Sps break; 30160786Sps#endif 30260786Sps case 'f': /* File name */ 303128348Stjr ap_str(get_filename(curr_ifile)); 30460786Sps break; 30560786Sps case 'i': /* Index into list of files */ 306128348Stjr#if TAGS 30789022Sps if (ntags()) 30889022Sps ap_int(curr_tag()); 30989022Sps else 310128348Stjr#endif 31189022Sps ap_int(get_index(curr_ifile)); 31260786Sps break; 31360786Sps case 'l': /* Current line number */ 314128348Stjr linenum = currline(where); 315128348Stjr if (linenum != 0) 316128348Stjr ap_linenum(linenum); 31760786Sps else 31860786Sps ap_quest(); 31960786Sps break; 32060786Sps case 'L': /* Final line number */ 32160786Sps len = ch_length(); 32260786Sps if (len == NULL_POSITION || len == ch_zero() || 323128348Stjr (linenum = find_linenum(len)) <= 0) 32460786Sps ap_quest(); 32560786Sps else 326128348Stjr ap_linenum(linenum-1); 32760786Sps break; 32860786Sps case 'm': /* Number of files */ 329128348Stjr#if TAGS 33089022Sps n = ntags(); 33189022Sps if (n) 33289022Sps ap_int(n); 33389022Sps else 334128348Stjr#endif 33589022Sps ap_int(nifile()); 33660786Sps break; 33760786Sps case 'p': /* Percent into file (bytes) */ 33860786Sps pos = curr_byte(where); 33960786Sps len = ch_length(); 34060786Sps if (pos != NULL_POSITION && len > 0) 34160786Sps ap_int(percentage(pos,len)); 34260786Sps else 34360786Sps ap_quest(); 34460786Sps break; 34560786Sps case 'P': /* Percent into file (lines) */ 346128348Stjr linenum = currline(where); 347128348Stjr if (linenum == 0 || 34860786Sps (len = ch_length()) == NULL_POSITION || len == ch_zero() || 349128348Stjr (last_linenum = find_linenum(len)) <= 0) 35060786Sps ap_quest(); 35160786Sps else 352128348Stjr ap_int(percentage(linenum, last_linenum)); 35360786Sps break; 35460786Sps case 's': /* Size of file */ 35560786Sps case 'B': 35660786Sps len = ch_length(); 35760786Sps if (len != NULL_POSITION) 35860786Sps ap_pos(len); 35960786Sps else 36060786Sps ap_quest(); 36160786Sps break; 36260786Sps case 't': /* Truncate trailing spaces in the message */ 36360786Sps while (mp > message && mp[-1] == ' ') 36460786Sps mp--; 36560786Sps break; 36689022Sps case 'T': /* Type of list */ 367128348Stjr#if TAGS 36889022Sps if (ntags()) 36989022Sps ap_str("tag"); 37089022Sps else 371128348Stjr#endif 37289022Sps ap_str("file"); 37389022Sps break; 37460786Sps case 'x': /* Name of next file */ 37560786Sps h = next_ifile(curr_ifile); 37660786Sps if (h != NULL_IFILE) 377128348Stjr ap_str(get_filename(h)); 378128348Stjr else 37960786Sps ap_quest(); 38060786Sps break; 38160786Sps } 38260786Sps} 38360786Sps 38460786Sps/* 38560786Sps * Skip a false conditional. 38660786Sps * When a false condition is found (either a false IF or the ELSE part 38760786Sps * of a true IF), this routine scans the prototype string to decide 38860786Sps * where to resume parsing the string. 38960786Sps * We must keep track of nested IFs and skip them properly. 39060786Sps */ 39160786Sps static char * 39260786Spsskipcond(p) 39360786Sps register char *p; 39460786Sps{ 39560786Sps register int iflevel; 39660786Sps 39760786Sps /* 39860786Sps * We came in here after processing a ? or :, 39960786Sps * so we start nested one level deep. 40060786Sps */ 40160786Sps iflevel = 1; 40260786Sps 40360786Sps for (;;) switch (*++p) 40460786Sps { 40560786Sps case '?': 40660786Sps /* 40760786Sps * Start of a nested IF. 40860786Sps */ 40960786Sps iflevel++; 41060786Sps break; 41160786Sps case ':': 41260786Sps /* 41360786Sps * Else. 41460786Sps * If this matches the IF we came in here with, 41560786Sps * then we're done. 41660786Sps */ 41760786Sps if (iflevel == 1) 41860786Sps return (p); 41960786Sps break; 42060786Sps case '.': 42160786Sps /* 42260786Sps * Endif. 42360786Sps * If this matches the IF we came in here with, 42460786Sps * then we're done. 42560786Sps */ 42660786Sps if (--iflevel == 0) 42760786Sps return (p); 42860786Sps break; 42960786Sps case '\\': 43060786Sps /* 43160786Sps * Backslash escapes the next character. 43260786Sps */ 43360786Sps ++p; 43460786Sps break; 43560786Sps case '\0': 43660786Sps /* 43760786Sps * Whoops. Hit end of string. 43860786Sps * This is a malformed conditional, but just treat it 43960786Sps * as if all active conditionals ends here. 44060786Sps */ 44160786Sps return (p-1); 44260786Sps } 44360786Sps /*NOTREACHED*/ 44460786Sps} 44560786Sps 44660786Sps/* 44760786Sps * Decode a char that represents a position on the screen. 44860786Sps */ 44960786Sps static char * 45060786Spswherechar(p, wp) 45160786Sps char *p; 45260786Sps int *wp; 45360786Sps{ 45460786Sps switch (*p) 45560786Sps { 45660786Sps case 'b': case 'd': case 'l': case 'p': case 'P': 45760786Sps switch (*++p) 45860786Sps { 45960786Sps case 't': *wp = TOP; break; 46060786Sps case 'm': *wp = MIDDLE; break; 46160786Sps case 'b': *wp = BOTTOM; break; 46260786Sps case 'B': *wp = BOTTOM_PLUS_ONE; break; 46360786Sps case 'j': *wp = adjsline(jump_sline); break; 46460786Sps default: *wp = TOP; p--; break; 46560786Sps } 46660786Sps } 46760786Sps return (p); 46860786Sps} 46960786Sps 47060786Sps/* 47160786Sps * Construct a message based on a prototype string. 47260786Sps */ 47360786Sps public char * 47460786Spspr_expand(proto, maxwidth) 47560786Sps char *proto; 47660786Sps int maxwidth; 47760786Sps{ 47860786Sps register char *p; 47960786Sps register int c; 48060786Sps int where; 48160786Sps 48260786Sps mp = message; 48360786Sps 48460786Sps if (*proto == '\0') 48560786Sps return (""); 48660786Sps 48760786Sps for (p = proto; *p != '\0'; p++) 48860786Sps { 48960786Sps switch (*p) 49060786Sps { 49160786Sps default: /* Just put the character in the message */ 49260786Sps ap_char(*p); 49360786Sps break; 49460786Sps case '\\': /* Backslash escapes the next character */ 49560786Sps p++; 49660786Sps ap_char(*p); 49760786Sps break; 49860786Sps case '?': /* Conditional (IF) */ 49960786Sps if ((c = *++p) == '\0') 50060786Sps --p; 50160786Sps else 50260786Sps { 50360786Sps where = 0; 50460786Sps p = wherechar(p, &where); 50560786Sps if (!cond(c, where)) 50660786Sps p = skipcond(p); 50760786Sps } 50860786Sps break; 50960786Sps case ':': /* ELSE */ 51060786Sps p = skipcond(p); 51160786Sps break; 51260786Sps case '.': /* ENDIF */ 51360786Sps break; 51460786Sps case '%': /* Percent escape */ 51560786Sps if ((c = *++p) == '\0') 51660786Sps --p; 51760786Sps else 51860786Sps { 51960786Sps where = 0; 52060786Sps p = wherechar(p, &where); 52160786Sps protochar(c, where, 52260786Sps#if EDITOR 52360786Sps (proto == editproto)); 52460786Sps#else 52560786Sps 0); 52660786Sps#endif 52760786Sps 52860786Sps } 52960786Sps break; 53060786Sps } 53160786Sps } 53260786Sps 53360786Sps if (mp == message) 534161478Sdelphij return (""); 53560786Sps if (maxwidth > 0 && mp >= message + maxwidth) 53660786Sps { 53760786Sps /* 53860786Sps * Message is too long. 53960786Sps * Return just the final portion of it. 54060786Sps */ 54160786Sps return (mp - maxwidth); 54260786Sps } 54360786Sps return (message); 54460786Sps} 54560786Sps 54660786Sps/* 54760786Sps * Return a message suitable for printing by the "=" command. 54860786Sps */ 54960786Sps public char * 55060786Spseq_message() 55160786Sps{ 55260786Sps return (pr_expand(eqproto, 0)); 55360786Sps} 55460786Sps 55560786Sps/* 55660786Sps * Return a prompt. 55760786Sps * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. 55860786Sps * If we can't come up with an appropriate prompt, return NULL 55960786Sps * and the caller will prompt with a colon. 56060786Sps */ 56160786Sps public char * 56260786Spspr_string() 56360786Sps{ 56489022Sps char *prompt; 56589022Sps 56689022Sps prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? 56789022Sps hproto : prproto[pr_type], 56889022Sps sc_width-so_s_width-so_e_width-2); 56989022Sps new_file = 0; 57089022Sps return (prompt); 57160786Sps} 57289022Sps 57389022Sps/* 57489022Sps * Return a message suitable for printing while waiting in the F command. 57589022Sps */ 57689022Sps public char * 57789022Spswait_message() 57889022Sps{ 57989022Sps return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); 58089022Sps} 581