prompt.c revision 229811
160814Sps/* $FreeBSD: stable/9/contrib/less/prompt.c 229811 2012-01-08 13:51:09Z dim $ */ 260786Sps/* 3221715Sdelphij * Copyright (C) 1984-2011 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 new_file; 2760786Spsextern int sc_width; 2860786Spsextern int so_s_width, so_e_width; 2960786Spsextern int linenums; 3060786Spsextern int hshift; 3160786Spsextern int sc_height; 3260786Spsextern int jump_sline; 33170259Sdelphijextern int less_is_more; 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"; 56170259Sdelphijstatic constant char more_proto[] = 57170259Sdelphij "--More--(?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)"; 5860786Sps 5960786Spspublic char *prproto[3]; 6060786Spspublic char constant *eqproto = e_proto; 6160786Spspublic char constant *hproto = h_proto; 6289022Spspublic char constant *wproto = w_proto; 6360786Sps 6460786Spsstatic char message[PROMPT_SIZE]; 6560786Spsstatic char *mp; 6660786Sps 6760786Sps/* 6860786Sps * Initialize the prompt prototype strings. 6960786Sps */ 7060786Sps public void 7160786Spsinit_prompt() 7260786Sps{ 7360786Sps prproto[0] = save(s_proto); 74170259Sdelphij prproto[1] = save(less_is_more ? more_proto : m_proto); 7560786Sps prproto[2] = save(M_proto); 7660786Sps eqproto = save(e_proto); 7760786Sps hproto = save(h_proto); 7889022Sps wproto = save(w_proto); 7960786Sps} 8060786Sps 8160786Sps/* 8260786Sps * Append a string to the end of the message. 8360786Sps */ 8460786Sps static void 8560786Spsap_str(s) 8660786Sps char *s; 8760786Sps{ 8860786Sps int len; 8960786Sps 9060786Sps len = strlen(s); 9160786Sps if (mp + len >= message + PROMPT_SIZE) 9260786Sps len = message + PROMPT_SIZE - mp - 1; 9360786Sps strncpy(mp, s, len); 9460786Sps mp += len; 9560786Sps *mp = '\0'; 9660786Sps} 9760786Sps 9860786Sps/* 9960786Sps * Append a character to the end of the message. 10060786Sps */ 10160786Sps static void 10260786Spsap_char(c) 10360786Sps char c; 10460786Sps{ 10560786Sps char buf[2]; 10660786Sps 10760786Sps buf[0] = c; 10860786Sps buf[1] = '\0'; 10960786Sps ap_str(buf); 11060786Sps} 11160786Sps 11260786Sps/* 11360786Sps * Append a POSITION (as a decimal integer) to the end of the message. 11460786Sps */ 11560786Sps static void 11660786Spsap_pos(pos) 11760786Sps POSITION pos; 11860786Sps{ 119128348Stjr char buf[INT_STRLEN_BOUND(pos) + 2]; 120128348Stjr 121128348Stjr postoa(pos, buf); 122128348Stjr ap_str(buf); 12360786Sps} 12460786Sps 12560786Sps/* 126128348Stjr * Append a line number to the end of the message. 127128348Stjr */ 128128348Stjr static void 129128348Stjrap_linenum(linenum) 130128348Stjr LINENUM linenum; 131128348Stjr{ 132128348Stjr char buf[INT_STRLEN_BOUND(linenum) + 2]; 133128348Stjr 134128348Stjr linenumtoa(linenum, buf); 135128348Stjr ap_str(buf); 136128348Stjr} 137128348Stjr 138128348Stjr/* 13960786Sps * Append an integer to the end of the message. 14060786Sps */ 14160786Sps static void 142128348Stjrap_int(num) 143128348Stjr int num; 14460786Sps{ 145128348Stjr char buf[INT_STRLEN_BOUND(num) + 2]; 14660786Sps 147128348Stjr inttoa(num, buf); 14860786Sps ap_str(buf); 14960786Sps} 15060786Sps 15160786Sps/* 15260786Sps * Append a question mark to the end of the message. 15360786Sps */ 15460786Sps static void 15560786Spsap_quest() 15660786Sps{ 15760786Sps ap_str("?"); 15860786Sps} 15960786Sps 16060786Sps/* 16160786Sps * Return the "current" byte offset in the file. 16260786Sps */ 16360786Sps static POSITION 16460786Spscurr_byte(where) 16560786Sps int where; 16660786Sps{ 16760786Sps POSITION pos; 16860786Sps 16960786Sps pos = position(where); 170161478Sdelphij while (pos == NULL_POSITION && where >= 0 && where < sc_height-1) 17160786Sps pos = position(++where); 17260786Sps if (pos == NULL_POSITION) 17360786Sps pos = ch_length(); 17460786Sps return (pos); 17560786Sps} 17660786Sps 17760786Sps/* 17860786Sps * Return the value of a prototype conditional. 17960786Sps * A prototype string may include conditionals which consist of a 18060786Sps * question mark followed by a single letter. 18160786Sps * Here we decode that letter and return the appropriate boolean value. 18260786Sps */ 18360786Sps static int 18460786Spscond(c, where) 18560786Sps char c; 18660786Sps int where; 18760786Sps{ 18860786Sps POSITION len; 18960786Sps 19060786Sps switch (c) 19160786Sps { 19260786Sps case 'a': /* Anything in the message yet? */ 19360786Sps return (mp > message); 19460786Sps case 'b': /* Current byte offset known? */ 19560786Sps return (curr_byte(where) != NULL_POSITION); 19660786Sps case 'c': 19760786Sps return (hshift != 0); 19860786Sps case 'e': /* At end of file? */ 199191930Sdelphij return (eof_displayed()); 20060786Sps case 'f': /* Filename known? */ 20160786Sps return (strcmp(get_filename(curr_ifile), "-") != 0); 20260786Sps case 'l': /* Line number known? */ 20360786Sps case 'd': /* Same as l */ 20460786Sps return (linenums); 20560786Sps case 'L': /* Final line number known? */ 206161478Sdelphij case 'D': /* Final page number known? */ 20760786Sps return (linenums && ch_length() != NULL_POSITION); 20860786Sps case 'm': /* More than one file? */ 209128348Stjr#if TAGS 21089022Sps return (ntags() ? (ntags() > 1) : (nifile() > 1)); 211128348Stjr#else 212128348Stjr return (nifile() > 1); 213128348Stjr#endif 21460786Sps case 'n': /* First prompt in a new file? */ 215128348Stjr#if TAGS 21689022Sps return (ntags() ? 1 : new_file); 217128348Stjr#else 218128348Stjr return (new_file); 219128348Stjr#endif 22060786Sps case 'p': /* Percent into file (bytes) known? */ 22160786Sps return (curr_byte(where) != NULL_POSITION && 22260786Sps ch_length() > 0); 22360786Sps case 'P': /* Percent into file (lines) known? */ 22460786Sps return (currline(where) != 0 && 22560786Sps (len = ch_length()) > 0 && 22660786Sps find_linenum(len) != 0); 22760786Sps case 's': /* Size of file known? */ 22860786Sps case 'B': 22960786Sps return (ch_length() != NULL_POSITION); 23060786Sps case 'x': /* Is there a "next" file? */ 231128348Stjr#if TAGS 23289022Sps if (ntags()) 23389022Sps return (0); 234128348Stjr#endif 23560786Sps return (next_ifile(curr_ifile) != NULL_IFILE); 23660786Sps } 23760786Sps return (0); 23860786Sps} 23960786Sps 24060786Sps/* 24160786Sps * Decode a "percent" prototype character. 24260786Sps * A prototype string may include various "percent" escapes; 24360786Sps * that is, a percent sign followed by a single letter. 24460786Sps * Here we decode that letter and take the appropriate action, 24560786Sps * usually by appending something to the message being built. 24660786Sps */ 24760786Sps static void 24860786Spsprotochar(c, where, iseditproto) 24960786Sps int c; 25060786Sps int where; 25160786Sps int iseditproto; 25260786Sps{ 25360786Sps POSITION pos; 25460786Sps POSITION len; 25560786Sps int n; 256128348Stjr LINENUM linenum; 257128348Stjr LINENUM last_linenum; 25860786Sps IFILE h; 25960786Sps 260161478Sdelphij#undef PAGE_NUM 261161478Sdelphij#define PAGE_NUM(linenum) ((((linenum) - 1) / (sc_height - 1)) + 1) 262161478Sdelphij 26360786Sps switch (c) 26460786Sps { 26560786Sps case 'b': /* Current byte offset */ 26660786Sps pos = curr_byte(where); 26760786Sps if (pos != NULL_POSITION) 26860786Sps ap_pos(pos); 26960786Sps else 27060786Sps ap_quest(); 27160786Sps break; 27260786Sps case 'c': 27360786Sps ap_int(hshift); 27460786Sps break; 27560786Sps case 'd': /* Current page number */ 276128348Stjr linenum = currline(where); 277128348Stjr if (linenum > 0 && sc_height > 1) 278161478Sdelphij ap_linenum(PAGE_NUM(linenum)); 27960786Sps else 28060786Sps ap_quest(); 28160786Sps break; 282161478Sdelphij case 'D': /* Final page number */ 283161478Sdelphij /* Find the page number of the last byte in the file (len-1). */ 28460786Sps len = ch_length(); 285161478Sdelphij if (len == NULL_POSITION) 28660786Sps ap_quest(); 287161478Sdelphij else if (len == 0) 288161478Sdelphij /* An empty file has no pages. */ 289161478Sdelphij ap_linenum(0); 29060786Sps else 291161478Sdelphij { 292161478Sdelphij linenum = find_linenum(len - 1); 293161478Sdelphij if (linenum <= 0) 294161478Sdelphij ap_quest(); 295161478Sdelphij else 296161478Sdelphij ap_linenum(PAGE_NUM(linenum)); 297161478Sdelphij } 29860786Sps break; 29960786Sps#if EDITOR 30060786Sps case 'E': /* Editor name */ 30160786Sps ap_str(editor); 30260786Sps break; 30360786Sps#endif 30460786Sps case 'f': /* File name */ 305128348Stjr ap_str(get_filename(curr_ifile)); 30660786Sps break; 307221715Sdelphij case 'F': /* Last component of file name */ 308221715Sdelphij ap_str(last_component(get_filename(curr_ifile))); 309221715Sdelphij break; 31060786Sps case 'i': /* Index into list of files */ 311128348Stjr#if TAGS 31289022Sps if (ntags()) 31389022Sps ap_int(curr_tag()); 31489022Sps else 315128348Stjr#endif 31689022Sps ap_int(get_index(curr_ifile)); 31760786Sps break; 31860786Sps case 'l': /* Current line number */ 319128348Stjr linenum = currline(where); 320128348Stjr if (linenum != 0) 321128348Stjr ap_linenum(linenum); 32260786Sps else 32360786Sps ap_quest(); 32460786Sps break; 32560786Sps case 'L': /* Final line number */ 32660786Sps len = ch_length(); 32760786Sps if (len == NULL_POSITION || len == ch_zero() || 328128348Stjr (linenum = find_linenum(len)) <= 0) 32960786Sps ap_quest(); 33060786Sps else 331128348Stjr ap_linenum(linenum-1); 33260786Sps break; 33360786Sps case 'm': /* Number of files */ 334128348Stjr#if TAGS 33589022Sps n = ntags(); 33689022Sps if (n) 33789022Sps ap_int(n); 33889022Sps else 339128348Stjr#endif 34089022Sps ap_int(nifile()); 34160786Sps break; 34260786Sps case 'p': /* Percent into file (bytes) */ 34360786Sps pos = curr_byte(where); 34460786Sps len = ch_length(); 34560786Sps if (pos != NULL_POSITION && len > 0) 34660786Sps ap_int(percentage(pos,len)); 34760786Sps else 34860786Sps ap_quest(); 34960786Sps break; 35060786Sps case 'P': /* Percent into file (lines) */ 351128348Stjr linenum = currline(where); 352128348Stjr if (linenum == 0 || 35360786Sps (len = ch_length()) == NULL_POSITION || len == ch_zero() || 354128348Stjr (last_linenum = find_linenum(len)) <= 0) 35560786Sps ap_quest(); 35660786Sps else 357128348Stjr ap_int(percentage(linenum, last_linenum)); 35860786Sps break; 35960786Sps case 's': /* Size of file */ 36060786Sps case 'B': 36160786Sps len = ch_length(); 36260786Sps if (len != NULL_POSITION) 36360786Sps ap_pos(len); 36460786Sps else 36560786Sps ap_quest(); 36660786Sps break; 36760786Sps case 't': /* Truncate trailing spaces in the message */ 36860786Sps while (mp > message && mp[-1] == ' ') 36960786Sps mp--; 370221715Sdelphij *mp = '\0'; 37160786Sps break; 37289022Sps case 'T': /* Type of list */ 373128348Stjr#if TAGS 37489022Sps if (ntags()) 37589022Sps ap_str("tag"); 37689022Sps else 377128348Stjr#endif 37889022Sps ap_str("file"); 37989022Sps break; 38060786Sps case 'x': /* Name of next file */ 38160786Sps h = next_ifile(curr_ifile); 38260786Sps if (h != NULL_IFILE) 383128348Stjr ap_str(get_filename(h)); 384128348Stjr else 38560786Sps ap_quest(); 38660786Sps break; 38760786Sps } 38860786Sps} 38960786Sps 39060786Sps/* 39160786Sps * Skip a false conditional. 39260786Sps * When a false condition is found (either a false IF or the ELSE part 39360786Sps * of a true IF), this routine scans the prototype string to decide 39460786Sps * where to resume parsing the string. 39560786Sps * We must keep track of nested IFs and skip them properly. 39660786Sps */ 397229811Sdim static char constant * 39860786Spsskipcond(p) 399229811Sdim register char constant *p; 40060786Sps{ 40160786Sps register int iflevel; 40260786Sps 40360786Sps /* 40460786Sps * We came in here after processing a ? or :, 40560786Sps * so we start nested one level deep. 40660786Sps */ 40760786Sps iflevel = 1; 40860786Sps 40960786Sps for (;;) switch (*++p) 41060786Sps { 41160786Sps case '?': 41260786Sps /* 41360786Sps * Start of a nested IF. 41460786Sps */ 41560786Sps iflevel++; 41660786Sps break; 41760786Sps case ':': 41860786Sps /* 41960786Sps * Else. 42060786Sps * If this matches the IF we came in here with, 42160786Sps * then we're done. 42260786Sps */ 42360786Sps if (iflevel == 1) 42460786Sps return (p); 42560786Sps break; 42660786Sps case '.': 42760786Sps /* 42860786Sps * Endif. 42960786Sps * If this matches the IF we came in here with, 43060786Sps * then we're done. 43160786Sps */ 43260786Sps if (--iflevel == 0) 43360786Sps return (p); 43460786Sps break; 43560786Sps case '\\': 43660786Sps /* 43760786Sps * Backslash escapes the next character. 43860786Sps */ 43960786Sps ++p; 44060786Sps break; 44160786Sps case '\0': 44260786Sps /* 44360786Sps * Whoops. Hit end of string. 44460786Sps * This is a malformed conditional, but just treat it 44560786Sps * as if all active conditionals ends here. 44660786Sps */ 44760786Sps return (p-1); 44860786Sps } 44960786Sps /*NOTREACHED*/ 45060786Sps} 45160786Sps 45260786Sps/* 45360786Sps * Decode a char that represents a position on the screen. 45460786Sps */ 455229811Sdim static char constant * 45660786Spswherechar(p, wp) 457229811Sdim char constant *p; 45860786Sps int *wp; 45960786Sps{ 46060786Sps switch (*p) 46160786Sps { 46260786Sps case 'b': case 'd': case 'l': case 'p': case 'P': 46360786Sps switch (*++p) 46460786Sps { 46560786Sps case 't': *wp = TOP; break; 46660786Sps case 'm': *wp = MIDDLE; break; 46760786Sps case 'b': *wp = BOTTOM; break; 46860786Sps case 'B': *wp = BOTTOM_PLUS_ONE; break; 46960786Sps case 'j': *wp = adjsline(jump_sline); break; 47060786Sps default: *wp = TOP; p--; break; 47160786Sps } 47260786Sps } 47360786Sps return (p); 47460786Sps} 47560786Sps 47660786Sps/* 47760786Sps * Construct a message based on a prototype string. 47860786Sps */ 47960786Sps public char * 48060786Spspr_expand(proto, maxwidth) 481229811Sdim char constant *proto; 48260786Sps int maxwidth; 48360786Sps{ 484229811Sdim register char constant *p; 48560786Sps register int c; 48660786Sps int where; 48760786Sps 48860786Sps mp = message; 48960786Sps 49060786Sps if (*proto == '\0') 49160786Sps return (""); 49260786Sps 49360786Sps for (p = proto; *p != '\0'; p++) 49460786Sps { 49560786Sps switch (*p) 49660786Sps { 49760786Sps default: /* Just put the character in the message */ 49860786Sps ap_char(*p); 49960786Sps break; 50060786Sps case '\\': /* Backslash escapes the next character */ 50160786Sps p++; 50260786Sps ap_char(*p); 50360786Sps break; 50460786Sps case '?': /* Conditional (IF) */ 50560786Sps if ((c = *++p) == '\0') 50660786Sps --p; 50760786Sps else 50860786Sps { 50960786Sps where = 0; 51060786Sps p = wherechar(p, &where); 51160786Sps if (!cond(c, where)) 51260786Sps p = skipcond(p); 51360786Sps } 51460786Sps break; 51560786Sps case ':': /* ELSE */ 51660786Sps p = skipcond(p); 51760786Sps break; 51860786Sps case '.': /* ENDIF */ 51960786Sps break; 52060786Sps case '%': /* Percent escape */ 52160786Sps if ((c = *++p) == '\0') 52260786Sps --p; 52360786Sps else 52460786Sps { 52560786Sps where = 0; 52660786Sps p = wherechar(p, &where); 52760786Sps protochar(c, where, 52860786Sps#if EDITOR 52960786Sps (proto == editproto)); 53060786Sps#else 53160786Sps 0); 53260786Sps#endif 53360786Sps 53460786Sps } 53560786Sps break; 53660786Sps } 53760786Sps } 53860786Sps 53960786Sps if (mp == message) 540161478Sdelphij return (""); 54160786Sps if (maxwidth > 0 && mp >= message + maxwidth) 54260786Sps { 54360786Sps /* 54460786Sps * Message is too long. 54560786Sps * Return just the final portion of it. 54660786Sps */ 54760786Sps return (mp - maxwidth); 54860786Sps } 54960786Sps return (message); 55060786Sps} 55160786Sps 55260786Sps/* 55360786Sps * Return a message suitable for printing by the "=" command. 55460786Sps */ 55560786Sps public char * 55660786Spseq_message() 55760786Sps{ 55860786Sps return (pr_expand(eqproto, 0)); 55960786Sps} 56060786Sps 56160786Sps/* 56260786Sps * Return a prompt. 56360786Sps * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. 56460786Sps * If we can't come up with an appropriate prompt, return NULL 56560786Sps * and the caller will prompt with a colon. 56660786Sps */ 56760786Sps public char * 56860786Spspr_string() 56960786Sps{ 57089022Sps char *prompt; 571170259Sdelphij int type; 57289022Sps 573170259Sdelphij type = (!less_is_more) ? pr_type : pr_type ? 0 : 1; 57489022Sps prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? 575170259Sdelphij hproto : prproto[type], 57689022Sps sc_width-so_s_width-so_e_width-2); 57789022Sps new_file = 0; 57889022Sps return (prompt); 57960786Sps} 58089022Sps 58189022Sps/* 58289022Sps * Return a message suitable for printing while waiting in the F command. 58389022Sps */ 58489022Sps public char * 58589022Spswait_message() 58689022Sps{ 58789022Sps return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); 58889022Sps} 589