1238730Sdelphij/* 2369759Sgit2svn * Copyright (C) 1984-2021 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 960786Sps 1060786Sps 1160786Sps/* 1260786Sps * Prompting and other messages. 1360786Sps * There are three flavors of prompts, SHORT, MEDIUM and LONG, 1460786Sps * selected by the -m/-M options. 1560786Sps * There is also the "equals message", printed by the = command. 1660786Sps * A prompt is a message composed of various pieces, such as the 1760786Sps * name of the file being viewed, the percentage into the file, etc. 1860786Sps */ 1960786Sps 2060786Sps#include "less.h" 2160786Sps#include "position.h" 2260786Sps 2360786Spsextern int pr_type; 2460786Spsextern int new_file; 2560786Spsextern int sc_width; 2660786Spsextern int so_s_width, so_e_width; 2760786Spsextern int linenums; 2860786Spsextern int hshift; 2960786Spsextern int sc_height; 3060786Spsextern int jump_sline; 31170259Sdelphijextern int less_is_more; 3260786Spsextern IFILE curr_ifile; 3360786Sps#if EDITOR 3460786Spsextern char *editor; 3560786Spsextern char *editproto; 3660786Sps#endif 3760786Sps 3860786Sps/* 3960786Sps * Prototypes for the three flavors of prompts. 4060786Sps * These strings are expanded by pr_expand(). 4160786Sps */ 4260786Spsstatic constant char s_proto[] = 4389022Sps "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t"; 4460786Spsstatic constant char m_proto[] = 4589022Sps "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; 4660786Spsstatic constant char M_proto[] = 4789022Sps "?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"; 4860786Spsstatic constant char e_proto[] = 4989022Sps "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; 5060786Spsstatic constant char h_proto[] = 5160786Sps "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done"; 5289022Spsstatic constant char w_proto[] = 5389022Sps "Waiting for data"; 54170259Sdelphijstatic constant char more_proto[] = 55170259Sdelphij "--More--(?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)"; 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 69355504Sdelphijinit_prompt(VOID_PARAM) 7060786Sps{ 7160786Sps prproto[0] = save(s_proto); 72170259Sdelphij prproto[1] = save(less_is_more ? more_proto : 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 88293190Sdelphij len = (int) strlen(s); 8960786Sps if (mp + len >= message + PROMPT_SIZE) 90293190Sdelphij len = (int) (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 */ 126369759Sgit2svn 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 153355504Sdelphijap_quest(VOID_PARAM) 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 { 190369759Sgit2svn case 'a': /* Anything in the message yet? */ 19160786Sps return (mp > message); 192369759Sgit2svn case 'b': /* Current byte offset known? */ 19360786Sps return (curr_byte(where) != NULL_POSITION); 19460786Sps case 'c': 19560786Sps return (hshift != 0); 196369759Sgit2svn case 'e': /* At end of file? */ 197191930Sdelphij return (eof_displayed()); 198369759Sgit2svn case 'f': /* Filename known? */ 199355504Sdelphij case 'g': 20060786Sps return (strcmp(get_filename(curr_ifile), "-") != 0); 201369759Sgit2svn case 'l': /* Line number known? */ 202369759Sgit2svn case 'd': /* Same as l */ 203355504Sdelphij if (!linenums) 204355504Sdelphij return 0; 205355504Sdelphij return (currline(where) != 0); 206369759Sgit2svn case 'L': /* Final line number known? */ 207369759Sgit2svn case 'D': /* Final page number known? */ 20860786Sps return (linenums && ch_length() != NULL_POSITION); 209369759Sgit2svn case 'm': /* More than one file? */ 210128348Stjr#if TAGS 21189022Sps return (ntags() ? (ntags() > 1) : (nifile() > 1)); 212128348Stjr#else 213128348Stjr return (nifile() > 1); 214128348Stjr#endif 215369759Sgit2svn case 'n': /* First prompt in a new file? */ 216128348Stjr#if TAGS 21789022Sps return (ntags() ? 1 : new_file); 218128348Stjr#else 219128348Stjr return (new_file); 220128348Stjr#endif 221369759Sgit2svn case 'p': /* Percent into file (bytes) known? */ 22260786Sps return (curr_byte(where) != NULL_POSITION && 22360786Sps ch_length() > 0); 224369759Sgit2svn case 'P': /* Percent into file (lines) known? */ 22560786Sps return (currline(where) != 0 && 22660786Sps (len = ch_length()) > 0 && 22760786Sps find_linenum(len) != 0); 228369759Sgit2svn case 's': /* Size of file known? */ 22960786Sps case 'B': 23060786Sps return (ch_length() != NULL_POSITION); 231369759Sgit2svn case 'x': /* Is there a "next" file? */ 232128348Stjr#if TAGS 23389022Sps if (ntags()) 23489022Sps return (0); 235128348Stjr#endif 23660786Sps return (next_ifile(curr_ifile) != NULL_IFILE); 23760786Sps } 23860786Sps return (0); 23960786Sps} 24060786Sps 24160786Sps/* 24260786Sps * Decode a "percent" prototype character. 24360786Sps * A prototype string may include various "percent" escapes; 24460786Sps * that is, a percent sign followed by a single letter. 24560786Sps * Here we decode that letter and take the appropriate action, 24660786Sps * usually by appending something to the message being built. 24760786Sps */ 24860786Sps static void 24960786Spsprotochar(c, where, iseditproto) 25060786Sps int c; 25160786Sps int where; 25260786Sps int iseditproto; 25360786Sps{ 25460786Sps POSITION pos; 25560786Sps POSITION len; 25660786Sps int n; 257128348Stjr LINENUM linenum; 258128348Stjr LINENUM last_linenum; 25960786Sps IFILE h; 260355504Sdelphij char *s; 26160786Sps 262161478Sdelphij#undef PAGE_NUM 263161478Sdelphij#define PAGE_NUM(linenum) ((((linenum) - 1) / (sc_height - 1)) + 1) 264161478Sdelphij 26560786Sps switch (c) 26660786Sps { 267369759Sgit2svn case 'b': /* Current byte offset */ 26860786Sps pos = curr_byte(where); 26960786Sps if (pos != NULL_POSITION) 27060786Sps ap_pos(pos); 27160786Sps else 27260786Sps ap_quest(); 27360786Sps break; 27460786Sps case 'c': 27560786Sps ap_int(hshift); 27660786Sps break; 277369759Sgit2svn case 'd': /* Current page number */ 278128348Stjr linenum = currline(where); 279128348Stjr if (linenum > 0 && sc_height > 1) 280161478Sdelphij ap_linenum(PAGE_NUM(linenum)); 28160786Sps else 28260786Sps ap_quest(); 28360786Sps break; 284369759Sgit2svn case 'D': /* Final page number */ 285161478Sdelphij /* Find the page number of the last byte in the file (len-1). */ 28660786Sps len = ch_length(); 287161478Sdelphij if (len == NULL_POSITION) 28860786Sps ap_quest(); 289161478Sdelphij else if (len == 0) 290161478Sdelphij /* An empty file has no pages. */ 291161478Sdelphij ap_linenum(0); 29260786Sps else 293161478Sdelphij { 294161478Sdelphij linenum = find_linenum(len - 1); 295161478Sdelphij if (linenum <= 0) 296161478Sdelphij ap_quest(); 297161478Sdelphij else 298161478Sdelphij ap_linenum(PAGE_NUM(linenum)); 299161478Sdelphij } 30060786Sps break; 30160786Sps#if EDITOR 302369759Sgit2svn case 'E': /* Editor name */ 30360786Sps ap_str(editor); 30460786Sps break; 30560786Sps#endif 306369759Sgit2svn case 'f': /* File name */ 307128348Stjr ap_str(get_filename(curr_ifile)); 30860786Sps break; 309369759Sgit2svn case 'F': /* Last component of file name */ 310221715Sdelphij ap_str(last_component(get_filename(curr_ifile))); 311221715Sdelphij break; 312369759Sgit2svn case 'g': /* Shell-escaped file name */ 313355504Sdelphij s = shell_quote(get_filename(curr_ifile)); 314355504Sdelphij ap_str(s); 315355504Sdelphij free(s); 316355504Sdelphij break; 317369759Sgit2svn case 'i': /* Index into list of files */ 318128348Stjr#if TAGS 31989022Sps if (ntags()) 32089022Sps ap_int(curr_tag()); 32189022Sps else 322128348Stjr#endif 32389022Sps ap_int(get_index(curr_ifile)); 32460786Sps break; 325369759Sgit2svn case 'l': /* Current line number */ 326128348Stjr linenum = currline(where); 327128348Stjr if (linenum != 0) 328128348Stjr ap_linenum(linenum); 32960786Sps else 33060786Sps ap_quest(); 33160786Sps break; 332369759Sgit2svn case 'L': /* Final line number */ 33360786Sps len = ch_length(); 33460786Sps if (len == NULL_POSITION || len == ch_zero() || 335128348Stjr (linenum = find_linenum(len)) <= 0) 33660786Sps ap_quest(); 33760786Sps else 338128348Stjr ap_linenum(linenum-1); 33960786Sps break; 340369759Sgit2svn case 'm': /* Number of files */ 341128348Stjr#if TAGS 34289022Sps n = ntags(); 34389022Sps if (n) 34489022Sps ap_int(n); 34589022Sps else 346128348Stjr#endif 34789022Sps ap_int(nifile()); 34860786Sps break; 349369759Sgit2svn case 'p': /* Percent into file (bytes) */ 35060786Sps pos = curr_byte(where); 35160786Sps len = ch_length(); 35260786Sps if (pos != NULL_POSITION && len > 0) 35360786Sps ap_int(percentage(pos,len)); 35460786Sps else 35560786Sps ap_quest(); 35660786Sps break; 357369759Sgit2svn case 'P': /* Percent into file (lines) */ 358128348Stjr linenum = currline(where); 359128348Stjr if (linenum == 0 || 36060786Sps (len = ch_length()) == NULL_POSITION || len == ch_zero() || 361128348Stjr (last_linenum = find_linenum(len)) <= 0) 36260786Sps ap_quest(); 36360786Sps else 364128348Stjr ap_int(percentage(linenum, last_linenum)); 36560786Sps break; 366369759Sgit2svn case 's': /* Size of file */ 36760786Sps case 'B': 36860786Sps len = ch_length(); 36960786Sps if (len != NULL_POSITION) 37060786Sps ap_pos(len); 37160786Sps else 37260786Sps ap_quest(); 37360786Sps break; 374369759Sgit2svn case 't': /* Truncate trailing spaces in the message */ 37560786Sps while (mp > message && mp[-1] == ' ') 37660786Sps mp--; 377221715Sdelphij *mp = '\0'; 37860786Sps break; 379369759Sgit2svn case 'T': /* Type of list */ 380128348Stjr#if TAGS 38189022Sps if (ntags()) 38289022Sps ap_str("tag"); 38389022Sps else 384128348Stjr#endif 38589022Sps ap_str("file"); 38689022Sps break; 387369759Sgit2svn case 'x': /* Name of next file */ 38860786Sps h = next_ifile(curr_ifile); 38960786Sps if (h != NULL_IFILE) 390128348Stjr ap_str(get_filename(h)); 391128348Stjr else 39260786Sps ap_quest(); 39360786Sps break; 39460786Sps } 39560786Sps} 39660786Sps 39760786Sps/* 39860786Sps * Skip a false conditional. 39960786Sps * When a false condition is found (either a false IF or the ELSE part 40060786Sps * of a true IF), this routine scans the prototype string to decide 40160786Sps * where to resume parsing the string. 40260786Sps * We must keep track of nested IFs and skip them properly. 40360786Sps */ 404237613Sdelphij static constant char * 40560786Spsskipcond(p) 406330570Sdelphij constant char *p; 40760786Sps{ 408330570Sdelphij int iflevel; 40960786Sps 41060786Sps /* 41160786Sps * We came in here after processing a ? or :, 41260786Sps * so we start nested one level deep. 41360786Sps */ 41460786Sps iflevel = 1; 41560786Sps 41660786Sps for (;;) switch (*++p) 41760786Sps { 41860786Sps case '?': 41960786Sps /* 42060786Sps * Start of a nested IF. 42160786Sps */ 42260786Sps iflevel++; 42360786Sps break; 42460786Sps case ':': 42560786Sps /* 42660786Sps * Else. 42760786Sps * If this matches the IF we came in here with, 42860786Sps * then we're done. 42960786Sps */ 43060786Sps if (iflevel == 1) 43160786Sps return (p); 43260786Sps break; 43360786Sps case '.': 43460786Sps /* 43560786Sps * Endif. 43660786Sps * If this matches the IF we came in here with, 43760786Sps * then we're done. 43860786Sps */ 43960786Sps if (--iflevel == 0) 44060786Sps return (p); 44160786Sps break; 44260786Sps case '\\': 44360786Sps /* 44460786Sps * Backslash escapes the next character. 44560786Sps */ 44660786Sps ++p; 44760786Sps break; 44860786Sps case '\0': 44960786Sps /* 45060786Sps * Whoops. Hit end of string. 45160786Sps * This is a malformed conditional, but just treat it 45260786Sps * as if all active conditionals ends here. 45360786Sps */ 45460786Sps return (p-1); 45560786Sps } 45660786Sps /*NOTREACHED*/ 45760786Sps} 45860786Sps 45960786Sps/* 46060786Sps * Decode a char that represents a position on the screen. 46160786Sps */ 462237613Sdelphij static constant char * 46360786Spswherechar(p, wp) 464229196Sdim char constant *p; 46560786Sps int *wp; 46660786Sps{ 46760786Sps switch (*p) 46860786Sps { 46960786Sps case 'b': case 'd': case 'l': case 'p': case 'P': 47060786Sps switch (*++p) 47160786Sps { 472369759Sgit2svn case 't': *wp = TOP; break; 473369759Sgit2svn case 'm': *wp = MIDDLE; break; 474369759Sgit2svn case 'b': *wp = BOTTOM; break; 475369759Sgit2svn case 'B': *wp = BOTTOM_PLUS_ONE; break; 476330570Sdelphij case 'j': *wp = sindex_from_sline(jump_sline); break; 477369759Sgit2svn default: *wp = TOP; p--; break; 47860786Sps } 47960786Sps } 48060786Sps return (p); 48160786Sps} 48260786Sps 48360786Sps/* 48460786Sps * Construct a message based on a prototype string. 48560786Sps */ 48660786Sps public char * 48760786Spspr_expand(proto, maxwidth) 488237613Sdelphij constant char *proto; 48960786Sps int maxwidth; 49060786Sps{ 491330570Sdelphij constant char *p; 492330570Sdelphij int c; 49360786Sps int where; 49460786Sps 49560786Sps mp = message; 49660786Sps 49760786Sps if (*proto == '\0') 49860786Sps return (""); 49960786Sps 50060786Sps for (p = proto; *p != '\0'; p++) 50160786Sps { 50260786Sps switch (*p) 50360786Sps { 504369759Sgit2svn default: /* Just put the character in the message */ 50560786Sps ap_char(*p); 50660786Sps break; 507369759Sgit2svn case '\\': /* Backslash escapes the next character */ 50860786Sps p++; 50960786Sps ap_char(*p); 51060786Sps break; 511369759Sgit2svn case '?': /* Conditional (IF) */ 51260786Sps if ((c = *++p) == '\0') 51360786Sps --p; 51460786Sps else 51560786Sps { 51660786Sps where = 0; 51760786Sps p = wherechar(p, &where); 51860786Sps if (!cond(c, where)) 51960786Sps p = skipcond(p); 52060786Sps } 52160786Sps break; 522369759Sgit2svn case ':': /* ELSE */ 52360786Sps p = skipcond(p); 52460786Sps break; 525369759Sgit2svn case '.': /* ENDIF */ 52660786Sps break; 527369759Sgit2svn case '%': /* Percent escape */ 52860786Sps if ((c = *++p) == '\0') 52960786Sps --p; 53060786Sps else 53160786Sps { 53260786Sps where = 0; 53360786Sps p = wherechar(p, &where); 53460786Sps protochar(c, where, 53560786Sps#if EDITOR 53660786Sps (proto == editproto)); 53760786Sps#else 53860786Sps 0); 53960786Sps#endif 54060786Sps 54160786Sps } 54260786Sps break; 54360786Sps } 54460786Sps } 54560786Sps 54660786Sps if (mp == message) 547161478Sdelphij return (""); 54860786Sps if (maxwidth > 0 && mp >= message + maxwidth) 54960786Sps { 55060786Sps /* 55160786Sps * Message is too long. 55260786Sps * Return just the final portion of it. 55360786Sps */ 55460786Sps return (mp - maxwidth); 55560786Sps } 55660786Sps return (message); 55760786Sps} 55860786Sps 55960786Sps/* 56060786Sps * Return a message suitable for printing by the "=" command. 56160786Sps */ 56260786Sps public char * 563355504Sdelphijeq_message(VOID_PARAM) 56460786Sps{ 565229195Sdim return (pr_expand(eqproto, 0)); 56660786Sps} 56760786Sps 56860786Sps/* 56960786Sps * Return a prompt. 57060786Sps * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. 57160786Sps * If we can't come up with an appropriate prompt, return NULL 57260786Sps * and the caller will prompt with a colon. 57360786Sps */ 57460786Sps public char * 575355504Sdelphijpr_string(VOID_PARAM) 57660786Sps{ 57789022Sps char *prompt; 578170259Sdelphij int type; 57989022Sps 580170259Sdelphij type = (!less_is_more) ? pr_type : pr_type ? 0 : 1; 58189022Sps prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? 582229195Sdim hproto : prproto[type], 58389022Sps sc_width-so_s_width-so_e_width-2); 58489022Sps new_file = 0; 58589022Sps return (prompt); 58660786Sps} 58789022Sps 58889022Sps/* 58989022Sps * Return a message suitable for printing while waiting in the F command. 59089022Sps */ 59189022Sps public char * 592355504Sdelphijwait_message(VOID_PARAM) 59389022Sps{ 594229195Sdim return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); 59589022Sps} 596