1 /*
2  * "fwin.c"                                 Copyright A C Norman 2003-2010
3  *
4  *
5  * Window interface for old-fashioned C applications. Intended to
6  * be better than just running them within rxvt/xterm, but some people will
7  * always believe that running them under emacs is best!
8  *
9  * Note that although the graphical bits of fwin and coded in C++ the
10  * parts needed for a text-only interface are in just C. This is so that
11  * on limited platforms where graphics are not relevant the C++ libraries
12  * do not have to be used.
13  */
14 
15 /**************************************************************************
16  * Copyright (C) 2010, Codemist Ltd.                     A C Norman       *
17  *                                                                        *
18  * Redistribution and use in source and binary forms, with or without     *
19  * modification, are permitted provided that the following conditions are *
20  * met:                                                                   *
21  *                                                                        *
22  *     * Redistributions of source code must retain the relevant          *
23  *       copyright notice, this list of conditions and the following      *
24  *       disclaimer.                                                      *
25  *     * Redistributions in binary form must reproduce the above          *
26  *       copyright notice, this list of conditions and the following      *
27  *       disclaimer in the documentation and/or other materials provided  *
28  *       with the distribution.                                           *
29  *                                                                        *
30  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    *
31  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      *
32  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS      *
33  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE         *
34  * COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,   *
35  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,   *
36  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  *
37  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
38  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR  *
39  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF     *
40  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
41  * DAMAGE.                                                                *
42  *************************************************************************/
43 
44 
45 /*
46  * Note that the above terms apply and must persist regardless of where or how
47  * this code is used. A copy of this will be included within a modified
48  * version of the FOX library and in that context the whole work has to
49  * be treated subject to the constraints of the LGPL (and not the FOX
50  * license addendum that would have granted static linking rights, because
51  * that only applies to unmodified versions of FOX as sanctioned by its
52  * original author). But this code can also be compiled and used without
53  * the GUI components, in that case LGPL obligations do not apply but BSD
54  * ones do.
55  */
56 
57 /* Signature: 7d16abe5 20-Aug-2010 */
58 
59 #ifdef HAVE_CONFIG_H
60 #include "config.h"
61 #else
62 /* Here I will suppose I am building as part of the FOX library */
63 #define PART_OF_FOX 1
64 #endif /* HAVE_CONFIG_H */
65 
66 #include "fwin.h"
67 
68 
69 extern int fwin_main(int argc, char *argv[]);
70 
71 #ifdef HAVE_LIBFOX
72 /*
73  * This case will apply when I am compiling this as part of an application
74  * that uses the autoconf tools to create a file "config.h" and when
75  * autoconf has reported that the FOX library is available. I require that
76  * this represents my updated and extended version of FOX because it will
77  * then contain a copy of most of this code. My application can then just
78  * start up by transferring control into FOX, passing in information about
79  * the callbacks that are needed. The code here is what is needed by
80  * CSL...
81  */
82 
main(int argc,char * argv[])83 int main(int argc, char *argv[])
84 {
85     fwin_startup(argc, argv, fwin_main);
86 }
87 
88 
89 #else /* HAVE_LIBFOX */
90 
91 /* Even without FOX if I am building on Windows I need this header file
92  * for (eg) GetModuleFileName().
93  */
94 #ifdef WIN32
95 /* Indicate that I expect to be using a RECENT version of Windows */
96 #ifndef _WIN32_WINNT
97 #define _WIN32_WINNT 0x0500
98 #endif
99 #include <windows.h>
100 #include <io.h>
101 #endif /* WIN32 */
102 
103 #include <string.h>
104 #include <stdio.h>
105 #include <stdlib.h>
106 #include <stdarg.h>
107 #include <ctype.h>
108 #include <time.h>
109 #include <signal.h>
110 
111 #if HAVE_UNISTD_H
112 #include <unistd.h>
113 #else
114 #ifndef _MSC_VER
115 extern char *getcwd(char *s, size_t n);
116 #endif
117 #endif /* HAVE_UNISTD_H */
118 
119 #include <sys/stat.h>
120 #include <sys/types.h>
121 #include <errno.h>
122 
123 #ifndef EMBEDDED
124 #ifdef HAVE_DIRENT_H
125 #include <dirent.h>
126 #else
127 #ifndef WIN32
128 #include <sys/dir.h>
129 #else
130 #include <direct.h>
131 #endif
132 #endif /* HAVE_DIRENT_H */
133 #endif
134 
135 /*
136  * I used to have this to give me X11 headers - but (a) if I am building
137  * without FOX I do not have a GUI at all and so they are not needed and
138  * (b) they conflict with the Mac-specific headers that follow. Specifically
139  * I have pain with "Cursor" being a name-clash.
140  *
141  * #ifndef WIN32
142  * #include <X11/Xlib.h>
143  * #endif
144  */
145 
146 #if defined MACINTOSH && defined MAC_FRAMEWORK
147 /*
148  * The extent to which any code here pays attention to Mac-specific
149  * features is and will probably remain minimal! However some may be
150  * used here.
151  */
152 #include <Carbon/Carbon.h>
153 #include <CoreServices/CoreServices.h>
154 #endif /* MAC stuff */
155 
156 #ifdef _MSC_VER
157 #define MS_CDECL __cdecl
158 #else
159 #define MS_CDECL
160 #endif /* Microsoft C */
161 
162 #include "termed.h"
163 
164 /*
165  * The next few are not exactly useful if FOX is not available
166  * and hence this code will run in line-mode only. However it is
167  * convenient to leave them available.
168  *
169  * Note that FOX as used here is licensed under the LGPL. Its terms
170  * require that if the work displays a copyright notice during execution
171  * than the FOX copyright notice and a reference directing users to
172  * a copy of the LGPL must be displayed to. Thus my suggestion is that the
173  * about-box information here should not purport to be a copyright notice,
174  * merely a reminder of who the key authors are. However despite not
175  * then being obliged to say anything at all about FOX I will put in the
176  * URL of its web-site, and of course that is a place where the LGPL can
177  * be found. Anything beyond that would make the size of the about box
178  * grow in a way I view as clumsy. The wording I use is as requested (but
179  * under LGPL not actually required!) by the FOX authors.
180  */
181 
182 char about_box_title[40]       = "About XXX";
183 char about_box_description[40] = "XXX version 1.1";
184                               // <icon appears here>
185 char about_box_rights_1[40]    = "Author info";
186 char about_box_rights_2[40]    = "Additional author";
187 char about_box_rights_3[40]    = "This software uses the FOX Toolkit";
188 char about_box_rights_4[40]    = "(http://www.fox-toolkit.org)";
189 
190 /*
191  * The value LONGEST_LEGAL_FILENAME should be seen as a problem wrt
192  * buffer overflow! I will just blandly assume throughout all my code that
193  * no string that denotes a full file-name (including its path) is ever
194  * longer than this.
195  */
196 #ifndef LONGEST_LEGAL_FILENAME
197 #define LONGEST_LEGAL_FILENAME 1024
198 #endif
199 
200 const char *colour_spec = "-";
201 
202 char fwin_prompt_string[MAX_PROMPT_LENGTH] = "> ";
203 
204 int fwin_linelength = 80;
205 
206 delay_callback_t *delay_callback;
207 interrupt_callback_t *interrupt_callback;
208 
209 extern const char *my_getenv(const char *s);
210 
211 #ifdef WIN32
212 static int program_name_dot_com;
213 #endif
214 
215 int windowed = 0;
216 
217 int texmacs_mode = 0;
218 
219 #ifdef HAVE_LIBXFT
220 int fwin_use_xft = 1;
221 #else
222 int fwin_use_xft = 0;
223 #endif
224 
225 int fwin_pause_at_end = 0;
226 
227 #ifdef WIN32
228 
consoleWait()229 void consoleWait()
230 {
231 /*
232  * If the console had to be created specially to view this information
233  * it is probable that it will close as soon as the program closes, and so
234  * to give at least a minimal chance for the user to inspect it I will
235  * put in a delay here.
236  */
237     clock_t c0 = clock() + 5*CLOCKS_PER_SEC;
238     while (clock() < c0);
239 }
240 
241 #endif
242 
243 #ifndef EMBEDDED
244 
245 #if defined PART_OF_FOX || defined CSL
fwin_startup(int argc,char * argv[],fwin_entrypoint * fwin_main)246 int fwin_startup(int argc, char *argv[], fwin_entrypoint *fwin_main)
247 #else
248 int main(int argc, char *argv[])
249 #endif
250 {
251     int i;
252 #ifndef WIN32
253     const char *disp;
254 #endif
255 /* I want to know the path to the directory from which this
256  * code was launched.
257  */
258     if (argc == 0)
259     {   fprintf(stderr,
260             "argc == 0. You tried to launch the code in a funny way?\n");
261         return 1;
262     }
263     if (find_program_directory(argv[0]))
264     {   fprintf(stderr, "Unable to identify program name and directory\n");
265         return 1;
266     }
267     texmacs_mode = 0;
268 /*
269  * An option "--my-path" just prints the path to the executable
270  * and stops.
271  */
272     for (i=1; i<argc; i++)
273     {   if (strcmp(argv[i], "--my-path") == 0)
274         {   printf("%s\n", programDir);
275             exit(0);
276         }
277     }
278 
279 #ifdef PART_OF_FOX
280 /*
281  * As the very first thing I will do, I will seek an argument
282  * that is just "-w", and if it is present record that I will want to
283  * run in text mode, not windowed mode. I also detected "--", "-f"
284  * and "-f" and use them to flag up a request to run minimised.
285  * Note that "-w" takes precedence over "--" here...
286  *
287  * I run as a minimise window (by default) in the "--" case since I can use
288  * the window title-bar to report progress even when all output is directed to
289  * file.
290  */
291     windowed = 1;
292 #ifdef WIN32
293 /* I have tried various messy Windows API calls here to get this right.
294  * But so far I find that the cases that apply to me are
295  *    (a) windows command prompt : normal case
296  *    (b) windows command prompt : stdin redirected via "<" on command line
297  *    (c) windows, but launched by a double-click, .com version
298  *    (d) windows, but launched by a double-click, .exe version
299  *    (e) cygwin shell : normal case
300  *    (f) cygwin shell : stdin redirected via "<"
301  * leave me in a state
302  *    (a) stdin exists and is a tty, a char device and a Console
303  *    (b) stdin exists and is a pipe or a file not a tty
304  *    (c) as (a)
305  *    (d) stdin seems to exist but is not a tty
306  *    (e) stdin exists and is a pipe
307  *    (f) as (e)
308  * I want (b), (c) and (f) to force a non-windowed treatment.  But you may see
309  * that various cases are not readily properly distinguished...
310  *
311  * So for now I will leave the code not doing ANYTHING special so that the
312  * user must go "-w" to specify windowed mode.
313  */
314 
315     if (program_name_dot_com)
316     {
317 /* The program was named "xxx.com". I will assume that that means it was
318  * a console-mode application and it is being launched directly from a
319  * Windows console.  Why do I feel these are plausible:
320  *  . The Makefile.in & configure.ac stuff arranges to build xxx.com as
321  *    console mode and xxx.exe as subsystem:windows
322  *  . A Windows command prompt will launch xxx.com in preference to xxx.exe
323  *    if both are present
324  *  . xxx.com is not given an icon, while xxx.exe is - people should not
325  *    double-click on the .com version (please)
326  * Obviously users can subvert this by copying xxx.exe to yyy.com, by
327  * double clicking where I did not want or by specifying an explicit
328  * extension when they launch a command from a console prompt. But in such
329  * cases I will take the view that they will get what they deserve!
330  */
331         HANDLE h;
332         DWORD w;
333         CONSOLE_SCREEN_BUFFER_INFO csb;
334 /* If either standard input or output has been redirected I will force use
335  * of console rather than windowed mode. Thus
336  *         xxx             launch in a window
337  *         xxx -w          run as console application
338  *         xxx < yyy       run as console application
339  *         xxx > yyy       run as console application
340  * My hope is that the detection of redirected stdin/stdout will help
341  * when the application is used in a script. There may remain a dodgy case!
342  * if xxx is run under a debugger at least some debuggers intercept standard
343  * input & output so debugging the windowed mode may be harded here. But I
344  * will defer that worry since the ".exe" not the ".com" file is the version
345  * with windowed use its prime interface.
346  */
347         h = GetStdHandle(STD_INPUT_HANDLE);
348         if (GetFileType(h) != FILE_TYPE_CHAR) windowed = 0;
349         else if (!GetConsoleMode(h, &w)) windowed = 0;
350         h = GetStdHandle(STD_OUTPUT_HANDLE);
351         if (GetFileType(h) != FILE_TYPE_CHAR) windowed = 0;
352         else if (!GetConsoleScreenBufferInfo(h, &csb)) windowed = 0;
353     }
354     else
355     {
356 /* The program was named "xxx.exe". I am going to suppose that this has NOT
357  * been launched from a normal Windows command prompt (since xxx.com would
358  * have been preferred). I am left with two scenarios. One is that the
359  * program was launched by double-clicking, and in that case it detached
360  * from its console as it started. The other is that it was launched from
361  * a cygwin prompt (which looks for xxx.exe but not xxx.com when you type
362  * just xxx).
363  */
364         HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
365 /* The discrimination I make here is based on an empirical check of what
366  * seems to happen under Windows XP with the version of cygwin current as
367  * of September 2004. What I find is that when stdin has been redirected by
368  * a shell (either the Windows command shell or cygwin, and in the cygwin
369  * case either with "<file" or "<<TAG") my standard input handle exists
370  * and identifies itself as type DISK. The the case of launching the code
371  * by double-clicking on the .exe file the handle is probably invalid, but
372  * GetFileType returns FILE_TYPE_UNKNOWN. The end effect is that I can
373  * detect cases where input has been redirected in a way that appears to
374  * work in both cases.  Note that if a user wants to launch an application
375  * via a pipe then they should EITHER launch the ".com" version or (better)
376  * explictly provide a "-w" flag to indicate that the application should
377  * work in stream/console mode.
378  */
379         if (GetFileType(h) == FILE_TYPE_DISK) windowed = 0;
380     }
381 #else  /* WIN32 */
382 /* If stdin or stdout is not from a "tty" I will run in non-windowed mode.
383  * This may help when the system is used in scripts. I worry a bit about
384  * what the status of stdin/stdout are when launched not from a command line
385  * but by clicking on an icon...
386  */
387     if (!isatty(fileno(stdin)) || !isatty(fileno(stdout))) windowed = 0;
388 
389 /* On Unix-like systems I will check the DISPLAY variable, and if it is not
390  * set I will suppose that I can not create a window. That case will normally
391  * arise when you have gained remote access to the system eg via telnet or
392  * ssh without X forwarding. I will also insist that if set it has a ":" in
393  * its value... that is to avoid trouble with it getting set to an empty
394  * string.
395  */
396     disp = my_getenv("DISPLAY");
397     if (disp == NULL || strchr(disp, ':')==NULL) windowed = 0;
398 #endif  /* WIN32 */
399 /*
400  * REGARDLESS of any decisions about windowing made so for things can be
401  * forced by command line options.
402  *    -w+ forces an attempt to run in a window even if it looks as if that
403  *        would not make sense or would fail. It is mainly for debugging.
404  *    -w. forces use of a window, but starts it minimised.
405  *    -w  forces command-line rather than windowed use (can also write
406  *        "-w-" for this case).
407  */
408     for (i=1; i<argc; i++)
409     {   if (strcmp(argv[i], "--texmacs") == 0) texmacs_mode = 1;
410         else if (strncmp(argv[i], "-w", 2) == 0)
411         {   if (argv[i][2] == '+') windowed = 1;
412             else if (argv[i][2] == '.') windowed = -1;
413             else windowed = 0;
414             break;
415         }
416         else if (strcmp(argv[i], "-h") == 0 ||
417                  strcmp(argv[i], "-H") == 0)
418 #ifdef HAVE_LIBXFT
419                  fwin_use_xft = 0;
420 #else
421                  ; /* Ignore "-h" option if Xft not available */
422 #endif
423 /*
424  * Note well that I detect just "--" as an entire argument here, so that
425  * extended options "--option" do not interfere.
426  */
427         else if ((strcmp(argv[i], "--") == 0 ||
428                   strcmp(argv[i], "-f") == 0 ||
429                   strcmp(argv[i], "-F") == 0) &&
430                  windowed != 0) windowed = -1;
431     }
432     if (texmacs_mode) windowed = 0;
433 #ifdef WIN32
434 /*
435  * If I am running under Windows and I have set command line options
436  * that tell me to run in a console then I will create one if one does
437  * not already exist.
438  */
439     if (windowed == 0)
440     {   int consoleCreated = AllocConsole();
441         if (consoleCreated)
442         {   freopen("CONIN$", "r", stdin);
443             freopen("CONOUT$", "w", stdout);
444             freopen("CONOUT$", "w", stderr);
445 /* I will also pause for 5 seconds at the end... */
446             atexit(consoleWait);
447         }
448     }
449 #endif /* WIN32 */
450 #else /* PART_OF_FOX */
451 /* If the FOX toolkit is not available there is no point in
452  * looking for a command-line option that controls whether to use it!
453  */
454 #endif /* PART_OF_FOX */
455 /* Windowed or not, if there is an argument "-b" or "-bxxxx" then the
456  * string xxx will do something about screen colours. An empty string
457  * will suggest no colouring, the string "-" (as in -b-) whatever default
458  * I choose.
459  */
460     colour_spec = "-";
461     for (i=1; i<argc; i++)
462     {   if (strncmp(argv[i], "-b", 2) == 0)
463         {   colour_spec = argv[i]+2;
464             break;
465         }
466     }
467 
468 #ifdef PART_OF_FOX
469     if (windowed==0) return plain_worker(argc, argv, fwin_main);
470     return windowed_worker(argc, argv, fwin_main);
471 #else
472 /* If I am using a text-only interface everything is now set up! */
473     return plain_worker(argc, argv, fwin_main);
474 #endif
475 }
476 
sigint_handler(int code)477 void MS_CDECL sigint_handler(int code)
478 {
479 #ifdef TEST_SIGNAL_CATCHER
480 /* For debugging I may want to see when signals get caught... */
481     fprintf(stderr, "sigint_handler called %d %#x\n", code, code);
482     fflush(stderr);
483 #endif
484     signal(SIGINT, sigint_handler);
485     if (interrupt_callback != NULL) (*interrupt_callback)(QUIET_INTERRUPT);
486     return;
487 }
488 
489 #endif /* EMBEDDED */
490 
491 #ifdef SIGBREAK
sigbreak_handler(int code)492 void MS_CDECL sigbreak_handler(int code)
493 {
494 #ifdef TEST_SIGNAL_CATCHER
495     fprintf(stderr, "sigbreak_handler called %d %#x\n", code, code);
496     fflush(stderr);
497 #endif
498     signal(SIGBREAK, sigbreak_handler);
499     if (interrupt_callback != NULL) (*interrupt_callback)(NOISY_INTERRUPT);
500     return;
501 }
502 #endif
503 
504 /*
505  * I will only try to use my own local editing and history package
506  * if both stdin and stdout are routed directly to a "tty" or "console".
507  * The test I apply can probably never be 100% satisfactory, but if I
508  * catch all the most common cases I will feel reasonably relaxed!
509  */
510 int using_termed = 0;
511 
direct_to_terminal(int argc,char * argv[])512 static int direct_to_terminal(int argc, char *argv[])
513 {
514 #ifdef WIN32
515     HANDLE h;
516     DWORD w;
517     CONSOLE_SCREEN_BUFFER_INFO csb;
518 /*
519  * Standard input must be from a character device and must be accepted
520  * by the GetConsoleMode function
521  */
522     h = GetStdHandle(STD_INPUT_HANDLE);
523     if (GetFileType(h) != FILE_TYPE_CHAR) return 0;
524     if (!GetConsoleMode(h, &w)) return 0;
525 /*
526  * Standard output must be a character device and a ConsoleScreenBuffer
527  */
528     h = GetStdHandle(STD_OUTPUT_HANDLE);
529     if (GetFileType(h) != FILE_TYPE_CHAR) return 0;
530     if (!GetConsoleScreenBufferInfo(h, &csb)) return 0;
531 /*
532  * Note that I will allow stderr to have been redirected as much
533  * as you like without that having an effect here.
534  */
535     return 1;
536 #else
537     return isatty(fileno(stdin)) && isatty(fileno(stdout));
538 #endif /* WIN32 */
539 }
540 
plain_worker(int argc,char * argv[],fwin_entrypoint * fwin_main)541 int plain_worker(int argc, char *argv[], fwin_entrypoint *fwin_main)
542 {
543     int r;
544     signal(SIGINT, sigint_handler);
545 #ifdef SIGBREAK
546     signal(SIGBREAK, sigbreak_handler);
547 #endif
548 #ifdef TEST_SIGNAL_CATCHER
549     fprintf(stderr, "handlers for sigint and sigbreak set up\n");
550     fflush(stderr);
551 #endif
552     if (!texmacs_mode && direct_to_terminal(argc, argv))
553     {   input_history_init();
554         term_setup(1, colour_spec);
555         atexit(term_close);
556         using_termed = 1;
557     }
558     else using_termed = 0;
559     strcpy(fwin_prompt_string, "> ");
560     r = (*fwin_main)(argc, argv);
561     input_history_end();
562     term_close();
563     return r;
564 }
565 
566 
567 #define INPUT_BUFFER_SIZE 100
568 
569 static const char *current_line;
570 static char input_buffer[INPUT_BUFFER_SIZE];
571 static int chars_left = 0;
572 static int prompt_needed = 1;
573 
fwin_plain_getchar()574 int fwin_plain_getchar()
575 {
576     int ch;
577     if (using_termed)
578     {   while (chars_left == 0)
579         {   term_setprompt(fwin_prompt_string);
580             current_line = term_getline();
581             if (current_line == NULL) return EOF;  // failed or EOF
582             chars_left = strlen(current_line);
583             input_history_add(current_line);
584         }
585     }
586     else if (chars_left == 0)
587     {   if (prompt_needed)
588         {   printf("%s", fwin_prompt_string);
589             prompt_needed = 0;
590         }
591         fflush(stdout);
592         for (chars_left=0; chars_left<INPUT_BUFFER_SIZE;)
593         {   int c = getchar();
594             if (c == EOF) c = (0x1f & 'D');
595             input_buffer[chars_left++] = c;
596             if (c == '\n' || c == (0x1f & 'D'))
597             {   prompt_needed = 1;
598                 break;
599             }
600         }
601         if (chars_left == 0) return EOF;
602         current_line = input_buffer;
603     }
604     chars_left--;
605     ch = *current_line++;
606     if (ch == (0x1f & 'D')) ch = EOF;
607     return ch;
608 }
609 
610 #ifndef PART_OF_FOX
611 
fwin_restore()612 void fwin_restore()
613 {
614 }
615 
fwin_putchar(int c)616 void fwin_putchar(int c)
617 {
618 /*
619  * Despite using termed during keyboard input I will just use the
620  * ordinary C stream functions for normal output. Provided I do an
621  * fflush(stdout) before requesting input I should be OK.
622  */
623 #ifdef RAW_CYGWIN
624 /*
625  * If I have built the system under Cygwin then we are running under
626  * Windows. To keep files tidy I will (mostly) insert CRs at line-end
627  * in case Cygwin does not...
628  */
629     if (c == '\n') putchar('\r');
630 #endif
631     putchar(c);
632 }
633 
fwin_puts(const char * s)634 void fwin_puts(const char *s)
635 {
636 /*
637  * See comment above where putchar() is used...
638  */
639 #ifdef RAW_CYGWIN
640     while (*s != 0) fwin_putchar(*s++);
641 #else
642     puts(s);
643 #endif
644 }
645 
646 
fwin_printf(const char * fmt,...)647 void MS_CDECL fwin_printf(const char *fmt, ...)
648 {
649     va_list a;
650     va_start(a, fmt);
651 /*
652  * See comment above where putchar() is used...
653  */
654 #ifdef RAW_CYGWIN
655 /* NOT reconstructed yet @@@ */
656     vfprintf(stdout, fmt, a);
657 #else
658     vfprintf(stdout, fmt, a);
659 #endif
660     va_end(a);
661 }
662 
fwin_vfprintf(const char * fmt,va_list a)663 void fwin_vfprintf(const char *fmt, va_list a)
664 {
665 /*
666  * See comment above where putchar() is used...
667  */
668 #ifdef RAW_CYGWIN
669 /* Not reconstructed yet @@@ */
670     vfprintf(stdout, fmt, a);
671 #else
672     vfprintf(stdout, fmt, a);
673 #endif
674 }
675 
fwin_ensure_screen()676 void fwin_ensure_screen()
677 {
678     fflush(stdout);
679 }
680 
fwin_report_left(const char * s)681 void fwin_report_left(const char *s)
682 {
683 }
684 
fwin_report_mid(const char * s)685 void fwin_report_mid(const char *s)
686 {
687 }
688 
fwin_report_right(const char * s)689 void fwin_report_right(const char *s)
690 {
691 }
692 
fwin_getchar()693 int fwin_getchar()
694 {
695     return fwin_plain_getchar();
696 }
697 
698 
fwin_set_prompt(const char * s)699 void fwin_set_prompt(const char *s)
700 {
701     strncpy(fwin_prompt_string, s, sizeof(fwin_prompt_string));
702     fwin_prompt_string[sizeof(fwin_prompt_string)-1] = 0;
703 }
704 
fwin_menus(char ** modules,char ** switches,review_switch_settings_function * f)705 extern void fwin_menus(char **modules, char **switches,
706                        review_switch_settings_function *f)
707 {
708 }
709 
fwin_refresh_switches(char ** switches,char ** packages)710 void fwin_refresh_switches(char **switches, char **packages)
711 {
712 }
713 
fwin_set_help_file(const char * key,const char * path)714 void fwin_set_help_file(const char *key, const char *path)
715 {
716 }
717 
fwin_acknowledge_tick()718 void fwin_acknowledge_tick()
719 {
720 }
721 
fwin_windowmode()722 int fwin_windowmode()
723 {
724     return 0;
725 }
726 
727 #endif /* !PART_OF_FOX */
728 
get_current_directory(char * s,int n)729 int get_current_directory(char *s, int n)
730 {
731     if (getcwd(s, n) == 0)
732     {   switch(errno)
733         {
734     case ERANGE: return -2; /* negative return value flags an error. */
735     case EACCES: return -3;
736     default:     return -4;
737         }
738     }
739     else return strlen(s);
740 }
741 
742 /*
743  * The next procedure is responsible for establishing information about
744  * both the "short-form" name of the program launched and the directory
745  * it was found in. This latter directory may be a good place to keep
746  * associated resources.
747  *
748  * The way of finding the information concerned differs between Windows and
749  * Unix/Linux, as one might expect.
750  *
751  * return non-zero value if failure.
752  */
753 
754 const char *fwin_full_program_name = "./fwin.exe";
755 const char *programName            = "fwin.exe";
756 const char *programDir             = ".";
757 
758 #ifdef WIN32
759 
760 static char this_executable[LONGEST_LEGAL_FILENAME];
761 
find_program_directory(char * argv0)762 int find_program_directory(char *argv0)
763 {
764     char *w;
765     int len, ndir, npgm, j;
766 /* In older code I believed that I could rely on Windows giving me
767  * the full path of my executable in argv[0]. With bits of mingw/cygwin
768  * anywhere near me that may not be so, so I grab the information directly
769  * from the Windows APIs.
770  */
771     GetModuleFileName(NULL, this_executable, LONGEST_LEGAL_FILENAME-2);
772     argv0 = this_executable;
773     program_name_dot_com = 0;
774     if (argv0[0] == 0)      /* should never happen - name is empty string! */
775     {   programDir = ".";
776         programName = "fwin";  /* nothing really known! */
777         fwin_full_program_name = ".\\fwin.exe";
778         return 0;
779     }
780 
781     fwin_full_program_name = argv0;
782     len = strlen(argv0);
783 /*
784  * If the current program is called c:\aaa\xxx.exe, then the directory
785  * is just c:\aaa and the simplified program name is just xxx
786  */
787     j = len-1;
788     if (len > 4 &&
789         argv0[len-4] == '.' &&
790         ((tolower(argv0[len-3]) == 'e' &&
791           tolower(argv0[len-2]) == 'x' &&
792           tolower(argv0[len-1]) == 'e') ||
793          (tolower(argv0[len-3]) == 'c' &&
794           tolower(argv0[len-2]) == 'o' &&
795           tolower(argv0[len-1]) == 'm')))
796     {   program_name_dot_com = (tolower(argv0[len-3]) == 'c');
797         len -= 4;
798     }
799     for (npgm=0; npgm<len; npgm++)
800     {   int c = argv0[len-npgm-1];
801         if (c == '\\') break;
802     }
803     ndir = len - npgm - 1;
804     if (ndir < 0) programDir = ".";  /* none really visible */
805     else
806     {   if ((w = (char *)malloc(ndir+1)) == NULL) return 1;
807         strncpy(w, argv0, ndir);
808         w[ndir] = 0;
809         programDir = w;
810     }
811     if ((w = (char *)malloc(npgm+1)) == NULL) return 1;
812     strncpy(w, argv0 + len - npgm, npgm);
813     w[npgm] = 0;
814     programName = w;
815     return 0;
816 }
817 
818 #else /* WIN32: now the Unix and Linux version */
819 
820 
821 /* Different systems put or do not put underscores in front of these
822  * names. My adaptation here should give me a chabce to work whichever
823  * way round it goes.
824  */
825 
826 #ifndef S_IFMT
827 # ifdef __S_IFMT
828 #  define S_IFMT __S_IFMT
829 # endif
830 #endif
831 
832 #ifndef S_IFDIR
833 # ifdef __S_IFDIR
834 #  define S_IFDIR __S_IFDIR
835 # endif
836 #endif
837 
838 #ifndef S_IFREG
839 # ifdef __S_IFREG
840 #  define S_IFREG __S_IFREG
841 # endif
842 #endif
843 
844 #ifndef S_ISLNK
845 # ifdef S_IFLNK
846 #  ifdef S_IFMT
847 #   define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
848 #  endif
849 # endif
850 #endif
851 
852 
853 /*
854  * the length set here is at least the longest length that I
855  * am prepared to worry about.
856  */
857 
find_program_directory(char * argv0)858 int find_program_directory(char *argv0)
859 {
860     char pgmname[LONGEST_LEGAL_FILENAME];
861     char *w;
862     int n, n1;
863 /*
864  * If the main reduce executable is has a full path-name /xxx/yyy/zzz then
865  * I will use /xxx/yyy as its directory To find this I need to find the full
866  * path for the executable. I ATTEMPT to follow the behaviour of "sh",
867  * "bash" and "csh".  But NOTE WELL that if anybody launches this code in
868  * an unusual manner (eg using an "exec" style function) that could confuse
869  * me substantially. What comes in via argv[0] is typically just the final
870  * component of the program name - what I am doing here is scanning to
871  * see what path it might have corresponded to.
872  *
873  *
874  * If the name of the executable starts with a "/" it is already an
875  * absolute path name. I believe that if the user types (to the shell)
876  * something like $DIR/bin/$PGMNAME or ~user/subdir/pgmname then the
877  * environment variables and user-name get expanded out by the shell before
878  * the command is actually launched.
879  */
880     if (argv0 == NULL || argv0[0] == 0) /* Information not there - return */
881     {   programDir = (const char *)"."; /* some sort of default. */
882         programName = (const char *)"fwin";
883         fwin_full_program_name = (const char *)"./fwin";
884         return 0;
885     }
886 /*
887  * I will treat 3 cases here
888  * (a)   /abc/def/ghi      fully rooted: already an absolute name;
889  * (b)   abc/def/ghi       treat as ./abc/def/ghi;
890  * (c)   ghi               scan $PATH to see where it may have come from.
891  */
892     else if (argv0[0] == '/') fwin_full_program_name = argv0;
893     else
894     {   for (w=argv0; *w!=0 && *w!='/'; w++);   /* seek a "/" */
895         if (*w == '/')      /* treat as if relative to current dir */
896         {   /* If the thing is actually written as "./abc/..." then */
897             /* strip of the initial "./" here just to be tidy. */
898             if (argv0[0] == '.' && argv0[1] == '/') argv0 += 2;
899             n = get_current_directory(pgmname, sizeof(pgmname));
900             if (n < 0) return 1;    /* fail! 1=current directory failure */
901             if (n + strlen(argv0) + 2 >= sizeof(pgmname) ||
902                 pgmname[0] == 0)
903                 return 2; /* Current dir unavailable or full name too long */
904             else
905             {   pgmname[n] = '/';
906                 strcpy(&pgmname[n+1], argv0);
907                 fwin_full_program_name = pgmname;
908             }
909         }
910         else
911         {   const char *path = my_getenv("PATH");
912 /*
913  * I omit checks for names of shell built-in functions, since my code is
914  * actually being executed by here. So I get my search path and look
915  * for an executable file somewhere on it. I note that the shells back this
916  * up with hash tables, and so in cases where "rehash" might be needed this
917  * code may become confused.
918  */
919             struct stat buf;
920             uid_t myuid = geteuid(), hisuid;
921             gid_t mygid = getegid(), hisgid;
922             int protection;
923             int ok = 0;
924 /* I expect $PATH to be a sequence of directories with ":" characters to
925  * separate them. I suppose it COULD be that somebody used directory names
926  * that had embedded colons, and quote marks or escapes in $PATH to allow
927  * for that. In such case this code will just fail to cope.
928  */
929             if (path != NULL)
930             {   while (*path != 0)
931                 {   while (*path == ':') path++; /* skip over ":" */
932                     n = 0;
933                     while (*path != 0 && *path != ':')
934                     {   pgmname[n++] = *path++;
935                         if (n > (int)(sizeof(pgmname)-3-strlen(argv0)))
936                             return 3; /* fail! 3=$PATH element overlong */
937                     }
938 /* Here I have separated off the next segment of my $PATH and put it at
939  * the start of pgmname. Observe that to avoid buffer overflow I
940  * exit abruptly if the entry on $PATH is itself too big for my buffer.
941  */
942                     pgmname[n++] = '/';
943                     strcpy(&pgmname[n], argv0);
944 /* see if the file whose name I have just built up exists at all. */
945                     if (stat(pgmname, &buf) == -1) continue;
946                     hisuid = buf.st_uid;
947                     hisgid = buf.st_gid;
948                     protection = buf.st_mode; /* info about the file found */
949 /*
950  * I now want to check if there is a file of the right name that is
951  * executable by the current (effective) user.
952  */
953                     if (protection & S_IXOTH ||
954                         (mygid == hisgid && protection & S_IXGRP) ||
955                         (myuid == hisuid && protection & S_IXUSR))
956                     {   ok = 1;   /* Haha - I have found the one we ... */
957                         break;    /* are presumably executing! */
958                     }
959                 }
960             }
961             if (!ok) return 4;    /* executable not found via $PATH */
962 /* Life is not yet quite easy! $PATH may contain some items that do not
963  * start with "/", ie that are still local paths relative to the
964  * current directory. I want to be able to return an absolute fully
965  * rooted path name! So unless the item we have at present starts with "/"
966  * I will stick the current directory's location in front.
967  */
968             if (pgmname[0] != '/')
969             {   char temp[LONGEST_LEGAL_FILENAME];
970                 strcpy(temp, pgmname);
971                 n = get_current_directory(pgmname, sizeof(pgmname));
972                 if (n < 0) return 1;    /* fail! 1=current directory failure */
973                 if ((n + strlen(temp) + 1) >= sizeof(pgmname)) return 9;
974                 pgmname[n++] = '/';
975                 strcpy(&pgmname[n], temp);
976             }
977             fwin_full_program_name = pgmname;
978         }
979     }
980 /*
981  * Now if I have a program name I will try to see if it is a symbolic link
982  * and if so I will follow it.
983  */
984     {   struct stat buf;
985         char temp[LONGEST_LEGAL_FILENAME];
986         if (lstat(fwin_full_program_name, &buf) != -1 &&
987             S_ISLNK(buf.st_mode) &&
988             (n1 = readlink(fwin_full_program_name,
989                            temp, sizeof(temp)-1)) > 0)
990         {   temp[n1] = 0;
991             strcpy(pgmname, temp);
992             fwin_full_program_name = pgmname;
993         }
994     }
995 /* Now fwin_full_program_name is set up, but may refer to an array that
996  * is stack allocated. I need to make it proper!
997  */
998     w = (char *)malloc(1+strlen(fwin_full_program_name));
999     if (w == NULL) return 5;           /* 5 = malloc fails */
1000     strcpy(w, fwin_full_program_name);
1001     fwin_full_program_name = w;
1002 #ifdef RAW_CYGWIN
1003 /*
1004  * Now if I built on raw cygwin I may have an unwanted ".com" or ".exe"
1005  * suffix, so I will purge that! This code exists here because the raw
1006  * cygwin build has a somewhat schitzo view as to whether it is a Windows
1007  * or a Unix-like system.
1008  */
1009     if (strlen(w) > 4)
1010     {   w += strlen(w) - 4;
1011         if (w[0] == '.' &&
1012             ((tolower(w[1]) == 'e' &&
1013               tolower(w[2]) == 'x' &&
1014               tolower(w[3]) == 'e') ||
1015              (tolower(w[1]) == 'c' &&
1016               tolower(w[2]) == 'o' &&
1017               tolower(w[3]) == 'm'))) w[0] = 0;
1018     }
1019 #endif
1020 /* OK now I have the full name, which is of the form
1021  *   abc/def/fgi/xyz
1022  * and I need to split it at the final "/" (and by now I very fully expect
1023  * there to be at least one "/".
1024  */
1025     for (n=strlen(fwin_full_program_name)-1; n>=0; n--)
1026         if (fwin_full_program_name[n] == '/') break;
1027     if (n < 0) return 6;               /* 6 = no "/" in full file path */
1028     w = (char *)malloc(1+n);
1029     if (w == NULL) return 7;           /* 7 = malloc fails */
1030     strncpy(w, fwin_full_program_name, n);
1031     w[n] = 0;
1032 /* Note that if the executable was "/foo" then programDir will end up as ""
1033  * so that programDir + "/" + programName works out properly.
1034  */
1035     programDir = w;
1036     n1 = strlen(fwin_full_program_name) - n;
1037     w = (char *)malloc(n1);
1038     if (w == NULL) return 8;           /* 8 = malloc fails */
1039     strncpy(w, fwin_full_program_name+n+1, n1-1);
1040     w[n1-1] = 0;
1041     programName = w;
1042     return 0;                          /* whew! */
1043 }
1044 
1045 #endif /* WIN32: end of Unix/Linux name unpicking */
1046 
1047 
1048 #ifndef S_IRUSR
1049 #ifdef __S_IRUSR
1050 #define S_IRUSR __S_IRUSR
1051 #endif
1052 #endif
1053 
1054 #ifndef S_IWUSR
1055 #ifdef __S_IWUSR
1056 #define S_IWUSR __S_IWUSR
1057 #endif
1058 #endif
1059 
1060 #ifndef S_IXUSR
1061 #ifdef __S_IXUSR
1062 #define S_IXUSR __S_IXUSR
1063 #endif
1064 #endif
1065 
1066 extern int get_home_directory(char *b, int len);
1067 extern int get_users_home_directory(char *b, int len);
1068 
1069 static lookup_function *look_in_variable = NULL;
1070 
fwin_set_lookup(lookup_function * f)1071 void fwin_set_lookup(lookup_function *f)
1072 {
1073     look_in_variable = f;
1074 }
1075 
process_file_name(char * filename,char * old,size_t n)1076 void process_file_name(char *filename, char *old, size_t n)
1077 /*
1078  * This procedure maps filenames by expanding some environment
1079  * variables.  It is very thoroughly system specific, which is why it
1080  * is in this file.  See also LONGEST_LEGAL_FILENAME in "tags.h" for a
1081  * limit on the permitted size of an expanded filename.
1082  * The input (old) is not necessarily properly terminated as a C string,
1083  * so n says how many characters to inspect.  Build a converted name
1084  * in filename.
1085  * At present the expansions I allow are:
1086  *
1087  *    $xxx   (terminated by '.', '/' or '\' with at least one char x)
1088  *    ${xxx} (self-terminating)
1089  *           First check for a Lisp variable @xxx. If this is set (and is
1090  *           a string or a symbol) then its value is used. If not then
1091  *           next inspect the environment variable xxx and dump its
1092  *           value into the output.  If the variable is unset then a check
1093  *           is made for the value of a global lisp variable called $xxx,
1094  *           and if that exists and is a string or symbol it is used.
1095  *           If $xxx is undefined a null string is inserted.
1096  *           If one of the variables is defined but has an improper value
1097  *           then the whole file-translation fails.
1098  *           The use of two Lisp variables makes it possible to control
1099  *           precedence between these and shell variables.
1100  *           At one stage I make the search order $xxx, env, @xxx, but then
1101  *           in shell scripts it is easier to go "-D@xxx=..." because
1102  *           "-D$xxx=..." tends to get subject to shell expansion. So now
1103  *           I give priority to the version I use most, to avoid being bitten
1104  *           when somebody has a stray shell variable set.
1105  *
1106  *    ~      ) followed by '.', '/' or '\'
1107  *    ~xxx   )
1108  *           On Unix these try to find home directories using
1109  *           getpwuid(getuid()) for '~' and getpwnam() for ~xxx.
1110  *           If that fails ~ expands into nothing at all.
1111  *           This syntax is only recognised at the very start of a file-name.
1112  *           For systems other than Unix this syntax will not be useful and
1113  *           should be avoided, however as an experimental place-holder I
1114  *           may do things with environment variables called HOME etc.
1115  *
1116  *
1117  * I convert file-names of the form aaa/bbb/ccc.ddd into something
1118  * acceptable to the system being used, even though this may result in
1119  * some native file titles that include '/' characters becoming unavailable.
1120  * The reasoning here is that scripts and programs can then use Unix-like
1121  * names and non-Unix hosts will treat them forgivingly.
1122  *
1123  *
1124  */
1125 {
1126     int i;
1127     int c;
1128     char *o;
1129     if (n == 0)
1130     {   *filename = 0;
1131         return;    /* deem zero-length name to be illegal */
1132     }
1133     o = filename;
1134     c = *old;
1135 /*
1136  * First I deal with a leading "~"
1137  */
1138     if (c == '~')
1139     {   old++;
1140         n--;
1141         while (n != 0)
1142         {   c = *old;
1143             if (c == '.' || c == '/' || c == '\\') break;
1144             old++;
1145             n--;
1146             *o++ = (char)c;
1147         }
1148         *o = 0;
1149 /*
1150  * actually deciding what the home directory is is passed down to a
1151  * system-specific call, but it is not to be relied upon especially
1152  * on personal computers.
1153  */
1154         if (o == filename)  /* '~' on its own */
1155         {   get_home_directory(filename, LONGEST_LEGAL_FILENAME);
1156             o = filename + strlen(filename);
1157         }
1158         else
1159         {   get_users_home_directory(filename, LONGEST_LEGAL_FILENAME);
1160             o = filename + strlen(filename);
1161         }
1162     }
1163 /*
1164  * Having copies a user-name across (if there was one) I now copy the
1165  * rest of the file-name, expanding $xxx and ${xxx} as necessary.
1166  */
1167     while (n != 0)
1168     {   c = *old++;
1169         n--;
1170 /*
1171  * If I find a "$" that is either at the end of the file-name or that is
1172  * immediately followed by ".", "/" or "\" then I will not use it for
1173  * parameter expansion. This at least gives me some help with the RISCOS
1174  * file-name $.abc.def where the "$" is used to indicate the root of the
1175  * current disc. Well RISCOS is no longer supported here so this does
1176  * not worry me a lot!
1177  */
1178         if (c == '$' && n != 0 &&
1179             (c = *old) != '.' && c != '/' && c != '\\')
1180         {   char *p = o;
1181             const char *w;
1182 /*
1183  * I collect the name of the parameter at the end of my file-name buffer,
1184  * but will over-write it later on when I actually do the expansion.
1185  */
1186             if (c == '{')
1187             {   old++;
1188                 n--;
1189                 while (n != 0)
1190                 {   c = *old++;
1191                     n--;
1192                     if (c == '}') break;
1193                     *p++ = (char)c;
1194                 }
1195             }
1196             else
1197             {   while (n != 0)
1198                 {   c = *old;
1199                     if (c == '.' || c == '/' || c == '\\') break;
1200                     old++;
1201                     n--;
1202                     *p++ = (char)c;
1203                 }
1204             }
1205             *p = 0;
1206             i = strlen(o) + 2;
1207             while (i-- != 0) o[i] = o[i-1];
1208             if (look_in_variable != NULL &&
1209                 (p = (*look_in_variable)(o, '@')) != NULL &&
1210                 p != o) o = p;
1211             else
1212             if ((w = my_getenv(o+1)) != NULL)    /* Shell variable? */
1213             {   strcpy(o, w);
1214                 o = o + strlen(o);
1215             }
1216             else if (look_in_variable != NULL &&
1217                      (p = (*look_in_variable)(o, '$')) != NULL)
1218                 o = p;
1219             else
1220             {   *filename = 0;  /* return reporting failure */
1221                 return;
1222             }
1223         }
1224         else *o++ = (char)c;
1225     }
1226     *o = 0;
1227 
1228 
1229 #ifdef WIN32
1230 /*
1231  * Now the filename has had $ and ~ prefix things expanded - I "just"
1232  * need to deal with sub-directory representation issues. Specifically I need
1233  * to map "/" separators into "\" so that if a user presents a file
1234  * name such as aaa/bbb/ccc.d it gets passed to the operating system
1235  * as aaa\bbb\ccc.d
1236  * Note that I enable this code under the heading MS_DOS but really it
1237  * means any file-system (eg Windows too) that uses "\" as its main
1238  * directory separator character.
1239  * As of September 2004 I will also map an intial sequence
1240  *         /cygdrive/x/
1241  * onto    x:\
1242  */
1243 
1244     if (strncmp(filename, "/cygdrive/", 10) == 0 &&
1245                 filename[11] == '/')
1246     {   char *p = filename+2, *tail = filename+11;
1247         filename[0] = filename[10];
1248         filename[1] = ':';
1249         while (*tail != 0) *p++ = *tail++;
1250         *p = 0;
1251     }
1252 /*
1253  * I map "/" characters in MSDOS filenames into "\" so that users
1254  * can give file names with Unix-like slashes as separators if they want.
1255  * People who WANT to use filenames with '/' in them will be hurt.
1256  */
1257     {   int j;
1258         char *tail = filename;
1259         while ((j = *tail) != 0)
1260         {   if (j == '/') *tail = '\\';
1261             tail++;
1262         }
1263 /*
1264  * stat and friends do not like directories referred to as "\foo\", so check
1265  * for a trailing slash, being careful to respect directories with names
1266  * like "\" and "a:\".
1267  */
1268        j = strlen(filename);
1269        if (j > 0 && j != 1 && !(j == 3 && *(filename+1) == ':'))
1270        {
1271            if ( (*(tail - 1) == '\\')) *(tail - 1) = 0;
1272        }
1273     }
1274 #endif /* WIN32 */
1275 #if defined MACINTOSH && defined MAC_FRAMEWORK
1276 /*
1277  * For MacOS the issue of "aliases" arises. The "preferred" file system
1278  * is HFS+ and that supports both links and aliases, but at the very least
1279  * some old users and legacy applications will certainly continue to use
1280  * links. However the Posix-style APIs do not provide any way to deal with
1281  * them! So here I use some Carbon calls to map a path to an alias into
1282  * a path to the file it refers to. Thise code was requested by Thomas
1283  * Sturm who provided a skeleton chunk of code showing what APIs needed to be
1284  * used and references to the documentation to them, so thanks are due.
1285  */
1286     {   char alias[LONGEST_LEGAL_FILENAME];
1287         FSRef ref;
1288         Boolean is_folder, is_alias;
1289 /*
1290  * This works by converting from a path to an FSRef object, which is the Mac
1291  * internal handle. It can then resolve the alias. I use the option that
1292  * will chain through sequences of aliases if necessary until a genuine
1293  * regular file is found. If no aliases has been involved I do nothing.
1294  * If any of the Mac system calls report errors of any sort I do
1295  * nothing.  In the end if all works I convert from an FSRef back to a path and
1296  * copy it to where I want it to be.
1297  */
1298         if (FSPathMakeRef((UInt8 *)filename, &ref, NULL) == noErr &&
1299             FSResolveAliasFile(&ref, TRUE, &is_folder, &is_alias) == noErr &&
1300             is_alias &&
1301             FSRefMakePath(&ref, (UInt8 *)alias, (UInt32)sizeof(alias)) == noErr)
1302         {   strcpy(filename, alias);
1303         }
1304     }
1305 #endif /* MAC stuff */
1306 }
1307 
1308 /*
1309  * datestamps that I store away have given me significant
1310  * trouble with regard to portability - so now I deal with times by
1311  * talking to the system in terms of broken down local time (struct tm).
1312  * I then pack things up for myself to get 32-bit timestamps. The
1313  * encoding I use aims at simplicity - it treats all months as 31 days
1314  * and thus does not have to worry about leap years etc.  The effect will be
1315  * rather as if dates were stored as strings. And MAYBE I thereby avoid
1316  * some of the oddities that arise when data files containing packed dates
1317  * are transported across time-zones.
1318  *
1319  * NOTE: dates here are based from 1970, and this will lead to overflow
1320  * beyond 32-bit offsets in around 2099. At the time of writing that is around
1321  * 100 years ahead, and I intend not to worry. Note it is important here to
1322  * us an unsigned number or else the overflow is sooner and might even cause
1323  * genuine pain!
1324  *
1325  * ANOTHER NOTE: I only allow the "seconds" field to run from 0 to 59.
1326  * In consequence I am quite possibly going to mess up when there are
1327  * leap seconds, and this confusion could make times processed here
1328  * disagree across systems by up to the number of leap seconds that
1329  * have been used to date. Well I have quite severe doubts about time
1330  * agreement closer than a few seconds anyway and so again I am going to
1331  * ignore this oddity! But those who keep systems synchronised at a
1332  * millisecond or microsecond resolution (GPS anybody?) might need to
1333  * know I have been sloppy.
1334  */
1335 
unpack_date(unsigned long int r,int * year,int * mon,int * day,int * hour,int * min,int * sec)1336 void unpack_date(unsigned long int r,
1337                  int *year, int *mon, int *day,
1338                  int *hour, int *min, int *sec)
1339 {
1340     *sec  = r%60; r = r/60;
1341     *min  = r%60; r = r/60;
1342     *hour = r%24; r = r/24;
1343     *day  = r%32; r = r/32;
1344     *mon  = r%12; r = r/12;
1345 /*
1346  * Please note that the Standard C "struct tm" structure specifies dates
1347  * in terms of years since 1900. Thus from the year 2000 on the year will
1348  * be a value of at least 100, but that is not supposed to be any special
1349  * cause of disaster. In particular the calculation involving "+70"
1350  * rather than "+1970" is NOT a bug here!
1351  */
1352     *year = 70+r;
1353 }
1354 
pack_date(int year,int mon,int day,int hour,int min,int sec)1355 unsigned long int pack_date(int year, int mon, int day,
1356                             int hour, int min, int sec)
1357 {
1358     unsigned long int r = (year-70)*12 + mon;
1359     r = r*32 + day;
1360     r = r*24 + hour;
1361     r = r*60 + min;
1362     return r*60 + sec;
1363 }
1364 
1365 typedef struct date_and_type
1366 {
1367     unsigned long int date;
1368     unsigned long int type;
1369 } date_and_type;
1370 
1371 
1372 #ifdef WIN32
1373 /*
1374  * This version is for Windows NT 3.1 with Microsoft VC++, Windows 95, 98,
1375  * NT 3.5, 4.0, 2000, XP etc etc, also with Watcom C, mingw32 and so on.
1376  * Note it uses the native Windows capabilities and so it is not intended
1377  * for use with cygwin and its Unix-portability layer.
1378  */
1379 
1380 #include "windows.h"
1381 
Cmkdir(char * name)1382 int Cmkdir(char *name)
1383 {
1384     SECURITY_ATTRIBUTES s;
1385     s.nLength = sizeof(s);
1386     s.lpSecurityDescriptor = NULL;
1387     s.bInheritHandle = FALSE;
1388     return CreateDirectory(name, &s);
1389 }
1390 
truncate_file(FILE * f,long int where)1391 int truncate_file(FILE *f, long int where)
1392 {
1393     if (fflush(f) != 0) return 1;
1394 #ifdef _MSC_VER
1395     return _chsize(_fileno(f), where);  /* Returns zero if success */
1396 #else
1397 #ifdef __CYGWIN__
1398     if (fflush(f) != 0) return 1;
1399     return ftruncate(fileno(f), where);  /* Returns zero if success */
1400 #else
1401     return chsize(fileno(f), where);    /* Returns zero if success */
1402 #endif
1403 #endif
1404 }
1405 
set_filedate(char * name,unsigned long int datestamp,unsigned long int filetype)1406 void set_filedate(char *name, unsigned long int datestamp,
1407                               unsigned long int filetype)
1408 {
1409     HANDLE h = CreateFile(name, GENERIC_WRITE, 0, NULL,
1410                           OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1411     SYSTEMTIME st;
1412     FILETIME ft;
1413     int yr, mon, day, hr, min, sec;
1414 /*
1415  * Here datestamp is a time expressed (sort of) in seconds since the start
1416  * of 1970. * I need to convert it into a broken-down SYSTEMTIME so that I
1417  * can then re-pack it as a Windows-NT FILETIME....
1418  */
1419     unpack_date(datestamp, &yr, &mon, &day, &hr, &min, &sec);
1420     st.wMilliseconds = 0;
1421     st.wYear = yr + 1900;  /* Windows NT uses full dates since the year 0 */
1422     st.wMonth = mon + 1;
1423     st.wDay = day;
1424     st.wHour = hr;
1425     st.wMinute = min;
1426     st.wSecond = sec;
1427     SystemTimeToFileTime(&st, &ft);
1428     SetFileTime(h, NULL, NULL, &ft);
1429     CloseHandle(h);
1430 }
1431 
put_fileinfo(date_and_type * p,char * name)1432 void put_fileinfo(date_and_type *p, char *name)
1433 {
1434     unsigned long int datestamp, filetype;
1435 #ifdef _MSC_VER
1436     struct _stat file_info;
1437 #else
1438     struct stat file_info;
1439 #endif
1440     struct tm *st;
1441 /*
1442  * Read file parameters...  Maybe I should use a Windows-style not a Unix-style
1443  * call here?
1444  */
1445 #ifdef _MSC_VER
1446     _stat(name, &file_info);
1447 #else
1448     stat(name, &file_info);
1449 #endif
1450     st = localtime(&(file_info.st_mtime));
1451     datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
1452                           st->tm_hour, st->tm_min, st->tm_sec);
1453     filetype = 0xfff;
1454     p->date = datestamp;
1455     p->type = filetype;
1456 }
1457 
1458 #else /* WIN32 */
1459 
1460 /*
1461  * On some Unix variants I may want this declaration inserted and on others
1462  * it would clash with a system-provided header file. Ugh! With luck the C
1463  * compiler will invent a suitable calling convention even if a declaration
1464  * is not present.
1465  * extern ftruncate(int, int);
1466  */
1467 
truncate_file(FILE * f,long int where)1468 int truncate_file(FILE *f, long int where)
1469 {
1470     if (fflush(f) != 0) return 1;
1471     return ftruncate(fileno(f), where);  /* Returns zero if success */
1472 }
1473 
1474 /* extern void mkdir(const char *, unsigned short int); */
1475 
Cmkdir(char * s)1476 int Cmkdir(char *s)
1477 {
1478     mkdir(s, 0775);
1479     return 1;
1480 }
1481 
1482 #include <utime.h>
1483 
1484 #ifdef EMBEDDED
1485 #ifdef __ARM_EABI__
1486 
1487 void utime(const char *s, struct utimbuf *t);
1488 
1489 #endif
1490 #endif
1491 
set_filedate(char * name,unsigned long int datestamp,unsigned long int filetype)1492 void set_filedate(char *name, unsigned long int datestamp,
1493                               unsigned long int filetype)
1494 {
1495 #ifdef UTIME_TIME_T
1496     time_t tt[2];
1497 #else
1498     struct utimbuf tt;
1499 #endif
1500     time_t t0;
1501     struct tm st;
1502     unpack_date(datestamp, &st.tm_year, &st.tm_mon, &st.tm_mday,
1503                            &st.tm_hour, &st.tm_min, &st.tm_sec);
1504     st.tm_isdst = -1;
1505     t0 = mktime(&st);
1506 #ifdef UTIME_TIME_T
1507     tt[0] = tt[1] = t0;
1508 #else
1509     tt.actime = tt.modtime = t0;
1510 #endif
1511     utime(name, &tt);
1512 }
1513 
put_fileinfo(date_and_type * p,char * name)1514 void put_fileinfo(date_and_type *p, char *name)
1515 {
1516     unsigned long int datestamp, filetype;
1517     struct stat file_info;
1518     struct tm *st;
1519 /*
1520  * Read file parameters...
1521  */
1522     stat(name, &file_info);
1523     st = localtime(&(file_info.st_mtime));
1524     datestamp = pack_date(st->tm_year, st->tm_mon, st->tm_mday,
1525                           st->tm_hour, st->tm_min, st->tm_sec);
1526     filetype = 0xfff;  /* should get access status here? */
1527     p->date = datestamp;
1528     p->type = filetype;
1529 }
1530 
1531 #endif /* WIN32 */
1532 
1533 
1534 /*
1535  * If I am to process directories I need a set of routines that will
1536  * scan sub-directories for me.  This is necessarily dependent on
1537  * the operating system I am running under, hence the conditional compilation
1538  * here.  The specification I want is:
1539  *       void scan_directory(const char *dir,
1540  *                    void (*proc)(char *name, int why, long int size));
1541  *
1542  * This is called with a file- or directory-name as its first argument
1543  * and a function as its second.
1544  * It calls the function for every directory and every file that can be found
1545  * rooted from the given place.  If the file to scan is specified as NULL
1546  * the current directory is processed. I also arrange that an input string
1547  * "." (on Windows, DOS and Unix) or "@" (Archimedes) is treated as a request
1548  * to scan the whole of the current directory.
1549  * When a simple file is found the procedure is called with the name of the
1550  * file, why=0, and the length (in bytes) of the file.  For a directory
1551  * the function is called with why=1, then the contents of the directory are
1552  * processed. For directories the size information will be 0.  There is no
1553  * guarantee of useful behaviour if some of the files to be scanned are
1554  * flagged as  "invisible" or "not readable" or if they are otherwise special.
1555  *
1556  * I also provide a similar function scan_files() with the same arguments that
1557  * does just the same except that it does not recurse into sub-directories,
1558  * but if the name originally passed is that of a directory then all the
1559  * files in it will be scanned.
1560  */
1561 
1562 /*
1563  * When scan_directory calls the procedure it has been passed, it will have
1564  * set scan_leafstart to the offset in the passed filename where the
1565  * original directory ended and the new information starts.
1566  */
1567 
1568 int scan_leafstart = 0;
1569 
1570 /*
1571  * For CSL's purposes the following 3 are in syscsl.h, but in general I do not
1572  * want to use that header with random fwin applications...
1573  */
1574 #define SCAN_FILE       0
1575 #define SCAN_STARTDIR   1
1576 #define SCAN_ENDDIR     2
1577 
1578 /*
1579  * I use a (static) flag to indicate how sub-directories should be
1580  * handled, and what to do about case. By default I fold to lower case
1581  * on windows. setting hostcase non-zero causes case to be preserved.
1582  */
1583 
1584 static int recursive_scan, hostcase = 0;
1585 
set_hostcase(int fg)1586 void set_hostcase(int fg)
1587 {
1588     hostcase = fg;
1589 }
1590 
1591 #ifdef WIN32
1592 
1593 /* Hmm - buffer overflow worry with the next line! */
1594 static char filename[LONGEST_LEGAL_FILENAME];
1595 
1596 static WIN32_FIND_DATA *found_files = NULL;
1597 static int n_found_files = 0, max_found_files = 0;
1598 
1599 #define TABLE_INCREMENT 50
1600 
more_files(void)1601 static int more_files(void)
1602 {
1603     if (n_found_files > max_found_files - 5)
1604     {   WIN32_FIND_DATA *fnew = (WIN32_FIND_DATA *)
1605             realloc((void *)found_files,
1606                     sizeof(WIN32_FIND_DATA)*
1607                        (max_found_files + TABLE_INCREMENT));
1608         if (fnew == NULL) return 1;  /* failure flag */
1609         found_files = fnew;
1610         max_found_files += TABLE_INCREMENT;
1611     }
1612     return 0;
1613 }
1614 
1615 /*
1616  * Anybody compiling using Microsoft Visual C++ had better note that
1617  * the type declared in the Microsoft header files for qsort insists
1618  * on a __cdecl here. Ugh.
1619  */
alphasort_files(const void * a,const void * b)1620 int MS_CDECL alphasort_files(const void *a, const void *b)
1621 {
1622     const WIN32_FIND_DATA *fa = (const WIN32_FIND_DATA *)a,
1623                           *fb = (const WIN32_FIND_DATA *)b;
1624     return strncmp(fb->cFileName, fa->cFileName, sizeof(fa->cFileName));
1625 }
1626 
exall(int namelength,void (* proc)(const char * name,int why,long int size))1627 static void exall(int namelength,
1628                   void (*proc)(const char *name, int why, long int size))
1629 /*
1630  * This procedure scans a directory-full of files, calling the given procedure
1631  * to process each one it finds.
1632  */
1633 {
1634 #ifdef EMBEDDED
1635     printf("exall function called - but not implemented here\n");
1636     return; /* Dummy version here */
1637 #else
1638     WIN32_FIND_DATA found;
1639     int rootlen = namelength, first = n_found_files;
1640     HANDLE hSearch = FindFirstFile(filename, &found);
1641     if (hSearch == INVALID_HANDLE_VALUE) return;  /* No files found at all */
1642     for (;;)
1643     {   if (more_files()) break;
1644         found_files[n_found_files++] = found;
1645         if (!FindNextFile(hSearch, &found)) break;
1646     }
1647     FindClose(hSearch);
1648     qsort((void *)&found_files[first],
1649           n_found_files-first,
1650           sizeof(WIN32_FIND_DATA),
1651           alphasort_files);
1652     while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
1653     while (n_found_files != first)
1654     {   char *p = (char *)&found_files[--n_found_files].cFileName;
1655         int c;
1656 /*
1657  * Fill out filename with the actual name I grabbed, i.e. with
1658  * wild-cards expanded.
1659  */
1660         namelength = rootlen+1;
1661 /*
1662  * I fold DOS filenames into lower case because it does not matter much
1663  * to DOS and I think it looks better - furthermore it helps when I move
1664  * archives to other systems.  So I do the same on NT.
1665  */
1666         while ((c = *p++) != 0)
1667         {   if (!hostcase) if (isupper(c)) c = tolower(c);
1668             filename[namelength++] = (char)c;
1669         }
1670         filename[namelength] = 0;
1671         if (found_files[n_found_files].dwFileAttributes &
1672             FILE_ATTRIBUTE_DIRECTORY)
1673         {   if (found_files[n_found_files].cFileName[0] != '.')
1674 /*
1675  * I filter out directory names that start with '.'.
1676  * This is to avoid calamity with recursion though chains such as .\.\.\.....
1677  */
1678             {   proc(filename, SCAN_STARTDIR, 0);
1679                 if (!recursive_scan) continue;
1680 
1681                 strcpy(&filename[namelength], "\\*.*");
1682 /*
1683  * Append "\*.*" to the directory-name and try again, thereby scanning
1684  * its contents.
1685  */
1686                 exall(namelength+4, proc);
1687                 filename[namelength] = 0;
1688                 proc(filename, SCAN_ENDDIR, 0);
1689             }
1690         }
1691         else proc(filename, SCAN_FILE,
1692                   found_files[n_found_files].nFileSizeLow);
1693     }
1694     return;
1695 #endif /* EMBEDDED */
1696 }
1697 
scan_directory(const char * dir,void (* proc)(const char * name,int why,long int size))1698 void scan_directory(const char *dir,
1699                     void (*proc)(const char *name, int why, long int size))
1700 {
1701     recursive_scan = 1;
1702     if (dir==NULL || strcmp(dir,".")==0)
1703     {   dir = "*.*";
1704         scan_leafstart = 0;
1705     }
1706     else scan_leafstart = strlen(dir)+1;
1707     strcpy(filename, dir);
1708     exall(strlen(filename), proc);
1709 }
1710 
scan_files(const char * dir,void (* proc)(const char * name,int why,long int size))1711 void scan_files(const char *dir,
1712                 void (*proc)(const char *name, int why, long int size))
1713 {
1714     recursive_scan = 0;
1715     if (dir==NULL || strcmp(dir,".")==0)
1716     {   strcpy(filename, "*.*");
1717         scan_leafstart = 0;
1718     }
1719     else
1720     {   scan_leafstart = strlen(dir);
1721         strcpy(filename, dir);
1722         if (filename[scan_leafstart-1] == '\\')
1723         {   /* Root directory */
1724             strcpy(filename+scan_leafstart, "*.*");
1725             --scan_leafstart;
1726         }
1727         else strcpy(filename+scan_leafstart, "\\*.*");
1728         scan_leafstart++;
1729     }
1730     exall(strlen(filename), proc);
1731 }
1732 
1733 #else  /* WIN32 */
1734 
1735 static char filename[LONGEST_LEGAL_FILENAME];
1736 
1737 /*
1738  * The code here uses opendir, readdir and closedir and as such ought to
1739  * be Posix compatible. The macro USE_DIRECT_H can cause an older variant
1740  * on this idea to be used. BUt it may need adjustment for different
1741  * systems.
1742  */
1743 
1744 
1745 static char **found_files = NULL;
1746 
1747 int n_found_files = 0, max_found_files = 0;
1748 
1749 #define TABLE_INCREMENT 50
1750 
more_files(void)1751 static int more_files(void)
1752 {
1753     if (n_found_files > max_found_files - 5)
1754     {   char **fnew = (char **)
1755             realloc((void *)found_files,
1756                     sizeof(char *) *
1757                        (max_found_files + TABLE_INCREMENT));
1758         if (fnew == NULL) return 1;  /* failure flag */
1759         found_files = fnew;
1760         max_found_files += TABLE_INCREMENT;
1761     }
1762     return 0;
1763 }
1764 
alphasort_files(const void * a,const void * b)1765 int alphasort_files(const void *a, const void *b)
1766 {
1767     const char *fa = *(const char **)a,
1768                *fb = *(const char **)b;
1769     return strcmp(fb, fa);
1770 }
1771 
1772 static void scan_file(int namelength,
1773                       void (*proc)(const char *name, int why, long int size));
1774 
exall(int namelength,void (* proc)(const char * name,int why,long int size))1775 static void exall(int namelength,
1776                   void (*proc)(const char *name, int why, long int size))
1777 {
1778 #ifdef EMBEDDED
1779     printf("exall function called - but not implemented here\n");
1780     return; /* Dummy version here */
1781 #else
1782     DIR *d;
1783 #ifdef USE_DIRECT_H
1784     struct direct *dd;
1785 #else
1786     struct dirent *dd;
1787 #endif
1788     int rootlen = namelength, first = n_found_files;
1789     proc(filename, SCAN_STARTDIR, 0);
1790     d = opendir(filename);
1791     if (d != NULL)
1792     {   while ((dd = readdir(d)) != NULL)
1793         {   char *leafname = dd->d_name;
1794             char *copyname;
1795 /*
1796  * readdir hands back both "." and ".." but I had better not recurse
1797  * into either!
1798  */
1799             if (strcmp(leafname, ".") == 0 ||
1800                 strcmp(leafname, "..") == 0) continue;
1801             if (more_files()) break;
1802             copyname = (char *)malloc(1+strlen(leafname));
1803             if (copyname == NULL) break;
1804             strcpy(copyname, leafname);
1805             found_files[n_found_files++] = copyname;
1806         }
1807         closedir(d);
1808     }
1809 
1810     qsort((void *)&found_files[first],
1811           n_found_files-first,
1812           sizeof(char *),
1813           alphasort_files);
1814 
1815     filename[rootlen] = '/';
1816     while (n_found_files != first)
1817     {   char *p = found_files[--n_found_files];
1818         int c;
1819         namelength = rootlen+1;
1820         while ((c = *p++) != 0) filename[namelength++] = (char)c;
1821         free((void *)found_files[n_found_files]);
1822         filename[namelength] = 0;
1823         scan_file(namelength, proc);
1824     }
1825 
1826     filename[rootlen] = 0;
1827     proc(filename, SCAN_ENDDIR, 0);
1828 #endif /* EMBEDDED */
1829 }
1830 
1831 #ifndef S_IFMT
1832 # ifdef __S_IFMT
1833 #  define S_IFMT __S_IFMT
1834 # endif
1835 #endif
1836 
1837 #ifndef S_IFDIR
1838 # ifdef __S_IFDIR
1839 #  define S_IFDIR __S_IFDIR
1840 # endif
1841 #endif
1842 
1843 #ifndef S_IFREG
1844 # ifdef __S_IFREG
1845 #  define S_IFREG __S_IFREG
1846 # endif
1847 #endif
1848 
scan_file(int namelength,void (* proc)(const char * name,int why,long int size))1849 static void scan_file(int namelength,
1850                       void (*proc)(const char *name, int why, long int size))
1851 {
1852     struct stat buf;
1853     stat(filename, &buf);
1854     if ((buf.st_mode & S_IFMT) == S_IFDIR)
1855     {   if (!recursive_scan) proc(filename, SCAN_STARTDIR, 0);
1856         else exall(namelength, proc);
1857     }
1858     else if ((buf.st_mode & S_IFMT) == S_IFREG)
1859         proc(filename, SCAN_FILE, buf.st_size);
1860 /*  else fprintf(stderr, "Mode of %s is %o\n", filename, buf.st_mode); */
1861 }
1862 
scan_directory(const char * dir,void (* proc)(const char * name,int why,long int size))1863 void scan_directory(const char *dir,
1864                     void (*proc)(const char *name, int why, long int size))
1865 {
1866     recursive_scan = 1;
1867     if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
1868     scan_leafstart = strlen(dir)+1;
1869     strcpy(filename, dir);
1870     scan_file(scan_leafstart-1, proc);
1871 }
1872 
scan_files(const char * dir,void (* proc)(const char * name,int why,long int size))1873 void scan_files(const char *dir,
1874                 void (*proc)(const char *name, int why, long int size))
1875 {
1876     recursive_scan = 0;
1877     if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
1878     scan_leafstart = strlen(dir)+1;
1879     strcpy(filename, dir);
1880     exall(scan_leafstart-1, proc);
1881 }
1882 
1883 #endif /* WIN32 */
1884 
1885 
open_file(char * filename,char * old,size_t n,char * mode,FILE * old_file)1886 FILE *open_file(char *filename, char *old, size_t n,
1887                 char *mode, FILE *old_file)
1888 {
1889 /*
1890  * mode is something like "r" or "w" or "rb", as needed by fopen(),
1891  * and old_file is NULL normally, but can be a (FILE *) to indicate
1892  * the use of freopen rather than fopen.
1893  */
1894     FILE *ff;
1895     process_file_name(filename, old, n);
1896     if (*filename == 0) return NULL;
1897     if (old_file == NULL) ff = fopen(filename, mode);
1898     else ff = freopen(filename, mode, old_file);
1899 /*
1900  * In suitable cases when the first attempt to open the file fails I
1901  * will try creating any necessary directories and then try again.
1902  */
1903     if (ff==NULL && *mode=='w')
1904     {   char *p = filename;
1905         while (*p != 0)
1906         {   int ch = *p;
1907             if (ch == '/' || ch == '\\')
1908             {   *p = 0;
1909                 Cmkdir(filename);
1910                 *p = ch;
1911             }
1912             p++;
1913         }
1914         if (old_file == NULL) ff = fopen(filename, mode);
1915         else ff = freopen(filename, mode, old_file);
1916     }
1917     return ff;
1918 }
1919 
1920 
1921 static char err_buf[LONGEST_LEGAL_FILENAME+100];
1922 
change_directory(char * filename,char * old,size_t n)1923 char *change_directory(char *filename, char *old, size_t n)
1924 {
1925     process_file_name(filename, old, n);
1926     if (*filename == 0)
1927     {   sprintf(err_buf, "Filename \"%s\" invalid.", old);
1928         return err_buf;
1929     }
1930     if (chdir(filename))
1931     {   const char *msg;
1932         switch (errno)
1933         {
1934     case ENOTDIR:
1935             msg = "A component of %s is not a directory.";
1936             break;
1937     case ENOENT:
1938             msg = "The directory %s does not exist.";
1939             break;
1940     case EACCES:
1941             msg = "Insufficient permission for %s.";
1942             break;
1943     case ENAMETOOLONG:
1944             msg = "The pathname %s is too long.";
1945             break;
1946     default:
1947             msg = "Cannot change directory to %s.";
1948             break;
1949        }
1950        sprintf(err_buf, msg, filename);
1951        return err_buf;
1952     }
1953     else return NULL;
1954 }
1955 
create_directory(char * filename,char * old,size_t n)1956 int create_directory(char *filename, char *old, size_t n)
1957 {
1958     process_file_name(filename, old, n);
1959     if (*filename == 0) return 1;
1960     return Cmkdir(filename);
1961 }
1962 
remove_files(const char * name,int dirp,long int size)1963 static void remove_files(const char *name, int dirp, long int size)
1964 /* Remove a file, or a directory and all its contents */
1965 {
1966     switch (dirp)
1967     {
1968 case 0:               /* SCAN_FILE */
1969       remove(name);
1970       return;
1971 case 2:               /* SCAN_ENDDIR */
1972       rmdir(name);
1973       return;
1974 default:              /* 1 == SCAN_STARTDIR */
1975       return;
1976     }
1977 }
1978 
delete_file(char * filename,char * old,size_t n)1979 int delete_file(char *filename, char *old, size_t n)
1980 {
1981     process_file_name(filename, old, n);
1982     if (*filename == 0) return 0;
1983     /*
1984      * We cannot simply use remove here, since this will not
1985      * work with directories and their contents.  Hence the
1986      * use of scan_directory.
1987      */
1988     scan_directory(filename, remove_files);
1989     return 0;
1990 }
1991 
1992 
file_length(char * filename,char * old,size_t n)1993 long file_length(char *filename, char *old, size_t n)
1994 {
1995     struct stat buf;
1996     process_file_name(filename, old, n);
1997     if (*filename == 0) return 0;
1998     if (stat(filename,&buf) == -1) return -1;
1999     return (long)(buf.st_size);
2000 }
2001 
2002 #ifdef NAG_VERSION
2003 
list_directory_members(char * filename,char * old,char ** filelist[],size_t n)2004 int list_directory_members(char *filename, char *old, char **filelist[],
2005                            size_t n)
2006 {   struct dirent **namelist;
2007     int number_of_entries, i;
2008     char **files;
2009 
2010     process_file_name(filename, old, n);
2011 
2012     /* scandir expects "." for the current directory */
2013     if (*filename == 0) number_of_entries = scandir(".",&namelist,NULL,NULL);
2014     else number_of_entries = scandir(filename,&namelist,NULL,NULL);
2015 
2016     /*
2017      * If the scandir failed then return now, since we make an assumption later
2018      * that we found at least two entries: "." and "..".
2019      */
2020     if (number_of_entries == -1) return -1;
2021 
2022     files=(char **)malloc(number_of_entries*sizeof(char *));
2023 
2024     for (i=0;i<number_of_entries;++i)
2025     {   files[i] = strdup(namelist[i]->d_name);
2026         free(namelist[i]);
2027     }
2028 
2029     free(namelist);
2030 
2031     *filelist = files;
2032 
2033     /*
2034      * When we return we will prepend the directory name to the files, so we
2035      * must make sure it is suitable for that.  This is done here since it is
2036      * platform dependent (i.e. in DOS we would need to ensure the last
2037      * character was "\").
2038      */
2039     /*
2040     i=strlen(filename);
2041     if (i > 0 && filename[i-1] != '/')
2042     {   filename[i]='/';
2043         filename[i+1]='\0';
2044     }
2045     */
2046 
2047     return number_of_entries;
2048 }
2049 
2050 #else
2051 
2052 
list_directory_members(char * filename,char * old,size_t n,void (* fn)(const char * name,int why,long int size))2053 void list_directory_members(char *filename, char *old,
2054                             size_t n,
2055                             void (*fn)(const char *name, int why, long int size))
2056 {
2057     process_file_name(filename, old, n);
2058     scan_files(filename, fn);
2059 }
2060 
2061 #endif
2062 
2063 
file_exists(char * filename,char * old,size_t n,char * tt)2064 int file_exists(char *filename, char *old, size_t n, char *tt)
2065 /*
2066  * This returns YES if the file exists, and as a side-effect copies a
2067  * textual form of the last-changed-time of the file into the buffer tt.
2068  */
2069 {
2070     struct stat statbuff;
2071     process_file_name(filename, old, n);
2072     if (*filename == 0) return 0;
2073     if (stat(filename, &statbuff) != 0) return 0;
2074     strcpy(tt, ctime(&(statbuff.st_mtime)));
2075     return 1;
2076 }
2077 
directoryp(char * filename,char * old,size_t n)2078 int directoryp(char *filename, char *old, size_t n)
2079 {
2080     struct stat buf;
2081     process_file_name(filename, old, n);
2082     if (*filename == 0) return 0;
2083     if (stat(filename,&buf) == -1) return 0;
2084     return ((buf.st_mode & S_IFMT) == S_IFDIR);
2085 }
2086 
get_truename(char * filename,char * old,size_t n)2087 char *get_truename(char *filename, char *old, size_t n)
2088 {
2089     struct stat buf;
2090     char *temp, *fn, *dir;
2091     char pwd[LONGEST_LEGAL_FILENAME];
2092 
2093     process_file_name(filename, old, n);
2094     if (*filename == 0)
2095     {   strcpy(filename, "truename");
2096         return NULL;
2097     }
2098 
2099     /* Find out whether we have a file or a directory */
2100     if (stat(filename,&buf) == -1)
2101     {   strcpy(filename, "truename: cannot stat file");
2102         return NULL;
2103     }
2104 
2105     /* Store current directory */
2106     if (get_current_directory(pwd, LONGEST_LEGAL_FILENAME) < 0)
2107     {   strcpy(filename, "truename: cannot get current working directory");
2108         return NULL;
2109     }
2110 
2111     if ((buf.st_mode & S_IFMT) == S_IFDIR)
2112     {   /* We have a directory */
2113         char *dir = (char*)malloc(LONGEST_LEGAL_FILENAME);
2114         if (chdir(filename) != 0)
2115         {   strcpy(filename, "truename: cannot change directory");
2116             return NULL;
2117         }
2118         if (getcwd(dir,LONGEST_LEGAL_FILENAME) == NULL)
2119         {   strcpy(filename, "truename: cannot get current working directory");
2120             return NULL;
2121         }
2122 
2123         if (chdir(pwd) != 0)
2124         {   strcpy(filename, "truename: cannot change directory");
2125             return NULL;
2126         }
2127 /*
2128  * Axiom-specific hack: truename preserves '/' at the end of
2129  * a path
2130  */
2131         if (old[n-1] == '/' && dir[strlen(dir)-1] != '/')
2132         {   n = strlen(dir);
2133             dir[n]   = '/';
2134             dir[n+1] = '\0';
2135         }
2136         return dir;
2137     }
2138     else
2139     {   /* Assume we have some kind of file */
2140         temp = strrchr(filename,'/');
2141         if (temp)
2142         {   /* Found a directory component */
2143             char theDir[LONGEST_LEGAL_FILENAME];
2144             fn   = (char *)malloc(1+strlen(temp));
2145             strcpy(fn, temp);
2146             *temp = '\0';
2147             /* fn is now "/file" and filename is the directory */
2148 
2149             if (chdir(filename) != 0)
2150             {   strcpy(filename, "truename: cannot change directory");
2151                 return NULL;
2152             }
2153             if (get_current_directory(theDir, LONGEST_LEGAL_FILENAME) < 0)
2154             {   strcpy(filename, "truename: cannot get current working directory");
2155                 return NULL;
2156             }
2157             temp = theDir;
2158             if (chdir(pwd) != 0)
2159             {   strcpy(filename, "truename: cannot change directory");
2160                 return NULL;
2161             }
2162             dir = (char *)malloc((strlen(temp) + strlen(fn) + 1)*sizeof(char));
2163             if (dir == NULL)
2164             {   strcpy(filename, "truename: run out of memory");
2165                 return NULL;
2166             }
2167             strcpy(dir, temp);
2168             strcat(dir, fn);
2169             free(fn);
2170             return dir;
2171         }
2172         else
2173         {   dir = (char *)malloc((strlen(pwd) + strlen(filename) + 2)*sizeof(char));
2174             if (dir == NULL)
2175             {   strcpy(filename, "truename: run out of memory");
2176                 return NULL;
2177             }
2178             strcpy(dir, pwd);
2179             strcat(dir, "/");
2180             strcat(dir, filename);
2181             return dir;
2182         }
2183     }
2184 }
2185 
2186 /*
2187  * The tests here are probably rather WRONG_MINDED in that they check the
2188  * status of the file and report whether its OWNER could read, write or
2189  * execute it, rather than whether the current user could. However what
2190  * I do here will hold the fort for now.
2191  */
2192 
file_readable(char * filename,char * old,size_t n)2193 int file_readable(char *filename, char *old, size_t n)
2194 {
2195     struct stat buf;
2196     process_file_name(filename, old, n);
2197     if (*filename == 0) return 0;
2198     if (stat(filename,&buf) == -1)
2199       return 0; /* File probably does not exist */
2200 /*
2201  * The #ifdef here is a cop-out and has surfaced while trying to build
2202  * using the Microsoft C compiler, where there will be a different API I
2203  * could use to get this information...
2204  */
2205 #ifndef S_IRUSR
2206     return 1;
2207 #else
2208     return (buf.st_mode & S_IRUSR);
2209 #endif
2210 }
2211 
2212 
file_writeable(char * filename,char * old,size_t n)2213 int file_writeable(char *filename, char *old, size_t n)
2214 {
2215     struct stat buf;
2216     process_file_name(filename, old, n);
2217     if (*filename == 0) return 0;
2218     if (stat(filename,&buf) == -1)
2219       return 0; /* Should we check to see if the directory is writeable? */
2220 #ifndef S_IWUSR
2221     return 1;
2222 #else
2223     return (buf.st_mode & S_IWUSR);
2224 #endif
2225 }
2226 
2227 
file_executable(char * filename,char * old,size_t n)2228 int file_executable(char *filename, char *old, size_t n)
2229 {
2230     struct stat buf;
2231     process_file_name(filename, old, n);
2232     if (*filename == 0) return 0;
2233     if (stat(filename,&buf) == -1)
2234       return 0; /* Should we check to see if the directory is writeable? */
2235 #ifndef S_IXUSR
2236     return 1;
2237 #else
2238     return (buf.st_mode & S_IXUSR);
2239 #endif
2240 }
2241 
2242 
rename_file(char * from_name,char * from_old,size_t from_size,char * to_name,char * to_old,size_t to_size)2243 int rename_file(char *from_name, char *from_old, size_t from_size,
2244                 char *to_name, char *to_old, size_t to_size)
2245 {
2246     process_file_name(from_name, from_old, from_size);
2247     process_file_name(to_name, to_old, to_size);
2248     if (*from_name == 0 || *to_name == 0) return 0;
2249     return rename(from_name,to_name);
2250 }
2251 
2252 /*
2253  * getenv() is a mild pain: Windows seems
2254  * to have a strong preference for upper case names.  To allow for
2255  * all this I do not call getenv() directly but go via the following
2256  * code that can patch things up.
2257  */
2258 
my_getenv(const char * s)2259 const char *my_getenv(const char *s)
2260 {
2261 #ifdef WIN32
2262     char uppercasename[LONGEST_LEGAL_FILENAME];
2263     char *p = uppercasename;
2264     int c;
2265     while ((c = *s++) != 0) *p++ = toupper(c);
2266     *p = 0;
2267     return getenv(uppercasename);
2268 #else
2269     return getenv(s);
2270 #endif
2271 }
2272 
2273 
my_system(char * s)2274 int my_system(char *s)
2275 {
2276     return system(s);
2277 }
2278 
2279 #define DO_NOT_USE_GETUID 1   /* For MinGW */
2280 
2281 #ifndef DO_NOT_USE_GETUID
2282 /*
2283  * "machine.h" should set DO_NOT_USE_GETUID if that function is not
2284  * properly available. Not having it will make the treatment of
2285  * (eg) "~xxx/..." in filenames less satisfactory.
2286  */
2287 
2288 #include <pwd.h>
2289 
get_home_directory(char * b,int len)2290 int get_home_directory(char *b, int len)
2291 {
2292     int i;
2293     struct passwd *pw = getpwuid(getuid());
2294     strcpy(b, pw->pw_dir);
2295     i = strlen(b);
2296 /* Here the directory handed back has "/" forced in as its final character */
2297     if ( b[i-1] != '/')
2298     {   b[i++] = '/';
2299         b[i] = 0;
2300     }
2301     return i;
2302 }
2303 
get_users_home_directory(char * b,int len)2304 int get_users_home_directory(char *b, int len)
2305 {
2306     struct passwd *pw = getpwnam(b);
2307     if (pw != NULL) strcpy(b, pw->pw_dir);
2308     else strcpy(b, ".");    /* use current directory if getpwnam() fails */
2309     return strlen(b);
2310 }
2311 
2312 #else /* USE_GETUID */
2313 
get_home_directory(char * b,int len)2314 int get_home_directory(char *b, int len)
2315 {
2316     int i;
2317     strcpy(b, getenv("HOME"));  /* Probably works with most shells */
2318     i = strlen(b);
2319     if ( b[i-1] != '/')
2320     {   b[i++] = '/';
2321         b[i] = 0;
2322     }
2323     return i;
2324 }
2325 
get_users_home_directory(char * b,int len)2326 int get_users_home_directory(char *b, int len)
2327 {
2328     strcpy(b, ".");    /* use current directory if getpwnam() no available */
2329     return 1;
2330 }
2331 
2332 #endif /* USE_GETUID */
2333 
2334 #endif /* HAVE_LIBFOX */
2335 
2336 #ifdef EMBEDDED
2337 #ifdef __ARM_EABI__
2338 
rmdir(const char * s)2339 int rmdir(const char *s)
2340 {
2341     return 0;
2342 }
2343 
getcwd(char * s,size_t n)2344 char *getcwd(char *s, size_t n)
2345 {
2346     return ".";
2347 }
2348 
chdir(const char * s)2349 int chdir(const char *s)
2350 {
2351     return 0;
2352 }
2353 
getuid()2354 uid_t getuid()
2355 {
2356     return 100;
2357 }
2358 
getpwuid(int x)2359 struct passwd *getpwuid(int x)
2360 {
2361     return NULL;
2362 }
2363 
utime(const char * s,struct utimbuf * t)2364 void utime(const char *s, struct utimbuf *t)
2365 {
2366 }
2367 
ftruncate(int a,long b)2368 int ftruncate(int a, long b)
2369 {
2370     return 0;
2371 }
2372 
lstat(char * n,struct stat b)2373 int lstat(char *n, struct stat b)
2374 {
2375     return 0;
2376 }
2377 
geteuid()2378 uid_t geteuid()
2379 {
2380     return 0;
2381 }
2382 
getegid()2383 gid_t getegid()
2384 {
2385     return 0;
2386 }
2387 
mkdir(const char * d,mode_t m)2388 int mkdir(const char *d, mode_t m)
2389 {
2390 }
2391 
popen(const char * s,const char * d)2392 FILE *popen(const char *s, const char *d)
2393 {
2394     return NULL;
2395 }
2396 
pclose(FILE * f)2397 int pclose(FILE *f)
2398 {
2399     return 0;
2400 }
2401 
readlink(const char * name,char * b,size_t n)2402 int readlink(const char *name, char *b, size_t n)
2403 {
2404     return 0;
2405 }
2406 
2407 
2408 #endif
2409 #endif
2410 
2411 /* end of fwin.c */
2412