~ubuntu-branches/ubuntu/raring/less/raring-proposed

« back to all changes in this revision

Viewing changes to prompt.c

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Schoepf
  • Date: 2002-04-04 16:43:52 UTC
  • Revision ID: james.westby@ubuntu.com-20020404164352-qldq048yoc7x5sd5
Tags: upstream-374
ImportĀ upstreamĀ versionĀ 374

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 1984-2000  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 hit_eof;
 
26
extern int new_file;
 
27
extern int sc_width;
 
28
extern int so_s_width, so_e_width;
 
29
extern int linenums;
 
30
extern int hshift;
 
31
extern int sc_height;
 
32
extern int jump_sline;
 
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
 
 
56
public char *prproto[3];
 
57
public char constant *eqproto = e_proto;
 
58
public char constant *hproto = h_proto;
 
59
public char constant *wproto = w_proto;
 
60
 
 
61
static char message[PROMPT_SIZE];
 
62
static char *mp;
 
63
 
 
64
/*
 
65
 * Initialize the prompt prototype strings.
 
66
 */
 
67
        public void
 
68
init_prompt()
 
69
{
 
70
        prproto[0] = save(s_proto);
 
71
        prproto[1] = save(m_proto);
 
72
        prproto[2] = save(M_proto);
 
73
        eqproto = save(e_proto);
 
74
        hproto = save(h_proto);
 
75
        wproto = save(w_proto);
 
76
}
 
77
 
 
78
/*
 
79
 * Append a string to the end of the message.
 
80
 */
 
81
        static void
 
82
ap_str(s)
 
83
        char *s;
 
84
{
 
85
        int len;
 
86
 
 
87
        len = strlen(s);
 
88
        if (mp + len >= message + PROMPT_SIZE)
 
89
                len = message + PROMPT_SIZE - mp - 1;
 
90
        strncpy(mp, s, len);
 
91
        mp += len;
 
92
        *mp = '\0';
 
93
}
 
94
 
 
95
/*
 
96
 * Append a character to the end of the message.
 
97
 */
 
98
        static void
 
99
ap_char(c)
 
100
        char c;
 
101
{
 
102
        char buf[2];
 
103
 
 
104
        buf[0] = c;
 
105
        buf[1] = '\0';
 
106
        ap_str(buf);
 
107
}
 
108
 
 
109
/*
 
110
 * Append a POSITION (as a decimal integer) to the end of the message.
 
111
 */
 
112
        static void
 
113
ap_pos(pos)
 
114
        POSITION pos;
 
115
{
 
116
        char buf[INT_STRLEN_BOUND(pos) + 1]; 
 
117
        char *p = buf + sizeof(buf) - 1;
 
118
        int neg = (pos < 0);
 
119
 
 
120
        if (neg)
 
121
                pos = -pos;
 
122
        *p = '\0';
 
123
        do
 
124
                *--p = '0' + (pos % 10);
 
125
        while ((pos /= 10) != 0);
 
126
        if (neg)
 
127
                *--p = '-';
 
128
        ap_str(p);
 
129
}
 
130
 
 
131
/*
 
132
 * Append an integer to the end of the message.
 
133
 */
 
134
        static void
 
135
ap_int(n)
 
136
        int n;
 
137
{
 
138
        char buf[INT_STRLEN_BOUND(n) + 1];
 
139
 
 
140
        sprintf(buf, "%d", n);
 
141
        ap_str(buf);
 
142
}
 
143
 
 
144
/*
 
145
 * Append a question mark to the end of the message.
 
146
 */
 
147
        static void
 
148
ap_quest()
 
149
{
 
150
        ap_str("?");
 
151
}
 
152
 
 
153
/*
 
154
 * Return the "current" byte offset in the file.
 
155
 */
 
156
        static POSITION
 
157
curr_byte(where)
 
158
        int where;
 
159
{
 
160
        POSITION pos;
 
161
 
 
162
        pos = position(where);
 
163
        while (pos == NULL_POSITION && where >= 0 && where < sc_height)
 
164
                pos = position(++where);
 
165
        if (pos == NULL_POSITION)
 
166
                pos = ch_length();
 
167
        return (pos);
 
168
}
 
169
 
 
170
/*
 
171
 * Return the value of a prototype conditional.
 
172
 * A prototype string may include conditionals which consist of a 
 
173
 * question mark followed by a single letter.
 
174
 * Here we decode that letter and return the appropriate boolean value.
 
175
 */
 
176
        static int
 
177
cond(c, where)
 
178
        char c;
 
179
        int where;
 
180
{
 
181
        POSITION len;
 
182
 
 
183
        switch (c)
 
184
        {
 
185
        case 'a':       /* Anything in the message yet? */
 
186
                return (mp > message);
 
187
        case 'b':       /* Current byte offset known? */
 
188
                return (curr_byte(where) != NULL_POSITION);
 
189
        case 'c':
 
190
                return (hshift != 0);
 
191
        case 'e':       /* At end of file? */
 
192
                return (hit_eof);
 
193
        case 'f':       /* Filename known? */
 
194
                return (strcmp(get_filename(curr_ifile), "-") != 0);
 
195
        case 'l':       /* Line number known? */
 
196
        case 'd':       /* Same as l */
 
197
                return (linenums);
 
198
        case 'L':       /* Final line number known? */
 
199
        case 'D':       /* Same as L */
 
200
                return (linenums && ch_length() != NULL_POSITION);
 
201
        case 'm':       /* More than one file? */
 
202
                return (ntags() ? (ntags() > 1) : (nifile() > 1));
 
203
        case 'n':       /* First prompt in a new file? */
 
204
                return (ntags() ? 1 : new_file);
 
205
        case 'p':       /* Percent into file (bytes) known? */
 
206
                return (curr_byte(where) != NULL_POSITION && 
 
207
                                ch_length() > 0);
 
208
        case 'P':       /* Percent into file (lines) known? */
 
209
                return (currline(where) != 0 &&
 
210
                                (len = ch_length()) > 0 &&
 
211
                                find_linenum(len) != 0);
 
212
        case 's':       /* Size of file known? */
 
213
        case 'B':
 
214
                return (ch_length() != NULL_POSITION);
 
215
        case 'x':       /* Is there a "next" file? */
 
216
                if (ntags())
 
217
                        return (0);
 
218
                return (next_ifile(curr_ifile) != NULL_IFILE);
 
219
        }
 
220
        return (0);
 
221
}
 
222
 
 
223
/*
 
224
 * Decode a "percent" prototype character.
 
225
 * A prototype string may include various "percent" escapes;
 
226
 * that is, a percent sign followed by a single letter.
 
227
 * Here we decode that letter and take the appropriate action,
 
228
 * usually by appending something to the message being built.
 
229
 */
 
230
        static void
 
231
protochar(c, where, iseditproto)
 
232
        int c;
 
233
        int where;
 
234
        int iseditproto;
 
235
{
 
236
        POSITION pos;
 
237
        POSITION len;
 
238
        int n;
 
239
        IFILE h;
 
240
 
 
241
        switch (c)
 
242
        {
 
243
        case 'b':       /* Current byte offset */
 
244
                pos = curr_byte(where);
 
245
                if (pos != NULL_POSITION)
 
246
                        ap_pos(pos);
 
247
                else
 
248
                        ap_quest();
 
249
                break;
 
250
        case 'c':
 
251
                ap_int(hshift);
 
252
                break;
 
253
        case 'd':       /* Current page number */
 
254
                n = currline(where);
 
255
                if (n > 0 && sc_height > 1)
 
256
                        ap_int(((n - 1) / (sc_height - 1)) + 1);
 
257
                else
 
258
                        ap_quest();
 
259
                break;
 
260
        case 'D':       /* Last page number */
 
261
                len = ch_length();
 
262
                if (len == NULL_POSITION || len == ch_zero() ||
 
263
                    (n = find_linenum(len)) <= 0)
 
264
                        ap_quest();
 
265
                else
 
266
                        ap_int(((n - 1) / (sc_height - 1)) + 1);
 
267
                break;
 
268
#if EDITOR
 
269
        case 'E':       /* Editor name */
 
270
                ap_str(editor);
 
271
                break;
 
272
#endif
 
273
        case 'f':       /* File name */
 
274
                ap_str(get_filename(curr_ifile));
 
275
                break;
 
276
        case 'i':       /* Index into list of files */
 
277
                if (ntags())
 
278
                        ap_int(curr_tag());
 
279
                else
 
280
                        ap_int(get_index(curr_ifile));
 
281
                break;
 
282
        case 'l':       /* Current line number */
 
283
                n = currline(where);
 
284
                if (n != 0)
 
285
                        ap_int(n);
 
286
                else
 
287
                        ap_quest();
 
288
                break;
 
289
        case 'L':       /* Final line number */
 
290
                len = ch_length();
 
291
                if (len == NULL_POSITION || len == ch_zero() ||
 
292
                    (n = find_linenum(len)) <= 0)
 
293
                        ap_quest();
 
294
                else
 
295
                        ap_int(n-1);
 
296
                break;
 
297
        case 'm':       /* Number of files */
 
298
                n = ntags();
 
299
                if (n)
 
300
                        ap_int(n);
 
301
                else
 
302
                        ap_int(nifile());
 
303
                break;
 
304
        case 'p':       /* Percent into file (bytes) */
 
305
                pos = curr_byte(where);
 
306
                len = ch_length();
 
307
                if (pos != NULL_POSITION && len > 0)
 
308
                        ap_int(percentage(pos,len));
 
309
                else
 
310
                        ap_quest();
 
311
                break;
 
312
        case 'P':       /* Percent into file (lines) */
 
313
                pos = (POSITION) currline(where);
 
314
                if (pos == 0 ||
 
315
                    (len = ch_length()) == NULL_POSITION || len == ch_zero() ||
 
316
                    (n = find_linenum(len)) <= 0)
 
317
                        ap_quest();
 
318
                else
 
319
                        ap_int(percentage(pos, (POSITION)n));
 
320
                break;
 
321
        case 's':       /* Size of file */
 
322
        case 'B':
 
323
                len = ch_length();
 
324
                if (len != NULL_POSITION)
 
325
                        ap_pos(len);
 
326
                else
 
327
                        ap_quest();
 
328
                break;
 
329
        case 't':       /* Truncate trailing spaces in the message */
 
330
                while (mp > message && mp[-1] == ' ')
 
331
                        mp--;
 
332
                break;
 
333
        case 'T':       /* Type of list */
 
334
                if (ntags())
 
335
                        ap_str("tag");
 
336
                else
 
337
                        ap_str("file");
 
338
                break;
 
339
        case 'x':       /* Name of next file */
 
340
                h = next_ifile(curr_ifile);
 
341
                if (h != NULL_IFILE)
 
342
                        ap_str(get_filename(h));
 
343
                else
 
344
                        ap_quest();
 
345
                break;
 
346
        }
 
347
}
 
348
 
 
349
/*
 
350
 * Skip a false conditional.
 
351
 * When a false condition is found (either a false IF or the ELSE part 
 
352
 * of a true IF), this routine scans the prototype string to decide
 
353
 * where to resume parsing the string.
 
354
 * We must keep track of nested IFs and skip them properly.
 
355
 */
 
356
        static char *
 
357
skipcond(p)
 
358
        register char *p;
 
359
{
 
360
        register int iflevel;
 
361
 
 
362
        /*
 
363
         * We came in here after processing a ? or :,
 
364
         * so we start nested one level deep.
 
365
         */
 
366
        iflevel = 1;
 
367
 
 
368
        for (;;) switch (*++p)
 
369
        {
 
370
        case '?':
 
371
                /*
 
372
                 * Start of a nested IF.
 
373
                 */
 
374
                iflevel++;
 
375
                break;
 
376
        case ':':
 
377
                /*
 
378
                 * Else.
 
379
                 * If this matches the IF we came in here with,
 
380
                 * then we're done.
 
381
                 */
 
382
                if (iflevel == 1)
 
383
                        return (p);
 
384
                break;
 
385
        case '.':
 
386
                /*
 
387
                 * Endif.
 
388
                 * If this matches the IF we came in here with,
 
389
                 * then we're done.
 
390
                 */
 
391
                if (--iflevel == 0)
 
392
                        return (p);
 
393
                break;
 
394
        case '\\':
 
395
                /*
 
396
                 * Backslash escapes the next character.
 
397
                 */
 
398
                ++p;
 
399
                break;
 
400
        case '\0':
 
401
                /*
 
402
                 * Whoops.  Hit end of string.
 
403
                 * This is a malformed conditional, but just treat it
 
404
                 * as if all active conditionals ends here.
 
405
                 */
 
406
                return (p-1);
 
407
        }
 
408
        /*NOTREACHED*/
 
409
}
 
410
 
 
411
/*
 
412
 * Decode a char that represents a position on the screen.
 
413
 */
 
414
        static char *
 
415
wherechar(p, wp)
 
416
        char *p;
 
417
        int *wp;
 
418
{
 
419
        switch (*p)
 
420
        {
 
421
        case 'b': case 'd': case 'l': case 'p': case 'P':
 
422
                switch (*++p)
 
423
                {
 
424
                case 't':   *wp = TOP;                  break;
 
425
                case 'm':   *wp = MIDDLE;               break;
 
426
                case 'b':   *wp = BOTTOM;               break;
 
427
                case 'B':   *wp = BOTTOM_PLUS_ONE;      break;
 
428
                case 'j':   *wp = adjsline(jump_sline); break;
 
429
                default:    *wp = TOP;  p--;            break;
 
430
                }
 
431
        }
 
432
        return (p);
 
433
}
 
434
 
 
435
/*
 
436
 * Construct a message based on a prototype string.
 
437
 */
 
438
        public char *
 
439
pr_expand(proto, maxwidth)
 
440
        char *proto;
 
441
        int maxwidth;
 
442
{
 
443
        register char *p;
 
444
        register int c;
 
445
        int where;
 
446
 
 
447
        mp = message;
 
448
 
 
449
        if (*proto == '\0')
 
450
                return ("");
 
451
 
 
452
        for (p = proto;  *p != '\0';  p++)
 
453
        {
 
454
                switch (*p)
 
455
                {
 
456
                default:        /* Just put the character in the message */
 
457
                        ap_char(*p);
 
458
                        break;
 
459
                case '\\':      /* Backslash escapes the next character */
 
460
                        p++;
 
461
                        ap_char(*p);
 
462
                        break;
 
463
                case '?':       /* Conditional (IF) */
 
464
                        if ((c = *++p) == '\0')
 
465
                                --p;
 
466
                        else
 
467
                        {
 
468
                                where = 0;
 
469
                                p = wherechar(p, &where);
 
470
                                if (!cond(c, where))
 
471
                                        p = skipcond(p);
 
472
                        }
 
473
                        break;
 
474
                case ':':       /* ELSE */
 
475
                        p = skipcond(p);
 
476
                        break;
 
477
                case '.':       /* ENDIF */
 
478
                        break;
 
479
                case '%':       /* Percent escape */
 
480
                        if ((c = *++p) == '\0')
 
481
                                --p;
 
482
                        else
 
483
                        {
 
484
                                where = 0;
 
485
                                p = wherechar(p, &where);
 
486
                                protochar(c, where,
 
487
#if EDITOR
 
488
                                        (proto == editproto));
 
489
#else
 
490
                                        0);
 
491
#endif
 
492
 
 
493
                        }
 
494
                        break;
 
495
                }
 
496
        }
 
497
 
 
498
        if (mp == message)
 
499
                return (NULL);
 
500
        if (maxwidth > 0 && mp >= message + maxwidth)
 
501
        {
 
502
                /*
 
503
                 * Message is too long.
 
504
                 * Return just the final portion of it.
 
505
                 */
 
506
                return (mp - maxwidth);
 
507
        }
 
508
        return (message);
 
509
}
 
510
 
 
511
/*
 
512
 * Return a message suitable for printing by the "=" command.
 
513
 */
 
514
        public char *
 
515
eq_message()
 
516
{
 
517
        return (pr_expand(eqproto, 0));
 
518
}
 
519
 
 
520
/*
 
521
 * Return a prompt.
 
522
 * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
 
523
 * If we can't come up with an appropriate prompt, return NULL
 
524
 * and the caller will prompt with a colon.
 
525
 */
 
526
        public char *
 
527
pr_string()
 
528
{
 
529
        char *prompt;
 
530
 
 
531
        prompt = pr_expand((ch_getflags() & CH_HELPFILE) ?
 
532
                                hproto : prproto[pr_type],
 
533
                        sc_width-so_s_width-so_e_width-2);
 
534
        new_file = 0;
 
535
        return (prompt);
 
536
}
 
537
 
 
538
/*
 
539
 * Return a message suitable for printing while waiting in the F command.
 
540
 */
 
541
        public char *
 
542
wait_message()
 
543
{
 
544
        return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2));
 
545
}