prompt.c revision 161478
153813Simp/* $FreeBSD: head/contrib/less/prompt.c 161478 2006-08-20 15:50:51Z delphij $ */ 2120330Simp/* 3100213Simp * Copyright (C) 1984-2004 Mark Nudelman 452506Simp * 552506Simp * You may distribute under the terms of either the GNU General Public 6140752Simp * License or the Less License, as specified in the README file. 752506Simp * 852506Simp * For more information about less, or for information on how to 952506Simp * contact the author, see the README file. 1052506Simp */ 1152506Simp 1252506Simp 1352506Simp/* 1452506Simp * Prompting and other messages. 1552506Simp * There are three flavors of prompts, SHORT, MEDIUM and LONG, 1652506Simp * selected by the -m/-M options. 1752506Simp * There is also the "equals message", printed by the = command. 1852506Simp * A prompt is a message composed of various pieces, such as the 1952506Simp * name of the file being viewed, the percentage into the file, etc. 2052506Simp */ 2152506Simp 2252506Simp#include "less.h" 2352506Simp#include "position.h" 2452506Simp 2552506Simpextern int pr_type; 2652506Simpextern int hit_eof; 2752506Simpextern int new_file; 2852506Simpextern int sc_width; 2952506Simpextern int so_s_width, so_e_width; 3052506Simpextern int linenums; 3152506Simpextern int hshift; 3252506Simpextern int sc_height; 3352506Simpextern int jump_sline; 3452506Simpextern IFILE curr_ifile; 3552506Simp#if EDITOR 3652506Simpextern char *editor; 3752506Simpextern char *editproto; 3852506Simp#endif 3952506Simp 4052506Simp/* 41140752Simp * Prototypes for the three flavors of prompts. 42140752Simp * These strings are expanded by pr_expand(). 43140752Simp */ 44140752Simpstatic constant char s_proto[] = 45140752Simp "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t"; 46140752Simpstatic constant char m_proto[] = 47140752Simp "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; 48140752Simpstatic constant char M_proto[] = 49140752Simp "?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"; 50140752Simpstatic constant char e_proto[] = 51140752Simp "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; 52140752Simpstatic constant char h_proto[] = 53140752Simp "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done"; 54140752Simpstatic constant char w_proto[] = 55140752Simp "Waiting for data"; 56140752Simp 57140752Simppublic char *prproto[3]; 58140752Simppublic char constant *eqproto = e_proto; 59140752Simppublic char constant *hproto = h_proto; 60140752Simppublic char constant *wproto = w_proto; 61140752Simp 62140752Simpstatic char message[PROMPT_SIZE]; 63140752Simpstatic char *mp; 64140752Simp 65140752Simp/* 66140752Simp * Initialize the prompt prototype strings. 67140752Simp */ 6852506Simp public void 69140749Simpinit_prompt() 70140749Simp{ 71140749Simp prproto[0] = save(s_proto); 72140749Simp prproto[1] = save(m_proto); 73140749Simp prproto[2] = save(M_proto); 7486269Simp eqproto = save(e_proto); 7552506Simp hproto = save(h_proto); 7652506Simp wproto = save(w_proto); 77140793Simp} 78140793Simp 7958545Simp/* 8052506Simp * Append a string to the end of the message. 8165039Simp */ 8265039Simp static void 8352506Simpap_str(s) 84140793Simp char *s; 85140793Simp{ 8652506Simp int len; 8752506Simp 8852506Simp len = strlen(s); 8952506Simp if (mp + len >= message + PROMPT_SIZE) 9058545Simp len = message + PROMPT_SIZE - mp - 1; 9152506Simp strncpy(mp, s, len); 9286455Simp mp += len; 9379270Simp *mp = '\0'; 94107359Snon} 9552506Simp 9686269Simp/* 9786455Simp * Append a character to the end of the message. 98119225Simp */ 9952506Simp static void 100140749Simpap_char(c) 10186455Simp char c; 10258545Simp{ 103104854Simp char buf[2]; 10486269Simp 105104854Simp buf[0] = c; 10652506Simp buf[1] = '\0'; 10786455Simp ap_str(buf); 10852506Simp} 10986455Simp 11053813Simp/* 111100213Simp * Append a POSITION (as a decimal integer) to the end of the message. 11258545Simp */ 11389945Simp static void 11484514Simpap_pos(pos) 11558545Simp POSITION pos; 116119234Simp{ 11769138Speter char buf[INT_STRLEN_BOUND(pos) + 2]; 118118634Simp 11952506Simp postoa(pos, buf); 12058545Simp ap_str(buf); 121140837Simp} 122140793Simp 123140793Simp/* 124140793Simp * Append a line number to the end of the message. 12558545Simp */ 12665039Simp static void 12792471Simpap_linenum(linenum) 128140793Simp LINENUM linenum; 129116207Simp{ 13084514Simp char buf[INT_STRLEN_BOUND(linenum) + 2]; 13179270Simp 132140793Simp linenumtoa(linenum, buf); 13379270Simp ap_str(buf); 134117438Simp} 135117602Simp 136118895Simp/* 137119240Simp * Append an integer to the end of the message. 138119240Simp */ 139119240Simp static void 140119240Simpap_int(num) 14193620Simp int num; 14286455Simp{ 143119240Simp char buf[INT_STRLEN_BOUND(num) + 2]; 144119240Simp 145119240Simp inttoa(num, buf); 146119240Simp ap_str(buf); 147119240Simp} 148140793Simp 149119240Simp/* 150119240Simp * Append a question mark to the end of the message. 151119240Simp */ 152140792Simp static void 153140792Simpap_quest() 154119240Simp{ 155119240Simp ap_str("?"); 15686455Simp} 157104854Simp 15894461Simp/* 159140793Simp * Return the "current" byte offset in the file. 16086455Simp */ 16186455Simp static POSITION 16289945Simpcurr_byte(where) 16353813Simp int where; 16471279Simp{ 16571283Simp POSITION pos; 166113667Ssanpei 16753813Simp pos = position(where); 16852506Simp while (pos == NULL_POSITION && where >= 0 && where < sc_height-1) 169140793Simp pos = position(++where); 170140792Simp if (pos == NULL_POSITION) 171107359Snon pos = ch_length(); 17271283Simp return (pos); 17352506Simp} 17452506Simp 17586269Simp/* 17686269Simp * Return the value of a prototype conditional. 17752506Simp * A prototype string may include conditionals which consist of a 17853813Simp * question mark followed by a single letter. 17952506Simp * Here we decode that letter and return the appropriate boolean value. 18065039Simp */ 18186269Simp static int 18286269Simpcond(c, where) 18386269Simp char c; 184135002Semax int where; 18586269Simp{ 18652506Simp POSITION len; 18752506Simp 18852506Simp switch (c) 18993893Simp { 19086455Simp case 'a': /* Anything in the message yet? */ 19184514Simp return (mp > message); 19252506Simp case 'b': /* Current byte offset known? */ 193104854Simp return (curr_byte(where) != NULL_POSITION); 194104854Simp case 'c': 19594461Simp return (hshift != 0); 19686269Simp case 'e': /* At end of file? */ 19786269Simp return (hit_eof); 19886269Simp case 'f': /* Filename known? */ 19986269Simp return (strcmp(get_filename(curr_ifile), "-") != 0); 20086269Simp case 'l': /* Line number known? */ 20186269Simp case 'd': /* Same as l */ 20286269Simp return (linenums); 20386269Simp case 'L': /* Final line number known? */ 20486269Simp case 'D': /* Final page number known? */ 205117614Simp return (linenums && ch_length() != NULL_POSITION); 206117614Simp case 'm': /* More than one file? */ 207117614Simp#if TAGS 20886269Simp return (ntags() ? (ntags() > 1) : (nifile() > 1)); 20986269Simp#else 21086269Simp return (nifile() > 1); 211140793Simp#endif 212140793Simp case 'n': /* First prompt in a new file? */ 213140793Simp#if TAGS 214140793Simp return (ntags() ? 1 : new_file); 21586455Simp#else 21686455Simp return (new_file); 21786455Simp#endif 218116207Simp case 'p': /* Percent into file (bytes) known? */ 219116207Simp return (curr_byte(where) != NULL_POSITION && 220116207Simp ch_length() > 0); 221117438Simp case 'P': /* Percent into file (lines) known? */ 222117445Ssimokawa return (currline(where) != 0 && 223117438Simp (len = ch_length()) > 0 && 22486269Simp find_linenum(len) != 0); 22586269Simp case 's': /* Size of file known? */ 22686269Simp case 'B': 22786269Simp return (ch_length() != NULL_POSITION); 228104854Simp case 'x': /* Is there a "next" file? */ 22986269Simp#if TAGS 23087757Simp if (ntags()) 23187757Simp return (0); 23287757Simp#endif 23386455Simp return (next_ifile(curr_ifile) != NULL_IFILE); 23486455Simp } 23586455Simp return (0); 236119231Simp} 237119231Simp 238119231Simp/* 239119231Simp * Decode a "percent" prototype character. 240119231Simp * A prototype string may include various "percent" escapes; 24186269Simp * that is, a percent sign followed by a single letter. 242117759Simp * Here we decode that letter and take the appropriate action, 24386269Simp * usually by appending something to the message being built. 244109455Sshiba */ 245104854Simp static void 24687044Simpprotochar(c, where, iseditproto) 24786269Simp int c; 24865039Simp int where; 24986269Simp int iseditproto; 25065039Simp{ 25165039Simp POSITION pos; 25252506Simp POSITION len; 25386455Simp int n; 25452506Simp LINENUM linenum; 25552506Simp LINENUM last_linenum; 25686269Simp IFILE h; 25786269Simp 25886269Simp#undef PAGE_NUM 25953813Simp#define PAGE_NUM(linenum) ((((linenum) - 1) / (sc_height - 1)) + 1) 26052506Simp 26152506Simp switch (c) 26252506Simp { 26352506Simp case 'b': /* Current byte offset */ 26452506Simp pos = curr_byte(where); 26552506Simp if (pos != NULL_POSITION) 26652506Simp ap_pos(pos); 26779270Simp else 26879270Simp ap_quest(); 26979270Simp break; 270119225Simp case 'c': 271119225Simp ap_int(hshift); 272119225Simp break; 273119225Simp case 'd': /* Current page number */ 27486455Simp linenum = currline(where); 27586455Simp if (linenum > 0 && sc_height > 1) 27686455Simp ap_linenum(PAGE_NUM(linenum)); 27789945Simp else 27889945Simp ap_quest(); 27989945Simp break; 28071279Simp case 'D': /* Final page number */ 28171279Simp /* Find the page number of the last byte in the file (len-1). */ 28271279Simp len = ch_length(); 28386269Simp if (len == NULL_POSITION) 284100213Simp ap_quest(); 28571279Simp else if (len == 0) 28686269Simp /* An empty file has no pages. */ 28786269Simp ap_linenum(0); 28886269Simp else 28989945Simp { 29089945Simp linenum = find_linenum(len - 1); 29189945Simp if (linenum <= 0) 29286269Simp ap_quest(); 29386269Simp else 29486269Simp ap_linenum(PAGE_NUM(linenum)); 29552506Simp } 296139963Simp break; 29752506Simp#if EDITOR 29886269Simp case 'E': /* Editor name */ 29953813Simp ap_str(editor); 300120330Simp break; 30153813Simp#endif 302100213Simp case 'f': /* File name */ 303100213Simp ap_str(get_filename(curr_ifile)); 304100213Simp break; 30592471Simp case 'i': /* Index into list of files */ 30692471Simp#if TAGS 30792471Simp if (ntags()) 30852506Simp ap_int(curr_tag()); 30971279Simp else 31065039Simp#endif 31165039Simp ap_int(get_index(curr_ifile)); 31265039Simp break; 31365039Simp case 'l': /* Current line number */ 31487044Simp linenum = currline(where); 31565039Simp if (linenum != 0) 31652506Simp ap_linenum(linenum); 31765039Simp else 31889103Simp ap_quest(); 31953813Simp break; 32052506Simp case 'L': /* Final line number */ 32165039Simp len = ch_length(); 32286269Simp if (len == NULL_POSITION || len == ch_zero() || 32365039Simp (linenum = find_linenum(len)) <= 0) 324121960Simp ap_quest(); 325129164Simp else 32665039Simp ap_linenum(linenum-1); 327119213Simp break; 328127422Simp case 'm': /* Number of files */ 329140520Simp#if TAGS 330140520Simp n = ntags(); 331140520Simp if (n) 332119213Simp ap_int(n); 33352506Simp else 33479270Simp#endif 335117764Simp ap_int(nifile()); 33652506Simp break; 33786269Simp case 'p': /* Percent into file (bytes) */ 33886269Simp pos = curr_byte(where); 33965039Simp len = ch_length(); 34086269Simp if (pos != NULL_POSITION && len > 0) 34186269Simp ap_int(percentage(pos,len)); 34286269Simp else 34358545Simp ap_quest(); 34471279Simp break; 34558545Simp case 'P': /* Percent into file (lines) */ 34652506Simp linenum = currline(where); 34786269Simp if (linenum == 0 || 34858545Simp (len = ch_length()) == NULL_POSITION || len == ch_zero() || 34952506Simp (last_linenum = find_linenum(len)) <= 0) 35052506Simp ap_quest(); 35152506Simp else 35286269Simp ap_int(percentage(linenum, last_linenum)); 35386269Simp break; 35486269Simp case 's': /* Size of file */ 35586269Simp case 'B': 35686269Simp len = ch_length(); 357107359Snon if (len != NULL_POSITION) 35886269Simp ap_pos(len); 359140749Simp else 360140749Simp ap_quest(); 361140749Simp break; 36252506Simp case 't': /* Truncate trailing spaces in the message */ 36386269Simp while (mp > message && mp[-1] == ' ') 36452506Simp mp--; 36553813Simp break; 36652506Simp case 'T': /* Type of list */ 36752506Simp#if TAGS 36858545Simp if (ntags()) 369140515Simp ap_str("tag"); 37058545Simp else 37158545Simp#endif 372118895Simp ap_str("file"); 373118895Simp break; 374118895Simp case 'x': /* Name of next file */ 37586269Simp h = next_ifile(curr_ifile); 37686269Simp if (h != NULL_IFILE) 37786269Simp ap_str(get_filename(h)); 37886455Simp else 37986269Simp ap_quest(); 38086269Simp break; 38186269Simp } 38286455Simp} 38386455Simp 38486455Simp/* 385104831Simp * Skip a false conditional. 386106891Simp * When a false condition is found (either a false IF or the ELSE part 387106891Simp * of a true IF), this routine scans the prototype string to decide 38886455Simp * where to resume parsing the string. 38986455Simp * We must keep track of nested IFs and skip them properly. 39086269Simp */ 39186455Simp static char * 392104854Simpskipcond(p) 39386455Simp register char *p; 39493620Simp{ 39586455Simp register int iflevel; 39679270Simp 39779270Simp /* 39879270Simp * We came in here after processing a ? or :, 399140792Simp * so we start nested one level deep. 400140792Simp */ 401140792Simp iflevel = 1; 40258545Simp 403100213Simp for (;;) switch (*++p) 40458545Simp { 40558545Simp case '?': 40693620Simp /* 40793620Simp * Start of a nested IF. 40893620Simp */ 40993620Simp iflevel++; 41065039Simp break; 411140515Simp case ':': 41265039Simp /* 413121586Simp * Else. 414121584Simp * If this matches the IF we came in here with, 41565039Simp * then we're done. 416104854Simp */ 417104854Simp if (iflevel == 1) 418104854Simp return (p); 41958545Simp break; 42058545Simp case '.': 42165039Simp /* 422107359Snon * Endif. 42386269Simp * If this matches the IF we came in here with, 424124789Sume * then we're done. 42586455Simp */ 42658545Simp if (--iflevel == 0) 427104831Simp return (p); 428104854Simp break; 429104831Simp case '\\': 43086455Simp /* 431120275Simp * Backslash escapes the next character. 43286455Simp */ 43386455Simp ++p; 43493622Simp break; 43586455Simp case '\0': 43684514Simp /* 43784514Simp * Whoops. Hit end of string. 43884514Simp * This is a malformed conditional, but just treat it 439107359Snon * as if all active conditionals ends here. 440107359Snon */ 441107359Snon return (p-1); 44286455Simp } 443120330Simp /*NOTREACHED*/ 44486455Simp} 44586455Simp 44686269Simp/* 447109103Sshiba * Decode a char that represents a position on the screen. 448109103Sshiba */ 44952506Simp static char * 450140837Simpwherechar(p, wp) 451140837Simp char *p; 452140837Simp int *wp; 45386269Simp{ 45486269Simp switch (*p) 45586269Simp { 45686269Simp case 'b': case 'd': case 'l': case 'p': case 'P': 45786269Simp switch (*++p) 45886269Simp { 45986269Simp case 't': *wp = TOP; break; 46086269Simp case 'm': *wp = MIDDLE; break; 46186269Simp case 'b': *wp = BOTTOM; break; 46252506Simp case 'B': *wp = BOTTOM_PLUS_ONE; break; 46352506Simp case 'j': *wp = adjsline(jump_sline); break; 46452506Simp default: *wp = TOP; p--; break; 465118063Simp } 466118063Simp } 467118063Simp return (p); 468117602Simp} 469117602Simp 470117602Simp/* 47152506Simp * Construct a message based on a prototype string. 47252506Simp */ 47353813Simp public char * 47453813Simppr_expand(proto, maxwidth) 47552506Simp char *proto; 47686269Simp int maxwidth; 47786269Simp{ 47886269Simp register char *p; 47986269Simp register int c; 48052506Simp int where; 48179270Simp 48289103Simp mp = message; 48352506Simp 48452506Simp if (*proto == '\0') 48586269Simp return (""); 48671279Simp 487118063Simp for (p = proto; *p != '\0'; p++) 48852506Simp { 48986269Simp switch (*p) 49086269Simp { 49186269Simp default: /* Just put the character in the message */ 49252506Simp ap_char(*p); 49386269Simp break; 49452506Simp case '\\': /* Backslash escapes the next character */ 49586269Simp p++; 49652506Simp ap_char(*p); 497104854Simp break; 498124015Skato case '?': /* Conditional (IF) */ 49952506Simp if ((c = *++p) == '\0') 50086269Simp --p; 50186269Simp else 50286269Simp { 50386269Simp where = 0; 50486269Simp p = wherechar(p, &where); 50586269Simp if (!cond(c, where)) 506107359Snon p = skipcond(p); 507107359Snon } 508107359Snon break; 50965039Simp case ':': /* ELSE */ 510120898Simp p = skipcond(p); 51165039Simp break; 512128064Srsm case '.': /* ENDIF */ 513128064Srsm break; 514128064Srsm case '%': /* Percent escape */ 51586571Simp if ((c = *++p) == '\0') 51686269Simp --p; 51786269Simp else 51879270Simp { 51965039Simp where = 0; 520128064Srsm p = wherechar(p, &where); 521128064Srsm protochar(c, where, 522128064Srsm#if EDITOR 52386269Simp (proto == editproto)); 524128064Srsm#else 525128064Srsm 0); 526128064Srsm#endif 527128064Srsm 528128064Srsm } 52952506Simp break; 53084514Simp } 53184514Simp } 53284514Simp 53386269Simp if (mp == message) 53486269Simp return (""); 53586269Simp if (maxwidth > 0 && mp >= message + maxwidth) 53686269Simp { 53752506Simp /* 53893622Simp * Message is too long. 539103169Simp * Return just the final portion of it. 54086269Simp */ 54186269Simp return (mp - maxwidth); 54286269Simp } 543113667Ssanpei return (message); 54486269Simp} 545104854Simp 54686269Simp/* 54758545Simp * Return a message suitable for printing by the "=" command. 54858545Simp */ 549120330Simp public char * 550107359Snoneq_message() 551116481Simp{ 55286281Simp return (pr_expand(eqproto, 0)); 55358545Simp} 554119215Simp 555107359Snon/* 55693620Simp * Return a prompt. 557118634Simp * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. 55886269Simp * If we can't come up with an appropriate prompt, return NULL 55986392Simp * and the caller will prompt with a colon. 560107359Snon */ 561114089Simp public char * 56286269Simppr_string() 56386269Simp{ 56486269Simp char *prompt; 56586269Simp 56686269Simp prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? 56779270Simp hproto : prproto[pr_type], 56886269Simp sc_width-so_s_width-so_e_width-2); 569113318Simp new_file = 0; 570107359Snon return (prompt); 57186269Simp} 572110935Sshiba 57386580Simp/* 57452506Simp * Return a message suitable for printing while waiting in the F command. 57552506Simp */ 576104854Simp public char * 577109453Sshibawait_message() 57893622Simp{ 579110175Sshiba return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); 58086269Simp} 581104854Simp