A terminal-based Inverted Index Search Engine written in C, using a custom hash table with chained linked lists to index words across multiple
.txtfiles.
- Overview
- Architecture
- Data Structures
- Project Structure
- Features
- How to Build & Run
- Menu Options
- Known Limitations
- Changelog & Bug Fixes
This project implements an Inverted Index β a classic data structure used in search engines β entirely in C. Given one or more .txt files, the program indexes every word, recording:
- Which files each word appears in
- How many times it appears in each file
The user can then query any word (or prefix) and instantly see all matches found across all indexed files, along with occurrence counts.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Hash Table (size 27) β
β [a] β [b] β ... β [z] β [#] (index 26 = non-alphabetic words) β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββΌββββββ
β mNode β β Main Node (one per unique word)
β *word β
βfilecount β
ββββ¬βββββ¬βββ
β βββββββββββββββββββββββββββββββ
mLinkβΌ sLinkβΌ
ββββββββββββ ββββββββββββ
β mNode β β sNode β β Sub Node
β "word2" β β*file_nameβ
ββββββββββββ β count β
ββββββ¬ββββββ
subLinkβΌ
ββββββββββββ
β sNode β
β*file_nameβ
ββββββββββββ
The hash table has 27 buckets β one per letter of the alphabet (indexed by first character, case-insensitive), and one catch-all bucket (index 26) for tokens beginning with non-alphabetic characters.
A singly-linked list tracking all files that have been loaded into the system. The filename is heap-allocated via strdup.
typedef struct Node {
char *file_name; /* heap-allocated via strdup */
struct Node *link;
} Flist;One node per unique word. The word string is heap-allocated via strdup, removing the old 19-character fixed-buffer limit.
typedef struct mainNode {
u_int filecount;
char *word; /* heap-allocated via strdup */
sNode *sLink;
struct mainNode *mLink;
} mNode;One node per file a word appears in. The filename is heap-allocated via strdup.
typedef struct subNode {
u_int wordcount;
char *file_name; /* heap-allocated via strdup */
struct subNode *subLink;
} sNode;Array of 27 of these, each holding an index and a pointer to the head of its mNode chain.
typedef struct hashT {
u_int index;
mNode *link;
} hash_T;inverted_search/
βββ main.c # Entry point, menu loop, user interaction
βββ main.h # All structs, enums, and function declarations
βββ color.h # ANSI color/style macros for terminal output
βββ create_database.c # Core indexing logic β reads files, builds the hash table
βββ search_database.c # Prefix-aware word lookup across the hash table
βββ display_database.c # Pretty-prints the entire database as a colored table
βββ save_database.c # Saves the database to database.txt
βββ update_database.c # Adds new files to an existing database (incremental)
βββ validation.c # File validation (extension, existence, empty, duplicate)
βββ flist_utils.c # Flist insert, print, free utilities
βββ hash_t_utils.c # Hash table init and free utilities
βββ files_utils.c # String utilities β strip_punctuation
βββ makefile # Build system (includes automated test target)
| Feature | Description |
|---|---|
| Multi-file indexing | Pass any number of .txt files as arguments |
| Prefix search | Searching "the" matches "the", "there", "they", etc. |
| Case-insensitive search | Hello and hello are treated as the same word |
| Punctuation stripping | "hello," and "hello" index as the same token |
| Smart apostrophe handling | it's is preserved; 'hello' strips the surrounding quotes |
| Duplicate file detection | The same file cannot be indexed twice |
| Incremental update | Add new files without re-indexing existing ones |
| Colorized terminal output | Full ANSI color support via color.h |
| Save to file | Export the full index to database.txt |
| Input validation | Non-numeric menu input is caught and handled gracefully |
| Automated testing | make test runs a full end-to-end flow automatically |
| Non-alphabetic word support | Tokens starting with digits or symbols go into bucket 26 |
- GCC (any modern version)
- A POSIX-compatible terminal (Linux/macOS recommended)
make./inverted_search.exe file1.txt file2.txt file3.txtGenerates test .txt files, runs the full menu flow (create β display β update β search β exit) automatically, and prints results to the terminal:
make testRemoves the binary, object files, all test .txt files, and database.txt:
make clean1. Create Database β Index all loaded files into the hash table
2. Display Database β Print the full index as a formatted, colored table
3. Search Database β Prefix-aware lookup (e.g. "the" matches "there", "they")
4. Update Database β Add new .txt files to the existing index
5. Save Database β Write the index to database.txt
6. Exit β Save, free all memory, and quit cleanly
- No punctuation stripping inside words containing digits β
C3POindexes asCPOsince non-alpha characters are dropped entirely. - The hash table uses only 27 buckets (keyed on first character). Words sharing a first letter share a chain, so heavily skewed text will increase chain traversal time.
- Prefix search scans the full bucket chain β it cannot use early-exit on sorted chains the way exact search can, since matching words may appear anywhere in the chain.
See CHANGE_LOG.md for a full record of every bug identified, its root cause, and how it was resolved.
See PROJECT_METRICS.md for a quantified breakdown of the project.
Built entirely in C. Terminal colors powered by color.h using ANSI escape codes.