Pretty Good Porting for MPE/iX

Version 1.0 by Lars Appel, July 1999

Abstract: This paper discusses the porting of Unix based Open Source software to MPE/iX. By sharing his experience from various freeware-porting projects, the author gives an overview of the porting process in general, and guides through the typical steps using a real freeware package as an example. The chosen PGP example combines typical porting issues as well as a few exotic ones.

About the author: Lars Appel works at the HP Response Center in Germany since 1991, providing primarily HP 3000 software support. In his leisure time, he ported Samba and several other freeware packages to MPE/iX. He also participates in the HP3000-L Internet discussion group.

Purpose of this Paper

Porting software from Unix to MPE/iX using the Posix environment is neither a science nor black magic. Trust me. I have ported the Samba file and printer sharing freeware for PC network integration to MPE/iX without being a C expert or PC networking guru, for example.

Every porting project is different, but it seems that there are quite a lot of common issues. So there are a lot of things that you learn during your first port that you can leverage in future porting projects. This is probably one of the reasons why porting can turn out to be quite a lot of fun. You get fairly productive over time, making utilities or applications run on MPE/iX, which you could never write from scratch with comparable time and effort.

In this paper I will try to share a good deal of what I learned during my various porting projects, hoping to give you an idea of the underlying concepts and issues as well as a bunch of tips to help you get started with your own porting efforts. I will discuss the typical porting issues using the strong encryption software Pretty Good Privacy (PGP) as an example. There are two main reasons why I chose the PGP example: one is that at the time when I had the idea to write a porting paper, I had just recently been playing with PGP on MPE/iX out of curiosity, and the other is that PGP has quite a nice combination of the typical porting issues and thus makes a very nice educational example.

I am not going to talk about encryption software or algorithms in detail. I am a total layman in this area. You might want to try running your own build of PGP on MPE/iX as an exercise while reading this paper. If you should do so, please make sure to examine the ReadMe and license files of the software to make yourself familiar with potential US export and/or licensing restrictions that apply to encryption software in general or PGP in specific. For example, the PGP5.0i version that I played with, has been legally exported outside the US/Canada (in printed form) but is freely available for non-commercial use only.

The aim of this paper is to give you an understanding of the general process of porting software from one of the various Unix flavors to MPE/iX using the Posix capabilities of the HP 3000, as well as typical issues encountered and potential solutions or workarounds. If you should get curious enough to pick your own favorite Posix software or freeware to give it a try and see if it ports to MPE/iX with reasonable effort, keep in mind that the HP3000-L or comp.sys.hp.mpe internet discussion forum can be an extremely helpful place to either share your success or ask for experiences or help from other members of the worldwide HP 3000 community.

Overview of the Process

Porting freeware typically means downloading and unpacking the source distribution, working through the included documentation to examine tips for installation, configuration or usage, making necessary adjustments to get the software to compile and link successfully and finally, testing whether it works reasonably well, or if there are restrictions on the new target platform that should be either fixed or at least documented.

Typical porting issues encountered are missing include files (e.g. arpa/inet.h) or missing declarations in existing include files (e.g. constants or function prototypes) as well as non-Posix routines that are used by the freeware because they are commonly available in many Unix flavors but might be missing on MPE/iX by default (e.g. the gettimeofday() function).

There are also a few more subtle issues that can cause you a fair amount of headaches when you first stumble across them, such as inappropriate declarations in include files (e.g. st_blksize which is not initialized to a reasonable value and thus should not be used by your freeware) or missing routines that cannot be detected easily because a routine with the same name happens to exist in NL.PUB.SYS or XL.PUB.SYS (e.g. bcopy(), bzero(), bcmp()). Another area to be aware of is system calls that need some kind of "privilege" to work (e.g. bind() for tcp port numbers below 1024), which tend to require a program being run by the "root" superuser on Unix, but usually require the use of PM capability on MPE/iX instead. However, don't get too frightened by such threats here.

Portable software like Open Source or Freeware, based on the C or C++ programming languages, typically uses the preprocessor directives #ifdef .. #else .. #endif for conditional compilation. This is because there is no such thing as "the real Unix" but only various flavors of implementations. Besides the inherent platform differences like availability or feature set and parameters of "common" system calls, there are also compiler differences to cope with. For example, the number of bits allocated for an int or long int variable does not have to be the same on all platforms or compilers. This can be quite a surprising experience for programmers used to working with ANSI COBOL or the JAVA programming language, for example.

One or more Makefile(s) are usually present to keep track of source file dependencies, control the build or rebuild process (compile, link, etc.) as well as define the right set of -D flags to select the appropriate #ifdef .. #else .. #endif source code sections for the target platform or desired optional features of the software.

The adjustments required for a new target platform like MPE/iX might have to be done to Makefile(s) or source code or both.

Many software packages use a freeware program called GNU autoconf to improve portability and reduce the amount of work needed to cover a new target platform. The underlying concept is to provide a shell script (called configure) as part of the software package that performs various tests to probe the target platform for required or desired features (like include file locations or library calls) and then generate a number of customized Makefile(s) or include files based on the test results. This approach can strongly reduce or even completely eliminate the need to manually tweak Makefile(s) or source files for porting to a new target platform.

If you have completed a port and the result works reasonably well on MPE/iX, you should seriously consider sharing your results with other people. This can even be valuable if there are still "open issues" to be resolved. As long as the limitations can be documented in some kind of ReadMe file, the other people can decide whether they "can live with" the limitations or work around them. Moreover, some of them might even feel encouraged to help mitigate or remove the remaining issues. This is where the Open Source model of software development can yield one of its inherent strengths (and also a lot of fun and motivation).

You should also consider whether it would make sense to submit MPE specific enhancements of the freeware package back to the original authors (who typically maintain some kind of mailing list or recommended newsgroup to provide a forum for people with a common interest to get in touch with each other). Otherwise you might have to reapply your MPE "diffs" to new versions of the original freeware as soon as they become available to provide new features or bug fixes.

System Prerequisites

I cannot give you exact prerequisites for your porting machine as the requirements depend strongly on the software package that is going to be ported. Some packages, for example, assume Perl being available, while others do not. Therefore, I can only provide some info about my porting machine here, to give you at least an idea.

My system is running MPE/iX 5.5 with PowerPatch 4 or later. I have fixed the (de)faulty permissions of various 5.5 Posix Shell and Utilities files (from r--r----- to r--r--r--) and also established a reasonable TZ timezone variable by adjusting my system-wide logon UDC as well as the /etc/profile settings. You can find quite a bunch of good tips in this regard in Michael Hensley's "Plug and Play Posix" paper on the web.

First, the networking on my porting machine is configured and started. I use NS/VT or Telnet for sessions and Samba for remote file access from my Windows PC. Using Samba is definitely not a must for porting but it tends to be extremely convenient to use PC tools like Windows Explorer, Netscape (web and file) browser, and PFE Programmer's File Editor to work with files and directories in the MPE/iX file system. Tools like EDITOR.PUB.SYS do not handle bytestream files by default, and using /bin/frombyte and /bin/tobyte for file conversions is possible but not quite convenient.

I have installed GNU core, GNU groff, GNU gcc, GNU autoconf, Perl and GNU m4 (from the sendmail package) from the Interex MPE Freeware Tape (which is a very nice alternative to downloading from the Internet) but not all of this is required for each of the ports that I have done so far. You typically notice missing parts when reading installation instructions or trying to run a configure script or execute make.

For most of my ports I also use the LibBSD library and include files from Jazz, which can save considerable porting time and effort. Please do not confuse the LibBSD package with Berkeley Sockets (also called BSD Sockets by some people). The latter is a programmer's API to the TCP/IP networking services and is part of the Posix Developer Kit and thus available on MPE/iX by default (keep in mind that I am talking about 5.5 or later releases). The former is a collection of include files and library calls that implement various non-Posix "general purpose" routines that are fairly common on many Unix flavors, like gettimeofday() for example, but not available on MPE/iX by default. Being a lazy guy, I prefer resolving missing pieces from LibBSD instead of writing my own stubs, if possible.

In addition to software installed on my porting machine, I also keep documentation like manuals, LaserROM, or Instant Information CD nearby. Most of these things can now also be found on the Internet. Sites like docs.hp.com or Alta Vista can be quite handy in this regard. The documentation used covers not just MPE/iX, but also Unix, as it is sometimes required to look up some info about a missing library function to understand what the ported software is trying to achieve and how to provide comparable service using MPE/iX Intrinsics, for example, unless one can even "no-op"-out the call. Mark Bixby's "MPE/iX Porting Guide" can also be a valuable resource on the web.

Knowledge Prerequisites

The more you know the better. But do not give up now, just because you are unable to walk on water. Remember that each port will increase your practice and experience. The less you know at the beginning, the more you will be able to learn, as long as you stay patient and don't give up too quickly. Keep in mind that you might find helpful people on HP3000-L, so sometimes it can be a good idea to not puzzle around for hours but post a quick question to the Internet instead. At least to get a hint.

You should be familiar with handling files, directories and symbolic links in the HFS namespace, using either MPE or Shell commands or both. You should also have an understanding of the Posix security scheme and how it is implemented with the (extended) MPE/iX ACDs. There is some quite good material available, in case you need to refresh your faint memory (or learn it from scratch).

Experience with shell commands and utilities like cd, ls, cp, mv, more, grep, and tar is quite helpful. Using the vi editor is not a must; it is more a matter of taste. If you know or like that editor, you might want to use it, otherwise you might prefer using Samba and PFE, for example. You might even be able to use your favorite MPE editor (like Qedit) to work with bytestream files in the HFS name space.

If you are not using Samba then you will probably have to use FTP or your terminal emulator for file transfers.

Using a C compiler like gcc or c89 and some basic understanding of make and Makefile(s) is required. But you do not have to be a full blown C programmer for porting, as you will not design and create a large architecture from scratch, but will probably spend most of your time examining and reading existing code or Makefile(s) and sometimes try to find places where minor adjustments for MPE/iX have to be made.

While we are on the subject of C compilers... You might notice that I tend to prefer the GNU gcc for doing porting work nowadays (as opposed to the time when I did my Samba/iX port). One of the reasons is that most of the freeware packages seem to use gcc as their default compiler, so why introduce additional unknowns by using a different compiler? Another reason is that gcc is available for free whereas c89 (HP C/iX) is a purchasable product. So the former seems like a more logical choice for porting freeware, doesn't it? On the other hand, using c89 can sometimes be more convenient, for example when calling MPE Intrinsics. It is possible with both compilers but c89 has a #pragma intrinsic directive and can utilize the info from SYSINTR.PUB.SYS to provide quite some convenience and parameter checking here.

Using the powerful System Debug/iX can be extremely helpful but is definitely not a must. Maybe I will include a few related tips later in this paper. If you do not have access to a (symbolic) debugger, you can always add printf statements to explore the code. Many freeware packages already supply some kind of debug instrumentation that can be enabled by a command line option or similar means.

A really big plus for porting is when you already know the software being ported from another platform or even have a running version available on another machine. Knowledge and experience will shorten your time wading through extensive documentation to get an idea how to configure and use the software once you have reached a point where compile and link complete successfully. Having some kind of reference installation on another system can be very helpful for troubleshooting as you can compare the MPE/iX version with that other platform to explore bugs or differences. For client/server software, the other machine can also act as the complementary partner for testing.

But again, the above is not a must. It just can be very helpful. In fact, for most of my own porting projects, it seems that I used the software for the very first time when my MPE/iX version compiled, linked and ran reasonably well... and yes, I typically had to spend considerable time reading doc files to get an idea how to install, configure and run the respective application or utility package.

Setting up the Account Structure

In the past, I used to create a separate account for each porting project (e.g. SAMBA). The downside of this approach was an increasing number of small accounts with typically only a single group (PUB) and a single user (MGR). After a while I changed my mind and now prefer to have a single IX account and create groups and users for porting projects inside that account, for example a PGP.IX user homed in a PGP.IX group.

 :newgroup pgp.ix
 :altgroup pgp.ix ;access=(r,x:ac;w,a,l,s:gl)
 :newuser pgp.ix
 :altuser pgp.ix ;pass=choose1 ;home=pgp ;cap=+ph,gl

By carefully using Posix permissions (notice the umask in my .profile example below) and the GL capability of MPE, the different porting projects can be isolated from each other in such a way that read access is allowed but write access is disabled. This way a new porting project can easily "steal" useful parts from older projects but there is no risk of damaging other projects by unintended accidental write access.

As many freeware packages tend to "feel at home" in default HFS locations like /usr/local/something (and sometimes have default pathnames for config files or similar in this way), I typically create a symbolic link under /usr/local that points to the MPE home group (i.e. /usr/local/pgp -> /IX/PGP in above example). Having the porting project reside under an MPE group instead of a plain HFS directory also allows me to redirect it to use disc space on a user volume set.

 :newlink /usr/local/pgp , /IX/PGP

Inside the MPE home group of the porting project I usually create a ./orig subdirectory for keeping the originally downloaded tar.gz file(s) as well as a ./mpe subdirectory for keeping track of the MPE specific changes or additions made during the port. The latter ./mpe directory typically gets additional subdirectories like inc for include files, lib for libraries, src for sources, as well as old and new to keep track of changed files in the original sources.

 shell/iX> mkdir orig mpe mpe/inc mpe/lib mpe/src mpe/old mpe/new

During account setup, it is also helpful to create .profile and .exrc files. See Michael Hensley's "Plug and Play Posix" for tips.

 shell/iX> cat .profile

 alias ll="ls -l"
 alias lsf="ls -F"
 alias mpe="callci ci,2"

 export PATH=/usr/local/bin:/bin:$HOME/bin:
 export MANPATH=/usr/local/man:/usr/man:$HOME/man
 export CC=gcc

 umask 0027

 shell/iX> cat -v .exrc

 map ^[A k
 map ^[B j
 map ^[D h
 map ^[C l
 map ^[V ^B
 map ^[U ^F

After preparing the account structure, it's probably a good idea to create and compile a little "hello world" program to test whether compiler and linker can be invoked successfully. Details of this are left as an exercise for the reader (unless a lot of people complain and convince me to add a detailed example here ;-)

Downloading the Source Code

The typical download from the Internet will probably be done with a PC or Unix workstation acting as a middleman, unless your 3000 has a direct connection to the Internet. When downloading binary files (like compressed tar archives) with ftp or a web browser, you should watch out to use binary transfer modes or "save as" options, to avoid files being changed by some undesired LF-to-CRLF conversion. The same holds true when pushing the files from PC or Unix workstation to the 3000 in a second step. This can be done with Samba/iX, or FTP or your favorite terminal emulator's file transfer option. With FTP it can be useful to specify a put command similar to "put pcfile ./hostfile;rec=,,b" to make sure that the target file gets an HFS filename and will be created as a bytestream file. Similar options should be available with your favorite emulator. Samba/iX uses HFS names and bytestream files by default.

As a side note, I have also been able to successfully use GNU wget for MPE/iX on my 3000 to download files from the Internet without needing the intermediate PC or Unix workstation step. My 3000 does not have a direct Internet connection but our network does have http and ftp proxy servers to enable web browsers to get to the outside world. GNU wget is quite similar to a web browser and thus can use those proxy servers as well. Using GNU wget is quite convenient, but might not apply to your individual networking context. Anyway, the PC or workstation middleman should also work fine.

When downloading software that includes encryption software, you might have to watch out to not violate ITAR restrictions regarding export outside the US or Canada, so make sure to read associated notes on the ftp or web servers. Also, see a few related notes later in this text.

Unpacking the Tarball

No idea where the term tarball comes from but some people seem to name those compressed tar archive files this way. Unpacking those tarballs can be done in two steps -i.e. first uncompress and then extract the files- or in a single command using the Shell's pipe facility. Something similar to "gunzip -c yourfile.tar.gz | tar xvof -" should do the trick, if your tarball is compressed with GNU gzip. The -c option tells gunzip to send output to stdout which is then piped into the tar command, which in turn expects to read from stdin as it is told by the f option and - argument. By specifying the o option, we make sure that we will be the owner of the unpacked files and the v option makes the output somewhat verbose.

 shell/iX> pwd

 /IX/PGP

 shell/iX> ll orig

 -rwxr-x---   1 PGP.IX    IX     927363 May 30 14:42 pgp50i-unix-src.tar.gz

 shell/iX> gunzip -c orig/pgp50i-unix-src.tar.gz | tar xvof -

 tar: blocksize = 20
 x pgp50i/LICENSE, 13177 bytes, 26 tape blocks
 x pgp50i/README, 6091 bytes, 12 tape blocks
 x pgp50i/WELCOME, 6023 bytes, 12 tape blocks
 x pgp50i/src/lib/pgp/utils/Makefile.in, 376 bytes, 1 tape block
 x pgp50i/src/lib/pgp/utils/makefile.msc, 764 bytes, 2 tape blocks
 ...
 x pgp50i/src/plugins/elm2.4.ME+.35+pgp50.patch, 9355 bytes, 19 tape blocks
 x pgp50i/src/plugins/mutt-0.81+pgp50.patch, 53729 bytes, 105 tape blocks
 x pgp50i/src/TODO, 677 bytes, 2 tape blocks

Before actually unpacking the tarball, it is usually a good idea to use tar tf or tar tvf to examine the table-of-contents to get an idea about the files and directories that will be created during the extraction. The typical tarballs have been created with relative pathnames and will thus unpack files relative to your current directory. Nevertheless, it is a good idea to check beforehand. You should also keep an eye on potentially illegal filenames on MPE/iX (for example files with special characters like #) and keep in mind that HFS filenames inside an MPE group cannot exceed 16 characters in length. Thus it might be a good idea to create a ./tmp subdirectory and perform the unpacking there (mkdir tmp; cd tmp).

Exploring ReadMe and License Files

Make yourself familiar with the files and directories extracted from the tarball to get a rough overview. Especially look for ReadMe files as they typically provide the best starting point (otherwise they would probably have been called IgnoreMe instead of ReadMe). They tend to give an overview of the package, available files or documentation, copyright and build instructions or at least pointers to these types of information. Detailed copyright and licensing info typically lives in a file named Copying or License. Build or installation instructions frequently come in a file named Install. Other documentation might come in plain text files, HTML documents, man pages, GNU info files, or other common or exotic formats. The amount of documentation can sometimes be overwhelming.

 shell/iX> ls -l pgp50i

 -rw-------   1 PGP.IX            IX         13177 Feb  7  1998 LICENSE
 -rw-------   1 PGP.IX            IX          6091 Feb  7  1998 README
 -rw-------   1 PGP.IX            IX          6023 Aug 10  1997 WELCOME
 drwxr-x---   7 PGP.IX            IX          2432 Feb  7  1998 src

There are quite a number of different flavors of Open Source software, just recall terms like freeware, shareware or public domain software, which are not synonyms. Thus you should read the copyright or license file to make yourself familiar with the conditions under which you are permitted to use, copy, modify, or redistribute the software. A common theme in the freeware arena is that the source code must remain freely available. Some types of Open Source software do have restrictions like "only free for non-commercial or educational use" or similar. This is why reading the associated files is important.

A pretty nice overview of concepts and history of Open Source software was given in WebReview a while ago.

For reasonably strong encryption software, there might be additional issues to consider, for example restrictions regarding export outside the US or Canada as encryption software is considered arms by some governments. The international version of PGP 5.0i encryption software, for example, has been legally exported outside the US/Canada in printed form (a series of books with OCR font, page checksums and the like) and scanned back to electronic format then. Checkout the PGP web site or documentation for details. Here it might also turn out to be important where you download your source code from. If you are located outside the US or Canada it might make quite a (legal) difference to download from an ftp server in the US or in the Netherlands, for example. So, make sure to read the documentation.

Another aspect in license conditions might be patents on algorithms used. This does not only apply to encryption software but seems to play an especially important role in that area. And again, it might depend strongly on the country that you are located in, as patents might only apply to some countries and not to others.

Please don't take all my above comments literally. I am not a lawyer, just a layman. Read the documents yourself.

Adjusting configure scripts and install-sh

Many freeware packages use GNU autoconf for improving portability across platforms. Packages based on GNU autoconf avoid the need to manually customize the Makefile(s) for a specific target platform. A shell script called configure performs a series of tests to probe the available compiler, header files, library routines and similar properties of the target platform. It saves intermediate results of these tests in config.cache and config.status files, in case the configure phase has to be rerun later, and finally generates Makefile(s) from Makefile.in template(s), applying customizations based on the test results. In many cases, it also generates a header file named config.h with a number of #define flags making the test results available to conditional compilation sections in the source code.

Unfortunately the configure scripts do not work on MPE/iX out of the box. Unlike many Unix linkers the MPE/iX link editor does not report unresolved externals at link time, as the loader will -by design- try to resolve missing pieces from system libraries like XL and NL in PUB.SYS, for example. This behavior confuses the configure script in such a way that the absence of link errors fools certain tests to report the availability of library routines that are not really available (which shows up in unresolved externals at run time, but then it is already "too late").

The affected tests inside the configure script can be adjusted for MPE/iX with fairly small modifications (that essentially force the test programs to not only be linked but also be loaded to check for potentially unresolved external references). Those adjustments have been developed by Mark Klein and/or Mark Bixby long ago. Manually applying such adjustments to a large configure script can get quite tedious and error prone, but fortunately this can be avoided in most cases.

In former times the configure scripts were handcoded (watch out if your favorite porting project still uses such a beast), but nowadays they have been typically generated from a configure.in definition file with the GNU autoconf utility. The freeware package does not only contain the configure script, but also the original configure.in definition, so by rerunning a GNU autoconf version that has been adjusted for MPE/iX, you can regenerate configure scripts that are tweaked for MPE/iX.

For running GNU autoconf, your system must have the GNU m4 macro processor as well as Perl installed. I picked up the m4 program from Mark Bixby's sendmail port (just the single NMPRG, nothing else needed) as I could not find it elsewhere. However, as far as I understand, the latest version of GNU core on Jazz already contains autoconf and m4 as well. Don't panic now, if you are not familiar with m4 macros or Perl scripts - you don't have to be. The tools must just be available on your system so GNU autoconf can find them during its execution.

 shell/iX> whence m4
 /usr/local/bin/m4

 shell/iX> whence perl
 /usr/local/bin/perl

When modifying files (e.g. configure), I typically move the original versions to ../mpe/old and a copy of the adjusted files to ../mpe/new so they could be identified easily and provide a handy reference for later diff's. In fact, this has changed slightly since I began writing this paper... Nowadays I use GNU cvs for revision control as it is quite helpful to keep track of file modifications or display diff's between versions, but if you don't want to learn another tool for your porting projects, you might prefer to use an approach similar to the ../mpe/old and ../mpe/new directories (at first).

 shell/iX> cd pgp50i/src

 shell/iX> ls -F

 Makefile.in      clean.bat        language50.txt   mkhdrs.bat
 README           config/          lib/             mklang.pl*
 TODO             config.h.in      libbuild.bat     pgpinst
 aclocal.m4       config_win32.h   make-mkhdrs.pl*  plugins/
 apps/            configure*       makefile.msc     release.bat
 build.bat        configure.in     man/             

 shell/iX> mv configure ~/mpe/old

 shell/iX> /usr/local/autoconf/bin/autoconf

 shell/iX> ls -l configure

 -rwxr-x---   1 PGP.IX    IX     64052 May 30 14:58 configure

Sometimes it is not sufficient to adjust the configure script, because it calls a config.guess and config.sub script to detect or classify the target platform and return a special identification string (e.g. hppa1.0-hp-mpeix). These generic scripts are also not MPE aware in most cases and have to be adjusted accordingly. This is not automatic like recreating the configure script from configure.in with GNU autoconf, but fortunately the necessary changes are minor and can be done by hand easily.

 shell/iX> ls -F config

 config.guess   config.sub     install-sh*    post.in        pre.in

 shell/iX> diff -u ~/mpe/old/config.guess config/config.guess

 --- /IX/PGP/mpe/old/config.guess        Sat Aug  9 23:45:33 1997
 +++ config/config.guess Sun May 30 15:02:58 1999
 @@ -254,6 +254,9 @@
                 HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
                 echo ${HP_ARCH}-hp-hpux${HPUX_REV}
                 exit 0 ;;
 +       *:MPE/iX:*:*)
 +               echo hppa1.0-hp-mpeix
 +               exit 0 ;;
         3050*:HI-UX:*:*)
                 sed 's/^        //' << EOF >dummy.c
                 #include <unistd.h>

 shell/iX> diff -u ~/mpe/old/config.sub config/config.sub

 --- /IX/PGP/mpe/old/config.sub  Sun Aug 10 21:22:11 1997
 +++ config/config.sub   Sun May 30 15:01:14 1999
 @@ -657,7 +657,7 @@
               | -hiux* | -386bsd* | -netbsd* | -freebsd* | -openbsd* \
               | -riscix* | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* \
               | -elf* | -ptx* | -coff* | -ecoff* | -winnt* | -domain* \
 -             | -vsta* | -udi* | -eabi* | -lites* )
 +             | -vsta* | -udi* | -eabi* | -lites* | mpeix* )
         # Remember, each alternative MUST END IN *, to match a version number.
                 ;;
         -sunos5*)

In most cases, there is also an install-sh script that will be used by make to place files in their target directories near the end of the build process. The install-sh script typically uses some temporary HFS filename internally that contains special characters like # which are legal on most Unix systems but not on MPE/iX. The required adjustment is small and can be done manually as well.

 shell/iX> diff -u ~/mpe/old/install-sh config/install-sh

 --- /IX/PGP/mpe/old/install-sh  Sat Aug  9 23:45:40 1997
 +++ config/install-sh   Sun May 30 15:05:35 1999
 @@ -210,7 +210,7 @@
 
  # Make a temp file name in the proper directory.
 
 -       dsttmp=$dstdir/#inst.$$#
 +       dsttmp=$dstdir/_inst.$$_
 
  # Move or copy the file name to the temp name

Notice that the diff -u output given above shows lines of the old (-) and/or the new (+) file version as well as a few lines of context. The -u option is unique to the GNU diff program (/usr/local/bin/diff), whereas the Posix Shell's /bin/diff only offers the default output format as well as the -c context diff option. All those different formats can be used to highlight text file differences.

Setting up CPPFLAGS and LIBS

As I mentioned before, the configure script performs various tests to probe the target platform's feature set and properties. Some of the tests involve running the C preprocessor or compiler, some even the linker or loader. When building programs in the Posix Shell, you typically need to pass flags like -D_POSIX_SOURCE or -D_SOCKET_SOURCE to the compiler, to influence the visibility of certain declarations in the /usr/include header files. These header files use conditional compilation via #ifdef SOMEFLAG .. #else .. #endif quite extensively. You also might need to pass linker options like -lsocket for code using Berkeley Sockets, for example.

Fortunately you do not have to adjust the configure script for these special flags and options as it typically is designed in such a way that it picks up shell variables like CPPFLAGS or LIBS from the environment. So you basically setup and export these two variables before running the configure script. To make it more convenient and avoid forgetting it occasionally, you should consider adding these variable assignments to your .profile or some similar script. I actually prefer to trigger those setup steps explicitly, so I place them into a dotme file, which I execute via the shell's dot command. In case you haven't heard about that strange dot (.) command: By default, the shell executes scripts in a child process and such a child process cannot modify the parent's environment variables (like CPPFLAGS or LIBS). Unlike CI variables, which belong to the job or session, the Posix environment variables belong to a process. The dot command of the shell causes a script being parsed by the shell process itself, instead of creating a child shell. This allows the script to actually modify the shell process's environment variables.

 shell/iX> cat ~/mpe/dotme

 # dot (.) this file to setup some shell env vars

 defs="-D_POSIX_SOURCE -D_SOCKET_SOURCE"
 incs="-I$HOME/mpe/inc -I/usr/include/bsd"
 libs="-L$HOME/mpe/lib -lappel -lbsd -lsocket"

 export CPPFLAGS="$defs $incs"
 export LIBS="$libs"

 echo Setup done for CPPFLAGS and LIBS 

While we are discussing the CPPFLAGS and LIBS settings... Being somewhat lazy -ahem- being an engineer, I typically add the LibBSD library and include files from Jazz when starting a port, i.e. add -I/usr/include/bsd to CPPFLAGS and -lbsd to LIBS. The LibBSD library and include files provide a number of common Unix functions and declarations, and thus can help to reduce the effort needed to supply missing pieces. To gain some additional flexibility in this regard, I also add -I$HOME/mpe/inc and -L$HOME/mpe/lib -lappel as this allows me to add missing headers or library functions (or override inappropriate existing pieces) without having to modify the global files in /usr/include or /usr/lib and similar locations. As my libappel.a is usually empty in the beginning, I use a symbolic link to libbsd.a at first and replace it with my own library as needed.

 shell/iX> ln -s /usr/lib/libbsd.a ~/mpe/lib/libappel.a

 shell/iX> . ~/mpe/dotme
 Setup done for CPPFLAGS and LIBS
 
 shell/iX> echo $CPPFLAGS
 -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/include/bsd

 shell/iX> echo $LIBS
 -L/IX/PGP/mpe/lib -lappel -lbsd -lsocket

Checking the Makefile.in templates

As mentioned earlier, the final stage of the configure script generates Makefile(s) from Makefile.in template(s). This process includes running the /bin/sed stream editor to perform batch find-and-replace operations, for example to substitute placeholders like @LIBS@ by values determined during the configure tests. In some porting projects I have noticed that those Makefile.in template(s) may fail to "pick up" values for CPPFLAGS and LIBS properly, so the make phase might end up using slightly different compiler flags and library lists than the configure phase did.

So it might be a good idea to check the Makefile.in templates if their compile commands do have the templates @CPPFLAGS@ and @LIBS@ so that your environment variables will be picked up by the configure script and also passed on to the generated Makefile(s). Shell commands like "find * -name Makefile.in | xargs -l1 -t grep CC" can probably be helpful in that regard, as they help locating those Makefile.in template(s) that contain a compile command (which is typically using a $(CC) macro). This is only a "rule of thumb" and it can be more complex at times, as some Makefile(s) are built from multiple templates e.g. header section, body part, trailer section.

 shell/iX> pwd 
 /IX/PGP/pgp50i/src

 shell/iX> find * -name '*.in' | xargs grep -l CPPFLAGS
 config/pre.in
 configure.in

 shell/iX> find * -name '*.in' | xargs grep -l LIBS
 config/post.in
 config/pre.in
 configure.in
 lib/bn/Makefile.in
 lib/pgp/Makefile.in
 lib/simple/Makefile.in
 lib/ttyui/Makefile.in

 shell/iX> grep CPPFLAGS config/pre.in config/post.in
 config/pre.in:CFLAGS=   $(OPT) $(OSDEF) @CPPFLAGS@ @WARN@ @DEFS@ $(DEFINES) ...

 shell/iX> grep LIBS config/pre.in config/post.in
  ...
 config/pre.in:LIBS=     $(LIBTTYUI) $(LIBPGP) $(LIBBN) $(LIBSIMPLE) @LIBS@
  ...
 config/post.in: $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(LOCALLIBS)
  ...

The above snippets only give a small impression of the situation described earlier. In our case the Makefile(s) are combined from Makefile.in as well as pre.in and post.in template files. The latter two contain the sections where @CPPFLAGS@ and @LIBS@ are picked up to construct the $(CC) compile command with the appropriate flags and switches.

There is no simple generic rule how to check Makefile.in templates for this potential issue. Another approach might be to not try such a check at all and closely monitor the echo of the compile commands during the make phase, to verify that the desired options from your CPPFLAGS and LIBS settings are actually used.

When adjusting Makefile.in templates, remember that it might be helpful to keep old/new versions in ../mpe/old and ../mpe/new directories for documentation purposes and future reference (recalling the diff's).

Running the configure script

The configure script typically offers a number of options for customizing its behavior or the overall build process. Some of those options are fairly generic, others are specific to the freeware package and allow customizing features, default pathnames or similar. Running configure --help usually gives a list of available options and ReadMe or Install instructions typically give more details.

 shell/iX> configure --help

 Usage: configure [options] [host]
 Options: [defaults in brackets after descriptions]
 Configuration:
   --cache-file=FILE       cache test results in FILE
   --help                  print this message
   --no-create             do not create output files
   --quiet, --silent       do not print `checking...' messages
   --version               print the version of autoconf that created configure
 Directory and file names:
   --prefix=PREFIX         install architecture-independent files in PREFIX
                           [/usr/local]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
                           [same as prefix]
   --bindir=DIR            user executables in DIR [EPREFIX/bin]
   --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
   --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
   --datadir=DIR           read-only architecture-independent data in DIR
                           [PREFIX/share]
   --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data in DIR
                           [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
   --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
   --includedir=DIR        C header files in DIR [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
   --infodir=DIR           info documentation in DIR [PREFIX/info]
   --mandir=DIR            man documentation in DIR [PREFIX/man]
   --srcdir=DIR            find the sources in DIR [configure dir or ..]
   --program-prefix=PREFIX prepend PREFIX to installed program names
   --program-suffix=SUFFIX append SUFFIX to installed program names
   --program-transform-name=PROGRAM
                           run sed PROGRAM on installed program names
 Host type:
   --build=BUILD           configure for building on BUILD [BUILD=HOST]
   --host=HOST             configure for HOST [guessed]
   --target=TARGET         configure for TARGET [TARGET=HOST]
 Features and packages:
   --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --x-includes=DIR        X include files are in DIR
   --x-libraries=DIR       X library files are in DIR

One option that I use most frequently is --prefix to specify the desired target directory in which the package should be installed. the default is frequently something like /usr/local but I prefer to adjust it to the symbolic link created earlier -in our case /usr/local/pgp, which points to /IX/PGP- as this results in the files being placed in and below my own home group. This way I do not need special capabilities like SM for the build process, which would be required to place files in /usr/local/bin and the like.

It's a good idea to monitor the progress messages of the configure script, even if it runs for quite a while. You will probably not be able to understand all of them (don't be worried, it's the same for me) but at least try to get an idea what is going on and whether it rings a bell or sounds suspect. You might be able to detect potential porting problems in this early stage. The more porting experience you have, the more issues you might "smell" here. But don't be worried about the issues that go unnoticed at this stage... they will strike back at you at a later time during the port :-) If you do not want the progress messages to scroll off your screen buffer and get lost, you can use the shell's tee command to pipe a copy into a disc file... (the 2>&1 redirection makes sure that the pipe captures stdout as well as stderr messages).

 shell/iX> configure --prefix=/usr/local/pgp 2>&1 | tee /tmp/my.log

 creating cache ./config.cache
 checking For C compiler... gcc
 checking whether we are using GNU CC... yes
 checking whether gcc accepts -g... yes
 checking for useful warning options ($WARN)... 
  -Wall -W -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings
 checking for socket in -lsocket... yes
 checking for gethostbyname in -lnsl... no
 checking for exp in -lm... yes
 checking for internal RSA encrypt/sign support... yes
 checking for lib/pgp/pubkey/pgpRSAKeyGen.c... yes
 checking for the Full License... yes
 checking how to run the C preprocessor... gcc -E
 checking how to make dependencies... 
  gcc -E -M -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/include/bsd
 checking for a BSD compatible install... config/install-sh -c
 checking for ranlib... :
 checking whether ln -s works... yes
 checking how to test for symlinks... test -L
 checking for ANSI C header files... yes
 checking for fcntl.h... yes
 checking for limits.h... yes
 checking for stdarg.h... yes
 checking for stdlib.h... yes
 checking for unistd.h... yes
 checking for sys/ioctl.h... yes
 checking for sys/time.h... yes
 checking for sys/timeb.h... no
 checking for sys/param.h... no
 checking for arpa/inet.h... no
 checking for sys/stat.h... yes
 checking whether time.h and sys/time.h may both be included... yes
 checking for working const... yes
 checking for off_t... yes
 checking for size_t... yes
 checking for gethrtime... no
 checking for clock_gettime... no
 checking for clock_getres... no
 checking for gettimeofday... yes
 checking for getitimer... no
 checking for setitimer... no
 checking for ftime... no
 checking for mkstemp... yes
 updating cache ./config.cache
 creating ./config.status
 Using ./config/pre.in for pre-makefile
 Using ./config/post.in for post-makefile
 creating Makefile
 creating lib/Makefile
 creating lib/bn/Makefile
 creating lib/pgp/Makefile
 creating lib/pgp/include/Makefile
 creating lib/pgp/helper/Makefile
 ...
 creating man/Makefile
 creating plugins/Makefile
 creating config.h

If the configure script shows suspect progress messages or even complains or fails with errors, you might find some useful details in the config.log file. If that does not help you can always look into the configure script itself, to find out what it tried to achieve and what went wrong. Remember, the configure script is mainly a big sequence of shell script building blocks that implement the various tests.

During the configure phase of our PGP example you might probably notice that it checks for sys/param.h or arpa/inet.h header files. An issue related to these files will show up later during the port and demonstrate that configure does handle a fairly large number of platform specifics but may also fail in some cases. You might also notice that the config.status script reports that it picks up the additional pre.in and post.in templates to generate the Makefile(s).

Running make and make install

Some freeware packages have a single Makefile that performs the build, but many packages also have a collection of Makefile(s) that call each other in a nested fashion. In the latter case, the source files are usually arranged in several subdirectories, e.g. for different modules or components of the package, and each subdirectory in the tree has its own Makefile. The "master" Makefile enters those different directories and executes the nested make to build the whole tree.

Sometimes it can be useful to try make -n to just preview the actions that make will perform, but especially with nested or chained Makefile trees this typically will fail at some point because later stages of the build might require files (e.g. libraries) that will be built by earlier stages of the make execution. So another approach might be to simply start the make and see what happens. It might be a good idea to use something like "make 2>&1 | tee /tmp/make.out" to capture a copy of the make progress messages into a temporary file for later reference or analysis. A typical make will run for quite a while and produce lots of output. This could scroll off your screen or buffer easily. And by capturing it in a file you can also walk away to get some more coffee during the build...

 shell/iX> make 2>&1 | tee /tmp/my.log

 touch .incs
 mkdir include
 make  headers
 make[1]: Entering directory `/IX/PGP/pgp50i/src'
 ./mklang.pl
 Making headers in lib
 make[2]: Entering directory `/IX/PGP/pgp50i/src/lib'
 Making headers in lib/bn
 make[3]: Entering directory `/IX/PGP/pgp50i/src/lib/bn'
 Linking bn.h bnprime.h bngermain.h to public include
 :
 :
 Making headers in plugins
 make[2]: Entering directory `/IX/PGP/pgp50i/src/plugins'
 make[2]: Leaving directory `/IX/PGP/pgp50i/src/plugins'
 make[1]: Leaving directory `/IX/PGP/pgp50i/src'
 Making all in lib
 make[1]: Entering directory `/IX/PGP/pgp50i/src/lib'
 Making all in lib/bn
 make[2]: Entering directory `/IX/PGP/pgp50i/src/lib/bn'
 gcc -O -DUNIX=1 -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/includ
 e/bsd -Wall -W -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings -DH
 AVE_CONFIG_H -DPGPTRUSTMODEL=0 -DDEBUG=1 -DUNFINISHED_CODE_ALLOWED=0    -I../../
 . -I../.././include -I../include -I. -I.   -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/I
 X/PGP/mpe/inc -I/usr/include/bsd  -c bn.c -o bn.o
 gcc -O -DUNIX=1 -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/includ
 e/bsd -Wall -W -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings -DH
 AVE_CONFIG_H -DPGPTRUSTMODEL=0 -DDEBUG=1 -DUNFINISHED_CODE_ALLOWED=0    -I../../
 . -I../.././include -I../include -I. -I.   -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/I
 X/PGP/mpe/inc -I/usr/include/bsd  -c bnimem.c -o bnimem.o
 :
 :
 gcc -O -DUNIX=1 -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/includ
 e/bsd -Wall -W -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings -DH
 AVE_CONFIG_H -DPGPTRUSTMODEL=0 -DDEBUG=1 -DUNFINISHED_CODE_ALLOWED=0    -I../../
 . -I../.././include -I../include -I. -I.   -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/I
 X/PGP/mpe/inc -I/usr/include/bsd  -c bni00.c -o bni00.o
 + echo bn.o bnimem.o bnsieve.o bnprime.o bngermain.o bnjacobi.o bnprint.o bnlega
 l.o bn00.o bni00.o 
 1> DONE 
 rm -f libbn.a
 ar cru libbn.a `cat DONE`
  
 10 OBJECT FILES HAVE BEEN ADDED.
  
 : libbn.a
 rm -f ../libbn.a
 ln -s ../lib/bn/libbn.a ../
 make[2]: Leaving directory `/IX/PGP/pgp50i/src/lib/bn'
 Making all in lib/pgp
 make[2]: Entering directory `/IX/PGP/pgp50i/src/lib/pgp'
 :
 :

In many cases, there will be a few compile warnings during the build. Some might be unimportant, others might indicate potential porting issues. During the PGP build you might see implicit declaration warnings for gettimeofday(), select(), strncasecmp() or sbrk(), for example. Being a lazy guy, I sometimes defer investigating all those warnings and first see if the build completes and the program files run (e.g. without unresolved externals). The warnings might indicate only minor issues or even cosmetic aspects of declarations, and I prefer to first spend time on really bad problems like missing library routines or runtime aborts. But you might prefer the more scientific approach to carefully check out the reason of each and every warning before proceeding. It's your choice.

Just be prepared that the initial build frequently does not complete successfully. Sometimes it will fail with only minor issues, sometimes it will throw pretty difficult riddles at you. Don't despair too quickly. Fixing those issues or working around them is probably the meat and potatoes of porting. And over time you will develop an arsenal of tricks or solutions that help in the typical situations. Many of them may even come from exchanging ideas with other people. Remember that porting does not need to be a one-man-show.

Issue 1: Missing include file machine/param.h

As I mentioned earlier, the configure script performs various tests to get an idea about existing header files and their exact location on the respective target system. So you might be surprised to encounter a compile error regarding a missing include file machine/param.h during the build. It sometimes happens that source code does not use the configure test results throughly enough, in other cases it also happens that the MPE/iX or LibBSD include files are slightly incomplete.

 ...
 Making all in apps/pgp
 make[2]: Entering directory `/IX/PGP/pgp50i/src/apps/pgp'
 gcc -O -DUNIX=1 -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/includ
 e/bsd -Wall -W -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings -DH
 AVE_CONFIG_H -DPGPTRUSTMODEL=0 -DDEBUG=1 -DUNFINISHED_CODE_ALLOWED=0    -I../../
 . -I../.././include -I../include -I. -I../common -I./../common   -D_POSIX_SOURCE
  -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/include/bsd  -c pgp.c -o pgp.o
 In file included from pgp.c:26:
 /usr/include/bsd/sys/param.h:28: machine/param.h: No such file or directory
 gcc: Internal compiler error: program cpp got fatal signal 33
 make[2]: *** [pgp.o] Error 1
 make[2]: Leaving directory `/IX/PGP/pgp50i/src/apps/pgp'
 make[1]: *** [all] Error 1
 make[1]: Leaving directory `/IX/PGP/pgp50i/src/apps'
 make: *** [all] Error 1
 shell/iX> 

Looking at our problem here shows that the bsd/sys/param.h file tries to include a machine/param.h file, but that is missing. It should not have happened this way, because config.h has a flag indicating the absence of the respective file (via some #define HAVE_SYS_PARAM_H 0) and the offending #include statement is covered by a flag test, but unfortunately an #ifdef was used instead of #if by accident. This results in the conditional compilation testing for existence of the flag symbol instead of nonzero value.

 shell/iX> more apps/pgp/pgp.c
 ...
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif 

 #include <stdio.h>
 #include <string.h>
 #include <time.h>

 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
 ...

 shell/iX> grep PARAM config.h
 #define HAVE_SYS_PARAM_H 0

A clean fix would be to correct the source file (and report back to the original authors), but sometimes I prefer to keep changes to source files to a minimum (as these changes would have to be reapplied when downloading a new version of the sources). So an alternate solution to this problem is to create an empty machine/param.h file in ~/mpe/inc via mkdir and touch. This prevents the compiler from complaining about the missing include file for now. The empty file should also be helpful in finding out which declarations or function prototypes it was referenced for, as the compiler should emit warnings about missing pieces as soon as we resume the build. Notice that make will typically resume where it left off, but sometimes adding a missing include file might need configure to be rerun as some of the tests might have recorded the absence of the respective header file in a config.cache line.

 shell/iX> mkdir ~/mpe/inc/machine
 shell/iX> touch ~/mpe/inc/machine/param.h

 shell/iX> make 2>&1 | tee -a /tmp/my.log
 Making all in lib
 make[1]: Entering directory `/IX/PGP/pgp50i/src/lib'
 Making all in lib/bn
 make[2]: Entering directory `/IX/PGP/pgp50i/src/lib/bn'
 make[2]: Leaving directory `/IX/PGP/pgp50i/src/lib/bn'
 :
 :
 Making all in apps/pgp
 make[2]: Entering directory `/IX/PGP/pgp50i/src/apps/pgp'
 gcc -O -DUNIX=1 -D_POSIX_SOURCE -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/includ
 e/bsd -Wall -W -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings -DH
 AVE_CONFIG_H -DPGPTRUSTMODEL=0 -DDEBUG=1 -DUNFINISHED_CODE_ALLOWED=0    -I../../
 . -I../.././include -I../include -I. -I../common -I./../common   -D_POSIX_SOURCE
  -D_SOCKET_SOURCE -I/IX/PGP/mpe/inc -I/usr/include/bsd  -c pgp.c -o pgp.o
 :
 :

Looking at the above trick to supply a missing header file (in our case by simply creating an empty stub) you can probably get the idea why we added the -I$HOME/mpe/inc compiler option to the CPPFLAGS earlier. This additional include directory does not only give us a chance to supply missing pieces, we could also use it to override files in /usr/include with "customized" versions.

Issue 2: Missing Unix/Posix routine tcflush()

After the make process completes successfully, it can be useful to test the resulting program files for unresolved externals. This step is not required, as the affected program files will catch your attention sooner or later, but I prefer to get an idea about missing pieces in some or all of the program files as soon as possible. A command like "run ./myprog ;stdin=oops" can be quite helpful for this test as it will either fail with an unresolved externals error or abort complaining about the missing oops file. The latter trick is simply to prevent the program to actually run, if all is OK (as it might expect certain parameters or input files to function properly).

 shell/iX> mpe

 :run ./apps/pgp/pgp ;stdin=oops
 UNRESOLVED EXTERNALS: tcflush  (LDRERR 512)
 Failed to locate the exported code symbol(s) for the imported code symbol(s)
 listed above. (LDRERR 522)
 Native mode loader message 522
 Unable to load program to be run. (CIERR 625)

 :run ./apps/pgpk/pgpk ;stdin=oops
 UNRESOLVED EXTERNALS: tcflush  (LDRERR 512)
 Failed to locate the exported code symbol(s) for the imported code symbol(s)
 listed above. (LDRERR 522)
 Native mode loader message 522
 Unable to load program to be run. (CIERR 625)

 :exit
 shell/iX>

In the PGP example we notice that a tcflush() routine is missing. It is not even available from the LibBSD library that we used in addition to the regular MPE/iX libraries. Looking it up in common Unix documentation (the web can be helpful at times), it seems to be related to flushing terminal I/O buffers, depending on certain parameters. The shell command "find * -name '*.c' | xargs grep -l tcflush" can be quite helpful to locate source files that reference the tcflush() routine. In our case we have such references in lib/ttyui/pgpKBUnix.c and it seems that the tcflush() routine is used to kbFlush() the keyboard typeahead buffer or somesuch. Some #ifdef indicates that ioctl() might be a potential alternate implementation, but that also fails to work on MPE/iX.

 shell/iX> pwd
 /IX/PGP/pgp50i/src
 
 shell/iX> find * -name '*.c' | xargs grep -F tcflush
 lib/ttyui/pgpKBUnix.c:  tcflush(kbFd, TCIFLUSH);

 shell/iX> more lib/ttyui/pgpKBUnix.c

 ...
 /*
  * Flush any pending input. If "thorough" is set, tries to be more
  * thorough about it. Ideally, wait for 1 second of quiet, but we
  * may do something more primitive.
  *
  * kbCbreak() has the side effect of flushing the inout queue, so this
  * is not too critical.
  */
 void
 kbFlush(int thorough)
 {
         if (thorough)
                 sleep(1);
 #if defined(TCIFLUSH)
         tcflush(kbFd, TCIFLUSH);
 #elif defined(TIOCFLUSH)
 #ifndef FREAD
 #define FREAD 1 /* The usual value */
 #endif
         ioctl(kbFd, TIOCFLUSH, FREAD);
 #endif
 }
 ...

A clean fix for this issue would be to implement a small tcflush() routine, e.g. based on the FDEVICECONTROL intrinsic, and make it available via the libappel.a library that we have already placed into the compile and link options via CPPFLAGS and LIBS. But being lazy, a closer look at the sources seems to indicate that the keyboard buffer is only flushed because the time between keystrokes is used to seed some random number generator. So the quick and dirty approach is to supply a tcflush() stub in libappel.a that is essentially a NOP (no operation) dummy, and avoid running the programs with typeahead enabled. By calling the PRINTOP intrinsic inside our tcflush() stub we could have the program remind us of the dirty trick. By calling PRINTOPREPLY, we might even be able to stop the program to locate/invest the runtime context where our stub is actually called.

 shell/iX> cd ~/mpe/src

 shell/iX> ls -l

 -rw-r-----   1 PGP.IX    IX           204 May 30 16:45 Makefile
 -rw-r-----   1 PGP.IX    IX           208 May 30 16:42 tcflush.c

 shell/iX> more tcflush.c

 #pragma intrinsic PRINTOP
 int tcflush(int fd, int flags)
 {
   PRINTOP("PGP cheating tcflush() via stub",-31,0);
   return 0;
 }
 #ifdef TESTMAIN
 main()
 {
   tcflush(0,0);
 }
 #endif

 shell/iX> more Makefile

 libappel.a:     tcflush.o
         ar rcv libappel.a tcflush.o

 tcflush.o:      tcflush.c
         c89 -c -D_POSIX_SOURCE tcflush.c

 install:
         cp libappel.a ../lib/libappel.a

 clean:
         rm -f *.o libappel.a

 shell/iX> rm ~/mpe/lib/libappel.a

 shell/iX> make
 c89 -c -D_POSIX_SOURCE tcflush.c
 ar rcv libappel.a tcflush.o
 
 a - /IX/PGP/mpe/src/tcflush.o
 1 OBJECT FILE HAS BEEN ADDED.
 
 shell/iX> make install
 cp libappel.a ../lib/libappel.a

Now you can also see why the ~/mpe/src directory and ~/mpe/lib/libappel.a were created earlier. The reason for the quick and dirty stub for tcflush() above is mainly my impatience... No, wait, I meant to phrase this differently... We are only creating the tcflush() workaround at this point in time to see what other porting issues we will be facing later on. It would not make sense to spend a lot of time implementing a nice tcflush() routine, if we'd encounter a major showstopper later in the port. So let's first get a working version, even if we have to take a few documented restrictions initially. We can improve that later, after we have a "proof of concept" version running.

So purge the pgp and pgpk program files and trigger another build to have it pick up our new tcflush() stub/workaround routine. This time the test for unresolved externals completes OK and we can use make install to place the necessary files into their respective target directories (in our case /usr/local/pgp and below as requested by configure --prefix earlier). A minor issue shows up as make install seems to fail if the /usr/local/pgp/bin directory does not yet exist, so we create it by hand using mkdir and then rerun make install successfully.

 shell/iX> cd ~/pgp50i/src

 shell/iX> rm apps/pgp/pgp apps/pgpk/pgpk

 shell/iX> make
 ...
 Making all in apps/pgp
 make[2]: Entering directory `/IX/PGP/pgp50i/src/apps/pgp'
 gcc -L../.././lib -o pgp ../common/pgpAppFile.o ../common/pgpExit.o ../common/pg
 pKeyRings.o ../common/pgpInitApp.o ../common/pgpOpt.o ../common/pgpFullLicense.o
  pgp.o parsearg.o filter.o -lpgptty -lpgp -lbn -lsimple -lm -lsocket -L/IX/PGP/m
 pe/lib -lappel -lbsd -lsocket  
 
 make[2]: Leaving directory `/IX/PGP/pgp50i/src/apps/pgp'
 Making all in apps/pgpk
 make[2]: Entering directory `/IX/PGP/pgp50i/src/apps/pgpk'
 gcc -L../.././lib -o pgpk ../common/pgpOpt.o ../common/pgpExit.o ../common/pgpIn
 itApp.o ../common/pgpAppFile.o ../common/pgpKeyRings.o ../common/pgpFullLicense.
 o keyserver.o pgpk.o pgpkUI.o pgpkKeyGen.o url.o -lpgptty -lpgp -lbn -lsimple -l
 m -lsocket -L/IX/PGP/mpe/lib -lappel -lbsd -lsocket  
 
 make[2]: Leaving directory `/IX/PGP/pgp50i/src/apps/pgpk'
 make[1]: Leaving directory `/IX/PGP/pgp50i/src/apps'
 ...

 shell/iX> callci ci,2

 :run ./apps/pgp/pgp ;stdin=oops
 Couldn't open specified $STDIN for program. (CIERR 684)
 :run ./apps/pgpk/pgpk ;stdin=oops
 Couldn't open specified $STDIN for program. (CIERR 684)

 :exit

 shell/iX> make install

 [ big snip here ]

 shell/iX> cd

 shell/iX> whence pgpk
 /IX/PGP/bin/pgpk

 shell/iX> whence pgp
 /IX/PGP/bin/pgp

Issue 3: Program abort during exitCleanup()

From looking at the man pages, it seems that pgpk --version and pgp (with no arguments) make a nice initial test for the compiled programs, as they perform a pretty simple function. The expected action seems to succeed but unfortunately, both programs show some strange kind of abort afterwards.

 shell/iX> pgpk --version

 Creating /IX/PGP/.pgp...complete.
 No randseed file found.
 Cannot open configuration file /IX/PGP/.pgp/pgp.cfg

 PGP for Personal Privacy Version:  unix50i1b
 [1] + Done(134) pgpk --version
   4390989       Abort   pgpk

 shell/iX> pgp

 Cannot open configuration file /IX/PGP/.pgp/pgp.cfg

 PGP is now invoked from different executables for different operations:

 pgpe    Encrypt (including Encrypt/Sign)
 pgps    Sign
 pgpv    Verify/Decrypt
 pgpk    Key management
 pgpo    PGP 2.6.2 command-line simulator (not yet implemented)

 See each application's respective man page or the general PGP documentation
 for more information.
 [1] + Done(134) pgp
   2818139       Abort   pgp

By examining the source code, it turns out that an exitWipe() routine in pgp[k]Exit.c tries to erase the stack during program cleanup. It seems to do this to make sure sensitive encryption data does not stay around in RAM for too long. Unfortunately the exitWipe() routine assumes that the stack is allocated downwards, i.e. starting at high memory addresses and growing towards lower memory addresses. This assumption does not work for PA-RISC systems like MPE/iX, which allocate stack frames in just the opposite direction. One option to fix this problem is to adjust the exitWipe() routine to handle PA-RISC style of stack growth, but an even simpler approach is to disable the stack filling in the common/pgpInitApp.c file (which is used by the pgp and pgpk programs). Disabling the stack wiping should not be too much of a problem as MPE/iX does allocate virgin pages to new processes.

 shell/iX> cd pgp50i/src

 shell/iX> more apps/common/pgpExit.c
 ...
 static void
 exitWipe1(int code, void *base)
 {
         /* Wipe the stack */
         if (stack0)
                 memset(base, 0, ((char *)(stack0)-(char *)base));

 #if 0   /* Disabled until stdio problems can be resolved */
         /* Wipe the heap */
         if (heap0) {
                 base = sbrk(0);
                 memset(heap0, 0, (char *)base-(char *)heap0);
         }
 #endif
 ...

 shell/iX> diff -u ~/mpe/old/pgpInitApp.c apps/common/pgpInitApp.c

 --- /IX/PGP/mpe/old/pgpInitApp.c        Sat Feb  7 20:54:56 1998
 +++ apps/common/pgpInitApp.c    Sun May 30 17:06:03 1999
 @@ -74,7 +74,11 @@
                 exitCleanup (PGPEXIT_NOMEM);
  
         /* setup the exit routines */
 +#ifdef mpeix
 +       exitSetup (env, NULL, NULL, exit_prog, newvers);
 +#else
         exitSetup (env, stacktop, heaptop, exit_prog, newvers);
 +#endif
  
         pgpSetDefaultPrefsInternal50Tmp(env);

Another make builds updated versions of the pgp and pgpk program files. They can be installed with make install or manual cp, the latter being somewhat faster as only two files have to actually be updated in /usr/local/pgp. The new versions of the program files no longer show the previous abort behavior.

 shell/iX> pgpk --version
 Cannot open configuration file /IX/PGP/.pgp/pgp.cfg

 PGP for Personal Privacy Version:  unix50i1b

 shell/iX> pgp  
 Cannot open configuration file /IX/PGP/.pgp/pgp.cfg

 PGP is now invoked from different executables for different operations:

 pgpe    Encrypt (including Encrypt/Sign)
 pgps    Sign
 pgpv    Verify/Decrypt
 pgpk    Key management
 pgpo    PGP 2.6.2 command-line simulator (not yet implemented)

 See each application's respective man page or the general PGP documentation
 for more information.

Issue 4: Program hangs in keyboard select()

After the above fix, program tests like pgpk -a, pgpk -l, pgpk -x, or pgpe -a -t -r look promising so far. However, when trying pgpk -g to generate a private/public key pair, the program seems to hang until Break and :Abort are used. Using the MPE/iX system debugger from another session is quite helpful to get a stack trace before aborting the hung program instance in the primary session.

 shell/iX> pgpk -g
 Cannot open configuration file /IX/PGP/.pgp/pgp.cfg

 Choose the type of your public key:
   1)  DSS/Diffie-Hellman - New algorithm for 5.0 (default)
   2)  RSA
 Choose 1 or 2: 1

 Pick your public/private keypair key size:
 (Sizes are Diffie-Hellman/DSS; Read the user's guide for more information)
  1)   768/768  bits- Commercial grade, probably not currently breakable
  2)  1024/1024 bits- High commercial grade, secure for many years
  3)  2048/1024 bits- "Military" grade, secure for forseeable future(default)
  4)  3072/1024 bits- Archival grade, slow, highest security
 Choose 1, 2, 3 or 4, or enter desired number of Diffie-Hellman bits
 (768 - 4096): 1


 You need a user ID for your public key.  The desired form for this
 user ID is your FULL name, followed by your E-mail address enclosed in
 <angle brackets>, if you have an E-mail address.  For example:
   Joe Smith <user@domain.com>
 If you violate this standard, you will lose much of the benefits of
 PGP 5.0's keyserver and email integration.

 Enter a user ID for your public key: Test Only <lars_appel@hp.com>

 Enter the validity period of your key in days from 0 - 999
 0 is forever (and the default): 7

 You need a pass phrase to protect your private key(s).
 Your pass phrase can be any sentence or phrase and may have many
 words, spaces, punctuation, or any other printable characters.
 Enter pass phrase: 
 Enter again, for confirmation:
 Enter pass phrase: 
 Collecting randomness for key...

 We need to generate 856 random bits.  This is done by measuring the
 time intervals between your keystrokes.  Please enter some random text
 on your keyboard until you hear the beep:
  856:

At this point the primary session hangs. We do not get any response when typing. So we debug from a second session.

Using "run ./pgpk ;debug" can be used to launch the debugger in such a way that it knows about program symbols (similar to using :debug and then some form of the pseudomap command). The stack trace of the hung process is obtained via "pin #xxx; tr,i,d; abort" then, where xxx is the decimal PIN number obtained by "showproc job=" and the abort command inside the debugger prevents the separate program instance to actually launch (as it was just used to get access to the symbol tables of the program file).

 :hello lars,manager.sys

 :showproc job=pgp.ix
 QPRI  CPUTIME   STATE  JOBNUM  PIN  (PROGRAM) STEP

 C152  0:00.448  WAIT   S3      62   :SH
 C152  0:08.693  WAIT   S3        70   (SH.HPBIN.SYS) -L
 C200  0:02.039  WAIT   S3          81   (/IX/PGP/bin/pgpk)
 
 :run /IX/PGP/bin/pgpk ;debug

 DEBUG/iX C.05.08 

 DEBUG Intrinsic at: 1c8.00045b7c ?$START$
 $1 ($4b) nmdebug > pin #81
 $2 ($51) nmdebug > trace ,i,d
       PC=a.0017670c enable_int+$2c
 NM* 0) SP=4183a478 RP=a.0109e534 notify_dispatcher.block_current_process+$324
 NM  1) SP=4183a478 RP=a.010a09bc notify_dispatcher+$264
 NM  2) SP=4183a3f8 RP=a.001a6c20 wait_for_active_port+$ec
 NM  3) SP=4183a2f8 RP=a.001a78a0 receive_from_port+$534
 NM  4) SP=4183a278 RP=a.0034c168 selective_receive+$3f0
 NM  5) SP=4183a078 RP=a.00316b50 hpselect_nm+$260
 NM  6) SP=41839f38 RP=a.003168bc ?hpselect_nm+$8
          export stub: a.00261628 HPSELECT+$f28
 NM  7) SP=41839bb8 RP=a.002606ec ?HPSELECT+$8
          export stub: 1c8.000f2028 select+$3b8
 NM  8) SP=41839638 RP=1c8.00051978 $CODE$+$218
 NM  9) SP=41839438 RP=1c8.0004f758 doKeyGenerate+$1e0
 NM  a) SP=41839338 RP=1c8.0004c644 $CODE$+$144
 NM  b) SP=41839078 RP=1c8.0004ca60 appMain+$360
 NM  c) SP=41839038 RP=1c8.000468f0 main+$30
 NM  d) SP=41838f38 RP=1c8.000fb238 _start+$bc
 NM  e) SP=41838ef8 RP=1c8.00045bb4 $START$+$1c
 NM  f) SP=41837d78 RP=1c8.00000000 
      (end of NM stack)
 $3 ($51) nmdebug > abort

 END OF PROGRAM
 :

The symbolic information in the stack trace helps to understand the context of the program hang. It shows that a select() call seems to hang -most probably waiting on keyboard input- somewhere inside a doKeyGenerate routine for seeding some random numbers code. The symbolic names can be used to locate the appropriate source code for more details, the "find * -name '*.c' | xargs grep -l xxx" shell command (pipe) was already discussed earlier, here we can use the doKeyGenerate string or a wildcard expression like "select *(" for the grep, the latter searching for occurrences of the word select followed by an open parenthesis and zero or more blanks in between (which helps to reduce the grep hits significantly, compared to a simple grep for the word select).

shell/iX> cd pgp50i/src

shell/iX> find * -name '*.c' | xargs fgrep -l doKeyGenerate

apps/pgpk/pgpk.c
apps/pgpk/pgpkKeyGen.c
apps/pgpk.old/pgpk.c

shell/iX> find * -name '*.c' | xargs grep 'select *('

lib/ttyui/pgpUserIO.c:              SelectReturn = select(fdDevRandom + 1, 
lib/ttyui/pgpUserIO.c:          select(LargestFD + 1, &AllDevicesSet, NULL, NULL, NULL);

It turns out that the randomizing is done by reading user input and evaluating the times between keystrokes. Unfortunately MPE/iX does have a problem with select() on terminals (see documentation or Mark Bixby's porting guide or the Samba/iX notes for limitations in the smbclient program, for example). But don't despair, a closer look at the source code reveals a viable workaround by faking a -1 return value in place of the original select() call in lib/ttyui/pgpUserIO.c as the select() was only used to wait for a keystroke and then read() the typed character. By skipping the select() call the read() will block until a character is received.

 shell/iX> more ~/mpe/old/pgpUserIO.c
 ...
            kbFlush (0); /* Typeahead is illegal */

            SelectReturn =
                select(LargestFD + 1, &AllDevicesSet, NULL, NULL, NULL);

            /*If the waiting input is on stdin, or there was some kind of
             *error, grab input from the keyboard:
             */
            if(SelectReturn < 0 ||
               (SelectReturn >= 0 && FD_ISSET(fdKB, &AllDevicesSet))) {
                   (void)kbGet (); /* Wait for next char */
            }
 ...

 shell/iX> diff -u ~/mpe/old/pgpUserIO.c lib/ttyui/pgpUserIO.c

 --- /IX/PGP/mpe/old/pgpUserIO.c Sun May 30 18:58:49 1999
 +++ lib/ttyui/pgpUserIO.c       Sun May 30 18:59:31 1999
 @@ -415,7 +415,11 @@
             kbFlush (0); /* Typeahead is illegal */
  
             SelectReturn =
 +#ifdef mpeix
 +                -1;
 +#else
                 select(LargestFD + 1, &AllDevicesSet, NULL, NULL, NULL);
 +#endif
 
             /*If the waiting input is on stdin, or there was some kind of
              *error, grab input from the keyboard:

Another make and make install (or manual cp) can be used to build an updated version of the program files. The new version works much better for pgpk -g and also allows pgps -u quite successfully. At this stage, the port seems to already be quite usable. Time to be happy. Testing locally is one approach, but it also can be helpful to find someone else with a non-MPE version of PGP to exchange a few files or messages for testing cross-platform interoperability as well. Testing against other platforms is not just a good idea for PGP; it can be also reasonable for many client/server packages, especially when you have access to a working version on another platform already.

Issue 5: Failure to open /dev/null

Further testing reveals that pgpv fails to open /dev/null under certain conditions (trailing blanks lines in *.asc input files?). The program does not abort, it just displays an error message in this regard. Locating the origin of this error message in the source code to get an idea about the root cause can be quite tricky, depending on the complexity of the sources and how familiar you are with its architecture.

 shell/iX> cat test.asc

 Hi there, below is some signed text part...

 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1

 Hi there
 this is a small test message
 :-) Lars

 -----BEGIN PGP SIGNATURE-----
 Version: PGPfreeware 5.0i for non-commercial use
 Charset: noconv

 iQA/AwUBN1F1gikAsuX82/VgEQLndwCfZ0RTqVEhxDAHNJBvy86SW/ipKI4AoOvP
 Sxvkh1oaEAD/2YdMpdcK8kyt
 =Kjy0
 -----END PGP SIGNATURE-----

 Cheerio, Lars.

 shell/iX> cat test.asc | pgpv

 No files specified.  Using stdin.

 Opening file "/dev/null" type text.
 Opening file "stdout" type binary.
 Hi there
 this is a small test message
 :-) Lars
 Good signature made 1999-05-30 17:29 GMT by key:
    768 bits, Key ID FCDBF560, Created 1999-05-30
    "Test Only <lars_appel@hp.com>"
 Opening file "/dev/null" type text.
 Cannot open file

 shell/iX> 

Using the "run ./pgpk ;debug" trick from above and setting a breakpoint via "b nmaddr('open')" can be quite helpful in this regard. But such an approach may encounter two major problems: (a) the program might rely on being run from the Posix shell and expect certain shell variables, for example, and (b) the program might use fork() to create child processes behind your back, which in turn would not stop at the breakpoint set above. Thus, a slight variation of debugging might be helpful... Use the "run ./pgpk ;debug" from another session and then "b nmaddr('open'):@" to set the breakpoint for all (potential) processes. Now you can run pgpk from the Posix Shell and have it (and all potential children) stop at the breakpoint. When finished debugging, simply return to the "run;debug" session, delete the breakpoint(s) with "bd 1:@" and "abort" to avoid actually executing the pgpk program in that session.

First, we arm the system-wide breakpoint (in the user program code) from a PM session like MANAGER.SYS...

 :hello lars,manager.sys
 
 :run /IX/PGP/bin/pgp ;debug

 DEBUG/iX C.05.08 

 DEBUG Intrinsic at: 51d.00044b7c ?$START$
 $1 ($5b) nmdebug > b nmaddr('open'):@,,,{dv arg0,4,b}
 added: NM   @[1] PROG 51d.00103404 open

With above breakpoint armed (to auto-display data from the first function parm), we now return to the primary session...

 shell/iX> cat test.asc | pgpv

 DEBUG/iX C.05.08 

 Break at: NM   @[1] PROG 51d.00103404 open
 VIRT $7d4.41b4025c $ 2f49582f 5047502f 2e706770 2f72616e  /IX/ PGP/ .pgp /ran
 $1 ($4a) nmdebug > c
 Break at: NM   @[1] PROG 51d.00103404 open
 VIRT $7d4.41b4025c $ 2f757372 2f6c6f63 616c2f70 67702f6c  /usr /loc al/p gp/l
 $2 ($4a) nmdebug > c
 :
 :
 No files specified.  Using stdin.
 Opening file "/dev/null" type text.
 Break at: NM   @[1] PROG 51d.00103404 open
 VIRT $7d4.4695fc98 $ 2f646576 2f6e756c 6c000000 7374646f  /dev /nul l... stdo
 :
 :
 $9 ($4a) nmdebug > c
 Opening file "stdout" type binary.
 Hi there
 this is a small test message
 :-) Lars
 Good signature made 1999-05-30 17:29 GMT by key:
    768 bits, Key ID FCDBF560, Created 1999-05-30
    "Test Only <lars_appel@hp.com>"
 Opening file "/dev/null" type text.
 Break at: NM   @[1] PROG 51d.00103404 open
 VIRT $7d4.41839900 $ 2f646576 2f6e7500 00000000 00000074  /dev /nu. .... ...t
 $a ($4a) nmdebug > trace
      PC=51d.00103404 open
 * 0) SP=41839a38 RP=51d.000fc78c _endopen+$f4
   1) SP=41839a38 RP=51d.000fc640 fopen+$28
   2) SP=418399b8 RP=51d.000634dc $CODE$+$34c
   3) SP=41839978 RP=51d.0006317c $CODE$+$30c
        export stub: 51d.0004b6b8 $CODE$+$4c8
   4) SP=418398f8 RP=51d.0004b1ac ?pgpTtyNewOutput+$8
        export stub: 51d.0009b364 Write+$30c
   5) SP=41839838 RP=51d.0009b044 $CODE$+$204
        export stub: 51d.0008fc28 writeExtraData+$120
   6) SP=418397b8 RP=51d.0009135c $CODE$+$68c
   7) SP=41839738 RP=51d.00090cbc $CODE$+$16c
        export stub: 51d.00064448 pgpFileReadPump+$230
   8) SP=418396b8 RP=51d.00047e2c mainProcessFlags+$56c
   9) SP=41839638 RP=51d.00048588 appMain+$440
   a) SP=41839178 RP=51d.00046970 main+$30
   b) SP=41839078 RP=51d.000fd37c _start+$bc
   c) SP=41839038 RP=51d.00044bb4 $START$+$1c
   d) SP=41837eb8 RP=51d.00000000 
      (end of NM stack)
 $b ($4a) nmdebug > c
 Cannot open file
 Break at: NM   @[1] PROG 51d.00103404 open
 VIRT $7d4.4697715c $ 545a5441 422e4c49 422e5359 53000000  TZTA B.LI B.SY S...
 $c ($4a) nmdebug > c
 Break at: NM   @[1] PROG 51d.00103404 open
 VIRT $7d4.46977170 $ 2f535953 2f4c4942 2f545a54 41420000  /SYS /LIB /TZT AB..
 $d ($4a) nmdebug > c
 Break at: NM   @[1] PROG 51d.00103404 open
 VIRT $7d4.41b402b4 $ 2f49582f 5047502f 2e706770 2f72616e  /IX/ PGP/ .pgp /ran
 $e ($4a) nmdebug > c

 shell/iX>

Back to the Debug/iX session to remove the system-wide program breakpoint again...

 $2 ($5b) nmdebug > bd 1:@
 deleted: NM   @[1] PROG 51d.00103404 open
        cmdlist: {dv arg0,4,b}
 $3 ($5b) nmdebug > abort
 
 END OF PROGRAM
 :

Admittedly, the above tricks with the MPE/iX System Debugger are not at the beginner's level. But I thought I'd at least mention them here to give you an idea. Feel free to use different debugging techniques for your own ports. Extensive source code analysis or adding printf statements at "strategic" locations in the source can also be successful. Notice that many freeware packages do already have some kind of debug instrumentation included. Check for command line options or environment variables in this regard.

But back to our /dev/null issue above. In the above debug snippets you might notice that the failing open() was actually trying to access /dev/nu instead of /dev/null. Extensive investigation reveals that the code in lib/pgp/pipe/file/pgpFileMod.c suffers from a bug in the MPE/iX version of the /usr/include/stdio.h header file. The FILENAME_MAX constant is #define'd to the value 8, which makes sense for classic MPE filenames, but should be much larger for Posix filenames. Unfortunately the header file does not have some kind of conditional compilation based on the _POSIX_SOURCE flag, for example.

 shell/iX> more lib/pgp/pipe/file/pgpFileMod.c
 ...
        FILE *fp;
        char filename[FILENAME_MAX];
        size_t len;
        int error;
 ...
        len = min (size, sizeof (filename) - 1);
        memcpy (filename, string, len);
        filename[len] = '\0';

        /* I need to know if this should be binary or not */
        fp = fopen (filename, "wb");
        if (!fp)
                return PGPERR_NO_FILE;

 shell/iX> find /usr/include -name '*.h' | xargs grep -F FILENAME_MAX

 /usr/include/stdio.h:#  define FILENAME_MAX 8

How to fix this (without waiting for a patch from HP)? Using the repeatedly mentioned ~/mpe/inc directory we can create an "intercept" version of stdio.h that #include's the official one and then uses #undef and #define to establish a better value for the faulty FILENAME_MAX constant (255 for cases where the _POSIX_SOURCE flag is set).

 shell/iX> cat ~/mpe/inc/stdio.h

 #ifndef _STDIO_INCLUDED

 #include "/usr/include/bsd/stdio.h"

 #ifdef _POSIX_SOURCE
 #undef  FILENAME_MAX
 #define FILENAME_MAX 255
 #endif

 #endif

A simple rebuild with make is not sufficient if the Makefile(s) do not cover dependencies on header files. So it is safer to locate all files that include stdio.h -or at least reference the broken FILENAME_MAX constant- (with the find & grep command discussed earlier) and remove the respective .o files (in our case only pgpFileMod.o) to force make to recompile them and thus pick up the new declarations.

 shell/iX> find * -name '*.c' | xargs fgrep -l FILENAME_MAX

 lib/pgp/pipe/file/pgpFileMod.c

 shell/iX> rm lib/pgp/pipe/file/pgpFileMod.o

 shell/iX> make
 ...

Cleaning up with make distclean

After a successful and complete build, there are usually lots of .o object and .a archive library files in the source directory tree. Sometimes the build even generates other auxiliary files needed during the build process (e.g. source files that are generated from some templates on the fly). To reduce the disc space there is usually one or more flavors of make clean available. Packages based on the GNU autoconf tools typically also have a make distclean (here make very-clean) that does not only remove the object and library files, but also the intermediate results of the configure phase (e.g. config.cache and config.status).

 shell/iX> cd pgp50i/src
 shell/iX> make very-clean

If you are planning to redistribute the ported package, you might want to try another fresh build after make distclean -maybe even in a different directory, MPE group or MPE account- to make sure that the successful build can be reproduced at will. One example why this might fail could be that you adjusted some Makefile that was generated from a Makefile.in template, but forgot to also adjust the latter for MPE/iX.

Finishing up for distribution

The following notes do not apply to the PGP porting example as there are various restrictions resulting from the PGP license file as well as the US government regarding ITAR and strong encryption software. Please keep this in mind. However, the notes below can be useful after porting other freeware packages.

My HP 3000 is running a web server which is configured with the USERDIR setting, i.e. allows every user to create his or her own web pages in a ~/public_html subdirectory inside their logon account. Thus I typically create such a subdirectory in the home directory of the respective port (here PGP.IX) and place various files related to the port there. An index.html file typically provides an overview and links to all the pieces, e.g. original location of the freeware on the Internet, original files used for the port, useful documentation, special adjustments done for MPE/iX, (potential or actual) limitations of the MPE/iX version, downloadable sources as well as compiled binaries. Keep in mind to use something like chmod 755 or chmod 644 to open the permissions wide enough so that the web server can actually access the files on behalf of a browsing client.

By using the ~/public_html trick, the porting result is accessible by a URL like http://my3000/~PGP.IX/ -notice that the part after ~ is case sensitive- which seems fairly easy to understand and remember and does not require access to the web server's directory tree for maintenance of the pages.

References and HP3000-L help

Below is a collection of web sites with related or otherwise useful information. I hope these links will remain valid for a while ;-)

Lars Appel, July 1999