1 /* 2 * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice in the documentation and/or other materials provided with 12 * the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 20 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 21 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 28 /* 29 * Prompting and other messages. 30 * There are three flavors of prompts, SHORT, MEDIUM and LONG, 31 * selected by the -m/-M options. 32 * There is also the "equals message", printed by the = command. 33 * A prompt is a message composed of various pieces, such as the 34 * name of the file being viewed, the percentage into the file, etc. 35 */ 36 37 #include "less.h" 38 #include "position.h" 39 40 extern int pr_type; 41 extern int hit_eof; 42 extern int new_file; 43 extern int sc_width; 44 extern int so_s_width, so_e_width; 45 extern int linenums; 46 extern int sc_height; 47 extern int jump_sline; 48 extern IFILE curr_ifile; 49 #if EDITOR 50 extern char *editor; 51 #endif 52 53 /* 54 * Prototypes for the three flavors of prompts. 55 * These strings are expanded by pr_expand(). 56 */ 57 static char s_proto[] = 58 "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t"; 59 static char m_proto[] = 60 "?f%f .?m(file %i of %m) .?e(END) ?x- Next\\: %x.:(?pB%pB\\%:byte %bB?s/%s..).%t"; 61 static char M_proto[] = 62 "?f%f .?n?m(file %i of %m) ..?ltline %lt?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t"; 63 static char e_proto[] = 64 "?f%f .?m(file %i of %m) .?ltline %lt?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; 65 66 public char *prproto[3]; 67 public char *eqproto = e_proto; 68 69 static char message[250]; 70 static char *mp; 71 72 /* 73 * Initialize the prompt prototype strings. 74 */ 75 public void 76 init_prompt() 77 { 78 prproto[0] = save(s_proto); 79 prproto[1] = save(m_proto); 80 prproto[2] = save(M_proto); 81 eqproto = save(e_proto); 82 } 83 84 /* 85 * Set the message pointer to the end of the message string. 86 */ 87 static void 88 setmp() 89 { 90 while (*mp != '\0') 91 mp++; 92 } 93 94 /* 95 * Append a POSITION (as a decimal integer) to the end of the message. 96 */ 97 static void 98 ap_pos(pos) 99 POSITION pos; 100 { 101 sprintf(mp, "%qd", pos); 102 setmp(); 103 } 104 105 /* 106 * Append an integer to the end of the message. 107 */ 108 static void 109 ap_int(n) 110 int n; 111 { 112 sprintf(mp, "%d", n); 113 setmp(); 114 } 115 116 /* 117 * Append a string to the end of the message. 118 */ 119 static void 120 ap_str(s) 121 char *s; 122 { 123 strtcpy(mp, s, (unsigned int)(&message[sizeof(message)] - mp)); 124 setmp(); 125 } 126 127 /* 128 * Append a question mark to the end of the message. 129 */ 130 static void 131 ap_quest() 132 { 133 *mp++ = '?'; 134 } 135 136 /* 137 * Return the "current" byte offset in the file. 138 */ 139 static POSITION 140 curr_byte(where) 141 int where; 142 { 143 POSITION pos; 144 145 pos = position(where); 146 while (pos == NULL_POSITION && where >= 0 && where < sc_height) 147 pos = position(++where); 148 if (pos == NULL_POSITION) 149 pos = ch_length(); 150 return (pos); 151 } 152 153 /* 154 * Return the value of a prototype conditional. 155 * A prototype string may include conditionals which consist of a 156 * question mark followed by a single letter. 157 * Here we decode that letter and return the appropriate boolean value. 158 */ 159 static int 160 cond(c, where) 161 char c; 162 int where; 163 { 164 switch (c) 165 { 166 case 'a': /* Anything in the message yet? */ 167 return (mp > message); 168 case 'b': /* Current byte offset known? */ 169 return (curr_byte(where) != NULL_POSITION); 170 case 'e': /* At end of file? */ 171 return (hit_eof); 172 case 'f': /* Filename known? */ 173 return (strcmp(get_filename(curr_ifile), "-") != 0); 174 case 'l': /* Line number known? */ 175 return (linenums); 176 case 'L': /* Final line number known? */ 177 return (linenums && ch_length() != NULL_POSITION); 178 case 'm': /* More than one file? */ 179 return (nifile() > 1); 180 case 'n': /* First prompt in a new file? */ 181 return (new_file); 182 case 'p': /* Percent into file known? */ 183 return (curr_byte(where) != NULL_POSITION && 184 ch_length() > 0); 185 case 's': /* Size of file known? */ 186 case 'B': 187 return (ch_length() != NULL_POSITION); 188 case 'x': /* Is there a "next" file? */ 189 return (next_ifile(curr_ifile) != NULL_IFILE); 190 } 191 return (0); 192 } 193 194 /* 195 * Decode a "percent" prototype character. 196 * A prototype string may include various "percent" escapes; 197 * that is, a percent sign followed by a single letter. 198 * Here we decode that letter and take the appropriate action, 199 * usually by appending something to the message being built. 200 */ 201 static void 202 protochar(c, where) 203 int c; 204 int where; 205 { 206 POSITION pos; 207 POSITION len; 208 int n; 209 IFILE h; 210 211 switch (c) 212 { 213 case 'b': /* Current byte offset */ 214 pos = curr_byte(where); 215 if (pos != NULL_POSITION) 216 ap_pos(pos); 217 else 218 ap_quest(); 219 break; 220 #if EDITOR 221 case 'E': /* Editor name */ 222 ap_str(editor); 223 break; 224 #endif 225 case 'f': /* File name */ 226 ap_str(get_filename(curr_ifile)); 227 break; 228 case 'i': /* Index into list of files */ 229 ap_int(get_index(curr_ifile)); 230 break; 231 case 'l': /* Current line number */ 232 n = currline(where); 233 if (n != 0) 234 ap_int(n); 235 else 236 ap_quest(); 237 break; 238 case 'L': /* Final line number */ 239 len = ch_length(); 240 if (len == NULL_POSITION || len == ch_zero() || 241 (n = find_linenum(len)) <= 0) 242 ap_quest(); 243 else 244 ap_int(n-1); 245 break; 246 case 'm': /* Number of files */ 247 ap_int(nifile()); 248 break; 249 case 'p': /* Percent into file */ 250 pos = curr_byte(where); 251 len = ch_length(); 252 if (pos != NULL_POSITION && len > 0) 253 ap_int(percentage(pos,len)); 254 else 255 ap_quest(); 256 break; 257 case 's': /* Size of file */ 258 case 'B': 259 len = ch_length(); 260 if (len != NULL_POSITION) 261 ap_pos(len); 262 else 263 ap_quest(); 264 break; 265 case 't': /* Truncate trailing spaces in the message */ 266 while (mp > message && mp[-1] == ' ') 267 mp--; 268 break; 269 case 'x': /* Name of next file */ 270 h = next_ifile(curr_ifile); 271 if (h != NULL_IFILE) 272 ap_str(get_filename(h)); 273 else 274 ap_quest(); 275 break; 276 } 277 } 278 279 /* 280 * Skip a false conditional. 281 * When a false condition is found (either a false IF or the ELSE part 282 * of a true IF), this routine scans the prototype string to decide 283 * where to resume parsing the string. 284 * We must keep track of nested IFs and skip them properly. 285 */ 286 static char * 287 skipcond(p) 288 register char *p; 289 { 290 register int iflevel; 291 292 /* 293 * We came in here after processing a ? or :, 294 * so we start nested one level deep. 295 */ 296 iflevel = 1; 297 298 for (;;) switch (*++p) 299 { 300 case '?': 301 /* 302 * Start of a nested IF. 303 */ 304 iflevel++; 305 break; 306 case ':': 307 /* 308 * Else. 309 * If this matches the IF we came in here with, 310 * then we're done. 311 */ 312 if (iflevel == 1) 313 return (p); 314 break; 315 case '.': 316 /* 317 * Endif. 318 * If this matches the IF we came in here with, 319 * then we're done. 320 */ 321 if (--iflevel == 0) 322 return (p); 323 break; 324 case '\\': 325 /* 326 * Backslash escapes the next character. 327 */ 328 ++p; 329 break; 330 case '\0': 331 /* 332 * Whoops. Hit end of string. 333 * This is a malformed conditional, but just treat it 334 * as if all active conditionals ends here. 335 */ 336 return (p-1); 337 } 338 /*NOTREACHED*/ 339 } 340 341 static char * 342 wherechar(p, wp) 343 char *p; 344 int *wp; 345 { 346 switch (*p) 347 { 348 case 'b': case 'l': case 'p': 349 switch (*++p) 350 { 351 case 't': *wp = TOP; break; 352 case 'm': *wp = MIDDLE; break; 353 case 'b': *wp = BOTTOM; break; 354 case 'B': *wp = BOTTOM_PLUS_ONE; break; 355 case 'j': *wp = adjsline(jump_sline); break; 356 default: *wp = TOP; p--; break; 357 } 358 } 359 return (p); 360 } 361 362 /* 363 * Construct a message based on a prototype string. 364 */ 365 public char * 366 pr_expand(proto, maxwidth) 367 char *proto; 368 int maxwidth; 369 { 370 register char *p; 371 register int c; 372 int where; 373 374 mp = message; 375 376 if (*proto == '\0') 377 return (""); 378 379 for (p = proto; *p != '\0'; p++) 380 { 381 switch (*p) 382 { 383 default: /* Just put the character in the message */ 384 *mp++ = *p; 385 break; 386 case '\\': /* Backslash escapes the next character */ 387 p++; 388 *mp++ = *p; 389 break; 390 case '?': /* Conditional (IF) */ 391 if ((c = *++p) == '\0') 392 --p; 393 else 394 { 395 p = wherechar(p, &where); 396 if (!cond(c, where)) 397 p = skipcond(p); 398 } 399 break; 400 case ':': /* ELSE */ 401 p = skipcond(p); 402 break; 403 case '.': /* ENDIF */ 404 break; 405 case '%': /* Percent escape */ 406 if ((c = *++p) == '\0') 407 --p; 408 else 409 { 410 p = wherechar(p, &where); 411 protochar(c, where); 412 } 413 break; 414 } 415 } 416 417 new_file = 0; 418 if (mp == message) 419 return (NULL); 420 *mp = '\0'; 421 if (maxwidth > 0 && mp >= message + maxwidth) 422 { 423 /* 424 * Message is too long. 425 * Return just the final portion of it. 426 */ 427 return (mp - maxwidth); 428 } 429 return (message); 430 } 431 432 /* 433 * Return a message suitable for printing by the "=" command. 434 */ 435 public char * 436 eq_message() 437 { 438 return (pr_expand(eqproto, 0)); 439 } 440 441 /* 442 * Return a prompt. 443 * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. 444 * If we can't come up with an appropriate prompt, return NULL 445 * and the caller will prompt with a colon. 446 */ 447 public char * 448 pr_string() 449 { 450 return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2)); 451 } 452