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.
- freedos_micro_python/__init__.py +8 -0
- freedos_micro_python/cli.py +106 -0
- freedos_micro_python/gen_qstrdefs.py +275 -0
- freedos_micro_python/port/arch/bpstruct.h +2 -0
- freedos_micro_python/port/arch/cc.h +4 -0
- freedos_micro_python/port/arch/epstruct.h +1 -0
- freedos_micro_python/port/base64_uc386dos.c +164 -0
- freedos_micro_python/port/file_uc386dos.c +228 -0
- freedos_micro_python/port/lib/axtls/crypto/crypto.h +45 -0
- freedos_micro_python/port/lwip-arch-cc.h +46 -0
- freedos_micro_python/port/lwip_uc386dos.c +248 -0
- freedos_micro_python/port/lwipopts.h +117 -0
- freedos_micro_python/port/math_gamma.c +63 -0
- freedos_micro_python/port/modtime_uc386dos.c +60 -0
- freedos_micro_python/port/modtls_axtls_uc386dos.c +461 -0
- freedos_micro_python/port/mpconfigport.h +358 -0
- freedos_micro_python/port/mphal_uc386dos.c +103 -0
- freedos_micro_python/port/mphalport.h +11 -0
- freedos_micro_python/port/os_uc386dos.c +264 -0
- freedos_micro_python/port/path_uc386dos.c +307 -0
- freedos_micro_python/port/pktdrv_uc386dos.c +650 -0
- freedos_micro_python/port/qstrdefsport.h +2 -0
- freedos_micro_python/port/shutil_uc386dos.c +111 -0
- freedos_micro_python/port/tempfile_uc386dos.c +129 -0
- freedos_micro_python/port/time_real_uc386dos.c +77 -0
- freedos_micro_python/port/uc386_net_uc386dos.c +126 -0
- freedos_micro_python/port/urllib_parse_uc386dos.c +360 -0
- freedos_micro_python/port/urllib_uc386dos.c +29 -0
- freedos_micro_python/scripts/build.sh +641 -0
- freedos_micro_python/scripts/build_port.sh +241 -0
- freedos_micro_python/scripts/fetch.sh +238 -0
- freedos_micro_python-0.1.0.dist-info/METADATA +131 -0
- freedos_micro_python-0.1.0.dist-info/RECORD +37 -0
- freedos_micro_python-0.1.0.dist-info/WHEEL +5 -0
- freedos_micro_python-0.1.0.dist-info/entry_points.txt +2 -0
- freedos_micro_python-0.1.0.dist-info/licenses/LICENSE +25 -0
- 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
|
+
};
|