Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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