Awesome, if I see any other quirks I'll shout out 😁.
NSIS 3.0a1
71 posts
Linux build
Can I build source of nsis3.0 on linux? 🙂
Can I build source of nsis3.0 on linux? 🙂
Not yet, though it will be worked on later in development, probably once everything is integrated and working well enough.
It would be really helpful if someone would be willing to try to fix POSIX and submit some patches...Originally Posted by nidzadragon View PostCan I build source of nsis3.0 on linux? 🙂
I already tried to build this version using SCons, but I get error "zlib is missing" even if I set correct path to zlib trough ZLIB_W32 environment variable.
I know very little about python but I'm giving posix compiling a go.
I'm using ubuntu 12.04, 64 bit.
If I have to, I'll do my testing on virtualbox under the 32 bit version.
This is the line I'm using: scons SKIPSTUBS=all SKIPPLUGINS=all SKIPUTILS=all SKIPMISC=all SKIPDOC=all
I got the zlib dependancy working, and halibut compiles up correctly but it won't execute, throwing up an error saying "Permission denied", so I have commented that section out of Sconstruct for now.
Question: is pluginapi.c really required for compiling makensis? Because the first line in it is: #include <windows.h>, which of course doesn't exist on linux, and it throws an error. I have substituted a basic cross-plaform ready file in it's place.
Also, I noticed that the first file that doesn't compile is related to makensis, specifically:
o build/urelease/makensis/7zip/7zGuids.o -c -Wno-non-virtual-dtor -Wall -O2 -DNSISCALL="__attribute__((__stdcall__))" -D_UNICODE -DUNICODE -D__BIG_ENDIAN__ -DMAKENSIS -D_WIN32_IE=0x0500 -DCOMPRESS_MF_BT -Ibuild/urelease/config Source/7zip/7zGuids.cpp
sh: 1: o: not found
I wil do some more testing on the 32 bit version and see if these pop up there too.
I'm using ubuntu 12.04, 64 bit.
If I have to, I'll do my testing on virtualbox under the 32 bit version.
This is the line I'm using: scons SKIPSTUBS=all SKIPPLUGINS=all SKIPUTILS=all SKIPMISC=all SKIPDOC=all
I got the zlib dependancy working, and halibut compiles up correctly but it won't execute, throwing up an error saying "Permission denied", so I have commented that section out of Sconstruct for now.
Question: is pluginapi.c really required for compiling makensis? Because the first line in it is: #include <windows.h>, which of course doesn't exist on linux, and it throws an error. I have substituted a basic cross-plaform ready file in it's place.
Also, I noticed that the first file that doesn't compile is related to makensis, specifically:
o build/urelease/makensis/7zip/7zGuids.o -c -Wno-non-virtual-dtor -Wall -O2 -DNSISCALL="__attribute__((__stdcall__))" -D_UNICODE -DUNICODE -D__BIG_ENDIAN__ -DMAKENSIS -D_WIN32_IE=0x0500 -DCOMPRESS_MF_BT -Ibuild/urelease/config Source/7zip/7zGuids.cpp
sh: 1: o: not found
I wil do some more testing on the 32 bit version and see if these pop up there too.
[REMOVED]
...nevermind, stupid me - found the issue on my end. Compiler flags FTW. 😉
...nevermind, stupid me - found the issue on my end. Compiler flags FTW. 😉
I'm running a 32 bit environment now, and all those errors above have vanished, with a new one (several, actually) popping up:
[edit] I managed to type-cast that function above so it will compile, wether it works or not is another story: readInto = _wcsdup((wchar_t*)arr);.
Here's one of the "not declared" errors:
Another question: I see there are some defines like this: '#if 0 '. Are these really needed? Because the number of errors are reduced alot when they are removed (like the one above is resolved). Should I put them in #ifdef _WIN32, #else, #endif blocks? Or remove them altogether?
g++ -o build/urelease/makensis/DialogTemplate.o -c -Wno-non-virtual-dtor -Wall -O2 -DNSISCALL="__attribute__((__stdcall__))" -D_UNICODE -DUNICODE -DMAKENSIS -D_WIN32_IE=0x0500 -Ibuild/urelease/config Source/DialogTemplate.cppI am also getting a bunch of functions that aren't declared: _wcsnicmp, _wcsicmp. It also appears the family of "wcs" functions are either not declared or have type-cast conversion problems. I'm still looking through the source code to find these functions and maybe fix them if I'm confident enough. I wish Visual Studio worked on linux, the "Find in Files" search is so great.
Source/DialogTemplate.cpp: In function 'void ReadVarLenArr(unsigned char*&, WCHAR*&, unsigned int)':
Source/DialogTemplate.cpp:62:38: error: cannot convert 'WCHAR* {aka short unsigned int*}' to 'const wchar_t*' for argument '1' to 'wchar_t* wcsdup(const wchar_t*)'
[edit] I managed to type-cast that function above so it will compile, wether it works or not is another story: readInto = _wcsdup((wchar_t*)arr);.
Here's one of the "not declared" errors:
Source/DialogTemplate.cpp: In member function 'void CDialogTemplate::AddItem(DialogItemTemplate)':Also, these functions that aren't declared are confusing, I followed the include chain and the function prototype is definitely being declared, so I don't know what's going on there 🧟.
Source/DialogTemplate.cpp:306:44: error: '_wcsdup' was not declared in this scope
Another question: I see there are some defines like this: '#if 0 '. Are these really needed? Because the number of errors are reduced alot when they are removed (like the one above is resolved). Should I put them in #ifdef _WIN32, #else, #endif blocks? Or remove them altogether?
We should try to stay away from casting to shut up the compiler. In the dialog stuff there might be some strings that are always UTF16LE that end up as resources in the .exe. IIRC there is a StrLenUTF16 in utf.h and some other helpers in winchar.cpp
I'll jump on IRC now if you want some more help...
I'll jump on IRC now if you want some more help...
I have found most of the type-cast's are dealing with TCHAR*, WCHAR* and wchar_t*. There are also some missing function prototypes, but I am making a list of each change I'm making so it's easier for experienced programmers to change. gcc is an anal b**** 😠.
I noticed that WCHAR is typedef'ed unsigned short in Platform.h. Apparently these two can be directly type-cast with no problems, though I don't know the truth.
#if !defined(_NATIVE_WCHAR_T_DEFINED)
typedef unsigned short WCHAR;
#else
typedef wchar_t WCHAR;
#endif
Yes but I believe it really is there just for the exehead stuff, you should not be calling the wcs* string functions with a WCHAR* parameter in makensis code (Not portable)Originally Posted by JasonFriday13 View PostI noticed that WCHAR is typedef'ed unsigned short in Platform.h. Apparently these two can be directly type-cast with no problems, though I don't know the truth.
I've had enough for now. I don't know if windows is smart and knows about the implied cast-typing, but gcc sure doesn't and it catches every type problem, which makes it very hard to troubleshoot. I might start on the non unicode version first as that seems easier to get change it around between ansi and unicode versions.
The ANSI version is going to have even more issues so I don't think that is a good idea. What about the diff I sent you?
I applied that patch to a fresh 3.0a1 source (compiling with unicode on) and it's getting better, DialogTemplate.cpp compiles with no errors now. Also I have to remove 'ExDLL' from the lib_plugins list in Sconstruct, because it tries compiling pluginapi.c, which of course includes windows.h and throws an error about not finding it.
I am now getting another error in ResourceEditor.cpp, in:
return UpdateResourceW(szType, MAKEINTRESOURCEW(szName), wLanguage, lpData, dwSize);
saying "no type conversion found for argument 1". So I added this function to convert it:
I am getting one error for this file still (other files don't compile if there are errors):
I am now getting another error in ResourceEditor.cpp, in:
return UpdateResourceW(szType, MAKEINTRESOURCEW(szName), wLanguage, lpData, dwSize);
saying "no type conversion found for argument 1". So I added this function to convert it:
return UpdateResourceW(WinWStrDupFromTChar(szType), MAKEINTRESOURCEW(szName), wLanguage, lpData, dwSize);I did this to the four "Resource" functions, and there are no errors there now. Also I changed this function call at line 985 to:
return WinWStrLen(m_szName); // wcslen(m_szName);which cleared up that error.
I am getting one error for this file still (other files don't compile if there are errors):
Source/ResourceEditor.cpp: In member function 'int CResourceDirectory::Find(const WCHAR*)':I'll look around for alternatives for that function.
Source/ResourceEditor.cpp:865:40: error: '_wtoi' was not declared in this scope
Calling WinWStrDupFromTChar like that is a memory leak but its fine for testing.
There is probably something like myatoi in util.c or one of the plugin files, use it to create a WinWStrToInt in winchar.cpp/h if you cannot come up with anything else. I'll try to take a look at the code tonight...
There is probably something like myatoi in util.c or one of the plugin files, use it to create a WinWStrToInt in winchar.cpp/h if you cannot come up with anything else. I'll try to take a look at the code tonight...
There is, it's in "source/exehead/util.c", so I copied it over into "winchar.c" and renamed it like this:
Now I'm getting this error:
#ifndef _WIN32 // existing #ifdefI added it to the header and to ResourceEditor.cpp, and it compiles. I hope it actually works.
int WinWStrToInt(const WINWCHAR *s)
{
unsigned int v=0;
int sign=1; // sign of positive
TCHAR m=10; // base of 10
TCHAR t=_T('9'); // cap top of numbers at 9
if (*s == _T('-'))
{
s++; //skip over -
sign=-1; // sign flip
}
if (*s == _T('0'))
{
s++; // skip over 0
if (s[0] >= _T('0') && s[0] <= _T('7'))
{
m=8; // base of 8
t=_T('7'); // cap top at 7
}
if ((s[0] & ~0x20) == _T('X'))
{
m=16; // base of 16
s++; // advance over 'x'
}
}
for (;; )
{
int c=*s++;
if (c >= _T('0') && c <= t) c-=_T('0');
// clever little trick to do both upper and lowercase A-F.
else if (m==16 && (c & ~0x20) >= _T('A') && (c & ~0x20) <= _T('F')) c = (c & 7) + 9;
else break;
v*=m;
v+=c;
}
return ((int)v)*sign;
}
Now I'm getting this error:
In file included from Source/ResourceVersionInfo.h:30:0,It comes up a few times. This is one of the code parts the function refers to in "strlist.h":
from Source/ResourceVersionInfo.cpp:19:
Source/strlist.h: In member function 'int SortedStringListND<T>::find(const TCHAR*, int, int, int, int*)':
Source/strlist.h:402:38: error: there are no arguments to '_wcsicmp' that depend on a template parameter, so a declaration of '_wcsicmp' must be available [-fpermissive]
Source/strlist.h:402:38: note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
if (case_sensitive)It's weird how it's brother "_wcscmp" is declared, but "_wcsicmp" is not declared in this scope. There are also some "'_swprintf' was not declared in this scope" errors, I would change these to "wsprintf", but is this a good function or should a different one be used?
res = _tcscmp(str, pCurr);
else
res = _tcsicmp(str, pCurr);
What about adding "|| !defined _WIN32" to line 53 in tchar.h? Because swprintf is recongnised under linux.
I also noticed on linux, swprintf also requires a max length argument:
int swprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, ...);I quick dodgied the code (with #warning tags for stuff I changed or removed) to see what other errors I got, and it looks like build.cpp has lots of errors there. Also I had to comment out line 246 in Sconstruct because "tchar.h" doesn't exist in the build directory. And all the TEXT() macros that are in the source files I have been changing to _T(), because that is supported under linux.
The *icmp stuff is not really part of the standard, the string support sucks.
Here is another diff, should probably apply it to svn trunk and not alpha 1 if you can.
Not sure what to do about swprintf, I'll have to think about it some more...
Here is another diff, should probably apply it to svn trunk and not alpha 1 if you can.
Not sure what to do about swprintf, I'll have to think about it some more...
you could try this
#ifndef _WIN32
int swprintf(wchar_t*buf, const wchar_t*fmt, ...) // non-standard overloaded version for MSVC compatibility
{
va_list val;
va_start(val, fmt);
int res = vswprintf(buf, -1, fmt, val);
va_end(val);
return res;
}
#endif
So went and downloaded the trunk source-tree, and added both of those diff's you gave me to a fresh copy.
I put that function into "Source/util.cpp" and it's prototype in "Source/util.h", it still gave a compile error saying it's undeclared, so I changed line 53 in "tchar.h" to
So far these are the files that are compiling successfully (in this order):
I put that function into "Source/util.cpp" and it's prototype in "Source/util.h", it still gave a compile error saying it's undeclared, so I changed line 53 in "tchar.h" to
#if (defined(_MSC_VER) && (_MSC_VER<=1310)) || defined(__MINGW32__) || !defined _WIN32and it compiles without an error.
So far these are the files that are compiling successfully (in this order):
scons: Cleaning targets ...Next on the fail list is build.cpp, which gives all these errors (I combed out the duplicates to save some space):
Removed build/urelease/halibut/biblio.o
Removed build/urelease/halibut/bk_xhtml.o
Removed build/urelease/halibut/contents.o
Removed build/urelease/halibut/error.o
Removed build/urelease/halibut/help.o
Removed build/urelease/halibut/index.o
Removed build/urelease/halibut/input.o
Removed build/urelease/halibut/keywords.o
Removed build/urelease/halibut/licence.o
Removed build/urelease/halibut/main.o
Removed build/urelease/halibut/malloc.o
Removed build/urelease/halibut/misc.o
Removed build/urelease/halibut/style.o
Removed build/urelease/halibut/tree234.o
Removed build/urelease/halibut/ustring.o
Removed build/urelease/halibut/version.o
Removed build/urelease/halibut/halibut
Removed build/urelease/Docs/html/IndexPage.html
Removed build/urelease/Docs/html/Contents.html
Removed build/urelease/Docs/html/Chapter1.html
Removed build/urelease/Docs/html/Chapter2.html
Removed build/urelease/Docs/html/Chapter3.html
Removed build/urelease/Docs/html/Chapter4.html
Removed build/urelease/Docs/html/Chapter5.html
Removed build/urelease/Docs/html/AppendixA.html
Removed build/urelease/Docs/html/AppendixB.html
Removed build/urelease/Docs/html/AppendixC.html
Removed build/urelease/Docs/html/AppendixD.html
Removed build/urelease/Docs/html/AppendixE.html
Removed build/urelease/Docs/html/AppendixF.html
Removed build/urelease/Docs/html/AppendixG.html
Removed build/urelease/Docs/html/AppendixH.html
Removed build/urelease/Docs/html/AppendixI.html
Removed build/urelease/makensis/7zip/7zGuids.o
Removed build/urelease/makensis/7zip/7zip/Common/OutBuffer.o
Removed build/urelease/makensis/7zip/7zip/Common/StreamUtils.o
Removed build/urelease/makensis/7zip/7zip/Compress/LZ/LZInWindow.o
Removed build/urelease/makensis/7zip/7zip/Compress/LZMA/LZMAEncoder.o
Removed build/urelease/makensis/7zip/7zip/Compress/RangeCoder/RangeCoderBit.o
Removed build/urelease/makensis/7zip/Common/Alloc.o
Removed build/urelease/makensis/7zip/Common/CRC.o
Removed build/urelease/makensis/DialogTemplate.o
Removed build/urelease/makensis/Plugins.o
Removed build/urelease/makensis/ResourceEditor.o
Removed build/urelease/makensis/ResourceVersionInfo.o
Removed build/urelease/makensis/ShConstants.o
scons: done cleaning targets.
g++ -o build/urelease/makensis/build.o -c -Wno-non-virtual-dtor -Wall -O2 -DNSISCALL="__attribute__((__stdcall__))" -D_UNICODE -DUNICODE -DMAKENSIS -D_WIN32_IE=0x0500 -Ibuild/urelease/config Source/build.cpp
Source/build.cpp: In member function 'void CEXEBuild::initialize(const TCHAR*)':
Source/build.cpp:388:44: error: '_wgetenv' was not declared in this scope
Source/build.cpp:394:16: error: invalid conversion from 'const char*' to 'wchar_t' [-fpermissive]
/usr/include/c++/4.6/bits/basic_string.h:560:7: error: initializing argument 1 of 'std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>:😳perator=(_CharT) [with _CharT = wchar_t, _Traits = std::char_traits<wchar_t>, _Alloc = std::allocator<wchar_t>, std::basic_string<_CharT, _Traits, _Alloc> = std::basic_string<wchar_t>]' [-fpermissive]
Source/build.cpp: In member function 'int CEXEBuild::add_string(const TCHAR*, int, UINT)':
Source/build.cpp:487:33: error: '_wcsdup' was not declared in this scope
Source/build.cpp: In function 'char* convert_processed_string_to_ansi(char*, const TCHAR*, WORD)':
Source/build.cpp:533:90: error: cannot convert 'const TCHAR* {aka const wchar_t*}' to 'LPCWSTR {aka const short unsigned int*}' for argument '3' to 'int WideCharToMultiByte(UINT, DWORD, LPCWSTR, int, LPSTR, int, LPCSTR, LPBOOL)'
Source/build.cpp:543:34: error: 'LOBYTE' was not declared in this scope
Source/build.cpp:544:34: error: 'HIBYTE' was not declared in this scope
Source/build.cpp: In member function 'int CEXEBuild:😛reprocess_string(TCHAR*, const TCHAR*, WORD)':
Source/build.cpp:655:69: error: 'MAKEWORD' was not declared in this scope
Source/build.cpp: In member function 'int CEXEBuild::add_label(const TCHAR*)':
Source/build.cpp: In member function 'int CEXEBuild::resolve_jump_int(const TCHAR*, int*, int, int, int)':
Source/build.cpp:1364:29: error: '_wtoi' was not declared in this scope
Source/build.cpp: In member function 'int CEXEBuild::AddVersionInfo()':
Source/build.cpp:1816:112: error: no matching function for call to 'CResourceEditor::UpdateResource(LPSTR, int, LANGID&, BYTE*, int)'
Source/build.cpp:1816:112: note: candidate is:
Source/ResourceEditor.h:117:9: note: bool CResourceEditor::UpdateResource(const TCHAR*, WORD, LANGID, BYTE*, DWORD)
Source/ResourceEditor.h:117:9: note: no known conversion for argument 1 from 'LPSTR {aka char*}' to 'const TCHAR* {aka const wchar_t*}'
Source/build.cpp: In member function 'int CEXEBuild::ProcessPages()':
Source/build.cpp:2186:10: error: no matching function for call to 'CResourceEditor::GetResource(LPSTR, int, int)'
Source/build.cpp:2186:10: note: candidate is:
Source/ResourceEditor.h:118:9: note: BYTE* CResourceEditor::GetResource(const TCHAR*, WORD, LANGID)
Source/ResourceEditor.h:118:9: note: no known conversion for argument 1 from 'LPSTR {aka char*}' to 'const TCHAR* {aka const wchar_t*}'
Source/build.cpp: In member function 'int CEXEBuild:😛ack_exe_header()':
Source/build.cpp:2505:28: error: '_wremove' was not declared in this scope
Source/build.cpp:2511:26: error: '_wremove' was not declared in this scope
Source/build.cpp: In member function 'int CEXEBuild::write_output()':
Source/build.cpp:2947:7: error: 'LPTSTR' was not declared in this scope
Source/build.cpp:2947:14: error: expected ';' before 'cmdstr'
Source/build.cpp:2948:14: error: expected ';' before 'arg'
Source/build.cpp:2949:11: error: 'arg' was not declared in this scope
Source/build.cpp:2952:9: error: 'cmdstrbuf' was not declared in this scope
Source/build.cpp:2952:30: error: expected ';' before 'malloc'
Source/build.cpp:2958:47: error: 'cmdstr' was not declared in this scope
Source/build.cpp:2965:49: error: 'cmdstr' was not declared in this scope
Source/build.cpp:2974:12: error: 'cmdstrbuf' was not declared in this scope
scons: *** [build/urelease/makensis/build.o] Error 1
scons: building terminated because of errors.
This should contain all fixes so far except swprintf.
because "tchar.h" doesn't exist in the build directory.I don't really know what this means. nsis-version.h?
Yeah, Sconstruct writes "#include "tchar.h"" into "nsis-version.h", but tchar.h is in the source directory, not the current directory ("build/urelease/config").
I'll add that patch now and get back to you.
I'll add that patch now and get back to you.
So I added that patch you gave me, and I had to add #include "tchar.h" and #include "utf.h" to "ResourceVersionInfo.cpp" for it to compile with no errors.
That Sconstruct patch has caused a problem, these two lines:
I'm still getting these errors in build.cpp (I combed the duplicates to save space):
That Sconstruct patch has caused a problem, these two lines:
defenv.Append(NSIS_CPPDEFINES = [('PREFIX_CONF', 'L"%s"' % defenv.subst('$PREFIX_CONF'))])gives this as it's output in "nsis-defines.h":
defenv.Append(NSIS_CPPDEFINES = [('PREFIX_DATA', 'L"%s"' % defenv.subst('$PREFIX_DATA'))])
definedlist.add(_T("PREFIX_CONF"), _T("L"/usr/local/etc""));Which of course causes a few compiler errors.
definedlist.add(_T("PREFIX_DATA"), _T("L"/usr/local/share/nsis""));
I'm still getting these errors in build.cpp (I combed the duplicates to save space):
Source/build.cpp:1816:112: error: no matching function for call to 'CResourceEditor::UpdateResource(LPSTR, int, LANGID&, BYTE*, int)'[edit] Turns out these are related to the MAKEINTRESOURCE macros in "Platform.h", and I've come up with a fix in "Platform.h" on line 491. The macro is basically a type-cast, so I just made up another that works for wchar_t (don't know if this will work at runtime though). The "D" suffix is obviously for 'define':
Source/build.cpp:1816:112: note: candidate is:
Source/ResourceEditor.h:117:9: note: bool CResourceEditor::UpdateResource(const TCHAR*, WORD, LANGID, BYTE*, DWORD)
Source/ResourceEditor.h:117:9: note: no known conversion for argument 1 from 'LPSTR {aka char*}' to 'const TCHAR* {aka const wchar_t*}'
Source/build.cpp:2186:10: error: no matching function for call to 'CResourceEditor::GetResource(LPSTR, int, int)'
Source/build.cpp:2186:10: note: candidate is:
Source/ResourceEditor.h:118:9: note: BYTE* CResourceEditor::GetResource(const TCHAR*, WORD, LANGID)
Source/ResourceEditor.h:118:9: note: no known conversion for argument 1 from 'LPSTR {aka char*}' to 'const TCHAR* {aka const wchar_t*}'
#ifndef _UNICODE
# define MAKEINTRESOURCED MAKEINTRESOURCEA
#else
# define MAKEINTRESOURCED(i) ((wchar_t*)((ULONG_PTR)((WORD)(i))))
#endif
#ifndef RT_BITMAP
# define RT_BITMAP MAKEINTRESOURCED(2)
#endif
#ifndef RT_ICON
# define RT_ICON MAKEINTRESOURCED(3)
#endif
#ifndef RT_DIALOG
# define RT_DIALOG MAKEINTRESOURCED(5)
#endif
#ifndef RT_GROUP_ICON
# define RT_GROUP_ICON MAKEINTRESOURCED(14)
#endif
#ifndef RT_VERSION
# define RT_VERSION MAKEINTRESOURCED(16)
#endif
Oh and I managed to fix "pluginapi.c" from trying to compile on linux. I changed line 691 in Scontruct from this:
for plugin in plugin_libs + plugins:To this:
for plugin in plugin_libs and plugins:
New Diff, I believe I fixed the tchar.h path issue and maybe messed up something else 😉
Yeah something else did mess up:
In file included from Source/util.h:25:0,
from Source/DialogTemplate.cpp:21:
Source/ResourceEditor.h: In member function 'bool CResourceEditor::UpdateResource(const WCHAR*, WORD, LANGID, BYTE*, DWORD)':
Source/ResourceEditor.h:129:28: error: cast from 'const WCHAR* {aka const short unsigned int*}' to 'WORD {aka short unsigned int}' loses precision [-fpermissive]
Source/ResourceEditor.h: In member function 'BYTE* CResourceEditor::GetResource(const WCHAR*, WORD, LANGID)':
Source/ResourceEditor.h:134:25: error: cast from 'const WCHAR* {aka const short unsigned int*}' to 'WORD {aka short unsigned int}' loses precision [-fpermissive]
Also, the compile system stops on the first error encountered, so several files still need to be compiled. For one, I know "dirreader.cpp" will need it's function prototypes changed, and "script.cpp" isn't being compiled yet either, along with a few others like "lineparse.cpp" and "lang.cpp".
I have also been working on some stuff and I have made a .txt file that should describe everything that's wrong (though I might have missed something) and a few minor fixes that I've done. It's over 1600 lines but 3/4 of it includes one of the patches you gave me.