exc/k&r/04-funcs-and-prog-struct/04-find/find-no-overflow.c

133 lines
4.1 KiB
C

#include <stdio.h>
#include <string.h>
#include <assert.h>
#define BUFF_BITS 10
#define BUFF_SIZE (1 << BUFF_BITS)
/* buffered_ln: structure for buffered input line */
struct buffered_ln {
unsigned long lineno; /* line number */
unsigned int len; /* line length */
char self[BUFF_SIZE]; /* line buffer */
int last_char; /* last read char of line
when equals to EOF or \n the line
has been read to completion */
};
void init_bln(struct buffered_ln *);
int get_bln(struct buffered_ln *);
void flush_bln(struct buffered_ln *);
int sub_of_bln(const char *pattern, struct buffered_ln *, unsigned plen);
/* find: finds occurences of pattern in input lines */
int find(char *pattern, char lineno_flag, char except_flag)
{
size_t plen = strlen(pattern);
if (plen-1 > BUFF_SIZE) {
printf("error: length of pattern > max line\n");
return -1;
}
struct buffered_ln bln;
init_bln(&bln);
while (get_bln(&bln))
if (sub_of_bln(pattern, &bln, plen) == !except_flag) {
if (lineno_flag)
printf("%ld:", bln.lineno);
flush_bln(&bln);
}
}
/* init_bln: initalizes buffered line struct */
void init_bln(struct buffered_ln *bln)
{
bln->lineno = 0;
bln->len = 0;
bln->last_char = '\n';
}
#define OVERFLOW_MASK (~0 << BUFF_BITS)
#define INDEX_MASK (~OVERFLOW_MASK)
#define bln_i(i) ((i) & INDEX_MASK) /* casts unsigned integer i to a valid bln index */
#define overflowed_bln(bln) (bln->len & OVERFLOW_MASK) /* 1 if bln is overflowed */
/* eol_bln: 1 if bln si finalized (last_char is \n or EOF) */
#define eol_bln(bln) (bln->last_char == EOF || bln->last_char == '\n')
#define bln_next_i(bln) bln_i(bln->len) /* next writable index of bln */
#define bln_start_i(bln) (overflowed_bln(bln) ? bln_next_i(bln) : 0) /* starting index of bln */
/* getchar_bln: gets char from input and writes it to bln, returns it */
#define getchar_bln(bln) \
(bln->self[bln_next_i(bln)] = bln->last_char, ++bln->len, bln->last_char = getchar())
/* get_bln: skips current line in buffer line bln, sets bln to next line of input
* returns 0 on EOF and a postivie value otherwise */
int get_bln(struct buffered_ln *bln)
{
/* finalize previous line */
while (!eol_bln(bln))
bln->last_char = getchar();
if (bln->last_char == EOF)
return 0;
/* init next line */
bln->lineno++;
bln->len = 0;
return (bln->last_char = getchar()) != EOF;
}
/* flush_bln: flushes buffered line bln to stdout */
void flush_bln(struct buffered_ln *bln)
{
unsigned i;
/* print overflowed chars */
if (overflowed_bln(bln)) {
for (i = BUFF_SIZE; i < bln->len; i++)
putchar('.');
for (i = bln_next_i(bln); i < BUFF_SIZE; i++)
putchar(bln->self[i]);
}
/* print buffered chars */
for (i = 0; i < bln_next_i(bln); i++)
putchar(bln->self[i]);
/* print not yet read chars */
for (; !eol_bln(bln); bln->last_char = getchar())
putchar(bln->last_char);
putchar('\n');
}
/* sub_of_bln: returns 0 if pattern with length plen is not a substring of
* buffered line bln and a positive value otherwise */
int sub_of_bln(const char *pattern, struct buffered_ln *bln, const unsigned plen)
{
/* buffered line must not be overflowed */
assert(!overflowed_bln(bln));
/* fill buffer with plen-1 chars */
for (; bln->len < plen-1; getchar_bln(bln))
if (eol_bln(bln))
return 0;
unsigned i, j;
int k;
/* handle buffered chars */
for (i = plen-1; i < bln->len; i++) {
for (j = i, k = plen-1; k >= 0 && bln->self[j] == pattern[k]; k--, j--)
;
if (k < 0)
return 1;
}
/* handle no yet read chars */
for (; !eol_bln(bln); getchar_bln(bln)) {
if (bln->last_char != pattern[plen-1])
continue;
for (j = bln_i(bln_next_i(bln)-1), k = plen-2; k >=0 && bln->self[j] == pattern[k]; k--, j = bln_i(j-1))
;
if (k < 0)
return 1;
}
return 0;
}