complete rewrite. the previous implementetion was incorrect, not all occurences

of pattern were found (eg. xy in xxy).
master
Tibor Bizjak 2023-07-25 16:55:19 +02:00
parent 09fb685153
commit d40f20afb7
1 changed files with 111 additions and 94 deletions

View File

@ -2,114 +2,131 @@
#include <string.h>
#include <assert.h>
int flush_lnbuff(void);
int getch_lnbuff(void);
void ignoreln_lnbuff(void);
#define BUFF_BITS 10
#define BUFF_SIZE (1 << BUFF_BITS)
/* find: prints lines containing pattern, returns number of matching lines
* gracefully handle line overflow
* compile with -O3 */
int find(const char *pattern, char lineno_flag, char except_flag)
/* 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)
{
extern long lineno;
int c, found = 0;
const char * const pstart = pattern;
while ((c = getch_lnbuff()) != EOF) {
if (c == *pattern) {
pattern++;
continue;
}
char match = !*pattern - (c == '\n');
match *= except_flag ? -1 : 1;
if (match < 0 && except_flag) // no match
ignoreln_lnbuff();
else if (match > 0) { // match
found++;
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:", lineno);
flush_lnbuff();
printf("%ld:", bln.lineno);
flush_bln(&bln);
}
pattern = pstart;
}
return found;
}
/* ------------------------ line buffer ---------------------- */
#define LNBUFF_LEN 1024
/* the line buffer lenght must be a power of two */
static_assert(!(LNBUFF_LEN % 2), "Line buffer length is not a power of two!");
long lineno;
/* line_buffer: global overflowing line buffer */
struct {
char self[LNBUFF_LEN];
unsigned len;
char newline;
} lnbuff = {.len=0, .newline=1};
/* LNBUFF_I: current index in line buffer */
#define LNBUFF_I (lnbuff.len & (LNBUFF_LEN - 1))
/* getch_lnbuff: getchar and add it to buffer */
int getch_lnbuff()
/* init_bln: initalizes buffered line struct */
void init_bln(struct buffered_ln *bln)
{
int c = getchar();
if (lnbuff.newline) {
lnbuff.len = 0;
lnbuff.newline = 0;
lineno++;
}
if (c == EOF || c == '\n') // new line
lnbuff.newline = 1;
else {
lnbuff.self[LNBUFF_I] = c;
lnbuff.len++;
}
return c;
bln->lineno = 0;
bln->len = 0;
bln->last_char = '\n';
}
/* flush_lnbuff: print contents of line buffer, return length */
int flush_lnbuff()
{
int i;
#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 */
/* handle overflowing chars */
if (lnbuff.len > LNBUFF_LEN) {
for (i = LNBUFF_LEN; i < lnbuff.len; i++)
#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 = LNBUFF_I; i < LNBUFF_LEN; i++)
putchar(lnbuff.self[i]);
for (i = bln_next_i(bln); i < BUFF_SIZE; i++)
putchar(bln->self[i]);
}
/* handle rest of chars */
for (i = 0; i < LNBUFF_I; i++)
putchar(lnbuff.self[i]);
/* print unbuffered part of line */
if (!lnbuff.newline)
while ((i = getchar()) != EOF && i != '\n')
putchar(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');
lnbuff.newline = 1;
return lnbuff.len;
}
/* ignoreln_lnbuff: ignore line of input */
void ignoreln_lnbuff()
/* 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)
{
int c;
if (lnbuff.newline)
return;
while ((c = getchar()) != EOF && c != '\n')
;
lnbuff.newline = 1;
/* 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;
}