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