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

116 lines
2.6 KiB
C

#include <stdio.h>
#include <string.h>
#include <assert.h>
int flush_lnbuff(void);
int getch_lnbuff(void);
void ignoreln_lnbuff(void);
/* 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)
{
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++;
if (lineno_flag)
printf("%ld:", lineno);
flush_lnbuff();
}
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()
{
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;
}
/* flush_lnbuff: print contents of line buffer, return length */
int flush_lnbuff()
{
int i;
/* handle overflowing chars */
if (lnbuff.len > LNBUFF_LEN) {
for (i = LNBUFF_LEN; i < lnbuff.len; i++)
putchar('.');
for (i = LNBUFF_I; i < LNBUFF_LEN; i++)
putchar(lnbuff.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);
putchar('\n');
lnbuff.newline = 1;
return lnbuff.len;
}
/* ignoreln_lnbuff: ignore line of input */
void ignoreln_lnbuff()
{
int c;
if (lnbuff.newline)
return;
while ((c = getchar()) != EOF && c != '\n')
;
lnbuff.newline = 1;
}