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
19static unit_t *units;
20static int unit_count = 0;
21
22/**
23 * initialization
24 */
25void unit_mgr_init() {
26 unit_count = 0;
27 units = NULL;
28}
29
30/**
31 * close up
32 */
33void 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 */
55int 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 */
87int 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 */
102int 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 */
199int 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 */
221int 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 */
256int 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