1238730Sdelphij/* 2330571Sdelphij * Copyright (C) 1984-2017 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 6960786Spsinit_prompt() 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 88294286Sdelphij len = (int) strlen(s); 8960786Sps if (mp + len >= message + PROMPT_SIZE) 90294286Sdelphij 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 */ 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? */ 197191930Sdelphij return (eof_displayed()); 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; 305221715Sdelphij case 'F': /* Last component of file name */ 306221715Sdelphij ap_str(last_component(get_filename(curr_ifile))); 307221715Sdelphij break; 30860786Sps case 'i': /* Index into list of files */ 309128348Stjr#if TAGS 31089022Sps if (ntags()) 31189022Sps ap_int(curr_tag()); 31289022Sps else 313128348Stjr#endif 31489022Sps ap_int(get_index(curr_ifile)); 31560786Sps break; 31660786Sps case 'l': /* Current line number */ 317128348Stjr linenum = currline(where); 318128348Stjr if (linenum != 0) 319128348Stjr ap_linenum(linenum); 32060786Sps else 32160786Sps ap_quest(); 32260786Sps break; 32360786Sps case 'L': /* Final line number */ 32460786Sps len = ch_length(); 32560786Sps if (len == NULL_POSITION || len == ch_zero() || 326128348Stjr (linenum = find_linenum(len)) <= 0) 32760786Sps ap_quest(); 32860786Sps else 329128348Stjr ap_linenum(linenum-1); 33060786Sps break; 33160786Sps case 'm': /* Number of files */ 332128348Stjr#if TAGS 33389022Sps n = ntags(); 33489022Sps if (n) 33589022Sps ap_int(n); 33689022Sps else 337128348Stjr#endif 33889022Sps ap_int(nifile()); 33960786Sps break; 34060786Sps case 'p': /* Percent into file (bytes) */ 34160786Sps pos = curr_byte(where); 34260786Sps len = ch_length(); 34360786Sps if (pos != NULL_POSITION && len > 0) 34460786Sps ap_int(percentage(pos,len)); 34560786Sps else 34660786Sps ap_quest(); 34760786Sps break; 34860786Sps case 'P': /* Percent into file (lines) */ 349128348Stjr linenum = currline(where); 350128348Stjr if (linenum == 0 || 35160786Sps (len = ch_length()) == NULL_POSITION || len == ch_zero() || 352128348Stjr (last_linenum = find_linenum(len)) <= 0) 35360786Sps ap_quest(); 35460786Sps else 355128348Stjr ap_int(percentage(linenum, last_linenum)); 35660786Sps break; 35760786Sps case 's': /* Size of file */ 35860786Sps case 'B': 35960786Sps len = ch_length(); 36060786Sps if (len != NULL_POSITION) 36160786Sps ap_pos(len); 36260786Sps else 36360786Sps ap_quest(); 36460786Sps break; 36560786Sps case 't': /* Truncate trailing spaces in the message */ 36660786Sps while (mp > message && mp[-1] == ' ') 36760786Sps mp--; 368221715Sdelphij *mp = '\0'; 36960786Sps break; 37089022Sps case 'T': /* Type of list */ 371128348Stjr#if TAGS 37289022Sps if (ntags()) 37389022Sps ap_str("tag"); 37489022Sps else 375128348Stjr#endif 37689022Sps ap_str("file"); 37789022Sps break; 37860786Sps case 'x': /* Name of next file */ 37960786Sps h = next_ifile(curr_ifile); 38060786Sps if (h != NULL_IFILE) 381128348Stjr ap_str(get_filename(h)); 382128348Stjr else 38360786Sps ap_quest(); 38460786Sps break; 38560786Sps } 38660786Sps} 38760786Sps 38860786Sps/* 38960786Sps * Skip a false conditional. 39060786Sps * When a false condition is found (either a false IF or the ELSE part 39160786Sps * of a true IF), this routine scans the prototype string to decide 39260786Sps * where to resume parsing the string. 39360786Sps * We must keep track of nested IFs and skip them properly. 39460786Sps */ 395237613Sdelphij static constant char * 39660786Spsskipcond(p) 397330571Sdelphij constant char *p; 39860786Sps{ 399330571Sdelphij int iflevel; 40060786Sps 40160786Sps /* 40260786Sps * We came in here after processing a ? or :, 40360786Sps * so we start nested one level deep. 40460786Sps */ 40560786Sps iflevel = 1; 40660786Sps 40760786Sps for (;;) switch (*++p) 40860786Sps { 40960786Sps case '?': 41060786Sps /* 41160786Sps * Start of a nested IF. 41260786Sps */ 41360786Sps iflevel++; 41460786Sps break; 41560786Sps case ':': 41660786Sps /* 41760786Sps * Else. 41860786Sps * If this matches the IF we came in here with, 41960786Sps * then we're done. 42060786Sps */ 42160786Sps if (iflevel == 1) 42260786Sps return (p); 42360786Sps break; 42460786Sps case '.': 42560786Sps /* 42660786Sps * Endif. 42760786Sps * If this matches the IF we came in here with, 42860786Sps * then we're done. 42960786Sps */ 43060786Sps if (--iflevel == 0) 43160786Sps return (p); 43260786Sps break; 43360786Sps case '\\': 43460786Sps /* 43560786Sps * Backslash escapes the next character. 43660786Sps */ 43760786Sps ++p; 43860786Sps break; 43960786Sps case '\0': 44060786Sps /* 44160786Sps * Whoops. Hit end of string. 44260786Sps * This is a malformed conditional, but just treat it 44360786Sps * as if all active conditionals ends here. 44460786Sps */ 44560786Sps return (p-1); 44660786Sps } 44760786Sps /*NOTREACHED*/ 44860786Sps} 44960786Sps 45060786Sps/* 45160786Sps * Decode a char that represents a position on the screen. 45260786Sps */ 453237613Sdelphij static constant char * 45460786Spswherechar(p, wp) 455229196Sdim char constant *p; 45660786Sps int *wp; 45760786Sps{ 45860786Sps switch (*p) 45960786Sps { 46060786Sps case 'b': case 'd': case 'l': case 'p': case 'P': 46160786Sps switch (*++p) 46260786Sps { 46360786Sps case 't': *wp = TOP; break; 46460786Sps case 'm': *wp = MIDDLE; break; 46560786Sps case 'b': *wp = BOTTOM; break; 46660786Sps case 'B': *wp = BOTTOM_PLUS_ONE; break; 467330571Sdelphij case 'j': *wp = sindex_from_sline(jump_sline); break; 46860786Sps default: *wp = TOP; p--; break; 46960786Sps } 47060786Sps } 47160786Sps return (p); 47260786Sps} 47360786Sps 47460786Sps/* 47560786Sps * Construct a message based on a prototype string. 47660786Sps */ 47760786Sps public char * 47860786Spspr_expand(proto, maxwidth) 479237613Sdelphij constant char *proto; 48060786Sps int maxwidth; 48160786Sps{ 482330571Sdelphij constant char *p; 483330571Sdelphij int c; 48460786Sps int where; 48560786Sps 48660786Sps mp = message; 48760786Sps 48860786Sps if (*proto == '\0') 48960786Sps return (""); 49060786Sps 49160786Sps for (p = proto; *p != '\0'; p++) 49260786Sps { 49360786Sps switch (*p) 49460786Sps { 49560786Sps default: /* Just put the character in the message */ 49660786Sps ap_char(*p); 49760786Sps break; 49860786Sps case '\\': /* Backslash escapes the next character */ 49960786Sps p++; 50060786Sps ap_char(*p); 50160786Sps break; 50260786Sps case '?': /* Conditional (IF) */ 50360786Sps if ((c = *++p) == '\0') 50460786Sps --p; 50560786Sps else 50660786Sps { 50760786Sps where = 0; 50860786Sps p = wherechar(p, &where); 50960786Sps if (!cond(c, where)) 51060786Sps p = skipcond(p); 51160786Sps } 51260786Sps break; 51360786Sps case ':': /* ELSE */ 51460786Sps p = skipcond(p); 51560786Sps break; 51660786Sps case '.': /* ENDIF */ 51760786Sps break; 51860786Sps case '%': /* Percent escape */ 51960786Sps if ((c = *++p) == '\0') 52060786Sps --p; 52160786Sps else 52260786Sps { 52360786Sps where = 0; 52460786Sps p = wherechar(p, &where); 52560786Sps protochar(c, where, 52660786Sps#if EDITOR 52760786Sps (proto == editproto)); 52860786Sps#else 52960786Sps 0); 53060786Sps#endif 53160786Sps 53260786Sps } 53360786Sps break; 53460786Sps } 53560786Sps } 53660786Sps 53760786Sps if (mp == message) 538161478Sdelphij return (""); 53960786Sps if (maxwidth > 0 && mp >= message + maxwidth) 54060786Sps { 54160786Sps /* 54260786Sps * Message is too long. 54360786Sps * Return just the final portion of it. 54460786Sps */ 54560786Sps return (mp - maxwidth); 54660786Sps } 54760786Sps return (message); 54860786Sps} 54960786Sps 55060786Sps/* 55160786Sps * Return a message suitable for printing by the "=" command. 55260786Sps */ 55360786Sps public char * 55460786Spseq_message() 55560786Sps{ 556229195Sdim return (pr_expand(eqproto, 0)); 55760786Sps} 55860786Sps 55960786Sps/* 56060786Sps * Return a prompt. 56160786Sps * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. 56260786Sps * If we can't come up with an appropriate prompt, return NULL 56360786Sps * and the caller will prompt with a colon. 56460786Sps */ 56560786Sps public char * 56660786Spspr_string() 56760786Sps{ 56889022Sps char *prompt; 569170259Sdelphij int type; 57089022Sps 571170259Sdelphij type = (!less_is_more) ? pr_type : pr_type ? 0 : 1; 57289022Sps prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? 573229195Sdim hproto : prproto[type], 57489022Sps sc_width-so_s_width-so_e_width-2); 57589022Sps new_file = 0; 57689022Sps return (prompt); 57760786Sps} 57889022Sps 57989022Sps/* 58089022Sps * Return a message suitable for printing while waiting in the F command. 58189022Sps */ 58289022Sps public char * 58389022Spswait_message() 58489022Sps{ 585229195Sdim return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); 58689022Sps} 587