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