mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 18:58:40 +00:00
Compare commits
3 commits
44ede48cb1
...
0f28d73522
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f28d73522 | ||
|
|
aeec34749d | ||
|
|
e21dd756bf |
3 changed files with 250 additions and 202 deletions
7
.github/pull_request_template.md
vendored
7
.github/pull_request_template.md
vendored
|
|
@ -9,13 +9,6 @@ Describe the changes made in this PR.
|
||||||
- [ ] Code has been tested
|
- [ ] Code has been tested
|
||||||
- [ ] Existing tests pass
|
- [ ] Existing tests pass
|
||||||
|
|
||||||
### Platform / Environment
|
|
||||||
What platform and environment were used for development and testing?
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
- Windows 11 / macOS / Linux
|
|
||||||
- MSYS2 / WSL2 / Debian
|
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
<!-- Add anything relevant about testing -->
|
<!-- Add anything relevant about testing -->
|
||||||
|
|
||||||
|
|
|
||||||
169
README.md
169
README.md
|
|
@ -67,6 +67,175 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="branding/bOS_full_gradient_cropped.png" alt="BoredOS Logo" width="450" />
|
||||||
|
|
||||||
|
<h3>A modern x86_64 hobbyist operating system built from the ground up.</h3>
|
||||||
|
|
||||||
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
[Docs](docs/README.md) · [Build & Run](docs/build/usage.md) · [AppDev SDK](docs/appdev/sdk_reference.md) · [Discord](https://discord.gg/J2BxWaFAgY) · [Support](https://buymeacoffee.com/boreddevhq)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> The screenshot above may represent a previous build and is subject to change as the UI evolves.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Kernel and Architecture
|
||||||
|
- **Long Mode Architecture** — Native x86_64 implementation utilizing 64-bit address space and registers
|
||||||
|
- **Symmetric Multi-Processing** — Scalable multi-core support with IPI-based scheduling and synchronization
|
||||||
|
- **Advanced Memory Management** — Custom slab allocator with object pooling and efficient physical/virtual page mapping
|
||||||
|
- **Hybrid VFS Layer** — Unified filesystem interface supporting FAT32, TAR, ProcFS, and SysFS
|
||||||
|
- **Preemptive Multitasking** — Prioritized process scheduling with full context isolation
|
||||||
|
- **Hardware Abstraction** — Comprehensive driver support for PCI, AHCI, PS/2, and ACPI
|
||||||
|
|
||||||
|
### Graphical Desktop Environment
|
||||||
|
- **BoredWM** — High-performance window manager featuring window stacking, focus management, and drag-and-drop interactions
|
||||||
|
- **Typography Engine** — Integrated font manager with TrueType (TTF) support and efficient glyph caching
|
||||||
|
- **Rich Media Subsystem** — Native hardware-independent decoding for PNG, JPEG, GIF, BMP, and TGA formats
|
||||||
|
- **LibWidget Toolkit** — Native UI component library for rapid application development
|
||||||
|
|
||||||
|
### Networking Stack
|
||||||
|
- **TCP/IP Integration** — Full lwIP-based network stack featuring DHCP, DNS, and Berkeley-style sockets
|
||||||
|
- **Network Services** — Integrated support for basic web browsing and real-time network telemetry
|
||||||
|
|
||||||
|
### Application Ecosystem
|
||||||
|
| Category | Applications |
|
||||||
|
|----------|--------------|
|
||||||
|
| Productivity | Text Editor, Markdown Viewer, BoredWord Processor, Web Browser, Calculator |
|
||||||
|
| Development | TCC (Tiny C Compiler), Lua|
|
||||||
|
| System | Explorer (File Manager), Task Manager, System Monitor, Graphing Utility |
|
||||||
|
| Games | doomgeneric, Minesweeper, 2048, Snake |
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
| Guide | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| [Documentation Index](docs/README.md) | Start here! |
|
||||||
|
| [Architecture Overview](docs/architecture/README.md) | Deep dive into the kernel |
|
||||||
|
| [Building and Running](docs/build/usage.md) | Set up your build environment |
|
||||||
|
| [AppDev SDK](docs/appdev/custom_apps.md) | Build your own apps for BoredOS |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/boreddevnl">
|
||||||
|
<img src="https://github.com/boreddevnl.png?size=80" width="60" /><br />
|
||||||
|
<sub><b>BoredDevNL</b></sub>
|
||||||
|
</a><br />
|
||||||
|
Creator & Lead Maintainer
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Lluciocc">
|
||||||
|
<img src="https://github.com/Lluciocc.png?size=80" width="60" /><br />
|
||||||
|
<sub><b>Lluciocc</b></sub>
|
||||||
|
</a><br />
|
||||||
|
Maintainer
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Mellurboo">
|
||||||
|
<img src="https://github.com/Mellurboo.png?size=80" width="60" /><br />
|
||||||
|
<sub><b>Mellurboo</b></sub>
|
||||||
|
</a><br />
|
||||||
|
Contributor
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/Artemix1508">
|
||||||
|
<img src="https://github.com/Artemix1508.png?size=80" width="60" /><br />
|
||||||
|
<sub><b>Artemix1508</b></sub>
|
||||||
|
</a><br />
|
||||||
|
Artwork
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/zeyadhost">
|
||||||
|
<img src="https://github.com/zeyadhost.png?size=80" width="60" /><br />
|
||||||
|
<sub><b>Zeyadhost</b></sub>
|
||||||
|
</a><br />
|
||||||
|
Contributor
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/naplon74">
|
||||||
|
<img src="https://github.com/naplon74.png?size=80" width="60" /><br />
|
||||||
|
<sub><b>Naplon74</b></sub>
|
||||||
|
</a><br />
|
||||||
|
Artwork
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/pixelyblah">
|
||||||
|
<img src="https://github.com/pixelyblah.png?size=80" width="60" /><br />
|
||||||
|
<sub><b>pixelyblah</b></sub>
|
||||||
|
</a><br />
|
||||||
|
Artwork
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/qwroffc">
|
||||||
|
<img src="https://github.com/qwroffc.png?size=80" width="60" /><br />
|
||||||
|
<sub><b>qwroffc</b></sub>
|
||||||
|
</a><br />
|
||||||
|
Artwork
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ☕ Support the Journey
|
||||||
|
|
||||||
|
If you find BoredOS interesting or useful, consider fueling development with a coffee!
|
||||||
|
|
||||||
|
<a href="https://buymeacoffee.com/boreddevhq" target="_blank">
|
||||||
|
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="50" style="border-radius: 8px;" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## History
|
||||||
|
|
||||||
|
**BoredOS** is the successor to **[BrewKernel](https://github.com/boreddevnl/brewkernel)**, a project started in 2023. BrewKernel served as the foundational learning ground but has since been officially deprecated and archived — it no longer receives updates, bug fixes, or pull request reviews.
|
||||||
|
|
||||||
|
BoredOS is a complete architectural reboot, applying years of lessons learned to build a cleaner, more modular, and more capable system.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Please direct all issues, discussions, and contributions to this repository. Legacy BrewKernel code is preserved for historical purposes only and is not compatible with BoredOS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
**Copyright (C) 2023–2026 boreddevnl**
|
||||||
|
|
||||||
|
Distributed under the **GNU General Public License v3**. See [`LICENSE`](LICENSE) for details.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> You must retain all copyright headers and include the original attribution in any redistributions or derivative works. See the [`NOTICE`](NOTICE) file for more details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## ☕ Support the Journey
|
## ☕ Support the Journey
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2026 maro (whitehai11)
|
// Copyright (c) 2026 Maro (whitehai11)
|
||||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||||
// BOREDOS_APP_DESC: Search for text inside a file.
|
// BOREDOS_APP_DESC: Search for text inside a file.
|
||||||
|
|
@ -10,24 +10,6 @@
|
||||||
|
|
||||||
#define READ_BUF_SIZE 4096
|
#define READ_BUF_SIZE 4096
|
||||||
#define LINE_BUF_SIZE 1024
|
#define LINE_BUF_SIZE 1024
|
||||||
#define MAX_PATH 512
|
|
||||||
#define MAX_ENTRIES 256
|
|
||||||
|
|
||||||
// Flags
|
|
||||||
static int g_show_numbers = 0;
|
|
||||||
static int g_ignore_case = 0;
|
|
||||||
static int g_count_only = 0;
|
|
||||||
static int g_invert = 0; // -v
|
|
||||||
static int g_files_only = 0; // -l
|
|
||||||
static int g_word_match = 0; // -w
|
|
||||||
static int g_line_match = 0; // -x
|
|
||||||
static int g_recursive = 0; // -r / -R
|
|
||||||
static int g_multi_file = 0; // more than one file → prefix output with filename
|
|
||||||
|
|
||||||
static const char *g_pattern = NULL;
|
|
||||||
|
|
||||||
// Total match count across all files (used for -c with -r)
|
|
||||||
static int g_total_matches = 0;
|
|
||||||
|
|
||||||
static int sc_strcmp(const char *a, const char *b) {
|
static int sc_strcmp(const char *a, const char *b) {
|
||||||
while (*a && *a == *b) { a++; b++; }
|
while (*a && *a == *b) { a++; b++; }
|
||||||
|
|
@ -35,20 +17,15 @@ static int sc_strcmp(const char *a, const char *b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_usage(void) {
|
static void print_usage(void) {
|
||||||
printf("Usage: grep [options] <pattern> <file> [file...]\n");
|
printf("Usage: grep [options] <text> <file>\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Search for text inside files.\n");
|
printf("Search for text inside a file.\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -n Show line numbers\n");
|
printf(" -n Show line numbers\n");
|
||||||
printf(" -i Case-insensitive search\n");
|
printf(" -i Case-insensitive search\n");
|
||||||
printf(" -c Print match count only\n");
|
printf(" -c Print match count only\n");
|
||||||
printf(" -v Invert match (print non-matching lines)\n");
|
printf(" -h Show this help\n");
|
||||||
printf(" -l Print only filenames with matches\n");
|
|
||||||
printf(" -w Match whole words only\n");
|
|
||||||
printf(" -x Match whole lines only\n");
|
|
||||||
printf(" -r, -R Recursive search in directories\n");
|
|
||||||
printf(" -h Show this help\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char to_lower(char c) {
|
static char to_lower(char c) {
|
||||||
|
|
@ -56,26 +33,6 @@ static char to_lower(char c) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int is_word_char(char c) {
|
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
|
||||||
(c >= '0' && c <= '9') || c == '_';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if needle appears in haystack at position i as a whole word */
|
|
||||||
static int match_at(const char *haystack, int i, int h_len,
|
|
||||||
const char *needle, int n_len, int ignore_case) {
|
|
||||||
for (int j = 0; j < n_len; j++) {
|
|
||||||
char h = ignore_case ? to_lower(haystack[i + j]) : haystack[i + j];
|
|
||||||
char n = ignore_case ? to_lower(needle[j]) : needle[j];
|
|
||||||
if (h != n) return 0;
|
|
||||||
}
|
|
||||||
if (g_word_match) {
|
|
||||||
if (i > 0 && is_word_char(haystack[i - 1])) return 0;
|
|
||||||
if (i + n_len < h_len && is_word_char(haystack[i + n_len])) return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int str_contains(const char *haystack, const char *needle, int ignore_case) {
|
static int str_contains(const char *haystack, const char *needle, int ignore_case) {
|
||||||
int h_len = (int)strlen(haystack);
|
int h_len = (int)strlen(haystack);
|
||||||
int n_len = (int)strlen(needle);
|
int n_len = (int)strlen(needle);
|
||||||
|
|
@ -84,146 +41,31 @@ static int str_contains(const char *haystack, const char *needle, int ignore_cas
|
||||||
if (n_len > h_len) return 0;
|
if (n_len > h_len) return 0;
|
||||||
|
|
||||||
for (int i = 0; i <= h_len - n_len; i++) {
|
for (int i = 0; i <= h_len - n_len; i++) {
|
||||||
if (match_at(haystack, i, h_len, needle, n_len, ignore_case))
|
int match = 1;
|
||||||
return 1;
|
for (int j = 0; j < n_len; j++) {
|
||||||
|
char h = ignore_case ? to_lower(haystack[i + j]) : haystack[i + j];
|
||||||
|
char n = ignore_case ? to_lower(needle[j]) : needle[j];
|
||||||
|
if (h != n) { match = 0; break; }
|
||||||
|
}
|
||||||
|
if (match) return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int line_matches(const char *line) {
|
|
||||||
if (g_line_match) {
|
|
||||||
/* Whole line must equal pattern */
|
|
||||||
int h_len = (int)strlen(line);
|
|
||||||
int n_len = (int)strlen(g_pattern);
|
|
||||||
if (h_len != n_len) return 0;
|
|
||||||
return match_at(line, 0, h_len, g_pattern, n_len, g_ignore_case);
|
|
||||||
}
|
|
||||||
return str_contains(line, g_pattern, g_ignore_case);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Grep a single open file descriptor, printing results prefixed by filename if needed */
|
|
||||||
static int grep_fd(int fd, const char *filename) {
|
|
||||||
static char read_buf[READ_BUF_SIZE];
|
|
||||||
static char line[LINE_BUF_SIZE];
|
|
||||||
int line_pos = 0;
|
|
||||||
int line_num = 0;
|
|
||||||
int match_cnt = 0;
|
|
||||||
|
|
||||||
/* Helper: process one complete line */
|
|
||||||
#define PROCESS_LINE() do { \
|
|
||||||
line[line_pos] = '\0'; \
|
|
||||||
line_num++; \
|
|
||||||
int matched = line_matches(line); \
|
|
||||||
if (g_invert) matched = !matched; \
|
|
||||||
if (matched) { \
|
|
||||||
match_cnt++; \
|
|
||||||
if (!g_count_only && !g_files_only) { \
|
|
||||||
if (g_multi_file) printf("%s:", filename); \
|
|
||||||
if (g_show_numbers) printf("%d:", line_num); \
|
|
||||||
printf("%s\n", line); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
line_pos = 0; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int bytes = sys_read(fd, read_buf, READ_BUF_SIZE);
|
|
||||||
if (bytes <= 0) break;
|
|
||||||
|
|
||||||
for (int i = 0; i < bytes; i++) {
|
|
||||||
char c = read_buf[i];
|
|
||||||
if (c == '\n' || line_pos >= LINE_BUF_SIZE - 1) {
|
|
||||||
PROCESS_LINE();
|
|
||||||
} else if (c != '\r') {
|
|
||||||
line[line_pos++] = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (line_pos > 0) PROCESS_LINE();
|
|
||||||
|
|
||||||
#undef PROCESS_LINE
|
|
||||||
|
|
||||||
if (g_count_only)
|
|
||||||
printf("%s%d\n", g_multi_file ? filename : "", match_cnt > 0 ? match_cnt : 0);
|
|
||||||
|
|
||||||
if (g_files_only && match_cnt > 0)
|
|
||||||
printf("%s\n", filename);
|
|
||||||
|
|
||||||
g_total_matches += match_cnt;
|
|
||||||
return match_cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int grep_file(const char *path) {
|
|
||||||
int fd = sys_open(path, "r");
|
|
||||||
if (fd < 0) {
|
|
||||||
printf("grep: cannot open '%s'\n", path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int n = grep_fd(fd, path);
|
|
||||||
sys_close(fd);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void grep_recursive(const char *path) {
|
|
||||||
FAT32_FileInfo info;
|
|
||||||
if (sys_get_file_info(path, &info) < 0) {
|
|
||||||
printf("grep: cannot access '%s'\n", path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!info.is_directory) {
|
|
||||||
grep_file(path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FAT32_FileInfo entries[MAX_ENTRIES];
|
|
||||||
int count = sys_list(path, entries, MAX_ENTRIES);
|
|
||||||
if (count < 0) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
const char *name = entries[i].name;
|
|
||||||
if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
char full[MAX_PATH];
|
|
||||||
int plen = (int)strlen(path);
|
|
||||||
int nlen = (int)strlen(name);
|
|
||||||
if (plen + 1 + nlen + 1 > MAX_PATH) continue;
|
|
||||||
|
|
||||||
int slash = (plen == 1 && path[0] == '/') ? 0 : 1;
|
|
||||||
for (int j = 0; j < plen; j++) full[j] = path[j];
|
|
||||||
if (slash) full[plen] = '/';
|
|
||||||
for (int j = 0; j <= nlen; j++) full[plen + slash + j] = name[j];
|
|
||||||
|
|
||||||
if (entries[i].is_directory)
|
|
||||||
grep_recursive(full);
|
|
||||||
else
|
|
||||||
grep_file(full);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
int arg_offset = 1;
|
int show_numbers = 0;
|
||||||
|
int ignore_case = 0;
|
||||||
|
int count_only = 0;
|
||||||
|
int arg_offset = 1;
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (argv[i][0] != '-') break;
|
if (argv[i][0] != '-') break;
|
||||||
if (sc_strcmp(argv[i], "-n") == 0) {
|
if (sc_strcmp(argv[i], "-n") == 0) {
|
||||||
g_show_numbers = 1; arg_offset++;
|
show_numbers = 1; arg_offset++;
|
||||||
} else if (sc_strcmp(argv[i], "-i") == 0) {
|
} else if (sc_strcmp(argv[i], "-i") == 0) {
|
||||||
g_ignore_case = 1; arg_offset++;
|
ignore_case = 1; arg_offset++;
|
||||||
} else if (sc_strcmp(argv[i], "-c") == 0) {
|
} else if (sc_strcmp(argv[i], "-c") == 0) {
|
||||||
g_count_only = 1; arg_offset++;
|
count_only = 1; arg_offset++;
|
||||||
} else if (sc_strcmp(argv[i], "-v") == 0) {
|
|
||||||
g_invert = 1; arg_offset++;
|
|
||||||
} else if (sc_strcmp(argv[i], "-l") == 0) {
|
|
||||||
g_files_only = 1; arg_offset++;
|
|
||||||
} else if (sc_strcmp(argv[i], "-w") == 0) {
|
|
||||||
g_word_match = 1; arg_offset++;
|
|
||||||
} else if (sc_strcmp(argv[i], "-x") == 0) {
|
|
||||||
g_line_match = 1; arg_offset++;
|
|
||||||
} else if (sc_strcmp(argv[i], "-r") == 0 ||
|
|
||||||
sc_strcmp(argv[i], "-R") == 0) {
|
|
||||||
g_recursive = 1; arg_offset++;
|
|
||||||
} else if (sc_strcmp(argv[i], "-h") == 0 ||
|
} else if (sc_strcmp(argv[i], "-h") == 0 ||
|
||||||
sc_strcmp(argv[i], "--help") == 0) {
|
sc_strcmp(argv[i], "--help") == 0) {
|
||||||
print_usage();
|
print_usage();
|
||||||
|
|
@ -234,29 +76,73 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc - arg_offset < 1) {
|
|
||||||
print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_pattern = argv[arg_offset];
|
|
||||||
|
|
||||||
// Need at least a path when not reading stdin
|
|
||||||
if (argc - arg_offset < 2) {
|
if (argc - arg_offset < 2) {
|
||||||
print_usage();
|
print_usage();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple files → prefix output with filename
|
const char *pattern = argv[arg_offset];
|
||||||
g_multi_file = (argc - arg_offset > 2) || g_recursive;
|
const char *path = argv[arg_offset + 1];
|
||||||
|
|
||||||
for (int i = arg_offset + 1; i < argc; i++) {
|
int fd = sys_open(path, "r");
|
||||||
if (g_recursive) {
|
if (fd < 0) {
|
||||||
grep_recursive(argv[i]);
|
printf("grep: cannot open '%s'\n", path);
|
||||||
} else {
|
return 1;
|
||||||
grep_file(argv[i]);
|
}
|
||||||
|
|
||||||
|
static char read_buf[READ_BUF_SIZE];
|
||||||
|
static char line[LINE_BUF_SIZE];
|
||||||
|
int line_pos = 0;
|
||||||
|
int line_num = 0;
|
||||||
|
int match_cnt = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int bytes = sys_read(fd, read_buf, READ_BUF_SIZE);
|
||||||
|
if (bytes <= 0) break;
|
||||||
|
|
||||||
|
for (int i = 0; i < bytes; i++) {
|
||||||
|
char c = read_buf[i];
|
||||||
|
|
||||||
|
if (c == '\n' || line_pos >= LINE_BUF_SIZE - 1) {
|
||||||
|
line[line_pos] = '\0';
|
||||||
|
line_num++;
|
||||||
|
|
||||||
|
if (str_contains(line, pattern, ignore_case)) {
|
||||||
|
match_cnt++;
|
||||||
|
if (!count_only) {
|
||||||
|
if (show_numbers)
|
||||||
|
printf("%d: %s\n", line_num, line);
|
||||||
|
else
|
||||||
|
printf("%s\n", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line_pos = 0;
|
||||||
|
} else if (c != '\r') {
|
||||||
|
line[line_pos++] = c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return g_total_matches > 0 ? 0 : 1;
|
// Handle last line if file doesn't end with newline
|
||||||
|
if (line_pos > 0) {
|
||||||
|
line[line_pos] = '\0';
|
||||||
|
line_num++;
|
||||||
|
if (str_contains(line, pattern, ignore_case)) {
|
||||||
|
match_cnt++;
|
||||||
|
if (!count_only) {
|
||||||
|
if (show_numbers)
|
||||||
|
printf("%d: %s\n", line_num, line);
|
||||||
|
else
|
||||||
|
printf("%s\n", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_close(fd);
|
||||||
|
|
||||||
|
if (count_only)
|
||||||
|
printf("%d\n", match_cnt);
|
||||||
|
|
||||||
|
return match_cnt > 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue