1 /*
2 This file is Copyright © 1994-1995 Olivier Montanuy,
3 Copyright © 1999-2005 André Majorel,
4 Copyright © 2006-2019 contributors to the DeuTex project.
5
6 DeuTex incorporates code derived from DEU 5.21 that was put in the
7 public domain in 1994 by Raphaël Quinet and Brendon Wyber.
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 /*
13 ** This code should contain all the tricky O/S related
14 ** functions. If you're porting DeuTex, look here!
15 */
16
17 #include "deutex.h"
18 #include <errno.h>
19 #include <stdarg.h>
20 #include "tools.h"
21
22 #include <memory.h>
23 #include <utime.h>
24
25 #include <time.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #ifdef _WIN32
31 #define WIN32_LEAN_AND_MEAN
32 #include <windows.h>
33 #endif
34
35 static const char hex_digit[16] = "0123456789ABCDEF";
36
37 /*
38 ** Get a file time stamp. (date of last modification)
39 */
Get_File_Time(const char * path)40 int32_t Get_File_Time(const char *path)
41 {
42 int32_t time;
43 struct stat statbuf;
44 stat(path, &statbuf);
45 time = statbuf.st_ctime;
46 return time;
47 }
48
49 /*
50 ** Set a file time stamp.
51 */
Set_File_Time(const char * path,int32_t time)52 void Set_File_Time(const char *path, int32_t time)
53 {
54 struct utimbuf stime;
55 stime.modtime = stime.actime = time;
56 utime(path, &stime);
57 }
58
59 /*
60 ** Copy memory
61 */
Memcpy(void * dest,const void * src,long n)62 void Memcpy(void *dest, const void *src, long n)
63 {
64 if (n < 0)
65 Bug("MM21", "MovInf"); /*move inf to zero */
66 if (n == 0)
67 return;
68 memcpy((char *) dest, (char *) src, (size_t) n);
69 }
70
71 /*
72 ** Set memory
73 */
Memset(void * dest,char car,long n)74 void Memset(void *dest, char car, long n)
75 {
76 if (n < 0)
77 Bug("MM11", "MStInf"); /*set inf to zero */
78 if (n == 0)
79 return;
80 memset(dest, car, (size_t) n);
81 }
82
83 /*
84 ** Allocate memory
85 */
86 /* actually, this is (size - 1) */
Malloc(long size)87 void *Malloc(long size)
88 {
89 void *ret;
90 if (size < 1) {
91 Warning("MM02", "Attempt to allocate %ld bytes", size);
92 size = 1;
93 }
94 if ((size_t) size != size)
95 ProgError("MM03",
96 "Tried to allocate %ld b but couldn't; use another compiler",
97 size);
98 ret = malloc((size_t) size);
99 if (ret == NULL)
100 ProgError("MM04", "Out of memory (needed %ld bytes)", size);
101 return ret;
102 }
103
104 /*
105 ** Reallocate memory
106 */
Realloc(void * old,long size)107 void *Realloc(void *old, long size)
108 {
109 void *ret;
110
111 if (size < 1) {
112 Warning("MM05", "Attempt to allocate %ld bytes", size);
113 size = 1;
114 }
115 if ((size_t) size != size)
116 ProgError("MM06",
117 "Tried to realloc %ld b but couldn't; use another compiler",
118 size);
119 ret = realloc(old, (size_t) size);
120 if (ret == NULL)
121 ProgError("MM07", "Out of memory (needed %ld bytes)", size);
122 return ret;
123 }
124
125 /*
126 ** Use only lower case file names
127 */
ToLowerCase(char * file)128 void ToLowerCase(char *file)
129 {
130 int16_t i;
131 for (i = 0; (i < 128) && (file[i] != '\0'); i++)
132 file[i] = tolower((((int16_t) file[i]) & 0xFF));
133 }
134
NameDir(char file[128],const char * path,const char * dir,const char * sdir)135 static void NameDir(char file[128], const char *path, const char *dir, const
136 char *sdir)
137 {
138 file[0] = '.';
139 file[1] = '\0';
140 if (path != NULL)
141 if (strlen(path) > 0) {
142 strncpy(file, path, 80);
143 }
144 if (dir != NULL)
145 if (strlen(dir) > 0) {
146 strcat(file, "/");
147 strncat(file, dir, 12);
148 }
149 if (sdir != NULL)
150 if (strlen(sdir) > 0) {
151 strcat(file, "/");
152 strncat(file, sdir, 12);
153 }
154 ToLowerCase(file);
155 }
156
157 /*
158 ** Create directory if it does not exists
159 */
MakeDir(char file[128],const char * path,const char * dir,const char * sdir)160 void MakeDir(char file[128], const char *path, const char *dir, const char
161 *sdir)
162 {
163 NameDir(file, path, dir, sdir);
164 #ifdef _WIN32
165 CreateDirectory(file, NULL);
166 #else
167 mkdir(file, (mode_t) 0777);
168 #endif
169 }
170
171 /*
172 ** Create a file name, by concatenation
173 ** returns true if file exists false otherwise
174 */
MakeFileName(char file[128],const char * path,const char * dir,const char * sdir,const char * name,const char * extens)175 bool MakeFileName(char file[128], const char *path, const char *dir, const
176 char *sdir, const char *name, const char *extens)
177 {
178 FILE *fp;
179 char name2[8]; /* AYM 1999-01-13: keep checker happy */
180 /* deal with VILE strange name
181 ** replace the VILE\
182 ** by VILE^
183 */
184 Normalise(name2, name);
185
186 /* Replace backslash as it is an illegal character on
187 Windows. */
188 switch (name2[4]) {
189 case '\\':
190 name2[4] = '^';
191 break;
192 }
193 switch (name2[6]) {
194 case '\\':
195 name2[6] = '^';
196 break;
197 }
198 NameDir(file, path, dir, sdir);
199 /*
200 ** file name
201 */
202 strcat(file, "/");
203 strncat(file, name2, 8);
204 strcat(file, ".");
205 strncat(file, extens, 4);
206 ToLowerCase(file);
207 /*
208 ** check if file exists
209 */
210 fp = fopen(file, FOPEN_RB);
211 if (fp != NULL) {
212 fclose(fp);
213 return true;
214 }
215 return false;
216 }
217
218 /*
219 ** Get the root name of a WAD file
220 */
GetNameOfWAD(char name[8],const char * path)221 void GetNameOfWAD(char name[8], const char *path)
222 {
223 int16_t n, nam, len;
224 len = (int16_t) strlen(path);
225 /*find end of DOS or Unix path */
226 for (nam = n = 0; n < len; n++)
227 switch (path[n]) {
228 #ifdef _WIN32
229 case '\\':
230 #endif
231 case '/':
232 nam = n + 1;
233 }
234 /*find root name */
235 /* FIXME AYM 1999-06-09: Do we really have to truncate to 8 ? */
236 for (n = 0; n < 8; n++) {
237 switch (path[nam + n]) {
238 case '.':
239 case '\0':
240 case '\n':
241 name[n] = '\0';
242 return;
243 default:
244 name[n] = toupper(path[nam + n]);
245 break;
246 }
247 }
248 return;
249 }
250
251 /*****************************************************/
252
253 /* convert 8 byte string to upper case, 0 padded*/
Normalise(char dest[8],const char * src)254 void Normalise(char dest[8], const char *src)
255 { /*strupr */
256 int16_t n;
257 bool pad = false;
258 char c = 'A';
259 for (n = 0; n < 8; n++) {
260 c = pad ? '\0' : src[n];
261 if (c == '\0')
262 pad = true;
263 else
264 c = (isprint(c)) ? toupper(c) : '*';
265 dest[n] = c;
266 }
267 }
268
269 /*
270 ** Output auxilliary functions
271 */
272
273 /*
274 * fnameofs - return string containing file name and offset
275 *
276 * Not reentrant (returns pointer on static buffer).
277 * FIXME: should encode non-printable characters.
278 * FIXME: should have shortening heuristic (E.G. print only basename).
279 */
fnameofs(const char * name,long ofs)280 char *fnameofs(const char *name, long ofs)
281 {
282 static char buf[81];
283 *buf = '\0';
284 strncat(buf, name, sizeof buf - 12);
285 sprintf(buf + strlen(buf), "(%06lXh)", ofs);
286 return buf;
287 }
288
289 /*
290 * fname - return string containing file name
291 *
292 * Not reentrant (returns pointer on static buffer).
293 * FIXME: should encode non-printable characters.
294 * FIXME: should have shortening heuristic (E.G. print only basename).
295 */
fname(const char * name)296 char *fname(const char *name)
297 {
298 static char buf[81];
299 *buf = '\0';
300 strncat(buf, name, sizeof buf - 1);
301 return buf;
302 }
303
304 /*
305 * lump_name - return string containing lump name
306 *
307 * Partially reentrant (returns pointer on one of two
308 * static buffers). The string is guaranteed to have at
309 * most 32 characters and to contain only graphic
310 * characters.
311 */
lump_name(const char * name)312 char *lump_name(const char *name)
313 {
314 static char buf1[9];
315 static char buf2[9];
316 static int buf_toggle = 0;
317 const char *const name_end = name + 8;
318 char *buf = buf_toggle ? buf2 : buf1;
319 char *p = buf;
320
321 buf_toggle = !buf_toggle;
322 if (*name == '\0')
323 strcpy(buf, "(empty)");
324 else {
325 for (; *name != '\0' && name < name_end; name++) {
326 if (isgraph((unsigned char) *name))
327 *p++ = toupper((unsigned char) *name);
328 else {
329 *p++ = '\\';
330 *p++ = 'x';
331 *p++ = ((unsigned char) *name) >> 4;
332 *p++ = *name & 0x0f;
333 }
334 }
335 *p = '\0';
336 }
337 return buf;
338 }
339
340 /*
341 * short_dump - return string containing hex dump of buffer
342 *
343 * Not reentrant (returns pointer on static buffer). Length
344 * is silently limited to 16 bytes.
345 */
short_dump(const char * data,size_t size)346 char *short_dump(const char *data, size_t size)
347 {
348 #define MAX_BYTES 16
349 static char buf[3 * MAX_BYTES];
350 char *b = buf;
351 size_t n;
352
353 for (n = 0; n < size && n < MAX_BYTES; n++) {
354 if (n > 0)
355 *b++ = ' ';
356 *b++ = hex_digit[((unsigned char) data[n]) >> 4];
357 *b++ = hex_digit[((unsigned char) data[n]) & 0x0f];
358 }
359 *b++ = '\0';
360 return buf;
361 }
362
363 /*
364 * quotechar - return the safe representation of a char
365 *
366 * Not reentrant (returns pointer on static buffer). The string is
367 * guaranteed to be exactly three characters long and not contain
368 * any control or non-ASCII characters.
369 */
quotechar(char c)370 const char *quotechar(char c)
371 {
372 static char buf[4];
373
374 if (c >= 32 && c <= 126) {
375 buf[0] = '"';
376 buf[1] = c;
377 buf[2] = '"';
378 buf[3] = '\0';
379 } else {
380 buf[0] = hex_digit[((unsigned char) c) >> 4];
381 buf[1] = hex_digit[((unsigned char) c) & 0x0f];
382 buf[2] = 'h';
383 buf[3] = '\0';
384 }
385 return buf;
386 }
387
388 /*
389 ** Output and Error handling
390 */
391 static bool asFile = false;
392 static int16_t Verbosity = 2;
393
PrintVerbosity(int16_t level)394 void PrintVerbosity(int16_t level)
395 {
396 Verbosity = (level < 0) ? 0 : (level > 4) ? 4 : level;
397 }
398
PrintExit(void)399 void PrintExit(void)
400 {
401 if (asFile) {
402 fclose(stdout);
403 fclose(stderr);
404 }
405 }
406
ActionDummy(void)407 void ActionDummy(void)
408 {
409 return;
410 }
411
412 static void (*Action) (void) = ActionDummy;
ProgErrorCancel(void)413 void ProgErrorCancel(void)
414 {
415 Action = ActionDummy;
416 }
417
ProgErrorAction(void (* action)(void))418 void ProgErrorAction(void (*action) (void))
419 {
420 Action = action;
421 }
422
ProgError(const char * code,const char * fmt,...)423 void ProgError(const char *code, const char *fmt, ...)
424 {
425 va_list args;
426
427 fflush(stdout);
428 fprintf(stderr, "E %s ", code);
429 va_start(args, fmt);
430 vfprintf(stderr, fmt, args);
431 va_end(args);
432 va_start(args, fmt);
433 va_end(args);
434 fputc('\n', stderr);
435 (*Action) (); /* execute error handler */
436 PrintExit();
437 exit(2);
438 }
439
440 /*
441 * nf_err - non fatal error message
442 */
nf_err(const char * code,const char * fmt,...)443 void nf_err(const char *code, const char *fmt, ...)
444 {
445 va_list args;
446
447 fflush(stdout);
448 fprintf(stderr, "%c %s ", MSGCLASS_ERR, code);
449 va_start(args, fmt);
450 vfprintf(stderr, fmt, args);
451 va_end(args);
452 va_start(args, fmt);
453 va_end(args);
454 fputc('\n', stderr);
455 fflush(stderr);
456 }
457
Bug(const char * code,const char * fmt,...)458 void Bug(const char *code, const char *fmt, ...)
459 {
460 va_list args;
461
462 fflush(stdout);
463 fprintf(stderr, "%c %s ", MSGCLASS_BUG, code);
464 va_start(args, fmt);
465 vfprintf(stderr, fmt, args);
466 va_end(args);
467 va_start(args, fmt);
468 va_end(args);
469 fputc('\n', stderr);
470 fputs("Please report that bug\n", stderr);
471 /* CloseWadFiles(); */
472 PrintExit();
473 exit(3);
474 }
475
Warning(const char * code,const char * fmt,...)476 void Warning(const char *code, const char *fmt, ...)
477 {
478 va_list args;
479
480 fflush(stdout);
481 fprintf(stderr, "%c %s ", MSGCLASS_WARN, code);
482 va_start(args, fmt);
483 vfprintf(stderr, fmt, args);
484 va_end(args);
485 va_start(args, fmt);
486 va_end(args);
487 fputc('\n', stderr);
488 }
489
LimitedWarn(int * left,const char * code,const char * fmt,...)490 void LimitedWarn(int *left, const char *code, const char *fmt, ...)
491 {
492 if (left == NULL || (left != NULL && *left > 0)) {
493 va_list args;
494
495 fflush(stdout);
496 fprintf(stderr, "%c %s ", MSGCLASS_WARN, code);
497 va_start(args, fmt);
498 vfprintf(stderr, fmt, args);
499 va_end(args);
500 va_start(args, fmt);
501 va_end(args);
502 fputc('\n', stderr);
503 }
504 if (left != NULL)
505 (*left)--;
506 }
507
LimitedEpilog(int * left,const char * code,const char * fmt,...)508 void LimitedEpilog(int *left, const char *code, const char *fmt, ...)
509 {
510 if (left != NULL && *left < 0) {
511 fflush(stdout);
512 if (fmt != NULL) {
513 va_list args;
514 fprintf(stderr, "%c %s ", MSGCLASS_WARN, code);
515 va_start(args, fmt);
516 vfprintf(stderr, fmt, args);
517 va_end(args);
518 va_start(args, fmt);
519 va_end(args);
520 }
521 fprintf(stderr, "%d warnings omitted\n", -*left);
522 }
523 }
524
Output(const char * fmt,...)525 void Output(const char *fmt, ...)
526 {
527 va_list args;
528
529 va_start(args, fmt);
530 vfprintf(stdout, fmt, args);
531 va_end(args);
532 va_start(args, fmt);
533 va_end(args);
534 }
535
Info(const char * code,const char * fmt,...)536 void Info(const char *code, const char *fmt, ...)
537 {
538 if (Verbosity >= 1) {
539 va_list args;
540
541 fprintf(stdout, "%c %s ", MSGCLASS_INFO, code);
542 va_start(args, fmt);
543 vfprintf(stdout, fmt, args);
544 va_end(args);
545 va_start(args, fmt);
546 va_end(args);
547 fputc('\n', stdout);
548 }
549 }
550
Phase(const char * code,const char * fmt,...)551 void Phase(const char *code, const char *fmt, ...)
552 {
553 if (Verbosity >= 2) {
554 va_list args;
555
556 fprintf(stdout, "%c %s ", MSGCLASS_INFO, code);
557 va_start(args, fmt);
558 vfprintf(stdout, fmt, args);
559 va_end(args);
560 va_start(args, fmt);
561 va_end(args);
562 fputc('\n', stdout);
563 }
564 }
565
Detail(const char * code,const char * fmt,...)566 void Detail(const char *code, const char *fmt, ...)
567 {
568 if (Verbosity >= 3) {
569 va_list args;
570
571 fprintf(stdout, "%c %s ", MSGCLASS_INFO, code);
572 va_start(args, fmt);
573 vfprintf(stdout, fmt, args);
574 va_end(args);
575 va_start(args, fmt);
576 va_end(args);
577 fputc('\n', stdout);
578 }
579 }
580