Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 463 lines (392 sloc) 12.36 kB
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
1 /* scan.c */
2 /* Copyright 1995 by Steve Kirkendall */
3
97d8998 @mbert Import Elvis 2.1_4 (written by Steve Kirkendall)
authored
4 char id_scan[] = "$Id: scan.c,v 2.17 1999/09/30 18:18:34 steve Exp $";
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
5
6 #include "elvis.h"
7
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
8 #ifdef FEATURE_LITRE
9 static struct scan_s saved;
10 static long changes;
11 #endif
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
12
13 /* This variable points to the top of the stack of scan contexts */
14 struct scan_s *scan__top;
15
16
17 /* This variable points to the head of a list of freed scan contexts.
18 * The scanalloc() function checks this variable, and recycles the first
19 * scan context instead of allocating a new one, to reduce the number of
20 * calls to safealloc().
21 */
22 static struct scan_s *recycle;
23
24
25 /* This variable is used by the scanmark() macro */
26 MARKBUF scan__markbuf;
27
28 /* This function creates a new scan context, and starts the scanning at
29 * a given mark. The scan context must be freed by a later scanfree()
30 * call. The cp argument is used to distinguish one scan context from
31 * another, and the (CHAR *) that it points to will be set to point to the
32 * appropriate CHAR in the buffer. The value of that pointer is returned.
33 * If the seek is past either end of the buffer, *cp is set to NULL.
34 */
35 #ifndef DEBUG_ALLOC
36 CHAR *scanalloc(cp, start)
37 #else
38 CHAR *_scanalloc(file, line, cp, start)
39 char *file; /* name of file where allocating */
40 int line; /* line number where allocating */
41 #endif
42 CHAR **cp; /* address of pointer which will be used for scanning */
43 MARK start; /* where the scanning should begin */
44 {
45 struct scan_s *newp;
46
47 assert(start != NULL && cp != NULL);
48
49 /* allocate a new scan context */
50 #ifndef DEBUG_ALLOC
51 if (recycle)
52 {
53 newp = recycle;
54 recycle = recycle->next;
55 memset((char *)newp, 0, sizeof(*newp));
56 }
57 else
58 {
59 newp = (struct scan_s *)safealloc(1, sizeof *newp);
60 }
61 #else
62 newp = (struct scan_s *)_safealloc(file, line, False, 1, sizeof *newp);
63 #endif
64 newp->next = scan__top;
65 scan__top = newp;
66
67 /* initialize it */
68 newp->ptr = cp;
69 #ifdef DEBUG_ALLOC
70 # ifdef DEBUG_SCAN
71 newp->file = file;
72 newp->line = line;
73 # endif
74 #endif
75 return scanseek(cp, start);
76 }
77
78
79
80 /* This function creates a new scan context for scanning a string. This makes
81 * it possible to write functions which can scan a string or the contents of a
82 * buffer with equal ease.
83 */
84 CHAR *scanstring(cp, str)
85 CHAR **cp; /* address of pointer which will be used for scanning */
86 CHAR *str; /* NUL-terminated string to scan */
87 {
88 struct scan_s *newp;
89
90 assert(str != NULL && cp != NULL);
91
92 /* allocate a new scan context */
93 if (recycle)
94 {
95 newp = recycle;
96 recycle = recycle->next;
97 memset((char *)newp, 0, sizeof(*newp));
98 }
99 else
100 {
101 newp = (struct scan_s *)safealloc(1, sizeof *newp);
102 }
103 newp->next = scan__top;
104 scan__top = newp;
105
106 /* initialize it */
107 newp->buffer = (BUFFER)0;
108 newp->bufinfo = (BLKNO)0;
109 newp->blkno = (BLKNO)0;
110 newp->leftedge = str;
111 newp->rightedge = str + CHARlen(str);
112 newp->ptr = cp;
113 newp->leoffset = 0;
114
115 /* initialize *cp to point to the start of the string */
116 *cp = str;
117 return *cp;
118 }
119
120
121 /* This function allocates a new scan context that is identical to an
122 * existing scan context. I.e., it is like scanalloc(&new, scanmark(&old))
123 * However, this function is usually much faster.
124 */
125 CHAR *scandup(cp, oldp)
126 CHAR **cp; /* address of pointer to use in new scan context */
127 CHAR **oldp; /* address of pointer used in existing scan context */
128 {
129 struct scan_s *newp;
130
131 assert(scan__top && scan__top->ptr == oldp);
132
133 /* allocate a new scan context */
134 if (recycle)
135 {
136 newp = recycle;
137 recycle = recycle->next;
138 memset((char *)newp, 0, sizeof(*newp));
139 }
140 else
141 {
142 newp = (struct scan_s *)safealloc(1, sizeof *newp);
143 }
144 newp->next = scan__top;
145 scan__top = newp;
146
147 /* initialize it to match the old scan context */
148 newp->buffer = newp->next->buffer;
149 newp->bufinfo = newp->next->bufinfo;
150 newp->blkno = newp->next->blkno;
151 newp->leftedge = newp->next->leftedge;
152 newp->rightedge = newp->next->rightedge;
153 newp->ptr = cp;
154 newp->leoffset = newp->next->leoffset;
155
156 /* initialize *cp to point to the correct character */
157 *cp = *oldp;
158
159 /* lock the block that *cp points to. */
160 if (newp->blkno)
161 {
162 seslock(newp->blkno, False, SES_CHARS);
163 }
164
165 return *cp;
166 }
167
168
169 /* This function frees a scan context which was created by scanalloc() */
170 void scanfree(cp)
171 CHAR **cp; /* address of pointer used for scanning */
172 {
173 struct scan_s *context;
174
175 assert(cp != NULL);
176 assert(scan__top != NULL);
177 assert(scan__top->ptr == cp);
178
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
179 #ifdef FEATURE_LITRE
180 /* save info from this scan, to speed up later scans */
97d8998 @mbert Import Elvis 2.1_4 (written by Steve Kirkendall)
authored
181 if (!scan__top->next && scan__top->buffer && scan__top->blkno != 0)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
182 {
183 saved = *scan__top;
97d8998 @mbert Import Elvis 2.1_4 (written by Steve Kirkendall)
authored
184 changes = saved.buffer->changes;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
185 }
186 #endif /* FEATURE_LITRE */
187
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
188 /* delete it from the list */
189 context = scan__top;
190 scan__top = scan__top->next;
191
192 /* free its resources */
193 if (context->blkno)
194 {
195 sesunlock(context->blkno, False);
196 }
197 #ifndef DEBUG_ALLOC
198 context->next = recycle;
199 recycle = context;
200 #else
201 safefree(context);
202 #endif
203 }
204
205 /* This function uses an existing scan context created by scanalloc() or
206 * scanstring(), to start a new scan at a given mark. If the original scan
207 * context was created by scanalloc(), the mark can even refer to a totally
208 * different buffer than the original scan. If the original scan context
209 * was created by scanstring(), then the mark can only be moved within the
210 * original string buffer.
211 */
212 CHAR *scanseek(cp, restart)
213 CHAR **cp; /* address of pointer used for scanning */
214 MARK restart;/* where scanning should resume */
215 {
216 COUNT left, right; /* number of chars in block */
217 BLKNO nextblkno;
218
219 assert(cp != NULL && restart != NULL);
220 assert(scan__top->ptr == cp);
221
222 /* Can't mix string scans with buffer seeks, or vice versa. Testing
223 * this is a bit tricky because scanalloc() calls scanseek() to fill in
224 * a bunch of fields, we have to allow that; hence the leftedge test.
225 */
226 assert(!(markbuffer(restart) && !scan__top->buffer && scan__top->leftedge));
227 assert(!(!markbuffer(restart) && scan__top->buffer));
228
229 /* string scan or buffer scan? */
230 if (!markbuffer(restart))
231 {
232 /* STRING */
233
234 /* compute the new value of the pointer */
235 *cp = &scan__top->leftedge[markoffset(restart)];
236
237 /* if seeking to a non-existent offset, return NULL */
238 if (*cp < scan__top->leftedge
239 || *cp >= scan__top->rightedge)
240 {
241 *cp = NULL;
242 return NULL;
243 }
244 }
245 else
246 {
247 /* BUFFER */
248
249 /* if seeking to a non-existent offset, return NULL */
250 if (markoffset(restart) < 0
251 || markoffset(restart) >= o_bufchars(markbuffer(restart)))
252 {
253 *cp = NULL;
254 return NULL;
255 }
256
257 /* find the bufinfo block for the buffer that MARK refers to */
258 scan__top->buffer = markbuffer(restart);
259 scan__top->bufinfo = bufbufinfo(scan__top->buffer);
260
261 /* find the chars block. If this scan context happens to be
262 * nested inside another one and we're seeking into the same
263 * block, then we can optimize this a lot.
264 */
265 if (scan__top->next
266 && scan__top->next->buffer == scan__top->buffer
267 && scan__top->next->leoffset <= markoffset(restart)
268 && scan__top->next->leoffset +
269 (int)(scan__top->next->rightedge - scan__top->next->leftedge)
270 > markoffset(restart))
271 {
272 /* hooray! We can avoid calling lowoffset() */
273 nextblkno = scan__top->next->blkno;
274 left = (int)(markoffset(restart) - scan__top->next->leoffset);
275 right = (int)(scan__top->next->rightedge - scan__top->next->leftedge)
276 - left;
277 }
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
278 #ifdef FEATURE_LITRE
279 else if (saved.buffer == scan__top->buffer
280 && changes == scan__top->buffer->changes
281 && saved.leoffset <= markoffset(restart)
282 && saved.leoffset + (int)(saved.rightedge - saved.leftedge)
283 > markoffset(restart))
284 {
285 /* In a previous block, but that block isn't locked
286 * anymore. Need to lock it, but at least we avoided
287 * calling lowoffset().
288 */
289 nextblkno = saved.blkno;
290 left = (int)(markoffset(restart) - saved.leoffset);
291 right = (int)(saved.rightedge - saved.leftedge) - left;
292 }
293 #endif /* FEATURE_LITRE */
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
294 else
295 {
296 /* can't optimize; must call lowoffset to find block */
297 nextblkno = lowoffset(scan__top->bufinfo,
298 markoffset(restart), &left, &right, NULL, NULL);
299 }
300 assert(right > 0 && nextblkno > 0);
301
302 /* unlock the old block, and lock the new one */
303 if (scan__top->blkno != nextblkno)
304 {
305 if (scan__top->blkno)
306 {
307 sesunlock(scan__top->blkno, False);
308 }
309 seslock(nextblkno, False, SES_CHARS);
310 scan__top->blkno = nextblkno;
311 }
312
313 /* set the other variables */
314 scan__top->leftedge = sesblk(scan__top->blkno)->chars.chars;
315 scan__top->rightedge = scan__top->leftedge + left + right;
316 scan__top->leoffset = markoffset(restart) - left;
317 *cp = scan__top->leftedge + left;
318 }
319
320 return *cp;
321 }
322
323 #ifdef DEBUG_SCAN
324 /* This function is a helper function for the scanleft() macro. This function
325 * should not be called directly.
326 *
327 * It returns the number of buffered characters to the left of the current
328 * scan point.
329 */
330 COUNT scan__left(cp)
331 CHAR **cp; /* address of pointer used for scanning */
332 {
333 assert(cp != NULL);
334 assert(scan__top->ptr == cp);
335
336 /* return the number of buffered CHARs */
337 return (COUNT)(*cp - scan__top->leftedge);
338 }
339
340 /* This function is a helper function for the scanright() macro. This function
341 * should not be called directly.
342 *
343 * It returns the number of buffered characters to the right of the current
344 * scan point.
345 */
346 COUNT scan__right(cp)
347 CHAR **cp; /* address of pointer used for scanning */
348 {
349 assert(cp != NULL);
350 assert(scan__top->ptr == cp);
351
352 /* return the number of buffered CHARs */
353 return (COUNT)(scan__top->rightedge - *cp);
354 }
355
356 /* create a mark which refers to the current point of the scan */
357 MARK scanmark(cp)
358 CHAR **cp; /* address of pointer used for scanning */
359 {
360 assert(cp != NULL);
361 assert(scan__top->ptr == cp);
362
363 /* return a temporary mark at the proper position */
364 return marktmp(scan__markbuf, scan__top->buffer, scan__top->leoffset + (int)(*cp - scan__top->leftedge));
365 }
366 #endif /* DEBUG_SCAN */
367
368 /* This function is a helper function for the scannext() macro. This function
369 * should not be called directly.
370 */
371 CHAR *scan__next(cp)
372 CHAR **cp; /* address of pointer used for scanning */
373 {
374 MARKBUF markbuf;
375
376 assert(cp != NULL && *cp != NULL);
377 assert(scan__top->ptr == cp);
378
379 /* if the next character is in the same block, its easy */
380 if (++*cp < scan__top->rightedge)
381 {
382 return *cp;
383 }
384
385 assert(*cp == scan__top->rightedge);
386
387 /* else seek to the first character of following block */
388 return scanseek(cp, marktmp(markbuf, scan__top->buffer,
389 scan__top->leoffset + (int)(scan__top->rightedge - scan__top->leftedge)));
390 }
391
392 /* This function is a helper function for the scanprev() macro. This function
393 * should not be called directly.
394 */
395 CHAR *scan__prev(cp)
396 CHAR **cp; /* address of pointer used for scanning */
397 {
398 MARKBUF markbuf;
399
400 assert(cp != NULL);
401 assert(scan__top->ptr == cp);
402
403 /* if the previous character is in the same block, its easy */
404 if (--*cp >= scan__top->leftedge)
405 {
406 return *cp;
407 }
408
409 /* if this was the first block, we're at the end */
410 if (scan__top->leoffset == 0)
411 {
412 if (scan__top->blkno != 0)
413 {
414 sesunlock(scan__top->blkno, False);
415 scan__top->blkno = 0;
416 }
417 *cp = NULL;
418 return NULL;
419 }
420
421 /* else seek to the last character of preceding block */
422 return scanseek(cp, marktmp(markbuf, scan__top->buffer, scan__top->leoffset - 1));
423 }
424
425
426 /* This function returns the single character at a given location. It is
427 * similar to scanseek(), except that this function doesn't use a scan context,
428 * and it returns a CHAR instead of a pointer-to-CHAR.
429 */
430 CHAR scanchar(mark)
431 MARK mark; /* buffer/offset of the character to fetch */
432 {
433 COUNT left, right; /* number of chars in block */
434 BLKNO bufinfo; /* block describing the buffer */
435 BLKNO blkno; /* block containing the character */
436 CHAR ch; /* the character */
437
438 /* if seeking to a non-existent offset, return '\0' */
439 if (markoffset(mark) < 0
440 || markoffset(mark) >= o_bufchars(markbuffer(mark)))
441 {
442 return '\0';
443 }
444
445 /* find the bufinfo block for the buffer that MARK refers to */
446 bufinfo = bufbufinfo(markbuffer(mark));
447
448 /* find the chars block */
449 blkno = lowoffset(bufinfo, markoffset(mark), &left, &right, NULL, NULL);
450 assert(right != 0);
451
452 /* lock the block */
453 seslock(blkno, False, SES_CHARS);
454
455 /* fetch the character */
456 ch = sesblk(blkno)->chars.chars[left];
457
458 /* unlock the block */
459 sesunlock(blkno, False);
460
461 return ch;
462 }
Something went wrong with that request. Please try again.