1 /*
2  * ============================================================================
3  *  Title:    Platform Portability "Library"
4  *  Author:   J. Zbiciak, T. Lindner
5  * ============================================================================
6  *  This module fills in missing features on various platforms.
7  * ============================================================================
8  *  GETTIMEOFDAY     -- Return current time in seconds/microseconds.
9  *  STRDUP           -- Copy a string into freshly malloc'd storage.
10  *  STRICMP          -- Case-insensitive string compare.
11  *  SNPRINTF         -- Like sprintf(), only with bounds checking.
12  *  PLAT_DELAY       -- Sleep w/ millisecond precision.
13  *  GET_EXE_DIR      -- Get the directory containing this executable
14  * ============================================================================
15  */
16 
17 #include "config.h"
18 #include "plat/plat_lib_config.h"
19 
20 #ifdef USE_TERMIO
21 # include <termio.h>
22 #endif
23 
24 #ifdef USE_SYS_IOCTL
25 # include <sys/ioctl.h>
26 #endif
27 
28 /* ======================================================================== */
29 /*  GENERIC PORTABLE VERSIONS...                                            */
30 /* ======================================================================== */
31 
32 /* ------------------------------------------------------------------------ */
33 /*  GET_TIME -- Return current time in seconds as a double.                 */
34 /* ------------------------------------------------------------------------ */
35 #if GET_TIME_STRATEGY == GTS_CLOCK_GETTIME
get_time(void)36 double get_time(void)
37 {
38     struct timespec now;
39     double seconds;
40 
41     clock_gettime(CLOCK_MONOTONIC, &now);
42 
43     seconds = (double)now.tv_sec + (double)now.tv_nsec * 1e-9;
44     return seconds;
45 }
46 #endif
47 
48 #if GET_TIME_STRATEGY == GTS_GETTIMEOFDAY
get_time(void)49 double get_time(void)
50 {
51     struct timeval now;
52     double seconds;
53 
54     gettimeofday(&now, NULL);
55 
56     seconds = (double)now.tv_sec + (double)now.tv_usec * 1e-6;
57 
58     return seconds;
59 }
60 #endif
61 
62 #if GET_TIME_STRATEGY == GTS_WIN_PERF_COUNTERS
63 #  ifndef WIN_HEADERS_INCLUDED
64 #     define WIN_HEADERS_INCLUDED
65 #     define WIN32_LEAN_AND_MEAN
66 #     include <windows.h>
67 #     include <math.h>
68 #  endif
69 
70 LOCAL double perf_rate = -1;
71 
72 extern double win32_get_time_fallback(void);
73 
get_time(void)74 double get_time(void)
75 {
76     LARGE_INTEGER li;
77 
78     if (perf_rate < 0)
79     {
80         if (QueryPerformanceFrequency(&li))
81         {
82             perf_rate = 1.0 / (double)li.QuadPart;
83         } else
84         {
85             perf_rate = 0;
86         }
87     }
88 
89     if (perf_rate > 0)
90     {
91         double seconds;
92         QueryPerformanceCounter(&li);
93         seconds = (double)li.QuadPart * perf_rate;
94         return seconds;
95     }
96 
97     return win32_get_time_fallback();
98 }
99 #endif
100 
101 #if GET_TIME_STRATEGY == GTS_WII
get_time(void)102 double get_time(void)
103 {
104     extern double wii_get_time();
105 
106     return wii_get_time();
107 }
108 #endif
109 
110 /* ------------------------------------------------------------------------ */
111 /*  PLAT_DELAY        -- Sleep w/ millisecond precision.                    */
112 /*  PLAT_DELAY_NO_SDL -- Sleep w/ millisecond precision.                    */
113 /* ------------------------------------------------------------------------ */
114 #if PLAT_DELAY_STRATEGY_NO_SDL == PDS_WIN_WAIT_TIMER
115 #  ifndef WIN_HEADERS_INCLUDED
116 #     define WIN_HEADERS_INCLUDED
117 #     define WIN32_LEAN_AND_MEAN
118 #     include <windows.h>
119 #     include <math.h>
120 #  endif
plat_delay_no_sdl(unsigned msec)121 void plat_delay_no_sdl(unsigned msec)
122 {
123     HANDLE timer;
124     LARGE_INTEGER ft;
125 
126     /* Windows wants 100ns intervals */
127     /* Also, -ve to specify _relative_ time */
128     ft.QuadPart = -(10000ll*msec);
129 
130     timer = CreateWaitableTimer(NULL, TRUE, NULL);
131     SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
132     WaitForSingleObject(timer, INFINITE);
133     CloseHandle(timer);
134 }
135 #endif
136 
137 #if PLAT_DELAY_STRATEGY_NO_SDL == PDS_NANOSLEEP
plat_delay_no_sdl(unsigned delay)138 void plat_delay_no_sdl(unsigned delay)
139 {
140     struct timespec ts;
141     ts.tv_sec = delay / 1000;
142     ts.tv_nsec = (delay % 1000) * 1000000;
143     nanosleep(&ts, NULL);
144 }
145 #endif
146 
147 #if PLAT_DELAY_STRATEGY_NO_SDL == PDS_USLEEP
plat_delay_no_sdl(unsigned delay)148 void plat_delay_no_sdl(unsigned delay)
149 {
150     usleep(delay * 1000);
151 }
152 #endif
153 
154 #if PLAT_DELAY_STRATEGY_NO_SDL == PDS_BUSY_LOOP
plat_delay_no_sdl(unsigned delay)155 void plat_delay_no_sdl(unsigned delay)
156 {
157     double soon = get_time() + ((double)delay / 1000.0);
158 
159     /* -------------------------------------------------------------------- */
160     /*  BAD BAD BAD: Sit in a busy loop until time expires.                 */
161     /* -------------------------------------------------------------------- */
162     while (get_time() < soon)
163         ;
164 
165     return;
166 }
167 #endif
168 
169 #if PLAT_DELAY_STRATEGY != PDS_MACINTOSH \
170    && PLAT_DELAY_STRATEGY != PDS_SDL_DELAY
plat_delay(unsigned delay)171 void plat_delay(unsigned delay)
172 {
173     plat_delay_no_sdl(delay);
174 }
175 #endif
176 
177 
178 /* ======================================================================== */
179 /*  Window size functions                                                   */
180 /*                                                                          */
181 /*  GET_DISP_WIDTH   -- Get the width of the text display.                  */
182 /*  SET_DISP_WIDTH   -- Try to set the width of the text display.           */
183 /*  INIT_DISP_WIDTH  -- Initialize the display width.                       */
184 /*                                                                          */
185 /*  If the display width is unknown, this should return '80', the default   */
186 /*  width jzIntv historically assumed.  Because 'set_disp_width' can fail,  */
187 /*  it returns the new width, which may be unchanged.                       */
188 /*                                                                          */
189 /*  On platforms which cannot detect display width, "INIT_DISP_WIDTH" can   */
190 /*  set the display width.  Passing in 0 here just tells it to detect or    */
191 /*  use the default width.                                                  */
192 /* ======================================================================== */
193 
194 /* ------------------------------------------------------------------------ */
195 /*  Outsourced implementation: Ask GNU Readline!                            */
196 /* ------------------------------------------------------------------------ */
197 #ifdef USE_GNU_READLINE
198 # define HAVE_WIDTH_IMPL
199 # include <readline/readline.h>
200 
get_disp_width(void)201 int get_disp_width(void)
202 {
203     int rows, cols;
204     rl_get_screen_size(&rows, &cols);
205     return cols;
206 }
207 
set_disp_width(int new_width)208 int set_disp_width(int new_width)
209 {
210     UNUSED(new_width);
211     return get_disp_width();
212 }
213 
init_disp_width(int width)214 void init_disp_width(int width)
215 {
216     UNUSED(width);
217 }
218 #endif
219 
220 /* ------------------------------------------------------------------------ */
221 /*  Weakest implementation:  Just return 80 unless we're told to return     */
222 /*  something else.                                                         */
223 /* ------------------------------------------------------------------------ */
224 #if !defined(HAVE_WIDTH_IMPL) && !defined(WIN32) && !defined(CAN_TIOCGWINSZ)
225 # define HAVE_WIDTH_IMPL
226 LOCAL int disp_width = -1;
227 
get_disp_width(void)228 int get_disp_width(void)
229 {
230     return disp_width;
231 }
232 
set_disp_width(int new_width)233 int set_disp_width(int new_width)
234 {
235     disp_width = new_width > 0 ? new_width : disp_width;
236     return disp_width;
237 }
238 
init_disp_width(int width)239 void init_disp_width(int width)
240 {
241     disp_width = width > 0 ? width : 80;
242 }
243 #endif
244 
245 /* ------------------------------------------------------------------------ */
246 /*  WIN32 Implementation:  Eventually use "mode con:" to change size.       */
247 /* ------------------------------------------------------------------------ */
248 #if !defined(HAVE_WIDTH_IMPL) && defined(WIN32)
249 # define HAVE_WIDTH_IMPL
250 LOCAL int disp_width = -1;
251 
set_width_from_mode_con(void)252 LOCAL int set_width_from_mode_con(void)
253 {
254     FILE *f;
255     f = popen("mode con:", "r");
256     if (f)
257     {
258         char buf[80];
259         int w = -1;
260 
261         while (fgets(buf, sizeof(buf), f) != NULL)
262         {
263             if (sscanf(buf, " Columns: %d", &w) > 0)
264                 disp_width = w;
265         }
266 
267         pclose(f);
268 
269         if ( w == -1 ) // couldn't find it!
270             return -1;
271         else
272             return 0;  // success
273     }
274     return 0;
275 }
276 
get_disp_width(void)277 int get_disp_width(void)
278 {
279     return disp_width;
280 }
281 
set_disp_width(int new_width)282 int set_disp_width(int new_width)
283 {
284     char buf[80];
285 
286     if (new_width == disp_width || new_width < 1)
287         return disp_width;
288 
289     sprintf(buf, "mode con: cols=%d", new_width);
290 
291     if (system(buf) == 0)
292         if ( set_width_from_mode_con() < 0 ) // asking Windows didn't work?
293             disp_width = new_width;          // hack:  assume it worked.
294 
295     return disp_width;
296 }
297 
init_disp_width(int width)298 void init_disp_width(int width)
299 {
300     disp_width = width > 0 ? width : 80;
301 }
302 
303 #endif
304 
305 /* ------------------------------------------------------------------------ */
306 /*  TIOCGWINSZ w/out SIGWINCH:  Always call the ioctl when asked the size.  */
307 /* ------------------------------------------------------------------------ */
308 #if !defined(HAVE_WIDTH_IMPL) && defined(CAN_TIOCGWINSZ) && !defined(CAN_SIGWINCH)
309 # define HAVE_WIDTH_IMPL
310 LOCAL int disp_width = 80;
311 
get_disp_width(void)312 int get_disp_width(void)
313 {
314     Dims my_win;
315 
316     if (ioctl(0, TIOCGWINSZ, &my_win) != 0)
317         return disp_width;
318 
319     return my_win.ws_col;
320 }
321 
set_disp_width(int new_width)322 int set_disp_width(int new_width)
323 {
324     disp_width = new_width > 0 ? new_width : disp_width;
325     return get_disp_width();
326 }
327 
init_disp_width(int width)328 void init_disp_width(int width)
329 {
330     disp_width = width > 0 ? width : 80;
331 }
332 #endif
333 
334 /* ------------------------------------------------------------------------ */
335 /*  TIOCGWINSZ with SIGWINCH:  Only call the ioctl if a SIGWINCH happened.  */
336 /* ------------------------------------------------------------------------ */
337 #if !defined(HAVE_WIDTH_IMPL) && defined(CAN_TIOCGWINSZ) && defined(CAN_SIGWINCH)
338 # define HAVE_WIDTH_IMPL
339 # include <signal.h>
340 
341 LOCAL volatile char need_width_ioctl = 1;
342 LOCAL int           disp_width       = 80;
343 
sigwinch_handler(int sig)344 LOCAL void sigwinch_handler(int sig)
345 {
346     UNUSED(sig);
347     need_width_ioctl = 1;
348 }
349 
get_disp_width(void)350 int get_disp_width(void)
351 {
352     struct winsize my_win;
353 
354     if (need_width_ioctl)
355     {
356         need_width_ioctl = 0;
357         if (ioctl(0, TIOCGWINSZ, &my_win) == 0)
358             disp_width = my_win.ws_col;
359     }
360     signal(SIGWINCH, sigwinch_handler);
361 
362     return disp_width;
363 }
364 
set_disp_width(int new_width)365 int set_disp_width(int new_width)
366 {
367     disp_width = new_width > 0 ? new_width : disp_width;
368     need_width_ioctl = 1;
369     return get_disp_width();
370 }
371 
init_disp_width(int new_width)372 void init_disp_width(int new_width)
373 {
374     disp_width = new_width > 0 ? new_width : disp_width;
375     need_width_ioctl = 1;
376 
377     signal(SIGWINCH, sigwinch_handler);
378 }
379 #endif
380 
381 /* ======================================================================== */
382 /*  This program is free software; you can redistribute it and/or modify    */
383 /*  it under the terms of the GNU General Public License as published by    */
384 /*  the Free Software Foundation; either version 2 of the License, or       */
385 /*  (at your option) any later version.                                     */
386 /*                                                                          */
387 /*  This program is distributed in the hope that it will be useful,         */
388 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
389 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
390 /*  General Public License for more details.                                */
391 /*                                                                          */
392 /*  You should have received a copy of the GNU General Public License along */
393 /*  with this program; if not, write to the Free Software Foundation, Inc., */
394 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
395 /* ======================================================================== */
396 /*           Copyright (c) 1998-2020, Joseph Zbiciak, Tim Lindner           */
397 /* ======================================================================== */
398