160814Sps/* $FreeBSD$ */ 260786Sps/* 3240121Sdelphij * Copyright (C) 1984-2012 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 * 8240121Sdelphij * For more information, see the README file. 960786Sps */ 1060786Sps 1160786Sps 1260786Sps/* 1360786Sps * Prompting and other messages. 1460786Sps * There are three flavors of prompts, SHORT, MEDIUM and LONG, 1560786Sps * selected by the -m/-M options. 1660786Sps * There is also the "equals message", printed by the = command. 1760786Sps * A prompt is a message composed of various pieces, such as the 1860786Sps * name of the file being viewed, the percentage into the file, etc. 1960786Sps */ 2060786Sps 2160786Sps#include "less.h" 2260786Sps#include "position.h" 2360786Sps 2460786Spsextern int pr_type; 2560786Spsextern int new_file; 2660786Spsextern int sc_width; 2760786Spsextern int so_s_width, so_e_width; 2860786Spsextern int linenums; 2960786Spsextern int hshift; 3060786Spsextern int sc_height; 3160786Spsextern int jump_sline; 32170259Sdelphijextern int less_is_more; 3360786Spsextern IFILE curr_ifile; 3460786Sps#if EDITOR 3560786Spsextern char *editor; 3660786Spsextern char *editproto; 3760786Sps#endif 3860786Sps 3960786Sps/* 4060786Sps * Prototypes for the three flavors of prompts. 4160786Sps * These strings are expanded by pr_expand(). 4260786Sps */ 4360786Spsstatic constant char s_proto[] = 4489022Sps "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t"; 4560786Spsstatic constant char m_proto[] = 4689022Sps "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; 4760786Spsstatic constant char M_proto[] = 4889022Sps "?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"; 4960786Spsstatic constant char e_proto[] = 5089022Sps "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; 5160786Spsstatic constant char h_proto[] = 5260786Sps "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done"; 5389022Spsstatic constant char w_proto[] = 5489022Sps "Waiting for data"; 55170259Sdelphijstatic constant char more_proto[] = 56170259Sdelphij "--More--(?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)"; 5760786Sps 5860786Spspublic char *prproto[3]; 5960786Spspublic char constant *eqproto = e_proto; 6060786Spspublic char constant *hproto = h_proto; 6189022Spspublic char constant *wproto = w_proto; 6260786Sps 6360786Spsstatic char message[PROMPT_SIZE]; 6460786Spsstatic char *mp; 6560786Sps 6660786Sps/* 6760786Sps * Initialize the prompt prototype strings. 6860786Sps */ 6960786Sps public void 7060786Spsinit_prompt() 7160786Sps{ 7260786Sps prproto[0] = save(s_proto); 73170259Sdelphij prproto[1] = save(less_is_more ? more_proto : m_proto); 7460786Sps prproto[2] = save(M_proto); 7560786Sps eqproto = save(e_proto); 7660786Sps hproto = save(h_proto); 7789022Sps wproto = save(w_proto); 7860786Sps} 7960786Sps 8060786Sps/* 8160786Sps * Append a string to the end of the message. 8260786Sps */ 8360786Sps static void 8460786Spsap_str(s) 8560786Sps char *s; 8660786Sps{ 8760786Sps int len; 8860786Sps 8960786Sps len = strlen(s); 9060786Sps if (mp + len >= message + PROMPT_SIZE) 9160786Sps len = message + PROMPT_SIZE - mp - 1; 9260786Sps strncpy(mp, s, len); 9360786Sps mp += len; 9460786Sps *mp = '\0'; 9560786Sps} 9660786Sps 9760786Sps/* 9860786Sps * Append a character to the end of the message. 9960786Sps */ 10060786Sps static void 10160786Spsap_char(c) 10260786Sps char c; 10360786Sps{ 10460786Sps char buf[2]; 10560786Sps 10660786Sps buf[0] = c; 10760786Sps buf[1] = '\0'; 10860786Sps ap_str(buf); 10960786Sps} 11060786Sps 11160786Sps/* 11260786Sps * Append a POSITION (as a decimal integer) to the end of the message. 11360786Sps */ 11460786Sps static void 11560786Spsap_pos(pos) 11660786Sps POSITION pos; 11760786Sps{ 118128348Stjr char buf[INT_STRLEN_BOUND(pos) + 2]; 119128348Stjr 120128348Stjr postoa(pos, buf); 121128348Stjr ap_str(buf); 12260786Sps} 12360786Sps 12460786Sps/* 125128348Stjr * Append a line number to the end of the message. 126128348Stjr */ 127128348Stjr static void 128128348Stjrap_linenum(linenum) 129128348Stjr LINENUM linenum; 130128348Stjr{ 131128348Stjr char buf[INT_STRLEN_BOUND(linenum) + 2]; 132128348Stjr 133128348Stjr linenumtoa(linenum, buf); 134128348Stjr ap_str(buf); 135128348Stjr} 136128348Stjr 137128348Stjr/* 13860786Sps * Append an integer to the end of the message. 13960786Sps */ 14060786Sps static void 141128348Stjrap_int(num) 142128348Stjr int num; 14360786Sps{ 144128348Stjr char buf[INT_STRLEN_BOUND(num) + 2]; 14560786Sps 146128348Stjr inttoa(num, buf); 14760786Sps ap_str(buf); 14860786Sps} 14960786Sps 15060786Sps/* 15160786Sps * Append a question mark to the end of the message. 15260786Sps */ 15360786Sps static void 15460786Spsap_quest() 15560786Sps{ 15660786Sps ap_str("?"); 15760786Sps} 15860786Sps 15960786Sps/* 16060786Sps * Return the "current" byte offset in the file. 16160786Sps */ 16260786Sps static POSITION 16360786Spscurr_byte(where) 16460786Sps int where; 16560786Sps{ 16660786Sps POSITION pos; 16760786Sps 16860786Sps pos = position(where); 169161478Sdelphij while (pos == NULL_POSITION && where >= 0 && where < sc_height-1) 17060786Sps pos = position(++where); 17160786Sps if (pos == NULL_POSITION) 17260786Sps pos = ch_length(); 17360786Sps return (pos); 17460786Sps} 17560786Sps 17660786Sps/* 17760786Sps * Return the value of a prototype conditional. 17860786Sps * A prototype string may include conditionals which consist of a 17960786Sps * question mark followed by a single letter. 18060786Sps * Here we decode that letter and return the appropriate boolean value. 18160786Sps */ 18260786Sps static int 18360786Spscond(c, where) 18460786Sps char c; 18560786Sps int where; 18660786Sps{ 18760786Sps POSITION len; 18860786Sps 18960786Sps switch (c) 19060786Sps { 19160786Sps case 'a': /* Anything in the message yet? */ 19260786Sps return (mp > message); 19360786Sps case 'b': /* Current byte offset known? */ 19460786Sps return (curr_byte(where) != NULL_POSITION); 19560786Sps case 'c': 19660786Sps return (hshift != 0); 19760786Sps case 'e': /* At end of file? */ 198191930Sdelphij return (eof_displayed()); 19960786Sps case 'f': /* Filename known? */ 20060786Sps return (strcmp(get_filename(curr_ifile), "-") != 0); 20160786Sps case 'l': /* Line number known? */ 20260786Sps case 'd': /* Same as l */ 20360786Sps return (linenums); 20460786Sps case 'L': /* Final line number known? */ 205161478Sdelphij case 'D': /* Final page number known? */ 20660786Sps return (linenums && ch_length() != NULL_POSITION); 20760786Sps case 'm': /* More than one file? */ 208128348Stjr#if TAGS 20989022Sps return (ntags() ? (ntags() > 1) : (nifile() > 1)); 210128348Stjr#else 211128348Stjr return (nifile() > 1); 212128348Stjr#endif 21360786Sps case 'n': /* First prompt in a new file? */ 214128348Stjr#if TAGS 21589022Sps return (ntags() ? 1 : new_file); 216128348Stjr#else 217128348Stjr return (new_file); 218128348Stjr#endif 21960786Sps case 'p': /* Percent into file (bytes) known? */ 22060786Sps return (curr_byte(where) != NULL_POSITION && 22160786Sps ch_length() > 0); 22260786Sps case 'P': /* Percent into file (lines) known? */ 22360786Sps return (currline(where) != 0 && 22460786Sps (len = ch_length()) > 0 && 22560786Sps find_linenum(len) != 0); 22660786Sps case 's': /* Size of file known? */ 22760786Sps case 'B': 22860786Sps return (ch_length() != NULL_POSITION); 22960786Sps case 'x': /* Is there a "next" file? */ 230128348Stjr#if TAGS 23189022Sps if (ntags()) 23289022Sps return (0); 233128348Stjr#endif 23460786Sps return (next_ifile(curr_ifile) != NULL_IFILE); 23560786Sps } 23660786Sps return (0); 23760786Sps} 23860786Sps 23960786Sps/* 24060786Sps * Decode a "percent" prototype character. 24160786Sps * A prototype string may include various "percent" escapes; 24260786Sps * that is, a percent sign followed by a single letter. 24360786Sps * Here we decode that letter and take the appropriate action, 24460786Sps * usually by appending something to the message being built. 24560786Sps */ 24660786Sps static void 24760786Spsprotochar(c, where, iseditproto) 24860786Sps int c; 24960786Sps int where; 25060786Sps int iseditproto; 25160786Sps{ 25260786Sps POSITION pos; 25360786Sps POSITION len; 25460786Sps int n; 255128348Stjr LINENUM linenum; 256128348Stjr LINENUM last_linenum; 25760786Sps IFILE h; 25860786Sps 259161478Sdelphij#undef PAGE_NUM 260161478Sdelphij#define PAGE_NUM(linenum) ((((linenum) - 1) / (sc_height - 1)) + 1) 261161478Sdelphij 26260786Sps switch (c) 26360786Sps { 26460786Sps case 'b': /* Current byte offset */ 26560786Sps pos = curr_byte(where); 26660786Sps if (pos != NULL_POSITION) 26760786Sps ap_pos(pos); 26860786Sps else 26960786Sps ap_quest(); 27060786Sps break; 27160786Sps case 'c': 27260786Sps ap_int(hshift); 27360786Sps break; 27460786Sps case 'd': /* Current page number */ 275128348Stjr linenum = currline(where); 276128348Stjr if (linenum > 0 && sc_height > 1) 277161478Sdelphij ap_linenum(PAGE_NUM(linenum)); 27860786Sps else 27960786Sps ap_quest(); 28060786Sps break; 281161478Sdelphij case 'D': /* Final page number */ 282161478Sdelphij /* Find the page number of the last byte in the file (len-1). */ 28360786Sps len = ch_length(); 284161478Sdelphij if (len == NULL_POSITION) 28560786Sps ap_quest(); 286161478Sdelphij else if (len == 0) 287161478Sdelphij /* An empty file has no pages. */ 288161478Sdelphij ap_linenum(0); 28960786Sps else 290161478Sdelphij { 291161478Sdelphij linenum = find_linenum(len - 1); 292161478Sdelphij if (linenum <= 0) 293161478Sdelphij ap_quest(); 294161478Sdelphij else 295161478Sdelphij ap_linenum(PAGE_NUM(linenum)); 296161478Sdelphij } 29760786Sps break; 29860786Sps#if EDITOR 29960786Sps case 'E': /* Editor name */ 30060786Sps ap_str(editor); 30160786Sps break; 30260786Sps#endif 30360786Sps case 'f': /* File name */ 304128348Stjr ap_str(get_filename(curr_ifile)); 30560786Sps break; 306221715Sdelphij case 'F': /* Last component of file name */ 307221715Sdelphij ap_str(last_component(get_filename(curr_ifile))); 308221715Sdelphij break; 30960786Sps case 'i': /* Index into list of files */ 310128348Stjr#if TAGS 31189022Sps if (ntags()) 31289022Sps ap_int(curr_tag()); 31389022Sps else 314128348Stjr#endif 31589022Sps ap_int(get_index(curr_ifile)); 31660786Sps break; 31760786Sps case 'l': /* Current line number */ 318128348Stjr linenum = currline(where); 319128348Stjr if (linenum != 0) 320128348Stjr ap_linenum(linenum); 32160786Sps else 32260786Sps ap_quest(); 32360786Sps break; 32460786Sps case 'L': /* Final line number */ 32560786Sps len = ch_length(); 32660786Sps if (len == NULL_POSITION || len == ch_zero() || 327128348Stjr (linenum = find_linenum(len)) <= 0) 32860786Sps ap_quest(); 32960786Sps else 330128348Stjr ap_linenum(linenum-1); 33160786Sps break; 33260786Sps case 'm': /* Number of files */ 333128348Stjr#if TAGS 33489022Sps n = ntags(); 33589022Sps if (n) 33689022Sps ap_int(n); 33789022Sps else 338128348Stjr#endif 33989022Sps ap_int(nifile()); 34060786Sps break; 34160786Sps case 'p': /* Percent into file (bytes) */ 34260786Sps pos = curr_byte(where); 34360786Sps len = ch_length(); 34460786Sps if (pos != NULL_POSITION && len > 0) 34560786Sps ap_int(percentage(pos,len)); 34660786Sps else 34760786Sps ap_quest(); 34860786Sps break; 34960786Sps case 'P': /* Percent into file (lines) */ 350128348Stjr linenum = currline(where); 351128348Stjr if (linenum == 0 || 35260786Sps (len = ch_length()) == NULL_POSITION || len == ch_zero() || 353128348Stjr (last_linenum = find_linenum(len)) <= 0) 35460786Sps ap_quest(); 35560786Sps else 356128348Stjr ap_int(percentage(linenum, last_linenum)); 35760786Sps break; 35860786Sps case 's': /* Size of file */ 35960786Sps case 'B': 36060786Sps len = ch_length(); 36160786Sps if (len != NULL_POSITION) 36260786Sps ap_pos(len); 36360786Sps else 36460786Sps ap_quest(); 36560786Sps break; 36660786Sps case 't': /* Truncate trailing spaces in the message */ 36760786Sps while (mp > message && mp[-1] == ' ') 36860786Sps mp--; 369221715Sdelphij *mp = '\0'; 37060786Sps break; 37189022Sps case 'T': /* Type of list */ 372128348Stjr#if TAGS 37389022Sps if (ntags()) 37489022Sps ap_str("tag"); 37589022Sps else 376128348Stjr#endif 37789022Sps ap_str("file"); 37889022Sps break; 37960786Sps case 'x': /* Name of next file */ 38060786Sps h = next_ifile(curr_ifile); 38160786Sps if (h != NULL_IFILE) 382128348Stjr ap_str(get_filename(h)); 383128348Stjr else 38460786Sps ap_quest(); 38560786Sps break; 38660786Sps } 38760786Sps} 38860786Sps 38960786Sps/* 39060786Sps * Skip a false conditional. 39160786Sps * When a false condition is found (either a false IF or the ELSE part 39260786Sps * of a true IF), this routine scans the prototype string to decide 39360786Sps * where to resume parsing the string. 39460786Sps * We must keep track of nested IFs and skip them properly. 39560786Sps */ 396240121Sdelphij static constant char * 39760786Spsskipcond(p) 398240121Sdelphij register constant char *p; 39960786Sps{ 40060786Sps register int iflevel; 40160786Sps 40260786Sps /* 40360786Sps * We came in here after processing a ? or :, 40460786Sps * so we start nested one level deep. 40560786Sps */ 40660786Sps iflevel = 1; 40760786Sps 40860786Sps for (;;) switch (*++p) 40960786Sps { 41060786Sps case '?': 41160786Sps /* 41260786Sps * Start of a nested IF. 41360786Sps */ 41460786Sps iflevel++; 41560786Sps break; 41660786Sps case ':': 41760786Sps /* 41860786Sps * Else. 41960786Sps * If this matches the IF we came in here with, 42060786Sps * then we're done. 42160786Sps */ 42260786Sps if (iflevel == 1) 42360786Sps return (p); 42460786Sps break; 42560786Sps case '.': 42660786Sps /* 42760786Sps * Endif. 42860786Sps * If this matches the IF we came in here with, 42960786Sps * then we're done. 43060786Sps */ 43160786Sps if (--iflevel == 0) 43260786Sps return (p); 43360786Sps break; 43460786Sps case '\\': 43560786Sps /* 43660786Sps * Backslash escapes the next character. 43760786Sps */ 43860786Sps ++p; 43960786Sps break; 44060786Sps case '\0': 44160786Sps /* 44260786Sps * Whoops. Hit end of string. 44360786Sps * This is a malformed conditional, but just treat it 44460786Sps * as if all active conditionals ends here. 44560786Sps */ 44660786Sps return (p-1); 44760786Sps } 44860786Sps /*NOTREACHED*/ 44960786Sps} 45060786Sps 45160786Sps/* 45260786Sps * Decode a char that represents a position on the screen. 45360786Sps */ 454240121Sdelphij static constant char * 45560786Spswherechar(p, wp) 456229811Sdim char constant *p; 45760786Sps int *wp; 45860786Sps{ 45960786Sps switch (*p) 46060786Sps { 46160786Sps case 'b': case 'd': case 'l': case 'p': case 'P': 46260786Sps switch (*++p) 46360786Sps { 46460786Sps case 't': *wp = TOP; break; 46560786Sps case 'm': *wp = MIDDLE; break; 46660786Sps case 'b': *wp = BOTTOM; break; 46760786Sps case 'B': *wp = BOTTOM_PLUS_ONE; break; 46860786Sps case 'j': *wp = adjsline(jump_sline); break; 46960786Sps default: *wp = TOP; p--; break; 47060786Sps } 47160786Sps } 47260786Sps return (p); 47360786Sps} 47460786Sps 47560786Sps/* 47660786Sps * Construct a message based on a prototype string. 47760786Sps */ 47860786Sps public char * 47960786Spspr_expand(proto, maxwidth) 480240121Sdelphij constant char *proto; 48160786Sps int maxwidth; 48260786Sps{ 483240121Sdelphij register constant char *p; 48460786Sps register int c; 48560786Sps int where; 48660786Sps 48760786Sps mp = message; 48860786Sps 48960786Sps if (*proto == '\0') 49060786Sps return (""); 49160786Sps 49260786Sps for (p = proto; *p != '\0'; p++) 49360786Sps { 49460786Sps switch (*p) 49560786Sps { 49660786Sps default: /* Just put the character in the message */ 49760786Sps ap_char(*p); 49860786Sps break; 49960786Sps case '\\': /* Backslash escapes the next character */ 50060786Sps p++; 50160786Sps ap_char(*p); 50260786Sps break; 50360786Sps case '?': /* Conditional (IF) */ 50460786Sps if ((c = *++p) == '\0') 50560786Sps --p; 50660786Sps else 50760786Sps { 50860786Sps where = 0; 50960786Sps p = wherechar(p, &where); 51060786Sps if (!cond(c, where)) 51160786Sps p = skipcond(p); 51260786Sps } 51360786Sps break; 51460786Sps case ':': /* ELSE */ 51560786Sps p = skipcond(p); 51660786Sps break; 51760786Sps case '.': /* ENDIF */ 51860786Sps break; 51960786Sps case '%': /* Percent escape */ 52060786Sps if ((c = *++p) == '\0') 52160786Sps --p; 52260786Sps else 52360786Sps { 52460786Sps where = 0; 52560786Sps p = wherechar(p, &where); 52660786Sps protochar(c, where, 52760786Sps#if EDITOR 52860786Sps (proto == editproto)); 52960786Sps#else 53060786Sps 0); 53160786Sps#endif 53260786Sps 53360786Sps } 53460786Sps break; 53560786Sps } 53660786Sps } 53760786Sps 53860786Sps if (mp == message) 539161478Sdelphij return (""); 54060786Sps if (maxwidth > 0 && mp >= message + maxwidth) 54160786Sps { 54260786Sps /* 54360786Sps * Message is too long. 54460786Sps * Return just the final portion of it. 54560786Sps */ 54660786Sps return (mp - maxwidth); 54760786Sps } 54860786Sps return (message); 54960786Sps} 55060786Sps 55160786Sps/* 55260786Sps * Return a message suitable for printing by the "=" command. 55360786Sps */ 55460786Sps public char * 55560786Spseq_message() 55660786Sps{ 55760786Sps return (pr_expand(eqproto, 0)); 55860786Sps} 55960786Sps 56060786Sps/* 56160786Sps * Return a prompt. 56260786Sps * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. 56360786Sps * If we can't come up with an appropriate prompt, return NULL 56460786Sps * and the caller will prompt with a colon. 56560786Sps */ 56660786Sps public char * 56760786Spspr_string() 56860786Sps{ 56989022Sps char *prompt; 570170259Sdelphij int type; 57189022Sps 572170259Sdelphij type = (!less_is_more) ? pr_type : pr_type ? 0 : 1; 57389022Sps prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? 574170259Sdelphij hproto : prproto[type], 57589022Sps sc_width-so_s_width-so_e_width-2); 57689022Sps new_file = 0; 57789022Sps return (prompt); 57860786Sps} 57989022Sps 58089022Sps/* 58189022Sps * Return a message suitable for printing while waiting in the F command. 58289022Sps */ 58389022Sps public char * 58489022Spswait_message() 58589022Sps{ 58689022Sps return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); 58789022Sps} 588