1 | // This file is part of SmallBASIC |
2 | // |
3 | // SmallBASIC Unit (SB units) manager |
4 | // |
5 | // This program is distributed under the terms of the GPL v2.0 or later |
6 | // Download the GNU Public License (GPL) from www.gnu.org |
7 | // |
8 | // Copyright(C) 2000 Nicholas Christopoulos |
9 | |
10 | #include "common/sys.h" |
11 | #include "common/kw.h" |
12 | #include "common/var.h" |
13 | #include "common/device.h" |
14 | #include "common/pproc.h" |
15 | #include "common/scan.h" |
16 | #include "common/units.h" |
17 | |
18 | // units table |
19 | static unit_t *units; |
20 | static int unit_count = 0; |
21 | |
22 | /** |
23 | * initialization |
24 | */ |
25 | void unit_mgr_init() { |
26 | unit_count = 0; |
27 | units = NULL; |
28 | } |
29 | |
30 | /** |
31 | * close up |
32 | */ |
33 | void unit_mgr_close() { |
34 | int i; |
35 | |
36 | for (i = 0; i < unit_count; i++) { |
37 | if (units[i].status == unit_loaded) { |
38 | close_unit(i); |
39 | } |
40 | } |
41 | unit_count = 0; |
42 | if (units) { |
43 | free(units); |
44 | units = NULL; |
45 | } |
46 | } |
47 | |
48 | /** |
49 | * returns the full-pathname of unit |
50 | * |
51 | * @param name unit's name |
52 | * @param file buffer to store the filename |
53 | * @return non-zero on success |
54 | */ |
55 | int find_unit_path(const char *name, char *file) { |
56 | strcpy(file, name); |
57 | strcat(file, ".bas" ); |
58 | |
59 | // find in unitpath |
60 | if (getenv("SBASICPATH" )) { |
61 | if (sys_search_path(getenv("SBASICPATH" ), file, file)) { |
62 | return 1; |
63 | } |
64 | } |
65 | |
66 | // find in program launch directory |
67 | if (gsb_bas_dir[0] && sys_search_path(gsb_bas_dir, file, file)) { |
68 | return 1; |
69 | } |
70 | |
71 | // find in current directory |
72 | if (sys_search_path("." , file, file)) { |
73 | return 1; |
74 | } |
75 | |
76 | return 0; |
77 | } |
78 | |
79 | /** |
80 | * returns the .sbu file name with the same path as the |
81 | * corresponding .bas file. |
82 | * |
83 | * @param name unit's name |
84 | * @param file buffer to store the filename |
85 | * @return non-zero on success |
86 | */ |
87 | int find_unit(const char *name, char *file) { |
88 | if (find_unit_path(name, file)) { |
89 | file[strlen(file) - 4] = 0; |
90 | strcat(file, ".sbu" ); |
91 | return 1; |
92 | } |
93 | return 0; |
94 | } |
95 | |
96 | /** |
97 | * open unit |
98 | * |
99 | * @param file is the filename |
100 | * @return the unit handle or -1 on error |
101 | */ |
102 | int open_unit(const char *file, const char *alias) { |
103 | int h; |
104 | unit_t u; |
105 | int uid = -1; |
106 | |
107 | char unitname[OS_PATHNAME_SIZE]; |
108 | char bas_file[OS_PATHNAME_SIZE]; |
109 | time_t ut, st; |
110 | int comp_rq = 0; |
111 | |
112 | // clean structure please |
113 | memset(&u, 0, sizeof(unit_t)); |
114 | |
115 | // find unit's source file name |
116 | if (!find_unit_path(file, bas_file)) { |
117 | return -1; |
118 | } |
119 | |
120 | // create corresponding sbu path version |
121 | strcpy(unitname, bas_file); |
122 | |
123 | if (strcmp(comp_file_name, bas_file) == 0) { |
124 | // unit and program are the same |
125 | return -1; |
126 | } |
127 | |
128 | unitname[strlen(bas_file) - 4] = 0; |
129 | strcat(unitname, ".sbu" ); |
130 | |
131 | if ((ut = sys_filetime(unitname)) == 0L) { |
132 | // binary not found - compile |
133 | comp_rq = 1; |
134 | } else { |
135 | if ((st = sys_filetime(bas_file))) { |
136 | // source found |
137 | if (ut < st) { |
138 | // executable is older than source - compile |
139 | comp_rq = 1; |
140 | } |
141 | } |
142 | } |
143 | |
144 | // compilation required |
145 | if (comp_rq && !comp_compile(bas_file)) { |
146 | return -1; |
147 | } |
148 | |
149 | // open unit |
150 | h = open(unitname, O_RDWR | O_BINARY); |
151 | if (h == -1) { |
152 | return -1; |
153 | } |
154 | |
155 | // read file header |
156 | int nread = read(h, &u.hdr, sizeof(unit_file_t)); |
157 | if (nread != sizeof(unit_file_t) || |
158 | u.hdr.version != SB_DWORD_VER || |
159 | memcmp(&u.hdr.sign, "SBUn" , 4) != 0) { |
160 | close(h); |
161 | return -1; |
162 | } |
163 | |
164 | // load symbol-table |
165 | if (u.hdr.sym_count) { |
166 | u.symbols = (unit_sym_t *)malloc(u.hdr.sym_count * sizeof(unit_sym_t)); |
167 | read(h, u.symbols, u.hdr.sym_count * sizeof(unit_sym_t)); |
168 | } |
169 | |
170 | // setup the rest |
171 | strcpy(u.name, unitname); |
172 | strcpy(u.hdr.base, alias); |
173 | u.status = unit_loaded; |
174 | |
175 | // add unit |
176 | uid = unit_count; |
177 | unit_count++; |
178 | |
179 | if (units == NULL) { |
180 | units = (unit_t *)malloc(unit_count * sizeof(unit_t)); |
181 | } else { |
182 | units = (unit_t *)realloc(units, unit_count * sizeof(unit_t)); |
183 | } |
184 | |
185 | // copy unit's data |
186 | memcpy(&units[uid], &u, sizeof(unit_t)); |
187 | |
188 | // cleanup |
189 | close(h); |
190 | return uid; |
191 | } |
192 | |
193 | /** |
194 | * closes a unit |
195 | * |
196 | * @param uid is the unit's handle |
197 | * @return 0 on success |
198 | */ |
199 | int close_unit(int uid) { |
200 | if (uid >= 0) { |
201 | unit_t *u = &units[uid]; |
202 | if (u->status == unit_loaded) { |
203 | u->status = unit_undefined; |
204 | free(u->symbols); |
205 | } else { |
206 | return -2; |
207 | } |
208 | } else { |
209 | return -1; |
210 | } |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | /** |
216 | * imports unit's names |
217 | * |
218 | * @param uid unit's handle |
219 | * @return 0 on success |
220 | */ |
221 | int import_unit(int uid) { |
222 | if (uid >= 0) { |
223 | unit_t *u = &units[uid]; |
224 | if (u->status == unit_loaded) { |
225 | for (int i = 0; i < u->hdr.sym_count; i++) { |
226 | // build the name with any path component removed from the name |
227 | char buf[SB_KEYWORD_SIZE + SB_KEYWORD_SIZE + 1]; |
228 | char *dir_sep = strrchr(u->hdr.base, OS_DIRSEP); |
229 | sprintf(buf, "%s.%s" , |
230 | dir_sep ? dir_sep + 1 : u->hdr.base, u->symbols[i].symbol); |
231 | switch (u->symbols[i].type) { |
232 | case stt_function: |
233 | comp_add_external_func(buf, uid | UID_UNIT_BIT); |
234 | break; |
235 | case stt_procedure: |
236 | comp_add_external_proc(buf, uid | UID_UNIT_BIT); |
237 | break; |
238 | case stt_variable: |
239 | comp_add_external_var(buf, uid | UID_UNIT_BIT); |
240 | break; |
241 | }; |
242 | } |
243 | } else { |
244 | return -2; |
245 | } |
246 | } else { |
247 | return -1; |
248 | } |
249 | |
250 | return 0; |
251 | } |
252 | |
253 | /** |
254 | * execute a call to a unit |
255 | */ |
256 | int unit_exec(int lib_id, int index, var_t *ret) { |
257 | unit_sym_t *us; // unit's symbol data |
258 | bc_symbol_rec_t *ps; // program's symbol data |
259 | int my_tid; |
260 | stknode_t udf_rv; |
261 | |
262 | my_tid = ctask->tid; |
263 | ps = &prog_symtable[index]; |
264 | us = &(taskinfo(ps->task_id)->sbe.exec.exptable[ps->exp_idx]); |
265 | |
266 | switch (ps->type) { |
267 | case stt_variable: |
268 | break; |
269 | case stt_procedure: |
270 | exec_sync_variables(1); |
271 | cmd_call_unit_udp(kwPROC, ps->task_id, us->address, INVALID_ADDR); |
272 | activate_task(ps->task_id); |
273 | if (prog_error) { |
274 | gsb_last_error = prog_error; |
275 | taskinfo(my_tid)->error = gsb_last_error; |
276 | return 0; |
277 | } |
278 | bc_loop(2); |
279 | if (prog_error) { |
280 | gsb_last_error = prog_error; |
281 | taskinfo(my_tid)->error = gsb_last_error; |
282 | return 0; |
283 | } |
284 | activate_task(my_tid); |
285 | exec_sync_variables(0); |
286 | break; |
287 | |
288 | case stt_function: |
289 | exec_sync_variables(1); |
290 | cmd_call_unit_udp(kwFUNC, ps->task_id, us->address, us->vid); |
291 | activate_task(ps->task_id); |
292 | if (prog_error) { |
293 | gsb_last_error = prog_error; |
294 | taskinfo(my_tid)->error = gsb_last_error; |
295 | return 0; |
296 | } |
297 | bc_loop(2); |
298 | if (prog_error) { |
299 | gsb_last_error = prog_error; |
300 | taskinfo(my_tid)->error = gsb_last_error; |
301 | return 0; |
302 | } |
303 | // get last variable from stack |
304 | code_pop(&udf_rv, kwTYPE_RET); |
305 | if (udf_rv.type != kwTYPE_RET) { |
306 | err_stackmess(); |
307 | } else { |
308 | v_set(ret, udf_rv.x.vdvar.vptr); |
309 | v_free(udf_rv.x.vdvar.vptr); // free ret-var |
310 | v_detach(udf_rv.x.vdvar.vptr); |
311 | } |
312 | |
313 | activate_task(my_tid); |
314 | exec_sync_variables(0); |
315 | break; |
316 | }; |
317 | |
318 | return (prog_error == 0); |
319 | } |
320 | |