1*84d9c625SLionel Sambuc /* $NetBSD: linenum.c,v 1.4 2013/09/04 19:44:21 tron Exp $ */
2f7cf2976SLionel Sambuc
3f7cf2976SLionel Sambuc /*
4*84d9c625SLionel Sambuc * Copyright (C) 1984-2012 Mark Nudelman
5f7cf2976SLionel Sambuc *
6f7cf2976SLionel Sambuc * You may distribute under the terms of either the GNU General Public
7f7cf2976SLionel Sambuc * License or the Less License, as specified in the README file.
8f7cf2976SLionel Sambuc *
9*84d9c625SLionel Sambuc * For more information, see the README file.
10f7cf2976SLionel Sambuc */
11f7cf2976SLionel Sambuc
12f7cf2976SLionel Sambuc
13f7cf2976SLionel Sambuc /*
14f7cf2976SLionel Sambuc * Code to handle displaying line numbers.
15f7cf2976SLionel Sambuc *
16f7cf2976SLionel Sambuc * Finding the line number of a given file position is rather tricky.
17f7cf2976SLionel Sambuc * We don't want to just start at the beginning of the file and
18f7cf2976SLionel Sambuc * count newlines, because that is slow for large files (and also
19f7cf2976SLionel Sambuc * wouldn't work if we couldn't get to the start of the file; e.g.
20f7cf2976SLionel Sambuc * if input is a long pipe).
21f7cf2976SLionel Sambuc *
22f7cf2976SLionel Sambuc * So we use the function add_lnum to cache line numbers.
23f7cf2976SLionel Sambuc * We try to be very clever and keep only the more interesting
24f7cf2976SLionel Sambuc * line numbers when we run out of space in our table. A line
25f7cf2976SLionel Sambuc * number is more interesting than another when it is far from
26f7cf2976SLionel Sambuc * other line numbers. For example, we'd rather keep lines
27f7cf2976SLionel Sambuc * 100,200,300 than 100,101,300. 200 is more interesting than
28f7cf2976SLionel Sambuc * 101 because 101 can be derived very cheaply from 100, while
29f7cf2976SLionel Sambuc * 200 is more expensive to derive from 100.
30f7cf2976SLionel Sambuc *
31f7cf2976SLionel Sambuc * The function currline() returns the line number of a given
32f7cf2976SLionel Sambuc * position in the file. As a side effect, it calls add_lnum
33f7cf2976SLionel Sambuc * to cache the line number. Therefore currline is occasionally
34f7cf2976SLionel Sambuc * called to make sure we cache line numbers often enough.
35f7cf2976SLionel Sambuc */
36f7cf2976SLionel Sambuc
37f7cf2976SLionel Sambuc #include "less.h"
38f7cf2976SLionel Sambuc
39f7cf2976SLionel Sambuc /*
40f7cf2976SLionel Sambuc * Structure to keep track of a line number and the associated file position.
41f7cf2976SLionel Sambuc * A doubly-linked circular list of line numbers is kept ordered by line number.
42f7cf2976SLionel Sambuc */
43f7cf2976SLionel Sambuc struct linenum_info
44f7cf2976SLionel Sambuc {
45f7cf2976SLionel Sambuc struct linenum_info *next; /* Link to next in the list */
46f7cf2976SLionel Sambuc struct linenum_info *prev; /* Line to previous in the list */
47f7cf2976SLionel Sambuc POSITION pos; /* File position */
48f7cf2976SLionel Sambuc POSITION gap; /* Gap between prev and next */
49f7cf2976SLionel Sambuc LINENUM line; /* Line number */
50f7cf2976SLionel Sambuc };
51f7cf2976SLionel Sambuc /*
52f7cf2976SLionel Sambuc * "gap" needs some explanation: the gap of any particular line number
53f7cf2976SLionel Sambuc * is the distance between the previous one and the next one in the list.
54f7cf2976SLionel Sambuc * ("Distance" means difference in file position.) In other words, the
55f7cf2976SLionel Sambuc * gap of a line number is the gap which would be introduced if this
56f7cf2976SLionel Sambuc * line number were deleted. It is used to decide which one to replace
57f7cf2976SLionel Sambuc * when we have a new one to insert and the table is full.
58f7cf2976SLionel Sambuc */
59f7cf2976SLionel Sambuc
60f7cf2976SLionel Sambuc #define NPOOL 200 /* Size of line number pool */
61f7cf2976SLionel Sambuc
62f7cf2976SLionel Sambuc #define LONGTIME (2) /* In seconds */
63f7cf2976SLionel Sambuc
64f7cf2976SLionel Sambuc static struct linenum_info anchor; /* Anchor of the list */
65f7cf2976SLionel Sambuc static struct linenum_info *freelist; /* Anchor of the unused entries */
66f7cf2976SLionel Sambuc static struct linenum_info pool[NPOOL]; /* The pool itself */
67f7cf2976SLionel Sambuc static struct linenum_info *spare; /* We always keep one spare entry */
68f7cf2976SLionel Sambuc
69f7cf2976SLionel Sambuc extern int linenums;
70f7cf2976SLionel Sambuc extern int sigs;
71f7cf2976SLionel Sambuc extern int sc_height;
72f7cf2976SLionel Sambuc extern int screen_trashed;
73f7cf2976SLionel Sambuc
74f7cf2976SLionel Sambuc static void calcgap __P((struct linenum_info *));
75f7cf2976SLionel Sambuc static void longloopmessage __P((void));
76f7cf2976SLionel Sambuc static void longish __P((void));
77f7cf2976SLionel Sambuc
78f7cf2976SLionel Sambuc /*
79f7cf2976SLionel Sambuc * Initialize the line number structures.
80f7cf2976SLionel Sambuc */
81f7cf2976SLionel Sambuc public void
clr_linenum()82f7cf2976SLionel Sambuc clr_linenum()
83f7cf2976SLionel Sambuc {
84f7cf2976SLionel Sambuc register struct linenum_info *p;
85f7cf2976SLionel Sambuc
86f7cf2976SLionel Sambuc /*
87f7cf2976SLionel Sambuc * Put all the entries on the free list.
88f7cf2976SLionel Sambuc * Leave one for the "spare".
89f7cf2976SLionel Sambuc */
90f7cf2976SLionel Sambuc for (p = pool; p < &pool[NPOOL-2]; p++)
91f7cf2976SLionel Sambuc p->next = p+1;
92f7cf2976SLionel Sambuc pool[NPOOL-2].next = NULL;
93f7cf2976SLionel Sambuc freelist = pool;
94f7cf2976SLionel Sambuc
95f7cf2976SLionel Sambuc spare = &pool[NPOOL-1];
96f7cf2976SLionel Sambuc
97f7cf2976SLionel Sambuc /*
98f7cf2976SLionel Sambuc * Initialize the anchor.
99f7cf2976SLionel Sambuc */
100f7cf2976SLionel Sambuc anchor.next = anchor.prev = &anchor;
101f7cf2976SLionel Sambuc anchor.gap = 0;
102f7cf2976SLionel Sambuc anchor.pos = (POSITION)0;
103f7cf2976SLionel Sambuc anchor.line = 1;
104f7cf2976SLionel Sambuc }
105f7cf2976SLionel Sambuc
106f7cf2976SLionel Sambuc /*
107f7cf2976SLionel Sambuc * Calculate the gap for an entry.
108f7cf2976SLionel Sambuc */
109f7cf2976SLionel Sambuc static void
calcgap(p)110f7cf2976SLionel Sambuc calcgap(p)
111f7cf2976SLionel Sambuc register struct linenum_info *p;
112f7cf2976SLionel Sambuc {
113f7cf2976SLionel Sambuc /*
114f7cf2976SLionel Sambuc * Don't bother to compute a gap for the anchor.
115f7cf2976SLionel Sambuc * Also don't compute a gap for the last one in the list.
116f7cf2976SLionel Sambuc * The gap for that last one should be considered infinite,
117f7cf2976SLionel Sambuc * but we never look at it anyway.
118f7cf2976SLionel Sambuc */
119f7cf2976SLionel Sambuc if (p == &anchor || p->next == &anchor)
120f7cf2976SLionel Sambuc return;
121f7cf2976SLionel Sambuc p->gap = p->next->pos - p->prev->pos;
122f7cf2976SLionel Sambuc }
123f7cf2976SLionel Sambuc
124f7cf2976SLionel Sambuc /*
125f7cf2976SLionel Sambuc * Add a new line number to the cache.
126f7cf2976SLionel Sambuc * The specified position (pos) should be the file position of the
127f7cf2976SLionel Sambuc * FIRST character in the specified line.
128f7cf2976SLionel Sambuc */
129f7cf2976SLionel Sambuc public void
add_lnum(linenum,pos)130f7cf2976SLionel Sambuc add_lnum(linenum, pos)
131f7cf2976SLionel Sambuc LINENUM linenum;
132f7cf2976SLionel Sambuc POSITION pos;
133f7cf2976SLionel Sambuc {
134f7cf2976SLionel Sambuc register struct linenum_info *p;
135f7cf2976SLionel Sambuc register struct linenum_info *new;
136f7cf2976SLionel Sambuc register struct linenum_info *nextp;
137f7cf2976SLionel Sambuc register struct linenum_info *prevp;
138f7cf2976SLionel Sambuc register POSITION mingap;
139f7cf2976SLionel Sambuc
140f7cf2976SLionel Sambuc /*
141f7cf2976SLionel Sambuc * Find the proper place in the list for the new one.
142f7cf2976SLionel Sambuc * The entries are sorted by position.
143f7cf2976SLionel Sambuc */
144f7cf2976SLionel Sambuc for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
145f7cf2976SLionel Sambuc if (p->line == linenum)
146f7cf2976SLionel Sambuc /* We already have this one. */
147f7cf2976SLionel Sambuc return;
148f7cf2976SLionel Sambuc nextp = p;
149f7cf2976SLionel Sambuc prevp = p->prev;
150f7cf2976SLionel Sambuc
151f7cf2976SLionel Sambuc if (freelist != NULL)
152f7cf2976SLionel Sambuc {
153f7cf2976SLionel Sambuc /*
154f7cf2976SLionel Sambuc * We still have free (unused) entries.
155f7cf2976SLionel Sambuc * Use one of them.
156f7cf2976SLionel Sambuc */
157f7cf2976SLionel Sambuc new = freelist;
158f7cf2976SLionel Sambuc freelist = freelist->next;
159f7cf2976SLionel Sambuc } else
160f7cf2976SLionel Sambuc {
161f7cf2976SLionel Sambuc /*
162f7cf2976SLionel Sambuc * No free entries.
163f7cf2976SLionel Sambuc * Use the "spare" entry.
164f7cf2976SLionel Sambuc */
165f7cf2976SLionel Sambuc new = spare;
166f7cf2976SLionel Sambuc spare = NULL;
167f7cf2976SLionel Sambuc }
168f7cf2976SLionel Sambuc
169f7cf2976SLionel Sambuc /*
170f7cf2976SLionel Sambuc * Fill in the fields of the new entry,
171f7cf2976SLionel Sambuc * and insert it into the proper place in the list.
172f7cf2976SLionel Sambuc */
173f7cf2976SLionel Sambuc new->next = nextp;
174f7cf2976SLionel Sambuc new->prev = prevp;
175f7cf2976SLionel Sambuc new->pos = pos;
176f7cf2976SLionel Sambuc new->line = linenum;
177f7cf2976SLionel Sambuc
178f7cf2976SLionel Sambuc nextp->prev = new;
179f7cf2976SLionel Sambuc prevp->next = new;
180f7cf2976SLionel Sambuc
181f7cf2976SLionel Sambuc /*
182f7cf2976SLionel Sambuc * Recalculate gaps for the new entry and the neighboring entries.
183f7cf2976SLionel Sambuc */
184f7cf2976SLionel Sambuc calcgap(new);
185f7cf2976SLionel Sambuc calcgap(nextp);
186f7cf2976SLionel Sambuc calcgap(prevp);
187f7cf2976SLionel Sambuc
188f7cf2976SLionel Sambuc if (spare == NULL)
189f7cf2976SLionel Sambuc {
190f7cf2976SLionel Sambuc /*
191f7cf2976SLionel Sambuc * We have used the spare entry.
192f7cf2976SLionel Sambuc * Scan the list to find the one with the smallest
193f7cf2976SLionel Sambuc * gap, take it out and make it the spare.
194f7cf2976SLionel Sambuc * We should never remove the last one, so stop when
195f7cf2976SLionel Sambuc * we get to p->next == &anchor. This also avoids
196f7cf2976SLionel Sambuc * looking at the gap of the last one, which is
197f7cf2976SLionel Sambuc * not computed by calcgap.
198f7cf2976SLionel Sambuc */
199f7cf2976SLionel Sambuc mingap = anchor.next->gap;
200f7cf2976SLionel Sambuc for (p = anchor.next; p->next != &anchor; p = p->next)
201f7cf2976SLionel Sambuc {
202f7cf2976SLionel Sambuc if (p->gap <= mingap)
203f7cf2976SLionel Sambuc {
204f7cf2976SLionel Sambuc spare = p;
205f7cf2976SLionel Sambuc mingap = p->gap;
206f7cf2976SLionel Sambuc }
207f7cf2976SLionel Sambuc }
208f7cf2976SLionel Sambuc spare->next->prev = spare->prev;
209f7cf2976SLionel Sambuc spare->prev->next = spare->next;
210f7cf2976SLionel Sambuc }
211f7cf2976SLionel Sambuc }
212f7cf2976SLionel Sambuc
213f7cf2976SLionel Sambuc /*
214f7cf2976SLionel Sambuc * If we get stuck in a long loop trying to figure out the
215f7cf2976SLionel Sambuc * line number, print a message to tell the user what we're doing.
216f7cf2976SLionel Sambuc */
217f7cf2976SLionel Sambuc static void
longloopmessage()218f7cf2976SLionel Sambuc longloopmessage()
219f7cf2976SLionel Sambuc {
220f7cf2976SLionel Sambuc ierror("Calculating line numbers", NULL_PARG);
221f7cf2976SLionel Sambuc }
222f7cf2976SLionel Sambuc
223f7cf2976SLionel Sambuc static int loopcount;
224f7cf2976SLionel Sambuc #if HAVE_TIME
225f7cf2976SLionel Sambuc static long startime;
226f7cf2976SLionel Sambuc #endif
227f7cf2976SLionel Sambuc
228f7cf2976SLionel Sambuc static void
longish()229f7cf2976SLionel Sambuc longish()
230f7cf2976SLionel Sambuc {
231f7cf2976SLionel Sambuc #if HAVE_TIME
232f7cf2976SLionel Sambuc if (loopcount >= 0 && ++loopcount > 100)
233f7cf2976SLionel Sambuc {
234f7cf2976SLionel Sambuc loopcount = 0;
235f7cf2976SLionel Sambuc if (get_time() >= startime + LONGTIME)
236f7cf2976SLionel Sambuc {
237f7cf2976SLionel Sambuc longloopmessage();
238f7cf2976SLionel Sambuc loopcount = -1;
239f7cf2976SLionel Sambuc }
240f7cf2976SLionel Sambuc }
241f7cf2976SLionel Sambuc #else
242f7cf2976SLionel Sambuc if (loopcount >= 0 && ++loopcount > LONGLOOP)
243f7cf2976SLionel Sambuc {
244f7cf2976SLionel Sambuc longloopmessage();
245f7cf2976SLionel Sambuc loopcount = -1;
246f7cf2976SLionel Sambuc }
247f7cf2976SLionel Sambuc #endif
248f7cf2976SLionel Sambuc }
249f7cf2976SLionel Sambuc
250f7cf2976SLionel Sambuc /*
251f7cf2976SLionel Sambuc * Turn off line numbers because the user has interrupted
252f7cf2976SLionel Sambuc * a lengthy line number calculation.
253f7cf2976SLionel Sambuc */
254f7cf2976SLionel Sambuc static void
abort_long()255f7cf2976SLionel Sambuc abort_long()
256f7cf2976SLionel Sambuc {
257f7cf2976SLionel Sambuc if (linenums == OPT_ONPLUS)
258f7cf2976SLionel Sambuc /*
259f7cf2976SLionel Sambuc * We were displaying line numbers, so need to repaint.
260f7cf2976SLionel Sambuc */
261f7cf2976SLionel Sambuc screen_trashed = 1;
262f7cf2976SLionel Sambuc linenums = 0;
263f7cf2976SLionel Sambuc error("Line numbers turned off", NULL_PARG);
264f7cf2976SLionel Sambuc }
265f7cf2976SLionel Sambuc
266f7cf2976SLionel Sambuc /*
267f7cf2976SLionel Sambuc * Find the line number associated with a given position.
268f7cf2976SLionel Sambuc * Return 0 if we can't figure it out.
269f7cf2976SLionel Sambuc */
270f7cf2976SLionel Sambuc public LINENUM
find_linenum(pos)271f7cf2976SLionel Sambuc find_linenum(pos)
272f7cf2976SLionel Sambuc POSITION pos;
273f7cf2976SLionel Sambuc {
274f7cf2976SLionel Sambuc register struct linenum_info *p;
275f7cf2976SLionel Sambuc register LINENUM linenum;
276f7cf2976SLionel Sambuc POSITION cpos;
277f7cf2976SLionel Sambuc
278f7cf2976SLionel Sambuc if (!linenums)
279f7cf2976SLionel Sambuc /*
280f7cf2976SLionel Sambuc * We're not using line numbers.
281f7cf2976SLionel Sambuc */
282f7cf2976SLionel Sambuc return (0);
283f7cf2976SLionel Sambuc if (pos == NULL_POSITION)
284f7cf2976SLionel Sambuc /*
285f7cf2976SLionel Sambuc * Caller doesn't know what he's talking about.
286f7cf2976SLionel Sambuc */
287f7cf2976SLionel Sambuc return (0);
288f7cf2976SLionel Sambuc if (pos <= ch_zero())
289f7cf2976SLionel Sambuc /*
290f7cf2976SLionel Sambuc * Beginning of file is always line number 1.
291f7cf2976SLionel Sambuc */
292f7cf2976SLionel Sambuc return (1);
293f7cf2976SLionel Sambuc
294f7cf2976SLionel Sambuc /*
295f7cf2976SLionel Sambuc * Find the entry nearest to the position we want.
296f7cf2976SLionel Sambuc */
297f7cf2976SLionel Sambuc for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
298f7cf2976SLionel Sambuc continue;
299f7cf2976SLionel Sambuc if (p->pos == pos)
300f7cf2976SLionel Sambuc /* Found it exactly. */
301f7cf2976SLionel Sambuc return (p->line);
302f7cf2976SLionel Sambuc
303f7cf2976SLionel Sambuc /*
304f7cf2976SLionel Sambuc * This is the (possibly) time-consuming part.
305f7cf2976SLionel Sambuc * We start at the line we just found and start
306f7cf2976SLionel Sambuc * reading the file forward or backward till we
307f7cf2976SLionel Sambuc * get to the place we want.
308f7cf2976SLionel Sambuc *
309f7cf2976SLionel Sambuc * First decide whether we should go forward from the
310f7cf2976SLionel Sambuc * previous one or backwards from the next one.
311f7cf2976SLionel Sambuc * The decision is based on which way involves
312f7cf2976SLionel Sambuc * traversing fewer bytes in the file.
313f7cf2976SLionel Sambuc */
314f7cf2976SLionel Sambuc #if HAVE_TIME
315f7cf2976SLionel Sambuc startime = get_time();
316f7cf2976SLionel Sambuc #endif
317f7cf2976SLionel Sambuc if (p == &anchor || pos - p->prev->pos < p->pos - pos)
318f7cf2976SLionel Sambuc {
319f7cf2976SLionel Sambuc /*
320f7cf2976SLionel Sambuc * Go forward.
321f7cf2976SLionel Sambuc */
322f7cf2976SLionel Sambuc p = p->prev;
323f7cf2976SLionel Sambuc if (ch_seek(p->pos))
324f7cf2976SLionel Sambuc return (0);
325f7cf2976SLionel Sambuc loopcount = 0;
326f7cf2976SLionel Sambuc for (linenum = p->line, cpos = p->pos; cpos < pos; linenum++)
327f7cf2976SLionel Sambuc {
328f7cf2976SLionel Sambuc /*
329f7cf2976SLionel Sambuc * Allow a signal to abort this loop.
330f7cf2976SLionel Sambuc */
331f7cf2976SLionel Sambuc cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL);
332f7cf2976SLionel Sambuc if (ABORT_SIGS()) {
333f7cf2976SLionel Sambuc abort_long();
334f7cf2976SLionel Sambuc return (0);
335f7cf2976SLionel Sambuc }
336f7cf2976SLionel Sambuc if (cpos == NULL_POSITION)
337f7cf2976SLionel Sambuc return (0);
338f7cf2976SLionel Sambuc longish();
339f7cf2976SLionel Sambuc }
340f7cf2976SLionel Sambuc /*
341f7cf2976SLionel Sambuc * We might as well cache it.
342f7cf2976SLionel Sambuc */
343f7cf2976SLionel Sambuc add_lnum(linenum, cpos);
344f7cf2976SLionel Sambuc /*
345f7cf2976SLionel Sambuc * If the given position is not at the start of a line,
346f7cf2976SLionel Sambuc * make sure we return the correct line number.
347f7cf2976SLionel Sambuc */
348f7cf2976SLionel Sambuc if (cpos > pos)
349f7cf2976SLionel Sambuc linenum--;
350f7cf2976SLionel Sambuc } else
351f7cf2976SLionel Sambuc {
352f7cf2976SLionel Sambuc /*
353f7cf2976SLionel Sambuc * Go backward.
354f7cf2976SLionel Sambuc */
355f7cf2976SLionel Sambuc if (ch_seek(p->pos))
356f7cf2976SLionel Sambuc return (0);
357f7cf2976SLionel Sambuc loopcount = 0;
358f7cf2976SLionel Sambuc for (linenum = p->line, cpos = p->pos; cpos > pos; linenum--)
359f7cf2976SLionel Sambuc {
360f7cf2976SLionel Sambuc /*
361f7cf2976SLionel Sambuc * Allow a signal to abort this loop.
362f7cf2976SLionel Sambuc */
363f7cf2976SLionel Sambuc cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL);
364f7cf2976SLionel Sambuc if (ABORT_SIGS()) {
365f7cf2976SLionel Sambuc abort_long();
366f7cf2976SLionel Sambuc return (0);
367f7cf2976SLionel Sambuc }
368f7cf2976SLionel Sambuc if (cpos == NULL_POSITION)
369f7cf2976SLionel Sambuc return (0);
370f7cf2976SLionel Sambuc longish();
371f7cf2976SLionel Sambuc }
372f7cf2976SLionel Sambuc /*
373f7cf2976SLionel Sambuc * We might as well cache it.
374f7cf2976SLionel Sambuc */
375f7cf2976SLionel Sambuc add_lnum(linenum, cpos);
376f7cf2976SLionel Sambuc }
377f7cf2976SLionel Sambuc
378f7cf2976SLionel Sambuc return (linenum);
379f7cf2976SLionel Sambuc }
380f7cf2976SLionel Sambuc
381f7cf2976SLionel Sambuc /*
382f7cf2976SLionel Sambuc * Find the position of a given line number.
383f7cf2976SLionel Sambuc * Return NULL_POSITION if we can't figure it out.
384f7cf2976SLionel Sambuc */
385f7cf2976SLionel Sambuc public POSITION
find_pos(linenum)386f7cf2976SLionel Sambuc find_pos(linenum)
387f7cf2976SLionel Sambuc LINENUM linenum;
388f7cf2976SLionel Sambuc {
389f7cf2976SLionel Sambuc register struct linenum_info *p;
390f7cf2976SLionel Sambuc POSITION cpos;
391f7cf2976SLionel Sambuc LINENUM clinenum;
392f7cf2976SLionel Sambuc
393f7cf2976SLionel Sambuc if (linenum <= 1)
394f7cf2976SLionel Sambuc /*
395f7cf2976SLionel Sambuc * Line number 1 is beginning of file.
396f7cf2976SLionel Sambuc */
397f7cf2976SLionel Sambuc return (ch_zero());
398f7cf2976SLionel Sambuc
399f7cf2976SLionel Sambuc /*
400f7cf2976SLionel Sambuc * Find the entry nearest to the line number we want.
401f7cf2976SLionel Sambuc */
402f7cf2976SLionel Sambuc for (p = anchor.next; p != &anchor && p->line < linenum; p = p->next)
403f7cf2976SLionel Sambuc continue;
404f7cf2976SLionel Sambuc if (p->line == linenum)
405f7cf2976SLionel Sambuc /* Found it exactly. */
406f7cf2976SLionel Sambuc return (p->pos);
407f7cf2976SLionel Sambuc
408f7cf2976SLionel Sambuc if (p == &anchor || linenum - p->prev->line < p->line - linenum)
409f7cf2976SLionel Sambuc {
410f7cf2976SLionel Sambuc /*
411f7cf2976SLionel Sambuc * Go forward.
412f7cf2976SLionel Sambuc */
413f7cf2976SLionel Sambuc p = p->prev;
414f7cf2976SLionel Sambuc if (ch_seek(p->pos))
415f7cf2976SLionel Sambuc return (NULL_POSITION);
416f7cf2976SLionel Sambuc for (clinenum = p->line, cpos = p->pos; clinenum < linenum; clinenum++)
417f7cf2976SLionel Sambuc {
418f7cf2976SLionel Sambuc /*
419f7cf2976SLionel Sambuc * Allow a signal to abort this loop.
420f7cf2976SLionel Sambuc */
421f7cf2976SLionel Sambuc cpos = forw_raw_line(cpos, (char **)NULL, (int *)NULL);
422f7cf2976SLionel Sambuc if (ABORT_SIGS())
423f7cf2976SLionel Sambuc return (NULL_POSITION);
424f7cf2976SLionel Sambuc if (cpos == NULL_POSITION)
425f7cf2976SLionel Sambuc return (NULL_POSITION);
426f7cf2976SLionel Sambuc }
427f7cf2976SLionel Sambuc } else
428f7cf2976SLionel Sambuc {
429f7cf2976SLionel Sambuc /*
430f7cf2976SLionel Sambuc * Go backward.
431f7cf2976SLionel Sambuc */
432f7cf2976SLionel Sambuc if (ch_seek(p->pos))
433f7cf2976SLionel Sambuc return (NULL_POSITION);
434f7cf2976SLionel Sambuc for (clinenum = p->line, cpos = p->pos; clinenum > linenum; clinenum--)
435f7cf2976SLionel Sambuc {
436f7cf2976SLionel Sambuc /*
437f7cf2976SLionel Sambuc * Allow a signal to abort this loop.
438f7cf2976SLionel Sambuc */
439f7cf2976SLionel Sambuc cpos = back_raw_line(cpos, (char **)NULL, (int *)NULL);
440f7cf2976SLionel Sambuc if (ABORT_SIGS())
441f7cf2976SLionel Sambuc return (NULL_POSITION);
442f7cf2976SLionel Sambuc if (cpos == NULL_POSITION)
443f7cf2976SLionel Sambuc return (NULL_POSITION);
444f7cf2976SLionel Sambuc }
445f7cf2976SLionel Sambuc }
446f7cf2976SLionel Sambuc /*
447f7cf2976SLionel Sambuc * We might as well cache it.
448f7cf2976SLionel Sambuc */
449f7cf2976SLionel Sambuc add_lnum(clinenum, cpos);
450f7cf2976SLionel Sambuc return (cpos);
451f7cf2976SLionel Sambuc }
452f7cf2976SLionel Sambuc
453f7cf2976SLionel Sambuc /*
454f7cf2976SLionel Sambuc * Return the line number of the "current" line.
455f7cf2976SLionel Sambuc * The argument "where" tells which line is to be considered
456f7cf2976SLionel Sambuc * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
457f7cf2976SLionel Sambuc */
458f7cf2976SLionel Sambuc public LINENUM
currline(where)459f7cf2976SLionel Sambuc currline(where)
460f7cf2976SLionel Sambuc int where;
461f7cf2976SLionel Sambuc {
462f7cf2976SLionel Sambuc POSITION pos;
463f7cf2976SLionel Sambuc POSITION len;
464f7cf2976SLionel Sambuc LINENUM linenum;
465f7cf2976SLionel Sambuc
466f7cf2976SLionel Sambuc pos = position(where);
467f7cf2976SLionel Sambuc len = ch_length();
468f7cf2976SLionel Sambuc while (pos == NULL_POSITION && where >= 0 && where < sc_height)
469f7cf2976SLionel Sambuc pos = position(++where);
470f7cf2976SLionel Sambuc if (pos == NULL_POSITION)
471f7cf2976SLionel Sambuc pos = len;
472f7cf2976SLionel Sambuc linenum = find_linenum(pos);
473f7cf2976SLionel Sambuc if (pos == len)
474f7cf2976SLionel Sambuc linenum--;
475f7cf2976SLionel Sambuc return (linenum);
476f7cf2976SLionel Sambuc }
477