cfad47cfa3/t3compiler/src/osportable3.cc

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* This file implements some of the functions described in
2
 * tads2/osifc.h.  Only functions needed by the TADS 3 compiler are
3
 * implemented here.
4
 *
5
 * The functions are "portable"; they don't make use of curses/ncurses.
6
 */
7
#include "common.h"
8
9
#include <stdio.h>
10
#include <string.h>
11
extern "C" {
12
#include <sys/stat.h>
13
#include <dirent.h>
14
}
15
16
#ifdef HAVE_GLOB_H
17
#include <glob.h>
18
#endif
19
20
#include "os.h"
21
22
23
/* Set the game title.
24
 *
25
 * Does nothing in the compiler.  This should actually be implemented in
26
 * tads3/os_stdio.cpp, but for some reason it's not.
27
 */
28
void
29
os_set_title( const char* )
30
{
31
}
32
33
34
/* Get the modification time for a file.
35
 */
36
int
37
os_get_file_mod_time( os_file_time_t* t, const char* fname )
38
{
39
	struct stat inf;
40
41
	if (stat(fname, &inf) != 0) return 1;
42
	t->t = inf.st_mtime;
43
	return 0;
44
}
45
46
47
/* Compare two file time values.
48
 */
49
int
50
os_cmp_file_times( const os_file_time_t* a, const os_file_time_t* b )
51
{
52
	if (a->t < b->t) return -1;
53
	if (a->t == b->t) return 0;
54
	return 1;
55
}
56
57
58
/* Install/uninstall the break handler.
59
 *
60
 * I don't think we need a break handler.
61
 */
62
void
63
os_instbrk( int )
64
{
65
}
66
67
68
/* We implement the file searching routines using <glob.h>.  It's not
69
 * available on all systems though, so we provide dummy fallbacks that
70
 * do nothing if <glob.h> doesn't exist.
71
 */
72
#ifdef HAVE_GLOB
73
/* Search context structure.
74
 */
75
struct oss_find_ctx_t
76
{
77
	glob_t* pglob;
78
	// Index of current filename in pglob->gl_pathv.
79
	size_t current;
80
	// Original search path prefix (we'll allocate more to fit the
81
	// string).
82
	char path[1];
83
};
84
85
86
/* Service routine for searching - build the full output path name.
87
 */
88
static void
89
oss_build_outpathbuf( char* outpathbuf, size_t outpathbufsiz, const char* path,
90
                      const char* fname )
91
{
92
	// If there's a full path buffer, build the full path.
93
	if (outpathbuf != 0) {
94
		size_t lp;
95
		size_t lf;
96
97
		// Copy the path prefix.
98
		lp = strlen(path);
99
		if (lp > outpathbufsiz - 1) {
100
			lp = outpathbufsiz - 1;
101
		}
102
		memcpy(outpathbuf, path, lp);
103
104
		// Add the filename if there's any room.
105
		lf = strlen(fname);
106
		if (lf > outpathbufsiz - lp - 1) {
107
			lf = outpathbufsiz - lp - 1;
108
		}
109
		memcpy(outpathbuf + lp, fname, lf);
110
111
		// Null-terminate the result.
112
		outpathbuf[lp + lf] = '\0';
113
	}
114
}
115
116
117
/* Find the first file matching a given pattern.
118
 */
119
void*
120
os_find_first_file( const char* dir, const char* pattern, char* outbuf, size_t outbufsiz,
121
                    int* isdir, char* outpathbuf, size_t outpathbufsiz)
122
{
123
	char realpat[OSFNMAX];
124
	size_t l;
125
	size_t path_len;
126
	const char* lastsep;
127
	const char* p;
128
	struct oss_find_ctx_t* ctx = 0;
129
130
	strcpy(realpat, dir);
131
	if ((l = strlen(realpat)) != 0 && realpat[l - 1] != '/') {
132
		realpat[l++] = '/';
133
	}
134
135
	if (pattern == 0) {
136
		strcpy(realpat + l, "*");
137
	} else {
138
		strcpy(realpat + l, pattern);
139
	}
140
141
	// Find the last separator in the original path.
142
	for (p = realpat, lastsep = 0 ; *p != '\0' ; ++p) {
143
		// If this is a separator, remember it.
144
		if (*p == '/') lastsep = p;
145
	}
146
147
	// If we found a separator, the path prefix is everything up to
148
	// and including the separator; otherwise, there's no path
149
	// prefix.
150
	if (lastsep != 0) {
151
		// The length of the path includes the separator.
152
		path_len = lastsep + 1 - realpat;
153
	} else {
154
		// There's no path prefix - everything is in the
155
		// current directory.
156
		path_len = 0;
157
	}
158
159
	// Allocate a context.
160
	ctx = static_cast(struct oss_find_ctx_t*)(malloc(sizeof(struct oss_find_ctx_t) + path_len));
161
	ctx->pglob = static_cast(glob_t *)(malloc(sizeof(glob_t)));
162
	ctx->current = -1;
163
164
	// Copy the path to the context. we may need it later to build
165
	// a full path.
166
	memcpy(ctx->path, realpat, path_len);
167
	ctx->path[path_len] = '\0';
168
169
	// Problem: before calling glob() we need to escape all [] in
170
	// realpat.  Count them first.
171
	int nbracket = 0;
172
	for (p = realpat; *p != '\0' ; ++p) {
173
		if (*p == '[' || *p == ']') ++nbracket;
174
	}
175
176
	// Allocate string for escaped pattern.
177
	char* escaped_realpat = static_cast(char*)(malloc(strlen(realpat) + nbracket + 1));
178
179
	// Copy pattern to new string, escaping all [ and ] with \.
180
	char* c = realpat;
181
	char* k = escaped_realpat;
182
	while (*c != '\0') {
183
		if (*c == '[' || *c == ']') {
184
			*k++ = '\\';
185
		}
186
		*k++ = *c++;
187
	}
188
	*k++ = '\0';
189
190
	int res = glob(escaped_realpat, GLOB_ERR | GLOB_MARK, NULL, ctx->pglob);
191
	if (res != 0) {
192
		// We don't really care what kind of error occured.
193
		// Just free the resources and return error. Note that
194
		// "no files found" is an error.
195
		if (ctx->pglob != 0) {
196
			globfree(ctx->pglob);
197
			free(ctx->pglob);
198
		}
199
		if (ctx != 0) {
200
			free(ctx);
201
		}
202
		free(escaped_realpat);
203
		*isdir = 0;
204
		return 0;
205
	}
206
207
	// If we get there, there was a match. set current index for
208
	// os_find_next().
209
	ctx->current = 0;
210
211
	// glob() returns filename with directory, but we need just the
212
	// basename. Copy first filename into the caller's buffer.
213
	char* ptr = &ctx->pglob->gl_pathv[0][path_len];
214
	l = strlen(ptr);
215
216
	if (l > outbufsiz - 1) {
217
		l = outbufsiz - 1;
218
	}
219
	memcpy(outbuf, ptr, l);
220
	outbuf[l] = '\0';
221
222
	// Build the full path, if desired.
223
	oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ptr);
224
225
	// Return the directory indication.
226
	*isdir = (ptr[l-1] == '/') ? true : false;
227
228
	free(escaped_realpat);
229
	return ctx;
230
}
231
232
/* Find the next matching file, continuing a search started with
233
 * os_find_first_file().
234
 */
235
void*
236
os_find_next_file( void* ctx0, char* outbuf, size_t outbufsiz, int* isdir, char* outpathbuf,
237
                   size_t outpathbufsiz )
238
{
239
	struct oss_find_ctx_t* ctx = static_cast(struct oss_find_ctx_t*)(ctx0);
240
	size_t l;
241
242
	// If the search has already ended, do nothing.
243
	if (ctx == 0) return 0;
244
245
	if (++ctx->current > ctx->pglob->gl_pathc - 1) {
246
		// No more files - delete the context and give up.
247
		globfree(ctx->pglob);
248
		free(ctx->pglob);
249
		free(ctx);
250
		// Indicate that the search is finished.
251
		return 0;
252
	}
253
254
	// Return the name. See comments in os_find_first_file().
255
	l = strlen(ctx->path);
256
	char* ptr = &ctx->pglob->gl_pathv[ctx->current][l];
257
258
	l = strlen(ptr);
259
260
	if (l > outbufsiz - 1) {
261
		l = outbufsiz - 1;
262
	}
263
	memcpy(outbuf, ptr, l);
264
	outbuf[l] = '\0';
265
266
	// Return the directory indication.
267
	*isdir = (ptr[l-1] == '/') ? true : false;
268
269
	// Build the full path, if desired.
270
	oss_build_outpathbuf(outpathbuf, outpathbufsiz, ctx->path, ptr);
271
272
	// Indicate that the search was successful by returning the
273
	// non-null context pointer -- the context has been updated for
274
	// the new position in the search, so we just return the same
275
	// context structure that we've been using all along.
276
	return ctx;
277
}
278
279
/* Cancel a search.
280
 */
281
void
282
os_find_close( void* ctx0 )
283
{
284
	struct oss_find_ctx_t* ctx = static_cast(struct oss_find_ctx_t*)(ctx0);
285
286
	// If the search was already cancelled, do nothing.
287
	if (ctx == 0) return;
288
289
	// Delete the search context.
290
	if (ctx->pglob != 0) {
291
		globfree(ctx->pglob);
292
		free(ctx->pglob);
293
	}
294
	free(ctx);
295
}
296
297
#else
298
299
/* <glob.h> isn't available.  We just provide do-nothing dummies.
300
 */
301
void*
302
os_find_first_file( const char* /*dir*/, const char* /*pattern*/, char* /*outbuf*/, size_t /*outbufsiz*/,
303
                    int* /*isdir*/, char* /*outpathbuf*/, size_t /*outpathbufsiz*/ )
304
{
305
	return 0;
306
}
307
308
309
void*
310
os_find_next_file( void* /*ctx*/, char* /*outbuf*/, size_t /*outbufsiz*/, int* /*isdir*/,
311
                   char* /*outpathbuf*/, size_t /*outpathbufsiz*/ )
312
{
313
	return 0;
314
}
315
316
317
void
318
os_find_close( void* /*ctx*/ )
319
{
320
}
321
#endif // HAVE_GLOB
322
323
324
/* Determine if the given filename refers to a special file.
325
 *
326
 * tads2/osnoui.c defines its own version when MSDOS is defined.
327
 */
328
#ifndef MSDOS
329
os_specfile_t
330
os_is_special_file( const char* fname )
331
{
332
	// We also check for "./" and "../" instead of just "." and
333
	// "..".  (We use OSPATHCHAR instead of '/' though.)
334
	const char selfWithSep[3] = {'.', OSPATHCHAR, '\0'};
335
	const char parentWithSep[4] = {'.', '.', OSPATHCHAR, '\0'};
336
	if ((strcmp(fname, ".") == 0) or (strcmp(fname, selfWithSep) == 0)) return OS_SPECFILE_SELF;
337
	if ((strcmp(fname, "..") == 0) or (strcmp(fname, parentWithSep) == 0)) return OS_SPECFILE_PARENT;
338
	return OS_SPECFILE_NONE;
339
}
340
#endif
341
342
343
/* Initialize.
344
 */
345
int
346
os_init( int*, char**, const char*, char*, int )
347
{
348
	return 0;
349
}
350
351
352
/* Uninitialize.
353
 */
354
void
355
os_uninit( void )
356
{
357
}
358
359
360
/* =====================================================================
361
 * These functions are currently not used by the compiler.  If the
362
 * linker barks, we must implement them.
363
 */
364
365
/* Get the creation time for a file.
366
 */
367
/*
368
int
369
os_get_file_cre_time( os_file_time_t* t, const char* fname )
370
{
371
}
372
*/
373
374
/* Get the access time for a file.
375
 */
376
/*
377
int
378
os_get_file_acc_time( os_file_time_t* t, const char* fname )
379
{
380
}
381
*/
382
383
/* Switch to a new working directory.
384
 */
385
/*
386
oid
387
os_set_pwd( const char* dir )
388
{
389
}
390
*/
391
392
/* Switch the working directory to the directory containing the given
393
 * file.
394
 */
395
/*
396
void
397
os_set_pwd_file( const char* filename )
398
{
399
}
400
*/