#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* constants for num & max terms, lines and chars */
enum { NTRM = 2, MAXT = 29, MAXL = 128, MAXC = 512 };

typedef struct {        /* search term struct */
    char term[MAXT];    /* search term */
    char occ[MAXL];     /* occurence per-line */
    size_t tlen;        /* search term length */
    size_t freq;        /* total occurences   */
} sterm;

char *str2lower (char *s);
FILE *xfopen (const char *fn, const char *mode);

int main (int argc, char **argv) {

    if (argc < NTRM + 1 ) {  /* validate NTRM search terms given */
	fprintf (stderr, "error: insufficient input.\n"
                         "usage: %s term term [file (stdin)]\n",
                         argv[0]);
	return 1;
    }

    sterm word[NTRM] = {{ .term = "" }};      /* initialize vars */
    size_t i, j, n = 0;
    char *delim = " ,.;\t\n";
    char buf[MAXC] = "";
    FILE *fp = argc > 3 ? xfopen (argv[3], "r") : stdin;

    for (i = 0; i < NTRM; i++) {
        strncpy (word[i].term, argv[i+1], MAXT);   /* copy terms */
        word[i].tlen = strlen (word[i].term);      /* get length */
        str2lower (word[i].term);            /* convert to lower */
    }

    while (n < MAXL && fgets (buf, MAXC, fp)) { /* for each line */
        int nl = strchr (buf, '\n') ? 1 : 0; /* check short-read */
        char *p = buf;   /* tokenize string and compare to terms */
        for (p = strtok (p, delim); p; p = strtok (NULL, delim))
            for (i = 0; i < NTRM; i++)   /* for each search term */
                if (!strncmp (word[i].term, str2lower(p), word[i].tlen))
                word[i].occ[n]++, word[i].freq++; /* update freq */
        if (nl)
            n++;  /* increment line count */
        else
            fprintf (stderr, "warning: short-read line: %zu\n", n);
    }
    if (fp != stdin) fclose (fp);     /* close file if not stdin */

    for (i = 0; i < NTRM; i++) {      /* print results */
        printf ("\n word '%s' appears %zu times\n",
                word[i].term, word[i].freq);
        for (j = 0; j < n; j++)
            if (word[i].occ[j])
                printf ("   %2d times in line %zu\n",
                        word[i].occ[j], j + 1);
    }
    putchar ('\n');

    return 0;
}

/** convert string to lowercase.
 *  returns string with all chars converted to lowercase.
 *  bit-6 is the case bit in 7-bit ASCII (1 = lowercase)
 */
char *str2lower (char *s)
{
    if (!s)  return NULL;
    if (!*s) return s;
    char *p = s;

    for (; *p; p++)
        if ('A' <= *p && *p <= 'Z')  /* if uppercase */
            *p |= (1 << 5); /* set case bit to '1' (lowercase) */

    return s;
}

/** fopen with error checking */
FILE *xfopen (const char *fn, const char *mode)
{
    if (!fn || !mode) exit (EXIT_FAILURE);
    FILE *fp = fopen (fn, mode);

    if (!fp) {
        fprintf (stderr, "xfopen() error: file open failed '%s'.\n", fn);
        exit (EXIT_FAILURE);
    }

    return fp;
}