freedos-micro-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. freedos_micro_python/__init__.py +8 -0
  2. freedos_micro_python/cli.py +106 -0
  3. freedos_micro_python/gen_qstrdefs.py +275 -0
  4. freedos_micro_python/port/arch/bpstruct.h +2 -0
  5. freedos_micro_python/port/arch/cc.h +4 -0
  6. freedos_micro_python/port/arch/epstruct.h +1 -0
  7. freedos_micro_python/port/base64_uc386dos.c +164 -0
  8. freedos_micro_python/port/file_uc386dos.c +228 -0
  9. freedos_micro_python/port/lib/axtls/crypto/crypto.h +45 -0
  10. freedos_micro_python/port/lwip-arch-cc.h +46 -0
  11. freedos_micro_python/port/lwip_uc386dos.c +248 -0
  12. freedos_micro_python/port/lwipopts.h +117 -0
  13. freedos_micro_python/port/math_gamma.c +63 -0
  14. freedos_micro_python/port/modtime_uc386dos.c +60 -0
  15. freedos_micro_python/port/modtls_axtls_uc386dos.c +461 -0
  16. freedos_micro_python/port/mpconfigport.h +358 -0
  17. freedos_micro_python/port/mphal_uc386dos.c +103 -0
  18. freedos_micro_python/port/mphalport.h +11 -0
  19. freedos_micro_python/port/os_uc386dos.c +264 -0
  20. freedos_micro_python/port/path_uc386dos.c +307 -0
  21. freedos_micro_python/port/pktdrv_uc386dos.c +650 -0
  22. freedos_micro_python/port/qstrdefsport.h +2 -0
  23. freedos_micro_python/port/shutil_uc386dos.c +111 -0
  24. freedos_micro_python/port/tempfile_uc386dos.c +129 -0
  25. freedos_micro_python/port/time_real_uc386dos.c +77 -0
  26. freedos_micro_python/port/uc386_net_uc386dos.c +126 -0
  27. freedos_micro_python/port/urllib_parse_uc386dos.c +360 -0
  28. freedos_micro_python/port/urllib_uc386dos.c +29 -0
  29. freedos_micro_python/scripts/build.sh +641 -0
  30. freedos_micro_python/scripts/build_port.sh +241 -0
  31. freedos_micro_python/scripts/fetch.sh +238 -0
  32. freedos_micro_python-0.1.0.dist-info/METADATA +131 -0
  33. freedos_micro_python-0.1.0.dist-info/RECORD +37 -0
  34. freedos_micro_python-0.1.0.dist-info/WHEEL +5 -0
  35. freedos_micro_python-0.1.0.dist-info/entry_points.txt +2 -0
  36. freedos_micro_python-0.1.0.dist-info/licenses/LICENSE +25 -0
  37. freedos_micro_python-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,264 @@
1
+ // uc386-dos custom `os` module — exposes the POSIX-style file/dir
2
+ // ops backed by uc386's libc (which translates them to INT 21h DOS
3
+ // calls). We don't use upstream's `extmod/modos.c` because that
4
+ // gates everything behind the full VFS layer (extmod/vfs.c +
5
+ // vfs_posix.c) which adds substantial code and complexity. This
6
+ // shim provides the basic surface most user code needs:
7
+ //
8
+ // os.mkdir(path) - INT 21h AH=0x39
9
+ // os.rmdir(path) - INT 21h AH=0x3A
10
+ // os.unlink(path) - INT 21h AH=0x41
11
+ // os.remove(path) - alias for unlink
12
+ // os.rename(old, new) - INT 21h AH=0x56
13
+ // os.chdir(path) - INT 21h AH=0x3B
14
+ // os.getcwd() - INT 21h AH=0x47 (+ drive prefix)
15
+ //
16
+ // Errors raise OSError with the DOS error code.
17
+
18
+ #include <string.h>
19
+ #include <unistd.h>
20
+ #include <sys/stat.h>
21
+ #include <stdio.h>
22
+
23
+ #include "py/runtime.h"
24
+ #include "py/mperrno.h"
25
+
26
+ #if defined(__has_include)
27
+ # if __has_include("py/objstr.h")
28
+ # include "py/objstr.h"
29
+ # endif
30
+ #endif
31
+
32
+ static const char *get_path_str(mp_obj_t arg) {
33
+ return mp_obj_str_get_str(arg);
34
+ }
35
+
36
+ static mp_obj_t mod_uc386dos_os_mkdir(mp_obj_t path_in) {
37
+ const char *path = get_path_str(path_in);
38
+ if (mkdir(path, 0777) != 0) {
39
+ mp_raise_OSError(MP_EIO);
40
+ }
41
+ return mp_const_none;
42
+ }
43
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_uc386dos_os_mkdir_obj, mod_uc386dos_os_mkdir);
44
+
45
+ static mp_obj_t mod_uc386dos_os_rmdir(mp_obj_t path_in) {
46
+ extern int rmdir(const char *path);
47
+ const char *path = get_path_str(path_in);
48
+ if (rmdir(path) != 0) {
49
+ mp_raise_OSError(MP_EIO);
50
+ }
51
+ return mp_const_none;
52
+ }
53
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_uc386dos_os_rmdir_obj, mod_uc386dos_os_rmdir);
54
+
55
+ static mp_obj_t mod_uc386dos_os_unlink(mp_obj_t path_in) {
56
+ const char *path = get_path_str(path_in);
57
+ if (unlink(path) != 0) {
58
+ mp_raise_OSError(MP_ENOENT);
59
+ }
60
+ return mp_const_none;
61
+ }
62
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_uc386dos_os_unlink_obj, mod_uc386dos_os_unlink);
63
+
64
+ static mp_obj_t mod_uc386dos_os_rename(mp_obj_t old_in, mp_obj_t new_in) {
65
+ const char *old_path = get_path_str(old_in);
66
+ const char *new_path = get_path_str(new_in);
67
+ if (rename(old_path, new_path) != 0) {
68
+ mp_raise_OSError(MP_EIO);
69
+ }
70
+ return mp_const_none;
71
+ }
72
+ static MP_DEFINE_CONST_FUN_OBJ_2(mod_uc386dos_os_rename_obj, mod_uc386dos_os_rename);
73
+
74
+ static mp_obj_t mod_uc386dos_os_chdir(mp_obj_t path_in) {
75
+ const char *path = get_path_str(path_in);
76
+ if (chdir(path) != 0) {
77
+ mp_raise_OSError(MP_ENOENT);
78
+ }
79
+ return mp_const_none;
80
+ }
81
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_uc386dos_os_chdir_obj, mod_uc386dos_os_chdir);
82
+
83
+ static mp_obj_t mod_uc386dos_os_getcwd(void) {
84
+ char buf[80];
85
+ if (getcwd(buf, sizeof(buf)) == NULL) {
86
+ mp_raise_OSError(MP_EIO);
87
+ }
88
+ return mp_obj_new_str(buf, strlen(buf));
89
+ }
90
+ static MP_DEFINE_CONST_FUN_OBJ_0(mod_uc386dos_os_getcwd_obj, mod_uc386dos_os_getcwd);
91
+
92
+ // `os.listdir([path])` — directory listing via DOS find-first /
93
+ // find-next. Returns a Python list of filenames (str).
94
+ //
95
+ // - No arg: list the current directory (mask "*.*").
96
+ // - One arg: list the given directory. We append "\\*.*" to the
97
+ // path and pass that as the find-first mask.
98
+ //
99
+ // Skips the synthetic "." and ".." entries DOS returns for
100
+ // subdirectories (matching CPython's behavior).
101
+ // uc386 prefixes `_` to C identifiers, so C `dos_find_first` →
102
+ // asm label `_dos_find_first` (matches our libc symbol). Don't
103
+ // prepend an extra underscore in C — that would yield asm label
104
+ // `__dos_find_first`, which nasm rejects as an undefined external.
105
+ extern int dos_find_first(const char *mask);
106
+ extern int dos_find_next(void);
107
+ extern const char *dos_dta_filename(void);
108
+
109
+ // Env-block helpers — see lib/i386_dos_libc.asm for impls. They walk
110
+ // the DOS PSP environment block (PSP[0x2C] = env_seg under PMODE/W's
111
+ // flat addressing).
112
+ extern const char *getenv(const char *name);
113
+ extern const char *dos_env_iter(unsigned index);
114
+ extern int system(const char *cmd);
115
+
116
+ // `os.path` submodule. Defined in path_uc386dos.c — registered as
117
+ // the `path` attribute of mp_module_os below so user code can do
118
+ // `os.path.join(a, b)` the way CPython programs expect.
119
+ extern const struct _mp_obj_module_t mp_module_os_path;
120
+
121
+ static mp_obj_t mod_uc386dos_os_listdir(size_t n_args, const mp_obj_t *args) {
122
+ char mask[80];
123
+ if (n_args == 0) {
124
+ // Current directory.
125
+ mask[0] = '*'; mask[1] = '.'; mask[2] = '*'; mask[3] = '\0';
126
+ } else {
127
+ const char *path = get_path_str(args[0]);
128
+ size_t path_len = strlen(path);
129
+ if (path_len + 5 > sizeof(mask)) {
130
+ mp_raise_OSError(MP_E2BIG);
131
+ }
132
+ memcpy(mask, path, path_len);
133
+ // Append "\\*.*" if path doesn't already end with a separator.
134
+ size_t i = path_len;
135
+ if (i > 0 && mask[i - 1] != '\\' && mask[i - 1] != '/') {
136
+ mask[i++] = '\\';
137
+ }
138
+ mask[i++] = '*';
139
+ mask[i++] = '.';
140
+ mask[i++] = '*';
141
+ mask[i] = '\0';
142
+ }
143
+ mp_obj_t list = mp_obj_new_list(0, NULL);
144
+ int rc = dos_find_first(mask);
145
+ while (rc == 0) {
146
+ const char *fname = dos_dta_filename();
147
+ // Skip "." and ".." entries.
148
+ if (!(fname[0] == '.' && (fname[1] == '\0' ||
149
+ (fname[1] == '.' && fname[2] == '\0')))) {
150
+ mp_obj_list_append(list, mp_obj_new_str(fname, strlen(fname)));
151
+ }
152
+ rc = dos_find_next();
153
+ }
154
+ return list;
155
+ }
156
+ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uc386dos_os_listdir_obj,
157
+ 0, 1, mod_uc386dos_os_listdir);
158
+
159
+ // `os.stat(path)` — returns a 10-tuple matching CPython's
160
+ // `os.stat_result`:
161
+ // (st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid,
162
+ // st_size, st_atime, st_mtime, st_ctime).
163
+ // Backed by uc386's libc `stat()` (INT 21h-based).
164
+ static mp_obj_t mod_uc386dos_os_stat(mp_obj_t path_in) {
165
+ const char *path = get_path_str(path_in);
166
+ struct stat st;
167
+ if (stat(path, &st) != 0) {
168
+ mp_raise_OSError(MP_ENOENT);
169
+ }
170
+ mp_obj_t fields[10] = {
171
+ mp_obj_new_int_from_uint(st.st_mode),
172
+ mp_obj_new_int_from_uint(0), // st_ino
173
+ mp_obj_new_int_from_uint(0), // st_dev
174
+ mp_obj_new_int_from_uint(1), // st_nlink
175
+ mp_obj_new_int_from_uint(0), // st_uid
176
+ mp_obj_new_int_from_uint(0), // st_gid
177
+ mp_obj_new_int_from_uint((unsigned)st.st_size),
178
+ mp_obj_new_int_from_uint((unsigned)st.st_atime),
179
+ mp_obj_new_int_from_uint((unsigned)st.st_mtime),
180
+ mp_obj_new_int_from_uint((unsigned)st.st_ctime),
181
+ };
182
+ return mp_obj_new_tuple(10, fields);
183
+ }
184
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_uc386dos_os_stat_obj, mod_uc386dos_os_stat);
185
+
186
+ // `os.system(cmd)` — invokes the DOS shell to run `cmd`. Backed by
187
+ // libc's `system()` which calls INT 21h AH=0x4B sub 0 (LOAD AND
188
+ // EXECUTE) on COMMAND.COM (or whatever COMSPEC points at) with
189
+ // "/C <cmd>" as the command tail, then reads the exit code via
190
+ // AH=0x4D (Get Return Code). Returns -1 on exec failure.
191
+ static mp_obj_t mod_uc386dos_os_system(mp_obj_t cmd_in) {
192
+ const char *cmd = get_path_str(cmd_in);
193
+ int rc = system(cmd);
194
+ return mp_obj_new_int(rc);
195
+ }
196
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_uc386dos_os_system_obj, mod_uc386dos_os_system);
197
+
198
+ // `os.getenv(name, default=None)` — POSIX-style env lookup, case-
199
+ // sensitive (DOS conventionally upper-cases names but we match
200
+ // exactly what's in the env block).
201
+ static mp_obj_t mod_uc386dos_os_getenv(size_t n_args, const mp_obj_t *args) {
202
+ const char *name = get_path_str(args[0]);
203
+ const char *val = getenv(name);
204
+ if (val) {
205
+ return mp_obj_new_str(val, strlen(val));
206
+ }
207
+ if (n_args >= 2) {
208
+ return args[1];
209
+ }
210
+ return mp_const_none;
211
+ }
212
+ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_uc386dos_os_getenv_obj,
213
+ 1, 2, mod_uc386dos_os_getenv);
214
+
215
+ // `os.environ()` — snapshot of the env block as a fresh `dict`.
216
+ // Returns a function rather than a property because MicroPython
217
+ // modules don't support attribute-getter delegation, and the env
218
+ // block is fixed at program start so a snapshot is what we'd want
219
+ // anyway. Differs from CPython, where `os.environ` is a live dict.
220
+ static mp_obj_t mod_uc386dos_os_environ(void) {
221
+ mp_obj_t d = mp_obj_new_dict(0);
222
+ for (unsigned i = 0; ; i++) {
223
+ const char *entry = dos_env_iter(i);
224
+ if (!entry) {
225
+ break;
226
+ }
227
+ const char *eq = strchr(entry, '=');
228
+ if (!eq) {
229
+ // Malformed entry (no '='); skip.
230
+ continue;
231
+ }
232
+ size_t key_len = (size_t)(eq - entry);
233
+ size_t val_len = strlen(eq + 1);
234
+ mp_obj_dict_store(d,
235
+ mp_obj_new_str(entry, key_len),
236
+ mp_obj_new_str(eq + 1, val_len));
237
+ }
238
+ return d;
239
+ }
240
+ static MP_DEFINE_CONST_FUN_OBJ_0(mod_uc386dos_os_environ_obj, mod_uc386dos_os_environ);
241
+
242
+ static const mp_rom_map_elem_t mp_module_os_globals_table[] = {
243
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_os) },
244
+ { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mod_uc386dos_os_mkdir_obj) },
245
+ { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mod_uc386dos_os_rmdir_obj) },
246
+ { MP_ROM_QSTR(MP_QSTR_unlink), MP_ROM_PTR(&mod_uc386dos_os_unlink_obj) },
247
+ { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mod_uc386dos_os_unlink_obj) },
248
+ { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mod_uc386dos_os_rename_obj) },
249
+ { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mod_uc386dos_os_chdir_obj) },
250
+ { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mod_uc386dos_os_getcwd_obj) },
251
+ { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mod_uc386dos_os_listdir_obj) },
252
+ { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mod_uc386dos_os_stat_obj) },
253
+ { MP_ROM_QSTR(MP_QSTR_system), MP_ROM_PTR(&mod_uc386dos_os_system_obj) },
254
+ { MP_ROM_QSTR(MP_QSTR_getenv), MP_ROM_PTR(&mod_uc386dos_os_getenv_obj) },
255
+ { MP_ROM_QSTR(MP_QSTR_environ), MP_ROM_PTR(&mod_uc386dos_os_environ_obj) },
256
+ { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__slash_) },
257
+ { MP_ROM_QSTR(MP_QSTR_path), MP_ROM_PTR(&mp_module_os_path) },
258
+ };
259
+ static MP_DEFINE_CONST_DICT(mp_module_os_globals, mp_module_os_globals_table);
260
+
261
+ const mp_obj_module_t mp_module_os = {
262
+ .base = { &mp_type_module },
263
+ .globals = (mp_obj_dict_t *)&mp_module_os_globals,
264
+ };
@@ -0,0 +1,307 @@
1
+ // uc386-dos `os.path` submodule. Registered as the `path` attribute
2
+ // of the os module (uc386-dos/os_uc386dos.c) so user code can call
3
+ // `os.path.join(...)` / `os.path.exists(...)` etc. the way CPython
4
+ // programs expect.
5
+ //
6
+ // We don't use upstream's vfs path machinery — that ties everything
7
+ // to a VFS layer and adds noticeable surface. This shim just does
8
+ // the string manipulation on DOS-style paths (backslash separator,
9
+ // optional drive letter prefix), backed by libc's `stat()` for the
10
+ // existence checks.
11
+
12
+ #include <string.h>
13
+ #include <sys/stat.h>
14
+ #include <unistd.h>
15
+
16
+ #include "py/runtime.h"
17
+ #include "py/objstr.h"
18
+ #include "py/objtuple.h"
19
+ #include "py/mperrno.h"
20
+
21
+ // True for backslash and forward-slash. DOS treats both as separators
22
+ // in most contexts, so we accept both for splitting / matching but
23
+ // always emit backslash when joining (matching `os.path.sep = "\\"`).
24
+ static inline int is_sep(char c) {
25
+ return c == '\\' || c == '/';
26
+ }
27
+
28
+ // Find the rightmost separator in `s` (length `len`). Returns the
29
+ // index of the separator, or -1 if none found.
30
+ static int last_sep(const char *s, size_t len) {
31
+ for (int i = (int)len - 1; i >= 0; i--) {
32
+ if (is_sep(s[i])) {
33
+ return i;
34
+ }
35
+ }
36
+ return -1;
37
+ }
38
+
39
+ // Find the rightmost '.' in `s[start:len]`. Returns -1 if none, or
40
+ // if the only '.' is at the start (we treat ".foo" as having no
41
+ // extension, matching CPython).
42
+ static int last_dot(const char *s, size_t start, size_t len) {
43
+ for (int i = (int)len - 1; i > (int)start; i--) {
44
+ if (s[i] == '.') {
45
+ // Skip a trailing run of dots ("foo..." has no ext) —
46
+ // require at least one non-dot before this '.'.
47
+ return i;
48
+ }
49
+ if (is_sep(s[i])) {
50
+ return -1;
51
+ }
52
+ }
53
+ return -1;
54
+ }
55
+
56
+ // `os.path.join(*parts)` — concatenate path parts with the
57
+ // platform separator. An empty `parts` returns "". A part starting
58
+ // with a drive letter or separator resets the result (matching
59
+ // CPython's behavior on Windows).
60
+ static mp_obj_t mod_path_join(size_t n_args, const mp_obj_t *args) {
61
+ if (n_args == 0) {
62
+ return mp_obj_new_str("", 0);
63
+ }
64
+ vstr_t vstr;
65
+ vstr_init(&vstr, 64);
66
+ for (size_t i = 0; i < n_args; i++) {
67
+ size_t plen;
68
+ const char *p = mp_obj_str_get_data(args[i], &plen);
69
+ if (plen == 0) {
70
+ continue;
71
+ }
72
+ // Absolute path or drive prefix → reset the buffer.
73
+ int absolute = is_sep(p[0]);
74
+ if (!absolute && plen >= 2 && p[1] == ':') {
75
+ absolute = 1;
76
+ }
77
+ if (absolute) {
78
+ vstr.len = 0;
79
+ } else if (vstr.len > 0 && !is_sep(vstr.buf[vstr.len - 1])) {
80
+ vstr_add_char(&vstr, '\\');
81
+ }
82
+ vstr_add_strn(&vstr, p, plen);
83
+ }
84
+ return mp_obj_new_str_from_vstr(&vstr);
85
+ }
86
+ static MP_DEFINE_CONST_FUN_OBJ_VAR(mod_path_join_obj, 0, mod_path_join);
87
+
88
+ // `os.path.split(path)` — return `(dirname, basename)`. The
89
+ // basename is the trailing component; the dirname is everything
90
+ // before it (with the trailing separator stripped, except when
91
+ // dirname would be empty or just "C:" / "/").
92
+ static mp_obj_t mod_path_split(mp_obj_t path_in) {
93
+ size_t len;
94
+ const char *p = mp_obj_str_get_data(path_in, &len);
95
+ int sep = last_sep(p, len);
96
+ mp_obj_t head, tail;
97
+ if (sep < 0) {
98
+ head = mp_obj_new_str("", 0);
99
+ tail = mp_obj_new_str(p, len);
100
+ } else {
101
+ // Keep the separator on the head only when it's a root-level
102
+ // marker ("\\foo" → ("\\", "foo"), "C:\\foo" → ("C:\\", "foo")).
103
+ size_t head_len = (size_t)sep;
104
+ if (head_len == 0 || (head_len == 2 && p[1] == ':')) {
105
+ head_len = (size_t)sep + 1;
106
+ }
107
+ head = mp_obj_new_str(p, head_len);
108
+ tail = mp_obj_new_str(p + sep + 1, len - (size_t)sep - 1);
109
+ }
110
+ mp_obj_t items[2] = { head, tail };
111
+ return mp_obj_new_tuple(2, items);
112
+ }
113
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_split_obj, mod_path_split);
114
+
115
+ // `os.path.splitext(path)` — return `(root, ext)`. ext includes
116
+ // the leading '.' or is empty.
117
+ static mp_obj_t mod_path_splitext(mp_obj_t path_in) {
118
+ size_t len;
119
+ const char *p = mp_obj_str_get_data(path_in, &len);
120
+ int sep = last_sep(p, len);
121
+ size_t name_start = sep < 0 ? 0 : (size_t)sep + 1;
122
+ int dot = last_dot(p, name_start, len);
123
+ mp_obj_t root, ext;
124
+ if (dot < 0) {
125
+ root = mp_obj_new_str(p, len);
126
+ ext = mp_obj_new_str("", 0);
127
+ } else {
128
+ root = mp_obj_new_str(p, (size_t)dot);
129
+ ext = mp_obj_new_str(p + dot, len - (size_t)dot);
130
+ }
131
+ mp_obj_t items[2] = { root, ext };
132
+ return mp_obj_new_tuple(2, items);
133
+ }
134
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_splitext_obj, mod_path_splitext);
135
+
136
+ // `os.path.basename(path)` — last component (just the second
137
+ // element of split()).
138
+ static mp_obj_t mod_path_basename(mp_obj_t path_in) {
139
+ size_t len;
140
+ const char *p = mp_obj_str_get_data(path_in, &len);
141
+ int sep = last_sep(p, len);
142
+ if (sep < 0) {
143
+ return mp_obj_new_str(p, len);
144
+ }
145
+ return mp_obj_new_str(p + sep + 1, len - (size_t)sep - 1);
146
+ }
147
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_basename_obj, mod_path_basename);
148
+
149
+ // `os.path.dirname(path)` — first component (just the first
150
+ // element of split()).
151
+ static mp_obj_t mod_path_dirname(mp_obj_t path_in) {
152
+ size_t len;
153
+ const char *p = mp_obj_str_get_data(path_in, &len);
154
+ int sep = last_sep(p, len);
155
+ if (sep < 0) {
156
+ return mp_obj_new_str("", 0);
157
+ }
158
+ size_t head_len = (size_t)sep;
159
+ if (head_len == 0 || (head_len == 2 && p[1] == ':')) {
160
+ head_len = (size_t)sep + 1;
161
+ }
162
+ return mp_obj_new_str(p, head_len);
163
+ }
164
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_dirname_obj, mod_path_dirname);
165
+
166
+ // `os.path.exists(path)` — true if `stat()` succeeds. Backed by
167
+ // uc386's libc which routes through INT 21h AH=0x4300 (get
168
+ // attribs) and AH=0x42 (lseek to end).
169
+ static mp_obj_t mod_path_exists(mp_obj_t path_in) {
170
+ const char *p = mp_obj_str_get_str(path_in);
171
+ struct stat st;
172
+ return mp_obj_new_bool(stat(p, &st) == 0);
173
+ }
174
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_exists_obj, mod_path_exists);
175
+
176
+ // `os.path.isfile(path)` — same as exists for our libc, which
177
+ // only stats regular files (directories can't be opened with
178
+ // AH=0x3D). Conservative: a true return guarantees a regular
179
+ // file; a false return doesn't necessarily mean it's a dir.
180
+ static mp_obj_t mod_path_isfile(mp_obj_t path_in) {
181
+ const char *p = mp_obj_str_get_str(path_in);
182
+ struct stat st;
183
+ if (stat(p, &st) != 0) {
184
+ return mp_obj_new_bool(0);
185
+ }
186
+ return mp_obj_new_bool((st.st_mode & 0xF000) == 0x8000); // S_IFREG
187
+ }
188
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_isfile_obj, mod_path_isfile);
189
+
190
+ // `os.path.getsize(path)` — file size in bytes, raises OSError if
191
+ // the path doesn't exist. Backed by libc stat() (INT 21h AH=0x42
192
+ // lseek-to-end via uc386's libc).
193
+ static mp_obj_t mod_path_getsize(mp_obj_t path_in) {
194
+ const char *p = mp_obj_str_get_str(path_in);
195
+ struct stat st;
196
+ if (stat(p, &st) != 0) {
197
+ mp_raise_OSError(MP_ENOENT);
198
+ }
199
+ return mp_obj_new_int_from_uint((unsigned)st.st_size);
200
+ }
201
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_getsize_obj, mod_path_getsize);
202
+
203
+ // `os.path.isabs(path)` — true if `path` starts with a separator
204
+ // or with a `<letter>:` drive prefix. Pure string check, no I/O.
205
+ static mp_obj_t mod_path_isabs(mp_obj_t path_in) {
206
+ size_t len;
207
+ const char *p = mp_obj_str_get_data(path_in, &len);
208
+ if (len >= 1 && is_sep(p[0])) {
209
+ return mp_obj_new_bool(1);
210
+ }
211
+ if (len >= 2 && p[1] == ':') {
212
+ return mp_obj_new_bool(1);
213
+ }
214
+ return mp_obj_new_bool(0);
215
+ }
216
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_isabs_obj, mod_path_isabs);
217
+
218
+ // `os.path.abspath(path)` — normalize and prefix with getcwd() if
219
+ // the path isn't already absolute. Doesn't resolve symlinks (DOS
220
+ // doesn't have them in any meaningful sense).
221
+ static mp_obj_t mod_path_abspath(mp_obj_t path_in) {
222
+ size_t len;
223
+ const char *p = mp_obj_str_get_data(path_in, &len);
224
+ int absolute = (len >= 1 && is_sep(p[0])) ||
225
+ (len >= 2 && p[1] == ':');
226
+ vstr_t vstr;
227
+ vstr_init(&vstr, len + 64);
228
+ if (!absolute) {
229
+ char cwd[80];
230
+ if (getcwd(cwd, sizeof(cwd)) == NULL) {
231
+ mp_raise_OSError(MP_EIO);
232
+ }
233
+ vstr_add_str(&vstr, cwd);
234
+ // getcwd typically returns "C:\\" or "C:\\subdir"; ensure
235
+ // we have a separator before appending the relative path.
236
+ if (vstr.len > 0 && !is_sep(vstr.buf[vstr.len - 1])) {
237
+ vstr_add_char(&vstr, '\\');
238
+ }
239
+ }
240
+ // Append `path`, deduplicating separators while we go (cheap
241
+ // inline normpath since we already need to scan the bytes).
242
+ int prev_sep = vstr.len > 0 && is_sep(vstr.buf[vstr.len - 1]);
243
+ for (size_t i = 0; i < len; i++) {
244
+ char c = p[i];
245
+ if (is_sep(c)) {
246
+ if (!prev_sep) {
247
+ vstr_add_char(&vstr, '\\');
248
+ prev_sep = 1;
249
+ }
250
+ } else {
251
+ vstr_add_char(&vstr, c);
252
+ prev_sep = 0;
253
+ }
254
+ }
255
+ return mp_obj_new_str_from_vstr(&vstr);
256
+ }
257
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_abspath_obj, mod_path_abspath);
258
+
259
+ // `os.path.normpath(path)` — collapse forward slashes to backslash,
260
+ // drop redundant separators. Doesn't resolve `..` (DOS conventions
261
+ // vary; conservative).
262
+ static mp_obj_t mod_path_normpath(mp_obj_t path_in) {
263
+ size_t len;
264
+ const char *p = mp_obj_str_get_data(path_in, &len);
265
+ vstr_t vstr;
266
+ vstr_init(&vstr, len + 1);
267
+ int prev_sep = 0;
268
+ for (size_t i = 0; i < len; i++) {
269
+ char c = p[i];
270
+ if (is_sep(c)) {
271
+ if (!prev_sep) {
272
+ vstr_add_char(&vstr, '\\');
273
+ prev_sep = 1;
274
+ }
275
+ } else {
276
+ vstr_add_char(&vstr, c);
277
+ prev_sep = 0;
278
+ }
279
+ }
280
+ if (vstr.len == 0) {
281
+ vstr_add_char(&vstr, '.');
282
+ }
283
+ return mp_obj_new_str_from_vstr(&vstr);
284
+ }
285
+ static MP_DEFINE_CONST_FUN_OBJ_1(mod_path_normpath_obj, mod_path_normpath);
286
+
287
+ static const mp_rom_map_elem_t mp_module_os_path_globals_table[] = {
288
+ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_path) },
289
+ { MP_ROM_QSTR(MP_QSTR_join), MP_ROM_PTR(&mod_path_join_obj) },
290
+ { MP_ROM_QSTR(MP_QSTR_split), MP_ROM_PTR(&mod_path_split_obj) },
291
+ { MP_ROM_QSTR(MP_QSTR_splitext), MP_ROM_PTR(&mod_path_splitext_obj) },
292
+ { MP_ROM_QSTR(MP_QSTR_basename), MP_ROM_PTR(&mod_path_basename_obj) },
293
+ { MP_ROM_QSTR(MP_QSTR_dirname), MP_ROM_PTR(&mod_path_dirname_obj) },
294
+ { MP_ROM_QSTR(MP_QSTR_exists), MP_ROM_PTR(&mod_path_exists_obj) },
295
+ { MP_ROM_QSTR(MP_QSTR_isfile), MP_ROM_PTR(&mod_path_isfile_obj) },
296
+ { MP_ROM_QSTR(MP_QSTR_normpath), MP_ROM_PTR(&mod_path_normpath_obj) },
297
+ { MP_ROM_QSTR(MP_QSTR_getsize), MP_ROM_PTR(&mod_path_getsize_obj) },
298
+ { MP_ROM_QSTR(MP_QSTR_isabs), MP_ROM_PTR(&mod_path_isabs_obj) },
299
+ { MP_ROM_QSTR(MP_QSTR_abspath), MP_ROM_PTR(&mod_path_abspath_obj) },
300
+ { MP_ROM_QSTR(MP_QSTR_sep), MP_ROM_QSTR(MP_QSTR__backslash_) },
301
+ };
302
+ static MP_DEFINE_CONST_DICT(mp_module_os_path_globals, mp_module_os_path_globals_table);
303
+
304
+ const mp_obj_module_t mp_module_os_path = {
305
+ .base = { &mp_type_module },
306
+ .globals = (mp_obj_dict_t *)&mp_module_os_path_globals,
307
+ };