source: trunk/emx/src/emxstack/emxstack.c@ 3232

Last change on this file since 3232 was 936, checked in by bird, 22 years ago

Don't use EMX specific getopt when the standard one will do.

  • Property cvs2svn:cvs-rev set to 1.6
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 15.2 KB
Line 
1/* emxstack.c -- Fix the stack size
2 Copyright (c) 1994-1998 Eberhard Mattes
3
4This file is part of emxstack.
5
6emxstack is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emxstack is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emxstack; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
20
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <stdarg.h>
25#include <string.h>
26#include <getopt.h>
27#include <sys/dirtree.h>
28#include "defs.h"
29
30/* How to open a file. This type is used for my_open(). */
31
32enum open_mode
33{
34 open_read, open_read_write
35};
36
37/* A file. F is the stream. NAME is the name of the file. */
38
39struct file
40{
41 FILE *f;
42 const char *name;
43};
44
45
46/* Mode of operation. */
47
48static enum op
49{
50 op_none, /* No command specified */
51 op_show, /* Display stack size */
52 op_check, /* Check whether fix is required */
53 op_fix, /* Fix stack size */
54 op_update, /* Update stack size */
55 op_force /* Set stack size */
56} operation;
57
58/* This variable is 2 if verbose output is requested (-v), 1 by
59 default, and 0 if emxstack should be quiet (-q). */
60
61static int verbosity;
62
63/* New stack size. */
64
65static long new_stack_size;
66
67/* Minimum stack size. */
68
69static long min_stack_size;
70
71
72/* Combine two 16-bit words (low word L and high word H) into a 32-bit
73 word. */
74
75#define COMBINE(L,H) ((L) | (long)((H) << 16))
76
77/* Round up to the next integral multiple of the page size. */
78
79#define ROUND_PAGE(X) ((((X) - 1) & ~0xfff) + 0x1000)
80
81
82static void error (const char *fmt, ...) NORETURN2;
83
84
85/* Print an error message and abort. This function is called like
86 printf() but never returns. The message will be prefixed with
87 "emxbind: " and a newline will be added at the end. */
88
89static void error (const char *fmt, ...)
90{
91 va_list arg_ptr;
92
93 va_start (arg_ptr, fmt);
94 fprintf (stderr, "emxstack: ");
95 vfprintf (stderr, fmt, arg_ptr);
96 fputc ('\n', stderr);
97 exit (2);
98}
99
100
101/* Print an error message and abort. This function is called like
102 printf() but never returns. The message will be prefixed with the
103 file name NAME and a newline will be added at the end. */
104
105static void file_error (const char *fmt, const char *name, ...)
106{
107 va_list arg_ptr;
108
109 va_start (arg_ptr, name);
110 fprintf (stderr, "%s: ", name);
111 vfprintf (stderr, fmt, arg_ptr);
112 fputc ('\n', stderr);
113 exit (2);
114}
115
116
117/* Display a warning message. If the -c or -f commands are used
118 without -v, the message is supressed. */
119
120static void file_warning (const char *fmt, const char *name, ...)
121{
122 va_list arg_ptr;
123
124 if (verbosity >= 2 || (operation != op_check && operation != op_fix))
125 {
126 va_start (arg_ptr, name);
127 fprintf (stderr, "%s: ", name);
128 vfprintf (stderr, fmt, arg_ptr);
129 fputc ('\n', stderr);
130 }
131}
132
133
134/* Read SIZE bytes from the file F to the buffer DST. If there aren't
135 SIZE bytes at the current location of the read/write pointer, an
136 error message will be displayed and the program will be
137 terminated. */
138
139static void my_read (void *dst, size_t size, struct file *f)
140{
141 if (fread (dst, 1, size, f->f) != size)
142 {
143 if (ferror (f->f))
144 file_error ("%s", f->name, strerror (errno));
145 else
146 file_error ("End of file reached", f->name);
147 }
148}
149
150
151/* Write SIZE bytes from SRC to the file F. */
152
153static void my_write (const void *src, size_t size, struct file *f)
154{
155 if (fwrite (src, 1, size, f->f) != size)
156 file_error ("%s", f->name, strerror (errno));
157}
158
159
160/* Open the file named NAME and let F reference that file. OMODE is
161 the open mode. The file is always opened in binary mode. */
162
163static void my_open (struct file *f, const char *name, enum open_mode omode)
164{
165 const char *ms;
166
167 switch (omode)
168 {
169 case open_read:
170 ms = "rb";
171 break;
172 case open_read_write:
173 ms = "r+b";
174 break;
175 default:
176 ms = NULL;
177 break;
178 }
179 f->name = name;
180 f->f = fopen (name, ms);
181 if (f->f == NULL)
182 file_error ("%s", name, strerror (errno));
183}
184
185
186/* Close the file F if it is open. */
187
188static void my_close (struct file *f)
189{
190 if (fflush (f->f) != 0 || fclose (f->f) != 0)
191 file_error ("%s", f->name, strerror (errno));
192}
193
194
195/* Set the read/write pointer of the file F to location POS. */
196
197static void my_seek (struct file *f, long pos)
198{
199 if (fseek (f->f, pos, SEEK_SET) != 0)
200 file_error ("%s", f->name, strerror (errno));
201}
202
203
204/* Return the length of the file F. Note that this function moves the
205 read/write pointer to the end of the file. */
206
207static long my_size (struct file *f)
208{
209 long n;
210
211 if (fseek (f->f, 0L, SEEK_END) != 0 || (n = ftell (f->f)) < 0)
212 error ("seek failed (`%s')", f->name);
213 return n;
214}
215
216
217/* Tell the user how to use this program. */
218
219static void usage (void)
220{
221 fputs ("emxstack " VERSION INNOTEK_VERSION " -- "
222 "Copyright (c) 1994-1995 by Eberhard Mattes\n\n", stderr);
223 fputs ("Usage: emxstack <command> [<options>] <file>...\n", stderr);
224 fputs ("\nCommands:\n", stderr);
225 fputs ("-c Check whether stack size should be fixed\n", stderr);
226 fputs ("-d Display stack size\n", stderr);
227 fputs ("-f Fix stack size for emx 0.9\n", stderr);
228 fputs ("-s<n> Set stack size to <n> Kbyte\n", stderr);
229 fputs ("-u<n> Update stack size to <n> Kbyte (if less than <n> Kbyte)\n",
230 stderr);
231 fputs ("\nOptions:\n", stderr);
232 fputs ("-p Act on all files in PATH\n", stderr);
233 fputs ("-q Be quiet\n", stderr);
234 fputs ("-v Be verbose\n", stderr);
235 exit (1);
236}
237
238
239/* Process the file F. */
240
241static void process_file (struct file *f)
242{
243 struct exe1_header h1;
244 struct exe2_header h2;
245 struct os2_header ho;
246 struct object stack_obj, temp_obj;
247 long off_new_exe, off_stack_obj, size;
248 long stack_size, stack_kb;
249 dword i;
250 int uses_emx;
251 byte buf[2];
252
253 /* Read the DOS EXE header. */
254
255 size = my_size (f);
256 if (size < sizeof (h1))
257 {
258 file_warning ("Not an executable file", f->name);
259 return;
260 }
261 my_seek (f, 0);
262 my_read (&h1, sizeof (h1), f);
263 if (h1.magic != 0x5a4d)
264 {
265 file_warning ("Not an executable file", f->name);
266 return;
267 }
268
269 /* Read the second part of the DOS EXE header. */
270
271 if (size < sizeof (h1) + sizeof (h2)
272 || h1.reloc_ptr < sizeof (h1) + sizeof (h2))
273 {
274 file_warning ("DOS executable", f->name);
275 return;
276 }
277 my_read (&h2, sizeof (h2), f);
278 off_new_exe = COMBINE (h2.new_lo, h2.new_hi);
279
280 /* Read the magic word of the new EXE header. */
281
282 if (off_new_exe + 2 > size)
283 {
284 file_warning ("DOS executable", f->name);
285 return;
286 }
287 my_seek (f, off_new_exe);
288 my_read (buf, 2, f);
289 if (!(buf[0] == 'L' && buf[1] == 'X'))
290 {
291 file_warning ("Not an LX executable file", f->name);
292 return;
293 }
294
295 /* Read the LX header. */
296
297 my_seek (f, off_new_exe);
298 my_read (&ho, sizeof (ho), f);
299
300 /* Skip dynamic link libraries; they don't have a stack object. */
301
302 if (ho.mod_flags & 0x8000)
303 {
304 file_warning ("Dynamic link library", f->name);
305 return;
306 }
307
308 /* Retrieve the stack size from the object-relative initial value of
309 ESP. We ignore the stack_size field of the LX header -- it is
310 not set by emxbind and was not set in older versions of
311 LINK386. */
312
313 stack_size = ho.stack_esp;
314 stack_kb = stack_size / 1024;
315
316 /* Check whether the object number for ESP is valid. */
317
318 if (ho.stack_obj < 1 || ho.stack_obj > ho.obj_count)
319 file_error ("Invalid stack object number", f->name);
320
321 /* Read the object record for the stack. */
322
323 off_stack_obj = (off_new_exe + ho.obj_offset
324 + (ho.stack_obj - 1) * sizeof (struct object));
325 my_seek (f, off_stack_obj);
326 my_read (&stack_obj, sizeof (stack_obj), f);
327
328 /* Scan the import module table to check whether the executable
329 imports EMX.DLL, EMXLIBC.DLL, or EMXWRAP.DLL. If at least one of
330 these DLLs is imported, `uses_emx' is set to true. */
331
332 uses_emx = 0;
333 my_seek (f, off_new_exe + ho.impmod_offset);
334 for (i = 0; i < ho.impmod_count; ++i)
335 {
336 byte len, name[255+1];
337
338 my_read (&len, 1, f);
339 if (len != 0)
340 my_read (name, len, f);
341 name[len] = 0;
342 if (stricmp (name, "EMX") == 0 || stricmp (name, "EMXLIBC") == 0
343 || stricmp (name, "EMXWRAP") == 0)
344 {
345 uses_emx = TRUE;
346 break;
347 }
348 }
349
350 /* Now perform the action requested by the user. */
351
352 switch (operation)
353 {
354 case op_show:
355 printf ("%s: %lu Kbyte\n", f->name, stack_kb);
356 break;
357
358 case op_check:
359 if (!uses_emx)
360 file_warning ("Does not use emx.dll", f->name);
361 else if (stack_size <= 16384)
362 printf ("%s: Needs to be fixed\n", f->name);
363 else if (verbosity >= 2)
364 printf ("%s: Does not need to be fixed\n", f->name);
365 break;
366
367 case op_fix:
368 if (!uses_emx)
369 {
370 file_warning ("Does not use emx.dll", f->name);
371 break;
372 }
373 /*NOBREAK*/
374
375 case op_update:
376 if (stack_size >= min_stack_size)
377 {
378 printf ("%s: Stack size is %lu Kbyte; not changed\n",
379 f->name, stack_kb);
380 break;
381 }
382 /*NOBREAK*/
383
384 case op_force:
385
386 /* Changing the stack size when there are pages assigned to the
387 stack object or any succeeding non-resource object is
388 difficult as the page map must be changed. Changing the
389 stack size when there are any non-resource objects following
390 the stack object is difficult or impossible as relocations
391 may have to be changed or invented. Therefore, we fail if
392 there are pages assigned to the stack object or if any
393 non-resource objects follow the stack object. */
394
395 if (stack_obj.map_count != 0)
396 {
397 file_warning ("Stack object has page map entries"
398 " -- stack size not changed", f->name);
399 return;
400 }
401
402 my_seek (f, off_stack_obj + sizeof (struct object));
403 for (i = ho.stack_obj + 1; i <= ho.obj_count; ++i)
404 {
405 my_read (&temp_obj, sizeof (temp_obj), f);
406 if (!(temp_obj.attr_flags & 0x08))
407 {
408 file_warning ("A non-resource object succeeds the stack object"
409 " -- stack size not changed", f->name);
410 return;
411 }
412 }
413
414 if (verbosity >= 2)
415 printf ("%s: Stack size was %lu Kbyte\n", f->name, stack_kb);
416
417 /* Update the initial value of ESP and the size of the stack
418 object. */
419
420 ho.stack_esp = new_stack_size;
421 stack_obj.virt_size = ROUND_PAGE (new_stack_size);
422
423 /* Set the stack_size field to zero -- it is not used. */
424
425 ho.stack_size = 0;
426
427 /* Write the modified LX header. */
428
429 my_seek (f, off_new_exe);
430 my_write (&ho, sizeof (ho), f);
431
432 /* Write the modified stack object record. */
433
434 my_seek (f, off_stack_obj);
435 my_write (&stack_obj, sizeof (stack_obj), f);
436 break;
437
438 default:
439 abort ();
440 }
441}
442
443
444/* Process the file FNAME. */
445
446static void process_fname (const char *fname)
447{
448 struct file f;
449
450 switch (operation)
451 {
452 case op_show:
453 case op_check:
454 my_open (&f, fname, open_read);
455 break;
456
457 case op_update:
458 case op_force:
459 my_open (&f, fname, open_read_write);
460 break;
461
462 default:
463 abort ();
464 }
465 process_file (&f);
466 my_close (&f);
467}
468
469
470/* Process all files in directory NAME. */
471
472static void process_dir (const char *name)
473{
474 struct _dt_tree *tree;
475 struct _dt_node *p;
476 char path[512], *s;
477
478 tree = _dt_read (name, "*.exe", 0);
479 if (tree == NULL)
480 {
481 file_warning ("Cannot open directory", name);
482 return;
483 }
484 strcpy (path, name);
485 s = strchr (path, 0);
486 if (s != path && strchr ("\\:/", s[-1]) == NULL)
487 *s++ = '\\';
488 for (p = tree->tree; p != NULL; p = p->next)
489 if (!(p->attr & 0x10))
490 {
491 strcpy (s, p->name);
492 process_fname (path);
493 }
494 _dt_free (tree);
495}
496
497
498/* Process all directories listed in LIST. */
499
500static void process_env (const char *list)
501{
502 char dir[512];
503 const char *end;
504 int i;
505
506 if (list == NULL || *list == 0)
507 return;
508 for (;;)
509 {
510 while (*list == ' ' || *list == '\t') ++list;
511 if (*list == 0) break;
512 end = list;
513 while (*end != 0 && *end != ';') ++end;
514 i = end - list;
515 while (i>0 && (list[i-1] == ' ' || list[i-1] == '\t')) --i;
516 if (i != 0)
517 {
518 memcpy (dir, list, i);
519 dir[i] = 0;
520 process_dir (dir);
521 }
522 if (*end == 0) break;
523 list = end + 1;
524 }
525}
526
527
528/* Set the mode of operation to X. */
529
530static void set_op (enum op x)
531{
532 if (operation != op_none)
533 error ("More than one operation specified");
534 operation = x;
535}
536
537
538/* Get the new stack size from the argument of the option currently
539 being parsed. */
540
541static void get_new_stack_size (void)
542{
543 long n;
544 char *e;
545
546 errno = 0;
547 n = strtol (optarg, &e, 0);
548 if (errno != 0 || n < 20 || n > 512*1024 || e == optarg || *e != 0)
549 error ("invalid stack size specified");
550 new_stack_size = ROUND_PAGE (n * 1024);
551}
552
553
554/* The main function of emxstack. */
555
556int main (int argc, char *argv[])
557{
558 int c;
559 int path;
560
561 /* Expand wildcards. */
562
563 _wildcard (&argc, &argv);
564
565 /* Set default values. */
566
567 verbosity = 1; path = FALSE;
568 new_stack_size = 0;
569 min_stack_size = 0;
570 operation = op_none;
571
572 /* Let getopt() display error messages. */
573
574 opterr = TRUE;
575 optind = 0;
576
577 /* Display usage information when called without arguments. */
578
579 if (argc < 2 || strcmp (argv[1], "-?") == 0)
580 usage ();
581
582 /* Parse the command line options. */
583
584 while ((c = getopt (argc, argv, "cdfpqs:u:v")) != EOF)
585 switch (c)
586 {
587 case 'c':
588 set_op (op_check);
589 break;
590
591 case 'd':
592 set_op (op_show);
593 break;
594
595 case 'f':
596 set_op (op_update);
597 min_stack_size = 17 * 1024;
598 new_stack_size = 8 * 1024 * 1024;
599 break;
600
601 case 'p':
602 path = TRUE;
603 break;
604
605 case 's':
606 set_op (op_force);
607 get_new_stack_size ();
608 min_stack_size = 0;
609 break;
610
611 case 'u':
612 set_op (op_update);
613 get_new_stack_size ();
614 min_stack_size = new_stack_size;
615 break;
616
617 case 'q':
618 verbosity = 0;
619 break;
620
621 case 'v':
622 verbosity = 2;
623 break;
624
625 default:
626 exit (1);
627 }
628
629 if (operation == op_none)
630 error ("No operation given on the command line");
631
632 if (path)
633 {
634 /* Use PATH. */
635
636 if (optind < argc)
637 error ("No files must be given on the command line if -p is used");
638 process_env (getenv ("PATH"));
639 }
640
641 else
642 {
643 /* Process the remaining arguments as files. */
644
645 int i;
646
647 i = optind;
648 if (i >= argc)
649 error ("No files given on the command line");
650 while (i < argc)
651 process_fname (argv[i++]);
652 }
653 return 0;
654}
Note: See TracBrowser for help on using the repository browser.