"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/prompt.c" of archive less-424.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Copyright (C) 1984-2008  Mark Nudelman
    3  *
    4  * You may distribute under the terms of either the GNU General Public
    5  * License or the Less License, as specified in the README file.
    6  *
    7  * For more information about less, or for information on how to
    8  * contact the author, see the README file.
    9  */
   10 
   11 
   12 /*
   13  * Prompting and other messages.
   14  * There are three flavors of prompts, SHORT, MEDIUM and LONG,
   15  * selected by the -m/-M options.
   16  * There is also the "equals message", printed by the = command.
   17  * A prompt is a message composed of various pieces, such as the
   18  * name of the file being viewed, the percentage into the file, etc.
   19  */
   20 
   21 #include "less.h"
   22 #include "position.h"
   23 
   24 extern int pr_type;
   25 extern int new_file;
   26 extern int sc_width;
   27 extern int so_s_width, so_e_width;
   28 extern int linenums;
   29 extern int hshift;
   30 extern int sc_height;
   31 extern int jump_sline;
   32 extern int less_is_more;
   33 extern IFILE curr_ifile;
   34 #if EDITOR
   35 extern char *editor;
   36 extern char *editproto;
   37 #endif
   38 
   39 /*
   40  * Prototypes for the three flavors of prompts.
   41  * These strings are expanded by pr_expand().
   42  */
   43 static constant char s_proto[] =
   44   "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t";
   45 static constant char m_proto[] =
   46   "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
   47 static constant char M_proto[] =
   48   "?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";
   49 static constant char e_proto[] =
   50   "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
   51 static constant char h_proto[] =
   52   "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done";
   53 static constant char w_proto[] =
   54   "Waiting for data";
   55 static constant char more_proto[] =
   56   "--More--(?eEND ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t)";
   57 
   58 public char *prproto[3];
   59 public char constant *eqproto = e_proto;
   60 public char constant *hproto = h_proto;
   61 public char constant *wproto = w_proto;
   62 
   63 static char message[PROMPT_SIZE];
   64 static char *mp;
   65 
   66 /*
   67  * Initialize the prompt prototype strings.
   68  */
   69 	public void
   70 init_prompt()
   71 {
   72 	prproto[0] = save(s_proto);
   73 	prproto[1] = save(less_is_more ? more_proto : m_proto);
   74 	prproto[2] = save(M_proto);
   75 	eqproto = save(e_proto);
   76 	hproto = save(h_proto);
   77 	wproto = save(w_proto);
   78 }
   79 
   80 /*
   81  * Append a string to the end of the message.
   82  */
   83 	static void
   84 ap_str(s)
   85 	char *s;
   86 {
   87 	int len;
   88 
   89 	len = strlen(s);
   90 	if (mp + len >= message + PROMPT_SIZE)
   91 		len = message + PROMPT_SIZE - mp - 1;
   92 	strncpy(mp, s, len);
   93 	mp += len;
   94 	*mp = '\0';
   95 }
   96 
   97 /*
   98  * Append a character to the end of the message.
   99  */
  100 	static void
  101 ap_char(c)
  102 	char c;
  103 {
  104 	char buf[2];
  105 
  106 	buf[0] = c;
  107 	buf[1] = '\0';
  108 	ap_str(buf);
  109 }
  110 
  111 /*
  112  * Append a POSITION (as a decimal integer) to the end of the message.
  113  */
  114 	static void
  115 ap_pos(pos)
  116 	POSITION pos;
  117 {
  118 	char buf[INT_STRLEN_BOUND(pos) + 2];
  119 
  120 	postoa(pos, buf);
  121 	ap_str(buf);
  122 }
  123 
  124 /*
  125  * Append a line number to the end of the message.
  126  */
  127  	static void
  128 ap_linenum(linenum)
  129 	LINENUM linenum;
  130 {
  131 	char buf[INT_STRLEN_BOUND(linenum) + 2];
  132 
  133 	linenumtoa(linenum, buf);
  134 	ap_str(buf);
  135 }
  136 
  137 /*
  138  * Append an integer to the end of the message.
  139  */
  140 	static void
  141 ap_int(num)
  142 	int num;
  143 {
  144 	char buf[INT_STRLEN_BOUND(num) + 2];
  145 
  146 	inttoa(num, buf);
  147 	ap_str(buf);
  148 }
  149 
  150 /*
  151  * Append a question mark to the end of the message.
  152  */
  153 	static void
  154 ap_quest()
  155 {
  156 	ap_str("?");
  157 }
  158 
  159 /*
  160  * Return the "current" byte offset in the file.
  161  */
  162 	static POSITION
  163 curr_byte(where)
  164 	int where;
  165 {
  166 	POSITION pos;
  167 
  168 	pos = position(where);
  169 	while (pos == NULL_POSITION && where >= 0 && where < sc_height-1)
  170 		pos = position(++where);
  171 	if (pos == NULL_POSITION)
  172 		pos = ch_length();
  173 	return (pos);
  174 }
  175 
  176 /*
  177  * Return the value of a prototype conditional.
  178  * A prototype string may include conditionals which consist of a
  179  * question mark followed by a single letter.
  180  * Here we decode that letter and return the appropriate boolean value.
  181  */
  182 	static int
  183 cond(c, where)
  184 	char c;
  185 	int where;
  186 {
  187 	POSITION len;
  188 
  189 	switch (c)
  190 	{
  191 	case 'a':	/* Anything in the message yet? */
  192 		return (mp > message);
  193 	case 'b':	/* Current byte offset known? */
  194 		return (curr_byte(where) != NULL_POSITION);
  195 	case 'c':
  196 		return (hshift != 0);
  197 	case 'e':	/* At end of file? */
  198 		return (eof_displayed());
  199 	case 'f':	/* Filename known? */
  200 		return (strcmp(get_filename(curr_ifile), "-") != 0);
  201 	case 'l':	/* Line number known? */
  202 	case 'd':	/* Same as l */
  203 		return (linenums);
  204 	case 'L':	/* Final line number known? */
  205 	case 'D':	/* Final page number known? */
  206 		return (linenums && ch_length() != NULL_POSITION);
  207 	case 'm':	/* More than one file? */
  208 #if TAGS
  209 		return (ntags() ? (ntags() > 1) : (nifile() > 1));
  210 #else
  211 		return (nifile() > 1);
  212 #endif
  213 	case 'n':	/* First prompt in a new file? */
  214 #if TAGS
  215 		return (ntags() ? 1 : new_file);
  216 #else
  217 		return (new_file);
  218 #endif
  219 	case 'p':	/* Percent into file (bytes) known? */
  220 		return (curr_byte(where) != NULL_POSITION &&
  221 				ch_length() > 0);
  222 	case 'P':	/* Percent into file (lines) known? */
  223 		return (currline(where) != 0 &&
  224 				(len = ch_length()) > 0 &&
  225 				find_linenum(len) != 0);
  226 	case 's':	/* Size of file known? */
  227 	case 'B':
  228 		return (ch_length() != NULL_POSITION);
  229 	case 'x':	/* Is there a "next" file? */
  230 #if TAGS
  231 		if (ntags())
  232 			return (0);
  233 #endif
  234 		return (next_ifile(curr_ifile) != NULL_IFILE);
  235 	}
  236 	return (0);
  237 }
  238 
  239 /*
  240  * Decode a "percent" prototype character.
  241  * A prototype string may include various "percent" escapes;
  242  * that is, a percent sign followed by a single letter.
  243  * Here we decode that letter and take the appropriate action,
  244  * usually by appending something to the message being built.
  245  */
  246 	static void
  247 protochar(c, where, iseditproto)
  248 	int c;
  249 	int where;
  250 	int iseditproto;
  251 {
  252 	POSITION pos;
  253 	POSITION len;
  254 	int n;
  255 	LINENUM linenum;
  256 	LINENUM last_linenum;
  257 	IFILE h;
  258 
  259 #undef  PAGE_NUM
  260 #define PAGE_NUM(linenum)  ((((linenum) - 1) / (sc_height - 1)) + 1)
  261 
  262 	switch (c)
  263 	{
  264 	case 'b':	/* Current byte offset */
  265 		pos = curr_byte(where);
  266 		if (pos != NULL_POSITION)
  267 			ap_pos(pos);
  268 		else
  269 			ap_quest();
  270 		break;
  271 	case 'c':
  272 		ap_int(hshift);
  273 		break;
  274 	case 'd':	/* Current page number */
  275 		linenum = currline(where);
  276 		if (linenum > 0 && sc_height > 1)
  277 			ap_linenum(PAGE_NUM(linenum));
  278 		else
  279 			ap_quest();
  280 		break;
  281 	case 'D':	/* Final page number */
  282 		/* Find the page number of the last byte in the file (len-1). */
  283 		len = ch_length();
  284 		if (len == NULL_POSITION)
  285 			ap_quest();
  286 		else if (len == 0)
  287 			/* An empty file has no pages. */
  288 			ap_linenum(0);
  289 		else
  290 		{
  291 			linenum = find_linenum(len - 1);
  292 			if (linenum <= 0)
  293 				ap_quest();
  294 			else
  295 				ap_linenum(PAGE_NUM(linenum));
  296 		}
  297 		break;
  298 #if EDITOR
  299 	case 'E':	/* Editor name */
  300 		ap_str(editor);
  301 		break;
  302 #endif
  303 	case 'f':	/* File name */
  304 		ap_str(get_filename(curr_ifile));
  305 		break;
  306 	case 'i':	/* Index into list of files */
  307 #if TAGS
  308 		if (ntags())
  309 			ap_int(curr_tag());
  310 		else
  311 #endif
  312 			ap_int(get_index(curr_ifile));
  313 		break;
  314 	case 'l':	/* Current line number */
  315 		linenum = currline(where);
  316 		if (linenum != 0)
  317 			ap_linenum(linenum);
  318 		else
  319 			ap_quest();
  320 		break;
  321 	case 'L':	/* Final line number */
  322 		len = ch_length();
  323 		if (len == NULL_POSITION || len == ch_zero() ||
  324 		    (linenum = find_linenum(len)) <= 0)
  325 			ap_quest();
  326 		else
  327 			ap_linenum(linenum-1);
  328 		break;
  329 	case 'm':	/* Number of files */
  330 #if TAGS
  331 		n = ntags();
  332 		if (n)
  333 			ap_int(n);
  334 		else
  335 #endif
  336 			ap_int(nifile());
  337 		break;
  338 	case 'p':	/* Percent into file (bytes) */
  339 		pos = curr_byte(where);
  340 		len = ch_length();
  341 		if (pos != NULL_POSITION && len > 0)
  342 			ap_int(percentage(pos,len));
  343 		else
  344 			ap_quest();
  345 		break;
  346 	case 'P':	/* Percent into file (lines) */
  347 		linenum = currline(where);
  348 		if (linenum == 0 ||
  349 		    (len = ch_length()) == NULL_POSITION || len == ch_zero() ||
  350 		    (last_linenum = find_linenum(len)) <= 0)
  351 			ap_quest();
  352 		else
  353 			ap_int(percentage(linenum, last_linenum));
  354 		break;
  355 	case 's':	/* Size of file */
  356 	case 'B':
  357 		len = ch_length();
  358 		if (len != NULL_POSITION)
  359 			ap_pos(len);
  360 		else
  361 			ap_quest();
  362 		break;
  363 	case 't':	/* Truncate trailing spaces in the message */
  364 		while (mp > message && mp[-1] == ' ')
  365 			mp--;
  366 		break;
  367 	case 'T':	/* Type of list */
  368 #if TAGS
  369 		if (ntags())
  370 			ap_str("tag");
  371 		else
  372 #endif
  373 			ap_str("file");
  374 		break;
  375 	case 'x':	/* Name of next file */
  376 		h = next_ifile(curr_ifile);
  377 		if (h != NULL_IFILE)
  378 			ap_str(get_filename(h));
  379 		else
  380 			ap_quest();
  381 		break;
  382 	}
  383 }
  384 
  385 /*
  386  * Skip a false conditional.
  387  * When a false condition is found (either a false IF or the ELSE part
  388  * of a true IF), this routine scans the prototype string to decide
  389  * where to resume parsing the string.
  390  * We must keep track of nested IFs and skip them properly.
  391  */
  392 	static char *
  393 skipcond(p)
  394 	register char *p;
  395 {
  396 	register int iflevel;
  397 
  398 	/*
  399 	 * We came in here after processing a ? or :,
  400 	 * so we start nested one level deep.
  401 	 */
  402 	iflevel = 1;
  403 
  404 	for (;;) switch (*++p)
  405 	{
  406 	case '?':
  407 		/*
  408 		 * Start of a nested IF.
  409 		 */
  410 		iflevel++;
  411 		break;
  412 	case ':':
  413 		/*
  414 		 * Else.
  415 		 * If this matches the IF we came in here with,
  416 		 * then we're done.
  417 		 */
  418 		if (iflevel == 1)
  419 			return (p);
  420 		break;
  421 	case '.':
  422 		/*
  423 		 * Endif.
  424 		 * If this matches the IF we came in here with,
  425 		 * then we're done.
  426 		 */
  427 		if (--iflevel == 0)
  428 			return (p);
  429 		break;
  430 	case '\\':
  431 		/*
  432 		 * Backslash escapes the next character.
  433 		 */
  434 		++p;
  435 		break;
  436 	case '\0':
  437 		/*
  438 		 * Whoops.  Hit end of string.
  439 		 * This is a malformed conditional, but just treat it
  440 		 * as if all active conditionals ends here.
  441 		 */
  442 		return (p-1);
  443 	}
  444 	/*NOTREACHED*/
  445 }
  446 
  447 /*
  448  * Decode a char that represents a position on the screen.
  449  */
  450 	static char *
  451 wherechar(p, wp)
  452 	char *p;
  453 	int *wp;
  454 {
  455 	switch (*p)
  456 	{
  457 	case 'b': case 'd': case 'l': case 'p': case 'P':
  458 		switch (*++p)
  459 		{
  460 		case 't':   *wp = TOP;			break;
  461 		case 'm':   *wp = MIDDLE;		break;
  462 		case 'b':   *wp = BOTTOM;		break;
  463 		case 'B':   *wp = BOTTOM_PLUS_ONE;	break;
  464 		case 'j':   *wp = adjsline(jump_sline);	break;
  465 		default:    *wp = TOP;  p--;		break;
  466 		}
  467 	}
  468 	return (p);
  469 }
  470 
  471 /*
  472  * Construct a message based on a prototype string.
  473  */
  474 	public char *
  475 pr_expand(proto, maxwidth)
  476 	char *proto;
  477 	int maxwidth;
  478 {
  479 	register char *p;
  480 	register int c;
  481 	int where;
  482 
  483 	mp = message;
  484 
  485 	if (*proto == '\0')
  486 		return ("");
  487 
  488 	for (p = proto;  *p != '\0';  p++)
  489 	{
  490 		switch (*p)
  491 		{
  492 		default:	/* Just put the character in the message */
  493 			ap_char(*p);
  494 			break;
  495 		case '\\':	/* Backslash escapes the next character */
  496 			p++;
  497 			ap_char(*p);
  498 			break;
  499 		case '?':	/* Conditional (IF) */
  500 			if ((c = *++p) == '\0')
  501 				--p;
  502 			else
  503 			{
  504 				where = 0;
  505 				p = wherechar(p, &where);
  506 				if (!cond(c, where))
  507 					p = skipcond(p);
  508 			}
  509 			break;
  510 		case ':':	/* ELSE */
  511 			p = skipcond(p);
  512 			break;
  513 		case '.':	/* ENDIF */
  514 			break;
  515 		case '%':	/* Percent escape */
  516 			if ((c = *++p) == '\0')
  517 				--p;
  518 			else
  519 			{
  520 				where = 0;
  521 				p = wherechar(p, &where);
  522 				protochar(c, where,
  523 #if EDITOR
  524 					(proto == editproto));
  525 #else
  526 					0);
  527 #endif
  528 
  529 			}
  530 			break;
  531 		}
  532 	}
  533 
  534 	if (mp == message)
  535 		return ("");
  536 	if (maxwidth > 0 && mp >= message + maxwidth)
  537 	{
  538 		/*
  539 		 * Message is too long.
  540 		 * Return just the final portion of it.
  541 		 */
  542 		return (mp - maxwidth);
  543 	}
  544 	return (message);
  545 }
  546 
  547 /*
  548  * Return a message suitable for printing by the "=" command.
  549  */
  550 	public char *
  551 eq_message()
  552 {
  553 	return (pr_expand(eqproto, 0));
  554 }
  555 
  556 /*
  557  * Return a prompt.
  558  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
  559  * If we can't come up with an appropriate prompt, return NULL
  560  * and the caller will prompt with a colon.
  561  */
  562 	public char *
  563 pr_string()
  564 {
  565 	char *prompt;
  566 	int type;
  567 
  568 	type = (!less_is_more) ? pr_type : pr_type ? 0 : 1;
  569 	prompt = pr_expand((ch_getflags() & CH_HELPFILE) ?
  570 				hproto : prproto[type],
  571 			sc_width-so_s_width-so_e_width-2);
  572 	new_file = 0;
  573 	return (prompt);
  574 }
  575 
  576 /*
  577  * Return a message suitable for printing while waiting in the F command.
  578  */
  579 	public char *
  580 wait_message()
  581 {
  582 	return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2));
  583 }