Skip to content

Commit c5954a0

Browse files
committed
stash, mostly sh23
1 parent 3f14468 commit c5954a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+5882
-275
lines changed

.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"makefile.makefilePath": "${workspaceFolder}/level2/sh23"
3+
}

README.md

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,18 @@
11
# validc-tools
2-
A userspace of tools that only uses functions in the C standard
2+
A userspace of tools that only uses functions in the C standard.
3+
4+
** Bootstrap, kinda
5+
6+
As part of this thought experiment, you can imagine the tools
7+
were created in this order.
8+
9+
*** led - a line editor
10+
11+
This is a basic line editor.
12+
13+
*** run - a command runner
14+
15+
This is the first build tool. It just reads lines
16+
from a text file and passes them to the system for execution.
17+
18+
***

build.bat

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
; build.bat - Build all tools in the C23 toolkit
2+
3+
; Compiler settings
4+
var CC=gcc
5+
var CFLAGS=-std=c23
6+
7+
; Update hash database to remove stale entries
8+
; fnvupdate
9+
; echo Updated file_hash.dat
10+
11+
; Build batch
12+
fnvtest batch.c
13+
ifc {{?}} == 1 {{CC}} {{CFLAGS}} -o batch batch.c
14+
echo batch build status: {{?}}
15+
16+
; Build echo
17+
fnvtest echo.c
18+
ifc {{?}} == 1 {{CC}} {{CFLAGS}} -o echo echo.c
19+
echo echo build status: {{?}}
20+
21+
; Build cat
22+
fnvtest cat.c
23+
ifc {{?}} == 1 {{CC}} {{CFLAGS}} -o cat cat.c
24+
echo cat build status: {{?}}
25+
26+
; Build fnvtest
27+
fnvtest fnvtest.c
28+
ifc {{?}} == 1 {{CC}} {{CFLAGS}} -o fnvtest fnvtest.c
29+
echo fnvtest build status: {{?}}
30+
31+
; Build fnvupdate
32+
fnvtest fnvupdate.c
33+
ifc {{?}} == 1 {{CC}} {{CFLAGS}} -o fnvupdate fnvupdate.c
34+
echo fnvupdate build status: {{?}}
35+
36+
; Build ifc
37+
fnvtest ifc.c
38+
ifc {{?}} == 1 {{CC}} {{CFLAGS}} -o ifc ifc.c
39+
echo ifc build status: {{?}}
40+
41+
; Final status
42+
echo Build complete

led/README

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
LINE EDITOR
2+
3+
This is a line editor in the spirit of `ed` called `led`.
4+
5+
It can be used to enter text files.
6+
7+
I didn't actually use this editor, but I *could have*, so
8+
philosophically am including it as part of my Valid C bootstrap.

led/README~

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
LINE EDITOR
2+
3+
This is a line editor in the spirit of `ed`.
4+
5+
It can be used to edit text files.
6+

led/bre_test.c

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include "bre.h"
4+
5+
int main(void) {
6+
// Test 1: Simple replacement of "foo" with "bar"
7+
const char *text1 = "This is foo in a string";
8+
const char *pattern1 = "foo";
9+
const char *replacement1 = "bar";
10+
11+
printf("Test 1: Simple replacement\n");
12+
printf("Original: %s\n", text1);
13+
printf("Pattern: %s, Replacement: %s\n", pattern1, replacement1);
14+
15+
BreMatch match1;
16+
if (bre_match(text1, pattern1, &match1)) {
17+
printf("Match found at position %d, length %d\n", match1.start, match1.length);
18+
} else {
19+
printf("No match found\n");
20+
}
21+
22+
char *result1 = bre_substitute(text1, pattern1, replacement1);
23+
if (result1) {
24+
printf("Result: %s\n", result1);
25+
free(result1);
26+
} else {
27+
printf("Substitution failed\n");
28+
}
29+
30+
// Test 2: Using capture group and backreference
31+
const char *text2 = "This is foo in a string";
32+
const char *pattern2 = "\\(foo\\)";
33+
const char *replacement2 = "\\1bar";
34+
35+
printf("\nTest 2: Capture group and backreference\n");
36+
printf("Original: %s\n", text2);
37+
printf("Pattern: %s, Replacement: %s\n", pattern2, replacement2);
38+
39+
BreMatch match2;
40+
if (bre_match(text2, pattern2, &match2)) {
41+
printf("Match found at position %d, length %d, groups %d\n",
42+
match2.start, match2.length, match2.num_groups);
43+
for (int i = 0; i < match2.num_groups; i++) {
44+
printf("Group %d: start %d, length %d\n",
45+
i + 1, match2.groups[i].start, match2.groups[i].length);
46+
}
47+
} else {
48+
printf("No match found\n");
49+
}
50+
51+
char *result2 = bre_substitute(text2, pattern2, replacement2);
52+
if (result2) {
53+
printf("Result: %s\n", result2);
54+
free(result2);
55+
} else {
56+
printf("Substitution failed\n");
57+
}
58+
59+
return 0;
60+
}

led/led.c

+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
5+
#define MAX_LINE 1024 // Maximum length of a line
6+
7+
typedef struct {
8+
char **lines; // Array of lines
9+
int num_lines; // Number of lines in buffer
10+
int current_line; // Current line (1-based, 0 means none)
11+
int dirty; // Tracks unsaved changes
12+
} Editor;
13+
14+
static char *my_strdup(const char *s);
15+
static void init_editor(Editor *ed);
16+
static int parse_address(Editor *ed, const char *addr);
17+
static void append_line(Editor *ed, int addr);
18+
static void insert_line(Editor *ed, int addr);
19+
static void print_line(Editor *ed, int addr);
20+
static void delete_line(Editor *ed, int addr);
21+
static void write_file(Editor *ed);
22+
static void free_editor(Editor *ed);
23+
static void execute_command(Editor *ed, char *cmd);
24+
static void load_file(Editor *ed, const char *filename); // New function
25+
26+
char *my_strdup(const char *s) {
27+
if (s == NULL) return NULL;
28+
size_t len = strlen(s) + 1; // Include null terminator
29+
char *dup = (char *)malloc(len);
30+
if (dup == NULL) return NULL; // Allocation failed
31+
strcpy(dup, s);
32+
return dup;
33+
}
34+
35+
static void init_editor(Editor *ed) {
36+
ed->lines = NULL;
37+
ed->num_lines = 0;
38+
ed->current_line = 0;
39+
ed->dirty = 0;
40+
}
41+
42+
static int parse_address(Editor *ed, const char *addr) {
43+
if (!addr || strlen(addr) == 0) return ed->current_line - 1;
44+
if (strcmp(addr, ".") == 0) return ed->current_line - 1;
45+
if (strcmp(addr, "$") == 0) return ed->num_lines - 1;
46+
int line = atoi(addr);
47+
if (line <= 0 || line > ed->num_lines) return -1; // Invalid
48+
return line - 1; // Convert to 0-based
49+
}
50+
51+
static void append_line(Editor *ed, int addr) {
52+
char line[MAX_LINE];
53+
printf("(Enter text, end with '.' on a new line)\n");
54+
while (fgets(line, MAX_LINE, stdin)) {
55+
line[strcspn(line, "\n")] = 0; // Remove newline
56+
if (strcmp(line, ".") == 0) break;
57+
58+
ed->lines = realloc(ed->lines, (ed->num_lines + 1) * sizeof(char *));
59+
ed->lines[ed->num_lines] = my_strdup(line);
60+
ed->num_lines++;
61+
if (addr < ed->num_lines - 1) {
62+
for (int i = ed->num_lines - 1; i > addr + 1; i--) {
63+
char *temp = ed->lines[i];
64+
ed->lines[i] = ed->lines[i - 1];
65+
ed->lines[i - 1] = temp;
66+
}
67+
addr++;
68+
}
69+
}
70+
ed->current_line = addr + 1;
71+
ed->dirty = 1;
72+
}
73+
74+
static void insert_line(Editor *ed, int addr) {
75+
char line[MAX_LINE];
76+
printf("(Enter text, end with '.' on a new line)\n");
77+
while (fgets(line, MAX_LINE, stdin)) {
78+
line[strcspn(line, "\n")] = 0; // Remove newline
79+
if (strcmp(line, ".") == 0) break;
80+
81+
ed->lines = realloc(ed->lines, (ed->num_lines + 1) * sizeof(char *));
82+
for (int i = ed->num_lines; i > addr; i--) {
83+
ed->lines[i] = ed->lines[i - 1];
84+
}
85+
ed->lines[addr] = my_strdup(line);
86+
ed->num_lines++;
87+
addr++;
88+
}
89+
ed->current_line = addr;
90+
ed->dirty = 1;
91+
}
92+
93+
static void print_line(Editor *ed, int addr) {
94+
if (ed->num_lines == 0 || addr < 0 || addr >= ed->num_lines) {
95+
printf("?\n");
96+
return;
97+
}
98+
printf("%s\n", ed->lines[addr]);
99+
ed->current_line = addr + 1;
100+
}
101+
102+
static void delete_line(Editor *ed, int addr) {
103+
if (ed->num_lines == 0 || addr < 0 || addr >= ed->num_lines) {
104+
printf("?\n");
105+
return;
106+
}
107+
free(ed->lines[addr]);
108+
for (int i = addr; i < ed->num_lines - 1; i++) {
109+
ed->lines[i] = ed->lines[i + 1];
110+
}
111+
ed->num_lines--;
112+
ed->lines = realloc(ed->lines, ed->num_lines * sizeof(char *));
113+
ed->current_line = addr + 1 > ed->num_lines ? ed->num_lines : addr + 1;
114+
ed->dirty = 1;
115+
}
116+
117+
static void write_file(Editor *ed) {
118+
char filename[MAX_LINE];
119+
printf("Enter filename: ");
120+
fgets(filename, MAX_LINE, stdin);
121+
filename[strcspn(filename, "\n")] = 0;
122+
123+
FILE *fp = fopen(filename, "w");
124+
if (!fp) {
125+
printf("?\n");
126+
return;
127+
}
128+
int bytes = 0;
129+
for (int i = 0; i < ed->num_lines; i++) {
130+
fprintf(fp, "%s\n", ed->lines[i]);
131+
bytes += strlen(ed->lines[i]) + 1; // Include newline
132+
}
133+
fclose(fp);
134+
printf("%d\n", bytes);
135+
ed->dirty = 0;
136+
}
137+
138+
static void free_editor(Editor *ed) {
139+
for (int i = 0; i < ed->num_lines; i++) {
140+
free(ed->lines[i]);
141+
}
142+
free(ed->lines);
143+
}
144+
145+
static void execute_command(Editor *ed, char *cmd) {
146+
cmd[strcspn(cmd, "\n")] = 0; // Remove newline
147+
if (strlen(cmd) == 0) return;
148+
149+
char addr_str[MAX_LINE] = "";
150+
char op = cmd[strlen(cmd) - 1];
151+
if (strlen(cmd) > 1) {
152+
strncpy(addr_str, cmd, strlen(cmd) - 1);
153+
addr_str[strlen(cmd) - 1] = 0;
154+
} else {
155+
op = cmd[0];
156+
}
157+
158+
if (strspn(cmd, "0123456789.$") == strlen(cmd)) {
159+
int addr = parse_address(ed, cmd);
160+
if (addr >= 0 && addr < ed->num_lines) ed->current_line = addr + 1;
161+
return;
162+
}
163+
164+
int addr = parse_address(ed, addr_str);
165+
if (addr < 0 && op != 'a' && op != 'q' && op != 'w') {
166+
printf("?\n");
167+
return;
168+
}
169+
170+
switch (op) {
171+
case 'a': append_line(ed, addr < 0 ? ed->num_lines - 1 : addr); break;
172+
case 'i': insert_line(ed, addr < 0 ? 0 : addr); break;
173+
case 'p': print_line(ed, addr); break;
174+
case 'd': delete_line(ed, addr); break;
175+
case 'w': write_file(ed); break;
176+
case 'q': if (ed->dirty) printf("?\n"); else exit(0); break;
177+
default: printf("?\n");
178+
}
179+
}
180+
181+
// New function to load a file into the editor buffer
182+
static void load_file(Editor *ed, const char *filename) {
183+
FILE *fp = fopen(filename, "r");
184+
if (!fp) {
185+
printf("?\n"); // File not found or can't be opened
186+
return;
187+
}
188+
189+
char line[MAX_LINE];
190+
int bytes = 0;
191+
while (fgets(line, MAX_LINE, fp)) {
192+
line[strcspn(line, "\n")] = 0; // Remove newline
193+
ed->lines = realloc(ed->lines, (ed->num_lines + 1) * sizeof(char *));
194+
ed->lines[ed->num_lines] = my_strdup(line);
195+
if (ed->lines[ed->num_lines] == NULL) {
196+
printf("?\n"); // Memory allocation failed
197+
fclose(fp);
198+
return;
199+
}
200+
ed->num_lines++;
201+
bytes += strlen(line) + 1; // Count bytes including newline
202+
}
203+
fclose(fp);
204+
ed->current_line = ed->num_lines; // Set current line to last line
205+
printf("%d\n", bytes); // Print byte count like POSIX ed
206+
}
207+
208+
int main(int argc, char *argv[]) {
209+
Editor ed;
210+
init_editor(&ed);
211+
char cmd[MAX_LINE];
212+
213+
// If a filename is provided as a command-line argument, load it
214+
if (argc > 1) {
215+
load_file(&ed, argv[1]);
216+
}
217+
218+
printf("Simple POSIX ed-like editor in C. Type commands (e.g., 'a', 'p', 'q')\n");
219+
while (1) {
220+
fgets(cmd, MAX_LINE, stdin);
221+
execute_command(&ed, cmd);
222+
}
223+
224+
free_editor(&ed); // Unreachable due to infinite loop, but good practice
225+
return 0;
226+
}

0 commit comments

Comments
 (0)