/* bagmatch.c * * Print all words from a dictionary file whose letters (single-byte ASCII) * form the same multiset ("bag") as the first command-line argument. * * Usage: ./bagmatch KEYWORD DICTIONARY_FILE * * Constraints & behavior per request: * - Words and KEYWORD are assumed to contain only letters; no validity checks. * - Maximum input word length is 64 bytes; program exits with error if exceeded. * - Sort letters in a word using an insertion-sort subroutine into canonical order. * - Compare canonical forms via memcmp. * - Use stdio for I/O; be fast and avoid dynamic allocation. */ #include #include #include #define MAXWORD 64 /* insertion_sort_chars: * Sorts n bytes in-place (array of unsigned char) using insertion sort. * This is optimized for small fixed-size keys (<=64). */ static void insertion_sort_chars(unsigned char *a, size_t n) { for (size_t i = 1; i < n; ++i) { unsigned char key = a[i]; size_t j = i; while (j > 0 && a[j - 1] > key) { a[j] = a[j - 1]; --j; } a[j] = key; } } int main(int argc, char *argv[]) { if (argc != 3) { fputs("Usage: bagmatch KEYWORD DICTIONARY_FILE\n", stderr); return 2; } unsigned char keybuf[MAXWORD]; size_t keylen = strlen(argv[1]); if (keylen > MAXWORD) { fputs("Error: keyword too long (max 64 bytes)\n", stderr); return 2; } /* copy then sort into canonical form */ memcpy(keybuf, argv[1], keylen); insertion_sort_chars(keybuf, keylen); FILE *f = fopen(argv[2], "rb"); if (!f) { perror("fopen"); return 2; } /* Read file line-by-line, each word followed by '\n' including last. We'll read into a buffer and detect newline. Use fread for speed. */ unsigned char inbuf[MAXWORD + 2]; /* allow room to detect overflow and newline */ size_t bufpos = 0; int c; while ((c = fgetc(f)) != EOF) { if (c == '\n') { /* word is in inbuf[0..bufpos-1] */ if (bufpos > MAXWORD) { fputs("Error: input word too long (max 64 bytes)\n", stderr); fclose(f); return 2; } /* Test words of the right length further. */ if (bufpos == keylen) { /* sort a copy so we don't disturb original for printing */ unsigned char work[MAXWORD]; memcpy(work, inbuf, bufpos); insertion_sort_chars(work, bufpos); if (memcmp(work, keybuf, keylen) == 0) { /* match: print original word (as bytes) followed by newline */ if (fwrite(inbuf, 1, bufpos, stdout) != bufpos || putchar('\n') == EOF) { perror("write"); fclose(f); return 2; } } } bufpos = 0; } else { /* append byte */ if (bufpos < sizeof(inbuf)) { inbuf[bufpos++] = (unsigned char)c; } else { /* we purposely allow detecting overflow; keep reading until newline but will error when newline encountered (or we can exit now) */ /* consume until newline to keep file position consistent, but per spec we should exit on word > 64 bytes rather than trying to handle dynamically. So we can report error now. */ fputs("Error: input word too long (max 64 bytes)\n", stderr); fclose(f); return 2; } } } /* If file doesn't end with newline, per spec that shouldn't happen. But we won't process a final partial line (spec says each word is followed by '\n'). */ fclose(f); return 0; }