complete rewrite. the previous implementetion was incorrect, not all occurences
of pattern were found (eg. xy in xxy).master
parent
09fb685153
commit
d40f20afb7
|
@ -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)
|
||||
{
|
||||
size_t plen = strlen(pattern);
|
||||
if (plen-1 > BUFF_SIZE) {
|
||||
printf("error: length of pattern > max line\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern long lineno;
|
||||
int c, found = 0;
|
||||
const char * const pstart = pattern;
|
||||
struct buffered_ln bln;
|
||||
init_bln(&bln);
|
||||
|
||||
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++;
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue