Just a sample of the Echomail archive
[ << oldest | < older | list | newer > | newest >> ]
|  Message 174  |
|  Ben Collver to All  |
|  Running GNU on DOS with DJGPP part 2  |
|  18 Feb 24 11:37:52  |
 
TZUTC: -0600
MSGID: 176.fido_dos@1:124/5016 2a377720
PID: Synchronet 3.20a-Linux master/862753d6c Feb 16 2024 GCC 11.4.0
TID: SBBSecho 3.20-Linux master/862753d6c Feb 16 2024 GCC 11.4.0
BBSID: EOTLBBS
CHRS: ASCII 1
NOTE: SlyEdit 1.88d (2024-02-16) (ICE style)
Leaving aside the fact that the DOS API is... ehem... bad, this
fundamental difference means that any Unix program ported to DOS has
a usability problem: you cannot use globs anymore when invoking it!
Something as simple and common as gcc -o program.exe *.c would just
not work. So then... how can we explain the following output from the
showargs.c program, a little piece of code that prints argv?
D:\>gcc -o showargs.exe showargs.c
D:\>.\showargs.exe *.c
argv[1] = headself.c
argv[2] = longcmd1.c
argv[3] = longcmd2.c
argv[4] = showargs.c
argv[5] = showpath.c
D:\>
In the picture above, you can see how I ran the showargs.c program
with *.c as its own argument and somehow it worked as you would
expect. But if we build it with a standard DOS compiler we get
different results:
D:\>tcc showargs.c
Turbo C++ Version 3.00 Copyright (c) 1992 Borland International
showargs.c:
Turbo Link Version 5.0 Copyright (c) 1992 Borland International
Available memory 4133648
D:>.\showargs.exe *.c
argv[1] = *.c
D:>_
GCC is actually doing something to make glob expansion work--and it
has to, because remember that DJGPP was not just about porting GCC:
it was about porting many more GNU developer tools to DOS. Having had
to patch them one by one to work with DOS' COMMAND.COM semantics
would have been a sad state of affairs.
To understand what's happening here, know that all C programs
compiled by any compiler include a prelude: main is not the program's
true entry point. All compilers wrap main with some code of their own
to set up the process and the C library, and DJGPP is no different.
Such code is often known as the crt (or C Runtime) and it comes in
two phases: crt0, written in assembly for early bootstrapping, and
crt1, written in C.
As you can imagine, this is where the magic lives. DJGPP's crt1 is in
charge of processing the flat command line that it receives from DOS
and transforming it into the argv that POSIX C programs expect,
following common Unix semantics. In a way, this code performs the job
of a Unix shell.
Once again, take a break to inspect the crt0 sources and, in
particular, the contents of the c1args.c file. Pay attention to file
reads and the "proxy" thing, both of which bring us to the next
section.
# Long command lines
Unix command lines aren't different just because of glob expansion.
They are also different because they are usually long, and they are
long in part because of glob expansion and in part because Unix has
supported long file names for much longer than DOS.
Unfortunately... DOS restricted command lines to a maximum of
126 characters--fewer characters than you can fit in a Tweet or an
SMS--and this posed a problem because the build process of most GNU
developer tools, if not all, required using long command lines. To
resolve these issues, DJGPP provides two features.
The first is support for response files. Response files are text
files that contain the full command line. These files are then passed
to a process with the @file.txt syntax, which then causes DJGPP's
crt1 code to load the response files and construct the long command
line in extended memory.
Let's take a look. If we reuse our previous showargs.c program that
prints the command line arguments, we can observe how the behavior
differs between building this program with a standard DOS compiler
and with DJGPP:
D:\>type args.txt
first
second
D:\>gcc -o showargs.exe showargs.c
D:\>.\showargs.exe @args.txt
argv[1] = first
argv[2] = second
D:\>tcc showargs.c
Turbo C++ Version 3.00 Copyright (c) 1992 Borland International
showargs.c:
Turbo Link Version 5.0 Copyright (c) 1992 Borland International
Available memory 4133648
D:\>.\showargs.exe @args.txt
argv[1] = @args.txt
D:\>
Response files are easy to implement and they are sufficient to
support long command lines: even if they require special handling on
the caller side to write the arguments to dsk and then place the
response file as an argument, this could all be hidden inside the
exec family of system calls. Unfortunately, using response files is
slow because, in order to invoke a program, you need to write the
command line to a file--only to load it immediately afterwards. And
disk I/O used to be really slow.
For this reason, DJGPP provides a different mechanism to pass long
command lines around, and this is via the transfer buffer described
earlier. This mechanism involves putting the command line in the
transfer buffer and telling the executed command where its command
line lives. This mechanism obviously only works when executing a
DJGPP program from another DJGPP program, because no matter what,
process executions are still routed through DOS and thus are bound by
DOS' 126 character limit.
Let's try this too. For this experiment, we'll play with two
programs: one that prints the length of the received command line and
another one that produces a long command line and executes the former.
The first program is longcmd1.c and is depicted below. All this
program does is allocate a command line longer than DOS' maximum
length of 126 characters and, once it has built the command line,
invokes longcmd2.exe with said long command line:
#ifdef __GNUC__
#include
|
[ << oldest | < older | list | newer > | newest >> ]