[1]
"Good programmers know what to write.Informal introduction into aspects of porting from UNIX/POSIX to OS/2 that are closely related to the emx development tools. Teaching ANSI C is not intended, some familiarity with programming in C or C++ is assumed; pointers to tutorials for rank beginners are included.
Great ones know what to rewrite (and reuse)."
(ERIC S. RAYMOND, IN: "THE CATHEDRAL AND THE BAZAAR")
Alfieri: > E la fama? <
Gozzi: > E la fame? <
(COMMEDIA DELL'ARTE)
(Authorship is usually indicated. Please mail me any errors and
suggested additions. I am looking forward to adding new chapters written by
interested readers. If the author's name is omitted and not evident, I am citing
usually an excerpt from the emx documentation. Those hundreds of trademarks
are those of their respective owners.)
"I have found a cool X utility which I want to port. How should I proceed?
Normally, with X utilities, you get an Imakefile. Simply proceed as if you were under Unix. Run xmkmf -a to compile. Read the programming section on special issues."
("The XFree86/OS2 FAQ")
Since I am not a professional programmer, I am advocating user
friendly tools and user friendly documentation. I do not think such helpful
things should aim at becoming large or comprehensive, but simple and down-to-earth.
It should just enable the first simple steps and help to reduce the beginners
problems, thus providing the necessary basis and some pointers so that you learn
how to get access to the huge piles of helpful information on the Net and elsewhere.
Well, it is time now to make the first step towards a (still epigonous and rudimentary)
porting tutorial, compiling in the form of short articles some of the helpful
information that's scattered in small bits and pieces everywhere (while trying
to identify and name the author). Everybody may contribute such a small article
for inclusion.
The number of new compilers for OS/2, which provide all (now standardized or still experimental) features of bleeding edge STL versions is very limited. 'Workframe' solutions fail, when a huge codebase with hundreds of megabytes of source is involved and a reasonable make process (i.e. based on Unix' 20 years of scientific and industrial experience with complex projects for heterogenous platforms and distributed networking, using quality software engineering strategies for code reliability) is required for any serious development. A prominent example is XFree86/OS2, where all make utilities and development tools except the GNU make utility and the GNU compiler gcc failed. Both are based on the emx libraries:
[excerpt from emx 0.9d INTRODUCTION 21-Dec-1998]This may be the right moment now to carefully read emx/doc/INSTALL file, and to test if you can successfully compile, run and debug the programs mentioned there.``Introduction
Welcome to emx 0.9d, an environment for creating 32-bit programs for OS/2 and DOS. You can use the GNU C compiler to compile programs for emx. The main design goal of emx is to simplify porting Unix software to OS/2 and DOS. Moreover, you can create `native' OS/2 programs, including Presentation Manager applications.
The emx package includes:
Additionally, the following GNU programs are available compiled and with patches and sources:
- emx.dll, emxio.dll, emxwrap.dll and emxlibc.dll dynamic link libraries for OS/2
- emx.exe DOS extender for running 32-bit programs under DOS
- emxfpemu, W. Metzenthen's FPU (387) emulator
- emxbind.exe for creating .exe files which work both under OS/2 and DOS
- emxomf.exe for converting .o object files (a.out) to .obj object files (OMF). With emxomf and LINK386 you can create OS/2 programs that don't need emx.dll
- emximp.exe for creating import libraries
- emxexp.exe for creating a list of export definitions from object files
- C header files and a complete C library
- a simple graphics library for 320x200 (256 colors) mode
- a library for screen output in windows
- header files and a library for using sockets of IBM TCP/IP for OS/2
You can get the complete unpatched GNU sources by anonymous ftp from prep.ai.mit.edu and other archives such as ftp.leo.org.
- GCC 2.8.1, the GNU C compiler, supporting the C, C++, and Objective C languages
- GAS 2.6, the GNU assembler
- GDB 4.16, the GNU debugger
- ld, an ancient version of the GNU linker hacked for emx
- strip: a member of an ancient version of the GNU binary utilties
- ar, nm, size, objdump: a subset of the GNU binary utilities 2.6
- gprof, the BSD/GNU profiler
- texinfo, the GNU documentation system
- Patches for the GNU sources
- Patched source for GCC, GAS, GDB, ld, ar, nm, size, strip, objdump, info, makeinfo, texindex. You can compile all these programs with the files that come with emx (but you also need a make utility, such as dmake).
Additionally, the following libraries are provided:
After unpacking the emx runtime and development system packages, you'll find further information in the following files:
- some parts of the BSD C library
- the BSD data base library: dbopen()
- BSD curses library
- Henry Spencer's regexp library
- The GNU and BSD termcap libraries [...]
After unpacking the GNU and BSD packages, you'll find further information in the following files: [...]
- emxdocCOPYING.EMX emx & library license, license overview
- emxdocCOPYING GNU General Public License
- emxdocinstall.doc Installation guide
- emxdocemxrt.doc User's guide to the emx runtime package emxdocemxdev.doc Application developer's guide
- emxdocemxlib.doc C library reference
- emxdocfaq.doc Frequently asked questions (with answers)
- emxdocbuild.doc Compiling the sources
- emxdochistory.doc Change log [...]
emx is available for anonymous ftp on
- emxdocemxgnu.doc GNU development tools information
- emxdocemxbsd.doc BSD library information [...]
An emx-related mailing list has been created. The address for people to request to be added to or removed from the list is:
- ftp.leo.org: /pub/comp/os/os2/leo/gnu/emx+gcc/
- ftp-os2.cdrom.com: /pub/os2/emx09d/
- ftp-os2.nmsu.edu: /pub/os2/dev/emx/v0.9d/ [...]
majordomo@iaehv.nl
To subscribe, send a message containing
subscribe emx
to majordomo@iaehv.nl. [...]
Highly optimized and bleeding edge C++-code can be compiled with the most recent experimental GNU Pentium Compiler Group's pgcc, which also contains tools updates, interesting for assembler (MMX) programmers.
Although not completely POSIX compliant, emx for OS/2 is nearly as close as can be for the pre-POSIX operating system OS/2. This makes it an ideal platform for CODE REUSE and OPEN PORTABILITY.
" X - a portable network transparent window system", as the authors call it, enables the reuse of Unix software for OS/2 and in general gives you the chance to write extremely portable gui code, running on a PC as well as on a super-computer by simply recompiling it, provided you have a POSIX-like programming interface.
"Why is writing portable code (and therefore emx) so important?"
"As for non-existing API, I would recommend to stick to well-known things like POSIX, BSD networking, X11 and avoid system-specific calls like DosWrite() etc. completely. If you're doing really multiplatform project. Really 'standard' calls are the same everywhere and rarely require re-implementation. There's some oddity here and there (I heard Ultrix(?) does not have strdup()?) but you can add it in transparent way, namely linking on Ultrix with your own implementation of strdup()."Well, the tutorials motto is:(Dr. Sergey Ayukov, Sternberg Astronomical Institute Moscow, Russia http://www.ayukov.com, http://crydee.sai.msu.ru/index-asv.html)
"Good programmers know what to write. Great ones know what to rewrite (and reuse)."Paramount for reuse in the future is reusability through open standards (and - even more important - good comments ;-). Here the open Posix/BSD/MIT API 's distinguish XFree/emx/gcc/pgcc from other compilers and make it the 'future compatible' development platform. For this reason it is outstanding and may well be recommended.
A good tutorial to ANSI C (cf. e.g. "An Introduction to C Programming", by Carsten Whimster"Heelpp, I'm stuck! Where to begin?"
(anonymous)
"Use standard ANSI C (or C++) whereever possible!"Only, in this moment this will probably help you less than nothing. Moreover, the only viable solution often requires low-level functions. Remember that ' EMX/GCC' as well as 'x11make' of XFree86/OS2 (or 'gmake') is generally a superset of the Unix standard 'cc', 'cxx' and 'make'. So, if you like to read a basic tutorial for beginners with a strictly didactical approach, I recommend as a good starting point:
"ANSI C for Programmers on UNIX Systems",
by T. P. Love, CUED Cambridge University Engineering Department)
<tpl@eng.cam.ac.uk>,
In the following, to fill some gaps, I'll give a very short summary of porting-related concepts and specialties. For general information please refer to the online documentation that comes with recent Linux and FreeBSD distributions. It is also necessary that you have access to a good Unix/Linux tutorial, explaining those numerous concepts unfamiliar for OS/2, which cannot be covered here. For standards, try e.g.:
This tutorial will concentrate on emx-specific questions that recently were discussed on the respective lists. Further sources of information are indexed by Timur Tabi:
"Those who do not understand Unix are condemned to reinvent it, poorly."(Henry Spencer)
The following are some excerpts from a terse introduction into how to behave in a FreeBSD Unix development environment (``A User's Guide to FreeBSD Programming Tools``)
``Simply proceed as if you were under Unix!''I'm sure, you'll soon discover how little a programmer who is writing portable ('posixified') applications has to re-learn or re-do.
``This section deals only with the GNU compiler for C and C++, since that comes with the base FreeBSD [and the emx] system. It can be invoked by either cc [write a cc.cmd file] or gcc. ... Once you've written your masterpiece, the next step is to convert it into something that will (hopefully!) run on FreeBSD [as well as on OS/2 after compiling your portable Posix source with emx gcc]. This usually involves several steps, each of which is done by a separate program:
There are lots and lots of options for cc, which are all in the man page. Here are a few of the most important ones, with examples of how to use them.
% cc -O -o foobar foobar.c
Create an optimised version of the executable. The compiler performs various clevertricks to try and produce an executable that runs faster than normal. You can add a number after the -O to specify a higher level of optimisation, but this often exposes bugsin the compiler's optimiser. ... Optimisation is usually only turned on when compiling a release version. ...
The following three flags will force cc to check that your code complies to the relevant international standard, often referred to as the ANSI standard, though strictly speaking it is an ISO standard.
Without these flags, cc will allow you to use some of its non-standard extensions to the standard. Some of these are very useful, but will not work with other compilers - in fact, one of the main aims of the standard is to allow people to write code that will work with any compiler on any system. This is known as portable code. Generally, you should try to make your code as portable as possible, as otherwise you may have to completely re-write the program later to get it to work somewhere else - and who knows what you may be using in a few years time?
The most common example of this is when compiling a program that uses some of the mathematical functions in C. Unlike most other platforms [emx included], these are in a separate library from the standard C one and you have to tell the compiler to add it.
The rule is that if the library is called libsomething.a, you give cc the argument -lsomething. For example, the math library is libm.a, so you give cc the argument -lm. A common "gotcha" with the math library is that it has to be the last library on the command line. [Emx does not cut off the leading 'lib' part of library names and m.a or m.lib is just a dummy library which can be used for your own common extension modules.]
If you are compiling C++ code, you need to add -lg++, or -lstdc++ ... to the command line argument to link the C++ library functions. ...
Each of these will both produce an executable foobar from the C++ source file foobar.cc. Note that, on Unix systems, C++ source files traditionally end in .C, .cxx or .cc, rather than the MS-DOS™ style .cpp (which was already used for something else). gcc used to rely on this to work out what kind of compiler to use on the source file; however, this restriction no longer applies, so you may now call your C++ files .cpp with impunity!
Q: I am trying to write a program which uses the sin() function and I get an error like this. What does it mean?
Q: All right, I wrote this simple program to practice using -lm. All it does is raise 2.1 to the power of 6.
int main()
{
float f = pow(2.1, 6);
printf("2.1 ^ 6 = %f", f);
return 0;
}
2.1 ^ 6 = 1023.000000
A: When the compiler sees you call a function, it checks if it has already seen a prototype for it. If it has not, it assumes the function returns an int, which is definitely not what you want here.
Q: So how do I fix this?
A: The prototypes for the mathematical functions are in math.h. If you include this file, the compiler will be able to find the prototype and it will stop doing strange things to your calculation!
#include <stdio.h>
int main() {
...
2.1 ^ 6 = 85.766121
Q: I compiled my program and it seemed to run all right at first, then there was an error and it said something about core dumped. What does that mean?
A: The name core dump dates back to the very early days of Unix, when the machines used core memory for storing data. Basically, if the program failed under certain conditions, the system would write the contents of core memory to disk in a file called core, which the programmer could then pore over to find out what went wrong.
Q: Fascinating stuff, but what I am supposed to do now?
A: Use gdb to analyse the core (see Section 5).
Q: When my program dumped core, it said something about a segmentation fault. What's that?
A: This basically means that your program tried to perform some sort of illegal operation on memory; Unix is designed to protect the operating system and other programs from rogue programs.
Common causes for this are [Read this carefully, well meant advice]:
Trying to write to a NULL pointer, eg
strcpy(foo, "bang!");
strcpy(foo, "bang!");
bar[27] = 6;
strcpy(foo, "bang!");
Doing naughty things with malloc() and free(), eg
free(bar);
free(foo);
free(foo);
It reads in a file, called a makefile, that tells it how different files depend on each other, and works out which files need to be re-compiled and which ones don't. For example, a rule could say something like "if fromboz.o is older than fromboz.c, that means someone must have changed fromboz.c, so it needs to be re-compiled." The makefile also has rules telling make how to re-compile the source file, making it a much more powerful tool.
Makefiles are typically kept in the same directory as the source they apply to, and can be called makefile, Makefile or MAKEFILE. Most programmers use the name Makefile, as this puts it near the top of a directory listing, where it can easily be seen.
Here's a very simple make file:
cc -o foo foo.c
The creation line starts with a tab (press the tab key) and then the command you would type to create foo if you were doing it at a command prompt. If foo is out of date, or does not exist, make then executes this command to create it. In other words, this is the rule which tells make how to re-compile foo.c.
So, when you type make, it will make sure that foo is up to date with respect to your latest changes to foo.c. This principle can be extended to Makefiles with hundreds of targets-in fact, on FreeBSD, it is possible to compile the entire operating system just by typing make world in the appropriate directory! Another useful property of makefiles is that the targets don't have to be programs. For instance, we could have a make file that looks like this:
cc -o foo.exe foo.c
install:
cp foo /home/me
If we just type make on its own, make will always look at the first target and then stop without looking at any others. So if we typed make here, it will just go to the foo target, re-compile foo if necessary, and then stop without going on to the install target.
Notice that the install target doesn't actually depend on anything! This means that the command on the following line is always executed when we try to make that target by typing make install. In this case, it will copy foo into the user's home directory. This is often used by application makefiles, so that the application can be installed in the correct directory when it has been correctly compiled.
This is a slightly confusing subject to try and explain. If you don't quite understand how make works, the best thing to do is to write a simple program like "hello world" and a make file like the one above and experiment. Then progress to using more than one source file, or having the source file include a header file. The touch command is very useful here-it changes the date on a file without you having to edit it. ...
Make is a very powerful tool, and can do much more than the simple example above shows. Unfortunately, there are several different versions of make, and they all differ considerably. The best way to learn what they can do is probably to read the documentation-hopefully this introduction will have given you a base from which you can do this.
The version of make that comes with FreeBSD is the Berkeley make ... Many applications in the ports use GNU make, which has a very good set of "info" pages. If you have installed any of these ports, GNU make will automatically have been installed as gmake. It's also available as a port and package in its own right.
To view the info pages for GNU make, you will have to edit the dir file in the
/usr/local/info directory to add an entry for it. This involves adding a line like
The debugger that comes with FreeBSD [and emx] is called gdb (GNU debugger). You start it up by typing
front-end for it xxgdb in the ports collection. [Emx calls it pmgdb.} ...
You'll need to have compiled the program with the -g option to get the most out of using gdb. It will work without, but you'll only see the name of the function you're in, instead of the source code. If you see a line like:
At the gdb prompt, type break main. This will tell the debugger to skip over the preliminary set-up code in the program and start at the beginning of your code. Now type run to start the program - it will start at the beginning of the set-up code and then get stopped by the debugger when it calls main(). (If you've ever wondered where main() gets called from, now you know!).
You can now step through the program, a line at a time, by pressing n. If you get to a function call, you can step into it by pressing s. Once you're in a function call, you can return from stepping into a function call by pressing f. You can also use up and down to take a quick look at the caller.
Here's a simple example of how to spot a mistake in a program with gdb. This is our program (with a deliberate mistake):
int bazz(int anint);
main() {
int i;
printf("This is my programn");
bazz(i);
return 0;
}
int bazz(int anint) {
printf("You gave me %dn", anint);
return anint;
}
When we compile and run the program we get
% ./temp
This is my program
anint = 4231
GDB is free software and you are welcome to distribute copies of it
under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc.
(gdb) break main Skip the set-up code
Breakpoint 1 at 0x160f: file temp.c, line 9. breakpoint at main()
(gdb) run Run as far as main()
Starting program: temp Program starts running
Breakpoint 1, main () at temp.c:9 gdb stops at main()
(gdb) n Go to next line
This is my program Program prints out
(gdb) s step into bazz()
bazz (anint=4231) at temp.c:17 gdb displays stack frame
(gdb)
#1 0x1625 in main () at temp.c:11 gdb displays stack frame
(gdb) p i Show us the value of i
$1 = 4231 gdb displays 4231
main() {
int i;
i = 5;
printf("This is my programn");
...
in that area of memory when the program ran, which in this case happened to be 4231.
Note: gdb displays the stack frame every time we go into or out of a function, even if we're using up and down to move around the call stack. This shows the name of the function and the values of its arguments, which helps us keep track of where we are and what's going on. (The stack is a storage area where the program stores information about the arguments passed to functions and where to go when it returns from a function call).
A core file is basically a file which contains the complete state of the process when it crashed. In "the good old days", programmers had to print out hex listings of core files and sweat over machine code manuals, but now life is a bit easier. Incidentally, under FreeBSD and other 4.4BSD systems, a core file is called progname.core instead of just core, to make it clearer which program a core file belongs to. [emx calls it just core.]
To examine a core file, start up gdb in the usual way. Instead of typing break or run, type
You should see something like this:
GDB is free software and you are welcome to distribute copies of it
under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.13 (i386-unknown-freebsd), Copyright 1994 Free Software Foundation, Inc.
(gdb) core a.out.core
Core was generated by `a.out'.
Program terminated with signal 11, Segmentation fault.
Cannot access memory at address 0x7020796d.
#0 0x164a in bazz (anint=0x5) at temp.c:17
(gdb)
Sometimes it's useful to be able to see how a function was called, as the problem could have occurred a long way up the call stack in a complex program. The bt command causes gdb to print out a back-trace of the call stack:
#0 0x164a in bazz (anint=0x5) at temp.c:17
#1 0xefbfd888 in end()
#2 0x162c in main() at temp.c:11
(gdb)
One of the neatest features about gdb is that it can attach to a program that's already running. Of course, that assumes you have sufficient permissions to do so. A common problem is when you are stepping through a program that forks, and you want to trace the child, but the debugger will only let you trace the parent.
What you do is start up another gdb, use ps to find the process ID for the child, and do
"That's all very well," you're probably thinking, "but by the time I've done that, the child process will be over the hill and far away". Fear not, gentle reader, here's how to do it (courtesy of the gdb info pages):
if ((pid = fork()) < 0) /* _Always_ check this */
error();
else if (pid == 0) { /* child */
int PauseMode = 1;
while (PauseMode)
sleep(10); /* Wait until someone attaches to us */
...
} else { /* parent */
...
Using Emacs as a Development Environment
Emacs
[...] Emacs is basically a highly customisable editor-indeed, it has been customised to the point where it's more like an operating system than an editor! Many developers and sysadmins do in fact spend practically all their time working inside Emacs, leaving it only to log out. [I recommend also to test elvis, a vi clone.] It's impossible even to summarise everything Emacs can do here, but here are some of the features of interest to developers:
Once it's installed, start it up and do C-h t to read an Emacs tutorial-that means hold down the control key, press h, let go of the control key, and then press t. (Alternatively, you can you use the mouse to select Emacs Tutorial from the Help menu).
Although Emacs does have menus, it's well worth learning the key bindings, as it's much quicker when you're editing something to press a couple of keys than to try and find the mouse and then click on the right place. And, when you're talking to seasoned Emacs users, you'll find they often casually throw around expressions like "M-x replace-s RET foo RET bar RET" so it's useful to know what they mean. And in any case, Emacs has far too many useful functions for them to all fit on the menu bars.
Fortunately, it's quite easy to pick up the key-bindings, as they're displayed next to the menu item. My advice is to use the menu item for, say, opening a file until you understand how it works and feel confident with it, then try doing C-x C-f. When you're happy with that, move on to another menu command.
If you can't remember what a particular combination of keys does, select Describe Key from the Help menu and type it in-Emacs will tell you what it does. You can also use the Command Apropos menu item to find out all the commands which contain a particular word in them, with the key binding next to it.
By the way, the expression above means hold down the Meta key, press x, release the Meta key, type replace-s (short for replace-string-another feature of Emacs is that you can abbreviate commands), press the return key, type foo (the string you want replaced), press the return key, type bar (the string you want to replace foo with) and press return again. Emacs will then do the search-and-replace operation you've just requested. If you're wondering what on earth the Meta key is, it's a special key that many Unix workstations have. Unfortunately, PC's don't have one, so it's usually the alt key (or if you're unlucky, the escape key).
Oh, and to get out of Emacs, do C-x C-c (that means hold down the control key, press x, press c and release the control key). If you have any unsaved files open, Emacs will ask you if you want to save them. (Ignore the bit in the documentation where it says C-z is the usual way to leave Emacs-that leaves Emacs hanging around in the background, and is only really useful if you're on a system which doesn't have virtual terminals).
Emacs does many wonderful things; some of them are built in, some of them need to be configured.
Instead of using a proprietary macro language for configuration, Emacs uses a version of Lisp specially adapted for editors, known as Emacs Lisp. This can be quite useful if you want to go on and learn something like Common Lisp, as it's considerably smaller than Common Lisp (although still quite big!).
The best way to learn Emacs Lisp is to download the Emacs Tutorial. However, there's no need to actually know any Lisp to get started with configuring Emacs, as I've included a sample .emacs file, which should be enough to get you started. Just copy it into your home directory and restart Emacs if it's already running; it will read the commands from the file and (hopefully) give you a useful basic setup.
Unfortunately, there's far too much here to explain it in detail; however there are one or two points worth mentioning.
This file supports syntax highlighting for C, C++, Perl, Lisp and Scheme, by guessing the language from the filename.
Emacs already has a pre-defined function called next-error. In a compilation output window, this allows you to move from one compilation error to the next by doing M-n; we define a complementary function, previous-error, that allows you to go to a previous error by doing M-p. The nicest feature of all is that C-c C-c will open up the source file in which the error occurred and jump to the appropriate line.
We enable Emacs's ability to act as a server, so that if you're doing something outside Emacs and you want to edit a file, you can just type in
;; This file is designed to be re-evaled; use the variable first-time
;; to avoid any problems with this.
(defvar first-time t
"Flag signifying this is the first time that .emacs has been evaled")
;; Meta
(global-set-key "M- " 'set-mark-command)
(global-set-key "M-C-h" 'backward-kill-word)
(global-set-key "M-C-r" 'query-replace)
(global-set-key "M-r" 'replace-string)
(global-set-key "M-g" 'goto-line)
(global-set-key "M-h" 'help-command)
;; Function keys
(global-set-key [f1] 'manual-entry)
(global-set-key [f2] 'info)
(global-set-key [f3] 'repeat-complex-command)
(global-set-key [f4] 'advertised-undo)
(global-set-key [f5] 'eval-current-buffer)
(global-set-key [f6] 'buffer-menu)
(global-set-key [f7] 'other-window)
(global-set-key [f8] 'find-file)
(global-set-key [f9] 'save-buffer)
(global-set-key [f10] 'next-error)
(global-set-key [f11] 'compile)
(global-set-key [f12] 'grep)
(global-set-key [C-f1] 'compile)
(global-set-key [C-f2] 'grep)
(global-set-key [C-f3] 'next-error)
(global-set-key [C-f4] 'previous-error)
(global-set-key [C-f5] 'display-faces)
(global-set-key [C-f8] 'dired)
(global-set-key [C-f10] 'kill-compilation)
;; Keypad bindings
(global-set-key [up] "C-p")
(global-set-key [down] "C-n")
(global-set-key [left] "C-b")
(global-set-key [right] "C-f")
(global-set-key [home] "C-a")
(global-set-key [end] "C-e")
(global-set-key [prior] "M-v")
(global-set-key [next] "C-v")
(global-set-key [C-up] "M-C-b")
(global-set-key [C-down] "M-C-f")
(global-set-key [C-left] "M-b")
(global-set-key [C-right] "M-f")
(global-set-key [C-home] "M-<")
(global-set-key [C-end] "M->")
(global-set-key [C-prior] "M-<")
(global-set-key [C-next] "M->")
;; Mouse
(global-set-key [mouse-3] 'imenu)
;; Misc
(global-set-key [C-tab] "C-qt") ; Control tab quotes a tab.
(setq backup-by-copying-when-mismatch t)
;; Treat 'y' or <CR> as yes, 'n' as no.
(fset 'yes-or-no-p 'y-or-n-p)
(define-key query-replace-map [return] 'act)
(define-key query-replace-map [?C-m] 'act)
;; Load packages
(require 'desktop)
(require 'tar-mode)
;; Pretty diff mode
(autoload 'ediff-buffers "ediff" "Intelligent Emacs interface to diff" t)
(autoload 'ediff-files "ediff" "Intelligent Emacs interface to diff" t)
(autoload 'ediff-files-remote "ediff"
"Intelligent Emacs interface to diff")
(if first-time
(setq auto-mode-alist
(append '((".cpp$" . c++-mode)
(".hpp$" . c++-mode)
(".lsp$" . lisp-mode)
(".scm$" . scheme-mode)
(".pl$" . perl-mode)
) auto-mode-alist)))
;; Auto font lock mode
(defvar font-lock-auto-mode-list
(list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'lisp-mode 'perl-mode 'scheme-mode)
"List of modes to always start in font-lock-mode")
(defvar font-lock-mode-keyword-alist
'((c++-c-mode . c-font-lock-keywords)
(perl-mode . perl-font-lock-keywords))
"Associations between modes and keywords")
(defun font-lock-auto-mode-select ()
"Automatically select font-lock-mode if the current major mode is
in font-lock-auto-mode-list"
(if (memq major-mode font-lock-auto-mode-list)
(progn
(font-lock-mode t))
)
)
(global-set-key [M-f1] 'font-lock-fontify-buffer)
;; New dabbrev stuff
;(require 'new-dabbrev)
(setq dabbrev-always-check-other-buffers t)
(setq dabbrev-abbrev-char-regexp "sw|s_")
(add-hook 'emacs-lisp-mode-hook
'(lambda ()
(set (make-local-variable 'dabbrev-case-fold-search) nil)
(set (make-local-variable 'dabbrev-case-replace) nil)))
(add-hook 'c-mode-hook
'(lambda ()
(set (make-local-variable 'dabbrev-case-fold-search) nil)
(set (make-local-variable 'dabbrev-case-replace) nil)))
(add-hook 'text-mode-hook
'(lambda ()
(set (make-local-variable 'dabbrev-case-fold-search) t)
(set (make-local-variable 'dabbrev-case-replace) t)))
;; C++ and C mode...
(defun my-c++-mode-hook ()
(setq tab-width 4)
(define-key c++-mode-map "C-m" 'reindent-then-newline-and-indent)
(define-key c++-mode-map "C-ce" 'c-comment-edit)
(setq c++-auto-hungry-initial-state 'none)
(setq c++-delete-function 'backward-delete-char)
(setq c++-tab-always-indent t)
(setq c-indent-level 4)
(setq c-continued-statement-offset 4)
(setq c++-empty-arglist-indent 4))
(defun my-c-mode-hook ()
(setq tab-width 4)
(define-key c-mode-map "C-m" 'reindent-then-newline-and-indent)
(define-key c-mode-map "C-ce" 'c-comment-edit)
(setq c-auto-hungry-initial-state 'none)
(setq c-delete-function 'backward-delete-char)
(setq c-tab-always-indent t)
;; BSD-ish indentation style
(setq c-indent-level 4)
(setq c-continued-statement-offset 4)
(setq c-brace-offset -4)
(setq c-argdecl-indent 0)
(setq c-label-offset -4))
;; Perl mode
(defun my-perl-mode-hook ()
(setq tab-width 4)
(define-key c++-mode-map "C-m" 'reindent-then-newline-and-indent)
(setq perl-indent-level 4)
(setq perl-continued-statement-offset 4))
;; Scheme mode...
(defun my-scheme-mode-hook ()
(define-key scheme-mode-map "C-m" 'reindent-then-newline-and-indent))
;; Emacs-Lisp mode...
(defun my-lisp-mode-hook ()
(define-key lisp-mode-map "C-m" 'reindent-then-newline-and-indent)
(define-key lisp-mode-map "C-i" 'lisp-indent-line)
(define-key lisp-mode-map "C-j" 'eval-print-last-sexp))
;; Add all of the hooks...
(add-hook 'c++-mode-hook 'my-c++-mode-hook)
(add-hook 'c-mode-hook 'my-c-mode-hook)
(add-hook 'scheme-mode-hook 'my-scheme-mode-hook)
(add-hook 'emacs-lisp-mode-hook 'my-lisp-mode-hook)
(add-hook 'lisp-mode-hook 'my-lisp-mode-hook)
(add-hook 'perl-mode-hook 'my-perl-mode-hook)
;; Complement to next-error
(defun previous-error (n)
"Visit previous compilation error message and corresponding source code."
(interactive "p")
(next-error (- n)))
;; Misc...
(transient-mark-mode 1)
(setq mark-even-if-inactive t)
(setq visible-bell nil)
(setq next-line-add-newlines nil)
(setq compile-command "make")
(setq suggest-key-bindings nil)
(put 'eval-expression 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(put 'set-goal-column 'disabled nil)
;; Elisp archive searching
(autoload 'format-lisp-code-directory "lispdir" nil t)
(autoload 'lisp-dir-apropos "lispdir" nil t)
(autoload 'lisp-dir-retrieve "lispdir" nil t)
(autoload 'lisp-dir-verify "lispdir" nil t)
;; Font lock mode
(defun my-make-face (face colour &optional bold)
"Create a face from a colour and optionally make it bold"
(make-face face)
(copy-face 'default face)
(set-face-foreground face colour)
(if bold (make-face-bold face))
)
(if (eq window-system 'x)
(progn
(my-make-face 'blue "blue")
(my-make-face 'red "red")
(my-make-face 'green "dark green")
(setq font-lock-comment-face 'blue)
(setq font-lock-string-face 'bold)
(setq font-lock-type-face 'bold)
(setq font-lock-keyword-face 'bold)
(setq font-lock-function-name-face 'red)
(setq font-lock-doc-string-face 'green)
(add-hook 'find-file-hooks 'font-lock-auto-mode-select)
(setq baud-rate 1000000)
(global-set-key "C-cmm" 'menu-bar-mode)
(global-set-key "C-cms" 'scroll-bar-mode)
(global-set-key [backspace] 'backward-delete-char)
; (global-set-key [delete] 'delete-char)
(standard-display-european t)
(load-library "iso-transl")))
;; X11 or PC using direct screen writes
(if window-system
(progn
;; (global-set-key [M-f1] 'hilit-repaint-command)
;; (global-set-key [M-f2] [?C-u M-f1])
(setq hilit-mode-enable-list
'(not text-mode c-mode c++-mode emacs-lisp-mode lisp-mode
scheme-mode)
hilit-auto-highlight nil
hilit-auto-rehighlight 'visible
hilit-inhibit-hooks nil
hilit-inhibit-rebinding t)
(require 'hilit19)
(require 'paren))
(setq baud-rate 2400) ; For slow serial connections
)
;; TTY type terminal
(if (and (not window-system)
(not (equal system-type 'ms-dos)))
(progn
(if first-time
(progn
(keyboard-translate ?C-h ?C-?)
(keyboard-translate ?C-? ?C-h)))))
;; Under UNIX
(if (not (equal system-type 'ms-dos))
(progn
(if first-time
(server-start))))
;; Add any face changes here
(add-hook 'term-setup-hook 'my-term-setup-hook)
(defun my-term-setup-hook ()
(if (eq window-system 'pc)
(progn
;; (set-face-background 'default "red")
)))
;; Restore the "desktop" - do this as late as possible
(if first-time
(progn
(desktop-load-default)
(desktop-read)))
;; Indicate that this file has been read at least once
(setq first-time nil)
;; No need to debug anything now
(setq debug-on-error nil)
;; All done
(message "All done, %s%s" (user-login-name) ".")
Now, this is all very well if you only want to program in the languages already catered for in the .emacs file (C, C++, Perl, Lisp and Scheme), but what happens if a new language called "whizbang" comes out, full of exciting features?
The first thing to do is find out if whizbang comes with any files that tell Emacs about the language. These usually end in .el, short for "Emacs Lisp". For example, if whizbang is a FreeBSD port, we can locate these files by doing
So for example, if the output from the find command was
Find the auto-mode-alist entry in .emacs and add a line for whizbang, such as:
(".lsp$" . lisp-mode)
(".wiz$" . whizbang-mode)
(".scm$" . scheme-mode)
...
ending in .wiz.
Just below this, you'll find the font-lock-auto-mode-list entry. Add whizbang-mode to it like so:
(defvar font-lock-auto-mode-list
(list 'c-mode 'c++-mode 'c++-c-mode 'emacs-lisp-mode 'whizbang-mode 'lisp-mode 'perl-mode 'scheme-mode)
"List of modes to always start in font-lock-mode")
And that's all that's needed. If there's anything else you want done automatically when you open up a .wiz file, you can add a whizbang-mode hook (see my-scheme-mode-hook for a simple example that adds auto-indent).
"Too much X around, I'm confused!"(A novice to X )
Running 'man X' on Unix - and on your system if the necessary backends are installed ('man' or 'xman' is generally a good idea for terse descriptions of terms, apps and interfaces) - spits out something (cryptical) like:
X - a portable, network-transparent window system
SYNOPSIS
The X Window System is a network transparent window system which
runs on a wide range of computing and graphics machines. It should
be relatively straightforward to build the X Consortium software
distribution on most ANSI C and POSIX compliant systems. Commercial
implementations are also available for a wide range of platforms.
The X Consortium requests that the following names be used when
referring to this software:
X ; X Window System; X Version 11; X Window System, Version 11;
X 11
X Window System is a trademark of X Consortium, Inc.
DESCRIPTION
X Window System servers run on computers with bitmap displays. The
server distributes user input to and accepts output requests from
various client programs through a variety of different interprocess
communication channels. Although the most common case is for the
client programs to be running on the same machine as the server,
clients can be run transparently from other machines (including
machines with different architectures and operating systems) as well.
X supports overlapping hierarchical subwindows and text and graphics
operations, on both monochrome and color dis plays. For a full
explanation of the functions that are available, see the Xlib -
C Language X Interface manual, the X Window System Protocol
specification, the X Toolkit Intrinsics - C Language Interface
manual, and various toolkit documents.
The number of programs that use X is quite large. Programs provided
in the core X Consortium distribution include: a terminal emulator,
xterm; a window manager, twm; [...] access control programs, xauth,
xhost, and iceauth; user preference setting programs, xrdb, xcmsdb,
xset, xsetroot, xstdcmap, and xmodmap; [...]; utilities for listing
information about [...] displays, xdpyinfo, xlsatoms, and xprop;
[...]; a display server and related utilities, Xserver [...]; a
utility to terminate clients, xkill; [...]
Many other utilities, window managers, games, toolkits, etc. are
included as user-contributed software in the X Consortium distribution,
or are available using anonymous ftp on the Internet. See your site
administrator for details.
The newest OS/2-port is introduced in "The XFree86/OS2 FAQ" (by Holger Veit, Sebastien Marineau):
*1.1 What is X11, X11R6.3, XFree86, XFree86/OS2?X11, more precisely called "The X Window System" is a complete window system that usually runs as the de-facto standard in Unix environments. X11R6.3 is the name of the current release (precisely, the most recent one is X11R6.4, but this is not part of the 3.X server line. XFree86 is a port of the X Window System to x86-based systems. XFree86/OS2 is a particular port of XFree86 for OS/2 based systems.
1.2 Where can I find more information?
Books about X11 exist in any well-sorted CS bookstore.
Some URLs:
Newsgroups:
- http://www.x11.org/ (this has a rather large bibliography)
http://www.xfree86.org/
http://set.gmd.de/~veit/os2/xf86os2.html (XFree86/OS2)
Mailing list (for XFree86/OS2): see Q 1.7 [...]
- comp.windows.x.* (X11 specific things)
comp.os.os2.programmer.* (OS/2 specific things)
*1.7 Is there a mailing list for XFree86/OS2?
Yes, read
for details. [...] There is also a digest version available, read the above URL. [...]
- http://set.gmd.de/~veit/os2/xf86mail.html
1.16 Are there some X11 books?
Sure, ask in your book store. A user's guide is for instance:
Niall Mansfield, The X Window System - A User's Guide, Addison Wesley, or The Definite Guides to the X Windows System, Volume Three, X Window System User's Guide, O'Reilly&Associates
The latter one is part of an eight (nine,ten?) volume documentation set for the X11 programmer. From my biased point of view of a programmer, this is the most comprehensive must-have for the serious programmer (however, it does not belong to the cheaper booksets, unfortunately)."
XFree86 and most of it's ports stem from Unix/POSIX; it is advisable that you download the Posix' standard utilities like rm, grep, mkdir, cp etc.
The required shell for Rexx is CMD.EXE. While this is familiar, porting will require a shell that acts like AT&T 'sh', like the xksh. I recommend a recent version of ash/bsh, the Berkeley Unix standard shell; other ported shells often have too small command-line buffers.
"Adrian Gschwend" <ktk@netlabs.org> introduces it: "Today Brian Smith released the first beta of EverBlue, the XLib-PM implementation for OS/2 which allows you to compile Xlib (Unix/Linux) applications as native PM applications.
The beta is not really usable for endusers but developers can use it to compile PM versions of X-Applications. There is just a small demo included so maybe it does not make sense to download it now if you are not a developer.
Sourcecode of this betadrop is also included in the package, just install it. If you want to support this project you can subscribe to the EverBlue mailinglist at Egroups, we still need a lot of support because the project is not yet complete. I will set up a CVS server at Netlabs as soon as we decided how to organise the structure of the source. Informations about CVS will be posted to the EverBlue mailinglist.
Run e.g. on Linux
NAME
System calls are documented in /emx/doc/system.doc.The term ' POSIX' denotes in the following all (development) platforms "aiming at IEEE 1003.1/2 compatability" and is not restricted to those relatively few commercial platforms that passed the official tests and paid the license fee. Please note that there is e.g. an old Linux/POSIX distribution with an 'official' POSIX label. This means: We will neglect all old broken pre-Posix Unix-like systems and encourage to completely forget about them.All system calls are declared in <emx/syscalls.h>. Interface routines are in the emx.a and emx.lib libraries. System call emulation routines are in the sys.lib libraries. Please do not use system calls from application programs - always use C library functions. If you need a system call, put a function into the library which issues the system call. [...] The goal is to have Unix-like system calls.
Moreover if we go into details we should explain XPG, X/OPEN, Spec1170, etc. Please consult Unix tutorials here.
Specify the desired environment (cf. emx GNU gcc manual 2.12/2.8):
__EMX__. If the -Zmt, -Zmts, or -Zmtd option is given on the
GCC command line, the symbol __MT__ is defined."
"The following assertions are predefined in the emx port of
gcc:
#system(unix),
#system(emx),
#cpu(i386) and
#machine(i386)."
If more than one of these are defined, they accumulate. For example:
_POSIX_SOURCE IEEE Std 1003.1.
_POSIX_C_SOURCE
if >=2 add IEEE Std 1003.2;
if >=199309L, add IEEE Std 1003.1b-1993
[Note: No specific emx support! Also cf.
Now time has come to compile some simple demo code. Here I will not add another one to the pile of introduction to ANSI C, several excellent tutorials exist. All I can express here is a kind of compliment to Eberhard Mattes for his emx development tools and libraries: There is not much to say about compiling standard C (and C++; provided you use a recent compiler version) with emx. Just start.
This is just the right moment to re-read the README and INSTALL file of emx. Then compile the sample code provided by Eberhard Mattes and have a close look at the first chapters of the Emx Application Developers Guide, which introduce its philosophy. Doing both carefully might save you trouble later:
``There are three methods for creating executable files:The default stack size is 0x8000 bytes. 512 kbytes of local stack are sufficient for small tools, but too small for complex programs; pre-allocate 32 Mbytes local stack to be on the safer side. Note that all of the command line arguments and environment pointers are copied to the stack on startup!!! Small local stacksizes are the most prominent reason for seemingly arbitrary erroneous program behaviour. (Your program code is correct: The error will disappear when you perform the usual testing in a debugger ;-).(E1) using ld and emxbind
(E2) using emxomf, emxomfld and LINK386, the program will use the emx.dll dynamic link library for performing system calls
(E3) using emxomf, emxomfld and LINK386, the program will be linked with a system call library (creating a stand-alone application or DLL)
The assembler [GNU as] creates a Unix-style a.out object file (.o file). When using method (E1), .o files created by the assembler are linked by a Unix-style linker [GNU ld] with Unix-style libraries (.a files) to create a Unix-style a.out file. Then, emxbind is used to turn this file into an .exe file that can be executed under both OS/2 and DOS. Using method (E1) enables core dumps and fork(). Moreover, programs created with method (E1) can be debugged using GDB, the GNU debugger. Programs created using method (E1) use emx (emx.dll under OS/2, emx.exe under DOS) for system calls.
When using method (E2), the .o files created by the assembler are converted to Object Module Format files (.obj files). These files are linked with the OS/2 linker LINK386. The libraries are .lib files. emxomfld is a front end to LINK386 which converts the ld command line to a LINK386 command line. Programs created with method (E2) cannot create core dumps, cannot call fork() and cannot be debugged with GDB. Method (E2) works only under OS/2 and creates programs that work only under OS/2. You can use IBM's IPMD and SD386 debuggers to debug programs created with methods (E2) and (E3). Files created with method (E2) are usually smaller. The emx.dll dynamic link library is used for system calls. The -Zomf option of GCC selects method (E2).
When using method (E3), the program won't call the emx.dll dynamic link library. A system call library (emx emulator) is linked to the program. The system call library maps system calls to OS/2 API calls. Only a subset of the emx system calls is available with method (E3). For instance, the general terminal interface is not available. Functions which are not available or are limited with method (E3) are marked [*] in the library reference. Use a module definition file and the STACKSIZE statement to set the stack size. Alternatively, you can use the -Zstack option of GCC.''
Now a trivial example to illustrate some Posix compliant emx interfaces. Refer to the Emx C Library Reference for the details about open(), read(), write(), etc.
(For an example how to use the more efficient ANSI streams counterparts fopen(), fread(), fwrite(), etc., and how to change file attributes like date and time, please cf. below: cpfile.c in the Chapter ``Some Notes on Portability and Coding Style''. It is an example for a widely portable symlink() replacement ).
Please read the comments in the source; in fact the source has only been written to illustrate the comments and to introduce the compiler commandline frontend 'gcc.exe'.
/* demonstrate_EMX_and_XFree86_os2_io.c: gcc -Zbin-files
Just try to compile this primitive example with any
commercial OS/2 compiler
*/
#define TRUE 1
#define _POSIX_C_SOURCE 2
#include <unistd.h> /* Posix io and constants; contains
#define _POSIX_SOURCE,
#define _POSIX_VERSION 199009L
*/
#include <stdio.h> /* ANSI conformant io, Posix extensions:
* fdopen(), fileno()
*/
#include <stdlib.h> /* ANSI C: getenv(); Posix: environ */
#include <sys/types.h> /* Posix: for open() you need
sys/types.h, */
#include <sys/stat.h> /* sys/stat.h and fcntl.h */
#include <fcntl.h> /* man pages/Lib Ref. of the command say
which include files need to be mentioned, for
portability follow ANSI and Posix, X apps should
conform to the similar MIT X Open standard */
#include <string.h>
/* MIT X Open/Posix and ANSI type globals */
ssize_t bytes_read; /* ssize_t is in unistd.h, sys/types.h */
size_t fp_bytes_read; /* size_t is in unistd.h, stdio.h */
int fd, fd2; /* File descriptors - unbuffered */
FILE *fp, *fp2; /* stdio.h - buffering */
char buffer[BUFSIZ] = ""; /* BUFSIZ is set up in stdio.h */
/* Use File descriptors - unbuffered */
static inline void
rawRead(const char *fileOrDevice)
{
#ifndef __EMX__ /* use only this for emx specific code,
not: OS2, 32BIT, etc. */
fd = open(fileOrDevice, O_RDONLY); /* include in low-level
fd-table */
#else
fd = open(fileOrDevice, O_RDONLY|O_TEXT); /* cf: emx C Library Reference */
#endif
if (fd == -1){
printf("Error! Reading fileOrDevice: ");
perror(fileOrDevice);
exit(1);
}
while (TRUE){
bytes_read = read(fd, buffer, BUFSIZ);
if (bytes_read > 0)
printf("%d bytes read from %s.n", bytes_read,
fileOrDevice);
else {
if (bytes_read == 0){
printf("End of file %s reachedn", fileOrDevice);
close(fd); /* Remove from the file-descriptor table!
Never mix buffered/raw i/o on the same
file or device */
break;
}
else if (bytes_read == -1){
printf("Error! Reading fileOrDevice: ");
perror(fileOrDevice);
exit(2);
}
}
}
/* use buffering */
static inline char const *
bufferedRead(void)
{
char const *etc_password =
(char const*)malloc(sizeof(getenv("ETC")) +
sizeof("TCPOS2.INI"));
strcpy((char*)etc_password, getenv("ETC"));
/* '/tcpos2.ini' is equivalent; cf. below
* the section about Unix file systems.*/
strcat((char*)etc_password, "TCPOS2.INI");
fp = fopen(etc_password, "rt"); /* "t" is ANSI */
if (fp == NULL){ /* read below about changing fopen(), etc. */
printf("fopen failed to open %s.", etc_password);
fputs("No secret file foundn", stderr);
exit(1);
}
while(TRUE){
fp_bytes_read = fread(buffer, 1, BUFSIZ, fp);
if (fp_bytes_read == 0)
break;
printf("n%ld bytes read and copied from %s.n",
fp_bytes_read, etc_password);
}
rewind(fp); /* go back to the start of the file */
/* Find the descriptor associated with a stream */
fd2 = fileno(fp);
if (fd2 == -1)
fputs("fileno() failedn", stderr);
/* Find the stream associated with a descriptor */
fp2 = fdopen (fd2, "rt");
if (fp2 == NULL)
fputs("fdopen failedn", stderr);
fclose(fp2);
return (etc_password);
}
int
main(int argc, const char *argv[])
{
const char *secret = bufferedRead();
printf("Your secret file found in: %sn", secret);
if (argv[1] == NULL)
rawRead("/config.sys");
else
rawRead(argv[1]);
return(0);
}
-----snip: cut here-----
compile the example with
demonstrate_EMX_and_XFree86_os2_io.c'
In my opinion, too small stack sizes, usually chosen with the -Zomf switch and its far too small default size of 32k, are the #1 reason for unexpected program behaviour or seemingly random crashes. The effect often only becomes visible if large data objects are processed. So please remember ! Don't be too shy to preallocate several Mbytes, as OS/2 will efficiently manage physical memory.
;running in an OS/2 window or under XFree86/OS2,
;
NAME WINDOWCOMPAT NEWFILES
DESCRIPTION
'demo programs, emx 0.9d runtime required'
; cf. emx Reference: Equal to 'gcc -Zstack 512'
STACKSIZE
524288
; enlarge if necessary
HEAPSIZE
524288
; enlarge if necessary
; Note: X apps coming from Unix often need huge values,
; 32 Mbytes won't hurt!
; end module definition file
------snap------
``The malloc() implementation of emx 0.9c and later can use multiple heap objects under OS/2; in consequence, the heap size is no longer limited under OS/2. However, if you use another malloc() implementation which does not support non-contiguous memory allocation or if the application is run under DOS, the maximum size of the heap available for malloc() is fixed. [...]The default heap size is 32 MByte under OS/2. For the emx implementation of malloc(), this is the size of the initial heap object and the minimum size of additional heap objects. If ld and emxbind are used for linking, you can change the heap size with the -h<heap_size> option of emxbind.
If emxomf and LINK386 without -Zsys are used for linking, the heap size cannot be changed. If -Zsys is used, initialize the variable _sys_heap_size to the desired heap size:
This can be done in any module explicitely linked to your application. ``
In the old days, when men still were REAL MEN and wrote their own device drivers...(Linus Thorvalds)
Many uncommon devices are being used for X.
A GUI through slip and a 9600 baud modem ain't no fun. And even an overcrowded Ethernet makes you spend your youth in front of a monitor waiting for response... So you will prefer fast local communication. For this you should compile your apps on OS/2!
This is what you will find in the current XFree86/OS2 implementation's device front:
"If you must do port I/O, use the functions from xf86sup.sys for this; this driver is there anyway, so why not take advantage of it? Common precautions for directly accessing hardware apply. Code that relies on Unix security, job control, tty/pty handling, needs major rewriting. In most cases, it is easier to make a separate file for OS/2 rather than clutter the original one with #ifdef clauses."So Inter-process communication uses BSD inet SOCKETS for remote, a NAMED PIPE for local communication, pseudo-terminals which are unfortunately not fully BSD compatible (for porting you need to look into the 'Xterm' port source code) and some other uncommon extensions, provided by 'XF86SUP.SYS'Q: What is the XF86SUP.SYS device driver good for?
"This device driver provides certain essential base functionality which is missing from standard OS/2." [...]"When I use xconsole, there is always a warning message in the first line. What is that? You mean the text Warning: process PID=????? is accessing /dev/fastio$, right? This is an intentional notification that some process uses the fastio device to perform access to I/O ports. It is also intentional that this message cannot be disabled. As long as the PID is the process ID of the X server, everything is okay. If the PID belongs to another, unknown process, you should seriously ask the author of the program what he/she is doing with our hardware. [...]"
(The XFree86/2 faq)
Cf. http://set.gmd.de/~veit/os2/xf86sup.html
and the excerpts of the faq, reordered and abridged by me for a faster overview:
OS/2 API : "You may use all Dos* API functions, as long as they don't conflict with emx somehow (e.g. _beginthread vs. DosCreateThread). Stay away from Kbd*, Mou*, Vio* calls - none of those will do what you expect, but you can severely influence the operation of the X11 system. [...]"Hint: Use instead ANSI C ('stdio.h') (or POSIX ('unistd.h')) interfaces!
"3.11 Can I use another shell like 'bash' in place of CMD.EXE in an xterm? Most shells are subtly incompatible with the PTY mechanism used in xterm. A symptom may be that they won't work either if you redirect console I/O to a COM terminal which should be possible. bash is such an incompatible shell. See also Q 3.12 and Q 3.13.
Meanwhile, there are special ports of tcsh and ksh which work in an xterm.
3.12 I use 4OS/2 (or some other shell) as my shell. Will it work in xterms? 4OS2 has a number of misfeatures, politely spoken, in an xterm. We attempt to fix this in the future. For now, don't use it in an an xterm. If 4OS2 or another shell is in your OS2_SHELL or SHELL variable, please add an environment variable named X11SHELL to point to a valid CMD.EXE path, e.g. SET X11SHELL=D:OS2CMD.EXE in your CONFIG.SYS. This will override the OS2_SHELL or SHELL setting which is used otherwise. 3.13 Why does OS2MORE.COM give a SYS0447 in an xterm? This is a program which silently assumes it has access to the keyboard and the screen: it uses functions from KBDCALLS, MOUCALLS, or VIOCALLS. There are more programs of this kind, for instance: most *.COM in os2, ATTRIB.EXE, BACKUP.EXE, CACHE.EXE, EAUTIL.EXE, FIND.EXE, HELPMSG.EXE, LINK.EXE, LINK386.EXE(!), PATCH.EXE, PSTAT.EXE, REPLACE.EXE, RESTORE.EXE, SETBOOT.EXE, SORT.EXE, SPOOL.EXE SYSLEVEL.EXE, TEDIT.EXE, TRACE.EXE, UNPACK.EXE, XCOPY.EXE, XDFCOPY.EXE, but also unfortunately TELNET and FTP, and some others; infact, almost any 16 bit application is a possible candidate. Note that CMD.EXE is an exception of the rule: it is largely still 16 bit, but is clean for XFree86/OS2 use.
You can use the EWS utility EXEMAP.EXE to check if these DLLs are linked in.
Note: the existance of these DLLs does not mean that the program does not work at all, actually EMX.DLL could call KBD API functions but usually does not in the X11 environment. However, certain unexpected effects may occur in an xterm. I'll try to find a workaround for this in the future, but don't count on this. 3.14 FTP seems to work in an xterm, but it does not hide the password? This is a side effect of what was described in Q 3.13. So actually FTP does NOT work. [...]
Now some concepts and short hints related to the NAMED PIPE for local communication with the X server. Emx unfortunately does not yet have a user friendly Posix compatible mkfifo() interface, this is due to the still missing Posix compatible file system. You can use the native dos*npipe() interfaces of OS/2 to create /PIPE/* devices, then register file handles via _imphandle() to use them with emx library functions as well as with the native OS/2 kernel api. Note: Their behaviour is not Posix compatible; cf. for details OS/2 api reference manuals.
"2.52 What does the error message "_X11TransOs2OpenClient: Open server pipe /PIPE/Xxf86.0 failed" mean?
This is for the local named pipe communication the same problem as Q 2.31 is for the network connection. It basically means: the server crashed for some reason, and now a client, e.g. xterm cannot connect to the server. The reason is probably hidden somewhere else in the XF86Config file, or some other setup problem (e.g. network configuration). [...]2.31 What does "SocketINETConnect() can't connect: errno = 65" mean? What does "SocketINETConnect() can't connect: errno = 61" mean? These messages *can* point out a network installation problem, in many cases they are secondary errors, though, and are really caused by a different problem. Nevertheless, you should ensure that your network is setup correctly. [...]
3.8 How can I enable authorization for host foo.edu without using xhost?
Create the file X0.hosts in XFree86libX11. To enable connections from a certain host, add in a line containing inet:hostname to the file. For example, your file may look like this: inet:foo.edu, inet:friendly.host.edu .. Note that the X0 refers to the zero in the display name os2systemname:0, i.e. in the case of Q 3.6 this would become a X1.hosts then. [...]
Q: Can emx be used to generate 16 bit code for programming OS/2 device drivers?
No!. :
(Holger Veit): "as gas lacks the ability to produce arbitrary segment types beyond 32 bit .text/.data/.bss segments. It can generate 16 bit instructions in a 32 bit segment (prefixed with 0x66/0x67), though."On Wed, 13 Oct 1999 17:58:35 +0100, Csaba Raduly wrote:
"But you can write device drivers in 32-bit using some (already existing) thunking mechanismCheck out
on hobbes."IBM distributes the device driver devel package (including compiler) for free! You may want to look at
Dynamic link libraries are great, but ain't easy.
Historic pre-Posix systems of the Unix family, using the a.out binary format, allowed static linkage only:
Common functions could only be archived for faster linking with the librarian ar and the librarian indexer ranlib (or ar -s), thus creating a.out archives. Most prominent is the standard C library system call library (libc.a), which every program needs. It is linked in automatically. This is equivalent to c.a (a.out) and c.lib (omf) of emx, plus some additional system specific helper libraries. Conversion is possible, cf. emxomf docs.
A difference is the math library (libm.a): Traditional Unix uses a separate libm.a. It must be specified explicitly as last entry of the linker command line (-lm).
Emx automatically statically links those functions in, - in fact they are a part of libc.a -, whereas libm.a is only a dummy. If you use -Zcrtdll, the equivalent import library versions for implicit dynamic linkage are used instead; more details later.
AT&T Unix System V Release 4 (SYSVr4) introduced the new ELF binary format, which allowed for the first time an efficient and flexible use of shared global functions and variables (shared objects; *.so).
This is achieved by creating shared read-only code and special shared read-write data segments, which can be used by multiple processes and are now better supported by the new binary format. The global emx picture is here with a grain of salt nearly the same as the one for Unix; omf binary format is potentially somewhat more complex, though. A process (session) consists mainly of the following memory sections:
All are assembled to relocatable object code - which still contain unresolved symbols - or data segments and later, after symbol fixup, moved to their final memory location by the OS's linker and the executable loader, respectively. A.out segments need in addition a small a.out-loader, which is added by emxbind or they need to be converted by -Zomf and emxomf to native object module format before linking with link386.
Don't confuse the following, just because on OS/2 and on ELF systems they use the same file type, i.e.: *.so or *.DLL, respectively:
Unlike most Posix systems, OS/2-emx does not export automatically any function or global variable. You must write a "module definition file", listing exported symbols. This gives you the chance to make sure that all entry points are identified by ordinal numbers, which have to be *identical* for new library revisions (binary compatibility). You can add new exports at the end of the list, though. If you don't make sure that your entry-points are identical among library revisions and that the called functions and exported global variables or constants do exactly what they should do, you will loose binary compatibility and you will have to relink all applications using the dll. After linking the dll this "module definition file" is then passed to the 'emximp' tool in order to create an "import library" (link library) that contains necessary fixups for the linker to resolve the exporty dll symbols when linking you application against this dll.
"EXPORTS Make functions and variables visible outside the .exe or .dll file. All entry points of a dynamic link library must be exported using EXPORTS. Exporting entry points of .exe files is less common. " (Emx docs).
The 'emxexp' tool as well as studying the library's public header file may help you to identify which symbols are worth to be exported for later linker fixup, when compiling your client application.
Hints:
Use short names (symbols) in a library, especially with C++. The omf linker (link386.exe) has a symbol length restrictions (512 characters). Else you'll probably see multiply defined symbols linker errors. Some more special tips at the end of this chapter.
You'll encounter a 'dynamic library' that differs from a 'shared object' (*.so), if you try to port recent apps like 'dia', that come from Posix/SYSVr4-Unix:
"Dia plugins are all dynamic libraries, so the program stops working once you start using NONAME keyword."Shared libraries - they are commonly called shared objects for clones of SYSVr4-Unix (which introduced the ELF-object module format) - are something implicitly linked against the main executable and loaded at the start-up of the executable. It has been described above how this is achieved explicitly by linking against import libraries created from module definition files.
Dynamic libraries are NOT linked to the main module but loaded at the run-time. Don't confuse them, just because on OS/2 and on ELF systems they are the same file. Any systems comforming to "The Single UNIX Specification version 1/2" from 'The Open Group' can use the dlopen() system call to load dynamic libraries at the run-time in a unified manner and call dlsym() to resolve symbols. Ports of this special mechanism are available separately; emx does not provide the dlopen() and dlsym() system calls.
Here a demo (the common libxpm library); cf. the port's diff by H. Veit. This xpm library revision is e.g. linked into xforms.dll rev. 0.88.
;under XFree86/OS2, linked with '-Zcrtdll'
; initialize shared objcts per instance
LIBRARY XPM INITINSTANCE TERMINSTANCE
DESCRIPTION "X11R6 XFree86 libXPM xpm.dll for OS/2 EMX09C VERSION=7 (xpm3.4g)"
;
;reduce memory fragmentation (for larger and less frequently used library
;code save memory and use the 'CODE LOADONCALL' linker flag instead)
CODE
PRELOAD
;
;.text objects are globally shared memory, .data is individual to any client
;process
DATA
MULTIPLE NONSHARED
;
;is this really needed here? Well, at least doesn't hurt.
STACKSIZE 65536
;
;exported client interface symbols for linker fixup, 'NONAME' reduces table
;size, helps that you link only with binary compatible libraries, else
;you'll experience immediate errors/crash (yes, we'll go the hard way ;-)
EXPORTS
XpmCreatePixmapFromData @ 1 NONAME
XpmCreateDataFromPixmap @ 2 NONAME
XpmReadFileToPixmap @ 3 NONAME
XpmWriteFileFromPixmap @ 4 NONAME
XpmCreateImageFromData @ 5 NONAME
XpmCreateDataFromImage @ 6 NONAME
XpmReadFileToImage @ 7 NONAME
XpmWriteFileFromImage @ 8 NONAME
XpmCreateImageFromBuffer @ 9 NONAME
XpmCreatePixmapFromBuffer @ 10 NONAME
XpmCreateBufferFromImage @ 11 NONAME
XpmCreateBufferFromPixmap @ 12 NONAME
XpmReadFileToBuffer @ 13 NONAME
XpmWriteFileFromBuffer @ 14 NONAME
XpmReadFileToData @ 15 NONAME
XpmWriteFileFromData @ 16 NONAME
XpmAttributesSize @ 17 NONAME
XpmFreeAttributes @ 18 NONAME
XpmFreeExtensions @ 19 NONAME
XpmFreeXpmImage @ 20 NONAME
XpmFreeXpmInfo @ 21 NONAME
XpmGetErrorString @ 22 NONAME
XpmLibraryVersion @ 23 NONAME
XpmReadFileToXpmImage @ 24 NONAME
XpmWriteFileFromXpmImage @ 25 NONAME
XpmCreatePixmapFromXpmImage @ 26 NONAME
XpmCreateImageFromXpmImage @ 27 NONAME
XpmCreateXpmImageFromImage @ 28 NONAME
XpmCreateXpmImageFromPixmap @ 29 NONAME
XpmCreateDataFromXpmImage @ 30 NONAME
XpmCreateXpmImageFromData @ 31 NONAME
XpmCreateXpmImageFromBuffer @ 32 NONAME
XpmCreateBufferFromXpmImage @ 33 NONAME
LIBRARY X11 INITINSTANCE TERMINSTANCE
DESCRIPTION
'X11.DLL (MIT X Standards Library for emx-port of XFree86).'
CODE
LOADONCALL
DATA
LOADONCALL MULTIPLE NONSHARED
STACKSIZE ;is this really useful?
0x80000
HEAPSIZE
0x160000
EXPORTS
XSaveContext @1 NONAME
.... etc.
I recommend the following simple (unorthodox, but nearly 'foolproof') procedure:
Use the static archive provided on the net or use the Makefile (normally created by Imake, but today more and more by the OS/2 port of autoconf; details later) to make the static archive foo_s.a. Then:
Linking order is crucial! The traditional C-style linker must find the fixup for still unresolved symbols in the same library or in another one which is _following_ on the command line. You might even need to link in rare cases like: -lfoo1 -lfoo2 -lfoo1
where foo1 needs symbols exported by foo2 and foo2 needs symbols exported by foo1.
For a debuggable library compiled with '-g' you'd use the same line (except: '-Zomf -s' and the '-Zlinker blurb', which are specific to link386).
Cf. emx docs:
"You should not use the GNU C++ libraries (libg++ and libstdc++) if you create a DLL (unless you are building a stand-alone DLL or a private C runtime DLL) as there is not yet a DLL version of these libraries.
As creating the module definition file for the DLL by hand is quite boring, you should let your computer do it: see the description of the emxexp tool for details. The sign sample of emxample.zip uses emxexp to create the module definition file.
To call the constructors and destructors for static objects in the DLL, make sure that the _DLL_InitTerm() function of the DLL calls __ctordtorInit() and __ctordtorTerm(). You should call _CRT_init() before __ctordtorInit() to ensure that the C library has been initialized for use by the constructors, even if OS/2 happens to initialize the DLLs in a wrong sequence. " [The default function, being linked automatically with -Zdll, should suffice.]
Beware: Redefines of functions are debugging headaches: The actual code won't be explicitly in the sources; you will forget, that you redef'd!
(On Mon, 26 Apr 1999 08:59:44 +0900, Shigeru Miyata wrote:)
Instead of using preprocessor macros, you can use import libraries for the purpose in some cases. If the prototypes of the functions are declared somewhere, you can run emximp to create .a/.lib on the module definition file like this:
EXPORTS
strcasecmp @219
strncasecmp @225
;getcwd @779
;chdir @764
;lstat @561
[...]
entryname is the name of the function as made visible outside of the .exe or .dll file. entryname is always converted to upper case. internalname is the name of the function as defined in your program. If =internalname is omitted, it is assumed to be identical to entryname. internalname is case sensitive. Exported functions not only have a name (entryname), they also have an ordinal number, the position within the name table. Using ordinal numbers when importing saves space and is supposed to be faster.Notice also that you can concatenate import libraries by ar/emxomfar. So if you don't like OS/2 specific libraries to be passed to the linker like -lmmap -lshm ... Then you can make iberty.a/iberty.lib for OS/2.Example:
Use NONAME to avoid putting entryname into the name tables.
- EXPORTS
my_qsort=qsort1 @1 RESIDENTNAME
(On Fri, 15 Oct 99 08:59:15, Asbjoern Pettersen wrote:)
>I'm no popuplog expert. Where do I find more info about it ?
IBM service pac CDs contain a Redbook ("The OS/2 Debugging Handbook, #0.6"), if you really want to undergo the pain of low level (Intel architecture) debugging.
Can't you just compile a.out (no -Zomf and -g, this links also the dll's with ld.exe, but you might have to link the *.o files directly, linking against archives *.a sometimes miss symbols despite ar -s) and use pmgdb or gdb? Moreover you have then the luxury of nice core dumps, that poor non-emx developers can only envy!
So you ought to provide debuggable a.out binaries. What you can do (and what I've done to build LYX dll's) is to compile without -Zomf and with -s. After that you'll compile an unbound a.out symbol file (same procedure as before, just leave out the '.dll' suffix from -o foo.dll. If need be, you can always feed the debugger this symbol file, while the dll has a reasonable size.
>> Well surely I can create "-g" dll monsters ;) But there is a problem, ld.exe
>> refuses to build dll from my .def files
>> complaining about some symbols, etc.. while link386 is up and ok.
>>link386 has /NOI flag, while ld does not have an equivalent.
>I haven't tested but modifying the line 47 of emx/src/emxbind/export.c
>may help.
>Anyway if I were you, I would compile only the relevant object into
>the exe with debugging on. It seems you haven't created the map file
>for glib12.dll Try using exemap as follows. Since I don't have
>gtk-1.2.5 here, the addresses below are not exact.
>Suppose you have a SIGSEGV in GLIB12.DLL 0001:00000e08
>exemap glib12.dll|grep "1 00000"|sort +2
>Now you will have something like
> 6 1 00000038 03 Entry32 EXPORT GLOBAL (G_ARRAY_NEW)
> 4 1 000000F8 03 Entry32 EXPORT GLOBAL (G_ARRAY_FREE)
> 3 1 00000168 03 Entry32 EXPORT GLOBAL (G_ARRAY_APPEND_VALS)
> 7 1 000001A8 03 Entry32 EXPORT GLOBAL (G_ARRAY_PREPEND_VALS)
> 5 1 00000208 03 Entry32 EXPORT GLOBAL (G_ARRAY_INSERT_VALS)
>[...]
> 27 1 00000C74 03 Entry32 EXPORT GLOBAL (G_CACHE_NEW)
> 24 1 00000DD4 03 Entry32 EXPORT GLOBAL (G_CACHE_DESTROY)
> 25 1 00000E34 03 Entry32 EXPORT GLOBAL (G_CACHE_INSERT)
> 28 1 00000EF4 03 Entry32 EXPORT GLOBAL (G_CACHE_REMOVE)
> 26 1 00000FB4 03 Entry32 EXPORT GLOBAL (G_CACHE_KEY_FOREACH)
>(The table above is that of glib12.dll v1.2.2 for emx/OS2)
>Now you know that the crash occurs in somewhere between "1 00000DD4"
>and "1 00000E34". Hence it is in G_CACHE_DESTROY @24 which is in
>gcache.obj
>It is a good bet that the problem is in fact enclosed in gcache.c
>and a few more sources. Then you don't have to build whole the
>library with debugging symbols.
>Add gcache.c to the source of scriptfu.exe, build and run it under
>gdb.
(On Sun, 17 Oct 1999 14:34:21 +0200 (MET DST), Stefan Neis wrote):
> Hi,
>> 1) When i compiling and linking programme with key -Zomf, i get in result link
>> error message: symbol defined more then once.
>> 2) When compiling and linking without omf libs (with ld) all ok.
>>
>AFAIR, there's some length limitation for symbol names in OMF-Format.
>gcc tends to generate really huge names. Especially for template
>instantiations a very large part at the beginning of two different
>instantiations might be identical, so the automatic truncating will give
>you the same name for different symbols.
>'nm' might be helpful to verify, if this is your problem.
>If it is, you can't do very much about it, besides praying for
>either IBM removing this stupid length limit or
>somebody modifying gcc to generate shorter unique symbols :-(
Note: pgcc already contains an experimental symbol name compression extension.
(On Wed, 20 Oct 1999 17:28:36 +0200, Alexander Mai wrote):
>Potential problem might be the DLL name: IIRC one can either use
> foo
>or
> z:barfoo.dll
>to access foo.dll (if in LIBPATH).
'foo.dll' generally does not work. It works if and only if it is already
loaded into memory by other processes (or the process itself). If you
want to use DLL as a dynamic library, specify the name either without
the extension or by the fullpath with the extension.
(Compiling qt apps under EMX; S.Miyata)
" -Qt 1.x apps must be compiled either with
emx09c without special flags
or
emx09d with the flags -fno-rtti -fno-exceptions
-Qt 2.x apps must be compiled with emx09d without special flags.
-Zomf is not recommended for Qt 2.x, the link may fail.
Now the problem with stdcpp.a turns out to be a bug in emx09d fixpack2
(missing exports in emxlibc?.dll). Using stdcpp.a from emx09c might work, but
it is the last resort. I cannot guarantee the result.
Also stdcpp.a is compiled with the default flags in gcc 2.8 with '-frtti
-fexceptions'. Only fix is to recompile stdcpp.a yourself with '-fno-rtti
-fno-exceptions' if you want to use it with Qt 1.x"
On Mon, 1 Nov 1999 16:25:35 +0100, Alexander Mai wrote:
>As long as things are rather simple, small and free of 'exceptions'
>everything seems to be fine. e.g. fltk rarely causes problems.
>And problems aren't solved in current egcs/gcc. It fails for valid
>code and in addition it fails for broken code which used to compile
>for years. It doesn't matther whom to blame for this,
>but from a user's point of view, it's a bad situation.
>Also it makes clear that an EMX 09e/ egcs 2.95.x wouldn't
>solve all problems (if any).
I recommend not to use exceptions, as they are slow and often buggy with all
current compilers. So the idea to recompile 'stdcpp.a' might often be the only
safe solution. Emx docs recommend not to link 'stdcpp.a' into a dll, as binary
compatibility is hard to achieve then.
I observed a wierd link386.exe bug wit C++-DLL's:
Linking against a static module/library (*.o/*.a) only works, if the system default libraries, which are passed automatically to the linker by the emx frontend, do not contain fixups for already resolved references. I.e.: Your mylib.lib contains your own version of foo::foo() and hence the symbol 'wierd-C++-symbol-name-for-foo::foo-function'. But foo::foo() is already defined in some standard library, which is automatically passed to the linker. Normally this is no problem, because the linker will only use the first reference and ignore the second one, so that you can safely override the standard version withyour private function.
But for some reason it behaves different for certain C++-symbols and tries to link against both contradicting function definitions. So you must temporarily disable the passing of the second symbol. To achieve this you may create a modified import library, which does no more contain the symbols you want to override.
On Fri, 12 Nov 1999 16:44:44 +0100, Holger Veit wrote:
>> Hi!
>>
>> Today when I run xchat in my X with panel and GNOME icq applet I received approx.
>> such message:
>>
>> Sys 0008: ZVT Not enough memory to run program. I checked theseus I had 30Mb of
>> RAM free.
>
>I have seen this before with memory intensive programs, or debuggable X11 DLLs
>(very large). Another resource is probably exhausted, such as the space
>available for DLLs (64MB) or the process heap (about 300MB).
So, respect DLL resource limits. Process heap limits may be lifted in future versions
of OS/2, meanwhile one must live with the restriction of of 512 MB per process address
space.'
Traditionally X apps come with an Imakefile to use the old tool available for virtually all official X11R6 platforms (and some others). According to a set of rules and system-specific templates, imake generates a giant Makefile for Unix- style make. OS/2-emx and XFree86/OS2 then uses the GNU make utility for conditional compilation and linkage. Unfortunately this one differs from original AT&T-style make especially in the handling of libraries (archives). If you experience problems, please cf. the original documentation, available in GNU INFO-format.
The FSF GNU project uses a different suite of test scripts called auto* tools, written for Unix: autoconf, autoheader, automake, ... An experimental and useful, yet - due to the different file system, kernel and API design necessarily imperfect - port to OS/2 exists.
I would like to share my experience about autoconf and Imakefile:
- The effort of rewriting Imakefile/Makefile.emx is almost at finding all DEFINES for gcc. This can be easily done by searching -Dfoo in Makefiles generated by configure.
- Following are the main steps I apply to do port:
The following is meant to overcome some simple, nevertheless annoying problems and to make things work faster: Quick and simple fixes, some dirty stolen tricks.
`` The easiest way to write an Imakefile is to find another one that does something similar and copy/modify it! To change any of these variables, edit the site.def file.Imake.tmpl provides defaults for the following variables: [...; cf. your current version of README]. Library.tmpl provides defaults for the following variables: [...; cf. README]. Server.tmpl provides defaults for the following variables: [...; cf. README]. Threads.tmpl provides defaults for the following variables: [...; cf. README]. An <os>Lib.rules file provides defaults for the following variables: [...; cf. README]. An <os>Lib.tmpl file provides defaults for the following variables: [...; cf. README]. The following variables are used by some part of the tree: [...; cf. README].
- Make Variables
- The following make variables are used by imake rules and may be set in an individual Imakefile.: [...; cf. README]. Many other make variables are set up by the imake config files and can be used in an Imakefile. The easiest way to discover them is to look at the Makefile generated by an empty Imakefile.
- Comments
- Use C comment syntax in an Imakefile for comments that should not appear in the resulting Makefile. Use "XCOMM" at the start of each line to produce a comment that will appear in the Makefile. (The "XCOMM" will be translated into the Makefile comment character "#" by imake.) Do NOT use "#" as a comment character in Imakefiles; it confuses the C preprocessor used by imake on some systems.
- Imake variables
- Don't abuse the variables in X11.tmpl that describe particular pieces of X by using them to describe your own subsystems. Instead, create new variables that are defaulted using Imake.tmpl variables. [...]
- Common Rules
- Here are some of the common rules for building programs. How to use them is described in Imake.rules and in the O'Reilly book "Software Portability with imake."
Basic program-building rules
All of these except NormalProgramTarget also generate rules to install the program and its manual page, and to generate dependencies.
[...]
- SimpleProgramTarget
- Use if there is only one program to be made and it has only one source file.
- ComplexProgramTarget
- Use if there is only one program to be made and it has multiple source files. Set SRCS to the names of the source files, set OBJS to the names of the object files, and set DEPLIBS to the libraries that this program depends on.
- ComplexProgramTarget_1
- Like ComplexProgramTarget, but uses SRCS1, OBJS1, and DEPLIBS1 and can be used with ComplexProgramTarget_2 and ComplexProgramTarget_3 to build up to three programs in the same directory. Set PROGRAMS to the programs built by all of these rules. For more than 3 programs, use NormalProgramTarget for each.
- ComplexProgramTarget_2
- Use after ComplexProgramTarget_1 for the second program in a directory. Uses SRCS2, OBJS2, and DEPLIBS2.
- ComplexProgramTarget_3
- Use after ComplexProgramTarget_2 for the third program in a directory. Uses SRCS3, OBJS3, and DEPLIBS3.
- NormalProgramTarget
- Build a program. Can be used multiple times with different arguments in the same Imakefile.
Imakefile for directory with subdirectories
#define IHaveSubdirs
#define PassCDebugFlags CDEBUGFLAGS="$(CDEBUGFLAGS)"
SUBDIRS = list of subdirs ...
MakeSubdirs($(SUBDIRS))
DependSubdirs($(SUBDIRS))
[...]''The following may be useful, too.
SMiyata <miyata@kusm.kyoto-u.ac.jp> pointed out in posting to the XFree86/OS2 mailing list:
As "far as possible, the changes must be confined to Imakefiles and definition files bundled in the application. The settings in os2.cf and os2.rules are shared by other applications, and hence may cause conflicts in them. Also, the changes to the template files are local to your setting, thus, although it may help to build binary on your system, it is not suitable for porting, unless they are reflected in the distribution of XFree86 itself.There are indeed the cases that modifications must be made on template files coming with XFree86 rather than Imakefile and definition files coming with the applications [those often have uncommon names like local.cf or machinefile, etc.]:
Akira Hatakeyama <akira@sra.co.jp> reported elsewhere that, in order to build Xvnc on OS/2, the lines
in os2.cf must be modified to
since, else, the settings in vnc.def is overwritten by those of os2.cf"My proposed changed /XFree86/lib/X11/config/os2.cf:
#define OSVendor IBM
#define OSMajorVersion 3
#define OSMinorVersion 0
/*
* C library features
*/
#ifdef __EMX__
/* set this to what you like (almost :-)
* OS/2 does a nonstandard bootstrap of imake
*/
#define BootstrapCFlags -DBSD43
/* X_WCHAR and X_LOCALE are set in Xosdefs.h */
#define StandardDefines /**/
#define XawI18nDefines -DUSE_XWCHAR_STRING -DUSE_XMBTOWC
/* This will redirect everything to /XFree86
* Care will be taken that programs that need a file
* from this tree will add the environment variable
* X11ROOT to the search path so you can redirect the
* stuff to a different drive
*/
#ifdef ProjectRoot
#undef ProjectRoot
#endif
/* This will be mangled with a drive letter in most OS/2
* rules ...
*/
#define ProjectRoot /XFree86
/* ... but not when a user tries to compile something
* from a different drive Please report any directories
* missed to Holger.Veit@gmd.de
*/
#ifdef UseInstalled
#define IncRoot $(X11ROOT)/XFree86/include
#define LdPreLib -L$(X11ROOT)/XFree86/lib
#endif
/* more directories */
#define AdmDir $(LIBDIR)/adm
#define ConfigDir $(X11ROOT)$(LIBDIR)/config
DESTDIR = $(X11ROOT)
/* for the man pages: Make clear they are from X11 */
#define BookFormatManPages YES
#define ManDirectoryRoot /XFree86/man/man
#define ManSuffix 1X11
#define LibManSuffix 3X11
#define FileManSuffix 4X11
#define XmanSearchPath /XFree86/man/man
#define ManSourcePath $(MANPATH)
/* you must have installed groff! */
#define TblCmd tbl
#define ColCmd cat
#define NeqnCmd eqn
/* groff -Tascii -mandoc is for format portability */
#define NroffCmd groff -Tascii -mandoc
#define EqnCmd eqn -Tascii
#define TroffCmd groff -Tps
NEQN = NeqnCmd
NROFF = NroffCmd
/* A set of standard defines (warning: underscored emx
* extensions are now excluded from the headers unless
* -D_WITH_UNDERSCORE is defined):
*/
#define OSDefines -D__EMX__ -D__i386__ -DOS2_
-Demxos2 -D_POSIX_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE
#ifndef BuildXKB
# define BuildXKB YES
#endif
#ifndef BuildImplibs
# define BuildImplibs YES
#endif
#ifndef BuildLBX
# define BuildLBX YES
#endif
/* some more properties of the OS/2 implementation */
#define HasNdbm YES
#define HasPutenv YES
#define HasSockets YES
#define HasSnprintf YES
#define HasBsearch NO
#define HasLargeTmp NO
#define HasPoll NO
#define HasBSD44Sockets NO
#define HasSecureRPC NO
#define HasSymLinks NO
#define HasVFork NO
#define HasVarDirectory NO
/* #define HasStrcasecmp NO
see below */
#define HasStrcasecmp YES
#define Malloc0ReturnsNull YES
#define SetTtyGroup NO
#ifndef HasLibCrypt
# define HasLibCrypt NO
#endif
#ifndef HasShm
# define HasShm YES
#endif
#define ConnectionFlags -DTCPCONN -DOS2PIPECONN
#define StdIncDir $(C_INCLUDE_PATH)
/* XF86Setup stuff - does not work yet! */
#define HasTcl NO
/*define TclLibDir $(X11ROOT)$(SHLIBDIR) */
/*define TclIncDir $(X11ROOT)$(INCROOT)/tcl */
/*define TclLibName xtcl */
#define HasTk NO
/*define TkLibDir $(X11ROOT)$(SHLIBDIR) */
/*define TkIncDir $(X11ROOT)$(INCROOT)/tk */
/*define TkLibName xtk */
/*
* Compiler Features
*/
#define HasGcc YES
#define HasGcc2 YES
#define HasCplusplus YES
#define HasGcc2ForCplusplus YES
#define GccUsesGas YES
#define UseGas YES
#define GnuCpp YES
#define DoRanlibCmd YES
#define NeedConstPrototypes YES
#define NeedFunctionPrototypes YES
#define NeedNestedPrototypes YES
#define NeedVarargsPrototypes YES
#define NeedWidePrototypes NO
#define CppCmd cpp
#define StandardCppDefines -traditional
#define PreProcessCmd CppCmd
#define CcCmd gcc -Wall -mpentium -O2
#define DefaultCCOptions -D__ST_MT_ERRNO__
-Dstrcasecmp=stricmp -Zmtd
#define LibraryCCOptions -D__ST_MT_ERRNO__
-Dstrcasecmp=stricmp -Zsysv-signals -Zmtd
#define ServerCCOptions -D__ST_MT_ERRNO__
-Dstrcasecmp=stricmp -Zsysv-signals -Zmtd
#define CplusplusCmd g++ -O3 -Wall -mpentium /* or gcc */
#define CplusplusOptions -Dstrcasecmp=stricmp -Zmtd
-Zsysv-signals
#define MakeCmd x11make
#define AsCmd gcc -S
#define GccGasOption -DGCCUSESGAS
#define AsmDefines -DUSE_GAS
#define InstallCmd install
#define LdCmd ld
#define ExtraLoadFlags -Zbin-files -Zmtd -Zsysv-signals
#define ExtraLibraries -lsocket -lbsd
/* quite a number of programs you need */
#define ArCmd ar cq
#define RanlibCmd ar s
#define BourneShell /**/
#define LexCmd flex -l
#define LexLib -lfl
#define YaccCmd bison
#define LintCmd /**/
#define MvCmd mv
#define CompressCmd compress
#define GzipCmd gzip
#define LnCmd cp
#define CpCmd cp
#define RmCmd ImakeHelper 4
#ifndef UseInstalled
#define ImakeCmd imake
#define MkdirHierCmd mkdirhier
#define DependCmd makedepend
#else
/* imake and mkdirhier come from Imake.tmpl, but DependCmd must be overriden */
#define DependCmd makedepend
#endif
/* "shell scripts" in OS/2 have this extension */
#define SHsuf cmd
#define InstPgmFlags /**/
#define InstBinFlags /**/
#define InstUidFlags /**/
#define InstLibFlags /**/
#define InstIncFlags /**/
#define InstManFlags /**/
#define InstDatFlags /**/
#define InstallFlags /**/
/*#define OptimizedCDebugFlags DefaultGcc2i386Opt*/
#define OptimizedCDebugFlags
#define ServerOSDefines XFree86ServerOSDefines -DDDXTIME
#define ServerExtraDefines GccGasOption XFree86ServerDefines
#if HasShm
# define ServerExtraSysLibs -lshm
#endif
/*
* Make & install Features
*/
#define AvoidNullMakeCommand YES
_NULLCMD_ = @ rem
#define NullMakeCommand @ rem
#define StripInstalledPrograms NO
#define CompressAllFonts YES
#define CompressManPages YES
#define GzipFontCompression YES
#define DefaultUserPath .;/os2;/emx/bin;/tcpip/bin;$(BINDIR)
#define DefaultSystemPath /os2;/emx/bin;$(BINDIR)
#ifndef ExtraFilesToClean
# define ExtraFilesToClean *.~* *.exe *.dll *.obj *.lib
*.map
#endif
#if CompressManPages
#define CompressManCmd gzip -n
COMPRESSMANCMD = CompressManCmd
#endif
/* Use ProgramTargetName() macro: OS/2 has '.exe' suffix
* Change your Imakefile!
*/
#define ProgramTargetName(target) target.exe
/* we don't name libraries lib*.a
* Change your Imakefile!
*/
#define LibraryTargetName(libname) libname.a
#define LibraryTargetNameSuffix(libname,suffix) Concat(libname,suffix.a)
/* ... and we even don't do it in rules that should have
* used the above LibraryTargetName() macro
*/
#ifndef UnSharedLibReferences
#define UnsharedLibReferences(varname,libname,libsource) @@
Concat3(DEP,varname,LIB) = _UseCat($(USRLIBDIR)/,$(BUILDLIBDIR)/,libname.a) @@
Concat(varname,LIB) = LoaderLibPrefix Concat(-l,libname) @@
LintLibReferences(varname,libname,libsource)
#endif
#ifndef SharedLibReferences
#define SharedLibReferences(varname,libname,libsource,revname,rev) @@
Concat3(DEP,varname,LIB) = SharedLibDependencies(libname,libsource,revname) @@
Concat(varname,LIB) = LoaderLibPrefix Concat(-l,libname) @@
LintLibReferences(varname,libname,libsource)
#endif
#include <os2.rules>
#include <os2Lib.rules>
# include <xfree86.cf>
#else
#error Edit os2.cf for your (non-EMX) OS/2 system
#endif
Initially I thought imake was a labyrinthic beast, deserving the title of the Minotaurus among the make tools. But with small and simple fixes to work around some annoying bugs I was able to tame it a bit, so that 'xmkmf' at least does something useful:
Usually Imakefiles silently assume they are running on UNIX in a Bourne 'sh' environment, since authors intentionally do not support non UNIX platforms.
Just try running the following changed Rexx scripts first, to create a working environment (make.cmd):
'@echo off' PARSE ARG a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
/* x11make.exe 'MAKE=x11make.exe SHELL=d:/os2/cmd.exe' a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 */
'SET SHELL=D:OS2CMD.EXE'
'SET X11SHELL=D:OS2CMD.EXE'
'set emxshell=D:OS2CMD.EXE'
'set CONFIG_SHELL=D:OS2CMD.EXE'
'set MAKE_SHELL=D:OS2CMD.EXE'
x11make.exe 'MAKE=x11make.exe SHELL= MAKE_SHELL=os2cmd.exe
CONFIG_SHELL=d:os2cmd.exe' a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
'@echo off'
PARSE ARG a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
'set SHELL=d:/bin/ash.exe'
'SET X11SHELL=D:OS2CMD.EXE'
'set emxshell=d:/bin/ash.exe'
'set MAKE_SHELL=d:/bin/ash.exe'
/* x11make.exe 'MAKE=x11make.exe SHELL=' a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 */
x11make.exe 'MAKE=x11make.exe SHELL=d:/bin/ash.exe MAKE_SHELL=d:/bin/ash.exe CONFIG_SHELL=d:/bin/ash.exe' a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
Now lets assume xmkmf worked. Theoretically the steps are:
make install; make install.man
Imake is misconfigured on most (commercial) UN*X platforms. Authors normally compensate this by including many lines of (normally highly complicated) Bourne shell code and calling several Bourne shell scripts in the Imakefile. In this case 'make' won't work. Nor does a sh.exe as make shell work: The current implementation depends on cmd.exe and Rexx. Finding and rewriting everything in those immense Imakefiles, helper scripts and Makefiles isn't really an alternative. Just try alternatively make and gmake (having installed e.g. /bin/ash.exe). Now make should simply resume where it has crashed. Just delete 'products' of those crashes (empty script output, truncated object files, core dumps, etc.) before resuming the process. This makes using Imake real fun!
Tips by John Williams <jwilliams@commerce.otago.ac.nz> (concerning 'configure' of GNU 'autoconf', fixed by the OS/2-port, but still valid for certain parallel Bourne sh-scripts very common in Imakefiles and/or config.local, etc.):
"3. Configure couldn't search the OS/2 path for programs because the shell construct:
apparently needs 'bar' to be a space or colon delimited list, whereas the OS/2 $PATH is semi-colon delimited.4. Configure could not check to see if it's test programs were being compiled because it tests for success using test -s which means test that the file is not empty. The emx port of GCC creates foo.exe executables and touches foo, leaving an empty file. [...]
5. Overcome the above problems by setting an environment variable UNIX_PATH to be a colon delimited path. Next, search and replace using sed thus:
where os2patch.sed looks like:
and then execute sh Configure.os2. Lines three and four are due to Ilya Zakharevich, who also pointed out the following issues:1.if /tmp doesn't exist, make it
2. ensure CONFIG_SHELL=sh is set in shell environment you are running Configure from.
6. You can find what functions are contained in what object files and libraries by using nm (comes with emx). This also tells you which functions are referenced (called) by functions in that file."
config/README states: "The easiest way to write an Imakefile is to find another one that does something similar and copy/modify it!"
Here some strongly shortened examples that use Bourne shell code: sh -c 'for i in '"$(TEXT) $(SRCS) $(MISC) $(HDRS)"' ; do... etc., etc.
Excerpt from Imakefile for xpaint read/write directory:
#include "../Local.config"
INCLUDES = -I.. $(XPM_INCLUDE) $(TIFF_INCLUDE)
$(JPEG_INCLUDE) $(PNG_INCLUDE)
# Nothing to change below here
TIFF_SRC = writeTIFF.c readTIFF.c
#ifdef HaveTIFF
TIFF_OBJ = writeTIFF.$O readTIFF.$O
TIFF_DEF = -DHAVE_TIFF
#endif
[...]
XPM_SRC = readWriteXPM.c
XPM_OBJ = readWriteXPM.$O
SGI_SRC = readWriteSGI.c
#ifdef SGIArchitecture
SGI_OBJ = readWriteSGI.$O
SGI_DEF = -DHAVE_SGI
#endif
DEFINES = $(XPM_DEF) $(TIFF_DEF) $(SGI_DEF)
$(JPEG_DEF) $(PNG_DEF)
SRCS = rwTable.c readWriteXBM.c readWritePNM.c readWriteXWD.c writePS.c readGIF.c writeGIF.c $(XPM_SRC) $(TIFF_SRC) $(SGI_SRC) $(JPEG_SRC)
$(PNG_SRC) libpnmrw.c
OBJS = rwTable.$O readWriteXBM.$O readWritePNM.$O
readWriteXWD.$O writePS.$O readGIF.$O writeGIF.$O
$(XPM_OBJ) $(TIFF_OBJ) $(SGI_OBJ) $(JPEG_OBJ)
$(PNG_OBJ) libpnmrw.$O
HDRS = libpnmrw.h rwTable.h
MISC = Imakefile
NormalLibraryTarget(rw, $(OBJS))
makelist: sh -c 'for i in '"$(TEXT) $(SRCS) $(MISC) $(HDRS)"' ; do echo $(CURRENT_DIR)/$$i >> ../filelist ; done '
#include "./Local.config"
DEFINES = $(ARCH_DEFINES) $(EXTRA_DEFINES)
$(XPM_INCLUDE) -DXPAINT_VERSION="$(VERSION)"
XPSRC = [...]
SRCS = $(XPSRC) $(OPSRC) $(XPWIDSRC)
OBJS = $(XPOBJ) $(OPOBJ) $(XPWIDOBJ)
HDRS = [...]
SYS_LIBRARIES = XawClientLibs -lm
#ifdef SGIArchitecture
SGI_LIB = -limage
#endif
DEPLIBS = always xart.man xart.ad
LOCAL_LIBRARIES = LibraryTargetName(rw/librw.a) $(XPM_LIB) $(TIFF_LIB)
$(SGI_LIB) $(JPEG_LIB) $(PNG_LIB)
SUBDIRS = rw
ComplexProgramTarget(xart)
InstallAppDefaults(XArt)
NamedMakeSubdirs(always, $(SUBDIRS))
MakefileSubdirs($(SUBDIRS) bitmaps Doc)
CleanSubdirs($(SUBDIRS))
# Other targets...
clean::
rm -f xart.ad.h DefaultRC.txt.h Help.txt.h PGP.*
xart.man xart.ad
includes:: xart.ad.h DefaultRC.txt.h Help.txt.h
xart.man: xart.man.in version
sed -e 'sáXPAINT_VERSIONá$(VERSION)á' < xart.man.in > $@
Colormap.$O: ColormapP.h Colormap.h
[...]
makelist::
-$(RM) filelist
@touch filelist
makelist::
sh -c 'for i in '"$(TEXT) $(SRCS) $(MISC) $(HDRS)"' ;
do echo $(CURRENT_DIR)/$$i >> $(TOP)/filelist ; done '
NamedTargetSubdirs(makelist, $(SUBDIRS) bitmaps Doc, , , makelist)
kit: makelist
sh -c 'sum="`cat filelist`" ; makekit -oMANIFEST MANIFEST
$$sum'
tar: makelist
sh -c 'cd .. ; rm -f xpaint-$(VERSION).tar.gz;
tar czf xpaint-$(VERSION).tar.gz
`sed -e "s:^./::" -e "s:^:xpaint/:" xpaint/filelist`'
TAGS: $(SRCS) $(HDRS) $(RWSRC)
etags -t -o ./TAGS $(XPSRC) $(OPSRC) $(XPWIDSRC) $(RWSRC)
$(HDRS)
certification: makelist
-$(RM) $(CERTIFICATION)
certify `cat filelist`
You may want to add parts of it to your Imakefile (or Local.config, etc.) in case of problems. Use comments, else you'll forget about these hackish def's!! Better: Include the necessary defines in config.h. You find them below being commented out (XCOMM XCOMM).
Beware: Redefines of functions are debugging headaches: The actual code won't be explicitly in the sources; you will forget, that you redef'd! Awful coding style!! Read the Portability chapter.
Giant command lines may require a good shell with a large input buffer. You can avoid this by adding necessary redefinitions to 'config.h' and not to the command line (-Dfoo).
#ifdef OS2Architecture
XCOMM XCOMM remove 'XCOMM XCOMM' comments, if you receive linkage errors
XCOMM XCOMM -DNEED_STRCASECMP
#include <string.h>
#define StrcasecmpDefines -Dstrcasecmp=stricmp
-Dstrncasecmp=strnicmp -Dmemcasecmp=memicmp
ARCH_DEFINES = -DSTDC_HEADERS=1 -DHAVE_STRING_H=1 -DHAVE_STDLIB_H=1
-DHAVE_PARAM_H=1 -DHAVE_MEMORY_H=1 -DHAVE_ALLOCA_H=1 -DHAVE_ALLOCA=1
-DHAVE_STRERROR=1 -Dlstat=stat -DS_ISLNK=false -DS_ISBLK=false
-Dsymlink()=(0) -Dreadlink(s,t,l)=(strcpy(t,s),strlen(t))
XCOMM XCOMM Try to include it in config.h
XCOMM XCOMM #define S_ISLNK() false
XCOMM XCOMM #define S_ISBLK() false
XCOMM XCOMM #define symlink() (0)
XCOMM XCOMM #define readlink(s,t,l) (strcpy(t,s),strlen(t))
XCOMM XCOMM /* Compare S1 and S2, ignoring case. */
XCOMM XCOMM int memicmp(__const__ char *, __const__ char *, size_t);
XCOMM XCOMM #ifndef memcasecmp
XCOMM XCOMM #define memcasecmp memicmp // emx uses M$ fn name
XCOMM XCOMM #endif
XCOMM XCOMM /* Compare S1 and S2, ignoring case. */
XCOMM XCOMM int stricmp(__const__ char *, __const__ char *);
XCOMM XCOMM #ifndef strcasecmp
XCOMM XCOMM #define strcasecmp stricmp
XCOMM XCOMM #define _strcasecmp stricmp
XCOMM XCOMM #endif
XCOMM XCOMM #if !defined(__STRICT_ANSI__) && !defined(_POSIX_SOURCE)
XCOMM XCOMM /* Compare no more than N chars of S1 and S2, ignoring case. */
XCOMM XCOMM int strnicmp(__const__ char *, __const__ char *, size_t);
XCOMM XCOMM #ifndef strncasecmp
XCOMM XCOMM #define strncasecmp strnicmp
XCOMM XCOMM #define _strncasecmp strnicmp
XCOMM XCOMM #endif
XCOMM XCOMM #endif /* !defined(__STRICT_ANSI__) */
XCOMM XCOMM /* drive letter support needs major rewriting:
XCOMM XCOMM * #define getcwd _getcwd2
XCOMM XCOMM * #define chdir _chdir2
XCOMM XCOMM */
XCOMM XCOMM /* Adapt hardcoded path names to XFree86/OS2 structure */
XCOMM XCOMM #if defined (__cplusplus)
XCOMM XCOMM extern "C" {
XCOMM XCOMM #endif
XCOMM XCOMM const char* __XOS2RedirRoot(const char* pathname);
XCOMM XCOMM #if defined (__cplusplus)
XCOMM XCOMM }
XCOMM XCOMM #endif
XCOMM XCOMM #define "/usr/X11/foo" (__XOS2RedirRoot("/xfree86/lib/x11/foo"))
#endif
Most X applications are single-threaded, i.e. multi-threading effects are 'emulated' using the Unix fork() syscall.
The Port of XFree86 to OS/2 is not thread-safe, i.e. you can only make X11 calls from a single-thread. emx select() (including all X i/o that depends on select()) are not (!) thread-safe. This means that using multi-threading within X applications is for experts only. Calls to a second thread that interacts with X will crash your application. (unless you use some inter-thread communication to serialize the calls).
Compile and link with
Notes:Existing servers and clients which use Posix threads (PTHREADS) have not yet been ported to OS/2. There is ongoing work to achieve PTHREADS compatibility for OS/2-emx, though. XFree86/2 libraries (DLL's) and applications are prepared for later extensive use of multi-threading.
Cf. emx docs:
"Use _beginthread() to start a new thread. Do not use DosCreateThread unless the new thread doesn't call C library functions.
The C library functions in mt/c.a, mt/c.lib, and emxlibcm.dll are not yet completely thread-safe. [...]
If you are very careful, you can write multithread programs that use the single-thread libraries. Only the main thread is allowed to call library functions that have side effects, including functions that set errno. All other threads should use OS/2 API functions instead. " [Parallel precautions apply to multithreaded X applications.]
For the following, please cf. the macros in <limits.h>:
The smallest integral type in C is named char, with the variants signed char and unsigned char. char differs from the other integral types in that it is not defined whether signed or unsigned. The next integral types are short int (often just referred to as short), int (including enum !) and long int (often just long.) All of them have unsigned versions.
To be on the safe side you must specify which you mean. However, conversions to other types require some precaution (see below). The maximum value for a char is CHAR_MAX, and the minimum CHAR_MIN, for signed char SCHAR_MAX and SCHAR_MIN and the maximum unsigned char is UCHAR_MAX (the minimum unsigned anything is always 0.)
There are three floating point types in C, float, double and long double. The floating point types are always signed. If you have operations with mixed types, the type with the largest value space decides the precision the operation will be done with. For char they're done at int precision. For float the arithmetics are done at double precision.
int main(void) {
unsigned char uc=255;
signed char sc = 255;
char c = 255;
int iuc = uc, isc = sc, ic = c;
printf(
"unsigned => %d, signed => %d, unknown => %dn",
iuc, isc, ic);
return 0;
}
Emx provides programming interfaces which comply to the recent standards of the ANSI mathematical amendments and IEEE, including infinity and NaN (not-a-number) values.
The value of infinity can be computed from 1.0/0.0. The value of a NaN can be computed from 0.0/0.0. Depending on its bit pattern, a NaN can be either quiet (NaNQ) or signaling (NaNS), as defined in the ANSI/IEEE Standard for Binary Floating-Point Arithmetic (754-1982). A NaNQ is masked and never generates exceptions. A NaNS may be masked and may generate an exception, but does not necessarily do so. Emx currently supports only quiet NaN values; all NaN values discussed below refer to quiet NaNs.
NaN and infinity values are defined as macro constants in the <math.h> header file. You can get the corresponding float and long double values by a cast, you may obtain negative values by using the unary minus operator. The value of 0.0 can also be positive or negative, e.g. 1.0/(-0.0) results in -INFINITY.
The IEEE standard for floating-point recommends a set of functions to be made available. Among these are functions to classify a value as NaN, Infinity, Zero, Denormalized, Normalized, and so on. Most implementations provide this functionality, although there are no standard names for the functions. Such implementations often provide predefined identifiers (such as _NAN, _INFINITY, etc) to allow you to generate these values.
If x is a floating point variable, then (x != x) will be TRUE if and only if x has the value NaN. Many Unix C implementations claim to be IEEE 748 conformant, but if you try the (x!=x) test above with x being a NaN, you'll find that they aren't.
In the mean time, you can write your own `standard' functions and macros, and provide versions of them for each system you use. If the system provides the functions you need, you #define your `standard' functions to be the system functions. Otherwise, you write your function as an interface to what the system provides, or write your own from scratch.
Some useful optimized extensions (functions and macros) are declared and defined in this header (simply include it instead of math.h):
Declarations and definitions for additional math functions.
*/
/*
Parts: Cephes Math Library Release 2.3: March, 1995
Copyright 1984, 1995 by Stephen L. Moshier
Documentation is included on the distribution media as
Unix-style manual pages that describe the functions and their
invocation. The primary documentation for the library functions
is the book by
Moshier, Methods and Programs for Mathematical Functions,
Prentice-Hall, 1989.
Parts: Copyright (C) 1991, 92, 93, 95, 96, 97, 98 Free Software
Foundation, Inc. This file is part of the GNU C Library.
See the GNU Library General Public License for more details.
*
* Extensions to ISO C Standard: 4.5 MATHEMATICS <math.h>
*/
#ifndef _GNUMATH_H__
#define _GNUMATH_H__
#if ( !defined (__STRICT_ANSI__) && !defined (_POSIX_SOURCE) &&
!defined (__NO_C9X) ) || defined (_WITH_UNDERSCORE)
#undef _WITH_UNDERSCORE
#define _WITH_UNDERSCORE 1
/* Machine-dependent inline versions */
#if (!__GNUC__ || __GNUC__ < 2 ||
(__GNUC__ == 2 && __GNUC_MINOR__ <= 7))
/* The gcc, version 2.7 or below, has problems with all this inlining
code. So disable it for this version of the compiler. */
# undef __NO_MATH_INLINES
# define __NO_MATH_INLINES
#else
# define MATH_INL static __inline__
MATH_INL double expm1(double);
MATH_INL double log1p(double);
MATH_INL long double log1pl(long double);
MATH_INL double asinh(double);
MATH_INL long double asinhl(long double);
MATH_INL double acosh(double);
MATH_INL long double acoshl(long double);
MATH_INL double atanh(double);
MATH_INL long double atanhl(long double);
MATH_INL double coshm1(double);
MATH_INL double acosh1p(double);
MATH_INL double logb(double);
MATH_INL double drem(double, double);
MATH_INL void sincos(double, double *, double *);
MATH_INL double pow2(double);
MATH_INL double hypot(double x, double y);
#endif /* __NO_MATH_INLINES */
#endif /* ANSI */
/* Get machine-dependent HUGE_VAL value (returned on overflow).
On all IEEE754 machines, this is +Infinity.
SVID wants HUGE instead of infinity.
Get machine-dependent NAN value (returned for some domain errors). */
#include <math.h>
/* X/Open wants another strange constant. */
#include <ieee.h> /* MAXFLOAT */
#include <float.h> /* Some useful constants. */
#include <limits.h>
#if !defined (__STRICT_ANSI__) && !defined (_POSIX_SOURCE) &&
!defined (__NO_C9X)
/* The above constants are not adequate for computation using `long double's.
Therefore we provide as an extension constants with similar names as a
GNU extension. (emx gcc version)
*/
#define M_El 2.71828182845904523536L /* e */
#define M_LOG2El 1.44269504088896340736L /* log 2e */
#define M_LOG10El 0.434294481903251827651L /* log 10e */
#define M_LN2l 0.693147180559945309422L /* log e2 */
#define M_LN10l 2.302585092994045684018L /* log e10 */
#define M_PIl 3.14159265358979323846L
#define M_PI_2l 1.57079632679489661923L /* pi/2 */
#define M_PI_4l 0.785398163397448309616L /* pi/4 */
#define M_1_PIl 0.318309886183790671538L /* 1/pi */
#define M_2_PIl 0.636619772367581343076L /* 2/pi */
#define M_2_SQRTPIl 1.12837916709551257390L /* 2/sqrt(pi) */
#define M_SQRT2l 1.41421356237309504880L /* sqrt(2) */
#define M_SQRT1_2l 0.707106781186547524401L /* 1/sqrt(2) */
/* Some Useful Constants From Cephes Math Library */
#define MAXNUML LDBL_MAX
#define MACHEPL LDBL_EPSILON
#define MAXLOGL 1.1356523406294143949700E+04L /* logl(MAXNUML) */
#define MINLOGL (-1.1355137111933024058900E+04L) /* logl(LDBL_MIN) */
#define LOGE2 6.9314718055994530941723E-1
#define LOGE2L 6.9314718055994530941723E-1L
#define LOG2EL 1.4426950408889634073599E0L
#define PIL 3.1415926535897932384626L
#define PIO2L 1.5707963267948966192313L
#define PIO4L 7.8539816339744830961566E-1L
#define NEGZEROL (-0.0L)
#define NANL (long double)NAN
#define INFINITYL (long double)INFINITY
#define NANF (float)NAN
#define INFINITYF (float)INFINITY
#include <errno.h>
#if !defined __NO_MATH_INLINES || defined _MATH_INLINES
#define _sgn(x) (x == 0.0 ? 0.0 : (x > 0.0 ? 1.0 : -1.0 ))
#define _sgnl(x) (x == 0.0L ? 0.0L : (x > 0.0L ? 1.0L : -1.0L))
#define _sgn1(x) (x >= 0.0 ? 1.0 : -1.0 )
#define _sgn1l(x) (x >= 0.0L ? 1.0L : -1.0L)
/* BSD Functions */
MATH_INL double
drem(double x, double y) {
register double value;
__asm__ __volatile__("1: fprem1nt" "fstsw %%axnt" "sahfnt" "jp 1b"
: "=t" (value) : "0" (x), "u" (y) : "ax", "cc");
return value;
}
MATH_INL double
expm1(double x) {
register double value, exponent, temp;
__asm__ __volatile__
("fldl2e # e^x - 1 = 2^(x * log2(e)) - 1nt"
"fmul %%st(1) # x * log2(e)nt"
"fstl %%st(1)nt"
"frndint # int(x * log2(e))nt"
"fxchnt"
"fsub %%st(1) # fract(x * log2(e))nt"
"f2xm1 # 2^(fract(x * log2(e))) - 1nt"
"fscale # 2^(x * log2(e)) - 2^(int(x * log2(e)))nt"
: "=t" (value), "=u" (exponent) : "0" (x));
__asm__ __volatile__
("fscale # 2^int(x * log2(e))nt"
: "=t" (temp) : "0" (1.0), "u" (exponent));
temp -= 1.0;
return temp + value;
}
/* Optimized versions for some non-standardized functions. */
MATH_INL double
hypot(double x, double y) {
return sqrt(x * x + y * y);
}
MATH_INL double
log1p(double x) {
register double value;
if (fabs (x) >= 1.0 - 0.5 * M_SQRT2)
value = log(1.0 + x);
else
__asm__ __volatile__
("fldln2nt"
"fxchnt"
"fyl2xp1"
: "=t" (value) : "0" (x));
return value;
}
MATH_INL long double
log1pl(long double x){
register long double value;
if (fabsl(x) >= 1.0L - 0.5L * M_SQRT2l)
value = logl(1.0 + x);
else
__asm__ __volatile__
("fldln2nt"
"fxchnt"
"fyl2xp1"
: "=t" (value) : "0" (x));
return value;
}
MATH_INL double
asinh(double x) {
register __const__ double y = fabs(x);
if (__isnan(x)) return x;
if (x == 0.0) return x;
if (!(__isfinite(x))) return x;
if (y > 1.0e10) return log(x) + LOGE2;
#ifdef GNUFNS /* This is the GNU original and it is wrong!!! */
/* return log1p ((y * y / (sqrt (y * y + 1.0) + 1.0) + y)
* _sgn1l(x));
*/
return log1p(y +(y * y/(sqrt(y * y + 1.0)+ 1.0)))* _sgn1(x);
#else
return log(x + sqrt(x * x + 1.0) );
#endif
}
MATH_INL double
acosh(double x) {
if (__isnan(x)) return x;
if (x < 1.0) {errno = EDOM; return NAN;}
if (x > 1.0e10) {
if (x == INFINITY) return INFINITY;
return(log(x) + LOGE2); }
return log(x + sqrt(x - 1.0) * sqrt(x + 1.0));
}
MATH_INL double
atanh(double x) {
register __const__ double y = fabs(x);
if (x == 0.0) return x;
if (y >= 1.0) {
if (x == 1.0) return INFINITY;
if (x == -1.0) return -INFINITY;
errno = EDOM; return NAN; }
if (y < 1.0e-8) return x;
#ifdef GNUFNS
return -0.5 * log1p(-(y + y)/(1.0 + y)) * _sgn1(x);
#else
return 0.5 * log ( (1.0 + x)/(1.0 - x) );
#endif
}
MATH_INL long double
asinhl(long double x) {
register __const__ long double y = fabsl(x);
if (__isnanl(x)) return x;
if (x == 0.0L) return x;
if (!(__isfinitel(x))) return x;
if (y > 1.0e10L) return logl(x) + LOGE2L;
#ifdef GNUFNS
return log1pl( (y * y /(sqrtl(y * y + 1.0L) + 1.0L) + y) )
* _sgn1l(x); /* This is corrected */
#else
return logl( x + sqrtl(x * x + 1.0L));
#endif
}
MATH_INL long double
acoshl(long double x) {
if (__isnanl(x)) return x;
if (x < 1.0L) {errno = EDOM; return NANL;}
if (x > 1.0e10L) {
if (x == INFINITYL) return INFINITYL;
return logl(x) + LOGE2L; }
return logl(x + sqrtl(x - 1.0L) * sqrtl(x + 1.0L));
}
MATH_INL long double
atanhl(long double x) {
register __const__ long double y = fabsl(x);
if (x == 0.0L) return x;
if (y >= 1.0L) {
if (x == 1.0L) return INFINITYL;
if (x == -1.0L) return -INFINITYL;
errno = EDOM; return NANL; }
if (y < 1.0e-8L) return x;
#ifdef GNUFNS
return -0.5L * log1pl(-(y + y)/(1.0L + y)) * _sgn1(x);
#else
return 0.5L * logl( (1.0L + x)/(1.0L - x) );
#endif
}
MATH_INL double
coshm1(double x) {
register __const__ double __exm1 = expm1 (fabs (x));
return 0.5 * (__exm1 / (__exm1 + 1.0)) * __exm1;
}
MATH_INL double
acosh1p(double x) {
return log1p(x + sqrt (x) * sqrt (x + 2.0));
}
MATH_INL double
logb(double x) {
register double value, __junk;
__asm__ __volatile__
("fxtractnt"
: "=t" (__junk), "=u" (value) : "0" (x));
return value;
}
MATH_INL void
sincos(double x, double *__sinx, double *__cosx) {
register double __cosr, __sinr;
__asm__ __volatile__ ("fsincos"
: "=t" (__cosr), "=u" (__sinr) : "0" (x));
*__sinx = __sinr;
*__cosx = __cosr;
}
MATH_INL double
pow2(double x) {
register double value, exponent;
int __p = (int) x;
if (x ==(double) __p)
return ldexp (1.0, __p);
__asm__ __volatile__
("fldl %%st(0)nt"
"frndint # int(x)nt"
"fxchnt"
"fsub %%st(1) # fract(x)nt"
"f2xm1 # 2^(fract(x)) - 1nt"
: "=t" (value), "=u" (exponent) : "0" (x));
value += 1.0;
__asm__ __volatile__
("fscale"
: "=t" (value) : "0" (value), "u" (exponent));
return value;
}
#endif /* _POSIX_SOURCE */
#endif /* __NO_MATH_INLINES */
#endif /* GNUMATH_H__ */
Last but not least some well meant recommendations from Chapter 5 of the "emx APPLICATION DEVELOPER'S GUIDE", with some additional comments. You might use them as a short checklist.
" If you want Unix-like wildcard expansion built into the program, use
int main (int argc, char *argv[]) { _wildcard (&argc, &argv); /* ... the program ... */ }
This should be done at the very beginning of main(), before ARGC and ARGV are used. See _wildcard() and _response()."
"Famous problems are related to the binary/text mode mess on OS/2, especially when using stdin/stdout." (Alexander Mai)
" Programs using stdin, stdout or stderr for binary data should call _fsetmode() to switch the stream to binary mode."
" Change all open(), fopen(), fdopen() and freopen() calls to use O_BINARY or "b", respectively, for binary files. If a file contains both binary and textual data, read the file in binary mode and do the conversion yourself."
Note: XFree86/OS2 uses '-Zbin-files'. So "do the conversion yourself" "and ignore carriage returns" and <CTRL Z>.
" Though fseek() and ftell() now work on text files, the offsets are different from what Unix programs expect. You may have to open the files in binary mode and ignore carriage returns (this has been done in GDB)."
" Programs reading a.out files should be changed to call _seek_hdr() or _fseek_hdr() before reading the header to support .exe files. More changes are usually required."
"Replace fork() and exec*() with spawn*(). Under OS/2, fork() is inefficient."
(emx docs)
The Posix syscall fork() is for traditional Unix systems the only possiblity to create a new process, i.e. a nearly identical clone of an existing one:
"This is achieved by copying the pointers in the PROCESS INFORMATION BLOCK (PIB) of the parent to the newly allocated block in the kernel. On the other hand, on DOS (Windows included) and on OS/2 (Windows NT included) there is no "fork" system call but there is a "spawn" instead. This system call just allocates a new process information block. Only file handles and the environment variable space are inherited and selectors to the address space of the parent process are not copied. So the child cannot access the contents of the memory of the parent. In fact, for OS/2 and Windows NT, it is impossible to implement "fork" in the same way as on the UNIX, since even root has only read access to the PIB table in the kernel. [In fact, there is no such security leak as 'root' in OS/2 (AH)]The emx library function fork() is actually spawn() system call with a little bit of IPC to copy memory contents. It is inefficient because all the memory contents rather than just pointers are copied.
Well, the difference I was talking was that a spawned child has a clean address space, while a forked one might have a corrupted one." (SMiyata).
"fork() works only in programs linked by ld. It does not work in programs linked by LINK386." [-Zomf]See the emx reference for more details about caveats. Use huge stack and heap size."fork() doesn't work correctly in multithread programs."
"If the parent process uses the general terminal interface for the keyboard, the child process cannot read from the keyboard using _read_kbd(), the general terminal interface, or the KBD OS/2 API functions.
If the process has a non-contiguous heap (that is, multiple heap objects), fork() will fail. Increase the initial heap size to work around this problem. If any DLL used by the program shares the heap with the program and uses the heap in _DLL_InitTerm() by calling sbrk(), malloc(), etc., fork() will fail. In both cases, fork() will return -1 and set errno to ENOMEM.
See also: alarm(), exec*(), _rmutex_create(), sbrk(), sigaction(), signal(), spawn*(), _uflags(), wait(), waitpid()"
In Posix the parent process clones itself with fork(), which generates a new process environment, whereas exec() overwrites its own present environment with a new one, keeping the PID. Usually this is combined: exec() is used in the forked child process, overwriting the parent's process status with a new one. Meanwhile the parent usually calls wait() or a home-brew function and waits for child termination, communicated by SIGCHLD.
ps (pstat.exe) gives you the details.
A common problem here is, that the child after termination does not find this wait() etc. point of the parent. Thus a 'zombie' remains. This may lead to a process table overflow, so that the system has to be shutdown and rebooted. All this hassle can be simplified by using spawn*().
Excerpt from exec*() documentation:
" When the new process ends, the parent process will be notified by SIGCHLD and wait() will return the original process ID (PID). [...]But for tracing and debugging:Restrictions:
Native DOS programs cannot be run. The new process gets a new process ID. Unless the new process is an emx program (using emx.dll or emx.exe), the actions for all signals are reset to SIG_DFL in the new process"
"Replace exec*() with spawn*() and exit() if the parent process waits for the termination of the new process (by calling wait() or by waiting for SIGCLD). This is required to keep the process ID of the child process. In a forked process, however, you don't have to do this because emx.dll does it for you."
"Do not use the PTRACE_TRACEME request of ptrace(): use P_DEBUG instead when starting the process with spawn*()."
It's no simple replacement when porting UNIX fork() and exec*() pairs to OS/2's spawn*(). The following two programs can be used as an example:The first using fork() and execv() should be linked with ld but I have no *.a libraries at hand so cannot test it. But I think it is correct.
The second is linked with LINK386 and works fine. Both programs create a child process and then write a message through a pipe to the standard input of the child process. You can test it with cat.exe, which displays what has been read from stdin. This should give you a hint how to port fork() and exec*(). Look at the source of BASH 2.0 for OS/2 for another way to tackle this problem.
#include <unistd.h>
#include <io.h>
static const char message[] = "Hello, kid!";
int
main(int argc, char**argv)
{ /* use the name of the program as argument */
int two_fd[2], two_other_fd[2], pid;
if (pipe(two_fd)) /* the reading end is [0] */
exit(1); /* error creating pipe ;-( */
pid = fork(); /* now I am two! */
if (pid) { /* we are the parent! */
if (pid == -1)
exit(2); /* error fork()ing! ;-( */
else {
close(two_fd[0]); /* close unneeded reading end */
write(two_fd[1], message, sizeof(message));
close(two_fd[1]);
}
} else { /* we are the forked child */
dup2(two_fd[0], STDIN_FILENO); /* connect reading
end to the new process' stdin, 'close' stdin */
/* dup2(two_other_fd[0], STDOUT_FILENO); */
close(two_fd[0]); /* close the old file descriptor */
close(two_fd[1]); /* close unneeded one */
/* close(two_other_fd[0]); close(two_other_fd[1]); */
execv("cat", NULL);
exit(3); /* if exec*() returns: Error ;-( */
}
return 0;
}
-------
/* pipe2.c, using spawn*() */
#include <process.h>
#include <fcntl.h>
#include <io.h>
static const char message[] = "Hello, kid!";
/* supply the name of the program you want to run as argument */
int
main(int argc, char**argv)
{
int fd[2], ftmp;
if (pipe(fd))
exit(1); /* error creating pipe */
ftmp = dup(0); /* save the stdin handle */
dup2(fd[0],0); /* assign the read end of the pipe to stdin */ close(fd[0]);
fcntl(ftmp, F_SETFD, FD_CLOEXEC);
fcntl(fd[1], F_SETFD, FD_CLOEXEC); spawnv(P_NOWAIT,argv[1],argv+1);
/* restore the original stdin file, necessary if
more input from stdin is needed */
dup2(ftmp,0);
close(ftmp);
write(fd[1], message, sizeof(message));
close(fd[1]);
return(0);
}
-------
/*
* Routine: popenRW
* Returns: PID of child process
* Action : Exec program connected via pipe, connect int fd's to
* both the stdin and stdout of the process.
* Params : Command - Program to run
* Pipes - Array of 2 ints to store the pipe descriptors
* Pipe[0] writes to child's stdin,
* Pipe[1] reads from child's stdout/stderr
*/
#include <process.h>
#include <fcntl.h>
#include <io.h>
#define INCLUDE_DOS
#include <os2.h>
int
popenRW(char *const argv[], int *pipes)
{
HFILE Out1, Out2, In1, In2;
HFILE Old0 = -1, Old1 = -1, Old2 = -1, Tmp;
int pid;
if (DosCreatePipe(&Out2, &Out1, 4096))
return FALSE;
if (DosCreatePipe(&In1, &In2, 4096))
{
DosClose (Out1);
DosClose (Out2);
return FALSE;
}
/* Save std{in,out,err} */
DosDupHandle (STDIN, &Old0);
DosSetFHState (Old1, OPEN_FLAGS_NOINHERIT);
DosDupHandle (STDOUT, &Old1);
DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
DosDupHandle (STDERR, &Old2);
DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
/* Redirect std{in,out,err} */
Tmp = STDIN;
DosDupHandle (In1, &Tmp);
Tmp = STDOUT;
DosDupHandle (Out1, &Tmp);
Tmp = STDERR;
DosDupHandle (Out1, &Tmp);
/* Close file handles not needed in child */
DosClose (In1);
DosClose (Out1);
DosSetFHState (In2, OPEN_FLAGS_NOINHERIT);
DosSetFHState (Out2, OPEN_FLAGS_NOINHERIT);
/* Spawn we now our hoary brood. */
pid = spawnvp (P_NOWAIT, argv[0], argv);
/* Restore std{in,out,err} */
Tmp = STDIN;
DosDupHandle (Old0, &Tmp);
DosClose (Old0);
Tmp = STDOUT;
DosDupHandle (Old1, &Tmp);
DosClose (Old1);
Tmp = STDERR;
DosDupHandle (Old2, &Tmp);
DosClose (Old2);
if(pid < 0)
{
DosClose (In2);
DosClose (Out2);
return -1;
}
pipes[0] = In2;
_setmode (pipes[0], O_BINARY);
pipes[1] = Out2;
_setmode (pipes[1], O_BINARY);
/* Save ID of write-to-child pipe for pclose() */
ll_insert ((LL_KEY) In2, (LL_VAL) pid);
return pid;
}
/*
* Routine: pclose
* Returns: TRUE on success
* Action : Close a pipe opened with popen();
* Params : Pipe - pipe to close
*/
int
pclose (FILE *Pipe)
{
RESULTCODES rc;
PID pid, pid1;
int Handle = fileno (Pipe);
fclose (Pipe);
rc.codeTerminate = -1;
pid1 = (PID) ll_lookup ((LL_KEY) Handle);
/* if pid1 is zero, something's seriously wrong */
if (pid1 != 0)
{
DosWaitChild (DCWA_PROCESSTREE, DCWW_WAIT, &rc, &pid, pid1);
ll_delete ((LL_KEY) Handle);
}
return rc.codeTerminate == 0 ? rc.codeResult : -1;
}
#if DIAGNOSTIC
void
main ()
{
FILE *fp1, *fp2, *fp3;
int c;
ll_print ();
fp1 = popen ("gcc --version", "r");
ll_print ();
fp2 = popen ("link386 /?", "r");
ll_print ();
fp3 = popen ("dir", "r");
ll_print ();
while ((c = getc (fp1)) != EOF)
printf ("%c", c);
while ((c = getc (fp2)) != EOF)
printf ("%c", c);
while ((c = getc (fp3)) != EOF)
printf ("%c", c);
pclose (fp1);
ll_print ();
pclose (fp2);
ll_print ();
pclose (fp3);
ll_print ();
return;
}
#endif /* DIAGNOSTIC */
The following example demonstrates how the emx spawnvp() interface brings about a considerable simplification of the initial fork(), execvp(), waitForChild() implementation. The frequent usage of '#ifdef __EMX__' reduces readability, though.
#include <sys/types.h>
#include <LString.h>
#ifdef __GNUG__
#pragma interface
#endif
/// Class to controll system-calls according to OS-specific interface
/**@Doc:
Class, which controlls a system-call according to the specific interface of
your operating system.
Instance starts and represents child processes.
You should use this class if you need to start an external program in LyX.
You can start a child in the background and have a callback function
executed when the child finishes by using the DontWait starttype.
*/
class Systemcalls {
public:
///
enum Starttype {
System,
Wait,
DontWait
};
/// Callback function gets commandline and returnvalue from child
typedef void (*Callbackfct)(LString cmd, int retval);
///
Systemcalls();
/** Generate instance and start childprocess
The string "what" contains a commandline with arguments separated
by spaces.
When the requested program finishes, the callback-function is
called with the commandline and the returnvalue from the program.
The instance is automatically added to a timercheck if starttype is
DontWait (i.e. background execution). When a background child
finishes, the timercheck will automatically call the callback
function.
*/
Systemcalls(Starttype how, LString what, Callbackfct call = 0);
///
~Systemcalls();
/** Start childprocess. what contains a command on systemlevel.
*/
int Startscript(Starttype how, LString what, Callbackfct call = 0); // for reuse
/** gets PID of childprocess. Used by timer */
inline pid_t Getpid() { return pid; }
/// Start callback
inline void Callback() { if (cbk) cbk(command, retval); }
/** Set return value. Used by timer */
inline void setRetValue(int r) { retval = r; }
private:
/// Type of execution: system, wait for child or background
Starttype start;
/// Callback function
Callbackfct cbk;
/// Commmand line
LString command;
/// Process ID of child
pid_t pid;
/// Return value from child
int retval;
///
int Startscript();
/// Spawn child according to the interface of your OS (AHanses)
pid_t Fork(int spawnFlag);
/// Wait for child process to finish. Updates returncode from child.
#ifndef __EMX__
/**
Waitforchild not necessary with spawnvp() of emx.
Note (concerning OS/2 specifics): Emx interface implementation of
spawnvp(P_WAIT, ...) returns returncode from child (AHanses).
*/
void waitForChild();
#endif
};
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <process.h>
#include "syscall.h"
#include "syscontr.h"
//--------------------------------------------------------
// Class, which controlls a system-call
//--------------------------------------------------------
// constructor
Systemcalls::Systemcalls()
{
pid = (pid_t) 0; // yet no child
}
// constructor
//
// starts child
Systemcalls::Systemcalls(Starttype how, LString what, Callbackfct cback)
{
start = how;
command = what;
cbk = cback;
pid = (pid_t) 0; // no child yet
retval = 0;
Startscript();
}
// destructor
// not yet implemented (?)
Systemcalls::~Systemcalls()
{
}
// Start a childprocess
//
// if child runs in background, add information to global controller.
int Systemcalls::Startscript()
{
retval = 0;
switch (start) {
case System:
retval = system(command.c_str());
Callback();
break;
case Wait:
pid = Fork(P_WAIT);
if (pid>0) { // Fork succesful. Wait for child
#ifndef __EMX__
waitForChild();
#endif
Callback();
} else
retval = 1;
break;
case DontWait:
pid=Fork(P_NOWAIT);
if (pid>0) {
// Now integrate into Controller
SystemcallsSingletoncontroller::Startcontroller starter;
SystemcallsSingletoncontroller *contr=
starter.GetController();
// Add this to contr
contr->AddCall(*this);
} else
retval = 1;
break;
//default: // error();
//break;
}
return retval;
}
// Wait for child process to finish. Returns returncode from child.
#ifndef __EMX__
void Systemcalls::waitForChild()
{
// We'll pretend that the child returns -1 on all errorconditions.
retval = 1;
int status;
bool wait = true;
while (wait) {
pid_t waitrpid = waitpid(pid, &status, WUNTRACED);
if (waitrpid == -1) {
perror("LyX: Error waiting for child");
wait = false;
} else if (WIFEXITED(status)) {
// Child exited normally. Update return value.
retval = WEXITSTATUS(status);
wait = false;
} else if (WIFSIGNALED(status)) {
fprintf(stderr,"LyX: Child didn't catch signal %d "
"and died. Too bad.n", WTERMSIG(status));
wait = false;
} else if (WIFSTOPPED(status)) {
fprintf(stderr,"LyX: Child (pid: %ld) stopped on "
"signal %d. Waiting for child to finish.n",
(long) pid, WSTOPSIG(status));
} else {
fprintf(stderr,"LyX: Something rotten happened while "
"waiting for child %ldn", (long) pid);
wait = false;
}
}
}
#endif
// generate child in background
pid_t Systemcalls::Fork(int spawnFlag)
{
#ifndef __EMX__
pid_t cpid=fork();
if (cpid == 0) { // child
#endif
LString childcommand(command); // copy
LString rest = command.split(childcommand, ' ');
const int MAX_ARGV = 255;
char *syscmd = NULL;
char *argv[MAX_ARGV];
int index = 0;
bool Abbruch;
do {
if (syscmd == NULL) {
syscmd = childcommand.copy();
}
argv[index++] = childcommand.copy();
// reinit
Abbruch = !rest.empty();
if (Abbruch)
rest = rest.split(childcommand, ' ');
} while (Abbruch);
argv[index] = NULL;
// replace by command. Expand using PATH-environment-var.
#ifndef __EMX__
execvp(syscmd, argv);
// If something goes wrong, we end up here:
perror("LyX: execvp failed");
} else {
#else // spawn child in another process; fork() is not portable
pid_t cpid = spawnvp(spawnFlag, syscmd, argv);
#endif
if (cpid < 0) // error
perror("LyX: Could not fork");
#ifndef __EMX__
} else // parent
#endif
return cpid;
return 0;
}
// Reuse of instance
int Systemcalls::Startscript(Starttype how, LString what, Callbackfct cback)
{
start = how;
command = what;
cbk = cback;
pid = (pid_t) 0; // yet no child
retval = 0;
return Startscript();
}
//
// Mini-Test-environment for script-classes
//
#ifdef TEST_MAIN
#include <stdio.h>
int SimulateTimer;
void back(LString cmd, int retval)
{
printf("Done: %s gave %dn", cmd.c_str(), retval);
SimulateTimer = 0;
}
int main(int, char**)
{
SystemcallsSingletoncontroller::Startcontroller starter;
SystemcallsSingletoncontroller *contr=starter.GetController();
Systemcalls one(Systemcalls::System, "ls -ltag", back);
Systemcalls two(Systemcalls::Wait, "ls -ltag", back);
SimulateTimer = 1;
Systemcalls three(Systemcalls::DontWait , "ls -ltag", back);
// Simulation of timer
while (SimulateTimer)
{
sleep(1);
contr->Timer();
}
}
#endif
[to be written]
The separator of path components is a slash '/' as opposed to OS/2's ''. Note that many APIs understand the '/' on OS/2, but cmd.exe and most apps don't. Multiple paths (e.g. in environment variables like PATH) are separated by a colon ':' The root dir on every un*x system is '/'. Often code checks whether a given path is an absolute one by comparing the beginning with '/'. Use grep for all standard paths and all commands which are mentioned in this introduction. (_fullpath() and _abspath() can also be useful.)
Using
#define chdir _chdir2
- Watch out for Unix file names (Unix is case sensitive, long file names and multiple dots are allowed). On OS/2's HPFS multiple dots are also allowed; however, trailing dots are not significant (except for the special file names `.' and `..').- The null device is called /dev/null under Unix. The __open() system call translates the filenames "/dev/null" and "/dev/tty" (lower case, with slashes) to "nul" and "con", respectively. However,
won't work as the standard OS/2 and DOS command interpreters don't recognize /dev/null.- If you want to use for separating directories, changes may be necessary. These changes are optional because / also works. "
"Unix does not have drive letters, so a file (resources, fonts, config, etc.) path starts with '/' normally. XFree86 is located in a tree below %X11ROOT%XFree86... To prepend the X11ROOT part to a file path, a special function named __XOS2RedirRoot exists which you are supposed to use in these cases rather than an insane own solution. The prototype of this function is:
[...] XFree86/OS2 stores all configuration files in a common directory. This is X11ROOT:XFree86libX11, where X11ROOT is an environment variable which contains the drive letter the directory tree is located. The common places like /usr/X11, /usr/X11R6, /usr/XFree86, /etc which are used for XFree86 in various Unix flavors, are not supported [...]. It is also there where you should put your XF86Config file, the color database (rgb.txt), the host authorization files (X0.hosts), and various other files." (FAQ)"Potential problem might be the DLL name: IIRC one can either use
to access foo.dll (if in LIBPATH)." (Alexander Mai)"Note that ///abc is a valid Unix filename. It's equivalent to /abc.
- Note that chdir ("..") is a no-op under Unix if the current working directory is the root directory. Under emx, chdir ("..") fails in the root directory.
- Use termio or termios or read the keyboard with _read_kbd() if you don't want to get input line by line.
- Under Unix, directories in environment variables (PATH, for instance) are separated by colons; use semicolons instead.
- The shell isn't called /bin/sh. Use system(). system() and popen() don't expand wildcards (unless COMSPEC points to a shell which expands wildcards)."
So, in short:
"grep for '/dev', '/bin', '/tmp', '/usr', '/proc', ... also check out all commands starting up another process:
exec*, popen, system,...
Remember that cmd.exe doesn't like slashes!" (Alexander Mai)
"emx includes support for sockets of IBM TCP/IP for OS/2 (including the Internet Access Kit of OS/2 3.0). The headers files are derived from NetBSD and are packaged in bsddev.zip. Some manual pages are packaged in bsddoc.zip."Use the -lsocket option to link with the socket library. Note that -lsocket, like all -l option, should be given after the source files:
``emx tries to provide seamless support for sockets, like Unix systems do. This approach is quite different from the one taken by IBM's TCP/IP for OS/2 Toolkit.Look at emx' netdb.h, where you will find an excerpt of the bsd paths.h mechanism, but hidden behind your back and without any further explanation what is going on:Sockets are not supported with the system call library sys.lib (-Zsys).
The header <sys/so_ioctl.h>, which includes TCP/IP-specific definitions for ioctl(), is included by <sys/ioctl.h> only if a TCP/IP-related header such as <sys/socket.h> has been included before <sys/ioctl.h>. If you don't want to reorder the #include directives, define the symbol _EMX_TCPIP before including <sys/ioctl.h>, or include <sys/so_ioctl.h> directly (however, the last alternative is not recommended).
When porting Unix applications, please note the following restrictions:
Socket handles are not inherited across exec*() and spawn*(). (Note that a process created by fork() inherits the socket handles of its parent process.) Inheriting socket handles is planned for a future release of emx.
The size of messages is restricted to 32767 bytes (this is a limitation of IBM TCP/IP for OS/2).
Initially, sockets are in binary mode. Use setmode() to switch to text mode. Text mode applies to read() and write(), only. recv() and send() always use binary mode.
The functions recvmsg(), sendmsg(), and socketpair() are not implemented."
#define _PATH_HOSTS "/tcpip/etc/hosts"
#define _PATH_NETWORKS "/tcpip/etc/networks"
#define _PATH_PROTOCOLS "/tcpip/etc/protocols"
#define _PATH_SERVICES "/tcpip/etc/services"
"The difficulty here is that one still needs to annotate each of these paths with a drive letter. XFree86 has to do this with almost any internal path by using a routine named __XOS2RedirRoot which prepends the %X11ROOT% variable. Here is the drawback of DOS like systems to behave like VMS ("everything is a device, and some of them, denoted by drive letters can contain file systems") in contrast to Unix philosophy ("everything is a file, and some of them may have special properties, like being a pipe, or a socket, or a device")." (Holger Veit <Holger.Veit@gmd.de>)
Unfortunately many OS/2 tools, including built-in cmd.exe commands have their counterparts on un*x with exactly the same name but different semantics/syntax. An incomplete list: echo, find, patch, sort.
" By default, signal processing is different when using signal(): SIG_ACK should be used instead of the signal handler address to re-enable a signal by calling signal() when the signal handler has been called. This behavior can be changed with the -Zbsd-signals and -Zsysv-signals options of GCC. If you use POSIX.1 functions for signal handling, SIG_ACK is not required.
- Printing single characters is inefficient. A solution is to use
setvbuf (stdout, NULL, _IOLBF, BUFSIZ)
and to use fflush (stdout) if you need the output immediately (flushing is required only after displaying prompting texts before reading input or displaying progress reports that don't end with a newline character). GDB output has been made much faster by using line buffering.
- Note that VEOF != VMIN and VEOL != VTIME. Programs which use VEOF and VEOL to access VMIN and VTIME, respectively, should be changed to use VMIN and VTIME. emx uses separate fields for VEOF, VEOL, VMIN and VTIME.
- To use termio, you have to reset the IDEFAULT bit of c_lflag. This does not apply to termios."
SMiyata in a contribution to the XFree86/OS2 mailing list pointed out:
Having as many softwares as possible really helps even if their qualities are sometimes questionable. On the other hand, for example, I am using LYX for a practical purpose so that if the reliability of it is lower than that on some other platform, say, LINUX, I will eventually be forced to switch the operating system I use daily. And (more or less) functional ports do not appear over one night. I myself cannot grasp the contents of the source code at a glance. I have to test semi-working versions and investigate the relevant parts of the source again and again to have something working. [...] I was really helped that most of the non-working features wererather evident. I could pin-point the relevent parts of the source easily.Alexander Mai wrote:The problem of automating dirty workaround stuffs is that unless the one who ports a software awares that s/he is doing something dirty, it is really likely for him/her to let the semi-working workarounds unnoticed.
There are at least two remedies for this problem.You can let the dummy stub functions to print warning messages [...], although this may alarm also the users of the port. Another possiblity is to make the warning very clear in the doc.
Or do it like other projects: Use an environment variable to switch on/off those warnings. Might even include multiple levels, one printing statements once and another which reminds everytime of some missing functionality.
Alexander Mai wrote:
I'm not sure at all what people think here, but I make some proposals ... :Here is a file_copy- like routine, that has been discussed on the emx mailing list. Diagnostics in case of errors should be written to a error.log file. In an application program this can be achieved by redirecting the output of stdout to a file (you may use freopen())in your application's data directory:a) use a file_copy routine for symlink (and don't clean up later perhaps)
b) let getdtablesize return some 'guessed' integer (w/o checking any docs, just get it working in most cases ...)
c) #define lstat stat (neglecting something one could imagine around TVFS)
From: "mithra" <mithra@bga.com>
To: emx@queue.iaehv.nl
Subject: Re: COPY/MOVE system function?
Since I needed a copy I could use everywhere without invoking
system(), here is cpfile.c:
****
* Copy file from 'in_name' to 'out_name'
*
* return:
* 0 if all's well
* 1 if can't open in_name
* 2 if can't open out_name for writing
* 3 if if stat() in_name returns an error
* 4 if copy failed (read error)
* 5 if copy failed (write error)
* 6 if out_name close() failed (file system error!)
* Check to prevent silent data loss, e.g. with network data.
* 7 if utime() failed (time set error)
* 8 if chmod() of output file failed
* 9 if in_name close() failed (file system error!)
*
* in future may want access status of output file...
* Curtis W. Rendon 12/20/1999 initial
* modified 04/01/00 A. Hanses
*
* hereby released to the public domain, please keep my comments,
* no warranty inferred, implied, or specified.
* Curtis W. Rendon mithra@earthling.net 12/29/1999
*
***** Prototype: */
int cpfile (const char *in_name, const char *out_name);
#include <stdio.h>
#ifdef VIOLATE_POSIX_USE_M$_EXTENSION
# include <io.h> /* PC extension, not (Posix) portable */
#else
# include <unistd.h> /* Emx has portable (Posix) headers */
#endif
#include <utime.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h> /* ANSI/ISO C forbids explicit declaration ! *
It is a thread local modifiable (int) lvalue (usually a macro) */
#ifndef lstat /* This is a file system dependency */
#define lstat(x,y) stat(x,y)
#endif
static FILE *out_p; /* This is the pointer to the new or trun- *
cated old output file. Input file attributes are also copied. */
static struct stat file_status_buf; /* File attributes buffers */
static struct utimbuf file_utime_buf;
static char buf[BUFSIZ]; /* r/w no more than BUFSIZ at once */
static size_t count; /* read/write at least count bytes at once */
static int status = 0; /* default return code */
int
cpfile(const char *in_name, const char *out_name)
{
FILE *const in_p = fopen(in_name, "rb"); /* point to in file */
if (status) /* reset and try your luck again, if a previous */
status = 0; /* call to cpfile() has returned an error */
if (in_p == NULL) /* error opening in file */
return (status = 1);
if ((access(out_name, W_OK)) == 0) /* overwriting out file */
fprintf(stderr, "Warning: Overwriting '%s'...n", out_name);
else if ((access(out_name, F_OK)) == 0) {
if (fclose (in_p) == EOF)
perror("cpfile.c: Error closing input file");
fprintf(stderr, "Error: Cannot write to '%s'n", out_name);
return (status = 2); /* error out file not writable */
} /* Note: Posix' link()/symlink() don't overwrite! To
* achieve an equivalent behaviour, you can test for
* W_OK and then return()
*/
if ((out_p = fopen(out_name, "wb")) == NULL)
status = 2; /* error opening out file */
else {
if (lstat(in_name, &file_status_buf) == -1) {
status = 3; perror("cpfile.c: Input file stat() error");
}
do { /* endless file i/o loop */
count = fread( (void*) buf, 1, sizeof(buf), in_p);
if ( (count < sizeof(buf)) && ferror(in_p) ) {
status = 4; perror("cpfile.c: Internal read error");
break;
}
if (fwrite( (void*) buf, 1, count, out_p) < count) {
status = 5;
perror("cpfile.c: Internal write error");
break;
}
} while ( ! feof(in_p) ); /* end file i/o loop */
if (fclose (out_p) == EOF)
return (status = 6); /* Now in danger of data loss ! */
file_utime_buf.actime = file_status_buf.st_atime;
file_utime_buf.modtime = file_status_buf.st_mtime;
if (utime(out_name, &file_utime_buf) == -1) {
status = 7; perror("cpfile.c: utime() error");
}
if (chmod(out_name, file_status_buf.st_mode) == -1) {
status = 8; perror("cpfile.c: chmod() error");
}
}
if (fclose (in_p) == EOF)
status = 9;
return (status);
}
#ifdef DEBUG
int main(const int argc, const char *const argv[])
{
if (argc != 3) {
puts("Usage: cpfile INFILE OUTFILE");
exit(-1);
}
errno = 0;
if (cpfile(argv[1], argv[2])) {
fprintf(stderr, "%s %s %s: Errorlevel: %dn",
argv[0], argv[1], argv[2], status);
if (errno) perror("cpfile.c");
}
return (status);
}
#endif
Macros can either be used to hide obscure hacks, or making the code more easily to understand. This depends on the situation. #ifdef clauses, namely nested ones are ill, ill, ill. Consider the following fragment in Xos.h:
#ifndef __TYPES__
#ifdef CRAY
#define word word_t
#endif /* CRAY */
#include <sys/types.h> /* forgot to protect it... */
#define __TYPES__
#endif /* __TYPES__ */
#else /* USG */
#if defined(_POSIX_SOURCE) && (defined(MOTOROLA) || defined(AMOEBA))
#undef _POSIX_SOURCE
#include <sys/types.h>
#define _POSIX_SOURCE
#else
#include <sys/types.h>
#endif
#endif /* USG */
The X11 headers have many more of these 'pearls', and the GNU glibc include headers are even worse. The only way to find out what really goes into the code is to run gcc -E on the file that includes this file.
> > b) a quick hack to satisfy compiler/linker
>
> using a function stub foo for a missing function:
>
> foo()
{
> fprintf (stderr, "sorry, foo() not implemented! I'm just too lazy!
> You may consider this as a bug; but I won't care at all ;-)" );
> return -1;
> };
>
> > and
> > c) a proper implementation
>
> Providing all this ifdef'd in the sources (no stubs) and doing it
> according to the books in a way that will always work and is easy to
> debug.
You can't prevent laziness in user code; I myself have routines marked with
#define TBD(x) fprintf(stderr, #x ": not yet implementedn")
The point is rather: missing functions, namely prominent things like 'link', 'readlink', 'chown', 'setuid' and others should be moved into a separate OS-dependent file, rather than handled in the following way:
(config.h)
#define _HAS_CHOWN 0
(foo.c)
#if _HAS_CHOWN
chown(blurb...);
#else
/*do nothing*/
#endif
scattered all over the place. It is just the problem that people don't learn at all (even not in CS classes at universities) how to practically write portable code. Code is still written in a plumbing manner: Add some pipes in some arbitrary house so that the water just flows through it, and if you need another kitchen sink somewhere, just add a flange in a place whereever your editor goto-line command accidentally points you to. Not even talking about Booch's OOA/OOD/OOP or other techniques of software analysis and design, but the drawback of bazaar programming is distributed hacking and patching.
>
> Well, here I'm too inexperienced.
II. Provide a LIBERTY.DLL
-------
We'd consider making some libiberty.a (adding at least stubs for all
routines that are missing in EMX or are implemented differently in Unix
dialects) a definite standard. The XFree86-4.0 server now for instance
also needs ndbm and regex, for some reasons. I had to take this from
elsewhere." (Holger Veit)
"Not #ifdef but implement! Or at least provide stubs, to go without the
need to #ifdef. #ifdef's when not maintained tend to rust much faster than
code without them. And with such core-level thing as XFree86 the time to
live is essential." (Sergey Ayukov)
III. Provide a BSD-like Paths.h Mechanism
------------
> #define _PATH_HEQUIV "/tcpip/etc/hosts.equiv"
> #define _PATH_HOSTS "/tcpip/etc/hosts"
> #define _PATH_NETWORKS "/tcpip/etc/networks"
> #define _PATH_PROTOCOLS "/tcpip/etc/protocols"
> #define _PATH_SERVICES "/tcpip/etc/services"
>
> Well, how do we get things mapped to the current values here?
>
> E.g. there are lots of sources assuming an existing '/tmp'.
> Should we have an EMX internal variable/function as
> a replacement?
That's what a "Common Filesystem Standard" was supposed to be good for.
EMX or any other redirection software (e.g. TVFS) is then responsible to
map such paths to really existing locations. The above paths.h snippet
implies that there is some directory, from now on known as "/tcpip/etc"
which contains two files "protocols" and "services". TVFS might redirect
*this directory* to e.g. "/mptn/etc" but should not tear the files apart
resulting in obscure to find bugs (probably not in this case, but situations
are possible where the definition of such well-known paths is important)."
On Tue, 04 May 1999 21:36:19 -0100, Martin Krischik wrote:
how about:
typedef enum pathId
{
pathHEQUIV ,
pathHOSTS ,
pathNETWORKS ,
pathPROTOCOLS ,
pathSERVICES ,
} pathId;
extern void
pathCalc(unsigned char aPath[],
enum PATH aID);
Would that not be a lot cleaner version. The Function would calculate the correct
filepath from the correct environment variable and filename.
IV. Beware of Global Variables
---------
On Thu, 21 Oct 99 14:44:43, Asbjoern Pettersen wrote:
>When i started GIMP porting a long time ago 0.9.18 i run into this problems.
>The main problem is use of global variables and forking() of other processes (DLL
>loading).
>They use global variables as input !! That have never been a good design on any
>OS'es.
>So the problem is how to set (and find) those global variables !
>This input is hidden and a new programmer will not know the real impact on changes
>he do. It's terrible! The program will get buggy and out of control.
V. Try Hard to Respect Posix (and ANSI/ISO C/C++)
----------------
Remember: Emx is very close to ANSI/ISO and Posix.
* Avoid common GNU extensions.
* Don't cling to vendor-specific so-called 'standards'.
* Follow closely internationally accepted independent portability standards.
This means that emx programmers are sometimes reminded of the fact
that 'Posix does not buy you much...'. This is often a headache, but it
also shows you which code is portable, aka standard conformant, and
which code isn't, therefore has to add some quirks to gain portability:
On 06 Sep 1999 20:22:17 +0200, Lars Gullik Bjnnes wrote:
>Duncan Simpson <dps@io.stargate.co.uk> writes:
>| According to my man page strcasecmp is conforming to BSD 4.3 (and
>| all unicies I have seen so far, inclduign SunOS). AFAIK GNU libc 2.1 is
>| conforming to ANSI and Posix but also includes common extensions, like mmap,
>| lstat, setrlimit, snprintf, select, fileno, *BSD sockets and whole bunch of
>| other stuff that is endemic in un*x source code and supported everywhere with
>| very few exceptions.
>| I can use fdopen in Posix programs. There are also some things that only exist
>| in glibc, and linux libc 5, like strfry(3) and memfrob(3), which are presumably
>| included for hack value.
>|
>| The average punter, and Un*x programmer, is not aware of how little Posix
>| gives him. As soon as you prevent a remote root exploit by using snprintf you
>| have violated Posix
>
>And you don't know if your prog will compile on a system not running gnu libs.
>
>|-and of course by doing any networking and using
>| select(2) you have violated Posix anyway. All internationalisation is beyond
>| the Posix standard too.
>
>Not quite true, check man setlocale:
>CONFORMING TO
> ANSI C, POSIX.1
>We do not want to use strcasecmp, and in LYX 1.1.x we are getting rid
>of it. (using more native C++ constructs instead.)
"As for non-existing API, I would recommend to stick to well-known things
like Posix, BSD networking, X11 and avoid system-specific calls like
DosWrite() etc. completely. If you're doing really multiplatform project.
Really 'standard' calls are the same everywhere and rarely require
re-implementation. There's some oddity here and there (I heard Ultrix(?)
does not have strdup()?) but you can add it in transparent way, namely
linking on Ultrix with your own implementation of strdup()."
(Dr. Sergey Ayukov, Sternberg Astronomical Institute Moscow, Russia
http://www.ayukov.com, http://crydee.sai.msu.ru/index-asv.html)
"Ok, can someone explain (together with an example each) what is
a) a dirty trick, b) a quick hack to satisfy compiler/linker c) a proper implementation"
On Tue, 4 May 1999 10:44:58 +0200, Holger Veit wrote:
> >Ok, can someone explain (together with an example each) what is > > > > a) a dirty trick, > > using a preprocessor macro instead of #ifdef in the sources, so that > you can watch in place what is going on (if things go wrong; remember: > Everything that can go wrong will go wrong!)
Macros can either be used to hide obscure hacks, or making the code more easily to understand. This depends on the situation. #ifdef clauses, namely nested ones are ill, ill, ill. Consider the following fragment in Xos.h:
#ifdef USG #ifndef __TYPES__ #ifdef CRAY #define word word_t #endif /* CRAY */ #include <sys/types.h> /* forgot to protect it... */ #define __TYPES__ #endif /* __TYPES__ */ #else /* USG */ #if defined(_POSIX_SOURCE) && (defined(MOTOROLA) || defined(AMOEBA)) #undef _POSIX_SOURCE #include <sys/types.h> #define _POSIX_SOURCE #else #include <sys/types.h> #endif #endif /* USG */
The X11 headers have many more of these 'pearls', and the GNU glibc include headers are even worse.
The only way to find out what really goes into the code is to run gcc -E on the file that includes this file.
> > b) a quick hack to satisfy compiler/linker > > using a function stub foo for a missing function: > > foo() { > fputs(stderr, "sorry, foo() not implemented! I'm just too lazy! > You may consider this as a bug; but I won't care at all ;-)" ); > return -1; > }; > > > and
> > c) a proper implementation > > Providing all this ifdef'd in the sources (no stubs) and doing it > according to the books in a way that will always work and is easy to > debug.
You can't prevent laziness in user code; I myself have routines marked with
#define TBD(x) fprintf(stderr, #x ": not yet implementedn")
The point is rather: missing functions, namely prominent things like 'link', 'readlink', 'chown', 'setuid' and others should be moved into a separate OS-dependent file, rather than handled in the following way:
(config.h) #define _HAS_CHOWN 0
(foo.c) #if _HAS_CHOWN chown(blurb...); #else /*do nothing*/ #endif
scattered all over the place.
> But for great projects this is simply not feasible even if investing a > reasonable amount of time.
It is. It is just the problem that people don't learn at all (even not in CS classes at universities) how to practically write portable code. Code is still written in a plumbing manner: add some pipes in some arbitrary house so that the water just flows through it, and if you need another kitchen sink somewhere, just add a flange in a place whereever your editor goto-line command accidentally points you to. Not even talking about Booch's OOA/OOD/OOP or other techniques of software analysis and design, but the drawback of bazaar programming is distributed hacking and patching.
> >I'm not sure at all what people think here, but I make some proposals ... : > > >a) use a file_copy routine for symlink (and don't clean up later perhaps) > > yes this is missing > > >b) let getdtablesize return some 'guessed' integer (w/o checking any > > docs, just get it working in most cases ...) > >c) #define lstat stat (neglecting something one could imagine around TVFS) > > Well, here I'm too inexperienced.
II. Provide a LIBERTY.DLL -------
We'd consider making some libiberty.a (adding at least stubs for all routines that are missing in emx or are implemented differently in Unix dialects) a definite standard. The XFree86-4.0 server now for instance also needs ndbm and regex, for some reasons. I had to take this from elsewhere." (Holger Veit)
"Not #ifdef but implement! Or at least provide stubs, to go without the need to #ifdef. #ifdef's when not maintained tend to rust much faster than code without them. And with such core-level thing as XFree86 the time to live is essential." (Sergey Ayukov)
III. Provide a BSD-like Paths.h Mechanism ------------
> #define _PATH_HEQUIV "/tcpip/etc/hosts.equiv" > #define _PATH_HOSTS "/tcpip/etc/hosts" > #define _PATH_NETWORKS "/tcpip/etc/networks" > #define _PATH_PROTOCOLS "/tcpip/etc/protocols" > #define _PATH_SERVICES "/tcpip/etc/services" > > Well, how do we get things mapped to the current values here? > > E.g. there are lots of sources assuming an existing '/tmp'. > Should we have an emx internal variable/function as > a replacement?
That's what a "Common Filesystem Standard" was supposed to be good for. emx or any other redirection software (e.g. TVFS) is then responsible to map such paths to really existing locations. The above paths.h snippet implies that there is some directory, from now on known as "/tcpip/etc" which contains two files "protocols" and "services". TVFS might redirect *this directory* to e.g. "/mptn/etc" but should not tear the files apart resulting in obscure to find bugs (probably not in this case, but situations are possible where the definition of such well-known paths is important)."
On Tue, 04 May 1999 21:36:19 -0100, Martin Krischik wrote:
how about:
typedef enum pathId { pathHEQUIV , pathHOSTS , pathNETWORKS , pathPROTOCOLS , pathSERVICES , } pathId;
extern void pathCalc(unsigned char aPath[], enum PATH aID);
Would that not be a lot cleaner version. The Function would calculate the correct filepath from the correct environment variable and filename.
IV. Beware of Global Variables ---------
On Thu, 21 Oct 99 14:44:43, Asbjoern Pettersen wrote:
>When i started GIMP porting a long time ago 0.9.18 i run into this problems. >The main problem is use of global variables and forking() of other processes (DLL >loading). >They use global variables as input !! That have never been a good design on any >OS'es. >So the problem is how to set (and find) those global variables ! >This input is hidden and a new programmer will not know the real impact on changes >he do. It's terrible! The program will get buggy and out of control.
V. Try Hard to Respect Posix (and ANSI/ISO C/C++) ----------------
Remember: Emx is very close to ANSI/ISO and Posix.
* Avoid common GNU extensions.
* Don't cling to vendor-specific so-called 'standards'.
* Follow closely internationally accepted independent portability standards.
This means that emx programmers are sometimes reminded of the fact that 'Posix does not buy you much...'. This is often a headache, but it also shows you which code is portable, aka standard conformant, and which code isn't, therefore has to add some quirks to gain portability:
On 06 Sep 1999 20:22:17 +0200, Lars Gullik Bjnnes wrote:
>Duncan Simpson <dps@io.stargate.co.uk> writes:
>| According to my man page strcasecmp is conforming to BSD 4.3 (and >| all unicies I have seen so far, inclduign SunOS). AFAIK GNU libc 2.1 is >| conforming to ANSI and Posix but also includes common extensions, like mmap, >| lstat, setrlimit, snprintf, select, fileno, *BSD sockets and whole bunch of >| other stuff that is endemic in un*x source code and supported everywhere with >| very few exceptions. >| I can use fdopen in Posix programs. There are also some things that only exist >| in glibc, and linux libc 5, like strfry(3) and memfrob(3), which are presumably >| included for hack value. >| >| The average punter, and Un*x programmer, is not aware of how little Posix >| gives him. As soon as you prevent a remote root exploit by using snprintf you >| have violated Posix > >And you don't know if your prog will compile on a system not running gnu libs. > >|-and of course by doing any networking and using >| select(2) you have violated Posix anyway. All internationalisation is beyond >| the Posix standard too. > >Not quite true, check man setlocale: >CONFORMING TO > ANSI C, POSIX.1
>We do not want to use strcasecmp, and in LYX 1.1.x we are getting rid >of it. (using more native C++ constructs instead.)
"As for non-existing API, I would recommend to stick to well-known things like Posix, BSD networking, X11 and avoid system-specific calls like DosWrite() etc. completely. If you're doing really multiplatform project. Really 'standard' calls are the same everywhere and rarely require re-implementation. There's some oddity here and there (I heard Ultrix(?) does not have strdup()?) but you can add it in transparent way, namely linking on Ultrix with your own implementation of strdup()."
(Dr. Sergey Ayukov, Sternberg Astronomical Institute Moscow, Russia http://www.ayukov.com, http://crydee.sai.msu.ru/index-asv.html)
Preface
A. Instead of an Introduction
I. Where to Start?
II. Some Frequently Used yet Uncommon Concepts
1. Another Interesting Name: What's 'X'?
2. And Posix, etc.?
3. And to How Get Posix, etc. Defined?
B. A Silly Demo and Some Comments
I. Source with Comments
II. Module Definition File
C. Notes on Libraries
I. Dynamic link libraries (DLL)
III. Shared libraries and dynamic libraries (TOG UNIX V.1/2)
IV. Exporting symbols from a DLL (OS/2 OMF)
V. 'dynamic library' and 'shared object' (*.so) versus DLL
1. dynamic link library
2. 'dynamic library' and 'shared object' (*.so) versus DLL
3. demo
VI. How to Link
VII. Some Special Tricks for libraries
D. Now the Hardware Gets Involved
I. Devices, X Servers, Clients
1) '/dev/fastio$', '/dev/console$', '/dev/ptyP0$', etc.
2) concepts related to hardware (vio) access and kernel level API 's
II. Inter Process (Session) Communication
III. Emx and device driver programming (hardware devices)
E. 'Imakefile' or 'configure', that's here the question!
I. A short overview: Autoconf vs. Imakefile
II. Preliminary steps
1) RTF docs
2) Some additions to /XFree86/lib/X11/config/os2.cf and os2.rules
III. Common initial problems
1) 'xmkmf -a' crashes because of 'sh' and 'Rexx' (path name) conflicts
2) 'make' crashes because of 'sh' and 'Rexx' conflicts
IV. Imakefile examples
1) creating a static library (archive)
2) creating an executable
3) Some maybe useful defines
E. What Shall I Better Change in the Sources?
I. Globbing
II. DOS "text" mode problems and binary reading
III. Process management and fork()
1) What is the difference between fork() and spawnvp()?
a) fork() and emx
b) making it work
c) replacing fork() and execvp(), etc.
2) Example
IV. Unix file system issues
V. Sockets
VI. Miscellanea
F. Some Notes on Portability and Coding Style
I. Write Proper Implementations
II. Provide a LIBERTY.DLL
III. Provide a BSD-like Paths.h Mechanism
IV. Beware of Global Variables
V. Try Hard to Respect Posix (and ANSI/ISO C/C++)
3) How to Link
----
I recommend the following simple (unorthodox, but nearly 'foolproof') procedure:
Use the static archive provided on the net or use the Makefile (normally created
by Imake, but today more and more by the OS/2 port of autoconf; details later)
to make the static archive foo_s.a. Then:
1. 'emxomf -s -l foo_s.a' (convert to native omf format)
2. 'emximp -o foo.a foo.def' (create import library for later ld linker fixup)
3. 'emxomf -s -l foo.a' (convert to native omf format for link386)
4. 'gcc foo.def -o foo.dll -Zomf -Zdll -Zmtd -Zsysv-signals -Zbin-files
-Zlinker /EXEPACK:2 -Zlinker /NOI -Zlinker /NOL -Zlinker /NOD -s -fstack-check
-Zlinker /PACKDATA -lfoo_s -lmore_foo_libs_here -lXmu -lXt -lSM -lICE -lX11
-lXext -lbsd -lsocket'
This produces a packed dll. While some linker flags aren't really necessary, they
won't hurt, unless you use OS/2 2.x (use '/EXEPACK' if in doubt). Isn't the
commandline nice?
Note #1 (linking order):
Linking order is crucial! The traditional C-style linker must find the
fixup for still unresolved symbols in the same library or in another one which
is _following_ on the command line. You might even need to link in rare cases
like:
-lfoo1 -lfoo2 -lfoo1
where foo1 needs symbols exported by foo2 and foo2 needs symbols exported by foo1.
Note #2 (library debugging):
For a debuggable library compiled with '-g' you'd use the line, except '-Zomf -s' and
all the '-Zlinker blurb', which are specific to link386.
Note #3 (XFree86/2 and multi-threading):
Note #4 (recommended flags for speed):
(emx0.9c)
CXXFLAGS = -m486 -O3 -Zmtd -Zsysv-signals -D__ST_MT_ERRNO__ -Wall -malign-loops=2
-malign-jumps=2 -malign-functions=2 -ffast-math
LDFLAGS = -s -Zmtd -Zsysv-signals -Zbin-files
[your mileage may vary; add your testing results here!]
Trick#3: (On Sun, 17 Oct 1999 14:34:21 +0200 (MET DST), Stefan Neis wrote):
Hint: Use instead ANSI C ('stdio.h') (or Posix ('unistd.h')) interfaces!
"3.11
Can I use another shell like 'bash' in place of CMD.EXE in an xterm?
Most shells are subtly incompatible with the PTY mechanism used in
xterm. A symptom may be that they won't work either if you redirect
console I/O to a COM terminal which should be possible. bash is
such an incompatible shell. See also Q 3.12 and Q 3.13.
Meanwhile, there are special ports of tcsh and ksh which work in
an xterm.
3.12
I use 4OS/2 (or some other shell) as my shell. Will it work in xterms?
4OS2 has a number of misfeatures, politely spoken, in an xterm. We
attempt to fix this in the future. For now, don't use it in an an xterm.
If 4OS2 or another shell is in your OS2_SHELL or SHELL variable,
please add an environment variable named X11SHELL to point to a
valid CMD.EXE path, e.g.
SET X11SHELL=D:OS2CMD.EXE
in your CONFIG.SYS. This will override the OS2_SHELL or SHELL
setting which is used otherwise.
3.13
Why does OS2MORE.COM give a SYS0447 in an xterm?
This is a program which silently assumes it has access to the
keyboard and the screen: it uses functions from KBDCALLS,
MOUCALLS, or VIOCALLS. There are more programs of this kind,
for instance: most *.COM in os2, ATTRIB.EXE, BACKUP.EXE,
CACHE.EXE, EAUTIL.EXE, FIND.EXE, HELPMSG.EXE, LINK.EXE,
LINK386.EXE(!), PATCH.EXE, PSTAT.EXE, REPLACE.EXE, RESTORE.EXE,
SETBOOT.EXE, SORT.EXE, SPOOL.EXE SYSLEVEL.EXE, TEDIT.EXE, TRACE.EXE,
UNPACK.EXE, XCOPY.EXE, XDFCOPY.EXE, but also
unfortunately TELNET and FTP, and some others; infact, almost any
16 bit application is a possible candidate. Note that CMD.EXE is an
exception of the rule: it is largely still 16 bit, but is clean for
XFree86/OS2 use.
You can use the EWS utility EXEMAP.EXE to check if these DLLs are
linked in.
Note: the existance of these DLLs does not mean that the program
does not work at all, actually EMX.DLL could call KBD API functions
but usually does not in the X11 environment. However, certain
unexpected effects may occur in an xterm. I'll try to find a
workaround for this in the future, but don't count on this.
3.14
FTP seems to work in an xterm, but it does not hide the password?
This is a side effect of what was described in Q 3.13. So actually FTP
does NOT work. [...]
II. Inter Process (Session) Communication
------------
" 2.46
I found that killing the X server or the window manager won't work well to end
X11. How can I shutdown X11?
Yes, we know it is a problem, mainly related to signal handling that is not fully
compatible to Unix signaling as well as some differences in session semantics.
XFree86 is not really intended to be started up and shutdown all the time [...]
2.52
What does the error message "_X11TransOs2OpenClient: Open server pipe
PIPEXxf86.0 failed" mean?
This is for the local named pipe communication the same problem as Q 2.31 is
for the network connection. It basically means: the server crashed for some
reason, and now a client, e.g. xterm cannot connect to the server. The reason
is probably hidden somewhere else in the XF86Config file, or some other setup
problem (e.g. network configuration). [...]
2.31
What does "SocketINETConnect() can't connect: errno = 65" mean?
What does "SocketINETConnect() can't connect: errno = 61" mean?
These messages *can* point out a network installation problem, in many cases
they are secondary errors, though, and are really caused by a different problem.
Nevertheless, you should ensure that your network is setup correctly. [...]
3.8
How can I enable authorization for host foo.edu without using xhost?
Create the file X0.hosts in XFree86libX11. To enable connections from a
certain host, add in a line containing
inet:hostname to the file. For example, your file may look
like this:
inet:foo.edu
inet:friendly.host.edu
..
Note that the X0 refers to the zero in the display name
os2systemname:0, i.e. in the case of Q 3.6 this would
become a X1.hosts then. [...]
III. Emx and device driver programming (hardware devices)
------------------
Q: Can EMX be used to generate 16 bit code for programming OS/2 device drivers?
No!.
:
>(Holger Veit): as gas lacks the ability to produce arbitrary segment
>types beyond 32 bit .text/.data/.bss segments. It can generate 16 bit
>instructions in a 32 bit segment (prefixed with 0x66/0x67), though.
On Wed, 13 Oct 1999 17:58:35 +0100, Csaba Raduly wrote:
>But you can write device drivers in 32-bit using some (already existing)
> thunking mechanism
>
>Check out /pub/os2/system/drivers/filesys/32drv170.zip on hobbes.
IBM distributes the device driver devel package (including compiler)
for free! You may want to look at
http://rover.wiesbaden.netsurf.de/~meile/warpstock/wsp02/en/index.html
for the details...
II. Preliminary steps
------
The following is meant to overcome some simple, nevertheless annoying
problems and to make things work faster: Quick and simple fixes, some dirty
stolen tricks.
1) RTF docs
---
- Read the README file! Or should it better be called DON'TREADME (psychology?)
- Read the INSTALL file!
- Read any additional info files from the program authors. Many
problems ocurring under special UN*X flavours have a similar counterpart on
OS/2 (others haven't, but this shall be a simple guide :-)
Read the /XFree86/lib/X11/config/README file, for a short introduction to
adapting Imake templates. The following may be useful, too.
2) Some additions to /XFree86/lib/X11/config/os2.cf and os2.rules
---------------------
I tried to make life easier, so I've added some defines to the vendor specific
imake template file: /XFree86/lib/X11/config/os2.cf.
Reasons for that:
- Never edit generated Makefiles by hand: Far too much work for larger
projects and errors are to be expected. (But it is sometimes unavoidable).
- If things work and don't break other projects, they should be mailed
to the respective lists in order to finally become standard:
But certainly, as SMiyata points out: As "far as possible, the changes must be
confined to Imakefiles and definition files bundled in the application.
The settings in os2.cf and os2.rules are shared by other applications, and hence
may cause conflicts in them. Also, the changes to the template files are local to
your setting, thus, although it may help to build binary on your system, it is not
suitable for porting, unless they are reflected in the distribution of XFree86
itself.
There are indeed the cases that modifications must be made on template files coming
with XFree86 rather than Imakefile and definition files coming with the applications:
[those often have uncommon names like 'local.cf' or 'machinefile', etc.]
Akira Hatakeyama <akira@sra.co.jp> reported elsewhere that, in
order to build Xvnc on OS/2, the lines
#define BuildXKB YES
#define BuildLBX YES
in os2.cf must be modified to
#ifndef BuildXKB
#define BuildXKB YES
#endif
#ifndef BuildLBX
#define BuildLBX YES
#endif
since, else, the settings in vnc.def is overwritten by those of os2.cf"
My proposed changes of /XFree86/lib/X11/config/os2.cf:
#define ManSuffix 1X11
#define LibManSuffix 3X11
#define FileManSuffix 4X11
instead of
#define ManSuffix 1 [... etc.]
(This helps a lot, if you already installed or will install several man pages from
bsd, Linux, etc. for the same function or tool.
I'm sure this will happen!! Also this is for good reasons now standard
on most recent UN*X versions.)
Add the following (after: #define
HasStrcasecmp NO):
#define StrcasecmpDefines -Dstrcasecmp=stricmp
-Dstrncasecmp=strnicmp [and probably: -DNEED_STRCASECMP]
(Just a hack, until emx has a working 'strcasecmp()' function interface).
Change
#define CplusplusCmd g++
to
#define CplusplusCmd gcc
#define CplusplusOptions -D__ST_MT_ERRNO__ -Zmtd -Zsysv-signals -O2
(I can't find any reason to call emx gcc by a different name for C++
or to use different options here.)
b) EMX docs recommend using object module format (-Zomf; link386.exe)
for better stability, if neither gdb.exe nor fork() shall be used. The
following additions to /XFree86/lib/X11/config/os2.rules are meant
to facilitate the switch in this case; nevertheless you'll have to
check dependency lines and rules, if a.out format is still used (and some
rules for libraries will become obsolete, but not harmful):
XCOMM $XConsortium: os2.rules /main/1 1996/10/31 14:47:27 kaleb $
XCOMM platform: $XFree86: xc/config/cf/os2.rules,v 3.15 1997/01/05
11:49:39 dawes Exp $
.SUFFIXES: .a .lib .o .obj .c .C .cc .cpp
.c.$O:
$(CC) $(CFLAGS) -c $*.c
#if HasCplusplus
.$C.$O:
$(CXX) $(CFLAGS) -c $*.$C
#endif
XCOMM Uncomment if using OMF (emxomf called automatically by gcc)!
XCOMM CC = gcc -Zomf
XCOMM O = obj
XCOMM LD = emxomfld
C = C XCOMM or .cpp
O = o
A = .a
III. Common initial problems
--------
Initially I thought imake was a labyrinthic beast, deserving the title
of the Minotaurus among the make tools. But with small and simple fixes
to work around some annoying bugs I was able to tame it a bit, so that
'xmkmf' at least does something useful:
1) 'xmkmf -a' crashes because of 'sh' and 'Rexx' conflicts
-------------------
Usually Imakefiles silently assume they are running on UNIX in a Bourne
'sh' environment, since authors intentionally do not support non UNIX
platforms.
Just try running the following changed Rexx scripts first, to create a
working environment:
make.cmd
/* REXX */
'@echo off'
PARSE ARG a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
/* x11make.exe 'MAKE=x11make.exe SHELL=d:/os2/cmd.exe' a1 a2 a3 a4 a5
a6 a7 a8 a9 a10 */
'SET SHELL=D:OS2CMD.EXE'
'SET X11SHELL=D:OS2CMD.EXE'
'set emxshell=D:OS2CMD.EXE'
'set CONFIG_SHELL=D:OS2CMD.EXE'
'set MAKE_SHELL=D:OS2CMD.EXE'
x11make.exe 'MAKE=x11make.exe SHELL= MAKE_SHELL=d:os2cmd.exe
CONFIG_SHELL=d:os2cmd.exe' a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
or
gmake.cmd
/* REXX */
'@echo off'
PARSE ARG a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
'set SHELL=d:/bin/ash.exe'
'SET X11SHELL=D:OS2CMD.EXE'
'set emxshell=d:/bin/ash.exe'
'set MAKE_SHELL=d:/bin/ash.exe'
/* x11make.exe 'MAKE=x11make.exe SHELL=' a1 a2 a3 a4 a5 a6 a7 a8 a9 a10
*/
x11make.exe 'MAKE=x11make.exe SHELL=d:/bin/ash.exe
MAKE_SHELL=d:/bin/ash.exe CONFIG_SHELL=d:/bin/ash.exe' a1 a2 a3 a4 a5
a6 a7 a8 a9 a10
==================================
Normally, running make.cmd should be sufficient, since the current OS/2
implementation by H. Veit and others makes extensive use of Rexx, needs
cmd.exe as command processor and is incompatible with Bourne shells.
Here a number of problems arise with many common Imakefiles.
Now lets assume xmkmf worked. Theoretically the steps are:
xmkmf
make Makefiles
make depend
make all (or simply: make)
.programname.exe for testing
make install
make install.man
2) 'make' crashes because of 'sh' and 'Rexx' conflicts
-----------------
Imake is misconfigured on most (commercial) UN*X platforms. Authors
normally compensate this by including many lines of (normally highly
complicated) Bourne shell code and calling several Bourne shell scripts in the
Imakefile. In this case 'make' won't work. Nor does a sh.exe as make shell work:
The current implementation depends on cmd.exe and Rexx. Finding and
rewriting everything in those immense Imakefiles, helper scripts and Makefiles
isn't really an alternative. Just try alternatively make and gmake (having installed
e.g. /bin/ash.exe). Now make should simply resume where it has crashed. Just
delete 'products' of those crashes (empty script output, truncated object
files, core dumps, etc.) before resuming the process. This makes using Imake real
fun!
Tips by John Williams <jwilliams@commerce.otago.ac.nz> (concerning 'configure' of
GNU 'autoconf', fixed by the OS/2-port, but still valid for certain parallel Bourne
sh-scripts very common in Imakefiles and/or config.local, etc.):
"3. Configure couldn't search the OS/2 path for programs because the shell construct:
for foo in bar do blah
apparently needs 'bar' to be a space or colon delimited list, whereas the OS/2
$PATH is semi-colon delimited.
4. Configure could not check to see if it's test programs were being compiled because
it tests for success using test -s which means test that the file is not empty.
The emx port of GCC creates foo.exe executables and touches foo, leaving an empty
file. Why does autoconf test for success with a non-empty file (-s) and not a
`file exists' (-f) test?
5. Overcome the above problems by setting an environment variable UNIX_PATH to be
a colon delimited path. Next, search and replace using sed thus:
sed -f os2patch.sed configure > configure.os2
where os2patch.sed looks like:
s/PATH/UNIX_PATH/
s/test -s/test -f/
s/host=NONE/host=OS2/
s/"${IFS}:"$/"${IFS};"/
and then execute sh Configure.os2. Lines three and four are due to Ilya Zakharevich,
who also pointed out the following issues:
1.if /tmp doesn't exist, make it
2.ensure CONFIG_SHELL=sh set in shell environment you are running Configure
from.
6. You can find what functions are contained in what object files and libraries by
using 'nm' (comes with emx). This also tells you which functions are referenced
(called) by functions in that file."
IV. Imakefile examples
------
$XFree86: config/README states:
"The easiest way to write an Imakefile is to find another one that
does
something similar and copy/modify it!"
Here some strongly shortened examples that use Bourne shell code:
sh -c 'for i in '"$(TEXT) $(SRCS) $(MISC) $(HDRS)"' ; do... etc., etc.
1) creating a static library (archive): NormalLibraryTarget(rw, $(OBJS))
----snip-----
# Imakefile for xpaint read/write directory
#include "../version"
#include "../Local.config"
INCLUDES = -I.. $(XPM_INCLUDE) $(TIFF_INCLUDE) $(JPEG_INCLUDE)
$(PNG_INCLUDE)
# Nothing to change below here
TIFF_SRC = writeTIFF.c readTIFF.c
#ifdef HaveTIFF
TIFF_OBJ = writeTIFF.$O readTIFF.$O
TIFF_DEF = -DHAVE_TIFF
#endif
[...]
XPM_SRC = readWriteXPM.c
XPM_OBJ = readWriteXPM.$O
SGI_SRC = readWriteSGI.c
#ifdef SGIArchitecture
SGI_OBJ = readWriteSGI.$O
SGI_DEF = -DHAVE_SGI
#endif
DEFINES = $(XPM_DEF) $(TIFF_DEF) $(SGI_DEF) $(JPEG_DEF) $(PNG_DEF)
SRCS = rwTable.c
readWriteXBM.c readWritePNM.c readWriteXWD.c writePS.c
readGIF.c writeGIF.c $(XPM_SRC) $(TIFF_SRC) $(SGI_SRC)
$(JPEG_SRC) $(PNG_SRC) libpnmrw.c
OBJS = rwTable.$O
readWriteXBM.$O readWritePNM.$O readWriteXWD.$O writePS.$O
readGIF.$O writeGIF.$O $(XPM_OBJ) $(TIFF_OBJ) $(SGI_OBJ)
$(JPEG_OBJ) $(PNG_OBJ) libpnmrw.$O
HDRS = libpnmrw.h rwTable.h
MISC = Imakefile
NormalLibraryTarget(rw, $(OBJS))
makelist:
sh -c 'for i in '"$(TEXT) $(SRCS) $(MISC) $(HDRS)"' ; do
echo $(CURRENT_DIR)/$$i >> ../filelist ; done '
rwTable.$O: ../Local.config
----snap------
D. What Shall I Better Change in the Sources?
--------------
Last but not least some well meant recommendations from Chapter 5 of
the "emx APPLICATION DEVELOPER'S GUIDE", with some additional comments.
You might use them as a short checklist.
I. Globbing
---
" If you want Unix-like wildcard expansion built into the program, use
int main (int argc, char *argv[])
{
_wildcard (&argc, &argv);
/* ... the program ... */
}
This should be done at the very beginning of main(), before ARGC and
ARGV are used. See _wildcard() and _response()."
II. DOS "text" mode, binary reading, etc.
-------------
"Famous problems are related to the
binary/text mode mess on OS/2,
especially when using stdin/stdout."
(Alexander Mai)
" Programs using stdin, stdout or stderr for binary data should call
_fsetmode() to switch the stream to binary mode."
" Change all open(), fopen(), fdopen() and freopen() calls to use
O_BINARY or "b", respectively, for binary files. If a file contains
both binary and textual data, read the file in binary mode and do
the conversion yourself."
Note: XFree86/OS2 uses '-Zbin-files'. So "do the conversion yourself" "and ignore
carriage returns" and <CTRL Z>.
" Though fseek() and ftell() now work on text files, the offsets are
different from what Unix programs expect. You may have to open the
files in binary mode and ignore carriage returns (this has been done
in GDB)."
" Programs reading a.out files should be changed to call _seek_hdr()
or _fseek_hdr() before reading the header to support .exe files.
More changes are usually required."
III. Process management and fork()
----------
"Replace fork() and exec*() with spawn*().
Under OS/2, fork() is inefficient."
(emx docs)
Note: This is usually not difficult; it is necessary to use '-Zomf'. Cf. the
example below.
Replacing vfork()-type lightweight processes with threads is complicated.
1) What is the difference between fork() and spawnvp()?
------------------
a) fork() and emx
-----
The Posix syscall fork() is for traditional Unix systems the only possiblity to
create a new process i.e. a nearly identical clone of an existing one:
"This is achieved by copying the pointers in the process information block (PIB)
of the parent to the newly allocated block in the kernel. On the other hand, on DOS
(Windows included) and on OS/2 (Windows NT included) there is no "fork" system call
but there is a "spawn" instead. This system call just allocates a new process
information block. Only file handles and the environment variable space are
inherited and selectors to the address space of the parent process are not copied.
So the child cannot access the contents of the memory of the parent. In fact, for
OS/2 and Windows NT, it is impossible to implement "fork" in the same way as on the
UNIX, since even root has only read access to the PIB table in the kernel.
[In fact, there is no such security leak as 'root' in OS/2 (AH)]
EMX library function fork() is actually spawn system call with a little bit of
IPC to copy memory contents. It is inefficient because all the memory contents
rather than just pointers are copied.
Well, the difference I was talking was that a *spawned* child has a clean address
space, while a *forked* one might have a corrupted one." (SMiyata).
b) making it work
-----
"fork() works only in programs linked by ld. It does not work in programs linked
by LINK386." [-Zomf]
"fork() doesn't work correctly in multithread programs."
"If the parent process uses the general terminal interface for the keyboard, the
child process cannot read from the keyboard using _read_kbd(), the general
terminal interface, or the Kbd OS/2 API functions.
If the process has a non-contiguous heap (that is, multiple heap objects), fork()
will fail. Increase the initial heap size to work around this problem. If any DLL
used by the program shares the heap with the program and uses the heap in
_DLL_InitTerm() by calling sbrk(), malloc(), etc., fork() will fail.
In both cases, fork() will return -1 and set errno to ENOMEM.
See also: alarm(), exec*(), _rmutex_create(), sbrk(), sigaction(), signal(),
spawn*(), _uflags(), wait(), waitpid()"
See the emx reference for more details about caveats. Use huge stack and heap size.
c) replacing fork() and execvp(), etc.
------------
Excerpt from exec*() documentation:
" When the new process ends, the parent process will be notified by SIGCHLD
and wait() will return the original process ID (PID). [...]
Restrictions:
Native DOS programs cannot be run. The new process gets a new process ID.
Unless the new process is an emx program (using emx.dll or emx.exe),
the actions for all signals are reset to SIG_DFL in the new process"
"Replace exec*() with spawn*() and exit() if the parent process waits
for the termination of the new process (by calling wait() or by
waiting for SIGCLD). This is required to keep the process ID of the
child process. In a forked process, however, you don't have to do this
because emx.dll does it for you."
What does this mean? Well, in a Posix OS traditionally the parent process clones
itself with fork(), which generates a new process environment, whereas exec()
overwrites its own present environment with a new one, keeping the PID.
Usually this is combined:
exec() is used in the forked child process, overwriting the parent's process
status with a new one. Meanwhile the parent usually calls wait() or a home-brew
function and waits for child termination, communicated by SIGCHLD. ps (pstat.exe)
gives you the details.
A common problem here is, that the child after termination does
not find this wait() etc. point of the parent. Thus a 'zombie' remains. This
may lead to a process table overflow, so that the system has to be shutdown and
rebooted. All this hassle can be simplified by using spawn*().
But for tracing and debugging:
"Do not use the PTRACE_TRACEME request of ptrace(): use P_DEBUG
instead when starting the process with spawn*()."
IV. Unix file system issues
--------
The separator of path components is a slash '/' as opposed to OS/2's ''.
Note that many APIs understand the '/' on OS/2, but cmd.exe and most apps don't.
Multiple paths (e.g. in environment variables like PATH) are separated by a colon ':'
The root dir on every un*x system is '/'. Often code checks whether a given path is an
absolute one by comparing the beginning with '/'. Use grep for all standard paths and
all commands which are mentioned in this introduction. (_fullpath() and _abspath()
can also be useful.)
Using
#define getcwd _getcwd2
#define chdir _chdir2
may help to support drive letters. Read the EMX docs:
"Watch out for Unix file system hacks: Unix allows deleting and
renaming an open file (the file will be deleted after being closed).
[unlink(), remove(), rename()]
- Watch out for Unix file names (Unix is case sensitive, long file
names and multiple dots are allowed). On OS/2's HPFS multiple dots
are also allowed; however, trailing dots are not significant (except
for the special file names `.' and `..').
- The null device is called /dev/null under Unix. The __open() system
call translates the filenames "/dev/null" and "/dev/tty" (lower
case, with slashes) to "nul" and "con", respectively. However,
system ("whatever >/dev/null");
won't work as the standard OS/2 and DOS command interpreters don't
recognize /dev/null.
- If you want to use for separating directories, changes may be
necessary. These changes are optional because / also works.
- Implement support for drive names. This can be done by using
#define getcwd _getcwd2
#define chdir _chdir2
In addition, some changes will be necessary. For instance, you have
to change code which checks whether a filename is an absolute path
name. _fullpath() and _abspath() can also be useful."
"Unix does not have drive letters, so a file (resources,
fonts, config, etc.) path starts with '/' normally. XFree86 is
located in a tree below %X11ROOT%XFree86...
To prepend the X11ROOT part to a file path, a special function
named __XOS2RedirRoot exists which you are supposed to use in
these cases rather than an insane own solution. The prototype of
this function is:
const char *__XOS2RedirRoot(const char *pathname);'
[...] XFree86/OS2 stores all configuration files in a common
directory. This is X11ROOT:XFree86libX11, where X11ROOT is
an environment variable which contains the drive letter the
directory tree is located. The common places like /usr/X11,
/usr/X11R6, /usr/XFree86, /etc which are used for XFree86 in
various Unix flavors, are not supported [...].
It is also there where you should put your XF86Config file,
the color database (rgb.txt), the host authorization files
(X0.hosts), and various other files." (FAQ)
"Potential problem might be the DLL name: IIRC one can either use
'foo' or 'x:barfoo.dll'
to access foo.dll (if in LIBPATH)." (Alexander Mai)
"Note that ///abc is a valid Unix filename. It's equivalent to /abc.
- Note that chdir ("..") is a no-op under Unix if the current working
directory is the root directory. Under emx, chdir ("..") fails in
the root directory.
- Use termio or termios or read the keyboard with _read_kbd() if you
don't want to get input line by line.
- Under Unix, directories in environment variables (PATH, for
instance) are separated by colons; use semicolons instead.
- The shell isn't called /bin/sh. Use system(). system() and popen()
don't expand wildcards (unless COMSPEC points to a shell which
expands wildcards)."
So, in short:
"grep for '/dev', '/bin', '/tmp', '/usr', '/proc', ...
also check out all commands starting up another process:
exec*, popen, system,...
Remember that cmd.exe doesn't like slashes!" (Alexander Mai)
1Cf. the references below.