Friday, August 20, 2010

Using Glib::RefPtr with STL sorted containers

It turns there's a bug in glibmm that prevents one from using Glib::RefPtr<T> with any of the STL sorted containers, e.g. std::map or std::set.

The issue is a side effect of Glib::RefPtr<T>:
  • declaring operators == and !=
  • declaring operator bool()
  • not declaring operators <, <=, >, >=

The operators == and != compare pointers for equality and work as expected. The operator bool() is defined to allow the following usage of a RefPtr:

if (ref_ptr)
    puts("is non-null");
else
    puts("is null");

STL sorted containers need operator< on the type they contain. Assuming p1 and p2 are of type Glib::RefPtr<T>, the following compiles just fine:

p1 < p2

This in turn means that e.g. std::set<Glib::RefPtr<T> > my_map; compiles. Unfortunately, with current version of glibmm, this breaks badly at runtime.

Why? As there's no custom operator<, the compiler interprets p1 < p2 like this:

(bool)p1 < (bool)p2

This is true iff p1 is NULL and p2 is non-NULL. The operators <=, >, >= also work on the results of operator bool(). So, depending on the pointers wrapped in p1 and p2, it's possible that the following would be true:

p1 != p2 && !(p1 < p2) && !(p1 > p2)

(this is true iff p1 and p2 wrap two different non-NULL pointers)

There's a bug report for the issue.

A workaround for the time being is to use a custom comparator like this:

template<typename T>
class RefPtrLess
{
public:
    bool operator() (const Glib::RefPtr<T> &a,
        const Glib::RefPtr<T> &b)
    {
        const T* const a_ptr = a.operator->();
        const T* const b_ptr = b.operator->();

        return a_ptr < b_ptr;
    }
};

typedef std::set<Glib::RefPtr<MyType>, RefPtrLess<MyType> > TMyTypeSet;

Thursday, July 15, 2010

Sending git patches from a different box

Here's a solution to a problem that's been bothering me for a while: I wanted to create git patches from a box where there is no real MTA and so git send-email can't be used.

One option is setting up real mail MTA or something simpler like msmtp or nullmailer. While this is the preferable, "true unix" way, I think plenty of ordinary linux users don't have this either.

If, for whatever reason, this is not an option, but you have access to another box, where a MTA is set up correctly, you can do this:
  1. commit the change(s) into the git tree
  2. create the patch emails with: git format-patch --to to@addr ...
  3. transfer the produced patch email files to the box where you can send mails from
  4. send the email(s) with: /usr/sbin/sendmail -oi -t < mail-formatted.patch

The first time I suggest you send the patch to yourself to verify it's all working as expected.

This approach has a downside: You don't see the mails you sent in your MUA sent mail folder. Depending on your MUA, there are other ways of sending the mail. If you're using mutt, you might be in luck. But in general MUAs tend to mangle the patches. :-(

Sunday, July 11, 2010

Configuring linux kernel for use on ALIX 2 -- update

Here is a short update on my older post on configuring linux kernel for use on ALIX2.

The general points are still valid. The specific locations of the options might have changed. But to provide something more up to date, here is a working linux 2.6.34.1 configuration for ALIX 2.

Use this only as a starting point to get a configuration that works well for you. There are plenty of options that are specific to my setup and you certainly want those changed. But you should be able to boot with this one.

Linux kernel configuration -- using configuration from an older version

As you probably know if you ever configured the linux kernel yourself (possibly by make menuconfig, or if you've been with linux long enough, by make config), the current kernel configuration that would be used for build is stored in the file .config.

The options in the .config file depend on the kernel version the file was produced with. So, you can't directly use a config file from an older kernel with a newer one. But what you can do is use make oldconfig. This way, the config options that are still present in the current kernel keep their values and you are asked to configure only what options were added.

To use this, just place the old .config file into the new kernel source tree and issue make oldconfig.

Yeah, this post was inspired by the comment asking for newer ALIX 2 kernel config. A comment that I missed. Sorry. Mike. :-(

Sunday, April 11, 2010

Soft-float (FPU emulation) with DJGPP

If you build a program using floating-point arithmetic with DJGPP and then try to run it on a box without a FPU (such as the 386, or 486SX), the program will die at the first attemt to use the floats.

The solution for this is to tell DJGPP to use floating point emulation.

While there is the -msoft-float GCC option, which seems to be recognized in the DJGPP port too, the option does not work under DJGPP.

There is a DJGPP FAQ item about FPU emulation. It turns out the easiest way is to just add -lemu to linker options.

Also, if you target the 386, add -march=i386 to compiler options. Otherwise the program might crash, as DJGPP can use an instruction that is not supported on the 386. (Not sure about 486. Is Pentium is the default target? Or the 486? Either way, it is good practice to specify -march as the lowest CPU the app needs to run on.)

TurboVision with DJGPP and DOSEMU

There's a nice port of the TUI library TurboVision to GCC. The port works on unix-like systems including linux, but it also still supports DOS (with DJGPP toolchain).

Here's a guide on how to build the DOS version.

First, grab the sources. I suggest you use the latest CVS version:
cvs -d:pserver:anonymous@tvision.cvs.sourceforge.net:/cvsroot/tvision login
(use empty password)
cvs -z3 -d:pserver:anonymous@tvision.cvs.sourceforge.net:/cvsroot/tvision co -P tvision

Then you need a DOS environment with DJGGP in it. DOSEMU + DJGPP worked ok for me (as detailed in my previous posts on the topic: 1 and 2).

Have a look into doc/install/djgpp.txt. As the file explains, you will need some extra GNU tools installed inside your DJGPP directory. Here is the list of the files that worked for me at the time of this writing:
  • djdev203.zip - basic DJGPP runtime
  • bnu219b.zip - GNU Binutils
  • gcc442b.zip - gcc
  • gpp442b.zip - g++
  • mak3791b.zip - GNU Make
  • fil41b.zip - GNU fileutils
  • txt20b.zip - GNU textutils
  • shl2011b.zip - GNU sh-utils
  • perl588b.zip - Perl

The TurboVision doc further mentions you will need GNU gettext. This is not a hard requirement -- you can build TurboVision without gettext support. If you do want to use gettext, be warned the procedure of installing GNU gettext is a bit complicated -- see the doc/install/djgpp.txt file and gnu/gtxt-010.40/djgpp/README inside the gettext archive (gtxt040b.zip).

So now all you need to do is put a copy of the TurboVision sources in a place that can be reached from your DOS environment, change to that directory and fire up configure.bat. (If building without gettext, add the --no-intl option.)

Unfortunately, you might just get this:
C:\tvision>configure
Configuring Turbo Vision v2.2.0 library

Unknown OS, you must do things by yourself at conflib.pl line 903.
Determining OS:

The reason here is that the "uname" command (part of GNU sh-utils) prints out:

c:\tvision>uname
??Unknow

instead of what the TurboVision configure script expects ("MS-DOS").

You either have to patch the configure script (hack the test if ($os=~/MS\-DOS/) to always pass, e.g. into if (1)) or change the uname command. (Quick and dirty hack to the uname command. Use the binary to overwrite the original djgpp\bin\uname.exe)

Either way, now the configure script should pass:

C:\tvision>configure --no-intl
Configuring Turbo Vision v2.2.0 library

Determining OS: DOS [djgpp]
Looking for a working gcc: gcc OK
C flags: -O2 -Wno-packed
C++ flags: -O2 -Wno-packed
Looking for the C++ compiler: gpp
Checking Architecture: x86
Looking for pointer size:  32 bits
Looking for prefix: c:/djgpp
Looking for GNU make: make
Looking for GNU ar: ar
Looking for install tool: install
Looking for xgettext: no
Checking DJGPP version: 2.0.3 OK
Checking for international support: disabled by user request.
Looking for allegro library: Bad command or filename - "allegro-config".
 no, disabling AlCon
Checking endianess: little endian

Generating Makefile
Configuring makefiles: intl/dummy/Makefile 
Configuring RHIDE: makes/rhide.env compat/rhide.env 
Configuring RHIDE: examples/rhide.env 
Generating configuration header: no changes
Extracting from makes/librhtv.imk: processing
Extracting from compat/compat.imk: processing
Processing winnt/bccmake.in => winnt/Makefile
Processing winnt/msvcmake.in => winnt/Makefile.nmk
Makefiles for examples.
Makefiles for translations.
Processing intl/gnumake.in => intl/Makefile
Processing redhat/librhtv.spec.in => redhat/librhtv-2.2.0.spec
Processing qnxrtp/tvision.qpg.in => qnxrtp/tvision.qpg

Succesful configuration!

Then fire up make. With the default DOSEMU configuration you might end up with this error:
cc1plus.exe: out of memory allocating 65536 bytes after a total of 6390696 bytes
make.exe[1]: *** [../makes/obj/iffilelen.o] Error 1
make.exe[1]: Leaving directory `c:/tvision/makes'
make.exe: *** [static-lib] Error 2

The solution is to increase the amount of DPMI memory in /etc/dosemu/dosemu.conf, e.g.: $_dpmi = (0x10000) instead of the default 0x5000.

You can that use make examples to build some sample code. Start up e.g. examples\demo\demo.exe to see if the library compiled and works fine (works best with xdosemu, don't expect it to work in dumb mode :-)).

To build your own programs with the TurboVision library, just add -IC:\tvision\include to compiler options and -Lc:\tvision\makes -lrhtv to linker options. (Substitute c:\tvision with your TurboVision sources path.)

Thursday, January 21, 2010

Building for DOS, part 2: Setting up DJGPP under DOSEMU

First edit your /etc/dosemu/dosemu.conf: bump up the size of the available DPMI memory from the default 0x5000 KiB to something like 0x10000 or even 0x20000, as you might otherwise run out of memory on more complicated source files.
Now you can proceed with DJGPP install. -- I suggest you use the Zip File Picker, as I already mentioned in the previous post.

The Zip File Picker suggests you make the zip extracting from inside DOSEMU. But in my experience extracting from linux works ok and it's more comfortable. Do read the readme. But the first part of the installation can be as simple as this (you could pick a different target directory name, as long as it's not dev or dev/something):

get all the needed ZIP files in a directory
$ cd ~/.dosemu/drive_c
$ mkdir djgpp
$ for i in /path/to/*.zip; do unzip $i -d djgpp; done

Next step is setting the DJGPP environment variable and adding c:\djgpp\bin into the PATH. The easiest way to do this with dosemu is by editing autoexec.bat. A word of warning -- if the file is a symlink and you can't edit the file the symlink points to (as is the case with default Ubuntu install), you have to change the symlink into a (editable) copy first, e.g.:

$ cd ~/.dosemu/drive_c
$ cp autoexec.bat autoexec.bat.copy; mv autoexec.bat.copy autoexec.bat

As the file uses DOS line end characters (CR LF), if might look odd in some linux text editors (like mcedit). The easiest way is to edit the file from within dosemu -- fire up DOSEMU, then type:

edit autoexec.bat

The default dosemu autoexec.bat file sets up some environment variables and then plays some DOSEMU-specific tricks. So before the unix and lredir tricks insert these two lines:

set DJGPP=C:\DJGPP\DJGPP.ENV
set PATH=C:\DJGPP\BIN;%PATH%

The last step is adapting config.sys (use edit config.sys). Zip File Picker suggests config.sys is to contain these three lines:

files=40
fcbs=40,0
shell=c:\dos\command.com c:\dos /e:2048 /p

On my ubuntu install, the "files=40" line is already present. So just "fcbs=40,0" needs to be added. The file also contains:

shellhigh=z:\command.com /e:1024 /p

It worked ok for me if I left it at "shellhigh" and just enlarged the environment size from 1024 to DJGPP-suggested 2048:

shellhigh=z:\command.com /e:2048 /p

Restart DOSEMU (exitemu in dosemu, then fire up DOSEMU again). If everything worked ok you should be able to build and run C (and optionally other languages, if you installed those too) programs in DOSEMU:

C:\test>gcc test1.c -o test1.exe
C:\test>test1
Hello World!

Note: On one of my boxes (running Ubuntu 9.10, DOSEMU 1.4.0+svn.1828-2ubuntu2) I was unable to get g++ versions 4.4.1 and 4.4.2 working. -- But 4.4.2 is working fine for me on Gentoo with dosemu-1.4.1_pre20091009. Here is the bug report.

It seems to be possible to work around the issue for the time being: Set $_cpu_emu = "vm86sim" in /etc/dosemu/dosemu.conf and use GCC version 4.3.2.

Monday, January 11, 2010

Building for DOS, part 1

And now for something completely different. :)

The unthinkable happened -- I had to build some C++ program of mine for FreeDOS. I understand that DOS is by all means a dead platform, but if you happen to have to target DOS, then read on. :)

The good news is that there is a port of GCC for DOS. It's called DJGPP. The website pretty much looks like it looked 10 years ago, which might imply it's dead. But in fact DJGPP is still alive, as the newsgroups and mailing lists prove.

There are more ways how you could use DJGPP: You could install it on a real DOS machine (yuck!). Or you could install linux to DJGPP cross-compiler (haven't tried, but doc implies it's tricky). Or you could install DJGGP inside DOSEMU. That is the way I chose.

Determining what files you need to download is a bit daunting, but DJGPP offers a handy tool, the Zip File Picker.

For DOSEMU you want to select "Build and run programs with DJGPP", then "DOSEMU". Select the languages you need. Unless you're feeling adventurous, you don't need any DOS GUI, you can stick to editing the files from linux, and just build in DOSEMU.

There are in fact more things than what the Zip File Picker offers that you can install. This includes various utilities and libraries. You can even get Perl for DOS. To see what's available, have a look into Getting djgpp - pick a mirror, then go to the "current" directory.

In second part I describe the configuration of DOSEMU for DJGPP.