This is scan.c in view mode; [Download] [Up]
/* scan.c */ /* Copyright 1995 by Steve Kirkendall */ char id_scan[] = "$Id: scan.c,v 2.14 1996/08/28 23:45:16 steve Exp $"; #include "elvis.h" /* This variable points to the top of the stack of scan contexts */ struct scan_s *scan__top; /* This variable points to the head of a list of freed scan contexts. * The scanalloc() function checks this variable, and recycles the first * scan context instead of allocating a new one, to reduce the number of * calls to safealloc(). */ static struct scan_s *recycle; /* This variable is used by the scanmark() macro */ MARKBUF scan__markbuf; /* This function creates a new scan context, and starts the scanning at * a given mark. The scan context must be freed by a later scanfree() * call. The cp argument is used to distinguish one scan context from * another, and the (CHAR *) that it points to will be set to point to the * appropriate CHAR in the buffer. The value of that pointer is returned. * If the seek is past either end of the buffer, *cp is set to NULL. */ #ifndef DEBUG_ALLOC CHAR *scanalloc(cp, start) #else CHAR *_scanalloc(file, line, cp, start) char *file; /* name of file where allocating */ int line; /* line number where allocating */ #endif CHAR **cp; /* address of pointer which will be used for scanning */ MARK start; /* where the scanning should begin */ { struct scan_s *newp; assert(start != NULL && cp != NULL); /* allocate a new scan context */ #ifndef DEBUG_ALLOC if (recycle) { newp = recycle; recycle = recycle->next; memset((char *)newp, 0, sizeof(*newp)); } else { newp = (struct scan_s *)safealloc(1, sizeof *newp); } #else newp = (struct scan_s *)_safealloc(file, line, False, 1, sizeof *newp); #endif newp->next = scan__top; scan__top = newp; /* initialize it */ newp->ptr = cp; #ifdef DEBUG_ALLOC # ifdef DEBUG_SCAN newp->file = file; newp->line = line; # endif #endif return scanseek(cp, start); } /* This function creates a new scan context for scanning a string. This makes * it possible to write functions which can scan a string or the contents of a * buffer with equal ease. */ CHAR *scanstring(cp, str) CHAR **cp; /* address of pointer which will be used for scanning */ CHAR *str; /* NUL-terminated string to scan */ { struct scan_s *newp; assert(str != NULL && cp != NULL); /* allocate a new scan context */ if (recycle) { newp = recycle; recycle = recycle->next; memset((char *)newp, 0, sizeof(*newp)); } else { newp = (struct scan_s *)safealloc(1, sizeof *newp); } newp->next = scan__top; scan__top = newp; /* initialize it */ newp->buffer = (BUFFER)0; newp->bufinfo = (BLKNO)0; newp->blkno = (BLKNO)0; newp->leftedge = str; newp->rightedge = str + CHARlen(str); newp->ptr = cp; newp->leoffset = 0; /* initialize *cp to point to the start of the string */ *cp = str; return *cp; } /* This function allocates a new scan context that is identical to an * existing scan context. I.e., it is like scanalloc(&new, scanmark(&old)) * However, this function is usually much faster. */ CHAR *scandup(cp, oldp) CHAR **cp; /* address of pointer to use in new scan context */ CHAR **oldp; /* address of pointer used in existing scan context */ { struct scan_s *newp; assert(scan__top && scan__top->ptr == oldp); /* allocate a new scan context */ if (recycle) { newp = recycle; recycle = recycle->next; memset((char *)newp, 0, sizeof(*newp)); } else { newp = (struct scan_s *)safealloc(1, sizeof *newp); } newp->next = scan__top; scan__top = newp; /* initialize it to match the old scan context */ newp->buffer = newp->next->buffer; newp->bufinfo = newp->next->bufinfo; newp->blkno = newp->next->blkno; newp->leftedge = newp->next->leftedge; newp->rightedge = newp->next->rightedge; newp->ptr = cp; newp->leoffset = newp->next->leoffset; /* initialize *cp to point to the correct character */ *cp = *oldp; /* lock the block that *cp points to. */ if (newp->blkno) { seslock(newp->blkno, False, SES_CHARS); } return *cp; } /* This function frees a scan context which was created by scanalloc() */ void scanfree(cp) CHAR **cp; /* address of pointer used for scanning */ { struct scan_s *context; assert(cp != NULL); assert(scan__top != NULL); assert(scan__top->ptr == cp); /* delete it from the list */ context = scan__top; scan__top = scan__top->next; /* free its resources */ if (context->blkno) { sesunlock(context->blkno, False); } #ifndef DEBUG_ALLOC context->next = recycle; recycle = context; #else safefree(context); #endif } /* This function uses an existing scan context created by scanalloc() or * scanstring(), to start a new scan at a given mark. If the original scan * context was created by scanalloc(), the mark can even refer to a totally * different buffer than the original scan. If the original scan context * was created by scanstring(), then the mark can only be moved within the * original string buffer. */ CHAR *scanseek(cp, restart) CHAR **cp; /* address of pointer used for scanning */ MARK restart;/* where scanning should resume */ { COUNT left, right; /* number of chars in block */ BLKNO nextblkno; assert(cp != NULL && restart != NULL); assert(scan__top->ptr == cp); /* Can't mix string scans with buffer seeks, or vice versa. Testing * this is a bit tricky because scanalloc() calls scanseek() to fill in * a bunch of fields, we have to allow that; hence the leftedge test. */ assert(!(markbuffer(restart) && !scan__top->buffer && scan__top->leftedge)); assert(!(!markbuffer(restart) && scan__top->buffer)); /* string scan or buffer scan? */ if (!markbuffer(restart)) { /* STRING */ /* compute the new value of the pointer */ *cp = &scan__top->leftedge[markoffset(restart)]; /* if seeking to a non-existent offset, return NULL */ if (*cp < scan__top->leftedge || *cp >= scan__top->rightedge) { *cp = NULL; return NULL; } } else { /* BUFFER */ /* if seeking to a non-existent offset, return NULL */ if (markoffset(restart) < 0 || markoffset(restart) >= o_bufchars(markbuffer(restart))) { *cp = NULL; return NULL; } /* find the bufinfo block for the buffer that MARK refers to */ scan__top->buffer = markbuffer(restart); scan__top->bufinfo = bufbufinfo(scan__top->buffer); /* find the chars block. If this scan context happens to be * nested inside another one and we're seeking into the same * block, then we can optimize this a lot. */ if (scan__top->next && scan__top->next->buffer == scan__top->buffer && scan__top->next->leoffset <= markoffset(restart) && scan__top->next->leoffset + (int)(scan__top->next->rightedge - scan__top->next->leftedge) > markoffset(restart)) { /* hooray! We can avoid calling lowoffset() */ nextblkno = scan__top->next->blkno; left = (int)(markoffset(restart) - scan__top->next->leoffset); right = (int)(scan__top->next->rightedge - scan__top->next->leftedge) - left; } else { /* can't optimize; must call lowoffset to find block */ nextblkno = lowoffset(scan__top->bufinfo, markoffset(restart), &left, &right, NULL, NULL); } assert(right > 0 && nextblkno > 0); /* unlock the old block, and lock the new one */ if (scan__top->blkno != nextblkno) { if (scan__top->blkno) { sesunlock(scan__top->blkno, False); } seslock(nextblkno, False, SES_CHARS); scan__top->blkno = nextblkno; } /* set the other variables */ scan__top->leftedge = sesblk(scan__top->blkno)->chars.chars; scan__top->rightedge = scan__top->leftedge + left + right; scan__top->leoffset = markoffset(restart) - left; *cp = scan__top->leftedge + left; } return *cp; } #ifdef DEBUG_SCAN /* This function is a helper function for the scanleft() macro. This function * should not be called directly. * * It returns the number of buffered characters to the left of the current * scan point. */ COUNT scan__left(cp) CHAR **cp; /* address of pointer used for scanning */ { assert(cp != NULL); assert(scan__top->ptr == cp); /* return the number of buffered CHARs */ return (COUNT)(*cp - scan__top->leftedge); } /* This function is a helper function for the scanright() macro. This function * should not be called directly. * * It returns the number of buffered characters to the right of the current * scan point. */ COUNT scan__right(cp) CHAR **cp; /* address of pointer used for scanning */ { assert(cp != NULL); assert(scan__top->ptr == cp); /* return the number of buffered CHARs */ return (COUNT)(scan__top->rightedge - *cp); } /* create a mark which refers to the current point of the scan */ MARK scanmark(cp) CHAR **cp; /* address of pointer used for scanning */ { assert(cp != NULL); assert(scan__top->ptr == cp); /* return a temporary mark at the proper position */ return marktmp(scan__markbuf, scan__top->buffer, scan__top->leoffset + (int)(*cp - scan__top->leftedge)); } #endif /* DEBUG_SCAN */ /* This function is a helper function for the scannext() macro. This function * should not be called directly. */ CHAR *scan__next(cp) CHAR **cp; /* address of pointer used for scanning */ { MARKBUF markbuf; assert(cp != NULL && *cp != NULL); assert(scan__top->ptr == cp); /* if the next character is in the same block, its easy */ if (++*cp < scan__top->rightedge) { return *cp; } assert(*cp == scan__top->rightedge); /* else seek to the first character of following block */ return scanseek(cp, marktmp(markbuf, scan__top->buffer, scan__top->leoffset + (int)(scan__top->rightedge - scan__top->leftedge))); } /* This function is a helper function for the scanprev() macro. This function * should not be called directly. */ CHAR *scan__prev(cp) CHAR **cp; /* address of pointer used for scanning */ { MARKBUF markbuf; assert(cp != NULL); assert(scan__top->ptr == cp); /* if the previous character is in the same block, its easy */ if (--*cp >= scan__top->leftedge) { return *cp; } /* if this was the first block, we're at the end */ if (scan__top->leoffset == 0) { if (scan__top->blkno != 0) { sesunlock(scan__top->blkno, False); scan__top->blkno = 0; } *cp = NULL; return NULL; } /* else seek to the last character of preceding block */ return scanseek(cp, marktmp(markbuf, scan__top->buffer, scan__top->leoffset - 1)); } /* This function returns the single character at a given location. It is * similar to scanseek(), except that this function doesn't use a scan context, * and it returns a CHAR instead of a pointer-to-CHAR. */ CHAR scanchar(mark) MARK mark; /* buffer/offset of the character to fetch */ { COUNT left, right; /* number of chars in block */ BLKNO bufinfo; /* block describing the buffer */ BLKNO blkno; /* block containing the character */ CHAR ch; /* the character */ /* if seeking to a non-existent offset, return '\0' */ if (markoffset(mark) < 0 || markoffset(mark) >= o_bufchars(markbuffer(mark))) { return '\0'; } /* find the bufinfo block for the buffer that MARK refers to */ bufinfo = bufbufinfo(markbuffer(mark)); /* find the chars block */ blkno = lowoffset(bufinfo, markoffset(mark), &left, &right, NULL, NULL); assert(right != 0); /* lock the block */ seslock(blkno, False, SES_CHARS); /* fetch the character */ ch = sesblk(blkno)->chars.chars[left]; /* unlock the block */ sesunlock(blkno, False); return ch; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.